Skip to content

Commit

Permalink
Merge pull request #8 from superdump/low-level-uastc-transcoder
Browse files Browse the repository at this point in the history
Add support for the low-level UASTC transcoder
  • Loading branch information
aclysma authored Jan 28, 2022
2 parents 1da3d5a + bda5ab6 commit c76bb01
Show file tree
Hide file tree
Showing 5 changed files with 407 additions and 3 deletions.
11 changes: 10 additions & 1 deletion basis-universal-sys/generate_bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ bindgen vendor/transcoding_wrapper.cpp -o src/transcoding_bindings.rs \
--whitelist-function basis_get_texture_type_name \
\
--whitelist-function basis_transcoder_format_is_uncompressed \
--whitelist-function basis_block_format_is_uncompressed \
--whitelist-function basis_get_uncompressed_bytes_per_pixel \
--whitelist-function basis_get_block_width \
--whitelist-function basis_get_block_height \
--whitelist-function basis_is_format_supported \
--whitelist-function basis_validate_output_buffer_size \
\
--whitelist-function low_level_uastc_transcoder_new \
--whitelist-function low_level_uastc_transcoder_delete \
\
--whitelist-function low_level_uastc_transcoder_transcode_slice \
\
--whitelist-function transcoder_new \
--whitelist-function transcoder_delete \
\
Expand All @@ -37,6 +43,9 @@ bindgen vendor/transcoding_wrapper.cpp -o src/transcoding_bindings.rs \
\
--whitelist-function basisu_transcoder_init \
\
--opaque-type LowLevelUastcTranscoder \
--opaque-type basist::block_format \
\
--opaque-type Transcoder \
--opaque-type basist::basisu_transcoder_state \
\
Expand Down Expand Up @@ -115,4 +124,4 @@ bindgen vendor/encoding_wrapper.cpp -o src/encoding_bindings.rs \
--opaque-type Compressor \
--opaque-type basisu::image \
\
-- -x c++ -std=c++14
-- -x c++ -std=c++14
50 changes: 50 additions & 0 deletions basis-universal-sys/src/transcoding_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,9 @@ extern "C" {
tex_type: basist_transcoder_texture_format
) -> bool;
}
extern "C" {
pub fn basis_block_format_is_uncompressed(fmt: basist_block_format) -> bool;
}
extern "C" {
pub fn basis_get_uncompressed_bytes_per_pixel(fmt: basist_transcoder_texture_format) -> u32;
}
Expand Down Expand Up @@ -910,6 +913,53 @@ extern "C" {
#[repr(C)]
#[repr(align(8))]
#[derive(Debug, Copy, Clone)]
pub struct LowLevelUastcTranscoder {
pub _bindgen_opaque_blob: u64,
}
#[test]
fn bindgen_test_layout_LowLevelUastcTranscoder() {
assert_eq!(
::std::mem::size_of::<LowLevelUastcTranscoder>(),
8usize,
concat!("Size of: ", stringify!(LowLevelUastcTranscoder))
);
assert_eq!(
::std::mem::align_of::<LowLevelUastcTranscoder>(),
8usize,
concat!("Alignment of ", stringify!(LowLevelUastcTranscoder))
);
}
extern "C" {
pub fn low_level_uastc_transcoder_new() -> *mut LowLevelUastcTranscoder;
}
extern "C" {
pub fn low_level_uastc_transcoder_delete(transcoder: *mut LowLevelUastcTranscoder);
}
extern "C" {
pub fn low_level_uastc_transcoder_transcode_slice(
transcoder: *mut LowLevelUastcTranscoder,
pDst_blocks: *mut ::std::os::raw::c_void,
num_blocks_x: u32,
num_blocks_y: u32,
pImage_data: *const u8,
image_data_size: u32,
fmt: basist_block_format,
output_block_or_pixel_stride_in_bytes: u32,
bc1_allow_threecolor_blocks: bool,
has_alpha: bool,
orig_width: u32,
orig_height: u32,
output_row_pitch_in_blocks_or_pixels: u32,
pState: *mut basist_basisu_transcoder_state,
output_rows_in_pixels: u32,
channel0: ::std::os::raw::c_int,
channel1: ::std::os::raw::c_int,
decode_flags: u32,
) -> bool;
}
#[repr(C)]
#[repr(align(8))]
#[derive(Debug, Copy, Clone)]
pub struct Transcoder {
pub _bindgen_opaque_blob: [u64; 2usize],
}
Expand Down
61 changes: 60 additions & 1 deletion basis-universal-sys/vendor/transcoding_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ extern "C" {
return basist::basis_transcoder_format_is_uncompressed(tex_type);
}

// Returns true if the block format is an uncompressed (raw pixel) format.
bool basis_block_format_is_uncompressed(basist::block_format fmt) {
return basist::basis_block_format_is_uncompressed(fmt);
}

// Returns the # of bytes per pixel for uncompressed formats, or 0 for block texture formats.
uint32_t basis_get_uncompressed_bytes_per_pixel(basist::transcoder_texture_format fmt) {
return basist::basis_get_uncompressed_bytes_per_pixel(fmt);
Expand Down Expand Up @@ -157,7 +162,61 @@ extern "C" {
// basisu_lowlevel_uastc_transcoder
//

// Not implemented
struct LowLevelUastcTranscoder {
basist::basisu_lowlevel_uastc_transcoder *pTranscoder;
};

LowLevelUastcTranscoder *low_level_uastc_transcoder_new() {
LowLevelUastcTranscoder *transcoder = new LowLevelUastcTranscoder;
transcoder->pTranscoder = new basist::basisu_lowlevel_uastc_transcoder();
return transcoder;
}

void low_level_uastc_transcoder_delete(LowLevelUastcTranscoder *transcoder) {
delete transcoder->pTranscoder;
delete transcoder;
}

bool low_level_uastc_transcoder_transcode_slice(
LowLevelUastcTranscoder *transcoder,
void* pDst_blocks,
uint32_t num_blocks_x,
uint32_t num_blocks_y,
const uint8_t* pImage_data,
uint32_t image_data_size,
basist::block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes,
bool bc1_allow_threecolor_blocks,
bool has_alpha,
const uint32_t orig_width,
const uint32_t orig_height,
uint32_t output_row_pitch_in_blocks_or_pixels,
basist::basisu_transcoder_state* pState,
uint32_t output_rows_in_pixels,
int channel0,
int channel1,
uint32_t decode_flags
) {
return transcoder->pTranscoder->transcode_slice(
pDst_blocks,
num_blocks_x,
num_blocks_y,
pImage_data,
image_data_size,
fmt,
output_block_or_pixel_stride_in_bytes,
bc1_allow_threecolor_blocks,
has_alpha,
orig_width,
orig_height,
output_row_pitch_in_blocks_or_pixels,
pState,
output_rows_in_pixels,
channel0,
channel1,
decode_flags
);
}


//
Expand Down
196 changes: 195 additions & 1 deletion basis-universal/src/transcoding/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ impl TranscoderTextureFormat {
unsafe { sys::basis_transcoder_format_has_alpha(self.into()) }
}

/// Returns true if the transcoder texture type is an uncompressed (raw pixel) format.
/// Returns true if the transcoder texture type is a compressed format.
pub fn is_compressed(self) -> bool {
unsafe { !sys::basis_transcoder_format_is_uncompressed(self.into()) }
}
Expand Down Expand Up @@ -324,3 +324,197 @@ bitflags::bitflags! {
const HIGH_QULITY = sys::basist_basisu_decode_flags_cDecodeFlagsHighQuality;
}
}

/// The block format to transcode universal texture data into
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(i32)]
pub enum TranscoderBlockFormat {
/// ETC1S RGB
ETC1 = sys::basist_block_format_cETC1,
/// full ETC2 EAC RGBA8 block
ETC2_RGBA = sys::basist_block_format_cETC2_RGBA,
/// DXT1 RGB
BC1 = sys::basist_block_format_cBC1,
/// BC4 block followed by a four color BC1 block
BC3 = sys::basist_block_format_cBC3,
/// DXT5A (alpha block only)
BC4 = sys::basist_block_format_cBC4,
/// two BC4 blocks
BC5 = sys::basist_block_format_cBC5,
/// opaque-only PVRTC1 4bpp
PVRTC1_4_RGB = sys::basist_block_format_cPVRTC1_4_RGB,
/// PVRTC1 4bpp RGBA
PVRTC1_4_RGBA = sys::basist_block_format_cPVRTC1_4_RGBA,
/// Full BC7 block, any mode
BC7 = sys::basist_block_format_cBC7,
/// RGB BC7 mode 5 color (writes an opaque mode 5 block)
BC7_M5_COLOR = sys::basist_block_format_cBC7_M5_COLOR,
/// alpha portion of BC7 mode 5 (cBC7_M5_COLOR output data must have been written to the output buffer first to set the mode/rot fields etc.)
BC7_M5_ALPHA = sys::basist_block_format_cBC7_M5_ALPHA,
/// alpha block of ETC2 EAC (first 8 bytes of the 16-bit ETC2 EAC RGBA format)
ETC2_EAC_A8 = sys::basist_block_format_cETC2_EAC_A8,
/// ASTC 4x4 (either color-only or color+alpha). Note that the transcoder always currently assumes sRGB is not enabled when outputting ASTC
/// data. If you use a sRGB ASTC format you'll get ~1 LSB of additional error, because of the different way ASTC decoders scale 8-bit endpoints to 16-bits during unpacking.
ASTC_4x4 = sys::basist_block_format_cASTC_4x4,

ATC_RGB = sys::basist_block_format_cATC_RGB,
ATC_RGBA_INTERPOLATED_ALPHA = sys::basist_block_format_cATC_RGBA_INTERPOLATED_ALPHA,
/// Opaque-only, has oddball 8x4 pixel block size
FXT1_RGB = sys::basist_block_format_cFXT1_RGB,

PVRTC2_4_RGB = sys::basist_block_format_cPVRTC2_4_RGB,
PVRTC2_4_RGBA = sys::basist_block_format_cPVRTC2_4_RGBA,

ETC2_EAC_R11 = sys::basist_block_format_cETC2_EAC_R11,
ETC2_EAC_RG11 = sys::basist_block_format_cETC2_EAC_RG11,

/// Used internally: Write 16-bit endpoint and selector indices directly to output (output block must be at least 32-bits)
Indices = sys::basist_block_format_cIndices,

/// Writes RGB components to 32bpp output pixels
RGB32 = sys::basist_block_format_cRGB32,
/// Writes RGB255 components to 32bpp output pixels
RGBA32 = sys::basist_block_format_cRGBA32,
/// Writes alpha component to 32bpp output pixels
A32 = sys::basist_block_format_cA32,

RGB565 = sys::basist_block_format_cRGB565,
BGR565 = sys::basist_block_format_cBGR565,

RGBA4444_COLOR = sys::basist_block_format_cRGBA4444_COLOR,
RGBA4444_ALPHA = sys::basist_block_format_cRGBA4444_ALPHA,
RGBA4444_COLOR_OPAQUE = sys::basist_block_format_cRGBA4444_COLOR_OPAQUE,
RGBA4444 = sys::basist_block_format_cRGBA4444,
}

impl Into<sys::basist_block_format> for TranscoderBlockFormat {
fn into(self) -> sys::basist_block_format {
self as sys::basist_block_format
}
}

impl From<sys::basist_block_format> for TranscoderBlockFormat {
fn from(value: sys::basist_block_format) -> Self {
unsafe { std::mem::transmute(value as i32) }
}
}

impl TranscoderBlockFormat {
/// For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel.
pub fn bytes_per_block_or_pixel(self) -> u32 {
match self {
TranscoderBlockFormat::ETC1 => 8,
TranscoderBlockFormat::ETC2_RGBA => 16,
TranscoderBlockFormat::BC1 => 8,
TranscoderBlockFormat::BC3 => 16,
TranscoderBlockFormat::BC4 => 8,
TranscoderBlockFormat::BC5 => 16,
TranscoderBlockFormat::PVRTC1_4_RGB => 8,
TranscoderBlockFormat::PVRTC1_4_RGBA => 8,
TranscoderBlockFormat::BC7 => 16,
TranscoderBlockFormat::BC7_M5_COLOR => 16,
TranscoderBlockFormat::BC7_M5_ALPHA => 16,
TranscoderBlockFormat::ETC2_EAC_A8 => 8,
TranscoderBlockFormat::ASTC_4x4 => 16,
TranscoderBlockFormat::ATC_RGB => 8,
TranscoderBlockFormat::ATC_RGBA_INTERPOLATED_ALPHA => 16,
TranscoderBlockFormat::FXT1_RGB => 8,
TranscoderBlockFormat::PVRTC2_4_RGB => 8,
TranscoderBlockFormat::PVRTC2_4_RGBA => 8,
TranscoderBlockFormat::ETC2_EAC_R11 => 8,
TranscoderBlockFormat::ETC2_EAC_RG11 => 16,
TranscoderBlockFormat::Indices => 2,
TranscoderBlockFormat::RGB32 => 4,
TranscoderBlockFormat::RGBA32 => 4,
TranscoderBlockFormat::A32 => 4,
TranscoderBlockFormat::RGB565 => 2,
TranscoderBlockFormat::BGR565 => 2,
TranscoderBlockFormat::RGBA4444_COLOR => 2,
TranscoderBlockFormat::RGBA4444_ALPHA => 2,
TranscoderBlockFormat::RGBA4444_COLOR_OPAQUE => 2,
TranscoderBlockFormat::RGBA4444 => 2,
}
}

/// Returns format's name in ASCII
pub fn format_name(self) -> &'static str {
unsafe {
let value = sys::basis_get_block_format_name(self.into());
CStr::from_ptr(value).to_str().unwrap()
}
}

/// Returns true if the block format is a compressed format.
pub fn is_compressed(self) -> bool {
unsafe { !sys::basis_block_format_is_uncompressed(self.into()) }
}

/// Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1.
pub fn block_width(self) -> u32 {
match self {
TranscoderBlockFormat::FXT1_RGB => 8,
_ => 4,
}
}

/// Returns the block height for the specified texture format, which is currently always 4.
pub fn block_height(self) -> u32 {
4
}

/// Calculate the minimum output buffer required to store transcoded data in blocks for
/// compressed formats and pixels for uncompressed formats
pub fn calculate_minimum_output_buffer_blocks_or_pixels(
self,
original_width: u32,
original_height: u32,
total_slice_blocks: u32,
output_row_pitch_in_blocks_or_pixels: Option<u32>,
output_rows_in_pixels: Option<u32>,
) -> u32 {
// Default of 0 is fine for these values
let mut output_row_pitch_in_blocks_or_pixels =
output_row_pitch_in_blocks_or_pixels.unwrap_or(0);
let mut output_rows_in_pixels = output_rows_in_pixels.unwrap_or(0);

// Derived from implementation of basis_validate_output_buffer_size

if !self.is_compressed() {
// Assume the output buffer is orig_width by orig_height
if output_row_pitch_in_blocks_or_pixels == 0 {
output_row_pitch_in_blocks_or_pixels = original_width;
}

if output_rows_in_pixels == 0 {
output_rows_in_pixels = original_height;
}

output_rows_in_pixels * output_row_pitch_in_blocks_or_pixels
} else if self == TranscoderBlockFormat::FXT1_RGB {
let num_blocks_fxt1_x = (original_width + 7) / 8;
let num_blocks_fxt1_y = (original_height + 3) / 4;
num_blocks_fxt1_x * num_blocks_fxt1_y
} else {
total_slice_blocks
}
}

/// Calculate the minimum output buffer required to store transcoded data in bytes
pub fn calculate_minimum_output_buffer_bytes(
self,
original_width: u32,
original_height: u32,
total_slice_blocks: u32,
output_row_pitch_in_blocks_or_pixels: Option<u32>,
output_rows_in_pixels: Option<u32>,
) -> u32 {
self.calculate_minimum_output_buffer_blocks_or_pixels(
original_width,
original_height,
total_slice_blocks,
output_row_pitch_in_blocks_or_pixels,
output_rows_in_pixels,
) * self.bytes_per_block_or_pixel()
}
}
Loading

0 comments on commit c76bb01

Please sign in to comment.