| File: System\Windows\Media\Effects\PixelShader.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.IO; using MS.Internal; using System.Windows.Media.Animation; using System.Windows.Media.Composition; using System.Windows.Navigation; 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 && !MS.Internal.IO.Packaging.PackUriHelper.IsPackUri(newUri)) { throw new ArgumentException(SR.Effect_SourceUriMustBeFileOrPack); } stream = WpfWebRequestHelper.CreateRequestAndGetResponseStream(newUri); } LoadPixelShaderFromStreamIntoMemory(stream); } finally { 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> private void LoadPixelShaderFromStreamIntoMemory(Stream source) { _shaderBytecode = null; if (source != null) { if (!source.CanSeek) { throw new InvalidOperationException(SR.Effect_ShaderSeekableStream); } int len = (int)source.Length; // only works on seekable streams. if (len % sizeof(int) != 0) { throw new InvalidOperationException(SR.Effect_ShaderBytecodeSize); } BinaryReader br = new BinaryReader(source); _shaderBytecode = new byte[len]; int lengthRead = br.Read(_shaderBytecode, 0, (int)len); // // The first 4 bytes contain version info in the form of // [Minor][Major][xx][xx] // if (_shaderBytecode != null && _shaderBytecode.Length > 3) { ShaderMajorVersion = _shaderBytecode[1]; ShaderMinorVersion = _shaderBytecode[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); } } 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 = (uint)(_shaderBytecode?.Length ?? 0); 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) { 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> private void CopyCommon(PixelShader shader) { byte[] sourceBytecode = shader._shaderBytecode; byte[] destinationBytecode = null; if (sourceBytecode != null) { destinationBytecode = new byte[sourceBytecode.Length]; sourceBytecode.CopyTo(destinationBytecode, 0); } _shaderBytecode = destinationBytecode; } private 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; } }