1 /*
2 ===========================================================================
3 Copyright (C) 2006 Kirk Barnes
4 Copyright (C) 2006-2008 Robert Beckebans <trebor_7@users.sourceforge.net>
5 
6 This file is part of XreaL source code.
7 
8 XreaL source code is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 XreaL source code is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with XreaL source code; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 // tr_fbo.c
24 #include "tr_local.h"
25 
26 #include "tr_dsa.h"
27 
28 /*
29 =============
30 R_CheckFBO
31 =============
32 */
R_CheckFBO(const FBO_t * fbo)33 qboolean R_CheckFBO(const FBO_t * fbo)
34 {
35 	GLenum code = qglCheckNamedFramebufferStatusEXT(fbo->frameBuffer, GL_FRAMEBUFFER);
36 
37 	if(code == GL_FRAMEBUFFER_COMPLETE)
38 		return qtrue;
39 
40 	// an error occured
41 	switch (code)
42 	{
43 		case GL_FRAMEBUFFER_UNSUPPORTED:
44 			ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Unsupported framebuffer format\n", fbo->name);
45 			break;
46 
47 		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
48 			ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete attachment\n", fbo->name);
49 			break;
50 
51 		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
52 			ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing attachment\n", fbo->name);
53 			break;
54 
55 		case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
56 			ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing draw buffer\n", fbo->name);
57 			break;
58 
59 		case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
60 			ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing read buffer\n", fbo->name);
61 			break;
62 
63 		case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
64 			ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete multisample\n", fbo->name);
65 			break;
66 
67 		default:
68 			ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) unknown error 0x%X\n", fbo->name, code);
69 			break;
70 	}
71 
72 	return qfalse;
73 }
74 
75 /*
76 ============
77 FBO_Create
78 ============
79 */
FBO_Create(const char * name,int width,int height)80 FBO_t          *FBO_Create(const char *name, int width, int height)
81 {
82 	FBO_t          *fbo;
83 
84 	if(strlen(name) >= MAX_QPATH)
85 	{
86 		ri.Error(ERR_DROP, "FBO_Create: \"%s\" is too long", name);
87 	}
88 
89 	if(width <= 0 || width > glRefConfig.maxRenderbufferSize)
90 	{
91 		ri.Error(ERR_DROP, "FBO_Create: bad width %i", width);
92 	}
93 
94 	if(height <= 0 || height > glRefConfig.maxRenderbufferSize)
95 	{
96 		ri.Error(ERR_DROP, "FBO_Create: bad height %i", height);
97 	}
98 
99 	if(tr.numFBOs == MAX_FBOS)
100 	{
101 		ri.Error(ERR_DROP, "FBO_Create: MAX_FBOS hit");
102 	}
103 
104 	fbo = tr.fbos[tr.numFBOs] = ri.Hunk_Alloc(sizeof(*fbo), h_low);
105 	Q_strncpyz(fbo->name, name, sizeof(fbo->name));
106 	fbo->index = tr.numFBOs++;
107 	fbo->width = width;
108 	fbo->height = height;
109 
110 	qglGenFramebuffers(1, &fbo->frameBuffer);
111 
112 	return fbo;
113 }
114 
115 /*
116 =================
117 FBO_CreateBuffer
118 =================
119 */
FBO_CreateBuffer(FBO_t * fbo,int format,int index,int multisample)120 void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample)
121 {
122 	uint32_t *pRenderBuffer;
123 	GLenum attachment;
124 	qboolean absent;
125 
126 	switch(format)
127 	{
128 		case GL_RGB:
129 		case GL_RGBA:
130 		case GL_RGB8:
131 		case GL_RGBA8:
132 		case GL_RGB16F_ARB:
133 		case GL_RGBA16F_ARB:
134 		case GL_RGB32F_ARB:
135 		case GL_RGBA32F_ARB:
136 			fbo->colorFormat = format;
137 			pRenderBuffer = &fbo->colorBuffers[index];
138 			attachment = GL_COLOR_ATTACHMENT0 + index;
139 			break;
140 
141 		case GL_DEPTH_COMPONENT:
142 		case GL_DEPTH_COMPONENT16_ARB:
143 		case GL_DEPTH_COMPONENT24_ARB:
144 		case GL_DEPTH_COMPONENT32_ARB:
145 			fbo->depthFormat = format;
146 			pRenderBuffer = &fbo->depthBuffer;
147 			attachment = GL_DEPTH_ATTACHMENT;
148 			break;
149 
150 		case GL_STENCIL_INDEX:
151 		case GL_STENCIL_INDEX1:
152 		case GL_STENCIL_INDEX4:
153 		case GL_STENCIL_INDEX8:
154 		case GL_STENCIL_INDEX16:
155 			fbo->stencilFormat = format;
156 			pRenderBuffer = &fbo->stencilBuffer;
157 			attachment = GL_STENCIL_ATTACHMENT;
158 			break;
159 
160 		case GL_DEPTH_STENCIL:
161 		case GL_DEPTH24_STENCIL8:
162 			fbo->packedDepthStencilFormat = format;
163 			pRenderBuffer = &fbo->packedDepthStencilBuffer;
164 			attachment = 0; // special for stencil and depth
165 			break;
166 
167 		default:
168 			ri.Printf(PRINT_WARNING, "FBO_CreateBuffer: invalid format %d\n", format);
169 			return;
170 	}
171 
172 	absent = *pRenderBuffer == 0;
173 	if (absent)
174 		qglGenRenderbuffers(1, pRenderBuffer);
175 
176 	if (multisample && glRefConfig.framebufferMultisample)
177 		qglNamedRenderbufferStorageMultisampleEXT(*pRenderBuffer, multisample, format, fbo->width, fbo->height);
178 	else
179 		qglNamedRenderbufferStorageEXT(*pRenderBuffer, format, fbo->width, fbo->height);
180 
181 	if(absent)
182 	{
183 		if (attachment == 0)
184 		{
185 			qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, GL_DEPTH_ATTACHMENT,   GL_RENDERBUFFER, *pRenderBuffer);
186 			qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *pRenderBuffer);
187 		}
188 		else
189 		{
190 			qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, attachment, GL_RENDERBUFFER, *pRenderBuffer);
191 		}
192 	}
193 }
194 
195 
196 /*
197 =================
198 FBO_AttachImage
199 =================
200 */
FBO_AttachImage(FBO_t * fbo,image_t * image,GLenum attachment,GLuint cubemapside)201 void FBO_AttachImage(FBO_t *fbo, image_t *image, GLenum attachment, GLuint cubemapside)
202 {
203 	GLenum target = GL_TEXTURE_2D;
204 	int index;
205 
206 	if (image->flags & IMGFLAG_CUBEMAP)
207 		target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + cubemapside;
208 
209 	qglNamedFramebufferTexture2DEXT(fbo->frameBuffer, attachment, target, image->texnum, 0);
210 	index = attachment - GL_COLOR_ATTACHMENT0;
211 	if (index >= 0 && index <= 15)
212 		fbo->colorImage[index] = image;
213 }
214 
215 
216 /*
217 ============
218 FBO_Bind
219 ============
220 */
FBO_Bind(FBO_t * fbo)221 void FBO_Bind(FBO_t * fbo)
222 {
223 	if (!glRefConfig.framebufferObject)
224 	{
225 		ri.Printf(PRINT_WARNING, "FBO_Bind() called without framebuffers enabled!\n");
226 		return;
227 	}
228 
229 	if (glState.currentFBO == fbo)
230 		return;
231 
232 	if (r_logFile->integer)
233 	{
234 		// don't just call LogComment, or we will get a call to va() every frame!
235 		GLimp_LogComment(va("--- FBO_Bind( %s ) ---\n", fbo ? fbo->name : "NULL"));
236 	}
237 
238 	GL_BindFramebuffer(GL_FRAMEBUFFER, fbo ? fbo->frameBuffer : 0);
239 	glState.currentFBO = fbo;
240 }
241 
242 /*
243 ============
244 FBO_Init
245 ============
246 */
FBO_Init(void)247 void FBO_Init(void)
248 {
249 	int             i;
250 	int             hdrFormat, multisample = 0;
251 
252 	ri.Printf(PRINT_ALL, "------- FBO_Init -------\n");
253 
254 	if(!glRefConfig.framebufferObject)
255 		return;
256 
257 	tr.numFBOs = 0;
258 
259 	GL_CheckErrors();
260 
261 	R_IssuePendingRenderCommands();
262 
263 	hdrFormat = GL_RGBA8;
264 	if (r_hdr->integer && glRefConfig.textureFloat)
265 		hdrFormat = GL_RGBA16F_ARB;
266 
267 	if (glRefConfig.framebufferMultisample)
268 		qglGetIntegerv(GL_MAX_SAMPLES, &multisample);
269 
270 	if (r_ext_framebuffer_multisample->integer < multisample)
271 		multisample = r_ext_framebuffer_multisample->integer;
272 
273 	if (multisample < 2 || !glRefConfig.framebufferBlit)
274 		multisample = 0;
275 
276 	if (multisample != r_ext_framebuffer_multisample->integer)
277 		ri.Cvar_SetValue("r_ext_framebuffer_multisample", (float)multisample);
278 
279 	// only create a render FBO if we need to resolve MSAA or do HDR
280 	// otherwise just render straight to the screen (tr.renderFbo = NULL)
281 	if (multisample && glRefConfig.framebufferMultisample)
282 	{
283 		tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height);
284 		FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, multisample);
285 		FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24, 0, multisample);
286 		R_CheckFBO(tr.renderFbo);
287 
288 		tr.msaaResolveFbo = FBO_Create("_msaaResolve", tr.renderDepthImage->width, tr.renderDepthImage->height);
289 		FBO_AttachImage(tr.msaaResolveFbo, tr.renderImage, GL_COLOR_ATTACHMENT0, 0);
290 		FBO_AttachImage(tr.msaaResolveFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0);
291 		R_CheckFBO(tr.msaaResolveFbo);
292 	}
293 	else if (r_hdr->integer)
294 	{
295 		tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height);
296 		FBO_AttachImage(tr.renderFbo, tr.renderImage, GL_COLOR_ATTACHMENT0, 0);
297 		FBO_AttachImage(tr.renderFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0);
298 		R_CheckFBO(tr.renderFbo);
299 	}
300 
301 	// clear render buffer
302 	// this fixes the corrupt screen bug with r_hdr 1 on older hardware
303 	if (tr.renderFbo)
304 	{
305 		GL_BindFramebuffer(GL_FRAMEBUFFER, tr.renderFbo->frameBuffer);
306 		qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
307 	}
308 
309 	if (tr.screenScratchImage)
310 	{
311 		tr.screenScratchFbo = FBO_Create("screenScratch", tr.screenScratchImage->width, tr.screenScratchImage->height);
312 		FBO_AttachImage(tr.screenScratchFbo, tr.screenScratchImage, GL_COLOR_ATTACHMENT0, 0);
313 		FBO_AttachImage(tr.screenScratchFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0);
314 		R_CheckFBO(tr.screenScratchFbo);
315 	}
316 
317 	if (tr.sunRaysImage)
318 	{
319 		tr.sunRaysFbo = FBO_Create("_sunRays", tr.renderDepthImage->width, tr.renderDepthImage->height);
320 		FBO_AttachImage(tr.sunRaysFbo, tr.sunRaysImage, GL_COLOR_ATTACHMENT0, 0);
321 		FBO_AttachImage(tr.sunRaysFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0);
322 		R_CheckFBO(tr.sunRaysFbo);
323 	}
324 
325 	if (MAX_DRAWN_PSHADOWS && tr.pshadowMaps[0])
326 	{
327 		for( i = 0; i < MAX_DRAWN_PSHADOWS; i++)
328 		{
329 			tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height);
330 			// FIXME: this next line wastes 16mb with 16x512x512 sun shadow maps, skip if OpenGL 4.3+ or ARB_framebuffer_no_attachments
331 			FBO_CreateBuffer(tr.pshadowFbos[i], GL_RGBA8, 0, 0);
332 			FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_DEPTH_ATTACHMENT, 0);
333 			R_CheckFBO(tr.pshadowFbos[i]);
334 		}
335 	}
336 
337 	if (tr.sunShadowDepthImage[0])
338 	{
339 		for (i = 0; i < 4; i++)
340 		{
341 			tr.sunShadowFbo[i] = FBO_Create("_sunshadowmap", tr.sunShadowDepthImage[i]->width, tr.sunShadowDepthImage[i]->height);
342 			// FIXME: this next line wastes 16mb with 4x1024x1024 sun shadow maps, skip if OpenGL 4.3+ or ARB_framebuffer_no_attachments
343 			// This at least gets sun shadows working on older GPUs (Intel)
344 			FBO_CreateBuffer(tr.sunShadowFbo[i], GL_RGBA8, 0, 0);
345 			FBO_AttachImage(tr.sunShadowFbo[i], tr.sunShadowDepthImage[i], GL_DEPTH_ATTACHMENT, 0);
346 			R_CheckFBO(tr.sunShadowFbo[i]);
347 		}
348 	}
349 
350 	if (tr.screenShadowImage)
351 	{
352 		tr.screenShadowFbo = FBO_Create("_screenshadow", tr.screenShadowImage->width, tr.screenShadowImage->height);
353 		FBO_AttachImage(tr.screenShadowFbo, tr.screenShadowImage, GL_COLOR_ATTACHMENT0, 0);
354 		R_CheckFBO(tr.screenShadowFbo);
355 	}
356 
357 	if (tr.textureScratchImage[0])
358 	{
359 		for (i = 0; i < 2; i++)
360 		{
361 			tr.textureScratchFbo[i] = FBO_Create(va("_texturescratch%d", i), tr.textureScratchImage[i]->width, tr.textureScratchImage[i]->height);
362 			FBO_AttachImage(tr.textureScratchFbo[i], tr.textureScratchImage[i], GL_COLOR_ATTACHMENT0, 0);
363 			R_CheckFBO(tr.textureScratchFbo[i]);
364 		}
365 	}
366 
367 	if (tr.calcLevelsImage)
368 	{
369 		tr.calcLevelsFbo = FBO_Create("_calclevels", tr.calcLevelsImage->width, tr.calcLevelsImage->height);
370 		FBO_AttachImage(tr.calcLevelsFbo, tr.calcLevelsImage, GL_COLOR_ATTACHMENT0, 0);
371 		R_CheckFBO(tr.calcLevelsFbo);
372 	}
373 
374 	if (tr.targetLevelsImage)
375 	{
376 		tr.targetLevelsFbo = FBO_Create("_targetlevels", tr.targetLevelsImage->width, tr.targetLevelsImage->height);
377 		FBO_AttachImage(tr.targetLevelsFbo, tr.targetLevelsImage, GL_COLOR_ATTACHMENT0, 0);
378 		R_CheckFBO(tr.targetLevelsFbo);
379 	}
380 
381 	if (tr.quarterImage[0])
382 	{
383 		for (i = 0; i < 2; i++)
384 		{
385 			tr.quarterFbo[i] = FBO_Create(va("_quarter%d", i), tr.quarterImage[i]->width, tr.quarterImage[i]->height);
386 			FBO_AttachImage(tr.quarterFbo[i], tr.quarterImage[i], GL_COLOR_ATTACHMENT0, 0);
387 			R_CheckFBO(tr.quarterFbo[i]);
388 		}
389 	}
390 
391 	if (tr.hdrDepthImage)
392 	{
393 		tr.hdrDepthFbo = FBO_Create("_hdrDepth", tr.hdrDepthImage->width, tr.hdrDepthImage->height);
394 		FBO_AttachImage(tr.hdrDepthFbo, tr.hdrDepthImage, GL_COLOR_ATTACHMENT0, 0);
395 		R_CheckFBO(tr.hdrDepthFbo);
396 	}
397 
398 	if (tr.screenSsaoImage)
399 	{
400 		tr.screenSsaoFbo = FBO_Create("_screenssao", tr.screenSsaoImage->width, tr.screenSsaoImage->height);
401 		FBO_AttachImage(tr.screenSsaoFbo, tr.screenSsaoImage, GL_COLOR_ATTACHMENT0, 0);
402 		R_CheckFBO(tr.screenSsaoFbo);
403 	}
404 
405 	if (tr.renderCubeImage)
406 	{
407 		tr.renderCubeFbo = FBO_Create("_renderCubeFbo", tr.renderCubeImage->width, tr.renderCubeImage->height);
408 		FBO_AttachImage(tr.renderCubeFbo, tr.renderCubeImage, GL_COLOR_ATTACHMENT0, 0);
409 		FBO_CreateBuffer(tr.renderCubeFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0);
410 		R_CheckFBO(tr.renderCubeFbo);
411 	}
412 
413 	GL_CheckErrors();
414 
415 	GL_BindFramebuffer(GL_FRAMEBUFFER, 0);
416 	glState.currentFBO = NULL;
417 }
418 
419 /*
420 ============
421 FBO_Shutdown
422 ============
423 */
FBO_Shutdown(void)424 void FBO_Shutdown(void)
425 {
426 	int             i, j;
427 	FBO_t          *fbo;
428 
429 	ri.Printf(PRINT_ALL, "------- FBO_Shutdown -------\n");
430 
431 	if(!glRefConfig.framebufferObject)
432 		return;
433 
434 	FBO_Bind(NULL);
435 
436 	for(i = 0; i < tr.numFBOs; i++)
437 	{
438 		fbo = tr.fbos[i];
439 
440 		for(j = 0; j < glRefConfig.maxColorAttachments; j++)
441 		{
442 			if(fbo->colorBuffers[j])
443 				qglDeleteRenderbuffers(1, &fbo->colorBuffers[j]);
444 		}
445 
446 		if(fbo->depthBuffer)
447 			qglDeleteRenderbuffers(1, &fbo->depthBuffer);
448 
449 		if(fbo->stencilBuffer)
450 			qglDeleteRenderbuffers(1, &fbo->stencilBuffer);
451 
452 		if(fbo->frameBuffer)
453 			qglDeleteFramebuffers(1, &fbo->frameBuffer);
454 	}
455 }
456 
457 /*
458 ============
459 R_FBOList_f
460 ============
461 */
R_FBOList_f(void)462 void R_FBOList_f(void)
463 {
464 	int             i;
465 	FBO_t          *fbo;
466 
467 	if(!glRefConfig.framebufferObject)
468 	{
469 		ri.Printf(PRINT_ALL, "GL_EXT_framebuffer_object is not available.\n");
470 		return;
471 	}
472 
473 	ri.Printf(PRINT_ALL, "             size       name\n");
474 	ri.Printf(PRINT_ALL, "----------------------------------------------------------\n");
475 
476 	for(i = 0; i < tr.numFBOs; i++)
477 	{
478 		fbo = tr.fbos[i];
479 
480 		ri.Printf(PRINT_ALL, "  %4i: %4i %4i %s\n", i, fbo->width, fbo->height, fbo->name);
481 	}
482 
483 	ri.Printf(PRINT_ALL, " %i FBOs\n", tr.numFBOs);
484 }
485 
FBO_BlitFromTexture(struct image_s * src,vec4_t inSrcTexCorners,vec2_t inSrcTexScale,FBO_t * dst,ivec4_t inDstBox,struct shaderProgram_s * shaderProgram,vec4_t inColor,int blend)486 void FBO_BlitFromTexture(struct image_s *src, vec4_t inSrcTexCorners, vec2_t inSrcTexScale, FBO_t *dst, ivec4_t inDstBox, struct shaderProgram_s *shaderProgram, vec4_t inColor, int blend)
487 {
488 	ivec4_t dstBox;
489 	vec4_t color;
490 	vec4_t quadVerts[4];
491 	vec2_t texCoords[4];
492 	vec2_t invTexRes;
493 	FBO_t *oldFbo = glState.currentFBO;
494 	mat4_t projection;
495 	int width, height;
496 
497 	if (!src)
498 	{
499 		ri.Printf(PRINT_WARNING, "Tried to blit from a NULL texture!\n");
500 		return;
501 	}
502 
503 	width  = dst ? dst->width  : glConfig.vidWidth;
504 	height = dst ? dst->height : glConfig.vidHeight;
505 
506 	if (inSrcTexCorners)
507 	{
508 		VectorSet2(texCoords[0], inSrcTexCorners[0], inSrcTexCorners[1]);
509 		VectorSet2(texCoords[1], inSrcTexCorners[2], inSrcTexCorners[1]);
510 		VectorSet2(texCoords[2], inSrcTexCorners[2], inSrcTexCorners[3]);
511 		VectorSet2(texCoords[3], inSrcTexCorners[0], inSrcTexCorners[3]);
512 	}
513 	else
514 	{
515 		VectorSet2(texCoords[0], 0.0f, 1.0f);
516 		VectorSet2(texCoords[1], 1.0f, 1.0f);
517 		VectorSet2(texCoords[2], 1.0f, 0.0f);
518 		VectorSet2(texCoords[3], 0.0f, 0.0f);
519 	}
520 
521 	// framebuffers are 0 bottom, Y up.
522 	if (inDstBox)
523 	{
524 		dstBox[0] = inDstBox[0];
525 		dstBox[1] = height - inDstBox[1] - inDstBox[3];
526 		dstBox[2] = inDstBox[0] + inDstBox[2];
527 		dstBox[3] = height - inDstBox[1];
528 	}
529 	else
530 	{
531 		VectorSet4(dstBox, 0, height, width, 0);
532 	}
533 
534 	if (inSrcTexScale)
535 	{
536 		VectorCopy2(inSrcTexScale, invTexRes);
537 	}
538 	else
539 	{
540 		VectorSet2(invTexRes, 1.0f, 1.0f);
541 	}
542 
543 	if (inColor)
544 	{
545 		VectorCopy4(inColor, color);
546 	}
547 	else
548 	{
549 		VectorCopy4(colorWhite, color);
550 	}
551 
552 	if (!shaderProgram)
553 	{
554 		shaderProgram = &tr.textureColorShader;
555 	}
556 
557 	FBO_Bind(dst);
558 
559 	qglViewport( 0, 0, width, height );
560 	qglScissor( 0, 0, width, height );
561 
562 	Mat4Ortho(0, width, height, 0, 0, 1, projection);
563 
564 	GL_Cull( CT_TWO_SIDED );
565 
566 	GL_BindToTMU(src, TB_COLORMAP);
567 
568 	VectorSet4(quadVerts[0], dstBox[0], dstBox[1], 0.0f, 1.0f);
569 	VectorSet4(quadVerts[1], dstBox[2], dstBox[1], 0.0f, 1.0f);
570 	VectorSet4(quadVerts[2], dstBox[2], dstBox[3], 0.0f, 1.0f);
571 	VectorSet4(quadVerts[3], dstBox[0], dstBox[3], 0.0f, 1.0f);
572 
573 	invTexRes[0] /= src->width;
574 	invTexRes[1] /= src->height;
575 
576 	GL_State( blend );
577 
578 	GLSL_BindProgram(shaderProgram);
579 
580 	GLSL_SetUniformMat4(shaderProgram, UNIFORM_MODELVIEWPROJECTIONMATRIX, projection);
581 	GLSL_SetUniformVec4(shaderProgram, UNIFORM_COLOR, color);
582 	GLSL_SetUniformVec2(shaderProgram, UNIFORM_INVTEXRES, invTexRes);
583 	GLSL_SetUniformVec2(shaderProgram, UNIFORM_AUTOEXPOSUREMINMAX, tr.refdef.autoExposureMinMax);
584 	GLSL_SetUniformVec3(shaderProgram, UNIFORM_TONEMINAVGMAXLINEAR, tr.refdef.toneMinAvgMaxLinear);
585 
586 	RB_InstantQuad2(quadVerts, texCoords);
587 
588 	FBO_Bind(oldFbo);
589 }
590 
FBO_Blit(FBO_t * src,ivec4_t inSrcBox,vec2_t srcTexScale,FBO_t * dst,ivec4_t dstBox,struct shaderProgram_s * shaderProgram,vec4_t color,int blend)591 void FBO_Blit(FBO_t *src, ivec4_t inSrcBox, vec2_t srcTexScale, FBO_t *dst, ivec4_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend)
592 {
593 	vec4_t srcTexCorners;
594 
595 	if (!src)
596 	{
597 		ri.Printf(PRINT_WARNING, "Tried to blit from a NULL FBO!\n");
598 		return;
599 	}
600 
601 	if (inSrcBox)
602 	{
603 		srcTexCorners[0] =  inSrcBox[0]                / (float)src->width;
604 		srcTexCorners[1] = (inSrcBox[1] + inSrcBox[3]) / (float)src->height;
605 		srcTexCorners[2] = (inSrcBox[0] + inSrcBox[2]) / (float)src->width;
606 		srcTexCorners[3] =  inSrcBox[1]                / (float)src->height;
607 	}
608 	else
609 	{
610 		VectorSet4(srcTexCorners, 0.0f, 0.0f, 1.0f, 1.0f);
611 	}
612 
613 	FBO_BlitFromTexture(src->colorImage[0], srcTexCorners, srcTexScale, dst, dstBox, shaderProgram, color, blend | GLS_DEPTHTEST_DISABLE);
614 }
615 
FBO_FastBlit(FBO_t * src,ivec4_t srcBox,FBO_t * dst,ivec4_t dstBox,int buffers,int filter)616 void FBO_FastBlit(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, int buffers, int filter)
617 {
618 	ivec4_t srcBoxFinal, dstBoxFinal;
619 	GLuint srcFb, dstFb;
620 
621 	if (!glRefConfig.framebufferBlit)
622 	{
623 		FBO_Blit(src, srcBox, NULL, dst, dstBox, NULL, NULL, 0);
624 		return;
625 	}
626 
627 	srcFb = src ? src->frameBuffer : 0;
628 	dstFb = dst ? dst->frameBuffer : 0;
629 
630 	if (!srcBox)
631 	{
632 		int width =  src ? src->width  : glConfig.vidWidth;
633 		int height = src ? src->height : glConfig.vidHeight;
634 
635 		VectorSet4(srcBoxFinal, 0, 0, width, height);
636 	}
637 	else
638 	{
639 		VectorSet4(srcBoxFinal, srcBox[0], srcBox[1], srcBox[0] + srcBox[2], srcBox[1] + srcBox[3]);
640 	}
641 
642 	if (!dstBox)
643 	{
644 		int width  = dst ? dst->width  : glConfig.vidWidth;
645 		int height = dst ? dst->height : glConfig.vidHeight;
646 
647 		VectorSet4(dstBoxFinal, 0, 0, width, height);
648 	}
649 	else
650 	{
651 		VectorSet4(dstBoxFinal, dstBox[0], dstBox[1], dstBox[0] + dstBox[2], dstBox[1] + dstBox[3]);
652 	}
653 
654 	GL_BindFramebuffer(GL_READ_FRAMEBUFFER, srcFb);
655 	GL_BindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFb);
656 	qglBlitFramebuffer(srcBoxFinal[0], srcBoxFinal[1], srcBoxFinal[2], srcBoxFinal[3],
657 	                      dstBoxFinal[0], dstBoxFinal[1], dstBoxFinal[2], dstBoxFinal[3],
658 						  buffers, filter);
659 
660 	GL_BindFramebuffer(GL_FRAMEBUFFER, 0);
661 	glState.currentFBO = NULL;
662 }
663