diff --git a/src/asahi/compiler/agx_optimizer.c b/src/asahi/compiler/agx_optimizer.c index 1df093b72249..ff2ac82e865c 100644 --- a/src/asahi/compiler/agx_optimizer.c +++ b/src/asahi/compiler/agx_optimizer.c @@ -616,6 +616,15 @@ agx_optimizer_fmov_rev(agx_context *ctx, agx_instr *I, agx_instr *use) (I->src[0].abs || I->src[0].neg || I->src[1].abs || I->src[1].neg)) return false; + /* M5 (AGX3C): the native perspective multiply only encodes a word + * destination (agx3_persp_mul_encode). Folding the f32-to-f16 move that + * narrows a mediump varying into it made the binary packer refuse the + * shader and the draw was skipped. Keep the explicit move. */ + if (ctx->key && ctx->key->dev.encoding == AGX_ISA_ENCODING_AGX3C && + I->op == AGX_OPCODE_PERSP_MUL_AGX3 && + I->dest[0].size == AGX_SIZE_32 && use->dest[0].size == AGX_SIZE_16) + return false; + /* saturate(saturate(x)) = saturate(x) */ I->saturate |= use->saturate; I->dest[0] = use->dest[0]; diff --git a/src/asahi/compiler/test/test-optimizer.cpp b/src/asahi/compiler/test/test-optimizer.cpp index 0bd63a55091b..2317b594ae89 100644 --- a/src/asahi/compiler/test/test-optimizer.cpp +++ b/src/asahi/compiler/test/test-optimizer.cpp @@ -204,6 +204,31 @@ TEST_F(Optimizer, G17PPreservesHalfWidenBeforeFltnorm) ASSERT_SHADER_EQUAL(actual->shader, expected->shader); } +/* + * The native projective f32mul (PERSP_MUL) has no 16-bit destination form. A + * mediump varying narrows its product with an f32 to f16 move; folding that + * move into the multiply made the shader unencodable on M5 (AGX3C), so the + * move stays a separate instruction. + */ +TEST_F(Optimizer, G17CKeepsPerspMulNarrowingMove) +{ + agx_builder *actual = agx_test_builder(mem_ctx); + agx_builder *expected = agx_test_builder(mem_ctx); + struct agx_shader_key key = {}; + key.dev.encoding = AGX_ISA_ENCODING_AGX3C; + key.dev.chip = AGX_CHIP_G17C; + for (agx_builder *b : {actual, expected}) { + b->shader->key = &key; + agx_index product = agx_temp(b->shader, AGX_SIZE_32); + agx_persp_mul_agx3_to(b, product, wx, wy, 1); + agx_index narrow = agx_temp(b->shader, AGX_SIZE_16); + agx_fmov_to(b, narrow, product); + agx_unit_test(b, narrow); + } + agx_optimize_and_dce(actual->shader); + ASSERT_SHADER_EQUAL(actual->shader, expected->shader); +} + /* * AGX3 has no select form without a register compare operand, so a select of * two immediates -- here fused out of a compare of two constants -- has to