void IDualityBackend.Shutdown() { if (activeInstance == this) { activeInstance = null; } if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); for (int i = 0; i < this.perVertexTypeVBO.Count; i++) { uint handle = this.perVertexTypeVBO[i]; if (handle != 0) { GL.DeleteBuffers(1, ref handle); } } this.perVertexTypeVBO.Clear(); } // Since the window outlives the graphics backend in the usual launcher setup, // we'll need to unhook early, so Duality can complete its cleanup before the window does. if (this.activeWindow != null) { this.activeWindow.UnhookFromDuality(); this.activeWindow = null; } }
void IDisposable.Dispose() { if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated && this.handle != 0) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); GL.DeleteShader(this.handle); this.handle = 0; } }
public void ApplyPostRender() { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); if (!this.pendingPostRender) { return; } // Resolve multisampling to the main FBO if (this.samples > 0) { GL.Ext.BindFramebuffer(FramebufferTarget.ReadFramebuffer, this.handleMsaaFBO); GL.Ext.BindFramebuffer(FramebufferTarget.DrawFramebuffer, this.handleMainFBO); for (int i = 0; i < this.targetInfos.Count; i++) { GL.ReadBuffer((ReadBufferMode)((int)ReadBufferMode.ColorAttachment0 + i)); GL.DrawBuffer((DrawBufferMode)((int)DrawBufferMode.ColorAttachment0 + i)); GL.Ext.BlitFramebuffer( 0, 0, this.targetInfos.Data[i].Target.Width, this.targetInfos.Data[i].Target.Height, 0, 0, this.targetInfos.Data[i].Target.Width, this.targetInfos.Data[i].Target.Height, ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Nearest); } } // Generate mipmaps for the target textures int lastTexId = -1; for (int i = 0; i < this.targetInfos.Count; i++) { if (!this.targetInfos.Data[i].Target.HasMipmaps) { continue; } if (lastTexId == -1) { GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); } int texId = this.targetInfos.Data[i].Target.Handle; GL.BindTexture(TextureTarget.Texture2D, texId); GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D); } // Reset OpenGL state if (lastTexId != -1) { GL.BindTexture(TextureTarget.Texture2D, lastTexId); } ApplyGLBind(curBound); this.pendingPostRender = false; }
void IDualityBackend.Init() { // Initialize OpenTK, if not done yet DefaultOpenTKBackendPlugin.InitOpenTK(); App.Log("Active graphics backend: OpenGL 2.1"); // Log information about the available display devices GraphicsBackend.LogDisplayDevices(); // Determine available and default graphics modes this.QueryGraphicsModes(); activeInstance = this; }
void INativeTexture.GetData(IntPtr target, ColorDataLayout dataLayout, ColorDataElementType dataElementType) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); int lastTexId; GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); GL.BindTexture(TextureTarget.Texture2D, this.handle); GL.GetTexImage(TextureTarget.Texture2D, 0, dataLayout.ToOpenTK(), dataElementType.ToOpenTK(), target); GL.BindTexture(TextureTarget.Texture2D, lastTexId); }
void INativeRenderTarget.GetData <T>(T[] buffer, ColorDataLayout dataLayout, ColorDataElementType dataElementType, int targetIndex, int x, int y, int width, int height) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); this.ApplyPostRender(); if (curBound != this) { ApplyGLBind(this); } { GL.Ext.BindFramebuffer(FramebufferTarget.ReadFramebuffer, this.handleMainFBO); GL.ReadBuffer((ReadBufferMode)((int)ReadBufferMode.ColorAttachment0 + targetIndex)); GL.ReadPixels(x, y, width, height, dataLayout.ToOpenTK(), dataElementType.ToOpenTK(), buffer); } ApplyGLBind(curBound); }
void IGraphicsBackend.GetOutputPixelData(IntPtr buffer, ColorDataLayout dataLayout, ColorDataElementType dataElementType, int x, int y, int width, int height) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); NativeRenderTarget lastRt = NativeRenderTarget.BoundRT; NativeRenderTarget.Bind(null); { // Use a temporary local buffer, since the image will be upside-down because // of OpenGL's coordinate system and we'll need to flip it before returning. byte[] byteData = new byte[width * height * 4]; // Retrieve pixel data GL.ReadPixels(x, y, width, height, dataLayout.ToOpenTK(), dataElementType.ToOpenTK(), byteData); // Flip the retrieved image vertically int bytesPerLine = width * 4; byte[] switchLine = new byte[width * 4]; for (int flipY = 0; flipY < height / 2; flipY++) { int lineIndex = flipY * width * 4; int lineIndex2 = (height - 1 - flipY) * width * 4; // Copy the current line to the switch buffer for (int lineX = 0; lineX < bytesPerLine; lineX++) { switchLine[lineX] = byteData[lineIndex + lineX]; } // Copy the opposite line to the current line for (int lineX = 0; lineX < bytesPerLine; lineX++) { byteData[lineIndex + lineX] = byteData[lineIndex2 + lineX]; } // Copy the switch buffer to the opposite line for (int lineX = 0; lineX < bytesPerLine; lineX++) { byteData[lineIndex2 + lineX] = switchLine[lineX]; } } // Copy the flipped data to the output buffer Marshal.Copy(byteData, 0, buffer, width * height * 4); } NativeRenderTarget.Bind(lastRt); }
void INativeTexture.SetupEmpty(TexturePixelFormat format, int width, int height, TextureMinFilter minFilter, TextureMagFilter magFilter, TextureWrapMode wrapX, TextureWrapMode wrapY, int anisoLevel, bool mipmaps) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); int lastTexId; GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); if (lastTexId != this.handle) { GL.BindTexture(TextureTarget.Texture2D, this.handle); } // Set texture parameters GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)ToOpenTKTextureMinFilter(minFilter)); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)ToOpenTKTextureMagFilter(magFilter)); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)ToOpenTKTextureWrapMode(wrapX)); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)ToOpenTKTextureWrapMode(wrapY)); // Anisotropic filtering if (anisoLevel > 0) { GL.TexParameter(TextureTarget.Texture2D, (TextureParameterName)ExtTextureFilterAnisotropic.TextureMaxAnisotropyExt, (float)anisoLevel); } // If needed, care for Mipmaps GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, mipmaps ? 1 : 0); // Setup pixel format GL.TexImage2D(TextureTarget.Texture2D, 0, ToOpenTKPixelFormat(format), width, height, 0, GLPixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); this.width = width; this.height = height; this.format = format; this.mipmaps = mipmaps; if (lastTexId != this.handle) { GL.BindTexture(TextureTarget.Texture2D, lastTexId); } }
void INativeTexture.LoadData(TexturePixelFormat format, int width, int height, IntPtr data, ColorDataLayout dataLayout, ColorDataElementType dataElementType) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); int lastTexId; GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); GL.BindTexture(TextureTarget.Texture2D, this.handle); // Load pixel data to video memory GL.TexImage2D(TextureTarget.Texture2D, 0, ToOpenTKPixelFormat(format), width, height, 0, dataLayout.ToOpenTK(), dataElementType.ToOpenTK(), data); this.width = width; this.height = height; this.format = format; GL.BindTexture(TextureTarget.Texture2D, lastTexId); }
void IGraphicsBackend.GetOutputPixelData <T>(T[] buffer, ColorDataLayout dataLayout, ColorDataElementType dataElementType, int x, int y, int width, int height) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); NativeRenderTarget lastRt = NativeRenderTarget.BoundRT; NativeRenderTarget.Bind(null); { GL.ReadPixels(x, y, width, height, dataLayout.ToOpenTK(), dataElementType.ToOpenTK(), buffer); // The image will be upside-down because of OpenGL's coordinate system. Flip it. int structSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); T[] switchLine = new T[width * 4 / structSize]; for (int flipY = 0; flipY < height / 2; flipY++) { int lineIndex = flipY * width * 4 / structSize; int lineIndex2 = (height - 1 - flipY) * width * 4 / structSize; // Copy the current line to the switch buffer for (int lineX = 0; lineX < width; lineX++) { switchLine[lineX] = buffer[lineIndex + lineX]; } // Copy the opposite line to the current line for (int lineX = 0; lineX < width; lineX++) { buffer[lineIndex + lineX] = buffer[lineIndex2 + lineX]; } // Copy the switch buffer to the opposite line for (int lineX = 0; lineX < width; lineX++) { buffer[lineIndex2 + lineX] = switchLine[lineX]; } } } NativeRenderTarget.Bind(lastRt); }
void IDisposable.Dispose() { if (DualityApp.ExecContext == DualityApp.ExecutionContext.Terminated) { return; } DefaultOpenTKBackendPlugin.GuardSingleThreadState(); // If there are changes pending to be applied to the bound textures, // they should be executed before the render target is gone. this.ApplyPostRender(); if (this.handleMainFBO != 0) { GL.Ext.DeleteFramebuffers(1, ref this.handleMainFBO); this.handleMainFBO = 0; } if (this.handleDepthRBO != 0) { GL.Ext.DeleteRenderbuffers(1, ref this.handleDepthRBO); this.handleDepthRBO = 0; } if (this.handleMsaaFBO != 0) { GL.Ext.DeleteFramebuffers(1, ref this.handleMsaaFBO); this.handleMsaaFBO = 0; } for (int i = 0; i < this.targetInfos.Count; i++) { if (this.targetInfos.Data[i].HandleMsaaColorRBO != 0) { GL.Ext.DeleteRenderbuffers(1, ref this.targetInfos.Data[i].HandleMsaaColorRBO); this.targetInfos.Data[i].HandleMsaaColorRBO = 0; } } }
void INativeShaderPart.LoadSource(string sourceCode, ShaderType type) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); if (this.handle == 0) { this.handle = GL.CreateShader(GetOpenTKShaderType(type)); } GL.ShaderSource(this.handle, sourceCode); GL.CompileShader(this.handle); int result; GL.GetShader(this.handle, ShaderParameter.CompileStatus, out result); if (result == 0) { string infoLog = GL.GetShaderInfoLog(this.handle); throw new BackendException(string.Format("{0} Compiler error:{2}{1}", type, infoLog, Environment.NewLine)); } // 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(sourceCode, blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings, match => { if (match.Value.StartsWith("/*") || match.Value.StartsWith("//")) { return(match.Value.StartsWith("//") ? Environment.NewLine : ""); } else { return(match.Value); } }, RegexOptions.Singleline); } // Scan remaining code chunk for variable declarations List <ShaderFieldInfo> varInfoList = new List <ShaderFieldInfo>(); string[] lines = sourceWithoutComments.Split(new[] { ';', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string t in lines) { string curLine = t.TrimStart(); ShaderFieldScope scope; int arrayLength; if (curLine.StartsWith("uniform")) { scope = ShaderFieldScope.Uniform; } else if (curLine.StartsWith("attribute")) { scope = ShaderFieldScope.Attribute; } else { continue; } string[] curLineSplit = curLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); ShaderFieldType varType = ShaderFieldType.Unknown; switch (curLineSplit[1].ToUpper()) { case "FLOAT": varType = ShaderFieldType.Float; break; case "VEC2": varType = ShaderFieldType.Vec2; break; case "VEC3": varType = ShaderFieldType.Vec3; break; case "VEC4": varType = ShaderFieldType.Vec4; break; case "MAT2": varType = ShaderFieldType.Mat2; break; case "MAT3": varType = ShaderFieldType.Mat3; break; case "MAT4": varType = ShaderFieldType.Mat4; break; case "INT": varType = ShaderFieldType.Int; break; case "BOOL": varType = ShaderFieldType.Bool; break; case "SAMPLER2D": varType = ShaderFieldType.Sampler2D; break; } curLineSplit = curLineSplit[2].Split(new[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries); arrayLength = (curLineSplit.Length > 1) ? int.Parse(curLineSplit[1]) : 1; varInfoList.Add(new ShaderFieldInfo(curLineSplit[0], varType, scope, arrayLength)); } this.fields = varInfoList.ToArray(); }
void INativeShaderProgram.LoadProgram(INativeShaderPart vertex, INativeShaderPart fragment) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); if (this.handle == 0) { this.handle = GL.CreateProgram(); } else { this.DetachShaders(); } // Attach both shaders if (vertex != null) { GL.AttachShader(this.handle, (vertex as NativeShaderPart).Handle); } if (fragment != null) { GL.AttachShader(this.handle, (fragment as NativeShaderPart).Handle); } // Link the shader program GL.LinkProgram(this.handle); int result; GL.GetProgram(this.handle, GetProgramParameterName.LinkStatus, out result); if (result == 0) { string errorLog = GL.GetProgramInfoLog(this.handle); this.RollbackAtFault(); throw new BackendException(string.Format("Linker error:{1}{0}", errorLog, Environment.NewLine)); } // Collect variable infos from sub programs { NativeShaderPart vert = vertex as NativeShaderPart; NativeShaderPart frag = fragment as NativeShaderPart; ShaderFieldInfo[] fragVarArray = frag != null ? frag.Fields : null; ShaderFieldInfo[] vertVarArray = vert != null ? vert.Fields : null; if (fragVarArray != null && vertVarArray != null) { this.fields = vertVarArray.Union(fragVarArray).ToArray(); } else if (vertVarArray != null) { this.fields = vertVarArray.ToArray(); } else { this.fields = fragVarArray.ToArray(); } } // Determine each variables location this.fieldLocations = new int[this.fields.Length]; for (int i = 0; i < this.fields.Length; i++) { if (this.fields[i].Scope == ShaderFieldScope.Uniform) { this.fieldLocations[i] = GL.GetUniformLocation(this.handle, this.fields[i].Name); } else { this.fieldLocations[i] = GL.GetAttribLocation(this.handle, this.fields[i].Name); } } }
void INativeRenderTarget.Setup(IReadOnlyList <INativeTexture> targets, AAQuality multisample, bool depthBuffer) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); if (targets == null) { return; } if (targets.Count == 0) { return; } if (targets.All(i => i == null)) { return; } int highestAALevel = MathF.RoundToInt(MathF.Log(MathF.Max(MaxRenderTargetSamples, 1.0f), 2.0f)); int targetAALevel = highestAALevel; switch (multisample) { 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)); GraphicsMode sampleMode = GraphicsBackend.ActiveInstance.AvailableGraphicsModes.LastOrDefault(m => m.Samples <= targetSampleCount) ?? GraphicsBackend.ActiveInstance.AvailableGraphicsModes.Last(); this.samples = sampleMode.Samples; this.depthBuffer = depthBuffer; // Synchronize target information { this.targetInfos.Reserve(targets.Count); int localIndex = 0; for (int i = 0; i < targets.Count; i++) { if (targets[i] == null) { continue; } this.targetInfos.Count = Math.Max(this.targetInfos.Count, localIndex + 1); this.targetInfos.Data[localIndex].Target = targets[i] as NativeTexture; localIndex++; } } // Setup OpenGL resources if (this.samples > 0) { this.SetupMultisampled(); } else { this.SetupNonMultisampled(); } }