Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a9590acee3 | |||
| 989818c2e6 |
+58
-17
@@ -1,7 +1,6 @@
|
||||
// daedalus-fourier — H.264 luma qpel mc02 (8x8, vertical half-pel), V3D 7.1.
|
||||
//
|
||||
// Sibling of cycle 9's v3d_h264_qpel_mc20.comp. Same 6-tap filter,
|
||||
// transposed to vertical direction:
|
||||
// v2: cooperative-load shared-memory tile.
|
||||
//
|
||||
// dst[r,c] = clip255(
|
||||
// ( s[r-2,c]
|
||||
@@ -14,9 +13,30 @@
|
||||
// ) >> 5)
|
||||
//
|
||||
// src+src_off points at row 0 col 0 of the OUTPUT block; the filter
|
||||
// reads rows -2..+3 (2 rows of top context, 3 rows of bottom).
|
||||
// reads rows -2..+3 (2 rows of top context, 3 rows of bottom), total
|
||||
// 13 distinct source rows × 8 cols = 104 bytes per 8x8 output.
|
||||
//
|
||||
// Same WG layout as mc20: 64 lanes / 1 block-per-WG / 1 lane-per-pixel.
|
||||
// v1 had each of the 64 lanes do 6 SSBO loads → 384 loads/WG to cover
|
||||
// 104 unique bytes (3.7x redundant), and each lane's loads were stride-
|
||||
// spaced (one cache line per byte under V3D's TMU). PR #36 bench
|
||||
// showed mc02 was the only qpel position where CPU NEON still beat
|
||||
// QPU (16.96 ns/op CPU vs 20.54 ns/op QPU; 1.21x CPU favoring).
|
||||
//
|
||||
// v2 splits the work into a coalesced load phase + a shared-memory
|
||||
// compute phase:
|
||||
//
|
||||
// Phase 1: each of the 64 lanes cooperatively loads the 104-byte
|
||||
// source tile into shared memory. Lanes 0..63 load bytes at indices
|
||||
// 0..63 (covers source rows 0..7 of the 13-row tile); lanes 0..39
|
||||
// second-load bytes 64..103 (rows 8..12). Reads within a row are
|
||||
// contiguous so the SIMD groups coalesce; total SSBO loads = 104,
|
||||
// matching the unique-byte count.
|
||||
//
|
||||
// Phase 2: all 64 lanes compute one output pixel each, reading 6
|
||||
// bytes from shared. Shared-memory access on V3D is local-store
|
||||
// backed (no TMU round-trip).
|
||||
//
|
||||
// Same WG layout as v1: 64 lanes / 1 block-per-WG / 1 lane-per-pixel.
|
||||
//
|
||||
// License: BSD-2-Clause.
|
||||
|
||||
@@ -36,31 +56,52 @@ layout(push_constant) uniform PC {
|
||||
uint _pad0, _pad1;
|
||||
} pc;
|
||||
|
||||
// 13 source rows × 8 cols. int storage (4 bytes each) — wasteful vs
|
||||
// uint8_t but avoids 8-bit-shared interop concerns on glslang+v3dv;
|
||||
// 416 bytes shared/WG is well within any reasonable local-store budget.
|
||||
shared int s_tile[13 * 8];
|
||||
|
||||
void main()
|
||||
{
|
||||
uint block_idx = gl_WorkGroupID.x;
|
||||
if (block_idx >= pc.n_blocks) return;
|
||||
|
||||
uint lane = gl_LocalInvocationID.x;
|
||||
uint r = lane >> 3;
|
||||
uint c = lane & 7u;
|
||||
|
||||
uint dst_off = u_meta.meta[block_idx].x;
|
||||
uint src_off = u_meta.meta[block_idx].y;
|
||||
uint stride = pc.stride_u8;
|
||||
|
||||
// Read the 6 rows of vertical context at col (c) of THIS output row.
|
||||
// src_off+r*stride+c is at the OUTPUT pixel position; the kernel
|
||||
// samples r-2..r+3 along the column. Unsigned-safe because the
|
||||
// public API contract guarantees src_off >= 2*stride.
|
||||
uint col_base = src_off + c;
|
||||
// Source-tile base: src_off points at output-row-0 col-0, the tile
|
||||
// starts 2 rows above. Unsigned-safe because the public API
|
||||
// contract guarantees src_off >= 2*stride.
|
||||
uint tile_base = src_off - 2u * stride;
|
||||
|
||||
int s_m2 = int(u_src.src[col_base + (r - 2u) * stride]);
|
||||
int s_m1 = int(u_src.src[col_base + (r - 1u) * stride]);
|
||||
int s_0 = int(u_src.src[col_base + r * stride]);
|
||||
int s_p1 = int(u_src.src[col_base + (r + 1u) * stride]);
|
||||
int s_p2 = int(u_src.src[col_base + (r + 2u) * stride]);
|
||||
int s_p3 = int(u_src.src[col_base + (r + 3u) * stride]);
|
||||
// Phase 1: cooperative load — 64 lanes load 104 bytes.
|
||||
{
|
||||
uint sr = lane >> 3; // 0..7
|
||||
uint sc = lane & 7u;
|
||||
s_tile[lane] = int(u_src.src[tile_base + sr * stride + sc]);
|
||||
}
|
||||
if (lane < 40u) {
|
||||
uint idx = lane + 64u; // 64..103
|
||||
uint sr = idx >> 3; // 8..12
|
||||
uint sc = idx & 7u;
|
||||
s_tile[idx] = int(u_src.src[tile_base + sr * stride + sc]);
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
// Phase 2: each lane computes one output pixel from the shared tile.
|
||||
uint r = lane >> 3;
|
||||
uint c = lane & 7u;
|
||||
|
||||
int s_m2 = s_tile[(r + 0u) * 8u + c];
|
||||
int s_m1 = s_tile[(r + 1u) * 8u + c];
|
||||
int s_0 = s_tile[(r + 2u) * 8u + c];
|
||||
int s_p1 = s_tile[(r + 3u) * 8u + c];
|
||||
int s_p2 = s_tile[(r + 4u) * 8u + c];
|
||||
int s_p3 = s_tile[(r + 5u) * 8u + c];
|
||||
|
||||
int v = s_m2 - 5 * s_m1 + 20 * s_0 + 20 * s_p1 - 5 * s_p2 + s_p3 + 16;
|
||||
int p = clamp(v >> 5, 0, 255);
|
||||
|
||||
+122
-73
@@ -2,25 +2,22 @@
|
||||
/* CLOCK_MONOTONIC under -std=c11 -CMAKE_C_EXTENSIONS=OFF. */
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
/*
|
||||
* bench_h264_primitives — NEON-path latency baseline for the H.264
|
||||
* primitive library landed across PRs #9–#23.
|
||||
* bench_h264_primitives — latency baseline for the H.264 primitive
|
||||
* library landed across PRs #9–#35.
|
||||
*
|
||||
* Each kernel is exercised at a representative per-frame N for 1080p
|
||||
* (8160 MBs); the per-kernel total + ns/op + ms/frame are reported.
|
||||
* Lets us answer "what's the total NEON-only budget for the H.264
|
||||
* decode at 1080p" — useful for sizing intercept-patch decisions
|
||||
* (which kernels NEED QPU shaders vs which are budget-fine on NEON).
|
||||
* (8160 MBs); the per-kernel total + ns/op + ms/frame are reported,
|
||||
* once per substrate (CPU NEON, QPU V3D7 compute). The QPU column
|
||||
* appears only when the host has a usable Vulkan device. When both
|
||||
* columns exist a CPU/QPU ratio is printed; that's the per-kernel
|
||||
* data the QPU-substrate decree (2026-05-23) deliberately overrides
|
||||
* but which is still useful to track over time as dispatch overhead
|
||||
* shrinks (buffer pool, persistent cmdbuf, dmabuf import — tasks 160-162).
|
||||
*
|
||||
* NOT a ctest — produces wall-time numbers, doesn't pass/fail.
|
||||
*
|
||||
* Invoke: ./build/bench_h264_primitives [iters]
|
||||
* (default iters = 50, post-warmup = 5)
|
||||
*
|
||||
* NB: results are inherently approximate — single-core, includes
|
||||
* loop overhead + memory access patterns that may not match what
|
||||
* a real decode would hit (we touch a small set of pages repeatedly).
|
||||
* The numbers are useful for relative comparison and order-of-
|
||||
* magnitude sizing, not absolute perf claims.
|
||||
* Invoke: ./build/bench_h264_primitives [iters [warmup]]
|
||||
* (default iters = 50, warmup = 5)
|
||||
*/
|
||||
|
||||
#include "daedalus.h"
|
||||
@@ -46,11 +43,6 @@ static double now_ms(void) {
|
||||
|
||||
/* Per-1080p-frame counts (8160 MBs at 1920x1088). */
|
||||
#define MBS_1080P 8160
|
||||
#define LUMA_4x4_PER_MB 16 /* if transform_8x8=0 */
|
||||
#define LUMA_8x8_PER_MB 4 /* if transform_8x8=1 */
|
||||
#define CHROMA_4x4_PER_MB 8 /* 4 Cb + 4 Cr */
|
||||
#define DEBLOCK_LUMA_EDGES_PER_MB 4 /* 4 horiz + 4 vert internal+MB-edge — ~4 each */
|
||||
#define DEBLOCK_CHROMA_EDGES_PER_MB 2 /* 2 each direction */
|
||||
|
||||
/* Standard benchmark loop. fn() is called n times per iteration. */
|
||||
typedef void (*bench_fn)(void);
|
||||
@@ -64,16 +56,18 @@ static double bench_ns(const char *name, int iters, int warmup,
|
||||
double t1 = now_ms();
|
||||
double total_ms = (t1 - t0);
|
||||
double ns_per_op = (total_ms * 1e6) / ((double) iters * ops_per_iter);
|
||||
printf(" %-32s %8.2f ns/op (%d iters x %d ops)\n",
|
||||
printf(" %-32s %10.2f ns/op (%d iters x %d ops)\n",
|
||||
name, ns_per_op, iters, ops_per_iter);
|
||||
return ns_per_op;
|
||||
}
|
||||
|
||||
/* ---- Per-kernel scaffolding. Each section sets up the buffers +
|
||||
* meta, then defines a static fn() that calls the corresponding
|
||||
* dispatch with a representative N. */
|
||||
* dispatch with a representative N. The substrate is read from the
|
||||
* global g_sub so the same fn() can be re-driven with CPU then QPU. */
|
||||
|
||||
static daedalus_ctx *ctx;
|
||||
static daedalus_ctx *ctx;
|
||||
static daedalus_substrate g_sub = DAEDALUS_SUBSTRATE_CPU;
|
||||
|
||||
/* --- IDCT 4x4 luma: N = 16 blocks per MB. Bench with 1024 blocks
|
||||
* per call (64 MBs worth). Per-MB the dispatch overhead is the
|
||||
@@ -83,7 +77,7 @@ static daedalus_h264_block_meta idct4_meta[1024];
|
||||
static uint8_t idct_dst[64 * 4 * 16 * 16]; /* 64 MB-rows × ... */
|
||||
|
||||
static void bench_idct4(void) {
|
||||
daedalus_dispatch_h264_idct4(ctx, DAEDALUS_SUBSTRATE_CPU,
|
||||
daedalus_dispatch_h264_idct4(ctx, g_sub,
|
||||
idct_dst, 64*16, idct4_coeffs, 1024, idct4_meta);
|
||||
}
|
||||
|
||||
@@ -92,7 +86,7 @@ static int16_t idct8_coeffs[256 * 64];
|
||||
static daedalus_h264_block_meta idct8_meta[256];
|
||||
|
||||
static void bench_idct8(void) {
|
||||
daedalus_dispatch_h264_idct8(ctx, DAEDALUS_SUBSTRATE_CPU,
|
||||
daedalus_dispatch_h264_idct8(ctx, g_sub,
|
||||
idct_dst, 64*16, idct8_coeffs, 256, idct8_meta);
|
||||
}
|
||||
|
||||
@@ -101,12 +95,12 @@ static daedalus_h264_deblock_meta deblock_meta[256];
|
||||
static uint8_t deblock_dst[256 * 16 * 16];
|
||||
|
||||
static void bench_deblock_v(void) {
|
||||
daedalus_dispatch_h264_deblock_luma_v(ctx, DAEDALUS_SUBSTRATE_CPU,
|
||||
daedalus_dispatch_h264_deblock_luma_v(ctx, g_sub,
|
||||
deblock_dst, 16, 256, deblock_meta);
|
||||
}
|
||||
|
||||
static void bench_deblock_h(void) {
|
||||
daedalus_dispatch_h264_deblock_luma_h(ctx, DAEDALUS_SUBSTRATE_CPU,
|
||||
daedalus_dispatch_h264_deblock_luma_h(ctx, g_sub,
|
||||
deblock_dst, 16, 256, deblock_meta);
|
||||
}
|
||||
|
||||
@@ -116,18 +110,43 @@ static uint8_t qpel_dst[256 * 16 * 16];
|
||||
static daedalus_h264_qpel_meta qpel_meta[256];
|
||||
|
||||
static void bench_qpel_mc20(void) {
|
||||
daedalus_dispatch_h264_qpel_mc20(ctx, DAEDALUS_SUBSTRATE_CPU,
|
||||
daedalus_dispatch_h264_qpel_mc20(ctx, g_sub,
|
||||
qpel_dst, qpel_src, 16, 256, qpel_meta);
|
||||
}
|
||||
static void bench_qpel_mc02(void) {
|
||||
daedalus_dispatch_h264_qpel_mc02(ctx, DAEDALUS_SUBSTRATE_CPU,
|
||||
daedalus_dispatch_h264_qpel_mc02(ctx, g_sub,
|
||||
qpel_dst, qpel_src, 16, 256, qpel_meta);
|
||||
}
|
||||
static void bench_qpel_mc22(void) {
|
||||
daedalus_dispatch_h264_qpel_mc22(ctx, DAEDALUS_SUBSTRATE_CPU,
|
||||
daedalus_dispatch_h264_qpel_mc22(ctx, g_sub,
|
||||
qpel_dst, qpel_src, 16, 256, qpel_meta);
|
||||
}
|
||||
|
||||
/* ---- One row of bench output:
|
||||
* - kernel name + N
|
||||
* - CPU ns/op
|
||||
* - QPU ns/op (or "n/a" if Vulkan absent)
|
||||
* - CPU/QPU ratio (>1 means QPU wins; <1 means CPU wins) */
|
||||
struct row {
|
||||
const char *name;
|
||||
int n_per_call;
|
||||
bench_fn fn;
|
||||
double cpu_ns;
|
||||
double qpu_ns; /* -1 if not measured */
|
||||
int frame_n; /* count per 1080p frame */
|
||||
};
|
||||
|
||||
static struct row rows[] = {
|
||||
{"IDCT 4x4 luma", 1024, bench_idct4, 0, -1, MBS_1080P * 16},
|
||||
{"IDCT 8x8 luma", 256, bench_idct8, 0, -1, MBS_1080P * 4},
|
||||
{"Deblock luma_v", 256, bench_deblock_v, 0, -1, MBS_1080P * 4},
|
||||
{"Deblock luma_h", 256, bench_deblock_h, 0, -1, MBS_1080P * 4},
|
||||
{"qpel mc20 (8x8)", 256, bench_qpel_mc20, 0, -1, MBS_1080P * 4},
|
||||
{"qpel mc02 (8x8)", 256, bench_qpel_mc02, 0, -1, MBS_1080P * 4},
|
||||
{"qpel mc22 (8x8)", 256, bench_qpel_mc22, 0, -1, MBS_1080P * 4},
|
||||
};
|
||||
#define N_ROWS ((int)(sizeof(rows)/sizeof(rows[0])))
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int iters = argc > 1 ? atoi(argv[1]) : 50;
|
||||
@@ -138,6 +157,7 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "ctx create failed (Vulkan?)\n");
|
||||
return 1;
|
||||
}
|
||||
int has_qpu = daedalus_ctx_has_qpu(ctx);
|
||||
|
||||
/* Pre-fill all input buffers with random data so the NEON inner
|
||||
* loops see realistic memory access patterns. */
|
||||
@@ -147,8 +167,7 @@ int main(int argc, char **argv)
|
||||
idct8_coeffs[i] = (int16_t)((int)(xs64() % 1024) - 512);
|
||||
for (size_t i = 0; i < sizeof(qpel_src); i++) qpel_src[i] = (uint8_t)(xs64() & 0xff);
|
||||
|
||||
/* IDCT meta: each block at offset i*16 (row layout matters less
|
||||
* here since we're just measuring per-block latency). */
|
||||
/* IDCT meta. */
|
||||
for (size_t i = 0; i < 1024; i++)
|
||||
idct4_meta[i].dst_off = (uint32_t)((i / 16) * 64 + (i % 16) * 4);
|
||||
for (size_t i = 0; i < 256; i++)
|
||||
@@ -162,58 +181,88 @@ int main(int argc, char **argv)
|
||||
for (int s = 0; s < 4; s++) deblock_meta[i].tc0[s] = (int8_t)(s + 1);
|
||||
}
|
||||
|
||||
/* qpel meta: src and dst at row 3 col 3 of each 16x16 tile. */
|
||||
/* qpel meta. */
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
qpel_meta[i].src_off = (uint32_t)(i * 256 + 3 * 16 + 3);
|
||||
qpel_meta[i].dst_off = (uint32_t)(i * 256 + 3 * 16 + 3);
|
||||
}
|
||||
|
||||
printf("bench_h264_primitives: %d iters (%d warmup), substrate=CPU NEON\n",
|
||||
iters, warmup);
|
||||
printf("Per-call N is set per kernel; ns/op is per BLOCK or EDGE.\n\n");
|
||||
printf("bench_h264_primitives: %d iters (%d warmup)\n", iters, warmup);
|
||||
printf(" ctx has_qpu=%d (CPU pass always runs; QPU pass skipped without Vulkan)\n\n", has_qpu);
|
||||
|
||||
double idct4_ns = bench_ns("IDCT 4x4 luma", iters, warmup, 1024, bench_idct4);
|
||||
double idct8_ns = bench_ns("IDCT 8x8 luma", iters, warmup, 256, bench_idct8);
|
||||
double debl_v_ns = bench_ns("Deblock luma_v", iters, warmup, 256, bench_deblock_v);
|
||||
double debl_h_ns = bench_ns("Deblock luma_h", iters, warmup, 256, bench_deblock_h);
|
||||
double qmc20_ns = bench_ns("qpel mc20 (8x8)", iters, warmup, 256, bench_qpel_mc20);
|
||||
double qmc02_ns = bench_ns("qpel mc02 (8x8)", iters, warmup, 256, bench_qpel_mc02);
|
||||
double qmc22_ns = bench_ns("qpel mc22 (8x8)", iters, warmup, 256, bench_qpel_mc22);
|
||||
/* Pass 1: CPU NEON. */
|
||||
g_sub = DAEDALUS_SUBSTRATE_CPU;
|
||||
printf("== CPU NEON ==\n");
|
||||
for (int i = 0; i < N_ROWS; i++)
|
||||
rows[i].cpu_ns = bench_ns(rows[i].name, iters, warmup, rows[i].n_per_call, rows[i].fn);
|
||||
|
||||
/* Per-frame budget summary at 1080p (8160 MBs). Worst-case
|
||||
* assumptions:
|
||||
* - All MBs are transform_4x4 (16 4x4 IDCTs each) — so 130,560
|
||||
* IDCT 4x4 blocks per frame. If High profile transform_8x8,
|
||||
* it'd be 32,640 IDCT 8x8 blocks instead.
|
||||
* - All MBs are intra (no MC — qpel zero) OR all inter (no
|
||||
* intra prediction). We report MC at "all inter, all qpel
|
||||
* mc22" worst case.
|
||||
* - Deblock: ~4 luma_v + 4 luma_h edges per MB; assume all 8
|
||||
* edges trigger filtering. */
|
||||
printf("\nProjected 1080p frame budgets (worst-case, CPU NEON only):\n");
|
||||
printf(" IDCT 4x4 (all-4x4 MBs): %7.2f ms (%d blocks)\n",
|
||||
idct4_ns * MBS_1080P * 16 / 1e6, MBS_1080P * 16);
|
||||
printf(" IDCT 8x8 (all-8x8 MBs): %7.2f ms (%d blocks)\n",
|
||||
idct8_ns * MBS_1080P * 4 / 1e6, MBS_1080P * 4);
|
||||
printf(" Deblock luma_v (all MBs): %7.2f ms (%d edges)\n",
|
||||
debl_v_ns * MBS_1080P * 4 / 1e6, MBS_1080P * 4);
|
||||
printf(" Deblock luma_h (all MBs): %7.2f ms (%d edges)\n",
|
||||
debl_h_ns * MBS_1080P * 4 / 1e6, MBS_1080P * 4);
|
||||
printf(" qpel mc22 (all 8x8 blocks): %7.2f ms (%d blocks)\n",
|
||||
qmc22_ns * MBS_1080P * 4 / 1e6, MBS_1080P * 4);
|
||||
/* Pass 2: QPU compute (if available). */
|
||||
if (has_qpu) {
|
||||
g_sub = DAEDALUS_SUBSTRATE_QPU;
|
||||
printf("\n== QPU V3D7 compute ==\n");
|
||||
for (int i = 0; i < N_ROWS; i++)
|
||||
rows[i].qpu_ns = bench_ns(rows[i].name, iters, warmup, rows[i].n_per_call, rows[i].fn);
|
||||
}
|
||||
|
||||
/* Summary table — both substrates side by side. */
|
||||
printf("\n== Per-kernel comparison ==\n");
|
||||
printf(" %-24s %12s %12s %8s %7s\n",
|
||||
"kernel", "CPU ns/op", "QPU ns/op", "winner", "ms/frame");
|
||||
for (int i = 0; i < N_ROWS; i++) {
|
||||
double cpu_ms = rows[i].cpu_ns * rows[i].frame_n / 1e6;
|
||||
double qpu_ms = rows[i].qpu_ns > 0 ? rows[i].qpu_ns * rows[i].frame_n / 1e6 : -1;
|
||||
const char *winner;
|
||||
char ratio[16];
|
||||
if (rows[i].qpu_ns <= 0) {
|
||||
winner = "CPU"; /* QPU n/a */
|
||||
snprintf(ratio, sizeof(ratio), "n/a");
|
||||
} else if (rows[i].cpu_ns < rows[i].qpu_ns) {
|
||||
winner = "CPU";
|
||||
snprintf(ratio, sizeof(ratio), "%.2fx", rows[i].qpu_ns / rows[i].cpu_ns);
|
||||
} else {
|
||||
winner = "QPU";
|
||||
snprintf(ratio, sizeof(ratio), "%.2fx", rows[i].cpu_ns / rows[i].qpu_ns);
|
||||
}
|
||||
char qpu_field[16];
|
||||
if (rows[i].qpu_ns > 0) snprintf(qpu_field, sizeof(qpu_field), "%.2f", rows[i].qpu_ns);
|
||||
else snprintf(qpu_field, sizeof(qpu_field), "n/a");
|
||||
char ms_field[24];
|
||||
if (qpu_ms > 0)
|
||||
snprintf(ms_field, sizeof(ms_field), "%.2f/%.2f", cpu_ms, qpu_ms);
|
||||
else
|
||||
snprintf(ms_field, sizeof(ms_field), "%.2f/n/a", cpu_ms);
|
||||
printf(" %-24s %12.2f %12s %3s %s %s\n",
|
||||
rows[i].name, rows[i].cpu_ns, qpu_field, winner, ratio, ms_field);
|
||||
}
|
||||
|
||||
/* Per-frame budget summary at 1080p (8160 MBs). */
|
||||
double cpu_idct4 = rows[0].cpu_ns * MBS_1080P * 16 / 1e6;
|
||||
double cpu_debl = (rows[2].cpu_ns + rows[3].cpu_ns) * MBS_1080P * 4 / 1e6;
|
||||
double cpu_mc = rows[6].cpu_ns * MBS_1080P * 4 / 1e6; /* mc22 worst-case */
|
||||
double cpu_sum = cpu_idct4 + cpu_debl + cpu_mc;
|
||||
|
||||
printf("\n== Projected 1080p worst-case (CPU NEON only) ==\n");
|
||||
printf(" IDCT 4x4 + deblock luma + qpel mc22: %.2f ms (30fps deadline 33.33)\n", cpu_sum);
|
||||
printf(" Margin: %+.2f ms\n", 33.33 - cpu_sum);
|
||||
|
||||
if (has_qpu) {
|
||||
double qpu_idct4 = rows[0].qpu_ns * MBS_1080P * 16 / 1e6;
|
||||
double qpu_debl = (rows[2].qpu_ns + rows[3].qpu_ns) * MBS_1080P * 4 / 1e6;
|
||||
double qpu_mc = rows[6].qpu_ns * MBS_1080P * 4 / 1e6;
|
||||
double qpu_sum = qpu_idct4 + qpu_debl + qpu_mc;
|
||||
printf("\n== Projected 1080p worst-case (QPU V3D7 compute only) ==\n");
|
||||
printf(" IDCT 4x4 + deblock luma + qpel mc22: %.2f ms (30fps deadline 33.33)\n", qpu_sum);
|
||||
printf(" Margin: %+.2f ms\n", 33.33 - qpu_sum);
|
||||
printf("\n CPU vs QPU sum ratio: %.2fx (>1 means QPU wins)\n",
|
||||
qpu_sum > 0 ? cpu_sum / qpu_sum : 0.0);
|
||||
}
|
||||
|
||||
double sum_idct_4x4 = idct4_ns * MBS_1080P * 16 / 1e6;
|
||||
double sum_deblock = (debl_v_ns + debl_h_ns) * MBS_1080P * 4 / 1e6;
|
||||
double sum_mc = qmc22_ns * MBS_1080P * 4 / 1e6; /* worst-case all-mc22 */
|
||||
printf("\n Sum (IDCT 4x4 + deblock luma + MC all-mc22): %7.2f ms\n",
|
||||
sum_idct_4x4 + sum_deblock + sum_mc);
|
||||
printf(" 30 fps deadline: 33.33 ms\n");
|
||||
printf(" Margin: %+.2f ms\n",
|
||||
33.33 - (sum_idct_4x4 + sum_deblock + sum_mc));
|
||||
printf("\n(NOT included: chroma deblock, chroma IDCT, intra prediction,\n");
|
||||
printf(" CABAC/CAVLC entropy. These bench numbers are a budget LOWER\n");
|
||||
printf(" bound; the real decode stack adds 20-40%% on top.)\n");
|
||||
(void) qmc20_ns; (void) qmc02_ns;
|
||||
printf(" bound; the real decode stack adds 20-40%% on top.\n");
|
||||
printf(" Per-kernel substrate decisions belong in daedalus_core.c recipe\n");
|
||||
printf(" table; the QPU substrate decree (2026-05-23) keeps everything\n");
|
||||
printf(" on QPU regardless of these numbers as a policy choice.)\n");
|
||||
|
||||
daedalus_ctx_destroy(ctx);
|
||||
return 0;
|
||||
|
||||
@@ -683,13 +683,13 @@ int main(void)
|
||||
printf(" H264_QPEL_MC20 recipe substrate: %d\n",
|
||||
(int) daedalus_recipe_substrate_for(DAEDALUS_KERNEL_H264_QPEL_MC20));
|
||||
|
||||
printf(" H264_DEBLOCK_LH recipe substrate: %d (CPU, no QPU H shader yet)\n",
|
||||
printf(" H264_DEBLOCK_LH recipe substrate: %d\n",
|
||||
(int) daedalus_recipe_substrate_for(DAEDALUS_KERNEL_H264_DEBLOCK_LH));
|
||||
printf(" H264_DEBLOCK_CV recipe substrate: %d (CPU)\n",
|
||||
printf(" H264_DEBLOCK_CV recipe substrate: %d\n",
|
||||
(int) daedalus_recipe_substrate_for(DAEDALUS_KERNEL_H264_DEBLOCK_CV));
|
||||
printf(" H264_DEBLOCK_CH recipe substrate: %d (CPU)\n",
|
||||
printf(" H264_DEBLOCK_CH recipe substrate: %d\n",
|
||||
(int) daedalus_recipe_substrate_for(DAEDALUS_KERNEL_H264_DEBLOCK_CH));
|
||||
printf(" H264_DEBLOCK_*_INTRA recipe substrate: %d (CPU, bS=4 set)\n",
|
||||
printf(" H264_DEBLOCK_*_INTRA recipe substrate: %d (bS=4 family, all on QPU)\n",
|
||||
(int) daedalus_recipe_substrate_for(DAEDALUS_KERNEL_H264_DEBLOCK_LV_INTRA));
|
||||
|
||||
int fail = 0;
|
||||
|
||||
Reference in New Issue
Block a user