/// <summary> /// Processes raw shader source into a new <see cref="ShaderSource"/> object. /// </summary> /// <param name="manager">The <see cref="ContentManager"/> that is loading the shader source.</param> /// <param name="metadata">The content processor metadata for the shader source that is being loaded.</param> /// <param name="source">The raw shader source to process.</param> /// <returns>A <see cref="ShaderSource"/> object that represents the processed shader source.</returns> public static ShaderSource ProcessRawSource(ContentManager manager, IContentProcessorMetadata metadata, String source) { var ssmd = new ShaderSourceMetadata(); return(ProcessInternal(ssmd, source, (line, output) => { if (ProcessIncludeDirective(manager, metadata, line, output, ssmd)) { return true; } if (ProcessIncludeResourceDirective(manager, metadata, line, output, ssmd)) { return true; } if (ProcessIfVerDirective(manager, metadata, line, output, ssmd)) { return true; } if (ProcessSamplerDirective(manager, metadata, line, output, ssmd)) { return true; } if (ProcessParamDirective(manager, metadata, line, output, ssmd)) { return true; } if (ProcessCameraDirective(manager, metadata, line, output, ssmd)) { return true; } return false; })); }
/// <summary> /// Processes #camera directives. /// </summary> private static Boolean ProcessCameraDirective(ContentManager manager, IContentProcessorMetadata metadata, String line, StringBuilder output, ShaderSourceMetadata ssmd) { var cameraMatch = regexCameraDirective.Match(line); if (cameraMatch.Success) { var parameter = cameraMatch.Groups["parameter"].Value; var uniform = cameraMatch.Groups["uniform"].Value; ssmd.AddCameraHint(parameter, uniform); return(true); } return(false); }
/// <summary> /// Processes #param directives. /// </summary> private static Boolean ProcessParamDirective(ContentManager manager, IContentProcessorMetadata metadata, String line, StringBuilder output, ShaderSourceMetadata ssmd) { var paramMatch = regexParamDirective.Match(line); if (paramMatch.Success) { var parameter = paramMatch.Groups["parameter"].Value; ssmd.AddParameterHint(parameter); return(true); } return(false); }
/// <summary> /// Processes #sampler directives. /// </summary> private static Boolean ProcessSamplerDirective(ContentManager manager, IContentProcessorMetadata metadata, String line, StringBuilder output, ShaderSourceMetadata ssmd) { var samplerMatch = regexSamplerDirective.Match(line); if (samplerMatch.Success) { var sampler = Int32.Parse(samplerMatch.Groups["sampler"].Value); var uniform = samplerMatch.Groups["uniform"].Value; ssmd.AddPreferredSamplerIndex(uniform, sampler); return(true); } return(false); }
/// <summary> /// Processes #ifver directives. /// </summary> private static Boolean ProcessIfVerDirective(ContentManager manager, IContentProcessorMetadata metadata, String line, StringBuilder output, ShaderSourceMetadata ssmd) { var ifVerMatch = regexIfVerDirective.Match(line); if (ifVerMatch.Success) { var source = ifVerMatch.Groups["source"].Value; var dirVersionIsGLES = !String.IsNullOrEmpty(ifVerMatch.Groups["gles"].Value); var dirVersionMajor = Int32.Parse(ifVerMatch.Groups["version_maj"].Value); var dirVersionMinor = Int32.Parse(ifVerMatch.Groups["version_min"].Value); var dirVersion = new Version(dirVersionMajor, dirVersionMinor); var dirSatisfied = false; var uvVersionIsGLES = gl.IsGLES; var uvVersionMajor = gl.MajorVersion; var uvVersionMinor = gl.MinorVersion; var uvVersion = new Version(uvVersionMajor, uvVersionMinor); if (dirVersionIsGLES != uvVersionIsGLES) { return(true); } switch (ifVerMatch.Groups["op"].Value) { case "ifver": dirSatisfied = (uvVersion == dirVersion); break; case "ifver_lt": dirSatisfied = (uvVersion < dirVersion); break; case "ifver_lte": dirSatisfied = (uvVersion <= dirVersion); break; case "ifver_gt": dirSatisfied = (uvVersion > dirVersion); break; case "ifver_gte": dirSatisfied = (uvVersion >= dirVersion); break; } if (dirSatisfied) { var includedSource = ProcessRawSource(manager, metadata, source); ssmd.Concat(includedSource.Metadata); output.Append(includedSource.Source); if (!includedSource.Source.EndsWith("\n")) { output.AppendLine(); } } return(true); } return(false); }
/// <summary> /// Processes #include directives. /// </summary> private static Boolean ProcessIncludeDirective(ContentManager manager, IContentProcessorMetadata metadata, String line, StringBuilder output, ShaderSourceMetadata ssmd) { var includeMatch = regexIncludeDirective.Match(line); if (includeMatch.Success) { if (manager == null || metadata == null) { throw new InvalidOperationException(OpenGLStrings.CannotIncludeShaderHeadersInStream); } var includePath = includeMatch.Groups["file"].Value; includePath = ContentManager.NormalizeAssetPath(Path.Combine(Path.GetDirectoryName(metadata.AssetPath), includePath)); metadata.AddAssetDependency(includePath); var includeSrc = manager.Load <ShaderSource>(includePath, metadata.AssetDensity, false, metadata.IsLoadedFromSolution); ssmd.Concat(includeSrc.Metadata); output.AppendLine(includeSrc.Source); return(true); } return(false); }
/// <summary> /// Processes #includeres directives. /// </summary> private static Boolean ProcessIncludeResourceDirective(ContentManager manager, IContentProcessorMetadata metadata, String line, StringBuilder output, ShaderSourceMetadata ssmd) { var includeResourceMatch = regexincludeResourceDirective.Match(line); if (includeResourceMatch.Success) { var includeResource = includeResourceMatch.Groups["resource"].Value; var includeAsm = includeResourceMatch.Groups["asm"]?.Value ?? "entry"; var asm = String.Equals("entry", includeAsm, StringComparison.OrdinalIgnoreCase) ? Assembly.GetEntryAssembly() : String.Equals("executing", includeAsm, StringComparison.OrdinalIgnoreCase) ? Assembly.GetExecutingAssembly() : null; var info = asm.GetManifestResourceInfo(includeResource); if (info == null) { throw new InvalidOperationException(OpenGLStrings.InvalidIncludedResource.Format(includeResource)); } using (var resStream = asm.GetManifestResourceStream(includeResource)) using (var resReader = new StreamReader(resStream)) { var includeSrc = ProcessRawSource(manager, metadata, resReader.ReadToEnd()); ssmd.Concat(includeSrc.Metadata); output.Append(includeSrc.Source); if (!includeSrc.Source.EndsWith("\n")) { output.AppendLine(); } } return(true); } return(false); }
/// <summary> /// Initializes a new instance of the <see cref="ShaderSource"/> class. /// </summary> /// <param name="source">The source code for this shader.</param> /// <param name="metadata">The metadata for this shader.</param> private ShaderSource(String source, ShaderSourceMetadata metadata) { this.Source = source; this.Metadata = metadata; }
/// <summary> /// Processes #extern directives. /// </summary> private static Boolean ProcessExternDirective(String line, StringBuilder output, ShaderSourceMetadata ssmd, Dictionary <String, String> externs) { var externMatch = regexExternDirective.Match(line); if (externMatch.Success) { var externName = externMatch.Groups["name"].Value; if (String.IsNullOrWhiteSpace(externName)) { throw new InvalidOperationException(OpenGLStrings.ShaderExternHasInvalidName); } var externValue = String.Empty; if (externs?.TryGetValue(externName, out externValue) ?? false) { output.AppendLine($"#define {externName} {externValue}"); } return(true); } return(false); }
/// <summary> /// Compiles the specified shader. /// </summary> /// <param name="shader">The shader handle.</param> /// <param name="source">The shader source.</param> /// <param name="log">The compiler log.</param> /// <param name="ssmd">The source metadata for this shader.</param> /// <returns>true if the shader compiled; otherwise, false.</returns> public static Boolean Compile(UInt32 shader, ShaderSource[] source, out String log, out ShaderSourceMetadata ssmd) { Contract.Require(source, nameof(source)); ssmd = new ShaderSourceMetadata(); foreach (var s in source) { ssmd.Concat(s.Metadata); } unsafe { var pSource = stackalloc IntPtr[source.Length]; var pLength = stackalloc Int32[source.Length]; for (var i = 0; i < source.Length; i++) { pSource[i] = Marshal.StringToHGlobalAnsi(source[i].Source); pLength[i] = source[i].Source.Length; } try { gl.ShaderSource(shader, source.Length, (sbyte **)pSource, pLength); gl.ThrowIfError(); gl.CompileShader(shader); gl.ThrowIfError(); gl.GetShaderInfoLog(shader, out log); gl.ThrowIfError(); } finally { for (var i = 0; i < source.Length; i++) { Marshal.FreeHGlobal(pSource[i]); } } var status = gl.GetShaderi(shader, gl.GL_COMPILE_STATUS); gl.ThrowIfError(); return(status != 0); } }
/// <summary> /// Initializes a new instance of the OpenGLShaderProgram class. /// </summary> /// <param name="uv">The Ultraviolet context.</param> /// <param name="vertexShader">The program's vertex shader.</param> /// <param name="fragmentShader">The program's fragment shader.</param> /// <param name="programOwnsShaders">A value indicating whether the program owns the shader objects.</param> public OpenGLShaderProgram(UltravioletContext uv, OpenGLVertexShader vertexShader, OpenGLFragmentShader fragmentShader, Boolean programOwnsShaders) : base(uv) { Contract.Require(vertexShader, nameof(vertexShader)); Contract.Require(fragmentShader, nameof(fragmentShader)); Ultraviolet.ValidateResource(vertexShader); Ultraviolet.ValidateResource(fragmentShader); this.vertexShader = vertexShader; this.fragmentShader = fragmentShader; this.programOwnsShaders = programOwnsShaders; var concatenatedSourceMetadata = new ShaderSourceMetadata(); concatenatedSourceMetadata.Concat(vertexShader.ShaderSourceMetadata); concatenatedSourceMetadata.Concat(fragmentShader.ShaderSourceMetadata); var program = 0u; uv.QueueWorkItem(state => { program = gl.CreateProgram(); gl.ThrowIfError(); gl.AttachShader(program, vertexShader.OpenGLName); gl.ThrowIfError(); gl.AttachShader(program, fragmentShader.OpenGLName); gl.ThrowIfError(); gl.LinkProgram(program); gl.ThrowIfError(); var log = gl.GetProgramInfoLog(program); gl.ThrowIfError(); var status = gl.GetProgrami(program, gl.GL_LINK_STATUS); gl.ThrowIfError(); var attributeCount = gl.GetProgrami(program, gl.GL_ACTIVE_ATTRIBUTES); gl.ThrowIfError(); unsafe { var namebuf = Marshal.AllocHGlobal(256); try { for (int i = 0; i < attributeCount; i++) { var attrNameLen = 0; var attrName = default(String); var attrSize = 0; var attrType = 0u; gl.GetActiveAttrib(program, (uint)i, 256, &attrNameLen, &attrSize, &attrType, (sbyte *)namebuf); gl.ThrowIfError(); attrName = Marshal.PtrToStringAnsi(namebuf); var location = gl.GetAttribLocation(program, attrName); gl.ThrowIfError(); attributeLocations[attrName] = location; attributeTypes[attrName] = attrType; } } finally { Marshal.FreeHGlobal(namebuf); } } if (status == 0) { throw new InvalidOperationException(log); } }).Wait(); this.program = program; this.uniforms = CreateUniformCollection(concatenatedSourceMetadata); }
/// <summary> /// Creates the effect pass' collection of uniforms. /// </summary> /// <param name="ssmd">The source metadata for this program.</param> /// <returns>The collection of uniforms that was created.</returns> private OpenGLShaderUniformCollection CreateUniformCollection(ShaderSourceMetadata ssmd) { var result = Ultraviolet.QueueWorkItem(state => { var programObject = ((OpenGLShaderProgram)state); var program = programObject.program; var uniforms = new List <OpenGLShaderUniform>(); var samplerCount = 0; var count = gl.GetProgrami(program, gl.GL_ACTIVE_UNIFORMS); gl.ThrowIfError(); for (uint i = 0; i < count; i++) { var type = 0u; var name = gl.GetActiveUniform(program, i, out type); gl.ThrowIfError(); var location = gl.GetUniformLocation(program, name); gl.ThrowIfError(); var isSampler = false; switch (type) { case gl.GL_SAMPLER_1D: case gl.GL_SAMPLER_1D_ARRAY: case gl.GL_SAMPLER_1D_ARRAY_SHADOW: case gl.GL_SAMPLER_1D_SHADOW: case gl.GL_SAMPLER_2D: case gl.GL_SAMPLER_2D_ARRAY: case gl.GL_SAMPLER_2D_ARRAY_SHADOW: case gl.GL_SAMPLER_2D_MULTISAMPLE: case gl.GL_SAMPLER_2D_MULTISAMPLE_ARRAY: case gl.GL_SAMPLER_2D_RECT: case gl.GL_SAMPLER_2D_RECT_SHADOW: case gl.GL_SAMPLER_2D_SHADOW: case gl.GL_SAMPLER_3D: case gl.GL_SAMPLER_CUBE: case gl.GL_SAMPLER_CUBE_MAP_ARRAY: case gl.GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: case gl.GL_SAMPLER_CUBE_SHADOW: isSampler = true; break; } var sampler = isSampler ? samplerCount++ : -1; if (isSampler && ssmd.PreferredSamplerIndices.ContainsKey(name)) { samplerCount = ssmd.PreferredSamplerIndices[name]; sampler = samplerCount++; } uniforms.Add(new OpenGLShaderUniform(programObject.Ultraviolet, name, type, program, location, sampler)); } return(uniforms); }, this).Result; // Validation: make sure all preferred samplers correspond to an actual uniform var missingUniform = ssmd.PreferredSamplerIndices.Keys.Where(x => !result.Where(y => String.Equals(y.Name, x, StringComparison.Ordinal)).Any()).FirstOrDefault(); if (missingUniform != null) { throw new ArgumentException(OpenGLStrings.SamplerDirectiveInvalidUniform.Format(missingUniform)); } return(new OpenGLShaderUniformCollection(result)); }