/* * Copyright © 2022 Imagination Technologies Ltd. * * 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 #include #include #include #include "compiler/shader_enums.h" #include "compiler/spirv/nir_spirv.h" #include "nir/nir.h" #include "rogue.h" #include "rogue_build_data.h" #include "rogue_compiler.h" #include "rogue_constreg.h" #include "rogue_encode.h" #include "rogue_nir.h" #include "rogue_nir_helpers.h" #include "rogue_operand.h" #include "rogue_regalloc.h" #include "rogue_shader.h" #include "rogue_validate.h" #include "util/macros.h" #include "util/memstream.h" #include "util/ralloc.h" /** * \file rogue.c * * \brief Contains the top-level Rogue compiler interface for Vulkan driver and * the offline compiler. */ /** * \brief Converts a SPIR-V shader to NIR. * * \param[in] ctx Shared multi-stage build context. * \param[in] stage Shader stage. * \param[in] spirv_size SPIR-V data length in DWORDs. * \param[in] spirv_data SPIR-V data. * \param[in] num_spec Number of SPIR-V specializations. * \param[in] spec SPIR-V specializations. * \return A nir_shader* if successful, or NULL if unsuccessful. */ nir_shader *rogue_spirv_to_nir(struct rogue_build_ctx *ctx, gl_shader_stage stage, const char *entry, size_t spirv_size, const uint32_t *spirv_data, unsigned num_spec, struct nir_spirv_specialization *spec) { nir_shader *nir; nir = spirv_to_nir(spirv_data, spirv_size, spec, num_spec, stage, entry, rogue_get_spirv_options(ctx->compiler), rogue_get_compiler_options(ctx->compiler)); if (!nir) return NULL; ralloc_steal(ctx, nir); /* Apply passes. */ if (!rogue_nir_passes(ctx, nir, stage)) { ralloc_free(nir); return NULL; } /* Collect I/O data to pass back to the driver. */ if (!rogue_collect_io_data(ctx, nir)) { ralloc_free(nir); return NULL; } return nir; } /** * \brief Converts a Rogue shader to binary. * * \param[in] ctx Shared multi-stage build context. * \param[in] shader Rogue shader. * \return A rogue_shader_binary* if successful, or NULL if unsuccessful. */ struct rogue_shader_binary *rogue_to_binary(struct rogue_build_ctx *ctx, const struct rogue_shader *shader) { struct rogue_shader_binary *binary; struct u_memstream mem; size_t buf_size; char *buf; if (!rogue_validate_shader(shader)) return NULL; if (!u_memstream_open(&mem, &buf, &buf_size)) return NULL; if (!rogue_encode_shader(shader, u_memstream_get(&mem))) { u_memstream_close(&mem); free(buf); return NULL; } u_memstream_close(&mem); binary = rzalloc_size(ctx, sizeof(*binary) + buf_size); if (!binary) { free(buf); return NULL; } binary->size = buf_size; memcpy(binary->data, buf, buf_size); free(buf); return binary; } static bool setup_alu_dest(struct rogue_instr *instr, size_t dest_index, nir_alu_instr *alu) { assert(dest_index == 0); /* Dest validation. */ assert(nir_dest_num_components(alu->dest.dest) == 1 || nir_dest_num_components(alu->dest.dest) == 4); assert(nir_dest_bit_size(alu->dest.dest) == 32); size_t nir_dest_reg = nir_alu_dest_regindex(alu); if (nir_dest_num_components(alu->dest.dest) == 1) { CHECK(rogue_instr_set_operand_vreg(instr, dest_index, nir_dest_reg)); } else { size_t comp = nir_alu_dest_comp(alu); CHECK(rogue_instr_set_operand_vreg_vec(instr, dest_index, comp, nir_dest_reg)); } return true; } static bool trans_constreg_operand(struct rogue_instr *instr, size_t operand_index, uint32_t const_value) { size_t const_reg = rogue_constreg_lookup(const_value); /* Only values that can be sourced from const regs should be left from the * rogue_nir_constreg pass. */ assert(const_reg != ROGUE_NO_CONST_REG); CHECK(rogue_instr_set_operand_reg(instr, operand_index, ROGUE_OPERAND_TYPE_REG_CONST, const_reg)); return true; } static bool trans_nir_alu_fmax(struct rogue_shader *shader, nir_alu_instr *alu) { /* Src validation. */ assert(nir_src_num_components(alu->src[0].src) == 1); assert(nir_src_bit_size(alu->src[0].src) == 32); assert(nir_src_num_components(alu->src[1].src) == 1); assert(nir_src_bit_size(alu->src[1].src) == 32); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MAX); CHECK(setup_alu_dest(instr, 0, alu)); for (size_t u = 0; u < nir_op_infos[nir_op_fmax].num_inputs; ++u) { /* Handle values that can be pulled from const regs. */ if (nir_alu_src_is_const(alu, u)) { CHECK(trans_constreg_operand(instr, u + 1, nir_alu_src_const(alu, u))); continue; } size_t nir_src_reg = nir_alu_src_regindex(alu, u); CHECK(rogue_instr_set_operand_vreg(instr, u + 1, nir_src_reg)); } return true; } static bool trans_nir_alu_fmin(struct rogue_shader *shader, nir_alu_instr *alu) { /* Src validation. */ assert(nir_src_num_components(alu->src[0].src) == 1); assert(nir_src_bit_size(alu->src[0].src) == 32); assert(nir_src_num_components(alu->src[1].src) == 1); assert(nir_src_bit_size(alu->src[1].src) == 32); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MIN); CHECK(setup_alu_dest(instr, 0, alu)); for (size_t u = 0; u < nir_op_infos[nir_op_fmin].num_inputs; ++u) { /* Handle values that can be pulled from const regs. */ if (nir_alu_src_is_const(alu, u)) { CHECK(trans_constreg_operand(instr, u + 1, nir_alu_src_const(alu, u))); continue; } size_t nir_src_reg = nir_alu_src_regindex(alu, u); CHECK(rogue_instr_set_operand_vreg(instr, u + 1, nir_src_reg)); } return true; } static bool trans_nir_alu_mov_imm(struct rogue_shader *shader, nir_alu_instr *alu) { /* Src validation. */ assert(nir_src_num_components(alu->src[0].src) == 1); assert(nir_src_bit_size(alu->src[0].src) == 32); uint32_t value = nir_alu_src_const(alu, 0); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV_IMM); CHECK(setup_alu_dest(instr, 0, alu)); CHECK(rogue_instr_set_operand_imm(instr, 1, value)); return true; } static bool trans_nir_alu_mov(struct rogue_shader *shader, nir_alu_instr *alu) { /* Constant value that isn't in constregs. */ if (nir_alu_src_is_const(alu, 0) && nir_dest_num_components(alu->dest.dest) == 1) return trans_nir_alu_mov_imm(shader, alu); /* Src validation. */ assert(nir_src_num_components(alu->src[0].src) == 1); assert(nir_src_bit_size(alu->src[0].src) == 32); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV); CHECK(setup_alu_dest(instr, 0, alu)); /* Handle values that can be pulled from const regs. */ if (nir_alu_src_is_const(alu, 0)) { return trans_constreg_operand(instr, 1, nir_alu_src_const(alu, 0)); } size_t nir_src_reg = nir_alu_src_regindex(alu, 0); CHECK(rogue_instr_set_operand_vreg(instr, 1, nir_src_reg)); return true; } static bool trans_nir_alu_pack_unorm_4x8(struct rogue_shader *shader, nir_alu_instr *alu) { /* Src/dest validation. */ assert(nir_dest_num_components(alu->dest.dest) == 1); assert(nir_dest_bit_size(alu->dest.dest) == 32); assert(nir_src_num_components(alu->src[0].src) == 4); assert(nir_src_bit_size(alu->src[0].src) == 32); size_t nir_src_reg = nir_alu_src_regindex(alu, 0); size_t nir_dest_reg = nir_alu_dest_regindex(alu); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_PACK_U8888); CHECK(rogue_instr_set_operand_vreg(instr, 0, nir_dest_reg)); /* Ensure all 4 components are being sourced in order. */ for (size_t u = 0; u < nir_src_num_components(alu->src[0].src); ++u) assert(alu->src->swizzle[u] == u); CHECK(rogue_instr_set_operand_vreg_vec(instr, 1, ROGUE_COMPONENT_ALL, nir_src_reg)); return true; } static bool trans_nir_alu_fmul(struct rogue_shader *shader, nir_alu_instr *alu) { /* Src validation. */ assert(nir_src_num_components(alu->src[0].src) == 1); assert(nir_src_bit_size(alu->src[0].src) == 32); assert(nir_src_num_components(alu->src[1].src) == 1); assert(nir_src_bit_size(alu->src[1].src) == 32); size_t nir_in_reg_a = nir_alu_src_regindex(alu, 0); size_t nir_in_reg_b = nir_alu_src_regindex(alu, 1); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MUL); CHECK(setup_alu_dest(instr, 0, alu)); CHECK(rogue_instr_set_operand_vreg(instr, 1, nir_in_reg_a)); CHECK(rogue_instr_set_operand_vreg(instr, 2, nir_in_reg_b)); return true; } static bool trans_nir_alu_ffma(struct rogue_shader *shader, nir_alu_instr *alu) { /* Src validation. */ assert(nir_src_num_components(alu->src[0].src) == 1); assert(nir_src_bit_size(alu->src[0].src) == 32); assert(nir_src_num_components(alu->src[1].src) == 1); assert(nir_src_bit_size(alu->src[1].src) == 32); assert(nir_src_num_components(alu->src[2].src) == 1); assert(nir_src_bit_size(alu->src[2].src) == 32); size_t nir_in_reg_a = nir_alu_src_regindex(alu, 0); size_t nir_in_reg_b = nir_alu_src_regindex(alu, 1); size_t nir_in_reg_c = nir_alu_src_regindex(alu, 2); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_FMA); CHECK(setup_alu_dest(instr, 0, alu)); CHECK(rogue_instr_set_operand_vreg(instr, 1, nir_in_reg_a)); CHECK(rogue_instr_set_operand_vreg(instr, 2, nir_in_reg_b)); CHECK(rogue_instr_set_operand_vreg(instr, 3, nir_in_reg_c)); return true; } static bool trans_nir_alu(struct rogue_shader *shader, nir_alu_instr *alu) { switch (alu->op) { case nir_op_fmax: return trans_nir_alu_fmax(shader, alu); case nir_op_fmin: return trans_nir_alu_fmin(shader, alu); case nir_op_pack_unorm_4x8: return trans_nir_alu_pack_unorm_4x8(shader, alu); case nir_op_mov: return trans_nir_alu_mov(shader, alu); case nir_op_fmul: return trans_nir_alu_fmul(shader, alu); case nir_op_ffma: return trans_nir_alu_ffma(shader, alu); default: break; } unreachable("Unimplemented NIR ALU instruction."); } static bool trans_nir_intrinsic_load_input_fs(struct rogue_shader *shader, nir_intrinsic_instr *intr) { struct rogue_fs_build_data *fs_data = &shader->ctx->stage_data.fs; /* Src/dest validation. */ assert(nir_dest_num_components(intr->dest) == 1); assert(nir_dest_bit_size(intr->dest) == 32); assert(nir_src_num_components(intr->src[0]) == 1); assert(nir_src_bit_size(intr->src[0]) == 32); assert(nir_intr_src_is_const(intr, 0)); /* Intrinsic index validation. */ assert(nir_intrinsic_dest_type(intr) == nir_type_float32); struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); size_t component = nir_intrinsic_component(intr); size_t coeff_index = rogue_coeff_index_fs(&fs_data->iterator_args, io_semantics.location, component); size_t wcoeff_index = rogue_coeff_index_fs(&fs_data->iterator_args, ~0, 0); size_t drc_num = rogue_acquire_drc(shader); uint64_t source_count = nir_dest_num_components(intr->dest); size_t nir_dest_reg = nir_intr_dest_regindex(intr); /* pixiter.w instruction. */ struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_PIX_ITER_W); CHECK(rogue_instr_set_operand_vreg(instr, 0, nir_dest_reg)); CHECK(rogue_instr_set_operand_drc(instr, 1, drc_num)); CHECK(rogue_instr_set_operand_reg(instr, 2, ROGUE_OPERAND_TYPE_REG_COEFF, coeff_index)); CHECK(rogue_instr_set_operand_reg(instr, 3, ROGUE_OPERAND_TYPE_REG_COEFF, wcoeff_index)); CHECK(rogue_instr_set_operand_imm(instr, 4, source_count)); /* wdf instruction must follow the pixiter.w. */ instr = rogue_shader_insert(shader, ROGUE_OP_WDF); CHECK(rogue_instr_set_operand_drc(instr, 0, drc_num)); rogue_release_drc(shader, drc_num); return true; } static bool trans_nir_intrinsic_load_input_vs(struct rogue_shader *shader, nir_intrinsic_instr *intr) { /* Src/dest validation. */ assert(nir_dest_num_components(intr->dest) == 1); assert(nir_dest_bit_size(intr->dest) == 32); assert(nir_src_num_components(intr->src[0]) == 1); assert(nir_src_bit_size(intr->src[0]) == 32); assert(nir_intr_src_is_const(intr, 0)); /* Intrinsic index validation. */ assert(nir_intrinsic_dest_type(intr) == nir_type_float32); size_t component = nir_intrinsic_component(intr); struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); size_t vi_reg_index = ((io_semantics.location - VERT_ATTRIB_GENERIC0) * 3) + component; /* TODO: get these properly with the * intrinsic index (ssa argument) */ size_t nir_dest_reg = nir_intr_dest_regindex(intr); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV); CHECK(rogue_instr_set_operand_vreg(instr, 0, nir_dest_reg)); CHECK(rogue_instr_set_operand_reg(instr, 1, ROGUE_OPERAND_TYPE_REG_VERTEX_IN, vi_reg_index)); return true; } static bool trans_nir_intrinsic_load_input(struct rogue_shader *shader, nir_intrinsic_instr *intr) { switch (shader->stage) { case MESA_SHADER_FRAGMENT: return trans_nir_intrinsic_load_input_fs(shader, intr); case MESA_SHADER_VERTEX: return trans_nir_intrinsic_load_input_vs(shader, intr); default: break; } unreachable("Unimplemented NIR load_input variant."); } static bool trans_nir_intrinsic_store_output_fs(struct rogue_shader *shader, nir_intrinsic_instr *intr) { /* Src/dest validation. */ assert(nir_src_num_components(intr->src[0]) == 1); assert(nir_src_bit_size(intr->src[0]) == 32); assert(!nir_intr_src_is_const(intr, 0)); assert(nir_src_num_components(intr->src[1]) == 1); assert(nir_src_bit_size(intr->src[1]) == 32); assert(nir_intr_src_is_const(intr, 1)); /* Intrinsic index validation. */ assert(nir_intrinsic_src_type(intr) == nir_type_uint32); /* Fetch the output offset. */ /* TODO: Is this really the right value to use for pixel out reg. num? */ size_t offset = nir_intr_src_const(intr, 1); /* Fetch the components. */ size_t src_reg = nir_intr_src_regindex(intr, 0); /* mov.olchk instruction. */ struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV); CHECK(rogue_instr_set_operand_reg(instr, 0, ROGUE_OPERAND_TYPE_REG_PIXEL_OUT, offset)); CHECK(rogue_instr_set_operand_vreg(instr, 1, src_reg)); CHECK(rogue_instr_set_flag(instr, ROGUE_INSTR_FLAG_OLCHK)); return true; } static bool trans_nir_intrinsic_store_output_vs(struct rogue_shader *shader, nir_intrinsic_instr *intr) { struct rogue_vs_build_data *vs_data = &shader->ctx->stage_data.vs; /* Src/dest validation. */ assert(nir_src_num_components(intr->src[0]) == 1); assert(nir_src_bit_size(intr->src[0]) == 32); assert(!nir_intr_src_is_const(intr, 0)); assert(nir_src_num_components(intr->src[1]) == 1); assert(nir_src_bit_size(intr->src[1]) == 32); assert(nir_intr_src_is_const(intr, 1)); /* Intrinsic index validation. */ assert(nir_intrinsic_src_type(intr) == nir_type_float32); assert(util_bitcount(nir_intrinsic_write_mask(intr)) == 1); struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr); size_t component = nir_intrinsic_component(intr); size_t vo_index = rogue_output_index_vs(&vs_data->outputs, io_semantics.location, component); size_t src_reg = nir_intr_src_regindex(intr, 0); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_VTXOUT); CHECK(rogue_instr_set_operand_imm(instr, 0, vo_index)); CHECK(rogue_instr_set_operand_vreg(instr, 1, src_reg)); return true; } static bool trans_nir_intrinsic_store_output(struct rogue_shader *shader, nir_intrinsic_instr *intr) { switch (shader->stage) { case MESA_SHADER_FRAGMENT: return trans_nir_intrinsic_store_output_fs(shader, intr); case MESA_SHADER_VERTEX: return trans_nir_intrinsic_store_output_vs(shader, intr); default: break; } unreachable("Unimplemented NIR store_output variant."); } static bool trans_nir_intrinsic_load_ubo(struct rogue_shader *shader, nir_intrinsic_instr *intr) { struct rogue_ubo_data *ubo_data = &shader->ctx->common_data[shader->stage].ubo_data; /* Src/dest validation. */ assert(nir_dest_num_components(intr->dest) == 1); assert(nir_dest_bit_size(intr->dest) == 32); assert(nir_src_num_components(intr->src[0]) == 2); assert(nir_src_bit_size(intr->src[0]) == 32); assert(nir_intr_src_is_const(intr, 0)); assert(nir_src_num_components(intr->src[1]) == 1); assert(nir_src_bit_size(intr->src[1]) == 32); assert(nir_intr_src_is_const(intr, 1)); /* Intrinsic index validation. */ assert((nir_intrinsic_range_base(intr) % ROGUE_REG_SIZE_BYTES) == 0); assert(nir_intrinsic_range(intr) == ROGUE_REG_SIZE_BYTES); size_t nir_dest_reg = nir_intr_dest_regindex(intr); size_t desc_set = nir_intr_src_comp_const(intr, 0, 0); size_t binding = nir_intr_src_comp_const(intr, 0, 1); size_t offset = nir_intrinsic_range_base(intr); size_t sh_num = rogue_ubo_reg(ubo_data, desc_set, binding, offset); struct rogue_instr *instr = rogue_shader_insert(shader, ROGUE_OP_MOV); CHECK(rogue_instr_set_operand_vreg(instr, 0, nir_dest_reg)); CHECK(rogue_instr_set_operand_reg(instr, 1, ROGUE_OPERAND_TYPE_REG_SHARED, sh_num)); return true; } static bool trans_nir_intrinsic(struct rogue_shader *shader, nir_intrinsic_instr *intr) { switch (intr->intrinsic) { case nir_intrinsic_load_input: return trans_nir_intrinsic_load_input(shader, intr); case nir_intrinsic_store_output: return trans_nir_intrinsic_store_output(shader, intr); case nir_intrinsic_load_ubo: return trans_nir_intrinsic_load_ubo(shader, intr); default: break; } unreachable("Unimplemented NIR intrinsic instruction."); } static bool trans_nir_load_const(struct rogue_shader *shader, nir_load_const_instr *load_const) { /* Src/dest validation. */ assert(load_const->def.bit_size == 32); /* Ensure that two-component load_consts are used only by load_ubos. */ if (load_const->def.num_components == 2) { nir_foreach_use (use_src, &load_const->def) { nir_instr *instr = use_src->parent_instr; assert(instr->type == nir_instr_type_intrinsic); ASSERTED nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr); assert(intr->intrinsic == nir_intrinsic_load_ubo); } } else { assert(load_const->def.num_components == 1); } /* TODO: This is currently done in MOV_IMM, but instead now would be the * time to lookup the constant value, see if it lives in const regs, or if * it needs to generate a MOV_IMM (or be constant calc-ed). */ return true; } static bool trans_nir_jump_return(struct rogue_shader *shader, nir_jump_instr *jump) { enum rogue_opcode return_op; switch (shader->stage) { case MESA_SHADER_FRAGMENT: return_op = ROGUE_OP_END_FRAG; break; case MESA_SHADER_VERTEX: return_op = ROGUE_OP_END_VERT; break; default: unreachable("Unimplemented NIR return instruction type."); } rogue_shader_insert(shader, return_op); return true; } static bool trans_nir_jump(struct rogue_shader *shader, nir_jump_instr *jump) { switch (jump->type) { case nir_jump_return: return trans_nir_jump_return(shader, jump); default: break; } unreachable("Unimplemented NIR jump instruction type."); } /** * \brief Converts a NIR shader to Rogue. * * \param[in] ctx Shared multi-stage build context. * \param[in] nir NIR shader. * \return A rogue_shader* if successful, or NULL if unsuccessful. */ struct rogue_shader *rogue_nir_to_rogue(struct rogue_build_ctx *ctx, const nir_shader *nir) { gl_shader_stage stage = nir->info.stage; struct rogue_shader *shader = rogue_shader_create(ctx, stage); if (!shader) return NULL; /* Make sure we only have a single function. */ assert(exec_list_length(&nir->functions) == 1); /* Translate shader entrypoint. */ nir_function_impl *entry = nir_shader_get_entrypoint((nir_shader *)nir); nir_foreach_block (block, entry) { nir_foreach_instr (instr, block) { switch (instr->type) { case nir_instr_type_alu: /* TODO: Cleanup on failure. */ CHECKF(trans_nir_alu(shader, nir_instr_as_alu(instr)), "Failed to translate NIR ALU instruction."); break; case nir_instr_type_intrinsic: CHECKF(trans_nir_intrinsic(shader, nir_instr_as_intrinsic(instr)), "Failed to translate NIR intrinsic instruction."); break; case nir_instr_type_load_const: CHECKF(trans_nir_load_const(shader, nir_instr_as_load_const(instr)), "Failed to translate NIR load_const instruction."); break; case nir_instr_type_jump: CHECKF(trans_nir_jump(shader, nir_instr_as_jump(instr)), "Failed to translate NIR jump instruction."); break; default: unreachable("Unimplemented NIR instruction type."); } } } /* Perform register allocation. */ /* TODO: handle failure. */ if (!rogue_ra_alloc(&shader->instr_list, shader->ra, &ctx->common_data[stage].temps, &ctx->common_data[stage].internals)) return NULL; return shader; } /** * \brief Creates and sets up a shared multi-stage build context. * * \param[in] compiler The compiler context. * \return A pointer to the new build context, or NULL on failure. */ struct rogue_build_ctx * rogue_create_build_context(struct rogue_compiler *compiler) { struct rogue_build_ctx *ctx; ctx = rzalloc_size(compiler, sizeof(*ctx)); if (!ctx) return NULL; ctx->compiler = compiler; /* nir/rogue/binary shaders need to be default-zeroed; * this is taken care of by rzalloc_size. */ /* Setup non-zero defaults. */ ctx->stage_data.fs.msaa_mode = ROGUE_MSAA_MODE_PIXEL; return ctx; }