Skip to content

Commit ac07470

Browse files
Merge pull request #550 from SceneTech/perf/cssom-noop-style-243
Skip identical CSS custom-property recascade
2 parents ca4fa17 + b60f5d2 commit ac07470

3 files changed

Lines changed: 170 additions & 0 deletions

File tree

‎experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_style.inc‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,22 @@
596596
if (!valid_priority) return;
597597
if (name.starts_with("--")) {
598598
if (!valid_custom_property_name(name)) return;
599+
if (!value.empty()) {
600+
const auto& authored = node->authored_style();
601+
const auto existing = authored.declarations.find(name);
602+
if (existing != authored.declarations.end()
603+
&& existing->second == value
604+
&& authored.important_declarations.contains(name) == important) {
605+
#if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION)
606+
if (self->resize_profile_active) {
607+
++self->last_resize_redundant_style_writes;
608+
}
609+
#endif
610+
self->notify_custom_element_attribute(
611+
*node, "style", old_value, node->attributes.at("style"));
612+
return;
613+
}
614+
}
599615
if (value.empty()) {
600616
if (!has_inline_style_declaration(*node, name)) {
601617
#if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION)

‎experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_browser_dom_tests.inc‎

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4311,6 +4311,159 @@ void test_async_save_acknowledgement_publishes_without_pointer_input()
43114311
webscene_engine_destroy(engine);
43124312
}
43134313

4314+
void test_identical_custom_property_writes_do_not_schedule_visual_work()
4315+
{
4316+
auto* engine = webscene_engine_create(0);
4317+
require(engine != nullptr, "custom-property no-op engine creation failed");
4318+
4319+
const webscene_scene_view* initial = nullptr;
4320+
for (auto attempt = 0; attempt < 100 && initial == nullptr; ++attempt) {
4321+
initial = webscene_engine_acquire_latest_scene(engine);
4322+
if (initial == nullptr) {
4323+
std::this_thread::sleep_for(std::chrono::milliseconds(2));
4324+
}
4325+
}
4326+
require(initial != nullptr, "custom-property no-op fixture had no initial scene");
4327+
require(
4328+
webscene_scene_acknowledge(initial) != 0,
4329+
"custom-property no-op fixture could not acknowledge its initial scene");
4330+
webscene_scene_release(initial);
4331+
4332+
execute_and_wait(
4333+
engine,
4334+
R"JS(
4335+
const style = document.createElement('style');
4336+
style.textContent = '#custom-property-noop { width: var(--editor-width); height: 24px; }';
4337+
document.head.appendChild(style);
4338+
const target = document.createElement('div');
4339+
target.id = 'custom-property-noop';
4340+
target.style.setProperty('--editor-width', '120px');
4341+
document.body.appendChild(target);
4342+
)JS",
4343+
"custom-property-noop-setup.js");
4344+
require(
4345+
evaluate(
4346+
engine,
4347+
"document.getElementById('custom-property-noop').offsetWidth",
4348+
"custom-property-noop-ready.js") == "120",
4349+
"custom-property no-op fixture did not complete initial layout");
4350+
4351+
auto setup_scene_acknowledged = false;
4352+
for (auto attempt = 0; attempt < 100; ++attempt) {
4353+
const auto* scene = webscene_engine_acquire_latest_scene(engine);
4354+
if (scene != nullptr) {
4355+
require(
4356+
webscene_scene_acknowledge(scene) != 0,
4357+
"custom-property no-op setup scene could not be acknowledged");
4358+
webscene_scene_release(scene);
4359+
setup_scene_acknowledged = true;
4360+
break;
4361+
}
4362+
std::this_thread::sleep_for(std::chrono::milliseconds(2));
4363+
}
4364+
require(
4365+
setup_scene_acknowledged,
4366+
"custom-property no-op setup scene was not published");
4367+
std::this_thread::sleep_for(std::chrono::milliseconds(25));
4368+
4369+
webscene_engine_metrics connected_before{};
4370+
webscene_engine_get_metrics(engine, &connected_before);
4371+
execute_and_wait(
4372+
engine,
4373+
R"JS(
4374+
const target = document.getElementById('custom-property-noop');
4375+
for (let index = 0; index < 4096; ++index) {
4376+
target.style.setProperty('--editor-width', '120px');
4377+
}
4378+
)JS",
4379+
"custom-property-noop-connected.js");
4380+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
4381+
webscene_engine_metrics connected_after{};
4382+
webscene_engine_get_metrics(engine, &connected_after);
4383+
require(
4384+
connected_after.layout_passes == connected_before.layout_passes
4385+
&& connected_after.published_scenes == connected_before.published_scenes,
4386+
"identical connected custom-property writes scheduled layout or publication");
4387+
4388+
execute_and_wait(
4389+
engine,
4390+
R"JS(
4391+
globalThis.__customPropertyNoopTarget =
4392+
document.getElementById('custom-property-noop');
4393+
globalThis.__customPropertyNoopTarget.remove();
4394+
)JS",
4395+
"custom-property-noop-detach.js");
4396+
auto detach_scene_acknowledged = false;
4397+
for (auto attempt = 0; attempt < 100; ++attempt) {
4398+
const auto* scene = webscene_engine_acquire_latest_scene(engine);
4399+
if (scene != nullptr) {
4400+
require(
4401+
webscene_scene_acknowledge(scene) != 0,
4402+
"custom-property no-op detach scene could not be acknowledged");
4403+
webscene_scene_release(scene);
4404+
detach_scene_acknowledged = true;
4405+
break;
4406+
}
4407+
std::this_thread::sleep_for(std::chrono::milliseconds(2));
4408+
}
4409+
require(
4410+
detach_scene_acknowledged,
4411+
"custom-property no-op detach scene was not published");
4412+
std::this_thread::sleep_for(std::chrono::milliseconds(25));
4413+
4414+
webscene_engine_metrics detached_before{};
4415+
webscene_engine_get_metrics(engine, &detached_before);
4416+
execute_and_wait(
4417+
engine,
4418+
R"JS(
4419+
for (let index = 0; index < 4096; ++index) {
4420+
__customPropertyNoopTarget.style.setProperty('--editor-width', '120px');
4421+
}
4422+
)JS",
4423+
"custom-property-noop-detached.js");
4424+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
4425+
webscene_engine_metrics detached_after{};
4426+
webscene_engine_get_metrics(engine, &detached_after);
4427+
require(
4428+
detached_after.layout_passes == detached_before.layout_passes
4429+
&& detached_after.published_scenes == detached_before.published_scenes,
4430+
"identical detached custom-property writes scheduled visual work");
4431+
4432+
execute_and_wait(
4433+
engine,
4434+
R"JS(
4435+
document.body.appendChild(__customPropertyNoopTarget);
4436+
delete globalThis.__customPropertyNoopTarget;
4437+
)JS",
4438+
"custom-property-noop-reattach.js");
4439+
webscene_engine_metrics reattached{};
4440+
for (auto attempt = 0; attempt < 100; ++attempt) {
4441+
webscene_engine_get_metrics(engine, &reattached);
4442+
if (reattached.published_scenes > detached_after.published_scenes) break;
4443+
std::this_thread::sleep_for(std::chrono::milliseconds(2));
4444+
}
4445+
require(
4446+
evaluate(
4447+
engine,
4448+
R"JS((() => {
4449+
const target = document.getElementById('custom-property-noop');
4450+
return {
4451+
specified: target.style.getPropertyValue('--editor-width'),
4452+
width: target.offsetWidth,
4453+
connected: target.isConnected
4454+
};
4455+
})())JS",
4456+
"custom-property-noop-reattach-result.js")
4457+
== R"({"specified":"120px","width":120,"connected":true})",
4458+
"custom-property no-op state did not survive detach and reconnect");
4459+
webscene_engine_get_metrics(engine, &reattached);
4460+
require(
4461+
reattached.layout_passes == detached_after.layout_passes + 1U
4462+
&& reattached.published_scenes == detached_after.published_scenes + 1U,
4463+
"custom-property no-op reconnect escaped one layout/publication");
4464+
webscene_engine_destroy(engine);
4465+
}
4466+
43144467
void test_ordered_scene_consumer_preserves_two_diff_chain()
43154468
{
43164469
auto* engine = webscene_engine_create(64);

‎experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,7 @@ int main()
15251525
test_runtime_work_is_attributed();
15261526
test_scene_flow_is_attributed();
15271527
test_read_only_evaluation_does_not_publish_scene();
1528+
test_identical_custom_property_writes_do_not_schedule_visual_work();
15281529
test_async_save_acknowledgement_publishes_without_pointer_input();
15291530
test_ordered_scene_consumer_preserves_two_diff_chain();
15301531
test_keyboard_and_pointer_focus_modality();

0 commit comments

Comments
 (0)