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(); }
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) }); }
/// <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); }
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(); }
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."); } }
/// <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); }