1 #region Copyright & License Information 2 /* 3 * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) 4 * This file is part of OpenRA, which is free software. It is made 5 * available to you under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, either version 3 of 7 * the License, or (at your option) any later version. For more 8 * information, see COPYING. 9 */ 10 #endregion 11 12 using System; 13 using System.Diagnostics; 14 using System.IO; 15 using OpenRA.Primitives; 16 17 namespace OpenRA.Platforms.Default 18 { 19 sealed class FrameBuffer : ThreadAffine, IFrameBuffer 20 { 21 readonly ITexture texture; 22 readonly Size size; 23 readonly Color clearColor; 24 uint framebuffer, depth; 25 bool disposed; 26 bool scissored; 27 FrameBuffer(Size size, ITextureInternal texture, Color clearColor)28 public FrameBuffer(Size size, ITextureInternal texture, Color clearColor) 29 { 30 this.size = size; 31 this.clearColor = clearColor; 32 if (!Exts.IsPowerOf2(size.Width) || !Exts.IsPowerOf2(size.Height)) 33 throw new InvalidDataException("Frame buffer size ({0}x{1}) must be a power of two".F(size.Width, size.Height)); 34 35 OpenGL.glGenFramebuffers(1, out framebuffer); 36 OpenGL.CheckGLError(); 37 OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, framebuffer); 38 OpenGL.CheckGLError(); 39 40 // Color 41 this.texture = texture; 42 texture.SetEmpty(size.Width, size.Height); 43 OpenGL.glFramebufferTexture2D(OpenGL.GL_FRAMEBUFFER, OpenGL.GL_COLOR_ATTACHMENT0, OpenGL.GL_TEXTURE_2D, texture.ID, 0); 44 OpenGL.CheckGLError(); 45 46 // Depth 47 OpenGL.glGenRenderbuffers(1, out depth); 48 OpenGL.CheckGLError(); 49 50 OpenGL.glBindRenderbuffer(OpenGL.GL_RENDERBUFFER, depth); 51 OpenGL.CheckGLError(); 52 53 var glDepth = OpenGL.Profile == GLProfile.Embedded ? OpenGL.GL_DEPTH_COMPONENT16 : OpenGL.GL_DEPTH_COMPONENT; 54 OpenGL.glRenderbufferStorage(OpenGL.GL_RENDERBUFFER, glDepth, size.Width, size.Height); 55 OpenGL.CheckGLError(); 56 57 OpenGL.glFramebufferRenderbuffer(OpenGL.GL_FRAMEBUFFER, OpenGL.GL_DEPTH_ATTACHMENT, OpenGL.GL_RENDERBUFFER, depth); 58 OpenGL.CheckGLError(); 59 60 // Test for completeness 61 var status = OpenGL.glCheckFramebufferStatus(OpenGL.GL_FRAMEBUFFER); 62 if (status != OpenGL.GL_FRAMEBUFFER_COMPLETE) 63 { 64 var error = "Error creating framebuffer: {0}\n{1}".F(status, new StackTrace()); 65 OpenGL.WriteGraphicsLog(error); 66 throw new InvalidOperationException("OpenGL Error: See graphics.log for details."); 67 } 68 69 // Restore default buffer 70 OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, 0); 71 OpenGL.CheckGLError(); 72 } 73 ViewportRectangle()74 static int[] ViewportRectangle() 75 { 76 var v = new int[4]; 77 OpenGL.glGetIntegerv(OpenGL.GL_VIEWPORT, out v[0]); 78 OpenGL.CheckGLError(); 79 return v; 80 } 81 82 int[] cv = new int[4]; Bind()83 public void Bind() 84 { 85 VerifyThreadAffinity(); 86 87 // Cache viewport rect to restore when unbinding 88 cv = ViewportRectangle(); 89 90 OpenGL.glFlush(); 91 OpenGL.CheckGLError(); 92 OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, framebuffer); 93 OpenGL.CheckGLError(); 94 OpenGL.glViewport(0, 0, size.Width, size.Height); 95 OpenGL.CheckGLError(); 96 OpenGL.glClearColor(clearColor.R, clearColor.G, clearColor.B, clearColor.A); 97 OpenGL.CheckGLError(); 98 OpenGL.glClear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); 99 OpenGL.CheckGLError(); 100 } 101 Unbind()102 public void Unbind() 103 { 104 if (scissored) 105 throw new InvalidOperationException("Attempting to unbind FrameBuffer with an active scissor region."); 106 107 VerifyThreadAffinity(); 108 OpenGL.glFlush(); 109 OpenGL.CheckGLError(); 110 OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, 0); 111 OpenGL.CheckGLError(); 112 OpenGL.glViewport(cv[0], cv[1], cv[2], cv[3]); 113 OpenGL.CheckGLError(); 114 } 115 EnableScissor(Rectangle rect)116 public void EnableScissor(Rectangle rect) 117 { 118 VerifyThreadAffinity(); 119 120 OpenGL.glScissor(rect.X, rect.Y, Math.Max(rect.Width, 0), Math.Max(rect.Height, 0)); 121 OpenGL.CheckGLError(); 122 OpenGL.glEnable(OpenGL.GL_SCISSOR_TEST); 123 OpenGL.CheckGLError(); 124 scissored = true; 125 } 126 DisableScissor()127 public void DisableScissor() 128 { 129 VerifyThreadAffinity(); 130 OpenGL.glDisable(OpenGL.GL_SCISSOR_TEST); 131 OpenGL.CheckGLError(); 132 scissored = false; 133 } 134 135 public ITexture Texture 136 { 137 get 138 { 139 VerifyThreadAffinity(); 140 return texture; 141 } 142 } 143 Dispose()144 public void Dispose() 145 { 146 Dispose(true); 147 GC.SuppressFinalize(this); 148 } 149 Dispose(bool disposing)150 void Dispose(bool disposing) 151 { 152 if (disposed) 153 return; 154 disposed = true; 155 if (disposing) 156 texture.Dispose(); 157 158 OpenGL.glDeleteFramebuffers(1, ref framebuffer); 159 OpenGL.CheckGLError(); 160 OpenGL.glDeleteRenderbuffers(1, ref depth); 161 OpenGL.CheckGLError(); 162 } 163 } 164 } 165