/* SPDX-License-Identifier: BSD-2-Clause */ /* * Scaffold smoke test — verifies the daedalus-decoder library links * cleanly against daedalus-fourier and the lifecycle entry points * don't immediately crash. No actual decoding work yet. * * Returns 0 on success, non-zero on any unexpected behaviour. */ #include #include #include #include "daedalus_decoder.h" #define EXPECT(cond, msg) do { \ if (!(cond)) { \ fprintf(stderr, "EXPECT FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \ return 1; \ } \ } while (0) int main(void) { printf("daedalus-decoder version: %s\n", daedalus_decoder_version()); /* Create / destroy null is a no-op. */ daedalus_decoder_destroy(NULL); /* Bad dimensions rejected. */ EXPECT(daedalus_decoder_create(0, 0 ) == NULL, "zero dims must reject"); EXPECT(daedalus_decoder_create(1919, 1088) == NULL, "non-16-multiple width must reject"); EXPECT(daedalus_decoder_create(1920, 1079) == NULL, "non-16-multiple height must reject"); /* Valid 1088p create. */ daedalus_decoder *dec = daedalus_decoder_create(1920, 1088); if (!dec) { /* Vulkan init failure on this host — degrades to skip, not fail. * (CI runners without V3D7 will hit this; the smoke test * shouldn't gate on hardware presence.) */ fprintf(stderr, "SKIP: daedalus_decoder_create returned NULL " "(Vulkan / V3D7 unavailable on this host)\n"); return 0; } printf("ctx created: 1920x1088, has_qpu=%d\n", daedalus_decoder_has_qpu(dec)); /* set_output_format mid-frame on virgin ctx is allowed * (mbs_appended == 0). */ EXPECT(daedalus_decoder_set_output_format(dec, DAEDALUS_DECODER_OUTPUT_RGBA) == 0, "switch to RGBA on virgin ctx"); EXPECT(daedalus_decoder_set_output_format(dec, DAEDALUS_DECODER_OUTPUT_NV12) == 0, "switch back to NV12"); /* Append rejects out-of-bounds + null inputs. */ int16_t coeffs[384] = {0}; struct daedalus_decoder_mb_input mb = {0}; mb.coeffs = coeffs; mb.mb_x = 0; mb.mb_y = 0; EXPECT(daedalus_decoder_append_mb(dec, NULL) == -1, "null mb rejects"); { struct daedalus_decoder_mb_input mb2 = mb; mb2.coeffs = NULL; EXPECT(daedalus_decoder_append_mb(dec, &mb2) == -1, "null coeffs rejects"); } { struct daedalus_decoder_mb_input mb2 = mb; mb2.mb_x = 9999; mb2.mb_y = 9999; EXPECT(daedalus_decoder_append_mb(dec, &mb2) == -1, "OOB coords reject"); } /* Append first MB at raster index 0 — should succeed. */ EXPECT(daedalus_decoder_append_mb(dec, &mb) == 0, "append (0,0)"); /* Skipping (0,1) and appending (1,0) violates raster order — reject. */ { struct daedalus_decoder_mb_input mb2 = mb; mb2.mb_x = 0; mb2.mb_y = 1; EXPECT(daedalus_decoder_append_mb(dec, &mb2) == -1, "out-of-raster-order rejects"); } /* In-order: (1,0). */ mb.mb_x = 1; mb.mb_y = 0; EXPECT(daedalus_decoder_append_mb(dec, &mb) == 0, "append (1,0)"); /* Flush an incomplete frame: should fail because mbs_appended != n_mbs. */ EXPECT(daedalus_decoder_flush_frame(dec, NULL, 0, NULL, 0) == -1, "incomplete-frame flush rejects"); /* set_output_format mid-frame (mbs_appended > 0) must reject. */ EXPECT(daedalus_decoder_set_output_format(dec, DAEDALUS_DECODER_OUTPUT_RGBA) == -1, "mid-frame format change rejects"); daedalus_decoder_destroy(dec); /* ---- Full-frame round-trip with all-zero coefficients. * Phase 1 stage 1 validation: flush_frame builds the per-frame IDCT * dispatch and a successful GPU round-trip returns 0. IDCT of * all-zero coefficients with zero-initialised predicted = all-zero * output pixels. */ dec = daedalus_decoder_create(1920, 1088); if (!dec) { fprintf(stderr, "SKIP roundtrip: ctx create failed\n"); return 0; } static int16_t zero_coeffs[384] = {0}; struct daedalus_decoder_mb_input zmb = {0}; zmb.coeffs = zero_coeffs; int mb_width = 1920 / 16; /* 120 */ int mb_height = 1088 / 16; /* 68 */ int n_mbs = mb_width * mb_height; for (int mby = 0; mby < mb_height; mby++) { for (int mbx = 0; mbx < mb_width; mbx++) { zmb.mb_x = (uint16_t) mbx; zmb.mb_y = (uint16_t) mby; if (daedalus_decoder_append_mb(dec, &zmb) != 0) { fprintf(stderr, "append (%d, %d) failed\n", mbx, mby); return 1; } } } printf("appended %d MBs (%dx%d)\n", n_mbs, mb_width, mb_height); size_t y_size = (size_t) 1920 * 1088; size_t uv_size = (size_t) 1920 * 1088 / 2; uint8_t *out_y = malloc(y_size); uint8_t *out_uv = malloc(uv_size); /* Pre-fill with sentinel so any read-then-write bug becomes visible. */ memset(out_y, 0xab, y_size); memset(out_uv, 0xcd, uv_size); int frc = daedalus_decoder_flush_frame(dec, out_y, 1920, out_uv, 1920); printf("flush_frame rc=%d\n", frc); EXPECT(frc == 0, "flush succeeds on full frame"); /* Y plane should be all zero (clip255(IDCT(zeros)) = 0). */ int y_nz = 0; for (size_t i = 0; i < y_size; i++) if (out_y[i] != 0) y_nz++; printf("Y non-zero bytes: %d / %zu\n", y_nz, y_size); EXPECT(y_nz == 0, "Y plane all zero for zero-coeff frame"); /* UV plane should be all zero now (real chroma IDCT runs with * zero coeffs → zero residual → clip255(0+0) = 0). Previously a * 128 placeholder when chroma was a memset stub; this PR replaced * that with the real dispatch. Sentinel 0xcd above guarantees we * are observing post-dispatch writes, not the leftover memset. */ int uv_nz = 0; for (size_t i = 0; i < uv_size; i++) if (out_uv[i] != 0) uv_nz++; printf("UV non-zero bytes: %d / %zu\n", uv_nz, uv_size); EXPECT(uv_nz == 0, "UV plane all zero for zero-coeff frame"); free(out_y); free(out_uv); daedalus_decoder_destroy(dec); printf("smoke OK\n"); return 0; }