// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // ----------------------------------------------------------------------------- // Original code from SlimDX project, ported from C++/CLI. // Greetings to SlimDX Group. Original code published with the following license: // ----------------------------------------------------------------------------- /* * Copyright (c) 2007-2011 SlimDX Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using System; using System.IO; using System.Runtime.InteropServices; using System.Text; using SharpDX.Direct3D; using SharpDX.IO; using SharpDX.Text; using Encoding = SharpDX.Text.Encoding; namespace SharpDX.D3DCompiler { using Multimedia; /// /// Represents the compiled bytecode of a shader or effect. /// /// Blob public class ShaderBytecode : IDisposable { /// /// Use this ShaderFlags constant in order to compile an effect with old D3D10CompileEffectFromMemory. /// public const ShaderFlags Effect10 = (ShaderFlags)0x40000000; /// /// Initializes a new instance of the class. /// /// A containing the compiled bytecode. public ShaderBytecode(DataStream data) { Data = new byte[data.Length]; data.Read(Data, 0, Data.Length); } /// /// Initializes a new instance of the class. /// /// A containing the compiled bytecode. public ShaderBytecode(Stream data) { int size = (int)(data.Length - data.Position); byte[] localBuffer = new byte[size]; data.Read(localBuffer, 0, size); Data = localBuffer; } /// /// Initializes a new instance of the class. /// /// The buffer. public ShaderBytecode(byte[] buffer) { Data = buffer; } /// /// Initializes a new instance of the class. /// /// a pointer to a compiler bytecode /// size of the bytecode public ShaderBytecode(IntPtr buffer, int sizeInBytes) { Data = new byte[sizeInBytes]; Utilities.Read(buffer, Data, 0, Data.Length); } /// /// Initializes a new instance of the class. /// /// The BLOB. protected internal ShaderBytecode(Blob blob) { Data = new byte[blob.BufferSize]; Utilities.Read(blob.BufferPointer, Data, 0, Data.Length); blob.Dispose(); } /// /// Gets the buffer pointer. /// public byte[] Data { get; private set; } /// /// Compiles the provided shader or effect source. /// /// A string containing the source of the shader or effect to compile. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// Name of the source file. /// The secondary data flags. /// The secondary data. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult Compile(string shaderSource, string profile, ShaderFlags shaderFlags = ShaderFlags.None, EffectFlags effectFlags = EffectFlags.None, string sourceFileName = "unknown", SecondaryDataFlags secondaryDataFlags = SecondaryDataFlags.None, DataStream secondaryData = null) { if (string.IsNullOrEmpty(shaderSource)) { throw new ArgumentNullException("shaderSource"); } var shaderSourcePtr = Marshal.StringToHGlobalAnsi(shaderSource); try { return Compile( shaderSourcePtr, shaderSource.Length, null, profile, shaderFlags, effectFlags, null, null, sourceFileName, secondaryDataFlags, secondaryData); } finally { if (shaderSourcePtr != IntPtr.Zero) Marshal.FreeHGlobal(shaderSourcePtr); } } /// /// Compiles the provided shader or effect source. /// /// A string containing the source of the shader or effect to compile. /// The name of the shader entry-point function, or null for an effect file. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// Name of the source file. /// The secondary data flags. /// The secondary data. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult Compile(string shaderSource, string entryPoint, string profile, ShaderFlags shaderFlags = ShaderFlags.None, EffectFlags effectFlags = EffectFlags.None, string sourceFileName = "unknown", SecondaryDataFlags secondaryDataFlags = SecondaryDataFlags.None, DataStream secondaryData = null) { if (string.IsNullOrEmpty(shaderSource)) { throw new ArgumentNullException("shaderSource"); } var shaderSourcePtr = Marshal.StringToHGlobalAnsi(shaderSource); try { return Compile(shaderSourcePtr, shaderSource.Length, entryPoint, profile, shaderFlags, effectFlags, null, null, sourceFileName, secondaryDataFlags, secondaryData); } finally { if (shaderSourcePtr != IntPtr.Zero) Marshal.FreeHGlobal(shaderSourcePtr); } } /// /// Compiles the provided shader or effect source. /// /// A string containing the source of the shader or effect to compile. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// A set of macros to define during compilation. /// An interface for handling include files. /// Name of the source file. /// The secondary data flags. /// The secondary data. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult Compile(string shaderSource, string profile, ShaderFlags shaderFlags, EffectFlags effectFlags, ShaderMacro[] defines, Include include, string sourceFileName = "unknown", SecondaryDataFlags secondaryDataFlags = SecondaryDataFlags.None, DataStream secondaryData = null) { if (string.IsNullOrEmpty(shaderSource)) { throw new ArgumentNullException("shaderSource"); } var shaderSourcePtr = Marshal.StringToHGlobalAnsi(shaderSource); try { return Compile(shaderSourcePtr, shaderSource.Length, null, profile, shaderFlags, effectFlags, defines, include, sourceFileName, secondaryDataFlags, secondaryData); } finally { if (shaderSourcePtr != IntPtr.Zero) Marshal.FreeHGlobal(shaderSourcePtr); } } /// /// Compiles the provided shader or effect source. /// /// An array of bytes containing the raw source of the shader or effect to compile. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// A set of macros to define during compilation. /// An interface for handling include files. /// Name of the source file. /// The secondary data flags. /// The secondary data. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult Compile(byte[] shaderSource, string profile, ShaderFlags shaderFlags, EffectFlags effectFlags, ShaderMacro[] defines, Include include, string sourceFileName = "unknown", SecondaryDataFlags secondaryDataFlags = SecondaryDataFlags.None, DataStream secondaryData = null) { return Compile(shaderSource, null, profile, shaderFlags, effectFlags, defines, include, sourceFileName, secondaryDataFlags, secondaryData); } /// /// Compiles the provided shader or effect source. /// /// A string containing the source of the shader or effect to compile. /// The name of the shader entry-point function, or null for an effect file. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// A set of macros to define during compilation. /// An interface for handling include files. /// Name of the source file. /// The secondary data flags. /// The secondary data. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult Compile(string shaderSource, string entryPoint, string profile, ShaderFlags shaderFlags, EffectFlags effectFlags, ShaderMacro[] defines, Include include, string sourceFileName = "unknown", SecondaryDataFlags secondaryDataFlags = SecondaryDataFlags.None, DataStream secondaryData = null) { if (string.IsNullOrEmpty(shaderSource)) { throw new ArgumentNullException("shaderSource"); } var shaderSourcePtr = Marshal.StringToHGlobalAnsi(shaderSource); try { return Compile(shaderSourcePtr, shaderSource.Length, entryPoint, profile, shaderFlags, effectFlags, defines, include, sourceFileName, secondaryDataFlags, secondaryData); } finally { if (shaderSourcePtr != IntPtr.Zero) Marshal.FreeHGlobal(shaderSourcePtr); } } /// /// Compiles the provided shader or effect source. /// /// An array of bytes containing the raw source of the shader or effect to compile. /// The name of the shader entry-point function, or null for an effect file. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// A set of macros to define during compilation. /// An interface for handling include files. /// Name of the source file used for reporting errors. Default is "unknown" /// The secondary data flags. /// The secondary data. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult Compile(byte[] shaderSource, string entryPoint, string profile, ShaderFlags shaderFlags, EffectFlags effectFlags, ShaderMacro[] defines, Include include, string sourceFileName = "unknown", SecondaryDataFlags secondaryDataFlags = SecondaryDataFlags.None, DataStream secondaryData = null) { unsafe { fixed (void* pData = &shaderSource[0]) return Compile( (IntPtr)pData, shaderSource.Length, entryPoint, profile, shaderFlags, effectFlags, defines, include, sourceFileName, secondaryDataFlags, secondaryData); } } /// /// Compiles the provided shader or effect source. /// /// The shader data. /// Size of the shader. /// The name of the shader entry-point function, or null for an effect file. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// A set of macros to define during compilation. /// An interface for handling include files. /// Name of the source file used for reporting errors. Default is "unknown" /// The secondary data flags. /// The secondary data. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult Compile(IntPtr textSource, int textSize, string entryPoint, string profile, ShaderFlags shaderFlags, EffectFlags effectFlags, ShaderMacro[] defines, Include include, string sourceFileName = "unknown", SecondaryDataFlags secondaryDataFlags = SecondaryDataFlags.None, DataStream secondaryData = null) { unsafe { if (string.IsNullOrWhiteSpace(profile)) throw new ArgumentNullException("profile"); if (!(profile.ToUpperInvariant().StartsWith("FX_") || profile.ToUpperInvariant().StartsWith("LIB_")) && string.IsNullOrWhiteSpace(entryPoint)) throw new ArgumentNullException("entryPoint"); Blob blobForCode = null; Blob blobForErrors = null; var resultCode = D3D.Compile2( (IntPtr)textSource, textSize, sourceFileName, PrepareMacros(defines), include, entryPoint, profile, shaderFlags, effectFlags, secondaryDataFlags, secondaryData != null ? secondaryData.DataPointer : IntPtr.Zero, secondaryData != null ? (int)secondaryData.Length : 0, out blobForCode, out blobForErrors); if (resultCode.Failure) { if (blobForErrors != null) { if (Configuration.ThrowOnShaderCompileError) throw new CompilationException(resultCode, Utilities.BlobToString(blobForErrors)); } else { throw new SharpDXException(resultCode); } } return new CompilationResult(blobForCode != null ? new ShaderBytecode(blobForCode) : null, resultCode, Utilities.BlobToString(blobForErrors)); } } /// /// Compiles the provided shader or effect source. /// /// An array of bytes containing the raw source of the shader or effect to compile. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// Name of the source file. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult Compile(byte[] shaderSource, string profile, ShaderFlags shaderFlags = ShaderFlags.None, EffectFlags effectFlags = EffectFlags.None, string sourceFileName = "unknown") { return Compile(shaderSource, null, profile, shaderFlags, effectFlags, null, null, sourceFileName); } /// /// Compiles the provided shader or effect source. /// /// An array of bytes containing the raw source of the shader or effect to compile. /// The name of the shader entry-point function, or null for an effect file. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// Name of the source file. /// The compiled shader bytecode, or null if the method fails. public static CompilationResult Compile(byte[] shaderSource, string entryPoint, string profile, ShaderFlags shaderFlags = ShaderFlags.None, EffectFlags effectFlags = EffectFlags.None, string sourceFileName = "unknown") { return Compile(shaderSource, entryPoint, profile, shaderFlags, effectFlags, null, null, sourceFileName); } /// /// Compiles a shader or effect from a file on disk. /// /// The name of the source file to compile. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// A set of macros to define during compilation. /// An interface for handling include files. /// The compiled shader bytecode, or null if the method fails. public static CompilationResult CompileFromFile(string fileName, string profile, ShaderFlags shaderFlags = ShaderFlags.None, EffectFlags effectFlags = EffectFlags.None, ShaderMacro[] defines = null, Include include = null) { return CompileFromFile(fileName, null, profile, shaderFlags, effectFlags, defines, include); } /// /// Compiles a shader or effect from a file on disk. /// /// The name of the source file to compile. /// The name of the shader entry-point function, or null for an effect file. /// The shader target or set of shader features to compile against. /// Shader compilation options. /// Effect compilation options. /// A set of macros to define during compilation. /// An interface for handling include files. /// /// The compiled shader bytecode, or null if the method fails. /// public static CompilationResult CompileFromFile(string fileName, string entryPoint, string profile, ShaderFlags shaderFlags = ShaderFlags.None, EffectFlags effectFlags = EffectFlags.None, ShaderMacro[] defines = null, Include include = null) { return Compile(NativeFile.ReadAllText(fileName), entryPoint, profile, shaderFlags, effectFlags, defines, include, fileName); } // Win 8.1 SDK removed the corresponding functions from the WinRT platform #if DESKTOP_APP /// /// Compresses a set of shaders into a more compact form. /// /// An array of structures that describe the set of shaders to compress. /// A compressed . /// HRESULT D3DCompressShaders([In] int uNumShaders,[In, Buffer] D3D_SHADER_DATA* pShaderData,[In] int uFlags,[Out] ID3DBlob** ppCompressedData) public static ShaderBytecode Compress(params ShaderBytecode[] shaderBytecodes) { Blob blob; // D3D.CompressShaders() var temp = new ShaderData[shaderBytecodes.Length]; var handles = new GCHandle[shaderBytecodes.Length]; try { for (int i = 0; i < temp.Length; i++) { handles[i] = GCHandle.Alloc(shaderBytecodes[i].Data, GCHandleType.Pinned); temp[i] = new ShaderData { BytecodePtr = handles[i].AddrOfPinnedObject(), BytecodeLength = shaderBytecodes[i].Data.Length }; } D3D.CompressShaders(shaderBytecodes.Length, temp, 1, out blob); } finally { foreach (var gcHandle in handles) gcHandle.Free(); } return new ShaderBytecode(blob) { IsCompressed = true }; } /// /// Decompresses all shaders from a compressed set. /// /// Returns an array of decompress shader bytecode. /// HRESULT D3DDecompressShaders([In, Buffer] const void* pSrcData,[In] SIZE_T SrcDataSize,[In] unsigned int uNumShaders,[In] unsigned int uStartIndex,[In, Buffer, Optional] unsigned int* pIndices,[In] unsigned int uFlags,[Out, Buffer] ID3D10Blob** ppShaders,[Out, Optional] unsigned int* pTotalShaders) public unsafe ShaderBytecode[] Decompress() { //First we call D3D.DecompressShaders with empty parameters to get the number of shaders in the compressed byte code (totalShadersRef) //I set shadersBlobs to an array of one null blob just to make shadersOut_ parameter in DecompressShaders function valid //which doesn't seem to accept zero pointer, I didn't find any other work around. var shadersBlobs = new Blob[1]; int totalShadersRef; fixed (void* bufferPtr = Data) D3D.DecompressShaders((IntPtr)bufferPtr, Data.Length, 0, 0, null, 0, shadersBlobs, out totalShadersRef); //Then we call D3D.DecompressShaders again and we know how much shaders we will get return Decompress(0, totalShadersRef); } /// /// Decompresses one or more shaders from a compressed set. /// /// The number of shaders to decompress. /// The index of the first shader to decompress. /// Returns an array of decompress shader bytecode. /// HRESULT D3DDecompressShaders([In, Buffer] const void* pSrcData,[In] SIZE_T SrcDataSize,[In] unsigned int uNumShaders,[In] unsigned int uStartIndex,[In, Buffer, Optional] unsigned int* pIndices,[In] unsigned int uFlags,[Out, Buffer] ID3D10Blob** ppShaders,[Out, Optional] unsigned int* pTotalShaders) public unsafe ShaderBytecode[] Decompress(int startIndex, int numShaders) { if (numShaders == 0) return null; var shadersBlobs = new Blob[numShaders]; int totalShadersRef; fixed (void* bufferPtr = Data) D3D.DecompressShaders((IntPtr)bufferPtr, Data.Length, numShaders, startIndex, null, 0, shadersBlobs, out totalShadersRef); //The size of shadersBlobs will not change //if the compressed shader contains less than requested in numShaders, null entries will appear in the result array var shadersByteArr = new ShaderBytecode[shadersBlobs.Length]; for (int i = 0; i < shadersBlobs.Length; i++) if (shadersBlobs[i] != null) shadersByteArr[i] = new ShaderBytecode(shadersBlobs[i]); return shadersByteArr; } /// /// Decompresses one or more shaders from a compressed set. /// /// An array of indexes that represent the shaders to decompress. /// Returns an array of decompress shader bytecode. /// HRESULT D3DDecompressShaders([In, Buffer] const void* pSrcData,[In] SIZE_T SrcDataSize,[In] unsigned int uNumShaders,[In] unsigned int uStartIndex,[In, Buffer, Optional] unsigned int* pIndices,[In] unsigned int uFlags,[Out, Buffer] ID3D10Blob** ppShaders,[Out, Optional] unsigned int* pTotalShaders) public unsafe ShaderBytecode[] Decompress(int[] indices) { if (indices.Length == 0) return null; var shadersBlobs = new Blob[indices.Length]; int totalShadersRef; fixed (void* bufferPtr = Data) D3D.DecompressShaders((IntPtr)bufferPtr, Data.Length, indices.Length, 0, indices, 0, shadersBlobs, out totalShadersRef); //The size of shadersBlobs will not change //if the compressed shader contains less than requested in numShaders, null entries will appear in the result array var shadersByteArr = new ShaderBytecode[shadersBlobs.Length]; for (int i = 0; i < shadersBlobs.Length; i++) if (shadersBlobs[i] != null) shadersByteArr[i] = new ShaderBytecode(shadersBlobs[i]); return shadersByteArr; } #endif /// /// Gets this instance is composed of compressed shaders. /// /// /// true if this instance is compressed; otherwise, false. /// public bool IsCompressed { get; private set; } /// /// Disassembles compiled HLSL code back into textual source. /// /// The textual source of the shader or effect. public string Disassemble() { return this.Disassemble(DisassemblyFlags.None, null); } /// /// Disassembles compiled HLSL code back into textual source. /// /// Flags affecting the output of the disassembly. /// The textual source of the shader or effect. public string Disassemble(DisassemblyFlags flags) { return this.Disassemble(flags, null); } /// /// Disassembles compiled HLSL code back into textual source. /// /// Flags affecting the output of the disassembly. /// Commenting information to embed in the disassembly. /// The textual source of the shader or effect. public unsafe string Disassemble(DisassemblyFlags flags, string comments) { Blob output; fixed (void* bufferPtr = Data) D3D.Disassemble((IntPtr)bufferPtr, Data.Length, flags, comments, out output); return Utilities.BlobToString(output); } /// /// Disassembles a region of a compiled HLSL code back into textual source. /// /// The flags. /// The comments. /// The start byte offset. /// The number of instructions. /// The finish byte offset ref. /// The textual source of the shader or effect. /// HRESULT D3DDisassembleRegion([In, Buffer] const void* pSrcData,[In] SIZE_T SrcDataSize,[In] unsigned int Flags,[In, Optional] const char* szComments,[In] SIZE_T StartByteOffset,[In] SIZE_T NumInsts,[Out, Optional] SIZE_T* pFinishByteOffset,[Out] ID3D10Blob** ppDisassembly) public unsafe string DisassembleRegion(DisassemblyFlags flags, string comments, PointerSize startByteOffset, PointerSize numberOfInstructions, out SharpDX.PointerSize finishByteOffsetRef) { Blob output; fixed (void* bufferPtr = Data) D3D.DisassembleRegion((IntPtr)bufferPtr, Data.Length, (int)flags, comments, startByteOffset, numberOfInstructions, out finishByteOffsetRef, out output); return Utilities.BlobToString(output); } /// /// Gets the trace instruction offsets. /// /// if set to true [is including non executable code]. /// Start index of the instructions. /// The number of instructions. /// The total instructions ref. /// An offset /// HRESULT D3DGetTraceInstructionOffsets([In, Buffer] const void* pSrcData,[In] SIZE_T SrcDataSize,[In] unsigned int Flags,[In] SIZE_T StartInstIndex,[In] SIZE_T NumInsts,[Out, Buffer, Optional] SIZE_T* pOffsets,[Out, Optional] SIZE_T* pTotalInsts) public unsafe PointerSize GetTraceInstructionOffsets(bool isIncludingNonExecutableCode, PointerSize startInstIndex, PointerSize numInsts, out SharpDX.PointerSize totalInstsRef) { fixed (void* bufferPtr = Data) return D3D.GetTraceInstructionOffsets((IntPtr)bufferPtr, Data.Length, isIncludingNonExecutableCode ? 1 : 0, startInstIndex, numInsts, out totalInstsRef); } /// /// Retrieves a specific part from a compilation result. /// /// /// D3DGetBlobPart retrieves the part of a blob (arbitrary length data buffer) that contains the type of data that the Part parameter specifies. /// /// A -typed value that specifies the part of the buffer to retrieve. /// Returns the extracted part. /// HRESULT D3DGetBlobPart([In, Buffer] const void* pSrcData,[In] SIZE_T SrcDataSize,[In] D3D_BLOB_PART Part,[In] int Flags,[Out] ID3DBlob** ppPart) public unsafe ShaderBytecode GetPart(ShaderBytecodePart part) { Blob blob; fixed (void* bufferPtr = Data) D3D.GetBlobPart((IntPtr)bufferPtr, Data.Length, part, 0, out blob); return new ShaderBytecode(blob); } /// /// Sets information in a compilation result. /// /// The part. /// The part data. /// The new shader in which the new part data is set. /// HRESULT D3DSetBlobPart([In, Buffer] const void* pSrcData,[In] SIZE_T SrcDataSize,[In] D3D_BLOB_PART Part,[In] unsigned int Flags,[In, Buffer] const void* pPart,[In] SIZE_T PartSize,[Out] ID3D10Blob** ppNewShader) public unsafe ShaderBytecode SetPart(ShaderBytecodePart part, DataStream partData) { Blob blob; fixed (void* bufferPtr = Data) D3D.SetBlobPart((IntPtr)bufferPtr, Data.Length, part, 0, partData.DataPointer, (int)partData.Length, out blob); return new ShaderBytecode(blob); } /// /// Loads from the specified stream. /// /// The stream. /// A shader bytecode public static ShaderBytecode Load(Stream stream) { var buffer = Utilities.ReadStream(stream); return new ShaderBytecode(buffer); } /// /// Saves this bytecode to the specified stream. /// /// The stream. public void Save(Stream stream) { if (Data.Length == 0) return; stream.Write(Data, 0, Data.Length); } /// /// Preprocesses the provided shader or effect source. /// /// A string containing the source of the shader or effect to preprocess. /// A set of macros to define during preprocessing. /// An interface for handling include files. /// Name of the source file. /// The preprocessed shader source. public static string Preprocess(string shaderSource, ShaderMacro[] defines = null, Include include = null, string sourceFileName = "") { string errors = null; if (string.IsNullOrEmpty(shaderSource)) { throw new ArgumentNullException("shaderSource"); } var shaderSourcePtr = Marshal.StringToHGlobalAnsi(shaderSource); try { return Preprocess(shaderSourcePtr, shaderSource.Length, defines, include, out errors, sourceFileName); } finally { if (shaderSourcePtr != IntPtr.Zero) Marshal.FreeHGlobal(shaderSourcePtr); } } /// /// Preprocesses the provided shader or effect source. /// /// An array of bytes containing the raw source of the shader or effect to preprocess. /// A set of macros to define during preprocessing. /// An interface for handling include files. /// Name of the source file. /// The preprocessed shader source. public static string Preprocess(byte[] shaderSource, ShaderMacro[] defines = null, Include include = null, string sourceFileName = "") { string errors = null; return Preprocess(shaderSource, defines, include, out errors, sourceFileName); } /// /// Preprocesses the provided shader or effect source. /// /// An array of bytes containing the raw source of the shader or effect to preprocess. /// A set of macros to define during preprocessing. /// An interface for handling include files. /// When the method completes, contains a string of compilation errors, or an empty string if preprocessing succeeded. /// Name of the source file. /// The preprocessed shader source. public static string Preprocess(byte[] shaderSource, ShaderMacro[] defines, Include include, out string compilationErrors, string sourceFileName = "") { unsafe { fixed (void* pData = &shaderSource[0]) return Preprocess((IntPtr)pData, shaderSource.Length, defines, include, out compilationErrors, sourceFileName); } } /// /// Preprocesses the provided shader or effect source. /// /// An array of bytes containing the raw source of the shader or effect to preprocess. /// /// A set of macros to define during preprocessing. /// An interface for handling include files. /// When the method completes, contains a string of compilation errors, or an empty string if preprocessing succeeded. /// Name of the source file. /// The preprocessed shader source. public static string Preprocess(IntPtr shaderSourcePtr, int shaderSourceLength, ShaderMacro[] defines, Include include, out string compilationErrors, string sourceFileName = "") { unsafe { Blob blobForText = null; Blob blobForErrors = null; compilationErrors = null; try { D3D.Preprocess(shaderSourcePtr, shaderSourceLength, sourceFileName, PrepareMacros(defines),include, out blobForText, out blobForErrors); } catch (SharpDXException ex) { if (blobForErrors != null) { compilationErrors = Utilities.BlobToString(blobForErrors); throw new CompilationException(ex.ResultCode, compilationErrors); } throw; } return Utilities.BlobToString(blobForText); } } /// /// Preprocesses the provided shader or effect source. /// /// A string containing the source of the shader or effect to preprocess. /// A set of macros to define during preprocessing. /// An interface for handling include files. /// When the method completes, contains a string of compilation errors, or an empty string if preprocessing succeeded. /// Name of the source file. /// The preprocessed shader source. public static string Preprocess(string shaderSource, ShaderMacro[] defines, Include include, out string compilationErrors, string sourceFileName = "") { if (string.IsNullOrEmpty(shaderSource)) { throw new ArgumentNullException("shaderSource"); } var shaderSourcePtr = Marshal.StringToHGlobalAnsi(shaderSource); try { return Preprocess(shaderSourcePtr, shaderSource.Length, defines, include, out compilationErrors, sourceFileName); } finally { if (shaderSourcePtr != IntPtr.Zero) Marshal.FreeHGlobal(shaderSourcePtr); } } /// /// Preprocesses a shader or effect from a file on disk. /// /// The name of the source file to compile. /// The preprocessed shader source. public static string PreprocessFromFile(string fileName) { string errors = null; if (fileName == null) { throw new ArgumentNullException("fileName"); } string str = NativeFile.ReadAllText(fileName); if (string.IsNullOrEmpty(str)) { throw new ArgumentNullException("fileName"); } return Preprocess(Encoding.ASCII.GetBytes(str), null, null, out errors, fileName); } /// /// Preprocesses a shader or effect from a file on disk. /// /// The name of the source file to compile. /// A set of macros to define during preprocessing. /// An interface for handling include files. /// The preprocessed shader source. public static string PreprocessFromFile(string fileName, ShaderMacro[] defines, Include include) { string errors = null; return PreprocessFromFile(fileName, defines, include, out errors); } /// /// Preprocesses a shader or effect from a file on disk. /// /// The name of the source file to compile. /// A set of macros to define during preprocessing. /// An interface for handling include files. /// When the method completes, contains a string of compilation errors, or an empty string if preprocessing succeeded. /// The preprocessed shader source. public static string PreprocessFromFile(string fileName, ShaderMacro[] defines, Include include, out string compilationErrors) { if (fileName == null) { throw new ArgumentNullException("fileName"); } return Preprocess(NativeFile.ReadAllText(fileName), defines, include, out compilationErrors); } /// /// Strips extraneous information from a compiled shader or effect. /// /// Options specifying what to remove from the shader. /// A string containing any errors that may have occurred. /// HRESULT D3DStripShader([In, Buffer] const void* pShaderBytecode,[In] SIZE_T BytecodeLength,[In] D3DCOMPILER_STRIP_FLAGS uStripFlags,[Out] ID3D10Blob** ppStrippedBlob) public unsafe ShaderBytecode Strip(StripFlags flags) { Blob blob; fixed (void* bufferPtr = Data) if (D3D.StripShader((IntPtr)bufferPtr, Data.Length, flags, out blob).Failure) return null; return new ShaderBytecode(blob); } /// /// Cast this to the underlying byte buffer. /// /// /// A byte buffer public static implicit operator byte[](ShaderBytecode shaderBytecode) { return shaderBytecode.Data; } /// /// Read a compiled shader bytecode from a Stream and return a ShaderBytecode /// /// /// public static ShaderBytecode FromStream(Stream stream) { return new ShaderBytecode(stream); } /// /// Read a compiled shader bytecode from a Stream and return a ShaderBytecode /// /// /// public static ShaderBytecode FromFile(string fileName) { return new ShaderBytecode(NativeFile.ReadAllBytes(fileName)); } internal static ShaderMacro[] PrepareMacros(ShaderMacro[] macros) { if (macros == null) return null; if (macros.Length == 0) return null; if (macros[macros.Length - 1].Name == null && macros[macros.Length - 1].Definition == null) return macros; var macroArray = new ShaderMacro[macros.Length + 1]; Array.Copy(macros, macroArray, macros.Length); macroArray[macros.Length] = new ShaderMacro(null, null); return macroArray; } /// /// Disposes all resource for the allocated object /// /// This is kept for backwards compatibility only, this actually does nothing in that case public void Dispose() { // Just to keep backward compatibility } /// /// Gets the shader type and version string from the provided bytecode. /// /// The type and version string of the provided shader bytecode. /// Is thrown when bytecode contains invalid data or the version could not be read. /// Is thrown when bytecode contains invalid data. public ShaderProfile GetVersion() { // the offset where chunks count is stored const int chunksCountOffset = 4 * 7; // the offset of the version bytes from the start of the chunk const int shaderCodeVersionOffset = 4 * 2; // invalid offset marker - used to find out if the shader version cannot be read const int invalidOffset = -1; var data = Data; // read the number of data chunks in the bytecode var chunksCount = BitConverter.ToUInt32(data, chunksCountOffset); // find the offset of the chunk where we can read the data: var chunkOffset = invalidOffset; var dx9ChunkOffset = invalidOffset; for (var i = 0; i < chunksCount; i++) { var offset = BitConverter.ToInt32(data, chunksCountOffset + 4 + 4 * i); var code = (FourCC)BitConverter.ToUInt32(data, offset); // these chunks contain the shader version if (code == "SHEX" || code == "SHDR") chunkOffset = offset; // this chunk is present only for d3d9-level hardware if (code == "Aon9") dx9ChunkOffset = offset; } if (chunkOffset == invalidOffset) throw new ArgumentException("Cannot find the chunk with version in provided bytecode"); // read the shader version var versionValue = BitConverter.ToInt32(data, chunkOffset + shaderCodeVersionOffset); var minor = DecodeValue(versionValue, 0, 3); var major = DecodeValue(versionValue, 4, 7); var type = (ShaderVersion)DecodeValue(versionValue, 16, 31); var profileMinor = 0; var profileMajor = 0; // we have a DX9 chunk here - try to decode profile level version // this is possible only for SM 4.0 if (dx9ChunkOffset != invalidOffset && major == 4 && minor == 0) { // http://msdn.microsoft.com/en-us/library/ff570156%28v=vs.85%29.aspx // 0xFFFE - Vertex Shader // 0xFFFF - Pixel Shader // order is inversed due to endianess byte magicByte1 = (byte)(type == ShaderVersion.VertexShader ? 0xfe : 0xff); byte magicByte2 = 0xff; // skip 16 bytes (4 dwords): FourCC, ChunkSize, // other 2 are some magic numbers that may mess with the VersionToken detection var startOffset = dx9ChunkOffset + 16; // chunk size is the second dword at the chunk offset var endOffset = dx9ChunkOffset + 8 + BitConverter.ToUInt32(data, dx9ChunkOffset + 4); for (var j = startOffset; j < endOffset; j += 4) { if (data[j + 2] == magicByte1 && data[j + 3] == magicByte2) { // first byte is the minor version, 0 maps to 4_0_level_9_1 and 1 maps to 4_0_level_9_3 profileMinor = data[j] == 1 ? 3 : 1; System.Diagnostics.Debug.Assert(data[j] == 0 || data[j] == 1); System.Diagnostics.Debug.Assert(data[j + 1] == 2); break; } } profileMajor = 9; } // just make sure we computed everything correctly System.Diagnostics.Debug.Assert((profileMajor == 9 && (profileMinor == 1 || profileMinor == 3)) || (profileMajor == 0 && profileMinor == 0)); return new ShaderProfile(type, major, minor, profileMajor, profileMinor); } /// /// Reads the value between start and end bits from the provided token. /// /// The source of the data to read. /// The start bit. /// The end bit. /// private static int DecodeValue(int token, int start, int end) { // create the mask to read the data var mask = 0; for (var i = start; i <= end; i++) mask |= 1 << i; // read the needed bits and shift them accordingly return (token & mask) >> start; } } }