// Copyright (c) 2025 Hans-Kristian Arntzen // SPDX-License-Identifier: MIT #pragma once #include #include "device.hpp" #include "buffer.hpp" #include "image.hpp" #include "pyrowave_config.hpp" #include "shaders/slangmosh.hpp" namespace PyroWave { struct BitstreamPacket { uint32_t offset_u32; uint32_t num_words; }; struct BitstreamHeader { uint16_t ballot; uint16_t payload_words : 12; uint16_t sequence : 3; uint16_t extended : 1; uint32_t quant_code : 8; uint32_t block_index : 24; }; static_assert(sizeof(BitstreamHeader) == 8, "BitstreamHeader is not 8 bytes."); enum { BITSTREAM_EXTENDED_CODE_START_OF_FRAME = 0, }; enum { CHROMA_RESOLUTION_420 = 0, CHROMA_RESOLUTION_444 = 1 }; enum { CHROMA_SITING_CENTER = 0, CHROMA_SITING_LEFT = 1 }; enum { YCBCR_RANGE_FULL = 0, YCBCR_RANGE_LIMITED = 1 }; enum { COLOR_PRIMARIES_BT709 = 0, COLOR_PRIMARIES_BT2020 = 1 }; enum { YCBCR_TRANSFORM_BT709 = 0, YCBCR_TRANSFORM_BT2020 = 1 }; enum { TRANSFER_FUNCTION_BT709 = 0, TRANSFER_FUNCTION_PQ = 1 }; static constexpr uint32_t SequenceCountMask = 0x7; struct BitstreamSequenceHeader { uint32_t width_minus_1 : 14; uint32_t height_minus_1 : 14; uint32_t sequence : 3; uint32_t extended : 1; uint32_t total_blocks : 24; uint32_t code : 2; uint32_t chroma_resolution : 1; uint32_t color_primaries : 1; uint32_t transfer_function : 1; uint32_t ycbcr_transform : 1; uint32_t ycbcr_range : 1; uint32_t chroma_siting : 1; }; static_assert(sizeof(BitstreamSequenceHeader) == 8, "BitstreamSequenceHeader is not 8 bytes."); struct QuantStats { uint16_t square_error_fp16; uint16_t encode_cost_bits; }; struct BlockStats { uint32_t num_planes; QuantStats stats[15]; }; static_assert(sizeof(BlockStats) == 64, "BlockStats is not 64 bytes."); struct BlockMeta { uint32_t code_word; uint32_t offset; }; static constexpr int DecompositionLevels = 5; static constexpr int Alignment = 1 << DecompositionLevels; // If the final decomposition band is too small, the mirroring will break since it starts double mirroring. static constexpr int MinimumImageSize = 4 << DecompositionLevels; static constexpr int NumComponents = 3; static constexpr int NumFrequencyBandsPerLevel = 4; static inline int align(int value, int align) { return (value + align - 1) & ~(align - 1); } static constexpr int MaxScaleExp = 4; static inline float decode_quant(uint8_t quant_code) { // Custom FP formulation for numbers in (0, 2) range. int e = MaxScaleExp - (quant_code >> 3); int m = quant_code & 0x7; float inv_quant = (1.0f / (8.0f * 1024.0f * 1024.0f)) * float((8 + m) * (1 << (20 + e))); return inv_quant; } static inline uint8_t encode_quant(float decoder_q_scale) { uint32_t v; memcpy(&v, &decoder_q_scale, sizeof(decoder_q_scale)); int e = ((v >> 23) & 0xff) - 127 - MaxScaleExp; int m = (v >> 20) & 0x7; e = -e; assert(e >= 0 && e <= 20); return (e << 3) | m; } class Configuration { public: static Configuration &get(); int get_precision() const; private: Configuration(); int precision; }; struct WaveletBuffers { bool init(Vulkan::Device *device, int width, int height, ChromaSubsampling chroma, bool fragment_path); Vulkan::Device *device = nullptr; Vulkan::ImageHandle wavelet_img_low_res; Vulkan::ImageHandle wavelet_img_high_res; Vulkan::SamplerHandle mirror_repeat_sampler; Vulkan::SamplerHandle border_sampler; Vulkan::ImageViewHandle component_layer_views[NumComponents][DecompositionLevels]; Vulkan::ImageViewHandle component_ll_views[NumComponents][DecompositionLevels]; // For fragment based iDWT. struct { struct { Vulkan::ImageHandle vert[2][2]; Vulkan::ImageHandle horiz[NumComponents]; Vulkan::ImageViewHandle decoded[NumComponents][NumFrequencyBandsPerLevel]; } levels[DecompositionLevels]; } fragment; struct BlockInfo { int block_offset_8x8; int block_stride_8x8; int block_offset_32x32; int block_stride_32x32; }; BlockInfo block_meta[NumComponents][DecompositionLevels][4] = {}; struct BlockMapping { int block_offset_8x8; int block_stride_8x8; int block_width_8x8; int block_height_8x8; }; std::vector block_32x32_to_8x8_mapping; int block_count_8x8 = 0; int block_count_32x32 = 0; int width = 0; int height = 0; int aligned_width = 0; int aligned_height = 0; bool use_readonly_texel_buffer = false; bool fragment_path = false; protected: void init_samplers(); void allocate_images(); void allocate_images_fragment(); virtual void init_block_meta(); ChromaSubsampling chroma = {}; Shaders<> shaders; private: void accumulate_block_mapping(int blocks_x_8x8, int blocks_y_8x8); }; }