mesa/src/freedreno/vulkan/tu_pipeline.c

4496 lines
168 KiB
C

/*
* Copyright © 2016 Red Hat.
* Copyright © 2016 Bas Nieuwenhuizen
*
* based in part on anv driver which is:
* Copyright © 2015 Intel Corporation
*
* 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 "common/freedreno_guardband.h"
#include "tu_private.h"
#include "ir3/ir3_nir.h"
#include "main/menums.h"
#include "nir/nir.h"
#include "nir/nir_builder.h"
#include "spirv/nir_spirv.h"
#include "util/debug.h"
#include "util/mesa-sha1.h"
#include "util/u_atomic.h"
#include "vk_format.h"
#include "vk_pipeline.h"
#include "vk_util.h"
#include "tu_cs.h"
/* Emit IB that preloads the descriptors that the shader uses */
static void
emit_load_state(struct tu_cs *cs, unsigned opcode, enum a6xx_state_type st,
enum a6xx_state_block sb, unsigned base, unsigned offset,
unsigned count)
{
/* Note: just emit one packet, even if count overflows NUM_UNIT. It's not
* clear if emitting more packets will even help anything. Presumably the
* descriptor cache is relatively small, and these packets stop doing
* anything when there are too many descriptors.
*/
tu_cs_emit_pkt7(cs, opcode, 3);
tu_cs_emit(cs,
CP_LOAD_STATE6_0_STATE_TYPE(st) |
CP_LOAD_STATE6_0_STATE_SRC(SS6_BINDLESS) |
CP_LOAD_STATE6_0_STATE_BLOCK(sb) |
CP_LOAD_STATE6_0_NUM_UNIT(MIN2(count, 1024-1)));
tu_cs_emit_qw(cs, offset | (base << 28));
}
static unsigned
tu6_load_state_size(struct tu_pipeline *pipeline,
struct tu_pipeline_layout *layout, bool compute)
{
const unsigned load_state_size = 4;
unsigned size = 0;
for (unsigned i = 0; i < layout->num_sets; i++) {
if (!(pipeline->active_desc_sets & (1u << i)))
continue;
struct tu_descriptor_set_layout *set_layout = layout->set[i].layout;
for (unsigned j = 0; j < set_layout->binding_count; j++) {
struct tu_descriptor_set_binding_layout *binding = &set_layout->binding[j];
unsigned count = 0;
/* Note: some users, like amber for example, pass in
* VK_SHADER_STAGE_ALL which includes a bunch of extra bits, so
* filter these out by using VK_SHADER_STAGE_ALL_GRAPHICS explicitly.
*/
VkShaderStageFlags stages = compute ?
binding->shader_stages & VK_SHADER_STAGE_COMPUTE_BIT :
binding->shader_stages & VK_SHADER_STAGE_ALL_GRAPHICS;
unsigned stage_count = util_bitcount(stages);
if (!binding->array_size)
continue;
switch (binding->type) {
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
/* IBO-backed resources only need one packet for all graphics stages */
if (stages & ~VK_SHADER_STAGE_COMPUTE_BIT)
count += 1;
if (stages & VK_SHADER_STAGE_COMPUTE_BIT)
count += 1;
break;
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
/* Textures and UBO's needs a packet for each stage */
count = stage_count;
break;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
/* Because of how we pack combined images and samplers, we
* currently can't use one packet for the whole array.
*/
count = stage_count * binding->array_size * 2;
break;
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
case VK_DESCRIPTOR_TYPE_MUTABLE_VALVE:
break;
default:
unreachable("bad descriptor type");
}
size += count * load_state_size;
}
}
return size;
}
static void
tu6_emit_load_state(struct tu_pipeline *pipeline,
struct tu_pipeline_layout *layout, bool compute)
{
unsigned size = tu6_load_state_size(pipeline, layout, compute);
if (size == 0)
return;
struct tu_cs cs;
tu_cs_begin_sub_stream(&pipeline->cs, size, &cs);
for (unsigned i = 0; i < layout->num_sets; i++) {
/* From 13.2.7. Descriptor Set Binding:
*
* A compatible descriptor set must be bound for all set numbers that
* any shaders in a pipeline access, at the time that a draw or
* dispatch command is recorded to execute using that pipeline.
* However, if none of the shaders in a pipeline statically use any
* bindings with a particular set number, then no descriptor set need
* be bound for that set number, even if the pipeline layout includes
* a non-trivial descriptor set layout for that set number.
*
* This means that descriptor sets unused by the pipeline may have a
* garbage or 0 BINDLESS_BASE register, which will cause context faults
* when prefetching descriptors from these sets. Skip prefetching for
* descriptors from them to avoid this. This is also an optimization,
* since these prefetches would be useless.
*/
if (!(pipeline->active_desc_sets & (1u << i)))
continue;
struct tu_descriptor_set_layout *set_layout = layout->set[i].layout;
for (unsigned j = 0; j < set_layout->binding_count; j++) {
struct tu_descriptor_set_binding_layout *binding = &set_layout->binding[j];
unsigned base = i;
unsigned offset = binding->offset / 4;
/* Note: some users, like amber for example, pass in
* VK_SHADER_STAGE_ALL which includes a bunch of extra bits, so
* filter these out by using VK_SHADER_STAGE_ALL_GRAPHICS explicitly.
*/
VkShaderStageFlags stages = compute ?
binding->shader_stages & VK_SHADER_STAGE_COMPUTE_BIT :
binding->shader_stages & VK_SHADER_STAGE_ALL_GRAPHICS;
unsigned count = binding->array_size;
if (count == 0 || stages == 0)
continue;
switch (binding->type) {
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
base = MAX_SETS;
offset = (layout->set[i].dynamic_offset_start +
binding->dynamic_offset_offset) / 4;
FALLTHROUGH;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {
unsigned mul = binding->size / (A6XX_TEX_CONST_DWORDS * 4);
/* IBO-backed resources only need one packet for all graphics stages */
if (stages & ~VK_SHADER_STAGE_COMPUTE_BIT) {
emit_load_state(&cs, CP_LOAD_STATE6, ST6_SHADER, SB6_IBO,
base, offset, count * mul);
}
if (stages & VK_SHADER_STAGE_COMPUTE_BIT) {
emit_load_state(&cs, CP_LOAD_STATE6_FRAG, ST6_IBO, SB6_CS_SHADER,
base, offset, count * mul);
}
break;
}
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
case VK_DESCRIPTOR_TYPE_MUTABLE_VALVE:
/* nothing - input attachment doesn't use bindless */
break;
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: {
tu_foreach_stage(stage, stages) {
emit_load_state(&cs, tu6_stage2opcode(stage),
binding->type == VK_DESCRIPTOR_TYPE_SAMPLER ?
ST6_SHADER : ST6_CONSTANTS,
tu6_stage2texsb(stage), base, offset, count);
}
break;
}
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
base = MAX_SETS;
offset = (layout->set[i].dynamic_offset_start +
binding->dynamic_offset_offset) / 4;
FALLTHROUGH;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: {
tu_foreach_stage(stage, stages) {
emit_load_state(&cs, tu6_stage2opcode(stage), ST6_UBO,
tu6_stage2shadersb(stage), base, offset, count);
}
break;
}
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: {
tu_foreach_stage(stage, stages) {
/* TODO: We could emit less CP_LOAD_STATE6 if we used
* struct-of-arrays instead of array-of-structs.
*/
for (unsigned i = 0; i < count; i++) {
unsigned tex_offset = offset + 2 * i * A6XX_TEX_CONST_DWORDS;
unsigned sam_offset = offset + (2 * i + 1) * A6XX_TEX_CONST_DWORDS;
emit_load_state(&cs, tu6_stage2opcode(stage),
ST6_CONSTANTS, tu6_stage2texsb(stage),
base, tex_offset, 1);
emit_load_state(&cs, tu6_stage2opcode(stage),
ST6_SHADER, tu6_stage2texsb(stage),
base, sam_offset, 1);
}
}
break;
}
default:
unreachable("bad descriptor type");
}
}
}
pipeline->load_state = tu_cs_end_draw_state(&pipeline->cs, &cs);
}
struct tu_pipeline_builder
{
struct tu_device *device;
void *mem_ctx;
struct vk_pipeline_cache *cache;
struct tu_pipeline_layout *layout;
const VkAllocationCallbacks *alloc;
const VkGraphicsPipelineCreateInfo *create_info;
struct tu_compiled_shaders *shaders;
struct ir3_shader_variant *binning_variant;
uint64_t shader_iova[MESA_SHADER_FRAGMENT + 1];
uint64_t binning_vs_iova;
uint32_t additional_cs_reserve_size;
struct tu_pvtmem_config pvtmem;
bool rasterizer_discard;
/* these states are affectd by rasterizer_discard */
bool emit_msaa_state;
bool depth_clip_disable;
VkSampleCountFlagBits samples;
bool use_color_attachments;
bool use_dual_src_blend;
bool alpha_to_coverage;
uint32_t color_attachment_count;
VkFormat color_attachment_formats[MAX_RTS];
VkFormat depth_attachment_format;
uint32_t render_components;
uint32_t multiview_mask;
bool subpass_raster_order_attachment_access;
bool subpass_feedback_loop_color;
bool subpass_feedback_loop_ds;
};
static bool
tu_logic_op_reads_dst(VkLogicOp op)
{
switch (op) {
case VK_LOGIC_OP_CLEAR:
case VK_LOGIC_OP_COPY:
case VK_LOGIC_OP_COPY_INVERTED:
case VK_LOGIC_OP_SET:
return false;
default:
return true;
}
}
static VkBlendFactor
tu_blend_factor_no_dst_alpha(VkBlendFactor factor)
{
/* treat dst alpha as 1.0 and avoid reading it */
switch (factor) {
case VK_BLEND_FACTOR_DST_ALPHA:
return VK_BLEND_FACTOR_ONE;
case VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA:
return VK_BLEND_FACTOR_ZERO;
default:
return factor;
}
}
static bool tu_blend_factor_is_dual_src(VkBlendFactor factor)
{
switch (factor) {
case VK_BLEND_FACTOR_SRC1_COLOR:
case VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR:
case VK_BLEND_FACTOR_SRC1_ALPHA:
case VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA:
return true;
default:
return false;
}
}
static bool
tu_blend_state_is_dual_src(const VkPipelineColorBlendStateCreateInfo *info)
{
if (!info)
return false;
for (unsigned i = 0; i < info->attachmentCount; i++) {
const VkPipelineColorBlendAttachmentState *blend = &info->pAttachments[i];
if (tu_blend_factor_is_dual_src(blend->srcColorBlendFactor) ||
tu_blend_factor_is_dual_src(blend->dstColorBlendFactor) ||
tu_blend_factor_is_dual_src(blend->srcAlphaBlendFactor) ||
tu_blend_factor_is_dual_src(blend->dstAlphaBlendFactor))
return true;
}
return false;
}
static const struct xs_config {
uint16_t reg_sp_xs_ctrl;
uint16_t reg_sp_xs_config;
uint16_t reg_sp_xs_instrlen;
uint16_t reg_hlsq_xs_ctrl;
uint16_t reg_sp_xs_first_exec_offset;
uint16_t reg_sp_xs_pvt_mem_hw_stack_offset;
} xs_config[] = {
[MESA_SHADER_VERTEX] = {
REG_A6XX_SP_VS_CTRL_REG0,
REG_A6XX_SP_VS_CONFIG,
REG_A6XX_SP_VS_INSTRLEN,
REG_A6XX_HLSQ_VS_CNTL,
REG_A6XX_SP_VS_OBJ_FIRST_EXEC_OFFSET,
REG_A6XX_SP_VS_PVT_MEM_HW_STACK_OFFSET,
},
[MESA_SHADER_TESS_CTRL] = {
REG_A6XX_SP_HS_CTRL_REG0,
REG_A6XX_SP_HS_CONFIG,
REG_A6XX_SP_HS_INSTRLEN,
REG_A6XX_HLSQ_HS_CNTL,
REG_A6XX_SP_HS_OBJ_FIRST_EXEC_OFFSET,
REG_A6XX_SP_HS_PVT_MEM_HW_STACK_OFFSET,
},
[MESA_SHADER_TESS_EVAL] = {
REG_A6XX_SP_DS_CTRL_REG0,
REG_A6XX_SP_DS_CONFIG,
REG_A6XX_SP_DS_INSTRLEN,
REG_A6XX_HLSQ_DS_CNTL,
REG_A6XX_SP_DS_OBJ_FIRST_EXEC_OFFSET,
REG_A6XX_SP_DS_PVT_MEM_HW_STACK_OFFSET,
},
[MESA_SHADER_GEOMETRY] = {
REG_A6XX_SP_GS_CTRL_REG0,
REG_A6XX_SP_GS_CONFIG,
REG_A6XX_SP_GS_INSTRLEN,
REG_A6XX_HLSQ_GS_CNTL,
REG_A6XX_SP_GS_OBJ_FIRST_EXEC_OFFSET,
REG_A6XX_SP_GS_PVT_MEM_HW_STACK_OFFSET,
},
[MESA_SHADER_FRAGMENT] = {
REG_A6XX_SP_FS_CTRL_REG0,
REG_A6XX_SP_FS_CONFIG,
REG_A6XX_SP_FS_INSTRLEN,
REG_A6XX_HLSQ_FS_CNTL,
REG_A6XX_SP_FS_OBJ_FIRST_EXEC_OFFSET,
REG_A6XX_SP_FS_PVT_MEM_HW_STACK_OFFSET,
},
[MESA_SHADER_COMPUTE] = {
REG_A6XX_SP_CS_CTRL_REG0,
REG_A6XX_SP_CS_CONFIG,
REG_A6XX_SP_CS_INSTRLEN,
REG_A6XX_HLSQ_CS_CNTL,
REG_A6XX_SP_CS_OBJ_FIRST_EXEC_OFFSET,
REG_A6XX_SP_CS_PVT_MEM_HW_STACK_OFFSET,
},
};
static uint32_t
tu_xs_get_immediates_packet_size_dwords(const struct ir3_shader_variant *xs)
{
const struct ir3_const_state *const_state = ir3_const_state(xs);
uint32_t base = const_state->offsets.immediate;
int32_t size = DIV_ROUND_UP(const_state->immediates_count, 4);
/* truncate size to avoid writing constants that shader
* does not use:
*/
size = MIN2(size + base, xs->constlen) - base;
return MAX2(size, 0) * 4;
}
/* We allocate fixed-length substreams for shader state, however some
* parts of the state may have unbound length. Their additional space
* requirements should be calculated here.
*/
static uint32_t
tu_xs_get_additional_cs_size_dwords(const struct ir3_shader_variant *xs)
{
const struct ir3_const_state *const_state = ir3_const_state(xs);
uint32_t size = tu_xs_get_immediates_packet_size_dwords(xs);
/* Variable number of UBO upload ranges. */
size += 4 * const_state->ubo_state.num_enabled;
/* Variable number of dwords for the primitive map */
size += xs->input_size;
size += xs->constant_data_size / 4;
return size;
}
void
tu6_emit_xs_config(struct tu_cs *cs,
gl_shader_stage stage, /* xs->type, but xs may be NULL */
const struct ir3_shader_variant *xs)
{
const struct xs_config *cfg = &xs_config[stage];
if (!xs) {
/* shader stage disabled */
tu_cs_emit_pkt4(cs, cfg->reg_sp_xs_config, 1);
tu_cs_emit(cs, 0);
tu_cs_emit_pkt4(cs, cfg->reg_hlsq_xs_ctrl, 1);
tu_cs_emit(cs, 0);
return;
}
tu_cs_emit_pkt4(cs, cfg->reg_sp_xs_config, 1);
tu_cs_emit(cs, A6XX_SP_VS_CONFIG_ENABLED |
COND(xs->bindless_tex, A6XX_SP_VS_CONFIG_BINDLESS_TEX) |
COND(xs->bindless_samp, A6XX_SP_VS_CONFIG_BINDLESS_SAMP) |
COND(xs->bindless_ibo, A6XX_SP_VS_CONFIG_BINDLESS_IBO) |
COND(xs->bindless_ubo, A6XX_SP_VS_CONFIG_BINDLESS_UBO) |
A6XX_SP_VS_CONFIG_NTEX(xs->num_samp) |
A6XX_SP_VS_CONFIG_NSAMP(xs->num_samp));
tu_cs_emit_pkt4(cs, cfg->reg_hlsq_xs_ctrl, 1);
tu_cs_emit(cs, A6XX_HLSQ_VS_CNTL_CONSTLEN(xs->constlen) |
A6XX_HLSQ_VS_CNTL_ENABLED);
}
void
tu6_emit_xs(struct tu_cs *cs,
gl_shader_stage stage, /* xs->type, but xs may be NULL */
const struct ir3_shader_variant *xs,
const struct tu_pvtmem_config *pvtmem,
uint64_t binary_iova)
{
const struct xs_config *cfg = &xs_config[stage];
if (!xs) {
/* shader stage disabled */
return;
}
enum a6xx_threadsize thrsz =
xs->info.double_threadsize ? THREAD128 : THREAD64;
switch (stage) {
case MESA_SHADER_VERTEX:
tu_cs_emit_regs(cs, A6XX_SP_VS_CTRL_REG0(
.fullregfootprint = xs->info.max_reg + 1,
.halfregfootprint = xs->info.max_half_reg + 1,
.branchstack = ir3_shader_branchstack_hw(xs),
.mergedregs = xs->mergedregs,
));
break;
case MESA_SHADER_TESS_CTRL:
tu_cs_emit_regs(cs, A6XX_SP_HS_CTRL_REG0(
.fullregfootprint = xs->info.max_reg + 1,
.halfregfootprint = xs->info.max_half_reg + 1,
.branchstack = ir3_shader_branchstack_hw(xs),
));
break;
case MESA_SHADER_TESS_EVAL:
tu_cs_emit_regs(cs, A6XX_SP_DS_CTRL_REG0(
.fullregfootprint = xs->info.max_reg + 1,
.halfregfootprint = xs->info.max_half_reg + 1,
.branchstack = ir3_shader_branchstack_hw(xs),
));
break;
case MESA_SHADER_GEOMETRY:
tu_cs_emit_regs(cs, A6XX_SP_GS_CTRL_REG0(
.fullregfootprint = xs->info.max_reg + 1,
.halfregfootprint = xs->info.max_half_reg + 1,
.branchstack = ir3_shader_branchstack_hw(xs),
));
break;
case MESA_SHADER_FRAGMENT:
tu_cs_emit_regs(cs, A6XX_SP_FS_CTRL_REG0(
.fullregfootprint = xs->info.max_reg + 1,
.halfregfootprint = xs->info.max_half_reg + 1,
.branchstack = ir3_shader_branchstack_hw(xs),
.mergedregs = xs->mergedregs,
.threadsize = thrsz,
.pixlodenable = xs->need_pixlod,
.diff_fine = xs->need_fine_derivatives,
.varying = xs->total_in != 0,
/* unknown bit, seems unnecessary */
.unk24 = true,
));
break;
case MESA_SHADER_COMPUTE:
tu_cs_emit_regs(cs, A6XX_SP_CS_CTRL_REG0(
.fullregfootprint = xs->info.max_reg + 1,
.halfregfootprint = xs->info.max_half_reg + 1,
.branchstack = ir3_shader_branchstack_hw(xs),
.mergedregs = xs->mergedregs,
.threadsize = thrsz,
));
break;
default:
unreachable("bad shader stage");
}
tu_cs_emit_pkt4(cs, cfg->reg_sp_xs_instrlen, 1);
tu_cs_emit(cs, xs->instrlen);
/* emit program binary & private memory layout
* binary_iova should be aligned to 1 instrlen unit (128 bytes)
*/
assert((binary_iova & 0x7f) == 0);
assert((pvtmem->iova & 0x1f) == 0);
tu_cs_emit_pkt4(cs, cfg->reg_sp_xs_first_exec_offset, 7);
tu_cs_emit(cs, 0);
tu_cs_emit_qw(cs, binary_iova);
tu_cs_emit(cs,
A6XX_SP_VS_PVT_MEM_PARAM_MEMSIZEPERITEM(pvtmem->per_fiber_size));
tu_cs_emit_qw(cs, pvtmem->iova);
tu_cs_emit(cs, A6XX_SP_VS_PVT_MEM_SIZE_TOTALPVTMEMSIZE(pvtmem->per_sp_size) |
COND(pvtmem->per_wave, A6XX_SP_VS_PVT_MEM_SIZE_PERWAVEMEMLAYOUT));
tu_cs_emit_pkt4(cs, cfg->reg_sp_xs_pvt_mem_hw_stack_offset, 1);
tu_cs_emit(cs, A6XX_SP_VS_PVT_MEM_HW_STACK_OFFSET_OFFSET(pvtmem->per_sp_size));
uint32_t shader_preload_size =
MIN2(xs->instrlen, cs->device->physical_device->info->a6xx.instr_cache_size);
tu_cs_emit_pkt7(cs, tu6_stage2opcode(stage), 3);
tu_cs_emit(cs, CP_LOAD_STATE6_0_DST_OFF(0) |
CP_LOAD_STATE6_0_STATE_TYPE(ST6_SHADER) |
CP_LOAD_STATE6_0_STATE_SRC(SS6_INDIRECT) |
CP_LOAD_STATE6_0_STATE_BLOCK(tu6_stage2shadersb(stage)) |
CP_LOAD_STATE6_0_NUM_UNIT(shader_preload_size));
tu_cs_emit_qw(cs, binary_iova);
/* emit immediates */
const struct ir3_const_state *const_state = ir3_const_state(xs);
uint32_t base = const_state->offsets.immediate;
unsigned immediate_size = tu_xs_get_immediates_packet_size_dwords(xs);
if (immediate_size > 0) {
tu_cs_emit_pkt7(cs, tu6_stage2opcode(stage), 3 + immediate_size);
tu_cs_emit(cs, CP_LOAD_STATE6_0_DST_OFF(base) |
CP_LOAD_STATE6_0_STATE_TYPE(ST6_CONSTANTS) |
CP_LOAD_STATE6_0_STATE_SRC(SS6_DIRECT) |
CP_LOAD_STATE6_0_STATE_BLOCK(tu6_stage2shadersb(stage)) |
CP_LOAD_STATE6_0_NUM_UNIT(immediate_size / 4));
tu_cs_emit(cs, CP_LOAD_STATE6_1_EXT_SRC_ADDR(0));
tu_cs_emit(cs, CP_LOAD_STATE6_2_EXT_SRC_ADDR_HI(0));
tu_cs_emit_array(cs, const_state->immediates, immediate_size);
}
if (const_state->constant_data_ubo != -1) {
uint64_t iova = binary_iova + xs->info.constant_data_offset;
/* Upload UBO state for the constant data. */
tu_cs_emit_pkt7(cs, tu6_stage2opcode(stage), 5);
tu_cs_emit(cs,
CP_LOAD_STATE6_0_DST_OFF(const_state->constant_data_ubo) |
CP_LOAD_STATE6_0_STATE_TYPE(ST6_UBO)|
CP_LOAD_STATE6_0_STATE_SRC(SS6_DIRECT) |
CP_LOAD_STATE6_0_STATE_BLOCK(tu6_stage2shadersb(stage)) |
CP_LOAD_STATE6_0_NUM_UNIT(1));
tu_cs_emit(cs, CP_LOAD_STATE6_1_EXT_SRC_ADDR(0));
tu_cs_emit(cs, CP_LOAD_STATE6_2_EXT_SRC_ADDR_HI(0));
int size_vec4s = DIV_ROUND_UP(xs->constant_data_size, 16);
tu_cs_emit_qw(cs,
iova |
(uint64_t)A6XX_UBO_1_SIZE(size_vec4s) << 32);
/* Upload the constant data to the const file if needed. */
const struct ir3_ubo_analysis_state *ubo_state = &const_state->ubo_state;
for (int i = 0; i < ubo_state->num_enabled; i++) {
if (ubo_state->range[i].ubo.block != const_state->constant_data_ubo ||
ubo_state->range[i].ubo.bindless) {
continue;
}
uint32_t start = ubo_state->range[i].start;
uint32_t end = ubo_state->range[i].end;
uint32_t size = MIN2(end - start,
(16 * xs->constlen) - ubo_state->range[i].offset);
tu_cs_emit_pkt7(cs, tu6_stage2opcode(stage), 3);
tu_cs_emit(cs,
CP_LOAD_STATE6_0_DST_OFF(ubo_state->range[i].offset / 16) |
CP_LOAD_STATE6_0_STATE_TYPE(ST6_CONSTANTS) |
CP_LOAD_STATE6_0_STATE_SRC(SS6_INDIRECT) |
CP_LOAD_STATE6_0_STATE_BLOCK(tu6_stage2shadersb(stage)) |
CP_LOAD_STATE6_0_NUM_UNIT(size / 16));
tu_cs_emit_qw(cs, iova + start);
}
}
/* emit FS driver param */
if (stage == MESA_SHADER_FRAGMENT && const_state->num_driver_params > 0) {
uint32_t base = const_state->offsets.driver_param;
int32_t size = DIV_ROUND_UP(const_state->num_driver_params, 4);
size = MAX2(MIN2(size + base, xs->constlen) - base, 0);
if (size > 0) {
tu_cs_emit_pkt7(cs, tu6_stage2opcode(stage), 3 + size * 4);
tu_cs_emit(cs, CP_LOAD_STATE6_0_DST_OFF(base) |
CP_LOAD_STATE6_0_STATE_TYPE(ST6_CONSTANTS) |
CP_LOAD_STATE6_0_STATE_SRC(SS6_DIRECT) |
CP_LOAD_STATE6_0_STATE_BLOCK(tu6_stage2shadersb(stage)) |
CP_LOAD_STATE6_0_NUM_UNIT(size));
tu_cs_emit(cs, CP_LOAD_STATE6_1_EXT_SRC_ADDR(0));
tu_cs_emit(cs, CP_LOAD_STATE6_2_EXT_SRC_ADDR_HI(0));
assert(size == 1);
tu_cs_emit(cs, xs->info.double_threadsize ? 128 : 64);
tu_cs_emit(cs, 0);
tu_cs_emit(cs, 0);
tu_cs_emit(cs, 0);
}
}
}
static void
tu6_emit_shared_consts_enable(struct tu_cs *cs, bool enable)
{
/* Enable/disable shared constants */
tu_cs_emit_regs(cs, A6XX_HLSQ_SHARED_CONSTS(.enable = enable));
tu_cs_emit_regs(cs, A6XX_SP_MODE_CONTROL(.constant_demotion_enable = true,
.isammode = ISAMMODE_GL,
.shared_consts_enable = enable));
}
static void
tu6_emit_cs_config(struct tu_cs *cs,
const struct ir3_shader_variant *v,
const struct tu_pvtmem_config *pvtmem,
uint64_t binary_iova)
{
bool shared_consts_enable = ir3_const_state(v)->shared_consts_enable;
tu6_emit_shared_consts_enable(cs, shared_consts_enable);
tu_cs_emit_regs(cs, A6XX_HLSQ_INVALIDATE_CMD(
.cs_state = true,
.cs_ibo = true,
.cs_shared_const = shared_consts_enable));
tu6_emit_xs_config(cs, MESA_SHADER_COMPUTE, v);
tu6_emit_xs(cs, MESA_SHADER_COMPUTE, v, pvtmem, binary_iova);
uint32_t shared_size = MAX2(((int)v->shared_size - 1) / 1024, 1);
tu_cs_emit_pkt4(cs, REG_A6XX_SP_CS_UNKNOWN_A9B1, 1);
tu_cs_emit(cs, A6XX_SP_CS_UNKNOWN_A9B1_SHARED_SIZE(shared_size) |
A6XX_SP_CS_UNKNOWN_A9B1_UNK6);
if (cs->device->physical_device->info->a6xx.has_lpac) {
tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_CS_UNKNOWN_B9D0, 1);
tu_cs_emit(cs, A6XX_HLSQ_CS_UNKNOWN_B9D0_SHARED_SIZE(shared_size) |
A6XX_HLSQ_CS_UNKNOWN_B9D0_UNK6);
}
uint32_t local_invocation_id =
ir3_find_sysval_regid(v, SYSTEM_VALUE_LOCAL_INVOCATION_ID);
uint32_t work_group_id =
ir3_find_sysval_regid(v, SYSTEM_VALUE_WORKGROUP_ID);
enum a6xx_threadsize thrsz = v->info.double_threadsize ? THREAD128 : THREAD64;
tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_CS_CNTL_0, 2);
tu_cs_emit(cs,
A6XX_HLSQ_CS_CNTL_0_WGIDCONSTID(work_group_id) |
A6XX_HLSQ_CS_CNTL_0_WGSIZECONSTID(regid(63, 0)) |
A6XX_HLSQ_CS_CNTL_0_WGOFFSETCONSTID(regid(63, 0)) |
A6XX_HLSQ_CS_CNTL_0_LOCALIDREGID(local_invocation_id));
tu_cs_emit(cs, A6XX_HLSQ_CS_CNTL_1_LINEARLOCALIDREGID(regid(63, 0)) |
A6XX_HLSQ_CS_CNTL_1_THREADSIZE(thrsz));
if (cs->device->physical_device->info->a6xx.has_lpac) {
tu_cs_emit_pkt4(cs, REG_A6XX_SP_CS_CNTL_0, 2);
tu_cs_emit(cs,
A6XX_SP_CS_CNTL_0_WGIDCONSTID(work_group_id) |
A6XX_SP_CS_CNTL_0_WGSIZECONSTID(regid(63, 0)) |
A6XX_SP_CS_CNTL_0_WGOFFSETCONSTID(regid(63, 0)) |
A6XX_SP_CS_CNTL_0_LOCALIDREGID(local_invocation_id));
tu_cs_emit(cs, A6XX_SP_CS_CNTL_1_LINEARLOCALIDREGID(regid(63, 0)) |
A6XX_SP_CS_CNTL_1_THREADSIZE(thrsz));
}
}
static void
tu6_emit_vs_system_values(struct tu_cs *cs,
const struct ir3_shader_variant *vs,
const struct ir3_shader_variant *hs,
const struct ir3_shader_variant *ds,
const struct ir3_shader_variant *gs,
bool primid_passthru)
{
const uint32_t vertexid_regid =
ir3_find_sysval_regid(vs, SYSTEM_VALUE_VERTEX_ID);
const uint32_t instanceid_regid =
ir3_find_sysval_regid(vs, SYSTEM_VALUE_INSTANCE_ID);
const uint32_t tess_coord_x_regid = hs ?
ir3_find_sysval_regid(ds, SYSTEM_VALUE_TESS_COORD) :
regid(63, 0);
const uint32_t tess_coord_y_regid = VALIDREG(tess_coord_x_regid) ?
tess_coord_x_regid + 1 :
regid(63, 0);
const uint32_t hs_rel_patch_regid = hs ?
ir3_find_sysval_regid(hs, SYSTEM_VALUE_REL_PATCH_ID_IR3) :
regid(63, 0);
const uint32_t ds_rel_patch_regid = hs ?
ir3_find_sysval_regid(ds, SYSTEM_VALUE_REL_PATCH_ID_IR3) :
regid(63, 0);
const uint32_t hs_invocation_regid = hs ?
ir3_find_sysval_regid(hs, SYSTEM_VALUE_TCS_HEADER_IR3) :
regid(63, 0);
const uint32_t gs_primitiveid_regid = gs ?
ir3_find_sysval_regid(gs, SYSTEM_VALUE_PRIMITIVE_ID) :
regid(63, 0);
const uint32_t vs_primitiveid_regid = hs ?
ir3_find_sysval_regid(hs, SYSTEM_VALUE_PRIMITIVE_ID) :
gs_primitiveid_regid;
const uint32_t ds_primitiveid_regid = ds ?
ir3_find_sysval_regid(ds, SYSTEM_VALUE_PRIMITIVE_ID) :
regid(63, 0);
const uint32_t gsheader_regid = gs ?
ir3_find_sysval_regid(gs, SYSTEM_VALUE_GS_HEADER_IR3) :
regid(63, 0);
/* Note: we currently don't support multiview with tess or GS. If we did,
* and the HW actually works, then we'd have to somehow share this across
* stages. Note that the blob doesn't support this either.
*/
const uint32_t viewid_regid =
ir3_find_sysval_regid(vs, SYSTEM_VALUE_VIEW_INDEX);
tu_cs_emit_pkt4(cs, REG_A6XX_VFD_CONTROL_1, 6);
tu_cs_emit(cs, A6XX_VFD_CONTROL_1_REGID4VTX(vertexid_regid) |
A6XX_VFD_CONTROL_1_REGID4INST(instanceid_regid) |
A6XX_VFD_CONTROL_1_REGID4PRIMID(vs_primitiveid_regid) |
A6XX_VFD_CONTROL_1_REGID4VIEWID(viewid_regid));
tu_cs_emit(cs, A6XX_VFD_CONTROL_2_REGID_HSRELPATCHID(hs_rel_patch_regid) |
A6XX_VFD_CONTROL_2_REGID_INVOCATIONID(hs_invocation_regid));
tu_cs_emit(cs, A6XX_VFD_CONTROL_3_REGID_DSRELPATCHID(ds_rel_patch_regid) |
A6XX_VFD_CONTROL_3_REGID_TESSX(tess_coord_x_regid) |
A6XX_VFD_CONTROL_3_REGID_TESSY(tess_coord_y_regid) |
A6XX_VFD_CONTROL_3_REGID_DSPRIMID(ds_primitiveid_regid));
tu_cs_emit(cs, 0x000000fc); /* VFD_CONTROL_4 */
tu_cs_emit(cs, A6XX_VFD_CONTROL_5_REGID_GSHEADER(gsheader_regid) |
0xfc00); /* VFD_CONTROL_5 */
tu_cs_emit(cs, COND(primid_passthru, A6XX_VFD_CONTROL_6_PRIMID_PASSTHRU)); /* VFD_CONTROL_6 */
}
static void
tu6_setup_streamout(struct tu_cs *cs,
const struct ir3_shader_variant *v,
struct ir3_shader_linkage *l)
{
const struct ir3_stream_output_info *info = &v->stream_output;
/* Note: 64 here comes from the HW layout of the program RAM. The program
* for stream N is at DWORD 64 * N.
*/
#define A6XX_SO_PROG_DWORDS 64
uint32_t prog[A6XX_SO_PROG_DWORDS * IR3_MAX_SO_STREAMS] = {};
BITSET_DECLARE(valid_dwords, A6XX_SO_PROG_DWORDS * IR3_MAX_SO_STREAMS) = {0};
/* TODO: streamout state should be in a non-GMEM draw state */
/* no streamout: */
if (info->num_outputs == 0) {
tu_cs_emit_pkt7(cs, CP_CONTEXT_REG_BUNCH, 4);
tu_cs_emit(cs, REG_A6XX_VPC_SO_CNTL);
tu_cs_emit(cs, 0);
tu_cs_emit(cs, REG_A6XX_VPC_SO_STREAM_CNTL);
tu_cs_emit(cs, 0);
return;
}
for (unsigned i = 0; i < info->num_outputs; i++) {
const struct ir3_stream_output *out = &info->output[i];
unsigned k = out->register_index;
unsigned idx;
/* Skip it, if it's an output that was never assigned a register. */
if (k >= v->outputs_count || v->outputs[k].regid == INVALID_REG)
continue;
/* linkage map sorted by order frag shader wants things, so
* a bit less ideal here..
*/
for (idx = 0; idx < l->cnt; idx++)
if (l->var[idx].slot == v->outputs[k].slot)
break;
assert(idx < l->cnt);
for (unsigned j = 0; j < out->num_components; j++) {
unsigned c = j + out->start_component;
unsigned loc = l->var[idx].loc + c;
unsigned off = j + out->dst_offset; /* in dwords */
assert(loc < A6XX_SO_PROG_DWORDS * 2);
unsigned dword = out->stream * A6XX_SO_PROG_DWORDS + loc/2;
if (loc & 1) {
prog[dword] |= A6XX_VPC_SO_PROG_B_EN |
A6XX_VPC_SO_PROG_B_BUF(out->output_buffer) |
A6XX_VPC_SO_PROG_B_OFF(off * 4);
} else {
prog[dword] |= A6XX_VPC_SO_PROG_A_EN |
A6XX_VPC_SO_PROG_A_BUF(out->output_buffer) |
A6XX_VPC_SO_PROG_A_OFF(off * 4);
}
BITSET_SET(valid_dwords, dword);
}
}
unsigned prog_count = 0;
unsigned start, end;
BITSET_FOREACH_RANGE(start, end, valid_dwords,
A6XX_SO_PROG_DWORDS * IR3_MAX_SO_STREAMS) {
prog_count += end - start + 1;
}
tu_cs_emit_pkt7(cs, CP_CONTEXT_REG_BUNCH, 10 + 2 * prog_count);
tu_cs_emit(cs, REG_A6XX_VPC_SO_STREAM_CNTL);
tu_cs_emit(cs, A6XX_VPC_SO_STREAM_CNTL_STREAM_ENABLE(info->streams_written) |
COND(info->stride[0] > 0,
A6XX_VPC_SO_STREAM_CNTL_BUF0_STREAM(1 + info->buffer_to_stream[0])) |
COND(info->stride[1] > 0,
A6XX_VPC_SO_STREAM_CNTL_BUF1_STREAM(1 + info->buffer_to_stream[1])) |
COND(info->stride[2] > 0,
A6XX_VPC_SO_STREAM_CNTL_BUF2_STREAM(1 + info->buffer_to_stream[2])) |
COND(info->stride[3] > 0,
A6XX_VPC_SO_STREAM_CNTL_BUF3_STREAM(1 + info->buffer_to_stream[3])));
for (uint32_t i = 0; i < 4; i++) {
tu_cs_emit(cs, REG_A6XX_VPC_SO_BUFFER_STRIDE(i));
tu_cs_emit(cs, info->stride[i]);
}
bool first = true;
BITSET_FOREACH_RANGE(start, end, valid_dwords,
A6XX_SO_PROG_DWORDS * IR3_MAX_SO_STREAMS) {
tu_cs_emit(cs, REG_A6XX_VPC_SO_CNTL);
tu_cs_emit(cs, COND(first, A6XX_VPC_SO_CNTL_RESET) |
A6XX_VPC_SO_CNTL_ADDR(start));
for (unsigned i = start; i < end; i++) {
tu_cs_emit(cs, REG_A6XX_VPC_SO_PROG);
tu_cs_emit(cs, prog[i]);
}
first = false;
}
}
static void
tu6_emit_const(struct tu_cs *cs, uint32_t opcode, uint32_t base,
enum a6xx_state_block block, uint32_t offset,
uint32_t size, const uint32_t *dwords) {
assert(size % 4 == 0);
tu_cs_emit_pkt7(cs, opcode, 3 + size);
tu_cs_emit(cs, CP_LOAD_STATE6_0_DST_OFF(base) |
CP_LOAD_STATE6_0_STATE_TYPE(ST6_CONSTANTS) |
CP_LOAD_STATE6_0_STATE_SRC(SS6_DIRECT) |
CP_LOAD_STATE6_0_STATE_BLOCK(block) |
CP_LOAD_STATE6_0_NUM_UNIT(size / 4));
tu_cs_emit(cs, CP_LOAD_STATE6_1_EXT_SRC_ADDR(0));
tu_cs_emit(cs, CP_LOAD_STATE6_2_EXT_SRC_ADDR_HI(0));
dwords = (uint32_t *)&((uint8_t *)dwords)[offset];
tu_cs_emit_array(cs, dwords, size);
}
static void
tu6_emit_link_map(struct tu_cs *cs,
const struct ir3_shader_variant *producer,
const struct ir3_shader_variant *consumer,
enum a6xx_state_block sb)
{
const struct ir3_const_state *const_state = ir3_const_state(consumer);
uint32_t base = const_state->offsets.primitive_map;
int size = DIV_ROUND_UP(consumer->input_size, 4);
size = (MIN2(size + base, consumer->constlen) - base) * 4;
if (size <= 0)
return;
tu6_emit_const(cs, CP_LOAD_STATE6_GEOM, base, sb, 0, size,
producer->output_loc);
}
static uint16_t
primitive_to_tess(enum shader_prim primitive) {
switch (primitive) {
case SHADER_PRIM_POINTS:
return TESS_POINTS;
case SHADER_PRIM_LINE_STRIP:
return TESS_LINES;
case SHADER_PRIM_TRIANGLE_STRIP:
return TESS_CW_TRIS;
default:
unreachable("");
}
}
void
tu6_emit_vpc(struct tu_cs *cs,
const struct ir3_shader_variant *vs,
const struct ir3_shader_variant *hs,
const struct ir3_shader_variant *ds,
const struct ir3_shader_variant *gs,
const struct ir3_shader_variant *fs,
uint32_t patch_control_points)
{
/* note: doesn't compile as static because of the array regs.. */
const struct reg_config {
uint16_t reg_sp_xs_out_reg;
uint16_t reg_sp_xs_vpc_dst_reg;
uint16_t reg_vpc_xs_pack;
uint16_t reg_vpc_xs_clip_cntl;
uint16_t reg_gras_xs_cl_cntl;
uint16_t reg_pc_xs_out_cntl;
uint16_t reg_sp_xs_primitive_cntl;
uint16_t reg_vpc_xs_layer_cntl;
uint16_t reg_gras_xs_layer_cntl;
} reg_config[] = {
[MESA_SHADER_VERTEX] = {
REG_A6XX_SP_VS_OUT_REG(0),
REG_A6XX_SP_VS_VPC_DST_REG(0),
REG_A6XX_VPC_VS_PACK,
REG_A6XX_VPC_VS_CLIP_CNTL,
REG_A6XX_GRAS_VS_CL_CNTL,
REG_A6XX_PC_VS_OUT_CNTL,
REG_A6XX_SP_VS_PRIMITIVE_CNTL,
REG_A6XX_VPC_VS_LAYER_CNTL,
REG_A6XX_GRAS_VS_LAYER_CNTL
},
[MESA_SHADER_TESS_CTRL] = {
0,
0,
0,
0,
0,
REG_A6XX_PC_HS_OUT_CNTL,
0,
0,
0
},
[MESA_SHADER_TESS_EVAL] = {
REG_A6XX_SP_DS_OUT_REG(0),
REG_A6XX_SP_DS_VPC_DST_REG(0),
REG_A6XX_VPC_DS_PACK,
REG_A6XX_VPC_DS_CLIP_CNTL,
REG_A6XX_GRAS_DS_CL_CNTL,
REG_A6XX_PC_DS_OUT_CNTL,
REG_A6XX_SP_DS_PRIMITIVE_CNTL,
REG_A6XX_VPC_DS_LAYER_CNTL,
REG_A6XX_GRAS_DS_LAYER_CNTL
},
[MESA_SHADER_GEOMETRY] = {
REG_A6XX_SP_GS_OUT_REG(0),
REG_A6XX_SP_GS_VPC_DST_REG(0),
REG_A6XX_VPC_GS_PACK,
REG_A6XX_VPC_GS_CLIP_CNTL,
REG_A6XX_GRAS_GS_CL_CNTL,
REG_A6XX_PC_GS_OUT_CNTL,
REG_A6XX_SP_GS_PRIMITIVE_CNTL,
REG_A6XX_VPC_GS_LAYER_CNTL,
REG_A6XX_GRAS_GS_LAYER_CNTL
},
};
const struct ir3_shader_variant *last_shader;
if (gs) {
last_shader = gs;
} else if (hs) {
last_shader = ds;
} else {
last_shader = vs;
}
const struct reg_config *cfg = &reg_config[last_shader->type];
struct ir3_shader_linkage linkage = {
.primid_loc = 0xff,
.clip0_loc = 0xff,
.clip1_loc = 0xff,
};
if (fs)
ir3_link_shaders(&linkage, last_shader, fs, true);
if (last_shader->stream_output.num_outputs)
ir3_link_stream_out(&linkage, last_shader);
/* We do this after linking shaders in order to know whether PrimID
* passthrough needs to be enabled.
*/
bool primid_passthru = linkage.primid_loc != 0xff;
tu6_emit_vs_system_values(cs, vs, hs, ds, gs, primid_passthru);
tu_cs_emit_pkt4(cs, REG_A6XX_VPC_VAR_DISABLE(0), 4);
tu_cs_emit(cs, ~linkage.varmask[0]);
tu_cs_emit(cs, ~linkage.varmask[1]);
tu_cs_emit(cs, ~linkage.varmask[2]);
tu_cs_emit(cs, ~linkage.varmask[3]);
/* a6xx finds position/pointsize at the end */
const uint32_t pointsize_regid =
ir3_find_output_regid(last_shader, VARYING_SLOT_PSIZ);
const uint32_t layer_regid =
ir3_find_output_regid(last_shader, VARYING_SLOT_LAYER);
const uint32_t view_regid =
ir3_find_output_regid(last_shader, VARYING_SLOT_VIEWPORT);
const uint32_t clip0_regid =
ir3_find_output_regid(last_shader, VARYING_SLOT_CLIP_DIST0);
const uint32_t clip1_regid =
ir3_find_output_regid(last_shader, VARYING_SLOT_CLIP_DIST1);
uint32_t flags_regid = gs ?
ir3_find_output_regid(gs, VARYING_SLOT_GS_VERTEX_FLAGS_IR3) : 0;
uint32_t pointsize_loc = 0xff, position_loc = 0xff, layer_loc = 0xff, view_loc = 0xff;
if (layer_regid != regid(63, 0)) {
layer_loc = linkage.max_loc;
ir3_link_add(&linkage, VARYING_SLOT_LAYER, layer_regid, 0x1, linkage.max_loc);
}
if (view_regid != regid(63, 0)) {
view_loc = linkage.max_loc;
ir3_link_add(&linkage, VARYING_SLOT_VIEWPORT, view_regid, 0x1, linkage.max_loc);
}
unsigned extra_pos = 0;
for (unsigned i = 0; i < last_shader->outputs_count; i++) {
if (last_shader->outputs[i].slot != VARYING_SLOT_POS)
continue;
if (position_loc == 0xff)
position_loc = linkage.max_loc;
ir3_link_add(&linkage, last_shader->outputs[i].slot,
last_shader->outputs[i].regid,
0xf, position_loc + 4 * last_shader->outputs[i].view);
extra_pos = MAX2(extra_pos, last_shader->outputs[i].view);
}
if (pointsize_regid != regid(63, 0)) {
pointsize_loc = linkage.max_loc;
ir3_link_add(&linkage, VARYING_SLOT_PSIZ, pointsize_regid, 0x1, linkage.max_loc);
}
uint8_t clip_cull_mask = last_shader->clip_mask | last_shader->cull_mask;
/* Handle the case where clip/cull distances aren't read by the FS */
uint32_t clip0_loc = linkage.clip0_loc, clip1_loc = linkage.clip1_loc;
if (clip0_loc == 0xff && clip0_regid != regid(63, 0)) {
clip0_loc = linkage.max_loc;
ir3_link_add(&linkage, VARYING_SLOT_CLIP_DIST0, clip0_regid,
clip_cull_mask & 0xf, linkage.max_loc);
}
if (clip1_loc == 0xff && clip1_regid != regid(63, 0)) {
clip1_loc = linkage.max_loc;
ir3_link_add(&linkage, VARYING_SLOT_CLIP_DIST1, clip1_regid,
clip_cull_mask >> 4, linkage.max_loc);
}
tu6_setup_streamout(cs, last_shader, &linkage);
/* The GPU hangs on some models when there are no outputs (xs_pack::CNT),
* at least when a DS is the last stage, so add a dummy output to keep it
* happy if there aren't any. We do this late in order to avoid emitting
* any unused code and make sure that optimizations don't remove it.
*/
if (linkage.cnt == 0)
ir3_link_add(&linkage, 0, 0, 0x1, linkage.max_loc);
/* map outputs of the last shader to VPC */
assert(linkage.cnt <= 32);
const uint32_t sp_out_count = DIV_ROUND_UP(linkage.cnt, 2);
const uint32_t sp_vpc_dst_count = DIV_ROUND_UP(linkage.cnt, 4);
uint32_t sp_out[16] = {0};
uint32_t sp_vpc_dst[8] = {0};
for (uint32_t i = 0; i < linkage.cnt; i++) {
((uint16_t *) sp_out)[i] =
A6XX_SP_VS_OUT_REG_A_REGID(linkage.var[i].regid) |
A6XX_SP_VS_OUT_REG_A_COMPMASK(linkage.var[i].compmask);
((uint8_t *) sp_vpc_dst)[i] =
A6XX_SP_VS_VPC_DST_REG_OUTLOC0(linkage.var[i].loc);
}
tu_cs_emit_pkt4(cs, cfg->reg_sp_xs_out_reg, sp_out_count);
tu_cs_emit_array(cs, sp_out, sp_out_count);
tu_cs_emit_pkt4(cs, cfg->reg_sp_xs_vpc_dst_reg, sp_vpc_dst_count);
tu_cs_emit_array(cs, sp_vpc_dst, sp_vpc_dst_count);
tu_cs_emit_pkt4(cs, cfg->reg_vpc_xs_pack, 1);
tu_cs_emit(cs, A6XX_VPC_VS_PACK_POSITIONLOC(position_loc) |
A6XX_VPC_VS_PACK_PSIZELOC(pointsize_loc) |
A6XX_VPC_VS_PACK_STRIDE_IN_VPC(linkage.max_loc) |
A6XX_VPC_VS_PACK_EXTRAPOS(extra_pos));
tu_cs_emit_pkt4(cs, cfg->reg_vpc_xs_clip_cntl, 1);
tu_cs_emit(cs, A6XX_VPC_VS_CLIP_CNTL_CLIP_MASK(clip_cull_mask) |
A6XX_VPC_VS_CLIP_CNTL_CLIP_DIST_03_LOC(clip0_loc) |
A6XX_VPC_VS_CLIP_CNTL_CLIP_DIST_47_LOC(clip1_loc));
tu_cs_emit_pkt4(cs, cfg->reg_gras_xs_cl_cntl, 1);
tu_cs_emit(cs, A6XX_GRAS_VS_CL_CNTL_CLIP_MASK(last_shader->clip_mask) |
A6XX_GRAS_VS_CL_CNTL_CULL_MASK(last_shader->cull_mask));
const struct ir3_shader_variant *geom_shaders[] = { vs, hs, ds, gs };
for (unsigned i = 0; i < ARRAY_SIZE(geom_shaders); i++) {
const struct ir3_shader_variant *shader = geom_shaders[i];
if (!shader)
continue;
bool primid = shader->type != MESA_SHADER_VERTEX &&
VALIDREG(ir3_find_sysval_regid(shader, SYSTEM_VALUE_PRIMITIVE_ID));
tu_cs_emit_pkt4(cs, reg_config[shader->type].reg_pc_xs_out_cntl, 1);
if (shader == last_shader) {
tu_cs_emit(cs, A6XX_PC_VS_OUT_CNTL_STRIDE_IN_VPC(linkage.max_loc) |
CONDREG(pointsize_regid, A6XX_PC_VS_OUT_CNTL_PSIZE) |
CONDREG(layer_regid, A6XX_PC_VS_OUT_CNTL_LAYER) |
CONDREG(view_regid, A6XX_PC_VS_OUT_CNTL_VIEW) |
COND(primid, A6XX_PC_VS_OUT_CNTL_PRIMITIVE_ID) |
A6XX_PC_VS_OUT_CNTL_CLIP_MASK(clip_cull_mask));
} else {
tu_cs_emit(cs, COND(primid, A6XX_PC_VS_OUT_CNTL_PRIMITIVE_ID));
}
}
/* if vertex_flags somehow gets optimized out, your gonna have a bad time: */
if (gs)
assert(flags_regid != INVALID_REG);
tu_cs_emit_pkt4(cs, cfg->reg_sp_xs_primitive_cntl, 1);
tu_cs_emit(cs, A6XX_SP_VS_PRIMITIVE_CNTL_OUT(linkage.cnt) |
A6XX_SP_GS_PRIMITIVE_CNTL_FLAGS_REGID(flags_regid));
tu_cs_emit_pkt4(cs, cfg->reg_vpc_xs_layer_cntl, 1);
tu_cs_emit(cs, A6XX_VPC_VS_LAYER_CNTL_LAYERLOC(layer_loc) |
A6XX_VPC_VS_LAYER_CNTL_VIEWLOC(view_loc));
tu_cs_emit_pkt4(cs, cfg->reg_gras_xs_layer_cntl, 1);
tu_cs_emit(cs, CONDREG(layer_regid, A6XX_GRAS_GS_LAYER_CNTL_WRITES_LAYER) |
CONDREG(view_regid, A6XX_GRAS_GS_LAYER_CNTL_WRITES_VIEW));
tu_cs_emit_regs(cs, A6XX_PC_PRIMID_PASSTHRU(primid_passthru));
tu_cs_emit_pkt4(cs, REG_A6XX_VPC_CNTL_0, 1);
tu_cs_emit(cs, A6XX_VPC_CNTL_0_NUMNONPOSVAR(fs ? fs->total_in : 0) |
COND(fs && fs->total_in, A6XX_VPC_CNTL_0_VARYING) |
A6XX_VPC_CNTL_0_PRIMIDLOC(linkage.primid_loc) |
A6XX_VPC_CNTL_0_VIEWIDLOC(linkage.viewid_loc));
if (hs) {
tu_cs_emit_pkt4(cs, REG_A6XX_PC_TESS_NUM_VERTEX, 1);
tu_cs_emit(cs, hs->tess.tcs_vertices_out);
/* Total attribute slots in HS incoming patch. */
tu_cs_emit_pkt4(cs, REG_A6XX_PC_HS_INPUT_SIZE, 1);
tu_cs_emit(cs, patch_control_points * vs->output_size / 4);
const uint32_t wavesize = 64;
const uint32_t max_wave_input_size = 64;
/* note: if HS is really just the VS extended, then this
* should be by MAX2(patch_control_points, hs->tess.tcs_vertices_out)
* however that doesn't match the blob, and fails some dEQP tests.
*/
uint32_t prims_per_wave = wavesize / hs->tess.tcs_vertices_out;
uint32_t max_prims_per_wave =
max_wave_input_size * wavesize / (vs->output_size * patch_control_points);
prims_per_wave = MIN2(prims_per_wave, max_prims_per_wave);
uint32_t total_size = vs->output_size * patch_control_points * prims_per_wave;
uint32_t wave_input_size = DIV_ROUND_UP(total_size, wavesize);
tu_cs_emit_pkt4(cs, REG_A6XX_SP_HS_WAVE_INPUT_SIZE, 1);
tu_cs_emit(cs, wave_input_size);
/* In SPIR-V generated from GLSL, the tessellation primitive params are
* are specified in the tess eval shader, but in SPIR-V generated from
* HLSL, they are specified in the tess control shader. */
const struct ir3_shader_variant *tess =
ds->tess.spacing == TESS_SPACING_UNSPECIFIED ? hs : ds;
tu_cs_emit_pkt4(cs, REG_A6XX_PC_TESS_CNTL, 1);
uint32_t output;
if (tess->tess.point_mode)
output = TESS_POINTS;
else if (tess->tess.primitive_mode == TESS_PRIMITIVE_ISOLINES)
output = TESS_LINES;
else if (tess->tess.ccw)
output = TESS_CCW_TRIS;
else
output = TESS_CW_TRIS;
enum a6xx_tess_spacing spacing;
switch (tess->tess.spacing) {
case TESS_SPACING_EQUAL:
spacing = TESS_EQUAL;
break;
case TESS_SPACING_FRACTIONAL_ODD:
spacing = TESS_FRACTIONAL_ODD;
break;
case TESS_SPACING_FRACTIONAL_EVEN:
spacing = TESS_FRACTIONAL_EVEN;
break;
case TESS_SPACING_UNSPECIFIED:
default:
unreachable("invalid tess spacing");
}
tu_cs_emit(cs, A6XX_PC_TESS_CNTL_SPACING(spacing) |
A6XX_PC_TESS_CNTL_OUTPUT(output));
tu6_emit_link_map(cs, vs, hs, SB6_HS_SHADER);
tu6_emit_link_map(cs, hs, ds, SB6_DS_SHADER);
}
if (gs) {
uint32_t vertices_out, invocations, output, vec4_size;
uint32_t prev_stage_output_size = ds ? ds->output_size : vs->output_size;
if (hs) {
tu6_emit_link_map(cs, ds, gs, SB6_GS_SHADER);
} else {
tu6_emit_link_map(cs, vs, gs, SB6_GS_SHADER);
}
vertices_out = gs->gs.vertices_out - 1;
output = primitive_to_tess(gs->gs.output_primitive);
invocations = gs->gs.invocations - 1;
/* Size of per-primitive alloction in ldlw memory in vec4s. */
vec4_size = gs->gs.vertices_in *
DIV_ROUND_UP(prev_stage_output_size, 4);
tu_cs_emit_pkt4(cs, REG_A6XX_PC_PRIMITIVE_CNTL_5, 1);
tu_cs_emit(cs,
A6XX_PC_PRIMITIVE_CNTL_5_GS_VERTICES_OUT(vertices_out) |
A6XX_PC_PRIMITIVE_CNTL_5_GS_OUTPUT(output) |
A6XX_PC_PRIMITIVE_CNTL_5_GS_INVOCATIONS(invocations));
tu_cs_emit_pkt4(cs, REG_A6XX_VPC_GS_PARAM, 1);
tu_cs_emit(cs, 0xff);
tu_cs_emit_pkt4(cs, REG_A6XX_PC_PRIMITIVE_CNTL_6, 1);
tu_cs_emit(cs, A6XX_PC_PRIMITIVE_CNTL_6_STRIDE_IN_VPC(vec4_size));
uint32_t prim_size = prev_stage_output_size;
if (prim_size > 64)
prim_size = 64;
else if (prim_size == 64)
prim_size = 63;
tu_cs_emit_pkt4(cs, REG_A6XX_SP_GS_PRIM_SIZE, 1);
tu_cs_emit(cs, prim_size);
}
}
static int
tu6_vpc_varying_mode(const struct ir3_shader_variant *fs,
uint32_t index,
uint8_t *interp_mode,
uint8_t *ps_repl_mode)
{
enum
{
INTERP_SMOOTH = 0,
INTERP_FLAT = 1,
INTERP_ZERO = 2,
INTERP_ONE = 3,
};
enum
{
PS_REPL_NONE = 0,
PS_REPL_S = 1,
PS_REPL_T = 2,
PS_REPL_ONE_MINUS_T = 3,
};
const uint32_t compmask = fs->inputs[index].compmask;
/* NOTE: varyings are packed, so if compmask is 0xb then first, second, and
* fourth component occupy three consecutive varying slots
*/
int shift = 0;
*interp_mode = 0;
*ps_repl_mode = 0;
if (fs->inputs[index].slot == VARYING_SLOT_PNTC) {
if (compmask & 0x1) {
*ps_repl_mode |= PS_REPL_S << shift;
shift += 2;
}
if (compmask & 0x2) {
*ps_repl_mode |= PS_REPL_T << shift;
shift += 2;
}
if (compmask & 0x4) {
*interp_mode |= INTERP_ZERO << shift;
shift += 2;
}
if (compmask & 0x8) {
*interp_mode |= INTERP_ONE << 6;
shift += 2;
}
} else if (fs->inputs[index].flat) {
for (int i = 0; i < 4; i++) {
if (compmask & (1 << i)) {
*interp_mode |= INTERP_FLAT << shift;
shift += 2;
}
}
}
return shift;
}
static void
tu6_emit_vpc_varying_modes(struct tu_cs *cs,
const struct ir3_shader_variant *fs)
{
uint32_t interp_modes[8] = { 0 };
uint32_t ps_repl_modes[8] = { 0 };
if (fs) {
for (int i = -1;
(i = ir3_next_varying(fs, i)) < (int) fs->inputs_count;) {
/* get the mode for input i */
uint8_t interp_mode;
uint8_t ps_repl_mode;
const int bits =
tu6_vpc_varying_mode(fs, i, &interp_mode, &ps_repl_mode);
/* OR the mode into the array */
const uint32_t inloc = fs->inputs[i].inloc * 2;
uint32_t n = inloc / 32;
uint32_t shift = inloc % 32;
interp_modes[n] |= interp_mode << shift;
ps_repl_modes[n] |= ps_repl_mode << shift;
if (shift + bits > 32) {
n++;
shift = 32 - shift;
interp_modes[n] |= interp_mode >> shift;
ps_repl_modes[n] |= ps_repl_mode >> shift;
}
}
}
tu_cs_emit_pkt4(cs, REG_A6XX_VPC_VARYING_INTERP_MODE(0), 8);
tu_cs_emit_array(cs, interp_modes, 8);
tu_cs_emit_pkt4(cs, REG_A6XX_VPC_VARYING_PS_REPL_MODE(0), 8);
tu_cs_emit_array(cs, ps_repl_modes, 8);
}
void
tu6_emit_fs_inputs(struct tu_cs *cs, const struct ir3_shader_variant *fs)
{
uint32_t face_regid, coord_regid, zwcoord_regid, samp_id_regid;
uint32_t ij_regid[IJ_COUNT];
uint32_t smask_in_regid;
bool sample_shading = fs->per_samp | fs->key.sample_shading;
bool enable_varyings = fs->total_in > 0;
samp_id_regid = ir3_find_sysval_regid(fs, SYSTEM_VALUE_SAMPLE_ID);
smask_in_regid = ir3_find_sysval_regid(fs, SYSTEM_VALUE_SAMPLE_MASK_IN);
face_regid = ir3_find_sysval_regid(fs, SYSTEM_VALUE_FRONT_FACE);
coord_regid = ir3_find_sysval_regid(fs, SYSTEM_VALUE_FRAG_COORD);
zwcoord_regid = VALIDREG(coord_regid) ? coord_regid + 2 : regid(63, 0);
for (unsigned i = 0; i < ARRAY_SIZE(ij_regid); i++)
ij_regid[i] = ir3_find_sysval_regid(fs, SYSTEM_VALUE_BARYCENTRIC_PERSP_PIXEL + i);
if (fs->num_sampler_prefetch > 0) {
assert(VALIDREG(ij_regid[IJ_PERSP_PIXEL]));
/* also, it seems like ij_pix is *required* to be r0.x */
assert(ij_regid[IJ_PERSP_PIXEL] == regid(0, 0));
}
tu_cs_emit_pkt4(cs, REG_A6XX_SP_FS_PREFETCH_CNTL, 1 + fs->num_sampler_prefetch);
tu_cs_emit(cs, A6XX_SP_FS_PREFETCH_CNTL_COUNT(fs->num_sampler_prefetch) |
A6XX_SP_FS_PREFETCH_CNTL_UNK4(regid(63, 0)) |
0x7000); // XXX);
for (int i = 0; i < fs->num_sampler_prefetch; i++) {
const struct ir3_sampler_prefetch *prefetch = &fs->sampler_prefetch[i];
tu_cs_emit(cs, A6XX_SP_FS_PREFETCH_CMD_SRC(prefetch->src) |
A6XX_SP_FS_PREFETCH_CMD_SAMP_ID(prefetch->samp_id) |
A6XX_SP_FS_PREFETCH_CMD_TEX_ID(prefetch->tex_id) |
A6XX_SP_FS_PREFETCH_CMD_DST(prefetch->dst) |
A6XX_SP_FS_PREFETCH_CMD_WRMASK(prefetch->wrmask) |
COND(prefetch->half_precision, A6XX_SP_FS_PREFETCH_CMD_HALF) |
A6XX_SP_FS_PREFETCH_CMD_CMD(prefetch->cmd));
}
if (fs->num_sampler_prefetch > 0) {
tu_cs_emit_pkt4(cs, REG_A6XX_SP_FS_BINDLESS_PREFETCH_CMD(0), fs->num_sampler_prefetch);
for (int i = 0; i < fs->num_sampler_prefetch; i++) {
const struct ir3_sampler_prefetch *prefetch = &fs->sampler_prefetch[i];
tu_cs_emit(cs,
A6XX_SP_FS_BINDLESS_PREFETCH_CMD_SAMP_ID(prefetch->samp_bindless_id) |
A6XX_SP_FS_BINDLESS_PREFETCH_CMD_TEX_ID(prefetch->tex_bindless_id));
}
}
tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_CONTROL_1_REG, 5);
tu_cs_emit(cs, 0x7);
tu_cs_emit(cs, A6XX_HLSQ_CONTROL_2_REG_FACEREGID(face_regid) |
A6XX_HLSQ_CONTROL_2_REG_SAMPLEID(samp_id_regid) |
A6XX_HLSQ_CONTROL_2_REG_SAMPLEMASK(smask_in_regid) |
A6XX_HLSQ_CONTROL_2_REG_CENTERRHW(ij_regid[IJ_PERSP_CENTER_RHW]));
tu_cs_emit(cs, A6XX_HLSQ_CONTROL_3_REG_IJ_PERSP_PIXEL(ij_regid[IJ_PERSP_PIXEL]) |
A6XX_HLSQ_CONTROL_3_REG_IJ_LINEAR_PIXEL(ij_regid[IJ_LINEAR_PIXEL]) |
A6XX_HLSQ_CONTROL_3_REG_IJ_PERSP_CENTROID(ij_regid[IJ_PERSP_CENTROID]) |
A6XX_HLSQ_CONTROL_3_REG_IJ_LINEAR_CENTROID(ij_regid[IJ_LINEAR_CENTROID]));
tu_cs_emit(cs, A6XX_HLSQ_CONTROL_4_REG_XYCOORDREGID(coord_regid) |
A6XX_HLSQ_CONTROL_4_REG_ZWCOORDREGID(zwcoord_regid) |
A6XX_HLSQ_CONTROL_4_REG_IJ_PERSP_SAMPLE(ij_regid[IJ_PERSP_SAMPLE]) |
A6XX_HLSQ_CONTROL_4_REG_IJ_LINEAR_SAMPLE(ij_regid[IJ_LINEAR_SAMPLE]));
tu_cs_emit(cs, 0xfcfc);
enum a6xx_threadsize thrsz = fs->info.double_threadsize ? THREAD128 : THREAD64;
tu_cs_emit_pkt4(cs, REG_A6XX_HLSQ_FS_CNTL_0, 1);
tu_cs_emit(cs, A6XX_HLSQ_FS_CNTL_0_THREADSIZE(thrsz) |
COND(enable_varyings, A6XX_HLSQ_FS_CNTL_0_VARYINGS));
bool need_size = fs->frag_face || fs->fragcoord_compmask != 0;
bool need_size_persamp = false;
if (VALIDREG(ij_regid[IJ_PERSP_CENTER_RHW])) {
if (sample_shading)
need_size_persamp = true;
else
need_size = true;
}
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_CNTL, 1);
tu_cs_emit(cs,
CONDREG(ij_regid[IJ_PERSP_PIXEL], A6XX_GRAS_CNTL_IJ_PERSP_PIXEL) |
CONDREG(ij_regid[IJ_PERSP_CENTROID], A6XX_GRAS_CNTL_IJ_PERSP_CENTROID) |
CONDREG(ij_regid[IJ_PERSP_SAMPLE], A6XX_GRAS_CNTL_IJ_PERSP_SAMPLE) |
CONDREG(ij_regid[IJ_LINEAR_PIXEL], A6XX_GRAS_CNTL_IJ_LINEAR_PIXEL) |
CONDREG(ij_regid[IJ_LINEAR_CENTROID], A6XX_GRAS_CNTL_IJ_LINEAR_CENTROID) |
CONDREG(ij_regid[IJ_LINEAR_SAMPLE], A6XX_GRAS_CNTL_IJ_LINEAR_SAMPLE) |
COND(need_size, A6XX_GRAS_CNTL_IJ_LINEAR_PIXEL) |
COND(need_size_persamp, A6XX_GRAS_CNTL_IJ_LINEAR_SAMPLE) |
COND(fs->fragcoord_compmask != 0, A6XX_GRAS_CNTL_COORD_MASK(fs->fragcoord_compmask)));
tu_cs_emit_pkt4(cs, REG_A6XX_RB_RENDER_CONTROL0, 2);
tu_cs_emit(cs,
CONDREG(ij_regid[IJ_PERSP_PIXEL], A6XX_RB_RENDER_CONTROL0_IJ_PERSP_PIXEL) |
CONDREG(ij_regid[IJ_PERSP_CENTROID], A6XX_RB_RENDER_CONTROL0_IJ_PERSP_CENTROID) |
CONDREG(ij_regid[IJ_PERSP_SAMPLE], A6XX_RB_RENDER_CONTROL0_IJ_PERSP_SAMPLE) |
CONDREG(ij_regid[IJ_LINEAR_PIXEL], A6XX_RB_RENDER_CONTROL0_IJ_LINEAR_PIXEL) |
CONDREG(ij_regid[IJ_LINEAR_CENTROID], A6XX_RB_RENDER_CONTROL0_IJ_LINEAR_CENTROID) |
CONDREG(ij_regid[IJ_LINEAR_SAMPLE], A6XX_RB_RENDER_CONTROL0_IJ_LINEAR_SAMPLE) |
COND(need_size, A6XX_RB_RENDER_CONTROL0_IJ_LINEAR_PIXEL) |
COND(enable_varyings, A6XX_RB_RENDER_CONTROL0_UNK10) |
COND(need_size_persamp, A6XX_RB_RENDER_CONTROL0_IJ_LINEAR_SAMPLE) |
COND(fs->fragcoord_compmask != 0,
A6XX_RB_RENDER_CONTROL0_COORD_MASK(fs->fragcoord_compmask)));
tu_cs_emit(cs,
A6XX_RB_RENDER_CONTROL1_FRAGCOORDSAMPLEMODE(
sample_shading ? FRAGCOORD_SAMPLE : FRAGCOORD_CENTER) |
CONDREG(smask_in_regid, A6XX_RB_RENDER_CONTROL1_SAMPLEMASK) |
CONDREG(samp_id_regid, A6XX_RB_RENDER_CONTROL1_SAMPLEID) |
CONDREG(ij_regid[IJ_PERSP_CENTER_RHW], A6XX_RB_RENDER_CONTROL1_CENTERRHW) |
COND(fs->frag_face, A6XX_RB_RENDER_CONTROL1_FACENESS));
tu_cs_emit_pkt4(cs, REG_A6XX_RB_SAMPLE_CNTL, 1);
tu_cs_emit(cs, COND(sample_shading, A6XX_RB_SAMPLE_CNTL_PER_SAMP_MODE));
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_LRZ_PS_INPUT_CNTL, 1);
tu_cs_emit(cs, CONDREG(samp_id_regid, A6XX_GRAS_LRZ_PS_INPUT_CNTL_SAMPLEID) |
A6XX_GRAS_LRZ_PS_INPUT_CNTL_FRAGCOORDSAMPLEMODE(
sample_shading ? FRAGCOORD_SAMPLE : FRAGCOORD_CENTER));
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_SAMPLE_CNTL, 1);
tu_cs_emit(cs, COND(sample_shading, A6XX_GRAS_SAMPLE_CNTL_PER_SAMP_MODE));
}
static void
tu6_emit_fs_outputs(struct tu_cs *cs,
const struct ir3_shader_variant *fs,
uint32_t mrt_count, bool dual_src_blend,
uint32_t render_components,
bool no_earlyz,
struct tu_pipeline *pipeline)
{
uint32_t smask_regid, posz_regid, stencilref_regid;
posz_regid = ir3_find_output_regid(fs, FRAG_RESULT_DEPTH);
smask_regid = ir3_find_output_regid(fs, FRAG_RESULT_SAMPLE_MASK);
stencilref_regid = ir3_find_output_regid(fs, FRAG_RESULT_STENCIL);
uint32_t fragdata_regid[8];
if (fs->color0_mrt) {
fragdata_regid[0] = ir3_find_output_regid(fs, FRAG_RESULT_COLOR);
for (uint32_t i = 1; i < ARRAY_SIZE(fragdata_regid); i++)
fragdata_regid[i] = fragdata_regid[0];
} else {
for (uint32_t i = 0; i < ARRAY_SIZE(fragdata_regid); i++)
fragdata_regid[i] = ir3_find_output_regid(fs, FRAG_RESULT_DATA0 + i);
}
tu_cs_emit_pkt4(cs, REG_A6XX_SP_FS_OUTPUT_CNTL0, 2);
tu_cs_emit(cs, A6XX_SP_FS_OUTPUT_CNTL0_DEPTH_REGID(posz_regid) |
A6XX_SP_FS_OUTPUT_CNTL0_SAMPMASK_REGID(smask_regid) |
A6XX_SP_FS_OUTPUT_CNTL0_STENCILREF_REGID(stencilref_regid) |
COND(dual_src_blend, A6XX_SP_FS_OUTPUT_CNTL0_DUAL_COLOR_IN_ENABLE));
tu_cs_emit(cs, A6XX_SP_FS_OUTPUT_CNTL1_MRT(mrt_count));
uint32_t fs_render_components = 0;
tu_cs_emit_pkt4(cs, REG_A6XX_SP_FS_OUTPUT_REG(0), 8);
for (uint32_t i = 0; i < ARRAY_SIZE(fragdata_regid); i++) {
tu_cs_emit(cs, A6XX_SP_FS_OUTPUT_REG_REGID(fragdata_regid[i]) |
(COND(fragdata_regid[i] & HALF_REG_ID,
A6XX_SP_FS_OUTPUT_REG_HALF_PRECISION)));
if (VALIDREG(fragdata_regid[i])) {
fs_render_components |= 0xf << (i * 4);
}
}
/* dual source blending has an extra fs output in the 2nd slot */
if (dual_src_blend) {
fs_render_components |= 0xf << 4;
}
/* There is no point in having component enabled which is not written
* by the shader. Per VK spec it is an UB, however a few apps depend on
* attachment not being changed if FS doesn't have corresponding output.
*/
fs_render_components &= render_components;
tu_cs_emit_regs(cs,
A6XX_SP_FS_RENDER_COMPONENTS(.dword = fs_render_components));
tu_cs_emit_pkt4(cs, REG_A6XX_RB_FS_OUTPUT_CNTL0, 2);
tu_cs_emit(cs, COND(fs->writes_pos, A6XX_RB_FS_OUTPUT_CNTL0_FRAG_WRITES_Z) |
COND(fs->writes_smask, A6XX_RB_FS_OUTPUT_CNTL0_FRAG_WRITES_SAMPMASK) |
COND(fs->writes_stencilref, A6XX_RB_FS_OUTPUT_CNTL0_FRAG_WRITES_STENCILREF) |
COND(dual_src_blend, A6XX_RB_FS_OUTPUT_CNTL0_DUAL_COLOR_IN_ENABLE));
tu_cs_emit(cs, A6XX_RB_FS_OUTPUT_CNTL1_MRT(mrt_count));
tu_cs_emit_regs(cs,
A6XX_RB_RENDER_COMPONENTS(.dword = fs_render_components));
if (pipeline) {
pipeline->lrz.fs_has_kill = fs->has_kill;
pipeline->lrz.early_fragment_tests = fs->fs.early_fragment_tests;
if (!fs->fs.early_fragment_tests &&
(fs->no_earlyz || fs->has_kill || fs->writes_pos || fs->writes_stencilref || no_earlyz || fs->writes_smask)) {
pipeline->lrz.force_late_z = true;
}
}
}
static void
tu6_emit_geom_tess_consts(struct tu_cs *cs,
const struct ir3_shader_variant *vs,
const struct ir3_shader_variant *hs,
const struct ir3_shader_variant *ds,
const struct ir3_shader_variant *gs,
uint32_t cps_per_patch)
{
struct tu_device *dev = cs->device;
uint32_t num_vertices =
hs ? cps_per_patch : gs->gs.vertices_in;
uint32_t vs_params[4] = {
vs->output_size * num_vertices * 4, /* vs primitive stride */
vs->output_size * 4, /* vs vertex stride */
0,
0,
};
uint32_t vs_base = ir3_const_state(vs)->offsets.primitive_param;
tu6_emit_const(cs, CP_LOAD_STATE6_GEOM, vs_base, SB6_VS_SHADER, 0,
ARRAY_SIZE(vs_params), vs_params);
if (hs) {
assert(ds->type != MESA_SHADER_NONE);
/* Create the shared tess factor BO the first time tess is used on the device. */
mtx_lock(&dev->mutex);
if (!dev->tess_bo)
tu_bo_init_new(dev, &dev->tess_bo, TU_TESS_BO_SIZE, TU_BO_ALLOC_NO_FLAGS);
mtx_unlock(&dev->mutex);
uint64_t tess_factor_iova = dev->tess_bo->iova;
uint64_t tess_param_iova = tess_factor_iova + TU_TESS_FACTOR_SIZE;
uint32_t hs_params[8] = {
vs->output_size * num_vertices * 4, /* hs primitive stride */
vs->output_size * 4, /* hs vertex stride */
hs->output_size,
cps_per_patch,
tess_param_iova,
tess_param_iova >> 32,
tess_factor_iova,
tess_factor_iova >> 32,
};
uint32_t hs_base = hs->const_state->offsets.primitive_param;
uint32_t hs_param_dwords = MIN2((hs->constlen - hs_base) * 4, ARRAY_SIZE(hs_params));
tu6_emit_const(cs, CP_LOAD_STATE6_GEOM, hs_base, SB6_HS_SHADER, 0,
hs_param_dwords, hs_params);
if (gs)
num_vertices = gs->gs.vertices_in;
uint32_t ds_params[8] = {
ds->output_size * num_vertices * 4, /* ds primitive stride */
ds->output_size * 4, /* ds vertex stride */
hs->output_size, /* hs vertex stride (dwords) */
hs->tess.tcs_vertices_out,
tess_param_iova,
tess_param_iova >> 32,
tess_factor_iova,
tess_factor_iova >> 32,
};
uint32_t ds_base = ds->const_state->offsets.primitive_param;
uint32_t ds_param_dwords = MIN2((ds->constlen - ds_base) * 4, ARRAY_SIZE(ds_params));
tu6_emit_const(cs, CP_LOAD_STATE6_GEOM, ds_base, SB6_DS_SHADER, 0,
ds_param_dwords, ds_params);
}
if (gs) {
const struct ir3_shader_variant *prev = ds ? ds : vs;
uint32_t gs_params[4] = {
prev->output_size * num_vertices * 4, /* gs primitive stride */
prev->output_size * 4, /* gs vertex stride */
0,
0,
};
uint32_t gs_base = gs->const_state->offsets.primitive_param;
tu6_emit_const(cs, CP_LOAD_STATE6_GEOM, gs_base, SB6_GS_SHADER, 0,
ARRAY_SIZE(gs_params), gs_params);
}
}
static void
tu6_emit_program_config(struct tu_cs *cs,
struct tu_pipeline_builder *builder)
{
gl_shader_stage stage = MESA_SHADER_VERTEX;
STATIC_ASSERT(MESA_SHADER_VERTEX == 0);
bool shared_consts_enable = tu6_shared_constants_enable(builder->layout,
builder->device->compiler);
tu6_emit_shared_consts_enable(cs, shared_consts_enable);
tu_cs_emit_regs(cs, A6XX_HLSQ_INVALIDATE_CMD(
.vs_state = true,
.hs_state = true,
.ds_state = true,
.gs_state = true,
.fs_state = true,
.gfx_ibo = true,
.gfx_shared_const = shared_consts_enable));
for (; stage < ARRAY_SIZE(builder->shader_iova); stage++) {
tu6_emit_xs_config(cs, stage, builder->shaders->variants[stage]);
}
}
static void
tu6_emit_program(struct tu_cs *cs,
struct tu_pipeline_builder *builder,
bool binning_pass,
struct tu_pipeline *pipeline)
{
const struct ir3_shader_variant *vs = builder->shaders->variants[MESA_SHADER_VERTEX];
const struct ir3_shader_variant *bs = builder->binning_variant;
const struct ir3_shader_variant *hs = builder->shaders->variants[MESA_SHADER_TESS_CTRL];
const struct ir3_shader_variant *ds = builder->shaders->variants[MESA_SHADER_TESS_EVAL];
const struct ir3_shader_variant *gs = builder->shaders->variants[MESA_SHADER_GEOMETRY];
const struct ir3_shader_variant *fs = builder->shaders->variants[MESA_SHADER_FRAGMENT];
gl_shader_stage stage = MESA_SHADER_VERTEX;
uint32_t cps_per_patch = builder->create_info->pTessellationState ?
builder->create_info->pTessellationState->patchControlPoints : 0;
bool multi_pos_output = builder->shaders->multi_pos_output;
/* Don't use the binning pass variant when GS is present because we don't
* support compiling correct binning pass variants with GS.
*/
if (binning_pass && !gs) {
vs = bs;
tu6_emit_xs(cs, stage, bs, &builder->pvtmem, builder->binning_vs_iova);
stage++;
}
for (; stage < ARRAY_SIZE(builder->shader_iova); stage++) {
const struct ir3_shader_variant *xs = builder->shaders->variants[stage];
if (stage == MESA_SHADER_FRAGMENT && binning_pass)
fs = xs = NULL;
tu6_emit_xs(cs, stage, xs, &builder->pvtmem, builder->shader_iova[stage]);
}
uint32_t multiview_views = util_logbase2(builder->multiview_mask) + 1;
uint32_t multiview_cntl = builder->multiview_mask ?
A6XX_PC_MULTIVIEW_CNTL_ENABLE |
A6XX_PC_MULTIVIEW_CNTL_VIEWS(multiview_views) |
COND(!multi_pos_output, A6XX_PC_MULTIVIEW_CNTL_DISABLEMULTIPOS)
: 0;
/* Copy what the blob does here. This will emit an extra 0x3f
* CP_EVENT_WRITE when multiview is disabled. I'm not exactly sure what
* this is working around yet.
*/
if (builder->device->physical_device->info->a6xx.has_cp_reg_write) {
tu_cs_emit_pkt7(cs, CP_REG_WRITE, 3);
tu_cs_emit(cs, CP_REG_WRITE_0_TRACKER(UNK_EVENT_WRITE));
tu_cs_emit(cs, REG_A6XX_PC_MULTIVIEW_CNTL);
} else {
tu_cs_emit_pkt4(cs, REG_A6XX_PC_MULTIVIEW_CNTL, 1);
}
tu_cs_emit(cs, multiview_cntl);
tu_cs_emit_pkt4(cs, REG_A6XX_VFD_MULTIVIEW_CNTL, 1);
tu_cs_emit(cs, multiview_cntl);
if (multiview_cntl &&
builder->device->physical_device->info->a6xx.supports_multiview_mask) {
tu_cs_emit_pkt4(cs, REG_A6XX_PC_MULTIVIEW_MASK, 1);
tu_cs_emit(cs, builder->multiview_mask);
}
tu_cs_emit_pkt4(cs, REG_A6XX_SP_HS_WAVE_INPUT_SIZE, 1);
tu_cs_emit(cs, 0);
tu6_emit_vpc(cs, vs, hs, ds, gs, fs, cps_per_patch);
tu6_emit_vpc_varying_modes(cs, fs);
bool no_earlyz = builder->depth_attachment_format == VK_FORMAT_S8_UINT;
uint32_t mrt_count = builder->color_attachment_count;
uint32_t render_components = builder->render_components;
if (builder->alpha_to_coverage) {
/* alpha to coverage can behave like a discard */
no_earlyz = true;
/* alpha value comes from first mrt */
render_components |= 0xf;
if (!mrt_count) {
mrt_count = 1;
/* Disable memory write for dummy mrt because it doesn't get set otherwise */
tu_cs_emit_regs(cs, A6XX_RB_MRT_CONTROL(0, .component_enable = 0));
}
}
if (fs) {
tu6_emit_fs_inputs(cs, fs);
tu6_emit_fs_outputs(cs, fs, mrt_count,
builder->use_dual_src_blend,
render_components,
no_earlyz,
pipeline);
} else {
/* TODO: check if these can be skipped if fs is disabled */
struct ir3_shader_variant dummy_variant = {};
tu6_emit_fs_inputs(cs, &dummy_variant);
tu6_emit_fs_outputs(cs, &dummy_variant, mrt_count,
builder->use_dual_src_blend,
render_components,
no_earlyz,
NULL);
}
if (gs || hs) {
tu6_emit_geom_tess_consts(cs, vs, hs, ds, gs, cps_per_patch);
}
}
#define TU6_EMIT_VERTEX_INPUT_MAX_DWORDS (MAX_VERTEX_ATTRIBS * 5 + 4)
static void
tu6_emit_vertex_input(struct tu_pipeline *pipeline,
struct tu_draw_state *vi_state,
const struct ir3_shader_variant *vs,
const VkPipelineVertexInputStateCreateInfo *info)
{
uint32_t binding_instanced = 0; /* bitmask of instanced bindings */
uint32_t step_rate[MAX_VBS];
struct tu_cs cs;
tu_cs_begin_sub_stream(&pipeline->cs,
TU6_EMIT_VERTEX_INPUT_MAX_DWORDS, &cs);
for (uint32_t i = 0; i < info->vertexBindingDescriptionCount; i++) {
const VkVertexInputBindingDescription *binding =
&info->pVertexBindingDescriptions[i];
if (!(pipeline->dynamic_state_mask & BIT(TU_DYNAMIC_STATE_VB_STRIDE))) {
tu_cs_emit_regs(&cs,
A6XX_VFD_FETCH_STRIDE(binding->binding, binding->stride));
}
if (binding->inputRate == VK_VERTEX_INPUT_RATE_INSTANCE)
binding_instanced |= 1 << binding->binding;
step_rate[binding->binding] = 1;
}
const VkPipelineVertexInputDivisorStateCreateInfoEXT *div_state =
vk_find_struct_const(info->pNext, PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT);
if (div_state) {
for (uint32_t i = 0; i < div_state->vertexBindingDivisorCount; i++) {
const VkVertexInputBindingDivisorDescriptionEXT *desc =
&div_state->pVertexBindingDivisors[i];
step_rate[desc->binding] = desc->divisor;
}
}
int32_t input_for_attr[MAX_VERTEX_ATTRIBS];
uint32_t used_attrs_count = 0;
for (uint32_t attr_idx = 0; attr_idx < info->vertexAttributeDescriptionCount; attr_idx++) {
input_for_attr[attr_idx] = -1;
for (uint32_t input_idx = 0; input_idx < vs->inputs_count; input_idx++) {
if ((vs->inputs[input_idx].slot - VERT_ATTRIB_GENERIC0) ==
info->pVertexAttributeDescriptions[attr_idx].location) {
input_for_attr[attr_idx] = input_idx;
used_attrs_count++;
break;
}
}
}
if (used_attrs_count)
tu_cs_emit_pkt4(&cs, REG_A6XX_VFD_DECODE_INSTR(0), used_attrs_count * 2);
for (uint32_t attr_idx = 0; attr_idx < info->vertexAttributeDescriptionCount; attr_idx++) {
const VkVertexInputAttributeDescription *attr =
&info->pVertexAttributeDescriptions[attr_idx];
if (input_for_attr[attr_idx] == -1)
continue;
const struct tu_native_format format = tu6_format_vtx(attr->format);
tu_cs_emit(&cs, A6XX_VFD_DECODE_INSTR(0,
.idx = attr->binding,
.offset = attr->offset,
.instanced = binding_instanced & (1 << attr->binding),
.format = format.fmt,
.swap = format.swap,
.unk30 = 1,
._float = !vk_format_is_int(attr->format)).value);
tu_cs_emit(&cs, A6XX_VFD_DECODE_STEP_RATE(0, step_rate[attr->binding]).value);
}
if (used_attrs_count)
tu_cs_emit_pkt4(&cs, REG_A6XX_VFD_DEST_CNTL_INSTR(0), used_attrs_count);
for (uint32_t attr_idx = 0; attr_idx < info->vertexAttributeDescriptionCount; attr_idx++) {
int32_t input_idx = input_for_attr[attr_idx];
if (input_idx == -1)
continue;
tu_cs_emit(&cs, A6XX_VFD_DEST_CNTL_INSTR(0,
.writemask = vs->inputs[input_idx].compmask,
.regid = vs->inputs[input_idx].regid).value);
}
tu_cs_emit_regs(&cs,
A6XX_VFD_CONTROL_0(
.fetch_cnt = used_attrs_count, /* decode_cnt for binning pass ? */
.decode_cnt = used_attrs_count));
*vi_state = tu_cs_end_draw_state(&pipeline->cs, &cs);
}
void
tu6_emit_viewport(struct tu_cs *cs, const VkViewport *viewports, uint32_t num_viewport,
bool z_negative_one_to_one)
{
VkExtent2D guardband = {511, 511};
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_CL_VPORT_XOFFSET(0), num_viewport * 6);
for (uint32_t i = 0; i < num_viewport; i++) {
const VkViewport *viewport = &viewports[i];
float offsets[3];
float scales[3];
scales[0] = viewport->width / 2.0f;
scales[1] = viewport->height / 2.0f;
if (z_negative_one_to_one) {
scales[2] = 0.5 * (viewport->maxDepth - viewport->minDepth);
} else {
scales[2] = viewport->maxDepth - viewport->minDepth;
}
offsets[0] = viewport->x + scales[0];
offsets[1] = viewport->y + scales[1];
if (z_negative_one_to_one) {
offsets[2] = 0.5 * (viewport->minDepth + viewport->maxDepth);
} else {
offsets[2] = viewport->minDepth;
}
for (uint32_t j = 0; j < 3; j++) {
tu_cs_emit(cs, fui(offsets[j]));
tu_cs_emit(cs, fui(scales[j]));
}
guardband.width =
MIN2(guardband.width, fd_calc_guardband(offsets[0], scales[0], false));
guardband.height =
MIN2(guardband.height, fd_calc_guardband(offsets[1], scales[1], false));
}
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_SC_VIEWPORT_SCISSOR_TL(0), num_viewport * 2);
for (uint32_t i = 0; i < num_viewport; i++) {
const VkViewport *viewport = &viewports[i];
VkOffset2D min;
VkOffset2D max;
min.x = (int32_t) viewport->x;
max.x = (int32_t) ceilf(viewport->x + viewport->width);
if (viewport->height >= 0.0f) {
min.y = (int32_t) viewport->y;
max.y = (int32_t) ceilf(viewport->y + viewport->height);
} else {
min.y = (int32_t)(viewport->y + viewport->height);
max.y = (int32_t) ceilf(viewport->y);
}
/* the spec allows viewport->height to be 0.0f */
if (min.y == max.y)
max.y++;
/* allow viewport->width = 0.0f for un-initialized viewports: */
if (min.x == max.x)
max.x++;
min.x = MAX2(min.x, 0);
min.y = MAX2(min.y, 0);
max.x = MAX2(max.x, 1);
max.y = MAX2(max.y, 1);
assert(min.x < max.x);
assert(min.y < max.y);
tu_cs_emit(cs, A6XX_GRAS_SC_VIEWPORT_SCISSOR_TL_X(min.x) |
A6XX_GRAS_SC_VIEWPORT_SCISSOR_TL_Y(min.y));
tu_cs_emit(cs, A6XX_GRAS_SC_VIEWPORT_SCISSOR_BR_X(max.x - 1) |
A6XX_GRAS_SC_VIEWPORT_SCISSOR_BR_Y(max.y - 1));
}
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_CL_Z_CLAMP(0), num_viewport * 2);
for (uint32_t i = 0; i < num_viewport; i++) {
const VkViewport *viewport = &viewports[i];
tu_cs_emit(cs, fui(MIN2(viewport->minDepth, viewport->maxDepth)));
tu_cs_emit(cs, fui(MAX2(viewport->minDepth, viewport->maxDepth)));
}
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_CL_GUARDBAND_CLIP_ADJ, 1);
tu_cs_emit(cs, A6XX_GRAS_CL_GUARDBAND_CLIP_ADJ_HORZ(guardband.width) |
A6XX_GRAS_CL_GUARDBAND_CLIP_ADJ_VERT(guardband.height));
/* TODO: what to do about this and multi viewport ? */
float z_clamp_min = num_viewport ? MIN2(viewports[0].minDepth, viewports[0].maxDepth) : 0;
float z_clamp_max = num_viewport ? MAX2(viewports[0].minDepth, viewports[0].maxDepth) : 0;
tu_cs_emit_regs(cs,
A6XX_RB_Z_CLAMP_MIN(z_clamp_min),
A6XX_RB_Z_CLAMP_MAX(z_clamp_max));
}
void
tu6_emit_scissor(struct tu_cs *cs, const VkRect2D *scissors, uint32_t scissor_count)
{
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_SC_SCREEN_SCISSOR_TL(0), scissor_count * 2);
for (uint32_t i = 0; i < scissor_count; i++) {
const VkRect2D *scissor = &scissors[i];
uint32_t min_x = scissor->offset.x;
uint32_t min_y = scissor->offset.y;
uint32_t max_x = min_x + scissor->extent.width - 1;
uint32_t max_y = min_y + scissor->extent.height - 1;
if (!scissor->extent.width || !scissor->extent.height) {
min_x = min_y = 1;
max_x = max_y = 0;
} else {
/* avoid overflow */
uint32_t scissor_max = BITFIELD_MASK(15);
min_x = MIN2(scissor_max, min_x);
min_y = MIN2(scissor_max, min_y);
max_x = MIN2(scissor_max, max_x);
max_y = MIN2(scissor_max, max_y);
}
tu_cs_emit(cs, A6XX_GRAS_SC_SCREEN_SCISSOR_TL_X(min_x) |
A6XX_GRAS_SC_SCREEN_SCISSOR_TL_Y(min_y));
tu_cs_emit(cs, A6XX_GRAS_SC_SCREEN_SCISSOR_BR_X(max_x) |
A6XX_GRAS_SC_SCREEN_SCISSOR_BR_Y(max_y));
}
}
void
tu6_emit_sample_locations(struct tu_cs *cs, const VkSampleLocationsInfoEXT *samp_loc)
{
if (!samp_loc) {
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_SAMPLE_CONFIG, 1);
tu_cs_emit(cs, 0);
tu_cs_emit_pkt4(cs, REG_A6XX_RB_SAMPLE_CONFIG, 1);
tu_cs_emit(cs, 0);
tu_cs_emit_pkt4(cs, REG_A6XX_SP_TP_SAMPLE_CONFIG, 1);
tu_cs_emit(cs, 0);
return;
}
assert(samp_loc->sampleLocationsPerPixel == samp_loc->sampleLocationsCount);
assert(samp_loc->sampleLocationGridSize.width == 1);
assert(samp_loc->sampleLocationGridSize.height == 1);
uint32_t sample_config =
A6XX_RB_SAMPLE_CONFIG_LOCATION_ENABLE;
uint32_t sample_locations = 0;
for (uint32_t i = 0; i < samp_loc->sampleLocationsCount; i++) {
sample_locations |=
(A6XX_RB_SAMPLE_LOCATION_0_SAMPLE_0_X(samp_loc->pSampleLocations[i].x) |
A6XX_RB_SAMPLE_LOCATION_0_SAMPLE_0_Y(samp_loc->pSampleLocations[i].y)) << i*8;
}
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_SAMPLE_CONFIG, 2);
tu_cs_emit(cs, sample_config);
tu_cs_emit(cs, sample_locations);
tu_cs_emit_pkt4(cs, REG_A6XX_RB_SAMPLE_CONFIG, 2);
tu_cs_emit(cs, sample_config);
tu_cs_emit(cs, sample_locations);
tu_cs_emit_pkt4(cs, REG_A6XX_SP_TP_SAMPLE_CONFIG, 2);
tu_cs_emit(cs, sample_config);
tu_cs_emit(cs, sample_locations);
}
static uint32_t
tu6_gras_su_cntl(const VkPipelineRasterizationStateCreateInfo *rast_info,
enum a5xx_line_mode line_mode,
bool multiview)
{
uint32_t gras_su_cntl = 0;
if (rast_info->cullMode & VK_CULL_MODE_FRONT_BIT)
gras_su_cntl |= A6XX_GRAS_SU_CNTL_CULL_FRONT;
if (rast_info->cullMode & VK_CULL_MODE_BACK_BIT)
gras_su_cntl |= A6XX_GRAS_SU_CNTL_CULL_BACK;
if (rast_info->frontFace == VK_FRONT_FACE_CLOCKWISE)
gras_su_cntl |= A6XX_GRAS_SU_CNTL_FRONT_CW;
gras_su_cntl |=
A6XX_GRAS_SU_CNTL_LINEHALFWIDTH(rast_info->lineWidth / 2.0f);
if (rast_info->depthBiasEnable)
gras_su_cntl |= A6XX_GRAS_SU_CNTL_POLY_OFFSET;
gras_su_cntl |= A6XX_GRAS_SU_CNTL_LINE_MODE(line_mode);
if (multiview) {
gras_su_cntl |=
A6XX_GRAS_SU_CNTL_UNK17 |
A6XX_GRAS_SU_CNTL_MULTIVIEW_ENABLE;
}
return gras_su_cntl;
}
void
tu6_emit_depth_bias(struct tu_cs *cs,
float constant_factor,
float clamp,
float slope_factor)
{
tu_cs_emit_pkt4(cs, REG_A6XX_GRAS_SU_POLY_OFFSET_SCALE, 3);
tu_cs_emit(cs, A6XX_GRAS_SU_POLY_OFFSET_SCALE(slope_factor).value);
tu_cs_emit(cs, A6XX_GRAS_SU_POLY_OFFSET_OFFSET(constant_factor).value);
tu_cs_emit(cs, A6XX_GRAS_SU_POLY_OFFSET_OFFSET_CLAMP(clamp).value);
}
static uint32_t
tu6_rb_mrt_blend_control(const VkPipelineColorBlendAttachmentState *att,
bool has_alpha)
{
const enum a3xx_rb_blend_opcode color_op = tu6_blend_op(att->colorBlendOp);
const enum adreno_rb_blend_factor src_color_factor = tu6_blend_factor(
has_alpha ? att->srcColorBlendFactor
: tu_blend_factor_no_dst_alpha(att->srcColorBlendFactor));
const enum adreno_rb_blend_factor dst_color_factor = tu6_blend_factor(
has_alpha ? att->dstColorBlendFactor
: tu_blend_factor_no_dst_alpha(att->dstColorBlendFactor));
const enum a3xx_rb_blend_opcode alpha_op = tu6_blend_op(att->alphaBlendOp);
const enum adreno_rb_blend_factor src_alpha_factor =
tu6_blend_factor(att->srcAlphaBlendFactor);
const enum adreno_rb_blend_factor dst_alpha_factor =
tu6_blend_factor(att->dstAlphaBlendFactor);
return A6XX_RB_MRT_BLEND_CONTROL_RGB_SRC_FACTOR(src_color_factor) |
A6XX_RB_MRT_BLEND_CONTROL_RGB_BLEND_OPCODE(color_op) |
A6XX_RB_MRT_BLEND_CONTROL_RGB_DEST_FACTOR(dst_color_factor) |
A6XX_RB_MRT_BLEND_CONTROL_ALPHA_SRC_FACTOR(src_alpha_factor) |
A6XX_RB_MRT_BLEND_CONTROL_ALPHA_BLEND_OPCODE(alpha_op) |
A6XX_RB_MRT_BLEND_CONTROL_ALPHA_DEST_FACTOR(dst_alpha_factor);
}
static uint32_t
tu6_rb_mrt_control(const VkPipelineColorBlendAttachmentState *att,
uint32_t rb_mrt_control_rop,
bool has_alpha)
{
uint32_t rb_mrt_control =
A6XX_RB_MRT_CONTROL_COMPONENT_ENABLE(att->colorWriteMask);
rb_mrt_control |= rb_mrt_control_rop;
if (att->blendEnable) {
rb_mrt_control |= A6XX_RB_MRT_CONTROL_BLEND;
if (has_alpha)
rb_mrt_control |= A6XX_RB_MRT_CONTROL_BLEND2;
}
return rb_mrt_control;
}
uint32_t
tu6_rb_mrt_control_rop(VkLogicOp op, bool *rop_reads_dst)
{
*rop_reads_dst = tu_logic_op_reads_dst(op);
return A6XX_RB_MRT_CONTROL_ROP_ENABLE |
A6XX_RB_MRT_CONTROL_ROP_CODE(tu6_rop(op));
}
static void
tu6_emit_rb_mrt_controls(struct tu_pipeline *pipeline,
const VkPipelineColorBlendStateCreateInfo *blend_info,
const VkFormat attachment_formats[MAX_RTS],
bool *rop_reads_dst,
uint32_t *color_bandwidth_per_sample)
{
const VkPipelineColorWriteCreateInfoEXT *color_info =
vk_find_struct_const(blend_info->pNext,
PIPELINE_COLOR_WRITE_CREATE_INFO_EXT);
/* The static state is ignored if it's dynamic. In that case assume
* everything is enabled and then the appropriate registers will be zero'd
* dynamically.
*/
if (pipeline->dynamic_state_mask & BIT(TU_DYNAMIC_STATE_COLOR_WRITE_ENABLE))
color_info = NULL;
*rop_reads_dst = false;
*color_bandwidth_per_sample = 0;
uint32_t rb_mrt_control_rop = 0;
if (blend_info->logicOpEnable) {
pipeline->logic_op_enabled = true;
rb_mrt_control_rop = tu6_rb_mrt_control_rop(blend_info->logicOp,
rop_reads_dst);
}
uint32_t total_bpp = 0;
pipeline->num_rts = blend_info->attachmentCount;
for (uint32_t i = 0; i < blend_info->attachmentCount; i++) {
const VkPipelineColorBlendAttachmentState *att =
&blend_info->pAttachments[i];
const VkFormat format = attachment_formats[i];
uint32_t rb_mrt_control = 0;
uint32_t rb_mrt_blend_control = 0;
if (format != VK_FORMAT_UNDEFINED &&
(!color_info || color_info->pColorWriteEnables[i])) {
const bool has_alpha = vk_format_has_alpha(format);
rb_mrt_control =
tu6_rb_mrt_control(att, rb_mrt_control_rop, has_alpha);
rb_mrt_blend_control = tu6_rb_mrt_blend_control(att, has_alpha);
/* calculate bpp based on format and write mask */
uint32_t write_bpp = 0;
if (att->colorWriteMask == 0xf) {
write_bpp = vk_format_get_blocksizebits(format);
} else {
const enum pipe_format pipe_format = vk_format_to_pipe_format(format);
for (uint32_t i = 0; i < 4; i++) {
if (att->colorWriteMask & (1 << i)) {
write_bpp += util_format_get_component_bits(pipe_format,
UTIL_FORMAT_COLORSPACE_RGB, i);
}
}
}
total_bpp += write_bpp;
pipeline->color_write_enable |= BIT(i);
if (att->blendEnable)
pipeline->blend_enable |= BIT(i);
if (att->blendEnable || *rop_reads_dst) {
total_bpp += write_bpp;
}
}
pipeline->rb_mrt_control[i] = rb_mrt_control & pipeline->rb_mrt_control_mask;
pipeline->rb_mrt_blend_control[i] = rb_mrt_blend_control;
}
*color_bandwidth_per_sample = total_bpp / 8;
}
static void
tu6_emit_blend_control(struct tu_pipeline *pipeline,
uint32_t blend_enable_mask,
bool dual_src_blend,
const VkPipelineMultisampleStateCreateInfo *msaa_info)
{
const uint32_t sample_mask =
msaa_info->pSampleMask ? (*msaa_info->pSampleMask & 0xffff)
: ((1 << msaa_info->rasterizationSamples) - 1);
pipeline->sp_blend_cntl =
A6XX_SP_BLEND_CNTL(.enable_blend = blend_enable_mask,
.dual_color_in_enable = dual_src_blend,
.alpha_to_coverage = msaa_info->alphaToCoverageEnable,
.unk8 = true).value & pipeline->sp_blend_cntl_mask;
/* set A6XX_RB_BLEND_CNTL_INDEPENDENT_BLEND only when enabled? */
pipeline->rb_blend_cntl =
A6XX_RB_BLEND_CNTL(.enable_blend = blend_enable_mask,
.independent_blend = true,
.sample_mask = sample_mask,
.dual_color_in_enable = dual_src_blend,
.alpha_to_coverage = msaa_info->alphaToCoverageEnable,
.alpha_to_one = msaa_info->alphaToOneEnable).value &
pipeline->rb_blend_cntl_mask;
}
static void
tu6_emit_blend(struct tu_cs *cs,
struct tu_pipeline *pipeline)
{
tu_cs_emit_regs(cs, A6XX_SP_BLEND_CNTL(.dword = pipeline->sp_blend_cntl));
tu_cs_emit_regs(cs, A6XX_RB_BLEND_CNTL(.dword = pipeline->rb_blend_cntl));
for (unsigned i = 0; i < pipeline->num_rts; i++) {
tu_cs_emit_regs(cs,
A6XX_RB_MRT_CONTROL(i, .dword = pipeline->rb_mrt_control[i]),
A6XX_RB_MRT_BLEND_CONTROL(i, .dword = pipeline->rb_mrt_blend_control[i]));
}
}
static uint32_t
calc_pvtmem_size(struct tu_device *dev, struct tu_pvtmem_config *config,
uint32_t pvtmem_bytes)
{
uint32_t per_fiber_size = ALIGN(pvtmem_bytes, 512);
uint32_t per_sp_size =
ALIGN(per_fiber_size * dev->physical_device->info->a6xx.fibers_per_sp, 1 << 12);
if (config) {
config->per_fiber_size = per_fiber_size;
config->per_sp_size = per_sp_size;
}
return dev->physical_device->info->num_sp_cores * per_sp_size;
}
static VkResult
tu_setup_pvtmem(struct tu_device *dev,
struct tu_pipeline *pipeline,
struct tu_pvtmem_config *config,
uint32_t pvtmem_bytes, bool per_wave)
{
if (!pvtmem_bytes) {
memset(config, 0, sizeof(*config));
return VK_SUCCESS;
}
uint32_t total_size = calc_pvtmem_size(dev, config, pvtmem_bytes);
config->per_wave = per_wave;
VkResult result =
tu_bo_init_new(dev, &pipeline->pvtmem_bo, total_size,
TU_BO_ALLOC_NO_FLAGS);
if (result != VK_SUCCESS)
return result;
config->iova = pipeline->pvtmem_bo->iova;
return result;
}
static VkResult
tu_pipeline_allocate_cs(struct tu_device *dev,
struct tu_pipeline *pipeline,
struct tu_pipeline_layout *layout,
struct tu_pipeline_builder *builder,
struct ir3_shader_variant *compute)
{
uint32_t size = 1024 + tu6_load_state_size(pipeline, layout, compute);
/* graphics case: */
if (builder) {
size += 2 * TU6_EMIT_VERTEX_INPUT_MAX_DWORDS;
for (uint32_t i = 0; i < ARRAY_SIZE(builder->shaders->variants); i++) {
if (builder->shaders->variants[i]) {
size += builder->shaders->variants[i]->info.size / 4;
}
}
size += builder->binning_variant->info.size / 4;
builder->additional_cs_reserve_size = 0;
for (unsigned i = 0; i < ARRAY_SIZE(builder->shaders->variants); i++) {
struct ir3_shader_variant *variant = builder->shaders->variants[i];
if (variant) {
builder->additional_cs_reserve_size +=
tu_xs_get_additional_cs_size_dwords(variant);
if (variant->binning) {
builder->additional_cs_reserve_size +=
tu_xs_get_additional_cs_size_dwords(variant->binning);
}
}
}
/* The additional size is used twice, once per tu6_emit_program() call. */
size += builder->additional_cs_reserve_size * 2;
} else {
size += compute->info.size / 4;
size += tu_xs_get_additional_cs_size_dwords(compute);
}
/* Allocate the space for the pipeline out of the device's RO suballocator.
*
* Sub-allocating BOs saves memory and also kernel overhead in refcounting of
* BOs at exec time.
*
* The pipeline cache would seem like a natural place to stick the
* suballocator, except that it is not guaranteed to outlive the pipelines
* created from it, so you can't store any long-lived state there, and you
* can't use its EXTERNALLY_SYNCHRONIZED flag to avoid atomics because
* pipeline destroy isn't synchronized by the cache.
*/
pthread_mutex_lock(&dev->pipeline_mutex);
VkResult result = tu_suballoc_bo_alloc(&pipeline->bo, &dev->pipeline_suballoc,
size * 4, 128);
pthread_mutex_unlock(&dev->pipeline_mutex);
if (result != VK_SUCCESS)
return result;
tu_cs_init_suballoc(&pipeline->cs, dev, &pipeline->bo);
return VK_SUCCESS;
}
static void
tu_pipeline_shader_key_init(struct ir3_shader_key *key,
const struct tu_pipeline *pipeline,
const VkGraphicsPipelineCreateInfo *pipeline_info)
{
for (uint32_t i = 0; i < pipeline_info->stageCount; i++) {
if (pipeline_info->pStages[i].stage == VK_SHADER_STAGE_GEOMETRY_BIT) {
key->has_gs = true;
break;
}
}
if (pipeline_info->pRasterizationState->rasterizerDiscardEnable &&
!(pipeline->dynamic_state_mask & BIT(TU_DYNAMIC_STATE_RASTERIZER_DISCARD)))
return;
const VkPipelineMultisampleStateCreateInfo *msaa_info = pipeline_info->pMultisampleState;
const struct VkPipelineSampleLocationsStateCreateInfoEXT *sample_locations =
vk_find_struct_const(msaa_info->pNext, PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT);
if (msaa_info->rasterizationSamples > 1 ||
/* also set msaa key when sample location is not the default
* since this affects varying interpolation */
(sample_locations && sample_locations->sampleLocationsEnable)) {
key->msaa = true;
}
/* The 1.3.215 spec says:
*
* Sample shading can be used to specify a minimum number of unique
* samples to process for each fragment. If sample shading is enabled,
* an implementation must provide a minimum of
*
* max(ceil(minSampleShadingFactor * totalSamples), 1)
*
* unique associated data for each fragment, where
* minSampleShadingFactor is the minimum fraction of sample shading.
*
* The definition is pretty much the same as OpenGL's GL_SAMPLE_SHADING.
* They both require unique associated data.
*
* There are discussions to change the definition, such that
* sampleShadingEnable does not imply unique associated data. Before the
* discussions are settled and before apps (i.e., ANGLE) are fixed to
* follow the new and incompatible definition, we should stick to the
* current definition.
*
* Note that ir3_shader_key::sample_shading is not actually used by ir3,
* just checked in tu6_emit_fs_inputs. We will also copy the value to
* tu_shader_key::force_sample_interp in a bit.
*/
if (msaa_info->sampleShadingEnable &&
(msaa_info->minSampleShading * msaa_info->rasterizationSamples) > 1.0f)
key->sample_shading = true;
/* We set this after we compile to NIR because we need the prim mode */
key->tessellation = IR3_TESS_NONE;
}
static uint32_t
tu6_get_tessmode(struct tu_shader* shader)
{
enum tess_primitive_mode primitive_mode = shader->ir3_shader->nir->info.tess._primitive_mode;
switch (primitive_mode) {
case TESS_PRIMITIVE_ISOLINES:
return IR3_TESS_ISOLINES;
case TESS_PRIMITIVE_TRIANGLES:
return IR3_TESS_TRIANGLES;
case TESS_PRIMITIVE_QUADS:
return IR3_TESS_QUADS;
case TESS_PRIMITIVE_UNSPECIFIED:
return IR3_TESS_NONE;
default:
unreachable("bad tessmode");
}
}
static uint64_t
tu_upload_variant(struct tu_pipeline *pipeline,
const struct ir3_shader_variant *variant)
{
struct tu_cs_memory memory;
if (!variant)
return 0;
/* this expects to get enough alignment because shaders are allocated first
* and total size is always aligned correctly
* note: an assert in tu6_emit_xs_config validates the alignment
*/
tu_cs_alloc(&pipeline->cs, variant->info.size / 4, 1, &memory);
memcpy(memory.map, variant->bin, variant->info.size);
return memory.iova;
}
static void
tu_append_executable(struct tu_pipeline *pipeline, struct ir3_shader_variant *variant,
char *nir_from_spirv)
{
struct tu_pipeline_executable exe = {
.stage = variant->type,
.nir_from_spirv = nir_from_spirv,
.nir_final = ralloc_strdup(pipeline->executables_mem_ctx, variant->disasm_info.nir),
.disasm = ralloc_strdup(pipeline->executables_mem_ctx, variant->disasm_info.disasm),
.stats = variant->info,
.is_binning = variant->binning_pass,
};
util_dynarray_append(&pipeline->executables, struct tu_pipeline_executable, exe);
}
static void
tu_link_shaders(struct tu_pipeline_builder *builder,
nir_shader **shaders, unsigned shaders_count)
{
nir_shader *consumer = NULL;
for (gl_shader_stage stage = shaders_count - 1;
stage >= MESA_SHADER_VERTEX; stage--) {
if (!shaders[stage])
continue;
nir_shader *producer = shaders[stage];
if (!consumer) {
consumer = producer;
continue;
}
if (nir_link_opt_varyings(producer, consumer)) {
NIR_PASS_V(consumer, nir_opt_constant_folding);
NIR_PASS_V(consumer, nir_opt_algebraic);
NIR_PASS_V(consumer, nir_opt_dce);
}
NIR_PASS_V(producer, nir_remove_dead_variables, nir_var_shader_out, NULL);
NIR_PASS_V(consumer, nir_remove_dead_variables, nir_var_shader_in, NULL);
bool progress = nir_remove_unused_varyings(producer, consumer);
nir_compact_varyings(producer, consumer, true);
if (progress) {
if (nir_lower_global_vars_to_local(producer)) {
/* Remove dead writes, which can remove input loads */
NIR_PASS_V(producer, nir_remove_dead_variables, nir_var_shader_temp, NULL);
NIR_PASS_V(producer, nir_opt_dce);
}
nir_lower_global_vars_to_local(consumer);
}
consumer = producer;
}
}
static void
tu_shader_key_init(struct tu_shader_key *key,
const VkPipelineShaderStageCreateInfo *stage_info,
struct tu_device *dev)
{
enum ir3_wavesize_option api_wavesize, real_wavesize;
if (stage_info) {
if (stage_info->flags &
VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT) {
api_wavesize = real_wavesize = IR3_SINGLE_OR_DOUBLE;
} else {
const VkPipelineShaderStageRequiredSubgroupSizeCreateInfo *size_info =
vk_find_struct_const(stage_info->pNext,
PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO);
if (size_info) {
if (size_info->requiredSubgroupSize == dev->compiler->threadsize_base) {
api_wavesize = IR3_SINGLE_ONLY;
} else {
assert(size_info->requiredSubgroupSize == dev->compiler->threadsize_base * 2);
api_wavesize = IR3_DOUBLE_ONLY;
}
} else {
/* Match the exposed subgroupSize. */
api_wavesize = IR3_DOUBLE_ONLY;
}
if (stage_info->flags &
VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT)
real_wavesize = api_wavesize;
else if (api_wavesize == IR3_SINGLE_ONLY)
real_wavesize = IR3_SINGLE_ONLY;
else
real_wavesize = IR3_SINGLE_OR_DOUBLE;
}
} else {
api_wavesize = real_wavesize = IR3_SINGLE_OR_DOUBLE;
}
key->api_wavesize = api_wavesize;
key->real_wavesize = real_wavesize;
}
static void
tu_hash_stage(struct mesa_sha1 *ctx,
const VkPipelineShaderStageCreateInfo *stage,
const struct tu_shader_key *key)
{
unsigned char stage_hash[SHA1_DIGEST_LENGTH];
vk_pipeline_hash_shader_stage(stage, stage_hash);
_mesa_sha1_update(ctx, stage_hash, sizeof(stage_hash));
_mesa_sha1_update(ctx, key, sizeof(*key));
}
/* Hash flags which can affect ir3 shader compilation which aren't known until
* logical device creation.
*/
static void
tu_hash_compiler(struct mesa_sha1 *ctx, const struct ir3_compiler *compiler)
{
_mesa_sha1_update(ctx, &compiler->robust_buffer_access2,
sizeof(compiler->robust_buffer_access2));
_mesa_sha1_update(ctx, &ir3_shader_debug, sizeof(ir3_shader_debug));
}
static void
tu_hash_shaders(unsigned char *hash,
const VkPipelineShaderStageCreateInfo **stages,
const struct tu_pipeline_layout *layout,
const struct tu_shader_key *keys,
const struct ir3_shader_key *ir3_key,
const struct ir3_compiler *compiler)
{
struct mesa_sha1 ctx;
_mesa_sha1_init(&ctx);
if (layout)
_mesa_sha1_update(&ctx, layout->sha1, sizeof(layout->sha1));
_mesa_sha1_update(&ctx, ir3_key, sizeof(ir3_key));
for (int i = 0; i < MESA_SHADER_STAGES; ++i) {
if (stages[i]) {
tu_hash_stage(&ctx, stages[i], &keys[i]);
}
}
tu_hash_compiler(&ctx, compiler);
_mesa_sha1_final(&ctx, hash);
}
static void
tu_hash_compute(unsigned char *hash,
const VkPipelineShaderStageCreateInfo *stage,
const struct tu_pipeline_layout *layout,
const struct tu_shader_key *key,
const struct ir3_compiler *compiler)
{
struct mesa_sha1 ctx;
_mesa_sha1_init(&ctx);
if (layout)
_mesa_sha1_update(&ctx, layout->sha1, sizeof(layout->sha1));
tu_hash_stage(&ctx, stage, key);
tu_hash_compiler(&ctx, compiler);
_mesa_sha1_final(&ctx, hash);
}
static bool
tu_shaders_serialize(struct vk_pipeline_cache_object *object,
struct blob *blob);
static struct vk_pipeline_cache_object *
tu_shaders_deserialize(struct vk_device *device,
const void *key_data, size_t key_size,
struct blob_reader *blob);
static void
tu_shaders_destroy(struct vk_pipeline_cache_object *object)
{
struct tu_compiled_shaders *shaders =
container_of(object, struct tu_compiled_shaders, base);
for (unsigned i = 0; i < ARRAY_SIZE(shaders->variants); i++)
ralloc_free(shaders->variants[i]);
vk_pipeline_cache_object_finish(&shaders->base);
vk_free(&object->device->alloc, shaders);
}
const struct vk_pipeline_cache_object_ops tu_shaders_ops = {
.serialize = tu_shaders_serialize,
.deserialize = tu_shaders_deserialize,
.destroy = tu_shaders_destroy,
};
static struct tu_compiled_shaders *
tu_shaders_init(struct tu_device *dev, const void *key_data, size_t key_size)
{
VK_MULTIALLOC(ma);
VK_MULTIALLOC_DECL(&ma, struct tu_compiled_shaders, shaders, 1);
VK_MULTIALLOC_DECL_SIZE(&ma, void, obj_key_data, key_size);
if (!vk_multialloc_zalloc(&ma, &dev->vk.alloc,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE))
return NULL;
memcpy(obj_key_data, key_data, key_size);
vk_pipeline_cache_object_init(&dev->vk, &shaders->base,
&tu_shaders_ops, obj_key_data, key_size);
return shaders;
}
static bool
tu_shaders_serialize(struct vk_pipeline_cache_object *object,
struct blob *blob)
{
struct tu_compiled_shaders *shaders =
container_of(object, struct tu_compiled_shaders, base);
blob_write_bytes(blob, shaders->push_consts, sizeof(shaders->push_consts));
blob_write_uint8(blob, shaders->active_desc_sets);
blob_write_uint8(blob, shaders->multi_pos_output);
for (unsigned i = 0; i < ARRAY_SIZE(shaders->variants); i++) {
if (shaders->variants[i]) {
blob_write_uint8(blob, 1);
ir3_store_variant(blob, shaders->variants[i]);
} else {
blob_write_uint8(blob, 0);
}
}
return true;
}
static struct vk_pipeline_cache_object *
tu_shaders_deserialize(struct vk_device *_device,
const void *key_data, size_t key_size,
struct blob_reader *blob)
{
struct tu_device *dev = container_of(_device, struct tu_device, vk);
struct tu_compiled_shaders *shaders =
tu_shaders_init(dev, key_data, key_size);
if (!shaders)
return NULL;
blob_copy_bytes(blob, shaders->push_consts, sizeof(shaders->push_consts));
shaders->active_desc_sets = blob_read_uint8(blob);
shaders->multi_pos_output = blob_read_uint8(blob);
for (unsigned i = 0; i < ARRAY_SIZE(shaders->variants); i++) {
bool has_shader = blob_read_uint8(blob);
if (has_shader) {
shaders->variants[i] = ir3_retrieve_variant(blob, dev->compiler, NULL);
}
}
return &shaders->base;
}
static struct tu_compiled_shaders *
tu_pipeline_cache_lookup(struct vk_pipeline_cache *cache,
const void *key_data, size_t key_size,
bool *application_cache_hit)
{
struct vk_pipeline_cache_object *object =
vk_pipeline_cache_lookup_object(cache, key_data, key_size,
&tu_shaders_ops, application_cache_hit);
if (object)
return container_of(object, struct tu_compiled_shaders, base);
else
return NULL;
}
static struct tu_compiled_shaders *
tu_pipeline_cache_insert(struct vk_pipeline_cache *cache,
struct tu_compiled_shaders *shaders)
{
struct vk_pipeline_cache_object *object =
vk_pipeline_cache_add_object(cache, &shaders->base);
return container_of(object, struct tu_compiled_shaders, base);
}
static VkResult
tu_pipeline_builder_compile_shaders(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
VkResult result = VK_SUCCESS;
const struct ir3_compiler *compiler = builder->device->compiler;
const VkPipelineShaderStageCreateInfo *stage_infos[MESA_SHADER_STAGES] = {
NULL
};
VkPipelineCreationFeedback pipeline_feedback = {
.flags = VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT,
};
VkPipelineCreationFeedback stage_feedbacks[MESA_SHADER_STAGES] = { 0 };
int64_t pipeline_start = os_time_get_nano();
const VkPipelineCreationFeedbackCreateInfo *creation_feedback =
vk_find_struct_const(builder->create_info->pNext, PIPELINE_CREATION_FEEDBACK_CREATE_INFO);
for (uint32_t i = 0; i < builder->create_info->stageCount; i++) {
gl_shader_stage stage =
vk_to_mesa_shader_stage(builder->create_info->pStages[i].stage);
stage_infos[stage] = &builder->create_info->pStages[i];
}
if (tu6_shared_constants_enable(builder->layout, builder->device->compiler)) {
pipeline->shared_consts = (struct tu_push_constant_range) {
.lo = 0,
.dwords = builder->layout->push_constant_size / 4,
};
}
struct tu_shader_key keys[ARRAY_SIZE(stage_infos)] = { };
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(keys); stage++) {
tu_shader_key_init(&keys[stage], stage_infos[stage], builder->device);
}
struct ir3_shader_key ir3_key = {};
tu_pipeline_shader_key_init(&ir3_key, pipeline, builder->create_info);
keys[MESA_SHADER_VERTEX].multiview_mask = builder->multiview_mask;
keys[MESA_SHADER_FRAGMENT].multiview_mask = builder->multiview_mask;
keys[MESA_SHADER_FRAGMENT].force_sample_interp = ir3_key.sample_shading;
unsigned char pipeline_sha1[20];
tu_hash_shaders(pipeline_sha1, stage_infos, builder->layout, keys, &ir3_key, compiler);
const bool executable_info = builder->create_info->flags &
VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR;
char *nir_initial_disasm[ARRAY_SIZE(stage_infos)] = { NULL };
struct tu_compiled_shaders *compiled_shaders;
if (!executable_info) {
bool application_cache_hit = false;
compiled_shaders =
tu_pipeline_cache_lookup(builder->cache, &pipeline_sha1,
sizeof(pipeline_sha1),
&application_cache_hit);
if (application_cache_hit && builder->cache != builder->device->mem_cache) {
pipeline_feedback.flags |=
VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT;
}
if (compiled_shaders)
goto done;
}
if (builder->create_info->flags &
VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT) {
return VK_PIPELINE_COMPILE_REQUIRED;
}
nir_shader *nir[ARRAY_SIZE(stage_infos)] = { NULL };
struct tu_shader *shaders[ARRAY_SIZE(nir)] = { NULL };
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(nir); stage++) {
const VkPipelineShaderStageCreateInfo *stage_info = stage_infos[stage];
if (!stage_info)
continue;
int64_t stage_start = os_time_get_nano();
nir[stage] = tu_spirv_to_nir(builder->device, builder->mem_ctx, stage_info, stage);
if (!nir[stage]) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
stage_feedbacks[stage].flags = VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT;
stage_feedbacks[stage].duration += os_time_get_nano() - stage_start;
}
if (!nir[MESA_SHADER_FRAGMENT]) {
const nir_shader_compiler_options *nir_options =
ir3_get_compiler_options(builder->device->compiler);
nir_builder fs_b = nir_builder_init_simple_shader(MESA_SHADER_FRAGMENT,
nir_options,
"noop_fs");
nir[MESA_SHADER_FRAGMENT] = fs_b.shader;
}
if (executable_info) {
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(nir); stage++) {
if (!nir[stage])
continue;
nir_initial_disasm[stage] =
nir_shader_as_str(nir[stage], pipeline->executables_mem_ctx);
}
}
tu_link_shaders(builder, nir, ARRAY_SIZE(nir));
uint32_t desc_sets = 0;
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(nir); stage++) {
if (!nir[stage])
continue;
int64_t stage_start = os_time_get_nano();
struct tu_shader *shader =
tu_shader_create(builder->device, nir[stage], &keys[stage],
builder->layout, builder->alloc);
if (!shader) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
/* In SPIR-V generated from GLSL, the primitive mode is specified in the
* tessellation evaluation shader, but in SPIR-V generated from HLSL,
* the mode is specified in the tessellation control shader. */
if ((stage == MESA_SHADER_TESS_EVAL || stage == MESA_SHADER_TESS_CTRL) &&
ir3_key.tessellation == IR3_TESS_NONE) {
ir3_key.tessellation = tu6_get_tessmode(shader);
}
if (stage > MESA_SHADER_TESS_CTRL) {
if (stage == MESA_SHADER_FRAGMENT) {
ir3_key.tcs_store_primid = ir3_key.tcs_store_primid ||
(nir[stage]->info.inputs_read & (1ull << VARYING_SLOT_PRIMITIVE_ID));
} else {
ir3_key.tcs_store_primid = ir3_key.tcs_store_primid ||
BITSET_TEST(nir[stage]->info.system_values_read, SYSTEM_VALUE_PRIMITIVE_ID);
}
}
/* Keep track of the status of each shader's active descriptor sets,
* which is set in tu_lower_io. */
desc_sets |= shader->active_desc_sets;
shaders[stage] = shader;
stage_feedbacks[stage].duration += os_time_get_nano() - stage_start;
}
struct tu_shader *last_shader = shaders[MESA_SHADER_GEOMETRY];
if (!last_shader)
last_shader = shaders[MESA_SHADER_TESS_EVAL];
if (!last_shader)
last_shader = shaders[MESA_SHADER_VERTEX];
uint64_t outputs_written = last_shader->ir3_shader->nir->info.outputs_written;
ir3_key.layer_zero = !(outputs_written & VARYING_BIT_LAYER);
ir3_key.view_zero = !(outputs_written & VARYING_BIT_VIEWPORT);
compiled_shaders =
tu_shaders_init(builder->device, &pipeline_sha1, sizeof(pipeline_sha1));
if (!compiled_shaders) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
compiled_shaders->active_desc_sets = desc_sets;
compiled_shaders->multi_pos_output =
shaders[MESA_SHADER_VERTEX]->multi_pos_output;
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(shaders); stage++) {
if (!shaders[stage])
continue;
int64_t stage_start = os_time_get_nano();
compiled_shaders->variants[stage] =
ir3_shader_create_variant(shaders[stage]->ir3_shader, &ir3_key,
executable_info);
if (!compiled_shaders->variants[stage])
return VK_ERROR_OUT_OF_HOST_MEMORY;
compiled_shaders->push_consts[stage] = shaders[stage]->push_consts;
stage_feedbacks[stage].duration += os_time_get_nano() - stage_start;
}
compiled_shaders->shared_consts = pipeline->shared_consts;
uint32_t safe_constlens = ir3_trim_constlen(compiled_shaders->variants, compiler);
ir3_key.safe_constlen = true;
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(shaders); stage++) {
if (!shaders[stage])
continue;
if (safe_constlens & (1 << stage)) {
int64_t stage_start = os_time_get_nano();
ralloc_free(compiled_shaders->variants[stage]);
compiled_shaders->variants[stage] =
ir3_shader_create_variant(shaders[stage]->ir3_shader, &ir3_key,
executable_info);
if (!compiled_shaders->variants[stage]) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
stage_feedbacks[stage].duration += os_time_get_nano() - stage_start;
}
}
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(nir); stage++) {
if (shaders[stage]) {
tu_shader_destroy(builder->device, shaders[stage], builder->alloc);
}
}
compiled_shaders =
tu_pipeline_cache_insert(builder->cache, compiled_shaders);
done:
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(nir); stage++) {
if (compiled_shaders->variants[stage]) {
tu_append_executable(pipeline, compiled_shaders->variants[stage],
nir_initial_disasm[stage]);
}
}
struct ir3_shader_variant *vs =
compiled_shaders->variants[MESA_SHADER_VERTEX];
struct ir3_shader_variant *variant;
if (!vs->stream_output.num_outputs && ir3_has_binning_vs(&vs->key)) {
tu_append_executable(pipeline, vs->binning, NULL);
variant = vs->binning;
} else {
variant = vs;
}
builder->binning_variant = variant;
builder->shaders = compiled_shaders;
pipeline->active_desc_sets = compiled_shaders->active_desc_sets;
if (compiled_shaders->variants[MESA_SHADER_TESS_CTRL]) {
pipeline->tess.patch_type =
compiled_shaders->variants[MESA_SHADER_TESS_CTRL]->key.tessellation;
}
pipeline_feedback.duration = os_time_get_nano() - pipeline_start;
if (creation_feedback) {
*creation_feedback->pPipelineCreationFeedback = pipeline_feedback;
assert(builder->create_info->stageCount ==
creation_feedback->pipelineStageCreationFeedbackCount);
for (uint32_t i = 0; i < builder->create_info->stageCount; i++) {
gl_shader_stage s =
vk_to_mesa_shader_stage(builder->create_info->pStages[i].stage);
creation_feedback->pPipelineStageCreationFeedbacks[i] = stage_feedbacks[s];
}
}
return VK_SUCCESS;
fail:
for (gl_shader_stage stage = MESA_SHADER_VERTEX;
stage < ARRAY_SIZE(nir); stage++) {
if (shaders[stage]) {
tu_shader_destroy(builder->device, shaders[stage], builder->alloc);
}
}
if (compiled_shaders)
vk_pipeline_cache_object_unref(&compiled_shaders->base);
return result;
}
static void
tu_pipeline_builder_parse_dynamic(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
const VkPipelineDynamicStateCreateInfo *dynamic_info =
builder->create_info->pDynamicState;
pipeline->gras_su_cntl_mask = ~0u;
pipeline->rb_depth_cntl_mask = ~0u;
pipeline->rb_stencil_cntl_mask = ~0u;
pipeline->pc_raster_cntl_mask = ~0u;
pipeline->vpc_unknown_9107_mask = ~0u;
pipeline->sp_blend_cntl_mask = ~0u;
pipeline->rb_blend_cntl_mask = ~0u;
pipeline->rb_mrt_control_mask = ~0u;
if (!dynamic_info)
return;
for (uint32_t i = 0; i < dynamic_info->dynamicStateCount; i++) {
VkDynamicState state = dynamic_info->pDynamicStates[i];
switch (state) {
case VK_DYNAMIC_STATE_VIEWPORT ... VK_DYNAMIC_STATE_STENCIL_REFERENCE:
if (state == VK_DYNAMIC_STATE_LINE_WIDTH)
pipeline->gras_su_cntl_mask &= ~A6XX_GRAS_SU_CNTL_LINEHALFWIDTH__MASK;
pipeline->dynamic_state_mask |= BIT(state);
break;
case VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT:
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_SAMPLE_LOCATIONS);
break;
case VK_DYNAMIC_STATE_CULL_MODE:
pipeline->gras_su_cntl_mask &=
~(A6XX_GRAS_SU_CNTL_CULL_BACK | A6XX_GRAS_SU_CNTL_CULL_FRONT);
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_GRAS_SU_CNTL);
break;
case VK_DYNAMIC_STATE_FRONT_FACE:
pipeline->gras_su_cntl_mask &= ~A6XX_GRAS_SU_CNTL_FRONT_CW;
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_GRAS_SU_CNTL);
break;
case VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY:
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY);
break;
case VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE:
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_VB_STRIDE);
break;
case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT:
pipeline->dynamic_state_mask |= BIT(VK_DYNAMIC_STATE_VIEWPORT);
break;
case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT:
pipeline->dynamic_state_mask |= BIT(VK_DYNAMIC_STATE_SCISSOR);
break;
case VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE:
pipeline->rb_depth_cntl_mask &=
~(A6XX_RB_DEPTH_CNTL_Z_TEST_ENABLE | A6XX_RB_DEPTH_CNTL_Z_READ_ENABLE);
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_RB_DEPTH_CNTL);
break;
case VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE:
pipeline->rb_depth_cntl_mask &= ~A6XX_RB_DEPTH_CNTL_Z_WRITE_ENABLE;
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_RB_DEPTH_CNTL);
break;
case VK_DYNAMIC_STATE_DEPTH_COMPARE_OP:
pipeline->rb_depth_cntl_mask &= ~A6XX_RB_DEPTH_CNTL_ZFUNC__MASK;
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_RB_DEPTH_CNTL);
break;
case VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE:
pipeline->rb_depth_cntl_mask &=
~(A6XX_RB_DEPTH_CNTL_Z_BOUNDS_ENABLE | A6XX_RB_DEPTH_CNTL_Z_READ_ENABLE);
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_RB_DEPTH_CNTL);
break;
case VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE:
pipeline->rb_stencil_cntl_mask &= ~(A6XX_RB_STENCIL_CONTROL_STENCIL_ENABLE |
A6XX_RB_STENCIL_CONTROL_STENCIL_ENABLE_BF |
A6XX_RB_STENCIL_CONTROL_STENCIL_READ);
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_RB_STENCIL_CNTL);
break;
case VK_DYNAMIC_STATE_STENCIL_OP:
pipeline->rb_stencil_cntl_mask &= ~(A6XX_RB_STENCIL_CONTROL_FUNC__MASK |
A6XX_RB_STENCIL_CONTROL_FAIL__MASK |
A6XX_RB_STENCIL_CONTROL_ZPASS__MASK |
A6XX_RB_STENCIL_CONTROL_ZFAIL__MASK |
A6XX_RB_STENCIL_CONTROL_FUNC_BF__MASK |
A6XX_RB_STENCIL_CONTROL_FAIL_BF__MASK |
A6XX_RB_STENCIL_CONTROL_ZPASS_BF__MASK |
A6XX_RB_STENCIL_CONTROL_ZFAIL_BF__MASK);
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_RB_STENCIL_CNTL);
break;
case VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE:
pipeline->gras_su_cntl_mask &= ~A6XX_GRAS_SU_CNTL_POLY_OFFSET;
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_GRAS_SU_CNTL);
break;
case VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE:
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE);
break;
case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE:
pipeline->pc_raster_cntl_mask &= ~A6XX_PC_RASTER_CNTL_DISCARD;
pipeline->vpc_unknown_9107_mask &= ~A6XX_VPC_UNKNOWN_9107_RASTER_DISCARD;
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_RASTERIZER_DISCARD);
break;
case VK_DYNAMIC_STATE_LOGIC_OP_EXT:
pipeline->sp_blend_cntl_mask &= ~A6XX_SP_BLEND_CNTL_ENABLE_BLEND__MASK;
pipeline->rb_blend_cntl_mask &= ~A6XX_RB_BLEND_CNTL_ENABLE_BLEND__MASK;
pipeline->rb_mrt_control_mask &= ~A6XX_RB_MRT_CONTROL_ROP_CODE__MASK;
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_BLEND);
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_LOGIC_OP);
break;
case VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT:
pipeline->sp_blend_cntl_mask &= ~A6XX_SP_BLEND_CNTL_ENABLE_BLEND__MASK;
pipeline->rb_blend_cntl_mask &= ~A6XX_RB_BLEND_CNTL_ENABLE_BLEND__MASK;
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_BLEND);
/* Dynamic color write enable doesn't directly change any of the
* registers, but it causes us to make some of the registers 0, so we
* set this dynamic state instead of making the register dynamic.
*/
pipeline->dynamic_state_mask |= BIT(TU_DYNAMIC_STATE_COLOR_WRITE_ENABLE);
break;
default:
assert(!"unsupported dynamic state");
break;
}
}
}
static void
tu_pipeline_set_linkage(struct tu_program_descriptor_linkage *link,
struct tu_push_constant_range *push_consts,
struct ir3_shader_variant *v)
{
link->const_state = *ir3_const_state(v);
link->constlen = v->constlen;
link->push_consts = *push_consts;
}
static void
tu_pipeline_builder_parse_shader_stages(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
struct tu_cs prog_cs;
/* Emit HLSQ_xS_CNTL/HLSQ_SP_xS_CONFIG *first*, before emitting anything
* else that could depend on that state (like push constants)
*
* Note also that this always uses the full VS even in binning pass. The
* binning pass variant has the same const layout as the full VS, and
* the constlen for the VS will be the same or greater than the constlen
* for the binning pass variant. It is required that the constlen state
* matches between binning and draw passes, as some parts of the push
* consts are emitted in state groups that are shared between the binning
* and draw passes.
*/
tu_cs_begin_sub_stream(&pipeline->cs, 512, &prog_cs);
tu6_emit_program_config(&prog_cs, builder);
pipeline->program.config_state = tu_cs_end_draw_state(&pipeline->cs, &prog_cs);
tu_cs_begin_sub_stream(&pipeline->cs, 512 + builder->additional_cs_reserve_size, &prog_cs);
tu6_emit_program(&prog_cs, builder, false, pipeline);
pipeline->program.state = tu_cs_end_draw_state(&pipeline->cs, &prog_cs);
tu_cs_begin_sub_stream(&pipeline->cs, 512 + builder->additional_cs_reserve_size, &prog_cs);
tu6_emit_program(&prog_cs, builder, true, pipeline);
pipeline->program.binning_state = tu_cs_end_draw_state(&pipeline->cs, &prog_cs);
VkShaderStageFlags stages = 0;
for (unsigned i = 0; i < builder->create_info->stageCount; i++) {
stages |= builder->create_info->pStages[i].stage;
}
pipeline->active_stages = stages;
for (unsigned i = 0; i < ARRAY_SIZE(builder->shaders->variants); i++) {
if (!builder->shaders->variants[i])
continue;
tu_pipeline_set_linkage(&pipeline->program.link[i],
&builder->shaders->push_consts[i],
builder->shaders->variants[i]);
}
}
static void
tu_pipeline_builder_parse_vertex_input(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
const VkPipelineVertexInputStateCreateInfo *vi_info =
builder->create_info->pVertexInputState;
const struct ir3_shader_variant *vs = builder->shaders->variants[MESA_SHADER_VERTEX];
const struct ir3_shader_variant *bs = builder->binning_variant;
/* Bindings may contain holes */
for (unsigned i = 0; i < vi_info->vertexBindingDescriptionCount; i++) {
pipeline->num_vbs =
MAX2(pipeline->num_vbs, vi_info->pVertexBindingDescriptions[i].binding + 1);
}
tu6_emit_vertex_input(pipeline, &pipeline->vi.state, vs, vi_info);
if (bs)
tu6_emit_vertex_input(pipeline, &pipeline->vi.binning_state, bs, vi_info);
}
static void
tu_pipeline_builder_parse_input_assembly(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
const VkPipelineInputAssemblyStateCreateInfo *ia_info =
builder->create_info->pInputAssemblyState;
pipeline->ia.primtype = tu6_primtype(ia_info->topology);
pipeline->ia.primitive_restart = ia_info->primitiveRestartEnable;
}
static bool
tu_pipeline_static_state(struct tu_pipeline *pipeline, struct tu_cs *cs,
uint32_t id, uint32_t size)
{
assert(id < ARRAY_SIZE(pipeline->dynamic_state));
if (pipeline->dynamic_state_mask & BIT(id))
return false;
pipeline->dynamic_state[id] = tu_cs_draw_state(&pipeline->cs, cs, size);
return true;
}
static void
tu_pipeline_builder_parse_tessellation(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
if (!(pipeline->active_stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ||
!(pipeline->active_stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))
return;
const VkPipelineTessellationStateCreateInfo *tess_info =
builder->create_info->pTessellationState;
assert(pipeline->ia.primtype == DI_PT_PATCHES0);
assert(tess_info->patchControlPoints <= 32);
pipeline->ia.primtype += tess_info->patchControlPoints;
const VkPipelineTessellationDomainOriginStateCreateInfo *domain_info =
vk_find_struct_const(tess_info->pNext, PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO);
pipeline->tess.upper_left_domain_origin = !domain_info ||
domain_info->domainOrigin == VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT;
const struct ir3_shader_variant *hs = builder->shaders->variants[MESA_SHADER_TESS_CTRL];
pipeline->tess.param_stride = hs->output_size * 4;
}
static void
tu_pipeline_builder_parse_viewport(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
/* The spec says:
*
* pViewportState is a pointer to an instance of the
* VkPipelineViewportStateCreateInfo structure, and is ignored if the
* pipeline has rasterization disabled."
*
* We leave the relevant registers stale in that case.
*/
if (builder->rasterizer_discard)
return;
const VkPipelineViewportStateCreateInfo *vp_info =
builder->create_info->pViewportState;
const VkPipelineViewportDepthClipControlCreateInfoEXT *depth_clip_info =
vk_find_struct_const(vp_info->pNext, PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT);
pipeline->z_negative_one_to_one = depth_clip_info ? depth_clip_info->negativeOneToOne : false;
struct tu_cs cs;
if (tu_pipeline_static_state(pipeline, &cs, VK_DYNAMIC_STATE_VIEWPORT, 8 + 10 * vp_info->viewportCount))
tu6_emit_viewport(&cs, vp_info->pViewports, vp_info->viewportCount, pipeline->z_negative_one_to_one);
if (tu_pipeline_static_state(pipeline, &cs, VK_DYNAMIC_STATE_SCISSOR, 1 + 2 * vp_info->scissorCount))
tu6_emit_scissor(&cs, vp_info->pScissors, vp_info->scissorCount);
}
static void
tu_pipeline_builder_parse_rasterization(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
const VkPipelineRasterizationStateCreateInfo *rast_info =
builder->create_info->pRasterizationState;
enum a6xx_polygon_mode mode = tu6_polygon_mode(rast_info->polygonMode);
builder->depth_clip_disable = rast_info->depthClampEnable;
const VkPipelineRasterizationDepthClipStateCreateInfoEXT *depth_clip_state =
vk_find_struct_const(rast_info, PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT);
if (depth_clip_state)
builder->depth_clip_disable = !depth_clip_state->depthClipEnable;
pipeline->line_mode = RECTANGULAR;
if (tu6_primtype_line(pipeline->ia.primtype) ||
(tu6_primtype_patches(pipeline->ia.primtype) &&
pipeline->tess.patch_type == IR3_TESS_ISOLINES)) {
const VkPipelineRasterizationLineStateCreateInfoEXT *rast_line_state =
vk_find_struct_const(rast_info->pNext,
PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT);
if (rast_line_state && rast_line_state->lineRasterizationMode ==
VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT) {
pipeline->line_mode = BRESENHAM;
}
}
struct tu_cs cs;
uint32_t cs_size = 9 +
(builder->device->physical_device->info->a6xx.has_shading_rate ? 8 : 0) +
(builder->emit_msaa_state ? 11 : 0);
pipeline->rast_state = tu_cs_draw_state(&pipeline->cs, &cs, cs_size);
tu_cs_emit_regs(&cs,
A6XX_GRAS_CL_CNTL(
.znear_clip_disable = builder->depth_clip_disable,
.zfar_clip_disable = builder->depth_clip_disable,
/* TODO should this be depth_clip_disable instead? */
.unk5 = rast_info->depthClampEnable,
.zero_gb_scale_z = pipeline->z_negative_one_to_one ? 0 : 1,
.vp_clip_code_ignore = 1));
tu_cs_emit_regs(&cs,
A6XX_VPC_POLYGON_MODE(mode));
tu_cs_emit_regs(&cs,
A6XX_PC_POLYGON_MODE(mode));
/* move to hw ctx init? */
tu_cs_emit_regs(&cs,
A6XX_GRAS_SU_POINT_MINMAX(.min = 1.0f / 16.0f, .max = 4092.0f),
A6XX_GRAS_SU_POINT_SIZE(1.0f));
if (builder->device->physical_device->info->a6xx.has_shading_rate) {
tu_cs_emit_regs(&cs, A6XX_RB_UNKNOWN_8A00());
tu_cs_emit_regs(&cs, A6XX_RB_UNKNOWN_8A10());
tu_cs_emit_regs(&cs, A6XX_RB_UNKNOWN_8A20());
tu_cs_emit_regs(&cs, A6XX_RB_UNKNOWN_8A30());
}
/* If samples count couldn't be devised from the subpass, we should emit it here.
* It happens when subpass doesn't use any color/depth attachment.
*/
if (builder->emit_msaa_state)
tu6_emit_msaa(&cs, builder->samples, pipeline->line_mode);
const VkPipelineRasterizationStateStreamCreateInfoEXT *stream_info =
vk_find_struct_const(rast_info->pNext,
PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT);
unsigned stream = stream_info ? stream_info->rasterizationStream : 0;
pipeline->pc_raster_cntl = A6XX_PC_RASTER_CNTL_STREAM(stream);
pipeline->vpc_unknown_9107 = 0;
if (rast_info->rasterizerDiscardEnable) {
pipeline->pc_raster_cntl |= A6XX_PC_RASTER_CNTL_DISCARD;
pipeline->vpc_unknown_9107 |= A6XX_VPC_UNKNOWN_9107_RASTER_DISCARD;
}
if (tu_pipeline_static_state(pipeline, &cs, TU_DYNAMIC_STATE_RASTERIZER_DISCARD, 4)) {
tu_cs_emit_regs(&cs, A6XX_PC_RASTER_CNTL(.dword = pipeline->pc_raster_cntl));
tu_cs_emit_regs(&cs, A6XX_VPC_UNKNOWN_9107(.dword = pipeline->vpc_unknown_9107));
}
pipeline->gras_su_cntl =
tu6_gras_su_cntl(rast_info, pipeline->line_mode, builder->multiview_mask != 0);
if (tu_pipeline_static_state(pipeline, &cs, TU_DYNAMIC_STATE_GRAS_SU_CNTL, 2))
tu_cs_emit_regs(&cs, A6XX_GRAS_SU_CNTL(.dword = pipeline->gras_su_cntl));
if (tu_pipeline_static_state(pipeline, &cs, VK_DYNAMIC_STATE_DEPTH_BIAS, 4)) {
tu6_emit_depth_bias(&cs, rast_info->depthBiasConstantFactor,
rast_info->depthBiasClamp,
rast_info->depthBiasSlopeFactor);
}
const struct VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *provoking_vtx_state =
vk_find_struct_const(rast_info->pNext, PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT);
pipeline->provoking_vertex_last = provoking_vtx_state &&
provoking_vtx_state->provokingVertexMode == VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
}
static void
tu_pipeline_builder_parse_depth_stencil(struct tu_pipeline_builder *builder,
struct tu_pipeline *pipeline)
{
/* The spec says:
*
* pDepthStencilState is a pointer to an instance of the
* VkPipelineDepthStencilStateCreateInfo structure, and is ignored if
* the pipeline has rasterization disabled or if the subpass of the
* render pass the pipeline is created against does not use a
* depth/stencil attachment.
*/
const VkPipelineDepthStencilStateCreateInfo *ds_info =
builder->create_info->pDepthStencilState;
const enum pipe_format pipe_format =
vk_format_to_pipe_format(builder->depth_attachment_format);
uint32_t rb_depth_cntl = 0, rb_stencil_cntl = 0;
struct tu_cs cs;
if (builder->depth_attachment_format != VK_FORMAT_UNDEFINED &&
builder->depth_attachment_format != VK_FORMAT_S8_UINT) {
if (ds_info->depthTestEnable) {
rb_depth_cntl |=
A6XX_RB_DEPTH_CNTL_Z_TEST_ENABLE |
A6XX_RB_DEPTH_CNTL_ZFUNC(tu6_compare_func(ds_info->depthCompareOp)) |
A6XX_RB_DEPTH_CNTL_Z_READ_ENABLE; /* TODO: don't set for ALWAYS/NEVER */
if (builder->depth_clip_disable)
rb_depth_cntl |= A6XX_RB_DEPTH_CNTL_Z_CLIP_DISABLE;
if (ds_info->depthWriteEnable)
rb_depth_cntl |= A6XX_RB_DEPTH_CNTL_Z_WRITE_ENABLE;
}
if (ds_info->depthBoundsTestEnable)
rb_depth_cntl |= A6XX_RB_DEPTH_CNTL_Z_BOUNDS_ENABLE | A6XX_RB_DEPTH_CNTL_Z_READ_ENABLE;
if (ds_info->depthBoundsTestEnable && !ds_info->depthTestEnable)
tu6_apply_depth_bounds_workaround(builder->device, &rb_depth_cntl);
pipeline->depth_cpp_per_sample = util_format_get_component_bits(
pipe_format, UTIL_FORMAT_COLORSPACE_ZS, 0) / 8;
} else {
/* if RB_DEPTH_CNTL is set dynamically, we need to make sure it is set
* to 0 when this pipeline is used, as enabling depth test when there
* is no depth attachment is a problem (at least for the S8_UINT case)
*/
if (pipeline->dynamic_state_mask & BIT(TU_DYNAMIC_STATE_RB_DEPTH_CNTL))
pipeline->rb_depth_cntl_disable = true;
}
if (builder->depth_attachment_format != VK_FORMAT_UNDEFINED) {
const VkStencilOpState *front = &ds_info->front;
const VkStencilOpState *back = &ds_info->back;
rb_stencil_cntl |=
A6XX_RB_STENCIL_CONTROL_FUNC(tu6_compare_func(front->compareOp)) |
A6XX_RB_STENCIL_CONTROL_FAIL(tu6_stencil_op(front->failOp)) |
A6XX_RB_STENCIL_CONTROL_ZPASS(tu6_stencil_op(front->passOp)) |
A6XX_RB_STENCIL_CONTROL_ZFAIL(tu6_stencil_op(front->depthFailOp)) |
A6XX_RB_STENCIL_CONTROL_FUNC_BF(tu6_compare_func(back->compareOp)) |
A6XX_RB_STENCIL_CONTROL_FAIL_BF(tu6_stencil_op(back->failOp)) |
A6XX_RB_STENCIL_CONTROL_ZPASS_BF(tu6_stencil_op(back->passOp)) |
A6XX_RB_STENCIL_CONTROL_ZFAIL_BF(tu6_stencil_op(back->depthFailOp));
if (ds_info->stencilTestEnable) {
rb_stencil_cntl |=
A6XX_RB_STENCIL_CONTROL_STENCIL_ENABLE |
A6XX_RB_STENCIL_CONTROL_STENCIL_ENABLE_BF |
A6XX_RB_STENCIL_CONTROL_STENCIL_READ;
}
pipeline->stencil_cpp_per_sample = util_format_get_component_bits(
pipe_format, UTIL_FORMAT_COLORSPACE_ZS, 1) / 8;
}
if (tu_pipeline_static_state(pipeline, &cs, TU_DYNAMIC_STATE_RB_DEPTH_CNTL, 2)) {
tu_cs_emit_pkt4(&cs, REG_A6XX_RB_DEPTH_CNTL, 1);
tu_cs_emit(&cs, rb_depth_cntl);
}
pipeline->rb_depth_cntl = rb_depth_cntl;
if (tu_pipeline_static_state(pipeline, &cs, TU_DYNAMIC_STATE_RB_STENCIL_CNTL, 2)) {
tu_cs_emit_pkt4(&cs, REG_A6XX_RB_STENCIL_CONTROL, 1);
tu_cs_emit(&cs, rb_stencil_cntl);
}
pipeline->rb_stencil_cntl = rb_stencil_cntl;
/* the remaining draw states arent used if there is no d/s, leave them empty */
if (builder->depth_attachment_format == VK_FORMAT_UNDEFINED)
return;
if (tu_pipeline_static_state(pipeline, &cs, VK_DYNAMIC_STATE_DEPTH_BOUNDS, 3)) {
tu_cs_emit_regs(&cs,
A6XX_RB_Z_BOUNDS_MIN(ds_info->minDepthBounds),
A6XX_RB_Z_BOUNDS_MAX(ds_info->maxDepthBounds));
}
if (tu_pipeline_static_state(pipeline, &cs, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, 2)) {
tu_cs_emit_regs(&cs, A6XX_RB_STENCILMASK(.mask = ds_info->front.compareMask & 0xff,
.bfmask = ds_info->back.compareMask & 0xff));
}
if (tu_pipeline_static_state(pipeline, &cs, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, 2)) {
update_stencil_mask(&pipeline->stencil_wrmask, VK_STENCIL_FACE_FRONT_BIT, ds_info->front.writeMask);
update_stencil_mask(&pipeline->stencil_wrmask, VK_STENCIL_FACE_BACK_BIT, ds_info->back.writeMask);
tu_cs_emit_regs(&cs, A6XX_RB_STENCILWRMASK(.dword = pipeline->stencil_wrmask));
}
if (tu_pipeline_static_state(pipeline, &cs, VK_DYNAMIC_STATE_STENCIL_REFERENCE, 2)) {
tu_cs_emit_regs(&cs, A6XX_RB_STENCILREF(.ref = ds_info->front.reference & 0xff,
.bfref = ds_info->back.reference & 0xff));
}
if (builder->shaders->variants[MESA_SHADER_FRAGMENT]) {
const struct ir3_shader_variant *fs = builder->shaders->variants[MESA_SHADER_FRAGMENT];
if (fs->has_kill || fs->no_earlyz || fs->writes_pos) {
pipeline->lrz.force_disable_mask |= TU_LRZ_FORCE_DISABLE_WRITE;
}
if (fs->no_earlyz || fs->writes_pos) {
pipeline->lrz.force_disable_mask = TU_LRZ_FORCE_DISABLE_LRZ;
}
}
}
static void
tu_pipeline_builder_parse_multisample_and_color_blend(
struct tu_pipeline_builder *builder, struct tu_pipeline *pipeline)
{
/* The spec says:
*
* pMultisampleState is a pointer to an instance of the
* VkPipelineMultisampleStateCreateInfo, and is ignored if the pipeline
* has rasterization disabled.
*
* Also,
*
* pColorBlendState is a pointer to an instance of the
* VkPipelineColorBlendStateCreateInfo structure, and is ignored if the
* pipeline has rasterization disabled or if the subpass of the render
* pass the pipeline is created against does not use any color
* attachments.
*
* We leave the relevant registers stale when rasterization is disabled.
*/
if (builder->rasterizer_discard)
return;
static const VkPipelineColorBlendStateCreateInfo dummy_blend_info;
const VkPipelineMultisampleStateCreateInfo *msaa_info =
builder->create_info->pMultisampleState;
const VkPipelineColorBlendStateCreateInfo *blend_info =
builder->use_color_attachments ? builder->create_info->pColorBlendState
: &dummy_blend_info;
struct tu_cs cs;
tu6_emit_rb_mrt_controls(pipeline, blend_info,
builder->color_attachment_formats,
&pipeline->rop_reads_dst,
&pipeline->color_bandwidth_per_sample);
uint32_t blend_enable_mask =
pipeline->rop_reads_dst ? pipeline->color_write_enable : pipeline->blend_enable;
tu6_emit_blend_control(pipeline, blend_enable_mask,
builder->use_dual_src_blend, msaa_info);
if (tu_pipeline_static_state(pipeline, &cs, TU_DYNAMIC_STATE_BLEND,
blend_info->attachmentCount * 3 + 4)) {
tu6_emit_blend(&cs, pipeline);
assert(cs.cur == cs.end); /* validate draw state size */
}
/* Disable LRZ writes when blend or logic op that reads the destination is
* enabled, since the resulting pixel value from the blend-draw depends on
* an earlier draw, which LRZ in the draw pass could early-reject if the
* previous blend-enabled draw wrote LRZ.
*
* TODO: We need to disable LRZ writes only for the binning pass.
* Therefore, we need to emit it in a separate draw state. We keep
* it disabled for sysmem path as well for the moment.
*/
if (blend_enable_mask)
pipeline->lrz.force_disable_mask |= TU_LRZ_FORCE_DISABLE_WRITE;
for (int i = 0; i < blend_info->attachmentCount; i++) {
VkPipelineColorBlendAttachmentState blendAttachment = blend_info->pAttachments[i];
/* From the PoV of LRZ, having masked color channels is
* the same as having blend enabled, in that the draw will
* care about the fragments from an earlier draw.
*/
VkFormat format = builder->color_attachment_formats[i];
unsigned mask = MASK(vk_format_get_nr_components(format));
if (format != VK_FORMAT_UNDEFINED &&
((blendAttachment.colorWriteMask & mask) != mask ||
!(pipeline->color_write_enable & BIT(i)))) {
pipeline->lrz.force_disable_mask |= TU_LRZ_FORCE_DISABLE_WRITE;
}
}
if (tu_pipeline_static_state(pipeline, &cs, VK_DYNAMIC_STATE_BLEND_CONSTANTS, 5)) {
tu_cs_emit_pkt4(&cs, REG_A6XX_RB_BLEND_RED_F32, 4);
tu_cs_emit_array(&cs, (const uint32_t *) blend_info->blendConstants, 4);
}
const struct VkPipelineSampleLocationsStateCreateInfoEXT *sample_locations =
vk_find_struct_const(msaa_info->pNext, PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT);
const VkSampleLocationsInfoEXT *samp_loc = NULL;
if (sample_locations && sample_locations->sampleLocationsEnable)
samp_loc = &sample_locations->sampleLocationsInfo;
if (tu_pipeline_static_state(pipeline, &cs, TU_DYNAMIC_STATE_SAMPLE_LOCATIONS,
samp_loc ? 9 : 6)) {
tu6_emit_sample_locations(&cs, samp_loc);
}
}
static void
tu_pipeline_builder_parse_rasterization_order(
struct tu_pipeline_builder *builder, struct tu_pipeline *pipeline)
{
if (builder->rasterizer_discard)
return;
pipeline->subpass_feedback_loop_ds = builder->subpass_feedback_loop_ds;
const VkPipelineColorBlendStateCreateInfo *blend_info =
builder->create_info->pColorBlendState;
const VkPipelineDepthStencilStateCreateInfo *ds_info =
builder->create_info->pDepthStencilState;
if (builder->use_color_attachments) {
pipeline->raster_order_attachment_access =
blend_info->flags &
VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_ARM;
}
if (builder->depth_attachment_format != VK_FORMAT_UNDEFINED) {
pipeline->raster_order_attachment_access |=
ds_info->flags &
(VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM |
VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM);
}
if (unlikely(builder->device->physical_device->instance->debug_flags & TU_DEBUG_RAST_ORDER))
pipeline->raster_order_attachment_access = true;
/* VK_EXT_blend_operation_advanced would also require ordered access
* when implemented in the future.
*/
uint32_t sysmem_prim_mode = NO_FLUSH;
uint32_t gmem_prim_mode = NO_FLUSH;
if (pipeline->raster_order_attachment_access) {
/* VK_ARM_rasterization_order_attachment_access:
*
* This extension allow access to framebuffer attachments when used as
* both input and color attachments from one fragment to the next,
* in rasterization order, without explicit synchronization.
*/
sysmem_prim_mode = FLUSH_PER_OVERLAP_AND_OVERWRITE;
gmem_prim_mode = FLUSH_PER_OVERLAP;
} else {
/* If there is a feedback loop, then the shader can read the previous value
* of a pixel being written out. It can also write some components and then
* read different components without a barrier in between. This is a
* problem in sysmem mode with UBWC, because the main buffer and flags
* buffer can get out-of-sync if only one is flushed. We fix this by
* setting the SINGLE_PRIM_MODE field to the same value that the blob does
* for advanced_blend in sysmem mode if a feedback loop is detected.
*/
if (builder->subpass_feedback_loop_color ||
builder->subpass_feedback_loop_ds) {
sysmem_prim_mode = FLUSH_PER_OVERLAP_AND_OVERWRITE;
}
}
struct tu_cs cs;
pipeline->prim_order_state_gmem = tu_cs_draw_state(&pipeline->cs, &cs, 2);
tu_cs_emit_write_reg(&cs, REG_A6XX_GRAS_SC_CNTL,
A6XX_GRAS_SC_CNTL_CCUSINGLECACHELINESIZE(2) |
A6XX_GRAS_SC_CNTL_SINGLE_PRIM_MODE(gmem_prim_mode));
pipeline->prim_order_state_sysmem = tu_cs_draw_state(&pipeline->cs, &cs, 2);
tu_cs_emit_write_reg(&cs, REG_A6XX_GRAS_SC_CNTL,
A6XX_GRAS_SC_CNTL_CCUSINGLECACHELINESIZE(2) |
A6XX_GRAS_SC_CNTL_SINGLE_PRIM_MODE(sysmem_prim_mode));
}
static void
tu_pipeline_finish(struct tu_pipeline *pipeline,
struct tu_device *dev,
const VkAllocationCallbacks *alloc)
{
tu_cs_finish(&pipeline->cs);
pthread_mutex_lock(&dev->pipeline_mutex);
tu_suballoc_bo_free(&dev->pipeline_suballoc, &pipeline->bo);
pthread_mutex_unlock(&dev->pipeline_mutex);
if (pipeline->pvtmem_bo)
tu_bo_finish(dev, pipeline->pvtmem_bo);
ralloc_free(pipeline->executables_mem_ctx);
}
static VkResult
tu_pipeline_builder_build(struct tu_pipeline_builder *builder,
struct tu_pipeline **pipeline)
{
VkResult result;
*pipeline = vk_object_zalloc(&builder->device->vk, builder->alloc,
sizeof(**pipeline), VK_OBJECT_TYPE_PIPELINE);
if (!*pipeline)
return VK_ERROR_OUT_OF_HOST_MEMORY;
(*pipeline)->executables_mem_ctx = ralloc_context(NULL);
util_dynarray_init(&(*pipeline)->executables, (*pipeline)->executables_mem_ctx);
/* compile and upload shaders */
result = tu_pipeline_builder_compile_shaders(builder, *pipeline);
if (result != VK_SUCCESS) {
vk_object_free(&builder->device->vk, builder->alloc, *pipeline);
return result;
}
result = tu_pipeline_allocate_cs(builder->device, *pipeline,
builder->layout, builder, NULL);
if (result != VK_SUCCESS) {
vk_object_free(&builder->device->vk, builder->alloc, *pipeline);
return result;
}
for (uint32_t i = 0; i < ARRAY_SIZE(builder->shader_iova); i++)
builder->shader_iova[i] =
tu_upload_variant(*pipeline, builder->shaders->variants[i]);
builder->binning_vs_iova =
tu_upload_variant(*pipeline, builder->binning_variant);
/* Setup private memory. Note that because we're sharing the same private
* memory for all stages, all stages must use the same config, or else
* fibers from one stage might overwrite fibers in another.
*/
uint32_t pvtmem_size = 0;
bool per_wave = true;
for (uint32_t i = 0; i < ARRAY_SIZE(builder->shaders->variants); i++) {
if (builder->shaders->variants[i]) {
pvtmem_size = MAX2(pvtmem_size, builder->shaders->variants[i]->pvtmem_size);
if (!builder->shaders->variants[i]->pvtmem_per_wave)
per_wave = false;
}
}
if (builder->binning_variant) {
pvtmem_size = MAX2(pvtmem_size, builder->binning_variant->pvtmem_size);
if (!builder->binning_variant->pvtmem_per_wave)
per_wave = false;
}
result = tu_setup_pvtmem(builder->device, *pipeline, &builder->pvtmem,
pvtmem_size, per_wave);
if (result != VK_SUCCESS) {
vk_object_free(&builder->device->vk, builder->alloc, *pipeline);
return result;
}
tu_pipeline_builder_parse_dynamic(builder, *pipeline);
tu_pipeline_builder_parse_shader_stages(builder, *pipeline);
tu_pipeline_builder_parse_vertex_input(builder, *pipeline);
tu_pipeline_builder_parse_input_assembly(builder, *pipeline);
tu_pipeline_builder_parse_tessellation(builder, *pipeline);
tu_pipeline_builder_parse_viewport(builder, *pipeline);
tu_pipeline_builder_parse_rasterization(builder, *pipeline);
tu_pipeline_builder_parse_depth_stencil(builder, *pipeline);
tu_pipeline_builder_parse_multisample_and_color_blend(builder, *pipeline);
tu_pipeline_builder_parse_rasterization_order(builder, *pipeline);
tu6_emit_load_state(*pipeline, builder->layout, false);
return VK_SUCCESS;
}
static void
tu_pipeline_builder_finish(struct tu_pipeline_builder *builder)
{
if (builder->shaders)
vk_pipeline_cache_object_unref(&builder->shaders->base);
ralloc_free(builder->mem_ctx);
}
static void
tu_pipeline_builder_init_graphics(
struct tu_pipeline_builder *builder,
struct tu_device *dev,
struct vk_pipeline_cache *cache,
const VkGraphicsPipelineCreateInfo *create_info,
const VkAllocationCallbacks *alloc)
{
TU_FROM_HANDLE(tu_pipeline_layout, layout, create_info->layout);
*builder = (struct tu_pipeline_builder) {
.device = dev,
.mem_ctx = ralloc_context(NULL),
.cache = cache,
.create_info = create_info,
.alloc = alloc,
.layout = layout,
};
bool rasterizer_discard_dynamic = false;
if (create_info->pDynamicState) {
for (uint32_t i = 0; i < create_info->pDynamicState->dynamicStateCount; i++) {
if (create_info->pDynamicState->pDynamicStates[i] ==
VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE) {
rasterizer_discard_dynamic = true;
break;
}
}
}
const struct tu_render_pass *pass =
tu_render_pass_from_handle(create_info->renderPass);
const struct tu_subpass *subpass =
&pass->subpasses[create_info->subpass];
builder->subpass_raster_order_attachment_access =
subpass->raster_order_attachment_access;
builder->subpass_feedback_loop_color = subpass->feedback_loop_color;
builder->subpass_feedback_loop_ds = subpass->feedback_loop_ds;
builder->multiview_mask = subpass->multiview_mask;
builder->rasterizer_discard =
builder->create_info->pRasterizationState->rasterizerDiscardEnable &&
!rasterizer_discard_dynamic;
/* variableMultisampleRate support */
builder->emit_msaa_state = (subpass->samples == 0) && !builder->rasterizer_discard;
if (builder->rasterizer_discard) {
builder->samples = VK_SAMPLE_COUNT_1_BIT;
} else {
builder->samples = create_info->pMultisampleState->rasterizationSamples;
builder->alpha_to_coverage = create_info->pMultisampleState->alphaToCoverageEnable;
const uint32_t a = subpass->depth_stencil_attachment.attachment;
builder->depth_attachment_format = (a != VK_ATTACHMENT_UNUSED) ?
pass->attachments[a].format : VK_FORMAT_UNDEFINED;
assert(subpass->color_count == 0 ||
!create_info->pColorBlendState ||
subpass->color_count == create_info->pColorBlendState->attachmentCount);
builder->color_attachment_count = subpass->color_count;
for (uint32_t i = 0; i < subpass->color_count; i++) {
const uint32_t a = subpass->color_attachments[i].attachment;
if (a == VK_ATTACHMENT_UNUSED)
continue;
builder->color_attachment_formats[i] = pass->attachments[a].format;
builder->use_color_attachments = true;
builder->render_components |= 0xf << (i * 4);
}
if (tu_blend_state_is_dual_src(create_info->pColorBlendState)) {
builder->color_attachment_count++;
builder->use_dual_src_blend = true;
/* dual source blending has an extra fs output in the 2nd slot */
if (subpass->color_attachments[0].attachment != VK_ATTACHMENT_UNUSED)
builder->render_components |= 0xf << 4;
}
}
}
static VkResult
tu_graphics_pipeline_create(VkDevice device,
VkPipelineCache pipelineCache,
const VkGraphicsPipelineCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkPipeline *pPipeline)
{
TU_FROM_HANDLE(tu_device, dev, device);
TU_FROM_HANDLE(vk_pipeline_cache, cache, pipelineCache);
cache = cache ? cache : dev->mem_cache;
struct tu_pipeline_builder builder;
tu_pipeline_builder_init_graphics(&builder, dev, cache,
pCreateInfo, pAllocator);
struct tu_pipeline *pipeline = NULL;
VkResult result = tu_pipeline_builder_build(&builder, &pipeline);
tu_pipeline_builder_finish(&builder);
if (result == VK_SUCCESS)
*pPipeline = tu_pipeline_to_handle(pipeline);
else
*pPipeline = VK_NULL_HANDLE;
return result;
}
VKAPI_ATTR VkResult VKAPI_CALL
tu_CreateGraphicsPipelines(VkDevice device,
VkPipelineCache pipelineCache,
uint32_t count,
const VkGraphicsPipelineCreateInfo *pCreateInfos,
const VkAllocationCallbacks *pAllocator,
VkPipeline *pPipelines)
{
VkResult final_result = VK_SUCCESS;
uint32_t i = 0;
for (; i < count; i++) {
VkResult result = tu_graphics_pipeline_create(device, pipelineCache,
&pCreateInfos[i], pAllocator,
&pPipelines[i]);
if (result != VK_SUCCESS) {
final_result = result;
pPipelines[i] = VK_NULL_HANDLE;
if (pCreateInfos[i].flags &
VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT)
break;
}
}
for (; i < count; i++)
pPipelines[i] = VK_NULL_HANDLE;
return final_result;
}
static VkResult
tu_compute_pipeline_create(VkDevice device,
VkPipelineCache pipelineCache,
const VkComputePipelineCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkPipeline *pPipeline)
{
TU_FROM_HANDLE(tu_device, dev, device);
TU_FROM_HANDLE(vk_pipeline_cache, cache, pipelineCache);
TU_FROM_HANDLE(tu_pipeline_layout, layout, pCreateInfo->layout);
const VkPipelineShaderStageCreateInfo *stage_info = &pCreateInfo->stage;
VkResult result;
cache = cache ? cache : dev->mem_cache;
struct tu_pipeline *pipeline;
*pPipeline = VK_NULL_HANDLE;
VkPipelineCreationFeedback pipeline_feedback = {
.flags = VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT,
};
const VkPipelineCreationFeedbackCreateInfo *creation_feedback =
vk_find_struct_const(pCreateInfo->pNext, PIPELINE_CREATION_FEEDBACK_CREATE_INFO);
int64_t pipeline_start = os_time_get_nano();
pipeline = vk_object_zalloc(&dev->vk, pAllocator, sizeof(*pipeline),
VK_OBJECT_TYPE_PIPELINE);
if (!pipeline)
return VK_ERROR_OUT_OF_HOST_MEMORY;
pipeline->executables_mem_ctx = ralloc_context(NULL);
util_dynarray_init(&pipeline->executables, pipeline->executables_mem_ctx);
struct tu_shader_key key = { };
tu_shader_key_init(&key, stage_info, dev);
void *pipeline_mem_ctx = ralloc_context(NULL);
unsigned char pipeline_sha1[20];
tu_hash_compute(pipeline_sha1, stage_info, layout, &key, dev->compiler);
struct tu_compiled_shaders *compiled = NULL;
const bool executable_info = pCreateInfo->flags &
VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR;
bool application_cache_hit = false;
if (!executable_info) {
compiled =
tu_pipeline_cache_lookup(cache, pipeline_sha1, sizeof(pipeline_sha1),
&application_cache_hit);
}
if (application_cache_hit && cache != dev->mem_cache) {
pipeline_feedback.flags |=
VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT;
}
if (tu6_shared_constants_enable(layout, dev->compiler)) {
pipeline->shared_consts = (struct tu_push_constant_range) {
.lo = 0,
.dwords = layout->push_constant_size / 4,
};
}
char *nir_initial_disasm = NULL;
if (!compiled) {
if (pCreateInfo->flags &
VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT) {
result = VK_PIPELINE_COMPILE_REQUIRED;
goto fail;
}
struct ir3_shader_key ir3_key = {};
nir_shader *nir = tu_spirv_to_nir(dev, pipeline_mem_ctx, stage_info,
MESA_SHADER_COMPUTE);
nir_initial_disasm = executable_info ?
nir_shader_as_str(nir, pipeline->executables_mem_ctx) : NULL;
struct tu_shader *shader =
tu_shader_create(dev, nir, &key, layout, pAllocator);
if (!shader) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
compiled = tu_shaders_init(dev, &pipeline_sha1, sizeof(pipeline_sha1));
if (!compiled) {
tu_shader_destroy(dev, shader, pAllocator);
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
compiled->active_desc_sets = shader->active_desc_sets;
compiled->push_consts[MESA_SHADER_COMPUTE] = shader->push_consts;
compiled->shared_consts = pipeline->shared_consts;
struct ir3_shader_variant *v =
ir3_shader_create_variant(shader->ir3_shader, &ir3_key, executable_info);
tu_shader_destroy(dev, shader, pAllocator);
if (!v) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
compiled->variants[MESA_SHADER_COMPUTE] = v;
compiled = tu_pipeline_cache_insert(cache, compiled);
}
pipeline_feedback.duration = os_time_get_nano() - pipeline_start;
if (creation_feedback) {
*creation_feedback->pPipelineCreationFeedback = pipeline_feedback;
assert(creation_feedback->pipelineStageCreationFeedbackCount == 1);
creation_feedback->pPipelineStageCreationFeedbacks[0] = pipeline_feedback;
}
pipeline->active_desc_sets = compiled->active_desc_sets;
struct ir3_shader_variant *v = compiled->variants[MESA_SHADER_COMPUTE];
tu_pipeline_set_linkage(&pipeline->program.link[MESA_SHADER_COMPUTE],
&compiled->push_consts[MESA_SHADER_COMPUTE], v);
result = tu_pipeline_allocate_cs(dev, pipeline, layout, NULL, v);
if (result != VK_SUCCESS)
goto fail;
uint64_t shader_iova = tu_upload_variant(pipeline, v);
struct tu_pvtmem_config pvtmem;
tu_setup_pvtmem(dev, pipeline, &pvtmem, v->pvtmem_size, v->pvtmem_per_wave);
for (int i = 0; i < 3; i++)
pipeline->compute.local_size[i] = v->local_size[i];
pipeline->compute.subgroup_size = v->info.double_threadsize ? 128 : 64;
struct tu_cs prog_cs;
uint32_t additional_reserve_size = tu_xs_get_additional_cs_size_dwords(v);
tu_cs_begin_sub_stream(&pipeline->cs, 64 + additional_reserve_size, &prog_cs);
tu6_emit_cs_config(&prog_cs, v, &pvtmem, shader_iova);
pipeline->program.state = tu_cs_end_draw_state(&pipeline->cs, &prog_cs);
tu6_emit_load_state(pipeline, layout, true);
tu_append_executable(pipeline, v, nir_initial_disasm);
vk_pipeline_cache_object_unref(&compiled->base);
ralloc_free(pipeline_mem_ctx);
*pPipeline = tu_pipeline_to_handle(pipeline);
return VK_SUCCESS;
fail:
if (compiled)
vk_pipeline_cache_object_unref(&compiled->base);
ralloc_free(pipeline_mem_ctx);
vk_object_free(&dev->vk, pAllocator, pipeline);
return result;
}
VKAPI_ATTR VkResult VKAPI_CALL
tu_CreateComputePipelines(VkDevice device,
VkPipelineCache pipelineCache,
uint32_t count,
const VkComputePipelineCreateInfo *pCreateInfos,
const VkAllocationCallbacks *pAllocator,
VkPipeline *pPipelines)
{
VkResult final_result = VK_SUCCESS;
uint32_t i = 0;
for (; i < count; i++) {
VkResult result = tu_compute_pipeline_create(device, pipelineCache,
&pCreateInfos[i],
pAllocator, &pPipelines[i]);
if (result != VK_SUCCESS) {
final_result = result;
pPipelines[i] = VK_NULL_HANDLE;
if (pCreateInfos[i].flags &
VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT)
break;
}
}
for (; i < count; i++)
pPipelines[i] = VK_NULL_HANDLE;
return final_result;
}
VKAPI_ATTR void VKAPI_CALL
tu_DestroyPipeline(VkDevice _device,
VkPipeline _pipeline,
const VkAllocationCallbacks *pAllocator)
{
TU_FROM_HANDLE(tu_device, dev, _device);
TU_FROM_HANDLE(tu_pipeline, pipeline, _pipeline);
if (!_pipeline)
return;
tu_pipeline_finish(pipeline, dev, pAllocator);
vk_object_free(&dev->vk, pAllocator, pipeline);
}
#define WRITE_STR(field, ...) ({ \
memset(field, 0, sizeof(field)); \
UNUSED int _i = snprintf(field, sizeof(field), __VA_ARGS__); \
assert(_i > 0 && _i < sizeof(field)); \
})
static const struct tu_pipeline_executable *
tu_pipeline_get_executable(struct tu_pipeline *pipeline, uint32_t index)
{
assert(index < util_dynarray_num_elements(&pipeline->executables,
struct tu_pipeline_executable));
return util_dynarray_element(
&pipeline->executables, struct tu_pipeline_executable, index);
}
VKAPI_ATTR VkResult VKAPI_CALL
tu_GetPipelineExecutablePropertiesKHR(
VkDevice _device,
const VkPipelineInfoKHR* pPipelineInfo,
uint32_t* pExecutableCount,
VkPipelineExecutablePropertiesKHR* pProperties)
{
TU_FROM_HANDLE(tu_device, dev, _device);
TU_FROM_HANDLE(tu_pipeline, pipeline, pPipelineInfo->pipeline);
VK_OUTARRAY_MAKE_TYPED(VkPipelineExecutablePropertiesKHR, out,
pProperties, pExecutableCount);
util_dynarray_foreach (&pipeline->executables, struct tu_pipeline_executable, exe) {
vk_outarray_append_typed(VkPipelineExecutablePropertiesKHR, &out, props) {
gl_shader_stage stage = exe->stage;
props->stages = mesa_to_vk_shader_stage(stage);
if (!exe->is_binning)
WRITE_STR(props->name, "%s", _mesa_shader_stage_to_abbrev(stage));
else
WRITE_STR(props->name, "Binning VS");
WRITE_STR(props->description, "%s", _mesa_shader_stage_to_string(stage));
props->subgroupSize =
dev->compiler->threadsize_base * (exe->stats.double_threadsize ? 2 : 1);
}
}
return vk_outarray_status(&out);
}
VKAPI_ATTR VkResult VKAPI_CALL
tu_GetPipelineExecutableStatisticsKHR(
VkDevice _device,
const VkPipelineExecutableInfoKHR* pExecutableInfo,
uint32_t* pStatisticCount,
VkPipelineExecutableStatisticKHR* pStatistics)
{
TU_FROM_HANDLE(tu_pipeline, pipeline, pExecutableInfo->pipeline);
VK_OUTARRAY_MAKE_TYPED(VkPipelineExecutableStatisticKHR, out,
pStatistics, pStatisticCount);
const struct tu_pipeline_executable *exe =
tu_pipeline_get_executable(pipeline, pExecutableInfo->executableIndex);
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Max Waves Per Core");
WRITE_STR(stat->description,
"Maximum number of simultaneous waves per core.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.max_waves;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Instruction Count");
WRITE_STR(stat->description,
"Total number of IR3 instructions in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.instrs_count;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Code size");
WRITE_STR(stat->description,
"Total number of dwords in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.sizedwords;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "NOPs Count");
WRITE_STR(stat->description,
"Number of NOP instructions in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.nops_count;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "MOV Count");
WRITE_STR(stat->description,
"Number of MOV instructions in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.mov_count;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "COV Count");
WRITE_STR(stat->description,
"Number of COV instructions in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.cov_count;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Registers used");
WRITE_STR(stat->description,
"Number of registers used in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.max_reg + 1;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Half-registers used");
WRITE_STR(stat->description,
"Number of half-registers used in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.max_half_reg + 1;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Instructions with SS sync bit");
WRITE_STR(stat->description,
"SS bit is set for instructions which depend on a result "
"of \"long\" instructions to prevent RAW hazard.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.ss;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Instructions with SY sync bit");
WRITE_STR(stat->description,
"SY bit is set for instructions which depend on a result "
"of loads from global memory to prevent RAW hazard.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.sy;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Estimated cycles stalled on SS");
WRITE_STR(stat->description,
"A better metric to estimate the impact of SS syncs.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.sstall;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "Estimated cycles stalled on SY");
WRITE_STR(stat->description,
"A better metric to estimate the impact of SY syncs.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.systall;
}
for (int i = 0; i < ARRAY_SIZE(exe->stats.instrs_per_cat); i++) {
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "cat%d instructions", i);
WRITE_STR(stat->description,
"Number of cat%d instructions.", i);
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.instrs_per_cat[i];
}
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "STP Count");
WRITE_STR(stat->description,
"Number of STore Private instructions in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.stp_count;
}
vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) {
WRITE_STR(stat->name, "LDP Count");
WRITE_STR(stat->description,
"Number of LoaD Private instructions in the final generated "
"shader executable.");
stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
stat->value.u64 = exe->stats.ldp_count;
}
return vk_outarray_status(&out);
}
static bool
write_ir_text(VkPipelineExecutableInternalRepresentationKHR* ir,
const char *data)
{
ir->isText = VK_TRUE;
size_t data_len = strlen(data) + 1;
if (ir->pData == NULL) {
ir->dataSize = data_len;
return true;
}
strncpy(ir->pData, data, ir->dataSize);
if (ir->dataSize < data_len)
return false;
ir->dataSize = data_len;
return true;
}
VKAPI_ATTR VkResult VKAPI_CALL
tu_GetPipelineExecutableInternalRepresentationsKHR(
VkDevice _device,
const VkPipelineExecutableInfoKHR* pExecutableInfo,
uint32_t* pInternalRepresentationCount,
VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations)
{
TU_FROM_HANDLE(tu_pipeline, pipeline, pExecutableInfo->pipeline);
VK_OUTARRAY_MAKE_TYPED(VkPipelineExecutableInternalRepresentationKHR, out,
pInternalRepresentations, pInternalRepresentationCount);
bool incomplete_text = false;
const struct tu_pipeline_executable *exe =
tu_pipeline_get_executable(pipeline, pExecutableInfo->executableIndex);
if (exe->nir_from_spirv) {
vk_outarray_append_typed(VkPipelineExecutableInternalRepresentationKHR, &out, ir) {
WRITE_STR(ir->name, "NIR from SPIRV");
WRITE_STR(ir->description,
"Initial NIR before any optimizations");
if (!write_ir_text(ir, exe->nir_from_spirv))
incomplete_text = true;
}
}
if (exe->nir_final) {
vk_outarray_append_typed(VkPipelineExecutableInternalRepresentationKHR, &out, ir) {
WRITE_STR(ir->name, "Final NIR");
WRITE_STR(ir->description,
"Final NIR before going into the back-end compiler");
if (!write_ir_text(ir, exe->nir_final))
incomplete_text = true;
}
}
if (exe->disasm) {
vk_outarray_append_typed(VkPipelineExecutableInternalRepresentationKHR, &out, ir) {
WRITE_STR(ir->name, "IR3 Assembly");
WRITE_STR(ir->description,
"Final IR3 assembly for the generated shader binary");
if (!write_ir_text(ir, exe->disasm))
incomplete_text = true;
}
}
return incomplete_text ? VK_INCOMPLETE : vk_outarray_status(&out);
}