/// <summary> /// Frees up any OpenGL resources that this RenderTarget might have occupied. /// </summary> public void FreeOpenGLRes() { DualityApp.GuardSingleThreadState(); if (this.glFboId != 0) { GL.Ext.DeleteFramebuffers(1, ref this.glFboId); this.glFboId = 0; } if (this.glRboIdDepth != 0) { GL.Ext.DeleteRenderbuffers(1, ref this.glRboIdDepth); this.glRboIdDepth = 0; } if (this.glFboIdMSAA != 0) { GL.Ext.DeleteFramebuffers(1, ref this.glFboIdMSAA); this.glFboIdMSAA = 0; } for (int i = 0; i < this.targetInfo.Count; i++) { TargetInfo infoTemp = this.targetInfo[i]; if (this.targetInfo[i].glRboIdColorMSAA != 0) { GL.Ext.DeleteRenderbuffers(1, ref infoTemp.glRboIdColorMSAA); infoTemp.glRboIdColorMSAA = 0; } this.targetInfo[i] = infoTemp; } }
/// <summary> /// Compiles the ShaderProgram. This is done automatically when loading the ShaderProgram /// or when binding it. /// </summary> public void Compile() { DualityApp.GuardSingleThreadState(); if (this.glProgramId == 0) { return; } if (this.compiled) { return; } GL.LinkProgram(this.glProgramId); int result; GL.GetProgram(this.glProgramId, GetProgramParameterName.LinkStatus, out result); if (result == 0) { string infoLog = GL.GetProgramInfoLog(this.glProgramId); Log.Core.WriteError("Error linking shader program. InfoLog:{1}{0}", infoLog, Environment.NewLine); return; } this.compiled = true; // Collect variable infos from sub programs ShaderVarInfo[] fragVarArray = this.frag.IsAvailable ? this.frag.Res.VarInfo : null; ShaderVarInfo[] vertVarArray = this.vert.IsAvailable ? this.vert.Res.VarInfo : null; if (fragVarArray != null && vertVarArray != null) { this.varInfo = vertVarArray.Union(fragVarArray).ToArray(); } else if (vertVarArray != null) { this.varInfo = vertVarArray.ToArray(); } else { this.varInfo = fragVarArray.ToArray(); } // Determine actual variable locations for (int i = 0; i < this.varInfo.Length; i++) { if (this.varInfo[i].scope == ShaderVarScope.Uniform) { this.varInfo[i].glVarLoc = GL.GetUniformLocation(this.glProgramId, this.varInfo[i].name); } else { this.varInfo[i].glVarLoc = GL.GetAttribLocation(this.glProgramId, this.varInfo[i].name); } } }
protected override void OnDisposing(bool manually) { base.OnDisposing(manually); if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated && this.glTexId != 0) { DualityApp.GuardSingleThreadState(); GL.DeleteTexture(this.glTexId); this.glTexId = 0; } }
/// <summary> /// Sets up the Textures OpenGL resources, clearing previously uploaded pixel data. /// </summary> protected void SetupOpenGLRes() { DualityApp.GuardSingleThreadState(); if (!initialized) { Init(); } if (this.glTexId == 0) { this.glTexId = GL.GenTexture(); } int lastTexId; GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); if (lastTexId != this.glTexId) { GL.BindTexture(TextureTarget.Texture2D, this.glTexId); } // Set texture parameters GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)this.filterMin); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)this.filterMag); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)this.wrapX); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)this.wrapY); // Anisotropic filtering GL.TexParameter(TextureTarget.Texture2D, (TextureParameterName)ExtTextureFilterAnisotropic.TextureMaxAnisotropyExt, this.anisoFilter ? maxAnisoLevel : 1.0f); // If needed, care for Mipmaps GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, this.HasMipmaps ? 1 : 0); // Setup pixel format if (this.compressed) { this.pixelformat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext; } if (this.pixelType == 0) { this.pixelType = PixelType.UnsignedByte; } GL.TexImage2D(TextureTarget.Texture2D, 0, this.pixelformat, this.texWidth, this.texHeight, 0, GLPixelFormat.Bgra, this.pixelType, IntPtr.Zero); if (lastTexId != this.glTexId) { GL.BindTexture(TextureTarget.Texture2D, lastTexId); } }
public void LoadData(IntPtr data, int width, int height) { DualityApp.GuardSingleThreadState(); if (this.glTexId == 0) { this.glTexId = GL.GenTexture(); } int lastTexId; GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); GL.BindTexture(TextureTarget.Texture2D, this.glTexId); if (data == IntPtr.Zero) { return; } AdjustSize(width, height); this.SetupOpenGLRes(); // Load pixel data to video memory if (Compressed) { var imageSize = ((this.PixelWidth + 3) / 4) * ((this.PixelHeight + 3) / 4) * 16; GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.CompressedRgbaS3tcDxt5Ext, this.PixelWidth, this.PixelHeight, 0, imageSize, data); } else { GL.TexImage2D(TextureTarget.Texture2D, 0, this.pixelformat, this.PixelWidth, this.PixelHeight, 0, GLPixelFormat.Rgba, PixelType.UnsignedByte, data); } // Adjust atlas to represent UV coordinates if (this.atlas != null) { Vector2 scale; scale.X = this.uvRatio.X / this.pxWidth; scale.Y = this.uvRatio.Y / this.pxHeight; for (int i = 0; i < this.atlas.Length; i++) { this.atlas[i].X *= scale.X; this.atlas[i].W *= scale.X; this.atlas[i].Y *= scale.Y; this.atlas[i].H *= scale.Y; } } GL.BindTexture(TextureTarget.Texture2D, lastTexId); }
private void Dispose(bool manually) { if (!this.disposed) { if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated && this.hndlPrimaryVBO != 0) { DualityApp.GuardSingleThreadState(); GL.DeleteBuffers(1, ref this.hndlPrimaryVBO); this.hndlPrimaryVBO = 0; } this.disposed = true; } }
protected override void OnDisposing(bool manually) { base.OnDisposing(manually); // Dispose unmanages Resources if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated && this.glTexId != 0) { DualityApp.GuardSingleThreadState(); GL.DeleteTexture(this.glTexId); this.glTexId = 0; } // Get rid of big data references, so the GC can collect them. this.basePixmap.Detach(); }
/// <summary> /// Attaches the specified <see cref="VertexShader">Vertex-</see> and <see cref="FragmentShader"/> to this ShaderProgram. /// </summary> /// <param name="v"></param> /// <param name="f"></param> public void AttachShaders(ContentRef <VertexShader> v, ContentRef <FragmentShader> f, ContentRef <GeometryShader> g = new ContentRef <GeometryShader>()) { DualityApp.GuardSingleThreadState(); if (this.glProgramId == 0) { this.glProgramId = GL.CreateProgram(); } else { this.DetachShaders(); } this.compiled = false; this.vert = v; this.frag = f; this.geom = g; // Assure all shaders are compiled if (this.vert.IsAvailable) { this.vert.Res.Compile(); } if (this.frag.IsAvailable) { this.frag.Res.Compile(); } if (this.geom.IsAvailable) { this.geom.Res.Compile(); } // Attach all shaders if (this.vert.IsAvailable) { GL.AttachShader(this.glProgramId, this.vert.Res.OglShaderId); } if (this.frag.IsAvailable) { GL.AttachShader(this.glProgramId, this.frag.Res.OglShaderId); } if (this.geom.IsAvailable) { GL.AttachShader(this.glProgramId, this.geom.Res.OglShaderId); } }
/// <summary> /// Retrieves the pixel data that is currently stored in video memory. /// </summary> /// <returns></returns> public Pixmap.Layer RetrievePixelData() { DualityApp.GuardSingleThreadState(); int lastTexId; GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); GL.BindTexture(TextureTarget.Texture2D, this.glTexId); byte[] data = new byte[this.texWidth * this.texHeight * 4]; GL.GetTexImage(TextureTarget.Texture2D, 0, GLPixelFormat.Rgba, PixelType.UnsignedByte, data); GL.BindTexture(TextureTarget.Texture2D, lastTexId); Pixmap.Layer result = new Pixmap.Layer(); result.SetPixelDataRgba(data, this.texWidth, this.texHeight); return(result); }
/// <summary> /// Loads the specified <see cref="Duality.Resources.Pixmap">Pixmaps</see> pixel data. /// </summary> /// <param name="basePixmap">The <see cref="Duality.Resources.Pixmap"/> that is used as pixel data source.</param> /// <param name="sizeMode">Specifies behaviour in case the source data has non-power-of-two dimensions.</param> public void LoadData(ContentRef <Pixmap> basePixmap, SizeMode sizeMode) { DualityApp.GuardSingleThreadState(); if (this.glTexId == 0) { this.glTexId = GL.GenTexture(); } this.needsReload = false; this.basePixmap = basePixmap; this.texSizeMode = sizeMode; int lastTexId; GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); GL.BindTexture(TextureTarget.Texture2D, this.glTexId); if (!this.basePixmap.IsExplicitNull) { Pixmap.Layer pixelData = null; Pixmap basePixmapRes = this.basePixmap.IsAvailable ? this.basePixmap.Res : null; if (basePixmapRes != null) { pixelData = basePixmapRes.MainLayer; this.atlas = basePixmapRes.Atlas != null?basePixmapRes.Atlas.ToArray() : null; } if (pixelData == null) { pixelData = Pixmap.Checkerboard.Res.MainLayer; } this.AdjustSize(pixelData.Width, pixelData.Height); this.SetupOpenGLRes(); if (this.texSizeMode != SizeMode.NonPowerOfTwo && (this.pxWidth != this.texWidth || this.pxHeight != this.texHeight)) { if (this.texSizeMode == SizeMode.Enlarge) { Pixmap.Layer oldData = pixelData; pixelData = oldData.CloneResize(this.texWidth, this.texHeight); // Fill border pixels manually - that's cheaper than ColorTransparentPixels here. oldData.DrawOnto(pixelData, BlendMode.Solid, this.pxWidth, 0, 1, this.pxHeight, this.pxWidth - 1, 0); oldData.DrawOnto(pixelData, BlendMode.Solid, 0, this.pxHeight, this.pxWidth, 1, 0, this.pxHeight - 1); } else { pixelData = pixelData.CloneRescale(this.texWidth, this.texHeight, Pixmap.FilterMethod.Linear); } } // Load pixel data to video memory GL.TexImage2D(TextureTarget.Texture2D, 0, this.pixelformat, pixelData.Width, pixelData.Height, 0, GLPixelFormat.Rgba, PixelType.UnsignedByte, pixelData.Data); // Adjust atlas to represent UV coordinates if (this.atlas != null) { Vector2 scale; scale.X = this.uvRatio.X / this.pxWidth; scale.Y = this.uvRatio.Y / this.pxHeight; for (int i = 0; i < this.atlas.Length; i++) { this.atlas[i].X *= scale.X; this.atlas[i].W *= scale.X; this.atlas[i].Y *= scale.Y; this.atlas[i].H *= scale.Y; } } } else { this.atlas = null; this.AdjustSize(this.size.X, this.size.Y); this.SetupOpenGLRes(); } GL.BindTexture(TextureTarget.Texture2D, lastTexId); }
/// <summary> /// Compiles the shader. This is done automatically when loading the shader /// or attaching it to a <see cref="Duality.Resources.ShaderProgram"/>. /// </summary> public void Compile() { DualityApp.GuardSingleThreadState(); if (this.compiled) { return; } if (String.IsNullOrEmpty(this.source)) { return; } if (this.glShaderId == 0) { this.glShaderId = GL.CreateShader(this.OglShaderType); } GL.ShaderSource(this.glShaderId, this.source); GL.CompileShader(this.glShaderId); int result; GL.GetShader(this.glShaderId, ShaderParameter.CompileStatus, out result); if (result == 0) { string infoLog = GL.GetShaderInfoLog(this.glShaderId); Log.Core.WriteError("Error compiling {0}. InfoLog:\n{1}", this.OglShaderType, infoLog); return; } this.compiled = true; // Remove comments from source code before extracting variables string sourceWithoutComments; { const string blockComments = @"/\*(.*?)\*/"; const string lineComments = @"//(.*?)\r?\n"; const string strings = @"""((\\[^\n]|[^""\n])*)"""; const string verbatimStrings = @"@(""[^""]*"")+"; sourceWithoutComments = Regex.Replace(this.source, blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings, me => { if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) { return(me.Value.StartsWith("//") ? Environment.NewLine : ""); } // Keep the literal strings return(me.Value); }, RegexOptions.Singleline); } // Scan remaining code chunk for variable declarations List <ShaderVarInfo> varInfoList = new List <ShaderVarInfo>(); string[] lines = sourceWithoutComments.Split(new[] { ';', '\n' }, StringSplitOptions.RemoveEmptyEntries); ShaderVarInfo varInfo = new ShaderVarInfo(); foreach (string t in lines) { string curLine = t.TrimStart(); if (curLine.StartsWith("uniform")) { varInfo.scope = ShaderVarScope.Uniform; } else if (curLine.StartsWith("attribute")) { varInfo.scope = ShaderVarScope.Attribute; } else { continue; } string[] curLineSplit = curLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); switch (curLineSplit[1].ToUpper()) { case "FLOAT": varInfo.type = ShaderVarType.Float; break; case "VEC2": varInfo.type = ShaderVarType.Vec2; break; case "VEC3": varInfo.type = ShaderVarType.Vec3; break; case "VEC4": varInfo.type = ShaderVarType.Vec4; break; case "MAT2": varInfo.type = ShaderVarType.Mat2; break; case "MAT3": varInfo.type = ShaderVarType.Mat3; break; case "MAT4": varInfo.type = ShaderVarType.Mat4; break; case "INT": varInfo.type = ShaderVarType.Int; break; case "SAMPLER2D": varInfo.type = ShaderVarType.Sampler2D; break; } curLineSplit = curLineSplit[2].Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries); varInfo.name = curLineSplit[0]; varInfo.arraySize = (curLineSplit.Length > 1) ? int.Parse(curLineSplit[1]) : 1; varInfo.glVarLoc = -1; varInfoList.Add(varInfo); } this.varInfo = varInfoList.ToArray(); }
/// <summary> /// Sets up all necessary OpenGL resources for this RenderTarget. /// </summary> public void SetupOpenGLRes() { DualityApp.GuardSingleThreadState(); if (this.targetInfo == null || this.targetInfo.Count == 0) { return; } int highestAALevel = MathF.RoundToInt(MathF.Log(MathF.Max(MaxRenderTargetSamples, 1.0f), 2.0f)); int targetAALevel = highestAALevel; switch (this.multisampling) { case AAQuality.High: targetAALevel = highestAALevel; break; case AAQuality.Medium: targetAALevel = highestAALevel / 2; break; case AAQuality.Low: targetAALevel = highestAALevel / 4; break; case AAQuality.Off: targetAALevel = 0; break; } int targetSampleCount = MathF.RoundToInt(MathF.Pow(2.0f, targetAALevel)); OpenTK.Graphics.GraphicsMode sampleMode = DualityApp.AvailableModes.LastOrDefault(m => m.Samples <= targetSampleCount) ?? DualityApp.AvailableModes.Last(); this.samples = sampleMode.Samples; #region Setup FBO & RBO: Non-multisampled if (this.samples == 0) { // Generate FBO if (this.glFboId == 0) { GL.Ext.GenFramebuffers(1, out this.glFboId); } GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, this.glFboId); // Attach textures int oglWidth = 0; int oglHeight = 0; for (int i = 0; i < this.targetInfo.Count; i++) { if (!this.targetInfo[i].target.IsAvailable) { continue; } FramebufferAttachment attachment = (FramebufferAttachment)((int)FramebufferAttachment.ColorAttachment0Ext + i); GL.Ext.FramebufferTexture2D( FramebufferTarget.FramebufferExt, attachment, TextureTarget.Texture2D, this.targetInfo[i].target.Res.OglTexId, 0); oglWidth = this.targetInfo[i].target.Res.TexelWidth; oglHeight = this.targetInfo[i].target.Res.TexelHeight; } // Generate Depth Renderbuffer if (this.glRboIdDepth == 0) { GL.Ext.GenRenderbuffers(1, out this.glRboIdDepth); } GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, this.glRboIdDepth); GL.Ext.RenderbufferStorage(RenderbufferTarget.RenderbufferExt, RenderbufferStorage.DepthComponent24, oglWidth, oglHeight); GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, this.glRboIdDepth); // Check status FramebufferErrorCode status = GL.Ext.CheckFramebufferStatus(FramebufferTarget.FramebufferExt); if (status != FramebufferErrorCode.FramebufferCompleteExt) { Log.Core.WriteError("Can't create RenderTarget '{0}'. Incomplete Framebuffer: {1}", this.path, status); } GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, 0); GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); } #endregion #region Setup FBO & RBO: Multisampled if (this.samples > 0) { // Generate texture target FBO if (this.glFboId == 0) { GL.Ext.GenFramebuffers(1, out this.glFboId); } GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, this.glFboId); // Attach textures int oglWidth = 0; int oglHeight = 0; for (int i = 0; i < this.targetInfo.Count; i++) { if (!this.targetInfo[i].target.IsAvailable) { continue; } FramebufferAttachment attachment = (FramebufferAttachment)((int)FramebufferAttachment.ColorAttachment0Ext + i); GL.Ext.FramebufferTexture2D( FramebufferTarget.FramebufferExt, attachment, TextureTarget.Texture2D, this.targetInfo[i].target.Res.OglTexId, 0); oglWidth = this.targetInfo[i].target.Res.TexelWidth; oglHeight = this.targetInfo[i].target.Res.TexelHeight; } // Check status FramebufferErrorCode status = GL.Ext.CheckFramebufferStatus(FramebufferTarget.FramebufferExt); if (status != FramebufferErrorCode.FramebufferCompleteExt) { Log.Core.WriteError("Can't create RenderTarget '{0}'. Incomplete Texture Framebuffer: {1}", this.path, status); } // Generate rendering FBO if (this.glFboIdMSAA == 0) { GL.Ext.GenFramebuffers(1, out this.glFboIdMSAA); } GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, this.glFboIdMSAA); // Attach color renderbuffers for (int i = 0; i < this.targetInfo.Count; i++) { if (!this.targetInfo[i].target.IsAvailable) { continue; } TargetInfo info = this.targetInfo[i]; FramebufferAttachment attachment = (FramebufferAttachment)((int)FramebufferAttachment.ColorAttachment0Ext + i); RenderbufferStorage rbColorFormat = TexFormatToRboFormat(info.target.Res.PixelFormat); if (info.glRboIdColorMSAA == 0) { GL.GenRenderbuffers(1, out info.glRboIdColorMSAA); } GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, info.glRboIdColorMSAA); GL.Ext.RenderbufferStorageMultisample(RenderbufferTarget.RenderbufferExt, this.samples, rbColorFormat, oglWidth, oglHeight); GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, attachment, RenderbufferTarget.RenderbufferExt, info.glRboIdColorMSAA); this.targetInfo[i] = info; } GL.Ext.BindRenderbuffer(RenderbufferTarget.Renderbuffer, 0); // Attach depth renderbuffer if (this.glRboIdDepth == 0) { GL.Ext.GenRenderbuffers(1, out this.glRboIdDepth); } GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, this.glRboIdDepth); GL.Ext.RenderbufferStorageMultisample(RenderbufferTarget.RenderbufferExt, this.samples, RenderbufferStorage.DepthComponent24, oglWidth, oglHeight); GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, this.glRboIdDepth); GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, 0); // Check status status = GL.Ext.CheckFramebufferStatus(FramebufferTarget.FramebufferExt); if (status != FramebufferErrorCode.FramebufferCompleteExt) { Log.Core.WriteError("Can't create RenderTarget '{0}'. Incomplete Multisample Framebuffer: {1}", this.path, status); } GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); } #endregion }