File: Core\CSharp\System\Windows\Media\Effects\PixelShader.cs
Project: wpf\src\PresentationCore.csproj (PresentationCore)
//------------------------------------------------------------------------------
//  Microsoft Windows Presentation Foundation
//  Copyright (c) Microsoft Corporation, 2008
//
//  File:       PixelShader.cs
//------------------------------------------------------------------------------
 
using System;
using System.IO;
using MS.Internal;
using MS.Win32.PresentationCore;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Reflection;
using System.Collections;
using System.Globalization;
using System.Security;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Composition;
using System.Windows;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.Windows.Markup;
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
using System.Windows.Navigation;
using System.IO.Packaging;
using MS.Internal.PresentationCore; 
 
namespace System.Windows.Media.Effects
{
    public sealed partial class PixelShader : Animatable, DUCE.IResource
    {
        // Method and not property so we don't need to hang onto the stream.
        public void SetStreamSource(Stream source)
        {
            WritePreamble();
            LoadPixelShaderFromStreamIntoMemory(source);
            WritePostscript();
        }
 
        /// <summary>
        /// The major version of the pixel shader
        /// </summary>
        internal short ShaderMajorVersion
        {
            get;
            private set;
        }
 
        /// <summary>
        /// The minor version of the pixel shader
        /// </summary>
        internal short ShaderMinorVersion
        {
            get;
            private set;
        }
 
        /// <summary>
        /// If any PixelShader cannot be processed by the render thread, this
        /// event will be raised.  
        /// </summary>
        public static event EventHandler InvalidPixelShaderEncountered
        {
            add
            {
                // Just forward to the internal event on MediaContext
                MediaContext mediaContext = MediaContext.CurrentMediaContext;
                mediaContext.InvalidPixelShaderEncountered += value;
            }
            remove
            {
                MediaContext mediaContext = MediaContext.CurrentMediaContext;
                mediaContext.InvalidPixelShaderEncountered -= value;
            }
        }
 
        /// <summary>
        /// This method is invoked whenever the source property changes. 
        /// </summary>
        private void UriSourcePropertyChangedHook(DependencyPropertyChangedEventArgs e)
        {
            // Decided against comparing the URI because the user might want to change the shader on the filesystem
            // and reload it. 
 
            // We do not support async loading of shaders here. If that is desired the user needs to use the SetStreamSource
            // API.
            
            Uri newUri = (Uri)e.NewValue;
            Stream stream = null;
 
            try {                    
                if (newUri != null)
                {
                    if (!newUri.IsAbsoluteUri)
                    {
                         newUri = BaseUriHelper.GetResolvedUri(BaseUriHelper.BaseUri, newUri);
                    }
 
                    Debug.Assert(newUri.IsAbsoluteUri);
 
                    // Now the URI is an absolute URI.
 
                    //
                    // Only allow file and pack URIs.
                    if (!newUri.IsFile && 
                        !PackUriHelper.IsPackUri(newUri))
                    {
                        throw new ArgumentException(SR.Get(SRID.Effect_SourceUriMustBeFileOrPack));
                    }
 
                    stream = WpfWebRequestHelper.CreateRequestAndGetResponseStream(newUri);
                }
 
                LoadPixelShaderFromStreamIntoMemory(stream);            
            }
            finally
            {
                if (stream != null)
                {
                    stream.Dispose();
                }
            }
        }
 
        /// <summary>
        /// Reads the byte code for the pixel shader into a local byte array. If the stream is null, the byte array
        /// will be empty (length 0). The compositor will use an identity shader.
        /// </summary>
        /// <SecurityNote>
        /// SecurityCritical - because this method sets the critical shader byte code data.
        /// TreatAsSafe - Demands UI window permission which enforces that the caller is trusted.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        private void LoadPixelShaderFromStreamIntoMemory(Stream source) 
        {
            SecurityHelper.DemandUIWindowPermission();
            
            _shaderBytecode = new SecurityCriticalData<byte[]>(null);
            
            if (source != null)
            {
                if (!source.CanSeek)
                {
                    throw new InvalidOperationException(SR.Get(SRID.Effect_ShaderSeekableStream));
                }
 
                int len = (int)source.Length;  // only works on seekable streams.
 
                if (len % sizeof(int) != 0)
                {
                    throw new InvalidOperationException(SR.Get(SRID.Effect_ShaderBytecodeSize));
                }
                
                BinaryReader br = new BinaryReader(source);
                _shaderBytecode = new SecurityCriticalData<byte[]>(new byte[len]);
                int lengthRead = br.Read(_shaderBytecode.Value, 0, (int)len);
 
                //
                // The first 4 bytes contain version info in the form of
                // [Minor][Major][xx][xx]
                //
                if (_shaderBytecode.Value != null && _shaderBytecode.Value.Length > 3)
                {
                    ShaderMajorVersion = _shaderBytecode.Value[1];
                    ShaderMinorVersion = _shaderBytecode.Value[0];
                }
                else
                {
                    ShaderMajorVersion = ShaderMinorVersion = 0;
                }
 
                Debug.Assert(len == lengthRead); 
            }
 
            // We received new stream data. Need to register for a async update to update the composition
            // engine. 
            RegisterForAsyncUpdateResource();
 
            //
            // Notify any ShaderEffects listening that the bytecode changed.
            // The bytecode may have changed from a ps_3_0 shader to a ps_2_0 shader, and any
            // ShaderEffects using this PixelShader need to check that they are using only
            // registers that are valid in ps_2_0.
            //
            if (_shaderBytecodeChanged != null)
            {
                _shaderBytecodeChanged(this, null);
            }
        }
 
 
        /// <SecurityNote>
        ///     Critical: This code accesses unsafe code blocks
        ///     TreatAsSafe: This code does is safe to call and calling a channel with pointers is ok
        /// </SecurityNote>
        [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))
            {
                checked
                {
                    DUCE.MILCMD_PIXELSHADER data;
                    data.Type = MILCMD.MilCmdPixelShader;
                    data.Handle = _duceResource.GetHandle(channel);
                    data.PixelShaderBytecodeSize = (_shaderBytecode.Value == null) ? 0 : (uint)(_shaderBytecode.Value).Length;
                    data.ShaderRenderMode = ShaderRenderMode;
                    data.CompileSoftwareShader = CompositionResourceManager.BooleanToUInt32(!(ShaderMajorVersion == 3 && ShaderMinorVersion == 0));
 
                    unsafe
                    {
                        channel.BeginCommand(
                            (byte*)&data,                      
                            sizeof(DUCE.MILCMD_PIXELSHADER),
                            (int)data.PixelShaderBytecodeSize);   // extra data
 
                            if (data.PixelShaderBytecodeSize > 0)
                            {
                                fixed (byte *pPixelShaderBytecode = _shaderBytecode.Value)
                                {
                                    channel.AppendCommandData(pPixelShaderBytecode, (int)data.PixelShaderBytecodeSize);
                                }
                            }
                    }
                }
 
                channel.EndCommand();
            }
        }
 
        /// <summary>
        /// Implementation of <see cref="System.Windows.Freezable.CloneCore(Freezable)">Freezable.CloneCore</see>.
        /// </summary>
        /// <param name="sourceFreezable"></param>
        protected override void CloneCore(Freezable sourceFreezable)
        {
            PixelShader shader = (PixelShader)sourceFreezable;
            base.CloneCore(sourceFreezable);
            CopyCommon(shader);
        }
 
 
        /// <summary>
        /// Implementation of <see cref="System.Windows.Freezable.CloneCurrentValueCore(Freezable)">Freezable.CloneCurrentValueCore</see>.
        /// </summary>
        /// <param name="sourceFreezable"></param>
        protected override void CloneCurrentValueCore(Freezable sourceFreezable)
        {
            PixelShader shader = (PixelShader)sourceFreezable;
            base.CloneCurrentValueCore(sourceFreezable);
            CopyCommon(shader);
        }
 
 
        /// <summary>
        /// Implementation of <see cref="System.Windows.Freezable.GetAsFrozenCore(Freezable)">Freezable.GetAsFrozenCore</see>.
        /// </summary>
        /// <param name="sourceFreezable"></param>
        protected override void GetAsFrozenCore(Freezable sourceFreezable)
        {
            PixelShader shader = (PixelShader)sourceFreezable;
            base.GetAsFrozenCore(sourceFreezable);
            CopyCommon(shader);
        }
 
 
        /// <summary>
        /// Implementation of <see cref="System.Windows.Freezable.GetCurrentValueAsFrozenCore(Freezable)">Freezable.GetCurrentValueAsFrozenCore</see>.
        /// </summary>
        /// <param name="sourceFreezable"></param>
        protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
        {
            PixelShader shader = (PixelShader)sourceFreezable;
            base.GetCurrentValueAsFrozenCore(sourceFreezable);
            CopyCommon(shader);
        }
 
        /// <summary>
        /// Clones values that do not have corresponding DPs.
        /// </summary>
        /// <param name="transform"></param>
        /// <SecurityNote>
        /// SecurityCritical - critical because it access the shader byte code which is a critical resource.
        /// TreatAsSafe - this API is not dangereous (and could be exposed publicly) because it copies the shader
        /// byte code from one PixelShader to another. Since the byte code is marked security critical, the source's byte
        /// code is trusted (verified or provided by a trusted caller). There is also no way to modify the byte code during
        /// the copy.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        private void CopyCommon(PixelShader shader)
        {
            byte[] sourceBytecode = shader._shaderBytecode.Value;
            byte[] destinationBytecode = null;
 
            if (sourceBytecode != null)
            {
                destinationBytecode = new byte[sourceBytecode.Length];
                sourceBytecode.CopyTo(destinationBytecode, 0);
            }                          
 
            _shaderBytecode = new SecurityCriticalData<byte[]>(destinationBytecode);
        }
 
        /// <SecurityNote>
        /// We need to ensure that _shaderByteCode contains only trusted data/shader byte code. This can be
        /// achieved via two means:
        /// 1) Verify the byte code to be safe to run on the GPU.
        /// 2) The shader byte code has been provided by a trusted source. 
        /// Currently 1) is not possible since we have no means to verify shader byte code. Therefore we 
        /// currently require that byte code provided to us can only come from a trusted source.
        /// </SecurityNote>
        private SecurityCriticalData<byte[]> _shaderBytecode;
 
        //
        // Introduced with ps_3_0 for ShaderEffect's use
        //
        // The scenario is setting up a ShaderEffect with a PixelShader containing a ps_3_0 shader,
        // setting a ps_3_0 only register (int, bool, or float registers 32 and above), then using
        // ShaderEffect.PixelShader.UriSource (or SetStreamSource) to swap the ps_3_0 shader out
        // for a ps_2_0 one. Now there's a value in a register that doesn't exist in ps_2_0.
        //
        // The fix is to have ShaderEffect listen for this event on its PixelShader, and verifying
        // that no invalid registers are used when switching a ps_3_0 register to a ps_2_0 one.
        //
        internal event EventHandler _shaderBytecodeChanged;
    }
}