Ejemplo n.º 1
0
        /// <summary>
        /// Converts the hlsl code into glsl and stores the result as plain text
        /// </summary>
        /// <param name="shaderSource">the hlsl shader</param>
        /// <param name="entryPoint">the entrypoint function name</param>
        /// <param name="stage">the shader pipeline stage</param>
        /// <param name="compilerParameters"></param>
        /// <param name="reflection">the reflection gathered from the hlsl analysis</param>
        /// <param name="sourceFilename">the name of the source file</param>
        /// <returns></returns>
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var isOpenGLES = compilerParameters.Get(CompilerParameters.GraphicsPlatformKey) == GraphicsPlatform.OpenGLES;
            var isOpenGLES3 = compilerParameters.Get(CompilerParameters.GraphicsProfileKey) >= GraphicsProfile.Level_10_0;
            var shaderBytecodeResult = new ShaderBytecodeResult();
            byte[] rawData;

            var shader = Compile(shaderSource, entryPoint, stage, isOpenGLES, isOpenGLES3, shaderBytecodeResult, sourceFilename);

            if (shader == null)
                return shaderBytecodeResult;

            if (isOpenGLES)
            {
                // store both ES 2 and ES 3 on OpenGL ES platforms
                var shaderBytecodes = new ShaderLevelBytecode();
                if (isOpenGLES3)
                {
                    shaderBytecodes.DataES3 = shader;
                    shaderBytecodes.DataES2 = null;
                }
                else
                {
                    shaderBytecodes.DataES2 = shader;
                    shaderBytecodes.DataES3 = Compile(shaderSource, entryPoint, stage, true, true, shaderBytecodeResult, sourceFilename);
                }
                using (var stream = new MemoryStream())
                {
                    BinarySerialization.Write(stream, shaderBytecodes);
#if !SILICONSTUDIO_RUNTIME_CORECLR
                    rawData = stream.GetBuffer();
#else
// FIXME: Manu: The call to "ToArray()" might be slower than "GetBuffer()"
                    rawData = stream.ToArray();
#endif
                }
            }
            else
            {
                // store string on OpenGL platforms
                rawData = Encoding.ASCII.GetBytes(shader);
            }
            
            var bytecodeId = ObjectId.FromBytes(rawData);
            var bytecode = new ShaderBytecode(bytecodeId, rawData);
            bytecode.Stage = stage;

            shaderBytecodeResult.Bytecode = bytecode;
            
            return shaderBytecodeResult;
        }
Ejemplo n.º 2
0
        private void UpdateReflection(ShaderBytecode shaderBytecode, EffectReflection effectReflection, LoggerResult log)
        {
            var shaderReflectionRaw = new SharpDX.D3DCompiler.ShaderReflection(shaderBytecode);
            var shaderReflectionRawDesc = shaderReflectionRaw.Description;

            // Constant Buffers
            for (int i = 0; i < shaderReflectionRawDesc.ConstantBuffers; ++i)
            {
                var constantBufferRaw = shaderReflectionRaw.GetConstantBuffer(i);
                var constantBufferRawDesc = constantBufferRaw.Description;
                var linkBuffer = effectReflection.ConstantBuffers.FirstOrDefault(buffer => buffer.Name == constantBufferRawDesc.Name && buffer.Stage == ShaderStage.None);

                var constantBuffer = GetConstantBufferReflection(constantBufferRaw, ref constantBufferRawDesc, linkBuffer, log);
                constantBuffer.Stage = shaderBytecode.Stage;
                effectReflection.ConstantBuffers.Add(constantBuffer);
            }

            // BoundResources
            for (int i = 0; i < shaderReflectionRawDesc.BoundResources; ++i)
            {
                var boundResourceDesc = shaderReflectionRaw.GetResourceBindingDescription(i);

                string linkKeyName = null;
                foreach (var linkResource in effectReflection.ResourceBindings)
                {
                    if (linkResource.Param.RawName == boundResourceDesc.Name && linkResource.Stage == ShaderStage.None)
                    {
                        linkKeyName = linkResource.Param.KeyName;
                        break;
                    }

                }

                if (linkKeyName == null)
                {
                    log.Error("Resource [{0}] has no link", boundResourceDesc.Name);
                }
                else
                {

                    var binding = GetResourceBinding(boundResourceDesc, linkKeyName, log);
                    binding.Stage = shaderBytecode.Stage;

                    effectReflection.ResourceBindings.Add(binding);
                }
            }
        }
Ejemplo n.º 3
0
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var isDebug = compilerParameters.Get(CompilerParameters.DebugKey);
            var profile = compilerParameters.Get(CompilerParameters.GraphicsProfileKey);
            
            var shaderModel = ShaderStageToString(stage) + "_" + ShaderProfileFromGraphicsProfile(profile);

            var shaderFlags = ShaderFlags.None;
            if (isDebug)
            {
                shaderFlags = ShaderFlags.OptimizationLevel0 | ShaderFlags.Debug;
            }

            SharpDX.Configuration.ThrowOnShaderCompileError = false;

            // Compile using D3DCompiler
            var compilationResult = SharpDX.D3DCompiler.ShaderBytecode.Compile(shaderSource, entryPoint, shaderModel, shaderFlags, EffectFlags.None, null, null, sourceFilename);

            var byteCodeResult = new ShaderBytecodeResult();

            if (compilationResult.HasErrors)
            {
                // Log compilation errors
                byteCodeResult.Error(compilationResult.Message);
            }
            else
            {
                // As effect bytecode binary can changed when having debug infos (with d3dcompiler_47), we are calculating a bytecodeId on the stripped version
                var rawData = compilationResult.Bytecode.Strip(StripFlags.CompilerStripDebugInformation | StripFlags.CompilerStripReflectionData);
                var bytecodeId = ObjectId.FromBytes(rawData);
                byteCodeResult.Bytecode = new ShaderBytecode(bytecodeId, compilationResult.Bytecode.Data) { Stage = stage };

                // If compilation succeed, then we can update reflection.
                UpdateReflection(byteCodeResult.Bytecode, reflection, byteCodeResult);

                if (!string.IsNullOrEmpty(compilationResult.Message))
                {
                    byteCodeResult.Warning(compilationResult.Message);
                }
            }

            return byteCodeResult;
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Converts the hlsl code into glsl and stores the result as plain text
        /// </summary>
        /// <param name="shaderSource">the hlsl shader</param>
        /// <param name="entryPoint">the entrypoint function name</param>
        /// <param name="stage">the shader pipeline stage</param>
        /// <param name="compilerParameters"></param>
        /// <param name="reflection">the reflection gathered from the hlsl analysis</param>
        /// <param name="sourceFilename">the name of the source file</param>
        /// <returns></returns>
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var isOpenGLES           = compilerParameters.Get(CompilerParameters.GraphicsPlatformKey) == GraphicsPlatform.OpenGLES;
            var shaderBytecodeResult = new ShaderBytecodeResult();


            PipelineStage pipelineStage = PipelineStage.None;

            switch (stage)
            {
            case ShaderStage.Vertex:
                pipelineStage = PipelineStage.Vertex;
                break;

            case ShaderStage.Pixel:
                pipelineStage = PipelineStage.Pixel;
                break;

            case ShaderStage.Geometry:
                shaderBytecodeResult.Error("Geometry stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                break;

            case ShaderStage.Hull:
                shaderBytecodeResult.Error("Hull stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                break;

            case ShaderStage.Domain:
                shaderBytecodeResult.Error("Domain stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                break;

            case ShaderStage.Compute:
                shaderBytecodeResult.Error("Compute stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                break;

            default:
                shaderBytecodeResult.Error("Unknown shader profile.");
                break;
            }

            if (shaderBytecodeResult.HasErrors)
            {
                return(shaderBytecodeResult);
            }

            // Convert from HLSL to GLSL
            // Note that for now we parse from shader as a string, but we could simply clone effectPass.Shader to avoid multiple parsing.
            var glslConvertor = new ShaderConverter(isOpenGLES);
            var glslShader    = glslConvertor.Convert(shaderSource, entryPoint, pipelineStage, sourceFilename, shaderBytecodeResult);

            // Add std140 layout
            foreach (var constantBuffer in glslShader.Declarations.OfType <ConstantBuffer>())
            {
                constantBuffer.Qualifiers |= new LayoutQualifier(new LayoutKeyValue("std140"));
            }

            // Output the result
            var glslShaderWriter = new HlslToGlslWriter();

            if (isOpenGLES)
            {
                glslShaderWriter.TrimFloatSuffix       = true;
                glslShaderWriter.GenerateUniformBlocks = false;
                foreach (var variable in glslShader.Declarations.OfType <Variable>())
                {
                    if (variable.Qualifiers.Contains(ParameterQualifier.In))
                    {
                        variable.Qualifiers.Values.Remove(ParameterQualifier.In);
                        // "in" becomes "attribute" in VS, "varying" in other stages
                        variable.Qualifiers.Values.Add(
                            pipelineStage == PipelineStage.Vertex
                                ? global::SiliconStudio.Shaders.Ast.Glsl.ParameterQualifier.Attribute
                                : global::SiliconStudio.Shaders.Ast.Glsl.ParameterQualifier.Varying);
                    }
                    if (variable.Qualifiers.Contains(ParameterQualifier.Out))
                    {
                        variable.Qualifiers.Values.Remove(ParameterQualifier.Out);
                        variable.Qualifiers.Values.Add(global::SiliconStudio.Shaders.Ast.Glsl.ParameterQualifier.Varying);
                    }
                }
            }

            // Write shader
            glslShaderWriter.Visit(glslShader);

            // Build shader source
            var glslShaderCode = new StringBuilder();

            // Append some header depending on target
            if (!isOpenGLES)
            {
                glslShaderCode
                .AppendLine("#version 420")
                .AppendLine();

                if (pipelineStage == PipelineStage.Pixel)
                {
                    glslShaderCode
                    .AppendLine("out vec4 gl_FragData[1];")
                    .AppendLine();
                }
            }

            if (isOpenGLES)
            {
                if (pipelineStage == PipelineStage.Pixel)
                {
                    glslShaderCode
                    .AppendLine("precision highp float;")
                    .AppendLine();
                }
            }

            glslShaderCode.Append(glslShaderWriter.Text);

            var realShaderSource = glslShaderCode.ToString();

            // optimize shader
            var optShaderSource = RunOptimizer(realShaderSource, isOpenGLES, false, pipelineStage == PipelineStage.Vertex);

            if (!String.IsNullOrEmpty(optShaderSource))
            {
                realShaderSource = optShaderSource;
            }

            var rawData    = Encoding.ASCII.GetBytes(realShaderSource);
            var bytecodeId = ObjectId.FromBytes(rawData);
            var bytecode   = new ShaderBytecode(bytecodeId, rawData);

            bytecode.Stage = stage;

            shaderBytecodeResult.Bytecode = bytecode;
            return(shaderBytecodeResult);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Create or updates the reflection for this shader
        /// </summary>
        /// <param name="effectReflection">the reflection from the hlsl</param>
        /// <param name="stage">the shader pipeline stage</param>
        private void CreateReflection(EffectReflection effectReflection, ShaderStage stage)
        {
            int currentProgram;

            GL.GetInteger(GetPName.CurrentProgram, out currentProgram);
            GL.UseProgram(resourceId);

            int uniformBlockCount;

            GL.GetProgram(resourceId, PdxActiveUniformBlocks, out uniformBlockCount);

            for (int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; ++uniformBlockIndex)
            {
                // TODO: get previous name to find te actual constant buffer in the reflexion
#if SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES
                const int sbCapacity = 128;
                int       length;
                var       sb = new StringBuilder(sbCapacity);
                GL.GetActiveUniformBlockName(resourceId, uniformBlockIndex, sbCapacity, out length, sb);
                var constantBufferName = sb.ToString();
#else
                var constantBufferName = GL.GetActiveUniformBlockName(resourceId, uniformBlockIndex);
#endif

                var constantBufferDescriptionIndex = effectReflection.ConstantBuffers.FindIndex(x => x.Name == constantBufferName);
                if (constantBufferDescriptionIndex == -1)
                {
                    reflectionResult.Error("Unable to find the constant buffer description [{0}]", constantBufferName);
                    return;
                }
                var constantBufferIndex = effectReflection.ResourceBindings.FindIndex(x => x.Param.RawName == constantBufferName);
                if (constantBufferIndex == -1)
                {
                    reflectionResult.Error("Unable to find the constant buffer [{0}]", constantBufferName);
                    return;
                }

                var constantBufferDescription = effectReflection.ConstantBuffers[constantBufferDescriptionIndex];
                var constantBuffer            = effectReflection.ResourceBindings[constantBufferIndex];

                GL.GetActiveUniformBlock(resourceId, uniformBlockIndex, ActiveUniformBlockParameter.UniformBlockDataSize, out constantBufferDescription.Size);

                int uniformCount;
                GL.GetActiveUniformBlock(resourceId, uniformBlockIndex, ActiveUniformBlockParameter.UniformBlockActiveUniforms, out uniformCount);

                // set the binding
                GL.UniformBlockBinding(resourceId, uniformBlockIndex, uniformBlockIndex);

                // Read uniforms desc
                var uniformIndices = new int[uniformCount];
                var uniformOffsets = new int[uniformCount];
                var uniformTypes   = new int[uniformCount];
                var uniformNames   = new string[uniformCount];
                GL.GetActiveUniformBlock(resourceId, uniformBlockIndex, ActiveUniformBlockParameter.UniformBlockActiveUniformIndices, uniformIndices);
                GL.GetActiveUniforms(resourceId, uniformIndices.Length, uniformIndices, ActiveUniformParameter.UniformOffset, uniformOffsets);
                GL.GetActiveUniforms(resourceId, uniformIndices.Length, uniformIndices, ActiveUniformParameter.UniformType, uniformTypes);

                for (int uniformIndex = 0; uniformIndex < uniformIndices.Length; ++uniformIndex)
                {
#if SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES
                    int size;
                    ActiveUniformType aut;
                    GL.GetActiveUniform(resourceId, uniformIndices[uniformIndex], sbCapacity, out length, out size, out aut, sb);
                    uniformNames[uniformIndex] = sb.ToString();
#else
                    uniformNames[uniformIndex] = GL.GetActiveUniformName(resourceId, uniformIndices[uniformIndex]);
#endif
                }

                // Reoder by offset
                var indexMapping = uniformIndices.Select((x, i) => new UniformMergeInfo {
                    Offset = uniformOffsets[i], Type = (ActiveUniformType)uniformTypes[i], Name = uniformNames[i], NextOffset = 0
                }).OrderBy(x => x.Offset).ToArray();
                indexMapping.Last().NextOffset = constantBufferDescription.Size;

                // Fill next offsets
                for (int i = 1; i < indexMapping.Length; ++i)
                {
                    indexMapping[i - 1].NextOffset = indexMapping[i].Offset;
                }

                // Group arrays/structures into one variable (std140 layout is enough for offset determinism inside arrays/structures)
                indexMapping = indexMapping.GroupBy(x =>
                {
                    // Use only first part of name (ignore structure/array part)
                    var name = x.Name;
                    if (name.Contains("."))
                    {
                        name = name.Substring(0, name.IndexOf('.'));
                    }
                    if (name.Contains("["))
                    {
                        name = name.Substring(0, name.IndexOf('['));
                    }
                    return(name);
                })
                               .Select(x =>
                {
                    var result        = x.First();
                    result.NextOffset = x.Last().NextOffset;

                    // Check weither it's an array or a struct
                    int dotIndex   = result.Name.IndexOf('.');
                    int arrayIndex = result.Name.IndexOf('[');

                    if (x.Count() > 1 && arrayIndex == -1 && dotIndex == -1)
                    {
                        throw new InvalidOperationException();
                    }

                    // TODO: Type processing

                    result.Name = x.Key;
                    return(result);
                }).ToArray();

                foreach (var variableIndexGroup in indexMapping)
                {
                    var variableIndex = -1;
                    for (var tentativeIndex = 0; tentativeIndex < constantBufferDescription.Members.Length; ++tentativeIndex)
                    {
                        if (constantBufferDescription.Members[tentativeIndex].Param.RawName == variableIndexGroup.Name)
                        {
                            variableIndex = tentativeIndex;
                            break;
                        }
                    }

                    if (variableIndex == -1)
                    {
                        reflectionResult.Error("Unable to find uniform [{0}] in constant buffer [{1}]", variableIndexGroup.Name, constantBufferName);
                        continue;
                    }
                    var variable = constantBufferDescription.Members[variableIndex];
                    variable.Param.Type = GetTypeFromActiveUniformType(variableIndexGroup.Type);
                    variable.Offset     = variableIndexGroup.Offset;
                    variable.Size       = variableIndexGroup.NextOffset - variableIndexGroup.Offset;

                    constantBufferDescription.Members[variableIndex] = variable;
                }

                constantBufferDescription.Stage = stage;
                constantBufferDescription.Type  = ConstantBufferType.ConstantBuffer;

                constantBuffer.SlotCount = 1; // constant buffers are not arrays
                constantBuffer.SlotStart = uniformBlockIndex;
                constantBuffer.Stage     = stage;

                // store the new values
                effectReflection.ConstantBuffers[constantBufferDescriptionIndex] = constantBufferDescription;
                effectReflection.ResourceBindings[constantBufferIndex]           = constantBuffer;
            }
//#endif

            // Register textures, samplers, etc...
            //TODO: (?) non texture/buffer uniform outside of a block
            {
                // Register "NoSampler", required by HLSL=>GLSL translation to support HLSL such as texture.Load().
                var noSampler = new EffectParameterResourceData {
                    Param = { RawName = "NoSampler", KeyName = "NoSampler", Class = EffectParameterClass.Sampler }, SlotStart = -1
                };
                Reflection.ResourceBindings.Add(noSampler);
                bool usingSamplerNoSampler = false;

                int activeUniformCount;
                GL.GetProgram(resourceId, ProgramParameter.ActiveUniforms, out activeUniformCount);
#if !SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES
                var uniformTypes = new int[activeUniformCount];
                GL.GetActiveUniforms(resourceId, activeUniformCount, Enumerable.Range(0, activeUniformCount).ToArray(), ActiveUniformParameter.UniformType, uniformTypes);
#endif

#if SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES
                if (GraphicsDevice.IsOpenGLES2)
                {
                    // Register global "fake" cbuffer
                    //var constantBuffer = new ShaderReflectionConstantBuffer
                    //    {
                    //        Name = "$Globals",
                    //        Variables = new List<ShaderReflectionVariable>(),
                    //        Type = ConstantBufferType.ConstantBuffer
                    //    };
                    //shaderReflection.ConstantBuffers.Add(constantBuffer);
                    //shaderReflection.BoundResources.Add(new InputBindingDescription { BindPoint = 0, BindCount = 1, Name = constantBuffer.Name, Type = ShaderInputType.ConstantBuffer });

                    // reset the size of the constant buffers
                    foreach (var constantBuffer in effectReflection.ConstantBuffers)
                    {
                        constantBuffer.Size = 0;
                    }

                    // set the state of the constant buffers
                    foreach (var constantBuffer in effectReflection.ConstantBuffers)
                    {
                        constantBuffer.Stage = stage;
                    }
                    for (int i = 0; i < effectReflection.ResourceBindings.Count; i++)
                    {
                        if (effectReflection.ResourceBindings[i].Param.Class != EffectParameterClass.ConstantBuffer)
                        {
                            continue;
                        }

                        var globalConstantBufferCopy = effectReflection.ResourceBindings[i];
                        globalConstantBufferCopy.Stage       = stage;
                        effectReflection.ResourceBindings[i] = globalConstantBufferCopy;
                    }

                    //Create a Globals constant buffer if necessary
                    var globalConstantBufferDescriptionIndex = effectReflection.ConstantBuffers.FindIndex(x => x.Name == "Globals");
                    var globalConstantBufferIndex            = effectReflection.ResourceBindings.FindIndex(x => x.Param.RawName == "Globals");
                    if (globalConstantBufferDescriptionIndex == -1 && globalConstantBufferIndex == -1)
                    {
                        var newConstantBufferDescription = new ShaderConstantBufferDescription
                        {
                            Name    = "Globals",
                            Stage   = stage,
                            Type    = ConstantBufferType.ConstantBuffer,
                            Size    = 0,
                            Members = new EffectParameterValueData[0],
                        };
                        var newConstantBuffer = new EffectParameterResourceData
                        {
                            Stage     = stage,
                            SlotStart = 0,
                            SlotCount = 1,
                            Param     = { RawName = "Globals", KeyName = "Globals", Type = EffectParameterType.ConstantBuffer, Class = EffectParameterClass.ConstantBuffer }
                        };

                        effectReflection.ConstantBuffers.Add(newConstantBufferDescription);
                        effectReflection.ResourceBindings.Add(newConstantBuffer);

                        globalConstantBufferDescriptionIndex = effectReflection.ConstantBuffers.Count - 1;
                        globalConstantBufferIndex            = effectReflection.ResourceBindings.Count - 1;
                    }

                    // Merge all the variables in the Globals constant buffer
                    if (globalConstantBufferDescriptionIndex != -1 && globalConstantBufferIndex != -1)
                    {
                        var globalConstantBufferDescription = effectReflection.ConstantBuffers[globalConstantBufferDescriptionIndex];
                        for (int cstDescrIndex = 0; cstDescrIndex < effectReflection.ConstantBuffers.Count; ++cstDescrIndex)
                        {
                            if (cstDescrIndex == globalConstantBufferDescriptionIndex)
                            {
                                continue;
                            }

                            var currentConstantBufferDescription = effectReflection.ConstantBuffers[cstDescrIndex];

                            globalConstantBufferDescription.Members = ArrayExtensions.Concat(
                                globalConstantBufferDescription.Members, currentConstantBufferDescription.Members);

                            effectReflection.ResourceBindings.RemoveAll(x => x.Param.RawName == currentConstantBufferDescription.Name);
                        }

                        // only keep the active uniforms
                        globalConstantBufferDescription.Members =
                            globalConstantBufferDescription.Members.Where(x => GL.GetUniformLocation(resourceId,
#if SILICONSTUDIO_PLATFORM_ANDROID
                                                                                                     new StringBuilder(x.Param.RawName)
#else
                                                                                                     x.Param.RawName
#endif
                                                                                                     ) >= 0).ToArray();

                        // remove all the constant buffers and their resource bindings except the Globals one
                        effectReflection.ConstantBuffers.Clear();
                        effectReflection.ConstantBuffers.Add(globalConstantBufferDescription);
                    }
                    else if (globalConstantBufferDescriptionIndex != -1 && globalConstantBufferIndex == -1)
                    {
                        reflectionResult.Error("Globals constant buffer has a description and no resource binding");
                    }
                    else if (globalConstantBufferDescriptionIndex == -1 && globalConstantBufferIndex != -1)
                    {
                        reflectionResult.Error("Globals constant buffer has a description and no resource binding");
                    }
                }
#endif

                int textureUnitCount = 0;

                const int sbCapacity = 128;
                int       length;
                var       sb = new StringBuilder(sbCapacity);

                for (int activeUniformIndex = 0; activeUniformIndex < activeUniformCount; ++activeUniformIndex)
                {
#if !SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES
                    var uniformType = (ActiveUniformType)uniformTypes[activeUniformIndex];
                    var uniformName = GL.GetActiveUniformName(resourceId, activeUniformIndex);
#else
                    ActiveUniformType uniformType;
                    int uniformCount;
                    GL.GetActiveUniform(resourceId, activeUniformIndex, sbCapacity, out length, out uniformCount, out uniformType, sb);
                    var uniformName = sb.ToString();
                    //int uniformSize;
                    //GL.GetActiveUniform(resourceId, activeUniformIndex, out uniformSize, ActiveUniformType.Float);
#endif

                    switch (uniformType)
                    {
#if SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES
                    case ActiveUniformType.Bool:
                    case ActiveUniformType.Int:
                        AddUniform(effectReflection, sizeof(int) * 1, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.BoolVec2:
                    case ActiveUniformType.IntVec2:
                        AddUniform(effectReflection, sizeof(int) * 2, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.BoolVec3:
                    case ActiveUniformType.IntVec3:
                        AddUniform(effectReflection, sizeof(int) * 3, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.BoolVec4:
                    case ActiveUniformType.IntVec4:
                        AddUniform(effectReflection, sizeof(int) * 4, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.Float:
                        AddUniform(effectReflection, sizeof(float) * 1, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.FloatVec2:
                        AddUniform(effectReflection, sizeof(float) * 2, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.FloatVec3:
                        AddUniform(effectReflection, sizeof(float) * 3, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.FloatVec4:
                        AddUniform(effectReflection, sizeof(float) * 4, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.FloatMat4:
                        AddUniform(effectReflection, sizeof(float) * 4 * 4, uniformCount, uniformName, uniformType);
                        break;

                    case ActiveUniformType.FloatMat2:
                    case ActiveUniformType.FloatMat3:
                        throw new NotImplementedException();
#endif
#if !SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES
                    case ActiveUniformType.Sampler1D:
                    case ActiveUniformType.Sampler1DShadow:
#endif
                    case ActiveUniformType.Sampler2D:
                    case ActiveUniformType.Sampler3D:     // TODO: remove Texture3D that is not available in OpenGL ES 2
                    case ActiveUniformType.SamplerCube:
                    case ActiveUniformType.Sampler2DShadow:
#if SILICONSTUDIO_PLATFORM_ANDROID
                        var uniformIndex = GL.GetUniformLocation(resourceId, new StringBuilder(uniformName));
#else
                        var uniformIndex = GL.GetUniformLocation(resourceId, uniformName);
#endif

                        // Temporary way to scan which texture and sampler created this texture_sampler variable (to fix with new HLSL2GLSL converter)

                        var startIndex             = -1;
                        var textureReflectionIndex = -1;
                        var samplerReflectionIndex = -1;
                        do
                        {
                            int middlePart  = uniformName.IndexOf('_', startIndex + 1);
                            var textureName = middlePart != -1 ? uniformName.Substring(0, middlePart) : uniformName;
                            var samplerName = middlePart != -1 ? uniformName.Substring(middlePart + 1) : null;

                            textureReflectionIndex =
                                effectReflection.ResourceBindings.FindIndex(x => x.Param.RawName == textureName);
                            samplerReflectionIndex =
                                effectReflection.ResourceBindings.FindIndex(x => x.Param.RawName == samplerName);

                            if (textureReflectionIndex != -1 && samplerReflectionIndex != -1)
                            {
                                break;
                            }

                            startIndex = middlePart;
                        } while (startIndex != -1);

                        if (startIndex == -1 || textureReflectionIndex == -1 || samplerReflectionIndex == -1)
                        {
                            reflectionResult.Error("Unable to find sampler and texture corresponding to [{0}]", uniformName);
                            continue;     // Error
                        }

                        var textureReflection = effectReflection.ResourceBindings[textureReflectionIndex];
                        var samplerReflection = effectReflection.ResourceBindings[samplerReflectionIndex];

                        // Contrary to Direct3D, samplers and textures are part of the same object in OpenGL
                        // Since we are exposing the Direct3D representation, a single sampler parameter key can be used for several textures, a single texture can be used with several samplers.
                        // When such a case is detected, we need to duplicate the resource binding.
                        textureReflectionIndex = GetReflexionIndex(textureReflection, textureReflectionIndex, effectReflection.ResourceBindings);
                        samplerReflectionIndex = GetReflexionIndex(samplerReflection, samplerReflectionIndex, effectReflection.ResourceBindings);

                        // Update texture uniform mapping
                        GL.Uniform1(uniformIndex, textureUnitCount);

                        textureReflection.Stage = stage;
                        //textureReflection.Param.RawName = uniformName;
                        textureReflection.Param.Type  = GetTypeFromActiveUniformType(uniformType);
                        textureReflection.SlotStart   = textureUnitCount;
                        textureReflection.SlotCount   = 1;   // TODO: texture arrays
                        textureReflection.Param.Class = EffectParameterClass.ShaderResourceView;

                        samplerReflection.Stage       = stage;
                        samplerReflection.SlotStart   = textureUnitCount;
                        samplerReflection.SlotCount   = 1;   // TODO: texture arrays
                        samplerReflection.Param.Class = EffectParameterClass.Sampler;

                        effectReflection.ResourceBindings[textureReflectionIndex] = textureReflection;
                        effectReflection.ResourceBindings[samplerReflectionIndex] = samplerReflection;

                        Textures.Add(new Texture(textureUnitCount));

                        textureUnitCount++;
                        break;
                    }
                }

                // Remove any optimized resource binding
                effectReflection.ResourceBindings.RemoveAll(x => x.SlotStart == -1);
            }

            GL.UseProgram(currentProgram);
        }
Ejemplo n.º 6
0
 private static void LinkVariable(EffectReflection reflection, string variableName, LocalParameterKey parameterKey)
 {
     var binding = new EffectParameterResourceData { Param = { KeyName = parameterKey.Name, Class = parameterKey.Class, Type = parameterKey.Type, RawName = variableName }, SlotStart = -1 };
     reflection.ResourceBindings.Add(binding);
 }
Ejemplo n.º 7
0
        private static void CleanupReflection(EffectReflection reflection)
        {
            // TODO GRAPHICS REFACTOR we hardcode several resource group we want to preserve or optimize completly
            // Somehow this should be handled some other place (or probably we shouldn't cleanup reflection at all?)
            bool hasMaterialGroup = false;
            bool hasLightingGroup = false;

            foreach (var resourceBinding in reflection.ResourceBindings)
            {
                if (resourceBinding.Stage != ShaderStage.None)
                {
                    if (!hasLightingGroup && resourceBinding.ResourceGroup == "PerLighting")
                        hasLightingGroup = true;
                    else if (!hasMaterialGroup && resourceBinding.ResourceGroup == "PerMaterial")
                        hasMaterialGroup = true;
                }
            }

            var usedConstantBuffers = new HashSet<string>();

            for (int i = reflection.ResourceBindings.Count - 1; i >= 0; i--)
            {
                var resourceBinding = reflection.ResourceBindings[i];

                // Do not touch anything if there is logical groups
                // TODO: We can do better than that: remove only if the full group can be optimized away
                if (resourceBinding.LogicalGroup != null)
                    continue;

                if (resourceBinding.Stage == ShaderStage.None && !(hasMaterialGroup && resourceBinding.ResourceGroup == "PerMaterial") && !(hasLightingGroup && resourceBinding.ResourceGroup == "PerLighting"))
                {
                    reflection.ResourceBindings.RemoveAt(i);
                }
                else if (resourceBinding.Class == EffectParameterClass.ConstantBuffer
                    || resourceBinding.Class == EffectParameterClass.TextureBuffer)
                {
                    // Mark associated cbuffer/tbuffer as used
                    usedConstantBuffers.Add(resourceBinding.KeyInfo.KeyName);
                }
            }

            // Remove unused cbuffer
            for (int i = reflection.ConstantBuffers.Count - 1; i >= 0; i--)
            {
                var cbuffer = reflection.ConstantBuffers[i];

                // Do not touch anything if there is logical groups
                // TODO: We can do better than that: remove only if the full group can be optimized away
                var hasLogicalGroup = false;
                foreach (var member in cbuffer.Members)
                {
                    if (member.LogicalGroup != null)
                    {
                        hasLogicalGroup = true;
                        break;
                    }
                }

                if (hasLogicalGroup)
                    continue;

                if (!usedConstantBuffers.Contains(cbuffer.Name))
                {
                    reflection.ConstantBuffers.RemoveAt(i);
                }
            }
        }
Ejemplo n.º 8
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ShaderLinker" /> class.
 /// </summary>
 /// <param name="parsingResult">The parsing result.</param>
 public ShaderLinker(ShaderMixinParsingResult parsingResult)
     : base(true, false)
 {
     this.parsingResult    = parsingResult;
     this.effectReflection = parsingResult.Reflection;
 }
Ejemplo n.º 9
0
        /// <summary>
        /// Converts the hlsl code into glsl and stores the result as plain text
        /// </summary>
        /// <param name="shaderSource">the hlsl shader</param>
        /// <param name="entryPoint">the entrypoint function name</param>
        /// <param name="stage">the shader pipeline stage</param>
        /// <param name="effectParameters"></param>
        /// <param name="reflection">the reflection gathered from the hlsl analysis</param>
        /// <param name="sourceFilename">the name of the source file</param>
        /// <returns></returns>
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, EffectCompilerParameters effectParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var shaderBytecodeResult = new ShaderBytecodeResult();
            byte[] rawData;

            GlslShaderPlatform shaderPlatform;
            int shaderVersion;

            switch (effectParameters.Platform)
            {
                case GraphicsPlatform.OpenGL:
                    shaderPlatform = GlslShaderPlatform.OpenGL;
                    shaderVersion = 420;
                    break;
                case GraphicsPlatform.OpenGLES:
                    shaderPlatform = GlslShaderPlatform.OpenGLES;
                    shaderVersion = effectParameters.Profile >= GraphicsProfile.Level_10_0 ? 300 : 100;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("effectParameters.Platform");
            }

            var shader = Compile(shaderSource, entryPoint, stage, shaderPlatform, shaderVersion, shaderBytecodeResult, reflection, sourceFilename);

            if (shader == null)
                return shaderBytecodeResult;

            if (effectParameters.Platform == GraphicsPlatform.OpenGLES)
            {
                // store both ES 2 and ES 3 on OpenGL ES platforms
                var shaderBytecodes = new ShaderLevelBytecode();
                if (effectParameters.Profile >= GraphicsProfile.Level_10_0)
                {
                    shaderBytecodes.DataES3 = shader;
                    shaderBytecodes.DataES2 = null;
                }
                else
                {
                    shaderBytecodes.DataES2 = shader;
                    shaderBytecodes.DataES3 = Compile(shaderSource, entryPoint, stage, GlslShaderPlatform.OpenGLES, 300, shaderBytecodeResult, reflection, sourceFilename);
                }
                using (var stream = new MemoryStream())
                {
                    BinarySerialization.Write(stream, shaderBytecodes);
#if !SILICONSTUDIO_RUNTIME_CORECLR
                    rawData = stream.GetBuffer();
#else
// FIXME: Manu: The call to "ToArray()" might be slower than "GetBuffer()"
                    rawData = stream.ToArray();
#endif
                }
            }
            else
            {
                // store string on OpenGL platforms
                rawData = Encoding.ASCII.GetBytes(shader);
            }
            
            var bytecodeId = ObjectId.FromBytes(rawData);
            var bytecode = new ShaderBytecode(bytecodeId, rawData);
            bytecode.Stage = stage;

            shaderBytecodeResult.Bytecode = bytecode;
            
            return shaderBytecodeResult;
        }
Ejemplo n.º 10
0
 private static void MarkResourceBindingAsUsed(EffectReflection reflection, int resourceBindingIndex, ShaderStage stage)
 {
     var resourceBinding = reflection.ResourceBindings[resourceBindingIndex];
     if (resourceBinding.Stage == ShaderStage.None)
     {
         resourceBinding.Stage = stage;
         reflection.ResourceBindings[resourceBindingIndex] = resourceBinding;
     }
 }
Ejemplo n.º 11
0
        private string Compile(string shaderSource, string entryPoint, ShaderStage stage, GlslShaderPlatform shaderPlatform, int shaderVersion, ShaderBytecodeResult shaderBytecodeResult, EffectReflection reflection, string sourceFilename = null)
        {
            if (shaderPlatform == GlslShaderPlatform.OpenGLES && shaderVersion < 300 && renderTargetCount > 1)
                shaderBytecodeResult.Error("OpenGL ES 2 does not support multiple render targets.");

            PipelineStage pipelineStage = PipelineStage.None;
            switch (stage)
            {
                case ShaderStage.Vertex:
                    pipelineStage = PipelineStage.Vertex;
                    break;
                case ShaderStage.Pixel:
                    pipelineStage = PipelineStage.Pixel;
                    break;
                case ShaderStage.Geometry:
                    shaderBytecodeResult.Error("Geometry stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                    break;
                case ShaderStage.Hull:
                    shaderBytecodeResult.Error("Hull stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                    break;
                case ShaderStage.Domain:
                    shaderBytecodeResult.Error("Domain stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                    break;
                case ShaderStage.Compute:
                    shaderBytecodeResult.Error("Compute stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                    break;
                default:
                    shaderBytecodeResult.Error("Unknown shader profile.");
                    break;
            }

            if (shaderBytecodeResult.HasErrors)
                return null;

            Shader glslShader;

            // null entry point means no shader. In that case, we return a default function in HlslToGlslWriter
            // TODO: support that directly in HlslToGlslConvertor?
            if (entryPoint == null)
            {
                glslShader = null;
            }
            else
            {
                // Convert from HLSL to GLSL
                // Note that for now we parse from shader as a string, but we could simply clone effectPass.Shader to avoid multiple parsing.
                var glslConvertor = new ShaderConverter(shaderPlatform, shaderVersion);
                glslShader = glslConvertor.Convert(shaderSource, entryPoint, pipelineStage, sourceFilename, shaderBytecodeResult);

                if (glslShader == null || shaderBytecodeResult.HasErrors)
                    return null;

                foreach (var constantBuffer in glslShader.Declarations.OfType<ConstantBuffer>())
                {
                    // Update constant buffer itself (first time only)
                    var reflectionConstantBuffer = reflection.ConstantBuffers.FirstOrDefault(x => x.Name == constantBuffer.Name && x.Size == 0);
                    if (reflectionConstantBuffer != null)
                    {
                        // Used to compute constant buffer size and member offsets (std140 rule)
                        int constantBufferOffset = 0;

                        // Fill members
                        for (int index = 0; index < reflectionConstantBuffer.Members.Length; index++)
                        {
                            var member = reflectionConstantBuffer.Members[index];

                            // Properly compute size and offset according to std140 rules
                            var memberSize = ComputeMemberSize(ref member.Type, ref constantBufferOffset);

                            // Store size/offset info
                            member.Offset = constantBufferOffset;
                            member.Size = memberSize;

                            // Adjust offset for next item
                            constantBufferOffset += memberSize;

                            reflectionConstantBuffer.Members[index] = member;
                        }

                        reflectionConstantBuffer.Size = constantBufferOffset;
                    }

                    // Find binding
                    var resourceBindingIndex = reflection.ResourceBindings.IndexOf(x => x.RawName == constantBuffer.Name);
                    if (resourceBindingIndex != -1)
                        MarkResourceBindingAsUsed(reflection, resourceBindingIndex, stage);
                }

                foreach (var variable in glslShader.Declarations.OfType<Variable>().Where(x => (x.Qualifiers.Contains(StorageQualifier.Uniform))))
                {
                    // Check if we have a variable that starts or ends with this name (in case of samplers)
                    // TODO: Have real AST support for all the list in Keywords.glsl
                    if (variable.Type.Name.Text.Contains("sampler1D")
                        || variable.Type.Name.Text.Contains("sampler2D")
                        || variable.Type.Name.Text.Contains("sampler3D")
                        || variable.Type.Name.Text.Contains("samplerCube"))
                    {
                        // TODO: Make more robust
                        var textureBindingIndex = reflection.ResourceBindings.IndexOf(x => variable.Name.ToString().StartsWith(x.RawName));
                        var samplerBindingIndex = reflection.ResourceBindings.IndexOf(x => variable.Name.ToString().EndsWith(x.RawName));

                        if (textureBindingIndex != -1)
                            MarkResourceBindingAsUsed(reflection, textureBindingIndex, stage);

                        if (samplerBindingIndex != -1)
                            MarkResourceBindingAsUsed(reflection, samplerBindingIndex, stage);
                    }
                    else
                    {
                        var resourceBindingIndex = reflection.ResourceBindings.IndexOf(x => x.RawName == variable.Name);
                        if (resourceBindingIndex != -1)
                            MarkResourceBindingAsUsed(reflection, resourceBindingIndex, stage);
                    }
                }

            }

            // Output the result
            var glslShaderWriter = new HlslToGlslWriter(shaderPlatform, shaderVersion, pipelineStage);

            if (shaderPlatform == GlslShaderPlatform.OpenGLES && shaderVersion < 320)
            {
                glslShaderWriter.ExtraHeaders = "#define texelFetchBufferPlaceholder";
            }

            // Write shader
            glslShaderWriter.Visit(glslShader);

            var shaderString = glslShaderWriter.Text;

            // Build shader source
            var glslShaderCode = new StringBuilder();

            // Append some header depending on target
            //if (isOpenGLES)
            //{
            //    if (isOpenGLES3)
            //    {
            //        glslShaderCode
            //            .AppendLine("#version 300 es") // TODO: 310 version?
            //            .AppendLine();
            //    }
            //
            //    if (pipelineStage == PipelineStage.Pixel)
            //        glslShaderCode
            //            .AppendLine("precision highp float;")
            //            .AppendLine();
            //}
            //else
            //{
            //    glslShaderCode
            //        .AppendLine("#version 420")
            //        .AppendLine()
            //        .AppendLine("#define samplerBuffer sampler2D")
            //        .AppendLine("#define isamplerBuffer isampler2D")
            //        .AppendLine("#define usamplerBuffer usampler2D")
            //        .AppendLine("#define texelFetchBuffer(sampler, P) texelFetch(sampler, ivec2((P) & 0xFFF, (P) >> 12), 0)");
            //        //.AppendLine("#define texelFetchBuffer(sampler, P) texelFetch(sampler, P)");
            //}

            glslShaderCode.Append(shaderString);

            var realShaderSource = glslShaderCode.ToString();

#if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
            // optimize shader
            try
            {
                var optShaderSource = RunOptimizer(shaderBytecodeResult, realShaderSource, shaderPlatform, shaderVersion, pipelineStage == PipelineStage.Vertex);
                if (!String.IsNullOrEmpty(optShaderSource))
                    realShaderSource = optShaderSource;
            }
            catch (Exception e)
            {
                shaderBytecodeResult.Warning("Could not run GLSL optimizer:\n{0}", e.Message);
            }
#else
            shaderBytecodeResult.Warning("GLSL optimized has not been executed because it is currently not supported on this platform.");
#endif

            return realShaderSource;
        }
Ejemplo n.º 12
0
        private void UpdateReflection(ShaderBytecode shaderBytecode, EffectReflection effectReflection, LoggerResult log)
        {
            var shaderReflectionRaw     = new SharpDX.D3DCompiler.ShaderReflection(shaderBytecode);
            var shaderReflectionRawDesc = shaderReflectionRaw.Description;

            foreach (var constantBuffer in effectReflection.ConstantBuffers)
            {
                UpdateConstantBufferReflection(constantBuffer);
            }

            // Constant Buffers
            for (int i = 0; i < shaderReflectionRawDesc.ConstantBuffers; ++i)
            {
                var constantBufferRaw     = shaderReflectionRaw.GetConstantBuffer(i);
                var constantBufferRawDesc = constantBufferRaw.Description;
                if (constantBufferRawDesc.Type == SharpDX.D3DCompiler.ConstantBufferType.ResourceBindInformation)
                {
                    continue;
                }

                try
                {
                    var linkBuffer = effectReflection.ConstantBuffers.First(buffer => buffer.Name == constantBufferRawDesc.Name);
                    ValidateConstantBufferReflection(constantBufferRaw, ref constantBufferRawDesc, linkBuffer, log);
                }
                catch (Exception)
                {
                    // couldn't find a match for this resource, skip
                }
            }

            // BoundResources
            for (int i = 0; i < shaderReflectionRawDesc.BoundResources; ++i)
            {
                var boundResourceDesc = shaderReflectionRaw.GetResourceBindingDescription(i);

                string linkKeyName   = null;
                string resourceGroup = null;
                string logicalGroup  = null;
                foreach (var linkResource in effectReflection.ResourceBindings)
                {
                    if (linkResource.RawName == boundResourceDesc.Name && linkResource.Stage == ShaderStage.None)
                    {
                        linkKeyName   = linkResource.KeyInfo.KeyName;
                        resourceGroup = linkResource.ResourceGroup;
                        logicalGroup  = linkResource.LogicalGroup;
                        break;
                    }
                }

                if (linkKeyName == null)
                {
                    log.Error($"Resource [{boundResourceDesc.Name}] has no link");
                }
                else
                {
                    var binding = GetResourceBinding(boundResourceDesc, linkKeyName, log);
                    binding.Stage         = shaderBytecode.Stage;
                    binding.ResourceGroup = resourceGroup;
                    binding.LogicalGroup  = logicalGroup;

                    effectReflection.ResourceBindings.Add(binding);
                }
            }
        }
Ejemplo n.º 13
0
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, EffectCompilerParameters effectParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var isDebug    = effectParameters.Debug;
            var optimLevel = effectParameters.OptimizationLevel;
            var profile    = effectParameters.Profile;

            var shaderModel = ShaderStageToString(stage) + "_" + ShaderProfileFromGraphicsProfile(profile);

            var shaderFlags = ShaderFlags.None;

            if (isDebug)
            {
                shaderFlags = ShaderFlags.Debug;
            }
            switch (optimLevel)
            {
            case 0:
                shaderFlags |= ShaderFlags.OptimizationLevel0;
                break;

            case 1:
                shaderFlags |= ShaderFlags.OptimizationLevel1;
                break;

            case 2:
                shaderFlags |= ShaderFlags.OptimizationLevel2;
                break;

            case 3:
                shaderFlags |= ShaderFlags.OptimizationLevel3;
                break;
            }
            SharpDX.Configuration.ThrowOnShaderCompileError = false;

            // Compile using D3DCompiler
            var compilationResult = SharpDX.D3DCompiler.ShaderBytecode.Compile(shaderSource, entryPoint, shaderModel, shaderFlags, EffectFlags.None, null, null, sourceFilename);

            var byteCodeResult = new ShaderBytecodeResult();

            if (compilationResult.HasErrors || compilationResult.Bytecode == null)
            {
                // Log compilation errors
                byteCodeResult.Error(compilationResult.Message);
            }
            else
            {
                // TODO: Make this optional
                try
                {
                    byteCodeResult.DisassembleText = compilationResult.Bytecode.Disassemble();
                }
                catch (SharpDXException)
                {
                }

                // As effect bytecode binary can changed when having debug infos (with d3dcompiler_47), we are calculating a bytecodeId on the stripped version
                var rawData    = compilationResult.Bytecode.Strip(StripFlags.CompilerStripDebugInformation | StripFlags.CompilerStripReflectionData);
                var bytecodeId = ObjectId.FromBytes(rawData);
                byteCodeResult.Bytecode = new ShaderBytecode(bytecodeId, compilationResult.Bytecode.Data)
                {
                    Stage = stage
                };

                // If compilation succeed, then we can update reflection.
                UpdateReflection(byteCodeResult.Bytecode, reflection, byteCodeResult);

                if (!string.IsNullOrEmpty(compilationResult.Message))
                {
                    byteCodeResult.Warning(compilationResult.Message);
                }
            }

            return(byteCodeResult);
        }
Ejemplo n.º 14
0
        private void AddUniform(EffectReflection effectReflection, int uniformSize, int uniformCount, string uniformName, ActiveUniformType uniformType)
        {
            // clean the name
            if (uniformName.Contains("."))
            {
                uniformName = uniformName.Substring(0, uniformName.IndexOf('.'));
            }
            if (uniformName.Contains("["))
            {
                uniformName = uniformName.Substring(0, uniformName.IndexOf('['));
            }

            if (GraphicsDevice.IsOpenGLES2)
            {
                var indexOfConstantBufferDescription = effectReflection.ConstantBuffers.FindIndex(x => x.Name == "Globals");
                var indexOfConstantBuffer            = effectReflection.ResourceBindings.FindIndex(x => x.Param.RawName == "Globals");

                if (indexOfConstantBufferDescription == -1 || indexOfConstantBuffer == -1)
                {
                    reflectionResult.Error("Unable to find uniform [{0}] in any constant buffer", uniformName);
                    return;
                }

                var constantBufferDescription = effectReflection.ConstantBuffers[indexOfConstantBufferDescription];
                var constantBuffer            = effectReflection.ResourceBindings[indexOfConstantBuffer];

                var elementSize = uniformSize;

                // For array, each element is rounded to register size
                if (uniformSize % 16 != 0 && uniformCount > 1)
                {
                    constantBufferDescription.Size = (constantBufferDescription.Size + 15) / 16 * 16;
                    uniformSize = (uniformSize + 15) / 16 * 16;
                }

                // Check if it can fits in the same register, otherwise starts at the next one
                if (uniformCount == 1 && constantBufferDescription.Size / 16 != (constantBufferDescription.Size + uniformSize - 1) / 16)
                {
                    constantBufferDescription.Size = (constantBufferDescription.Size + 15) / 16 * 16;
                }

                var indexOfUniform = -1;
                for (var tentativeIndex = 0; tentativeIndex < constantBufferDescription.Members.Length; ++tentativeIndex)
                {
                    if (constantBufferDescription.Members[tentativeIndex].Param.RawName == uniformName)
                    {
                        indexOfUniform = tentativeIndex;
                        break;
                    }
                }

                var variable = constantBufferDescription.Members[indexOfUniform];

                variable.Param.Type = GetTypeFromActiveUniformType(uniformType);
                //variable.SourceOffset = variableIndexGroup.Offset;
                variable.Offset = constantBufferDescription.Size;
                variable.Count  = uniformCount;
                variable.Size   = uniformSize * uniformCount;

                constantBufferDescription.Type = ConstantBufferType.ConstantBuffer;

                constantBuffer.SlotStart = 0;

                constantBufferDescription.Members[indexOfUniform]        = variable;
                effectReflection.ResourceBindings[indexOfConstantBuffer] = constantBuffer;

                // No need to compare last element padding.
                // TODO: In case of float1/float2 arrays (rare) it is quite non-optimal to do a CompareMemory
                var compareSize = uniformSize * (uniformCount - 1) + elementSize;

                Uniforms.Add(new Uniform
                {
                    Type        = uniformType,
                    Count       = uniformCount,
                    CompareSize = compareSize,
                    Offset      = constantBufferDescription.Size,
#if SILICONSTUDIO_PLATFORM_ANDROID
                    UniformIndex = GL.GetUniformLocation(resourceId, new StringBuilder(uniformName))
#else
                    UniformIndex = GL.GetUniformLocation(resourceId, uniformName)
#endif
                });
Ejemplo n.º 15
0
        private string Compile(string shaderSource, string entryPoint, ShaderStage stage, GlslShaderPlatform shaderPlatform, int shaderVersion, ShaderBytecodeResult shaderBytecodeResult, EffectReflection reflection, IDictionary <int, string> inputAttributeNames, Dictionary <string, int> resourceBindings, string sourceFilename = null)
        {
            if (shaderPlatform == GlslShaderPlatform.OpenGLES && shaderVersion < 300 && renderTargetCount > 1)
            {
                shaderBytecodeResult.Error("OpenGL ES 2 does not support multiple render targets.");
            }

            PipelineStage pipelineStage = PipelineStage.None;

            switch (stage)
            {
            case ShaderStage.Vertex:
                pipelineStage = PipelineStage.Vertex;
                break;

            case ShaderStage.Pixel:
                pipelineStage = PipelineStage.Pixel;
                break;

            case ShaderStage.Geometry:
                shaderBytecodeResult.Error("Geometry stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                break;

            case ShaderStage.Hull:
                shaderBytecodeResult.Error("Hull stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                break;

            case ShaderStage.Domain:
                shaderBytecodeResult.Error("Domain stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                break;

            case ShaderStage.Compute:
                shaderBytecodeResult.Error("Compute stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported");
                break;

            default:
                shaderBytecodeResult.Error("Unknown shader profile.");
                break;
            }

            if (shaderBytecodeResult.HasErrors)
            {
                return(null);
            }

            Shader glslShader;

            // null entry point means no shader. In that case, we return a default function in HlslToGlslWriter
            // TODO: support that directly in HlslToGlslConvertor?
            if (entryPoint == null)
            {
                glslShader = null;
            }
            else
            {
                // Convert from HLSL to GLSL
                // Note that for now we parse from shader as a string, but we could simply clone effectPass.Shader to avoid multiple parsing.
                var glslConvertor = new ShaderConverter(shaderPlatform, shaderVersion);
                glslShader = glslConvertor.Convert(shaderSource, entryPoint, pipelineStage, sourceFilename, inputAttributeNames, shaderBytecodeResult);

                if (glslShader == null || shaderBytecodeResult.HasErrors)
                {
                    return(null);
                }

                foreach (var constantBuffer in glslShader.Declarations.OfType <ConstantBuffer>())
                {
                    // Update constant buffer itself (first time only)
                    var reflectionConstantBuffer = reflection.ConstantBuffers.FirstOrDefault(x => x.Name == constantBuffer.Name && x.Size == 0);
                    if (reflectionConstantBuffer != null)
                    {
                        // Used to compute constant buffer size and member offsets (std140 rule)
                        int constantBufferOffset = 0;

                        // Fill members
                        for (int index = 0; index < reflectionConstantBuffer.Members.Length; index++)
                        {
                            var member = reflectionConstantBuffer.Members[index];

                            // Properly compute size and offset according to std140 rules
                            var memberSize = ComputeMemberSize(ref member.Type, ref constantBufferOffset);

                            // Store size/offset info
                            member.Offset = constantBufferOffset;
                            member.Size   = memberSize;

                            // Adjust offset for next item
                            constantBufferOffset += memberSize;

                            reflectionConstantBuffer.Members[index] = member;
                        }

                        reflectionConstantBuffer.Size = constantBufferOffset;
                    }

                    // Find binding
                    var resourceBindingIndex = reflection.ResourceBindings.IndexOf(x => x.RawName == constantBuffer.Name);
                    if (resourceBindingIndex != -1)
                    {
                        MarkResourceBindingAsUsed(reflection, resourceBindingIndex, stage);
                    }
                }

                foreach (var variable in glslShader.Declarations.OfType <Variable>().Where(x => (x.Qualifiers.Contains(StorageQualifier.Uniform))))
                {
                    // Check if we have a variable that starts or ends with this name (in case of samplers)
                    // TODO: Have real AST support for all the list in Keywords.glsl
                    if (variable.Type.Name.Text.Contains("sampler1D") ||
                        variable.Type.Name.Text.Contains("sampler2D") ||
                        variable.Type.Name.Text.Contains("sampler3D") ||
                        variable.Type.Name.Text.Contains("samplerCube") ||
                        variable.Type.Name.Text.Contains("samplerBuffer"))
                    {
                        // TODO: Make more robust
                        var textureBindingIndex = reflection.ResourceBindings.IndexOf(x => variable.Name.ToString().StartsWith(x.RawName));
                        var samplerBindingIndex = reflection.ResourceBindings.IndexOf(x => variable.Name.ToString().EndsWith(x.RawName));

                        if (textureBindingIndex != -1)
                        {
                            MarkResourceBindingAsUsed(reflection, textureBindingIndex, stage);
                        }

                        if (samplerBindingIndex != -1)
                        {
                            MarkResourceBindingAsUsed(reflection, samplerBindingIndex, stage);
                        }
                    }
                    else
                    {
                        var resourceBindingIndex = reflection.ResourceBindings.IndexOf(x => x.RawName == variable.Name);
                        if (resourceBindingIndex != -1)
                        {
                            MarkResourceBindingAsUsed(reflection, resourceBindingIndex, stage);
                        }
                    }
                }

                if (shaderPlatform == GlslShaderPlatform.Vulkan)
                {
                    // Defines the ordering of resource groups in Vulkan. This is mirrored in the PipelineState
                    var resourceGroups = reflection.ResourceBindings.Select(x => x.ResourceGroup ?? "Globals").Distinct().ToList();

                    var bindings = resourceGroups.SelectMany(resourceGroup => reflection.ResourceBindings
                                                             .Where(x => x.ResourceGroup == resourceGroup || (x.ResourceGroup == null && resourceGroup == "Globals"))
                                                             .GroupBy(x => new { KeyName = x.KeyInfo.KeyName, RawName = x.RawName, Class = x.Class, Type = x.Type, SlotCount = x.SlotCount, LogicalGroup = x.LogicalGroup })
                                                             .OrderBy(x => x.Key.Class == EffectParameterClass.ConstantBuffer ? 0 : 1))
                                   .ToList();

                    // Add layout(set, bindings) qualifier to all constant buffers
                    foreach (var constantBuffer in glslShader.Declarations.OfType <ConstantBuffer>())
                    {
                        var layoutBindingIndex = bindings.IndexOf(x => x.Key.RawName == constantBuffer.Name);
                        if (layoutBindingIndex != -1)
                        {
                            var layoutQualifier = constantBuffer.Qualifiers.OfType <SiliconStudio.Shaders.Ast.Glsl.LayoutQualifier>().FirstOrDefault();
                            if (layoutQualifier == null)
                            {
                                layoutQualifier            = new SiliconStudio.Shaders.Ast.Glsl.LayoutQualifier();
                                constantBuffer.Qualifiers |= layoutQualifier;
                            }

                            //layoutQualifier.Layouts.Add(new LayoutKeyValue("set", resourceGroups.IndexOf(resourceGroup)));
                            layoutQualifier.Layouts.Add(new LayoutKeyValue("set", 0));
                            layoutQualifier.Layouts.Add(new LayoutKeyValue("binding", layoutBindingIndex + 1));

                            resourceBindings.Add(bindings[layoutBindingIndex].Key.KeyName, layoutBindingIndex + 1);
                        }
                    }

                    // Add layout(set, bindings) qualifier to all other uniforms
                    foreach (var variable in glslShader.Declarations.OfType <Variable>().Where(x => (x.Qualifiers.Contains(StorageQualifier.Uniform))))
                    {
                        var layoutBindingIndex = bindings.IndexOf(x => variable.Name.Text.StartsWith(x.Key.RawName));

                        if (layoutBindingIndex != -1)
                        {
                            var layoutQualifier = variable.Qualifiers.OfType <SiliconStudio.Shaders.Ast.Glsl.LayoutQualifier>().FirstOrDefault();
                            if (layoutQualifier == null)
                            {
                                layoutQualifier      = new SiliconStudio.Shaders.Ast.Glsl.LayoutQualifier();
                                variable.Qualifiers |= layoutQualifier;
                            }

                            //layoutQualifier.Layouts.Add(new LayoutKeyValue("set", resourceGroups.IndexOf(resourceGroup)));
                            layoutQualifier.Layouts.Add(new LayoutKeyValue("set", 0));
                            layoutQualifier.Layouts.Add(new LayoutKeyValue("binding", layoutBindingIndex + 1));

                            resourceBindings.Add(bindings[layoutBindingIndex].Key.KeyName, layoutBindingIndex + 1);
                        }
                    }
                }
            }

            // Output the result
            var glslShaderWriter = new HlslToGlslWriter(shaderPlatform, shaderVersion, pipelineStage);

            if (shaderPlatform == GlslShaderPlatform.OpenGLES && shaderVersion < 320)
            {
                glslShaderWriter.ExtraHeaders = "#define texelFetchBufferPlaceholder";
            }

            // Write shader
            glslShaderWriter.Visit(glslShader);

            var shaderString = glslShaderWriter.Text;

            // Build shader source
            var glslShaderCode = new StringBuilder();

            // Append some header depending on target
            //if (isOpenGLES)
            //{
            //    if (isOpenGLES3)
            //    {
            //        glslShaderCode
            //            .AppendLine("#version 300 es") // TODO: 310 version?
            //            .AppendLine();
            //    }
            //
            //    if (pipelineStage == PipelineStage.Pixel)
            //        glslShaderCode
            //            .AppendLine("precision highp float;")
            //            .AppendLine();
            //}
            //else
            //{
            //    glslShaderCode
            //        .AppendLine("#version 420")
            //        .AppendLine()
            //        .AppendLine("#define samplerBuffer sampler2D")
            //        .AppendLine("#define isamplerBuffer isampler2D")
            //        .AppendLine("#define usamplerBuffer usampler2D")
            //        .AppendLine("#define texelFetchBuffer(sampler, P) texelFetch(sampler, ivec2((P) & 0xFFF, (P) >> 12), 0)");
            //        //.AppendLine("#define texelFetchBuffer(sampler, P) texelFetch(sampler, P)");
            //}

            glslShaderCode.Append(shaderString);

            var realShaderSource = glslShaderCode.ToString();

// TODO: Disabled because glsl optimizer don't properly put lowp/highp qualifier inside struct (which make shader compilation fails since we use struct for lighting)
#if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
            // optimize shader
            try
            {
                var optShaderSource = RunOptimizer(shaderBytecodeResult, realShaderSource, shaderPlatform, shaderVersion, pipelineStage == PipelineStage.Vertex);
                if (!String.IsNullOrEmpty(optShaderSource))
                {
                    realShaderSource = optShaderSource;
                }
            }
            catch (Exception e)
            {
                shaderBytecodeResult.Warning("Could not run GLSL optimizer}", e);
            }
#else
            shaderBytecodeResult.Warning("GLSL optimized has not been executed because it is currently not supported on this platform.");
#endif

            return(realShaderSource);
        }
Ejemplo n.º 16
0
        /// <summary>
        /// Converts the hlsl code into glsl and stores the result as plain text
        /// </summary>
        /// <param name="shaderSource">the hlsl shader</param>
        /// <param name="entryPoint">the entrypoint function name</param>
        /// <param name="stage">the shader pipeline stage</param>
        /// <param name="effectParameters"></param>
        /// <param name="reflection">the reflection gathered from the hlsl analysis</param>
        /// <param name="sourceFilename">the name of the source file</param>
        /// <returns></returns>
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, EffectCompilerParameters effectParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var shaderBytecodeResult = new ShaderBytecodeResult();

            byte[] rawData;

            var inputAttributeNames = new Dictionary <int, string>();
            var resourceBindings    = new Dictionary <string, int>();

            GlslShaderPlatform shaderPlatform;
            int shaderVersion;

            switch (effectParameters.Platform)
            {
            case GraphicsPlatform.OpenGL:
                shaderPlatform = GlslShaderPlatform.OpenGL;
                shaderVersion  = 410;
                break;

            case GraphicsPlatform.OpenGLES:
                shaderPlatform = GlslShaderPlatform.OpenGLES;
                shaderVersion  = effectParameters.Profile >= GraphicsProfile.Level_10_0 ? 300 : 100;
                break;

            case GraphicsPlatform.Vulkan:
                shaderPlatform = GlslShaderPlatform.Vulkan;
                shaderVersion  = 450;
                break;

            default:
                throw new ArgumentOutOfRangeException("effectParameters.Platform");
            }

            var shader = Compile(shaderSource, entryPoint, stage, shaderPlatform, shaderVersion, shaderBytecodeResult, reflection, inputAttributeNames, resourceBindings, sourceFilename);

            if (shader == null)
            {
                return(shaderBytecodeResult);
            }

            if (effectParameters.Platform == GraphicsPlatform.OpenGLES)
            {
                // store both ES 2 and ES 3 on OpenGL ES platforms
                var shaderBytecodes = new ShaderLevelBytecode();
                if (effectParameters.Profile >= GraphicsProfile.Level_10_0)
                {
                    shaderBytecodes.DataES3 = shader;
                    shaderBytecodes.DataES2 = null;
                }
                else
                {
                    shaderBytecodes.DataES2 = shader;
                    shaderBytecodes.DataES3 = Compile(shaderSource, entryPoint, stage, GlslShaderPlatform.OpenGLES, 300, shaderBytecodeResult, reflection, inputAttributeNames, resourceBindings, sourceFilename);
                }
                using (var stream = new MemoryStream())
                {
                    BinarySerialization.Write(stream, shaderBytecodes);
#if !SILICONSTUDIO_RUNTIME_CORECLR && !SILICONSTUDIO_PLATFORM_UWP
                    rawData = stream.GetBuffer();
#else
// FIXME: Manu: The call to "ToArray()" might be slower than "GetBuffer()"
                    rawData = stream.ToArray();
#endif
                }
            }
#if !SILICONSTUDIO_RUNTIME_CORECLR && !SILICONSTUDIO_PLATFORM_UWP
            else if (effectParameters.Platform == GraphicsPlatform.Vulkan)
            {
                string inputFileExtension;
                switch (stage)
                {
                case ShaderStage.Vertex: inputFileExtension = ".vert"; break;

                case ShaderStage.Pixel: inputFileExtension = ".frag"; break;

                case ShaderStage.Geometry: inputFileExtension = ".geom"; break;

                case ShaderStage.Domain: inputFileExtension = ".tese"; break;

                case ShaderStage.Hull: inputFileExtension = ".tesc"; break;

                case ShaderStage.Compute: inputFileExtension = ".comp"; break;

                default:
                    shaderBytecodeResult.Error("Unknown shader profile");
                    return(shaderBytecodeResult);
                }

                var inputFileName  = Path.ChangeExtension(Path.GetTempFileName(), inputFileExtension);
                var outputFileName = Path.ChangeExtension(inputFileName, ".spv");

                // Write shader source to disk
                File.WriteAllBytes(inputFileName, Encoding.ASCII.GetBytes(shader));

                // Run shader compiler
                var filename = Platform.Type == PlatformType.Windows ? "glslangValidator.exe" : "glslangValidator";
                ShellHelper.RunProcessAndRedirectToLogger(filename, $"-V -o {outputFileName} {inputFileName}", null, shaderBytecodeResult);

                if (!File.Exists(outputFileName))
                {
                    shaderBytecodeResult.Error("Failed to generate SPIR-V from GLSL");
                    return(shaderBytecodeResult);
                }

                // Read compiled shader
                var shaderBytecodes = new ShaderInputBytecode
                {
                    InputAttributeNames = inputAttributeNames,
                    ResourceBindings    = resourceBindings,
                    Data = File.ReadAllBytes(outputFileName),
                };

                using (var stream = new MemoryStream())
                {
                    BinarySerialization.Write(stream, shaderBytecodes);
                    rawData = stream.ToArray();
                }

                // Cleanup temp files
                File.Delete(inputFileName);
                File.Delete(outputFileName);
            }
#endif
            else
            {
                // store string on OpenGL platforms
                rawData = Encoding.UTF8.GetBytes(shader);
            }

            var bytecodeId = ObjectId.FromBytes(rawData);
            var bytecode   = new ShaderBytecode(bytecodeId, rawData);
            bytecode.Stage = stage;

            shaderBytecodeResult.Bytecode = bytecode;

            return(shaderBytecodeResult);
        }
Ejemplo n.º 17
0
        /// <summary>
        /// Converts the hlsl code into glsl and stores the result as plain text
        /// </summary>
        /// <param name="shaderSource">the hlsl shader</param>
        /// <param name="entryPoint">the entrypoint function name</param>
        /// <param name="stage">the shader pipeline stage</param>
        /// <param name="compilerParameters"></param>
        /// <param name="reflection">the reflection gathered from the hlsl analysis</param>
        /// <param name="sourceFilename">the name of the source file</param>
        /// <returns></returns>
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var isOpenGLES           = compilerParameters.Get(CompilerParameters.GraphicsPlatformKey) == GraphicsPlatform.OpenGLES;
            var isOpenGLES3          = compilerParameters.Get(CompilerParameters.GraphicsProfileKey) >= GraphicsProfile.Level_10_0;
            var shaderBytecodeResult = new ShaderBytecodeResult();

            byte[] rawData;

            var shader = Compile(shaderSource, entryPoint, stage, isOpenGLES, isOpenGLES3, shaderBytecodeResult, sourceFilename);

            if (shader == null)
            {
                return(shaderBytecodeResult);
            }

            if (isOpenGLES)
            {
                // store both ES 2 and ES 3 on OpenGL ES platforms
                var shaderBytecodes = new ShaderLevelBytecode();
                if (isOpenGLES3)
                {
                    shaderBytecodes.DataES3 = shader;
                    shaderBytecodes.DataES2 = null;
                }
                else
                {
                    shaderBytecodes.DataES2 = shader;
                    shaderBytecodes.DataES3 = Compile(shaderSource, entryPoint, stage, true, true, shaderBytecodeResult, sourceFilename);
                }
                using (var stream = new MemoryStream())
                {
                    BinarySerialization.Write(stream, shaderBytecodes);
                    rawData = stream.GetBuffer();
                }
            }
            else
            {
                // store string on OpenGL platforms
                rawData = Encoding.ASCII.GetBytes(shader);
            }

            var bytecodeId = ObjectId.FromBytes(rawData);
            var bytecode   = new ShaderBytecode(bytecodeId, rawData);

            bytecode.Stage = stage;

            shaderBytecodeResult.Bytecode = bytecode;

            return(shaderBytecodeResult);
        }
Ejemplo n.º 18
0
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, EffectCompilerParameters effectParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var isDebug = effectParameters.Debug;
            var optimLevel = effectParameters.OptimizationLevel;
            var profile = effectParameters.Profile;
            
            var shaderModel = ShaderStageToString(stage) + "_" + ShaderProfileFromGraphicsProfile(profile);

            var shaderFlags = ShaderFlags.None;
            if (isDebug)
            {
                shaderFlags = ShaderFlags.Debug;
            }
            switch (optimLevel)
            {
                case 0:
                    shaderFlags |= ShaderFlags.OptimizationLevel0;
                    break;
                case 1:
                    shaderFlags |= ShaderFlags.OptimizationLevel1;
                    break;
                case 2:
                    shaderFlags |= ShaderFlags.OptimizationLevel2;
                    break;
                case 3:
                    shaderFlags |= ShaderFlags.OptimizationLevel3;
                    break;
            }
            SharpDX.Configuration.ThrowOnShaderCompileError = false;

            // Compile using D3DCompiler
            var compilationResult = SharpDX.D3DCompiler.ShaderBytecode.Compile(shaderSource, entryPoint, shaderModel, shaderFlags, EffectFlags.None, null, null, sourceFilename);

            var byteCodeResult = new ShaderBytecodeResult();

            if (compilationResult.HasErrors || compilationResult.Bytecode == null)
            {
                // Log compilation errors
                byteCodeResult.Error(compilationResult.Message);
            }
            else
            {
                // TODO: Make this optional
                try
                {
                    byteCodeResult.DisassembleText = compilationResult.Bytecode.Disassemble();
                }
                catch (SharpDXException)
                {
                }

                // As effect bytecode binary can changed when having debug infos (with d3dcompiler_47), we are calculating a bytecodeId on the stripped version
                var rawData = compilationResult.Bytecode.Strip(StripFlags.CompilerStripDebugInformation | StripFlags.CompilerStripReflectionData);
                var bytecodeId = ObjectId.FromBytes(rawData);
                byteCodeResult.Bytecode = new ShaderBytecode(bytecodeId, compilationResult.Bytecode.Data) { Stage = stage };

                // If compilation succeed, then we can update reflection.
                UpdateReflection(byteCodeResult.Bytecode, reflection, byteCodeResult);

                if (!string.IsNullOrEmpty(compilationResult.Message))
                {
                    byteCodeResult.Warning(compilationResult.Message);
                }
            }

            return byteCodeResult;
        }
Ejemplo n.º 19
0
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var isDebug = compilerParameters.Get(CompilerParameters.DebugKey);
            var profile = compilerParameters.Get(CompilerParameters.GraphicsProfileKey);

            var shaderModel = ShaderStageToString(stage) + "_" + ShaderProfileFromGraphicsProfile(profile);

            var shaderFlags = ShaderFlags.None;

            if (isDebug)
            {
                shaderFlags = ShaderFlags.OptimizationLevel0 | ShaderFlags.Debug;
            }

            SharpDX.Configuration.ThrowOnShaderCompileError = false;

            // Compile using D3DCompiler
            var compilationResult = SharpDX.D3DCompiler.ShaderBytecode.Compile(shaderSource, entryPoint, shaderModel, shaderFlags, EffectFlags.None, null, null, sourceFilename);

            var byteCodeResult = new ShaderBytecodeResult();

            if (compilationResult.HasErrors)
            {
                // Log compilation errors
                byteCodeResult.Error(compilationResult.Message);
            }
            else
            {
                // As effect bytecode binary can changed when having debug infos (with d3dcompiler_47), we are calculating a bytecodeId on the stripped version
                var rawData    = compilationResult.Bytecode.Strip(StripFlags.CompilerStripDebugInformation | StripFlags.CompilerStripReflectionData);
                var bytecodeId = ObjectId.FromBytes(rawData);
                byteCodeResult.Bytecode = new ShaderBytecode(bytecodeId, compilationResult.Bytecode.Data)
                {
                    Stage = stage
                };

                // If compilation succeed, then we can update reflection.
                UpdateReflection(byteCodeResult.Bytecode, reflection, byteCodeResult);

                if (!string.IsNullOrEmpty(compilationResult.Message))
                {
                    byteCodeResult.Warning(compilationResult.Message);
                }
            }

            return(byteCodeResult);
        }
Ejemplo n.º 20
0
 private static void LinkVariable(EffectReflection reflection, string variableName, LocalParameterKey parameterKey, int slotCount)
 {
     var binding = new EffectResourceBindingDescription { KeyInfo = { KeyName = parameterKey.Name }, Class = parameterKey.Type.Class, Type = parameterKey.Type.Type, RawName = variableName, SlotStart = -1, SlotCount = slotCount > 0 ? slotCount : 1, ResourceGroup = parameterKey.ResourceGroup, LogicalGroup = parameterKey.LogicalGroup };
     reflection.ResourceBindings.Add(binding);
 }
Ejemplo n.º 21
0
        /// <summary>
        /// Create or updates the reflection for this shader
        /// </summary>
        /// <param name="effectReflection">the reflection from the hlsl</param>
        /// <param name="stage">the shader pipeline stage</param>
        private void CreateReflection(EffectReflection effectReflection, ShaderStage stage)
        {
            int currentProgram;

            GL.GetInteger(GetPName.CurrentProgram, out currentProgram);
            GL.UseProgram(ProgramId);

            int uniformBlockCount;

            GL.GetProgram(ProgramId, XkActiveUniformBlocks, out uniformBlockCount);

            var validConstantBuffers = new bool[effectReflection.ConstantBuffers.Count];

            for (int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; ++uniformBlockIndex)
            {
                // TODO: get previous name to find te actual constant buffer in the reflexion
#if SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                const int sbCapacity = 128;
                int       length;
                var       sb = new StringBuilder(sbCapacity);
                GL.GetActiveUniformBlockName(ProgramId, uniformBlockIndex, sbCapacity, out length, sb);
                var constantBufferName = sb.ToString();
#else
                var constantBufferName = GL.GetActiveUniformBlockName(ProgramId, uniformBlockIndex);
#endif

                var constantBufferDescriptionIndex = effectReflection.ConstantBuffers.FindIndex(x => x.Name == constantBufferName);
                if (constantBufferDescriptionIndex == -1)
                {
                    reflectionResult.Error("Unable to find the constant buffer description [{0}]", constantBufferName);
                    return;
                }
                var constantBufferIndex = effectReflection.ResourceBindings.FindIndex(x => x.RawName == constantBufferName);
                if (constantBufferIndex == -1)
                {
                    reflectionResult.Error("Unable to find the constant buffer [{0}]", constantBufferName);
                    return;
                }

                var constantBufferDescription = effectReflection.ConstantBuffers[constantBufferDescriptionIndex];
                var constantBuffer            = effectReflection.ResourceBindings[constantBufferIndex];

                GL.GetActiveUniformBlock(ProgramId, uniformBlockIndex, ActiveUniformBlockParameter.UniformBlockDataSize, out constantBufferDescription.Size);

                int uniformCount;
                GL.GetActiveUniformBlock(ProgramId, uniformBlockIndex, ActiveUniformBlockParameter.UniformBlockActiveUniforms, out uniformCount);

                // set the binding
                GL.UniformBlockBinding(ProgramId, uniformBlockIndex, uniformBlockIndex);

                // Read uniforms desc
                var uniformIndices = new int[uniformCount];
                var uniformOffsets = new int[uniformCount];
                var uniformTypes   = new int[uniformCount];
                var uniformNames   = new string[uniformCount];
                GL.GetActiveUniformBlock(ProgramId, uniformBlockIndex, ActiveUniformBlockParameter.UniformBlockActiveUniformIndices, uniformIndices);
                GL.GetActiveUniforms(ProgramId, uniformIndices.Length, uniformIndices, ActiveUniformParameter.UniformOffset, uniformOffsets);
                GL.GetActiveUniforms(ProgramId, uniformIndices.Length, uniformIndices, ActiveUniformParameter.UniformType, uniformTypes);

                for (int uniformIndex = 0; uniformIndex < uniformIndices.Length; ++uniformIndex)
                {
#if SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                    int size;
                    ActiveUniformType aut;
                    GL.GetActiveUniform(ProgramId, uniformIndices[uniformIndex], sbCapacity, out length, out size, out aut, sb);
                    uniformNames[uniformIndex] = sb.ToString();
#else
                    uniformNames[uniformIndex] = GL.GetActiveUniformName(ProgramId, uniformIndices[uniformIndex]);
#endif
                }

                // Reoder by offset
                var indexMapping = uniformIndices.Select((x, i) => new UniformMergeInfo {
                    Offset = uniformOffsets[i], Type = (ActiveUniformType)uniformTypes[i], Name = uniformNames[i], NextOffset = 0
                }).OrderBy(x => x.Offset).ToArray();
                indexMapping.Last().NextOffset = constantBufferDescription.Size;

                // Fill next offsets
                for (int i = 1; i < indexMapping.Length; ++i)
                {
                    indexMapping[i - 1].NextOffset = indexMapping[i].Offset;
                }

                // Group arrays/structures into one variable (std140 layout is enough for offset determinism inside arrays/structures)
                indexMapping = indexMapping.GroupBy(x =>
                {
                    // Use only first part of name (ignore structure/array part)
                    var name = x.Name;
                    if (name.Contains("."))
                    {
                        name = name.Substring(0, name.IndexOf('.'));
                    }
                    if (name.Contains("["))
                    {
                        name = name.Substring(0, name.IndexOf('['));
                    }
                    return(name);
                })
                               .Select(x =>
                {
                    var result        = x.First();
                    result.NextOffset = x.Last().NextOffset;

                    // Check weither it's an array or a struct
                    int dotIndex   = result.Name.IndexOf('.');
                    int arrayIndex = result.Name.IndexOf('[');

                    if (x.Count() > 1 && arrayIndex == -1 && dotIndex == -1)
                    {
                        throw new InvalidOperationException();
                    }

                    // TODO: Type processing

                    result.Name = x.Key;
                    return(result);
                }).ToArray();

                foreach (var variableIndexGroup in indexMapping)
                {
                    var variableIndex = -1;
                    for (var tentativeIndex = 0; tentativeIndex < constantBufferDescription.Members.Length; ++tentativeIndex)
                    {
                        if (constantBufferDescription.Members[tentativeIndex].RawName == variableIndexGroup.Name)
                        {
                            variableIndex = tentativeIndex;
                            break;
                        }
                    }

                    if (variableIndex == -1)
                    {
                        reflectionResult.Error("Unable to find uniform [{0}] in constant buffer [{1}]", variableIndexGroup.Name, constantBufferName);
                        continue;
                    }
                    var variable = constantBufferDescription.Members[variableIndex];
                    variable.Type.Type = GetTypeFromActiveUniformType(variableIndexGroup.Type);
                    variable.Offset    = variableIndexGroup.Offset;
                    variable.Size      = variableIndexGroup.NextOffset - variableIndexGroup.Offset;

                    constantBufferDescription.Members[variableIndex] = variable;
                }

                constantBufferDescription.Type = ConstantBufferType.ConstantBuffer;

                constantBuffer.SlotCount = 1; // constant buffers are not arrays
                constantBuffer.SlotStart = uniformBlockIndex;
                constantBuffer.Stage     = stage;

                // store the new values
                validConstantBuffers[constantBufferDescriptionIndex]             = true;
                effectReflection.ConstantBuffers[constantBufferDescriptionIndex] = constantBufferDescription;
                effectReflection.ResourceBindings[constantBufferIndex]           = constantBuffer;
            }
//#endif

            // Remove unecessary cbuffer and resource bindings

            // Register textures, samplers, etc...
            //TODO: (?) non texture/buffer uniform outside of a block
            {
                // Register "NoSampler", required by HLSL=>GLSL translation to support HLSL such as texture.Load().
                var noSampler = new EffectResourceBindingDescription {
                    KeyInfo = { KeyName = "NoSampler" }, RawName = "NoSampler", Class = EffectParameterClass.Sampler, SlotStart = -1, SlotCount = 1
                };
                Reflection.ResourceBindings.Add(noSampler);

                int activeUniformCount;
                GL.GetProgram(ProgramId, GetProgramParameterName.ActiveUniforms, out activeUniformCount);
#if !SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                var uniformTypes = new int[activeUniformCount];
                GL.GetActiveUniforms(ProgramId, activeUniformCount, Enumerable.Range(0, activeUniformCount).ToArray(), ActiveUniformParameter.UniformType, uniformTypes);
#endif

                int textureUnitCount = 0;

                const int sbCapacity = 128;
                var       sb         = new StringBuilder(sbCapacity);

                for (int activeUniformIndex = 0; activeUniformIndex < activeUniformCount; ++activeUniformIndex)
                {
#if !SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                    var uniformType = (ActiveUniformType)uniformTypes[activeUniformIndex];
                    var uniformName = GL.GetActiveUniformName(ProgramId, activeUniformIndex);
#else
                    ActiveUniformType uniformType;
                    int uniformCount;
                    int length;
                    GL.GetActiveUniform(ProgramId, activeUniformIndex, sbCapacity, out length, out uniformCount, out uniformType, sb);
                    var uniformName = sb.ToString();

                    //this is a special OpenglES case , it is declared as built in uniform, and the driver will take care of it, we just need to ignore it here
                    if (uniformName.StartsWith("gl_DepthRange"))
                    {
                        continue;
                    }
#endif

#if SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                    // Process uniforms
                    if (GraphicsDevice.IsOpenGLES2)
                    {
                        switch (uniformType)
                        {
                        case ActiveUniformType.Bool:
                        case ActiveUniformType.Int:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(int) * 1, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.BoolVec2:
                        case ActiveUniformType.IntVec2:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(int) * 2, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.BoolVec3:
                        case ActiveUniformType.IntVec3:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(int) * 3, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.BoolVec4:
                        case ActiveUniformType.IntVec4:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(int) * 4, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.Float:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(float) * 1, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.FloatVec2:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(float) * 2, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.FloatVec3:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(float) * 3, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.FloatVec4:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(float) * 4, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.FloatMat4:
                            AddUniform(effectReflection, validConstantBuffers, sizeof(float) * 4 * 4, uniformCount, uniformName, uniformType);
                            break;

                        case ActiveUniformType.FloatMat2:
                        case ActiveUniformType.FloatMat3:
                            throw new NotImplementedException();
                        }
                    }
#endif

                    switch (uniformType)
                    {
#if !SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                    case ActiveUniformType.Sampler1D:
                    case ActiveUniformType.Sampler1DShadow:
                    case ActiveUniformType.IntSampler1D:
                    case ActiveUniformType.UnsignedIntSampler1D:

                    case ActiveUniformType.SamplerBuffer:
                    case ActiveUniformType.UnsignedIntSamplerBuffer:
                    case ActiveUniformType.IntSamplerBuffer:
#endif
                    case ActiveUniformType.Sampler2D:
                    case ActiveUniformType.Sampler2DShadow:
                    case ActiveUniformType.Sampler3D:     // TODO: remove Texture3D that is not available in OpenGL ES 2
                    case ActiveUniformType.SamplerCube:
                    case ActiveUniformType.IntSampler2D:
                    case ActiveUniformType.IntSampler3D:
                    case ActiveUniformType.IntSamplerCube:
                    case ActiveUniformType.UnsignedIntSampler2D:
                    case ActiveUniformType.UnsignedIntSampler3D:
                    case ActiveUniformType.UnsignedIntSamplerCube:
                        var uniformIndex = GL.GetUniformLocation(ProgramId, uniformName);

                        // Temporary way to scan which texture and sampler created this texture_sampler variable (to fix with new HLSL2GLSL converter)

                        var startIndex             = -1;
                        var textureReflectionIndex = -1;
                        var samplerReflectionIndex = -1;
                        do
                        {
                            int middlePart  = uniformName.IndexOf('_', startIndex + 1);
                            var textureName = middlePart != -1 ? uniformName.Substring(0, middlePart) : uniformName;
                            var samplerName = middlePart != -1 ? uniformName.Substring(middlePart + 1) : null;

                            textureReflectionIndex =
                                effectReflection.ResourceBindings.FindIndex(x => x.RawName == textureName);
                            samplerReflectionIndex =
                                effectReflection.ResourceBindings.FindIndex(x => x.RawName == samplerName);

                            if (textureReflectionIndex != -1 && samplerReflectionIndex != -1)
                            {
                                break;
                            }

                            startIndex = middlePart;
                        } while (startIndex != -1);

                        if (startIndex == -1 || textureReflectionIndex == -1 || samplerReflectionIndex == -1)
                        {
                            reflectionResult.Error("Unable to find sampler and texture corresponding to [{0}]", uniformName);
                            continue;     // Error
                        }

                        var textureReflection = effectReflection.ResourceBindings[textureReflectionIndex];
                        var samplerReflection = effectReflection.ResourceBindings[samplerReflectionIndex];

                        // Contrary to Direct3D, samplers and textures are part of the same object in OpenGL
                        // Since we are exposing the Direct3D representation, a single sampler parameter key can be used for several textures, a single texture can be used with several samplers.
                        // When such a case is detected, we need to duplicate the resource binding.
                        textureReflectionIndex = GetReflexionIndex(textureReflection, textureReflectionIndex, effectReflection.ResourceBindings);
                        samplerReflectionIndex = GetReflexionIndex(samplerReflection, samplerReflectionIndex, effectReflection.ResourceBindings);

                        // Update texture uniform mapping
                        GL.Uniform1(uniformIndex, textureUnitCount);

                        textureReflection.Stage = stage;
                        //textureReflection.Param.RawName = uniformName;
                        textureReflection.Type      = GetTypeFromActiveUniformType(uniformType);
                        textureReflection.Class     = EffectParameterClass.ShaderResourceView;
                        textureReflection.SlotStart = textureUnitCount;
                        textureReflection.SlotCount = 1;     // TODO: texture arrays

                        samplerReflection.Stage     = stage;
                        samplerReflection.Class     = EffectParameterClass.Sampler;
                        samplerReflection.SlotStart = textureUnitCount;
                        samplerReflection.SlotCount = 1;     // TODO: texture arrays

                        effectReflection.ResourceBindings[textureReflectionIndex] = textureReflection;
                        effectReflection.ResourceBindings[samplerReflectionIndex] = samplerReflection;

                        Textures.Add(new Texture(textureUnitCount));

                        textureUnitCount++;
                        break;
                    }
                }

                // Remove any optimized resource binding
                effectReflection.ResourceBindings.RemoveAll(x => x.SlotStart == -1);
                effectReflection.ConstantBuffers = effectReflection.ConstantBuffers.Where((cb, i) => validConstantBuffers[i]).ToList();
            }

            GL.UseProgram(currentProgram);
        }
Ejemplo n.º 22
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ShaderLinker" /> class.
 /// </summary>
 /// <param name="parsingResult">The parsing result.</param>
 public ShaderLinker(ShaderMixinParsingResult parsingResult)
     : base(true, false)
 {
     this.parsingResult = parsingResult;
     this.effectReflection = parsingResult.Reflection;
 }
Ejemplo n.º 23
0
        private void AddUniform(EffectReflection effectReflection, bool[] validConstantBuffers, int uniformSize, int uniformCount, string uniformName, ActiveUniformType uniformType)
        {
            // OpenGL ES 2 is adding uniform for each cbuffer member, so we need to remove array and struct indexers so that we can identify it in cbuffer and find offset
            var uniformParts     = new List <UniformPart>(8);
            var uniformLastStart = 0;

            for (var index = 0; index <= uniformName.Length; index++)
            {
                char c = index == uniformName.Length ? '.' : uniformName[index]; // Treat string end same as '.'
                if (c == '.' || c == '[')
                {
                    var uniformPart = new UniformPart {
                        Start = uniformLastStart, Count = index - uniformLastStart, Indexer = -1
                    };

                    // Read array index (if any)
                    if (c == '[')
                    {
                        var indexerStart = ++index;
                        while (uniformName[index] != ']')
                        {
                            index++;
                        }
                        // TODO: Avoid substring
                        uniformPart.Indexer = int.Parse(uniformName.Substring(indexerStart, index - indexerStart));
                        index++;
                    }

                    uniformParts.Add(uniformPart);
                    uniformLastStart = index + 1;
                }
            }

            var variableName = uniformName.Substring(0, uniformParts[0].Count);

            // check that this uniform is in a constant buffer
            int indexOfConstantBuffer = -1;
            int indexOfMember         = -1;
            EffectConstantBufferDescription constantBufferDescription = null;

            for (int cbIndex = 0; cbIndex < effectReflection.ConstantBuffers.Count; cbIndex++)
            {
                var currentConstantBuffer = effectReflection.ConstantBuffers[cbIndex];
                for (int index = 0; index < currentConstantBuffer.Members.Length; index++)
                {
                    var member = currentConstantBuffer.Members[index];
                    if (member.RawName.Equals(variableName))
                    {
                        indexOfConstantBuffer     = cbIndex;
                        indexOfMember             = index;
                        constantBufferDescription = currentConstantBuffer;
                        break;
                    }
                }
                if (constantBufferDescription != null)
                {
                    break;
                }
            }

            if (constantBufferDescription == null)
            {
                throw new Exception("The uniform value " + variableName + " is defined outside of a uniform block, which is not supported by the engine.");
            }

            var indexOfResource = effectReflection.ResourceBindings.FindIndex(x => x.RawName == constantBufferDescription.Name);

            if (indexOfResource == -1)
            {
                reflectionResult.Error("Unable to find uniform [{0}] in any constant buffer", uniformName);
                return;
            }

            //var constantBufferDescription = effectReflection.ConstantBuffers[indexOfConstantBufferDescription];
            var constantBuffer = effectReflection.ResourceBindings[indexOfResource];

            // First time we encounter this cbuffer?
            if (!validConstantBuffers[indexOfConstantBuffer])
            {
                constantBuffer.SlotStart = ConstantBufferOffsets.Length - 1;

                // Find next cbuffer slot
                Array.Resize(ref ConstantBufferOffsets, ConstantBufferOffsets.Length + 1);

                effectReflection.ResourceBindings[indexOfResource] = constantBuffer;

                ConstantBufferOffsets[constantBuffer.SlotStart + 1] = ConstantBufferOffsets[constantBuffer.SlotStart] + constantBufferDescription.Size;

                validConstantBuffers[indexOfConstantBuffer] = true;
            }

            //var elementSize = uniformSize;

            // For array, each element is rounded to register size
            //if (uniformSize%16 != 0 && uniformCount > 1)
            //{
            //    constantBufferDescription.Size = (constantBufferDescription.Size + 15)/16*16;
            //    uniformSize = (uniformSize + 15)/16*16;
            //}

            // Check if it can fits in the same register, otherwise starts at the next one
            //if (uniformCount == 1 && constantBufferDescription.Size/16 != (constantBufferDescription.Size + uniformSize - 1)/16)
            //    constantBufferDescription.Size = (constantBufferDescription.Size + 15)/16*16;

            var variable = constantBufferDescription.Members[indexOfMember];

            // Resolve array/member
            var offset = variable.Offset;
            var type   = variable.Type;

            for (int i = 0; i < uniformParts.Count; ++i)
            {
                var uniformPart = uniformParts[i];

                // Apply member
                if (i > 0)
                {
                    if (type.Members == null)
                    {
                        throw new InvalidOperationException($"Tried to find member \"{uniformName.Substring(uniformPart.Start, uniformPart.Count)}\" on a non-struct type when processing \"{uniformName}\"");
                    }

                    bool memberFound = false;
                    for (int memberIndex = 0; memberIndex < type.Members.Length; ++memberIndex)
                    {
                        var member = type.Members[memberIndex];
                        if (string.Compare(member.Name, 0, uniformName, uniformPart.Start, uniformPart.Count) == 0)
                        {
                            // Adjust offset and set new type
                            offset += member.Offset;
                            type    = member.Type;

                            memberFound = true;
                            break;
                        }
                    }

                    if (!memberFound)
                    {
                        throw new InvalidOperationException($"Couldn't find member \"{uniformName.Substring(uniformPart.Start, uniformPart.Count)}\" on struct type \"{type.Name}\" when processing \"{uniformName}\"");
                    }
                }

                // Apply indexer for arrays
                if (uniformPart.Indexer != -1)
                {
                    offset += (type.ElementSize + 15) / 16 * 16 * uniformPart.Indexer;
                }
            }

            // Check type
            if (type.Type != GetTypeFromActiveUniformType(uniformType))
            {
                throw new InvalidOperationException($"Uniform [{uniformName}] of type [{variable.Type.Type}] doesn't match OpenGL shader expected type [{GetTypeFromActiveUniformType(uniformType)}]");
            }

            // No need to compare last element padding.
            // TODO: In case of float1/float2 arrays (rare) it is quite non-optimal to do a CompareMemory
            //variable.Size = uniformSize * (uniformCount - 1) + elementSize;
            //constantBufferDescription.Members[indexOfUniform] = variable;

            Uniforms.Add(new Uniform
            {
                Type               = uniformType,
                Count              = uniformCount,
                CompareSize        = uniformSize + (uniformSize + 15) / 16 * 16 * (uniformCount - 1),
                ConstantBufferSlot = constantBuffer.SlotStart,
                Offset             = offset,
                UniformIndex       = GL.GetUniformLocation(ProgramId, uniformName)
            });
        }
Ejemplo n.º 24
0
        /// <summary>
        /// Converts the hlsl code into glsl and stores the result as plain text
        /// </summary>
        /// <param name="shaderSource">the hlsl shader</param>
        /// <param name="entryPoint">the entrypoint function name</param>
        /// <param name="stage">the shader pipeline stage</param>
        /// <param name="effectParameters"></param>
        /// <param name="reflection">the reflection gathered from the hlsl analysis</param>
        /// <param name="sourceFilename">the name of the source file</param>
        /// <returns></returns>
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, EffectCompilerParameters effectParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var shaderBytecodeResult = new ShaderBytecodeResult();

            byte[] rawData;

            var inputAttributeNames = new Dictionary <int, string>();
            var resourceBindings    = new Dictionary <string, int>();

            GlslShaderPlatform shaderPlatform;
            int shaderVersion;

            switch (effectParameters.Platform)
            {
            case GraphicsPlatform.OpenGL:
                shaderPlatform = GlslShaderPlatform.OpenGL;
                shaderVersion  = 410;
                break;

            case GraphicsPlatform.OpenGLES:
                shaderPlatform = GlslShaderPlatform.OpenGLES;
                shaderVersion  = 300;
                break;

            case GraphicsPlatform.Vulkan:
                shaderPlatform = GlslShaderPlatform.Vulkan;
                shaderVersion  = 450;
                break;

            default:
                throw new ArgumentOutOfRangeException("effectParameters.Platform");
            }

            var shader = Compile(shaderSource, entryPoint, stage, shaderPlatform, shaderVersion, shaderBytecodeResult, reflection, inputAttributeNames, resourceBindings, sourceFilename);

            if (shader == null)
            {
                return(shaderBytecodeResult);
            }

            if (effectParameters.Platform == GraphicsPlatform.OpenGLES)      // TODO: Add check to run on android only. The current version breaks OpenGL ES on windows.
            {
                //TODO: Remove this ugly hack!
                if (shaderSource.Contains($"Texture2D XenkoInternal_TextureExt0") && shader.Contains("uniform sampler2D"))
                {
                    if (shaderPlatform != GlslShaderPlatform.OpenGLES || shaderVersion != 300)
                    {
                        throw new Exception("Invalid GLES platform or version: require OpenGLES 300");
                    }

                    shader = shader.Replace("uniform sampler2D", "uniform samplerExternalOES");
                    shader = shader.Replace("#version 300 es", "#version 300 es\n#extension GL_OES_EGL_image_external_essl3 : require");
                }
            }

            if (effectParameters.Platform == GraphicsPlatform.Vulkan)
            {
                string inputFileExtension;
                switch (stage)
                {
                case ShaderStage.Vertex: inputFileExtension = ".vert"; break;

                case ShaderStage.Pixel: inputFileExtension = ".frag"; break;

                case ShaderStage.Geometry: inputFileExtension = ".geom"; break;

                case ShaderStage.Domain: inputFileExtension = ".tese"; break;

                case ShaderStage.Hull: inputFileExtension = ".tesc"; break;

                case ShaderStage.Compute: inputFileExtension = ".comp"; break;

                default:
                    shaderBytecodeResult.Error("Unknown shader profile");
                    return(shaderBytecodeResult);
                }

                var inputFileName  = Path.ChangeExtension(Path.GetTempFileName(), inputFileExtension);
                var outputFileName = Path.ChangeExtension(inputFileName, ".spv");

                // Write shader source to disk
                File.WriteAllBytes(inputFileName, Encoding.ASCII.GetBytes(shader));

                // Run shader compiler
                var filename = Platform.Type == PlatformType.Windows ? "glslangValidator.exe" : "glslangValidator";
                ShellHelper.RunProcessAndRedirectToLogger(filename, $"-V -o {outputFileName} {inputFileName}", null, shaderBytecodeResult);

                if (!File.Exists(outputFileName))
                {
                    shaderBytecodeResult.Error("Failed to generate SPIR-V from GLSL");
                    return(shaderBytecodeResult);
                }

                // Read compiled shader
                var shaderBytecodes = new ShaderInputBytecode
                {
                    InputAttributeNames = inputAttributeNames,
                    ResourceBindings    = resourceBindings,
                    Data = File.ReadAllBytes(outputFileName),
                };

                using (var stream = new MemoryStream())
                {
                    BinarySerialization.Write(stream, shaderBytecodes);
                    rawData = stream.ToArray();
                }

                // Cleanup temp files
                File.Delete(inputFileName);
                File.Delete(outputFileName);
            }
            else
            {
                // store string on OpenGL platforms
                rawData = Encoding.UTF8.GetBytes(shader);
            }

            var bytecodeId = ObjectId.FromBytes(rawData);
            var bytecode   = new ShaderBytecode(bytecodeId, rawData);

            bytecode.Stage = stage;

            shaderBytecodeResult.Bytecode = bytecode;

            return(shaderBytecodeResult);
        }
Ejemplo n.º 25
0
        private static void CleanupReflection(EffectReflection reflection)
        {
            // TODO GRAPHICS REFACTOR we hardcode several resource group we want to preserve or optimize completly
            // Somehow this should be handled some other place (or probably we shouldn't cleanup reflection at all?)
            bool hasMaterialGroup = false;
            bool hasLightingGroup = false;

            foreach (var resourceBinding in reflection.ResourceBindings)
            {
                if (resourceBinding.Stage != ShaderStage.None)
                {
                    if (!hasLightingGroup && resourceBinding.ResourceGroup == "PerLighting")
                    {
                        hasLightingGroup = true;
                    }
                    else if (!hasMaterialGroup && resourceBinding.ResourceGroup == "PerMaterial")
                    {
                        hasMaterialGroup = true;
                    }
                }
            }

            var usedConstantBuffers = new HashSet <string>();

            for (int i = reflection.ResourceBindings.Count - 1; i >= 0; i--)
            {
                var resourceBinding = reflection.ResourceBindings[i];

                // Do not touch anything if there is logical groups
                // TODO: We can do better than that: remove only if the full group can be optimized away
                if (resourceBinding.LogicalGroup != null)
                {
                    continue;
                }

                if (resourceBinding.Stage == ShaderStage.None && !(hasMaterialGroup && resourceBinding.ResourceGroup == "PerMaterial") && !(hasLightingGroup && resourceBinding.ResourceGroup == "PerLighting"))
                {
                    reflection.ResourceBindings.RemoveAt(i);
                }
                else if (resourceBinding.Class == EffectParameterClass.ConstantBuffer ||
                         resourceBinding.Class == EffectParameterClass.TextureBuffer)
                {
                    // Mark associated cbuffer/tbuffer as used
                    usedConstantBuffers.Add(resourceBinding.KeyInfo.KeyName);
                }
            }

            // Remove unused cbuffer
            for (int i = reflection.ConstantBuffers.Count - 1; i >= 0; i--)
            {
                var cbuffer = reflection.ConstantBuffers[i];

                // Do not touch anything if there is logical groups
                // TODO: We can do better than that: remove only if the full group can be optimized away
                var hasLogicalGroup = false;
                foreach (var member in cbuffer.Members)
                {
                    if (member.LogicalGroup != null)
                    {
                        hasLogicalGroup = true;
                        break;
                    }
                }

                if (hasLogicalGroup)
                {
                    continue;
                }

                if (!usedConstantBuffers.Contains(cbuffer.Name))
                {
                    reflection.ConstantBuffers.RemoveAt(i);
                }
            }
        }
Ejemplo n.º 26
0
        private static void CleanupReflection(EffectReflection reflection)
        {
            for (int i = reflection.ConstantBuffers.Count - 1; i >= 0; i--)
            {
                var cBuffer = reflection.ConstantBuffers[i];
                if (cBuffer.Stage == ShaderStage.None)
                {
                    reflection.ConstantBuffers.RemoveAt(i);
                }
            }

            for (int i = reflection.ResourceBindings.Count - 1; i >= 0; i--)
            {
                var resourceBinding = reflection.ResourceBindings[i];
                if (resourceBinding.Stage == ShaderStage.None)
                {
                    reflection.ResourceBindings.RemoveAt(i);
                }
            }
        }
Ejemplo n.º 27
0
        /// <summary>
        /// Converts the hlsl code into glsl and stores the result as plain text
        /// </summary>
        /// <param name="shaderSource">the hlsl shader</param>
        /// <param name="entryPoint">the entrypoint function name</param>
        /// <param name="stage">the shader pipeline stage</param>
        /// <param name="effectParameters"></param>
        /// <param name="reflection">the reflection gathered from the hlsl analysis</param>
        /// <param name="sourceFilename">the name of the source file</param>
        /// <returns></returns>
        public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, EffectCompilerParameters effectParameters, EffectReflection reflection, string sourceFilename = null)
        {
            var shaderBytecodeResult = new ShaderBytecodeResult();
            byte[] rawData;

            var inputAttributeNames = new Dictionary<int, string>();
            var resourceBindings = new Dictionary<string, int>();

            GlslShaderPlatform shaderPlatform;
            int shaderVersion;

            switch (effectParameters.Platform)
            {
                case GraphicsPlatform.OpenGL:
                    shaderPlatform = GlslShaderPlatform.OpenGL;
                    shaderVersion = 410;
                    break;
                case GraphicsPlatform.OpenGLES:
                    shaderPlatform = GlslShaderPlatform.OpenGLES;
                    shaderVersion = effectParameters.Profile >= GraphicsProfile.Level_10_0 ? 300 : 100;
                    break;
                case GraphicsPlatform.Vulkan:
                    shaderPlatform = GlslShaderPlatform.Vulkan;
                    shaderVersion = 450;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("effectParameters.Platform");
            }

            var shader = Compile(shaderSource, entryPoint, stage, shaderPlatform, shaderVersion, shaderBytecodeResult, reflection, inputAttributeNames, resourceBindings, sourceFilename);

            if (shader == null)
                return shaderBytecodeResult;

            if (effectParameters.Platform == GraphicsPlatform.OpenGLES)
            {
                // store both ES 2 and ES 3 on OpenGL ES platforms
                var shaderBytecodes = new ShaderLevelBytecode();
                if (effectParameters.Profile >= GraphicsProfile.Level_10_0)
                {
                    shaderBytecodes.DataES3 = shader;
                    shaderBytecodes.DataES2 = null;
                }
                else
                {
                    shaderBytecodes.DataES2 = shader;
                    shaderBytecodes.DataES3 = Compile(shaderSource, entryPoint, stage, GlslShaderPlatform.OpenGLES, 300, shaderBytecodeResult, reflection, inputAttributeNames, resourceBindings, sourceFilename);
                }
                using (var stream = new MemoryStream())
                {
                    BinarySerialization.Write(stream, shaderBytecodes);
#if !SILICONSTUDIO_RUNTIME_CORECLR && !SILICONSTUDIO_PLATFORM_UWP
                    rawData = stream.GetBuffer();
#else
// FIXME: Manu: The call to "ToArray()" might be slower than "GetBuffer()"
                    rawData = stream.ToArray();
#endif
                }
            }
#if !SILICONSTUDIO_RUNTIME_CORECLR && !SILICONSTUDIO_PLATFORM_UWP
            else if (effectParameters.Platform == GraphicsPlatform.Vulkan)
            {
                string inputFileExtension;
                switch (stage)
                {
                    case ShaderStage.Vertex: inputFileExtension = ".vert"; break;
                    case ShaderStage.Pixel: inputFileExtension = ".frag"; break;
                    case ShaderStage.Geometry: inputFileExtension = ".geom"; break;
                    case ShaderStage.Domain: inputFileExtension = ".tese"; break;
                    case ShaderStage.Hull: inputFileExtension = ".tesc"; break;
                    case ShaderStage.Compute: inputFileExtension = ".comp"; break;
                    default:
                        shaderBytecodeResult.Error("Unknown shader profile");
                        return shaderBytecodeResult;
                }

                var inputFileName = Path.ChangeExtension(Path.GetTempFileName(), inputFileExtension);
                var outputFileName = Path.ChangeExtension(inputFileName, ".spv");

                // Write shader source to disk
                File.WriteAllBytes(inputFileName, Encoding.ASCII.GetBytes(shader));

                // Run shader compiler
                var filename = Platform.Type == PlatformType.Windows ? "glslangValidator.exe" : "glslangValidator";
                ShellHelper.RunProcessAndRedirectToLogger(filename, $"-V -o {outputFileName} {inputFileName}", null, shaderBytecodeResult);

                if (!File.Exists(outputFileName))
                {
                    shaderBytecodeResult.Error("Failed to generate SPIR-V from GLSL");
                    return shaderBytecodeResult;
                }

                // Read compiled shader
                var shaderBytecodes = new ShaderInputBytecode
                {
                    InputAttributeNames = inputAttributeNames,
                    ResourceBindings = resourceBindings,
                    Data = File.ReadAllBytes(outputFileName),
                };

                using (var stream = new MemoryStream())
                {
                    BinarySerialization.Write(stream, shaderBytecodes);
                    rawData = stream.ToArray();
                }

                // Cleanup temp files
                File.Delete(inputFileName);
                File.Delete(outputFileName);
            }
#endif
            else
            {
                // store string on OpenGL platforms
                rawData = Encoding.UTF8.GetBytes(shader);
            }
            
            var bytecodeId = ObjectId.FromBytes(rawData);
            var bytecode = new ShaderBytecode(bytecodeId, rawData);
            bytecode.Stage = stage;

            shaderBytecodeResult.Bytecode = bytecode;
            
            return shaderBytecodeResult;
        }