예제 #1
0
        private EffectProgram(GraphicsDevice device, EffectBytecode bytecode)
            : base(device)
        {
            effectBytecode = bytecode;

            // make a copy of the effect's reflection before modifying it.
            Reflection = new EffectReflection
            {
                // The members that are not modified and can be shallowly copied.
                SamplerStates = effectBytecode.Reflection.SamplerStates,
                ShaderStreamOutputDeclarations = effectBytecode.Reflection.ShaderStreamOutputDeclarations,
                StreamOutputRasterizedStream = effectBytecode.Reflection.StreamOutputRasterizedStream,
                StreamOutputStrides = effectBytecode.Reflection.StreamOutputStrides,

                // The members that are modified and should be deeply copied.
                ConstantBuffers = effectBytecode.Reflection.ConstantBuffers.Select(cb => cb.Clone()).ToList(),
                ResourceBindings = new List<EffectParameterResourceData>(effectBytecode.Reflection.ResourceBindings),
            };

            CreateShaders();
        }
예제 #2
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)
            });
        }
예제 #3
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);
        }
예제 #4
0
        internal EffectProgram(GraphicsDevice device, EffectBytecode bytecode, bool emulateDepthClamp) : base(device)
        {
            effectBytecode = bytecode;
            this.emulateDepthClamp = emulateDepthClamp;

            // TODO OPENGL currently we modify the reflection info; need to find a better way to deal with that
            Reflection = effectBytecode.Reflection;
            CreateShaders();
        }
예제 #5
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
                });
                constantBufferDescription.Size += uniformSize*uniformCount;
            }
            else
            {
                // check that this uniform is in a constant buffer
                foreach (var constantBuffer in effectReflection.ConstantBuffers)
                {
                    foreach (var member in constantBuffer.Members)
                    {
                        if (member.Param.RawName.Equals(uniformName))
                            return;
                    }
                }
                throw new Exception("The uniform value " + uniformName + " is defined outside of a uniform block in OpenGL ES 3, which is not supported by the engine.");
            }
        }
예제 #6
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, XkActiveUniformBlocks, out uniformBlockCount);

            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(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_XENKO_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);

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

#if SILICONSTUDIO_XENKO_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;
                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(resourceId, activeUniformIndex);
#else
                    ActiveUniformType uniformType;
                    int uniformCount;
                    int length;
                    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_XENKO_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_XENKO_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);
        }