示例#1
0
            public new PipelineUniform this[string key]
            {
                get
                {
                    PipelineUniform temp;
                    if (!TryGetValue(key, out temp))
                    {
                        var ui = new UniformInfo();
                        ui.Opaque = null;
                        temp      = this[key] = new PipelineUniform(null);
                    }

                    return(temp);
                }

                internal set => base[key] = value;
示例#2
0
            public new PipelineUniform this[string key]
            {
                get
                {
                    PipelineUniform temp;
                    if (!TryGetValue(key, out temp))
                    {
                        var ui = new UniformInfo();
                        ui.Handle = Owner.Owner.GetEmptyUniformHandle();
                        temp      = this[key] = new PipelineUniform(Owner, ui);
                    }

                    return(temp);
                }

                internal set
                {
                    base[key] = value;
                }
            }
示例#3
0
 internal void AddUniformInfo(UniformInfo ui)
 {
     _UniformInfos.Add(ui);
 }
示例#4
0
		internal void AddUniformInfo(UniformInfo ui)
		{
			_UniformInfos.Add(ui);
		}
示例#5
0
		public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required, string memo)
		{
			if (!vertexShader.Available || !fragmentShader.Available)
			{
				string errors = string.Format("Vertex Shader:\r\n {0} \r\n-------\r\nFragment Shader:\r\n{1}", vertexShader.Errors, fragmentShader.Errors);
				if (required)
					throw new InvalidOperationException("Couldn't build required GL pipeline:\r\n" + errors);
				var pipeline = new Pipeline(this, null, false, null, null, null);
				pipeline.Errors = errors;
				return pipeline;
			}

			VertexElement[] ves = new VertexElement[vertexLayout.Items.Count];
			int stride = 0;
			foreach (var kvp in vertexLayout.Items)
			{
				var item = kvp.Value;
				d3d9.DeclarationType decltype = DeclarationType.Float1;
				switch (item.AttribType)
				{
					case gl.VertexAttribPointerType.Float:
						if (item.Components == 1) decltype = DeclarationType.Float1;
						else if (item.Components == 2) decltype = DeclarationType.Float2;
						else if (item.Components == 3) decltype = DeclarationType.Float3;
						else if (item.Components == 4) decltype = DeclarationType.Float4;
						else throw new NotSupportedException();
						stride += 4 * item.Components;
						break;
					default:
						throw new NotSupportedException();
				}

				d3d9.DeclarationUsage usage = DeclarationUsage.Position;
				byte usageIndex = 0;
				switch(item.Usage)
				{
					case AttributeUsage.Position: 
						usage = DeclarationUsage.Position; 
						break;
					case AttributeUsage.Texcoord0: 
						usage = DeclarationUsage.TextureCoordinate;
						break;
					case AttributeUsage.Texcoord1: 
						usage = DeclarationUsage.TextureCoordinate;
						usageIndex = 1;
						break;
					case AttributeUsage.Color0:
						usage = DeclarationUsage.Color;
						break;
					default:
						throw new NotSupportedException();
				}

				ves[kvp.Key] = new VertexElement(0, (short)item.Offset, decltype, DeclarationMethod.Default, usage, usageIndex);
			}


			var pw = new PipelineWrapper()
			{
				VertexDeclaration = new VertexDeclaration(dev, ves),
				VertexShader = vertexShader.Opaque as ShaderWrapper,
				FragmentShader = fragmentShader.Opaque as ShaderWrapper,
				VertexStride = stride,
			};

			//scan uniforms from constant tables
			//handles must be disposed later (with the pipeline probably)
			var uniforms = new List<UniformInfo>();
			var fs = pw.FragmentShader;
			var vs = pw.VertexShader;
			var fsct = fs.bytecode.ConstantTable;
			var vsct = vs.bytecode.ConstantTable;
			foreach(var ct in new[]{fsct,vsct})
			{
				Queue<Tuple<string,EffectHandle>> todo = new Queue<Tuple<string,EffectHandle>>();
				int n = ct.Description.Constants;
				for (int i = 0; i < n; i++)
				{
					var handle = ct.GetConstant(null, i);
					todo.Enqueue(Tuple.Create("", handle));
				}

				while(todo.Count != 0)
				{
					var tuple = todo.Dequeue();
					var prefix = tuple.Item1;
					var handle = tuple.Item2;
					var descr = ct.GetConstantDescription(handle);

					//Console.WriteLine("D3D UNIFORM: " + descr.Name);

					if (descr.StructMembers != 0)
					{
						string newprefix = prefix + descr.Name + ".";
						for (int j = 0; j < descr.StructMembers; j++)
						{
							var subhandle = ct.GetConstant(handle, j);
							todo.Enqueue(Tuple.Create(newprefix, subhandle));
						}
						continue;
					}

					UniformInfo ui = new UniformInfo();
					UniformWrapper uw = new UniformWrapper();

					ui.Opaque = uw;
					string name = prefix + descr.Name;

					//mehhh not happy about this stuff
					if (fs.MapCodeToNative != null || vs.MapCodeToNative != null)
					{
						string key = name.TrimStart('$');
						if (descr.Rows != 1)
							key = key + "[0]";
						if (fs.MapCodeToNative != null && ct == fsct) if (fs.MapCodeToNative.ContainsKey(key)) name = fs.MapCodeToNative[key];
						if (vs.MapCodeToNative != null && ct == vsct) if (vs.MapCodeToNative.ContainsKey(key)) name = vs.MapCodeToNative[key];
					}
					
					ui.Name = name;
					uw.Description = descr;
					uw.EffectHandle = handle;
					uw.FS = (ct == fsct);
					uw.CT = ct;
					if (descr.Type == ParameterType.Sampler2D)
					{
						ui.IsSampler = true;
						ui.SamplerIndex = descr.RegisterIndex;
						uw.SamplerIndex = descr.RegisterIndex;
					}
					uniforms.Add(ui);
				}
			}

			pw.fsct = fsct;
			pw.vsct = vsct;

			return new Pipeline(this, pw, true, vertexLayout, uniforms,memo);
		}
示例#6
0
		public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required, string memo)
		{
			//if the shaders arent available, the pipeline isn't either
			if (!vertexShader.Available || !fragmentShader.Available)
			{
				string errors = string.Format("Vertex Shader:\r\n {0} \r\n-------\r\nFragment Shader:\r\n{1}", vertexShader.Errors, fragmentShader.Errors);
				if (required)
					throw new InvalidOperationException("Couldn't build required GL pipeline:\r\n" + errors);
				var pipeline = new Pipeline(this, null, false, null, null, null);
				pipeline.Errors = errors;
				return pipeline;
			}

			bool success = true;

			var vsw = vertexShader.Opaque as ShaderWrapper;
			var fsw = fragmentShader.Opaque as ShaderWrapper;
			var sws = new[] { vsw,fsw };

			bool mapVariables = vsw.MapCodeToNative != null || fsw.MapCodeToNative != null;

			ErrorCode errcode;
			int pid = GL.CreateProgram();
			GL.AttachShader(pid, vsw.sid);
			errcode = GL.GetError();
			GL.AttachShader(pid, fsw.sid);
			errcode = GL.GetError();

			//NOT BEING USED NOW: USING SEMANTICS INSTEAD
			////bind the attribute locations from the vertex layout
			////as we go, look for attribute mappings (CGC will happily reorder and rename our attribute mappings)
			////what's more it will _RESIZE_ them but this seems benign..somehow..
			////WELLLLLLL we wish we could do that by names
			////but the shaders dont seem to be adequate quality (oddly named attributes.. texCoord vs texCoord1). need to use semantics instead.
			//foreach (var kvp in vertexLayout.Items)
			//{
			//  string name = kvp.Value.Name;
			//  //if (mapVariables)
			//  //{
			//  //  foreach (var sw in sws)
			//  //  {
			//  //    if (sw.MapNativeToCode.ContainsKey(name))
			//  //    {
			//  //      name = sw.MapNativeToCode[name];
			//  //      break;
			//  //    }
			//  //  }
			//  //}

			//  if(mapVariables) {
			//    ////proxy for came-from-cgc
			//    //switch (kvp.Value.Usage)
			//    //{
			//    //  case AttributeUsage.Position: 
			//    //}
			//  }
				
			//  //GL.BindAttribLocation(pid, kvp.Key, name);
			//}

			
			GL.LinkProgram(pid);
			errcode = GL.GetError();

			string resultLog = GL.GetProgramInfoLog(pid);

			if (errcode != ErrorCode.NoError)
				if (required)
					throw new InvalidOperationException("Error creating pipeline (error returned from glLinkProgram): " + errcode + "\r\n\r\n" + resultLog);
				else success = false;
			
			int linkStatus;
			GL.GetProgram(pid, GetProgramParameterName.LinkStatus, out linkStatus);
			if (linkStatus == 0)
				if (required)
					throw new InvalidOperationException("Error creating pipeline (link status false returned from glLinkProgram): " + "\r\n\r\n" + resultLog);
				else success = false;

			//need to work on validation. apparently there are some weird caveats to glValidate which make it complicated and possibly excuses (barely) the intel drivers' dysfunctional operation
			//"A sampler points to a texture unit used by fixed function with an incompatible target"
			//
			//info:
			//http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgram.xml
			//This function mimics the validation operation that OpenGL implementations must perform when rendering commands are issued while programmable shaders are part of current state.
			//glValidateProgram checks to see whether the executables contained in program can execute given the current OpenGL state
			//This function is typically useful only during application development.
			//
			//So, this is no big deal. we shouldnt be calling validate right now anyway.
			//conclusion: glValidate is very complicated and is of virtually no use unless your draw calls are returning errors and you want to know why
			//GL.ValidateProgram(pid);
			//errcode = GL.GetError();
			//resultLog = GL.GetProgramInfoLog(pid);
			//if (errcode != ErrorCode.NoError)
			//  throw new InvalidOperationException("Error creating pipeline (error returned from glValidateProgram): " + errcode + "\r\n\r\n" + resultLog);
			//int validateStatus;
			//GL.GetProgram(pid, GetProgramParameterName.ValidateStatus, out validateStatus);
			//if (validateStatus == 0)
			//  throw new InvalidOperationException("Error creating pipeline (validateStatus status false returned from glValidateProgram): " + "\r\n\r\n" + resultLog);

			//set the program to active, in case we need to set sampler uniforms on it
			GL.UseProgram(pid);

			//get all the attributes (not needed)
			List<AttributeInfo> attributes = new List<AttributeInfo>();
			int nAttributes;
			GL.GetProgram(pid, GetProgramParameterName.ActiveAttributes, out nAttributes);
			for (int i = 0; i < nAttributes; i++)
			{
				int size, length;
				var sbName = new System.Text.StringBuilder(1024);
				ActiveAttribType type;
				GL.GetActiveAttrib(pid, i, 1024, out length, out size, out type, sbName);
				attributes.Add(new AttributeInfo() { Handle = new IntPtr(i), Name = sbName.ToString() });
			}

			//get all the uniforms
			List<UniformInfo> uniforms = new List<UniformInfo>();
			int nUniforms;
			GL.GetProgram(pid,GetProgramParameterName.ActiveUniforms,out nUniforms);
			List<int> samplers = new List<int>();

			for (int i = 0; i < nUniforms; i++)
			{
				int size, length;
				ActiveUniformType type;
				var sbName = new System.Text.StringBuilder(1024);
				GL.GetActiveUniform(pid, i, 1024, out length, out size, out type, sbName);
				errcode = GL.GetError();
				string name = sbName.ToString();
				int loc = GL.GetUniformLocation(pid, name);

				//translate name if appropriate
				//not sure how effective this approach will be, due to confusion of vertex and fragment uniforms
				if (mapVariables)
				{
					if (vsw.MapCodeToNative.ContainsKey(name)) name = vsw.MapCodeToNative[name];
					if (fsw.MapCodeToNative.ContainsKey(name)) name = fsw.MapCodeToNative[name];
				}

				var ui = new UniformInfo();
				ui.Name = name;
				ui.Opaque = loc;

				if (type == ActiveUniformType.Sampler2D)
				{
					ui.IsSampler = true;
					ui.SamplerIndex = samplers.Count;
					ui.Opaque = loc | (samplers.Count << 24);
					samplers.Add(loc);
				}

				uniforms.Add(ui);
			}

			//deactivate the program, so we dont accidentally use it
			GL.UseProgram(0);

			if (!vertexShader.Available) success = false;
			if (!fragmentShader.Available) success = false;

			var pw = new PipelineWrapper() { pid = pid, VertexShader = vertexShader, FragmentShader = fragmentShader, SamplerLocs = samplers };

			return new Pipeline(this, pw, success, vertexLayout, uniforms, memo);
		}
示例#7
0
		public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required)
		{
			bool success = true;

			ErrorCode errcode;
			int pid = GL.CreateProgram();
			GL.AttachShader(pid, vertexShader.Id.ToInt32());
			errcode = GL.GetError();
			GL.AttachShader(pid, fragmentShader.Id.ToInt32());
			errcode = GL.GetError();

			//bind the attribute locations from the vertex layout
			foreach (var kvp in vertexLayout.Items)
				GL.BindAttribLocation(pid, kvp.Key, kvp.Value.Name);
			
			GL.LinkProgram(pid);
			errcode = GL.GetError();

			string resultLog = GL.GetProgramInfoLog(pid);

			if (errcode != ErrorCode.NoError)
				if (required)
					throw new InvalidOperationException("Error creating pipeline (error returned from glLinkProgram): " + errcode + "\r\n\r\n" + resultLog);
				else success = false;
			
			int linkStatus;
			GL.GetProgram(pid, GetProgramParameterName.LinkStatus, out linkStatus);
			if (linkStatus == 0)
				if (required)
					throw new InvalidOperationException("Error creating pipeline (link status false returned from glLinkProgram): " + "\r\n\r\n" + resultLog);
				else success = false;

			//need to work on validation. apparently there are some weird caveats to glValidate which make it complicated and possibly excuses (barely) the intel drivers' dysfunctional operation
			//"A sampler points to a texture unit used by fixed function with an incompatible target"
			//
			//info:
			//http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgram.xml
			//This function mimics the validation operation that OpenGL implementations must perform when rendering commands are issued while programmable shaders are part of current state.
			//glValidateProgram checks to see whether the executables contained in program can execute given the current OpenGL state
			//This function is typically useful only during application development.
			//
			//So, this is no big deal. we shouldnt be calling validate right now anyway.
			//conclusion: glValidate is very complicated and is of virtually no use unless your draw calls are returning errors and you want to know why
			//GL.ValidateProgram(pid);
			//errcode = GL.GetError();
			//resultLog = GL.GetProgramInfoLog(pid);
			//if (errcode != ErrorCode.NoError)
			//  throw new InvalidOperationException("Error creating pipeline (error returned from glValidateProgram): " + errcode + "\r\n\r\n" + resultLog);
			//int validateStatus;
			//GL.GetProgram(pid, GetProgramParameterName.ValidateStatus, out validateStatus);
			//if (validateStatus == 0)
			//  throw new InvalidOperationException("Error creating pipeline (validateStatus status false returned from glValidateProgram): " + "\r\n\r\n" + resultLog);

			//set the program to active, in case we need to set sampler uniforms on it
			GL.UseProgram(pid);

			////get all the attributes (not needed)
			//List<AttributeInfo> attributes = new List<AttributeInfo>();
			//int nAttributes;
			//GL.GetProgram(pid, GetProgramParameterName.ActiveAttributes, out nAttributes);
			//for (int i = 0; i < nAttributes; i++)
			//{
			//  int size, length;
			//  var sbName = new System.Text.StringBuilder();
			//  ActiveAttribType type;
			//  GL.GetActiveAttrib(pid, i, 1024, out length, out size, out type, sbName);
			//  attributes.Add(new AttributeInfo() { Handle = new IntPtr(i), Name = sbName.ToString() });
			//}

			//get all the uniforms
			List<UniformInfo> uniforms = new List<UniformInfo>();
			int nUniforms;
			int nSamplers = 0;
			GL.GetProgram(pid,GetProgramParameterName.ActiveUniforms,out nUniforms);

			for (int i = 0; i < nUniforms; i++)
			{
				int size, length;
				ActiveUniformType type;
				var sbName = new System.Text.StringBuilder();
				GL.GetActiveUniform(pid, i, 1024, out length, out size, out type, sbName);
				errcode = GL.GetError();
				string name = sbName.ToString();
				int loc = GL.GetUniformLocation(pid, name);
				var ui = new UniformInfo();
				ui.Name = name;
				ui.Handle = new IntPtr(loc);

				//automatically assign sampler uniforms to texture units (and bind them)
				bool isSampler = (type == ActiveUniformType.Sampler2D);
				if (isSampler)
				{
					ui.SamplerIndex = nSamplers;
					GL.Uniform1(loc, nSamplers);
					nSamplers++;
				}

				uniforms.Add(ui);
			}

			//deactivate the program, so we dont accidentally use it
			GL.UseProgram(0);

			if (!vertexShader.Available) success = false;
			if (!fragmentShader.Available) success = false;

			return new Pipeline(this, new IntPtr(pid), success, vertexLayout, uniforms);
		}
示例#8
0
 internal PipelineUniform(Pipeline owner, UniformInfo info)
 {
     Owner = owner;
     Id = info.Handle;
     SamplerIndex = info.SamplerIndex;
 }
示例#9
0
 internal PipelineUniform(Pipeline owner, UniformInfo info)
 {
     Owner        = owner;
     Id           = info.Handle;
     SamplerIndex = info.SamplerIndex;
 }
示例#10
0
        /// <exception cref="InvalidOperationException">
        /// <paramref name="required"/> is <see langword="true"/> and either <paramref name="vertexShader"/> or <paramref name="fragmentShader"/> is unavailable (their <see cref="Shader.Available"/> property is <see langword="false"/>), or
        /// <c>glLinkProgram</c> call did not produce expected result
        /// </exception>
        public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required, string memo)
        {
            //if the shaders arent available, the pipeline isn't either
            if (!vertexShader.Available || !fragmentShader.Available)
            {
                string errors = $"Vertex Shader:\r\n {vertexShader.Errors} \r\n-------\r\nFragment Shader:\r\n{fragmentShader.Errors}";
                if (required)
                {
                    throw new InvalidOperationException($"Couldn't build required GL pipeline:\r\n{errors}");
                }
                var pipeline = new Pipeline(this, null, false, null, null, null);
                pipeline.Errors = errors;
                return(pipeline);
            }

            bool success = true;

            var vsw = vertexShader.Opaque as ShaderWrapper;
            var fsw = fragmentShader.Opaque as ShaderWrapper;
            var sws = new[] { vsw, fsw };

            bool mapVariables = vsw.MapCodeToNative != null || fsw.MapCodeToNative != null;

            ErrorCode errcode;
            int       pid = GL.CreateProgram();

            GL.AttachShader(pid, vsw.sid);
            errcode = GL.GetError();
            GL.AttachShader(pid, fsw.sid);
            errcode = GL.GetError();

            //NOT BEING USED NOW: USING SEMANTICS INSTEAD
            ////bind the attribute locations from the vertex layout
            ////as we go, look for attribute mappings (CGC will happily reorder and rename our attribute mappings)
            ////what's more it will _RESIZE_ them but this seems benign..somehow..
            ////WELLLLLLL we wish we could do that by names
            ////but the shaders dont seem to be adequate quality (oddly named attributes.. texCoord vs texCoord1). need to use semantics instead.
            //foreach (var kvp in vertexLayout.Items)
            //{
            //  string name = kvp.Value.Name;
            //  //if (mapVariables)
            //  //{
            //  //  foreach (var sw in sws)
            //  //  {
            //  //    if (sw.MapNativeToCode.ContainsKey(name))
            //  //    {
            //  //      name = sw.MapNativeToCode[name];
            //  //      break;
            //  //    }
            //  //  }
            //  //}

            //  if(mapVariables) {
            //    ////proxy for came-from-cgc
            //    //switch (kvp.Value.Usage)
            //    //{
            //    //  case AttributeUsage.Position:
            //    //}
            //  }

            //  //GL.BindAttribLocation(pid, kvp.Key, name);
            //}


            GL.LinkProgram(pid);
            errcode = GL.GetError();

            string resultLog = GL.GetProgramInfoLog(pid);

            if (errcode != ErrorCode.NoError)
            {
                if (required)
                {
                    throw new InvalidOperationException($"Error creating pipeline (error returned from glLinkProgram): {errcode}\r\n\r\n{resultLog}");
                }
                else
                {
                    success = false;
                }
            }

            int linkStatus;

            GL.GetProgram(pid, GetProgramParameterName.LinkStatus, out linkStatus);
            if (linkStatus == 0)
            {
                if (required)
                {
                    throw new InvalidOperationException($"Error creating pipeline (link status false returned from glLinkProgram): \r\n\r\n{resultLog}");
                }
                else
                {
                    success = false;
                }
            }

            //need to work on validation. apparently there are some weird caveats to glValidate which make it complicated and possibly excuses (barely) the intel drivers' dysfunctional operation
            //"A sampler points to a texture unit used by fixed function with an incompatible target"
            //
            //info:
            //http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgram.xml
            //This function mimics the validation operation that OpenGL implementations must perform when rendering commands are issued while programmable shaders are part of current state.
            //glValidateProgram checks to see whether the executables contained in program can execute given the current OpenGL state
            //This function is typically useful only during application development.
            //
            //So, this is no big deal. we shouldnt be calling validate right now anyway.
            //conclusion: glValidate is very complicated and is of virtually no use unless your draw calls are returning errors and you want to know why
            //GL.ValidateProgram(pid);
            //errcode = GL.GetError();
            //resultLog = GL.GetProgramInfoLog(pid);
            //if (errcode != ErrorCode.NoError)
            //  throw new InvalidOperationException($"Error creating pipeline (error returned from glValidateProgram): {errcode}\r\n\r\n{resultLog}");
            //int validateStatus;
            //GL.GetProgram(pid, GetProgramParameterName.ValidateStatus, out validateStatus);
            //if (validateStatus == 0)
            //  throw new InvalidOperationException($"Error creating pipeline (validateStatus status false returned from glValidateProgram): \r\n\r\n{resultLog}");

            //set the program to active, in case we need to set sampler uniforms on it
            GL.UseProgram(pid);

            //get all the attributes (not needed)
            List <AttributeInfo> attributes = new List <AttributeInfo>();
            int nAttributes;

            GL.GetProgram(pid, GetProgramParameterName.ActiveAttributes, out nAttributes);
            for (int i = 0; i < nAttributes; i++)
            {
                int              size, length;
                string           name = new System.Text.StringBuilder(1024).ToString();
                ActiveAttribType type;
                GL.GetActiveAttrib(pid, i, 1024, out length, out size, out type, out name);
                attributes.Add(new AttributeInfo()
                {
                    Handle = new IntPtr(i), Name = name
                });
            }

            //get all the uniforms
            List <UniformInfo> uniforms = new List <UniformInfo>();
            int nUniforms;

            GL.GetProgram(pid, GetProgramParameterName.ActiveUniforms, out nUniforms);
            List <int> samplers = new List <int>();

            for (int i = 0; i < nUniforms; i++)
            {
                int size, length;
                ActiveUniformType type;
                string            name = new System.Text.StringBuilder(1024).ToString();
                GL.GetActiveUniform(pid, i, 1024, out length, out size, out type, out name);
                errcode = GL.GetError();
                int loc = GL.GetUniformLocation(pid, name);

                //translate name if appropriate
                //not sure how effective this approach will be, due to confusion of vertex and fragment uniforms
                if (mapVariables)
                {
                    if (vsw.MapCodeToNative.ContainsKey(name))
                    {
                        name = vsw.MapCodeToNative[name];
                    }
                    if (fsw.MapCodeToNative.ContainsKey(name))
                    {
                        name = fsw.MapCodeToNative[name];
                    }
                }

                var ui = new UniformInfo();
                ui.Name   = name;
                ui.Opaque = loc;

                if (type == ActiveUniformType.Sampler2D)
                {
                    ui.IsSampler    = true;
                    ui.SamplerIndex = samplers.Count;
                    ui.Opaque       = loc | (samplers.Count << 24);
                    samplers.Add(loc);
                }

                uniforms.Add(ui);
            }

            //deactivate the program, so we dont accidentally use it
            GL.UseProgram(0);

            if (!vertexShader.Available)
            {
                success = false;
            }
            if (!fragmentShader.Available)
            {
                success = false;
            }

            var pw = new PipelineWrapper()
            {
                pid = pid, VertexShader = vertexShader, FragmentShader = fragmentShader, SamplerLocs = samplers
            };

            return(new Pipeline(this, pw, success, vertexLayout, uniforms, memo));
        }