summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp')
-rw-r--r--Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp958
1 files changed, 958 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp
new file mode 100644
index 0000000..d397c6e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp
@@ -0,0 +1,958 @@
+#include "UnityPrefix.h"
+#include "RenderTextureGLES20.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "DebugGLES20.h"
+#include "TextureIdMapGLES20.h"
+#include "UnityGLES20Ext.h"
+
+// seems like code is the best place to leave TODO:
+// GetCurrentFBImpl - it is called in random places - should just put somehwere (begin frame?)
+// discard logic - both color/depth have flags, but only color is used, also - mrt case
+
+
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+
+#if 1
+ #define DBG_LOG_RT_GLES20(...) {}
+#else
+ #define DBG_LOG_RT_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#endif
+
+
+#if GFX_SUPPORTS_OPENGLES20
+
+#define GL_RT_COMMON_GLES2 1
+#include "Runtime/GfxDevice/GLRTCommon.h"
+#undef GL_RT_COMMON_GLES2
+
+#if UNITY_IPHONE
+ extern "C" bool UnityDefaultFBOHasMSAA();
+ extern "C" void* UnityDefaultFBOColorBuffer();
+#endif
+
+
+struct RenderColorSurfaceGLES2 : public RenderSurfaceBase
+{
+ GLuint m_ColorBuffer;
+ RenderTextureFormat format;
+ TextureDimension dim;
+};
+
+struct RenderDepthSurfaceGLES2 : public RenderSurfaceBase
+{
+ GLuint m_DepthBuffer;
+ DepthBufferFormat depthFormat;
+ bool depthWithStencil;
+};
+
+extern GLint gDefaultFBO;
+static bool gDefaultFboInited = false;
+
+static const unsigned long kOpenGLESTextureDimensionTable[kTexDimCount] = {0, 0, GL_TEXTURE_2D, 0, GL_TEXTURE_CUBE_MAP, 0};
+
+static RenderColorSurfaceGLES2* s_ActiveColorTarget[kMaxSupportedRenderTargets] = {0};
+static int s_ActiveColorTargetCount = 0;
+static int s_ActiveMip = 0;
+static CubemapFace s_ActiveFace = kCubeFaceUnknown;
+static RenderDepthSurfaceGLES2* s_ActiveDepthTarget = 0;
+
+static RenderColorSurfaceGLES2 s_BackBufferColor;
+static RenderDepthSurfaceGLES2 s_BackBufferDepth;
+
+
+// at least on ios when changing ext of attachments there is huge perf penalty
+// so do fbo per rt-ext
+
+typedef std::pair<unsigned, unsigned> FBKey;
+typedef std::map<FBKey, GLuint> FBMap;
+
+static FBMap _FBMap;
+static GLuint GetFBFromAttachments(RenderColorSurfaceGLES2* color, RenderDepthSurfaceGLES2* depth)
+{
+ // while it may seems bad that we freely mix rb/texture - we do the distinction per-gpu/per-platform mostly
+ // so it is actually ok
+ unsigned ckey = color->textureID.m_ID ? color->textureID.m_ID : color->m_ColorBuffer;
+ unsigned dkey = depth->textureID.m_ID ? depth->textureID.m_ID : depth->m_DepthBuffer;
+ FBKey key = std::make_pair(ckey, dkey);
+
+
+ FBMap::iterator fbi = _FBMap.find(key);
+ if(fbi == _FBMap.end())
+ {
+ GLuint fb=0;
+ GLES_CHK(glGenFramebuffers(1, &fb));
+
+ fbi = _FBMap.insert(std::make_pair(key, fb)).first;
+ }
+
+ Assert(fbi != _FBMap.end());
+ return fbi->second;
+}
+
+// this should be called on context loss
+// TODO: need we care about the case of actually deleting FBOs
+void ClearFBMapping()
+{
+ _FBMap.clear();
+}
+
+// this is not the best way, but unless we have proper rt->fbo mapping let's live with that
+// the reason is to avoid leaks when we destroy rb/tex that is still attached to some FBO
+static void OnFBOAttachmentDelete(RenderSurfaceBase* rs)
+{
+ unsigned key = rs->textureID.m_ID;
+ if(key == 0)
+ key = rs->colorSurface ? ((RenderColorSurfaceGLES2*)rs)->m_ColorBuffer : ((RenderDepthSurfaceGLES2*)rs)->m_DepthBuffer;
+
+ int curFB = GetCurrentFBGLES2();
+
+ for(FBMap::iterator i = _FBMap.begin() ; i != _FBMap.end() ; )
+ {
+ if(i->first.first == key || i->first.second == key)
+ {
+ // make sure we also drop attachments before killing FBO (some drivers may leak otherwise)
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, i->second));
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, curFB));
+
+ GLES_CHK(glDeleteFramebuffers(1, &i->second));
+ FBMap::iterator rem = i;
+ ++i;
+ _FBMap.erase(rem);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+}
+
+static const char* GetFBOStatusError( GLenum status )
+{
+ Assert( status != GL_FRAMEBUFFER_COMPLETE ); // should not be called when everything is ok...
+ switch( status )
+ {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return "INCOMPLETE_DIMENSIONS";
+ case GL_FRAMEBUFFER_UNSUPPORTED: return "UNSUPPORTED";
+ default: return "unknown error";
+ }
+}
+static const char* GetFBOAttachementType(GLint type)
+{
+ switch (type)
+ {
+ case GL_RENDERBUFFER: return "GL_RENDERBUFFER";
+ case GL_TEXTURE: return "GL_TEXTURE";
+ default: return "GL_NONE";
+ }
+}
+
+static int GetCurrentFBImpl()
+{
+ GLint curFB;
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &curFB));
+
+ return (int)curFB;
+}
+
+int GetCurrentFBGLES2()
+{
+ return GetCurrentFBImpl();
+}
+
+void EnsureDefaultFBInitedGLES2()
+{
+ if(!gDefaultFboInited)
+ {
+ gDefaultFBO = GetCurrentFBImpl();
+ // this will be called from correct thread, so no need to sync or whatever
+ extern void GfxDeviceGLES20_InitFramebufferDepthFormat();
+ GfxDeviceGLES20_InitFramebufferDepthFormat();
+
+ gDefaultFboInited = true;
+ }
+}
+
+void DiscardCurrentFBImpl(bool discardColor, bool discardDepth, GLenum target=GL_FRAMEBUFFER)
+{
+ if(gGraphicsCaps.gles20.hasDiscardFramebuffer)
+ {
+ // TODO: mrt case?
+ GLenum discardUserAttach[] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
+ GLenum discardSystemAttach[] = {GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT};
+
+ const GLenum* discardTarget = discardUserAttach;
+ if(s_ActiveColorTarget[0]->backBuffer && GetCurrentFBGLES2() == 0)
+ discardTarget = discardSystemAttach;
+
+
+ if(!discardColor) discardTarget += 1;
+
+ unsigned discardCount = 0;
+ if(discardColor) discardCount += 1;
+ if(discardDepth) discardCount += 2;
+
+ if(discardCount)
+ GLES_CHK(gGlesExtFunc.glDiscardFramebufferEXT(GL_FRAMEBUFFER, discardCount, discardTarget));
+ }
+}
+
+void ClearCurrentFBImpl(bool clearColor, bool clearDepth)
+{
+ if(gGraphicsCaps.hasTiledGPU)
+ {
+ UInt32 clearFlags = (clearColor ? kGfxClearColor : 0) | (clearDepth ? kGfxClearDepthStencil : 0);
+ float clearColor[] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+ GetRealGfxDevice().Clear(clearFlags, clearColor, 1.0f, 0);
+ }
+}
+
+enum
+{
+ discardDiscardFB = 0,
+ discardClearFB = 1
+};
+
+static void DiscardContentsImpl(int discardPhase)
+{
+ // TODO: mrt?
+ if(s_ActiveColorTarget[0] || s_ActiveDepthTarget)
+ {
+ bool* colorVar = discardPhase == discardDiscardFB ? &s_ActiveColorTarget[0]->shouldDiscard : &s_ActiveColorTarget[0]->shouldClear;
+ bool* depthVar = discardPhase == discardDiscardFB ? &s_ActiveDepthTarget->shouldDiscard : &s_ActiveDepthTarget->shouldClear;
+
+ bool color = *colorVar, depth = *depthVar;
+
+ *colorVar = *depthVar = false;
+
+ if(color || depth)
+ {
+ if(discardPhase == discardDiscardFB) DiscardCurrentFBImpl(color, depth);
+ else ClearCurrentFBImpl(color, depth);
+ }
+ }
+}
+
+
+void DiscardContentsGLES2(RenderSurfaceHandle rs)
+{
+ // TODO: handle bb
+ if(rs.IsValid())
+ {
+ RenderColorSurfaceGLES2* rsgles = reinterpret_cast<RenderColorSurfaceGLES2*>(rs.object);
+ // discard only makes sense for current active rt
+ rsgles->shouldDiscard = gGraphicsCaps.gles20.hasDiscardFramebuffer && rsgles == s_ActiveColorTarget[0];
+ rsgles->shouldClear = gGraphicsCaps.hasTiledGPU;
+ }
+}
+
+bool SetRenderTargetGLES2 (int count, RenderSurfaceHandle* colorHandle, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, GLuint globalSharedFBO)
+{
+ RenderColorSurfaceGLES2* rcolor0 = reinterpret_cast<RenderColorSurfaceGLES2*>( colorHandle[0].object );
+ RenderDepthSurfaceGLES2* rdepth = reinterpret_cast<RenderDepthSurfaceGLES2*>( depthHandle.object );
+
+ // Exit if nothing to do
+ if( s_ActiveColorTargetCount == count && s_ActiveDepthTarget == rdepth && s_ActiveFace == face && s_ActiveMip == mipLevel )
+ {
+ bool colorSame = true;
+ for(int i = 0 ; i < count && colorSame ; ++i)
+ {
+ if(s_ActiveColorTarget[i] != reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object))
+ colorSame = false;
+ }
+ if (colorSame)
+ return false;
+ }
+
+ EnsureDefaultFBInitedGLES2();
+ DiscardContentsImpl(discardDiscardFB);
+
+ AssertIf (!gGraphicsCaps.hasRenderToTexture);
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange(); // stats
+
+
+ Assert(colorHandle[0].IsValid() && depthHandle.IsValid());
+ Assert(colorHandle[0].object->backBuffer == depthHandle.object->backBuffer);
+
+
+ GLenum colorAttachStart = GL_COLOR_ATTACHMENT0;
+ if(gGraphicsCaps.gles20.hasNVMRT)
+ colorAttachStart = GL_COLOR_ATTACHMENT0_NV;
+
+ Assert(gGraphicsCaps.gles20.hasNVMRT || count==1);
+
+ if(!rcolor0->backBuffer)
+ {
+ GLuint fb = GetFBFromAttachments(rcolor0, rdepth);
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, fb));
+
+ GLenum drawBuffers[kMaxSupportedRenderTargets] = {0};
+ for(int i = 0 ; i < count ; ++i)
+ {
+ // when we start to support EXT variant - add check here
+ GLenum colorAttach = colorAttachStart + i;
+ RenderColorSurfaceGLES2* rcolor = reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object);
+ Assert(rcolor->colorSurface);
+
+ GLuint targetColorTex = (GLuint)TextureIdMap::QueryNativeTexture(rcolor->textureID);
+
+ drawBuffers[i] = colorAttach;
+ if(!IsDepthRTFormat(rcolor->format) && targetColorTex)
+ {
+ if (rcolor->dim == kTexDimCUBE)
+ {
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_CUBE_MAP_POSITIVE_X + clamp<int>(face,0,5), targetColorTex, mipLevel));
+ }
+ else
+ {
+ if(rcolor->samples > 1 && gGraphicsCaps.gles20.hasImgMSAA)
+ GLES_CHK(gGlesExtFunc.glFramebufferTexture2DMultisampleIMG(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_2D, targetColorTex, mipLevel, rcolor->samples));
+ else
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_2D, targetColorTex, mipLevel));
+ }
+ }
+ else
+ {
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, colorAttach, GL_RENDERBUFFER, rcolor->m_ColorBuffer));
+ if(rcolor->m_ColorBuffer == 0)
+ drawBuffers[i] = GL_NONE;
+ }
+ }
+
+ if(gGraphicsCaps.gles20.hasNVMRT)
+ gGlesExtFunc.glDrawBuffersNV(count, drawBuffers);
+
+ GLuint targetDepthTex = (GLuint)TextureIdMap::QueryNativeTexture(rdepth->textureID);
+ bool needAttachStencil = gGraphicsCaps.hasStencil && rdepth->depthWithStencil;
+
+ // depth surface
+ AssertIf (rdepth->colorSurface);
+ if (targetDepthTex
+#if UNITY_PEPPER
+// Workaround for http://code.google.com/p/angleproject/issues/detail?id=211
+ || rdepth->m_DepthBuffer == 0
+#endif
+ )
+ {
+ DBG_LOG_RT_GLES20("glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, %d, 0);", rdepth->textureID.m_ID);
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, targetDepthTex, 0));
+ // A driver might not support attaching depth & stencil as textures here.
+ if (!gGraphicsCaps.hasRenderTargetStencil)
+ needAttachStencil = false;
+ if (needAttachStencil)
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, targetDepthTex, 0));
+ else
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0));
+ }
+ else
+ {
+ DBG_LOG_RT_GLES20("glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, %d, 0);", rdepth->m_DepthBuffer);
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rdepth->m_DepthBuffer));
+ if(needAttachStencil)
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rdepth->m_DepthBuffer));
+ else
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ }
+
+ GLenum status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ int colorParam, depthParam, stencilParam;
+ int colorValue, depthValue, stencilValue;
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &colorParam);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthParam);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilParam);
+
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorValue);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthValue);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilValue);
+
+ AssertString (Format(
+ "FBO fail: %s\nDetailed description:\n"
+ "GL_COLOR_ATTACHMENT0 Type:%s Value:%d\n"
+ "GL_DEPTH_ATTACHMENT Type:%s Value:%d\n"
+ "GL_STENCIL_ATTACHEMENT Type:%s Value:%d\n",
+ GetFBOStatusError(status),
+ GetFBOAttachementType(colorParam), colorValue,
+ GetFBOAttachementType(depthParam), depthValue,
+ GetFBOAttachementType(stencilParam), stencilValue));
+ }
+ Assert(status == GL_FRAMEBUFFER_COMPLETE);
+ }
+ else
+ {
+ Assert(rcolor0->backBuffer && rdepth->backBuffer); // OpenGL can't mix FBO and native window at once
+ GLES_CHK(glBindFramebuffer( GL_FRAMEBUFFER, gDefaultFBO ));
+ }
+
+ // If we previously had a mip-mapped render texture, generate mip levels for it now.
+ if (s_ActiveColorTarget[0] &&
+ (s_ActiveColorTarget[0]->flags & kSurfaceCreateMipmap) &&
+ (s_ActiveColorTarget[0]->flags & kSurfaceCreateAutoGenMips))
+ {
+ const int textureTarget = kOpenGLESTextureDimensionTable[s_ActiveColorTarget[0]->dim];
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, s_ActiveColorTarget[0]->textureID, s_ActiveColorTarget[0]->dim, std::numeric_limits<float>::infinity());
+ GLES_CHK(glGenerateMipmap(textureTarget));
+ }
+
+ s_ActiveColorTargetCount = count;
+ for(int i = 0 ; i < count ; ++i)
+ s_ActiveColorTarget[i] = reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object);
+
+ s_ActiveDepthTarget = rdepth;
+ s_ActiveFace = face;
+ s_ActiveMip = mipLevel;
+
+ DiscardContentsImpl(discardClearFB);
+
+ return true;
+}
+
+// TODO: take index into account here too
+RenderSurfaceHandle GetActiveRenderColorSurfaceGLES2()
+{
+ return RenderSurfaceHandle(s_ActiveColorTarget[0]);
+}
+RenderSurfaceHandle GetActiveRenderDepthSurfaceGLES2()
+{
+ return RenderSurfaceHandle(s_ActiveDepthTarget);
+}
+
+bool IsActiveRenderTargetWithColorGLES2()
+{
+ return !s_ActiveColorTarget[0] || s_ActiveColorTarget[0]->backBuffer || !IsDepthRTFormat (s_ActiveColorTarget[0]->format);
+}
+
+
+static void CreateFBORenderColorSurfaceGLES (RenderColorSurfaceGLES2& rs)
+{
+ int textureTarget = kOpenGLESTextureDimensionTable[rs.dim];
+
+ if( !IsDepthRTFormat(rs.format) )
+ {
+ GLenum internalFormat = (rs.flags & kSurfaceCreateSRGB) ? RTColorInternalFormatSRGBGLES2(rs.format) : RTColorInternalFormatGLES2(rs.format);
+ GLenum textureFormat = (rs.flags & kSurfaceCreateSRGB) ? RTColorTextureFormatSRGBGLES2(rs.format) : RTColorTextureFormatGLES2(rs.format);
+ GLenum textureType = RTColorTextureTypeGLES2(rs.format);
+
+ // Create texture to render for color
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs.textureID, rs.dim, std::numeric_limits<float>::infinity());
+
+ if (rs.dim == kTexDimCUBE)
+ {
+ // cubemap: initialize all faces
+ for( int f = 0; f < 6; ++f )
+ {
+ glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, internalFormat, rs.width, rs.height, 0, textureFormat, textureType, NULL );
+ }
+ DBG_LOG_RT_GLES20("Creating FBO Color Surface (GL_TEXTURE_CUBE_MAP) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ if (rs.flags & kSurfaceCreateMipmap) { // establish mip map chain if needed
+ GLES_CHK(glGenerateMipmap( textureTarget ));
+ }
+ }
+ // not the best way around, but oh well
+ else if(!rs.textureID.m_ID)
+ {
+ internalFormat = RBColorInternalFormatGLES2(rs.format);
+ GLES_CHK(glGenRenderbuffers(1, &rs.m_ColorBuffer));
+ GLES_CHK(glBindRenderbuffer(GL_RENDERBUFFER, rs.m_ColorBuffer));
+ if(rs.samples > 1 && gGraphicsCaps.gles20.hasAppleMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, rs.samples, internalFormat, rs.width, rs.height));
+ else
+ GLES_CHK(glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rs.width, rs.height));
+ }
+ else
+ {
+ // regular texture: initialize
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ GLES_CHK(glTexImage2D( textureTarget, 0, internalFormat, rs.width, rs.height, 0, textureFormat, textureType, NULL ));
+
+ DBG_LOG_RT_GLES20("Creating FBO Color Surface (GL_TEXTURE_2D) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ if (rs.flags & kSurfaceCreateMipmap) { // establish mip map chain if needed
+ GLES_CHK(glGenerateMipmap( textureTarget ));
+ }
+ }
+ }
+ else
+ {
+ // Note : For GLES2.0Emu and NaCl we must create RenderBuffer even if no color
+ #if UNITY_WIN || UNITY_NACL
+ glGenRenderbuffers( 1, &rs.m_ColorBuffer );
+ glBindRenderbuffer( GL_RENDERBUFFER, rs.m_ColorBuffer );
+ glRenderbufferStorage( GL_RENDERBUFFER, GL_RGBA4, rs.width, rs.height );
+ DBG_LOG_RT_GLES20("Creating dummy color buffer (GL_RENDERBUFFER) [%d] - Width(%d) x Height(%d)", rs.m_ColorBuffer, rs.width, rs.height);
+ #endif
+ }
+}
+
+
+static void CreateFBORenderDepthSurfaceGLES (RenderDepthSurfaceGLES2& rs)
+{
+ int textureTarget = kOpenGLESTextureDimensionTable[kTexDim2D];
+
+ GLenum depthFormat;
+ GLenum genericFormat;
+ GLenum typeFormat;
+
+ // TODO 24bit depth should be revisited
+ if (rs.depthFormat == kDepthFormat24 && gGraphicsCaps.hasStencil)
+ {
+ rs.depthWithStencil = true;
+ depthFormat = GL_DEPTH24_STENCIL8_OES;
+ genericFormat = GL_DEPTH_STENCIL_OES;
+ typeFormat = GL_UNSIGNED_INT_24_8_OES;
+ }
+ else if (rs.depthFormat == kDepthFormat24 && gGraphicsCaps.gles20.has24DepthForFBO)
+ {
+ depthFormat = GL_DEPTH_COMPONENT24_OES;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_BYTE;
+ }
+ else if (gGraphicsCaps.gles20.hasNLZ)
+ {
+ depthFormat = 0x8E2C; // GL_DEPTH_COMPONENT16_NONLINEAR_NV
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_SHORT;
+ }
+ else
+ {
+ depthFormat = GL_DEPTH_COMPONENT16;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_SHORT;
+ }
+
+ GLuint targetDepthTex = (GLuint)TextureIdMap::QueryNativeTexture(rs.textureID);
+ if (!targetDepthTex)
+ {
+ // Note : For GLES2.0Emu we must create RenderBuffer even if depth format is none
+#if !UNITY_WIN
+ if( rs.depthFormat != kDepthFormatNone )
+#endif
+ {
+ GLES_CHK(glGenRenderbuffers( 1, &rs.m_DepthBuffer ));
+ GLES_CHK(glBindRenderbuffer( GL_RENDERBUFFER, rs.m_DepthBuffer ));
+
+ if(rs.samples > 1)
+ {
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, rs.samples, depthFormat, rs.width, rs.height));
+ else if(gGraphicsCaps.gles20.hasImgMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleIMG(GL_RENDERBUFFER, rs.samples, depthFormat, rs.width, rs.height));
+ }
+ else
+ {
+ GLES_CHK(glRenderbufferStorage( GL_RENDERBUFFER, depthFormat, rs.width, rs.height ));
+ }
+
+ DBG_LOG_RT_GLES20("Creating FBO Depth Surface (GL_RENDERBUFFER) [%d] - Width(%d) x Height(%d)", rs.m_DepthBuffer, rs.width, rs.height);
+ }
+ }
+ else
+ {
+ // create depth texture
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs.textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glTexImage2D( textureTarget, 0, genericFormat, rs.width, rs.height, 0, genericFormat, typeFormat, NULL ));
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (rs.flags & kSurfaceCreateShadowmap)
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_COMPARE_REF_TO_TEXTURE_EXT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_EXT, GL_LEQUAL);
+ }
+
+ DBG_LOG_RT_GLES20("Creating FBO Depth Surface (GL_TEXTURE_2D) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ }
+}
+
+static RenderColorSurfaceGLES2* CreateRenderColorSurfaceGLES2Impl(void* rs, TextureID textureID, unsigned rbID, int width, int height, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, int samples)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(textureID);
+ Assert(rbID == 0 || targetTex == 0);
+
+ if( !gGraphicsCaps.hasRenderToTexture || !gGraphicsCaps.supportsRenderTextureFormat[format] )
+ return 0;
+
+ RenderColorSurfaceGLES2* ret = rs ? (RenderColorSurfaceGLES2*)rs : new RenderColorSurfaceGLES2;
+ RenderSurfaceBase_InitColor(*ret);
+ ret->width = width;
+ ret->height = height;
+ ret->format = format;
+ ret->dim = dim;
+ ret->flags = createFlags;
+ ret->samples = samples > gGraphicsCaps.gles20.maxSamples ? gGraphicsCaps.gles20.maxSamples : samples;
+
+ ret->textureID = textureID;
+ ret->m_ColorBuffer = rbID;
+
+ if(textureID.m_ID && !rbID)
+ TextureIdMapGLES20_QueryOrCreate(textureID);
+
+ return ret;
+}
+
+RenderSurfaceHandle CreateRenderColorSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, int samples)
+{
+ RenderColorSurfaceGLES2* rs = CreateRenderColorSurfaceGLES2Impl(0, textureID, rbID, width, height, dim, createFlags, format, samples);
+ if(rs)
+ CreateFBORenderColorSurfaceGLES(*rs);
+
+ return RenderSurfaceHandle(rs);
+}
+
+static RenderDepthSurfaceGLES2* CreateRenderDepthSurfaceGLES2Impl(void* rs, TextureID textureID, unsigned rbID, int width, int height, UInt32 createFlags, DepthBufferFormat depthFormat, int samples)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(textureID);
+ Assert(rbID == 0 || targetTex == 0);
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return 0;
+
+ RenderDepthSurfaceGLES2* ret = rs ? (RenderDepthSurfaceGLES2*)rs : new RenderDepthSurfaceGLES2;
+ RenderSurfaceBase_InitDepth(*ret);
+ ret->depthWithStencil = false;
+ ret->width = width;
+ ret->height = height;
+ ret->depthFormat = depthFormat;
+ ret->flags = createFlags;
+ ret->samples = samples > gGraphicsCaps.gles20.maxSamples ? gGraphicsCaps.gles20.maxSamples : samples;
+
+ ret->textureID = textureID;
+ ret->m_DepthBuffer = rbID;
+
+ if(textureID.m_ID && !rbID)
+ TextureIdMapGLES20_QueryOrCreate(textureID);
+
+ return ret;
+}
+
+
+RenderSurfaceHandle CreateRenderDepthSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, UInt32 createFlags, DepthBufferFormat depthFormat, int samples)
+{
+ RenderDepthSurfaceGLES2* rs = CreateRenderDepthSurfaceGLES2Impl(0, textureID, rbID, width, height, createFlags, depthFormat, samples);
+ if(rs)
+ CreateFBORenderDepthSurfaceGLES(*rs);
+
+ return RenderSurfaceHandle(rs);
+}
+
+
+static void InternalDestroyRenderSurfaceGLES (RenderSurfaceBase* rs)
+{
+ AssertIf( !rs );
+
+ if (rs->textureID.m_ID)
+ {
+ GetRealGfxDevice().DeleteTexture( rs->textureID );
+ DBG_LOG_RT_GLES20("Destroying GL_TEXTURE_2D/GL_CUBE_MAP %d", rs->textureID.m_ID);
+ }
+ GLESAssert ();
+
+
+ RenderSurfaceHandle defaultColor = GetRealGfxDevice().GetBackBufferColorSurface();
+ RenderSurfaceHandle defaultDepth = GetRealGfxDevice().GetBackBufferDepthSurface();
+
+
+ for (int i = 0 ; i < s_ActiveColorTargetCount ; ++i)
+ {
+ if (s_ActiveColorTarget[i] == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetGLES2 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, gDefaultFBO);
+ }
+ }
+ if (s_ActiveDepthTarget == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetGLES2 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, gDefaultFBO);
+ }
+
+ OnFBOAttachmentDelete(rs);
+
+ if (rs->colorSurface)
+ {
+ RenderColorSurfaceGLES2* rsc = static_cast<RenderColorSurfaceGLES2*>(rs);
+ if( rsc->m_ColorBuffer )
+ {
+ GLES_CHK(glDeleteRenderbuffers( 1, &rsc->m_ColorBuffer ));
+ DBG_LOG_RT_GLES20("Destroying GL_RENDER_BUFFER %d", rsc->m_ColorBuffer);
+ }
+ }
+ else
+ {
+ RenderDepthSurfaceGLES2* rsd = static_cast<RenderDepthSurfaceGLES2*>(rs);
+ if( rsd->m_DepthBuffer )
+ {
+ GLES_CHK(glDeleteRenderbuffers( 1, &rsd->m_DepthBuffer ));
+ DBG_LOG_RT_GLES20("Destroying GL_RENDER_BUFFER %d", rsd->m_DepthBuffer);
+ }
+ }
+}
+
+
+void DestroyRenderSurfaceGLES2 (RenderSurfaceHandle& rsHandle)
+{
+ if(rsHandle.object->backBuffer)
+ return;
+
+ RenderSurfaceBase* rs = rsHandle.object;
+ InternalDestroyRenderSurfaceGLES (rs);
+ delete rs;
+ rsHandle.object = NULL;
+}
+
+void ResolveMSAA(GLuint dstTex, GLuint srcRB, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ GLint oldFBO;
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO));
+
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, globalSharedFBO));
+ GLES_CHK(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTex, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_APPLE, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_APPLE, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ ClearCurrentFBImpl(true, false);
+
+ GLES_CHK(glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, helperFBO));
+ GLES_CHK(glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, srcRB));
+ GLES_CHK(gGlesExtFunc.glResolveMultisampleFramebufferAPPLE());
+
+ DiscardCurrentFBImpl(true, false, GL_READ_FRAMEBUFFER_APPLE);
+ GLES_CHK(glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0));
+
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO));
+ }
+}
+
+RenderSurfaceBase* ResolveMSAASetupFBO(void* screenRS, int format, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ RenderSurfaceHandle ret;
+
+ RenderColorSurfaceGLES2* screen = (RenderColorSurfaceGLES2*)screenRS;
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ TextureID texid = GetRealGfxDevice().CreateTextureID();
+ ret = CreateRenderColorSurfaceGLES2(texid, 0, screen->width, screen->height, kTexDim2D, 0, (RenderTextureFormat)format, 1);
+
+ GLuint gltex = (GLuint)TextureIdMap::QueryNativeTexture(texid);
+ ResolveMSAA(gltex, screen->m_ColorBuffer, globalSharedFBO, helperFBO);
+
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, globalSharedFBO));
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gltex, 0));
+ }
+
+ return ret.object;
+}
+
+void ResolveMSAASetupFBO_Cleanup(RenderSurfaceBase* rs)
+{
+ RenderSurfaceHandle handle(rs);
+
+ if(handle.IsValid())
+ DestroyRenderSurfaceGLES2(handle);
+}
+
+
+void ResolveColorSurfaceGLES2(RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ // TODO: unify all the places we do resolve
+
+ Assert (srcHandle.IsValid());
+ Assert (dstHandle.IsValid());
+ RenderColorSurfaceGLES2* src = reinterpret_cast<RenderColorSurfaceGLES2*>(srcHandle.object);
+ RenderColorSurfaceGLES2* dst = reinterpret_cast<RenderColorSurfaceGLES2*>(dstHandle.object);
+ if (!src->colorSurface || !dst->colorSurface)
+ {
+ WarningString("RenderTexture: Resolving non-color surfaces.");
+ return;
+ }
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(dst->textureID);
+ if (!src->m_ColorBuffer || !targetTex)
+ {
+ WarningString("RenderTexture: Resolving NULL buffers.");
+ return;
+ }
+
+ ResolveMSAA(targetTex, src->m_ColorBuffer, globalSharedFBO, helperFBO);
+}
+
+void GrabIntoRenderTextureGLES2 (RenderSurfaceHandle rsHandle, RenderSurfaceHandle rdHandle, int x, int y, int width, int height, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ if(!rsHandle.IsValid() || rsHandle.object->backBuffer)
+ return;
+
+ RenderColorSurfaceGLES2* rs = reinterpret_cast<RenderColorSurfaceGLES2*>(rsHandle.object);
+
+#if UNITY_IPHONE
+ GLint oldFBO = 0;
+ RenderSurfaceBase* resolveRS = 0;
+
+ if(IsActiveMSAARenderTargetGLES2() && gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ RenderColorSurfaceGLES2* screen = (RenderColorSurfaceGLES2*)UnityDefaultFBOColorBuffer();
+
+ bool fullScreenResolve = (x==0 && y==0 && width == screen->width && height == screen->height);
+ if(fullScreenResolve)
+ {
+ GLuint texColor = (GLuint)TextureIdMap::QueryNativeTexture(rs->textureID);
+ ResolveMSAA(texColor, screen->m_ColorBuffer, globalSharedFBO, helperFBO);
+
+ return;
+ }
+ else
+ {
+ // intentional fall-through - we setup resolved FBO
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO));
+ resolveRS = ResolveMSAASetupFBO(screen, rs->format, globalSharedFBO, helperFBO);
+ }
+ }
+#endif
+
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs->textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ bool needsReadPixelsFallback = false;
+#if UNITY_WIN
+ // GLES 2.0 Emulator seems to have a bug, in that it can only do glCopyTexSubImage2D for power of two textures.
+ needsReadPixelsFallback = !IsPowerOfTwo(rs->width) || !IsPowerOfTwo(rs->height);
+#endif
+#if UNITY_ANDROID
+ // on pre-honeycomb devices AND in case of EGL_ANDROID_framebuffer_target
+ // we may end up with alphabits=0 explicitely, which, on some devices, cases FB to be considered RGB
+ {
+ GLint rbits=0, gbits=0, bbits=0, abits=0;
+ CHECK(glGetIntegerv(GL_RED_BITS, &rbits));
+ CHECK(glGetIntegerv(GL_GREEN_BITS, &gbits));
+ CHECK(glGetIntegerv(GL_BLUE_BITS, &bbits));
+ CHECK(glGetIntegerv(GL_ALPHA_BITS, &abits));
+
+ if(rbits==8 && gbits==8 && bbits==8 && abits==0)
+ needsReadPixelsFallback = true;
+ }
+#endif
+
+ if (needsReadPixelsFallback)
+ {
+ UInt8* data = NULL;
+ switch (rs->format)
+ {
+ case kRTFormatARGB32:
+ data = new UInt8[width*height*4];
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data));
+ GLES_CHK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data));
+ break;
+
+ default:
+ ErrorStringMsg ("Unsupported render texture format :%d", rs->format);
+ break;
+ }
+ delete [] data;
+ }
+ else
+ {
+ GLES_CHK(glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, x, y, width, height));
+ }
+
+#if UNITY_IPHONE
+ if(resolveRS)
+ {
+ ResolveMSAASetupFBO_Cleanup(resolveRS);
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO));
+ }
+#endif
+}
+
+RenderTextureFormat GetCurrentFBColorFormatGLES20()
+{
+ if( s_ActiveColorTarget[0] )
+ return s_ActiveColorTarget[0]->format;
+
+ return QueryFBColorFormatGLES2();
+}
+
+void InitBackBufferGLES2(RenderSurfaceBase** outColor, RenderSurfaceBase** outDepth)
+{
+ RenderSurfaceBase_InitColor(s_BackBufferColor);
+ s_BackBufferColor.backBuffer = true;
+
+ RenderSurfaceBase_InitDepth(s_BackBufferDepth);
+ s_BackBufferDepth.backBuffer = true;
+
+ *outColor = s_ActiveColorTarget[0] = &s_BackBufferColor;
+ *outDepth = s_ActiveDepthTarget = &s_BackBufferDepth;
+}
+
+void SetBackBufferGLES2()
+{
+ s_ActiveColorTarget[0] = &s_BackBufferColor;
+ s_ActiveColorTargetCount= 0;
+ s_ActiveMip = 0;
+ s_ActiveFace = kCubeFaceUnknown;
+
+ s_ActiveDepthTarget = &s_BackBufferDepth;
+
+ EnsureDefaultFBInitedGLES2();
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, gDefaultFBO));
+}
+
+
+#if UNITY_IPHONE
+bool IsActiveMSAARenderTargetGLES2()
+{
+ return s_ActiveColorTarget[0] && !s_ActiveColorTarget[0]->backBuffer ? s_ActiveColorTarget[0]->samples > 1 : UnityDefaultFBOHasMSAA();
+}
+
+void* UnityCreateUpdateExternalColorSurfaceGLES2(void* surf, unsigned texid, unsigned rbid, int width, int height, bool is32bit)
+{
+ TextureID tex = surf ? ((RenderSurfaceBase*)surf)->textureID : GetUncheckedGfxDevice().CreateTextureID();
+ TextureIdMap::UpdateTexture(tex, texid);
+
+ // TODO for android we will probably need to properly distinct 24/32
+ RenderTextureFormat format = is32bit ? kRTFormatARGB32 : kRTFormatRGB565;
+ return CreateRenderColorSurfaceGLES2Impl(surf, tex, rbid, width, height, kTexDim2D, 0, format, 1);
+}
+void* UnityCreateUpdateExternalDepthSurfaceGLES2(void* surf, unsigned texid, unsigned rbid, int width, int height, bool is24bit)
+{
+ TextureID tex = surf ? ((RenderSurfaceBase*)surf)->textureID : GetUncheckedGfxDevice().CreateTextureID();
+ TextureIdMap::UpdateTexture(tex, texid);
+
+ DepthBufferFormat format = is24bit ? kDepthFormat24 : kDepthFormat16;
+ return CreateRenderDepthSurfaceGLES2Impl(surf, tex, rbid, width, height, 0, format, 1);
+}
+
+void UnityDestroyExternalColorSurfaceGLES2(void* surf)
+{
+ delete (RenderColorSurfaceGLES2*)surf;
+}
+void UnityDestroyExternalDepthSurfaceGLES2(void* surf)
+{
+ delete (RenderDepthSurfaceGLES2*)surf;
+}
+#endif
+
+
+#endif // GFX_SUPPORTS_OPENGLES20