# SPDX-License-Identifier: BSD-2-Clause
#
# daedalus-decoder — frame-level GPU H.264 decoder for V3D7 (Pi 5).
# Phase 1 scaffold; see DESIGN.md for architecture.
#
# Build dependencies:
#   - daedalus-fourier ≥ 0.1.0 (kernel pack, V3D primitives + recipe layer)
#     resolved via pkg-config; install via the daedalus-fourier upstream
#     `cmake --install` rule (PR #5 made the .pc relocatable, so any
#     install prefix works as long as $PKG_CONFIG_PATH is set).
#   - Vulkan headers + libvulkan (pulled in transitively via
#     daedalus-fourier, listed here explicitly for the link order).
#
# Build:
#   cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
#   cmake --build build
#   ctest --test-dir build

cmake_minimum_required(VERSION 3.20)
project(daedalus-decoder
    VERSION 0.0.1
    DESCRIPTION "Frame-level GPU H.264 decoder for Raspberry Pi 5 / V3D7"
    LANGUAGES C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

# Pi 5 is the only supported target.  Other aarch64 SoCs (Pi 4 V3D4,
# RK3588 Mali, …) might work but would need explicit substrate +
# shader-pack validation per the daedalus-fourier architecture
# backlog.  Don't pretend to support what we haven't validated.
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
    message(WARNING
        "daedalus-decoder is designed for aarch64 (Pi 5 BCM2712 / V3D7). "
        "Build will proceed but is unlikely to function.")
endif()

add_compile_options(-Wall -Wextra -Wno-unused-parameter)

# ---- Dependencies --------------------------------------------------

find_package(PkgConfig REQUIRED)

# daedalus-fourier — find_package via pkg-config per the Phase 1
# decision §9.6.  Minimum version 0.1.0 (the cycle 6-9 shaders + pool
# + recipe-flip baseline).  PKG_CONFIG_PATH should point at the
# directory holding daedalus-fourier.pc (e.g. /usr/local/lib/pkgconfig
# or a custom install prefix).
pkg_check_modules(DAEDALUS_FOURIER REQUIRED daedalus-fourier>=0.1.0)

# Vulkan — daedalus-fourier already depends on this; we add it
# explicitly so the link order stays correct (daedalus-fourier static
# archive contains undefined vk* symbols that the loader resolves).
find_package(Vulkan REQUIRED)

# ---- Version string baked into the library ------------------------

# git rev tagged onto the version string for traceability; degrades
# gracefully to bare semver if git isn't available.
execute_process(
    COMMAND git -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse --short=7 HEAD
    OUTPUT_VARIABLE DAEDALUS_DECODER_GITREV
    OUTPUT_STRIP_TRAILING_WHITESPACE
    ERROR_QUIET)
if(DAEDALUS_DECODER_GITREV)
    set(DAEDALUS_DECODER_VERSION "${PROJECT_VERSION}+g${DAEDALUS_DECODER_GITREV}")
else()
    set(DAEDALUS_DECODER_VERSION "${PROJECT_VERSION}")
endif()
message(STATUS "daedalus-decoder version: ${DAEDALUS_DECODER_VERSION}")

# ---- Library ------------------------------------------------------

add_library(daedalus_decoder STATIC
    src/daedalus_decoder.c
)
target_include_directories(daedalus_decoder
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
    PRIVATE
        src
        ${DAEDALUS_FOURIER_INCLUDE_DIRS}
)
target_link_directories(daedalus_decoder
    PUBLIC
        ${DAEDALUS_FOURIER_LIBRARY_DIRS}
)
target_link_libraries(daedalus_decoder
    PUBLIC
        # Order matters: daedalus-fourier static archive references
        # vulkan symbols; the loader needs daedalus-fourier first then
        # vulkan to resolve them.
        ${DAEDALUS_FOURIER_LIBRARIES}
        Vulkan::Vulkan
)
target_compile_definitions(daedalus_decoder
    PRIVATE
        DAEDALUS_DECODER_VERSION="${DAEDALUS_DECODER_VERSION}"
)
target_compile_options(daedalus_decoder PRIVATE -O2)

# ---- Smoke test ---------------------------------------------------

enable_testing()

add_executable(test_smoke tests/test_smoke.c)
target_link_libraries(test_smoke PRIVATE daedalus_decoder)
target_compile_options(test_smoke PRIVATE -O2)
add_test(NAME smoke COMMAND test_smoke)

add_executable(test_idct_bitexact tests/test_idct_bitexact.c)
target_link_libraries(test_idct_bitexact PRIVATE daedalus_decoder)
target_compile_options(test_idct_bitexact PRIVATE -O2)

# 320x240 QVGA — fast inner-loop test (300 MBs, sub-second).
add_test(NAME idct_bitexact COMMAND test_idct_bitexact)

# Same QVGA test re-run on the CPU NEON path (forces fallback even on
# V3D7 hosts).  Catches silent drift between the V3D shader and the
# NEON reference path — both must produce identical output for the
# same coefficient input.  Also keeps the bit-exact gate alive on
# hosts without V3D7 (CI runners, x86 dev boxes).
add_test(NAME idct_bitexact_cpu COMMAND test_idct_bitexact 320 240
         0xfeedface5a5a5a5a cpu)

# 1920x1088 1080p — deployment-scale test (8160 MBs, ~0.25 s on hertz).
# Validates the per-MB block index + pixel offset math at full coded
# height (1088, not 1080 — see daedalus_decoder.h on H.264 coded vs
# displayed dims).  Cheap enough to run unconditionally; if it ever
# gets slow we'll split into a CTest LABEL for opt-in.
add_test(NAME idct_bitexact_1080p COMMAND test_idct_bitexact 1920 1088)

# ---- Stage 2 PR-b deblock smoke ------------------------------------
#
# Validates flush_frame's per-frame deblock dispatch (luma + chroma,
# V + H, bS<4 + bS=4 intra — up to 8 dispatches added after IDCT).
# Strategy: same input through substrate=CPU and substrate=QPU, assert
# byte-exact match (transitive bit-exact gate — daedalus-fourier's own
# test_api_h264 already validates each substrate against a C reference,
# so CPU-QPU equivalence here means both match the spec).  Plus an
# anti-no-op check: run a third pass with edges removed and assert
# different output, proving deblock actually ran.
add_executable(test_deblock_smoke tests/test_deblock_smoke.c)
target_link_libraries(test_deblock_smoke PRIVATE daedalus_decoder)
target_compile_options(test_deblock_smoke PRIVATE -O2)
add_test(NAME deblock_smoke COMMAND test_deblock_smoke)

# ---- Benchmarks (not gated by ctest) ------------------------------
#
# Build-time only; user runs them by hand when checking perf.  Adding
# them as ctest would make every CI run slow and the numbers would
# get drowned in pass/fail noise.  See the header of each .c for what
# they measure.

add_executable(bench_flush_frame tests/bench_flush_frame.c)
target_link_libraries(bench_flush_frame PRIVATE daedalus_decoder)
target_compile_options(bench_flush_frame PRIVATE -O2)

# ---- Tools (not gated by ctest; opt-in via DAEDALUS_BUILD_TOOLS) ----
#
# daedalus_decode_h264 — option A standalone test harness that
# wraps libavcodec + daedalus-decoder and bit-exact-compares their
# outputs on real H.264 streams.  Identity-passthrough mode in this
# first iteration (predicted = AVFrame pixels, coeffs = 0, no
# deblock edges); follow-up PRs use the per-MB inspection callback
# (marfrit-packages patch 0016) to feed REAL per-MB state.
#
# Requires libavcodec + libavformat headers + libs.  Off by default
# so the standard ctest build doesn't pull in FFmpeg as a hard dep.
option(DAEDALUS_BUILD_TOOLS "Build daedalus-decoder CLI tools (requires libavcodec)" OFF)
if(DAEDALUS_BUILD_TOOLS)
    pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libavutil)
    add_executable(daedalus_decode_h264 tools/daedalus_decode_h264.c)
    target_link_libraries(daedalus_decode_h264
        PRIVATE daedalus_decoder ${FFMPEG_LIBRARIES})
    target_include_directories(daedalus_decode_h264
        PRIVATE ${FFMPEG_INCLUDE_DIRS})
    target_link_directories(daedalus_decode_h264
        PRIVATE ${FFMPEG_LIBRARY_DIRS})
    target_compile_options(daedalus_decode_h264
        PRIVATE -O2 ${FFMPEG_CFLAGS_OTHER})
endif()

# ---- Install ------------------------------------------------------
#
# Library + public header.  Stage 2/3 will add a pkg-config file and
# CMake config exports once the API stabilises; pre-0.1 the scaffold
# install just gives the static archive a home.

include(GNUInstallDirs)
install(TARGETS daedalus_decoder
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES include/daedalus_decoder.h
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
