diff --git a/CMakeLists.txt b/CMakeLists.txt
index 62d4a41..7302fe0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,7 +26,7 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS -std=c++11)
-set(GCC_COMPILE_FLAGS "-fvisibility=hidden -fvisibility-inlines-hidden -fPIC -fopenmp -fno-strict-aliasing -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -Wextra -Wno-unused-local-typedefs -Wno-unused-value -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-unused-variable -Wno-reorder")
+set(GCC_COMPILE_FLAGS "-fvisibility=hidden -fvisibility-inlines-hidden -fPIC -fno-strict-aliasing -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -Wextra -Wno-unused-local-typedefs -Wno-unused-value -Wno-unused-parameter -Wno-unused-variable -Wno-reorder")
if (NOT BUILD_X64)
set(GCC_COMPILE_FLAGS "${GCC_COMPILE_FLAGS} -m32")
diff --git a/basisu.vcxproj b/basisu.vcxproj
index 332ae13..88de436 100644
--- a/basisu.vcxproj
+++ b/basisu.vcxproj
@@ -98,7 +98,6 @@
Level4
Disabled
true
- true
true
@@ -134,7 +133,6 @@
true
true
true
- true
NDEBUG;_MBCS;%(PreprocessorDefinitions)
diff --git a/basisu_backend.cpp b/basisu_backend.cpp
index 665320c..0ec918a 100644
--- a/basisu_backend.cpp
+++ b/basisu_backend.cpp
@@ -44,7 +44,7 @@ namespace basisu
m_output.clear();
}
- void basisu_backend::init(basisu_frontend *pFront_end, basisu_backend_params ¶ms, const basisu_backend_slice_desc_vec &slice_descs, const basist::etc1_global_selector_codebook *pGlobal_sel_codebook)
+ void basisu_backend::init(basisu_frontend* pFront_end, basisu_backend_params& params, const basisu_backend_slice_desc_vec& slice_descs, const basist::etc1_global_selector_codebook* pGlobal_sel_codebook)
{
m_pFront_end = pFront_end;
m_params = params;
@@ -62,7 +62,7 @@ namespace basisu
params.m_use_hybrid_sel_codebooks);
debug_printf("Frontend endpoints: %u selectors: %u\n", m_pFront_end->get_total_endpoint_clusters(), m_pFront_end->get_total_selector_clusters());
-
+
for (uint32_t i = 0; i < m_slices.size(); i++)
{
debug_printf("Slice: %u, OrigWidth: %u, OrigHeight: %u, Width: %u, Height: %u, NumBlocksX: %u, NumBlocksY: %u, FirstBlockIndex: %u\n",
@@ -76,15 +76,15 @@ namespace basisu
void basisu_backend::create_endpoint_palette()
{
- const basisu_frontend &r = *m_pFront_end;
+ const basisu_frontend& r = *m_pFront_end;
m_output.m_num_endpoints = r.get_total_endpoint_clusters();
m_endpoint_palette.resize(r.get_total_endpoint_clusters());
for (uint32_t i = 0; i < r.get_total_endpoint_clusters(); i++)
{
- etc1_endpoint_palette_entry &e = m_endpoint_palette[i];
-
+ etc1_endpoint_palette_entry& e = m_endpoint_palette[i];
+
e.m_color5_valid = r.get_endpoint_cluster_color_is_used(i, false);
e.m_color5 = r.get_endpoint_cluster_unscaled_color(i, false);
e.m_inten5 = r.get_endpoint_cluster_inten_table(i, false);
@@ -95,7 +95,7 @@ namespace basisu
void basisu_backend::create_selector_palette()
{
- const basisu_frontend &r = *m_pFront_end;
+ const basisu_frontend& r = *m_pFront_end;
m_output.m_num_selectors = r.get_total_selector_clusters();
@@ -107,9 +107,9 @@ namespace basisu
for (int i = 0; i < static_cast(r.get_total_selector_clusters()); i++)
{
- basist::etc1_selector_palette_entry &selector_pal_entry = m_selector_palette[i];
+ basist::etc1_selector_palette_entry& selector_pal_entry = m_selector_palette[i];
- etc1_global_selector_cb_entry_desc &pal_entry_desc = m_global_selector_palette_desc[i];
+ etc1_global_selector_cb_entry_desc& pal_entry_desc = m_global_selector_palette_desc[i];
pal_entry_desc.m_pal_index = r.get_selector_cluster_global_selector_entry_ids()[i].m_palette_index;
pal_entry_desc.m_mod_index = r.get_selector_cluster_global_selector_entry_ids()[i].m_modifier.get_index();
@@ -119,7 +119,7 @@ namespace basisu
if (pal_entry_desc.m_was_used)
{
- const etc_block &selector_bits = r.get_selector_cluster_selector_bits(i);
+ const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
(void)selector_bits;
basist::etc1_selector_palette_entry global_pal_entry(m_pGlobal_sel_codebook->get_entry(r.get_selector_cluster_global_selector_entry_ids()[i]));
@@ -136,7 +136,7 @@ namespace basisu
}
else
{
- const etc_block &selector_bits = r.get_selector_cluster_selector_bits(i);
+ const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
@@ -148,9 +148,9 @@ namespace basisu
{
for (uint32_t i = 0; i < r.get_total_selector_clusters(); i++)
{
- basist::etc1_selector_palette_entry &s = m_selector_palette[i];
+ basist::etc1_selector_palette_entry& s = m_selector_palette[i];
- const etc_block &selector_bits = r.get_selector_cluster_selector_bits(i);
+ const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
for (uint32_t y = 0; y < 4; y++)
{
@@ -162,28 +162,27 @@ namespace basisu
}
}
}
-
- static const struct
+
+ static const struct
{
int8_t m_dx, m_dy;
- } g_endpoint_preds[] =
+ } g_endpoint_preds[] =
{
{ -1, 0 },
{ 0, -1 },
{ -1, -1 }
};
- const uint32_t NUM_ENDPOINT_PREDS = BASISU_ARRAY_SIZE(g_endpoint_preds);
- const uint32_t NO_ENDPOINT_PRED_INDEX = 3;//NUM_ENDPOINT_PREDS;
- void basisu_backend::reoptimize_and_sort_endpoints_codebook(uint32_t total_block_endpoints_remapped, uint_vec &all_endpoint_indices)
+ void basisu_backend::reoptimize_and_sort_endpoints_codebook(uint32_t total_block_endpoints_remapped, uint_vec& all_endpoint_indices)
{
- basisu_frontend &r = *m_pFront_end;
+ basisu_frontend& r = *m_pFront_end;
+ const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
if ((total_block_endpoints_remapped) && (m_params.m_compression_level > 0))
{
// We're changed the block endpoint indices, so we need to go and adjust the endpoint codebook (remove unused entries, optimize existing entries that have changed)
uint_vec new_block_endpoints(get_total_blocks());
-
+
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
@@ -197,7 +196,7 @@ namespace basisu
int_vec old_to_new_endpoint_indices;
r.reoptimize_remapped_endpoints(new_block_endpoints, old_to_new_endpoint_indices, true);
-
+
create_endpoint_palette();
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
@@ -215,8 +214,8 @@ namespace basisu
{
const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
- encoder_block &m = m_slice_encoder_blocks[slice_index](block_x, block_y);
-
+ encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
+
m.m_endpoint_index = old_to_new_endpoint_indices[m.m_endpoint_index];
} // block_x
} // block_y
@@ -224,7 +223,7 @@ namespace basisu
for (uint32_t i = 0; i < all_endpoint_indices.size(); i++)
all_endpoint_indices[i] = old_to_new_endpoint_indices[all_endpoint_indices[i]];
-
+
} //if (total_block_endpoints_remapped)
// Sort endpoint codebook
@@ -239,64 +238,165 @@ namespace basisu
void basisu_backend::sort_selector_codebook()
{
- basisu_frontend &r = *m_pFront_end;
+ basisu_frontend& r = *m_pFront_end;
m_selector_remap_table_new_to_old.resize(r.get_total_selector_clusters());
-
- m_selector_remap_table_new_to_old[0] = 0;
- uint32_t prev_selector_index = 0;
- int_vec remaining_selectors;
- remaining_selectors.reserve(r.get_total_selector_clusters() - 1);
- for (uint32_t i = 1; i < r.get_total_selector_clusters(); i++)
- remaining_selectors.push_back(i);
-
- uint_vec selector_palette_bytes(m_selector_palette.size());
- for (uint32_t i = 0; i < m_selector_palette.size(); i++)
- selector_palette_bytes[i] = m_selector_palette[i].get_byte(0) | (m_selector_palette[i].get_byte(1) << 8) |(m_selector_palette[i].get_byte(2) << 16) |(m_selector_palette[i].get_byte(3) << 24);
-
- // This is the traveling salesman problem.
- for (uint32_t i = 1; i < r.get_total_selector_clusters(); i++)
+ if (m_params.m_compression_level == 0)
{
- uint32_t best_hamming_dist = 100;
- uint32_t best_index = 0;
+ for (uint32_t i = 0; i < r.get_total_selector_clusters(); i++)
+ m_selector_remap_table_new_to_old[i] = i;
+ }
+ else
+ {
+ m_selector_remap_table_new_to_old[0] = 0;
+ uint32_t prev_selector_index = 0;
+
+ int_vec remaining_selectors;
+ remaining_selectors.reserve(r.get_total_selector_clusters() - 1);
+ for (uint32_t i = 1; i < r.get_total_selector_clusters(); i++)
+ remaining_selectors.push_back(i);
+
+ uint_vec selector_palette_bytes(m_selector_palette.size());
+ for (uint32_t i = 0; i < m_selector_palette.size(); i++)
+ selector_palette_bytes[i] = m_selector_palette[i].get_byte(0) | (m_selector_palette[i].get_byte(1) << 8) | (m_selector_palette[i].get_byte(2) << 16) | (m_selector_palette[i].get_byte(3) << 24);
+
+ // This is the traveling salesman problem.
+ for (uint32_t i = 1; i < r.get_total_selector_clusters(); i++)
+ {
+ uint32_t best_hamming_dist = 100;
+ uint32_t best_index = 0;
#if BASISU_FASTER_SELECTOR_REORDERING
- const uint32_t step = (remaining_selectors.size() > 16) ? 16 : 1;
- for (uint32_t j = 0; j < remaining_selectors.size(); j += step)
+ const uint32_t step = (remaining_selectors.size() > 16) ? 16 : 1;
+ for (uint32_t j = 0; j < remaining_selectors.size(); j += step)
#else
- for (uint32_t j = 0; j < remaining_selectors.size(); j++)
+ for (uint32_t j = 0; j < remaining_selectors.size(); j++)
#endif
- {
- int selector_index = remaining_selectors[j];
-
- uint32_t k = selector_palette_bytes[prev_selector_index] ^ selector_palette_bytes[selector_index];
- uint32_t hamming_dist = g_hamming_dist[k & 0xFF] + g_hamming_dist[(k >> 8) & 0xFF] + g_hamming_dist[(k >> 16) & 0xFF] + g_hamming_dist[k >> 24];
-
- if (hamming_dist < best_hamming_dist)
{
- best_hamming_dist = hamming_dist;
- best_index = j;
- if (best_hamming_dist <= 1)
- break;
+ int selector_index = remaining_selectors[j];
+
+ uint32_t k = selector_palette_bytes[prev_selector_index] ^ selector_palette_bytes[selector_index];
+ uint32_t hamming_dist = g_hamming_dist[k & 0xFF] + g_hamming_dist[(k >> 8) & 0xFF] + g_hamming_dist[(k >> 16) & 0xFF] + g_hamming_dist[k >> 24];
+
+ if (hamming_dist < best_hamming_dist)
+ {
+ best_hamming_dist = hamming_dist;
+ best_index = j;
+ if (best_hamming_dist <= 1)
+ break;
+ }
}
+
+ prev_selector_index = remaining_selectors[best_index];
+ m_selector_remap_table_new_to_old[i] = prev_selector_index;
+
+ remaining_selectors[best_index] = remaining_selectors.back();
+ remaining_selectors.resize(remaining_selectors.size() - 1);
}
-
- prev_selector_index = remaining_selectors[best_index];
- m_selector_remap_table_new_to_old[i] = prev_selector_index;
-
- remaining_selectors[best_index] = remaining_selectors.back();
- remaining_selectors.resize(remaining_selectors.size() - 1);
}
-
+
m_selector_remap_table_old_to_new.resize(r.get_total_selector_clusters());
for (uint32_t i = 0; i < m_selector_remap_table_new_to_old.size(); i++)
m_selector_remap_table_old_to_new[m_selector_remap_table_new_to_old[i]] = i;
}
-
+ int basisu_backend::find_video_frame(int slice_index, int delta)
+ {
+ for (uint32_t s = 0; s < m_slices.size(); s++)
+ {
+ if ((int)m_slices[s].m_source_file_index != ((int)m_slices[slice_index].m_source_file_index + delta))
+ continue;
+ if (m_slices[s].m_mip_index != m_slices[slice_index].m_mip_index)
+ continue;
+
+ // Being super paranoid here.
+ if (m_slices[s].m_num_blocks_x != (m_slices[slice_index].m_num_blocks_x))
+ continue;
+ if (m_slices[s].m_num_blocks_y != (m_slices[slice_index].m_num_blocks_y))
+ continue;
+ if (m_slices[s].m_alpha != (m_slices[slice_index].m_alpha))
+ continue;
+ return s;
+ }
+
+ return -1;
+ }
+
+ void basisu_backend::check_for_valid_cr_blocks()
+ {
+ basisu_frontend& r = *m_pFront_end;
+ const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
+
+ if (!is_video)
+ return;
+
+ uint32_t total_crs = 0;
+ uint32_t total_invalid_crs = 0;
+
+ for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
+ {
+ const bool is_iframe = m_slices[slice_index].m_iframe;
+ const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
+
+ const uint32_t width = m_slices[slice_index].m_width;
+ const uint32_t height = m_slices[slice_index].m_height;
+ const uint32_t num_blocks_x = m_slices[slice_index].m_num_blocks_x;
+ const uint32_t num_blocks_y = m_slices[slice_index].m_num_blocks_y;
+ const int prev_frame_slice_index = find_video_frame(slice_index, -1);
+
+ // If we don't have a previous frame, and we're not an i-frame, something is wrong.
+ if ((prev_frame_slice_index < 0) && (!is_iframe))
+ {
+ BASISU_BACKEND_VERIFY(0);
+ }
+
+ if ((is_iframe) || (prev_frame_slice_index < 0))
+ {
+ // Ensure no blocks use CR's
+ for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
+ {
+ for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
+ {
+ encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
+ BASISU_BACKEND_VERIFY(m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX);
+ }
+ }
+ }
+ else
+ {
+ // For blocks that use CR's, make sure the endpoints/selectors haven't really changed.
+ for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
+ {
+ for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
+ {
+ encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
+
+ if (m.m_endpoint_predictor == basist::CR_ENDPOINT_PRED_INDEX)
+ {
+ total_crs++;
+
+ encoder_block& prev_m = m_slice_encoder_blocks[prev_frame_slice_index](block_x, block_y);
+
+ if ((m.m_endpoint_index != prev_m.m_endpoint_index) || (m.m_selector_index != prev_m.m_selector_index))
+ {
+ total_invalid_crs++;
+ }
+ }
+ } // block_x
+ } // block_y
+
+ } // !slice_index
+
+ } // slice_index
+
+ debug_printf("Total CR's: %u, Total invalid CR's: %u\n", total_crs, total_invalid_crs);
+
+ BASISU_BACKEND_VERIFY(total_invalid_crs == 0);
+ }
void basisu_backend::create_encoder_blocks()
{
- basisu_frontend &r = *m_pFront_end;
+ basisu_frontend& r = *m_pFront_end;
+ const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
m_slice_encoder_blocks.resize(m_slices.size());
@@ -304,9 +404,11 @@ namespace basisu
uint_vec all_endpoint_indices;
all_endpoint_indices.reserve(get_total_blocks());
-
+
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
+ const int prev_frame_slice_index = is_video ? find_video_frame(slice_index, -1) : -1;
+ const bool is_iframe = m_slices[slice_index].m_iframe;
const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
const uint32_t width = m_slices[slice_index].m_width;
@@ -322,50 +424,68 @@ namespace basisu
{
const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
- encoder_block &m = m_slice_encoder_blocks[slice_index](block_x, block_y);
-
+ encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
+
m.m_endpoint_index = r.get_subblock_endpoint_cluster_index(block_index, 0);
BASISU_BACKEND_VERIFY(r.get_subblock_endpoint_cluster_index(block_index, 0) == r.get_subblock_endpoint_cluster_index(block_index, 1));
m.m_selector_index = r.get_block_selector_cluster_index(block_index);
- m.m_endpoint_predictor = NO_ENDPOINT_PRED_INDEX;
+ m.m_endpoint_predictor = basist::NO_ENDPOINT_PRED_INDEX;
const uint32_t block_endpoint = m.m_endpoint_index;
uint32_t best_endpoint_pred = UINT32_MAX;
- for (uint32_t endpoint_pred = 0; endpoint_pred < NUM_ENDPOINT_PREDS; endpoint_pred++)
+ for (uint32_t endpoint_pred = 0; endpoint_pred < basist::NUM_ENDPOINT_PREDS; endpoint_pred++)
{
- int pred_block_x = block_x + g_endpoint_preds[endpoint_pred].m_dx;
- if ((pred_block_x < 0) || (pred_block_x >= (int)num_blocks_x))
- continue;
-
- int pred_block_y = block_y + g_endpoint_preds[endpoint_pred].m_dy;
- if ((pred_block_y < 0) || (pred_block_y >= (int)num_blocks_y))
- continue;
-
- uint32_t pred_endpoint = m_slice_encoder_blocks[slice_index](pred_block_x, pred_block_y).m_endpoint_index;
-
- if (pred_endpoint == block_endpoint)
+ if ((is_video) && (endpoint_pred == basist::CR_ENDPOINT_PRED_INDEX))
{
- if (endpoint_pred < best_endpoint_pred)
+ if ((prev_frame_slice_index != -1) && (!is_iframe))
{
- best_endpoint_pred = endpoint_pred;
+ const uint32_t cur_endpoint = m_slice_encoder_blocks[slice_index](block_x, block_y).m_endpoint_index;
+ const uint32_t cur_selector = m_slice_encoder_blocks[slice_index](block_x, block_y).m_selector_index;
+ const uint32_t prev_endpoint = m_slice_encoder_blocks[prev_frame_slice_index](block_x, block_y).m_endpoint_index;
+ const uint32_t prev_selector = m_slice_encoder_blocks[prev_frame_slice_index](block_x, block_y).m_selector_index;
+ if ((cur_endpoint == prev_endpoint) && (cur_selector == prev_selector))
+ {
+ best_endpoint_pred = basist::CR_ENDPOINT_PRED_INDEX;
+ m_slice_encoder_blocks[prev_frame_slice_index](block_x, block_y).m_is_cr_target = true;
+ }
}
}
-
+ else
+ {
+ int pred_block_x = block_x + g_endpoint_preds[endpoint_pred].m_dx;
+ if ((pred_block_x < 0) || (pred_block_x >= (int)num_blocks_x))
+ continue;
+
+ int pred_block_y = block_y + g_endpoint_preds[endpoint_pred].m_dy;
+ if ((pred_block_y < 0) || (pred_block_y >= (int)num_blocks_y))
+ continue;
+
+ uint32_t pred_endpoint = m_slice_encoder_blocks[slice_index](pred_block_x, pred_block_y).m_endpoint_index;
+
+ if (pred_endpoint == block_endpoint)
+ {
+ if (endpoint_pred < best_endpoint_pred)
+ {
+ best_endpoint_pred = endpoint_pred;
+ }
+ }
+ }
+
} // endpoint_pred
if (best_endpoint_pred != UINT32_MAX)
{
m.m_endpoint_predictor = best_endpoint_pred;
-
+
total_endpoint_pred_hits++;
}
else if (m_params.m_endpoint_rdo_quality_thresh > 0.0f)
{
- const pixel_block &src_pixels = r.get_source_pixel_block(block_index);
+ const pixel_block& src_pixels = r.get_source_pixel_block(block_index);
etc_block etc_blk(r.get_output_block(block_index));
@@ -379,11 +499,13 @@ namespace basisu
uint64_t best_err = UINT64_MAX;
uint32_t best_endpoint_index = 0;
-
+
best_endpoint_pred = UINT32_MAX;
-
- for (uint32_t endpoint_pred = 0; endpoint_pred < NUM_ENDPOINT_PREDS; endpoint_pred++)
+
+ for (uint32_t endpoint_pred = 0; endpoint_pred < basist::NUM_ENDPOINT_PREDS; endpoint_pred++)
{
+ if ((is_video) && (endpoint_pred == basist::CR_ENDPOINT_PRED_INDEX))
+ continue;
int pred_block_x = block_x + g_endpoint_preds[endpoint_pred].m_dx;
if ((pred_block_x < 0) || (pred_block_x >= (int)num_blocks_x))
continue;
@@ -391,12 +513,12 @@ namespace basisu
int pred_block_y = block_y + g_endpoint_preds[endpoint_pred].m_dy;
if ((pred_block_y < 0) || (pred_block_y >= (int)num_blocks_y))
continue;
-
+
uint32_t pred_endpoint_index = m_slice_encoder_blocks[slice_index](pred_block_x, pred_block_y).m_endpoint_index;
uint32_t pred_inten = r.get_endpoint_cluster_inten_table(pred_endpoint_index, false);
color_rgba pred_color = r.get_endpoint_cluster_unscaled_color(pred_endpoint_index, false);
-
+
trial_etc_block.set_block_color5(pred_color, pred_color);
trial_etc_block.set_inten_table(0, pred_inten);
trial_etc_block.set_inten_table(1, pred_inten);
@@ -411,7 +533,7 @@ namespace basisu
if (trial_err > thresh_err)
break;
}
-
+
if (trial_err <= thresh_err)
{
if ((trial_err < best_err) || ((trial_err == best_err) && (endpoint_pred < best_endpoint_pred)))
@@ -442,25 +564,26 @@ namespace basisu
total_endpoint_pred_missed++;
}
- if (m.m_endpoint_predictor == NO_ENDPOINT_PRED_INDEX)
+ if (m.m_endpoint_predictor == basist::NO_ENDPOINT_PRED_INDEX)
{
all_endpoint_indices.push_back(m.m_endpoint_index);
}
-
+
} // block_x
} // block_y
} // slice
- debug_printf("total_endpoint_pred_missed: %u (%3.2f%%) total_endpoint_pred_hit: %u (%3.2f%%), total_block_endpoints_remapped: %u (%3.2f%%)\n",
- total_endpoint_pred_missed, total_endpoint_pred_missed * 100.0f / get_total_blocks(),
+ debug_printf("total_endpoint_pred_missed: %u (%3.2f%%) total_endpoint_pred_hit: %u (%3.2f%%), total_block_endpoints_remapped: %u (%3.2f%%)\n",
+ total_endpoint_pred_missed, total_endpoint_pred_missed * 100.0f / get_total_blocks(),
total_endpoint_pred_hits, total_endpoint_pred_hits * 100.0f / get_total_blocks(),
total_block_endpoints_remapped, total_block_endpoints_remapped * 100.0f / get_total_blocks());
reoptimize_and_sort_endpoints_codebook(total_block_endpoints_remapped, all_endpoint_indices);
-
+
sort_selector_codebook();
+ check_for_valid_cr_blocks();
}
void basisu_backend::compute_slice_crcs()
@@ -482,22 +605,22 @@ namespace basisu
{
const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
- encoder_block &m = m_slice_encoder_blocks[slice_index](block_x, block_y);
+ encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
{
- etc_block &output_block = *(etc_block *)gi.get_block_ptr(block_x, block_y);
+ etc_block& output_block = *(etc_block*)gi.get_block_ptr(block_x, block_y);
output_block.set_diff_bit(true);
output_block.set_flip_bit(true);
-
+
const uint32_t endpoint_index = m.m_endpoint_index;
-
+
output_block.set_block_color5_etc1s(m_endpoint_palette[endpoint_index].m_color5);
output_block.set_inten_tables_etc1s(m_endpoint_palette[endpoint_index].m_inten5);
const uint32_t selector_idx = m.m_selector_index;
- const basist::etc1_selector_palette_entry &selectors = m_selector_palette[selector_idx];
+ const basist::etc1_selector_palette_entry& selectors = m_selector_palette[selector_idx];
for (uint32_t sy = 0; sy < 4; sy++)
for (uint32_t sx = 0; sx < 4; sx++)
output_block.set_selector(sx, sy, selectors(sx, sy));
@@ -521,23 +644,24 @@ namespace basisu
#endif
save_png(buf, gi_unpacked);
}
-
+
} // slice_index
}
// TODO: Split this into multiple methods.
bool basisu_backend::encode_image()
{
- basisu_frontend &r = *m_pFront_end;
+ basisu_frontend& r = *m_pFront_end;
+ const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
uint32_t total_used_selector_history_buf = 0;
uint32_t total_selector_indices_remapped = 0;
basist::approx_move_to_front selector_history_buf(basist::MAX_SELECTOR_HISTORY_BUF_SIZE);
histogram selector_history_buf_histogram(basist::MAX_SELECTOR_HISTORY_BUF_SIZE);
- histogram selector_histogram(r.get_total_selector_clusters() + basist::MAX_SELECTOR_HISTORY_BUF_SIZE + 1);
+ histogram selector_histogram(r.get_total_selector_clusters() + basist::MAX_SELECTOR_HISTORY_BUF_SIZE + 1);
histogram selector_history_buf_rle_histogram(1 << basist::SELECTOR_HISTORY_BUF_RLE_COUNT_BITS);
-
+
std::vector selector_syms(m_slices.size());
const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = r.get_total_selector_clusters();
@@ -548,14 +672,16 @@ namespace basisu
histogram delta_endpoint_histogram(r.get_total_endpoint_clusters());
histogram endpoint_pred_histogram(basist::ENDPOINT_PRED_TOTAL_SYMBOLS);
- std::vector endpoint_pred_syms(m_slices.size());
-
+ std::vector endpoint_pred_syms(m_slices.size());
+
uint32_t total_endpoint_indices_remapped = 0;
uint_vec block_endpoint_indices, block_selector_indices;
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
+ const int prev_frame_slice_index = is_video ? find_video_frame(slice_index, -1) : -1;
+ const int next_frame_slice_index = is_video ? find_video_frame(slice_index, 1) : -1;
const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
const uint32_t width = m_slices[slice_index].m_width;
const uint32_t height = m_slices[slice_index].m_height;
@@ -565,9 +691,9 @@ namespace basisu
selector_history_buf.reset();
int selector_history_buf_rle_count = 0;
-
+
int prev_endpoint_pred_sym_bits = -1, endpoint_pred_repeat_count = 0;
-
+
uint32_t prev_endpoint_index = 0;
vector2D block_endpoints_are_referenced(num_blocks_x, num_blocks_y);
@@ -578,24 +704,32 @@ namespace basisu
{
const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
- encoder_block &m = m_slice_encoder_blocks[slice_index](block_x, block_y);
+ encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
if (m.m_endpoint_predictor == 0)
block_endpoints_are_referenced(block_x - 1, block_y) = true;
else if (m.m_endpoint_predictor == 1)
block_endpoints_are_referenced(block_x, block_y - 1) = true;
else if (m.m_endpoint_predictor == 2)
- block_endpoints_are_referenced(block_x - 1, block_y - 1) = true;
- }
- }
-
+ {
+ if (!is_video)
+ block_endpoints_are_referenced(block_x - 1, block_y - 1) = true;
+ }
+ if (is_video)
+ {
+ if (m.m_is_cr_target)
+ block_endpoints_are_referenced(block_x, block_y) = true;
+ }
+
+ } // block_x
+ } // block_y
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
{
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
{
const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
- encoder_block &m = m_slice_encoder_blocks[slice_index](block_x, block_y);
+ encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
if (((block_x & 1) == 0) && ((block_y & 1) == 0))
{
@@ -607,15 +741,15 @@ namespace basisu
{
const uint32_t bx = block_x + x;
const uint32_t by = block_y + y;
-
- uint32_t pred = NO_ENDPOINT_PRED_INDEX;
+
+ uint32_t pred = basist::NO_ENDPOINT_PRED_INDEX;
if ((bx < num_blocks_x) && (by < num_blocks_y))
pred = m_slice_encoder_blocks[slice_index](bx, by).m_endpoint_predictor;
-
+
endpoint_pred_cur_sym_bits |= (pred << (x * 2 + y * 4));
}
}
-
+
if ((int)endpoint_pred_cur_sym_bits == prev_endpoint_pred_sym_bits)
{
endpoint_pred_repeat_count++;
@@ -628,7 +762,7 @@ namespace basisu
{
endpoint_pred_histogram.inc(basist::ENDPOINT_PRED_REPEAT_LAST_SYMBOL);
endpoint_pred_syms[slice_index].push_back(basist::ENDPOINT_PRED_REPEAT_LAST_SYMBOL);
-
+
endpoint_pred_syms[slice_index].push_back(endpoint_pred_repeat_count);
}
else
@@ -652,26 +786,26 @@ namespace basisu
int new_endpoint_index = m_endpoint_remap_table_old_to_new[m.m_endpoint_index];
- if (m.m_endpoint_predictor == NO_ENDPOINT_PRED_INDEX)
+ if (m.m_endpoint_predictor == basist::NO_ENDPOINT_PRED_INDEX)
{
int endpoint_delta = new_endpoint_index - prev_endpoint_index;
-
+
if ((m_params.m_endpoint_rdo_quality_thresh > 1.0f) && (iabs(endpoint_delta) > 1) && (!block_endpoints_are_referenced(block_x, block_y)))
{
- const pixel_block &src_pixels = r.get_source_pixel_block(block_index);
+ const pixel_block& src_pixels = r.get_source_pixel_block(block_index);
etc_block etc_blk(r.get_output_block(block_index));
const uint64_t cur_err = etc_blk.evaluate_etc1_error(src_pixels.get_ptr(), r.get_params().m_perceptual);
- if (cur_err)
+ if (cur_err)
{
const float endpoint_remap_thresh = maximum(1.0f, m_params.m_endpoint_rdo_quality_thresh);
const uint64_t thresh_err = (uint64_t)(cur_err * endpoint_remap_thresh);
uint64_t best_trial_err = UINT64_MAX;
int best_trial_idx = 0;
-
+
etc_block trial_etc_blk(etc_blk);
const int MAX_ENDPOINT_SEARCH_DIST = 32;
@@ -687,10 +821,10 @@ namespace basisu
if (trial_idx == new_endpoint_index)
continue;
- const etc1_endpoint_palette_entry &p = m_endpoint_palette[m_endpoint_remap_table_new_to_old[trial_idx]];
+ const etc1_endpoint_palette_entry& p = m_endpoint_palette[m_endpoint_remap_table_new_to_old[trial_idx]];
trial_etc_blk.set_block_color5_etc1s(p.m_color5);
trial_etc_blk.set_inten_tables_etc1s(p.m_inten5);
-
+
uint64_t trial_err = trial_etc_blk.evaluate_etc1_error(src_pixels.get_ptr(), r.get_params().m_perceptual);
if (trial_err <= thresh_err)
@@ -708,14 +842,14 @@ namespace basisu
m.m_endpoint_index = m_endpoint_remap_table_new_to_old[best_trial_idx];
new_endpoint_index = best_trial_idx;
-
+
endpoint_delta = new_endpoint_index - prev_endpoint_index;
total_endpoint_indices_remapped++;
}
}
}
-
+
if (endpoint_delta < 0)
endpoint_delta += (int)r.get_total_endpoint_clusters();
@@ -723,16 +857,32 @@ namespace basisu
}
block_endpoint_indices.push_back(m_endpoint_remap_table_new_to_old[new_endpoint_index]);
-
+
prev_endpoint_index = new_endpoint_index;
-
+
+ if ((!is_video) || (m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX))
{
int new_selector_index = m_selector_remap_table_old_to_new[m.m_selector_index];
int selector_history_buf_index = -1;
+ if (m.m_is_cr_target)
{
- const pixel_block &src_pixels = r.get_source_pixel_block(block_index);
+ for (uint32_t j = 0; j < selector_history_buf.size(); j++)
+ {
+ const int trial_idx = selector_history_buf[j];
+ if (trial_idx == new_selector_index)
+ {
+ total_used_selector_history_buf++;
+ selector_history_buf_index = j;
+ selector_history_buf_histogram.inc(j);
+ break;
+ }
+ }
+ }
+ else
+ {
+ const pixel_block& src_pixels = r.get_source_pixel_block(block_index);
etc_block etc_blk(r.get_output_block(block_index));
@@ -742,43 +892,55 @@ namespace basisu
uint64_t cur_err = 0;
for (uint32_t p = 0; p < 16; p++)
cur_err += color_distance(r.get_params().m_perceptual, src_pixels.get_ptr()[p], etc_blk_unpacked[p], false);
-
+
uint64_t best_trial_err = UINT64_MAX;
int best_trial_idx = 0;
uint32_t best_trial_history_buf_idx = 0;
- etc_block best_trial_etc_block;
const float selector_remap_thresh = maximum(1.0f, m_params.m_selector_rdo_quality_thresh); //2.5f;
+ const bool use_strict_search = (m_params.m_compression_level == 0) && (selector_remap_thresh == 1.0f);
for (uint32_t j = 0; j < selector_history_buf.size(); j++)
{
- int trial_idx = selector_history_buf[j];
+ const int trial_idx = selector_history_buf[j];
- for (uint32_t sy = 0; sy < 4; sy++)
- for (uint32_t sx = 0; sx < 4; sx++)
- etc_blk.set_selector(sx, sy, m_selector_palette[m_selector_remap_table_new_to_old[trial_idx]](sx, sy));
-
- // TODO: Optimize this
- unpack_etc1(etc_blk, etc_blk_unpacked);
-
- uint64_t trial_err = 0;
- const uint64_t thresh_err = minimum((uint64_t)ceilf(cur_err * selector_remap_thresh), best_trial_err);
- for (uint32_t p = 0; p < 16; p++)
+ if (use_strict_search)
{
- trial_err += color_distance(r.get_params().m_perceptual, src_pixels.get_ptr()[p], etc_blk_unpacked[p], false);
- if (trial_err > thresh_err)
- break;
- }
-
- if (trial_err <= cur_err * selector_remap_thresh)
- {
- if (trial_err < best_trial_err)
+ if (trial_idx == new_selector_index)
{
- best_trial_err = trial_err;
+ best_trial_err = 0;
best_trial_idx = trial_idx;
- best_trial_etc_block = etc_blk;
best_trial_history_buf_idx = j;
+ break;
+ }
+ }
+ else
+ {
+ for (uint32_t sy = 0; sy < 4; sy++)
+ for (uint32_t sx = 0; sx < 4; sx++)
+ etc_blk.set_selector(sx, sy, m_selector_palette[m_selector_remap_table_new_to_old[trial_idx]](sx, sy));
+
+ // TODO: Optimize this
+ unpack_etc1(etc_blk, etc_blk_unpacked);
+
+ uint64_t trial_err = 0;
+ const uint64_t thresh_err = minimum((uint64_t)ceilf(cur_err * selector_remap_thresh), best_trial_err);
+ for (uint32_t p = 0; p < 16; p++)
+ {
+ trial_err += color_distance(r.get_params().m_perceptual, src_pixels.get_ptr()[p], etc_blk_unpacked[p], false);
+ if (trial_err > thresh_err)
+ break;
+ }
+
+ if (trial_err <= cur_err * selector_remap_thresh)
+ {
+ if (trial_err < best_trial_err)
+ {
+ best_trial_err = trial_err;
+ best_trial_idx = trial_idx;
+ best_trial_history_buf_idx = j;
+ }
}
}
}
@@ -789,7 +951,7 @@ namespace basisu
total_selector_indices_remapped++;
new_selector_index = best_trial_idx;
-
+
total_used_selector_history_buf++;
selector_history_buf_index = best_trial_history_buf_idx;
@@ -799,8 +961,7 @@ namespace basisu
} // if (m_params.m_selector_rdo_quality_thresh > 0.0f)
m.m_selector_index = m_selector_remap_table_new_to_old[new_selector_index];
-
- block_selector_indices.push_back(m.m_selector_index);
+
if ((selector_history_buf_rle_count) && (selector_history_buf_index != 0))
{
@@ -853,24 +1014,25 @@ namespace basisu
}
m.m_selector_history_buf_index = selector_history_buf_index;
-
+
if (selector_history_buf_index < 0)
selector_history_buf.add(new_selector_index);
else if (selector_history_buf.size())
selector_history_buf.use(selector_history_buf_index);
}
+ block_selector_indices.push_back(m.m_selector_index);
} // block_x
} // block_y
-
+
if (endpoint_pred_repeat_count > 0)
{
if (endpoint_pred_repeat_count > (int)basist::ENDPOINT_PRED_MIN_REPEAT_COUNT)
{
endpoint_pred_histogram.inc(basist::ENDPOINT_PRED_REPEAT_LAST_SYMBOL);
endpoint_pred_syms[slice_index].push_back(basist::ENDPOINT_PRED_REPEAT_LAST_SYMBOL);
-
+
endpoint_pred_syms[slice_index].push_back(endpoint_pred_repeat_count);
}
else
@@ -914,12 +1076,12 @@ namespace basisu
selector_history_buf_rle_count = 0;
}
-
+
} // slice_index
debug_printf("Endpoint pred RDO total endpoint indices remapped: %u %3.2f%%\n",
total_endpoint_indices_remapped, total_endpoint_indices_remapped * 100.0f / get_total_blocks());
-
+
debug_printf("Selector history RDO total selector indices remapped: %u %3.2f%%, Used history buf: %u %3.2f%%\n",
total_selector_indices_remapped, total_selector_indices_remapped * 100.0f / get_total_blocks(),
total_used_selector_history_buf, total_used_selector_history_buf * 100.0f / get_total_blocks());
@@ -928,18 +1090,21 @@ namespace basisu
{
int_vec unused;
r.reoptimize_remapped_endpoints(block_endpoint_indices, unused, false, &block_selector_indices);
-
+
create_endpoint_palette();
}
+ check_for_valid_cr_blocks();
compute_slice_crcs();
-
+
double endpoint_pred_entropy = endpoint_pred_histogram.get_entropy() / endpoint_pred_histogram.get_total();
double delta_endpoint_entropy = delta_endpoint_histogram.get_entropy() / delta_endpoint_histogram.get_total();
double selector_entropy = selector_histogram.get_entropy() / selector_histogram.get_total();
-
+
debug_printf("Histogram entropy: EndpointPred: %3.3f DeltaEndpoint: %3.3f DeltaSelector: %3.3f\n", endpoint_pred_entropy, delta_endpoint_entropy, selector_entropy);
+ if (!endpoint_pred_histogram.get_total())
+ endpoint_pred_histogram.inc(0);
huffman_encoding_table endpoint_pred_model;
if (!endpoint_pred_model.init(endpoint_pred_histogram, 16))
{
@@ -947,12 +1112,16 @@ namespace basisu
return false;
}
+ if (!delta_endpoint_histogram.get_total())
+ delta_endpoint_histogram.inc(0);
huffman_encoding_table delta_endpoint_model;
if (!delta_endpoint_model.init(delta_endpoint_histogram, 16))
{
error_printf("delta_endpoint_model.init() failed!");
return false;
}
+ if (!selector_histogram.get_total())
+ selector_histogram.inc(0);
huffman_encoding_table selector_model;
if (!selector_model.init(selector_histogram, 16))
@@ -980,7 +1149,7 @@ namespace basisu
uint32_t selector_history_buf_run_sym_bits = coder.emit_huffman_table(selector_history_buf_rle_model);
coder.put_bits(basist::MAX_SELECTOR_HISTORY_BUF_SIZE, 13);
-
+
debug_printf("Model sizes: EndpointPred: %u bits %u bytes (%3.3f bpp) DeltaEndpoint: %u bits %u bytes (%3.3f bpp) Selector: %u bits %u bytes (%3.3f bpp) SelectorHistBufRLE: %u bits %u bytes (%3.3f bpp)\n",
endpoint_pred_model_bits, (endpoint_pred_model_bits + 7) / 8, endpoint_pred_model_bits / float(get_total_input_texels()),
delta_endpoint_bits, (delta_endpoint_bits + 7) / 8, delta_endpoint_bits / float(get_total_input_texels()),
@@ -1011,14 +1180,14 @@ namespace basisu
int endpoint_pred_repeat_count = 0;
uint32_t cur_endpoint_pred_sym_ofs = 0;
- uint32_t prev_endpoint_pred_sym = 0;
+// uint32_t prev_endpoint_pred_sym = 0;
uint32_t prev_endpoint_index = 0;
-
+
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
{
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
{
- const encoder_block &m = m_slice_encoder_blocks[slice_index](block_x, block_y);
+ const encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
if (((block_x & 1) == 0) && ((block_y & 1) == 0))
{
@@ -1036,23 +1205,23 @@ namespace basisu
endpoint_pred_repeat_count = endpoint_pred_syms[slice_index][cur_endpoint_pred_sym_ofs++];
assert(endpoint_pred_repeat_count >= (int)basist::ENDPOINT_PRED_MIN_REPEAT_COUNT);
-
- total_endpoint_pred_bits += coder.put_vlc(endpoint_pred_repeat_count - basist::ENDPOINT_PRED_MIN_REPEAT_COUNT, basist::ENDPOINT_PRED_COUNT_VLC_BITS);
+
+ total_endpoint_pred_bits += coder.put_vlc(endpoint_pred_repeat_count - basist::ENDPOINT_PRED_MIN_REPEAT_COUNT, basist::ENDPOINT_PRED_COUNT_VLC_BITS);
endpoint_pred_repeat_count--;
}
else
{
total_endpoint_pred_bits += coder.put_code(sym, endpoint_pred_model);
-
- prev_endpoint_pred_sym = sym;
+
+ //prev_endpoint_pred_sym = sym;
}
}
}
const int new_endpoint_index = m_endpoint_remap_table_old_to_new[m.m_endpoint_index];
- if (m.m_endpoint_predictor == NO_ENDPOINT_PRED_INDEX)
+ if (m.m_endpoint_predictor == basist::NO_ENDPOINT_PRED_INDEX)
{
int endpoint_delta = new_endpoint_index - prev_endpoint_index;
if (endpoint_delta < 0)
@@ -1062,33 +1231,36 @@ namespace basisu
}
prev_endpoint_index = new_endpoint_index;
-
- if (!selector_rle_count)
+
+ if ((!is_video) || (m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX))
{
- uint32_t selector_sym_index = selector_syms[slice_index][cur_selector_sym_ofs++];
-
- if (selector_sym_index == SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX)
- selector_rle_count = selector_syms[slice_index][cur_selector_sym_ofs++];
-
- total_selector_bits += coder.put_code(selector_sym_index, selector_model);
-
- if (selector_sym_index == SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX)
+ if (!selector_rle_count)
{
- int run_sym = selector_rle_count - basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
- if (run_sym >= ((int)basist::SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))
- {
- total_selector_bits += coder.put_code(basist::SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1, selector_history_buf_rle_model);
-
- uint32_t n = selector_rle_count - basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
- total_selector_bits += coder.put_vlc(n, 7);
- }
- else
- total_selector_bits += coder.put_code(run_sym, selector_history_buf_rle_model);
- }
- }
+ uint32_t selector_sym_index = selector_syms[slice_index][cur_selector_sym_ofs++];
- if (selector_rle_count)
- selector_rle_count--;
+ if (selector_sym_index == SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX)
+ selector_rle_count = selector_syms[slice_index][cur_selector_sym_ofs++];
+
+ total_selector_bits += coder.put_code(selector_sym_index, selector_model);
+
+ if (selector_sym_index == SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX)
+ {
+ int run_sym = selector_rle_count - basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
+ if (run_sym >= ((int)basist::SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))
+ {
+ total_selector_bits += coder.put_code(basist::SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1, selector_history_buf_rle_model);
+
+ uint32_t n = selector_rle_count - basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
+ total_selector_bits += coder.put_vlc(n, 7);
+ }
+ else
+ total_selector_bits += coder.put_code(run_sym, selector_history_buf_rle_model);
+ }
+ }
+
+ if (selector_rle_count)
+ selector_rle_count--;
+ }
} // block_x
@@ -1096,7 +1268,7 @@ namespace basisu
BASISU_BACKEND_VERIFY(cur_endpoint_pred_sym_ofs == endpoint_pred_syms[slice_index].size());
BASISU_BACKEND_VERIFY(cur_selector_sym_ofs == selector_syms[slice_index].size());
-
+
coder.flush();
m_output.m_slice_image_data[slice_index] = coder.get_bytes();
@@ -1122,8 +1294,8 @@ namespace basisu
bool basisu_backend::encode_endpoint_palette()
{
- const basisu_frontend &r = *m_pFront_end;
-
+ const basisu_frontend& r = *m_pFront_end;
+
// Maps NEW to OLD endpoints
uint_vec endpoint_remap_table_inv(r.get_total_endpoint_clusters());
for (uint32_t old_endpoint_index = 0; old_endpoint_index < m_endpoint_remap_table_old_to_new.size(); old_endpoint_index++)
@@ -1141,23 +1313,23 @@ namespace basisu
break;
}
}
-
+
histogram color5_delta_hist0(32); // prev 0-9, delta is -9 to 31
histogram color5_delta_hist1(32); // prev 10-21, delta is -21 to 21
histogram color5_delta_hist2(32); // prev 22-31, delta is -31 to 9
histogram inten_delta_hist(8);
-
+
color_rgba prev_color5(16, 16, 16, 0);
uint32_t prev_inten = 0;
-
+
for (uint32_t new_endpoint_index = 0; new_endpoint_index < r.get_total_endpoint_clusters(); new_endpoint_index++)
{
const uint32_t old_endpoint_index = endpoint_remap_table_inv[new_endpoint_index];
-
+
int delta_inten = m_endpoint_palette[old_endpoint_index].m_inten5 - prev_inten;
inten_delta_hist.inc(delta_inten & 7);
prev_inten = m_endpoint_palette[old_endpoint_index].m_inten5;
-
+
for (uint32_t i = 0; i < (is_grayscale ? 1U : 3U); i++)
{
const int delta = (m_endpoint_palette[old_endpoint_index].m_color5[i] - prev_color5[i]) & 31;
@@ -1176,7 +1348,7 @@ namespace basisu
if (!color5_delta_hist0.get_total()) color5_delta_hist0.inc(0);
if (!color5_delta_hist1.get_total()) color5_delta_hist1.inc(0);
if (!color5_delta_hist2.get_total()) color5_delta_hist2.inc(0);
-
+
huffman_encoding_table color5_delta_model0, color5_delta_model1, color5_delta_model2, inten_delta_model;
if (!color5_delta_model0.init(color5_delta_hist0, 16))
{
@@ -1201,7 +1373,7 @@ namespace basisu
error_printf("inten3_model.init() failed!");
return false;
}
-
+
bitwise_coder coder;
coder.init(8192);
@@ -1210,7 +1382,7 @@ namespace basisu
coder.emit_huffman_table(color5_delta_model1);
coder.emit_huffman_table(color5_delta_model2);
coder.emit_huffman_table(inten_delta_model);
-
+
coder.put_bits(is_grayscale, 1);
prev_color5.set(16, 16, 16, 0);
@@ -1219,11 +1391,11 @@ namespace basisu
for (uint32_t new_endpoint_index = 0; new_endpoint_index < r.get_total_endpoint_clusters(); new_endpoint_index++)
{
const uint32_t old_endpoint_index = endpoint_remap_table_inv[new_endpoint_index];
-
+
int delta_inten = (m_endpoint_palette[old_endpoint_index].m_inten5 - prev_inten) & 7;
coder.put_code(delta_inten, inten_delta_model);
prev_inten = m_endpoint_palette[old_endpoint_index].m_inten5;
-
+
for (uint32_t i = 0; i < (is_grayscale ? 1U : 3U); i++)
{
const int delta = (m_endpoint_palette[old_endpoint_index].m_color5[i] - prev_color5[i]) & 31;
@@ -1252,8 +1424,8 @@ namespace basisu
bool basisu_backend::encode_selector_palette()
{
- const basisu_frontend &r = *m_pFront_end;
-
+ const basisu_frontend& r = *m_pFront_end;
+
if ((m_params.m_use_global_sel_codebook) && (!m_params.m_use_hybrid_sel_codebooks))
{
histogram global_mod_indices(1 << m_params.m_global_sel_codebook_mod_bits);
@@ -1296,7 +1468,7 @@ namespace basisu
if (m_params.m_global_sel_codebook_mod_bits)
total_mod_bits += coder.put_code(m_global_selector_palette_desc[i].m_mod_index, global_mod_model);
}
-
+
coder.flush();
m_output.m_selector_palette = coder.get_bytes();
@@ -1392,7 +1564,7 @@ namespace basisu
if (!q)
continue;
- const basist::etc1_selector_palette_entry &cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
+ const basist::etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
const basist::etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
for (uint32_t j = 0; j < 4; j++)
@@ -1428,7 +1600,7 @@ namespace basisu
continue;
}
- const basist::etc1_selector_palette_entry &cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
+ const basist::etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
const basist::etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
for (uint32_t j = 0; j < 4; j++)
@@ -1463,8 +1635,8 @@ namespace basisu
} // if (m_params.m_use_global_sel_codebook)
- debug_printf("Selector codebook bits: %u bytes: %u, Bits per entry: %3.1f, Avg bits/texel: %3.3f\n",
- (int)m_output.m_selector_palette.size() * 8, (int)m_output.m_selector_palette.size(),
+ debug_printf("Selector codebook bits: %u bytes: %u, Bits per entry: %3.1f, Avg bits/texel: %3.3f\n",
+ (int)m_output.m_selector_palette.size() * 8, (int)m_output.m_selector_palette.size(),
m_output.m_selector_palette.size() * 8.0f / r.get_total_selector_clusters(), m_output.m_selector_palette.size() * 8.0f / get_total_input_texels());
return true;
@@ -1472,9 +1644,10 @@ namespace basisu
uint32_t basisu_backend::encode()
{
+ const bool is_video = m_pFront_end->get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
m_output.m_slice_desc = m_slices;
m_output.m_etc1s = m_params.m_etc1s;
-
+
create_endpoint_palette();
create_selector_palette();
diff --git a/basisu_backend.h b/basisu_backend.h
index cd1f020..e8f783e 100644
--- a/basisu_backend.h
+++ b/basisu_backend.h
@@ -36,6 +36,7 @@ namespace basisu
int m_selector_history_buf_index;
+ bool m_is_cr_target;
void clear()
{
m_endpoint_predictor = 0;
@@ -44,6 +45,7 @@ namespace basisu
m_selector_index = 0;
m_selector_history_buf_index = 0;
+ m_is_cr_target = false;
}
};
@@ -105,6 +107,14 @@ namespace basisu
struct basisu_backend_slice_desc
{
+ basisu_backend_slice_desc()
+ {
+ clear();
+ }
+ void clear()
+ {
+ clear_obj(*this);
+ }
uint32_t m_first_block_index;
uint32_t m_orig_width;
@@ -122,6 +132,7 @@ namespace basisu
uint32_t m_source_file_index; // also the basis image index
uint32_t m_mip_index;
bool m_alpha;
+ bool m_iframe;
};
typedef std::vector basisu_backend_slice_desc_vec;
@@ -308,6 +319,8 @@ namespace basisu
bool encode_image();
bool encode_endpoint_palette();
bool encode_selector_palette();
+ int find_video_frame(int slice_index, int delta);
+ void check_for_valid_cr_blocks();
};
} // namespace basisu
diff --git a/basisu_basis_file.cpp b/basisu_basis_file.cpp
index e4a73a0..19752c4 100644
--- a/basisu_basis_file.cpp
+++ b/basisu_basis_file.cpp
@@ -86,6 +86,8 @@ namespace basisu
if (slice_descs[i].m_alpha)
m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsIsAlphaData;
+ if (slice_descs[i].m_iframe)
+ m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsFrameIsIFrame;
m_images_descs[i].m_orig_width = slice_descs[i].m_orig_width;
m_images_descs[i].m_orig_height = slice_descs[i].m_orig_height;
diff --git a/basisu_comp.cpp b/basisu_comp.cpp
index 7826a42..89cf004 100644
--- a/basisu_comp.cpp
+++ b/basisu_comp.cpp
@@ -17,12 +17,13 @@
#include
#define BASISU_USE_STB_IMAGE_RESIZE_FOR_MIPMAP_GEN 0
-#define DEBUG_RESIZE_TEXTURE_TO_64x64 (0)
+#define DEBUG_CROP_TEXTURE_TO_64x64 (0)
+#define DEBUG_RESIZE_TEXTURE (0)
#define DEBUG_EXTRACT_SINGLE_BLOCK (0)
namespace basisu
{
- basis_compressor::basis_compressor() :
+ basis_compressor::basis_compressor() :
m_total_blocks(0),
m_auto_global_sel_pal(false),
m_basis_file_size(0),
@@ -68,6 +69,8 @@ namespace basisu
PRINT_BOOL_VALUE(m_check_for_alpha)
PRINT_BOOL_VALUE(m_force_alpha)
PRINT_BOOL_VALUE(m_seperate_rg_to_color_alpha);
+ PRINT_BOOL_VALUE(m_multithreading);
+ PRINT_BOOL_VALUE(m_disable_hierarchical_endpoint_codebooks);
PRINT_FLOAT_VALUE(m_hybrid_sel_cb_quality_thresh);
@@ -314,9 +317,14 @@ namespace basisu
file_image = block_image;
#endif
-#if DEBUG_RESIZE_TEXTURE_TO_64x64
+#if DEBUG_CROP_TEXTURE_TO_64x64
file_image.resize(64, 64);
#endif
+#if DEBUG_RESIZE_TEXTURE
+ image temp_img((file_image.get_width() + 1) / 2, (file_image.get_height() + 1) / 2);
+ image_resample(file_image, temp_img, m_params.m_perceptual, "kaiser");
+ temp_img.swap(file_image);
+#endif
if ((!file_image.get_width()) || (!file_image.get_height()))
{
@@ -444,6 +452,11 @@ namespace basisu
slice_desc.m_mip_index = mip_indices[slice_index];
slice_desc.m_alpha = is_alpha_slice;
+ slice_desc.m_iframe = false;
+ if (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames)
+ {
+ slice_desc.m_iframe = (source_file_index == 0);
+ }
m_total_blocks += slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;
total_macroblocks += slice_desc.m_num_macroblocks_x * slice_desc.m_num_macroblocks_y;
@@ -487,8 +500,8 @@ namespace basisu
{
const basisu_backend_slice_desc &slice_desc = m_slice_descs[i];
- printf("Slice: %u, alpha: %u, orig width/height: %ux%u, width/height: %ux%u, first_block: %u, image_index: %u, mip_level: %u\n",
- i, slice_desc.m_alpha, slice_desc.m_orig_width, slice_desc.m_orig_height, slice_desc.m_width, slice_desc.m_height, slice_desc.m_first_block_index, slice_desc.m_source_file_index, slice_desc.m_mip_index);
+ printf("Slice: %u, alpha: %u, orig width/height: %ux%u, width/height: %ux%u, first_block: %u, image_index: %u, mip_level: %u, iframe: %u\n",
+ i, slice_desc.m_alpha, slice_desc.m_orig_width, slice_desc.m_orig_height, slice_desc.m_width, slice_desc.m_height, slice_desc.m_first_block_index, slice_desc.m_source_file_index, slice_desc.m_mip_index, slice_desc.m_iframe);
if (m_any_source_image_has_alpha)
{
@@ -522,6 +535,11 @@ namespace basisu
if ((slice_desc.m_orig_width > slice_desc.m_width) || (slice_desc.m_orig_height > slice_desc.m_height))
return false;
+ if ((slice_desc.m_source_file_index == 0) && (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames))
+ {
+ if (!slice_desc.m_iframe)
+ return false;
+ }
}
return true;
@@ -778,7 +796,11 @@ namespace basisu
p.m_debug_stats = m_params.m_debug;
p.m_debug_images = m_params.m_debug_images;
p.m_compression_level = m_params.m_compression_level;
-
+ p.m_tex_type = m_params.m_tex_type;
+ p.m_multithreaded = m_params.m_multithreading;
+ p.m_disable_hierarchical_endpoint_codebooks = m_params.m_disable_hierarchical_endpoint_codebooks;
+ p.m_pJob_pool = m_params.m_pJob_pool;
+
if ((m_params.m_global_sel_pal) || (m_auto_global_sel_pal))
{
p.m_pGlobal_sel_codebook = m_params.m_pSel_codebook;
diff --git a/basisu_comp.h b/basisu_comp.h
index 415335e..5311d0b 100644
--- a/basisu_comp.h
+++ b/basisu_comp.h
@@ -186,7 +186,8 @@ namespace basisu
m_quality_level(-1),
m_mip_scale(1.0f, .000125f, 4.0f),
m_mip_smallest_dimension(1, 1, 16384),
- m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL)
+ m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
+ m_pJob_pool(nullptr)
{
clear();
}
@@ -217,10 +218,12 @@ namespace basisu
m_compute_stats.clear();
m_check_for_alpha.clear();
m_force_alpha.clear();
+ m_multithreading.clear();
m_seperate_rg_to_color_alpha.clear();
m_hybrid_sel_cb_quality_thresh.clear();
m_global_pal_bits.clear();
m_global_mod_bits.clear();
+ m_disable_hierarchical_endpoint_codebooks.clear();
m_no_endpoint_rdo.clear();
m_endpoint_rdo_thresh.clear();
@@ -243,6 +246,8 @@ namespace basisu
m_userdata0 = 0;
m_userdata1 = 0;
m_us_per_frame = 0;
+
+ m_pJob_pool = nullptr;
}
// Pointer to the global selector codebook, or nullptr to not use a global selector codebook
@@ -303,10 +308,13 @@ namespace basisu
// Always put alpha slices in the output basis file, even when the input doesn't have alpha
bool_param m_force_alpha;
+ bool_param m_multithreading;
// Split the R channel to RGB and the G channel to alpha, then write a basis file with alpha channels
bool_param m_seperate_rg_to_color_alpha;
+ bool_param m_disable_hierarchical_endpoint_codebooks;
+
// Global/hybrid selector codebook parameters
param m_hybrid_sel_cb_quality_thresh;
param m_global_pal_bits;
@@ -334,6 +342,8 @@ namespace basisu
uint32_t m_userdata0;
uint32_t m_userdata1;
uint32_t m_us_per_frame;
+
+ job_pool *m_pJob_pool;
};
class basis_compressor
diff --git a/basisu_enc.cpp b/basisu_enc.cpp
index 48f724d..ba374b3 100644
--- a/basisu_enc.cpp
+++ b/basisu_enc.cpp
@@ -1197,4 +1197,181 @@ namespace basisu
}
}
+ uint32_t hash_hsieh(const uint8_t *pBuf, size_t len)
+ {
+ if (!pBuf || !len)
+ return 0;
+
+ uint32_t h = static_cast(len);
+
+ const uint32_t bytes_left = len & 3;
+ len >>= 2;
+
+ while (len--)
+ {
+ const uint16_t *pWords = reinterpret_cast(pBuf);
+
+ h += pWords[0];
+
+ const uint32_t t = (pWords[1] << 11) ^ h;
+ h = (h << 16) ^ t;
+
+ pBuf += sizeof(uint32_t);
+
+ h += h >> 11;
+ }
+
+ switch (bytes_left)
+ {
+ case 1:
+ h += *reinterpret_cast(pBuf);
+ h ^= h << 10;
+ h += h >> 1;
+ break;
+ case 2:
+ h += *reinterpret_cast(pBuf);
+ h ^= h << 11;
+ h += h >> 17;
+ break;
+ case 3:
+ h += *reinterpret_cast(pBuf);
+ h ^= h << 16;
+ h ^= (static_cast(pBuf[sizeof(uint16_t)])) << 18;
+ h += h >> 11;
+ break;
+ default:
+ break;
+ }
+
+ h ^= h << 3;
+ h += h >> 5;
+ h ^= h << 4;
+ h += h >> 17;
+ h ^= h << 25;
+ h += h >> 6;
+
+ return h;
+ }
+
+ job_pool::job_pool(uint32_t num_threads) :
+ m_kill_flag(false),
+ m_num_active_jobs(0)
+ {
+ assert(num_threads >= 1U);
+
+ debug_printf("job_pool::job_pool: %u total threads\n", num_threads);
+
+ if (num_threads > 1)
+ {
+ m_threads.resize(num_threads - 1);
+
+ for (int i = 0; i < ((int)num_threads - 1); i++)
+ m_threads[i] = std::thread([this, i] { job_thread(i); });
+ }
+ }
+
+ job_pool::~job_pool()
+ {
+ debug_printf("job_pool::~job_pool\n");
+
+ // Notify all workers that they need to die right now.
+ m_kill_flag = true;
+
+ m_has_work.notify_all();
+
+ // Wait for all workers to die.
+ for (uint32_t i = 0; i < m_threads.size(); i++)
+ m_threads[i].join();
+ }
+
+ void job_pool::add_job(const std::function& job)
+ {
+ std::unique_lock lock(m_mutex);
+
+ m_queue.emplace_back(job);
+
+ const size_t queue_size = m_queue.size();
+
+ lock.unlock();
+
+ if (queue_size > 1)
+ m_has_work.notify_one();
+ }
+
+ void job_pool::add_job(std::function&& job)
+ {
+ std::unique_lock lock(m_mutex);
+
+ m_queue.emplace_back(std::move(job));
+
+ const size_t queue_size = m_queue.size();
+
+ lock.unlock();
+
+ if (queue_size > 1)
+ m_has_work.notify_one();
+ }
+
+ void job_pool::wait_for_all()
+ {
+ std::unique_lock lock(m_mutex);
+
+ // Drain the job queue on the calling thread.
+ while (!m_queue.empty())
+ {
+ std::function job(m_queue.back());
+ m_queue.pop_back();
+
+ lock.unlock();
+
+ job();
+
+ lock.lock();
+ }
+
+ // The queue is empty, now wait for all active jobs to finish up.
+ m_no_more_jobs.wait(lock, [this]{ return !m_num_active_jobs; } );
+ }
+
+ void job_pool::job_thread(uint32_t index)
+ {
+ debug_printf("job_pool::job_thread: starting %u\n", index);
+
+ while (true)
+ {
+ std::unique_lock lock(m_mutex);
+
+ // Wait for any jobs to be issued.
+ m_has_work.wait(lock, [this] { return m_kill_flag || m_queue.size(); } );
+
+ // Check to see if we're supposed to exit.
+ if (m_kill_flag)
+ break;
+
+ // Get the job and execute it.
+ std::function job(m_queue.back());
+ m_queue.pop_back();
+
+ ++m_num_active_jobs;
+
+ lock.unlock();
+
+ job();
+
+ lock.lock();
+
+ --m_num_active_jobs;
+
+ // Now check if there are no more jobs remaining.
+ const bool all_done = m_queue.empty() && !m_num_active_jobs;
+
+ lock.unlock();
+
+ if (all_done)
+ m_no_more_jobs.notify_all();
+ }
+
+ debug_printf("job_pool::job_thread: exiting\n");
+ }
+
} // namespace basisu
diff --git a/basisu_enc.h b/basisu_enc.h
index 0a9e721..157e717 100644
--- a/basisu_enc.h
+++ b/basisu_enc.h
@@ -17,6 +17,13 @@
#include "basisu_enc.h"
#include "transcoder/basisu_transcoder_internal.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
#ifndef _WIN32
#include
#endif
@@ -36,6 +43,42 @@ namespace basisu
{
return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i);
}
+
+ // Hashing
+
+ inline uint32_t bitmix32c(uint32_t v)
+ {
+ v = (v + 0x7ed55d16) + (v << 12);
+ v = (v ^ 0xc761c23c) ^ (v >> 19);
+ v = (v + 0x165667b1) + (v << 5);
+ v = (v + 0xd3a2646c) ^ (v << 9);
+ v = (v + 0xfd7046c5) + (v << 3);
+ v = (v ^ 0xb55a4f09) ^ (v >> 16);
+ return v;
+ }
+
+ inline uint32_t bitmix32(uint32_t v)
+ {
+ v -= (v << 6);
+ v ^= (v >> 17);
+ v -= (v << 9);
+ v ^= (v << 4);
+ v -= (v << 3);
+ v ^= (v << 10);
+ v ^= (v >> 15);
+ return v;
+ }
+
+ uint32_t hash_hsieh(const uint8_t* pBuf, size_t len);
+
+ template
+ struct bit_hasher
+ {
+ std::size_t operator()(const Key& k) const
+ {
+ return hash_hsieh(reinterpret_cast(&k), sizeof(k));
+ }
+ };
// Linear algebra
@@ -165,8 +208,10 @@ namespace basisu
inline T length() const { return sqrt(norm()); }
inline T squared_distance(const vec &other) const { T d2 = 0; for (uint32_t i = 0; i < N; i++) { T d = m_v[i] - other.m_v[i]; d2 += d * d; } return d2; }
+ inline double squared_distance_d(const vec& other) const { double d2 = 0; for (uint32_t i = 0; i < N; i++) { double d = (double)m_v[i] - (double)other.m_v[i]; d2 += d * d; } return d2; }
- inline T distance(const vec &other) const { return squared_distance(other); }
+ inline T distance(const vec &other) const { return static_cast(sqrt(squared_distance(other))); }
+ inline double distance_d(const vec& other) const { return sqrt(squared_distance_d(other)); }
inline vec &normalize_in_place() { T len = length(); if (len != 0.0f) *this *= (1.0f / len); return *this; }
@@ -176,6 +221,22 @@ namespace basisu
m_v[i] = basisu::clamp(m_v[i], l, h);
return *this;
}
+
+ static vec component_min(const vec& a, const vec& b)
+ {
+ vec res;
+ for (uint32_t i = 0; i < N; i++)
+ res[i] = minimum(a[i], b[i]);
+ return res;
+ }
+
+ static vec component_max(const vec& a, const vec& b)
+ {
+ vec res;
+ for (uint32_t i = 0; i < N; i++)
+ res[i] = maximum(a[i], b[i]);
+ return res;
+ }
};
typedef vec<4, double> vec4D;
@@ -290,6 +351,37 @@ namespace basisu
[pKeys](uint32_t a, uint32_t b) { return pKeys[a] < pKeys[b]; }
);
}
+
+ // Very simple job pool with no dependencies.
+ class job_pool
+ {
+ BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(job_pool);
+
+ public:
+ job_pool(uint32_t num_threads);
+ ~job_pool();
+
+ void add_job(const std::function& job);
+ void add_job(std::function&& job);
+
+ void wait_for_all();
+
+ size_t get_total_threads() const { return 1 + m_threads.size(); }
+
+ private:
+ std::vector m_threads;
+ std::vector > m_queue;
+
+ std::mutex m_mutex;
+ std::condition_variable m_has_work;
+ std::condition_variable m_no_more_jobs;
+
+ uint32_t m_num_active_jobs;
+
+ std::atomic m_kill_flag;
+
+ void job_thread(uint32_t index);
+ };
// Simple 32-bit color class
@@ -867,7 +959,8 @@ namespace basisu
class tree_vector_quant
{
public:
- typedef std::pair training_vec_with_weight;
+ typedef TrainingVectorType training_vec_type;
+ typedef std::pair training_vec_with_weight;
typedef std::vector< training_vec_with_weight > array_of_weighted_training_vecs;
tree_vector_quant() :
@@ -882,7 +975,11 @@ namespace basisu
m_next_codebook_index = 0;
}
- void add_training_vec(const TrainingVectorType &v, uint32_t weight) { m_training_vecs.push_back(std::make_pair(v, weight)); }
+ void add_training_vec(const TrainingVectorType &v, uint64_t weight) { m_training_vecs.push_back(std::make_pair(v, weight)); }
+
+ size_t get_total_training_vecs() const { return m_training_vecs.size(); }
+ const array_of_weighted_training_vecs &get_training_vecs() const { return m_training_vecs; }
+ array_of_weighted_training_vecs &get_training_vecs() { return m_training_vecs; }
void retrieve(std::vector< std::vector > &codebook) const
{
@@ -1025,7 +1122,7 @@ namespace basisu
for (uint32_t i = 0; i < m_training_vecs.size(); i++)
{
const TrainingVectorType &v = m_training_vecs[i].first;
- const uint32_t weight = m_training_vecs[i].second;
+ const uint64_t weight = m_training_vecs[i].second;
root.m_training_vecs.push_back(i);
@@ -1049,7 +1146,8 @@ namespace basisu
float l_var = 0.0f, r_var = 0.0f;
// Compute initial left/right child origins
- prep_split(m_nodes[node_index], l_child_org, r_child_org);
+ if (!prep_split(m_nodes[node_index], l_child_org, r_child_org))
+ return false;
// Use k-means iterations to refine these children vectors
if (!refine_split(m_nodes[node_index], l_child_org, l_weight, l_var, l_children, r_child_org, r_weight, r_var, r_children))
@@ -1071,6 +1169,34 @@ namespace basisu
l_child.set(l_child_org, l_weight, l_var, l_children);
r_child.set(r_child_org, r_weight, r_var, r_children);
+ if ((l_child.m_var <= 0.0f) && (l_child.m_training_vecs.size() > 1))
+ {
+ TrainingVectorType v(m_training_vecs[l_child.m_training_vecs[0]].first);
+
+ for (uint32_t i = 1; i < l_child.m_training_vecs.size(); i++)
+ {
+ if (!(v == m_training_vecs[l_child.m_training_vecs[i]].first))
+ {
+ l_child.m_var = 1e-4f;
+ break;
+ }
+ }
+ }
+
+ if ((r_child.m_var <= 0.0f) && (r_child.m_training_vecs.size() > 1))
+ {
+ TrainingVectorType v(m_training_vecs[r_child.m_training_vecs[0]].first);
+
+ for (uint32_t i = 1; i < r_child.m_training_vecs.size(); i++)
+ {
+ if (!(v == m_training_vecs[r_child.m_training_vecs[i]].first))
+ {
+ r_child.m_var = 1e-4f;
+ break;
+ }
+ }
+ }
+
if ((l_child.m_var > 0.0f) && (l_child.m_training_vecs.size() > 1))
var_heap.add_heap(l_child_index, l_var);
@@ -1111,7 +1237,7 @@ namespace basisu
return compute_pca_from_covar(cmatrix);
}
- void prep_split(const tsvq_node &node, TrainingVectorType &l_child_result, TrainingVectorType &r_child_result) const
+ bool prep_split(const tsvq_node &node, TrainingVectorType &l_child_result, TrainingVectorType &r_child_result) const
{
const uint32_t N = TrainingVectorType::num_elements;
@@ -1119,7 +1245,7 @@ namespace basisu
{
l_child_result = m_training_vecs[node.m_training_vecs[0]].first;
r_child_result = m_training_vecs[node.m_training_vecs[1]].first;
- return;
+ return true;
}
TrainingVectorType axis(compute_split_axis(node)), l_child(0.0f), r_child(0.0f);
@@ -1152,17 +1278,77 @@ namespace basisu
}
else
{
- // Empty cell problem
- l_child_result = node.m_origin;
- r_child_result = node.m_origin;
-
- // Nudge the two cells apart and hope k-means can separate them.
- for (uint32_t i = 0; i < N; i++)
+ TrainingVectorType l(1e+20f);
+ TrainingVectorType h(-1e+20f);
+ for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
{
- l_child_result[i] -= .000125f;
- r_child_result[i] += .000125f;
+ const TrainingVectorType& v = m_training_vecs[node.m_training_vecs[i]].first;
+
+ l = TrainingVectorType::component_min(l, v);
+ h = TrainingVectorType::component_max(h, v);
+ }
+
+ TrainingVectorType r(h - l);
+
+ float largest_axis_v = 0.0f;
+ int largest_axis_index = -1;
+ for (uint32_t i = 0; i < TrainingVectorType::num_elements; i++)
+ {
+ if (r[i] > largest_axis_v)
+ {
+ largest_axis_v = r[i];
+ largest_axis_index = i;
+ }
+ }
+
+ if (largest_axis_index < 0)
+ return false;
+
+ std::vector keys(node.m_training_vecs.size());
+ for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
+ keys[i] = m_training_vecs[node.m_training_vecs[i]].first[largest_axis_index];
+
+ uint_vec indices(node.m_training_vecs.size());
+ indirect_sort((uint32_t)node.m_training_vecs.size(), &indices[0], &keys[0]);
+
+ l_child.set_zero();
+ l_weight = 0;
+
+ r_child.set_zero();
+ r_weight = 0;
+
+ const uint32_t half_index = (uint32_t)node.m_training_vecs.size() / 2;
+ for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
+ {
+ const float weight = (float)m_training_vecs[node.m_training_vecs[i]].second;
+
+ const TrainingVectorType& v = m_training_vecs[node.m_training_vecs[i]].first;
+
+ if (i < half_index)
+ {
+ l_child += v * weight;
+ l_weight += weight;
+ }
+ else
+ {
+ r_child += v * weight;
+ r_weight += weight;
+ }
+ }
+
+ if ((l_weight > 0.0f) && (r_weight > 0.0f))
+ {
+ l_child_result = l_child * static_cast(1.0f / l_weight);
+ r_child_result = r_child * static_cast(1.0f / r_weight);
+ }
+ else
+ {
+ l_child_result = l;
+ r_child_result = h;
}
}
+
+ return true;
}
bool refine_split(const tsvq_node &node,
@@ -1191,9 +1377,9 @@ namespace basisu
for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
{
const TrainingVectorType &v = m_training_vecs[node.m_training_vecs[i]].first;
- const uint32_t weight = m_training_vecs[node.m_training_vecs[i]].second;
+ const uint64_t weight = m_training_vecs[node.m_training_vecs[i]].second;
- double left_dist2 = l_child.squared_distance(v), right_dist2 = r_child.squared_distance(v);
+ double left_dist2 = l_child.squared_distance_d(v), right_dist2 = r_child.squared_distance_d(v);
if (left_dist2 >= right_dist2)
{
@@ -1214,7 +1400,36 @@ namespace basisu
}
if ((!l_weight) || (!r_weight))
- return false;
+ {
+ TrainingVectorType firstVec;
+ for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
+ {
+ const TrainingVectorType& v = m_training_vecs[node.m_training_vecs[i]].first;
+ const uint64_t weight = m_training_vecs[node.m_training_vecs[i]].second;
+
+ if ((!i) || (v == firstVec))
+ {
+ firstVec = v;
+
+ new_r_child += (v * static_cast(weight));
+ r_weight += weight;
+
+ r_ttsum += weight * v.dot(v);
+ r_children.push_back(node.m_training_vecs[i]);
+ }
+ else
+ {
+ new_l_child += (v * static_cast(weight));
+ l_weight += weight;
+
+ l_ttsum += weight * v.dot(v);
+ l_children.push_back(node.m_training_vecs[i]);
+ }
+ }
+
+ if (!l_weight)
+ return false;
+ }
l_var = static_cast(l_ttsum - (new_l_child.dot(new_l_child) / l_weight));
r_var = static_cast(r_ttsum - (new_r_child.dot(new_r_child) / r_weight));
@@ -1242,6 +1457,238 @@ namespace basisu
}
};
+ struct weighted_block_group
+ {
+ uint64_t m_total_weight;
+ uint_vec m_indices;
+ };
+
+ template
+ bool generate_hierarchical_codebook_threaded_internal(Quantizer& q,
+ uint32_t max_codebook_size, uint32_t max_parent_codebook_size,
+ std::vector& codebook,
+ std::vector& parent_codebook,
+ uint32_t max_threads, bool limit_clusterizers, job_pool *pJob_pool)
+ {
+ codebook.resize(0);
+ parent_codebook.resize(0);
+
+ if ((max_threads <= 1) || (q.get_training_vecs().size() < 256) || (max_codebook_size < max_threads * 16))
+ {
+ if (!q.generate(max_codebook_size))
+ return false;
+
+ q.retrieve(codebook);
+
+ if (max_parent_codebook_size)
+ q.retrieve(max_parent_codebook_size, parent_codebook);
+
+ return true;
+ }
+
+ const uint32_t cMaxThreads = 16;
+ if (max_threads > cMaxThreads)
+ max_threads = cMaxThreads;
+
+ if (!q.generate(max_threads))
+ return false;
+
+ std::vector initial_codebook;
+
+ q.retrieve(initial_codebook);
+
+ if (initial_codebook.size() < max_threads)
+ {
+ codebook = initial_codebook;
+
+ if (max_parent_codebook_size)
+ q.retrieve(max_parent_codebook_size, parent_codebook);
+
+ return true;
+ }
+
+ Quantizer quantizers[cMaxThreads];
+
+ bool success_flags[cMaxThreads];
+ clear_obj(success_flags);
+
+ std::vector local_clusters[cMaxThreads];
+ std::vector local_parent_clusters[cMaxThreads];
+
+ for (uint32_t thread_iter = 0; thread_iter < max_threads; thread_iter++)
+ {
+ pJob_pool->add_job( [thread_iter, &local_clusters, &local_parent_clusters, &success_flags, &quantizers, &initial_codebook, &q, &limit_clusterizers, &max_codebook_size, &max_threads, &max_parent_codebook_size] {
+
+ Quantizer& lq = quantizers[thread_iter];
+ uint_vec& cluster_indices = initial_codebook[thread_iter];
+
+ uint_vec local_to_global(cluster_indices.size());
+
+ for (uint32_t i = 0; i < cluster_indices.size(); i++)
+ {
+ const uint32_t global_training_vec_index = cluster_indices[i];
+ local_to_global[i] = global_training_vec_index;
+
+ lq.add_training_vec(q.get_training_vecs()[global_training_vec_index].first, q.get_training_vecs()[global_training_vec_index].second);
+ }
+
+ const uint32_t max_clusters = limit_clusterizers ? ((max_codebook_size + max_threads - 1) / max_threads) : (uint32_t)lq.get_total_training_vecs();
+
+ success_flags[thread_iter] = lq.generate(max_clusters);
+
+ if (success_flags[thread_iter])
+ {
+ lq.retrieve(local_clusters[thread_iter]);
+
+ for (uint32_t i = 0; i < local_clusters[thread_iter].size(); i++)
+ {
+ for (uint32_t j = 0; j < local_clusters[thread_iter][i].size(); j++)
+ local_clusters[thread_iter][i][j] = local_to_global[local_clusters[thread_iter][i][j]];
+ }
+
+ if (max_parent_codebook_size)
+ {
+ lq.retrieve((max_parent_codebook_size + max_threads - 1) / max_threads, local_parent_clusters[thread_iter]);
+
+ for (uint32_t i = 0; i < local_parent_clusters[thread_iter].size(); i++)
+ {
+ for (uint32_t j = 0; j < local_parent_clusters[thread_iter][i].size(); j++)
+ local_parent_clusters[thread_iter][i][j] = local_to_global[local_parent_clusters[thread_iter][i][j]];
+ }
+ }
+ }
+
+ } );
+
+ } // thread_iter
+
+ pJob_pool->wait_for_all();
+
+ uint32_t total_clusters = 0, total_parent_clusters = 0;
+
+ for (int thread_iter = 0; thread_iter < (int)max_threads; thread_iter++)
+ {
+ if (!success_flags[thread_iter])
+ return false;
+ total_clusters += (uint32_t)local_clusters[thread_iter].size();
+ total_parent_clusters += (uint32_t)local_parent_clusters[thread_iter].size();
+ }
+
+ codebook.reserve(total_clusters);
+ parent_codebook.reserve(total_parent_clusters);
+
+ for (uint32_t thread_iter = 0; thread_iter < max_threads; thread_iter++)
+ {
+ for (uint32_t j = 0; j < local_clusters[thread_iter].size(); j++)
+ {
+ codebook.resize(codebook.size() + 1);
+ codebook.back().swap(local_clusters[thread_iter][j]);
+ }
+
+ for (uint32_t j = 0; j < local_parent_clusters[thread_iter].size(); j++)
+ {
+ parent_codebook.resize(parent_codebook.size() + 1);
+ parent_codebook.back().swap(local_parent_clusters[thread_iter][j]);
+ }
+ }
+
+ return true;
+ }
+
+ template
+ bool generate_hierarchical_codebook_threaded(Quantizer& q,
+ uint32_t max_codebook_size, uint32_t max_parent_codebook_size,
+ std::vector& codebook,
+ std::vector& parent_codebook,
+ uint32_t max_threads, job_pool *pJob_pool)
+ {
+ typedef bit_hasher training_vec_bit_hasher;
+ typedef std::unordered_map < typename Quantizer::training_vec_type, weighted_block_group,
+ training_vec_bit_hasher> group_hash;
+
+ group_hash unique_vecs;
+
+ weighted_block_group g;
+ g.m_indices.resize(1);
+
+ for (uint32_t i = 0; i < q.get_training_vecs().size(); i++)
+ {
+ g.m_total_weight = q.get_training_vecs()[i].second;
+ g.m_indices[0] = i;
+
+ auto ins_res = unique_vecs.insert(std::make_pair(q.get_training_vecs()[i].first, g));
+
+ if (!ins_res.second)
+ {
+ (ins_res.first)->second.m_total_weight += g.m_total_weight;
+ (ins_res.first)->second.m_indices.push_back(i);
+ }
+ }
+
+ debug_printf("generate_hierarchical_codebook_threaded: %u training vectors, %u unique training vectors\n", q.get_total_training_vecs(), (uint32_t)unique_vecs.size());
+
+ Quantizer group_quant;
+ typedef typename group_hash::const_iterator group_hash_const_iter;
+ std::vector unique_vec_iters;
+ unique_vec_iters.reserve(unique_vecs.size());
+
+ for (auto iter = unique_vecs.begin(); iter != unique_vecs.end(); ++iter)
+ {
+ group_quant.add_training_vec(iter->first, iter->second.m_total_weight);
+ unique_vec_iters.push_back(iter);
+ }
+
+ bool limit_clusterizers = true;
+ if (unique_vecs.size() <= max_codebook_size)
+ limit_clusterizers = false;
+
+ debug_printf("Limit clusterizers: %u\n", limit_clusterizers);
+
+ std::vector group_codebook, group_parent_codebook;
+ bool status = generate_hierarchical_codebook_threaded_internal(group_quant,
+ max_codebook_size, max_parent_codebook_size,
+ group_codebook,
+ group_parent_codebook,
+ (unique_vecs.size() < 65536*4) ? 1 : max_threads, limit_clusterizers, pJob_pool);
+
+ if (!status)
+ return false;
+
+ codebook.resize(0);
+ for (uint32_t i = 0; i < group_codebook.size(); i++)
+ {
+ codebook.resize(codebook.size() + 1);
+
+ for (uint32_t j = 0; j < group_codebook[i].size(); j++)
+ {
+ const uint32_t group_index = group_codebook[i][j];
+
+ typename group_hash::const_iterator group_iter = unique_vec_iters[group_index];
+ const uint_vec& training_vec_indices = group_iter->second.m_indices;
+
+ append_vector(codebook.back(), training_vec_indices);
+ }
+ }
+
+ parent_codebook.resize(0);
+ for (uint32_t i = 0; i < group_parent_codebook.size(); i++)
+ {
+ parent_codebook.resize(parent_codebook.size() + 1);
+
+ for (uint32_t j = 0; j < group_parent_codebook[i].size(); j++)
+ {
+ const uint32_t group_index = group_parent_codebook[i][j];
+
+ typename group_hash::const_iterator group_iter = unique_vec_iters[group_index];
+ const uint_vec& training_vec_indices = group_iter->second.m_indices;
+
+ append_vector(parent_codebook.back(), training_vec_indices);
+ }
+ }
+
+ return true;
+ }
+
// Canonical Huffman coding
class histogram
@@ -2353,7 +2800,7 @@ namespace basisu
}
void fill_buffer_with_random_bytes(void *pBuf, size_t size, uint32_t seed = 1);
-
+
} // namespace basisu
diff --git a/basisu_etc.cpp b/basisu_etc.cpp
index 5d6be63..24f233b 100644
--- a/basisu_etc.cpp
+++ b/basisu_etc.cpp
@@ -1250,11 +1250,10 @@ namespace basisu
// Packs solid color blocks efficiently using a set of small precomputed tables.
// For random 888 inputs, MSE results are better than Erricson's ETC1 packer in "slow" mode ~9.5% of the time, is slightly worse only ~.01% of the time, and is equal the rest of the time.
- static uint64_t pack_etc1_block_solid_color(etc_block& block, const uint8_t* pColor, basis_etc1_pack_params& pack_params, pack_etc1_block_context& context)
+ uint64_t pack_etc1_block_solid_color(etc_block& block, const uint8_t* pColor, bool diff_only)
{
assert(g_etc1_inverse_lookup[0][255]);
- context, pack_params;
static uint32_t s_next_comp[4] = { 1, 2, 0, 1 };
uint32_t best_error = UINT32_MAX, best_i = 0;
@@ -1281,6 +1280,10 @@ namespace basisu
do
{
const uint32_t x = *pTable++;
+
+ const bool diff = (x & 1) != 0;
+ if ((diff_only) && (!diff))
+ continue;
#ifdef BASISU_BUILD_DEBUG
const uint32_t diff = x & 1;
@@ -1309,6 +1312,9 @@ namespace basisu
}
found_perfect_match:
+ if (best_error == UINT32_MAX)
+ return UINT64_MAX;
+
const uint32_t diff = best_x & 1;
const uint32_t inten = (best_x >> 1) & 7;
@@ -1338,14 +1344,11 @@ namespace basisu
static uint32_t pack_etc1_block_solid_color_constrained(
etc1_optimizer::results& results,
uint32_t num_colors, const uint8_t *pColor,
- basis_etc1_pack_params& pack_params,
- pack_etc1_block_context& context,
bool use_diff,
const color_rgba* pBase_color5_unscaled)
{
assert(g_etc1_inverse_lookup[0][255]);
- context, pack_params;
static uint32_t s_next_comp[4] = { 1, 2, 0, 1 };
uint32_t best_error = UINT32_MAX, best_i = 0;
@@ -1587,7 +1590,7 @@ namespace basisu
return upper_lower_sum < left_right_sum;
}
-
+
uint64_t pack_etc1_block(etc_block& dst_block, const color_rgba* pSrc_pixels, basis_etc1_pack_params& pack_params, pack_etc1_block_context& context, const uint8_t* pForce_selectors)
{
#if BASISU_DEBUG_ETC_ENCODER
@@ -1607,7 +1610,7 @@ namespace basisu
#if BASISU_DEBUG_ETC_ENCODER
printf("** Block is a single solid color\n");
#endif
- uint64_t err = 16 * pack_etc1_block_solid_color(dst_block, &pSrc_pixels[0].r, pack_params, context);
+ uint64_t err = 16 * pack_etc1_block_solid_color(dst_block, &pSrc_pixels[0].r);
dst_block.set_flip_bit(true);
return err;
}
@@ -1741,7 +1744,7 @@ namespace basisu
break;
if (!r)
{
- pack_etc1_block_solid_color_constrained(results[2], 8, &subblock_pixel0.r, pack_params, context, !use_color4, (subblock && !use_color4) ? &results[0].m_block_color_unscaled : nullptr);
+ pack_etc1_block_solid_color_constrained(results[2], 8, &subblock_pixel0.r, !use_color4, (subblock && !use_color4) ? &results[0].m_block_color_unscaled : nullptr);
}
}
@@ -1961,5 +1964,6 @@ namespace basisu
return best_error;
}
-
+
+
} // namespace basisu
diff --git a/basisu_etc.h b/basisu_etc.h
index 398dcfa..da7b5f0 100644
--- a/basisu_etc.h
+++ b/basisu_etc.h
@@ -558,6 +558,21 @@ namespace basisu
uint64_t evaluate_etc1_error(const color_rgba* pBlock_pixels, bool perceptual, int subblock_index = -1) const;
void get_subblock_pixels(color_rgba* pPixels, int subblock_index = -1) const;
+ void get_selector_range(uint32_t& low, uint32_t& high) const
+ {
+ low = 3;
+ high = 0;
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ const uint32_t s = get_selector(x, y);
+ low = minimum(low, s);
+ high = maximum(high, s);
+ }
+ }
+ }
+
void set_block_color4(const color_rgba &c0_unscaled, const color_rgba &c1_unscaled)
{
set_diff_bit(false);
@@ -1034,6 +1049,6 @@ namespace basisu
bool pack_etc1_estimate_flipped(const color_rgba* pSrc_pixels);
- uint64_t pack_etc1_block_solid_color(etc_block& block, const uint8_t* pColor);
+ uint64_t pack_etc1_block_solid_color(etc_block& block, const uint8_t* pColor, bool diff_only = true);
} // namespace basisu
diff --git a/basisu_frontend.cpp b/basisu_frontend.cpp
index 168f58d..32d6822 100644
--- a/basisu_frontend.cpp
+++ b/basisu_frontend.cpp
@@ -22,28 +22,58 @@
#include
#include
-#define BASISU_FRONTEND_VERIFY(c) verify(c, __LINE__);
+#define BASISU_FRONTEND_VERIFY(c) do { if (!(c)) handle_verify_failure(__LINE__); } while(0)
namespace basisu
{
+ const uint32_t cMaxCodebookCreationThreads = 8;
+
const uint32_t BASISU_MAX_ENDPOINT_REFINEMENT_STEPS = 3;
const uint32_t BASISU_MAX_SELECTOR_REFINEMENT_STEPS = 3;
const uint32_t BASISU_ENDPOINT_PARENT_CODEBOOK_SIZE = 16;
const uint32_t BASISU_SELECTOR_PARENT_CODEBOOK_SIZE = 16;
-
+
// TODO - How to handle internal verifies in the basisu lib
- static inline void verify(bool condition, int line)
+ static inline void handle_verify_failure(int line)
{
- if (!condition)
- {
fprintf(stderr, "ERROR: basisu_frontend: verify check failed at line %i!\n", line);
abort();
- }
}
bool basisu_frontend::init(const params &p)
{
+#if 0
+ // HACK HACK
+ FILE* pFile;
+ fopen_s(&pFile, "tv.bin", "rb");
+ if (pFile)
+ {
+ debug_printf("Using tv.bin\n");
+
+ fseek(pFile, 0, SEEK_END);
+ uint32_t size = ftell(pFile);
+ fseek(pFile, 0, SEEK_SET);
+
+ uint32_t tv = size / sizeof(vec6F_quantizer::training_vec_with_weight);
+
+ std::vector v(tv);
+ fread(&v[0], 1, sizeof(v[0]) * tv, pFile);
+
+ for (uint32_t i = 0; i < tv; i++)
+ m_endpoint_clusterizer.add_training_vec(v[i].first, v[i].second);
+
+ m_endpoint_clusterizer.generate(16128);
+ std::vector codebook;
+ m_endpoint_clusterizer.retrieve(codebook);
+
+ printf("Generated %u entries\n", (uint32_t)codebook.size());
+
+ fclose(pFile);
+ exit(0);
+ }
+#endif
+
if (p.m_use_hybrid_selector_codebooks)
{
if (!p.m_pGlobal_sel_codebook)
@@ -53,8 +83,8 @@ namespace basisu
}
}
- debug_printf("basisu_frontend::init: NumEndpointClusters: %u, NumSelectorClusters: %u, Perceptual: %u, CompressionLevel: %u\n",
- p.m_max_endpoint_clusters, p.m_max_selector_clusters, p.m_perceptual, p.m_compression_level);
+ debug_printf("basisu_frontend::init: Multithreaded: %u, NumEndpointClusters: %u, NumSelectorClusters: %u, Perceptual: %u, CompressionLevel: %u\n",
+ p.m_multithreaded, p.m_max_endpoint_clusters, p.m_max_selector_clusters, p.m_perceptual, p.m_compression_level);
debug_printf("Global sel codebook pal bits: %u, Global sel codebook mod bits: %u, Use hybrid selector codebook: %u, Hybrid codebook quality thresh: %f\n",
p.m_num_global_sel_codebook_pal_bits,
@@ -92,6 +122,7 @@ namespace basisu
m_endpoint_refinement = true;
m_use_hierarchical_endpoint_codebooks = true;
m_use_hierarchical_selector_codebooks = true;
+
break;
}
case 2:
@@ -131,6 +162,9 @@ namespace basisu
}
+ if (m_params.m_disable_hierarchical_endpoint_codebooks)
+ m_use_hierarchical_endpoint_codebooks = false;
+
debug_printf("Endpoint refinement: %u, Hierarchical endpoint codebooks: %u, Hierarchical selector codebooks: %u, Endpoint codebook iters: %u, Selector codebook iters: %u\n",
m_endpoint_refinement, m_use_hierarchical_endpoint_codebooks, m_use_hierarchical_selector_codebooks, m_num_endpoint_codebook_iterations, m_num_selector_codebook_iterations);
@@ -164,12 +198,8 @@ namespace basisu
if ((m_params.m_debug_images) && (m_params.m_dump_endpoint_clusterization))
{
char buf[256];
-#ifdef _WIN32
- sprintf_s(buf, sizeof(buf), "endpoint_cluster_vis_pre_%u.png", refine_endpoint_step);
-#else
snprintf(buf, sizeof(buf), "endpoint_cluster_vis_pre_%u.png", refine_endpoint_step);
-#endif
- dump_endpoint_clusterization_visualization(buf);
+ dump_endpoint_clusterization_visualization(buf, false);
}
bool early_out = false;
@@ -181,18 +211,24 @@ namespace basisu
if (!refine_endpoint_clusterization())
early_out = true;
+ if ((m_params.m_tex_type == basist::cBASISTexTypeVideoFrames) && (!refine_endpoint_step) && (m_num_endpoint_codebook_iterations == 1))
+ {
+ eliminate_redundant_or_empty_endpoint_clusters();
+ generate_endpoint_codebook(refine_endpoint_step);
+ }
+
if ((m_params.m_debug_images) && (m_params.m_dump_endpoint_clusterization))
{
char buf[256];
-#ifdef _WIN32
- sprintf_s(buf, sizeof(buf), "endpoint_cluster_vis_post_%u.png", refine_endpoint_step);
-#else
snprintf(buf, sizeof(buf), "endpoint_cluster_vis_post_%u.png", refine_endpoint_step);
-#endif
- dump_endpoint_clusterization_visualization(buf);
+
+ dump_endpoint_clusterization_visualization(buf, false);
+ snprintf(buf, sizeof(buf), "endpoint_cluster_colors_vis_post_%u.png", refine_endpoint_step);
+
+ dump_endpoint_clusterization_visualization(buf, true);
}
}
-
+
eliminate_redundant_or_empty_endpoint_clusters();
if (m_params.m_debug_stats)
@@ -208,7 +244,7 @@ namespace basisu
create_initial_packed_texture();
- create_selector_clusters();
+ generate_selector_clusters();
if (m_use_hierarchical_selector_codebooks)
compute_selector_clusters_within_each_parent_cluster();
@@ -218,6 +254,8 @@ namespace basisu
create_optimized_selector_codebook(0);
find_optimal_selector_clusters_for_each_block();
+
+ introduce_special_selector_clusters();
}
else
{
@@ -228,7 +266,9 @@ namespace basisu
find_optimal_selector_clusters_for_each_block();
- if (m_params.m_compression_level >= 3)
+ introduce_special_selector_clusters();
+
+ if ((m_params.m_compression_level >= 3) || (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames))
{
if (!refine_block_endpoints_given_selectors())
break;
@@ -254,6 +294,110 @@ namespace basisu
return true;
}
+ void basisu_frontend::introduce_special_selector_clusters()
+ {
+ debug_printf("introduce_special_selector_clusters\n");
+
+ if (m_params.m_pGlobal_sel_codebook)
+ return;
+
+ uint32_t total_blocks_relocated = 0;
+ const uint32_t initial_selector_clusters = (uint32_t)m_selector_cluster_indices.size();
+
+ bool_vec block_relocated_flags(m_total_blocks);
+
+ // Make sure the selector codebook always has pure flat blocks for each possible selector, to avoid obvious artifacts.
+ // optimize_selector_codebook() will clean up any redundant clusters we create here.
+ for (uint32_t sel = 0; sel < 4; sel++)
+ {
+ etc_block blk;
+ clear_obj(blk);
+ for (uint32_t j = 0; j < 16; j++)
+ blk.set_selector(j & 3, j >> 2, sel);
+
+ int k;
+ for (k = 0; k < (int)m_optimized_cluster_selectors.size(); k++)
+ if (m_optimized_cluster_selectors[k].get_raw_selector_bits() == blk.get_raw_selector_bits())
+ break;
+ if (k < (int)m_optimized_cluster_selectors.size())
+ continue;
+
+ debug_printf("Introducing sel %u\n", sel);
+
+ const uint32_t new_selector_cluster_index = (uint32_t)m_optimized_cluster_selectors.size();
+
+ m_optimized_cluster_selectors.push_back(blk);
+
+ vector_ensure_element_is_valid(m_selector_cluster_indices, new_selector_cluster_index);
+
+ for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+ {
+ if (m_orig_encoded_blocks[block_index].get_raw_selector_bits() != blk.get_raw_selector_bits())
+ continue;
+
+ // See if using flat selectors actually decreases the block's error.
+ const uint32_t old_selector_cluster_index = m_block_selector_cluster_index[block_index];
+
+ etc_block cur_blk;
+ const uint32_t endpoint_cluster_index = get_subblock_endpoint_cluster_index(block_index, 0);
+ cur_blk.set_block_color5_etc1s(get_endpoint_cluster_unscaled_color(endpoint_cluster_index, false));
+ cur_blk.set_inten_tables_etc1s(get_endpoint_cluster_inten_table(endpoint_cluster_index, false));
+ cur_blk.set_raw_selector_bits(get_selector_cluster_selector_bits(old_selector_cluster_index).get_raw_selector_bits());
+
+ const uint64_t cur_err = cur_blk.evaluate_etc1_error(get_source_pixel_block(block_index).get_ptr(), m_params.m_perceptual);
+
+ cur_blk.set_raw_selector_bits(blk.get_raw_selector_bits());
+
+ const uint64_t new_err = cur_blk.evaluate_etc1_error(get_source_pixel_block(block_index).get_ptr(), m_params.m_perceptual);
+
+ if (new_err >= cur_err)
+ continue;
+
+ // Change the block to use the new cluster
+ m_block_selector_cluster_index[block_index] = new_selector_cluster_index;
+
+ m_selector_cluster_indices[new_selector_cluster_index].push_back(block_index);
+
+ block_relocated_flags[block_index] = true;
+
+#if 0
+ int j = vector_find(m_selector_cluster_indices[old_selector_cluster_index], block_index);
+ if (j >= 0)
+ m_selector_cluster_indices[old_selector_cluster_index].erase(m_selector_cluster_indices[old_selector_cluster_index].begin() + j);
+#endif
+
+ total_blocks_relocated++;
+
+ m_encoded_blocks[block_index].set_raw_selector_bits(blk.get_raw_selector_bits());
+
+ } // block_index
+
+ } // sel
+
+ if (total_blocks_relocated)
+ {
+ debug_printf("Fixing selector codebook\n");
+
+ for (int selector_cluster_index = 0; selector_cluster_index < (int)initial_selector_clusters; selector_cluster_index++)
+ {
+ uint_vec& block_indices = m_selector_cluster_indices[selector_cluster_index];
+
+ uint32_t dst_ofs = 0;
+
+ for (uint32_t i = 0; i < block_indices.size(); i++)
+ {
+ const uint32_t block_index = block_indices[i];
+ if (!block_relocated_flags[block_index])
+ block_indices[dst_ofs++] = block_index;
+ }
+
+ block_indices.resize(dst_ofs);
+ }
+ }
+
+ debug_printf("Total blocks relocated to new flat selector clusters: %u\n", total_blocks_relocated);
+ }
+
void basisu_frontend::optimize_selector_codebook()
{
debug_printf("optimize_selector_codebook\n");
@@ -331,80 +475,118 @@ namespace basisu
m_etc1_blocks_etc1s.resize(m_total_blocks);
-#pragma omp parallel for
- for (int block_index = 0; block_index < static_cast(m_total_blocks); block_index++)
+ const uint32_t N = 4096;
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
{
- const pixel_block &source_blk = get_source_pixel_block(block_index);
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum(m_total_blocks, first_index + N);
+
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index] {
- etc1_optimizer optimizer;
- etc1_optimizer::params optimizer_params;
- etc1_optimizer::results optimizer_results;
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const pixel_block &source_blk = get_source_pixel_block(block_index);
+
+ etc1_optimizer optimizer;
+ etc1_optimizer::params optimizer_params;
+ etc1_optimizer::results optimizer_results;
- if (m_params.m_compression_level == 0)
- optimizer_params.m_quality = cETCQualityFast;
- else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
- optimizer_params.m_quality = cETCQualityUber;
+ if (m_params.m_compression_level == 0)
+ optimizer_params.m_quality = cETCQualityFast;
+ else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
+ optimizer_params.m_quality = cETCQualityUber;
- optimizer_params.m_num_src_pixels = 16;
- optimizer_params.m_pSrc_pixels = source_blk.get_ptr();
- optimizer_params.m_perceptual = m_params.m_perceptual;
+ optimizer_params.m_num_src_pixels = 16;
+ optimizer_params.m_pSrc_pixels = source_blk.get_ptr();
+ optimizer_params.m_perceptual = m_params.m_perceptual;
- uint8_t selectors[16];
- optimizer_results.m_pSelectors = selectors;
- optimizer_results.m_n = 16;
+ uint8_t selectors[16];
+ optimizer_results.m_pSelectors = selectors;
+ optimizer_results.m_n = 16;
- optimizer.init(optimizer_params, optimizer_results);
- optimizer.compute();
+ optimizer.init(optimizer_params, optimizer_results);
+ optimizer.compute();
- etc_block &blk = m_etc1_blocks_etc1s[block_index];
+ etc_block &blk = m_etc1_blocks_etc1s[block_index];
- memset(&blk, 0, sizeof(blk));
- blk.set_block_color5_etc1s(optimizer_results.m_block_color_unscaled);
- blk.set_inten_tables_etc1s(optimizer_results.m_block_inten_table);
- blk.set_flip_bit(true);
+ memset(&blk, 0, sizeof(blk));
+ blk.set_block_color5_etc1s(optimizer_results.m_block_color_unscaled);
+ blk.set_inten_tables_etc1s(optimizer_results.m_block_inten_table);
+ blk.set_flip_bit(true);
- for (uint32_t y = 0; y < 4; y++)
- for (uint32_t x = 0; x < 4; x++)
- blk.set_selector(x, y, selectors[x + y * 4]);
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ blk.set_selector(x, y, selectors[x + y * 4]);
+ }
+
+ } );
}
+
+ m_params.m_pJob_pool->wait_for_all();
}
void basisu_frontend::init_endpoint_training_vectors()
{
debug_printf("init_endpoint_training_vectors\n");
- for (int block_index = 0; block_index < (int)m_total_blocks; block_index++)
+ vec6F_quantizer::array_of_weighted_training_vecs &training_vecs = m_endpoint_clusterizer.get_training_vecs();
+
+ training_vecs.resize(m_total_blocks * 2);
+
+ const uint32_t N = 16384;
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
{
- const etc_block &blk = m_etc1_blocks_etc1s[block_index];
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum(m_total_blocks, first_index + N);
- color_rgba block_colors[2];
- blk.get_block_low_high_colors(block_colors, 0);
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index, &training_vecs] {
- vec6F v;
- v[0] = block_colors[0].r * (1.0f / 255.0f);
- v[1] = block_colors[0].g * (1.0f / 255.0f);
- v[2] = block_colors[0].b * (1.0f / 255.0f);
- v[3] = block_colors[1].r * (1.0f / 255.0f);
- v[4] = block_colors[1].g * (1.0f / 255.0f);
- v[5] = block_colors[1].b * (1.0f / 255.0f);
-
- m_endpoint_clusterizer.add_training_vec(v, 1);
- m_endpoint_clusterizer.add_training_vec(v, 1);
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const etc_block &blk = m_etc1_blocks_etc1s[block_index];
- } // block_index
+ color_rgba block_colors[2];
+ blk.get_block_low_high_colors(block_colors, 0);
+
+ vec6F v;
+ v[0] = block_colors[0].r * (1.0f / 255.0f);
+ v[1] = block_colors[0].g * (1.0f / 255.0f);
+ v[2] = block_colors[0].b * (1.0f / 255.0f);
+ v[3] = block_colors[1].r * (1.0f / 255.0f);
+ v[4] = block_colors[1].g * (1.0f / 255.0f);
+ v[5] = block_colors[1].b * (1.0f / 255.0f);
+
+ training_vecs[block_index * 2 + 0] = std::make_pair(v, 1);
+ training_vecs[block_index * 2 + 1] = std::make_pair(v, 1);
+
+ } // block_index;
+
+ } );
+
+ } // block_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
}
void basisu_frontend::generate_endpoint_clusters()
{
debug_printf("Begin endpoint quantization\n");
- m_endpoint_clusterizer.generate(m_params.m_max_endpoint_clusters);
+ const uint32_t parent_codebook_size = (m_params.m_max_endpoint_clusters >= 256) ? BASISU_ENDPOINT_PARENT_CODEBOOK_SIZE : 0;
+ uint32_t max_threads = 0;
+ max_threads = m_params.m_multithreaded ? minimum(std::thread::hardware_concurrency(), cMaxCodebookCreationThreads) : 0;
- m_endpoint_clusterizer.retrieve(m_endpoint_clusters);
+ debug_printf("Using %u threads to create codebook\n", max_threads);
+ bool status = generate_hierarchical_codebook_threaded(m_endpoint_clusterizer,
+ m_params.m_max_endpoint_clusters, m_use_hierarchical_endpoint_codebooks ? parent_codebook_size : 0,
+ m_endpoint_clusters,
+ m_endpoint_parent_clusters,
+ max_threads, m_params.m_pJob_pool);
+ BASISU_FRONTEND_VERIFY(status);
if (m_use_hierarchical_endpoint_codebooks)
{
- if (m_endpoint_clusters.size() < BASISU_ENDPOINT_PARENT_CODEBOOK_SIZE)
+ if (!m_endpoint_parent_clusters.size())
{
m_endpoint_parent_clusters.resize(0);
m_endpoint_parent_clusters.resize(1);
@@ -414,13 +596,12 @@ namespace basisu
m_endpoint_parent_clusters[0].push_back(i*2+1);
}
}
- else
- m_endpoint_clusterizer.retrieve(BASISU_ENDPOINT_PARENT_CODEBOOK_SIZE, m_endpoint_parent_clusters);
BASISU_ASSUME(BASISU_ENDPOINT_PARENT_CODEBOOK_SIZE <= UINT8_MAX);
m_block_parent_endpoint_cluster.resize(0);
m_block_parent_endpoint_cluster.resize(m_total_blocks);
+ vector_set_all(m_block_parent_endpoint_cluster, 0xFF);
for (uint32_t parent_cluster_index = 0; parent_cluster_index < m_endpoint_parent_clusters.size(); parent_cluster_index++)
{
const uint_vec &cluster = m_endpoint_parent_clusters[parent_cluster_index];
@@ -431,6 +612,11 @@ namespace basisu
}
}
+ for (uint32_t i = 0; i < m_total_blocks; i++)
+ {
+ BASISU_FRONTEND_VERIFY(m_block_parent_endpoint_cluster[i] != 0xFF);
+ }
+
// Ensure that all the blocks within each cluster are all in the same parent cluster, or something is very wrong.
for (uint32_t cluster_index = 0; cluster_index < m_endpoint_clusters.size(); cluster_index++)
{
@@ -514,71 +700,84 @@ namespace basisu
{
m_subblock_endpoint_quant_err_vec.resize(0);
-#pragma omp parallel for
- for (int cluster_index = 0; cluster_index < static_cast(m_endpoint_clusters.size()); cluster_index++)
+ const uint32_t N = 512;
+ for (uint32_t cluster_index_iter = 0; cluster_index_iter < m_endpoint_clusters.size(); cluster_index_iter += N)
{
- const std::vector& cluster_indices = m_endpoint_clusters[cluster_index];
+ const uint32_t first_index = cluster_index_iter;
+ const uint32_t last_index = minimum((uint32_t)m_endpoint_clusters.size(), cluster_index_iter + N);
- assert(cluster_indices.size());
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index] {
- for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
- {
- std::vector cluster_pixels(8);
-
- const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
- const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
-
- const bool flipped = true;
-
- const color_rgba *pSource_block_pixels = get_source_pixel_block(block_index).get_ptr();
-
- for (uint32_t pixel_index = 0; pixel_index < 8; pixel_index++)
+ for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
- cluster_pixels[pixel_index] = pSource_block_pixels[g_etc1_pixel_indices[flipped][subblock_index][pixel_index]];
- }
+ const std::vector& cluster_indices = m_endpoint_clusters[cluster_index];
- const endpoint_cluster_etc_params &etc_params = m_endpoint_cluster_etc_params[cluster_index];
+ assert(cluster_indices.size());
- assert(etc_params.m_valid);
-
- color_rgba block_colors[4];
- etc_block::get_block_colors5(block_colors, etc_params.m_color_unscaled[0], etc_params.m_inten_table[0], true);
-
- uint64_t total_err = 0;
-
- for (uint32_t i = 0; i < 8; i++)
- {
- const color_rgba &c = cluster_pixels[i];
-
- uint64_t best_err = UINT64_MAX;
- uint32_t best_index = 0;
-
- for (uint32_t s = 0; s < 4; s++)
+ for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
{
- uint64_t err = color_distance(m_params.m_perceptual, c, block_colors[s], false);
- if (err < best_err)
+ std::vector cluster_pixels(8);
+
+ const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
+ const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
+
+ const bool flipped = true;
+
+ const color_rgba *pSource_block_pixels = get_source_pixel_block(block_index).get_ptr();
+
+ for (uint32_t pixel_index = 0; pixel_index < 8; pixel_index++)
{
- best_err = err;
- best_index = s;
+ cluster_pixels[pixel_index] = pSource_block_pixels[g_etc1_pixel_indices[flipped][subblock_index][pixel_index]];
+ }
+
+ const endpoint_cluster_etc_params &etc_params = m_endpoint_cluster_etc_params[cluster_index];
+
+ assert(etc_params.m_valid);
+
+ color_rgba block_colors[4];
+ etc_block::get_block_colors5(block_colors, etc_params.m_color_unscaled[0], etc_params.m_inten_table[0], true);
+
+ uint64_t total_err = 0;
+
+ for (uint32_t i = 0; i < 8; i++)
+ {
+ const color_rgba &c = cluster_pixels[i];
+
+ uint64_t best_err = UINT64_MAX;
+ //uint32_t best_index = 0;
+
+ for (uint32_t s = 0; s < 4; s++)
+ {
+ uint64_t err = color_distance(m_params.m_perceptual, c, block_colors[s], false);
+ if (err < best_err)
+ {
+ best_err = err;
+ //best_index = s;
+ }
+ }
+
+ total_err += best_err;
+ }
+
+ subblock_endpoint_quant_err quant_err;
+ quant_err.m_total_err = total_err;
+ quant_err.m_cluster_index = cluster_index;
+ quant_err.m_cluster_subblock_index = cluster_indices_iter;
+ quant_err.m_block_index = block_index;
+ quant_err.m_subblock_index = subblock_index;
+
+ {
+ std::lock_guard lock(m_lock);
+
+ m_subblock_endpoint_quant_err_vec.push_back(quant_err);
}
}
+ } // cluster_index
- total_err += best_err;
- }
+ } );
+ } // cluster_index_iter
- subblock_endpoint_quant_err quant_err;
- quant_err.m_total_err = total_err;
- quant_err.m_cluster_index = cluster_index;
- quant_err.m_cluster_subblock_index = cluster_indices_iter;
- quant_err.m_block_index = block_index;
- quant_err.m_subblock_index = subblock_index;
-
-#pragma omp critical
- {
- m_subblock_endpoint_quant_err_vec.push_back(quant_err);
- }
- }
- }
+ m_params.m_pJob_pool->wait_for_all();
vector_sort(m_subblock_endpoint_quant_err_vec);
}
@@ -628,6 +827,15 @@ namespace basisu
if (unordered_set_contains(training_vector_was_relocated, training_vector_index ^ 1))
continue;
+#if 0
+ const uint32_t block_index = subblock_to_move.m_block_index;
+ const etc_block& blk = m_etc1_blocks_etc1s[block_index];
+ uint32_t ls, hs;
+ blk.get_selector_range(ls, hs);
+ if (ls != hs)
+ continue;
+#endif
+
const uint32_t new_endpoint_cluster_index = (uint32_t)m_endpoint_clusters.size();
enlarge_vector(m_endpoint_clusters, 1)->push_back(training_vector_index);
@@ -679,123 +887,135 @@ namespace basisu
m_endpoint_cluster_etc_params.resize(m_endpoint_clusters.size());
-#pragma omp parallel for
- for (int cluster_index = 0; cluster_index < static_cast(m_endpoint_clusters.size()); cluster_index++)
+ const uint32_t N = 128;
+ for (uint32_t cluster_index_iter = 0; cluster_index_iter < m_endpoint_clusters.size(); cluster_index_iter += N)
{
- //debug_printf("%u of %u\n", cluster_index, clusters.size());
+ const uint32_t first_index = cluster_index_iter;
+ const uint32_t last_index = minimum((uint32_t)m_endpoint_clusters.size(), cluster_index_iter + N);
+
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index, step ] {
- const std::vector& cluster_indices = m_endpoint_clusters[cluster_index];
-
- BASISU_FRONTEND_VERIFY(cluster_indices.size());
-
- const uint32_t total_pixels = (uint32_t)cluster_indices.size() * 8;
-
- std::vector cluster_pixels(total_pixels);
-
- for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
- {
- const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
- const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
-
- const bool flipped = true;
-
- const color_rgba *pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
-
- for (uint32_t pixel_index = 0; pixel_index < 8; pixel_index++)
+ for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
- cluster_pixels[cluster_indices_iter * 8 + pixel_index] = pBlock_pixels[g_etc1_pixel_indices[flipped][subblock_index][pixel_index]];
- }
- }
+ const std::vector& cluster_indices = m_endpoint_clusters[cluster_index];
- endpoint_cluster_etc_params new_subblock_params;
-
- {
- etc1_optimizer optimizer;
- etc1_solution_coordinates solutions[2];
+ BASISU_FRONTEND_VERIFY(cluster_indices.size());
- etc1_optimizer::params cluster_optimizer_params;
- cluster_optimizer_params.m_num_src_pixels = total_pixels;
- cluster_optimizer_params.m_pSrc_pixels = &cluster_pixels[0];
+ const uint32_t total_pixels = (uint32_t)cluster_indices.size() * 8;
- cluster_optimizer_params.m_use_color4 = false;
- cluster_optimizer_params.m_perceptual = m_params.m_perceptual;
+ std::vector cluster_pixels(total_pixels);
- if (m_params.m_compression_level == 0)
- cluster_optimizer_params.m_quality = cETCQualityMedium;
- else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
- cluster_optimizer_params.m_quality = cETCQualityUber;
-
- etc1_optimizer::results cluster_optimizer_results;
-
- std::vector cluster_selectors(total_pixels);
- cluster_optimizer_results.m_n = total_pixels;
- cluster_optimizer_results.m_pSelectors = &cluster_selectors[0];
-
- optimizer.init(cluster_optimizer_params, cluster_optimizer_results);
-
- optimizer.compute();
-
- new_subblock_params.m_color_unscaled[0] = cluster_optimizer_results.m_block_color_unscaled;
- new_subblock_params.m_inten_table[0] = cluster_optimizer_results.m_block_inten_table;
- new_subblock_params.m_color_error[0] = cluster_optimizer_results.m_error;
- }
-
- endpoint_cluster_etc_params &prev_etc_params = m_endpoint_cluster_etc_params[cluster_index];
-
- bool use_new_subblock_params = false;
- if ((!step) || (!prev_etc_params.m_valid))
- use_new_subblock_params = true;
- else
- {
- assert(prev_etc_params.m_valid);
-
- uint64_t total_prev_err = 0;
-
- {
- color_rgba block_colors[4];
-
- etc_block::get_block_colors5(block_colors, prev_etc_params.m_color_unscaled[0], prev_etc_params.m_inten_table[0], false);
-
- uint64_t total_err = 0;
-
- for (uint32_t i = 0; i < total_pixels; i++)
+ for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
{
- const color_rgba &c = cluster_pixels[i];
+ const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
+ const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
- uint64_t best_err = UINT64_MAX;
- uint32_t best_index = 0;
+ const bool flipped = true;
- for (uint32_t s = 0; s < 4; s++)
+ const color_rgba *pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
+
+ for (uint32_t pixel_index = 0; pixel_index < 8; pixel_index++)
{
- uint64_t err = color_distance(m_params.m_perceptual, c, block_colors[s], false);
- if (err < best_err)
- {
- best_err = err;
- best_index = s;
- }
+ const color_rgba &c = pBlock_pixels[g_etc1_pixel_indices[flipped][subblock_index][pixel_index]];
+ cluster_pixels[cluster_indices_iter * 8 + pixel_index] = c;
}
-
- total_err += best_err;
}
- total_prev_err += total_err;
- }
+ endpoint_cluster_etc_params new_subblock_params;
+
+ {
+ etc1_optimizer optimizer;
+ etc1_solution_coordinates solutions[2];
- // See if we should update this cluster's endpoints (if the error has actually fallen)
- if (total_prev_err > (new_subblock_params.m_color_error[0] + new_subblock_params.m_color_error[1]))
- {
- use_new_subblock_params = true;
- }
- }
+ etc1_optimizer::params cluster_optimizer_params;
+ cluster_optimizer_params.m_num_src_pixels = total_pixels;
+ cluster_optimizer_params.m_pSrc_pixels = &cluster_pixels[0];
- if (use_new_subblock_params)
- {
- new_subblock_params.m_valid = true;
+ cluster_optimizer_params.m_use_color4 = false;
+ cluster_optimizer_params.m_perceptual = m_params.m_perceptual;
- prev_etc_params = new_subblock_params;
- }
+ if (m_params.m_compression_level == 0)
+ cluster_optimizer_params.m_quality = cETCQualityMedium;
+ else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
+ cluster_optimizer_params.m_quality = cETCQualityUber;
- } // cluster_index
+ etc1_optimizer::results cluster_optimizer_results;
+
+ std::vector cluster_selectors(total_pixels);
+ cluster_optimizer_results.m_n = total_pixels;
+ cluster_optimizer_results.m_pSelectors = &cluster_selectors[0];
+
+ optimizer.init(cluster_optimizer_params, cluster_optimizer_results);
+
+ optimizer.compute();
+
+ new_subblock_params.m_color_unscaled[0] = cluster_optimizer_results.m_block_color_unscaled;
+ new_subblock_params.m_inten_table[0] = cluster_optimizer_results.m_block_inten_table;
+ new_subblock_params.m_color_error[0] = cluster_optimizer_results.m_error;
+ }
+
+ endpoint_cluster_etc_params &prev_etc_params = m_endpoint_cluster_etc_params[cluster_index];
+
+ bool use_new_subblock_params = false;
+ if ((!step) || (!prev_etc_params.m_valid))
+ use_new_subblock_params = true;
+ else
+ {
+ assert(prev_etc_params.m_valid);
+
+ uint64_t total_prev_err = 0;
+
+ {
+ color_rgba block_colors[4];
+
+ etc_block::get_block_colors5(block_colors, prev_etc_params.m_color_unscaled[0], prev_etc_params.m_inten_table[0], false);
+
+ uint64_t total_err = 0;
+
+ for (uint32_t i = 0; i < total_pixels; i++)
+ {
+ const color_rgba &c = cluster_pixels[i];
+
+ uint64_t best_err = UINT64_MAX;
+ //uint32_t best_index = 0;
+
+ for (uint32_t s = 0; s < 4; s++)
+ {
+ uint64_t err = color_distance(m_params.m_perceptual, c, block_colors[s], false);
+ if (err < best_err)
+ {
+ best_err = err;
+ //best_index = s;
+ }
+ }
+
+ total_err += best_err;
+ }
+
+ total_prev_err += total_err;
+ }
+
+ // See if we should update this cluster's endpoints (if the error has actually fallen)
+ if (total_prev_err > new_subblock_params.m_color_error[0])
+ {
+ use_new_subblock_params = true;
+ }
+ }
+
+ if (use_new_subblock_params)
+ {
+ new_subblock_params.m_valid = true;
+
+ prev_etc_params = new_subblock_params;
+ }
+
+ } // cluster_index
+
+ } );
+
+ } // cluster_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
}
bool basisu_frontend::check_etc1s_constraints() const
@@ -854,78 +1074,91 @@ namespace basisu
uint_vec best_cluster_indices(m_total_blocks);
-#pragma omp parallel for
- for (int block_index = 0; block_index < (int)m_total_blocks; block_index++)
+ const uint32_t N = 1024;
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
{
- const bool is_flipped = true;
-
- const uint32_t cluster_index = block_clusters[block_index][0];
- BASISU_FRONTEND_VERIFY(cluster_index == block_clusters[block_index][1]);
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum(m_total_blocks, first_index + N);
- const color_rgba *subblock_pixels = get_source_pixel_block(block_index).get_ptr();
- const uint32_t num_subblock_pixels = 16;
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index, &best_cluster_indices, &block_clusters] {
- uint64_t best_cluster_err = UINT64_MAX;
- uint32_t best_cluster_index = 0;
-
- const uint32_t block_parent_endpoint_cluster_index = m_block_parent_endpoint_cluster.size() ? m_block_parent_endpoint_cluster[block_index] : 0;
- const uint_vec *pCluster_indices = m_endpoint_clusters_within_each_parent_cluster.size() ? &m_endpoint_clusters_within_each_parent_cluster[block_parent_endpoint_cluster_index] : nullptr;
-
- const uint32_t total_clusters = m_use_hierarchical_endpoint_codebooks ? (uint32_t)pCluster_indices->size() : (uint32_t)m_endpoint_clusters.size();
-
- for (uint32_t i = 0; i < total_clusters; i++)
- {
- const uint32_t cluster_iter = m_use_hierarchical_endpoint_codebooks ? (*pCluster_indices)[i] : i;
-
- color_rgba cluster_etc_base_color(m_endpoint_cluster_etc_params[cluster_iter].m_color_unscaled[0]);
- uint32_t cluster_etc_inten = m_endpoint_cluster_etc_params[cluster_iter].m_inten_table[0];
-
- uint64_t total_err = 0;
-
- const uint32_t low_selector = 0;//subblock_etc_params_vec[j].m_low_selectors[0];
- const uint32_t high_selector = 3;//subblock_etc_params_vec[j].m_high_selectors[0];
- color_rgba subblock_colors[4];
- // Can't assign it here - may result in too much error when selector quant occurs
- if (cluster_etc_inten > m_endpoint_cluster_etc_params[cluster_index].m_inten_table[0])
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
- total_err = UINT64_MAX;
- goto skip_cluster;
- }
+ const bool is_flipped = true;
+
+ const uint32_t cluster_index = block_clusters[block_index][0];
+ BASISU_FRONTEND_VERIFY(cluster_index == block_clusters[block_index][1]);
- etc_block::get_block_colors5(subblock_colors, cluster_etc_base_color, cluster_etc_inten);
+ const color_rgba *subblock_pixels = get_source_pixel_block(block_index).get_ptr();
+ const uint32_t num_subblock_pixels = 16;
- for (uint32_t p = 0; p < num_subblock_pixels; p++)
- {
- uint64_t best_err = UINT64_MAX;
+ uint64_t best_cluster_err = UINT64_MAX;
+ uint32_t best_cluster_index = 0;
- for (uint32_t r = low_selector; r <= high_selector; r++)
+ const uint32_t block_parent_endpoint_cluster_index = m_block_parent_endpoint_cluster.size() ? m_block_parent_endpoint_cluster[block_index] : 0;
+ const uint_vec *pCluster_indices = m_endpoint_clusters_within_each_parent_cluster.size() ? &m_endpoint_clusters_within_each_parent_cluster[block_parent_endpoint_cluster_index] : nullptr;
+
+ const uint32_t total_clusters = m_use_hierarchical_endpoint_codebooks ? (uint32_t)pCluster_indices->size() : (uint32_t)m_endpoint_clusters.size();
+
+ for (uint32_t i = 0; i < total_clusters; i++)
{
- uint64_t err = color_distance(m_params.m_perceptual, subblock_pixels[p], subblock_colors[r], false);
- best_err = minimum(best_err, err);
- if (!best_err)
- break;
- }
+ const uint32_t cluster_iter = m_use_hierarchical_endpoint_codebooks ? (*pCluster_indices)[i] : i;
- total_err += best_err;
- if (total_err > best_cluster_err)
- break;
- } // p
+ color_rgba cluster_etc_base_color(m_endpoint_cluster_etc_params[cluster_iter].m_color_unscaled[0]);
+ uint32_t cluster_etc_inten = m_endpoint_cluster_etc_params[cluster_iter].m_inten_table[0];
- skip_cluster:
- if ((total_err < best_cluster_err) ||
- ((cluster_iter == cluster_index) && (total_err == best_cluster_err)))
- {
- best_cluster_err = total_err;
- best_cluster_index = cluster_iter;
+ uint64_t total_err = 0;
+
+ const uint32_t low_selector = 0;//subblock_etc_params_vec[j].m_low_selectors[0];
+ const uint32_t high_selector = 3;//subblock_etc_params_vec[j].m_high_selectors[0];
+ color_rgba subblock_colors[4];
+ // Can't assign it here - may result in too much error when selector quant occurs
+ if (cluster_etc_inten > m_endpoint_cluster_etc_params[cluster_index].m_inten_table[0])
+ {
+ total_err = UINT64_MAX;
+ goto skip_cluster;
+ }
+
+ etc_block::get_block_colors5(subblock_colors, cluster_etc_base_color, cluster_etc_inten);
+
+ for (uint32_t p = 0; p < num_subblock_pixels; p++)
+ {
+ uint64_t best_err = UINT64_MAX;
+
+ for (uint32_t r = low_selector; r <= high_selector; r++)
+ {
+ uint64_t err = color_distance(m_params.m_perceptual, subblock_pixels[p], subblock_colors[r], false);
+ best_err = minimum(best_err, err);
+ if (!best_err)
+ break;
+ }
+
+ total_err += best_err;
+ if (total_err > best_cluster_err)
+ break;
+ } // p
+
+ skip_cluster:
+ if ((total_err < best_cluster_err) ||
+ ((cluster_iter == cluster_index) && (total_err == best_cluster_err)))
+ {
+ best_cluster_err = total_err;
+ best_cluster_index = cluster_iter;
- if (!best_cluster_err)
- break;
- }
- } // j
+ if (!best_cluster_err)
+ break;
+ }
+ } // j
- best_cluster_indices[block_index] = best_cluster_index;
+ best_cluster_indices[block_index] = best_cluster_index;
- } // block_index
+ } // block_index
+
+ } );
+
+ } // block_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
std::vector > optimized_endpoint_clusters(m_endpoint_clusters.size());
uint32_t total_subblocks_reassigned = 0;
@@ -1024,29 +1257,42 @@ namespace basisu
{
debug_printf("create_initial_packed_texture\n");
-#pragma omp parallel for
- for (int block_index = 0; block_index < static_cast(m_total_blocks); block_index++)
+ const uint32_t N = 4096;
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
{
- uint32_t cluster0 = m_block_endpoint_clusters_indices[block_index][0];
- uint32_t cluster1 = m_block_endpoint_clusters_indices[block_index][1];
- BASISU_FRONTEND_VERIFY(cluster0 == cluster1);
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum(m_total_blocks, first_index + N);
- const color_rgba *pSource_pixels = get_source_pixel_block(block_index).get_ptr();
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index] {
+
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ uint32_t cluster0 = m_block_endpoint_clusters_indices[block_index][0];
+ uint32_t cluster1 = m_block_endpoint_clusters_indices[block_index][1];
+ BASISU_FRONTEND_VERIFY(cluster0 == cluster1);
- etc_block &blk = m_encoded_blocks[block_index];
+ const color_rgba *pSource_pixels = get_source_pixel_block(block_index).get_ptr();
- color_rgba unscaled[2] = { m_endpoint_cluster_etc_params[cluster0].m_color_unscaled[0], m_endpoint_cluster_etc_params[cluster1].m_color_unscaled[0] };
- uint32_t inten[2] = { m_endpoint_cluster_etc_params[cluster0].m_inten_table[0], m_endpoint_cluster_etc_params[cluster1].m_inten_table[0] };
+ etc_block &blk = m_encoded_blocks[block_index];
+
+ color_rgba unscaled[2] = { m_endpoint_cluster_etc_params[cluster0].m_color_unscaled[0], m_endpoint_cluster_etc_params[cluster1].m_color_unscaled[0] };
+ uint32_t inten[2] = { m_endpoint_cluster_etc_params[cluster0].m_inten_table[0], m_endpoint_cluster_etc_params[cluster1].m_inten_table[0] };
- blk.set_block_color5(unscaled[0], unscaled[1]);
- blk.set_flip_bit(true);
+ blk.set_block_color5(unscaled[0], unscaled[1]);
+ blk.set_flip_bit(true);
- blk.set_inten_table(0, inten[0]);
- blk.set_inten_table(1, inten[1]);
+ blk.set_inten_table(0, inten[0]);
+ blk.set_inten_table(1, inten[1]);
- blk.determine_selectors(pSource_pixels, m_params.m_perceptual);
+ blk.determine_selectors(pSource_pixels, m_params.m_perceptual);
- } // block_index
+ } // block_index
+
+ } );
+
+ } // block_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
m_orig_encoded_blocks = m_encoded_blocks;
}
@@ -1093,64 +1339,85 @@ namespace basisu
}
}
- void basisu_frontend::create_selector_clusters()
+ void basisu_frontend::generate_selector_clusters()
{
- debug_printf("create_selector_clusters\n");
+ debug_printf("generate_selector_clusters\n");
typedef vec<16, float> vec16F;
typedef tree_vector_quant vec16F_clusterizer;
vec16F_clusterizer::array_of_weighted_training_vecs training_vecs(m_total_blocks);
-#pragma omp parallel for
- for (int block_index = 0; block_index < static_cast(m_total_blocks); block_index++)
+ const uint32_t N = 4096;
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
{
- const etc_block &blk = m_encoded_blocks[block_index];
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum(m_total_blocks, first_index + N);
- vec16F v;
- for (uint32_t y = 0; y < 4; y++)
- for (uint32_t x = 0; x < 4; x++)
- v[x + y * 4] = static_cast(blk.get_selector(x, y));
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index, &training_vecs] {
- const uint32_t subblock_index = (blk.get_inten_table(0) > blk.get_inten_table(1)) ? 0 : 1;
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const etc_block &blk = m_encoded_blocks[block_index];
- color_rgba block_colors[2];
- blk.get_block_low_high_colors(block_colors, subblock_index);
+ vec16F v;
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ v[x + y * 4] = static_cast(blk.get_selector(x, y));
- const uint32_t dist = color_distance(m_params.m_perceptual, block_colors[0], block_colors[1], false);
+ const uint32_t subblock_index = (blk.get_inten_table(0) > blk.get_inten_table(1)) ? 0 : 1;
- const uint32_t cColorDistToWeight = 300;
- const uint32_t cMaxWeight = 4096;
- uint32_t weight = clamp(dist / cColorDistToWeight, 1, cMaxWeight);
+ color_rgba block_colors[2];
+ blk.get_block_low_high_colors(block_colors, subblock_index);
+
+ const uint32_t dist = color_distance(m_params.m_perceptual, block_colors[0], block_colors[1], false);
+
+ const uint32_t cColorDistToWeight = 300;
+ const uint32_t cMaxWeight = 4096;
+ uint32_t weight = clamp(dist / cColorDistToWeight, 1, cMaxWeight);
- training_vecs[block_index].first = v;
- training_vecs[block_index].second = weight;
- }
+ training_vecs[block_index].first = v;
+ training_vecs[block_index].second = weight;
+
+ } // block_index
+
+ } );
+
+ } // block_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
vec16F_clusterizer selector_clusterizer;
for (uint32_t i = 0; i < m_total_blocks; i++)
selector_clusterizer.add_training_vec(training_vecs[i].first, training_vecs[i].second);
- selector_clusterizer.generate(m_params.m_max_selector_clusters);
+ const uint32_t parent_codebook_size = (m_params.m_max_selector_clusters >= 256) ? BASISU_SELECTOR_PARENT_CODEBOOK_SIZE : 0;
- selector_clusterizer.retrieve(m_selector_cluster_indices);
+ uint32_t max_threads = 0;
+ max_threads = m_params.m_multithreaded ? minimum(std::thread::hardware_concurrency(), cMaxCodebookCreationThreads) : 0;
+
+ bool status = generate_hierarchical_codebook_threaded(selector_clusterizer,
+ m_params.m_max_selector_clusters, m_use_hierarchical_selector_codebooks ? parent_codebook_size : 0,
+ m_selector_cluster_indices,
+ m_selector_parent_cluster_indices,
+ max_threads, m_params.m_pJob_pool);
+ BASISU_FRONTEND_VERIFY(status);
if (m_use_hierarchical_selector_codebooks)
{
- if (m_selector_cluster_indices.size() < BASISU_SELECTOR_PARENT_CODEBOOK_SIZE)
+ if (!m_selector_parent_cluster_indices.size())
{
m_selector_parent_cluster_indices.resize(0);
m_selector_parent_cluster_indices.resize(1);
for (uint32_t i = 0; i < m_total_blocks; i++)
m_selector_parent_cluster_indices[0].push_back(i);
}
- else
- selector_clusterizer.retrieve(BASISU_SELECTOR_PARENT_CODEBOOK_SIZE, m_selector_parent_cluster_indices);
BASISU_ASSUME(BASISU_SELECTOR_PARENT_CODEBOOK_SIZE <= UINT8_MAX);
m_block_parent_selector_cluster.resize(0);
m_block_parent_selector_cluster.resize(m_total_blocks);
+ vector_set_all(m_block_parent_selector_cluster, 0xFF);
for (uint32_t parent_cluster_index = 0; parent_cluster_index < m_selector_parent_cluster_indices.size(); parent_cluster_index++)
{
@@ -1158,6 +1425,10 @@ namespace basisu
for (uint32_t j = 0; j < cluster.size(); j++)
m_block_parent_selector_cluster[cluster[j]] = static_cast(parent_cluster_index);
}
+ for (uint32_t i = 0; i < m_total_blocks; i++)
+ {
+ BASISU_FRONTEND_VERIFY(m_block_parent_selector_cluster[i] != 0xFF);
+ }
// Ensure that all the blocks within each cluster are all in the same parent cluster, or something is very wrong.
for (uint32_t cluster_index = 0; cluster_index < m_selector_cluster_indices.size(); cluster_index++)
@@ -1197,56 +1468,70 @@ namespace basisu
m_optimized_cluster_selector_global_cb_ids.resize(total_selector_clusters);
-#pragma omp parallel for
- for (int cluster_index = 0; cluster_index < static_cast(total_selector_clusters); cluster_index++)
+ const uint32_t N = 256;
+ for (uint32_t cluster_index_iter = 0; cluster_index_iter < total_selector_clusters; cluster_index_iter += N)
{
- const std::vector &cluster_block_indices = m_selector_cluster_indices[cluster_index];
+ const uint32_t first_index = cluster_index_iter;
+ const uint32_t last_index = minimum((uint32_t)total_selector_clusters, cluster_index_iter + N);
+
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index, &total_clusters_processed, &total_selector_clusters] {
+
+ for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
+ {
+ const std::vector &cluster_block_indices = m_selector_cluster_indices[cluster_index];
- if (!cluster_block_indices.size())
- continue;
+ if (!cluster_block_indices.size())
+ continue;
- etc_block_vec etc_blocks;
- pixel_block_vec pixel_blocks;
+ etc_block_vec etc_blocks;
+ pixel_block_vec pixel_blocks;
- for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
- {
- const uint32_t block_index = cluster_block_indices[cluster_block_index];
+ for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
+ {
+ const uint32_t block_index = cluster_block_indices[cluster_block_index];
- etc_blocks.push_back(m_encoded_blocks[block_index]);
+ etc_blocks.push_back(m_encoded_blocks[block_index]);
- pixel_blocks.push_back(get_source_pixel_block(block_index));
- }
+ pixel_blocks.push_back(get_source_pixel_block(block_index));
+ }
- uint32_t palette_index;
- basist::etc1_global_palette_entry_modifier palette_modifier;
+ uint32_t palette_index;
+ basist::etc1_global_palette_entry_modifier palette_modifier;
-#if 0
- m_params.m_pGlobal_sel_codebook->find_best_entry(etc_blocks.size(), pixel_blocks.get_ptr(), etc_blocks.get_ptr(),
- palette_index, palette_modifier,
- m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
-#else
- etc1_global_selector_codebook_find_best_entry(*m_params.m_pGlobal_sel_codebook,
- (uint32_t)etc_blocks.size(), &pixel_blocks[0], &etc_blocks[0],
- palette_index, palette_modifier,
- m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
-#endif
+ #if 0
+ m_params.m_pGlobal_sel_codebook->find_best_entry(etc_blocks.size(), pixel_blocks.get_ptr(), etc_blocks.get_ptr(),
+ palette_index, palette_modifier,
+ m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
+ #else
+ etc1_global_selector_codebook_find_best_entry(*m_params.m_pGlobal_sel_codebook,
+ (uint32_t)etc_blocks.size(), &pixel_blocks[0], &etc_blocks[0],
+ palette_index, palette_modifier,
+ m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
+ #endif
- m_optimized_cluster_selector_global_cb_ids[cluster_index].set(palette_index, palette_modifier);
+ m_optimized_cluster_selector_global_cb_ids[cluster_index].set(palette_index, palette_modifier);
- basist::etc1_selector_palette_entry pal_entry(m_params.m_pGlobal_sel_codebook->get_entry(palette_index, palette_modifier));
+ basist::etc1_selector_palette_entry pal_entry(m_params.m_pGlobal_sel_codebook->get_entry(palette_index, palette_modifier));
- for (uint32_t y = 0; y < 4; y++)
- for (uint32_t x = 0; x < 4; x++)
- m_optimized_cluster_selectors[cluster_index].set_selector(x, y, pal_entry(x, y));
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ m_optimized_cluster_selectors[cluster_index].set_selector(x, y, pal_entry(x, y));
-#pragma omp critical
- {
- total_clusters_processed++;
- if ((total_clusters_processed % 63) == 0)
- debug_printf("Global selector palette optimization: %3.1f%% complete\n", total_clusters_processed * 100.0f / total_selector_clusters);
- }
+ {
+ std::lock_guard lock(m_lock);
- } // cluster_index
+ total_clusters_processed++;
+ if ((total_clusters_processed % 63) == 0)
+ debug_printf("Global selector palette optimization: %3.1f%% complete\n", total_clusters_processed * 100.0f / total_selector_clusters);
+ }
+
+ } // cluster_index
+
+ } );
+
+ } // cluster_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
}
else
{
@@ -1261,119 +1546,131 @@ namespace basisu
// For each selector codebook entry, and for each of the 4x4 selectors, determine which selector minimizes the error across all the blocks that use that quantized selector.
-#pragma omp parallel for
- for (int cluster_index = 0; cluster_index < static_cast(total_selector_clusters); cluster_index++)
+ const uint32_t N = 256;
+ for (uint32_t cluster_index_iter = 0; cluster_index_iter < total_selector_clusters; cluster_index_iter += N)
{
- const std::vector &cluster_block_indices = m_selector_cluster_indices[cluster_index];
-
- if (!cluster_block_indices.size())
- continue;
-
- uint64_t overall_best_err = 0;
-
- for (uint32_t y = 0; y < 4; y++)
- {
- for (uint32_t x = 0; x < 4; x++)
+ const uint32_t first_index = cluster_index_iter;
+ const uint32_t last_index = minimum((uint32_t)total_selector_clusters, cluster_index_iter + N);
+
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index, &uses_hybrid_sel_codebook, &total_clusters_processed, &total_selector_clusters] {
+
+ for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
- uint64_t best_err = UINT64_MAX;
- uint32_t best_s = 0;
+ const std::vector &cluster_block_indices = m_selector_cluster_indices[cluster_index];
- for (uint32_t s = 0; s < 4; s++)
+ if (!cluster_block_indices.size())
+ continue;
+
+ uint64_t overall_best_err = 0;
+
+ for (uint32_t y = 0; y < 4; y++)
{
- uint32_t total_err = 0;
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ uint64_t best_err = UINT64_MAX;
+ uint32_t best_s = 0;
+
+ for (uint32_t s = 0; s < 4; s++)
+ {
+ uint32_t total_err = 0;
+
+ for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
+ {
+ const uint32_t block_index = cluster_block_indices[cluster_block_index];
+
+ const etc_block &blk = m_encoded_blocks[block_index];
+
+ const color_rgba &orig_color = get_source_pixel_block(block_index)(x, y);
+
+ color_rgba block_color;
+ blk.get_block_color(block_color, blk.get_subblock_index(x, y), s);
+ total_err += color_distance(m_params.m_perceptual, block_color, orig_color, false);
+
+ if (total_err > best_err)
+ break;
+
+ } // block_index
+
+ if (total_err < best_err)
+ {
+ best_err = total_err;
+ best_s = s;
+ if (!best_err)
+ break;
+ }
+
+ } // s
+
+ m_optimized_cluster_selectors[cluster_index].set_selector(x, y, best_s);
+
+ overall_best_err += best_err;
+
+ } // x
+ } // y
+
+ if (uses_hybrid_sel_codebook)
+ {
+ etc_block_vec etc_blocks;
+ pixel_block_vec pixel_blocks;
for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
{
const uint32_t block_index = cluster_block_indices[cluster_block_index];
- const etc_block &blk = m_encoded_blocks[block_index];
+ etc_blocks.push_back(m_encoded_blocks[block_index]);
- const color_rgba &orig_color = get_source_pixel_block(block_index)(x, y);
-
- color_rgba block_color;
- blk.get_block_color(block_color, blk.get_subblock_index(x, y), s);
- total_err += color_distance(m_params.m_perceptual, block_color, orig_color, false);
-
- if (total_err > best_err)
- break;
-
- } // block_index
-
- if (total_err < best_err)
- {
- best_err = total_err;
- best_s = s;
- if (!best_err)
- break;
+ pixel_blocks.push_back(get_source_pixel_block(block_index));
}
- } // s
+ uint32_t palette_index;
+ basist::etc1_global_palette_entry_modifier palette_modifier;
- m_optimized_cluster_selectors[cluster_index].set_selector(x, y, best_s);
+ #if 0
+ uint64_t best_global_cb_err = m_params.m_pGlobal_sel_codebook->find_best_entry(etc_blocks.size(), pixel_blocks.get_ptr(), etc_blocks.get_ptr(),
+ palette_index, palette_modifier,
+ m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
+ #else
+ uint64_t best_global_cb_err = etc1_global_selector_codebook_find_best_entry(*m_params.m_pGlobal_sel_codebook, (uint32_t)etc_blocks.size(), &pixel_blocks[0], &etc_blocks[0],
+ palette_index, palette_modifier,
+ m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
+ #endif
- overall_best_err += best_err;
+ if (best_global_cb_err <= overall_best_err * m_params.m_hybrid_codebook_quality_thresh)
+ {
+ m_selector_cluster_uses_global_cb[cluster_index] = true;
- } // x
- } // y
+ m_optimized_cluster_selector_global_cb_ids[cluster_index].set(palette_index, palette_modifier);
- if (uses_hybrid_sel_codebook)
- {
- etc_block_vec etc_blocks;
- pixel_block_vec pixel_blocks;
+ basist::etc1_selector_palette_entry pal_entry(m_params.m_pGlobal_sel_codebook->get_entry(palette_index, palette_modifier));
- for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
- {
- const uint32_t block_index = cluster_block_indices[cluster_block_index];
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ m_optimized_cluster_selectors[cluster_index].set_selector(x, y, pal_entry(x, y));
+ }
+ else
+ {
+ m_optimized_cluster_selector_global_cb_ids[cluster_index].set(0, basist::etc1_global_palette_entry_modifier(0));
- etc_blocks.push_back(m_encoded_blocks[block_index]);
+ m_selector_cluster_uses_global_cb[cluster_index] = false;
+ }
+ }
- pixel_blocks.push_back(get_source_pixel_block(block_index));
- }
+ if (uses_hybrid_sel_codebook)
+ {
+ std::lock_guard lock(m_lock);
+
+ total_clusters_processed++;
+ if ((total_clusters_processed % 63) == 0)
+ debug_printf("Global selector palette optimization: %3.1f%% complete\n", total_clusters_processed * 100.0f / total_selector_clusters);
+ }
- uint32_t palette_index;
- basist::etc1_global_palette_entry_modifier palette_modifier;
+ } // cluster_index
-#if 0
- uint64_t best_global_cb_err = m_params.m_pGlobal_sel_codebook->find_best_entry(etc_blocks.size(), pixel_blocks.get_ptr(), etc_blocks.get_ptr(),
- palette_index, palette_modifier,
- m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
-#else
- uint64_t best_global_cb_err = etc1_global_selector_codebook_find_best_entry(*m_params.m_pGlobal_sel_codebook, (uint32_t)etc_blocks.size(), &pixel_blocks[0], &etc_blocks[0],
- palette_index, palette_modifier,
- m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
-#endif
+ } );
- if (best_global_cb_err <= overall_best_err * m_params.m_hybrid_codebook_quality_thresh)
- {
- m_selector_cluster_uses_global_cb[cluster_index] = true;
+ } // cluster_index_iter
- m_optimized_cluster_selector_global_cb_ids[cluster_index].set(palette_index, palette_modifier);
-
- basist::etc1_selector_palette_entry pal_entry(m_params.m_pGlobal_sel_codebook->get_entry(palette_index, palette_modifier));
-
- for (uint32_t y = 0; y < 4; y++)
- for (uint32_t x = 0; x < 4; x++)
- m_optimized_cluster_selectors[cluster_index].set_selector(x, y, pal_entry(x, y));
- }
- else
- {
- m_optimized_cluster_selector_global_cb_ids[cluster_index].set(0, basist::etc1_global_palette_entry_modifier(0));
-
- m_selector_cluster_uses_global_cb[cluster_index] = false;
- }
- }
-
- if (uses_hybrid_sel_codebook)
- {
-#pragma omp critical
- {
- total_clusters_processed++;
- if ((total_clusters_processed % 63) == 0)
- debug_printf("Global selector palette optimization: %3.1f%% complete\n", total_clusters_processed * 100.0f / total_selector_clusters);
- }
- }
-
- } // cluster_index
+ m_params.m_pJob_pool->wait_for_all();
} // if (m_params.m_pGlobal_sel_codebook)
@@ -1410,11 +1707,7 @@ namespace basisu
}
char buf[256];
-#ifdef _WIN32
- sprintf_s(buf, sizeof(buf), "selector_cluster_vis_%u.png", iter);
-#else
snprintf(buf, sizeof(buf), "selector_cluster_vis_%u.png", iter);
-#endif
save_png(buf, selector_cluster_vis);
}
}
@@ -1441,66 +1734,81 @@ namespace basisu
// For each block: Determine which quantized selectors best encode that block, given its quantized endpoints.
- #pragma omp parallel for
- for (int block_index = 0; block_index < static_cast(m_total_blocks); block_index++)
+ const uint32_t N = 1024;
+ for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
{
- const color_rgba* pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
+ const uint32_t first_index = block_index_iter;
+ const uint32_t last_index = minimum(m_total_blocks, first_index + N);
- etc_block& blk = m_encoded_blocks[block_index];
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index, &new_cluster_indices] {
+
+ for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+ {
+ const color_rgba* pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
+
+ etc_block& blk = m_encoded_blocks[block_index];
- color_rgba trial_block_colors[4];
- blk.get_block_colors(trial_block_colors, 0);
+ color_rgba trial_block_colors[4];
+ blk.get_block_colors(trial_block_colors, 0);
- uint64_t best_cluster_err = UINT64_MAX;
- uint32_t best_cluster_index = 0;
+ uint64_t best_cluster_err = UINT64_MAX;
+ uint32_t best_cluster_index = 0;
- const uint32_t parent_selector_cluster = m_block_parent_selector_cluster.size() ? m_block_parent_selector_cluster[block_index] : 0;
- const uint_vec *pCluster_indices = m_selector_clusters_within_each_parent_cluster.size() ? &m_selector_clusters_within_each_parent_cluster[parent_selector_cluster] : nullptr;
+ const uint32_t parent_selector_cluster = m_block_parent_selector_cluster.size() ? m_block_parent_selector_cluster[block_index] : 0;
+ const uint_vec *pCluster_indices = m_selector_clusters_within_each_parent_cluster.size() ? &m_selector_clusters_within_each_parent_cluster[parent_selector_cluster] : nullptr;
- const uint32_t total_clusters = m_use_hierarchical_selector_codebooks ? (uint32_t)pCluster_indices->size() : (uint32_t)m_selector_cluster_indices.size();
+ const uint32_t total_clusters = m_use_hierarchical_selector_codebooks ? (uint32_t)pCluster_indices->size() : (uint32_t)m_selector_cluster_indices.size();
- for (uint32_t cluster_iter = 0; cluster_iter < total_clusters; cluster_iter++)
- {
- const uint32_t cluster_index = m_use_hierarchical_selector_codebooks ? (*pCluster_indices)[cluster_iter] : cluster_iter;
-
- const etc_block& cluster_blk = m_optimized_cluster_selectors[cluster_index];
-
- uint64_t trial_err = 0;
- for (int y = 0; y < 4; y++)
+ for (uint32_t cluster_iter = 0; cluster_iter < total_clusters; cluster_iter++)
{
- for (int x = 0; x < 4; x++)
+ const uint32_t cluster_index = m_use_hierarchical_selector_codebooks ? (*pCluster_indices)[cluster_iter] : cluster_iter;
+
+ const etc_block& cluster_blk = m_optimized_cluster_selectors[cluster_index];
+
+ uint64_t trial_err = 0;
+ for (int y = 0; y < 4; y++)
{
- const uint32_t sel = cluster_blk.get_selector(x, y);
+ for (int x = 0; x < 4; x++)
+ {
+ const uint32_t sel = cluster_blk.get_selector(x, y);
- trial_err += color_distance(m_params.m_perceptual, trial_block_colors[sel], pBlock_pixels[x + y * 4], false);
- if (trial_err > best_cluster_err)
- goto early_out;
+ trial_err += color_distance(m_params.m_perceptual, trial_block_colors[sel], pBlock_pixels[x + y * 4], false);
+ if (trial_err > best_cluster_err)
+ goto early_out;
+ }
}
- }
- if (trial_err < best_cluster_err)
- {
- best_cluster_err = trial_err;
- best_cluster_index = cluster_index;
- if (!best_cluster_err)
- break;
+ if (trial_err < best_cluster_err)
+ {
+ best_cluster_err = trial_err;
+ best_cluster_index = cluster_index;
+ if (!best_cluster_err)
+ break;
+ }
+
+ early_out:
+ ;
}
- early_out:
- ;
- }
+ blk.set_raw_selector_bits(m_optimized_cluster_selectors[best_cluster_index].get_raw_selector_bits());
- blk.set_raw_selector_bits(m_optimized_cluster_selectors[best_cluster_index].get_raw_selector_bits());
+ m_block_selector_cluster_index[block_index] = best_cluster_index;
+
+ {
+ std::lock_guard lock(m_lock);
- m_block_selector_cluster_index[block_index] = best_cluster_index;
+ vector_ensure_element_is_valid(new_cluster_indices, best_cluster_index);
+ new_cluster_indices[best_cluster_index].push_back(block_index);
+ }
+
+ } // block_index
- #pragma omp critical
- {
- vector_ensure_element_is_valid(new_cluster_indices, best_cluster_index);
- new_cluster_indices[best_cluster_index].push_back(block_index);
- }
- }
+ } );
+ } // block_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
+
m_selector_cluster_indices.swap(new_cluster_indices);
}
@@ -1697,7 +2005,7 @@ namespace basisu
return total_subblocks_refined;
}
- void basisu_frontend::dump_endpoint_clusterization_visualization(const char *pFilename)
+ void basisu_frontend::dump_endpoint_clusterization_visualization(const char *pFilename, bool vis_endpoint_colors)
{
debug_printf("dump_endpoint_clusterization_visualization\n");
@@ -1744,19 +2052,31 @@ namespace basisu
const uint32_t block_index = training_vector_index >> 1;
const uint32_t subblock_index = training_vector_index & 1;
+ const etc_block& blk2 = m_etc1_blocks_etc1s[block_index];
+
const color_rgba *pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
- const etc_block &blk2 = m_etc1_blocks_etc1s[block_index];
-
color_rgba subblock_pixels[8];
- for (uint32_t i = 0; i < 8; i++)
- subblock_pixels[i] = pBlock_pixels[g_etc1_pixel_indices[blk2.get_flip_bit()][subblock_index][i]];
+
+ if (vis_endpoint_colors)
+ {
+ color_rgba colors[2];
+ blk2.get_block_low_high_colors(colors, subblock_index);
+ for (uint32_t i = 0; i < 8; i++)
+ subblock_pixels[i] = colors[subblock_index];
+ }
+ else
+ {
+ for (uint32_t i = 0; i < 8; i++)
+ subblock_pixels[i] = pBlock_pixels[g_etc1_pixel_indices[blk2.get_flip_bit()][subblock_index][i]];
+ }
endpoint_cluster_vis.set_block_clipped(subblock_pixels, 12 + 5 * subblock_iter, 3 * unsorted_cluster_iter, 4, 2);
}
}
save_png(pFilename, endpoint_cluster_vis);
+ debug_printf("Wrote debug visualization file %s\n", pFilename);
}
void basisu_frontend::finalize()
@@ -1786,91 +2106,102 @@ namespace basisu
std::vector cluster_valid(new_endpoint_cluster_block_indices.size());
std::vector cluster_improved(new_endpoint_cluster_block_indices.size());
-#pragma omp parallel for
- for (int cluster_index = 0; cluster_index < static_cast(new_endpoint_cluster_block_indices.size()); cluster_index++)
+ const uint32_t N = 256;
+ for (uint32_t cluster_index_iter = 0; cluster_index_iter < new_endpoint_cluster_block_indices.size(); cluster_index_iter += N)
{
- const std::vector& cluster_block_indices = new_endpoint_cluster_block_indices[cluster_index];
+ const uint32_t first_index = cluster_index_iter;
+ const uint32_t last_index = minimum((uint32_t)new_endpoint_cluster_block_indices.size(), cluster_index_iter + N);
+
+ m_params.m_pJob_pool->add_job( [this, first_index, last_index, &cluster_improved, &cluster_valid, &new_endpoint_cluster_block_indices, &pBlock_selector_indices ] {
+ for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
+ {
+ const std::vector& cluster_block_indices = new_endpoint_cluster_block_indices[cluster_index];
- if (!cluster_block_indices.size())
- continue;
+ if (!cluster_block_indices.size())
+ continue;
- const uint32_t total_pixels = (uint32_t)cluster_block_indices.size() * 16;
+ const uint32_t total_pixels = (uint32_t)cluster_block_indices.size() * 16;
- std::vector cluster_pixels(total_pixels);
- uint8_vec force_selectors(total_pixels);
+ std::vector cluster_pixels(total_pixels);
+ uint8_vec force_selectors(total_pixels);
- etc_block blk;
- blk.set_block_color5_etc1s(get_endpoint_cluster_unscaled_color(cluster_index, false));
- blk.set_inten_tables_etc1s(get_endpoint_cluster_inten_table(cluster_index, false));
- blk.set_flip_bit(true);
+ etc_block blk;
+ blk.set_block_color5_etc1s(get_endpoint_cluster_unscaled_color(cluster_index, false));
+ blk.set_inten_tables_etc1s(get_endpoint_cluster_inten_table(cluster_index, false));
+ blk.set_flip_bit(true);
- uint64_t cur_err = 0;
+ uint64_t cur_err = 0;
- for (uint32_t cluster_block_indices_iter = 0; cluster_block_indices_iter < cluster_block_indices.size(); cluster_block_indices_iter++)
- {
- const uint32_t block_index = cluster_block_indices[cluster_block_indices_iter];
+ for (uint32_t cluster_block_indices_iter = 0; cluster_block_indices_iter < cluster_block_indices.size(); cluster_block_indices_iter++)
+ {
+ const uint32_t block_index = cluster_block_indices[cluster_block_indices_iter];
- const color_rgba *pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
+ const color_rgba *pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
- memcpy(&cluster_pixels[cluster_block_indices_iter * 16], pBlock_pixels, 16 * sizeof(color_rgba));
+ memcpy(&cluster_pixels[cluster_block_indices_iter * 16], pBlock_pixels, 16 * sizeof(color_rgba));
- const uint32_t selector_cluster_index = pBlock_selector_indices ? (*pBlock_selector_indices)[block_index] : get_block_selector_cluster_index(block_index);
+ const uint32_t selector_cluster_index = pBlock_selector_indices ? (*pBlock_selector_indices)[block_index] : get_block_selector_cluster_index(block_index);
- const etc_block &blk_selectors = get_selector_cluster_selector_bits(selector_cluster_index);
+ const etc_block &blk_selectors = get_selector_cluster_selector_bits(selector_cluster_index);
- blk.set_raw_selector_bits(blk_selectors.get_raw_selector_bits());
+ blk.set_raw_selector_bits(blk_selectors.get_raw_selector_bits());
- cur_err += blk.evaluate_etc1_error(pBlock_pixels, m_params.m_perceptual);
+ cur_err += blk.evaluate_etc1_error(pBlock_pixels, m_params.m_perceptual);
- for (uint32_t y = 0; y < 4; y++)
- for (uint32_t x = 0; x < 4; x++)
- force_selectors[cluster_block_indices_iter * 16 + x + y * 4] = static_cast(blk_selectors.get_selector(x, y));
- }
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ force_selectors[cluster_block_indices_iter * 16 + x + y * 4] = static_cast(blk_selectors.get_selector(x, y));
+ }
- endpoint_cluster_etc_params new_endpoint_cluster_etc_params;
+ endpoint_cluster_etc_params new_endpoint_cluster_etc_params;
- {
- etc1_optimizer optimizer;
- etc1_solution_coordinates solutions[2];
+ {
+ etc1_optimizer optimizer;
+ etc1_solution_coordinates solutions[2];
- etc1_optimizer::params cluster_optimizer_params;
- cluster_optimizer_params.m_num_src_pixels = total_pixels;
- cluster_optimizer_params.m_pSrc_pixels = &cluster_pixels[0];
+ etc1_optimizer::params cluster_optimizer_params;
+ cluster_optimizer_params.m_num_src_pixels = total_pixels;
+ cluster_optimizer_params.m_pSrc_pixels = &cluster_pixels[0];
- cluster_optimizer_params.m_use_color4 = false;
- cluster_optimizer_params.m_perceptual = m_params.m_perceptual;
- cluster_optimizer_params.m_pForce_selectors = &force_selectors[0];
+ cluster_optimizer_params.m_use_color4 = false;
+ cluster_optimizer_params.m_perceptual = m_params.m_perceptual;
+ cluster_optimizer_params.m_pForce_selectors = &force_selectors[0];
- if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
- cluster_optimizer_params.m_quality = cETCQualityUber;
+ if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
+ cluster_optimizer_params.m_quality = cETCQualityUber;
- etc1_optimizer::results cluster_optimizer_results;
+ etc1_optimizer::results cluster_optimizer_results;
- std::vector cluster_selectors(total_pixels);
- cluster_optimizer_results.m_n = total_pixels;
- cluster_optimizer_results.m_pSelectors = &cluster_selectors[0];
+ std::vector cluster_selectors(total_pixels);
+ cluster_optimizer_results.m_n = total_pixels;
+ cluster_optimizer_results.m_pSelectors = &cluster_selectors[0];
- optimizer.init(cluster_optimizer_params, cluster_optimizer_results);
+ optimizer.init(cluster_optimizer_params, cluster_optimizer_results);
- optimizer.compute();
+ optimizer.compute();
- new_endpoint_cluster_etc_params.m_color_unscaled[0] = cluster_optimizer_results.m_block_color_unscaled;
- new_endpoint_cluster_etc_params.m_inten_table[0] = cluster_optimizer_results.m_block_inten_table;
- new_endpoint_cluster_etc_params.m_color_error[0] = cluster_optimizer_results.m_error;
- new_endpoint_cluster_etc_params.m_color_used[0] = true;
- new_endpoint_cluster_etc_params.m_valid = true;
- }
+ new_endpoint_cluster_etc_params.m_color_unscaled[0] = cluster_optimizer_results.m_block_color_unscaled;
+ new_endpoint_cluster_etc_params.m_inten_table[0] = cluster_optimizer_results.m_block_inten_table;
+ new_endpoint_cluster_etc_params.m_color_error[0] = cluster_optimizer_results.m_error;
+ new_endpoint_cluster_etc_params.m_color_used[0] = true;
+ new_endpoint_cluster_etc_params.m_valid = true;
+ }
- if (new_endpoint_cluster_etc_params.m_color_error[0] < cur_err)
- {
- m_endpoint_cluster_etc_params[cluster_index] = new_endpoint_cluster_etc_params;
+ if (new_endpoint_cluster_etc_params.m_color_error[0] < cur_err)
+ {
+ m_endpoint_cluster_etc_params[cluster_index] = new_endpoint_cluster_etc_params;
- cluster_improved[cluster_index] = true;
- }
+ cluster_improved[cluster_index] = true;
+ }
- cluster_valid[cluster_index] = true;
+ cluster_valid[cluster_index] = true;
- } // cluster_index
+ } // cluster_index
+ } );
+
+ } // cluster_index_iter
+
+ m_params.m_pJob_pool->wait_for_all();
uint32_t total_unused_clusters = 0;
uint32_t total_improved_clusters = 0;
@@ -1980,7 +2311,8 @@ namespace basisu
for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
{
-#define CHECK(x) do { if (!(x)) return false; } while(0)
+//#define CHECK(x) do { if (!(x)) { DebugBreak(); return false; } } while(0)
+#define CHECK(x) BASISU_FRONTEND_VERIFY(x);
CHECK(get_output_block(block_index).get_flip_bit() == true);
diff --git a/basisu_frontend.h b/basisu_frontend.h
index 2a630e0..4543677 100644
--- a/basisu_frontend.h
+++ b/basisu_frontend.h
@@ -17,6 +17,7 @@
#include "basisu_etc.h"
#include "basisu_gpu_texture.h"
#include "basisu_global_selector_palette_helpers.h"
+#include "transcoder/basisu_file_headers.h"
namespace basisu
{
@@ -71,13 +72,17 @@ namespace basisu
m_perceptual(true),
m_debug_stats(false),
m_debug_images(false),
- m_dump_endpoint_clusterization(false),
+ m_dump_endpoint_clusterization(true),
m_pGlobal_sel_codebook(NULL),
m_num_global_sel_codebook_pal_bits(0),
m_num_global_sel_codebook_mod_bits(0),
m_use_hybrid_selector_codebooks(false),
m_hybrid_codebook_quality_thresh(0.0f),
- m_validate(false)
+ m_validate(false),
+ m_tex_type(basist::cBASISTexType2D),
+ m_multithreaded(false),
+ m_disable_hierarchical_endpoint_codebooks(false),
+ m_pJob_pool(nullptr)
{
}
@@ -94,12 +99,17 @@ namespace basisu
bool m_debug_images;
bool m_dump_endpoint_clusterization;
bool m_validate;
+ bool m_multithreaded;
+ bool m_disable_hierarchical_endpoint_codebooks;
const basist::etc1_global_selector_codebook *m_pGlobal_sel_codebook;
uint32_t m_num_global_sel_codebook_pal_bits;
uint32_t m_num_global_sel_codebook_mod_bits;
bool m_use_hybrid_selector_codebooks;
float m_hybrid_codebook_quality_thresh;
+ basist::basis_texture_type m_tex_type;
+
+ job_pool *m_pJob_pool;
};
bool init(const params &p);
@@ -313,11 +323,13 @@ namespace basisu
// The sorted subblock endpoint quant error for each endpoint cluster
std::vector m_subblock_endpoint_quant_err_vec;
+ std::mutex m_lock;
+
//-----------------------------------------------------------------------------
void init_etc1_images();
void init_endpoint_training_vectors();
- void dump_endpoint_clusterization_visualization(const char *pFilename);
+ void dump_endpoint_clusterization_visualization(const char *pFilename, bool vis_endpoint_colors);
void generate_endpoint_clusters();
void compute_endpoint_subblock_error_vec();
void introduce_new_endpoint_clusters();
@@ -328,12 +340,13 @@ namespace basisu
void compute_endpoint_clusters_within_each_parent_cluster();
void compute_selector_clusters_within_each_parent_cluster();
void create_initial_packed_texture();
- void create_selector_clusters();
+ void generate_selector_clusters();
void create_optimized_selector_codebook(uint32_t iter);
void find_optimal_selector_clusters_for_each_block();
uint32_t refine_block_endpoints_given_selectors();
void finalize();
bool validate_output() const;
+ void introduce_special_selector_clusters();
void optimize_selector_codebook();
bool check_etc1s_constraints() const;
};
diff --git a/basisu_ssim.cpp b/basisu_ssim.cpp
index 2c92885..5ca0cbe 100644
--- a/basisu_ssim.cpp
+++ b/basisu_ssim.cpp
@@ -105,7 +105,7 @@ namespace basisu
dst.crop(dst_width, dst_height);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int oy = 0; oy < dst_height; oy++)
{
for (int ox = 0; ox < dst_width; ox++)
@@ -139,7 +139,7 @@ namespace basisu
{
dst.resize(src);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int y = 0; y < (int)dst.get_height(); y++)
{
for (uint32_t x = 0; x < dst.get_width(); x++)
@@ -158,7 +158,7 @@ namespace basisu
{
dst.resize(src);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int y = 0; y < (int)dst.get_height(); y++)
{
for (uint32_t x = 0; x < dst.get_width(); x++)
@@ -173,7 +173,7 @@ namespace basisu
{
dst.resize(src);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int y = 0; y < (int)dst.get_height(); y++)
{
for (uint32_t x = 0; x < dst.get_width(); x++)
@@ -194,7 +194,7 @@ namespace basisu
{
dst.resize(src1);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int y = 0; y < (int)dst.get_height(); y++)
{
for (uint32_t x = 0; x < dst.get_width(); x++)
@@ -215,7 +215,7 @@ namespace basisu
{
dst.resize(src1);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int y = 0; y < (int)dst.get_height(); y++)
{
for (uint32_t x = 0; x < dst.get_width(); x++)
@@ -232,7 +232,7 @@ namespace basisu
{
dst.resize(src);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int y = 0; y < (int)dst.get_height(); y++)
{
for (uint32_t x = 0; x < dst.get_width(); x++)
@@ -248,7 +248,7 @@ namespace basisu
{
dst.resize(src1);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int y = 0; y < (int)dst.get_height(); y++)
{
for (uint32_t x = 0; x < dst.get_width(); x++)
@@ -274,7 +274,7 @@ namespace basisu
{
dst.resize(src1);
-#pragma omp parallel for
+//#pragma omp parallel for
for (int y = 0; y < (int)dst.get_height(); y++)
{
for (uint32_t x = 0; x < dst.get_width(); x++)
diff --git a/basisu_tool.cpp b/basisu_tool.cpp
index 602111f..68b582f 100644
--- a/basisu_tool.cpp
+++ b/basisu_tool.cpp
@@ -22,15 +22,12 @@
#include "basisu_comp.h"
#include "transcoder/basisu_transcoder.h"
#include "basisu_ssim.h"
-#if defined(_OPENMP)
-#include
-#endif
#define BASISU_CATCH_EXCEPTIONS 1
using namespace basisu;
-#define BASISU_TOOL_VERSION "1.07.00"
+#define BASISU_TOOL_VERSION "1.08.00"
enum tool_mode
{
@@ -73,6 +70,7 @@ static void print_usage()
" -stats: Compute and display image quality metrics (slightly slower).\n"
" -tex_type <2d, 2darray, 3d, video, cubemap>: Set Basis file header's texture type field. Cubemap arrays require multiples of 6 images, in X+, X-, Y+, Y-, Z+, Z- order, each image must be the same resolutions.\n"
" 2d=arbitrary 2D images, 2darray=2D array, 3D=volume texture slices, video=video frames, cubemap=array of faces. For 2darray/3d/cubemaps/video, each source image's dimensions and # of mipmap levels must be the same.\n"
+ " For video, the .basis file will be written with the first frame being an I-Frame, and subsequent frames being P-Frames (using conditional replenishment). Playback must always occur in order from first to last image.\n"
" -framerate X: Set framerate in header to X/frames sec.\n"
" -individual: Process input images individually and output multiple .basis files (not as a texture array)\n"
" -fuzz_testing: Use with -validate: Disables CRC16 validation of file contents before transcoding\n"
@@ -85,9 +83,10 @@ static void print_usage()
" -no_alpha: Always output non-alpha basis files, even if one or more inputs has alpha\n"
" -force_alpha: Always output alpha basis files, even if no inputs has alpha\n"
" -seperate_rg_to_color_alpha: Seperate input R and G channels to RGB and A (for tangent space XY normal maps)\n"
- " -no_multithreading: Disable OpenMP multithreading\n"
+ " -no_multithreading: Disable multithreading\n"
" -no_ktx: Disable KTX writing when unpacking (faster)\n"
" -etc1_only: Only unpack to ETC1, skipping the other texture formats during -unpack\n"
+ " -disable_hierarchical_endpoint_codebooks: Disable hierarchical endpoint codebook usage, slower but higher quality on some compression levels\n"
"\n"
"Mipmap generation options:\n"
" -mipmap: Generate mipmaps for each source image\n"
@@ -130,6 +129,9 @@ static void print_usage()
" basisu -linear -global_sel_pal -file x.png: Compress a non-sRGB image, use hybrid selector codebooks for slightly improved compression (but slower encoding)\n"
" basisu -tex_type video -framerate 20 -multifile_printf \"x%02u.png\" -multifile_first 1 -multifile_count 20 : Compress a 20 sRGB source image video sequence (x01.png, x02.png, x03.png, etc.) to x01.basis\n"
"\n"
+ "Note: For video use, it's recommended you use a very powerful machine with many cores. Use -slower for better codebook generation, specify very large codebooks using -max_endpoints and -max_selectors, and reduce\n"
+ "the default endpoint RDO threshold (-endpoint_rdo_thresh) to around 1.25. Videos may have mipmaps and alpha channels. Videos must always be played back by the transcoder in first to last image order.\n"
+ "Video files currently use I-Frames on the first image, and P-Frames using conditional replenishment on subsequent frames.\n"
"Compression level details:\n"
" Level 0: Fastest, but has marginal quality and is a work in progress. Brittle on complex images. Avg. Y dB: 35.45\n"
" Level 1: Hierarchical codebook searching. 36.87 dB, ~1.4x slower vs. level 0. (This is the default setting.)\n"
@@ -345,9 +347,7 @@ public:
m_comp_params.m_seperate_rg_to_color_alpha = true;
else if (strcasecmp(pArg, "-no_multithreading") == 0)
{
-#if defined(_OPENMP)
- omp_set_num_threads(1);
-#endif
+ m_comp_params.m_multithreading = false;
}
else if (strcasecmp(pArg, "-mipmap") == 0)
m_comp_params.m_mip_gen = true;
@@ -355,6 +355,8 @@ public:
m_no_ktx = true;
else if (strcasecmp(pArg, "-etc1_only") == 0)
m_etc1_only = true;
+ else if (strcasecmp(pArg, "-disable_hierarchical_endpoint_codebooks") == 0)
+ m_comp_params.m_disable_hierarchical_endpoint_codebooks = true;
else if (strcasecmp(pArg, "-mip_scale") == 0)
{
REMAINING_ARGS_CHECK(1);
@@ -612,6 +614,18 @@ static bool expand_multifile(command_line_params &opts)
static bool compress_mode(command_line_params &opts)
{
basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);
+
+ uint32_t num_threads = 1;
+
+ if (opts.m_comp_params.m_multithreading)
+ {
+ num_threads = std::thread::hardware_concurrency();
+ if (num_threads < 1)
+ num_threads = 1;
+ }
+
+ job_pool jpool(num_threads);
+ opts.m_comp_params.m_pJob_pool = &jpool;
if (!expand_multifile(opts))
{
@@ -643,7 +657,7 @@ static bool compress_mode(command_line_params &opts)
}
printf("Processing %u total files\n", (uint32_t)opts.m_input_filenames.size());
-
+
for (size_t file_index = 0; file_index < (opts.m_individual ? opts.m_input_filenames.size() : 1U); file_index++)
{
if (opts.m_individual)
@@ -895,6 +909,21 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
ii.m_num_blocks_x, ii.m_num_blocks_y, ii.m_first_slice_index, (uint32_t)ii.m_alpha_flag);
}
+ printf("\nSlice info:\n");
+ for (uint32_t i = 0; i < fileinfo.m_slice_info.size(); i++)
+ {
+ const basist::basisu_slice_info &sliceinfo = fileinfo.m_slice_info[i];
+ printf("%u: OrigWidthHeight: %ux%u, BlockDim: %ux%u, TotalBlocks: %u, Compressed size: %u, Image: %u, Level: %u, UnpackedCRC16: 0x%X, alpha: %u, iframe: %i\n",
+ i,
+ sliceinfo.m_orig_width, sliceinfo.m_orig_height,
+ sliceinfo.m_num_blocks_x, sliceinfo.m_num_blocks_y,
+ sliceinfo.m_total_blocks,
+ sliceinfo.m_compressed_size,
+ sliceinfo.m_image_index, sliceinfo.m_level_index,
+ sliceinfo.m_unpacked_slice_crc16,
+ (uint32_t)sliceinfo.m_alpha_flag,
+ (uint32_t)sliceinfo.m_iframe_flag);
+ }
printf("\n");
if (!dec.start_transcoding(&basis_data[0], (uint32_t)basis_data.size()))
@@ -925,6 +954,8 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
}
// Now transcode the file to all supported texture formats and save mipmapped KTX files
+ for (int format_iter = first_format; format_iter < last_format; format_iter++)
+ {
for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++)
{
for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++)
@@ -937,8 +968,6 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
return false;
}
- for (int format_iter = first_format; format_iter < last_format; format_iter++)
- {
const basist::transcoder_texture_format transcoder_tex_fmt = static_cast(format_iter);
if (transcoder_tex_fmt == basist::cTFPVRTC1_4_OPAQUE_ONLY)
@@ -1048,7 +1077,7 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
if ((!opts.m_no_ktx) && (fileinfo.m_tex_type != basist::cBASISTexTypeCubemapArray))
{
- std::string ktx_filename(base_filename + string_format("_transcoded_%s_%u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), image_index));
+ std::string ktx_filename(base_filename + string_format("_transcoded_%s_%04u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), image_index));
if (!write_compressed_texture_file(ktx_filename.c_str(), gi))
{
error_printf("Failed writing KTX file \"%s\"!\n", ktx_filename.c_str());
@@ -1075,7 +1104,11 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
}
//u.crop(level_info.m_orig_width, level_info.m_orig_height);
- std::string rgb_filename(base_filename + string_format("_unpacked_rgb_%s_%u_%u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index, level_index));
+ std::string rgb_filename;
+ if (gi.size() > 1)
+ rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index);
+ else
+ rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index);
if (!save_png(rgb_filename, u, cImageSaveIgnoreAlpha))
{
error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
@@ -1085,7 +1118,11 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
if (basis_transcoder_format_has_alpha(transcoder_tex_fmt))
{
- std::string a_filename(base_filename + string_format("_unpacked_a_%s_%u_%u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index, level_index));
+ std::string a_filename;
+ if (gi.size() > 1)
+ a_filename = base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index);
+ else
+ a_filename = base_filename + string_format("_unpacked_a_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index);
if (!save_png(a_filename, u, cImageSaveGrayscale, 3))
{
error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str());
@@ -1190,8 +1227,7 @@ static bool compare_mode(command_line_params &opts)
const int X = 2;
-#pragma omp parallel for
- for (int y = 0; y < (int)a.get_height(); y++)
+ for (uint32_t y = 0; y < a.get_height(); y++)
{
for (uint32_t x = 0; x < a.get_width(); x++)
{
diff --git a/transcoder/basisu_file_headers.h b/transcoder/basisu_file_headers.h
index 124b7f8..4b3bcf3 100644
--- a/transcoder/basisu_file_headers.h
+++ b/transcoder/basisu_file_headers.h
@@ -21,6 +21,7 @@ namespace basist
enum basis_slice_desc_flags
{
cSliceDescFlagsIsAlphaData = 1,
+ cSliceDescFlagsFrameIsIFrame = 2 // Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols)
};
#pragma pack(push)
diff --git a/transcoder/basisu_transcoder.cpp b/transcoder/basisu_transcoder.cpp
index 40896dc..a2ba411 100644
--- a/transcoder/basisu_transcoder.cpp
+++ b/transcoder/basisu_transcoder.cpp
@@ -47,16 +47,20 @@
#define BASISD_WRITE_NEW_DXT1_TABLES 0
#define BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES 0
+#ifndef BASISD_ENABLE_DEBUG_FLAGS
+#define BASISD_ENABLE_DEBUG_FLAGS 0
+#endif
+
namespace basisu
{
- static bool g_debug_printf;
+ bool g_debug_printf;
void enable_debug_printf(bool enabled)
{
g_debug_printf = enabled;
}
- void debug_printf(const char *pFmt, ...)
+ void debug_printf(const char* pFmt, ...)
{
#if BASISU_DEVEL_MESSAGES
g_debug_printf = true;
@@ -73,14 +77,34 @@ namespace basisu
namespace basist
{
- #include "basisu_transcoder_tables_bc7_m6.inc"
-
- uint16_t crc16(const void *r, size_t size, uint16_t crc)
+#include "basisu_transcoder_tables_bc7_m6.inc"
+
+#if BASISD_ENABLE_DEBUG_FLAGS
+ static uint32_t g_debug_flags = 0;
+#endif
+
+ uint32_t get_debug_flags()
+ {
+#if BASISD_ENABLE_DEBUG_FLAGS
+ return g_debug_flags;
+#else
+ return 0;
+#endif
+ }
+
+ void set_debug_flags(uint32_t f)
+ {
+ (void)f;
+#if BASISD_ENABLE_DEBUG_FLAGS
+ g_debug_flags = f;
+#endif
+ }
+ uint16_t crc16(const void* r, size_t size, uint16_t crc)
{
crc = ~crc;
- const uint8_t *p = reinterpret_cast(r);
- for ( ; size; --size)
+ const uint8_t* p = reinterpret_cast(r);
+ for (; size; --size)
{
const uint16_t q = *p++ ^ (crc >> 8);
uint16_t k = (q >> 4) ^ q;
@@ -89,21 +113,21 @@ namespace basist
return static_cast(~crc);
}
-
+
const uint32_t g_global_selector_cb[] =
#include "basisu_global_selector_cb.h"
;
const uint32_t g_global_selector_cb_size = sizeof(g_global_selector_cb) / sizeof(g_global_selector_cb[0]);
- void etc1_global_selector_codebook::init(uint32_t N, const uint32_t *pEntries)
+ void etc1_global_selector_codebook::init(uint32_t N, const uint32_t* pEntries)
{
m_palette.resize(N);
for (uint32_t i = 0; i < N; i++)
m_palette[i].set_uint32(pEntries[i]);
}
- void etc1_global_selector_codebook::print_code(FILE *pFile)
+ void etc1_global_selector_codebook::print_code(FILE* pFile)
{
fprintf(pFile, "{\n");
for (uint32_t i = 0; i < m_palette.size(); i++)
@@ -266,7 +290,7 @@ namespace basist
assert((x | y | val) < 4);
const uint32_t bit_index = x * 4 + y;
- uint8_t * p = &m_bytes[7 - (bit_index >> 3)];
+ uint8_t* p = &m_bytes[7 - (bit_index >> 3)];
const uint32_t byte_bit_ofs = bit_index & 7;
const uint32_t mask = 1 << byte_bit_ofs;
@@ -291,7 +315,7 @@ namespace basist
const uint32_t bit_index = x * 4 + y;
const uint32_t byte_bit_ofs = bit_index & 7;
- const uint8_t * p = &m_bytes[7 - (bit_index >> 3)];
+ const uint8_t* p = &m_bytes[7 - (bit_index >> 3)];
const uint32_t lsb = (p[0] >> byte_bit_ofs) & 1;
const uint32_t msb = (p[-2] >> byte_bit_ofs) & 1;
const uint32_t val = lsb | (msb << 1);
@@ -367,7 +391,7 @@ namespace basist
set_byte_bits(cETC1DeltaColor3BBitOffset, 3, c & 7);
}
- void set_block_color4(const color32 & c0_unscaled, const color32 & c1_unscaled)
+ void set_block_color4(const color32& c0_unscaled, const color32& c1_unscaled)
{
set_diff_bit(false);
@@ -375,7 +399,7 @@ namespace basist
set_base4_color(1, pack_color4(c1_unscaled, false));
}
- void set_block_color5(const color32 & c0_unscaled, const color32 & c1_unscaled)
+ void set_block_color5(const color32& c0_unscaled, const color32& c1_unscaled)
{
set_diff_bit(true);
@@ -388,7 +412,7 @@ namespace basist
set_delta3_color(pack_delta3(dr, dg, db));
}
- bool set_block_color5_check(const color32 & c0_unscaled, const color32 & c1_unscaled)
+ bool set_block_color5_check(const color32& c0_unscaled, const color32& c1_unscaled)
{
set_diff_bit(true);
@@ -438,7 +462,7 @@ namespace basist
return (m_bytes[3] >> ofs) & 7;
}
- static uint16_t pack_color4(const color32 & color, bool scaled, uint32_t bias = 127U)
+ static uint16_t pack_color4(const color32& color, bool scaled, uint32_t bias = 127U)
{
return pack_color4(color.r, color.g, color.b, scaled, bias);
}
@@ -459,7 +483,7 @@ namespace basist
return static_cast(b | (g << 4U) | (r << 8U));
}
- static uint16_t pack_color5(const color32 & color, bool scaled, uint32_t bias = 127U)
+ static uint16_t pack_color5(const color32& color, bool scaled, uint32_t bias = 127U)
{
return pack_color5(color.r, color.g, color.b, scaled, bias);
}
@@ -480,7 +504,7 @@ namespace basist
return static_cast(b | (g << 5U) | (r << 10U));
}
- uint16_t pack_delta3(const color32 & color)
+ uint16_t pack_delta3(const color32& color)
{
return pack_delta3(color.r, color.g, color.b);
}
@@ -512,7 +536,7 @@ namespace basist
return color32(r, g, b, alpha);
}
- static void unpack_color5(uint32_t & r, uint32_t & g, uint32_t & b, uint16_t packed_color5, bool scaled)
+ static void unpack_color5(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color5, bool scaled)
{
color32 c(unpack_color5(packed_color5, scaled, 0));
r = c.r;
@@ -520,7 +544,7 @@ namespace basist
b = c.b;
}
- static void get_diff_subblock_colors(color32 * pDst, uint16_t packed_color5, uint32_t table_idx)
+ static void get_diff_subblock_colors(color32* pDst, uint16_t packed_color5, uint32_t table_idx)
{
assert(table_idx < cETC1IntenModifierValues);
const int* pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
@@ -556,7 +580,7 @@ namespace basist
return x;
}
- static void get_block_colors5(color32 * pBlock_colors, const color32 & base_color5, uint32_t inten_table)
+ static void get_block_colors5(color32* pBlock_colors, const color32& base_color5, uint32_t inten_table)
{
color32 b(base_color5);
@@ -571,8 +595,8 @@ namespace basist
pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);
pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);
}
-
- static void get_block_colors5_bounds(color32 * pBlock_colors, const color32 & base_color5, uint32_t inten_table, uint32_t l = 0, uint32_t h = 3)
+
+ static void get_block_colors5_bounds(color32* pBlock_colors, const color32& base_color5, uint32_t inten_table, uint32_t l = 0, uint32_t h = 3)
{
color32 b(base_color5);
@@ -589,8 +613,8 @@ namespace basist
enum dxt_constants
{
- cDXT1SelectorBits = 2U, cDXT1SelectorValues = 1U << cDXT1SelectorBits, cDXT1SelectorMask = cDXT1SelectorValues - 1U,
- cDXT5SelectorBits = 3U, cDXT5SelectorValues = 1U << cDXT5SelectorBits, cDXT5SelectorMask = cDXT5SelectorValues - 1U,
+ cDXT1SelectorBits = 2U, cDXT1SelectorValues = 1U << cDXT1SelectorBits, cDXT1SelectorMask = cDXT1SelectorValues - 1U,
+ cDXT5SelectorBits = 3U, cDXT5SelectorValues = 1U << cDXT5SelectorBits, cDXT5SelectorMask = cDXT5SelectorValues - 1U,
};
static const uint8_t g_etc1_x_selector_unpack[4][256] =
@@ -649,14 +673,14 @@ namespace basist
inline void clear() { basisu::clear_obj(*this); }
- inline uint32_t get_high_color() const { return m_high_color[0] | (m_high_color[1] << 8U); }
+ inline uint32_t get_high_color() const { return m_high_color[0] | (m_high_color[1] << 8U); }
inline uint32_t get_low_color() const { return m_low_color[0] | (m_low_color[1] << 8U); }
inline void set_low_color(uint16_t c) { m_low_color[0] = static_cast(c & 0xFF); m_low_color[1] = static_cast((c >> 8) & 0xFF); }
inline void set_high_color(uint16_t c) { m_high_color[0] = static_cast(c & 0xFF); m_high_color[1] = static_cast((c >> 8) & 0xFF); }
inline uint32_t get_selector(uint32_t x, uint32_t y) const { assert((x < 4U) && (y < 4U)); return (m_selectors[y] >> (x * cDXT1SelectorBits)) & cDXT1SelectorMask; }
inline void set_selector(uint32_t x, uint32_t y, uint32_t val) { assert((x < 4U) && (y < 4U) && (val < 4U)); m_selectors[y] &= (~(cDXT1SelectorMask << (x * cDXT1SelectorBits))); m_selectors[y] |= (val << (x * cDXT1SelectorBits)); }
- static uint16_t pack_color(const color32 &color, bool scaled, uint32_t bias = 127U)
+ static uint16_t pack_color(const color32& color, bool scaled, uint32_t bias = 127U)
{
uint32_t r = color.r, g = color.g, b = color.b;
if (scaled)
@@ -928,8 +952,8 @@ namespace basist
};
static bc1_match_entry g_bc1_match5_equals_1[256], g_bc1_match6_equals_1[256]; // selector 1, allow equals hi/lo
static bc1_match_entry g_bc1_match5_equals_0[256], g_bc1_match6_equals_0[256]; // selector 0, allow equals hi/lo
-
- static void prepare_bc1_single_color_table(bc1_match_entry *pTable, const uint8_t *pExpand, int size, int sel)
+
+ static void prepare_bc1_single_color_table(bc1_match_entry* pTable, const uint8_t* pExpand, int size, int sel)
{
int total_e = 0;
@@ -942,7 +966,7 @@ namespace basist
{
const int lo_e = pExpand[lo], hi_e = pExpand[hi];
int e;
-
+
if (sel == 1)
{
// Selector 1
@@ -960,7 +984,7 @@ namespace basist
{
pTable[i].m_hi = static_cast(hi);
pTable[i].m_lo = static_cast(lo);
-
+
lowest_e = e;
}
@@ -972,7 +996,7 @@ namespace basist
}
#endif // BASISD_SUPPORT_DXT1
- #if BASISD_WRITE_NEW_DXT1_TABLES
+#if BASISD_WRITE_NEW_DXT1_TABLES
static void create_etc1_to_dxt1_5_conversion_table()
{
FILE* pFile = nullptr;
@@ -1224,7 +1248,7 @@ namespace basist
std::vector m_selectors_temp;
};
- static uint64_t pack_eac_a8_exhaustive(pack_eac_a8_results & results, const uint8_t * pPixels, uint32_t num_pixels)
+ static uint64_t pack_eac_a8_exhaustive(pack_eac_a8_results& results, const uint8_t* pPixels, uint32_t num_pixels)
{
results.m_selectors.resize(num_pixels);
results.m_selectors_temp.resize(num_pixels);
@@ -1601,7 +1625,7 @@ namespace basist
pack_eac_a8_results pack_results;
pack_eac_a8_exhaustive(pack_results, pixels, num_pixels);
- etc1_g_to_etc2_a8_conversion & c = s_etc1_g_to_etc2_a8[base + inten * 32][sel_range];
+ etc1_g_to_etc2_a8_conversion& c = s_etc1_g_to_etc2_a8[base + inten * 32][sel_range];
c.m_base = pack_results.m_base;
c.m_table_mul = pack_results.m_table * 16 + pack_results.m_multiplier;
@@ -1658,13 +1682,13 @@ namespace basist
bc1_expand5[i] = static_cast((i << 3) | (i >> 2));
prepare_bc1_single_color_table(g_bc1_match5_equals_1, bc1_expand5, 32, 1);
prepare_bc1_single_color_table(g_bc1_match5_equals_0, bc1_expand5, 32, 0);
-
+
uint8_t bc1_expand6[64];
for (int i = 0; i < 64; i++)
bc1_expand6[i] = static_cast((i << 2) | (i >> 4));
prepare_bc1_single_color_table(g_bc1_match6_equals_1, bc1_expand6, 64, 1);
prepare_bc1_single_color_table(g_bc1_match6_equals_0, bc1_expand6, 64, 0);
-
+
for (uint32_t i = 0; i < NUM_ETC1_TO_DXT1_SELECTOR_RANGES; i++)
{
uint32_t l = g_etc1_to_dxt1_selector_ranges[i].m_low;
@@ -1724,7 +1748,7 @@ namespace basist
}
#if BASISD_SUPPORT_DXT1
- static void convert_etc1s_to_dxt1(dxt1_block * pDst_block, const decoder_etc_block *pSrc_block, const selector * pSelector, bool use_threecolor_blocks)
+ static void convert_etc1s_to_dxt1(dxt1_block* pDst_block, const decoder_etc_block* pSrc_block, const selector* pSelector, bool use_threecolor_blocks)
{
#if !BASISD_WRITE_NEW_DXT1_TABLES
const uint32_t low_selector = pSelector->m_lo_selector;
@@ -1756,7 +1780,7 @@ namespace basist
// Make l > h
if (min16 > 0)
min16--;
- else
+ else
{
// l = h = 0
assert(min16 == max16 && max16 == 0);
@@ -1765,7 +1789,7 @@ namespace basist
min16 = 0;
mask = 0x55;
}
-
+
assert(max16 > min16);
}
@@ -1774,7 +1798,7 @@ namespace basist
std::swap(max16, min16);
mask ^= 0x55;
}
-
+
pDst_block->set_low_color(static_cast(max16));
pDst_block->set_high_color(static_cast(min16));
pDst_block->m_selectors[0] = static_cast(mask);
@@ -1810,28 +1834,28 @@ namespace basist
{
min16--;
- l = 0;
+ l = 0;
h = 0;
}
- else
+ else
{
// l = h = 0
assert(min16 == max16 && max16 == 0);
max16 = 1;
min16 = 0;
-
+
l = 1;
h = 1;
}
-
+
assert(max16 > min16);
}
if (max16 < min16)
{
std::swap(max16, min16);
- l = 1;
+ l = 1;
h = 0;
}
@@ -1854,9 +1878,9 @@ namespace basist
const uint32_t selector_range_table = g_etc1_to_dxt1_selector_range_index[low_selector][high_selector];
//[32][8][RANGES][MAPPING]
- const etc1_to_dxt1_56_solution *pTable_r = &g_etc1_to_dxt_5[(inten_table * 32 + base_color.r) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
- const etc1_to_dxt1_56_solution *pTable_g = &g_etc1_to_dxt_6[(inten_table * 32 + base_color.g) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
- const etc1_to_dxt1_56_solution *pTable_b = &g_etc1_to_dxt_5[(inten_table * 32 + base_color.b) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
+ const etc1_to_dxt1_56_solution* pTable_r = &g_etc1_to_dxt_5[(inten_table * 32 + base_color.r) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
+ const etc1_to_dxt1_56_solution* pTable_g = &g_etc1_to_dxt_6[(inten_table * 32 + base_color.g) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
+ const etc1_to_dxt1_56_solution* pTable_b = &g_etc1_to_dxt_5[(inten_table * 32 + base_color.b) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
uint32_t best_err = UINT_MAX;
uint32_t best_mapping = 0;
@@ -1870,7 +1894,7 @@ namespace basist
uint32_t l = dxt1_block::pack_unscaled_color(pTable_r[best_mapping].m_lo, pTable_g[best_mapping].m_lo, pTable_b[best_mapping].m_lo);
uint32_t h = dxt1_block::pack_unscaled_color(pTable_r[best_mapping].m_hi, pTable_g[best_mapping].m_hi, pTable_b[best_mapping].m_hi);
- const uint8_t *pSelectors_xlat = &g_etc1_to_dxt1_selector_mappings1[best_mapping][0];
+ const uint8_t* pSelectors_xlat = &g_etc1_to_dxt1_selector_mappings1[best_mapping][0];
if (l < h)
{
@@ -1892,7 +1916,7 @@ namespace basist
// Make l > h
if (h > 0)
h--;
- else
+ else
{
// l = h = 0
assert(l == h && h == 0);
@@ -1906,7 +1930,7 @@ namespace basist
pDst_block->set_low_color(static_cast(l));
pDst_block->set_high_color(static_cast(h));
}
-
+
pDst_block->m_selectors[0] = mask;
pDst_block->m_selectors[1] = mask;
pDst_block->m_selectors[2] = mask;
@@ -1980,6 +2004,25 @@ namespace basist
pDst_block->m_selectors[3] = (uint8_t)dxt1_sels3;
#endif
}
+#if BASISD_ENABLE_DEBUG_FLAGS
+ static void convert_etc1s_to_dxt1_vis(dxt1_block* pDst_block, const decoder_etc_block* pSrc_block, const selector* pSelector, bool use_threecolor_blocks)
+ {
+ convert_etc1s_to_dxt1(pDst_block, pSrc_block, pSelector, use_threecolor_blocks);
+ if (g_debug_flags & cDebugFlagVisBC1Sels)
+ {
+ uint32_t l = dxt1_block::pack_unscaled_color(31, 63, 31);
+ uint32_t h = dxt1_block::pack_unscaled_color(0, 0, 0);
+ pDst_block->set_low_color(static_cast(l));
+ pDst_block->set_high_color(static_cast(h));
+ }
+ else if (g_debug_flags & cDebugFlagVisBC1Endpoints)
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ pDst_block->set_selector(x, y, (y < 2) ? 0 : 1);
+ }
+ }
+#endif
#endif
static dxt_selector_range s_dxt5a_selector_ranges[] =
@@ -2354,7 +2397,7 @@ namespace basist
return 6;
}
- static uint32_t get_block_values8(color32 * pDst, uint32_t l, uint32_t h)
+ static uint32_t get_block_values8(color32* pDst, uint32_t l, uint32_t h)
{
pDst[0].a = static_cast(l);
pDst[1].a = static_cast(h);
@@ -2367,7 +2410,7 @@ namespace basist
return 8;
}
- static uint32_t get_block_values(color32 * pDst, uint32_t l, uint32_t h)
+ static uint32_t get_block_values(color32* pDst, uint32_t l, uint32_t h)
{
if (l > h)
return get_block_values8(pDst, l, h);
@@ -2376,7 +2419,7 @@ namespace basist
}
};
- static void convert_etc1s_to_dxt5a(dxt5a_block *pDst_block, const decoder_etc_block *pSrc_block, const selector *pSelector)
+ static void convert_etc1s_to_dxt5a(dxt5a_block* pDst_block, const decoder_etc_block* pSrc_block, const selector* pSelector)
{
const uint32_t low_selector = pSelector->m_lo_selector;
const uint32_t high_selector = pSelector->m_hi_selector;
@@ -2434,7 +2477,7 @@ namespace basist
if (selector_range_table >= NUM_DXT5A_SELECTOR_RANGES)
selector_range_table = 0;
- const etc1_g_to_dxt5a_conversion *pTable_entry = &g_etc1_g_to_dxt5a[base_color.r + inten_table * 32][selector_range_table];
+ const etc1_g_to_dxt5a_conversion* pTable_entry = &g_etc1_g_to_dxt5a[base_color.r + inten_table * 32][selector_range_table];
pDst_block->set_low_alpha(pTable_entry->m_lo);
pDst_block->set_high_alpha(pTable_entry->m_hi);
@@ -2649,7 +2692,7 @@ namespace basist
}
// accepts 5554 or 8888
- inline void set_endpoint(uint32_t endpoint_index, const color32 & c, bool opaque_endpoint, bool pack = false, uint32_t pack_round = 128)
+ inline void set_endpoint(uint32_t endpoint_index, const color32& c, bool opaque_endpoint, bool pack = false, uint32_t pack_round = 128)
{
assert(endpoint_index < 2);
const uint32_t m = m_endpoints & 1;
@@ -2817,7 +2860,7 @@ namespace basist
}
// TODO: Support decoding a non-pow2 ETC1S texture into the next larger pow2 PVRTC texture.
- static void fixup_pvrtc1_4_modulation(const decoder_etc_block *pETC_Blocks, const uint32_t *pPVRTC_endpoints, void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, bool pvrtc_wrap_addressing)
+ static void fixup_pvrtc1_4_modulation(const decoder_etc_block* pETC_Blocks, const uint32_t* pPVRTC_endpoints, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, bool pvrtc_wrap_addressing)
{
const uint32_t x_mask = num_blocks_x - 1;
const uint32_t y_mask = num_blocks_y - 1;
@@ -2874,15 +2917,14 @@ namespace basist
swizzled |= ((y >> min_bits) << (min_bits * 2));
}
-
- pvrtc4_block *pDst_block = static_cast(pDst_blocks) + swizzled;
+ pvrtc4_block* pDst_block = static_cast(pDst_blocks) + swizzled;
pDst_block->m_endpoints = pPVRTC_endpoints[block_index];
uint32_t base_r = g_etc_5_to_8[src_block.m_differential.m_red1];
uint32_t base_g = g_etc_5_to_8[src_block.m_differential.m_green1];
uint32_t base_b = g_etc_5_to_8[src_block.m_differential.m_blue1];
- const int *pInten_table48 = g_etc1_inten_tables48[src_block.m_differential.m_cw1];
+ const int* pInten_table48 = g_etc1_inten_tables48[src_block.m_differential.m_cw1];
int by = (base_r + base_g + base_b) * 16;
int block_colors_y_x16[4];
block_colors_y_x16[0] = by + pInten_table48[2];
@@ -3045,12 +3087,12 @@ namespace basist
};
};
- static void convert_etc1s_to_bc7_m6(bc7_mode_6 * pDst_block, const decoder_etc_block * pSrc_block, const selector * pSelector)
+ static void convert_etc1s_to_bc7_m6(bc7_mode_6* pDst_block, const decoder_etc_block* pSrc_block, const selector* pSelector)
{
#if !BASISD_WRITE_NEW_BC7_TABLES
const uint32_t low_selector = pSelector->m_lo_selector;
const uint32_t high_selector = pSelector->m_hi_selector;
-
+
const uint32_t inten_table = pSrc_block->m_differential.m_cw1;
const uint32_t base_color_r = pSrc_block->m_differential.m_red1;
const uint32_t base_color_g = pSrc_block->m_differential.m_green1;
@@ -3079,7 +3121,7 @@ namespace basist
const uint32_t b1 = block_colors[high_selector].b;
const uint32_t low_bits1 = (r1 & 1) + (g1 & 1) + (b1 & 1);
uint32_t p1 = low_bits1 >= 2;
-
+
pDst_block->m_lo.m_r0 = r0 >> 1;
pDst_block->m_lo.m_g0 = g0 >> 1;
pDst_block->m_lo.m_b0 = b0 >> 1;
@@ -3088,7 +3130,7 @@ namespace basist
pDst_block->m_lo.m_r1 = r1 >> 1;
pDst_block->m_lo.m_g1 = g1 >> 1;
pDst_block->m_lo.m_b1 = b1 >> 1;
-
+
uint32_t output_low_selector = 0;
uint32_t output_bit_offset = 1;
uint64_t output_hi_bits = p1;
@@ -3099,7 +3141,7 @@ namespace basist
{
uint32_t s = pSrc_block->get_selector(x, y);
uint32_t os = (s == low_selector) ? output_low_selector : (15 ^ output_low_selector);
-
+
uint32_t num_bits = 4;
if ((x | y) == 0)
@@ -3118,7 +3160,7 @@ namespace basist
output_hi_bits &= ~1ULL;
output_hi_bits |= p0;
std::swap(p0, p1);
-
+
output_low_selector = 15;
os = 0;
}
@@ -3132,12 +3174,12 @@ namespace basist
}
pDst_block->m_hi_bits = output_hi_bits;
-
+
assert(pDst_block->m_hi.m_p1 == p1);
-
+
return;
}
-
+
uint32_t selector_range_table = g_etc1_to_bc7_m6_selector_range_index[low_selector][high_selector];
const uint32_t* pTable_r = g_etc1_to_bc7_m6_table[base_color_r + inten_table * 32] + (selector_range_table * NUM_ETC1_TO_BC7_M6_SELECTOR_MAPPINGS);
@@ -3253,7 +3295,7 @@ namespace basist
#endif
#if BASISD_SUPPORT_ETC2_EAC_A8
- static void convert_etc1s_to_etc2_eac_a8(eac_a8_block * pDst_block, const decoder_etc_block * pSrc_block, const selector * pSelector)
+ static void convert_etc1s_to_etc2_eac_a8(eac_a8_block* pDst_block, const decoder_etc_block* pSrc_block, const selector* pSelector)
{
const uint32_t low_selector = pSelector->m_lo_selector;
const uint32_t high_selector = pSelector->m_hi_selector;
@@ -3289,7 +3331,7 @@ namespace basist
if (selector_range_table >= NUM_ETC2_EAC_A8_SELECTOR_RANGES)
selector_range_table = 0;
- const etc1_g_to_etc2_a8_conversion *pTable_entry = &s_etc1_g_to_etc2_a8[base_color.r + inten_table * 32][selector_range_table];
+ const etc1_g_to_etc2_a8_conversion* pTable_entry = &s_etc1_g_to_etc2_a8[base_color.r + inten_table * 32][selector_range_table];
pDst_block->m_base = pTable_entry->m_base;
pDst_block->m_table = pTable_entry->m_table_mul >> 4;
@@ -3315,15 +3357,15 @@ namespace basist
}
#endif // BASISD_SUPPORT_ETC2_EAC_A8
- basisu_lowlevel_transcoder::basisu_lowlevel_transcoder(const etc1_global_selector_codebook * pGlobal_sel_codebook) :
+ basisu_lowlevel_transcoder::basisu_lowlevel_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
m_pGlobal_sel_codebook(pGlobal_sel_codebook),
m_selector_history_buf_size(0)
{
}
bool basisu_lowlevel_transcoder::decode_palettes(
- uint32_t num_endpoints, const uint8_t * pEndpoints_data, uint32_t endpoints_data_size,
- uint32_t num_selectors, const uint8_t * pSelectors_data, uint32_t selectors_data_size)
+ uint32_t num_endpoints, const uint8_t* pEndpoints_data, uint32_t endpoints_data_size,
+ uint32_t num_selectors, const uint8_t* pSelectors_data, uint32_t selectors_data_size)
{
bitwise_decoder sym_codec;
@@ -3331,53 +3373,53 @@ namespace basist
if (!sym_codec.init(pEndpoints_data, endpoints_data_size))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 0\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 0\n");
return false;
}
-
+
if (!sym_codec.read_huffman_table(color5_delta_model0))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 1\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 1\n");
return false;
}
if (!sym_codec.read_huffman_table(color5_delta_model1))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 1a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 1a\n");
return false;
}
if (!sym_codec.read_huffman_table(color5_delta_model2))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2a\n");
return false;
}
-
+
if (!sym_codec.read_huffman_table(inten_delta_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2b\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2b\n");
return false;
}
if (!color5_delta_model0.is_valid() || !color5_delta_model1.is_valid() || !color5_delta_model2.is_valid() || !inten_delta_model.is_valid())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2b\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2b\n");
return false;
}
const bool endpoints_are_grayscale = sym_codec.get_bits(1) != 0;
-
+
m_endpoints.resize(num_endpoints);
color32 prev_color5(16, 16, 16, 0);
uint32_t prev_inten = 0;
-
+
for (uint32_t i = 0; i < num_endpoints; i++)
{
uint32_t inten_delta = sym_codec.decode_huffman(inten_delta_model);
m_endpoints[i].m_inten5 = static_cast((inten_delta + prev_inten) & 7);
prev_inten = m_endpoints[i].m_inten5;
-
+
for (uint32_t c = 0; c < (endpoints_are_grayscale ? 1U : 3U); c++)
{
int delta;
@@ -3389,7 +3431,7 @@ namespace basist
delta = sym_codec.decode_huffman(color5_delta_model2);
int v = (prev_color5[c] + delta) & 31;
-
+
m_endpoints[i].m_color5[c] = static_cast(v);
prev_color5[c] = static_cast(v);
@@ -3401,14 +3443,14 @@ namespace basist
m_endpoints[i].m_color5[2] = m_endpoints[i].m_color5[0];
}
}
-
+
sym_codec.stop();
m_selectors.resize(num_selectors);
if (!sym_codec.init(pSelectors_data, selectors_data_size))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 5\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 5\n");
return false;
}
@@ -3427,16 +3469,16 @@ namespace basist
{
if (!sym_codec.read_huffman_table(mod_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 6\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 6\n");
return false;
}
if (!mod_model.is_valid())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 6a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 6a\n");
return false;
}
}
-
+
for (uint32_t i = 0; i < num_selectors; i++)
{
uint32_t pal_index = 0;
@@ -3446,10 +3488,10 @@ namespace basist
uint32_t mod_index = 0;
if (mod_bits)
mod_index = sym_codec.decode_huffman(mod_model);
-
+
if (pal_index >= m_pGlobal_sel_codebook->size())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7z\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7z\n");
return false;
}
@@ -3459,7 +3501,7 @@ namespace basist
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
m_selectors[i].set_selector(x, y, e[x + y * 4]);
-
+
m_selectors[i].init_flags();
}
}
@@ -3475,12 +3517,12 @@ namespace basist
basist::huffman_decoding_table uses_global_cb_bitflags_model;
if (!sym_codec.read_huffman_table(uses_global_cb_bitflags_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7\n");
return false;
}
if (!uses_global_cb_bitflags_model.is_valid())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7a\n");
return false;
}
@@ -3489,19 +3531,19 @@ namespace basist
{
if (!sym_codec.read_huffman_table(global_mod_indices_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8\n");
return false;
}
if (!global_mod_indices_model.is_valid())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8a\n");
return false;
}
}
uint32_t cur_uses_global_cb_bitflags = 0;
uint32_t uses_global_cb_bitflags_remaining = 0;
-
+
for (uint32_t q = 0; q < num_selectors; q++)
{
if (!uses_global_cb_bitflags_remaining)
@@ -3522,7 +3564,7 @@ namespace basist
if (pal_index >= m_pGlobal_sel_codebook->size())
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8b\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8b\n");
return false;
}
@@ -3569,18 +3611,18 @@ namespace basist
{
if (!sym_codec.read_huffman_table(delta_selector_pal_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 10\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 10\n");
return false;
}
if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 10a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 10a\n");
return false;
}
uint8_t prev_bytes[4] = { 0, 0, 0, 0 };
-
+
for (uint32_t i = 0; i < num_selectors; i++)
{
if (!i)
@@ -3589,7 +3631,7 @@ namespace basist
{
uint32_t cur_byte = sym_codec.get_bits(8);
prev_bytes[j] = static_cast(cur_byte);
-
+
for (uint32_t k = 0; k < 4; k++)
m_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
@@ -3618,124 +3660,144 @@ namespace basist
return true;
}
- bool basisu_lowlevel_transcoder::decode_tables(const uint8_t * pTable_data, uint32_t table_data_size)
+ bool basisu_lowlevel_transcoder::decode_tables(const uint8_t* pTable_data, uint32_t table_data_size)
{
basist::bitwise_decoder sym_codec;
if (!sym_codec.init(pTable_data, table_data_size))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 0\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 0\n");
return false;
}
if (!sym_codec.read_huffman_table(m_endpoint_pred_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1\n");
return false;
}
-
+
if (m_endpoint_pred_model.get_code_sizes().size() == 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1a\n");
return false;
}
if (!sym_codec.read_huffman_table(m_delta_endpoint_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 2\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 2\n");
return false;
}
if (m_delta_endpoint_model.get_code_sizes().size() == 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 2a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 2a\n");
return false;
}
if (!sym_codec.read_huffman_table(m_selector_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 3\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 3\n");
return false;
}
if (m_selector_model.get_code_sizes().size() == 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 3a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 3a\n");
return false;
}
if (!sym_codec.read_huffman_table(m_selector_history_buf_rle_model))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 4\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 4\n");
return false;
}
if (m_selector_history_buf_rle_model.get_code_sizes().size() == 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 4a\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 4a\n");
return false;
}
m_selector_history_buf_size = sym_codec.get_bits(13);
-
+
sym_codec.stop();
return true;
}
-
- bool basisu_lowlevel_transcoder::transcode_slice(void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t *pImage_data, uint32_t image_data_size, block_format fmt,
- uint32_t output_block_stride_in_bytes, bool pvrtc_wrap_addressing, bool bc1_allow_threecolor_blocks, uint32_t output_row_pitch_in_blocks)
+
+ bool basisu_lowlevel_transcoder::transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
+ uint32_t output_block_stride_in_bytes, bool pvrtc_wrap_addressing, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks, basisu_transcoder_state* pState)
{
+ if (!pState)
+ pState = &m_def_state;
+
+ const bool is_video = (header.m_tex_type == cBASISTexTypeVideoFrames);
const uint32_t total_blocks = num_blocks_x * num_blocks_y;
if (!output_row_pitch_in_blocks)
output_row_pitch_in_blocks = num_blocks_x;
+
+ std::vector* pPrev_frame_indices = nullptr;
+ if (is_video)
+ {
+ // TODO: Add check to make sure the caller hasn't tried skipping past p-frames
+ const bool alpha_flag = (slice_desc.m_flags & cSliceDescFlagsIsAlphaData) != 0;
+ const uint32_t level_index = slice_desc.m_level_index;
+
+ if (level_index >= basisu_transcoder_state::cMaxPrevFrameLevels)
+ {
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: unsupported level_index\n");
+ return false;
+ }
+
+ pPrev_frame_indices = &pState->m_prev_frame_indices[alpha_flag][level_index];
+ if (pPrev_frame_indices->size() < total_blocks)
+ pPrev_frame_indices->resize(total_blocks);
+ }
basist::bitwise_decoder sym_codec;
-
+
if (!sym_codec.init(pImage_data, image_data_size))
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: sym_codec.init failed\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: sym_codec.init failed\n");
return false;
}
-
+
approx_move_to_front selector_history_buf(m_selector_history_buf_size);
-
- int prev_selector_index = 0;
const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = (uint32_t)m_selectors.size();
const uint32_t SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX = m_selector_history_buf_size + SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX;
uint32_t cur_selector_rle_count = 0;
-
+
decoder_etc_block block;
memset(&block, 0, sizeof(block));
block.set_flip_bit(true);
block.set_diff_bit(true);
- void *pPVRTC_work_mem = nullptr;
- uint32_t *pPVRTC_endpoints = nullptr;
+ void* pPVRTC_work_mem = nullptr;
+ uint32_t* pPVRTC_endpoints = nullptr;
if (fmt == cPVRTC1_4_OPAQUE_ONLY)
{
pPVRTC_work_mem = malloc(num_blocks_x * num_blocks_y * (sizeof(decoder_etc_block) + sizeof(uint32_t)));
if (!pPVRTC_work_mem)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: malloc failed\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: malloc failed\n");
return false;
}
- pPVRTC_endpoints = (uint32_t *)&((decoder_etc_block*)pPVRTC_work_mem)[num_blocks_x * num_blocks_y];
+ pPVRTC_endpoints = (uint32_t*) & ((decoder_etc_block*)pPVRTC_work_mem)[num_blocks_x * num_blocks_y];
}
-
- if (m_block_endpoint_preds[0].size() < num_blocks_x)
+
+ if (pState->m_block_endpoint_preds[0].size() < num_blocks_x)
{
- m_block_endpoint_preds[0].resize(num_blocks_x);
- m_block_endpoint_preds[1].resize(num_blocks_x);
+ pState->m_block_endpoint_preds[0].resize(num_blocks_x);
+ pState->m_block_endpoint_preds[1].resize(num_blocks_x);
}
uint32_t cur_pred_bits = 0;
int prev_endpoint_pred_sym = 0;
int endpoint_pred_repeat_count = 0;
uint32_t prev_endpoint_index = 0;
-
+
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
{
const uint32_t cur_block_endpoint_pred_array = block_y & 1;
@@ -3758,7 +3820,7 @@ namespace basist
if (cur_pred_bits == ENDPOINT_PRED_REPEAT_LAST_SYMBOL)
{
endpoint_pred_repeat_count = sym_codec.decode_vlc(ENDPOINT_PRED_COUNT_VLC_BITS) + ENDPOINT_PRED_MIN_REPEAT_COUNT - 1;
-
+
cur_pred_bits = prev_endpoint_pred_sym;
}
else
@@ -3767,16 +3829,16 @@ namespace basist
}
}
- m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x].m_pred_bits = (uint8_t)(cur_pred_bits >> 4);
- }
- else
- {
- cur_pred_bits = m_block_endpoint_preds[cur_block_endpoint_pred_array][block_x].m_pred_bits;
- }
+ pState->m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x].m_pred_bits = (uint8_t)(cur_pred_bits >> 4);
+ }
+ else
+ {
+ cur_pred_bits = pState->m_block_endpoint_preds[cur_block_endpoint_pred_array][block_x].m_pred_bits;
+ }
}
// Decode endpoint index
- uint32_t endpoint_index;
+ uint32_t endpoint_index, selector_index = 0;
const uint32_t pred = cur_pred_bits & 3;
cur_pred_bits >>= 2;
@@ -3786,7 +3848,7 @@ namespace basist
// Left
if (!block_x)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (0)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (0)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
@@ -3799,26 +3861,36 @@ namespace basist
// Upper
if (!block_y)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (1)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (1)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
}
- endpoint_index = m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x].m_endpoint_index;
+ endpoint_index = pState->m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x].m_endpoint_index;
}
else if (pred == 2)
{
- // Upper left
- if ((!block_x) || (!block_y))
+ if (is_video)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (2)\n");
- if (pPVRTC_work_mem)
- free(pPVRTC_work_mem);
- return false;
+ assert(pred == CR_ENDPOINT_PRED_INDEX);
+ endpoint_index = (*pPrev_frame_indices)[block_x + block_y * num_blocks_x];
+ selector_index = endpoint_index >> 16;
+ endpoint_index &= 0xFFFFU;
}
+ else
+ {
+ // Upper left
+ if ((!block_x) || (!block_y))
+ {
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (2)\n");
+ if (pPVRTC_work_mem)
+ free(pPVRTC_work_mem);
+ return false;
+ }
- endpoint_index = m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x - 1].m_endpoint_index;
+ endpoint_index = pState->m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x - 1].m_endpoint_index;
+ }
}
else
{
@@ -3830,94 +3902,109 @@ namespace basist
endpoint_index -= (int)m_endpoints.size();
}
- m_block_endpoint_preds[cur_block_endpoint_pred_array][block_x].m_endpoint_index = (uint16_t)endpoint_index;
+ pState->m_block_endpoint_preds[cur_block_endpoint_pred_array][block_x].m_endpoint_index = (uint16_t)endpoint_index;
prev_endpoint_index = endpoint_index;
-
+
// Decode selector index
- uint32_t selector_index;
- int selector_sym;
- if (cur_selector_rle_count > 0)
+ if ((!is_video) || (pred != CR_ENDPOINT_PRED_INDEX))
{
- cur_selector_rle_count--;
-
- selector_sym = (int)m_selectors.size();
- }
- else
- {
- selector_sym = sym_codec.decode_huffman(m_selector_model);
-
- if (selector_sym == static_cast(SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX))
+ int selector_sym;
+ if (cur_selector_rle_count > 0)
{
- int run_sym = sym_codec.decode_huffman(m_selector_history_buf_rle_model);
+ cur_selector_rle_count--;
- if (run_sym == (SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))
- cur_selector_rle_count = sym_codec.decode_vlc(7) + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
- else
- cur_selector_rle_count = run_sym + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
+ selector_sym = (int)m_selectors.size();
+ }
+ else
+ {
+ selector_sym = sym_codec.decode_huffman(m_selector_model);
- if (cur_selector_rle_count > total_blocks)
+ if (selector_sym == static_cast(SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX))
+ {
+ int run_sym = sym_codec.decode_huffman(m_selector_history_buf_rle_model);
+
+ if (run_sym == (SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))
+ cur_selector_rle_count = sym_codec.decode_vlc(7) + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
+ else
+ cur_selector_rle_count = run_sym + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
+
+ if (cur_selector_rle_count > total_blocks)
+ {
+ // The file is corrupted or we've got a bug.
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (3)\n");
+ if (pPVRTC_work_mem)
+ free(pPVRTC_work_mem);
+ return false;
+ }
+
+ selector_sym = (int)m_selectors.size();
+
+ cur_selector_rle_count--;
+ }
+ }
+
+ if (selector_sym >= (int)m_selectors.size())
+ {
+ assert(m_selector_history_buf_size > 0);
+
+ int history_buf_index = selector_sym - (int)m_selectors.size();
+
+ if (history_buf_index >= (int)selector_history_buf.size())
{
// The file is corrupted or we've got a bug.
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (3)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (4)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
}
- selector_sym = (int)m_selectors.size();
+ selector_index = selector_history_buf[history_buf_index];
- cur_selector_rle_count--;
+ if (history_buf_index != 0)
+ selector_history_buf.use(history_buf_index);
}
- }
-
- if (selector_sym >= (int)m_selectors.size())
- {
- assert(m_selector_history_buf_size > 0);
-
- int history_buf_index = selector_sym - (int)m_selectors.size();
-
- if (history_buf_index >= (int)selector_history_buf.size())
+ else
{
- // The file is corrupted or we've got a bug.
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (4)\n");
- if (pPVRTC_work_mem)
- free(pPVRTC_work_mem);
- return false;
+ selector_index = selector_sym;
+
+ if (m_selector_history_buf_size)
+ selector_history_buf.add(selector_index);
}
-
- selector_index = selector_history_buf[history_buf_index];
-
- if (history_buf_index != 0)
- selector_history_buf.use(history_buf_index);
}
- else
- {
- selector_index = selector_sym;
-
- if (m_selector_history_buf_size)
- selector_history_buf.add(selector_index);
- }
-
- prev_selector_index = selector_index;
if ((endpoint_index >= m_endpoints.size()) || (selector_index >= m_selectors.size()))
{
// The file is corrupted or we've got a bug.
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (5)\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (5)\n");
if (pPVRTC_work_mem)
free(pPVRTC_work_mem);
return false;
}
- const endpoint *pEndpoint0 = &m_endpoints[endpoint_index];
-
+ if (is_video)
+ (*pPrev_frame_indices)[block_x + block_y * num_blocks_x] = endpoint_index | (selector_index << 16);
+
+#if BASISD_ENABLE_DEBUG_FLAGS
+ if ((g_debug_flags & cDebugFlagVisCRs) && ((fmt == cETC1) || (fmt == cBC1)))
+ {
+ if ((is_video) && (pred == 2))
+ {
+ decoder_etc_block* pDst_block = reinterpret_cast(static_cast(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks) * output_block_stride_in_bytes);
+ memset(pDst_block, 0xFF, 8);
+ continue;
+ }
+ }
+#endif
+
+ const endpoint* pEndpoint0 = &m_endpoints[endpoint_index];
+
block.set_base5_color(decoder_etc_block::pack_color5(pEndpoint0->m_color5, false));
block.set_inten_table(0, pEndpoint0->m_inten5);
block.set_inten_table(1, pEndpoint0->m_inten5);
- const selector *pSelector = &m_selectors[selector_index];
+ const selector* pSelector = &m_selectors[selector_index];
switch (fmt)
{
@@ -3935,7 +4022,12 @@ namespace basist
void* pDst_block = static_cast(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks) * output_block_stride_in_bytes;
#if BASISD_SUPPORT_DXT1
- convert_etc1s_to_dxt1(static_cast(pDst_block), &block, pSelector, bc1_allow_threecolor_blocks);
+#if BASISD_ENABLE_DEBUG_FLAGS
+ if (g_debug_flags & (cDebugFlagVisBC1Sels | cDebugFlagVisBC1Endpoints))
+ convert_etc1s_to_dxt1_vis(static_cast(pDst_block), &block, pSelector, bc1_allow_threecolor_blocks);
+ else
+#endif
+ convert_etc1s_to_dxt1(static_cast(pDst_block), &block, pSelector, bc1_allow_threecolor_blocks);
#else
assert(0);
#endif
@@ -3991,7 +4083,7 @@ namespace basist
{
#if BASISD_SUPPORT_BC7
block.set_raw_selector_bits(pSelector->m_bytes[0], pSelector->m_bytes[1], pSelector->m_bytes[2], pSelector->m_bytes[3]);
-
+
void* pDst_block = static_cast(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks) * output_block_stride_in_bytes;
convert_etc1s_to_bc7_m6(static_cast(pDst_block), &block, pSelector);
#else
@@ -4023,12 +4115,12 @@ namespace basist
if (endpoint_pred_repeat_count != 0)
{
- BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: endpoint_pred_repeat_count != 0. The file is corrupted or this is a bug\n");
+ BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: endpoint_pred_repeat_count != 0. The file is corrupted or this is a bug\n");
return false;
}
//assert(endpoint_pred_repeat_count == 0);
-
+
if (fmt == cPVRTC1_4_OPAQUE_ONLY)
{
// PVRTC post process - create per-pixel modulation values.
@@ -4043,9 +4135,7 @@ namespace basist
return true;
}
- basisu_transcoder::basisu_transcoder(const etc1_global_selector_codebook * pGlobal_sel_codebook) :
- m_pFile_data(NULL),
- m_file_data_size(0),
+ basisu_transcoder::basisu_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
m_lowlevel_decoder(pGlobal_sel_codebook)
{
}
@@ -4055,12 +4145,12 @@ namespace basist
if (!validate_header(pData, data_size))
return false;
- const basis_file_header *pHeader = reinterpret_cast(pData);
+ const basis_file_header* pHeader = reinterpret_cast(pData);
#if !BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS
if (crc16(&pHeader->m_data_size, sizeof(basis_file_header) - BASISU_OFFSETOF(basis_file_header, m_data_size), 0) != pHeader->m_header_crc16)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header CRC check failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header CRC check failed\n");
return false;
}
@@ -4068,7 +4158,7 @@ namespace basist
{
if (crc16(reinterpret_cast(pData) + sizeof(basis_file_header), pHeader->m_data_size, 0) != pHeader->m_data_crc16)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: data CRC check failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: data CRC check failed\n");
return false;
}
}
@@ -4076,24 +4166,24 @@ namespace basist
return true;
}
-
+
bool basisu_transcoder::validate_header_quick(const void* pData, uint32_t data_size) const
{
if (data_size <= sizeof(basis_file_header))
return false;
- const basis_file_header *pHeader = reinterpret_cast(pData);
+ const basis_file_header* pHeader = reinterpret_cast(pData);
if ((pHeader->m_sig != basis_file_header::cBASISSigValue) || (pHeader->m_ver != BASISD_SUPPORTED_BASIS_VERSION) || (pHeader->m_header_size != sizeof(basis_file_header)))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header has an invalid signature, or file version is unsupported\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header has an invalid signature, or file version is unsupported\n");
return false;
}
uint32_t expected_file_size = sizeof(basis_file_header) + pHeader->m_data_size;
if (data_size < expected_file_size)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: source buffer is too small\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: source buffer is too small\n");
return false;
}
@@ -4103,14 +4193,14 @@ namespace basist
return false;
}
- if ( (pHeader->m_slice_desc_file_ofs >= data_size) ||
- ((data_size - pHeader->m_slice_desc_file_ofs) < (sizeof(basis_slice_desc) * pHeader->m_total_slices))
+ if ((pHeader->m_slice_desc_file_ofs >= data_size) ||
+ ((data_size - pHeader->m_slice_desc_file_ofs) < (sizeof(basis_slice_desc) * pHeader->m_total_slices))
)
{
BASISU_DEVEL_ERROR("basisu_transcoder::validate_header_quick: passed in buffer is too small or data is corrupted\n");
return false;
}
-
+
return true;
}
@@ -4118,34 +4208,34 @@ namespace basist
{
if (data_size <= sizeof(basis_file_header))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: input source buffer is too small\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: input source buffer is too small\n");
return false;
}
- const basis_file_header *pHeader = reinterpret_cast(pData);
+ const basis_file_header* pHeader = reinterpret_cast(pData);
if ((pHeader->m_sig != basis_file_header::cBASISSigValue) || (pHeader->m_ver != BASISD_SUPPORTED_BASIS_VERSION) || (pHeader->m_header_size != sizeof(basis_file_header)))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header has an invalid signature, or file version is unsupported\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header has an invalid signature, or file version is unsupported\n");
return false;
}
uint32_t expected_file_size = sizeof(basis_file_header) + pHeader->m_data_size;
if (data_size < expected_file_size)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: input source buffer is too small, or header is corrupted\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: input source buffer is too small, or header is corrupted\n");
return false;
}
if ((!pHeader->m_total_images) || (!pHeader->m_total_slices))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (total images or slices are 0)\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (total images or slices are 0)\n");
return false;
}
if (pHeader->m_total_images > pHeader->m_total_slices)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (too many images)\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (too many images)\n");
return false;
}
@@ -4153,7 +4243,7 @@ namespace basist
{
if (pHeader->m_total_slices & 1)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid alpha basis file\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid alpha basis file\n");
return false;
}
}
@@ -4161,12 +4251,12 @@ namespace basist
if ((pHeader->m_flags & cBASISHeaderFlagETC1S) == 0)
{
// We only support ETC1S in basis universal
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (ETC1S flag check)\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: invalid basis file (ETC1S flag check)\n");
return false;
}
- if ( (pHeader->m_slice_desc_file_ofs >= data_size) ||
- ((data_size - pHeader->m_slice_desc_file_ofs) < (sizeof(basis_slice_desc) * pHeader->m_total_slices))
+ if ((pHeader->m_slice_desc_file_ofs >= data_size) ||
+ ((data_size - pHeader->m_slice_desc_file_ofs) < (sizeof(basis_slice_desc) * pHeader->m_total_slices))
)
{
BASISU_DEVEL_ERROR("basisu_transcoder::validate_header_quick: passed in buffer is too small or data is corrupted\n");
@@ -4176,18 +4266,18 @@ namespace basist
return true;
}
- basis_texture_type basisu_transcoder::get_texture_type(const void *pData, uint32_t data_size) const
+ basis_texture_type basisu_transcoder::get_texture_type(const void* pData, uint32_t data_size) const
{
if (!validate_header_quick(pData, data_size))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_texture_type: header validation failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_texture_type: header validation failed\n");
return cBASISTexType2DArray;
}
- const basis_file_header *pHeader = static_cast(pData);
+ const basis_file_header* pHeader = static_cast(pData);
basis_texture_type btt = static_cast(static_cast(pHeader->m_tex_type));
-
+
if (btt >= cBASISTexTypeTotal)
{
BASISU_DEVEL_ERROR("basisu_transcoder::validate_header_quick: header's texture type field is invalid\n");
@@ -4197,77 +4287,78 @@ namespace basist
return btt;
}
- bool basisu_transcoder::get_userdata(const void *pData, uint32_t data_size, uint32_t &userdata0, uint32_t &userdata1) const
+ bool basisu_transcoder::get_userdata(const void* pData, uint32_t data_size, uint32_t& userdata0, uint32_t& userdata1) const
{
if (!validate_header_quick(pData, data_size))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_userdata: header validation failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_userdata: header validation failed\n");
return false;
}
- const basis_file_header *pHeader = static_cast(pData);
+ const basis_file_header* pHeader = static_cast(pData);
userdata0 = pHeader->m_userdata0;
userdata1 = pHeader->m_userdata1;
return true;
}
-
- uint32_t basisu_transcoder::get_total_images(const void *pData, uint32_t data_size) const
+
+ uint32_t basisu_transcoder::get_total_images(const void* pData, uint32_t data_size) const
{
if (!validate_header_quick(pData, data_size))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header validation failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_images: header validation failed\n");
return 0;
}
- const basis_file_header *pHeader = static_cast(pData);
+ const basis_file_header* pHeader = static_cast(pData);
return pHeader->m_total_images;
}
- bool basisu_transcoder::get_image_info(const void *pData, uint32_t data_size, basisu_image_info &image_info, uint32_t image_index) const
+ bool basisu_transcoder::get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const
{
if (!validate_header_quick(pData, data_size))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: header validation failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: header validation failed\n");
return false;
}
-
+
int slice_index = find_first_slice_index(pData, data_size, image_index, 0);
if (slice_index < 0)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid slice index\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid slice index\n");
return false;
}
- const basis_file_header *pHeader = static_cast(pData);
+ const basis_file_header* pHeader = static_cast(pData);
if (image_index >= pHeader->m_total_images)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid image_index\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid image_index\n");
return false;
}
- const basis_slice_desc *pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
+ const basis_slice_desc* pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
uint32_t total_levels = 1;
for (uint32_t i = slice_index + 1; i < pHeader->m_total_slices; i++)
if (pSlice_descs[i].m_image_index == image_index)
total_levels = basisu::maximum(total_levels, pSlice_descs[i].m_level_index + 1);
- else
+ else
break;
if (total_levels > 16)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid image_index\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid image_index\n");
return false;
}
- const basis_slice_desc &slice_desc = pSlice_descs[slice_index];
+ const basis_slice_desc& slice_desc = pSlice_descs[slice_index];
image_info.m_image_index = image_index;
image_info.m_total_levels = total_levels;
image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;
+ image_info.m_iframe_flag = (slice_desc.m_flags & cSliceDescFlagsFrameIsIFrame) != 0;
image_info.m_width = slice_desc.m_num_blocks_x * 4;
image_info.m_height = slice_desc.m_num_blocks_y * 4;
image_info.m_orig_width = slice_desc.m_orig_width;
@@ -4279,112 +4370,114 @@ namespace basist
return true;
}
-
- uint32_t basisu_transcoder::get_total_image_levels(const void *pData, uint32_t data_size, uint32_t image_index) const
+
+ uint32_t basisu_transcoder::get_total_image_levels(const void* pData, uint32_t data_size, uint32_t image_index) const
{
if (!validate_header_quick(pData, data_size))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: header validation failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: header validation failed\n");
return false;
}
int slice_index = find_first_slice_index(pData, data_size, image_index, 0);
if (slice_index < 0)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: failed finding slice\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: failed finding slice\n");
return false;
}
- const basis_file_header *pHeader = static_cast(pData);
+ const basis_file_header* pHeader = static_cast(pData);
if (image_index >= pHeader->m_total_images)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: invalid image_index\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: invalid image_index\n");
return false;
}
- const basis_slice_desc *pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
+ const basis_slice_desc* pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
uint32_t total_levels = 1;
for (uint32_t i = slice_index + 1; i < pHeader->m_total_slices; i++)
if (pSlice_descs[i].m_image_index == image_index)
total_levels = basisu::maximum(total_levels, pSlice_descs[i].m_level_index + 1);
- else
+ else
break;
- if (total_levels > 16)
+ const uint32_t cMaxSupportedLevels = 16;
+ if (total_levels > cMaxSupportedLevels)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: invalid image levels!\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_total_image_levels: invalid image levels!\n");
return false;
}
-
+
return total_levels;
}
-
- bool basisu_transcoder::get_image_level_desc(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, uint32_t &orig_width, uint32_t &orig_height, uint32_t &total_blocks) const
+
+ bool basisu_transcoder::get_image_level_desc(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, uint32_t& orig_width, uint32_t& orig_height, uint32_t& total_blocks) const
{
if (!validate_header_quick(pData, data_size))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: header validation failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: header validation failed\n");
return false;
}
-
+
int slice_index = find_first_slice_index(pData, data_size, image_index, level_index);
if (slice_index < 0)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: failed finding slice\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: failed finding slice\n");
return false;
}
- const basis_file_header *pHeader = static_cast(pData);
+ const basis_file_header* pHeader = static_cast(pData);
if (image_index >= pHeader->m_total_images)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: invalid image_index\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: invalid image_index\n");
return false;
}
- const basis_slice_desc *pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
-
- const basis_slice_desc &slice_desc = pSlice_descs[slice_index];
+ const basis_slice_desc* pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
+
+ const basis_slice_desc& slice_desc = pSlice_descs[slice_index];
orig_width = slice_desc.m_orig_width;
orig_height = slice_desc.m_orig_height;
total_blocks = slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;
-
+
return true;
}
- bool basisu_transcoder::get_image_level_info(const void *pData, uint32_t data_size, basisu_image_level_info &image_info, uint32_t image_index, uint32_t level_index) const
+ bool basisu_transcoder::get_image_level_info(const void* pData, uint32_t data_size, basisu_image_level_info& image_info, uint32_t image_index, uint32_t level_index) const
{
if (!validate_header_quick(pData, data_size))
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: validate_file_checksums failed\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: validate_file_checksums failed\n");
return false;
}
-
+
int slice_index = find_first_slice_index(pData, data_size, image_index, level_index);
if (slice_index < 0)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: failed finding slice\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: failed finding slice\n");
return false;
}
- const basis_file_header *pHeader = static_cast(pData);
+ const basis_file_header* pHeader = static_cast(pData);
if (image_index >= pHeader->m_total_images)
{
- BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: invalid image_index\n");
+ BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: invalid image_index\n");
return false;
}
- const basis_slice_desc *pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
+ const basis_slice_desc* pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
- const basis_slice_desc &slice_desc = pSlice_descs[slice_index];
+ const basis_slice_desc& slice_desc = pSlice_descs[slice_index];
image_info.m_image_index = image_index;
image_info.m_level_index = level_index;
image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;
+ image_info.m_iframe_flag = (slice_desc.m_flags & cSliceDescFlagsFrameIsIFrame) != 0;
image_info.m_width = slice_desc.m_num_blocks_x * 4;
image_info.m_height = slice_desc.m_num_blocks_y * 4;
image_info.m_orig_width = slice_desc.m_orig_width;
@@ -4397,17 +4490,17 @@ namespace basist
return true;
}
- bool basisu_transcoder::get_file_info(const void* pData, uint32_t data_size, basisu_file_info & file_info) const
+ bool basisu_transcoder::get_file_info(const void* pData, uint32_t data_size, basisu_file_info& file_info) const
{
if (!validate_file_checksums(pData, data_size, false))
{
BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: validate_file_checksums failed\n");
return false;
}
-
+
const basis_file_header* pHeader = static_cast(pData);
const basis_slice_desc* pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs);
-
+
file_info.m_version = pHeader->m_ver;
file_info.m_total_header_size = sizeof(basis_file_header) + pHeader->m_total_slices * sizeof(basis_slice_desc);
@@ -4441,7 +4534,7 @@ namespace basist
file_info.m_us_per_frame = pHeader->m_us_per_frame;
file_info.m_userdata0 = pHeader->m_userdata0;
file_info.m_userdata1 = pHeader->m_userdata1;
-
+
file_info.m_image_mipmap_levels.resize(0);
file_info.m_image_mipmap_levels.resize(pHeader->m_total_images);
@@ -4452,7 +4545,7 @@ namespace basist
file_info.m_slices_size += pSlice_descs[i].m_file_size;
basisu_slice_info& slice_info = file_info.m_slice_info[i];
-
+
slice_info.m_orig_width = pSlice_descs[i].m_orig_width;
slice_info.m_orig_height = pSlice_descs[i].m_orig_height;
slice_info.m_width = pSlice_descs[i].m_num_blocks_x * 4;
@@ -4466,6 +4559,7 @@ namespace basist
slice_info.m_level_index = pSlice_descs[i].m_level_index;
slice_info.m_unpacked_slice_crc16 = pSlice_descs[i].m_slice_data_crc16;
slice_info.m_alpha_flag = (pSlice_descs[i].m_flags & cSliceDescFlagsIsAlphaData) != 0;
+ slice_info.m_iframe_flag = (pSlice_descs[i].m_flags & cSliceDescFlagsFrameIsIFrame) != 0;
if (pSlice_descs[i].m_image_index >= pHeader->m_total_images)
{
@@ -4485,23 +4579,23 @@ namespace basist
return true;
}
- bool basisu_transcoder::start_transcoding(const void *pData, uint32_t data_size) const
+ bool basisu_transcoder::start_transcoding(const void* pData, uint32_t data_size) const
{
if (m_lowlevel_decoder.m_endpoints.size())
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: already called start_transcoding\n");
return true;
}
-
+
if (!validate_header_quick(pData, data_size))
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: header validation failed\n");
return false;
}
- const basis_file_header *pHeader = reinterpret_cast(pData);
+ const basis_file_header* pHeader = reinterpret_cast(pData);
- const uint8_t *pDataU8 = static_cast(pData);
+ const uint8_t* pDataU8 = static_cast(pData);
if (!pHeader->m_endpoint_cb_file_size || !pHeader->m_selector_cb_file_size || !pHeader->m_tables_file_size)
{
@@ -4513,7 +4607,7 @@ namespace basist
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: file is corrupted or passed in buffer too small (1)\n");
return false;
}
-
+
if (pHeader->m_endpoint_cb_file_size > (data_size - pHeader->m_endpoint_cb_file_ofs))
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: file is corrupted or passed in buffer too small (2)\n");
@@ -4549,31 +4643,31 @@ namespace basist
return true;
}
- bool basisu_transcoder::transcode_slice(const void *pData, uint32_t data_size, uint32_t slice_index, void *pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks, block_format fmt,
- uint32_t output_block_stride_in_bytes, uint32_t decode_flags, uint32_t output_row_pitch_in_blocks) const
+ bool basisu_transcoder::transcode_slice(const void* pData, uint32_t data_size, uint32_t slice_index, void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks, block_format fmt,
+ uint32_t output_block_stride_in_bytes, uint32_t decode_flags, uint32_t output_row_pitch_in_blocks, basisu_transcoder_state* pState) const
{
if (!m_lowlevel_decoder.m_endpoints.size())
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: must call start_transcoding first\n");
return false;
}
-
+
if (decode_flags & cDecodeFlagsPVRTCDecodeToNextPow2)
{
// TODO: Not yet supported
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: cDecodeFlagsPVRTCDecodeToNextPow2 currently unsupported\n");
return false;
}
-
+
if (!validate_header_quick(pData, data_size))
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: header validation failed\n");
return false;
}
-
- const basis_file_header *pHeader = reinterpret_cast(pData);
- const uint8_t *pDataU8 = static_cast(pData);
+ const basis_file_header* pHeader = reinterpret_cast(pData);
+
+ const uint8_t* pDataU8 = static_cast(pData);
if (slice_index >= pHeader->m_total_slices)
{
@@ -4581,7 +4675,7 @@ namespace basist
return false;
}
- const basis_slice_desc &slice_desc = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs)[slice_index];
+ const basis_slice_desc& slice_desc = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs)[slice_index];
uint32_t total_blocks = slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;
if (output_blocks_buf_size_in_blocks < total_blocks)
@@ -4615,34 +4709,34 @@ namespace basist
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: invalid slice_desc.m_file_size, or passed in buffer too small\n");
return false;
}
-
+
return m_lowlevel_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,
pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,
- fmt, output_block_stride_in_bytes, (decode_flags & cDecodeFlagsPVRTCWrapAddressing) != 0, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, output_row_pitch_in_blocks);
+ fmt, output_block_stride_in_bytes, (decode_flags & cDecodeFlagsPVRTCWrapAddressing) != 0, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc, output_row_pitch_in_blocks, pState);
}
- int basisu_transcoder::find_first_slice_index(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const
+ int basisu_transcoder::find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const
{
(void)data_size;
- const basis_file_header *pHeader = reinterpret_cast(pData);
- const uint8_t *pDataU8 = static_cast(pData);
+ const basis_file_header* pHeader = reinterpret_cast(pData);
+ const uint8_t* pDataU8 = static_cast(pData);
// For very large basis files this search could be painful
// TODO: Binary search this
for (uint32_t slice_iter = 0; slice_iter < pHeader->m_total_slices; slice_iter++)
{
- const basis_slice_desc &slice_desc = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs)[slice_iter];
+ const basis_slice_desc& slice_desc = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs)[slice_iter];
if ((slice_desc.m_image_index == image_index) && (slice_desc.m_level_index == level_index))
return slice_iter;
}
-
+
BASISU_DEVEL_ERROR("basisu_transcoder::find_first_slice_index: didn't find slice\n");
return -1;
}
- int basisu_transcoder::find_slice(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const
+ int basisu_transcoder::find_slice(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const
{
if (!validate_header_quick(pData, data_size))
{
@@ -4650,9 +4744,9 @@ namespace basist
return false;
}
- const basis_file_header *pHeader = reinterpret_cast(pData);
- const uint8_t *pDataU8 = static_cast(pData);
- const basis_slice_desc *pSlice_descs = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs);
+ const basis_file_header* pHeader = reinterpret_cast(pData);
+ const uint8_t* pDataU8 = static_cast(pData);
+ const basis_slice_desc* pSlice_descs = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs);
// For very large basis files this search could be painful
// TODO: Binary search this
@@ -4666,22 +4760,22 @@ namespace basist
return slice_iter;
}
}
-
+
BASISU_DEVEL_ERROR("basisu_transcoder::find_slice: didn't find slice\n");
return -1;
}
static void write_opaque_alpha_blocks(
- uint32_t num_blocks_x, uint32_t num_blocks_y,
- void *pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks, block_format fmt,
+ uint32_t num_blocks_x, uint32_t num_blocks_y,
+ void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks, block_format fmt,
uint32_t block_stride_in_bytes, uint32_t output_row_pitch_in_blocks)
{
BASISU_NOTE_UNUSED(output_blocks_buf_size_in_blocks);
if (!output_row_pitch_in_blocks)
output_row_pitch_in_blocks = num_blocks_x;
-
+
if (fmt == cETC2_EAC_A8)
{
#if BASISD_SUPPORT_ETC2_EAC_A8
@@ -4689,7 +4783,7 @@ namespace basist
blk.m_base = 255;
blk.m_multiplier = 1;
blk.m_table = 13;
-
+
// Selectors are all 4's
static const uint8_t s_etc2_eac_a8_sel4[6] = { 0x92, 0x49, 0x24, 0x92, 0x49, 0x24 };
memcpy(&blk.m_selectors, s_etc2_eac_a8_sel4, sizeof(s_etc2_eac_a8_sel4));
@@ -4699,7 +4793,7 @@ namespace basist
uint32_t dst_ofs = y * output_row_pitch_in_blocks * block_stride_in_bytes;
for (uint32_t x = 0; x < num_blocks_x; x++)
{
- memcpy((uint8_t *)pOutput_blocks + dst_ofs, &blk, sizeof(blk));
+ memcpy((uint8_t*)pOutput_blocks + dst_ofs, &blk, sizeof(blk));
dst_ofs += block_stride_in_bytes;
}
}
@@ -4712,33 +4806,33 @@ namespace basist
blk.m_endpoints[0] = 255;
blk.m_endpoints[1] = 255;
memset(blk.m_selectors, 0, sizeof(blk.m_selectors));
-
+
for (uint32_t y = 0; y < num_blocks_y; y++)
{
uint32_t dst_ofs = y * output_row_pitch_in_blocks * block_stride_in_bytes;
for (uint32_t x = 0; x < num_blocks_x; x++)
{
- memcpy((uint8_t *)pOutput_blocks + dst_ofs, &blk, sizeof(blk));
+ memcpy((uint8_t*)pOutput_blocks + dst_ofs, &blk, sizeof(blk));
dst_ofs += block_stride_in_bytes;
}
}
#endif
}
}
-
+
bool basisu_transcoder::transcode_image_level(
- const void *pData, uint32_t data_size,
- uint32_t image_index, uint32_t level_index,
- void *pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks,
+ const void* pData, uint32_t data_size,
+ uint32_t image_index, uint32_t level_index,
+ void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks,
transcoder_texture_format fmt,
- uint32_t decode_flags, uint32_t output_row_pitch_in_blocks) const
+ uint32_t decode_flags, uint32_t output_row_pitch_in_blocks, basisu_transcoder_state *pState) const
{
if (!m_lowlevel_decoder.m_endpoints.size())
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: must call start_transcoding() first\n");
return false;
}
-
+
const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
if (decode_flags & cDecodeFlagsPVRTCDecodeToNextPow2)
@@ -4754,14 +4848,14 @@ namespace basist
return false;
}
- const basis_file_header *pHeader = reinterpret_cast(pData);
+ const basis_file_header* pHeader = reinterpret_cast(pData);
- const uint8_t *pDataU8 = static_cast(pData);
+ const uint8_t* pDataU8 = static_cast(pData);
- const basis_slice_desc *pSlice_descs = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs);
+ const basis_slice_desc* pSlice_descs = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs);
const bool basis_file_has_alpha_slices = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0;
-
+
int slice_index = find_first_slice_index(pData, data_size, image_index, level_index);
if (slice_index < 0)
{
@@ -4769,7 +4863,7 @@ namespace basist
// Unable to find the requested image/level
return false;
}
-
+
uint32_t total_slices = 1;
switch (fmt)
{
@@ -4781,11 +4875,12 @@ namespace basist
default:
break;
}
-
+ (void)total_slices;
+
if (pSlice_descs[slice_index].m_flags & cSliceDescFlagsIsAlphaData)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file has out of order alpha slice\n");
-
+
// The first slice shouldn't have alpha data in a properly formed basis file
return false;
}
@@ -4807,7 +4902,7 @@ namespace basist
// This slice should have alpha data
return false;
}
-
+
if ((pSlice_descs[slice_index].m_num_blocks_x != pSlice_descs[slice_index + 1].m_num_blocks_x) || (pSlice_descs[slice_index].m_num_blocks_y != pSlice_descs[slice_index + 1].m_num_blocks_y))
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: alpha basis file slice dimensions bad\n");
@@ -4832,19 +4927,27 @@ namespace basist
bool status = false;
const uint32_t total_slice_blocks = pSlice_descs[slice_index].m_num_blocks_x * pSlice_descs[slice_index].m_num_blocks_y;
-
+
+ if ((fmt == cTFPVRTC1_4_OPAQUE_ONLY) && (output_blocks_buf_size_in_blocks > total_slice_blocks))
+ {
+ // The transcoder doesn't write beyond total_slice_blocks, so we need to clear the rest ourselves.
+ // For GL usage, PVRTC1 4bpp image size is (max(width, 8)* max(height, 8) * 4 + 7) / 8.
+ // However, for KTX and internally in Basis this formula isn't used, it's just ((width+3)/4) * ((height+3)/4) * bytes_per_block. This is all the transcoder actually writes to memory.
+ memset(static_cast(pOutput_blocks) + total_slice_blocks * bytes_per_block, 0, (output_blocks_buf_size_in_blocks - total_slice_blocks) * bytes_per_block);
+ }
+
switch (fmt)
{
case cTFETC1:
{
assert(total_slices == 1);
-
+
uint32_t slice_index_to_decode = slice_index;
// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
slice_index_to_decode++;
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cETC1, bytes_per_block, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cETC1, bytes_per_block, decode_flags, output_row_pitch_in_blocks, pState);
if (!status)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC1 failed\n");
@@ -4863,7 +4966,7 @@ namespace basist
if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
slice_index_to_decode++;
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC1, bytes_per_block, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC1, bytes_per_block, decode_flags, output_row_pitch_in_blocks, pState);
if (!status)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC1 failed\n");
@@ -4882,7 +4985,7 @@ namespace basist
if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
slice_index_to_decode++;
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC4, bytes_per_block, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC4, bytes_per_block, decode_flags, output_row_pitch_in_blocks, pState);
if (!status)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC4 failed\n");
@@ -4902,7 +5005,7 @@ namespace basist
slice_index_to_decode++;
// output_row_pitch_in_blocks is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cPVRTC1_4_OPAQUE_ONLY, bytes_per_block, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cPVRTC1_4_OPAQUE_ONLY, bytes_per_block, decode_flags, output_row_pitch_in_blocks, pState);
if (!status)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 opaque only failed\n");
@@ -4915,13 +5018,13 @@ namespace basist
return false;
#endif
assert(total_slices == 1);
-
+
uint32_t slice_index_to_decode = slice_index;
// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
slice_index_to_decode++;
- status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC7_M6_OPAQUE_ONLY, bytes_per_block, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC7_M6_OPAQUE_ONLY, bytes_per_block, decode_flags, output_row_pitch_in_blocks, pState);
if (!status)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC7 m6 opaque only failed\n");
@@ -4934,11 +5037,11 @@ namespace basist
return false;
#endif
assert(total_slices == 2);
-
+
if (basis_file_has_alpha_slices)
{
// First decode the alpha data
- status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks, cETC2_EAC_A8, 16, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks, cETC2_EAC_A8, 16, decode_flags, output_row_pitch_in_blocks, pState);
}
else
{
@@ -4949,7 +5052,7 @@ namespace basist
if (status)
{
// Now decode the color data
- status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks, cETC1, 16, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks, cETC1, 16, decode_flags, output_row_pitch_in_blocks, pState);
if (!status)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 RGB failed\n");
@@ -4974,7 +5077,7 @@ namespace basist
// First decode the alpha data
if (basis_file_has_alpha_slices)
{
- status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC4, 16, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC4, 16, decode_flags, output_row_pitch_in_blocks, pState);
}
else
{
@@ -4985,7 +5088,7 @@ namespace basist
if (status)
{
// Now decode the color data. Forbid 3 color blocks, which aren't allowed in BC3.
- status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks, cBC1, 16, decode_flags | cDecodeFlagsBC1ForbidThreeColorBlocks, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks, cBC1, 16, decode_flags | cDecodeFlagsBC1ForbidThreeColorBlocks, output_row_pitch_in_blocks, pState);
if (!status)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 RGB failed\n");
@@ -5005,13 +5108,13 @@ namespace basist
#endif
assert(total_slices == 2);
// Decode the R data (actually the green channel of the color data slice in the basis file)
- status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC4, 16, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks, cBC4, 16, decode_flags, output_row_pitch_in_blocks, pState);
if (status)
{
if (basis_file_has_alpha_slices)
{
// Decode the G data (actually the green channel of the alpha data slice in the basis file)
- status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks, cBC4, 16, decode_flags, output_row_pitch_in_blocks);
+ status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks, cBC4, 16, decode_flags, output_row_pitch_in_blocks, pState);
if (!status)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 1 failed\n");
@@ -5062,7 +5165,7 @@ namespace basist
return 0;
}
- const char *basis_get_format_name(transcoder_texture_format fmt)
+ const char* basis_get_format_name(transcoder_texture_format fmt)
{
switch (fmt)
{
@@ -5082,19 +5185,19 @@ namespace basist
return "";
}
- const char *basis_get_texture_type_name(basis_texture_type tex_type)
+ const char* basis_get_texture_type_name(basis_texture_type tex_type)
{
switch (tex_type)
{
- case cBASISTexType2D: return "2D";
- case cBASISTexType2DArray: return "2D array";
- case cBASISTexTypeCubemapArray: return "cubemap array";
- case cBASISTexTypeVideoFrames: return "video";
- case cBASISTexTypeVolume: return "3D";
- default:
- assert(0);
- BASISU_DEVEL_ERROR("basis_get_texture_type_name: Invalid tex_type\n");
- break;
+ case cBASISTexType2D: return "2D";
+ case cBASISTexType2DArray: return "2D array";
+ case cBASISTexTypeCubemapArray: return "cubemap array";
+ case cBASISTexTypeVideoFrames: return "video";
+ case cBASISTexTypeVolume: return "3D";
+ default:
+ assert(0);
+ BASISU_DEVEL_ERROR("basis_get_texture_type_name: Invalid tex_type\n");
+ break;
}
return "";
}
@@ -5103,8 +5206,8 @@ namespace basist
{
switch (fmt)
{
- case cTFETC2:
- case cTFBC3:
+ case cTFETC2:
+ case cTFBC3:
return true;
default:
break;
@@ -5116,18 +5219,18 @@ namespace basist
{
switch (fmt)
{
- case cTFETC1: return basisu::cETC1;
- case cTFBC1: return basisu::cBC1;
- case cTFBC4: return basisu::cBC4;
- case cTFPVRTC1_4_OPAQUE_ONLY: return basisu::cPVRTC1_4_RGB;
- case cTFBC7_M6_OPAQUE_ONLY: return basisu::cBC7;
- case cTFETC2: return basisu::cETC2_RGBA;
- case cTFBC3: return basisu::cBC3;
- case cTFBC5: return basisu::cBC5;
- default:
- assert(0);
- BASISU_DEVEL_ERROR("basis_get_basisu_texture_format: Invalid fmt\n");
- break;
+ case cTFETC1: return basisu::cETC1;
+ case cTFBC1: return basisu::cBC1;
+ case cTFBC4: return basisu::cBC4;
+ case cTFPVRTC1_4_OPAQUE_ONLY: return basisu::cPVRTC1_4_RGB;
+ case cTFBC7_M6_OPAQUE_ONLY: return basisu::cBC7;
+ case cTFETC2: return basisu::cETC2_RGBA;
+ case cTFBC3: return basisu::cBC3;
+ case cTFBC5: return basisu::cBC5;
+ default:
+ assert(0);
+ BASISU_DEVEL_ERROR("basis_get_basisu_texture_format: Invalid fmt\n");
+ break;
}
return basisu::cInvalidTextureFormat;
}
diff --git a/transcoder/basisu_transcoder.h b/transcoder/basisu_transcoder.h
index c70845a..bd5807d 100644
--- a/transcoder/basisu_transcoder.h
+++ b/transcoder/basisu_transcoder.h
@@ -59,6 +59,22 @@ namespace basist
const char *basis_get_texture_type_name(basis_texture_type tex_type);
class basisu_transcoder;
+
+ // This struct holds all state used during transcoding. For video, it needs to persist between image transcodes (it holds the previous frame).
+ // For threading you can use one state per thread.
+ struct basisu_transcoder_state
+ {
+ struct block_preds
+ {
+ uint16_t m_endpoint_index;
+ uint8_t m_pred_bits;
+ };
+
+ std::vector m_block_endpoint_preds[2];
+
+ enum { cMaxPrevFrameLevels = 16 };
+ std::vector m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index]
+ };
class basisu_lowlevel_transcoder
{
@@ -74,7 +90,7 @@ namespace basist
bool decode_tables(const uint8_t *pTable_data, uint32_t table_data_size);
bool transcode_slice(void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t *pImage_data, uint32_t image_data_size, block_format fmt,
- uint32_t output_stride, bool wrap_addressing, bool bc1_allow_threecolor_blocks, uint32_t output_row_pitch_in_blocks = 0);
+ uint32_t output_stride, bool wrap_addressing, bool bc1_allow_threecolor_blocks, const basis_file_header &header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks = 0, basisu_transcoder_state *pState = nullptr);
private:
struct endpoint
@@ -94,14 +110,8 @@ namespace basist
huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;
uint32_t m_selector_history_buf_size;
-
- struct block_preds
- {
- uint16_t m_endpoint_index;
- uint8_t m_pred_bits;
- };
-
- std::vector m_block_endpoint_preds[2];
+
+ basisu_transcoder_state m_def_state;
};
struct basisu_slice_info
@@ -125,6 +135,7 @@ namespace basist
uint32_t m_unpacked_slice_crc16;
bool m_alpha_flag; // true if the slice has alpha data
+ bool m_iframe_flag; // true if the slice is an I-Frame
};
typedef std::vector basisu_slice_info_vec;
@@ -147,6 +158,7 @@ namespace basist
uint32_t m_first_slice_index;
bool m_alpha_flag; // true if the image has alpha data
+ bool m_iframe_flag; // true if the image is an I-Frame
};
struct basisu_image_level_info
@@ -167,6 +179,7 @@ namespace basist
uint32_t m_first_slice_index;
bool m_alpha_flag; // true if the image has alpha data
+ bool m_iframe_flag; // true if the image is an I-Frame
};
struct basisu_file_info
@@ -270,7 +283,7 @@ namespace basist
uint32_t image_index, uint32_t level_index,
void *pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks,
transcoder_texture_format fmt,
- uint32_t decode_flags = cDecodeFlagsPVRTCWrapAddressing, uint32_t output_row_pitch_in_blocks = 0) const;
+ uint32_t decode_flags = cDecodeFlagsPVRTCWrapAddressing, uint32_t output_row_pitch_in_blocks = 0, basisu_transcoder_state *pState = nullptr) const;
// Finds the basis slice corresponding to the specified image/level/alpha params, or -1 if the slice can't be found.
int find_slice(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const;
@@ -284,12 +297,9 @@ namespace basist
// output_row_pitch_in_blocks: Number of blocks per row. If 0, the transcoder uses the slice's num_blocks_x. Ignored for PVRTC1 (due to texture swizzling).
bool transcode_slice(const void *pData, uint32_t data_size, uint32_t slice_index,
void *pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks,
- block_format fmt, uint32_t output_block_stride_in_bytes, uint32_t decode_flags = cDecodeFlagsPVRTCWrapAddressing, uint32_t output_row_pitch_in_blocks = 0) const;
+ block_format fmt, uint32_t output_block_stride_in_bytes, uint32_t decode_flags = cDecodeFlagsPVRTCWrapAddressing, uint32_t output_row_pitch_in_blocks = 0, basisu_transcoder_state * pState = nullptr) const;
private:
- const void *m_pFile_data;
- uint32_t m_file_data_size;
-
mutable basisu_lowlevel_transcoder m_lowlevel_decoder;
int find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const;
@@ -300,4 +310,12 @@ namespace basist
// basisu_transcoder_init() must be called before a .basis file can be transcoded.
void basisu_transcoder_init();
+ enum debug_flags_t
+ {
+ cDebugFlagVisCRs = 1,
+ cDebugFlagVisBC1Sels = 2,
+ cDebugFlagVisBC1Endpoints = 4
+ };
+ uint32_t get_debug_flags();
+ void set_debug_flags(uint32_t f);
} // namespace basisu
diff --git a/transcoder/basisu_transcoder_internal.h b/transcoder/basisu_transcoder_internal.h
index d4d210e..cf1c3d4 100644
--- a/transcoder/basisu_transcoder_internal.h
+++ b/transcoder/basisu_transcoder_internal.h
@@ -33,6 +33,11 @@
#define BASISD_znew (z = 36969 * (z & 65535) + (z >> 16))
+namespace basisu
+{
+ extern bool g_debug_printf;
+}
+
namespace basist
{
const int COLOR5_PAL0_PREV_HI = 9, COLOR5_PAL0_DELTA_LO = -9, COLOR5_PAL0_DELTA_HI = 31;
@@ -45,6 +50,9 @@ namespace basist
const uint32_t ENDPOINT_PRED_MIN_REPEAT_COUNT = 3;
const uint32_t ENDPOINT_PRED_COUNT_VLC_BITS = 4;
+ const uint32_t NUM_ENDPOINT_PREDS = 3;// BASISU_ARRAY_SIZE(g_endpoint_preds);
+ const uint32_t CR_ENDPOINT_PRED_INDEX = NUM_ENDPOINT_PREDS - 1;
+ const uint32_t NO_ENDPOINT_PRED_INDEX = 3;//NUM_ENDPOINT_PREDS;
const uint32_t MAX_SELECTOR_HISTORY_BUF_SIZE = 64;
const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH = 3;
const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_BITS = 6;
diff --git a/webgl/transcoder/basis_wrappers.cpp b/webgl/transcoder/basis_wrappers.cpp
index 408cbd7..d389d5a 100644
--- a/webgl/transcoder/basis_wrappers.cpp
+++ b/webgl/transcoder/basis_wrappers.cpp
@@ -1,6 +1,7 @@
// basis_wrappers.cpp - Simple C-style wrappers to the C++ transcoder for WebGL use.
#include "basisu_transcoder.h"
#include
+#include
using namespace emscripten;
using namespace basist;
@@ -118,6 +119,16 @@ struct basis_file
if (!m_transcoder.get_image_level_desc(m_file.data(), m_file.size(), image_index, level_index, orig_width, orig_height, total_blocks))
return 0;
+ if (format == cTFPVRTC1_4_OPAQUE_ONLY)
+ {
+ // For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures:
+ // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
+ const uint32_t width = (orig_width + 3) & ~3;
+ const uint32_t height = (orig_height + 3) & ~3;
+ const uint32_t size_in_bytes = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8;
+ return size_in_bytes;
+ }
+
return total_blocks * bytes_per_block;
}
@@ -145,6 +156,17 @@ struct basis_file
uint32_t required_size = total_blocks * bytes_per_block;
+ if (format == cTFPVRTC1_4_OPAQUE_ONLY)
+ {
+ // For PVRTC1, Basis only writes (or requires) total_blocks * bytes_per_block. But GL requires extra padding for very small textures:
+ // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
+ // The transcoder will clear the extra bytes followed the used blocks to 0.
+ const uint32_t width = (orig_width + 3) & ~3;
+ const uint32_t height = (orig_height + 3) & ~3;
+ required_size = (std::max(8U, width) * std::max(8U, height) * 4 + 7) / 8;
+ assert(required_size >= total_blocks * bytes_per_block);
+ }
+
std::vector dst_data(required_size);
uint32_t status = m_transcoder.transcode_image_level(