From c1453f902ad54619ab378307a5754f6a1e036a37 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 12 Oct 2026 03:12:48 -0400 Subject: [PATCH] Applying a material override merges the local textures it tracks into the render material applyOverride meant to merge the override's tracking map into the render material's, but passed one iterator as both ends of the range, so std::map inserted an empty range and merged nothing. It has been that way since local texture tracking came in with upstream's SL-20523 (begin() as both ends then; the hashing rework later shortened it to the same iterator twice). A local texture set on an override was never tracked on the render material: updateTextureTracking never associated it with its local bitmap, so the object did not follow changes to the file, and the local texture digest the hash carries left it out. Where both track a local texture, the override's entry now wins, as its texture ids do and as every other write to the map assigns. On an LLFetchedGLTFMaterial the choice is brief anyway: updateTextureTracking associates each tracking id with its bitmap, which resets the entry to the bitmap's current world id. The test applies an override carrying a tracked local texture and checks that the render material keeps its own entry, gains the override's, finds both in updateTextureTracking and digests both; and that the override's entry wins a tracking id both hold. Co-Authored-By: Claude Opus 5.5 --- indra/llprimitive/llgltfmaterial.cpp | 8 +- .../llprimitive/tests/llgltfmaterial_test.cpp | 82 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp index 36c9f07863..c257058524 100644 --- a/indra/llprimitive/llgltfmaterial.cpp +++ b/indra/llprimitive/llgltfmaterial.cpp @@ -646,8 +646,12 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat) if (!override_mat.mTrackingIdToLocalTexture.empty()) { - auto it = override_mat.mTrackingIdToLocalTexture.begin(); - mTrackingIdToLocalTexture.insert(it, it); + // The override is the layer on top, so where both track a local + // texture its entry wins, as its texture ids do above. + for (const auto& [tracking_id, local_texture] : override_mat.mTrackingIdToLocalTexture) + { + mTrackingIdToLocalTexture.insert_or_assign(tracking_id, local_texture); + } updateLocalTexDataDigest(); updateTextureTracking(); } diff --git a/indra/llprimitive/tests/llgltfmaterial_test.cpp b/indra/llprimitive/tests/llgltfmaterial_test.cpp index 479f3fc0cd..a18939f2c5 100644 --- a/indra/llprimitive/tests/llgltfmaterial_test.cpp +++ b/indra/llprimitive/tests/llgltfmaterial_test.cpp @@ -443,4 +443,86 @@ namespace ENSURE_HASH_CHANGED(hash_mat, source_mat, mOverrideDoubleSided); ENSURE_HASH_CHANGED(hash_mat, source_mat, mOverrideAlphaMode); } + +namespace +{ + // Notes the tracking ids updateTextureTracking() finds, where + // LLFetchedGLTFMaterial associates each with its local bitmap + struct tracking_material : public LLGLTFMaterial + { + void updateTextureTracking() override + { + mTrackedIds.clear(); + for (const auto& [tracking_id, local_texture] : mTrackingIdToLocalTexture) + { + mTrackedIds.insert(tracking_id); + } + } + + std::set mTrackedIds; + }; +} + + // Test that a local texture on an override is tracked on the render + // material the override is applied to + template<> template<> + void llgltfmaterial_object_t::test<13>() + { + { + const LLUUID base_tracking_id = LLUUID::generateNewID(); + const LLUUID base_local_texture = LLUUID::generateNewID(); + const LLUUID override_tracking_id = LLUUID::generateNewID(); + const LLUUID override_local_texture = LLUUID::generateNewID(); + + LLGLTFMaterial override_material; + override_material.setBaseColorId(override_local_texture, true); + override_material.addLocalTextureTracking(override_tracking_id, override_local_texture); + + tracking_material render_material; + apply_test_material_texture_ids(render_material); + render_material.setNormalId(base_local_texture); + render_material.addLocalTextureTracking(base_tracking_id, base_local_texture); + + render_material.applyOverride(override_material); + + ensure_equals("LLGLTFMaterial: override's local texture applied", render_material.mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR], override_local_texture); + const auto override_tracked = render_material.mTrackingIdToLocalTexture.find(override_tracking_id); + ensure("LLGLTFMaterial: render material tracks the override's local texture", override_tracked != render_material.mTrackingIdToLocalTexture.end()); + ensure_equals("LLGLTFMaterial: render material tracks the override's local texture by its world id", override_tracked->second, override_local_texture); + const auto base_tracked = render_material.mTrackingIdToLocalTexture.find(base_tracking_id); + ensure("LLGLTFMaterial: render material still tracks its own local texture", base_tracked != render_material.mTrackingIdToLocalTexture.end()); + ensure_equals("LLGLTFMaterial: render material still tracks its own local texture by its world id", base_tracked->second, base_local_texture); + ensure_equals("LLGLTFMaterial: render material tracks only the two local textures", render_material.mTrackingIdToLocalTexture.size(), size_t(2)); + + ensure("LLGLTFMaterial: updateTextureTracking finds the override's tracking id", render_material.mTrackedIds.count(override_tracking_id) == 1); + ensure("LLGLTFMaterial: updateTextureTracking finds the material's own tracking id", render_material.mTrackedIds.count(base_tracking_id) == 1); + + LLGLTFMaterial expected_material; + expected_material.addLocalTextureTracking(base_tracking_id, base_local_texture); + expected_material.addLocalTextureTracking(override_tracking_id, override_local_texture); + ensure_equals("LLGLTFMaterial: local texture digest covers the override's local texture", render_material.mLocalTexDataDigest, expected_material.mLocalTexDataDigest); + } + + { + // Both track the same local texture: the override's entry wins, + // as its texture ids do + const LLUUID tracking_id = LLUUID::generateNewID(); + const LLUUID base_local_texture = LLUUID::generateNewID(); + const LLUUID override_local_texture = LLUUID::generateNewID(); + + LLGLTFMaterial override_material; + override_material.setBaseColorId(override_local_texture, true); + override_material.addLocalTextureTracking(tracking_id, override_local_texture); + + LLGLTFMaterial render_material; + render_material.setBaseColorId(base_local_texture); + render_material.addLocalTextureTracking(tracking_id, base_local_texture); + + render_material.applyOverride(override_material); + + const auto tracked = render_material.mTrackingIdToLocalTexture.find(tracking_id); + ensure("LLGLTFMaterial: render material tracks a local texture both track", tracked != render_material.mTrackingIdToLocalTexture.end()); + ensure_equals("LLGLTFMaterial: override's world id replaces the material's for a local texture both track", tracked->second, override_local_texture); + } + } }