private static void BrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // The first change to the default value of a mutable collection property (e.g. GeometryGroup.Children) // will promote the property value from a default value to a local value. This is technically a sub-property // change because the collection was changed and not a new collection set (GeometryGroup.Children. // Add versus GeometryGroup.Children = myNewChildrenCollection). However, we never marshalled // the default value to the compositor. If the property changes from a default value, the new local value // needs to be marshalled to the compositor. We detect this scenario with the second condition // e.OldValueSource != e.NewValueSource. Specifically in this scenario the OldValueSource will be // Default and the NewValueSource will be Local. if (e.IsASubPropertyChange && (e.OldValueSource == e.NewValueSource)) { return; } SpecularMaterial target = ((SpecularMaterial)d); Brush oldV = (Brush)e.OldValue; Brush newV = (Brush)e.NewValue; System.Windows.Threading.Dispatcher dispatcher = target.Dispatcher; if (dispatcher != null) { DUCE.IResource targetResource = (DUCE.IResource)target; using (CompositionEngineLock.Acquire()) { int channelCount = targetResource.GetChannelCount(); for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) { DUCE.Channel channel = targetResource.GetChannel(channelIndex); Debug.Assert(!channel.IsOutOfBandChannel); Debug.Assert(!targetResource.GetHandle(channel).IsNull); target.ReleaseResource(oldV, channel); target.AddRefResource(newV, channel); } } } target.PropertyChanged(BrushProperty); }
/// <summary> /// ReleaseOnChannel /// </summary> void DUCE.IResource.ReleaseOnChannel(DUCE.Channel channel) { // reconsider the need for this lock using (CompositionEngineLock.Acquire()) { Debug.Assert(_duceResource.IsOnChannel(channel)); //release from this channel _duceResource.ReleaseOnChannel(channel); if (!_duceResource.IsOnAnyChannel) { // If this was the last reference on the channel then clear up our state. // Again, we assume here that if the target DependencyObject is animated that // it will be associated with a Dispatcher and that this animation resource // will also be associated with that Dispatcher's channel. DependencyObject d = (DependencyObject)_dependencyObject.Target; // DependencyObject shouldn't have been garbage collected before we've // released all of its property animation resources. Debug.Assert(d != null); // The target DependencyObject should be associated with a Dispatcher. Debug.Assert(d.Dispatcher != null); // Make sure the target belongs to this thread Debug.Assert(d.CheckAccess()); // If we're invalid, that means we've added our _updateResourceHandler to the // MediaContext's ResourcesUpdated event. Since we've been entirely released // from the channel we can cancel this update by removing the handler. if (!_isValid) { MediaContext mediaContext = MediaContext.From(d.Dispatcher); mediaContext.ResourcesUpdated -= _updateResourceHandler; _isValid = true; } _updateResourceHandler = null; } } }
public static bool Startup(MediaContext mc) { // // Note to stress triagers: // // This call will fail if PresentationCore.dll and milcore.dll have mismatched // versions -- please make sure that both binaries have been properly built // and deployed. // // *** Failure here does NOT indicate a bug in MediaContext.Startup! *** // HRESULT.Check(UnsafeNativeMethods.MilVersionCheck(MS.Internal.Composition.Version.MilSdkVersion)); using (CompositionEngineLock.Acquire()) { _mediaContexts.Add(mc); //Is this the first startup? if (0 == s_refCount) { HRESULT.Check(SafeNativeMethods.MilCompositionEngine_InitializePartitionManager( 0 // THREAD_PRIORITY_NORMAL )); s_forceSoftareForGraphicsStreamMagnifier = UnsafeNativeMethods.WgxConnection_ShouldForceSoftwareForGraphicsStreamClient(); ConnectTransport(); // Read a flag from the registry to determine whether we should run // animation smoothing code. ReadAnimationSmoothingSetting(); } s_refCount++; } // Consider making MediaSystem.ConnectTransport return the state of transport connectedness so // that we can initialize the media system to a disconnected state. return(true); }
internal static void Shutdown(MediaContext mc) { using (CompositionEngineLock.Acquire()) { Debug.Assert(s_refCount > 0); _mediaContexts.Remove(mc); s_refCount--; if (0 == s_refCount) { // We can shut-down. // Debug.WriteLine("MediSystem::NotifyDisconnect Stop Transport\n"); if (IsTransportConnected) { DisconnectTransport(); } HRESULT.Check(SafeNativeMethods.MilCompositionEngine_DeinitializePartitionManager()); } } }
/// <summary> /// /// </summary> internal override void FreeContent(DUCE.Channel channel) { // // Disconnect hosted visual from this channel. // using (CompositionEngineLock.Acquire()) { // if there's a pending disconnect, do it now preemptively; // otherwise do the disconnect the normal way. // This ensures we do the disconnect before calling base, // as required. if (!DoPendingDisconnect(channel)) { DisconnectHostedVisual( channel, /* removeChannelFromCollection */ true); } } base.FreeContent(channel); }
void DUCE.IResource.ReleaseOnChannel(DUCE.Channel channel) { using (CompositionEngineLock.Acquire()) { Debug.Assert(_duceResource.IsOnChannel(channel)); if (_duceResource.ReleaseOnChannel(channel)) { Brush vBrush = Brush; if (vBrush != null) { ((DUCE.IResource)vBrush).ReleaseOnChannel(channel); } DashStyle vDashStyle = DashStyle; if (vDashStyle != null) { ((DUCE.IResource)vDashStyle).ReleaseOnChannel(channel); } ReleaseOnChannelAnimations(channel); } } }
void DUCE.IResource.ReleaseOnChannel(DUCE.Channel channel) { using (CompositionEngineLock.Acquire()) { Debug.Assert(_duceResource.IsOnChannel(channel)); // AddRef'ing or Releasing the renderdata itself doesn't propgate through the dependents, // unless our ref goes from or to 0. This is why we have this if statement guarding // the inner loop. if (_duceResource.ReleaseOnChannel(channel)) { for (int i = 0; i < _dependentResources.Count; i++) { DUCE.IResource resource = _dependentResources[i] as DUCE.IResource; if (resource != null) { resource.ReleaseOnChannel(channel); } } } } }
/// <summary> /// AddRefOnChannel /// </summary> DUCE.ResourceHandle DUCE.IResource.AddRefOnChannel(DUCE.Channel channel) { // reconsider the need for this lock using (CompositionEngineLock.Acquire()) { #if DEBUG // We assume that a multi-channel resource can only be multi-channel // if it is Frozen and does not have animated properties. In this case we know // the target resource has at least one animated property so we expect that this // independently animated property resource will only be added to the channel // associated with the MediaContext associated with the target object's Dispatcher. DependencyObject d = (DependencyObject)_dependencyObject.Target; // I'm not sure how our target animated DependencyObject would get garbage // collected before we call AddRefOnChannel on one of its animated property // resources, but if it happens it will be a bad thing. Debug.Assert(d != null); // Any animated DependencyObject must be associated with a Dispatcher because the // AnimationClocks doing the animating must be associated with a Dispatcher. Debug.Assert(d.Dispatcher != null); // Make sure the target belongs to this thread Debug.Assert(d.CheckAccess()); #endif if (_duceResource.CreateOrAddRefOnChannel(this, channel, ResourceType)) { _updateResourceHandler = new MediaContext.ResourcesUpdatedHandler(UpdateResource); UpdateResourceCore(channel); } return(_duceResource.GetHandle(channel)); } }
//------------------------------------------------------ // // Public Properties // //------------------------------------------------------ private static void ChildrenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // The first change to the default value of a mutable collection property (e.g. GeometryGroup.Children) // will promote the property value from a default value to a local value. This is technically a sub-property // change because the collection was changed and not a new collection set (GeometryGroup.Children. // Add versus GeometryGroup.Children = myNewChildrenCollection). However, we never marshalled // the default value to the compositor. If the property changes from a default value, the new local value // needs to be marshalled to the compositor. We detect this scenario with the second condition // e.OldValueSource != e.NewValueSource. Specifically in this scenario the OldValueSource will be // Default and the NewValueSource will be Local. if (e.IsASubPropertyChange && (e.OldValueSource == e.NewValueSource)) { return; } Model3DGroup target = ((Model3DGroup)d); // If this is both non-null and mutable, we need to unhook the Changed event. Model3DCollection oldCollection = null; Model3DCollection newCollection = null; if ((e.OldValueSource != BaseValueSourceInternal.Default) || e.IsOldValueModified) { oldCollection = (Model3DCollection)e.OldValue; if ((oldCollection != null) && !oldCollection.IsFrozen) { oldCollection.ItemRemoved -= target.ChildrenItemRemoved; oldCollection.ItemInserted -= target.ChildrenItemInserted; } } // If this is both non-null and mutable, we need to hook the Changed event. if ((e.NewValueSource != BaseValueSourceInternal.Default) || e.IsNewValueModified) { newCollection = (Model3DCollection)e.NewValue; if ((newCollection != null) && !newCollection.IsFrozen) { newCollection.ItemInserted += target.ChildrenItemInserted; newCollection.ItemRemoved += target.ChildrenItemRemoved; } } if (oldCollection != newCollection && target.Dispatcher != null) { using (CompositionEngineLock.Acquire()) { DUCE.IResource targetResource = (DUCE.IResource)target; int channelCount = targetResource.GetChannelCount(); for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) { DUCE.Channel channel = targetResource.GetChannel(channelIndex); Debug.Assert(!channel.IsOutOfBandChannel); Debug.Assert(!targetResource.GetHandle(channel).IsNull); // resource shouldn't be null because // 1) If the field is one of our collections, we don't allow null elements // 2) Codegen already made sure the collection contains DUCE.IResources // ... so we'll Assert it if (newCollection != null) { int count = newCollection.Count; for (int i = 0; i < count; i++) { DUCE.IResource resource = newCollection.Internal_GetItem(i) as DUCE.IResource; Debug.Assert(resource != null); resource.AddRefOnChannel(channel); } } if (oldCollection != null) { int count = oldCollection.Count; for (int i = 0; i < count; i++) { DUCE.IResource resource = oldCollection.Internal_GetItem(i) as DUCE.IResource; Debug.Assert(resource != null); resource.ReleaseOnChannel(channel); } } } } } target.PropertyChanged(ChildrenProperty); }
//---------------------------------------------------------------------- // // Private Methods // //---------------------------------------------------------------------- #region Private Methods /// <summary> /// Connects the hosted visual on a channel if necessary. /// </summary> private void EnsureHostedVisualConnected(DUCE.Channel channel) { // // Conditions for connecting VisualTarget to Host Visual:- // 1. The channel on which we are rendering should not be synchronous. This // scenario is not supported currently. // 2. VisualTarget should not be null. // 3. They should not be already connected. // if (!(channel.IsSynchronous) && _target != null && !_connectedChannels.Contains(channel)) { Debug.Assert(IsOnChannel(channel)); DUCE.ResourceHandle targetsHandle = DUCE.ResourceHandle.Null; bool doDuplication = true; // // If HostVisual and VisualTarget are on same thread, then we just addref // VisualTarget. Otherwise, if on different threads, then we duplicate // VisualTarget onto Hostvisual's channel. // if (_target.CheckAccess()) { Debug.Assert(_target._contentRoot.IsOnChannel(channel)); Debug.Assert(_target.OutOfBandChannel == MediaContext.CurrentMediaContext.OutOfBandChannel); bool created = _target._contentRoot.CreateOrAddRefOnChannel(this, channel, VisualTarget.s_contentRootType); Debug.Assert(!created); targetsHandle = _target._contentRoot.GetHandle(channel); } else { // // Duplicate the target's handle onto our channel. // // We must wait synchronously for the _targets Dispatcher to call // back and do handle duplication. We can't do handle duplication // on this thread because access to the _target CompositionNode // is not synchronized. If we duplicated here, we could potentially // corrupt the _target OutOfBandChannel or the CompositionNode // MultiChannelResource. We have to wait synchronously because // we need the resulting duplicated handle to hook up as a child // to this HostVisual. // object returnValue = _target.Dispatcher.Invoke( DispatcherPriority.Normal, TimeSpan.FromMilliseconds(1000), new DispatcherOperationCallback(DoHandleDuplication), channel ); // // Duplication and flush is complete, we can resume processing // Only if the Invoke succeeded will we have a handle returned. // if (returnValue != null) { targetsHandle = (DUCE.ResourceHandle)returnValue; } else { // The Invoke didn't complete doDuplication = false; } } if (doDuplication) { if (!targetsHandle.IsNull) { using (CompositionEngineLock.Acquire()) { DUCE.CompositionNode.InsertChildAt( _proxy.GetHandle(channel), targetsHandle, 0, channel); } _connectedChannels.Add(channel); // // Indicate that that content composition root has been // connected, this needs to be taken into account to // properly manage children of this visual. // SetFlags(channel, true, VisualProxyFlags.IsContentNodeConnected); } } else { // // We didn't get a handle, because _target belongs to a // different thread, and the Invoke operation failed. We can't do // anything except try again in the next render pass. We can't // call Invalidate during the render pass because it pushes up // flags that are being modified within the render pass, so get // the local Dispatcher to do it for us later. // Dispatcher.BeginInvoke( DispatcherPriority.Normal, (DispatcherOperationCallback) delegate(object args) { Invalidate(); return(null); }, null ); } } }
/// <summary> /// This should be called at the end of any method that alters the /// storage in any way. This method will make sure the peer dp is /// set correctly and notify Animatables if something changes. /// </summary> internal void WritePostscript() { DependencyObject d = (DependencyObject)_dependencyObject.Target; if (d == null) { return; } FrugalMap animatedPropertyMap = AnimatedPropertyMapField.GetValue(d); if (animatedPropertyMap.Count == 0 || animatedPropertyMap[_dependencyProperty.GlobalIndex] == DependencyProperty.UnsetValue) { if (!IsEmpty) { // This is kind of tricky: // // Because FrugalMap is a struct instead of a class, we must // be sure to add this AnimationStorage to the map before // setting the FrugalMap into the UncommonField storage. If // we don't and the FrugalMap is empty then an empty FrugalMap // will be set into the UncommonField storage. If we were to // then add our AnimationStorage to the local FrugalMap, it // would only allocate its own internal storage at that point // which would not apply to the FrugalMap we set into the // UncommonField storage. Once a FrugalMap has allocated its // internal storage, though, that storage is copied with the // FrugalMap. This is what will happen when we add our // AnimationStorage to the FrugalMap first as we do below: animatedPropertyMap[_dependencyProperty.GlobalIndex] = this; // Since FrugalMap is a struct and adding a new value to it // may re-allocate the storage, we need to set this value // each time we make a change to the map. AnimatedPropertyMapField.SetValue(d, animatedPropertyMap); if (animatedPropertyMap.Count == 1) { d.IAnimatable_HasAnimatedProperties = true; } // If this the target is an Animatable we'll need to // invalidate it so that the animation resource for this // newly animated property will be passed across to the UCE. Animatable a = d as Animatable; if (a != null) { a.RegisterForAsyncUpdateResource(); } // If this AnimationStorage is a resource, add it to the // channel now. DUCE.IResource animationResource = this as DUCE.IResource; if (animationResource != null) { DUCE.IResource targetResource = d as DUCE.IResource; if (targetResource != null) { using (CompositionEngineLock.Acquire()) { int channelCount = targetResource.GetChannelCount(); for (int i = 0; i < channelCount; i++) { DUCE.Channel channel = targetResource.GetChannel(i); if (!targetResource.GetHandle(channel).IsNull) { animationResource.AddRefOnChannel(channel); } } } } } } } else { Debug.Assert(animatedPropertyMap.Count > 0); Debug.Assert(animatedPropertyMap[_dependencyProperty.GlobalIndex] != DependencyProperty.UnsetValue); if (IsEmpty) { // If this AnimationStorage is a resource, release it from // the channel now. DUCE.IResource animationResource = this as DUCE.IResource; if (animationResource != null) { DUCE.IResource targetResource = d as DUCE.IResource; if (targetResource != null) { using (CompositionEngineLock.Acquire()) { int channelCount = targetResource.GetChannelCount(); for (int i = 0; i < channelCount; i++) { DUCE.Channel channel = targetResource.GetChannel(i); if (!targetResource.GetHandle(channel).IsNull) { animationResource.ReleaseOnChannel(channel); } } } } } // If this the target is an Animatable we'll need to // invalidate it so that the animation resource for this // no longer animated property will no longer be passed // across to the UCE. Animatable a = d as Animatable; if (a != null) { a.RegisterForAsyncUpdateResource(); } animatedPropertyMap[_dependencyProperty.GlobalIndex] = DependencyProperty.UnsetValue; if (animatedPropertyMap.Count == 0) { AnimatedPropertyMapField.ClearValue(d); d.IAnimatable_HasAnimatedProperties = false; } else { AnimatedPropertyMapField.SetValue(d, animatedPropertyMap); } // We've removed animation storage for this DP, so if we were storing the local // base value here then it has to go back to its non-animated storage spot. if (_baseValue != DependencyProperty.UnsetValue) { d.SetValue(_dependencyProperty, _baseValue); } } } // recompute animated value d.InvalidateProperty(_dependencyProperty); }