void IDisposable.Dispose() { if (DualityApp.ExecContext == DualityApp.ExecutionContext.Terminated) { return; } DefaultOpenTKBackendPlugin.GuardSingleThreadState(); 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 IDualityBackend.Shutdown() { if (activeInstance == this) { activeInstance = null; } if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); if (this.sharedBatchIBO != null) { this.sharedBatchIBO.Dispose(); this.sharedBatchIBO = null; } } // 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 IDualityBackend.Init() { AudioLibraryLoader.LoadAudioLibrary(); // Initialize OpenTK, if not done yet DefaultOpenTKBackendPlugin.InitOpenTK(); Log.Core.Write("Available devices:" + Environment.NewLine + "{0}", AudioContext.AvailableDevices.ToString(d => d == AudioContext.DefaultDevice ? d + " (Default)" : d, "," + Environment.NewLine)); // Create OpenAL audio context this.context = new AudioContext(); Log.Core.Write("Current device: {0}", this.context.CurrentDevice); // Create extension interfaces try { this.extFx = new EffectsExtension(); if (!this.extFx.IsInitialized) { this.extFx = null; } } catch (Exception) { this.extFx = null; } activeInstance = this; // Log all OpenAL specs for diagnostic purposes LogOpenALSpecs(); // Generate OpenAL source pool for (int i = 0; i < 256; i++) { int newSrc = AL.GenSource(); if (!Backend.DefaultOpenTK.AudioBackend.CheckOpenALErrors(true)) { this.sourcePool.Push(newSrc); } else { break; } } this.availSources = this.sourcePool.Count; Log.Core.Write("{0} sources available", this.sourcePool.Count); // Set up the streaming thread this.streamWorkerEnd = false; this.streamWorkerQueue = new List <NativeAudioSource>(); this.streamWorkerQueueEvent = new AutoResetEvent(false); this.streamWorker = new Thread(ThreadStreamFunc); this.streamWorker.IsBackground = true; this.streamWorker.Start(); }
void IDualityBackend.Init() { // Initialize OpenTK, if not done yet DefaultOpenTKBackendPlugin.InitOpenTK(); // Determine available and default graphics modes this.QueryGraphicsModes(); activeInstance = this; }
void INativeShaderPart.LoadSource(string sourceCode, ShaderType type) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); this.type = type; if (this.handle == 0) { this.handle = GL.CreateShader(GetOpenTKShaderType(type)); } GL.ShaderSource(this.handle, sourceCode); GL.CompileShader(this.handle); // Log all errors and warnings from the info log string infoLog = GL.GetShaderInfoLog(this.handle); if (!string.IsNullOrWhiteSpace(infoLog)) { using (StringReader reader = new StringReader(infoLog)) { while (true) { string line = reader.ReadLine(); if (line == null) { break; } if (string.IsNullOrWhiteSpace(line)) { continue; } if (line.IndexOf("warning", StringComparison.InvariantCultureIgnoreCase) != -1) { Logs.Core.WriteWarning("{0}", line); } else if (line.IndexOf("error", StringComparison.InvariantCultureIgnoreCase) != -1) { Logs.Core.WriteError("{0}", line); } else { Logs.Core.Write("{0}", line); } } } } // If compilation failed, throw an exception int result; GL.GetShader(this.handle, ShaderParameter.CompileStatus, out result); if (result == 0) { throw new BackendException(string.Format("Failed to compile {0} shader:{2}{1}", type, infoLog, Environment.NewLine)); } }
void IDisposable.Dispose() { if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated && this.handle != 0) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); GL.DeleteTexture(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; }
public void Dispose() { if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated && this.handle != 0) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); GL.DeleteBuffer(this.handle); } this.handle = 0; this.bufferSize = 0; }
void IDualityBackend.Init() { // Initialize OpenTK, if not done yet DefaultOpenTKBackendPlugin.InitOpenTK(); // Log information about the available display devices GraphicsBackend.LogDisplayDevices(); // Determine available and default graphics modes this.QueryGraphicsModes(); activeInstance = this; }
void INativeRenderTarget.GetData <T>(T[] buffer, ColorDataLayout dataLayout, ColorDataElementType dataElementType, int targetIndex, int x, int y, int width, int height) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); NativeRenderTarget lastRt = BoundRT; Bind(this); { GL.ReadBuffer((ReadBufferMode)((int)ReadBufferMode.ColorAttachment0 + targetIndex)); GL.ReadPixels(x, y, width, height, dataLayout.ToOpenTK(), dataElementType.ToOpenTK(), buffer); GL.ReadBuffer(ReadBufferMode.Back); } Bind(lastRt); }
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(IntPtr 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 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); } } // Determine whether we're using builtin shader variables this.builtinIndex = new int[this.fields.Length]; bool anyBuildinUsed = false; for (int i = 0; i < this.fields.Length; i++) { if (this.fields[i].Scope == ShaderFieldScope.Uniform) { this.builtinIndex[i] = BuiltinShaderFields.GetIndex(this.fields[i].Name); if (this.builtinIndex[i] != BuiltinShaderFields.InvalidIndex) { anyBuildinUsed = true; } } else { this.builtinIndex[i] = BuiltinShaderFields.InvalidIndex; } } if (!anyBuildinUsed) { this.builtinIndex = new int[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 "SAMPLER2D": varType = ShaderFieldType.Sampler2D; break; } curLineSplit = curLineSplit[2].Split(new char[] { '[', ']' }, 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(IEnumerable <INativeShaderPart> shaderParts, IEnumerable <ShaderFieldInfo> shaderFields) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); // Verify that we have exactly one shader part for each stage. // Other scenarios are valid in desktop GL, but not GL ES, so // we'll enforce the stricter rules manually to ease portability. int vertexCount = 0; int fragmentCount = 0; foreach (INativeShaderPart part in shaderParts) { Resources.ShaderType type = (part as NativeShaderPart).Type; if (type == Resources.ShaderType.Fragment) { fragmentCount++; } else if (type == Resources.ShaderType.Vertex) { vertexCount++; } } if (vertexCount == 0) { throw new ArgumentException("Cannot load program without vertex shader."); } if (fragmentCount == 0) { throw new ArgumentException("Cannot load program without fragment shader."); } if (vertexCount > 1) { throw new ArgumentException("Cannot attach multiple vertex shaders to the same program."); } if (fragmentCount > 1) { throw new ArgumentException("Cannot attach multiple fragment shaders to the same program."); } // Create or reset GL program if (this.handle == 0) { this.handle = GL.CreateProgram(); } else { this.DetachShaders(); } // Attach all individual shaders to the program foreach (INativeShaderPart part in shaderParts) { GL.AttachShader(this.handle, (part 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 variables that are available through shader reflection, e.g. // haven't been optimized of #ifdef'd away. List <int> validLocations = new List <int>(); List <ShaderFieldInfo> validFields = new List <ShaderFieldInfo>(); foreach (ShaderFieldInfo field in shaderFields) { int location; if (field.Scope == ShaderFieldScope.Uniform) { location = GL.GetUniformLocation(this.handle, field.Name); } else { location = GL.GetAttribLocation(this.handle, field.Name); } if (location >= 0) { validLocations.Add(location); validFields.Add(field); } } this.fields = validFields.ToArray(); this.fieldLocations = validLocations.ToArray(); }
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(); } }
void INativeRenderTarget.Setup(IReadOnlyList <INativeTexture> targets, AAQuality multisample) { 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; // 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++; } } #region Setup FBO & RBO: Non-multisampled if (this.samples == 0) { // Generate FBO if (this.handleMainFBO == 0) { GL.Ext.GenFramebuffers(1, out this.handleMainFBO); } GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, this.handleMainFBO); // Attach textures int oglWidth = 0; int oglHeight = 0; for (int i = 0; i < this.targetInfos.Count; i++) { NativeTexture tex = this.targetInfos[i].Target; FramebufferAttachment attachment = (FramebufferAttachment)((int)FramebufferAttachment.ColorAttachment0Ext + i); GL.Ext.FramebufferTexture2D( FramebufferTarget.FramebufferExt, attachment, TextureTarget.Texture2D, tex.Handle, 0); oglWidth = tex.Width; oglHeight = tex.Height; } // Generate Depth Renderbuffer if (this.handleDepthRBO == 0) { GL.Ext.GenRenderbuffers(1, out this.handleDepthRBO); } GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, this.handleDepthRBO); GL.Ext.RenderbufferStorage(RenderbufferTarget.RenderbufferExt, RenderbufferStorage.DepthComponent24, oglWidth, oglHeight); GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, this.handleDepthRBO); // Check status FramebufferErrorCode status = GL.Ext.CheckFramebufferStatus(FramebufferTarget.FramebufferExt); if (status != FramebufferErrorCode.FramebufferCompleteExt) { throw new BackendException(string.Format("Incomplete Framebuffer: {0}", 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.handleMainFBO == 0) { GL.Ext.GenFramebuffers(1, out this.handleMainFBO); } GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, this.handleMainFBO); // Attach textures int oglWidth = 0; int oglHeight = 0; for (int i = 0; i < this.targetInfos.Count; i++) { NativeTexture tex = this.targetInfos[i].Target; FramebufferAttachment attachment = (FramebufferAttachment)((int)FramebufferAttachment.ColorAttachment0Ext + i); GL.Ext.FramebufferTexture2D( FramebufferTarget.FramebufferExt, attachment, TextureTarget.Texture2D, tex.Handle, 0); oglWidth = tex.Width; oglHeight = tex.Height; } // Check status FramebufferErrorCode status = GL.Ext.CheckFramebufferStatus(FramebufferTarget.FramebufferExt); if (status != FramebufferErrorCode.FramebufferCompleteExt) { throw new BackendException(string.Format("Incomplete Framebuffer: {0}", status)); } // Generate rendering FBO if (this.handleMsaaFBO == 0) { GL.Ext.GenFramebuffers(1, out this.handleMsaaFBO); } GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, this.handleMsaaFBO); // Attach color renderbuffers for (int i = 0; i < this.targetInfos.Count; i++) { TargetInfo info = this.targetInfos.Data[i]; FramebufferAttachment attachment = (FramebufferAttachment)((int)FramebufferAttachment.ColorAttachment0Ext + i); RenderbufferStorage rbColorFormat = TexFormatToRboFormat(info.Target.Format); if (info.HandleMsaaColorRBO == 0) { GL.GenRenderbuffers(1, out info.HandleMsaaColorRBO); } GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, info.HandleMsaaColorRBO); GL.Ext.RenderbufferStorageMultisample(RenderbufferTarget.RenderbufferExt, this.samples, rbColorFormat, oglWidth, oglHeight); GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, attachment, RenderbufferTarget.RenderbufferExt, info.HandleMsaaColorRBO); this.targetInfos.Data[i] = info; } GL.Ext.BindRenderbuffer(RenderbufferTarget.Renderbuffer, 0); // Attach depth renderbuffer if (this.handleDepthRBO == 0) { GL.Ext.GenRenderbuffers(1, out this.handleDepthRBO); } GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, this.handleDepthRBO); GL.Ext.RenderbufferStorageMultisample(RenderbufferTarget.RenderbufferExt, this.samples, RenderbufferStorage.DepthComponent24, oglWidth, oglHeight); GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, this.handleDepthRBO); GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, 0); // Check status status = GL.Ext.CheckFramebufferStatus(FramebufferTarget.FramebufferExt); if (status != FramebufferErrorCode.FramebufferCompleteExt) { throw new BackendException(string.Format("Incomplete Multisample Framebuffer: {0}", status)); } GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); } #endregion }