void Object::SendEvent(StringHash eventType, VariantMap& eventData) { if (!Thread::IsMainThread()) { LOGERROR("Sending events is only supported from the main thread"); return; } // Make a weak pointer to self to check for destruction during event handling WeakPtr<Object> self(this); Context* context = context_; HashSet<Object*> processed; context->BeginSendEvent(this); // Check first the specific event receivers const HashSet<Object*>* group = context->GetEventReceivers(this, eventType); if (group) { for (HashSet<Object*>::ConstIterator i = group->Begin(); i != group->End();) { HashSet<Object*>::ConstIterator current = i++; Object* receiver = *current; Object* next = 0; if (i != group->End()) next = *i; unsigned oldSize = group->Size(); receiver->OnEvent(this, eventType, eventData); // If self has been destroyed as a result of event handling, exit if (self.Expired()) { context->EndSendEvent(); return; } // If group has changed size during iteration (removed/added subscribers) try to recover /// \todo This is not entirely foolproof, as a subscriber could have been added to make up for the removed one if (group->Size() != oldSize) i = group->Find(next); processed.Insert(receiver); } } // Then the non-specific receivers group = context->GetEventReceivers(eventType); if (group) { if (processed.Empty()) { for (HashSet<Object*>::ConstIterator i = group->Begin(); i != group->End();) { HashSet<Object*>::ConstIterator current = i++; Object* receiver = *current; Object* next = 0; if (i != group->End()) next = *i; unsigned oldSize = group->Size(); receiver->OnEvent(this, eventType, eventData); if (self.Expired()) { context->EndSendEvent(); return; } if (group->Size() != oldSize) i = group->Find(next); } } else { // If there were specific receivers, check that the event is not sent doubly to them for (HashSet<Object*>::ConstIterator i = group->Begin(); i != group->End();) { HashSet<Object*>::ConstIterator current = i++; Object* receiver = *current; Object* next = 0; if (i != group->End()) next = *i; if (!processed.Contains(receiver)) { unsigned oldSize = group->Size(); receiver->OnEvent(this, eventType, eventData); if (self.Expired()) { context->EndSendEvent(); return; } if (group->Size() != oldSize) i = group->Find(next); } } } } context->EndSendEvent(); }
int ChoiceForm::AutotuneHelper(HashSet<String> & selectedChoices, EnumerableDictionary<String, Spire::Compiler::ShaderChoiceValue>& currentChoices, float timeBudget, bool countOnly) { auto choices = choiceControl->GetChoices(currentShaderName, currentChoices); List<Spire::Compiler::ShaderChoice> filteredChoices; for (auto & choice : choices) if (!currentChoices.ContainsKey(choice.ChoiceName)) filteredChoices.Add(choice); choices = _Move(filteredChoices); // find first non-trivial choice Spire::Compiler::ShaderChoice * currentChoice = nullptr; for (auto & choice : choices) { if (choice.Options.Count() > 1 && selectedChoices.Contains(choice.ChoiceName)) { currentChoice = &choice; break; } } if (currentChoice) { int count = 0; for (auto & opt : currentChoice->Options) { EnumerableDictionary<String, Spire::Compiler::ShaderChoiceValue> newChoices = currentChoices; newChoices[currentChoice->ChoiceName] = opt; count += AutotuneHelper(selectedChoices, newChoices, timeBudget, countOnly); } return count; } else { if (!countOnly) { printf("Testing shader variant..."); // compile and evaluate auto schedule = GenerateSchedule(currentChoices, EnumerableDictionary<String, EnumerableDictionary<String, String>>()); choiceControl->RecompileShader(currentShaderName, schedule); // render 1000 frames and measure time float minTime = 1e30f; for (int f = 0; f < 20; f++) { glFinish(); auto timeP = CoreLib::Diagnostics::PerformanceCounter::Start(); for (int i = 0; i < 10; i++) { choiceControl->RenderFrame(); } glFinish(); auto time = (float)CoreLib::Diagnostics::PerformanceCounter::ToSeconds(CoreLib::Diagnostics::PerformanceCounter::End(timeP)) * 100.0f; if (time < minTime) minTime = time; } choiceControl->UpdateWindow(); auto frameData = ReadFrameData(choiceControl->RenderFrame()); float error = MeasureError(referenceFrame, frameData); float value = 0; if (minTime < timeBudget) value = error; else value = 20.0f + minTime; if (value < currentBestValue) { currentBestValue = value; currentBestChoices = currentChoices; } autotuningLog << minTime << L"," << error<<EndLine; printf("%f ms, error: %f\n", minTime, error); } return 1; } }
void NavigationMesh::CollectGeometries(Vector<NavigationGeometryInfo>& geometryList, Node* node, HashSet<Node*>& processedNodes, bool recursive) { // Make sure nodes are not included twice if (processedNodes.Contains(node)) return; // Exclude obstacles and crowd agents from consideration if (node->HasComponent<Obstacle>() || node->HasComponent<CrowdAgent>()) return; processedNodes.Insert(node); Matrix3x4 inverse = node_->GetWorldTransform().Inverse(); #ifdef ATOMIC_PHYSICS // Prefer compatible physics collision shapes (triangle mesh, convex hull, box) if found. // Then fallback to visible geometry PODVector<CollisionShape*> collisionShapes; node->GetComponents<CollisionShape>(collisionShapes); bool collisionShapeFound = false; for (unsigned i = 0; i < collisionShapes.Size(); ++i) { CollisionShape* shape = collisionShapes[i]; if (!shape->IsEnabledEffective()) continue; ShapeType type = shape->GetShapeType(); if ((type == SHAPE_BOX || type == SHAPE_TRIANGLEMESH || type == SHAPE_CONVEXHULL) && shape->GetCollisionShape()) { Matrix3x4 shapeTransform(shape->GetPosition(), shape->GetRotation(), shape->GetSize()); NavigationGeometryInfo info; info.component_ = shape; info.transform_ = inverse * node->GetWorldTransform() * shapeTransform; info.boundingBox_ = shape->GetWorldBoundingBox().Transformed(inverse); geometryList.Push(info); collisionShapeFound = true; } } if (!collisionShapeFound) #endif { PODVector<Drawable*> drawables; node->GetDerivedComponents<Drawable>(drawables); for (unsigned i = 0; i < drawables.Size(); ++i) { /// \todo Evaluate whether should handle other types. Now StaticModel & TerrainPatch are supported, others skipped Drawable* drawable = drawables[i]; if (!drawable->IsEnabledEffective()) continue; NavigationGeometryInfo info; if (drawable->GetType() == StaticModel::GetTypeStatic()) info.lodLevel_ = static_cast<StaticModel*>(drawable)->GetOcclusionLodLevel(); else if (drawable->GetType() == TerrainPatch::GetTypeStatic()) info.lodLevel_ = 0; else continue; info.component_ = drawable; info.transform_ = inverse * node->GetWorldTransform(); info.boundingBox_ = drawable->GetWorldBoundingBox().Transformed(inverse); geometryList.Push(info); } } if (recursive) { const Vector<SharedPtr<Node> >& children = node->GetChildren(); for (unsigned i = 0; i < children.Size(); ++i) CollectGeometries(geometryList, children[i], processedNodes, recursive); } }
void DFA_Graph::Generate(NFA_Graph * nfa) { table = new RegexCharTable(); List<RegexCharSet * > charSets; for (int i=0; i<nfa->translations.Count(); i++) { if (nfa->translations[i]->CharSet && nfa->translations[i]->CharSet->Ranges.Count()) charSets.Add(nfa->translations[i]->CharSet.operator->()); } CharTableGenerator gen(table.operator ->()); int elements = gen.Generate(charSets); CharElements = gen.elements; List<DFA_Node *> L,D; startNode = new DFA_Node(elements); startNode->ID = 0; startNode->Nodes.Add(nfa->start); L.Add(startNode); nodes.Add(startNode); List<Word> charElem; do { DFA_Node * node = L.Last(); L.RemoveAt(L.Count()-1); charElem.Clear(); node->IsFinal = false; for (int i=0; i<node->Nodes.Count(); i++) { CombineCharElements(node->Nodes[i], charElem); if (node->Nodes[i]->IsFinal) node->IsFinal = true; } for (int i=0; i<charElem.Count(); i++) { DFA_Node * n = new DFA_Node(0); for (int j=0; j<node->Nodes.Count(); j++) { for (int k=0; k<node->Nodes[j]->Translations.Count(); k++) { NFA_Translation * trans = node->Nodes[j]->Translations[k]; if (trans->CharSet->Elements.Contains(charElem[i])) { if (!n->Nodes.Contains(node->Nodes[j]->Translations[k]->NodeDest)) n->Nodes.Add(node->Nodes[j]->Translations[k]->NodeDest); } } } int fid = -1; for (int j=0; j<nodes.Count(); j++) { if ((*nodes[j]) == *n) { fid = j; break; } } if (fid == -1) { n->Translations.SetSize(elements); for (int m=0; m<elements; m++) n->Translations[m] = 0; n->ID = nodes.Count(); L.Add(n); nodes.Add(n); fid = nodes.Count()-1; } else delete n; n = nodes[fid].operator ->(); node->Translations[charElem[i]] = n; } } while (L.Count()); // Set Terminal Identifiers HashSet<int> terminalIdentifiers; for (int i=0; i<nodes.Count(); i++) { terminalIdentifiers.Clear(); for (int j=0; j<nodes[i]->Nodes.Count(); j++) { if (nodes[i]->Nodes[j]->IsFinal && !terminalIdentifiers.Contains(nodes[i]->Nodes[j]->TerminalIdentifier)) { nodes[i]->IsFinal = true; terminalIdentifiers.Add(nodes[i]->Nodes[j]->TerminalIdentifier); nodes[i]->TerminalIdentifiers.Add(nodes[i]->Nodes[j]->TerminalIdentifier); } } nodes[i]->TerminalIdentifiers.Sort(); } }
void AnimationController::SetNetAnimationsAttr(const PODVector<unsigned char>& value) { MemoryBuffer buf(value); AnimatedModel* model = GetComponent<AnimatedModel>(); // Check which animations we need to remove HashSet<StringHash> processedAnimations; unsigned numAnimations = buf.ReadVLE(); while (numAnimations--) { String animName = buf.ReadString(); StringHash animHash(animName); processedAnimations.Insert(animHash); // Check if the animation state exists. If not, add new AnimationState* state = GetAnimationState(animHash); if (!state) { Animation* newAnimation = GetSubsystem<ResourceCache>()->GetResource<Animation>(animName); state = AddAnimationState(newAnimation); if (!state) { LOGERROR("Animation update applying aborted due to unknown animation"); return; } } // Check if the internal control structure exists. If not, add new unsigned index; for (index = 0; index < animations_.Size(); ++index) { if (animations_[index].hash_ == animHash) break; } if (index == animations_.Size()) { AnimationControl newControl; newControl.name_ = animName; newControl.hash_ = animHash; animations_.Push(newControl); } unsigned char ctrl = buf.ReadUByte(); state->SetLayer(buf.ReadUByte()); state->SetLooped((ctrl & CTRL_LOOPED) != 0); animations_[index].speed_ = (float)buf.ReadShort() / 2048.0f; // 11 bits of decimal precision, max. 16x playback speed animations_[index].targetWeight_ = (float)buf.ReadUByte() / 255.0f; // 8 bits of decimal precision animations_[index].fadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade if (ctrl & CTRL_STARTBONE) { StringHash boneHash = buf.ReadStringHash(); if (model) state->SetStartBone(model->GetSkeleton().GetBone(boneHash)); } else state->SetStartBone(0); if (ctrl & CTRL_AUTOFADE) animations_[index].autoFadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade else animations_[index].autoFadeTime_ = 0.0f; animations_[index].removeOnCompletion_ = (ctrl & CTRL_REMOVEONCOMPLETION) != 0; if (ctrl & CTRL_SETTIME) { unsigned char setTimeRev = buf.ReadUByte(); unsigned short setTime = buf.ReadUShort(); // Apply set time command only if revision differs if (setTimeRev != animations_[index].setTimeRev_) { state->SetTime(((float)setTime / 65535.0f) * state->GetLength()); animations_[index].setTimeRev_ = setTimeRev; } } if (ctrl & CTRL_SETWEIGHT) { unsigned char setWeightRev = buf.ReadUByte(); unsigned char setWeight = buf.ReadUByte(); // Apply set weight command only if revision differs if (setWeightRev != animations_[index].setWeightRev_) { state->SetWeight((float)setWeight / 255.0f); animations_[index].setWeightRev_ = setWeightRev; } } } // Set any extra animations to fade out for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i) { if (!processedAnimations.Contains(i->hash_)) { i->targetWeight_ = 0.0f; i->fadeTime_ = EXTRA_ANIM_FADEOUT_TIME; } } }
void SceneResolver::Resolve() { // Nodes do not have component or node ID attributes, so only have to go through components HashSet<StringHash> noIDAttributes; for (HashMap<unsigned, WeakPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i) { Component* component = i->second_; if (!component || noIDAttributes.Contains(component->GetType())) continue; bool hasIDAttributes = false; const Vector<AttributeInfo>* attributes = component->GetAttributes(); if (!attributes) { noIDAttributes.Insert(component->GetType()); continue; } for (unsigned j = 0; j < attributes->Size(); ++j) { const AttributeInfo& info = attributes->At(j); if (info.mode_ & AM_NODEID) { hasIDAttributes = true; unsigned oldNodeID = component->GetAttribute(j).GetUInt(); if (oldNodeID) { HashMap<unsigned, WeakPtr<Node> >::ConstIterator k = nodes_.Find(oldNodeID); if (k != nodes_.End() && k->second_) { unsigned newNodeID = k->second_->GetID(); component->SetAttribute(j, Variant(newNodeID)); } else URHO3D_LOGWARNING("Could not resolve node ID " + String(oldNodeID)); } } else if (info.mode_ & AM_COMPONENTID) { hasIDAttributes = true; unsigned oldComponentID = component->GetAttribute(j).GetUInt(); if (oldComponentID) { HashMap<unsigned, WeakPtr<Component> >::ConstIterator k = components_.Find(oldComponentID); if (k != components_.End() && k->second_) { unsigned newComponentID = k->second_->GetID(); component->SetAttribute(j, Variant(newComponentID)); } else URHO3D_LOGWARNING("Could not resolve component ID " + String(oldComponentID)); } } else if (info.mode_ & AM_NODEIDVECTOR) { hasIDAttributes = true; const VariantVector& oldNodeIDs = component->GetAttribute(j).GetVariantVector(); if (oldNodeIDs.Size()) { // The first index stores the number of IDs redundantly. This is for editing unsigned numIDs = oldNodeIDs[0].GetUInt(); VariantVector newIDs; newIDs.Push(numIDs); for (unsigned k = 1; k < oldNodeIDs.Size(); ++k) { unsigned oldNodeID = oldNodeIDs[k].GetUInt(); HashMap<unsigned, WeakPtr<Node> >::ConstIterator l = nodes_.Find(oldNodeID); if (l != nodes_.End() && l->second_) newIDs.Push(l->second_->GetID()); else { // If node was not found, retain number of elements, just store ID 0 newIDs.Push(0); URHO3D_LOGWARNING("Could not resolve node ID " + String(oldNodeID)); } } component->SetAttribute(j, newIDs); } } } // If component type had no ID attributes, cache this fact for optimization if (!hasIDAttributes) noIDAttributes.Insert(component->GetType()); } // Attributes have been resolved, so no need to remember the nodes after this Reset(); }
void ShaderIR::EliminateDeadCode() { // mark entry points auto MarkUsing = [&](String compName, String userWorld) { if (auto defs = DefinitionsByComponent.TryGetValue(compName)) { if (auto def = defs->TryGetValue(userWorld)) (*def)->IsEntryPoint = true; else { for (auto & world : Shader->Pipeline->WorldDependency[userWorld]()) { if (auto def2 = defs->TryGetValue(world)) { (*def2)->IsEntryPoint = true; break; } } } } }; for (auto & impOp : Shader->Pipeline->SyntaxNode->ImportOperators) for (auto & ref : impOp->Usings) MarkUsing(ref.Content, impOp->DestWorld.Content); for (auto & w : Shader->Pipeline->SyntaxNode->Worlds) for (auto & ref : w->Usings) MarkUsing(ref.Content, w->Name.Content); for (auto & comp : Definitions) if (comp->Implementation->ExportWorlds.Contains(comp->World) || Shader->Pipeline->IsAbstractWorld(comp->World) && (comp->Implementation->SyntaxNode->LayoutAttributes.ContainsKey(L"Pinned") || Shader->Pipeline->Worlds[comp->World]().SyntaxNode->LayoutAttributes.ContainsKey(L"Pinned"))) { comp->IsEntryPoint = true; } List<ComponentDefinitionIR*> workList; HashSet<ComponentDefinitionIR*> referencedDefs; for (auto & def : Definitions) { if (def->IsEntryPoint) { if (referencedDefs.Add(def.Ptr())) workList.Add(def.Ptr()); } } for (int i = 0; i < workList.Count(); i++) { auto def = workList[i]; for (auto & dep : def->Dependency) { if (referencedDefs.Add(dep)) workList.Add(dep); } } List<RefPtr<ComponentDefinitionIR>> newDefinitions; for (auto & def : Definitions) { if (referencedDefs.Contains(def.Ptr())) { newDefinitions.Add(def); EnumerableHashSet<ComponentDefinitionIR*> newSet; for (auto & comp : def->Users) if (referencedDefs.Contains(comp)) { newSet.Add(comp); } def->Users = newSet; newSet.Clear(); for (auto & comp : def->Dependency) if (referencedDefs.Contains(comp)) { newSet.Add(comp); } def->Dependency = newSet; } } Definitions = _Move(newDefinitions); for (auto & kv : DefinitionsByComponent) { for (auto & def : kv.Value) if (!referencedDefs.Contains(def.Value)) kv.Value.Remove(def.Key); } }
/* Generate a shader variant by applying mechanic choice rules and the choice file. The choice file provides "preferred" definitions, as represented in ShaderComponentSymbol::Type::PinnedWorlds The process resolves the component references by picking a pinned definition if one is available, or a definition with the preferred import path as defined by import operator ordering. After all references are resolved, all unreferenced definitions (dead code) are eliminated, resulting a shader variant ready for code generation. */ RefPtr<ShaderIR> GenerateShaderVariantIR(CompileResult & cresult, ShaderClosure * shader, Schedule & schedule, SymbolTable * symbolTable) { RefPtr<ShaderIR> result = new ShaderIR(); result->Shader = shader; result->SymbolTable = symbolTable; // mark pinned worlds for (auto & comp : shader->Components) { for (auto & impl : comp.Value->Implementations) { for (auto & w : impl->Worlds) { if (impl->SrcPinnedWorlds.Contains(w) || impl->SyntaxNode->IsInline() || impl->ExportWorlds.Contains(w) || impl->SyntaxNode->IsInput()) { comp.Value->Type->PinnedWorlds.Add(w); } } } } // apply choices Dictionary<String, ShaderComponentSymbol*> choiceComps; for (auto & comp : shader->AllComponents) { for (auto & choiceName : comp.Value.Symbol->ChoiceNames) choiceComps[choiceName] = comp.Value.Symbol; } HashSet<ShaderComponentImplSymbol*> pinnedImpl; for (auto & choice : schedule.Choices) { ShaderComponentSymbol * comp = nullptr; if (choiceComps.TryGetValue(choice.Key, comp)) { comp->Type->PinnedWorlds.Clear(); for (auto & selectedDef : choice.Value) { if (comp->Type->ConstrainedWorlds.Contains(selectedDef->WorldName)) { comp->Type->PinnedWorlds.Add(selectedDef->WorldName); // find specified impl for (auto & impl : comp->Implementations) { if (impl->Worlds.Contains(selectedDef->WorldName)) pinnedImpl.Add(impl.Ptr()); } } else { cresult.GetErrorWriter()->diagnose(selectedDef.Ptr()->Position, Diagnostics::worldIsNotAValidChoiceForKey, selectedDef->WorldName, choice.Key); } } } } for (auto & attribs : schedule.AddtionalAttributes) { ShaderComponentSymbol * comp = nullptr; if (choiceComps.TryGetValue(attribs.Key, comp)) { // apply attributes for (auto & impl : comp->Implementations) { for (auto & attrib : attribs.Value) { auto modifier = new SimpleAttribute(); modifier->Key = attrib.Key; modifier->Value.Content = attrib.Value; modifier->next = impl->SyntaxNode->modifiers.first; impl->SyntaxNode->modifiers.first = modifier; } } } } // generate definitions Dictionary<ShaderClosure*, ModuleInstanceIR*> moduleInstanceMap; auto createModuleInstance = [&](ShaderClosure * closure) { ModuleInstanceIR * inst; if (moduleInstanceMap.TryGetValue(closure, inst)) return inst; List<String> namePath; auto parent = closure; while (parent) { if (parent->Name.Length()) namePath.Add(parent->Name); else namePath.Add(parent->ModuleSyntaxNode->Name.Content); parent = parent->Parent; } StringBuilder sbBindingName; for (int i = namePath.Count() - 2; i >= 0; i--) { sbBindingName << namePath[i]; if (i > 0) sbBindingName << "."; } // if this is the root module, its binding name is module name. if (namePath.Count() == 1) sbBindingName << namePath[0]; inst = new ModuleInstanceIR(); inst->SyntaxNode = closure->ModuleSyntaxNode; inst->BindingIndex = closure->BindingIndex; inst->UsingPosition = closure->UsingPosition; result->ModuleInstances.Add(inst); inst->BindingName = sbBindingName.ProduceString(); moduleInstanceMap[closure] = inst; return inst; }; for (auto & comp : shader->AllComponents) { EnumerableDictionary<String, ComponentDefinitionIR*> defs; Dictionary<String, ShaderComponentImplSymbol*> impls; for (auto & impl : comp.Value.Symbol->Implementations) { auto createComponentDef = [&](const String & w) { RefPtr<ComponentDefinitionIR> def = new ComponentDefinitionIR(); def->OriginalName = comp.Value.Symbol->Name; def->UniqueKey = comp.Value.Symbol->UniqueKey; def->UniqueName = comp.Value.Symbol->UniqueName; def->Type = comp.Value.Symbol->Type->DataType; def->IsEntryPoint = (impl->ExportWorlds.Contains(w) || impl->SyntaxNode->IsParam() || (shader->Pipeline->IsAbstractWorld(w) && (impl->SyntaxNode->HasSimpleAttribute("Pinned") || shader->Pipeline->Worlds[w]()->HasSimpleAttribute("Pinned")))); CloneContext cloneCtx; def->SyntaxNode = impl->SyntaxNode->Clone(cloneCtx); def->World = w; def->ModuleInstance = createModuleInstance(comp.Value.Closure); return def; }; // parameter component will only have one defintion that is shared by all worlds if (impl->SyntaxNode->IsParam()) { auto def = createComponentDef("<uniform>"); result->Definitions.Add(def); defs["<uniform>"] = def.Ptr(); } else { for (auto & w : impl->Worlds) { auto def = createComponentDef(w); result->Definitions.Add(def); bool existingDefIsPinned = false; if (defs.ContainsKey(w)) existingDefIsPinned = pinnedImpl.Contains(impls[w]()); if (!existingDefIsPinned) { defs[w] = def.Ptr(); impls[w] = impl.Ptr(); } } } } result->DefinitionsByComponent[comp.Key] = defs; } bool changed = true; while (changed) { changed = false; result->ResolveComponentReference(); result->EliminateDeadCode(); // check circular references for (auto & def : result->Definitions) { if (def->Dependency.Contains(def.Ptr())) { cresult.GetErrorWriter()->diagnose(def->SyntaxNode->Position, Diagnostics::componentDefinitionCircularity, def->OriginalName); return nullptr; } } /* // eliminate redundant (downstream) definitions, one at a time auto comps = result->GetComponentDependencyOrder(); for (int i = comps.Count() - 1; i >= 0; i--) { auto comp = comps[i]; auto & defs = result->DefinitionsByComponent[comp->UniqueName](); EnumerableHashSet<ComponentDefinitionIR*> removedDefs; for (auto & def : defs) if (!def.Value->IsEntryPoint && !comp->Type->PinnedWorlds.Contains(def.Value->World)) { for (auto & otherDef : defs) { if (otherDef.Value != def.Value && !removedDefs.Contains(otherDef.Value) && shader->Pipeline->IsWorldReachable(otherDef.Value->World, def.Value->World)) { removedDefs.Add(def.Value); break; } } } if (removedDefs.Count()) { result->RemoveDefinitions([&](ComponentDefinitionIR* def) {return removedDefs.Contains(def); }); changed = true; } } */ } return result; }