blob: 4507610ab85976971517d881b2c4023ddc29d8a7 [file] [edit]
// basisu_bc15_spmd.h
// Standalone opaque 4-color BC1 (DXT1) encoder for the Basis Universal encoder library.
//
// Two entry points share the SAME algorithm (omatch-solid fast path -> PCA endpoint seed -> 2x integer-threshold
// least-squares -> low-variance avg-solid 2nd seed (keep-better) -> integer-threshold selectors):
// - encode_bc1_scalar : portable scalar reference ("oracle"), no SIMD.
// - encode_bc1_spmd : SSE4.1 cppspmd kernel, 4 blocks per vector; falls back to the scalar path when SSE4.1
// is unavailable (e.g. WASM without SIMD).
// Benchmarked (vs basist::encode_bc1): ~3.4-4.7x faster on photos, ~+0.06 dB average, and it beats encode_bc1
// on low-dynamic-range / gradient blocks. Opaque 4-color only (no 3-color / punchthrough).
#pragma once
#include "basisu_enc.h" // basisu::color_rgba
namespace basisu
{
namespace bc_spmd
{
// Build the solid-color omatch tables. Call once before encoding (idempotent). Not safe to call
// concurrently with itself or the encoders on first use -- call it once at startup (e.g. right after
// basisu_encoder_init(), which also performs the CPU-feature detection encode_bc1_spmd dispatches on).
void init();
// Encode num_blocks opaque BC1 blocks.
// pSrc_pixels : num_blocks * 16 color_rgba, block-contiguous; texel index within a block = y*4 + x.
// pBlocks : output; BC1 block b is written at (uint8_t*)pBlocks + b * 8 * block_stride.
// num_blocks : any count; counts not divisible by 4 are handled internally (no caller padding needed).
// block_stride : output spacing in units of 8-byte BC1 blocks. 1 = contiguous BC1. 2 = write into every
// other 8-byte slot, e.g. the color half of 16-byte BC3/BC5 blocks (pass pBlocks already
// offset to that half). Input layout is unaffected.
// The caller is responsible for extracting the 16 pixels per block from its source image.
void encode_bc1_scalar(void* pBlocks, const color_rgba* pSrc_pixels, uint32_t num_blocks, uint32_t block_stride = 1);
void encode_bc1_spmd(void* pBlocks, const color_rgba* pSrc_pixels, uint32_t num_blocks, uint32_t block_stride = 1);
// Encode num_blocks single-channel BC4 (RGTC1) blocks. Clone of basist::encode_bc4's algorithm: raw bbox
// min/max endpoints + exact nearest-of-8 integer-threshold selector, ALWAYS 8-value mode (red0 > red1) --
// including a forced-8-value solid-block path so we NEVER emit a 6-value block (safe for BC3/BC5). Quality
// matches encode_bc4 (bit-exact on non-solid blocks; identical decode on solid). (Least-squares endpoint
// refinement may be layered on later.)
// pSrc_pixels : num_blocks * 16 uint8_t, block-contiguous; texel index within a block = y*4 + x. The
// caller extracts the 16 single-channel values per block itself (e.g. one channel of RGBA).
// pBlocks : output; BC4 block b is written at (uint8_t*)pBlocks + b * 8 * block_stride.
// num_blocks : any count; the <4 tail is handled internally.
// block_stride : output spacing in units of 8-byte BC4 blocks. 1 = contiguous BC4. 2 = write into every
// other 8-byte slot, e.g. the alpha half of a 16-byte BC3 block or one half of a BC5 pair.
// high_quality : false (default) = fast bbox encoder, matches encode_bc4 quality, ~1.4x faster. true = add a
// 1-D least-squares endpoint refit (keep-best, monotonic): ~+0.8 dB on photos but ~0.6x speed.
// src_stride : BYTE stride between consecutive texel values in pSrc_pixels. 1 (default) = tightly packed
// single channel. 4 = one channel of color_rgba (point pSrc_pixels at the desired channel
// byte, e.g. &rgba[0].g) -- lets callers encode a channel in place with no extraction copy.
void encode_bc4_scalar(void* pBlocks, const uint8_t* pSrc_pixels, uint32_t num_blocks, uint32_t block_stride = 1, bool high_quality = false, uint32_t src_stride = 1);
void encode_bc4_spmd(void* pBlocks, const uint8_t* pSrc_pixels, uint32_t num_blocks, uint32_t block_stride = 1, bool high_quality = false, uint32_t src_stride = 1);
// ---------------- High-level format helpers (RGBA in -> complete GPU blocks out) ----------------
// Convenience wrappers over the low-level encoders. Each takes pPixels = num_blocks * 16 color_rgba,
// block-contiguous (texel index = y*4 + x), and writes complete blocks. num_blocks may be ANY count >= 1
// (not necessarily a multiple of 4); the <4 tail is handled internally with NO output overrun.
// use_spmd : true = SSE4.1 SPMD path (auto-falls back to scalar when SSE4.1 is unavailable); false = scalar.
// high_quality : enables the BC4 channel least-squares refit for the alpha/red/green block(s) (BC3/BC4/BC5).
// Output block sizes / layout (matches the transcoder's bcu unpackers):
// BC1: 8 bytes/block -- RGB, opaque 4-color.
// BC2: 16 bytes/block -- explicit 4-bit alpha [0..7] (scalar) + BC1 color [8..15].
// BC3: 16 bytes/block -- BC4 alpha block [0..7] + BC1 color [8..15].
// BC4: 8 bytes/block -- single channel = R.
// BC5: 16 bytes/block -- BC4 of R [0..7] + BC4 of G [8..15].
void encode_bc1(void* pBlocks, const color_rgba* pPixels, uint32_t num_blocks, bool use_spmd = true);
void encode_bc2(void* pBlocks, const color_rgba* pPixels, uint32_t num_blocks, bool use_spmd = true);
void encode_bc3(void* pBlocks, const color_rgba* pPixels, uint32_t num_blocks, bool use_spmd = true, bool high_quality = false);
void encode_bc4(void* pBlocks, const color_rgba* pPixels, uint32_t num_blocks, bool use_spmd = true, bool high_quality = false);
void encode_bc5(void* pBlocks, const color_rgba* pPixels, uint32_t num_blocks, bool use_spmd = true, bool high_quality = false);
} // namespace bc_spmd
} // namespace basisu