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