|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Windows.Threading;
using System.Windows.Media.Composition;
using MS.Internal;
using MS.Utility;
namespace System.Windows.Media
{
public abstract class CompositionTarget : DispatcherObject, IDisposable, ICompositionTarget
{
//
// Data types for communicating state information between
// CompositionTarget and its host.
//
internal enum HostStateFlags : uint
{
None = 0,
WorldTransform = 1,
ClipBounds = 2
};
//----------------------------------------------------------------------
//
// Constructors
//
//----------------------------------------------------------------------
#region Constructors
/// <summary>
/// CompositionTarget
/// </summary>
internal CompositionTarget()
{
#if TRACE_MVR
markVisibleCountTotal = 0;
#endif
}
/// <summary>
/// This method is used to create all uce resources either on Startup or session connect
/// </summary>
internal virtual void CreateUCEResources(DUCE.Channel channel, DUCE.Channel outOfBandChannel)
{
Debug.Assert(channel != null);
Debug.Assert(!_contentRoot.IsOnChannel(channel));
Debug.Assert(outOfBandChannel != null);
Debug.Assert(!_contentRoot.IsOnChannel(outOfBandChannel));
//
// Create root visual on the current channel and send
// this command out of band to ensure that composition node is
// created by the time this visual target is available for hosting
// and to avoid life-time issues when we are working with this node
// from the different channels.
//
bool resourceCreated = _contentRoot.CreateOrAddRefOnChannel(this, outOfBandChannel, s_contentRootType);
Debug.Assert(resourceCreated);
_contentRoot.DuplicateHandle(outOfBandChannel, channel);
outOfBandChannel.CloseBatch();
outOfBandChannel.Commit();
}
/// <summary>
/// This method is used to release all uce resources either on Shutdown or session disconnect
/// </summary>
internal virtual void ReleaseUCEResources(DUCE.Channel channel, DUCE.Channel outOfBandChannel)
{
if (_rootVisual != null)
{
((DUCE.IResource)(_rootVisual)).ReleaseOnChannel(channel);
}
//
// Release the root visual.
//
if (_contentRoot.IsOnChannel(channel))
{
_contentRoot.ReleaseOnChannel(channel);
}
if (_contentRoot.IsOnChannel(outOfBandChannel))
{
_contentRoot.ReleaseOnChannel(outOfBandChannel);
}
}
#endregion Constructors
//----------------------------------------------------------------------
//
// Public Methods
//
//----------------------------------------------------------------------
#region Public Methods
/// <summary>
/// Disposes CompositionTarget.
/// </summary>
public virtual void Dispose()
{
//
// Here we cannot use VerifyAPI methods because they check
// for the disposed state.
//
VerifyAccess();
if (!_isDisposed)
{
//
// Disconnect the root visual so that all of the child
// animations and resources are cleaned up.
//
_isDisposed = true;
GC.SuppressFinalize(this);
}
}
/// <summary>
/// Returns true if the CompositionTarget is disposed; otherwise returns false.
/// </summary>
internal bool IsDisposed { get { return _isDisposed; } }
#endregion Public Methods
//----------------------------------------------------------------------
//
// Public Properties
//
//----------------------------------------------------------------------
#region Public Properties
/// <summary>
/// Gets and sets the root Visual of the CompositionTarget.
/// </summary>
/// <value></value>
/// <remarks>
/// Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API.
/// </remarks>
public virtual Visual RootVisual
{
get
{
VerifyAPIReadOnly();
return (_rootVisual);
}
set
{
VerifyAPIReadWrite();
if (_rootVisual != value)
{
SetRootVisual(value);
MediaContext.From(Dispatcher).PostRender();
}
}
}
/// <summary>
/// Returns matrix that can be used to transform coordinates from this
/// target to the rendering destination device.
/// </summary>
public abstract Matrix TransformToDevice { get; }
/// <summary>
/// Returns matrix that can be used to transform coordinates from
/// the rendering destination device to this target.
/// </summary>
public abstract Matrix TransformFromDevice { get; }
#endregion Public Properties
//----------------------------------------------------------------------
//
// Internal Methods
//
//----------------------------------------------------------------------
#region Internal Methods
/// <summary>
///
/// </summary>
internal object StateChangedCallback(object arg)
{
object[] argArray = arg as object[];
HostStateFlags stateFlags = (HostStateFlags)argArray[0];
//
// Check if world transform of the host has changed and
// update cached value accordingly.
//
if ((stateFlags & HostStateFlags.WorldTransform) != 0)
{
_worldTransform = (Matrix)argArray[1];
}
//
// Check if clip bounds have changed, update cached value.
//
if ((stateFlags & HostStateFlags.ClipBounds) != 0)
{
_worldClipBounds = (Rect)argArray[2];
}
//
// Set corresponding flags on the root visual and schedule
// render if one has not already been scheduled.
//
if (_rootVisual != null)
{
//
// When replacing the root visual, we need to re-realize all
// content in the new tree
//
Visual.PropagateFlags(
_rootVisual,
VisualFlags.IsSubtreeDirtyForPrecompute,
VisualProxyFlags.IsSubtreeDirtyForRender
);
}
return null;
}
void ICompositionTarget.AddRefOnChannel(DUCE.Channel channel, DUCE.Channel outOfBandChannel)
{
// create all uce resources.
CreateUCEResources(channel, outOfBandChannel);
}
void ICompositionTarget.ReleaseOnChannel(DUCE.Channel channel, DUCE.Channel outOfBandChannel)
{
// release all the uce resources.
ReleaseUCEResources(channel, outOfBandChannel);
}
/// <summary>
/// Render method renders the visual tree.
/// </summary>
void ICompositionTarget.Render(bool inResize, DUCE.Channel channel)
{
#if DEBUG_CLR_MEM
bool clrTracingEnabled = false;
if (CLRProfilerControl.ProcessIsUnderCLRProfiler &&
(CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Performance))
{
clrTracingEnabled = true;
++_renderCLRPass;
CLRProfilerControl.CLRLogWriteLine("Begin_FullRender_{0}", _renderCLRPass);
}
#endif // DEBUG_CLR_MEM
//
// Now we render the scene
//
#if MEDIA_PERFORMANCE_COUNTERS
_frameRateTimer.Begin();
#endif
if (_rootVisual != null)
{
bool etwTracingEnabled = false;
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordGeneral | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
{
etwTracingEnabled = true;
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientPrecomputeSceneBegin, EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, PerfService.GetPerfElementID(this));
}
#if MEDIA_PERFORMANCE_COUNTERS
_precomputeRateTimer.Begin();
#endif
// precompute is channel agnostic
_rootVisual.Precompute();
#if MEDIA_PERFORMANCE_COUNTERS
_precomputeRateTimer.End();
#endif
if (etwTracingEnabled)
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientPrecomputeSceneEnd, EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info);
}
#if DEBUG
MediaTrace.RenderPass.Trace("Full Update");
#endif
if (etwTracingEnabled)
{
EventTrace.EventProvider.TraceEvent(
EventTrace.Event.WClientCompileSceneBegin, EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, PerfService.GetPerfElementID(this));
}
#if MEDIA_PERFORMANCE_COUNTERS
_renderRateTimer.Begin();
#endif
Compile(channel);
#if MEDIA_PERFORMANCE_COUNTERS
_renderRateTimer.End();
#endif
if (etwTracingEnabled)
{
EventTrace.EventProvider.TraceEvent(
EventTrace.Event.WClientCompileSceneEnd, EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info);
}
}
#if DEBUG_CLR_MEM
if (clrTracingEnabled &&
CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Performance)
{
CLRProfilerControl.CLRLogWriteLine("End_FullRender_{0}", _renderCLRPass);
}
#endif // DEBUG_CLR_MEM
#if MEDIA_PERFORMANCE_COUNTERS
_frameRateTimer.End();
System.Console.WriteLine("RENDERING PERFORMANCE DATA");
System.Console.WriteLine("Frame rendering time: " + _frameRateTimer.TimeOfLastPeriod + "ms");
System.Console.WriteLine("Frame precompute time: " + _precomputeRateTimer.TimeOfLastPeriod + "ms");
System.Console.WriteLine("Frame render time: " + _renderRateTimer.TimeOfLastPeriod + "ms");
#endif
}
#endregion Internal Methods
//----------------------------------------------------------------------
//
// Internal Properties
//
//----------------------------------------------------------------------
#region Internal Properties
internal DUCE.MultiChannelResource _contentRoot = new DUCE.MultiChannelResource();
internal const DUCE.ResourceType s_contentRootType = DUCE.ResourceType.TYPE_VISUAL;
/// <summary>
///
/// </summary>
internal Matrix WorldTransform
{
get
{
return _worldTransform;
}
}
internal Rect WorldClipBounds
{
get
{
return _worldClipBounds;
}
}
#endregion Internal Properties
//----------------------------------------------------------------------
//
// Private Methods
//
//----------------------------------------------------------------------
#region Private Methods
/// <summary>
/// The compile method transforms the Visual Scene Graph into the Composition Scene Graph.
/// </summary>
private void Compile(DUCE.Channel channel)
{
MediaContext mctx = MediaContext.From(Dispatcher);
Invariant.Assert(_rootVisual!=null);
// 1) Check if we have a cached render context.
// 2) Initialize the render context.
// 3) Call to render the scene graph (transforming it into the composition scene graph).
// 4) Deinitalize the render context and cache it if possible.
// ------------------------------------------------------------------------------------
// 1) Get cached render context if possible.
// For performance reasons the render context is cached between frames. Here we check if
// we have a cached one. If we don't we just create a new one. If we do have one, we use
// the render context. Note that we null out the _cachedRenderContext field. This means
// that in failure cases we will always recreate the render context.
RenderContext rc = null;
Invariant.Assert(channel != null);
if (_cachedRenderContext != null)
{
rc = _cachedRenderContext;
_cachedRenderContext = null;
}
else
{
rc = new RenderContext();
}
// ------------------------------------------------------------------------------------
// 2) Prepare the render context.
rc.Initialize(channel, _contentRoot.GetHandle(channel));
// ------------------------------------------------------------------------------------
// 3) Compile the scene.
if (mctx.IsConnected)
{
_rootVisual.Render(rc, 0);
}
// ------------------------------------------------------------------------------------
// 4) Cache the render context.
Debug.Assert(_cachedRenderContext == null);
_cachedRenderContext = rc;
}
/// <summary>
/// Internal method to set the root visual.
/// </summary>
/// <param name="visual">Root visual, can be null, but can not be a child of another
/// Visual.</param>
private void SetRootVisual(Visual visual)
{
// We need to make this function robust by leaving the
// _rootVisual in a consistent state.
if (visual != null &&
(visual._parent != null
|| visual.IsRootElement))
{
// If a Visual has already a parent it can not be the root in a CompositionTarget because
// otherwise we would have two CompositionTargets party on the same Visual tree.
// If want to allow this we need to explicitly add support for this.
throw new System.ArgumentException(SR.CompositionTarget_RootVisual_HasParent);
}
DUCE.ChannelSet channelSet = MediaContext.From(Dispatcher).GetChannels();
DUCE.Channel channel = channelSet.Channel;
if (_rootVisual != null && _contentRoot.IsOnChannel(channel))
{
ClearRootNode(channel);
((DUCE.IResource)_rootVisual).ReleaseOnChannel(channel);
_rootVisual.IsRootElement = false;
}
_rootVisual = visual;
if (_rootVisual != null)
{
_rootVisual.IsRootElement = true;
_rootVisual.SetFlagsOnAllChannels(
true,
VisualProxyFlags.IsSubtreeDirtyForRender);
}
}
/// <summary>
/// Removes all children from the current root node.
/// </summary>
private void ClearRootNode(DUCE.Channel channel)
{
//
// Currently we enqueue this command on the channel immediately
// because if we put it in the delayed release queue, then
// the _contentRoot might have been disposed by the time we
// process the queue.
//
// Note: Currently we might flicker when replacing the root of the
// compositionTarget.
DUCE.CompositionNode.RemoveAllChildren(
_contentRoot.GetHandle(channel),
channel);
}
/// <summary>
/// Verifies that the CompositionTarget can be accessed.
/// </summary>
internal void VerifyAPIReadOnly()
{
VerifyAccess();
ObjectDisposedException.ThrowIf(_isDisposed, typeof(CompositionTarget));
}
/// <summary>
/// Verifies that the CompositionTarget can be accessed.
/// </summary>
internal void VerifyAPIReadWrite()
{
VerifyAccess();
ObjectDisposedException.ThrowIf(_isDisposed, typeof(CompositionTarget));
MediaContext.From(Dispatcher).VerifyWriteAccess();
}
#endregion
//----------------------------------------------------------------------
//
// Private Fields
//
//----------------------------------------------------------------------
#region Private Fields
private bool _isDisposed;
private Visual _rootVisual;
private RenderContext _cachedRenderContext;
private Matrix _worldTransform = Matrix.Identity;
//
// ISSUE-ABaioura-10/19/2004: For now we assume a very large client
// rect, because currently clip infromation cannot be robustly
// communicated from the host. When this is fixed, we can start off
// an empty rect; clip bounds will be updated based on the host clip.
//
private Rect _worldClipBounds = new Rect(
Double.MinValue / 2.0,
Double.MinValue / 2.0,
Double.MaxValue,
Double.MaxValue);
#if DEBUG_CLR_MEM
//
// Used for CLRProfiler comments.
//
private static int _renderCLRPass = 0;
#endif // DEBUG_CLR_MEM
#if MEDIA_PERFORMANCE_COUNTERS
private HFTimer _frameRateTimer;
private HFTimer _precomputeRateTimer;
private HFTimer _renderRateTimer;
#endif
#endregion Private Fields
//----------------------------------------------------------------------
//
// Static Events
//
//----------------------------------------------------------------------
#region Static Events
/// <summary>
/// Rendering event. Registers a delegate to be notified after animation and layout but before rendering
/// </summary>
public static event EventHandler Rendering
{
add
{
MediaContext mc = MediaContext.From(Dispatcher.CurrentDispatcher);
mc.Rendering += value;
// We need to get a new rendering operation queued.
mc.PostRender();
}
remove
{
MediaContext mc = MediaContext.From(Dispatcher.CurrentDispatcher);
mc.Rendering -= value;
}
}
#endregion
}
}
|