Skip to content

Opt-out switch for DynamicResource optimization #9501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ public static bool DisableFluentThemeWindowBackdrop
return LocalAppContext.GetCachedSwitchValue(DisableFluentThemeWindowBackdropSwitchName, ref _DisableFluentThemeWindowBackdrop);
}
}


// Switch to disable DynamicResource optimizations
internal const string DisableDynamicResourceOptimizationSwitchName = "Switch.System.Windows.Controls.DisableDynamicResourceOptimization";
private static int _DisableDynamicResourceOptimization;
public static bool DisableDynamicResourceOptimization
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return LocalAppContext.GetCachedSwitchValue(DisableDynamicResourceOptimizationSwitchName, ref _DisableDynamicResourceOptimization);
}
}
}

#pragma warning restore 436
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1735,20 +1735,43 @@ private object FetchResource(
DeferredResourceReference deferredResourceReference;
if (!IsThemeDictionary)
{
// Cache the deferredResourceReference so that it can be validated
// in case of a dictionary change prior to its inflation
_deferredResourceReferences ??= new DeferredResourceReferenceList();

if (_deferredResourceReferences.Get(resourceKey) is { } existingDeferredResourceReference
&& existingDeferredResourceReference.Dictionary == this)
if (FrameworkAppContextSwitches.DisableDynamicResourceOptimization)
{
deferredResourceReference = existingDeferredResourceReference;
if (_ownerApps is not null)
{
deferredResourceReference = new DeferredAppResourceReference(this, resourceKey);
}
else
{
deferredResourceReference = new DeferredResourceReference(this, resourceKey);
}

// Cache the deferredResourceReference so that it can be validated
// in case of a dictionary change prior to its inflation
if (_weakDeferredResourceReferences is null)
{
_weakDeferredResourceReferences = new WeakReferenceList();
}

_weakDeferredResourceReferences.Add(deferredResourceReference, true /*SkipFind*/);
}
else
{
deferredResourceReference = _ownerApps is not null ? new DeferredAppResourceReference(this, resourceKey) : new DeferredResourceReference(this, resourceKey);
// Cache the deferredResourceReference so that it can be validated
// in case of a dictionary change prior to its inflation
_deferredResourceReferencesList ??= new DeferredResourceReferenceList();

if (_deferredResourceReferencesList.Get(resourceKey) is { } existingDeferredResourceReference
&& existingDeferredResourceReference.Dictionary == this)
{
deferredResourceReference = existingDeferredResourceReference;
}
else
{
deferredResourceReference = _ownerApps is not null ? new DeferredAppResourceReference(this, resourceKey) : new DeferredResourceReference(this, resourceKey);

_deferredResourceReferences.AddOrSet(deferredResourceReference);
_deferredResourceReferencesList.AddOrSet(deferredResourceReference);
}
}
}
else
Expand All @@ -1771,35 +1794,55 @@ private object FetchResource(
/// </summary>
private void ValidateDeferredResourceReferences(object resourceKey)
{
if (_deferredResourceReferences is null)
if (FrameworkAppContextSwitches.DisableDynamicResourceOptimization)
{
return;
}

if (resourceKey is null)
{
foreach (DeferredResourceReference deferredResourceReference in _deferredResourceReferences)
if (_weakDeferredResourceReferences != null)
{
Inflate(deferredResourceReference);
foreach (Object o in _weakDeferredResourceReferences)
{
DeferredResourceReference deferredResourceReference = o as DeferredResourceReference;
if (deferredResourceReference != null && (resourceKey == null || Object.Equals(resourceKey, deferredResourceReference.Key)))
{
// This will inflate the deferred reference, causing it
// to be removed from the list. The list may also be
// purged of dead references.
deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown);
}
}
}
}
else
{
DeferredResourceReference deferredResourceReference = _deferredResourceReferences.Get(resourceKey);
if (_deferredResourceReferencesList is null)
{
return;
}

Inflate(deferredResourceReference);
}
if (resourceKey is null)
{
foreach (DeferredResourceReference deferredResourceReference in _deferredResourceReferencesList)
{
Inflate(deferredResourceReference);
}
}
else
{
DeferredResourceReference deferredResourceReference = _deferredResourceReferencesList.Get(resourceKey);

return;
Inflate(deferredResourceReference);
}

void Inflate(DeferredResourceReference deferredResourceReference)
{
if (deferredResourceReference is not null)
return;

void Inflate(DeferredResourceReference deferredResourceReference)
{
// This will inflate the deferred reference, causing it
// to be removed from the list. The list may also be
// purged of dead references.
deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown);
if (deferredResourceReference is not null)
{
// This will inflate the deferred reference, causing it
// to be removed from the list. The list may also be
// purged of dead references.
deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown);
}
}
}
}
Expand Down Expand Up @@ -2066,9 +2109,14 @@ internal WeakReferenceList ApplicationOwners

#region Properties

internal DeferredResourceReferenceList DeferredResourceReferences
internal WeakReferenceList WeakDeferredResourceReferences
{
get { return _deferredResourceReferences; }
get { return _weakDeferredResourceReferences; }
}

internal DeferredResourceReferenceList DeferredResourceReferencesList
{
get { return _deferredResourceReferencesList; }
}

#endregion Properties
Expand Down Expand Up @@ -2485,13 +2533,30 @@ private void CopyDeferredContentFrom(ResourceDictionary loadedRD)

private void MoveDeferredResourceReferencesFrom(ResourceDictionary loadedRD)
{
// copy the list
_deferredResourceReferences = loadedRD._deferredResourceReferences;
if (FrameworkAppContextSwitches.DisableDynamicResourceOptimization)
{
// copy the list
_weakDeferredResourceReferences = loadedRD._weakDeferredResourceReferences;

// redirect each entry toward its new owner
if (_deferredResourceReferences != null)
// redirect each entry toward its new owner
if (_weakDeferredResourceReferences != null)
{
foreach (DeferredResourceReference drr in _weakDeferredResourceReferences)
{
drr.Dictionary = this;
}
}
}
else
{
_deferredResourceReferences.ChangeDictionary(this);
// copy the list
_deferredResourceReferencesList = loadedRD._deferredResourceReferencesList;

// redirect each entry toward its new owner
if (_deferredResourceReferencesList != null)
{
_deferredResourceReferencesList.ChangeDictionary(this);
}
}
}

Expand Down Expand Up @@ -2558,7 +2623,8 @@ private enum FallbackState
private WeakReferenceList _ownerFEs = null;
private WeakReferenceList _ownerFCEs = null;
private WeakReferenceList _ownerApps = null;
private DeferredResourceReferenceList _deferredResourceReferences = null;
private WeakReferenceList _weakDeferredResourceReferences = null;
private DeferredResourceReferenceList _deferredResourceReferencesList = null;
private ObservableCollection<ResourceDictionary> _mergedDictionaries = null;
private Uri _source = null;
private Uri _baseUri = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,17 @@ private void InvalidateCacheValue()
}
}

// This will inflate the deferred reference, causing it
// to be removed from the list. The list may also be
// purged of dead references.
deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown);
if (FrameworkAppContextSwitches.DisableDynamicResourceOptimization)
{
deferredResourceReference.RemoveFromDictionary();
}
else
{
// This will inflate the deferred reference, causing it
// to be removed from the list. The list may also be
// purged of dead references.
deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown);
}
}

StopListeningForFreezableChanges(resource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1744,9 +1744,17 @@ internal override object GetValue(BaseValueSourceInternal valueSource)
{
// Note that we are replacing the _keyorValue field
// with the value and deleting the _dictionary field.
RemoveFromDictionary();
// Update after removal from dictionary as we need the key for proper removal
_keyOrValue = value;
if (FrameworkAppContextSwitches.DisableDynamicResourceOptimization)
{
_keyOrValue = value;
RemoveFromDictionary();
}
else
{
RemoveFromDictionary();
// Update after removal from dictionary as we need the key for proper removal
_keyOrValue = value;
}
}

// Freeze if this value originated from a style or template
Expand Down Expand Up @@ -1804,11 +1812,18 @@ internal override Type GetValueType()
}

// remove this DeferredResourceReference from its ResourceDictionary
protected virtual void RemoveFromDictionary()
internal virtual void RemoveFromDictionary()
{
if (_dictionary != null)
{
_dictionary.DeferredResourceReferences.Remove(this);
if (FrameworkAppContextSwitches.DisableDynamicResourceOptimization)
{
_dictionary.WeakDeferredResourceReferences.Remove(this);
}
else
{
_dictionary.DeferredResourceReferencesList.Remove(this);
}
_dictionary = null;
}
}
Expand Down Expand Up @@ -1974,7 +1989,7 @@ internal override Type GetValueType()
}

// remove this DeferredResourceReference from its ResourceDictionary
protected override void RemoveFromDictionary()
internal override void RemoveFromDictionary()
{
// DeferredThemeResourceReferences are never added to the dictionary's
// list of deferred references, so they don't need to be removed.
Expand Down
Loading