#region License /* FNA - XNA4 Reimplementation for Desktop Platforms * Copyright 2009-2020 Ethan Lee and the MonoGame Team * * Released under the Microsoft Public License. * See LICENSE for details. */ #endregion #region Using Statements using System; using System.Collections.Generic; using System.Runtime.InteropServices; #endregion namespace Microsoft.Xna.Framework.Graphics { public class Effect : GraphicsResource { #region Public Properties private EffectTechnique INTERNAL_currentTechnique; public EffectTechnique CurrentTechnique { get { return INTERNAL_currentTechnique; } set { MojoShader.MOJOSHADER_effectSetTechnique( glEffect.EffectData, value.TechniquePointer ); INTERNAL_currentTechnique = value; } } public EffectParameterCollection Parameters { get; private set; } public EffectTechniqueCollection Techniques { get; private set; } #endregion #region Internal Variables internal IGLEffect glEffect; #endregion #region Private Variables private Dictionary samplerMap = new Dictionary(new IntPtrBoxlessComparer()); private class IntPtrBoxlessComparer : IEqualityComparer { public bool Equals(IntPtr x, IntPtr y) { return x == y; } public int GetHashCode(IntPtr obj) { return obj.GetHashCode(); } } private IntPtr stateChangesPtr; #endregion #region Private Static Variables private static readonly EffectParameterType[] XNAType = new EffectParameterType[] { EffectParameterType.Void, // MOJOSHADER_SYMTYPE_VOID EffectParameterType.Bool, // MOJOSHADER_SYMTYPE_BOOL EffectParameterType.Int32, // MOJOSHADER_SYMTYPE_INT EffectParameterType.Single, // MOJOSHADER_SYMTYPE_FLOAT EffectParameterType.String, // MOJOSHADER_SYMTYPE_STRING EffectParameterType.Texture, // MOJOSHADER_SYMTYPE_TEXTURE EffectParameterType.Texture1D, // MOJOSHADER_SYMTYPE_TEXTURE1D EffectParameterType.Texture2D, // MOJOSHADER_SYMTYPE_TEXTURE2D EffectParameterType.Texture3D, // MOJOSHADER_SYMTYPE_TEXTURE3D EffectParameterType.TextureCube // MOJOSHADER_SYMTYPE_TEXTURECUBE }; private static readonly EffectParameterClass[] XNAClass = new EffectParameterClass[] { EffectParameterClass.Scalar, // MOJOSHADER_SYMCLASS_SCALAR EffectParameterClass.Vector, // MOJOSHADER_SYMCLASS_VECTOR EffectParameterClass.Matrix, // MOJOSHADER_SYMCLASS_MATRIX_ROWS EffectParameterClass.Matrix, // MOJOSHADER_SYMCLASS_MATRIX_COLUMNS EffectParameterClass.Object, // MOJOSHADER_SYMCLASS_OBJECT EffectParameterClass.Struct // MOJOSHADER_SYMCLASS_STRUCT }; private static readonly Blend[] XNABlend = new Blend[] { (Blend) (-1), // NOPE Blend.Zero, // MOJOSHADER_BLEND_ZERO Blend.One, // MOJOSHADER_BLEND_ONE Blend.SourceColor, // MOJOSHADER_BLEND_SRCCOLOR Blend.InverseSourceColor, // MOJOSHADER_BLEND_INVSRCCOLOR Blend.SourceAlpha, // MOJOSHADER_BLEND_SRCALPHA Blend.InverseSourceAlpha, // MOJOSHADER_BLEND_INVSRCALPHA Blend.DestinationAlpha, // MOJOSHADER_BLEND_DESTALPHA Blend.InverseDestinationAlpha, // MOJOSHADER_BLEND_INVDESTALPHA Blend.DestinationColor, // MOJOSHADER_BLEND_DESTCOLOR Blend.InverseDestinationColor, // MOJOSHADER_BLEND_INVDESTCOLOR Blend.SourceAlphaSaturation, // MOJOSHADER_BLEND_SRCALPHASAT (Blend) (-1), // NOPE (Blend) (-1), // NOPE Blend.BlendFactor, // MOJOSHADER_BLEND_BLENDFACTOR Blend.InverseBlendFactor // MOJOSHADER_BLEND_INVBLENDFACTOR }; private static readonly BlendFunction[] XNABlendOp = new BlendFunction[] { (BlendFunction) (-1), // NOPE BlendFunction.Add, // MOJOSHADER_BLENDOP_ADD BlendFunction.Subtract, // MOJOSHADER_BLENDOP_SUBTRACT BlendFunction.ReverseSubtract, // MOJOSHADER_BLENDOP_REVSUBTRACT BlendFunction.Min, // MOJOSHADER_BLENDOP_MIN BlendFunction.Max // MOJOSHADER_BLENDOP_MAX }; private static readonly CompareFunction[] XNACompare = new CompareFunction[] { (CompareFunction) (-1), // NOPE CompareFunction.Never, // MOJOSHADER_CMP_NEVER CompareFunction.Less, // MOJOSHADER_CMP_LESS CompareFunction.Equal, // MOJOSHADER_CMP_EQUAL CompareFunction.LessEqual, // MOJOSHADER_CMP_LESSEQUAL CompareFunction.Greater, // MOJOSHADER_CMP_GREATER CompareFunction.NotEqual, // MOJOSHADER_CMP_NOTEQUAL CompareFunction.GreaterEqual, // MOJOSHADER_CMP_GREATEREQUAL CompareFunction.Always // MOJOSHADER_CMP_ALWAYS }; private static readonly StencilOperation[] XNAStencilOp = new StencilOperation[] { (StencilOperation) (-1), // NOPE StencilOperation.Keep, // MOJOSHADER_STENCILOP_KEEP StencilOperation.Zero, // MOJOSHADER_STENCILOP_ZERO StencilOperation.Replace, // MOJOSHADER_STENCILOP_REPLACE StencilOperation.IncrementSaturation, // MOJOSHADER_STENCILOP_INCRSAT StencilOperation.DecrementSaturation, // MOJOSHADER_STENCILOP_DECRSAT StencilOperation.Invert, // MOJOSHADER_STENCILOP_INVERT StencilOperation.Increment, // MOJOSHADER_STENCILOP_INCR StencilOperation.Decrement // MOJOSHADER_STENCILOP_DECR }; private static readonly TextureAddressMode[] XNAAddress = new TextureAddressMode[] { (TextureAddressMode) (-1), // NOPE TextureAddressMode.Wrap, // MOJOSHADER_TADDRESS_WRAP TextureAddressMode.Mirror, // MOJOSHADER_TADDRESS_MIRROR TextureAddressMode.Clamp // MOJOSHADER_TADDRESS_CLAMP }; private static readonly MojoShader.MOJOSHADER_textureFilterType[] XNAMag = new MojoShader.MOJOSHADER_textureFilterType[] { MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.Linear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.Point MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC, // TextureFilter.Anisotropic MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.LinearMipPoint MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.PointMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.MinLinearMagPointMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.MinLinearMagPointMipPoint MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.MinPointMagLinearMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR // TextureFilter.MinPointMagLinearMipPoint }; private static readonly MojoShader.MOJOSHADER_textureFilterType[] XNAMin = new MojoShader.MOJOSHADER_textureFilterType[] { MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.Linear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.Point MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC, // TextureFilter.Anisotropic MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.LinearMipPoint MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.PointMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.MinLinearMagPointMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.MinLinearMagPointMipPoint MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.MinPointMagLinearMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT // TextureFilter.MinPointMagLinearMipPoint }; private static readonly MojoShader.MOJOSHADER_textureFilterType[] XNAMip = new MojoShader.MOJOSHADER_textureFilterType[] { MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.Linear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.Point MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC, // TextureFilter.Anisotropic MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.LinearMipPoint MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.PointMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.MinLinearMagPointMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT, // TextureFilter.MinLinearMagPointMipPoint MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR, // TextureFilter.MinPointMagLinearMipLinear MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT // TextureFilter.MinPointMagLinearMipPoint }; #endregion #region Public Constructor public Effect(GraphicsDevice graphicsDevice, byte[] effectCode) { GraphicsDevice = graphicsDevice; // Send the blob to the GLDevice to be parsed/compiled glEffect = graphicsDevice.GLDevice.CreateEffect(effectCode); // This is where it gets ugly... INTERNAL_parseEffectStruct(); // The default technique is the first technique. CurrentTechnique = Techniques[0]; // Use native memory for changes, .NET loves moving this around unsafe { stateChangesPtr = Marshal.AllocHGlobal( sizeof(MojoShader.MOJOSHADER_effectStateChanges) ); MojoShader.MOJOSHADER_effectStateChanges *stateChanges = (MojoShader.MOJOSHADER_effectStateChanges*) stateChangesPtr; stateChanges->render_state_change_count = 0; stateChanges->sampler_state_change_count = 0; stateChanges->vertex_sampler_state_change_count = 0; } } #endregion #region Protected Constructor protected Effect(Effect cloneSource) { GraphicsDevice = cloneSource.GraphicsDevice; // Send the parsed data to be cloned and recompiled by MojoShader glEffect = GraphicsDevice.GLDevice.CloneEffect( cloneSource.glEffect ); // Double the ugly, double the fun! INTERNAL_parseEffectStruct(); // Copy texture parameters, if applicable for (int i = 0; i < cloneSource.Parameters.Count; i += 1) { Parameters[i].texture = cloneSource.Parameters[i].texture; } // The default technique is whatever the current technique was. for (int i = 0; i < cloneSource.Techniques.Count; i += 1) { if (cloneSource.Techniques[i] == cloneSource.CurrentTechnique) { CurrentTechnique = Techniques[i]; } } // Use native memory for changes, .NET loves moving this around unsafe { stateChangesPtr = Marshal.AllocHGlobal( sizeof(MojoShader.MOJOSHADER_effectStateChanges) ); MojoShader.MOJOSHADER_effectStateChanges *stateChanges = (MojoShader.MOJOSHADER_effectStateChanges*) stateChangesPtr; stateChanges->render_state_change_count = 0; stateChanges->sampler_state_change_count = 0; stateChanges->vertex_sampler_state_change_count = 0; } } #endregion #region Public Methods public virtual Effect Clone() { return new Effect(this); } #endregion #region Protected Methods protected override void Dispose(bool disposing) { if (!IsDisposed) { if (glEffect != null) { GraphicsDevice.GLDevice.AddDisposeEffect(glEffect); } if (stateChangesPtr != IntPtr.Zero) { Marshal.FreeHGlobal(stateChangesPtr); stateChangesPtr = IntPtr.Zero; } } base.Dispose(disposing); } protected internal virtual void OnApply() { } #endregion #region Internal Methods internal unsafe void INTERNAL_applyEffect(uint pass) { GraphicsDevice.GLDevice.ApplyEffect( glEffect, CurrentTechnique.TechniquePointer, pass, stateChangesPtr ); MojoShader.MOJOSHADER_effectStateChanges *stateChanges = (MojoShader.MOJOSHADER_effectStateChanges*) stateChangesPtr; if (stateChanges->render_state_change_count > 0) { PipelineCache pipelineCache = GraphicsDevice.PipelineCache; pipelineCache.BeginApplyBlend(); pipelineCache.BeginApplyDepthStencil(); pipelineCache.BeginApplyRasterizer(); // Used to avoid redundant device state application bool blendStateChanged = false; bool depthStencilStateChanged = false; bool rasterizerStateChanged = false; MojoShader.MOJOSHADER_effectState* states = (MojoShader.MOJOSHADER_effectState*) stateChanges->render_state_changes; for (int i = 0; i < stateChanges->render_state_change_count; i += 1) { MojoShader.MOJOSHADER_renderStateType type = states[i].type; if ( type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_VERTEXSHADER || type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_PIXELSHADER ) { // Skip shader states continue; } if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_ZENABLE) { MojoShader.MOJOSHADER_zBufferType* val = (MojoShader.MOJOSHADER_zBufferType*) states[i].value.values; pipelineCache.DepthBufferEnable = (*val == MojoShader.MOJOSHADER_zBufferType.MOJOSHADER_ZB_TRUE); depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_FILLMODE) { MojoShader.MOJOSHADER_fillMode* val = (MojoShader.MOJOSHADER_fillMode*) states[i].value.values; if (*val == MojoShader.MOJOSHADER_fillMode.MOJOSHADER_FILL_SOLID) { pipelineCache.FillMode = FillMode.Solid; } else if (*val == MojoShader.MOJOSHADER_fillMode.MOJOSHADER_FILL_WIREFRAME) { pipelineCache.FillMode = FillMode.WireFrame; } rasterizerStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_ZWRITEENABLE) { int* val = (int*) states[i].value.values; pipelineCache.DepthBufferWriteEnable = (*val == 1); depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_SRCBLEND) { MojoShader.MOJOSHADER_blendMode* val = (MojoShader.MOJOSHADER_blendMode*) states[i].value.values; pipelineCache.ColorSourceBlend = XNABlend[(int) *val]; if (!pipelineCache.SeparateAlphaBlend) { pipelineCache.AlphaSourceBlend = XNABlend[(int) *val]; } blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_DESTBLEND) { MojoShader.MOJOSHADER_blendMode* val = (MojoShader.MOJOSHADER_blendMode*) states[i].value.values; pipelineCache.ColorDestinationBlend = XNABlend[(int) *val]; if (!pipelineCache.SeparateAlphaBlend) { pipelineCache.AlphaDestinationBlend = XNABlend[(int) *val]; } blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_CULLMODE) { MojoShader.MOJOSHADER_cullMode* val = (MojoShader.MOJOSHADER_cullMode*) states[i].value.values; if (*val == MojoShader.MOJOSHADER_cullMode.MOJOSHADER_CULL_NONE) { pipelineCache.CullMode = CullMode.None; } else if (*val == MojoShader.MOJOSHADER_cullMode.MOJOSHADER_CULL_CW) { pipelineCache.CullMode = CullMode.CullClockwiseFace; } else if (*val == MojoShader.MOJOSHADER_cullMode.MOJOSHADER_CULL_CCW) { pipelineCache.CullMode = CullMode.CullCounterClockwiseFace; } rasterizerStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_ZFUNC) { MojoShader.MOJOSHADER_compareFunc* val = (MojoShader.MOJOSHADER_compareFunc*) states[i].value.values; pipelineCache.DepthBufferFunction = XNACompare[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_ALPHABLENDENABLE) { // FIXME: Assuming no other blend calls are made in the effect! -flibit int* val = (int*) states[i].value.values; if (*val == 0) { pipelineCache.ColorSourceBlend = Blend.One; pipelineCache.ColorDestinationBlend = Blend.Zero; pipelineCache.AlphaSourceBlend = Blend.One; pipelineCache.AlphaDestinationBlend = Blend.Zero; blendStateChanged = true; } } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_STENCILENABLE) { int* val = (int*) states[i].value.values; pipelineCache.StencilEnable = (*val == 1); depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_STENCILFAIL) { MojoShader.MOJOSHADER_stencilOp* val = (MojoShader.MOJOSHADER_stencilOp*) states[i].value.values; pipelineCache.StencilFail = XNAStencilOp[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_STENCILZFAIL) { MojoShader.MOJOSHADER_stencilOp* val = (MojoShader.MOJOSHADER_stencilOp*) states[i].value.values; pipelineCache.StencilDepthBufferFail = XNAStencilOp[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_STENCILPASS) { MojoShader.MOJOSHADER_stencilOp* val = (MojoShader.MOJOSHADER_stencilOp*) states[i].value.values; pipelineCache.StencilPass = XNAStencilOp[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_STENCILFUNC) { MojoShader.MOJOSHADER_compareFunc* val = (MojoShader.MOJOSHADER_compareFunc*) states[i].value.values; pipelineCache.StencilFunction = XNACompare[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_STENCILREF) { int* val = (int*) states[i].value.values; pipelineCache.ReferenceStencil = *val; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_STENCILMASK) { int* val = (int*) states[i].value.values; pipelineCache.StencilMask = *val; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_STENCILWRITEMASK) { int* val = (int*) states[i].value.values; pipelineCache.StencilWriteMask = *val; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_MULTISAMPLEANTIALIAS) { int* val = (int*) states[i].value.values; pipelineCache.MultiSampleAntiAlias = (*val == 1); rasterizerStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_MULTISAMPLEMASK) { int* val = (int*) states[i].value.values; pipelineCache.MultiSampleMask = *val; blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_COLORWRITEENABLE) { int* val = (int*) states[i].value.values; pipelineCache.ColorWriteChannels = (ColorWriteChannels) (*val); blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_BLENDOP) { MojoShader.MOJOSHADER_blendOp* val = (MojoShader.MOJOSHADER_blendOp*) states[i].value.values; pipelineCache.ColorBlendFunction = XNABlendOp[(int) *val]; blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_SCISSORTESTENABLE) { int* val = (int*) states[i].value.values; pipelineCache.ScissorTestEnable = (*val == 1); rasterizerStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_SLOPESCALEDEPTHBIAS) { float* val = (float*) states[i].value.values; pipelineCache.SlopeScaleDepthBias = *val; rasterizerStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_TWOSIDEDSTENCILMODE) { int* val = (int*) states[i].value.values; pipelineCache.TwoSidedStencilMode = (*val == 1); depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_CCW_STENCILFAIL) { MojoShader.MOJOSHADER_stencilOp* val = (MojoShader.MOJOSHADER_stencilOp*) states[i].value.values; pipelineCache.CCWStencilFail = XNAStencilOp[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_CCW_STENCILZFAIL) { MojoShader.MOJOSHADER_stencilOp* val = (MojoShader.MOJOSHADER_stencilOp*) states[i].value.values; pipelineCache.CCWStencilDepthBufferFail = XNAStencilOp[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_CCW_STENCILPASS) { MojoShader.MOJOSHADER_stencilOp* val = (MojoShader.MOJOSHADER_stencilOp*) states[i].value.values; pipelineCache.CCWStencilPass = XNAStencilOp[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_CCW_STENCILFUNC) { MojoShader.MOJOSHADER_compareFunc* val = (MojoShader.MOJOSHADER_compareFunc*) states[i].value.values; pipelineCache.CCWStencilFunction = XNACompare[(int) *val]; depthStencilStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_COLORWRITEENABLE1) { int* val = (int*) states[i].value.values; pipelineCache.ColorWriteChannels1 = (ColorWriteChannels) (*val); blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_COLORWRITEENABLE2) { int* val = (int*) states[i].value.values; pipelineCache.ColorWriteChannels2 = (ColorWriteChannels) (*val); blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_COLORWRITEENABLE3) { int* val = (int*) states[i].value.values; pipelineCache.ColorWriteChannels3 = (ColorWriteChannels) (*val); blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_BLENDFACTOR) { // FIXME: RGBA? -flibit int* val = (int*) states[i].value.values; pipelineCache.BlendFactor = new Color( (*val >> 24) & 0xFF, (*val >> 16) & 0xFF, (*val >> 8) & 0xFF, *val & 0xFF ); blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_DEPTHBIAS) { float* val = (float*) states[i].value.values; pipelineCache.DepthBias = *val; rasterizerStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_SEPARATEALPHABLENDENABLE) { int* val = (int*) states[i].value.values; pipelineCache.SeparateAlphaBlend = (*val == 1); // FIXME: Do we want to update the state for this...? -flibit } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_SRCBLENDALPHA) { MojoShader.MOJOSHADER_blendMode* val = (MojoShader.MOJOSHADER_blendMode*) states[i].value.values; pipelineCache.AlphaSourceBlend = XNABlend[(int) *val]; blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_DESTBLENDALPHA) { MojoShader.MOJOSHADER_blendMode* val = (MojoShader.MOJOSHADER_blendMode*) states[i].value.values; pipelineCache.AlphaDestinationBlend = XNABlend[(int) *val]; blendStateChanged = true; } else if (type == MojoShader.MOJOSHADER_renderStateType.MOJOSHADER_RS_BLENDOPALPHA) { MojoShader.MOJOSHADER_blendOp* val = (MojoShader.MOJOSHADER_blendOp*) states[i].value.values; pipelineCache.AlphaBlendFunction = XNABlendOp[(int) *val]; blendStateChanged = true; } else if (type == (MojoShader.MOJOSHADER_renderStateType) 178) { /* Apparently this is "SetSampler"? */ } else { throw new NotImplementedException("Unhandled render state! " + type); } } if (blendStateChanged) { pipelineCache.EndApplyBlend(); } if (depthStencilStateChanged) { pipelineCache.EndApplyDepthStencil(); } if (rasterizerStateChanged) { pipelineCache.EndApplyRasterizer(); } } if (stateChanges->sampler_state_change_count > 0) { INTERNAL_updateSamplers( stateChanges->sampler_state_change_count, (MojoShader.MOJOSHADER_samplerStateRegister*) stateChanges->sampler_state_changes, GraphicsDevice.Textures, GraphicsDevice.SamplerStates ); } if (stateChanges->vertex_sampler_state_change_count > 0) { INTERNAL_updateSamplers( stateChanges->vertex_sampler_state_change_count, (MojoShader.MOJOSHADER_samplerStateRegister*) stateChanges->vertex_sampler_state_changes, GraphicsDevice.VertexTextures, GraphicsDevice.VertexSamplerStates ); } } private unsafe void INTERNAL_updateSamplers( uint changeCount, MojoShader.MOJOSHADER_samplerStateRegister* registers, TextureCollection textures, SamplerStateCollection samplers ) { for (int i = 0; i < changeCount; i += 1) { if (registers[i].sampler_state_count == 0) { // Nothing to do continue; } int register = (int) registers[i].sampler_register; PipelineCache pipelineCache = GraphicsDevice.PipelineCache; pipelineCache.BeginApplySampler(samplers, register); // Used to prevent redundant sampler changes bool samplerChanged = false; bool filterChanged = false; // Current sampler filter TextureFilter filter = pipelineCache.Filter; MojoShader.MOJOSHADER_textureFilterType magFilter = XNAMag[(int) filter]; MojoShader.MOJOSHADER_textureFilterType minFilter = XNAMin[(int) filter]; MojoShader.MOJOSHADER_textureFilterType mipFilter = XNAMip[(int) filter]; MojoShader.MOJOSHADER_effectSamplerState* states = (MojoShader.MOJOSHADER_effectSamplerState*) registers[i].sampler_states; for (int j = 0; j < registers[i].sampler_state_count; j += 1) { MojoShader.MOJOSHADER_samplerStateType type = states[j].type; if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_TEXTURE) { EffectParameter texParam; if (samplerMap.TryGetValue(registers[i].sampler_name, out texParam)) { Texture texture = texParam.texture; if (texture != null) { textures[register] = texture; } } } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_ADDRESSU) { MojoShader.MOJOSHADER_textureAddress* val = (MojoShader.MOJOSHADER_textureAddress*) states[j].value.values; pipelineCache.AddressU = XNAAddress[(int) *val]; samplerChanged = true; } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_ADDRESSV) { MojoShader.MOJOSHADER_textureAddress* val = (MojoShader.MOJOSHADER_textureAddress*) states[j].value.values; pipelineCache.AddressV = XNAAddress[(int) *val]; samplerChanged = true; } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_ADDRESSW) { MojoShader.MOJOSHADER_textureAddress* val = (MojoShader.MOJOSHADER_textureAddress*) states[j].value.values; pipelineCache.AddressW = XNAAddress[(int) *val]; samplerChanged = true; } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_MAGFILTER) { MojoShader.MOJOSHADER_textureFilterType* val = (MojoShader.MOJOSHADER_textureFilterType*) states[j].value.values; magFilter = *val; filterChanged = true; } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_MINFILTER) { MojoShader.MOJOSHADER_textureFilterType* val = (MojoShader.MOJOSHADER_textureFilterType*) states[j].value.values; minFilter = *val; filterChanged = true; } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_MIPFILTER) { MojoShader.MOJOSHADER_textureFilterType* val = (MojoShader.MOJOSHADER_textureFilterType*) states[j].value.values; mipFilter = *val; filterChanged = true; } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_MIPMAPLODBIAS) { float* val = (float*) states[j].value.values; pipelineCache.MipMapLODBias = *val; samplerChanged = true; } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_MAXMIPLEVEL) { int* val = (int*) states[j].value.values; pipelineCache.MaxMipLevel = *val; samplerChanged = true; } else if (type == MojoShader.MOJOSHADER_samplerStateType.MOJOSHADER_SAMP_MAXANISOTROPY) { int* val = (int*) states[j].value.values; pipelineCache.MaxAnisotropy = *val; samplerChanged = true; } else { throw new NotImplementedException("Unhandled sampler state! " + type); } } if (filterChanged) { if (magFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT) { if (minFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT) { if ( mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_NONE || mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT ) { pipelineCache.Filter = TextureFilter.Point; } else if ( mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR || mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC ) { pipelineCache.Filter = TextureFilter.PointMipLinear; } else { throw new NotImplementedException("Unhandled mipfilter type! " + mipFilter); } } else if ( minFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR || minFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC ) { if ( mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_NONE || mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT ) { pipelineCache.Filter = TextureFilter.MinLinearMagPointMipPoint; } else if ( mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR || mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC ) { pipelineCache.Filter = TextureFilter.MinLinearMagPointMipLinear; } else { throw new NotImplementedException("Unhandled mipfilter type! " + mipFilter); } } else { throw new NotImplementedException("Unhandled minfilter type! " + minFilter); } } else if ( magFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR || magFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC ) { if (minFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT) { if ( mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_NONE || mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT ) { pipelineCache.Filter = TextureFilter.MinPointMagLinearMipPoint; } else if ( mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR || mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC ) { pipelineCache.Filter = TextureFilter.MinPointMagLinearMipLinear; } else { throw new NotImplementedException("Unhandled mipfilter type! " + mipFilter); } } else if ( minFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR || minFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC ) { if ( mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_NONE || mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_POINT ) { pipelineCache.Filter = TextureFilter.LinearMipPoint; } else if ( mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_LINEAR || mipFilter == MojoShader.MOJOSHADER_textureFilterType.MOJOSHADER_TEXTUREFILTER_ANISOTROPIC ) { pipelineCache.Filter = TextureFilter.Linear; } else { throw new NotImplementedException("Unhandled mipfilter type! " + mipFilter); } } else { throw new NotImplementedException("Unhandled minfilter type! " + minFilter); } } else { throw new NotImplementedException("Unhandled magfilter type! " + magFilter); } samplerChanged = true; } if (samplerChanged) { pipelineCache.EndApplySampler(samplers, register); } } } #endregion #region Private Methods private unsafe void INTERNAL_parseEffectStruct() { MojoShader.MOJOSHADER_effect* effectPtr = (MojoShader.MOJOSHADER_effect*) glEffect.EffectData; // Set up Parameters MojoShader.MOJOSHADER_effectParam* paramPtr = (MojoShader.MOJOSHADER_effectParam*) effectPtr->parameters; List parameters = new List(); for (int i = 0; i < effectPtr->param_count; i += 1) { MojoShader.MOJOSHADER_effectParam param = paramPtr[i]; if ( param.value.type.parameter_type == MojoShader.MOJOSHADER_symbolType.MOJOSHADER_SYMTYPE_VERTEXSHADER || param.value.type.parameter_type == MojoShader.MOJOSHADER_symbolType.MOJOSHADER_SYMTYPE_PIXELSHADER ) { // Skip shader objects... continue; } else if ( param.value.type.parameter_type >= MojoShader.MOJOSHADER_symbolType.MOJOSHADER_SYMTYPE_SAMPLER && param.value.type.parameter_type <= MojoShader.MOJOSHADER_symbolType.MOJOSHADER_SYMTYPE_SAMPLERCUBE ) { string textureName = String.Empty; MojoShader.MOJOSHADER_effectSamplerState* states = (MojoShader.MOJOSHADER_effectSamplerState*) param.value.values; for (int j = 0; j < param.value.value_count; j += 1) { if ( states[j].value.type.parameter_type >= MojoShader.MOJOSHADER_symbolType.MOJOSHADER_SYMTYPE_TEXTURE && states[j].value.type.parameter_type <= MojoShader.MOJOSHADER_symbolType.MOJOSHADER_SYMTYPE_TEXTURECUBE ) { MojoShader.MOJOSHADER_effectObject *objectPtr = (MojoShader.MOJOSHADER_effectObject*) effectPtr->objects; int* index = (int*) states[j].value.values; textureName = Marshal.PtrToStringAnsi(objectPtr[*index].mapping.name); break; } } /* Because textures have to be declared before the sampler, * we can assume that it will always be in the list by the * time we get to this point. * -flibit */ for (int j = 0; j < parameters.Count; j += 1) { if (textureName.Equals(parameters[j].Name)) { samplerMap[param.value.name] = parameters[j]; break; } } continue; } EffectParameterCollection structMembers = null; if (param.value.type.member_count > 0) { List memList = new List(); unsafe { MojoShader.MOJOSHADER_symbolStructMember* mem = (MojoShader.MOJOSHADER_symbolStructMember*) param.value.type.members; IntPtr curOffset = IntPtr.Zero; for (int j = 0; j < param.value.type.member_count; j += 1) { uint memSize = mem[j].info.rows * mem[j].info.columns; if (mem[j].info.elements > 0) { memSize *= mem[j].info.elements; } memList.Add(new EffectParameter( Marshal.PtrToStringAnsi(mem[j].name), null, (int) mem[j].info.rows, (int) mem[j].info.columns, (int) mem[j].info.elements, XNAClass[(int) mem[j].info.parameter_class], XNAType[(int) mem[j].info.parameter_type], null, // FIXME: Nested structs! -flibit null, param.value.values + curOffset.ToInt32(), memSize * 4 )); curOffset += (int) memSize * 4; } } structMembers = new EffectParameterCollection(memList); } parameters.Add(new EffectParameter( Marshal.PtrToStringAnsi(param.value.name), Marshal.PtrToStringAnsi(param.value.semantic), (int) param.value.type.rows, (int) param.value.type.columns, (int) param.value.type.elements, XNAClass[(int) param.value.type.parameter_class], XNAType[(int) param.value.type.parameter_type], structMembers, INTERNAL_readAnnotations( param.annotations, param.annotation_count ), param.value.values, param.value.value_count * sizeof(float) )); } Parameters = new EffectParameterCollection(parameters); // Set up Techniques MojoShader.MOJOSHADER_effectTechnique* techPtr = (MojoShader.MOJOSHADER_effectTechnique*) effectPtr->techniques; List techniques = new List(effectPtr->technique_count); for (int i = 0; i < techniques.Capacity; i += 1, techPtr += 1) { // Set up Passes MojoShader.MOJOSHADER_effectPass* passPtr = (MojoShader.MOJOSHADER_effectPass*) techPtr->passes; List passes = new List((int) techPtr->pass_count); for (int j = 0; j < passes.Capacity; j += 1) { MojoShader.MOJOSHADER_effectPass pass = passPtr[j]; passes.Add(new EffectPass( Marshal.PtrToStringAnsi(pass.name), INTERNAL_readAnnotations( pass.annotations, pass.annotation_count ), this, (IntPtr) techPtr, (uint) j )); } techniques.Add(new EffectTechnique( Marshal.PtrToStringAnsi(techPtr->name), (IntPtr) techPtr, new EffectPassCollection(passes), INTERNAL_readAnnotations( techPtr->annotations, techPtr->annotation_count ) )); } Techniques = new EffectTechniqueCollection(techniques); } private unsafe EffectAnnotationCollection INTERNAL_readAnnotations( IntPtr rawAnnotations, uint numAnnotations ) { MojoShader.MOJOSHADER_effectAnnotation* annoPtr = (MojoShader.MOJOSHADER_effectAnnotation*) rawAnnotations; List annotations = new List((int) numAnnotations); for (int i = 0; i < numAnnotations; i += 1) { MojoShader.MOJOSHADER_effectAnnotation anno = annoPtr[i]; annotations.Add(new EffectAnnotation( Marshal.PtrToStringAnsi(anno.name), Marshal.PtrToStringAnsi(anno.semantic), (int) anno.type.rows, (int) anno.type.columns, XNAClass[(int) anno.type.parameter_class], XNAType[(int) anno.type.parameter_type], anno.values )); } return new EffectAnnotationCollection(annotations); } #endregion } }