コード例 #1
0
ファイル: IGL_GdiPlus.cs プロジェクト: raiscan/BizHawk
 public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required)
 {
     return null;
 }
コード例 #2
0
ファイル: IGL_SlimDX9.cs プロジェクト: SaxxonPike/BizHawk
		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);
		}
コード例 #3
0
ファイル: IGL_SlimDX9.cs プロジェクト: SaxxonPike/BizHawk
		public void Internal_FreeShader(Shader shader)
		{
			var sw = shader.Opaque as ShaderWrapper;
			sw.bytecode.Dispose();
			if (sw.ps != null) sw.ps.Dispose();
			if (sw.vs != null) sw.vs.Dispose();
		}
コード例 #4
0
ファイル: IGL_TK.cs プロジェクト: CadeLaRen/BizHawk
		public void Internal_FreeShader(Shader shader)
		{
			GL.DeleteShader((int)shader.Opaque);
		}
コード例 #5
0
ファイル: IGL_SlimDX9.cs プロジェクト: SaxxonPike/BizHawk
		public Shader CreateVertexShader(bool cg, string source, string entry, bool required)
		{
			try
			{
				ShaderWrapper sw = new ShaderWrapper();
				if (cg)
				{
					var cgc = new CGC();
					var results = cgc.Run(source, entry, "hlslv", true);
					source = results.Code;
					entry = "main";
					if (!results.Succeeded)
					{
						if (required) throw new InvalidOperationException(results.Errors);
						else return new Shader(this, null, false);
					}

					sw.MapCodeToNative = results.MapCodeToNative;
					sw.MapNativeToCode = results.MapNativeToCode;
				}

				string errors = null;
				d3d9.ShaderBytecode bytecode = null;

				try
				{
					//cgc can create shaders that will need backwards compatibility...
					string profile = "vs_1_1";
					if (cg)
						profile = "vs_3_0"; //todo - smarter logic somehow

					bytecode = d3d9.ShaderBytecode.Compile(source, null, null, entry, profile, ShaderFlags.EnableBackwardsCompatibility, out errors);
				}
				catch (Exception ex)
				{
					throw new InvalidOperationException("Error compiling shader: " + errors, ex);
				}

				sw.vs = new VertexShader(dev, bytecode);
				sw.bytecode = bytecode;

				Shader s = new Shader(this, sw, true);
				sw.IGLShader = s;
				return s;
			}
			catch(Exception ex)
			{
				if (required)
					throw;
				var s = new Shader(this, null, false);
				s.Errors = ex.ToString();
				return s;
			}
		}
コード例 #6
0
ファイル: IGL_GdiPlus.cs プロジェクト: CadeLaRen/BizHawk
		public void Internal_FreeShader(Shader shader)
		{
		}
コード例 #7
0
ファイル: IGL_TK.cs プロジェクト: CadeLaRen/BizHawk
		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);
		}
コード例 #8
0
ファイル: IGL_SlimDX9.cs プロジェクト: cas1993per/bizhawk
        public Shader CreateVertexShader(string source, bool required)
        {
            ShaderWrapper sw = new ShaderWrapper();
            string errors;
            using (var bytecode = d3d9.ShaderBytecode.Compile(source, null, null, "vsmain", "vs_2_0", ShaderFlags.None, out errors))
            {
                sw.ct = bytecode.ConstantTable;
                sw.vs = new VertexShader(dev, bytecode);
            }

            Shader s = new Shader(this, IntPtr.Zero, true);
            s.Opaque = sw;
            return s;
        }
コード例 #9
0
ファイル: IGL_SlimDX9.cs プロジェクト: cas1993per/bizhawk
        public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required)
        {
            VertexElement[] ves = new VertexElement[vertexLayout.Items.Count];
            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();
                        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();
            pw.VertexDeclaration = new VertexDeclaration(dev, ves);

            Pipeline pipeline = new Pipeline(this,IntPtr.Zero,true, vertexLayout, new List<UniformInfo>());
            pipeline.Opaque = pw;

            return pipeline;
        }
コード例 #10
0
ファイル: IGL_TK.cs プロジェクト: ddugovic/RASuite
		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);
		}
コード例 #11
0
ファイル: IGL_SlimDX9.cs プロジェクト: henke37/BizHawk
		public Shader CreateFragmentShader(bool cg, string source, string entry, bool required)
		{
			try
			{
				ShaderWrapper sw = new ShaderWrapper();
				if (cg)
				{
					var cgc = new CGC();
					var results = cgc.Run(source, entry, "hlslf", true);
					source = results.Code;
					entry = "main";
					if (!results.Succeeded)
					{
						if (required) throw new InvalidOperationException(results.Errors);
						else return new Shader(this, null, false);
					}

					sw.MapCodeToNative = results.MapCodeToNative;
					sw.MapNativeToCode = results.MapNativeToCode;
				}

				string errors = null;
				d3d9.ShaderBytecode bytecode = null;

				try
				{
					//cgc can create shaders that will need backwards compatibility...
					string profile = "ps_1_0";
					if (cg)
						profile = "ps_3_0"; //todo - smarter logic somehow

					//ShaderFlags.EnableBackwardsCompatibility - used this once upon a time (please leave a note about why)
					//
					bytecode = d3d9.ShaderBytecode.Compile(source, null, null, entry, profile, ShaderFlags.UseLegacyD3DX9_31Dll, out errors);
				}
				catch (Exception ex)
				{
					throw new InvalidOperationException("Error compiling shader: " + errors, ex);
				}

				sw.ps = new PixelShader(dev, bytecode);
				sw.bytecode = bytecode;

				Shader s = new Shader(this, sw, true);
				sw.IGLShader = s;

				return s;
			}
			catch
			{
				if (required)
					throw;
				else return new Shader(this, null, false);
			}
		}
コード例 #12
0
ファイル: IGL_SlimDX9.cs プロジェクト: TechnoTaff/BizHawk
		public Shader CreateFragmentShader(bool cg, string source, string entry, bool required)
		{
			ShaderWrapper sw = new ShaderWrapper();
			if (cg)
			{
				var cgc = new CGC();
				var results = cgc.Run(source, entry, "hlslf");
				source = results.Code;
				entry = "main";
				if (!results.Succeeded)
				{
					if (required) throw new InvalidOperationException(results.Errors);
					else return new Shader(this, null, false);
				}

				sw.MapCodeToNative = results.MapCodeToNative;
				sw.MapNativeToCode = results.MapNativeToCode;
			}

			string errors = null;
			d3d9.ShaderBytecode bytecode = null;

			try
			{
				//cgc can create shaders that will need backwards compatibility...
				bytecode = d3d9.ShaderBytecode.Compile(source, null, null, entry, "ps_3_0", ShaderFlags.EnableBackwardsCompatibility, out errors);
			}
			catch(Exception ex)
			{
				throw new InvalidOperationException("Error compiling shader: " + errors, ex);
			}

			sw.ps = new PixelShader(dev, bytecode);
			sw.bytecode = bytecode;

			Shader s = new Shader(this, sw, true);
			sw.IGLShader = s;
			
			return s;
		}