| // basisu_astc_ldr_fencode.h | |
| #pragma once | |
| #include "../transcoder/basisu.h" | |
| #include "../transcoder/basisu_transcoder_internal.h" | |
| #include "basisu_astc_ldr_common.h" | |
| namespace basisu | |
| { | |
| namespace astc_ldrf | |
| { | |
| BASISU_FORCE_INLINE int popcount64(uint64_t x) | |
| { | |
| #if defined(__cplusplus) && (__cplusplus >= 202002L) && defined(__cpp_lib_bitops) | |
| return static_cast<int>(std::popcount(x)); | |
| #elif defined(__EMSCRIPTEN__) || defined(__clang__) || defined(__GNUC__) | |
| return __builtin_popcountll(static_cast<unsigned long long>(x)); | |
| #elif defined(_MSC_VER) && defined(_M_X64) | |
| return static_cast<int>(__popcnt64(x)); | |
| #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM64) || defined(_M_ARM)) | |
| return static_cast<int>( | |
| __popcnt(static_cast<uint32_t>(x)) + | |
| __popcnt(static_cast<uint32_t>(x >> 32)) | |
| ); | |
| #else | |
| int count = 0; | |
| while (x) | |
| { | |
| x &= (x - 1); | |
| ++count; | |
| } | |
| return count; | |
| #endif | |
| } | |
| struct bitmask192 | |
| { | |
| uint64_t m_a, m_b, m_c; // a=lowest qword (bits 0-63), b=middle (64-127), c=highest (128-191) | |
| inline bitmask192() {} | |
| constexpr inline bitmask192(const bitmask192& o) noexcept | |
| : m_a(o.m_a), m_b(o.m_b), m_c(o.m_c) | |
| { | |
| } | |
| inline bitmask192& operator=(const bitmask192& o) noexcept | |
| { | |
| m_a = o.m_a; | |
| m_b = o.m_b; | |
| m_c = o.m_c; | |
| return *this; | |
| } | |
| constexpr inline bitmask192(uint64_t a, uint64_t b, uint64_t c) noexcept | |
| : m_a(a), m_b(b), m_c(c) | |
| { | |
| } | |
| static constexpr inline bitmask192 zero() noexcept | |
| { | |
| return bitmask192(0, 0, 0); | |
| } | |
| static constexpr inline bitmask192 all() noexcept | |
| { | |
| return bitmask192(UINT64_MAX, UINT64_MAX, UINT64_MAX); | |
| } | |
| inline void clear() noexcept | |
| { | |
| m_a = 0; | |
| m_b = 0; | |
| m_c = 0; | |
| } | |
| inline void set_all() noexcept | |
| { | |
| m_a = UINT64_MAX; | |
| m_b = UINT64_MAX; | |
| m_c = UINT64_MAX; | |
| } | |
| inline bool is_zero() const noexcept | |
| { | |
| return (m_a | m_b | m_c) == 0; | |
| } | |
| inline bool any() const noexcept | |
| { | |
| return (m_a | m_b | m_c) != 0; | |
| } | |
| inline bool none() const noexcept | |
| { | |
| return !any(); | |
| } | |
| inline bool all_set() const noexcept | |
| { | |
| return (m_a == UINT64_MAX) && (m_b == UINT64_MAX) && (m_c == UINT64_MAX); | |
| } | |
| inline uint32_t popcount() const noexcept | |
| { | |
| return popcount64(m_a) + popcount64(m_b) + popcount64(m_c); | |
| } | |
| constexpr inline bitmask192 operator~() const noexcept | |
| { | |
| return bitmask192(~m_a, ~m_b, ~m_c); | |
| } | |
| constexpr inline bitmask192 operator&(const bitmask192& rhs) const noexcept | |
| { | |
| return bitmask192(m_a & rhs.m_a, m_b & rhs.m_b, m_c & rhs.m_c); | |
| } | |
| constexpr inline bitmask192 operator|(const bitmask192& rhs) const noexcept | |
| { | |
| return bitmask192(m_a | rhs.m_a, m_b | rhs.m_b, m_c | rhs.m_c); | |
| } | |
| constexpr inline bitmask192 operator^(const bitmask192& rhs) const noexcept | |
| { | |
| return bitmask192(m_a ^ rhs.m_a, m_b ^ rhs.m_b, m_c ^ rhs.m_c); | |
| } | |
| inline bitmask192& operator&=(const bitmask192& rhs) noexcept | |
| { | |
| m_a &= rhs.m_a; | |
| m_b &= rhs.m_b; | |
| m_c &= rhs.m_c; | |
| return *this; | |
| } | |
| inline bitmask192& operator|=(const bitmask192& rhs) noexcept | |
| { | |
| m_a |= rhs.m_a; | |
| m_b |= rhs.m_b; | |
| m_c |= rhs.m_c; | |
| return *this; | |
| } | |
| inline bitmask192& operator^=(const bitmask192& rhs) noexcept | |
| { | |
| m_a ^= rhs.m_a; | |
| m_b ^= rhs.m_b; | |
| m_c ^= rhs.m_c; | |
| return *this; | |
| } | |
| constexpr inline bool operator==(const bitmask192& rhs) const noexcept | |
| { | |
| return (m_a == rhs.m_a) && (m_b == rhs.m_b) && (m_c == rhs.m_c); | |
| } | |
| constexpr inline bool operator!=(const bitmask192& rhs) const noexcept | |
| { | |
| return !(*this == rhs); | |
| } | |
| // m_a is the lowest qword, m_b highest | |
| constexpr inline bool operator<(const bitmask192& rhs) const noexcept | |
| { | |
| if (m_c != rhs.m_c) return m_c < rhs.m_c; | |
| if (m_b != rhs.m_b) return m_b < rhs.m_b; | |
| return m_a < rhs.m_a; | |
| } | |
| constexpr inline bool operator>(const bitmask192& rhs) const noexcept | |
| { | |
| return rhs < *this; | |
| } | |
| constexpr inline bool operator<=(const bitmask192& rhs) const noexcept | |
| { | |
| return !(rhs < *this); | |
| } | |
| constexpr inline bool operator>=(const bitmask192& rhs) const noexcept | |
| { | |
| return !(*this < rhs); | |
| } | |
| explicit constexpr inline operator bool() const noexcept | |
| { | |
| return (m_a | m_b | m_c) != 0; | |
| } | |
| inline bool intersects(const bitmask192& rhs) const noexcept | |
| { | |
| return ((m_a & rhs.m_a) | (m_b & rhs.m_b) | (m_c & rhs.m_c)) != 0; | |
| } | |
| inline bool disjoint(const bitmask192& rhs) const noexcept | |
| { | |
| return !intersects(rhs); | |
| } | |
| constexpr inline bool contains_all(const bitmask192& rhs) const noexcept | |
| { | |
| return ((m_a & rhs.m_a) == rhs.m_a) && | |
| ((m_b & rhs.m_b) == rhs.m_b) && | |
| ((m_c & rhs.m_c) == rhs.m_c); | |
| } | |
| constexpr inline bool is_subset_of(const bitmask192& rhs) const noexcept | |
| { | |
| return rhs.contains_all(*this); | |
| } | |
| inline bool contains_any(const bitmask192& rhs) const noexcept | |
| { | |
| return intersects(rhs); | |
| } | |
| constexpr inline uint64_t low64() const noexcept | |
| { | |
| return m_a; | |
| } | |
| constexpr inline uint64_t mid64() const noexcept | |
| { | |
| return m_b; | |
| } | |
| constexpr inline uint64_t high64() const noexcept | |
| { | |
| return m_c; | |
| } | |
| inline void set_bit(uint32_t index) noexcept | |
| { | |
| assert(index < 192); | |
| if (index < 64) | |
| m_a |= uint64_t(1) << index; | |
| else if (index < 128) | |
| m_b |= uint64_t(1) << (index - 64); | |
| else | |
| m_c |= uint64_t(1) << (index - 128); | |
| } | |
| inline void set_bit(uint32_t index, uint32_t bit_val) noexcept | |
| { | |
| assert(index < 192); | |
| assert(bit_val <= 1); | |
| if (index < 64) | |
| { | |
| m_a &= ~(uint64_t(1) << index); | |
| m_a |= uint64_t(bit_val) << index; | |
| } | |
| else if (index < 128) | |
| { | |
| m_b &= ~(uint64_t(1) << (index - 64)); | |
| m_b |= uint64_t(bit_val) << (index - 64); | |
| } | |
| else | |
| { | |
| m_c &= ~(uint64_t(1) << (index - 128)); | |
| m_c |= uint64_t(bit_val) << (index - 128); | |
| } | |
| } | |
| inline bool is_bit_set(uint32_t index) const noexcept | |
| { | |
| assert(index < 192); | |
| if (index < 64) | |
| return ((m_a >> index) & 1) != 0; | |
| else if (index < 128) | |
| return ((m_b >> (index - 64)) & 1) != 0; | |
| else | |
| return ((m_c >> (index - 128)) & 1) != 0; | |
| } | |
| static inline bitmask192 lsb_mask(uint32_t num_bits) noexcept | |
| { | |
| assert(num_bits <= 192); | |
| if (num_bits == 0) | |
| return bitmask192(0, 0, 0); | |
| if (num_bits < 64) | |
| return bitmask192((uint64_t(1) << num_bits) - 1, 0, 0); | |
| if (num_bits == 64) | |
| return bitmask192(UINT64_MAX, 0, 0); | |
| if (num_bits < 128) | |
| { | |
| return bitmask192( | |
| UINT64_MAX, | |
| (uint64_t(1) << (num_bits - 64)) - 1, | |
| 0); | |
| } | |
| if (num_bits == 128) | |
| return bitmask192(UINT64_MAX, UINT64_MAX, 0); | |
| if (num_bits < 192) | |
| { | |
| return bitmask192( | |
| UINT64_MAX, | |
| UINT64_MAX, | |
| (uint64_t(1) << (num_bits - 128)) - 1); | |
| } | |
| return bitmask192(UINT64_MAX, UINT64_MAX, UINT64_MAX); | |
| } | |
| }; | |
| inline uint32_t popcount192(const bitmask192& a) { return a.popcount(); } | |
| const uint32_t MAX_CANDIDATES = 512; | |
| struct rgba32_image | |
| { | |
| const uint8_t* m_pPixels; | |
| uint32_t m_width; | |
| uint32_t m_height; | |
| uint32_t m_row_pitch_in_texels; // pitch in pixels/texels, not bytes | |
| }; | |
| struct single_subset_enc_context | |
| { | |
| uint32_t m_block_width, m_block_height; | |
| uint32_t m_block_size_index; | |
| uint32_t m_total_block_pixels; | |
| uint32_t m_max_candidates; | |
| uint32_t m_num_ls_iterations; | |
| uint32_t m_chan_weights[4]; | |
| basist::astc_ldr_t::dct2f m_dct; | |
| astc_helpers::decode_mode m_astc_decode_mode; | |
| bool m_disable_dual_plane; | |
| bool m_weight_polishing; | |
| bool m_has_alpha; | |
| bool m_try_base_ofs; | |
| bool m_higher_effort_bc; | |
| }; | |
| const uint32_t MAX_UNIQUE_2SUBSET_PATS = 838; | |
| const uint32_t MAX_UNIQUE_3SUBSET_PATS = 626; | |
| typedef basisu::vector<astc_helpers::log_astc_block> astc_lblock_vec; | |
| // source pEndpoints[] = ASTC direct order: LR HR LG HG LB HB LA HA | |
| bool cem_encode(uint32_t cem_index, const float pEndpoints[8], uint32_t endpoint_ise_range, uint8_t* pCEM_values, bool allow_bc = true, bool high_effort = true); | |
| bool init_single_subset_context( | |
| single_subset_enc_context& ctx, | |
| uint32_t block_width, uint32_t block_height, | |
| astc_helpers::decode_mode astc_decode_mode, | |
| const uint32_t chan_weights[4], | |
| uint32_t max_candidates, uint32_t num_ls_iterations, bool disable_dual_plane, bool has_alpha, bool weight_polishing); | |
| // best_lblock will be in ise space (see is_lblock_ise() below), elements in array pointed to by pAll_candidates (which may be null) will be in rank space | |
| double compress_single_subset( | |
| single_subset_enc_context& ctx, | |
| const uint8_t* pBlock_pixels, | |
| astc_helpers::log_astc_block& best_lblock, | |
| astc_lblock_vec* pAll_candidates, | |
| bool always_compute_error); | |
| static const uint32_t NUM_DOT_THRESH_FRACTS = 6; | |
| struct subset_enc_context : single_subset_enc_context | |
| { | |
| subset_enc_context() {} | |
| uint32_t m_max_subsets; | |
| uint32_t m_num_carrier_candidates; | |
| uint32_t m_num_pattern_candidates; | |
| float m_two_subset_var_thresh; | |
| float m_three_subset_var_thresh; | |
| uint32_t m_two_subset_dot_thresh_fract_index; | |
| bitmask192 m_two_subset_pat_bitmask[MAX_UNIQUE_2SUBSET_PATS]; | |
| uint32_t m_num_unique_two_subset_pats; | |
| const uint16_t* m_pUnique_two_subset_pats; | |
| bool m_use_method1; | |
| bool m_use_method2; | |
| astc_ldr::partitions_data* m_pPart_data_p2; | |
| astc_ldr::partitions_data* m_pPart_data_p3; | |
| }; | |
| // must have first called init_single_subset_context() on ctx | |
| bool init_multi_subset_context( | |
| subset_enc_context& ctx, | |
| uint32_t max_subsets, | |
| uint32_t num_carrier_candidates, uint32_t num_pattern_candidates, | |
| float two_subset_var_thresh, uint32_t two_subset_dot_thresh_fract_index, | |
| float three_subset_var_thresh, | |
| astc_ldr::partitions_data* pPart_data_p2, astc_ldr::partitions_data* pPart_data_p3); | |
| struct subset_enc_thread_context | |
| { | |
| astc_ldr::partition_pattern_vec m_pat_vec; | |
| }; | |
| double compress_block_subsets( | |
| const subset_enc_context& enc_context, | |
| subset_enc_thread_context& enc_thread_context, | |
| const uint8_t* pBlock_pixels, | |
| astc_helpers::log_astc_block& best_lblock, astc_lblock_vec* pAll_candidates); | |
| enum | |
| { | |
| cUserModeISEValues = 0, | |
| cUserModeRankValues = 1 | |
| }; | |
| static inline bool is_lblock_ise(const astc_helpers::log_astc_block& log_blk) | |
| { | |
| // the default user mode value == ISE, 1 = ranks (which is the exceptional case for astc_helpers) | |
| return log_blk.m_user_mode == cUserModeISEValues; | |
| } | |
| void convert_rank_lblock_to_ise(astc_helpers::log_astc_block& log_blk); | |
| void convert_ise_lblock_to_rank(astc_helpers::log_astc_block& log_blk); | |
| } // namespace astc_ldr_f | |
| } // namespace basisu |