Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / System / Windows / Media / Effects / ShaderEffect.cs / 1 / ShaderEffect.cs
//------------------------------------------------------------------------------
// Microsoft Windows Presentation Foundation
// Copyright (c) Microsoft Corporation, 2008
//
// File: ShaderEffect.cs
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using System.IO;
using System.Windows.Markup;
using System.Windows.Media.Composition;
using System.Windows.Media.Media3D;
using System.Security;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
namespace System.Windows.Media.Effects
{
public abstract partial class ShaderEffect : Effect
{
///
/// Padding is used to specify that an effect's output texture is larger than its input
// texture in a specific direction, e.g. for a drop shadow effect.
///
protected double PaddingTop
{
get
{
ReadPreamble();
return _topPadding;
}
set
{
WritePreamble();
if (value < 0.0)
{
throw new ArgumentOutOfRangeException("PaddingTop", value, SR.Get(SRID.Effect_ShaderEffectPadding));
}
else
{
_topPadding = value;
RegisterForAsyncUpdateResource();
}
WritePostscript();
}
}
///
/// Padding is used to specify that an effect's output texture is larger than its input
// texture in a specific direction, e.g. for a drop shadow effect.
///
protected double PaddingBottom
{
get
{
ReadPreamble();
return _bottomPadding;
}
set
{
WritePreamble();
if (value < 0.0)
{
throw new ArgumentOutOfRangeException("PaddingBottom", value, SR.Get(SRID.Effect_ShaderEffectPadding));
}
else
{
_bottomPadding = value;
RegisterForAsyncUpdateResource();
}
WritePostscript();
}
}
///
/// Padding is used to specify that an effect's output texture is larger than its input
// texture in a specific direction, e.g. for a drop shadow effect.
///
protected double PaddingLeft
{
get
{
ReadPreamble();
return _leftPadding;
}
set
{
WritePreamble();
if (value < 0.0)
{
throw new ArgumentOutOfRangeException("PaddingLeft", value, SR.Get(SRID.Effect_ShaderEffectPadding));
}
else
{
_leftPadding = value;
RegisterForAsyncUpdateResource();
}
WritePostscript();
}
}
///
/// Padding is used to specify that an effect's output texture is larger than its input
// texture in a specific direction, e.g. for a drop shadow effect.
///
protected double PaddingRight
{
get
{
ReadPreamble();
return _rightPadding;
}
set
{
WritePreamble();
if (value < 0.0)
{
throw new ArgumentOutOfRangeException("PaddingRight", value, SR.Get(SRID.Effect_ShaderEffectPadding));
}
else
{
_rightPadding = value;
RegisterForAsyncUpdateResource();
}
WritePostscript();
}
}
///
/// To specify a shader constant register to set to the size of the
/// destination. Default is -1, which means to not send any. Only
/// intended to be set once, in the constructor, and will fail if set
/// after the effect is initially processed.
///
protected int DdxUvDdyUvRegisterIndex
{
get
{
ReadPreamble();
return _ddxUvDdyUvRegisterIndex;
}
set
{
WritePreamble();
if (_sentFirstTime)
{
throw new InvalidOperationException(SR.Get(SRID.Effect_ShaderDdxUvDdyUvRegisterIndex));
}
_ddxUvDdyUvRegisterIndex = value;
WritePostscript();
}
}
///
/// Tells the Effect that the shader constant or sampler corresponding
/// to the specified DependencyProperty needs to be updated.
///
protected void UpdateShaderValue(DependencyProperty dp)
{
if (dp != null)
{
WritePreamble();
object val = this.GetValue(dp);
var metadata = dp.GetMetadata(this);
if (metadata != null)
{
var callback = metadata.PropertyChangedCallback;
if (callback != null)
{
callback(this, new DependencyPropertyChangedEventArgs(dp, val, val));
}
}
WritePostscript();
}
}
///
/// Construct a PropertyChangedCallback which, when invoked, will result in the DP being
/// associated with the specified shader constant register index.
///
protected static PropertyChangedCallback PixelShaderConstantCallback(int floatRegisterIndex)
{
return
(obj, args) =>
{
ShaderEffect eff = obj as ShaderEffect;
if (eff != null)
{
eff.UpdateShaderConstant(args.Property, args.NewValue, floatRegisterIndex);
}
};
}
///
/// Construct a PropertyChangedCallback which, when invoked, will result
/// in the DP being associated with the specified shader sampler
/// register index. Expected to be called on a Brush-valued
/// DependencyProperty.
///
protected static PropertyChangedCallback PixelShaderSamplerCallback(int samplerRegisterIndex)
{
return PixelShaderSamplerCallback(samplerRegisterIndex, _defaultSamplingMode);
}
///
/// Construct a PropertyChangedCallback which, when invoked, will result
/// in the DP being associated with the specified shader sampler
/// register index. Expected to be called on a Brush-valued
/// DependencyProperty.
///
protected static PropertyChangedCallback PixelShaderSamplerCallback(int samplerRegisterIndex, SamplingMode samplingMode)
{
return
(obj, args) =>
{
ShaderEffect eff = obj as ShaderEffect;
if (eff != null)
{
if (args.IsAValueChange)
{
eff.UpdateShaderSampler(args.Property, args.NewValue, samplerRegisterIndex, samplingMode);
}
}
};
}
///
/// Helper for defining Brush-valued DependencyProperties to associate with a
/// sampler register in the PixelShader.
///
protected static DependencyProperty RegisterPixelShaderSamplerProperty(string dpName,
Type ownerType,
int samplerRegisterIndex)
{
return RegisterPixelShaderSamplerProperty(dpName, ownerType, samplerRegisterIndex, _defaultSamplingMode);
}
///
/// Helper for defining Brush-valued DependencyProperties to associate with a
/// sampler register in the PixelShader.
///
protected static DependencyProperty RegisterPixelShaderSamplerProperty(string dpName,
Type ownerType,
int samplerRegisterIndex,
SamplingMode samplingMode)
{
return
DependencyProperty.Register(dpName, typeof(Brush), ownerType,
new UIPropertyMetadata(Effect.ImplicitInput,
PixelShaderSamplerCallback(samplerRegisterIndex,
samplingMode)));
}
// Updates the shader constant referred to by the DP. Converts to the
// form that the HLSL shaders want, and stores that value, since it will
// be sent on every update.
// We WritePreamble/Postscript here since this method is called by the user with the callback
// created in PixelShaderConstantCallback.
private void UpdateShaderConstant(DependencyProperty dp, object newValue, int registerIndex)
{
WritePreamble();
Type t = DetermineShaderConstantType(dp.PropertyType);
if (t == null)
{
throw new InvalidOperationException(SR.Get(SRID.Effect_ShaderConstantType, dp.PropertyType.Name));
}
else
{
int registerMax = 32;
if (registerIndex >= registerMax || registerIndex < 0)
{
throw new ArgumentException(SR.Get(SRID.Effect_ShaderConstantRegisterLimit), "dp");
}
if (t == typeof(float))
{
MilColorF fourTuple;
ConvertValueToMilColorF(newValue, out fourTuple);
StashInPosition(ref _floatRegisters, registerIndex, fourTuple, 32, ref _floatCount);
}
else
{
// We should convert all acceptable types to float.
Debug.Assert(false);
}
}
// Propagate dirty
this.PropertyChanged(dp);
WritePostscript();
}
// Updates the shader sampler referred to by the DP. Converts to the
// form that the HLSL shaders want, and stores that value, since it will
// be sent on every update.
// We WritePreamble/Postscript here since this method is called by the user with the callback
// created in PixelShaderSamplerCallback.
private void UpdateShaderSampler(DependencyProperty dp, object newValue, int registerIndex, SamplingMode samplingMode)
{
WritePreamble();
if (newValue != null)
{
if (!(typeof(VisualBrush).IsInstanceOfType(newValue) ||
typeof(ImplicitInputBrush).IsInstanceOfType(newValue) ||
typeof(ImageBrush).IsInstanceOfType(newValue))
)
{
// Note that if the type of the brush is ImplicitInputBrush and the value is non null, the value is actually
// Effect.ImplicitInput. This is because ImplicitInputBrush is internal and the user can only get to the singleton
// Effect.ImplicitInput.
throw new ArgumentException(SR.Get(SRID.Effect_ShaderSamplerType), "dp");
}
}
// PS2.0 allows max 16, but some cards seem to have trouble with 16 samplers being set.
// Restricting to 4 for now.
if (registerIndex >= 4 || registerIndex < 0) // allow -1 for default
{
throw new ArgumentException(SR.Get(SRID.Effect_ShaderSamplerRegisterLimit));
}
SamplerData sd = new SamplerData()
{
_brush = (Brush)newValue,
_samplingMode = samplingMode
};
StashSamplerDataInPosition(registerIndex, sd, 16);
// Propagate dirty
this.PropertyChanged(dp);
WritePostscript();
}
// Ensures that list is extended to 'position', and that
// the specified value is inserted there. For lists of value types.
private static void StashInPosition(ref List list, int position, T value, int maxIndex, ref int count) where T : struct
{
if (list == null)
{
list = new List(maxIndex);
}
if (list.Count <= position)
{
int numToAdd = position - list.Count + 1;
for (int i = 0; i < numToAdd; i++)
{
list.Add((T?)null);
}
}
if (!list[position].HasValue)
{
// Going from null to having a value, so increment count
count++;
}
list[position] = value;
}
// Ensures that _samplerData is extended to 'position', and that
// the specified value is inserted there.
private void StashSamplerDataInPosition(int position, SamplerData newSampler, int maxIndex)
{
if (_samplerData == null)
{
_samplerData = new List(maxIndex);
}
if (_samplerData.Count <= position)
{
int numToAdd = position - _samplerData.Count + 1;
for (int i = 0; i < numToAdd; i++)
{
_samplerData.Add((SamplerData?)null);
}
}
if (!_samplerData[position].HasValue)
{
// Going from null to having a value, so increment count
_samplerCount++;
}
System.Windows.Threading.Dispatcher dispatcher = this.Dispatcher;
// Release the old value if it is a resource on channel. AddRef the
// new value.
if (dispatcher != null)
{
SamplerData? oldSampler = _samplerData[position];
Brush oldBrush = null;
if (oldSampler.HasValue)
{
SamplerData ss = oldSampler.Value;
oldBrush = ss._brush;
}
Brush newBrush = newSampler._brush;
DUCE.IResource targetResource = (DUCE.IResource)this;
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);
ReleaseResource(oldBrush,channel);
AddRefResource(newBrush,channel);
}
}
}
_samplerData[position] = newSampler;
}
///
/// Critical: This code accesses unsafe code blocks
/// TreatAsSafe: This code does is safe to call and calling a channel with pointers is ok
///
[SecurityCritical,SecurityTreatAsSafe]
private void ManualUpdateResource(DUCE.Channel channel, bool skipOnChannelCheck)
{
// If we're told we can skip the channel check, then we must be on channel
Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel));
if (skipOnChannelCheck || _duceResource.IsOnChannel(channel))
{
if (PixelShader == null)
{
throw new InvalidOperationException(SR.Get(SRID.Effect_ShaderPixelShaderSet));
}
checked
{
DUCE.MILCMD_SHADEREFFECT data;
data.Type = MILCMD.MilCmdShaderEffect;
data.Handle = _duceResource.GetHandle(channel);
data.TopPadding = _topPadding;
data.BottomPadding = _bottomPadding;
data.LeftPadding = _leftPadding;
data.RightPadding = _rightPadding;
data.DdxUvDdyUvRegisterIndex = this.DdxUvDdyUvRegisterIndex;
data.hPixelShader = ((DUCE.IResource)PixelShader).GetHandle(channel);
unsafe
{
data.ShaderConstantFloatRegistersSize = (uint)(sizeof(Int16) * _floatCount);
data.DependencyPropertyFloatValuesSize = (uint)(4 * sizeof(Single) * _floatCount);
data.ShaderSamplerRegistrationInfoSize = (uint)(2 * sizeof(uint) * _samplerCount); // 2 pieces of data per sampler.
data.DependencyPropertySamplerValuesSize = (uint)(1 * sizeof(DUCE.ResourceHandle) * _samplerCount);
channel.BeginCommand(
(byte*)&data,
sizeof(DUCE.MILCMD_SHADEREFFECT),
(int)(data.ShaderConstantFloatRegistersSize +
data.DependencyPropertyFloatValuesSize +
data.ShaderSamplerRegistrationInfoSize +
data.DependencyPropertySamplerValuesSize )
);
// Arrays appear in this order:
// 1) float register indices
// 2) float dp values
// 3) sampler registration info
// 4) sampler dp values
// 1) float register indices
AppendRegisters(channel, _floatRegisters);
// 2) float dp values
if (_floatRegisters != null)
{
for (int i = 0; i < _floatRegisters.Count; i++)
{
MilColorF? v = _floatRegisters[i];
if (v.HasValue)
{
MilColorF valueToPush = v.Value;
channel.AppendCommandData((byte*)&valueToPush, sizeof(MilColorF));
}
}
}
// 3) sampler registration info
if (_samplerCount > 0)
{
int count = _samplerData.Count;
for (int i = 0; i < count; i++)
{
SamplerData? ssn = _samplerData[i];
if (ssn.HasValue)
{
SamplerData ss = ssn.Value;
// add as a 2-tuple (SamplerRegisterIndex,
// SamplingMode)
channel.AppendCommandData((byte*)&i, sizeof(int));
int value = (int)(ss._samplingMode);
channel.AppendCommandData((byte*)&value, sizeof(int));
}
}
}
// 4) sampler dp values
if (_samplerCount > 0)
{
for (int i = 0; i < _samplerData.Count; i++)
{
SamplerData? ssn = _samplerData[i];
if (ssn.HasValue)
{
SamplerData ss = ssn.Value;
// Making this assumption by storing a collection of
// handles as an Int32Collection
Debug.Assert(sizeof(DUCE.ResourceHandle) == sizeof(Int32));
DUCE.ResourceHandle hBrush = ss._brush != null
? ((DUCE.IResource)ss._brush).GetHandle(channel)
: DUCE.ResourceHandle.Null;
Debug.Assert(!hBrush.IsNull || ss._brush == null, "If brush isn't null, hBrush better not be");
channel.AppendCommandData((byte*)&hBrush, sizeof(DUCE.ResourceHandle));
}
}
}
// That's it...
channel.EndCommand();
}
}
}
}
// write the non-null values of the list of nullables to the command data.
///
/// Critical: This code accesses unsafe code blocks
/// TreatAsSafe: This code does is safe to call and calling a channel with pointers is ok
///
[SecurityCritical,SecurityTreatAsSafe]
private void AppendRegisters(DUCE.Channel channel, List list) where T : struct
{
if (list != null)
{
unsafe
{
for (int i = 0; i < list.Count; i++)
{
T? v = list[i];
if (v.HasValue)
{
Int16 regIndex = (Int16)i; // put onto stack so next &-operator compiles
channel.AppendCommandData((byte*)®Index, sizeof(Int16));
}
}
}
}
}
// Written by hand to include management of input Brushes (which aren't DPs).
internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel)
{
if (_duceResource.CreateOrAddRefOnChannel(channel, System.Windows.Media.Composition.DUCE.ResourceType.TYPE_SHADEREFFECT))
{
// Ensures brushes are property instantiated into Duce resources.
if (_samplerCount > 0)
{
int numSamplers = _samplerData.Count;
for (int i = 0; i < numSamplers; i++)
{
SamplerData? ssn = _samplerData[i];
if (ssn.HasValue)
{
SamplerData ss = ssn.Value;
DUCE.IResource brush = ss._brush as DUCE.IResource;
if (brush != null)
{
brush.AddRefOnChannel(channel);
}
}
}
}
PixelShader vPixelShader = PixelShader;
if (vPixelShader != null) ((DUCE.IResource)vPixelShader).AddRefOnChannel(channel);
AddRefOnChannelAnimations(channel);
UpdateResource(channel, true /* skip "on channel" check - we already know that we're on channel */ );
}
return _duceResource.GetHandle(channel);
}
// Written by hand to include management of input Brushes (which aren't DPs).
internal override void ReleaseOnChannelCore(DUCE.Channel channel)
{
Debug.Assert(_duceResource.IsOnChannel(channel));
if (_duceResource.ReleaseOnChannel(channel))
{
// Ensure that brushes are released.
if (_samplerCount > 0)
{
int numSamplers = _samplerData.Count;
for (int i = 0; i < numSamplers; i++)
{
SamplerData? ssn = _samplerData[i];
if (ssn.HasValue)
{
SamplerData ss = ssn.Value;
DUCE.IResource brush = ss._brush as DUCE.IResource;
if (brush != null)
{
brush.ReleaseOnChannel(channel);
}
}
}
}
PixelShader vPixelShader = PixelShader;
if (vPixelShader != null) ((DUCE.IResource)vPixelShader).ReleaseOnChannel(channel);
ReleaseOnChannelAnimations(channel);
}
}
// Shader constants can be coerced into 4-tuples of floats only, at this time.
// Determine which type the incoming type can go to, if any.
internal static Type DetermineShaderConstantType(Type type)
{
Type result = null;
if (type == typeof(double) ||
type == typeof(float) ||
type == typeof(Color) ||
type == typeof(Point) ||
type == typeof(Size) ||
type == typeof(Vector) ||
type == typeof(Point3D) ||
type == typeof(Vector3D) ||
type == typeof(Point4D))
{
result = typeof(float);
}
return result;
}
// Convert to float four tuple
internal static void ConvertValueToMilColorF(object value, out MilColorF newVal)
{
Type t = value.GetType();
// Fill in four-tuples. Always fill in 1.0's for where there are
// empty slots, to avoid division by zero on vector operations that
// these values are subjected to.
// Should order these in terms of most likely to be hit first.
if (t == typeof(double) || t == typeof(float))
{
float fVal = (t == typeof(double)) ? (float)(double)value : (float)value;
// Scalars extend out to fill entire vector.
newVal.r = newVal.g = newVal.b = newVal.a = fVal;
}
else if (t == typeof(Color))
{
Color col = (Color)value;
newVal.r = (float)col.R / 255f;
newVal.g = (float)col.G / 255f;
newVal.b = (float)col.B / 255f;
newVal.a = (float)col.A / 255f;
}
else if (t == typeof(Point))
{
Point p = (Point)value;
newVal.r = (float)p.X;
newVal.g = (float)p.Y;
newVal.b = 1f;
newVal.a = 1f;
}
else if (t == typeof(Size))
{
Size s = (Size)value;
newVal.r = (float)s.Width;
newVal.g = (float)s.Height;
newVal.b = 1f;
newVal.a = 1f;
}
else if (t == typeof(Vector))
{
Vector v = (Vector)value;
newVal.r = (float)v.X;
newVal.g = (float)v.Y;
newVal.b = 1f;
newVal.a = 1f;
}
else if (t == typeof(Point3D))
{
Point3D p = (Point3D)value;
newVal.r = (float)p.X;
newVal.g = (float)p.Y;
newVal.b = (float)p.Z;
newVal.a = 1f;
}
else if (t == typeof(Vector3D))
{
Vector3D v = (Vector3D)value;
newVal.r = (float)v.X;
newVal.g = (float)v.Y;
newVal.b = (float)v.Z;
newVal.a = 1f;
}
else if (t == typeof(Point4D))
{
Point4D p = (Point4D)value;
newVal.r = (float)p.X;
newVal.g = (float)p.Y;
newVal.b = (float)p.Z;
newVal.a = (float)p.W;
}
else
{
// We should never hit this case, since we check the type using DetermineShaderConstantType
// before we call this method.
Debug.Assert(false);
newVal.r = newVal.b = newVal.g = newVal.a = 1f;
}
}
///
/// Implementation of Freezable.CloneCore .
///
///
protected override void CloneCore(Freezable sourceFreezable)
{
ShaderEffect effect = (ShaderEffect)sourceFreezable;
base.CloneCore(sourceFreezable);
CopyCommon(effect);
}
///
/// Implementation of Freezable.CloneCurrentValueCore .
///
///
protected override void CloneCurrentValueCore(Freezable sourceFreezable)
{
ShaderEffect effect = (ShaderEffect)sourceFreezable;
base.CloneCurrentValueCore(sourceFreezable);
CopyCommon(effect);
}
///
/// Implementation of Freezable.GetAsFrozenCore .
///
///
protected override void GetAsFrozenCore(Freezable sourceFreezable)
{
ShaderEffect effect = (ShaderEffect)sourceFreezable;
base.GetAsFrozenCore(sourceFreezable);
CopyCommon(effect);
}
///
/// Implementation of Freezable.GetCurrentValueAsFrozenCore .
///
///
protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
{
ShaderEffect effect = (ShaderEffect)sourceFreezable;
base.GetCurrentValueAsFrozenCore(sourceFreezable);
CopyCommon(effect);
}
///
/// Clones values that do not have corresponding DPs.
///
///
private void CopyCommon(ShaderEffect effect)
{
_topPadding = effect._topPadding;
_bottomPadding = effect._bottomPadding;
_leftPadding = effect._leftPadding;
_rightPadding = effect._rightPadding;
if (_floatRegisters != null)
{
_floatRegisters = new List(effect._floatRegisters);
}
if (_samplerData != null)
{
_samplerData = new List(effect._samplerData);
}
_floatCount = effect._floatCount;
_samplerCount = effect._samplerCount;
_ddxUvDdyUvRegisterIndex = effect._ddxUvDdyUvRegisterIndex;
_sentFirstTime = effect._sentFirstTime;
}
private struct SamplerData
{
public Brush _brush;
public SamplingMode _samplingMode;
}
private const SamplingMode _defaultSamplingMode = SamplingMode.Auto;
// Instance data
private double _topPadding = 0.0;
private double _bottomPadding = 0.0;
private double _leftPadding = 0.0;
private double _rightPadding = 0.0;
private List _floatRegisters = null;
private List _samplerData = null;
private int _floatCount = 0;
private int _samplerCount = 0;
private int _ddxUvDdyUvRegisterIndex = -1;
private bool _sentFirstTime = false;
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
// Microsoft Windows Presentation Foundation
// Copyright (c) Microsoft Corporation, 2008
//
// File: ShaderEffect.cs
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using System.IO;
using System.Windows.Markup;
using System.Windows.Media.Composition;
using System.Windows.Media.Media3D;
using System.Security;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
namespace System.Windows.Media.Effects
{
public abstract partial class ShaderEffect : Effect
{
///
/// Padding is used to specify that an effect's output texture is larger than its input
// texture in a specific direction, e.g. for a drop shadow effect.
///
protected double PaddingTop
{
get
{
ReadPreamble();
return _topPadding;
}
set
{
WritePreamble();
if (value < 0.0)
{
throw new ArgumentOutOfRangeException("PaddingTop", value, SR.Get(SRID.Effect_ShaderEffectPadding));
}
else
{
_topPadding = value;
RegisterForAsyncUpdateResource();
}
WritePostscript();
}
}
///
/// Padding is used to specify that an effect's output texture is larger than its input
// texture in a specific direction, e.g. for a drop shadow effect.
///
protected double PaddingBottom
{
get
{
ReadPreamble();
return _bottomPadding;
}
set
{
WritePreamble();
if (value < 0.0)
{
throw new ArgumentOutOfRangeException("PaddingBottom", value, SR.Get(SRID.Effect_ShaderEffectPadding));
}
else
{
_bottomPadding = value;
RegisterForAsyncUpdateResource();
}
WritePostscript();
}
}
///
/// Padding is used to specify that an effect's output texture is larger than its input
// texture in a specific direction, e.g. for a drop shadow effect.
///
protected double PaddingLeft
{
get
{
ReadPreamble();
return _leftPadding;
}
set
{
WritePreamble();
if (value < 0.0)
{
throw new ArgumentOutOfRangeException("PaddingLeft", value, SR.Get(SRID.Effect_ShaderEffectPadding));
}
else
{
_leftPadding = value;
RegisterForAsyncUpdateResource();
}
WritePostscript();
}
}
///
/// Padding is used to specify that an effect's output texture is larger than its input
// texture in a specific direction, e.g. for a drop shadow effect.
///
protected double PaddingRight
{
get
{
ReadPreamble();
return _rightPadding;
}
set
{
WritePreamble();
if (value < 0.0)
{
throw new ArgumentOutOfRangeException("PaddingRight", value, SR.Get(SRID.Effect_ShaderEffectPadding));
}
else
{
_rightPadding = value;
RegisterForAsyncUpdateResource();
}
WritePostscript();
}
}
///
/// To specify a shader constant register to set to the size of the
/// destination. Default is -1, which means to not send any. Only
/// intended to be set once, in the constructor, and will fail if set
/// after the effect is initially processed.
///
protected int DdxUvDdyUvRegisterIndex
{
get
{
ReadPreamble();
return _ddxUvDdyUvRegisterIndex;
}
set
{
WritePreamble();
if (_sentFirstTime)
{
throw new InvalidOperationException(SR.Get(SRID.Effect_ShaderDdxUvDdyUvRegisterIndex));
}
_ddxUvDdyUvRegisterIndex = value;
WritePostscript();
}
}
///
/// Tells the Effect that the shader constant or sampler corresponding
/// to the specified DependencyProperty needs to be updated.
///
protected void UpdateShaderValue(DependencyProperty dp)
{
if (dp != null)
{
WritePreamble();
object val = this.GetValue(dp);
var metadata = dp.GetMetadata(this);
if (metadata != null)
{
var callback = metadata.PropertyChangedCallback;
if (callback != null)
{
callback(this, new DependencyPropertyChangedEventArgs(dp, val, val));
}
}
WritePostscript();
}
}
///
/// Construct a PropertyChangedCallback which, when invoked, will result in the DP being
/// associated with the specified shader constant register index.
///
protected static PropertyChangedCallback PixelShaderConstantCallback(int floatRegisterIndex)
{
return
(obj, args) =>
{
ShaderEffect eff = obj as ShaderEffect;
if (eff != null)
{
eff.UpdateShaderConstant(args.Property, args.NewValue, floatRegisterIndex);
}
};
}
///
/// Construct a PropertyChangedCallback which, when invoked, will result
/// in the DP being associated with the specified shader sampler
/// register index. Expected to be called on a Brush-valued
/// DependencyProperty.
///
protected static PropertyChangedCallback PixelShaderSamplerCallback(int samplerRegisterIndex)
{
return PixelShaderSamplerCallback(samplerRegisterIndex, _defaultSamplingMode);
}
///
/// Construct a PropertyChangedCallback which, when invoked, will result
/// in the DP being associated with the specified shader sampler
/// register index. Expected to be called on a Brush-valued
/// DependencyProperty.
///
protected static PropertyChangedCallback PixelShaderSamplerCallback(int samplerRegisterIndex, SamplingMode samplingMode)
{
return
(obj, args) =>
{
ShaderEffect eff = obj as ShaderEffect;
if (eff != null)
{
if (args.IsAValueChange)
{
eff.UpdateShaderSampler(args.Property, args.NewValue, samplerRegisterIndex, samplingMode);
}
}
};
}
///
/// Helper for defining Brush-valued DependencyProperties to associate with a
/// sampler register in the PixelShader.
///
protected static DependencyProperty RegisterPixelShaderSamplerProperty(string dpName,
Type ownerType,
int samplerRegisterIndex)
{
return RegisterPixelShaderSamplerProperty(dpName, ownerType, samplerRegisterIndex, _defaultSamplingMode);
}
///
/// Helper for defining Brush-valued DependencyProperties to associate with a
/// sampler register in the PixelShader.
///
protected static DependencyProperty RegisterPixelShaderSamplerProperty(string dpName,
Type ownerType,
int samplerRegisterIndex,
SamplingMode samplingMode)
{
return
DependencyProperty.Register(dpName, typeof(Brush), ownerType,
new UIPropertyMetadata(Effect.ImplicitInput,
PixelShaderSamplerCallback(samplerRegisterIndex,
samplingMode)));
}
// Updates the shader constant referred to by the DP. Converts to the
// form that the HLSL shaders want, and stores that value, since it will
// be sent on every update.
// We WritePreamble/Postscript here since this method is called by the user with the callback
// created in PixelShaderConstantCallback.
private void UpdateShaderConstant(DependencyProperty dp, object newValue, int registerIndex)
{
WritePreamble();
Type t = DetermineShaderConstantType(dp.PropertyType);
if (t == null)
{
throw new InvalidOperationException(SR.Get(SRID.Effect_ShaderConstantType, dp.PropertyType.Name));
}
else
{
int registerMax = 32;
if (registerIndex >= registerMax || registerIndex < 0)
{
throw new ArgumentException(SR.Get(SRID.Effect_ShaderConstantRegisterLimit), "dp");
}
if (t == typeof(float))
{
MilColorF fourTuple;
ConvertValueToMilColorF(newValue, out fourTuple);
StashInPosition(ref _floatRegisters, registerIndex, fourTuple, 32, ref _floatCount);
}
else
{
// We should convert all acceptable types to float.
Debug.Assert(false);
}
}
// Propagate dirty
this.PropertyChanged(dp);
WritePostscript();
}
// Updates the shader sampler referred to by the DP. Converts to the
// form that the HLSL shaders want, and stores that value, since it will
// be sent on every update.
// We WritePreamble/Postscript here since this method is called by the user with the callback
// created in PixelShaderSamplerCallback.
private void UpdateShaderSampler(DependencyProperty dp, object newValue, int registerIndex, SamplingMode samplingMode)
{
WritePreamble();
if (newValue != null)
{
if (!(typeof(VisualBrush).IsInstanceOfType(newValue) ||
typeof(ImplicitInputBrush).IsInstanceOfType(newValue) ||
typeof(ImageBrush).IsInstanceOfType(newValue))
)
{
// Note that if the type of the brush is ImplicitInputBrush and the value is non null, the value is actually
// Effect.ImplicitInput. This is because ImplicitInputBrush is internal and the user can only get to the singleton
// Effect.ImplicitInput.
throw new ArgumentException(SR.Get(SRID.Effect_ShaderSamplerType), "dp");
}
}
// PS2.0 allows max 16, but some cards seem to have trouble with 16 samplers being set.
// Restricting to 4 for now.
if (registerIndex >= 4 || registerIndex < 0) // allow -1 for default
{
throw new ArgumentException(SR.Get(SRID.Effect_ShaderSamplerRegisterLimit));
}
SamplerData sd = new SamplerData()
{
_brush = (Brush)newValue,
_samplingMode = samplingMode
};
StashSamplerDataInPosition(registerIndex, sd, 16);
// Propagate dirty
this.PropertyChanged(dp);
WritePostscript();
}
// Ensures that list is extended to 'position', and that
// the specified value is inserted there. For lists of value types.
private static void StashInPosition(ref List list, int position, T value, int maxIndex, ref int count) where T : struct
{
if (list == null)
{
list = new List(maxIndex);
}
if (list.Count <= position)
{
int numToAdd = position - list.Count + 1;
for (int i = 0; i < numToAdd; i++)
{
list.Add((T?)null);
}
}
if (!list[position].HasValue)
{
// Going from null to having a value, so increment count
count++;
}
list[position] = value;
}
// Ensures that _samplerData is extended to 'position', and that
// the specified value is inserted there.
private void StashSamplerDataInPosition(int position, SamplerData newSampler, int maxIndex)
{
if (_samplerData == null)
{
_samplerData = new List(maxIndex);
}
if (_samplerData.Count <= position)
{
int numToAdd = position - _samplerData.Count + 1;
for (int i = 0; i < numToAdd; i++)
{
_samplerData.Add((SamplerData?)null);
}
}
if (!_samplerData[position].HasValue)
{
// Going from null to having a value, so increment count
_samplerCount++;
}
System.Windows.Threading.Dispatcher dispatcher = this.Dispatcher;
// Release the old value if it is a resource on channel. AddRef the
// new value.
if (dispatcher != null)
{
SamplerData? oldSampler = _samplerData[position];
Brush oldBrush = null;
if (oldSampler.HasValue)
{
SamplerData ss = oldSampler.Value;
oldBrush = ss._brush;
}
Brush newBrush = newSampler._brush;
DUCE.IResource targetResource = (DUCE.IResource)this;
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);
ReleaseResource(oldBrush,channel);
AddRefResource(newBrush,channel);
}
}
}
_samplerData[position] = newSampler;
}
///
/// Critical: This code accesses unsafe code blocks
/// TreatAsSafe: This code does is safe to call and calling a channel with pointers is ok
///
[SecurityCritical,SecurityTreatAsSafe]
private void ManualUpdateResource(DUCE.Channel channel, bool skipOnChannelCheck)
{
// If we're told we can skip the channel check, then we must be on channel
Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel));
if (skipOnChannelCheck || _duceResource.IsOnChannel(channel))
{
if (PixelShader == null)
{
throw new InvalidOperationException(SR.Get(SRID.Effect_ShaderPixelShaderSet));
}
checked
{
DUCE.MILCMD_SHADEREFFECT data;
data.Type = MILCMD.MilCmdShaderEffect;
data.Handle = _duceResource.GetHandle(channel);
data.TopPadding = _topPadding;
data.BottomPadding = _bottomPadding;
data.LeftPadding = _leftPadding;
data.RightPadding = _rightPadding;
data.DdxUvDdyUvRegisterIndex = this.DdxUvDdyUvRegisterIndex;
data.hPixelShader = ((DUCE.IResource)PixelShader).GetHandle(channel);
unsafe
{
data.ShaderConstantFloatRegistersSize = (uint)(sizeof(Int16) * _floatCount);
data.DependencyPropertyFloatValuesSize = (uint)(4 * sizeof(Single) * _floatCount);
data.ShaderSamplerRegistrationInfoSize = (uint)(2 * sizeof(uint) * _samplerCount); // 2 pieces of data per sampler.
data.DependencyPropertySamplerValuesSize = (uint)(1 * sizeof(DUCE.ResourceHandle) * _samplerCount);
channel.BeginCommand(
(byte*)&data,
sizeof(DUCE.MILCMD_SHADEREFFECT),
(int)(data.ShaderConstantFloatRegistersSize +
data.DependencyPropertyFloatValuesSize +
data.ShaderSamplerRegistrationInfoSize +
data.DependencyPropertySamplerValuesSize )
);
// Arrays appear in this order:
// 1) float register indices
// 2) float dp values
// 3) sampler registration info
// 4) sampler dp values
// 1) float register indices
AppendRegisters(channel, _floatRegisters);
// 2) float dp values
if (_floatRegisters != null)
{
for (int i = 0; i < _floatRegisters.Count; i++)
{
MilColorF? v = _floatRegisters[i];
if (v.HasValue)
{
MilColorF valueToPush = v.Value;
channel.AppendCommandData((byte*)&valueToPush, sizeof(MilColorF));
}
}
}
// 3) sampler registration info
if (_samplerCount > 0)
{
int count = _samplerData.Count;
for (int i = 0; i < count; i++)
{
SamplerData? ssn = _samplerData[i];
if (ssn.HasValue)
{
SamplerData ss = ssn.Value;
// add as a 2-tuple (SamplerRegisterIndex,
// SamplingMode)
channel.AppendCommandData((byte*)&i, sizeof(int));
int value = (int)(ss._samplingMode);
channel.AppendCommandData((byte*)&value, sizeof(int));
}
}
}
// 4) sampler dp values
if (_samplerCount > 0)
{
for (int i = 0; i < _samplerData.Count; i++)
{
SamplerData? ssn = _samplerData[i];
if (ssn.HasValue)
{
SamplerData ss = ssn.Value;
// Making this assumption by storing a collection of
// handles as an Int32Collection
Debug.Assert(sizeof(DUCE.ResourceHandle) == sizeof(Int32));
DUCE.ResourceHandle hBrush = ss._brush != null
? ((DUCE.IResource)ss._brush).GetHandle(channel)
: DUCE.ResourceHandle.Null;
Debug.Assert(!hBrush.IsNull || ss._brush == null, "If brush isn't null, hBrush better not be");
channel.AppendCommandData((byte*)&hBrush, sizeof(DUCE.ResourceHandle));
}
}
}
// That's it...
channel.EndCommand();
}
}
}
}
// write the non-null values of the list of nullables to the command data.
///
/// Critical: This code accesses unsafe code blocks
/// TreatAsSafe: This code does is safe to call and calling a channel with pointers is ok
///
[SecurityCritical,SecurityTreatAsSafe]
private void AppendRegisters(DUCE.Channel channel, List list) where T : struct
{
if (list != null)
{
unsafe
{
for (int i = 0; i < list.Count; i++)
{
T? v = list[i];
if (v.HasValue)
{
Int16 regIndex = (Int16)i; // put onto stack so next &-operator compiles
channel.AppendCommandData((byte*)®Index, sizeof(Int16));
}
}
}
}
}
// Written by hand to include management of input Brushes (which aren't DPs).
internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel)
{
if (_duceResource.CreateOrAddRefOnChannel(channel, System.Windows.Media.Composition.DUCE.ResourceType.TYPE_SHADEREFFECT))
{
// Ensures brushes are property instantiated into Duce resources.
if (_samplerCount > 0)
{
int numSamplers = _samplerData.Count;
for (int i = 0; i < numSamplers; i++)
{
SamplerData? ssn = _samplerData[i];
if (ssn.HasValue)
{
SamplerData ss = ssn.Value;
DUCE.IResource brush = ss._brush as DUCE.IResource;
if (brush != null)
{
brush.AddRefOnChannel(channel);
}
}
}
}
PixelShader vPixelShader = PixelShader;
if (vPixelShader != null) ((DUCE.IResource)vPixelShader).AddRefOnChannel(channel);
AddRefOnChannelAnimations(channel);
UpdateResource(channel, true /* skip "on channel" check - we already know that we're on channel */ );
}
return _duceResource.GetHandle(channel);
}
// Written by hand to include management of input Brushes (which aren't DPs).
internal override void ReleaseOnChannelCore(DUCE.Channel channel)
{
Debug.Assert(_duceResource.IsOnChannel(channel));
if (_duceResource.ReleaseOnChannel(channel))
{
// Ensure that brushes are released.
if (_samplerCount > 0)
{
int numSamplers = _samplerData.Count;
for (int i = 0; i < numSamplers; i++)
{
SamplerData? ssn = _samplerData[i];
if (ssn.HasValue)
{
SamplerData ss = ssn.Value;
DUCE.IResource brush = ss._brush as DUCE.IResource;
if (brush != null)
{
brush.ReleaseOnChannel(channel);
}
}
}
}
PixelShader vPixelShader = PixelShader;
if (vPixelShader != null) ((DUCE.IResource)vPixelShader).ReleaseOnChannel(channel);
ReleaseOnChannelAnimations(channel);
}
}
// Shader constants can be coerced into 4-tuples of floats only, at this time.
// Determine which type the incoming type can go to, if any.
internal static Type DetermineShaderConstantType(Type type)
{
Type result = null;
if (type == typeof(double) ||
type == typeof(float) ||
type == typeof(Color) ||
type == typeof(Point) ||
type == typeof(Size) ||
type == typeof(Vector) ||
type == typeof(Point3D) ||
type == typeof(Vector3D) ||
type == typeof(Point4D))
{
result = typeof(float);
}
return result;
}
// Convert to float four tuple
internal static void ConvertValueToMilColorF(object value, out MilColorF newVal)
{
Type t = value.GetType();
// Fill in four-tuples. Always fill in 1.0's for where there are
// empty slots, to avoid division by zero on vector operations that
// these values are subjected to.
// Should order these in terms of most likely to be hit first.
if (t == typeof(double) || t == typeof(float))
{
float fVal = (t == typeof(double)) ? (float)(double)value : (float)value;
// Scalars extend out to fill entire vector.
newVal.r = newVal.g = newVal.b = newVal.a = fVal;
}
else if (t == typeof(Color))
{
Color col = (Color)value;
newVal.r = (float)col.R / 255f;
newVal.g = (float)col.G / 255f;
newVal.b = (float)col.B / 255f;
newVal.a = (float)col.A / 255f;
}
else if (t == typeof(Point))
{
Point p = (Point)value;
newVal.r = (float)p.X;
newVal.g = (float)p.Y;
newVal.b = 1f;
newVal.a = 1f;
}
else if (t == typeof(Size))
{
Size s = (Size)value;
newVal.r = (float)s.Width;
newVal.g = (float)s.Height;
newVal.b = 1f;
newVal.a = 1f;
}
else if (t == typeof(Vector))
{
Vector v = (Vector)value;
newVal.r = (float)v.X;
newVal.g = (float)v.Y;
newVal.b = 1f;
newVal.a = 1f;
}
else if (t == typeof(Point3D))
{
Point3D p = (Point3D)value;
newVal.r = (float)p.X;
newVal.g = (float)p.Y;
newVal.b = (float)p.Z;
newVal.a = 1f;
}
else if (t == typeof(Vector3D))
{
Vector3D v = (Vector3D)value;
newVal.r = (float)v.X;
newVal.g = (float)v.Y;
newVal.b = (float)v.Z;
newVal.a = 1f;
}
else if (t == typeof(Point4D))
{
Point4D p = (Point4D)value;
newVal.r = (float)p.X;
newVal.g = (float)p.Y;
newVal.b = (float)p.Z;
newVal.a = (float)p.W;
}
else
{
// We should never hit this case, since we check the type using DetermineShaderConstantType
// before we call this method.
Debug.Assert(false);
newVal.r = newVal.b = newVal.g = newVal.a = 1f;
}
}
///
/// Implementation of Freezable.CloneCore .
///
///
protected override void CloneCore(Freezable sourceFreezable)
{
ShaderEffect effect = (ShaderEffect)sourceFreezable;
base.CloneCore(sourceFreezable);
CopyCommon(effect);
}
///
/// Implementation of Freezable.CloneCurrentValueCore .
///
///
protected override void CloneCurrentValueCore(Freezable sourceFreezable)
{
ShaderEffect effect = (ShaderEffect)sourceFreezable;
base.CloneCurrentValueCore(sourceFreezable);
CopyCommon(effect);
}
///
/// Implementation of Freezable.GetAsFrozenCore .
///
///
protected override void GetAsFrozenCore(Freezable sourceFreezable)
{
ShaderEffect effect = (ShaderEffect)sourceFreezable;
base.GetAsFrozenCore(sourceFreezable);
CopyCommon(effect);
}
///
/// Implementation of Freezable.GetCurrentValueAsFrozenCore .
///
///
protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
{
ShaderEffect effect = (ShaderEffect)sourceFreezable;
base.GetCurrentValueAsFrozenCore(sourceFreezable);
CopyCommon(effect);
}
///
/// Clones values that do not have corresponding DPs.
///
///
private void CopyCommon(ShaderEffect effect)
{
_topPadding = effect._topPadding;
_bottomPadding = effect._bottomPadding;
_leftPadding = effect._leftPadding;
_rightPadding = effect._rightPadding;
if (_floatRegisters != null)
{
_floatRegisters = new List(effect._floatRegisters);
}
if (_samplerData != null)
{
_samplerData = new List(effect._samplerData);
}
_floatCount = effect._floatCount;
_samplerCount = effect._samplerCount;
_ddxUvDdyUvRegisterIndex = effect._ddxUvDdyUvRegisterIndex;
_sentFirstTime = effect._sentFirstTime;
}
private struct SamplerData
{
public Brush _brush;
public SamplingMode _samplingMode;
}
private const SamplingMode _defaultSamplingMode = SamplingMode.Auto;
// Instance data
private double _topPadding = 0.0;
private double _bottomPadding = 0.0;
private double _leftPadding = 0.0;
private double _rightPadding = 0.0;
private List _floatRegisters = null;
private List _samplerData = null;
private int _floatCount = 0;
private int _samplerCount = 0;
private int _ddxUvDdyUvRegisterIndex = -1;
private bool _sentFirstTime = false;
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PropertyInformation.cs
- transactioncontext.cs
- XmlSchemaAppInfo.cs
- WebPartCancelEventArgs.cs
- MLangCodePageEncoding.cs
- DataGridViewTextBoxColumn.cs
- BoundColumn.cs
- HttpCapabilitiesEvaluator.cs
- BitmapEffectDrawingContextWalker.cs
- PointF.cs
- ServiceMetadataBehavior.cs
- CleanUpVirtualizedItemEventArgs.cs
- TrueReadOnlyCollection.cs
- COM2PropertyBuilderUITypeEditor.cs
- MemoryPressure.cs
- OrderedHashRepartitionEnumerator.cs
- VirtualPathProvider.cs
- ProfileParameter.cs
- ImplicitInputBrush.cs
- DateTimeStorage.cs
- ConfigurationStrings.cs
- DataSvcMapFile.cs
- TaskHelper.cs
- DetailsViewAutoFormat.cs
- ChunkedMemoryStream.cs
- DbProviderFactoriesConfigurationHandler.cs
- CodeIdentifier.cs
- PeerDuplexChannelListener.cs
- ComponentChangedEvent.cs
- WindowsToolbar.cs
- InitializationEventAttribute.cs
- PropertyPushdownHelper.cs
- TypeResolver.cs
- DuplicateMessageDetector.cs
- BlockUIContainer.cs
- SemaphoreSlim.cs
- RotateTransform.cs
- WinEventTracker.cs
- ValidationHelper.cs
- XsdDateTime.cs
- OrthographicCamera.cs
- ProxyWebPartManager.cs
- _LocalDataStoreMgr.cs
- AsymmetricKeyExchangeFormatter.cs
- FontEditor.cs
- XmlSiteMapProvider.cs
- QueueNameHelper.cs
- EventMappingSettingsCollection.cs
- DefaultValidator.cs
- SQLChars.cs
- SiteMap.cs
- DetailsViewPageEventArgs.cs
- TokenBasedSet.cs
- ThicknessConverter.cs
- BuildProviderAppliesToAttribute.cs
- CodeIdentifiers.cs
- DateTimeFormatInfo.cs
- DataGridColumnHeaderCollection.cs
- WindowsMenu.cs
- WebBrowsableAttribute.cs
- LocalizableResourceBuilder.cs
- SoapAttributeOverrides.cs
- AttachedAnnotationChangedEventArgs.cs
- TextContainerHelper.cs
- ThicknessAnimationUsingKeyFrames.cs
- PassportAuthenticationModule.cs
- PackageDigitalSignatureManager.cs
- SR.cs
- Interfaces.cs
- DataServiceHostFactory.cs
- LinkedDataMemberFieldEditor.cs
- TimeSpanFormat.cs
- CqlParser.cs
- XmlQuerySequence.cs
- ApplicationHost.cs
- Utils.cs
- UInt32Converter.cs
- OleDbDataReader.cs
- XD.cs
- XmlAnyAttributeAttribute.cs
- WindowsListBox.cs
- MenuEventArgs.cs
- HMACSHA384.cs
- _FtpDataStream.cs
- DbBuffer.cs
- IsolatedStorageException.cs
- State.cs
- FunctionNode.cs
- InvokeMethodActivityDesigner.cs
- BindingExpressionUncommonField.cs
- CqlLexerHelpers.cs
- RectAnimationUsingKeyFrames.cs
- XmlJsonReader.cs
- TextStore.cs
- TableItemProviderWrapper.cs
- ArrayTypeMismatchException.cs
- CompatibleIComparer.cs
- ControlCachePolicy.cs
- MsmqChannelFactory.cs
- OptimalTextSource.cs