mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
Performance improvement for Value change notifications.
This commit is contained in:
parent
07e7188eb1
commit
66ecd6bc7c
3 changed files with 92 additions and 60 deletions
|
|
@ -23,39 +23,100 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
class SharedValueSourceUpdater : public ReferenceCountedObject,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
SharedValueSourceUpdater() : insideCallback (false) {}
|
||||
|
||||
typedef ReferenceCountedObjectPtr<SharedValueSourceUpdater> Ptr;
|
||||
|
||||
void update (Value::ValueSource* source)
|
||||
{
|
||||
sourcesToUpdate.add (source);
|
||||
|
||||
if (! insideCallback)
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
static SharedValueSourceUpdater* getOrCreateSharedUpdater()
|
||||
{
|
||||
Ptr& p = getSharedUpdater();
|
||||
|
||||
if (p == nullptr)
|
||||
p = new SharedValueSourceUpdater();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void releaseIfUnused()
|
||||
{
|
||||
if (Ptr& p = getSharedUpdater())
|
||||
if (p->getReferenceCount() == 1)
|
||||
p = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
ReferenceCountedArray<Value::ValueSource> sourcesToUpdate;
|
||||
bool insideCallback;
|
||||
|
||||
static Ptr& getSharedUpdater()
|
||||
{
|
||||
static Ptr updater;
|
||||
return updater;
|
||||
}
|
||||
|
||||
void handleAsyncUpdate()
|
||||
{
|
||||
int maxLoops = 10;
|
||||
const ScopedValueSetter<bool> inside (insideCallback, true, false);
|
||||
const Ptr localRef (this);
|
||||
|
||||
while (sourcesToUpdate.size() > 0 && --maxLoops >= 0)
|
||||
{
|
||||
ReferenceCountedArray<Value::ValueSource> sources;
|
||||
sources.swapWithArray (sourcesToUpdate);
|
||||
|
||||
for (int i = 0; i < sources.size(); ++i)
|
||||
sources.getObjectPointerUnchecked(i)->sendChangeMessage (true);
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedValueSourceUpdater);
|
||||
};
|
||||
|
||||
Value::ValueSource::ValueSource()
|
||||
{
|
||||
}
|
||||
|
||||
Value::ValueSource::~ValueSource()
|
||||
{
|
||||
asyncUpdater = nullptr;
|
||||
SharedValueSourceUpdater::releaseIfUnused();
|
||||
}
|
||||
|
||||
void Value::ValueSource::sendChangeMessage (const bool synchronous)
|
||||
{
|
||||
if (synchronous)
|
||||
const int numListeners = valuesWithListeners.size();
|
||||
|
||||
if (numListeners > 0)
|
||||
{
|
||||
// (hold a local reference to this object in case it's freed during the callbacks)
|
||||
const ReferenceCountedObjectPtr<ValueSource> localRef (this);
|
||||
|
||||
for (int i = valuesWithListeners.size(); --i >= 0;)
|
||||
if (synchronous)
|
||||
{
|
||||
Value* const v = valuesWithListeners[i];
|
||||
asyncUpdater = nullptr;
|
||||
const ReferenceCountedObjectPtr<ValueSource> localRef (this);
|
||||
|
||||
if (v != nullptr)
|
||||
v->callListeners();
|
||||
for (int i = numListeners; --i >= 0;)
|
||||
if (Value* const v = valuesWithListeners[i])
|
||||
v->callListeners();
|
||||
}
|
||||
else if (asyncUpdater == nullptr)
|
||||
{
|
||||
SharedValueSourceUpdater* const updater = SharedValueSourceUpdater::getOrCreateSharedUpdater();
|
||||
asyncUpdater = updater;
|
||||
updater->update (this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (valuesWithListeners.size() > 0)
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void Value::ValueSource::handleAsyncUpdate()
|
||||
{
|
||||
sendChangeMessage (true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -169,8 +169,7 @@ public:
|
|||
of a ValueSource object. If you're feeling adventurous, you can create your own custom
|
||||
ValueSource classes to allow Value objects to represent your own custom data items.
|
||||
*/
|
||||
class JUCE_API ValueSource : public SingleThreadedReferenceCountedObject,
|
||||
private AsyncUpdater
|
||||
class JUCE_API ValueSource : public SingleThreadedReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
ValueSource();
|
||||
|
|
@ -196,8 +195,7 @@ public:
|
|||
//==============================================================================
|
||||
friend class Value;
|
||||
SortedSet <Value*> valuesWithListeners;
|
||||
|
||||
void handleAsyncUpdate();
|
||||
ReferenceCountedObjectPtr<ReferenceCountedObject> asyncUpdater;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -60,11 +60,8 @@ public:
|
|||
void sendPropertyChangeMessage (ValueTree& tree, const Identifier& property)
|
||||
{
|
||||
for (int i = valueTreesWithListeners.size(); --i >= 0;)
|
||||
{
|
||||
ValueTree* const v = valueTreesWithListeners[i];
|
||||
if (v != nullptr)
|
||||
if (ValueTree* const v = valueTreesWithListeners[i])
|
||||
v->listeners.call (&ValueTree::Listener::valueTreePropertyChanged, tree, property);
|
||||
}
|
||||
}
|
||||
|
||||
void sendPropertyChangeMessage (const Identifier& property)
|
||||
|
|
@ -78,11 +75,8 @@ public:
|
|||
void sendChildAddedMessage (ValueTree& tree, ValueTree& child)
|
||||
{
|
||||
for (int i = valueTreesWithListeners.size(); --i >= 0;)
|
||||
{
|
||||
ValueTree* const v = valueTreesWithListeners[i];
|
||||
if (v != nullptr)
|
||||
if (ValueTree* const v = valueTreesWithListeners[i])
|
||||
v->listeners.call (&ValueTree::Listener::valueTreeChildAdded, tree, child);
|
||||
}
|
||||
}
|
||||
|
||||
void sendChildAddedMessage (ValueTree child)
|
||||
|
|
@ -96,11 +90,8 @@ public:
|
|||
void sendChildRemovedMessage (ValueTree& tree, ValueTree& child)
|
||||
{
|
||||
for (int i = valueTreesWithListeners.size(); --i >= 0;)
|
||||
{
|
||||
ValueTree* const v = valueTreesWithListeners[i];
|
||||
if (v != nullptr)
|
||||
if (ValueTree* const v = valueTreesWithListeners[i])
|
||||
v->listeners.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child);
|
||||
}
|
||||
}
|
||||
|
||||
void sendChildRemovedMessage (ValueTree child)
|
||||
|
|
@ -114,11 +105,8 @@ public:
|
|||
void sendChildOrderChangedMessage (ValueTree& tree)
|
||||
{
|
||||
for (int i = valueTreesWithListeners.size(); --i >= 0;)
|
||||
{
|
||||
ValueTree* const v = valueTreesWithListeners[i];
|
||||
if (v != nullptr)
|
||||
if (ValueTree* const v = valueTreesWithListeners[i])
|
||||
v->listeners.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree);
|
||||
}
|
||||
}
|
||||
|
||||
void sendChildOrderChangedMessage()
|
||||
|
|
@ -134,18 +122,12 @@ public:
|
|||
ValueTree tree (this);
|
||||
|
||||
for (int j = children.size(); --j >= 0;)
|
||||
{
|
||||
SharedObject* const child = children.getObjectPointer (j);
|
||||
if (child != nullptr)
|
||||
if (SharedObject* const child = children.getObjectPointer (j))
|
||||
child->sendParentChangeMessage();
|
||||
}
|
||||
|
||||
for (int i = valueTreesWithListeners.size(); --i >= 0;)
|
||||
{
|
||||
ValueTree* const v = valueTreesWithListeners[i];
|
||||
if (v != nullptr)
|
||||
if (ValueTree* const v = valueTreesWithListeners[i])
|
||||
v->listeners.call (&ValueTree::Listener::valueTreeParentChanged, tree);
|
||||
}
|
||||
}
|
||||
|
||||
const var& getProperty (const Identifier& name) const noexcept
|
||||
|
|
@ -167,9 +149,7 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
const var* const existingValue = properties.getVarPointer (name);
|
||||
|
||||
if (existingValue != nullptr)
|
||||
if (const var* const existingValue = properties.getVarPointer (name))
|
||||
{
|
||||
if (*existingValue != newValue)
|
||||
undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false));
|
||||
|
|
@ -270,16 +250,10 @@ public:
|
|||
|
||||
bool isAChildOf (const SharedObject* const possibleParent) const noexcept
|
||||
{
|
||||
const SharedObject* p = parent;
|
||||
|
||||
while (p != nullptr)
|
||||
{
|
||||
for (const SharedObject* p = parent; p != nullptr; p = p->parent)
|
||||
if (p == possibleParent)
|
||||
return true;
|
||||
|
||||
p = p->parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -611,10 +585,9 @@ public:
|
|||
|
||||
UndoableAction* createCoalescedAction (UndoableAction* nextAction)
|
||||
{
|
||||
MoveChildAction* next = dynamic_cast <MoveChildAction*> (nextAction);
|
||||
|
||||
if (next != nullptr && next->parent == parent && next->startIndex == endIndex)
|
||||
return new MoveChildAction (parent, startIndex, next->endIndex);
|
||||
if (MoveChildAction* next = dynamic_cast <MoveChildAction*> (nextAction))
|
||||
if (next->parent == parent && next->startIndex == endIndex)
|
||||
return new MoveChildAction (parent, startIndex, next->endIndex);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue