PageRenderTime 76ms CodeModel.GetById 29ms app.highlight 42ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llrender/llrendertarget.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 535 lines | 429 code | 77 blank | 29 comment | 59 complexity | 59ff2c73529a77b0fd337f428be808d4 MD5 | raw file
  1/** 
  2 * @file llrendertarget.cpp
  3 * @brief LLRenderTarget implementation
  4 *
  5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library 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.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28
 29#include "llrendertarget.h"
 30#include "llrender.h"
 31#include "llgl.h"
 32
 33LLRenderTarget* LLRenderTarget::sBoundTarget = NULL;
 34U32 LLRenderTarget::sBytesAllocated = 0;
 35
 36void check_framebuffer_status()
 37{
 38	if (gDebugGL)
 39	{
 40		GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
 41		switch (status)
 42		{
 43		case GL_FRAMEBUFFER_COMPLETE:
 44			break;
 45		default:
 46			llwarns << "check_framebuffer_status failed -- " << std::hex << status << llendl;
 47			ll_fail("check_framebuffer_status failed");	
 48			break;
 49		}
 50	}
 51}
 52
 53bool LLRenderTarget::sUseFBO = false;
 54
 55LLRenderTarget::LLRenderTarget() :
 56	mResX(0),
 57	mResY(0),
 58	mTex(0),
 59	mFBO(0),
 60	mDepth(0),
 61	mStencil(0),
 62	mUseDepth(false),
 63	mRenderDepth(false),
 64	mUsage(LLTexUnit::TT_TEXTURE)
 65{
 66}
 67
 68LLRenderTarget::~LLRenderTarget()
 69{
 70	release();
 71}
 72
 73bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, S32 samples)
 74{
 75	stop_glerror();
 76	release();
 77	stop_glerror();
 78
 79	mResX = resx;
 80	mResY = resy;
 81
 82	mStencil = stencil;
 83	mUsage = usage;
 84	mUseDepth = depth;
 85
 86	if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject)
 87	{
 88		if (depth)
 89		{
 90			if (!allocateDepth())
 91			{
 92				llwarns << "Failed to allocate depth buffer for render target." << llendl;
 93				return false;
 94			}
 95		}
 96
 97		glGenFramebuffers(1, (GLuint *) &mFBO);
 98
 99		if (mDepth)
100		{
101			glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
102			if (mStencil)
103			{
104				glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepth);
105				stop_glerror();
106				glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepth);
107				stop_glerror();
108			}
109			else
110			{
111				glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0);
112				stop_glerror();
113			}
114			glBindFramebuffer(GL_FRAMEBUFFER, 0);
115		}
116		
117		stop_glerror();
118	}
119
120	return addColorAttachment(color_fmt);
121}
122
123bool LLRenderTarget::addColorAttachment(U32 color_fmt)
124{
125	if (color_fmt == 0)
126	{
127		return true;
128	}
129
130	U32 offset = mTex.size();
131	if (offset >= 4 ||
132		(offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers)))
133	{
134		llerrs << "Too many color attachments!" << llendl;
135	}
136
137	U32 tex;
138	LLImageGL::generateTextures(1, &tex);
139	gGL.getTexUnit(0)->bindManual(mUsage, tex);
140
141	stop_glerror();
142
143
144	{
145		clear_glerror();
146		LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
147		if (glGetError() != GL_NO_ERROR)
148		{
149			llwarns << "Could not allocate color buffer for render target." << llendl;
150			return false;
151		}
152	}
153	
154	sBytesAllocated += mResX*mResY*4;
155
156	stop_glerror();
157
158	
159	if (offset == 0)
160	{ //use bilinear filtering on single texture render targets that aren't multisampled
161		gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
162		stop_glerror();
163	}
164	else
165	{ //don't filter data attachments
166		gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
167		stop_glerror();
168	}
169
170	if (mUsage != LLTexUnit::TT_RECT_TEXTURE)
171	{
172		gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_MIRROR);
173		stop_glerror();
174	}
175	else
176	{
177		// ATI doesn't support mirrored repeat for rectangular textures.
178		gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
179		stop_glerror();
180	}
181		
182	if (mFBO)
183	{
184		stop_glerror();
185		glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
186		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset,
187			LLTexUnit::getInternalType(mUsage), tex, 0);
188			stop_glerror();
189
190		check_framebuffer_status();
191		
192		glBindFramebuffer(GL_FRAMEBUFFER, 0);
193	}
194
195	mTex.push_back(tex);
196
197	if (gDebugGL)
198	{ //bind and unbind to validate target
199		bindTarget();
200		flush();
201	}
202
203	return true;
204}
205
206bool LLRenderTarget::allocateDepth()
207{
208	if (mStencil)
209	{
210		//use render buffers where stencil buffers are in play
211		glGenRenderbuffers(1, (GLuint *) &mDepth);
212		glBindRenderbuffer(GL_RENDERBUFFER, mDepth);
213		stop_glerror();
214		clear_glerror();
215		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mResX, mResY);
216		glBindRenderbuffer(GL_RENDERBUFFER, 0);
217	}
218	else
219	{
220		LLImageGL::generateTextures(1, &mDepth);
221		gGL.getTexUnit(0)->bindManual(mUsage, mDepth);
222		
223		U32 internal_type = LLTexUnit::getInternalType(mUsage);
224		stop_glerror();
225		clear_glerror();
226		LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
227		gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
228	}
229
230	sBytesAllocated += mResX*mResY*4;
231
232	if (glGetError() != GL_NO_ERROR)
233	{
234		llwarns << "Unable to allocate depth buffer for render target." << llendl;
235		return false;
236	}
237
238	return true;
239}
240
241void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target)
242{
243	if (!mFBO || !target.mFBO)
244	{
245		llerrs << "Cannot share depth buffer between non FBO render targets." << llendl;
246	}
247
248	if (target.mDepth)
249	{
250		llerrs << "Attempting to override existing depth buffer.  Detach existing buffer first." << llendl;
251	}
252
253	if (target.mUseDepth)
254	{
255		llerrs << "Attempting to override existing shared depth buffer. Detach existing buffer first." << llendl;
256	}
257
258	if (mDepth)
259	{
260		stop_glerror();
261		glBindFramebuffer(GL_FRAMEBUFFER, target.mFBO);
262		stop_glerror();
263
264		if (mStencil)
265		{
266			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepth);
267			stop_glerror();
268			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepth);			
269			stop_glerror();
270			target.mStencil = true;
271		}
272		else
273		{
274			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0);
275			stop_glerror();
276		}
277
278		check_framebuffer_status();
279
280		glBindFramebuffer(GL_FRAMEBUFFER, 0);
281
282		target.mUseDepth = true;
283	}
284}
285
286void LLRenderTarget::release()
287{
288	if (mDepth)
289	{
290		if (mStencil)
291		{
292			glDeleteRenderbuffers(1, (GLuint*) &mDepth);
293			stop_glerror();
294		}
295		else
296		{
297			LLImageGL::deleteTextures(1, &mDepth, true);
298			stop_glerror();
299		}
300		mDepth = 0;
301
302		sBytesAllocated -= mResX*mResY*4;
303	}
304	else if (mUseDepth && mFBO)
305	{ //detach shared depth buffer
306		glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
307		if (mStencil)
308		{ //attached as a renderbuffer
309			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
310			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
311			mStencil = false;
312		}
313		else
314		{ //attached as a texture
315			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), 0, 0);
316		}
317		mUseDepth = false;
318	}
319
320	if (mFBO)
321	{
322		glDeleteFramebuffers(1, (GLuint *) &mFBO);
323		mFBO = 0;
324	}
325
326	if (mTex.size() > 0)
327	{
328		sBytesAllocated -= mResX*mResY*4*mTex.size();
329		LLImageGL::deleteTextures(mTex.size(), &mTex[0], true);
330		mTex.clear();
331	}
332	
333	mResX = mResY = 0;
334
335	sBoundTarget = NULL;
336}
337
338void LLRenderTarget::bindTarget()
339{
340	if (mFBO)
341	{
342		stop_glerror();
343		
344		glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
345		stop_glerror();
346		if (gGLManager.mHasDrawBuffers)
347		{ //setup multiple render targets
348			GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0,
349									GL_COLOR_ATTACHMENT1,
350									GL_COLOR_ATTACHMENT2,
351									GL_COLOR_ATTACHMENT3};
352			glDrawBuffersARB(mTex.size(), drawbuffers);
353		}
354			
355		if (mTex.empty())
356		{ //no color buffer to draw to
357			glDrawBuffer(GL_NONE);
358			glReadBuffer(GL_NONE);
359		}
360
361		check_framebuffer_status();
362
363		stop_glerror();
364	}
365
366	glViewport(0, 0, mResX, mResY);
367	sBoundTarget = this;
368}
369
370// static
371void LLRenderTarget::unbindTarget()
372{
373	if (gGLManager.mHasFramebufferObject)
374	{
375		glBindFramebuffer(GL_FRAMEBUFFER, 0);
376	}
377	sBoundTarget = NULL;
378}
379
380void LLRenderTarget::clear(U32 mask_in)
381{
382	U32 mask = GL_COLOR_BUFFER_BIT;
383	if (mUseDepth)
384	{
385		mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
386	}
387	if (mFBO)
388	{
389		check_framebuffer_status();
390		stop_glerror();
391		glClear(mask & mask_in);
392		stop_glerror();
393	}
394	else
395	{
396		LLGLEnable scissor(GL_SCISSOR_TEST);
397		glScissor(0, 0, mResX, mResY);
398		stop_glerror();
399		glClear(mask & mask_in);
400	}
401}
402
403U32 LLRenderTarget::getTexture(U32 attachment) const
404{
405	if (attachment > mTex.size()-1)
406	{
407		llerrs << "Invalid attachment index." << llendl;
408	}
409	if (mTex.empty())
410	{
411		return 0;
412	}
413	return mTex[attachment];
414}
415
416void LLRenderTarget::bindTexture(U32 index, S32 channel)
417{
418	gGL.getTexUnit(channel)->bindManual(mUsage, getTexture(index));
419}
420
421void LLRenderTarget::flush(bool fetch_depth)
422{
423	gGL.flush();
424	if (!mFBO)
425	{
426		gGL.getTexUnit(0)->bind(this);
427		glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, 0, 0, 0, 0, mResX, mResY);
428
429		if (fetch_depth)
430		{
431			if (!mDepth)
432			{
433				allocateDepth();
434			}
435
436			gGL.getTexUnit(0)->bind(this);
437			glCopyTexImage2D(LLTexUnit::getInternalType(mUsage), 0, GL_DEPTH24_STENCIL8, 0, 0, mResX, mResY, 0);
438		}
439
440		gGL.getTexUnit(0)->disable();
441	}
442	else
443	{
444		stop_glerror();
445		glBindFramebuffer(GL_FRAMEBUFFER, 0);
446		stop_glerror();
447	}
448}
449
450void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1,
451						S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter)
452{
453	GLboolean write_depth = mask & GL_DEPTH_BUFFER_BIT ? TRUE : FALSE;
454
455	LLGLDepthTest depth(write_depth, write_depth);
456
457	gGL.flush();
458	if (!source.mFBO || !mFBO)
459	{
460		llwarns << "Cannot copy framebuffer contents for non FBO render targets." << llendl;
461		return;
462	}
463
464	
465	if (mask == GL_DEPTH_BUFFER_BIT && source.mStencil != mStencil)
466	{
467		stop_glerror();
468		
469		glBindFramebuffer(GL_FRAMEBUFFER, source.mFBO);
470		check_framebuffer_status();
471		gGL.getTexUnit(0)->bind(this, true);
472		stop_glerror();
473		glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1);
474		stop_glerror();
475		glBindFramebuffer(GL_FRAMEBUFFER, 0);
476		stop_glerror();
477	}
478	else
479	{
480		glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO);
481		stop_glerror();
482		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFBO);
483		stop_glerror();
484		check_framebuffer_status();
485		stop_glerror();
486		glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
487		stop_glerror();
488		glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
489		stop_glerror();
490		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
491		stop_glerror();
492		glBindFramebuffer(GL_FRAMEBUFFER, 0);
493		stop_glerror();
494	}
495}
496
497//static
498void LLRenderTarget::copyContentsToFramebuffer(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1,
499						S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter)
500{
501	if (!source.mFBO)
502	{
503		llerrs << "Cannot copy framebuffer contents for non FBO render targets." << llendl;
504	}
505	{
506		GLboolean write_depth = mask & GL_DEPTH_BUFFER_BIT ? TRUE : FALSE;
507
508		LLGLDepthTest depth(write_depth, write_depth);
509		
510		glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO);
511		stop_glerror();
512		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
513		stop_glerror();
514		check_framebuffer_status();
515		stop_glerror();
516		glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
517		stop_glerror();
518		glBindFramebuffer(GL_FRAMEBUFFER, 0);
519		stop_glerror();
520	}
521}
522
523bool LLRenderTarget::isComplete() const
524{
525	return (!mTex.empty() || mDepth) ? true : false;
526}
527
528void LLRenderTarget::getViewport(S32* viewport)
529{
530	viewport[0] = 0;
531	viewport[1] = 0;
532	viewport[2] = mResX;
533	viewport[3] = mResY;
534}
535