Generate lower resolution color preview first when 'preview result colors' is enabled

This commit is contained in:
sentientstardust
2026-05-10 08:24:22 +01:00
parent d7603ed561
commit 5823c83691
3 changed files with 233 additions and 72 deletions

View File

@@ -7961,6 +7961,7 @@ void GLCanvas3D::_render_overlays()
m_labels.render(sorted_instances);
if (Slic3r::texture_preview_simulation_is_pending()) {
const bool temporary_texture_preview_pending = Slic3r::texture_preview_simulation_has_temporary_pending();
ImGuiWrapper &imgui = *wxGetApp().imgui();
const Size cnv_size = get_canvas_size();
const float scale = imgui.get_style_scaling();
@@ -7977,7 +7978,9 @@ void GLCanvas3D::_render_overlays()
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoFocusOnAppearing);
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
imgui.text(_L("Generating simulated color preview..."));
imgui.text(temporary_texture_preview_pending ?
_L("Generating simulated color preview (1/2)...") :
_L("Generating simulated color preview (2/2)..."));
imgui.end();
ImGui::PopStyleColor(2);
ImGui::PopStyleVar(2);

View File

@@ -21,6 +21,7 @@
#include <future>
#include <limits>
#include <memory>
#include <mutex>
#include <numeric>
#include <optional>
#include <unordered_map>
@@ -37,6 +38,9 @@ constexpr float k_polygon_offset_units = -1.f;
constexpr float k_epsilon = 1e-6f;
constexpr unsigned int k_simulated_texture_preview_max_edge = 2048;
constexpr size_t k_simulated_texture_preview_max_pixels = 2048ull * 2048ull;
constexpr unsigned int k_temporary_simulated_texture_preview_max_edge = 1024;
constexpr size_t k_temporary_simulated_texture_preview_max_pixels = 1024ull * 1024ull;
constexpr size_t k_temporary_simulated_texture_preview_min_source_pixels = k_temporary_simulated_texture_preview_max_pixels * 3 / 2;
constexpr const char *TEXTURE_MAPPING_BACKGROUND_COLOR_CONFIG_KEY = "texture_mapping_background_color";
struct TexturePreviewMixCandidate
@@ -54,6 +58,12 @@ struct TexturePreviewMixCandidateKdNode
uint8_t axis { 0 };
};
struct TexturePreviewGenericMixCandidateCache
{
std::once_flag init;
ColorSolverCandidateSet candidates;
};
struct TexturePreviewSimulationSettings
{
int mapping_mode = int(TextureMappingZone::TextureMappingFilamentBlending);
@@ -72,7 +82,7 @@ struct TexturePreviewSimulationSettings
std::vector<std::array<float, 3>> component_colors;
std::vector<float> component_strength_factors;
std::vector<size_t> semantic_component_indices;
ColorSolverCandidateSet generic_mix_candidates;
std::shared_ptr<TexturePreviewGenericMixCandidateCache> generic_mix_candidate_cache;
};
struct SurfaceGradientPreviewSettings
@@ -107,13 +117,21 @@ struct TexturePreviewSimulationResult
std::vector<unsigned char> rgba;
};
struct TexturePreviewSimulationJobState
{
std::mutex mutex;
std::optional<TexturePreviewSimulationResult> temporary_result;
};
struct TexturePreviewSimulationCacheEntry
{
std::unique_ptr<GUI::GLTexture> texture;
size_t uploaded_signature { 0 };
size_t pending_signature { 0 };
size_t temporary_pending_signature { 0 };
bool ready_frame_requested { false };
std::future<TexturePreviewSimulationResult> pending_future;
std::shared_ptr<TexturePreviewSimulationJobState> pending_job_state;
};
struct TexturePreviewVertexColorSimulationResult
@@ -448,28 +466,31 @@ void configure_texture_preview_sampler(const GUI::GLTexture &texture)
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
}
std::array<unsigned int, 2> limited_simulated_texture_preview_size(unsigned int width, unsigned int height)
std::array<unsigned int, 2> limited_simulated_texture_preview_size(unsigned int width,
unsigned int height,
unsigned int max_edge = k_simulated_texture_preview_max_edge,
size_t max_pixels = k_simulated_texture_preview_max_pixels)
{
if (width == 0 || height == 0)
return { 0, 0 };
double scale = 1.0;
const unsigned int max_edge = std::max(width, height);
if (max_edge > k_simulated_texture_preview_max_edge)
scale = std::min(scale, double(k_simulated_texture_preview_max_edge) / double(max_edge));
const unsigned int source_max_edge = std::max(width, height);
if (source_max_edge > max_edge)
scale = std::min(scale, double(max_edge) / double(source_max_edge));
const double pixel_count = double(width) * double(height);
if (pixel_count * scale * scale > double(k_simulated_texture_preview_max_pixels))
scale = std::min(scale, std::sqrt(double(k_simulated_texture_preview_max_pixels) / pixel_count));
if (pixel_count * scale * scale > double(max_pixels))
scale = std::min(scale, std::sqrt(double(max_pixels) / pixel_count));
if (scale >= 1.0)
return { width, height };
unsigned int limited_width = std::max(1u, unsigned(std::lround(double(width) * scale)));
unsigned int limited_height = std::max(1u, unsigned(std::lround(double(height) * scale)));
while (limited_width > k_simulated_texture_preview_max_edge ||
limited_height > k_simulated_texture_preview_max_edge ||
size_t(limited_width) * size_t(limited_height) > k_simulated_texture_preview_max_pixels) {
while (limited_width > max_edge ||
limited_height > max_edge ||
size_t(limited_width) * size_t(limited_height) > max_pixels) {
if (limited_width >= limited_height && limited_width > 1)
--limited_width;
else if (limited_height > 1)
@@ -480,6 +501,47 @@ std::array<unsigned int, 2> limited_simulated_texture_preview_size(unsigned int
return { limited_width, limited_height };
}
std::array<unsigned int, 2> simulated_texture_preview_size(unsigned int width,
unsigned int height,
const TexturePreviewSimulationSettings &settings,
unsigned int max_edge = 0,
size_t max_pixels = 0)
{
if (max_edge > 0 && max_pixels > 0)
return limited_simulated_texture_preview_size(width, height, max_edge, max_pixels);
return settings.limit_texture_resolution ?
limited_simulated_texture_preview_size(width, height) :
std::array<unsigned int, 2>{ width, height };
}
bool simulated_texture_preview_needs_temporary_result(unsigned int width,
unsigned int height,
const TexturePreviewSimulationSettings &settings)
{
const size_t source_pixels = size_t(width) * size_t(height);
if (source_pixels <= k_temporary_simulated_texture_preview_min_source_pixels)
return false;
const std::array<unsigned int, 2> final_size = simulated_texture_preview_size(width, height, settings);
const std::array<unsigned int, 2> temporary_size =
simulated_texture_preview_size(width,
height,
settings,
k_temporary_simulated_texture_preview_max_edge,
k_temporary_simulated_texture_preview_max_pixels);
return size_t(final_size[0]) * size_t(final_size[1]) > size_t(temporary_size[0]) * size_t(temporary_size[1]);
}
size_t temporary_simulated_texture_preview_signature(size_t signature)
{
signature ^= 0x4a7c15f17e315123ull + (signature << 6) + (signature >> 2);
signature ^= std::hash<unsigned int>{}(k_temporary_simulated_texture_preview_max_edge) + 0x9e3779b97f4a7c15ull +
(signature << 6) + (signature >> 2);
signature ^= std::hash<size_t>{}(k_temporary_simulated_texture_preview_max_pixels) + 0x9e3779b97f4a7c15ull +
(signature << 6) + (signature >> 2);
return signature;
}
std::array<unsigned char, 4> sample_texture_preview_rgba_bilinear(const std::vector<unsigned char> &rgba,
unsigned int width,
unsigned int height,
@@ -1390,9 +1452,10 @@ std::vector<float> component_weights_for_texture_preview(const TexturePreviewSim
if (optimized.size() == component_count)
desired = std::move(optimized);
else {
std::vector<float> best = !settings.generic_mix_candidates.empty() ?
std::vector<float> best = settings.generic_mix_candidate_cache != nullptr &&
!settings.generic_mix_candidate_cache->candidates.empty() ?
solve_color_solver_weights_for_target(
settings.generic_mix_candidates,
settings.generic_mix_candidate_cache->candidates,
target,
color_solver_lookup_mode_from_index(settings.generic_solver_lookup_mode),
color_solver_mode_from_index(settings.generic_solver_mode)) :
@@ -1427,12 +1490,18 @@ void prepare_texture_preview_simulation_settings(TexturePreviewSimulationSetting
semantic_component_indices_for_texture_preview(settings.component_colors,
settings.filament_color_mode,
settings.force_sequential_filaments);
if (texture_preview_uses_generic_solver(settings))
settings.generic_mix_candidates =
build_color_solver_candidates(generic_solver_component_colors(settings),
color_solver_mix_model_from_index(settings.generic_solver_mix_model));
else
settings.generic_mix_candidates = ColorSolverCandidateSet();
if (texture_preview_uses_generic_solver(settings)) {
if (settings.generic_mix_candidate_cache == nullptr)
settings.generic_mix_candidate_cache = std::make_shared<TexturePreviewGenericMixCandidateCache>();
const std::vector<std::array<float, 3>> component_colors = generic_solver_component_colors(settings);
const ColorSolverMixModel mix_model = color_solver_mix_model_from_index(settings.generic_solver_mix_model);
std::shared_ptr<TexturePreviewGenericMixCandidateCache> cache = settings.generic_mix_candidate_cache;
std::call_once(cache->init, [cache, component_colors, mix_model]() {
cache->candidates = build_color_solver_candidates(component_colors, mix_model);
});
} else {
settings.generic_mix_candidate_cache.reset();
}
}
ColorRGBA simulated_texture_preview_color_for_vertex_color(const ColorRGBA *source_color,
@@ -1558,21 +1627,22 @@ size_t texture_preview_simulation_signature(const ModelVolume &model_volume,
TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t signature,
unsigned int width,
unsigned int height,
std::vector<unsigned char> source_rgba,
std::vector<unsigned char> source_raw_offsets,
const std::vector<unsigned char> &source_rgba,
const std::vector<unsigned char> &source_raw_offsets,
unsigned int source_raw_channels,
std::vector<size_t> source_raw_component_channels,
const std::vector<size_t> &source_raw_component_channels,
ColorRGBA background_color,
TexturePreviewSimulationSettings settings)
TexturePreviewSimulationSettings settings,
unsigned int max_preview_edge = 0,
size_t max_preview_pixels = 0)
{
TexturePreviewSimulationResult result;
result.signature = signature;
if (width == 0 || height == 0 || source_rgba.size() < size_t(width) * size_t(height) * 4)
return result;
const std::array<unsigned int, 2> preview_size = settings.limit_texture_resolution ?
limited_simulated_texture_preview_size(width, height) :
std::array<unsigned int, 2>{ width, height };
const std::array<unsigned int, 2> preview_size =
simulated_texture_preview_size(width, height, settings, max_preview_edge, max_preview_pixels);
result.width = preview_size[0];
result.height = preview_size[1];
result.rgba.resize(size_t(result.width) * size_t(result.height) * 4, 0);
@@ -1699,17 +1769,17 @@ size_t &texture_preview_vertex_color_simulation_generation()
return *generation;
}
void discard_ready_texture_preview_future(TexturePreviewSimulationCacheEntry &entry)
void discard_ready_texture_preview_future(std::future<TexturePreviewSimulationResult> &future, size_t &pending_signature)
{
if (!entry.pending_future.valid() ||
entry.pending_future.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
if (!future.valid() ||
future.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
return;
try {
(void) entry.pending_future.get();
(void) future.get();
} catch (...) {
}
entry.pending_signature = 0;
pending_signature = 0;
}
void discard_ready_texture_preview_vertex_color_future(TexturePreviewVertexColorSimulationCacheEntry &entry)
@@ -1794,10 +1864,12 @@ void clear_texture_preview_simulation_cache()
}
entry->uploaded_signature = 0;
entry->pending_signature = 0;
entry->temporary_pending_signature = 0;
entry->pending_job_state.reset();
if (entry->pending_future.valid()) {
if (entry->pending_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
discard_ready_texture_preview_future(*entry);
discard_ready_texture_preview_future(entry->pending_future, entry->pending_signature);
} else {
abandoned_texture_preview_futures().emplace_back(std::move(entry->pending_future));
}
@@ -1843,29 +1915,22 @@ bool texture_preview_simulation_is_pending_impl()
continue;
}
if (!entry->pending_future.valid()) {
++it;
continue;
const bool has_future = entry->pending_future.valid();
if (has_future) {
if (entry->pending_future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
pending = true;
entry->ready_frame_requested = false;
} else if (entry->pending_signature != 0 && !entry->ready_frame_requested) {
pending = true;
entry->ready_frame_requested = true;
}
}
if (entry->pending_future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
pending = true;
entry->ready_frame_requested = false;
++it;
continue;
}
if (entry->texture == nullptr && entry->pending_signature == 0) {
discard_ready_texture_preview_future(*entry);
if (!has_future && entry->texture == nullptr) {
it = cache.erase(it);
continue;
}
if (entry->pending_signature != 0 && !entry->ready_frame_requested) {
pending = true;
entry->ready_frame_requested = true;
}
++it;
}
@@ -1918,6 +1983,24 @@ bool texture_preview_simulation_is_pending_impl()
return pending;
}
bool texture_preview_simulation_has_temporary_pending_impl()
{
auto &cache = texture_preview_simulation_cache();
for (auto it = cache.begin(); it != cache.end();) {
const std::shared_ptr<TexturePreviewSimulationCacheEntry> &entry = it->second;
if (entry == nullptr) {
it = cache.erase(it);
continue;
}
if (entry->pending_future.valid() && entry->temporary_pending_signature != 0)
return true;
++it;
}
return false;
}
size_t texture_preview_simulation_cache_key(const ModelVolume &model_volume, unsigned int filament_id)
{
size_t key = reinterpret_cast<size_t>(&model_volume);
@@ -1925,6 +2008,47 @@ size_t texture_preview_simulation_cache_key(const ModelVolume &model_volume, uns
return key;
}
bool upload_simulated_texture_preview_result(TexturePreviewSimulationCacheEntry &entry,
TexturePreviewSimulationResult result,
size_t expected_signature)
{
if (result.signature != expected_signature || result.rgba.empty() || result.width == 0 || result.height == 0)
return false;
if (entry.texture == nullptr)
entry.texture = std::make_unique<GUI::GLTexture>();
else
entry.texture->reset();
if (!entry.texture->load_from_raw_data(std::move(result.rgba), result.width, result.height)) {
entry.uploaded_signature = 0;
return false;
}
configure_texture_preview_sampler(*entry.texture);
entry.uploaded_signature = result.signature;
return true;
}
void consume_temporary_simulated_texture_preview_result(TexturePreviewSimulationCacheEntry &entry)
{
if (entry.temporary_pending_signature == 0 || entry.pending_job_state == nullptr)
return;
std::optional<TexturePreviewSimulationResult> result;
{
std::lock_guard<std::mutex> lock(entry.pending_job_state->mutex);
if (!entry.pending_job_state->temporary_result.has_value())
return;
result = std::move(entry.pending_job_state->temporary_result);
entry.pending_job_state->temporary_result.reset();
}
if (result.has_value())
upload_simulated_texture_preview_result(entry, std::move(*result), entry.temporary_pending_signature);
entry.temporary_pending_signature = 0;
}
const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const ModelVolume &model_volume,
unsigned int filament_id,
size_t num_physical,
@@ -1947,23 +2071,22 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model
entry_ref = std::make_shared<TexturePreviewSimulationCacheEntry>();
TexturePreviewSimulationCacheEntry &entry = *entry_ref;
const unsigned int width = model_volume.imported_texture_width;
const unsigned int height = model_volume.imported_texture_height;
const bool needs_temporary_result = simulated_texture_preview_needs_temporary_result(width, height, *settings);
const size_t temporary_signature = needs_temporary_result ?
temporary_simulated_texture_preview_signature(simulation_signature) :
size_t(0);
consume_temporary_simulated_texture_preview_result(entry);
if (entry.pending_future.valid() && entry.pending_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
TexturePreviewSimulationResult result = entry.pending_future.get();
if (result.signature == entry.pending_signature && !result.rgba.empty() && result.width > 0 && result.height > 0) {
if (entry.texture == nullptr)
entry.texture = std::make_unique<GUI::GLTexture>();
else
entry.texture->reset();
if (entry.texture->load_from_raw_data(std::move(result.rgba), result.width, result.height)) {
configure_texture_preview_sampler(*entry.texture);
entry.uploaded_signature = result.signature;
} else {
entry.uploaded_signature = 0;
}
}
upload_simulated_texture_preview_result(entry, std::move(result), entry.pending_signature);
entry.pending_signature = 0;
entry.temporary_pending_signature = 0;
entry.ready_frame_requested = false;
entry.pending_job_state.reset();
}
if (entry.texture != nullptr && entry.uploaded_signature == simulation_signature && entry.texture->get_id() != 0)
@@ -1971,41 +2094,70 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model
if (!entry.pending_future.valid()) {
entry.pending_signature = simulation_signature;
entry.temporary_pending_signature = temporary_signature;
entry.ready_frame_requested = false;
const unsigned int width = model_volume.imported_texture_width;
const unsigned int height = model_volume.imported_texture_height;
std::vector<unsigned char> source_rgba(model_volume.imported_texture_rgba.begin(), model_volume.imported_texture_rgba.end());
std::vector<unsigned char> source_raw_offsets(model_volume.imported_texture_raw_filament_offsets.begin(),
model_volume.imported_texture_raw_filament_offsets.end());
entry.pending_job_state = std::make_shared<TexturePreviewSimulationJobState>();
auto source_rgba = std::make_shared<std::vector<unsigned char>>(model_volume.imported_texture_rgba.begin(),
model_volume.imported_texture_rgba.end());
auto source_raw_offsets = std::make_shared<std::vector<unsigned char>>(model_volume.imported_texture_raw_filament_offsets.begin(),
model_volume.imported_texture_raw_filament_offsets.end());
const unsigned int source_raw_channels = model_volume.imported_texture_raw_channels;
TexturePreviewSimulationSettings simulation_settings = *settings;
simulation_settings.generic_mix_candidate_cache = std::make_shared<TexturePreviewGenericMixCandidateCache>();
std::vector<size_t> source_raw_component_channels =
raw_component_source_channels_for_texture_preview(model_volume.imported_texture_raw_metadata_json,
source_raw_channels,
simulation_settings.filament_color_mode,
simulation_settings.component_colors.size());
std::shared_ptr<TexturePreviewSimulationJobState> job_state = entry.pending_job_state;
entry.pending_future = std::async(std::launch::async,
[simulation_signature,
temporary_signature,
width,
height,
source_rgba = std::move(source_rgba),
source_raw_offsets = std::move(source_raw_offsets),
source_rgba,
source_raw_offsets,
source_raw_channels,
source_raw_component_channels = std::move(source_raw_component_channels),
background_color,
job_state,
simulation_settings = std::move(simulation_settings)]() mutable {
if (temporary_signature != 0 && job_state != nullptr) {
const unsigned int temp_edge = k_temporary_simulated_texture_preview_max_edge;
const size_t temp_pixels = k_temporary_simulated_texture_preview_max_pixels;
TexturePreviewSimulationResult temporary_result =
build_simulated_texture_preview_result(temporary_signature,
width,
height,
*source_rgba,
*source_raw_offsets,
source_raw_channels,
source_raw_component_channels,
background_color,
simulation_settings,
temp_edge,
temp_pixels);
std::lock_guard<std::mutex> lock(job_state->mutex);
job_state->temporary_result = std::move(temporary_result);
}
return build_simulated_texture_preview_result(simulation_signature,
width,
height,
std::move(source_rgba),
std::move(source_raw_offsets),
*source_rgba,
*source_raw_offsets,
source_raw_channels,
std::move(source_raw_component_channels),
source_raw_component_channels,
background_color,
std::move(simulation_settings));
});
}
if (temporary_signature != 0 &&
entry.texture != nullptr &&
entry.uploaded_signature == temporary_signature &&
entry.texture->get_id() != 0)
return entry.texture.get();
return &fallback_texture;
}
@@ -3185,6 +3337,11 @@ bool texture_preview_simulation_is_pending()
return texture_preview_simulation_is_pending_impl();
}
bool texture_preview_simulation_has_temporary_pending()
{
return texture_preview_simulation_has_temporary_pending_impl();
}
bool texture_preview_simulation_enabled_for_filament(unsigned int filament_id,
size_t num_physical,
const TextureMappingManager *texture_mgr)

View File

@@ -66,6 +66,7 @@ bool ensure_model_volume_texture_preview(const ModelVolume &model_volume,
size_t &texture_signature);
bool texture_preview_simulation_is_pending();
bool texture_preview_simulation_has_temporary_pending();
void clear_texture_preview_simulation_cache();
bool texture_preview_simulation_enabled_for_filament(unsigned int filament_id,
size_t num_physical,