1 /**
2  * @file
3  * @brief Framebuffer Objects support
4  */
5 
6 /*
7  Copyright (C) 2008 Victor Luchits
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License
11  as published by the Free Software Foundation; either version 2
12  of the License, or (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18  See the GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with this program; if not, write to the Free Software
22  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24 
25 #include "r_local.h"
26 #include "r_framebuffer.h"
27 #include "r_error.h"
28 
29 static int frameBufferObjectCount;
30 static r_framebuffer_t frameBufferObjects[MAX_GL_FRAMEBUFFERS];
31 static GLuint frameBufferTextures[MAX_GL_FRAMEBUFFERS];
32 
33 static r_framebuffer_t screenBuffer;
34 static GLenum* colorAttachments;
35 
R_GetFreeFBOTexture(void)36 static GLuint R_GetFreeFBOTexture (void)
37 {
38 	int i;
39 
40 	for (i = 0; i < MAX_GL_FRAMEBUFFERS; i++) {
41 		if (frameBufferTextures[i] == 0) {
42 			glGenTextures(1, &frameBufferTextures[i]);
43 			return frameBufferTextures[i];
44 		}
45 	}
46 
47 	Com_Error(ERR_FATAL, "Exceeded max frame buffer textures");
48 }
49 
R_FreeFBOTexture(int texnum)50 static void R_FreeFBOTexture (int texnum)
51 {
52 	int i;
53 
54 	for (i = 0; i < MAX_GL_FRAMEBUFFERS; i++) {
55 		if (frameBufferTextures[i] == texnum)
56 			break;
57 	}
58 
59 	assert(i >= 0);
60 	assert(i < MAX_GL_FRAMEBUFFERS);
61 	glDeleteTextures(1, &frameBufferTextures[i]);
62 	frameBufferTextures[i] = 0;
63 }
64 
R_InitFBObjects(void)65 void R_InitFBObjects (void)
66 {
67 	unsigned int filters[2];
68 	float scales[DOWNSAMPLE_PASSES];
69 	int i;
70 
71 	if (!r_config.frameBufferObject || !r_programs->integer)
72 		return;
73 
74 	frameBufferObjectCount = 0;
75 	OBJZERO(frameBufferObjects);
76 	OBJZERO(frameBufferTextures);
77 
78 	r_state.frameBufferObjectsInitialized = true;
79 
80 	for (i = 0; i < DOWNSAMPLE_PASSES; i++)
81 		scales[i] = powf(DOWNSAMPLE_SCALE, i + 1);
82 
83 	/* setup default screen framebuffer */
84 	screenBuffer.fbo = 0;
85 	screenBuffer.depth = 0;
86 	screenBuffer.nTextures = 0;
87 	screenBuffer.width = viddef.context.width;
88 	screenBuffer.height = viddef.context.height;
89 	R_SetupViewport(&screenBuffer, 0, 0, viddef.context.width, viddef.context.height);
90 	Vector4Clear(screenBuffer.clearColor);
91 
92 	/* use default framebuffer */
93 	qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
94 	r_state.activeFramebuffer = &screenBuffer;
95 
96 	colorAttachments = Mem_AllocTypeN(GLenum, r_config.maxDrawBuffers);
97 	for (i = 0; i < r_config.maxDrawBuffers; i++)
98 		colorAttachments[i] = GL_COLOR_ATTACHMENT0_EXT + i;
99 
100 	filters[0] = GL_NEAREST;
101 	filters[1] = GL_LINEAR_MIPMAP_LINEAR;
102 
103 	/* setup main 3D render target */
104 	r_state.renderBuffer = R_CreateFramebuffer(viddef.context.width, viddef.context.height, 2, true, false, filters);
105 
106 	/* setup bloom render targets */
107 	fbo_bloom0 = R_CreateFramebuffer(viddef.context.width, viddef.context.height, 1, false, false, filters);
108 	fbo_bloom1 = R_CreateFramebuffer(viddef.context.width, viddef.context.height, 1, false, false, filters);
109 
110 	filters[0] = GL_LINEAR;
111 	/* setup extra framebuffers */
112 	for (i = 0; i < DOWNSAMPLE_PASSES; i++) {
113 		const int h = (int)((float)viddef.context.height / scales[i]);
114 		const int w = (int)((float)viddef.context.width / scales[i]);
115 		r_state.buffers0[i] = R_CreateFramebuffer(w, h, 1, false, false, filters);
116 		r_state.buffers1[i] = R_CreateFramebuffer(w, h, 1, false, false, filters);
117 		r_state.buffers2[i] = R_CreateFramebuffer(w, h, 1, false, false, filters);
118 
119 		R_CheckError();
120 	}
121 }
122 
123 
124 /**
125  * @brief Delete framebuffer object along with attached render buffer
126  */
R_DeleteFBObject(r_framebuffer_t * buf)127 void R_DeleteFBObject (r_framebuffer_t* buf)
128 {
129 	if (buf->depth)
130 		qglDeleteRenderbuffersEXT(1, &buf->depth);
131 	buf->depth = 0;
132 
133 	if (buf->textures) {
134 		int i;
135 		for (i = 0; i < buf->nTextures; i++)
136 			R_FreeFBOTexture(buf->textures[i]);
137 		Mem_Free(buf->textures);
138 	}
139 	buf->textures = 0;
140 
141 	if (buf->fbo)
142 		qglDeleteFramebuffersEXT(1, &buf->fbo);
143 	buf->fbo = 0;
144 }
145 
146 
147 /**
148  * @brief Delete all registered framebuffer and render buffer objects, clear memory
149  */
R_ShutdownFBObjects(void)150 void R_ShutdownFBObjects (void)
151 {
152 	int i;
153 
154 	if (!r_state.frameBufferObjectsInitialized)
155 		return;
156 
157 	for (i = 0; i < frameBufferObjectCount; i++)
158 		R_DeleteFBObject(&frameBufferObjects[i]);
159 
160 	R_UseFramebuffer(&screenBuffer);
161 
162 	frameBufferObjectCount = 0;
163 	OBJZERO(frameBufferObjects);
164 	r_state.frameBufferObjectsInitialized = false;
165 
166 	Mem_Free(colorAttachments);
167 }
168 
169 
170 /**
171  * @brief create a new framebuffer object
172  * @param[in] width The width of the framebuffer
173  * @param[in] height The height of the framebuffer
174  * @param[in] ntextures The amount of textures for this framebuffer. See also the filters array.
175  * @param[in] depth Also generate a depth buffer
176  * @param[in] halfFloat Use half float pixel format
177  * @param[in] filters Filters for the textures. Must have @c ntextures entries
178  */
R_CreateFramebuffer(int width,int height,int ntextures,bool depth,bool halfFloat,unsigned int * filters)179 r_framebuffer_t* R_CreateFramebuffer (int width, int height, int ntextures, bool depth, bool halfFloat, unsigned int* filters)
180 {
181 	r_framebuffer_t* buf;
182 	int i;
183 
184 	if (!r_state.frameBufferObjectsInitialized) {
185 		Com_Printf("Warning: framebuffer creation failed; framebuffers not initialized!\n");
186 		return nullptr;
187 	}
188 
189 	if (frameBufferObjectCount >= lengthof(frameBufferObjects)) {
190 		Com_Printf("Warning: framebuffer creation failed; already created too many framebuffers!\n");
191 		return nullptr;
192 	}
193 
194 	buf = &frameBufferObjects[frameBufferObjectCount++];
195 	OBJZERO(*buf);
196 
197 	if (ntextures > r_config.maxDrawBuffers) {
198 		Com_Printf("Couldn't allocate requested number of drawBuffers in R_SetupFramebuffer!\n");
199 		ntextures = r_config.maxDrawBuffers;
200 	}
201 
202 	Vector4Clear(buf->clearColor);
203 
204 	buf->width = width;
205 	buf->height = height;
206 	R_SetupViewport(buf, 0, 0, width, height);
207 
208 	buf->nTextures = ntextures;
209 	buf->textures = Mem_AllocTypeN(unsigned int, ntextures);
210 
211 #ifdef GL_VERSION_ES_CM_1_0
212 	buf->pixelFormat = GL_RGBA;
213 	buf->byteFormat = GL_UNSIGNED_BYTE;
214 #else
215 	buf->pixelFormat = halfFloat ? GL_RGBA16F_ARB : GL_RGBA8;
216 	buf->byteFormat = halfFloat ? GL_HALF_FLOAT_ARB : GL_UNSIGNED_BYTE;
217 #endif
218 
219 	/* Presence of depth buffer indicates render target that could use antialiasing */
220 	if (depth) {
221 		/** @todo also check if we are running on older (SM2.0) hardware, which doesn't support antialiased MRT */
222 		if (qglRenderbufferStorageMultisampleEXT && qglBlitFramebuffer) {
223 			const int samples = r_multisample->integer;
224 			if (samples > 1)
225 				buf->samples = samples;
226 		}
227 	}
228 
229 	for (i = 0; i < buf->nTextures; i++) {
230 		buf->textures[i] = R_GetFreeFBOTexture();
231 		glBindTexture(GL_TEXTURE_2D, buf->textures[i]);
232 		glTexImage2D(GL_TEXTURE_2D, 0, buf->pixelFormat, buf->width, buf->height, 0, GL_RGBA, buf->byteFormat, 0);
233 
234 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filters[i]);
235 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
236 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
237 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
238 		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
239 		qglGenerateMipmapEXT(GL_TEXTURE_2D);
240 		if (r_config.anisotropic)
241 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_config.maxAnisotropic);
242 
243 		R_CheckError();
244 	}
245 	glBindTexture(GL_TEXTURE_2D, 0);
246 
247 	/* create FBO itself */
248 	qglGenFramebuffersEXT(1, &buf->fbo);
249 	R_CheckError();
250 	qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buf->fbo);
251 
252 	/* create&attach depth renderbuffer */
253 	if (depth) {
254 		qglGenRenderbuffersEXT(1, &buf->depth);
255 		qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, buf->depth);
256 		if (buf->samples)
257 			qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, buf->samples, GL_DEPTH_COMPONENT, buf->width, buf->height);
258 		else
259 			qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, buf->width, buf->height);
260 		qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, buf->depth);
261 	} else {
262 		buf->depth = 0;
263 	}
264 
265 	/* create multisample color buffers if needed */
266 	if (buf->samples) {
267 		/* generate color buffers */
268 		for (i = 0; i < buf->nTextures; i++) {
269 			unsigned colorbuffer;
270 			qglGenRenderbuffersEXT(1, &colorbuffer);
271 			qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, colorbuffer);
272 			qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, buf->samples, buf->pixelFormat, buf->width, buf->height);
273 			qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, colorAttachments[i], GL_RENDERBUFFER_EXT, colorbuffer);
274 			R_CheckError();
275 		}
276 		/* proxy framebuffer object for resolving MSAA */
277 		qglGenFramebuffersEXT(1, &buf->proxyFBO);
278 		R_CheckError();
279 		qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buf->proxyFBO);
280 	}
281 
282 	/* Whether multisampling was enabled or not, current FBO should be populated with render-to-texture bindings */
283 	for (i = 0; i < buf->nTextures; i++) {
284 		qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, colorAttachments[i], GL_TEXTURE_2D, buf->textures[i], 0);
285 		R_CheckError();
286 	}
287 
288 	R_CheckError();
289 
290 	/* unbind the framebuffer and return to default state */
291 	qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
292 
293 	return buf;
294 }
295 
296 /**
297  * @brief Forces multisample antialiasing resolve on given framebuffer, if needed
298  * @param[in] buf the framebuffer to use
299  */
R_ResolveMSAA(const r_framebuffer_t * buf)300 void R_ResolveMSAA (const r_framebuffer_t* buf)
301 {
302 	int i;
303 
304 	if (!buf->samples)
305 		return;
306 
307 	qglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT,buf->fbo);
308 	qglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT,buf->proxyFBO);
309 	for (i = 0; i < buf->nTextures; i++)	 {
310 #ifndef GL_VERSION_ES_CM_1_0
311 		glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + i);
312 		glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + i);
313 #endif
314 		qglBlitFramebuffer(0, 0, buf->width, buf-> height, 0, 0, buf->width, buf->height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
315 
316 		R_CheckError();
317 	}
318 	R_CheckError();
319 
320 	qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT,  screenBuffer.fbo);
321 	R_CheckError();
322 }
323 
324 
325 /**
326  * @brief bind specified framebuffer object so we render to it
327  * @param[in] buf the framebuffer to use, if @c nullptr the screen buffer will be used.
328  */
R_UseFramebuffer(const r_framebuffer_t * buf)329 void R_UseFramebuffer (const r_framebuffer_t* buf)
330 {
331 	if (!r_config.frameBufferObject || !r_programs->integer || !r_postprocess->integer)
332 		return;
333 
334 	if (!r_state.frameBufferObjectsInitialized) {
335 		Com_Printf("Can't bind framebuffer: framebuffers not initialized\n");
336 		return;
337 	}
338 
339 	if (!buf)
340 		buf = &screenBuffer;
341 
342 	/* don't re-bind if we're already using the requested buffer */
343 	if (buf == r_state.activeFramebuffer)
344 		return;
345 
346 	qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buf->fbo);
347 
348 	/* don't call glDrawBuffers for main screenbuffer */
349 	/** @todo buf != &screenBuffer */
350 	if (buf->nTextures > 0)
351 		qglDrawBuffers(buf->nTextures, colorAttachments);
352 
353 	glClearColor(r_state.activeFramebuffer->clearColor[0], r_state.activeFramebuffer->clearColor[1], r_state.activeFramebuffer->clearColor[2], r_state.activeFramebuffer->clearColor[3]);
354 	glClear(GL_COLOR_BUFFER_BIT | (buf->depth ? GL_DEPTH_BUFFER_BIT : 0));
355 
356 	r_state.activeFramebuffer = buf;
357 
358 	R_CheckError();
359 }
360 
361 /**
362  * @brief Sets the framebuffer dimensions of the viewport
363  * @param[out] buf The framebuffer to initialize the viewport for. If @c nullptr the screen buffer will be taken.
364  * @sa R_UseViewport
365  */
R_SetupViewport(r_framebuffer_t * buf,int x,int y,int width,int height)366 void R_SetupViewport (r_framebuffer_t* buf, int x, int y, int width, int height)
367 {
368 	if (!buf)
369 		buf = &screenBuffer;
370 
371 	buf->viewport.x = x;
372 	buf->viewport.y = y;
373 	buf->viewport.width = width;
374 	buf->viewport.height = height;
375 }
376 
377 /**
378  * @brief Set the viewport to the dimensions of the given framebuffer
379  * @param[out] buf The framebuffer to set the viewport for. If @c nullptr the screen buffer will be taken.
380  * @sa R_SetupViewport
381  */
R_UseViewport(const r_framebuffer_t * buf)382 void R_UseViewport (const r_framebuffer_t* buf)
383 {
384 	if (!r_state.frameBufferObjectsInitialized || !r_config.frameBufferObject || !r_postprocess->integer || !r_programs->integer)
385 		return;
386 
387 	if (!buf)
388 		buf = &screenBuffer;
389 	glViewport(buf->viewport.x, buf->viewport.y, buf->viewport.width, buf->viewport.height);
390 }
391 
392 /**
393  * @brief Activate draw buffer(s)
394  * @param[in] drawBufferNum The number of buffers to activate
395  * @todo introduce enum or speaking constants for the buffer numbers that are drawn here and elsewhere
396  */
R_DrawBuffers(unsigned int drawBufferNum)397 void R_DrawBuffers (unsigned int drawBufferNum)
398 {
399 	R_BindColorAttachments(drawBufferNum, colorAttachments);
400 }
401 
402 /**
403  * @brief Activate draw buffer(s)
404  * @param n The number of buffers to activate
405  * @param attachments The buffers we are rendering into
406  * @note The order of the attachments define the gl_FragData order in the shaders
407 */
R_BindColorAttachments(unsigned int n,unsigned int * attachments)408 void R_BindColorAttachments (unsigned int n, unsigned int* attachments)
409 {
410 	if (!r_state.frameBufferObjectsInitialized || !r_config.frameBufferObject || !r_postprocess->integer || !r_programs->integer)
411 		return;
412 
413 	if (n >= r_config.maxDrawBuffers) {
414 		Com_DPrintf(DEBUG_RENDERER, "Max drawbuffers hit\n");
415 		n = r_config.maxDrawBuffers;
416 	}
417 
418 	if (r_state.activeFramebuffer && r_state.activeFramebuffer->nTextures > 0)
419 		qglDrawBuffers(n, attachments);
420 }
421 
422 /**
423  * @brief Enable the render to the framebuffer
424  * @param enable If @c true we are enabling the rendering to fbo_render, if @c false we are rendering
425  * to fbo that represents the screen
426  * @sa R_DrawBuffers
427  * @return @c true if the fbo was bound, @c false if not supported or deactivated
428  */
R_EnableRenderbuffer(bool enable)429 bool R_EnableRenderbuffer (bool enable)
430 {
431 	if (!r_state.frameBufferObjectsInitialized || !r_config.frameBufferObject || !r_postprocess->integer || !r_programs->integer)
432 		return false;
433 
434 	if (enable != r_state.renderbuffer_enabled) {
435 		r_state.renderbuffer_enabled = enable;
436 		if (enable)
437 			R_UseFramebuffer(fbo_render);
438 		else
439 			R_UseFramebuffer(fbo_screen);
440 	}
441 
442 	R_DrawBuffers(1);
443 
444 	return true;
445 }
446 
R_RenderbufferEnabled(void)447 bool R_RenderbufferEnabled (void)
448 {
449 	return r_state.renderbuffer_enabled;
450 }
451