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