Exemplo n.º 1
0
        /// <summary>
        /// Create OpenGL object. Standard object constructor for ProtoFX.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="debugging"></param>
        public GLVertinput(Compiler.Block block, Dict scene, bool debugging)
            : base(block.Name, block.Anno)
        {
            var err = new CompileException($"vertinput '{Name}'");

            // CREATE OPENGL OBJECT
            glname = GL.GenVertexArray();
            GL.BindVertexArray(glname);

            int numAttr = 0;

            foreach (var cmd in block["attr"])
            {
                Attach(numAttr++, cmd, scene, err | $"command '{cmd.Text}'");
            }

            // if errors occurred throw exception
            if (err.HasErrors)
            {
                throw err;
            }

            // unbind object and check for errors
            GL.BindVertexArray(0);
            if (HasErrorOrGlError(err, block))
            {
                throw err;
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Create OpenGL object. Standard object constructor for ProtoFX.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="debugging"></param>
        public GLSampler(Compiler.Block block, Dict scene, bool debugging)
            : base(block.Name, block.Anno)
        {
            var err = new CompileException($"sampler '{Name}'");

            // PARSE ARGUMENTS
            Cmds2Fields(block, err);

            // CREATE OPENGL OBJECT
            glname = GL.GenSampler();
            int mini  = (int)Minfilter;
            int magi  = (int)Magfilter;
            int wrapi = (int)Wrap;

            GL.SamplerParameterI(glname, SamplerParameterName.TextureMinFilter, ref mini);
            GL.SamplerParameterI(glname, SamplerParameterName.TextureMagFilter, ref magi);
            GL.SamplerParameterI(glname, SamplerParameterName.TextureWrapR, ref wrapi);
            GL.SamplerParameterI(glname, SamplerParameterName.TextureWrapS, ref wrapi);
            GL.SamplerParameterI(glname, SamplerParameterName.TextureWrapT, ref wrapi);

            HasErrorOrGlError(err, block);
            if (err.HasErrors)
            {
                throw err;
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Create OpenGL object. Standard object constructor for ProtoFX.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="debugging"></param>
        public GLVertoutput(Compiler.Block block, Dict scene, bool debugging)
            : base(block.Name, block.Anno)
        {
            var err = new CompileException($"vertoutput '{Name}'");

            // PARSE ARGUMENTS
            Cmds2Fields(block, err);

            // CREATE OPENGL OBJECT
            glname = GL.GenTransformFeedback();
            GL.BindTransformFeedback(TransformFeedbackTarget.TransformFeedback, glname);

            // parse commands
            int numbindings = 0;

            foreach (var cmd in block["buff"])
            {
                Attach(numbindings++, cmd, scene, err | $"command '{cmd.Text}'");
            }

            // if errors occurred throw exception
            if (err.HasErrors)
            {
                throw err;
            }

            // unbind object and check for errors
            GL.BindTransformFeedback(TransformFeedbackTarget.TransformFeedback, 0);
            if (HasErrorOrGlError(err, block))
            {
                throw err;
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Create a new external class instance by processing the specified compiler block.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="cmd"></param>
        /// <param name="err"></param>
        /// <returns></returns>
        private object CreateInstance(Compiler.Block block, Compiler.Command cmd, Dict scene, CompileException err)
        {
            // check if the command is valid
            if (cmd.ArgCount < 2)
            {
                err.Add("'class' command must specify a class name.", block);
                return(null);
            }

            // create OpenGL name lookup dictionary
            var glNames = new Dictionary <string, int>(scene.Count);

            scene.Keys.ForEach(scene.Values, (k, v) => glNames.Add(k, v.glname));

            // create main class from compiled files
            var classname = cmd[1].Text;
            var instance  = CompilerResults.CompiledAssembly.CreateInstance(
                classname, false, BindingFlags.Default, null,
                new object[] { block.Name, ToDict(block), glNames }, CultureInfo.CurrentCulture, null);

            if (instance == null)
            {
                throw err.Add($"Main class '{classname}' could not be found.", cmd);
            }

            InvokeMethod <List <string> >(instance, "GetErrors")?.ForEach(msg => err.Add(msg, cmd));

            return(instance);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Create a new external class instance by processing the specified compiler block.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="err"></param>
        /// <returns></returns>
        internal static object CreateInstance(Compiler.Block block, Dict scene, CompileException err)
        {
            // GET CLASS COMMAND
            var cmds = block["class"].ToList();

            if (cmds.Count == 0)
            {
                err.Add("Instance must specify a 'class' command " +
                        "(e.g., class csharp_name class_name).", block);
                return(null);
            }
            var cmd = cmds.First();

            // check command
            if (cmd.ArgCount < 1)
            {
                err.Add("'class' command must specify a csharp object name.", cmd);
                return(null);
            }

            // FIND CSHARP CLASS DEFINITION
            var csharp = scene.GetValueOrDefault <GLCsharp>(cmd[0].Text);

            if (csharp == null)
            {
                err.Add($"Could not find csharp code '{cmd[0].Text}' of command '{cmd.Text}' ", cmd);
                return(null);
            }

            // INSTANTIATE CSHARP CLASS
            return(csharp.CreateInstance(block, cmd, scene, err));
        }
Exemplo n.º 6
0
        /// <summary>
        /// Convert code block commands to dictionary.
        /// </summary>
        /// <param name="block"></param>
        /// <returns></returns>
        private Dictionary <string, string[]> ToDict(Compiler.Block block)
        {
            // convert to dictionary of string arrays
            var dict = new Dictionary <string, string[]>();

            // add commands to dictionary
            block.ForEach(cmd => dict.Add(cmd.Name, cmd.Select(x => x.Text).ToArray()));
            return(dict);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Check for compiler and OpenGL errors.
        /// </summary>
        /// <param name="err"></param>
        /// <param name="block"></param>
        /// <returns></returns>
        static protected bool HasErrorOrGlError(CompileException err, Compiler.Block block)
        {
            var errcode = GL.GetError();

            if (errcode != ErrorCode.NoError)
            {
                err.Add($"OpenGL error '{errcode}' occurred.", block);
                return(true);
            }
            return(err.HasErrors);
        }
Exemplo n.º 8
0
        private GLShader Attach(Compiler.Block block, string shadername, Dict classes,
                                CompileException err)
        {
            GLShader obj = null;

            // get shader from class list
            if (shadername != null && classes.TryGetValue(shadername, out obj, block, err))
            {
                GL.UseProgramStages(glname, ShaderType2ShaderBit(obj.ShaderType), obj.glname);
            }

            return(obj);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Create OpenGL object. Standard object constructor for ProtoFX.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="debugging"></param>
        public GLCsharp(Compiler.Block block, Dict scene, bool debugging)
            : base(block.Name, block.Anno)
        {
            var err = new CompileException($"csharp '{Name}'");

            // PARSE ARGUMENTS
            Cmds2Fields(block, err);

            // check for errors
            if (err.HasErrors)
            {
                throw err;
            }
            if (File == null || File.Length == 0)
            {
                return;
            }

            // LOAD ADDITIONAL ASSEMBLIES
            if (Assembly != null)
            {
                foreach (var assemblypath in Assembly)
                {
                    try {
                        System.Reflection.Assembly.LoadFrom(assemblypath);
                    } catch (FileNotFoundException) {
                        err.Add($"Assembly file '{assemblypath}' cound not be found.", block);
                    } catch (FileLoadException) {
                        err.Add($"Assembly '{assemblypath}' cound not be loaded.", block);
                    } catch {
                        err.Add($"Unknown exception when loading assembly '{assemblypath}'.", block);
                    }
                }
            }

            // replace placeholders with actual path
            var dir      = Path.GetDirectoryName(block.Filename) + Path.DirectorySeparatorChar;
            var filepath = ProcessPaths(dir, File);

            // COMPILE FILES
            CompilerResults = CompileFilesOrSource(filepath.ToArray(), Version, block, err);

            // check for errors
            if (err.HasErrors)
            {
                throw err;
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Create OpenGL object. Standard object constructor for ProtoFX.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="debugging"></param>
        public GLTech(Compiler.Block block, Dict scene, bool debugging)
            : base(block.Name, block.Anno, 309, debugging)
        {
            var err = new CompileException($"tech '{Name}'");

            // PARSE COMMANDS
            ParsePasses(ref init, block, scene, err);
            ParsePasses(ref passes, block, scene, err);
            ParsePasses(ref uninit, block, scene, err);

            // IF THERE ARE ERRORS THROW AND EXCEPTION
            if (err.HasErrors)
            {
                throw err;
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// Parse commands in block.
        /// </summary>
        /// <param name="list"></param>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="err"></param>
        private void ParsePasses(ref List <GLPass> list, Compiler.Block block, Dict scene,
                                 CompileException err)
        {
            var cmdName = ReferenceEquals(list, init)
                ? "init"
                : ReferenceEquals(list, passes) ? "pass" : "uninit";

            foreach (var cmd in block[cmdName])
            {
                if (scene.TryGetValue(cmd[0].Text, out GLPass pass, block,
                                      err | $"command '{cmd.Text}'"))
                {
                    list.Add(pass);
                }
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Create OpenGL object. Standard object constructor for ProtoFX.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="debugging"></param>
        public GLBuffer(Compiler.Block block, Dict scene, bool debugging)
            : base(block.Name, block.Anno)
        {
            var err = new CompileException($"buffer '{block.Name}'");

            // PARSE COMMANDS AND CONVERT THEM TO CLASS FIELDS
            Cmds2Fields(block, err);

            // PARSE COMMANDS
            var datalist = new List <byte[]>();

            foreach (var cmd in block["txt"])
            {
                datalist.Add(LoadText(cmd, scene, err | $"command {cmd.Name} 'txt'"));
            }

            foreach (var cmd in block["xml"])
            {
                datalist.Add(LoadXml(cmd, scene, err | $"command {cmd.Name} 'xml'"));
            }

            // merge data into a single array
            var iter = datalist.Cat();
            var data = iter.Take(Size == 0 ? iter.Count() : Size).ToArray();

            if (Size == 0)
            {
                Size = data.Length;
            }

            // CONVERT DATA
            var clazz = block["class"].FirstOrDefault();

            if (clazz != null && Size > 0)
            {
                var converter = GLCsharp.GetMethod(clazz, scene, err);
                data = (byte[])converter?.Invoke(null, new[] { data });
            }

            // CREATE OPENGL OBJECT
            CreateBuffer(data);
            if (HasErrorOrGlError(err, block))
            {
                throw err;
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Create OpenGL object specifying the referenced scene objects directly.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="glbuff"></param>
        /// <param name="glimg"></param>
        public GLTexture(Compiler.Block block, Dict scene, GLBuffer glbuff, GLImage glimg)
            : base(block.Name, block.Anno)
        {
            var err = new CompileException($"texture '{Name}'");

            // PARSE ARGUMENTS
            Cmds2Fields(block, err);

            // set name
            glBuff = glbuff;
            glImg  = glimg;

            // GET REFERENCES
            if (Buff != null)
            {
                scene.TryGetValue(Buff, out glBuff, block, err);
            }
            if (Img != null)
            {
                scene.TryGetValue(Img, out glImg, block, err);
            }
            if (glBuff != null && glImg != null)
            {
                err.Add("Only an image or a buffer can be bound to a texture object.", block);
            }
            if (glBuff == null && glImg == null)
            {
                err.Add("Ether an image or a buffer has to be bound to a texture object.", block);
            }

            // IF THERE ARE ERRORS THROW AND EXCEPTION
            if (err.HasErrors)
            {
                throw err;
            }

            // INCASE THIS IS A TEXTURE OBJECT
            Link(block.Filename, block.LineInFile, err);
            if (HasErrorOrGlError(err, block))
            {
                throw err;
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Process object block and try to convert commands to class internal fields.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="err"></param>
        protected void Cmds2Fields(Compiler.Block block, CompileException err = null)
        {
            foreach (var cmd in block)
            {
                // check if the name is too short for a field
                if (cmd.Name.Length < 2)
                {
                    continue;
                }

                // get field member info
                var member = GetFxField(cmd.Name);

                if (member != null)
                {
                    // set value of field
                    SetValue(this, member, member is FieldInfo
                        ? (member as FieldInfo).FieldType
                        : (member as PropertyInfo).PropertyType, cmd, err);
                }
            }
        }
Exemplo n.º 15
0
        /// <summary>
        /// Create class instance of a C# class compiled through GLCSharp.
        /// </summary>
        /// <param name="params">Input parameters for GLObject creation.</param>
        public GLInstance(Compiler.Block block, Dict scene, bool debugging)
            : base(block.Name, block.Anno)
        {
            var err = new CompileException($"instance '{Name}'");

            // INSTANTIATE CSHARP CLASS FROM CODE BLOCK
            Instance = GLCsharp.CreateInstance(block, scene, err);
            if (err.HasErrors)
            {
                throw err;
            }

            // get Bind method from main class instance
            update = Instance.GetType().GetMethod("Update", new[] {
                typeof(int), typeof(int), typeof(int), typeof(int), typeof(int)
            });

            // get Unbind method from main class instance
            endpass = Instance.GetType().GetMethod("EndPass", new[] { typeof(int) });

            // get Delete method from main class instance
            delete = Instance.GetType().GetMethod("Delete");

            // get all public methods and check whether
            // they can be used as event handlers for glControl
            var reference = scene.GetValueOrDefault <GLReference>(GraphicControl.nullname);
            var glControl = (GraphicControl)reference.Reference;
            var methods   = Instance.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);

            foreach (var method in methods)
            {
                var info = glControl.GetType().GetEvent(method.Name);
                if (info != null)
                {
                    var csmethod = Delegate.CreateDelegate(info.EventHandlerType, Instance, method.Name);
                    info.AddEventHandler(glControl, csmethod);
                }
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Create OpenGL object. Standard object constructor for ProtoFX.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="debugging"></param>
        public GLShader(Compiler.Block block, Dict scene, bool debugging)
            : base(block.Name, block.Anno)
        {
            var err = new CompileException($"shader '{Name}'");

            // COMPILE AND LINK SHADER INTO A SHADER PROGRAM

            switch (Anno)
            {
            case "vert": ShaderType = ShaderType.VertexShader; break;

            case "tess": ShaderType = ShaderType.TessControlShader; break;

            case "eval": ShaderType = ShaderType.TessEvaluationShader; break;

            case "geom": ShaderType = ShaderType.GeometryShader; break;

            case "frag": ShaderType = ShaderType.FragmentShader; break;

            case "comp": ShaderType = ShaderType.ComputeShader; break;

            default: throw err.Add($"Shader type '{Anno}' is not supported.", block);
            }

            glname = GL.CreateShaderProgram(ShaderType, 1, new[] { block.Body });

            // check for errors

            GL.GetProgram(glname, GetProgramParameterName.LinkStatus, out int status);
            if (status != 1)
            {
                err.Add($"\n{GL.GetProgramInfoLog(glname)}", block);
            }
            if (HasErrorOrGlError(err, block))
            {
                throw err;
            }

            // CREATE CSHARP DEBUG CODE

            string          code;
            CompilerResults rs;

            if (debugging)
            {
                code = Converter.Shader2Class(ShaderType, Name, block.Body, block.BodyIndex);
                rs   = GLCsharp.CompileFilesOrSource(new[] { code }, null, block, err, new[] { Name });
                if (rs.Errors.Count == 0)
                {
                    DebugShader = (Shader)rs.CompiledAssembly.CreateInstance(
                        $"App.Glsl.{Name}", false, BindingFlags.Default, null,
                        new object[] { block.LineInFile }, CultureInfo.CurrentCulture, null);
                }
            }

            // check for errors
            if (err.HasErrors)
            {
                throw err;
            }
        }
Exemplo n.º 17
0
        /// <summary>
        /// Compile a list of files or a list of source code.
        /// </summary>
        /// <param name="code"></param>
        /// <param name="block"></param>
        /// <param name="err"></param>
        /// <returns></returns>
        internal static CompilerResults CompileFilesOrSource(string[] code, string version,
                                                             Compiler.Block block, CompileException err, string[] tmpNames = null)
        {
            CompilerResults rs = null;

            try
            {
                var compilerParams = new CompilerParameters();

                // set compicompiler parameters
                compilerParams.GenerateInMemory   = true;
                compilerParams.GenerateExecutable = false;
                compilerParams.TempFiles          = new TempFileCollection("tmp", false);
#if DEBUG
                compilerParams.IncludeDebugInformation = true;
                compilerParams.CompilerOptions         = "/define:DEBUG";
#else
                compilerParams.IncludeDebugInformation = false;
#endif
                // add assemblies
                compilerParams.ReferencedAssemblies.Add(
                    System.Reflection.Assembly.GetExecutingAssembly().Location);
                compilerParams.ReferencedAssemblies.AddRange(
                    Properties.Resources.CSHARP_REFERENCES.Split('\n')
                    .Select(s => s.Trim()).ToArray());

                // select compiler version
                var provider = version != null
                    ? new CSharpCodeProvider(new Dictionary <string, string> {
                    { "CompilerVersion", version }
                })
                    : new CSharpCodeProvider();
                var check = code.Count(x => IsFilename(x));
                if (check == code.Length)
                {
                    rs = provider.CompileAssemblyFromFile(compilerParams, code);
                }
                else if (check == 0)
                {
#if DEBUG
                    Directory.CreateDirectory("tmp");
                    var filenames = new string[code.Length];
                    for (int i = 0; i < filenames.Length; i++)
                    {
                        filenames[i] = $"tmp{Path.DirectorySeparatorChar}tmp_{tmpNames[i]}.cs";
                        if (System.IO.File.Exists(filenames[i]))
                        {
                            var tmp = System.IO.File.ReadAllText(filenames[i]);
                            if (tmp != code[i])
                            {
                                System.IO.File.WriteAllText(filenames[i], code[i]);
                            }
                        }
                        else
                        {
                            System.IO.File.WriteAllText(filenames[i], code[i]);
                        }
                    }
                    rs = provider.CompileAssemblyFromFile(compilerParams, filenames);
#else
                    rs = provider.CompileAssemblyFromSource(compilerParams, code);
#endif
                }
                else
                {
                    throw err.Add("Cannot mix filenames and source code"
                                  + "strings when compiling C# code.", block);
                }
            }
            catch (DirectoryNotFoundException ex)
            {
                throw err.Add(ex.Message, block);
            }
            catch (FileNotFoundException ex)
            {
                throw err.Add(ex.Message, block);
            }
            finally
            {
                // check for compiler errors
                if (rs?.Errors.Count != 0)
                {
                    string msg = "";
                    foreach (var message in rs.Errors)
                    {
                        msg += $"\n{message}";
                    }
                    err.Add(msg, block);
                }
            }
            return(rs);
        }
Exemplo n.º 18
0
 /// <summary>
 /// Create OpenGL object. Standard object constructor for ProtoFX.
 /// </summary>
 /// <param name="block"></param>
 /// <param name="scene"></param>
 /// <param name="debugging"></param>
 public GLTexture(Compiler.Block block, Dict scene, bool debugging)
     : this(block, scene, null, null)
 {
 }
Exemplo n.º 19
0
 public static CompileException Add(this CompileException err, string message,
                                    Compiler.Block block)
 {
     return(err.Add(message, block.Filename, block.LineInFile));
 }
Exemplo n.º 20
0
 public static bool TryGetValue <T>(this Dict dict, string key, out T obj,
                                    Compiler.Block block, CompileException err)
     where T : GLObject
 {
     return(dict.TryGetValue(key, out obj, block.LineInFile, block.Filename, err));
 }
Exemplo n.º 21
0
        /// <summary>
        /// Create OpenGL object. Standard object constructor for ProtoFX.
        /// </summary>
        /// <param name="block"></param>
        /// <param name="scene"></param>
        /// <param name="genDebugInfo"></param>
        public GLPass(Compiler.Block block, Dict scene, bool genDebugInfo)
            : base(block.Name, block.Anno, 309, genDebugInfo)
        {
            var err = new CompileException($"pass '{Name}'");

            GenDebugInfo = genDebugInfo;

            /// PARSE COMMANDS AND CONVERT THEM TO CLASS FIELDS

            Cmds2Fields(block, err);

            /// PARSE COMMANDS

            foreach (var cmd in block)
            {
                // ignore class fields
                var field = GetType().GetField(cmd.Name, Instance | Public | NonPublic);
                var attr  = field?.GetCustomAttributes(typeof(FxField), false);
                if (attr?.Length > 0)
                {
                    continue;
                }

                using (var e = err | $"command '{cmd.Name}' line {cmd.LineInFile}")
                {
                    switch (cmd.Name)
                    {
                    case "draw": ParseDrawCall(cmd, scene, e); break;

                    case "compute": ParseComputeCall(cmd, scene, e); break;

                    case "tex": ParseTexCmd(cmd, scene, e); break;

                    case "img": ParseImgCmd(cmd, scene, e); break;

                    case "samp": ParseSampCmd(cmd, scene, e); break;

                    case "exec": ParseCsharpExec(cmd, scene, e); break;

                    case "vertout": vertoutput = new Vertoutput(cmd, scene, e);  break;

                    case "fragout": scene.TryGetValue(cmd[0].Text, out fragoutput, cmd, e); break;

                    default: ParseOpenGLCall(cmd, e); break;
                    }
                }
            }

            if (err.HasErrors)
            {
                throw err;
            }

            /// CREATE OPENGL OBJECT

            if (Vert != null || Comp != null)
            {
                GL.CreateProgramPipelines(1, out glname);

                // Attach shader objects.
                // First try attaching a compute shader. If that
                // fails, try attaching the default shader pipeline.
                if ((glcomp = Attach(block, Comp, scene, err)) == null)
                {
                    glvert = Attach(block, Vert, scene, err);
                    gltess = Attach(block, Tess, scene, err);
                    gleval = Attach(block, Eval, scene, err);
                    glgeom = Attach(block, Geom, scene, err);
                    glfrag = Attach(block, Frag, scene, err);
                }

                // get debug shaders
                if (GenDebugInfo)
                {
                    if (glcomp != null)
                    {
                        dbgcomp = (CompShader)glcomp.DebugShader;
                    }
                    else
                    {
                        Shader prev =
                            dbgvert = (VertShader)glvert.DebugShader;
                        dbgtess     = (TessShader)gltess?.DebugShader;
                        dbgeval     = (EvalShader)gleval?.DebugShader;
                        dbggeom     = (GeomShader)glgeom?.DebugShader;
                        dbgfrag     = (FragShader)glfrag?.DebugShader;
                        if (dbgtess != null)
                        {
                            dbgtess.Prev = prev;
                            prev         = dbgtess;
                        }
                        if (dbgeval != null)
                        {
                            dbgeval.Prev = prev;
                            prev         = dbgeval;
                        }
                        if (dbggeom != null)
                        {
                            dbggeom.Prev = prev;
                            prev         = dbggeom;
                        }
                    }
                }
            }

            /// CHECK FOR ERRORS

            if (GL.GetError() != ErrorCode.NoError)
            {
                err.Add($"OpenGL error '{GL.GetError()}' occurred " +
                        "during shader program creation.", block);
            }
            if (err.HasErrors)
            {
                throw err;
            }
        }