void AfterPresentSurfaceGLES(EAGLSurfaceDesc* surface)
{
	if(surface->use32bitColor != UnityUse32bitDisplayBuffer())
	{
		surface->use32bitColor = UnityUse32bitDisplayBuffer();
		CreateSurfaceGLES(surface);
		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->renderbuffer) );
	}

#if GL_APPLE_framebuffer_multisample
	if (_supportsMSAA)
	{
		const int desiredMSAASamples = UnityGetDesiredMSAASampleCount(MSAA_DEFAULT_SAMPLE_COUNT);
		if (surface->msaaSamples != desiredMSAASamples)
		{
			surface->msaaSamples = desiredMSAASamples;
			CreateSurfaceMultisampleBuffersGLES(surface);
			GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->renderbuffer) );
		}

		if (surface->msaaSamples > 1)
		{
			UNITY_DBG_LOG ("  glBindFramebufferOES (GL_FRAMEBUFFER_OES, %i); // PresentSurface\n", surface->msaaFramebuffer);
			GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->msaaFramebuffer) );

			gDefaultFBO = surface->msaaFramebuffer;
		}
	}
#endif
}
void DestroySurfaceMultisampleBuffersGLES(EAGLSurfaceDesc* surface)
{
	if(surface->depthbuffer)
	{
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->depthbuffer) );
	}
	surface->depthbuffer = 0;

	if(surface->msaaDepthbuffer)
	{
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->msaaDepthbuffer) );
	}
	surface->msaaDepthbuffer = 0;

	if(surface->msaaRenderbuffer)
	{
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->msaaRenderbuffer) );
	}
	surface->msaaRenderbuffer = 0;

	if (surface->msaaFramebuffer)
	{
		GLES_CHK( glDeleteFramebuffersOES(1, &surface->msaaFramebuffer) );
	}
	surface->msaaFramebuffer = 0;
}
void DestroySurfaceGLES(EAGLSurfaceDesc* surface)
{
	if( surface->systemRenderbuffer )
	{
		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->systemRenderbuffer) );
		DeallocateRenderBufferStorageFromEAGLLayer();

		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, 0) );
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->systemRenderbuffer) );

		surface->systemRenderbuffer = 0;
	}

	if( surface->systemFramebuffer )
	{
		GLES_CHK( glDeleteFramebuffersOES(1, &surface->systemFramebuffer) );
		surface->systemFramebuffer = 0;
	}

	DestroyRenderingSurfaceGLES(surface);

	if(surface->depthbuffer)
	{
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->depthbuffer) );
		surface->depthbuffer = 0;
	}
}
void DestroyRenderingSurfaceGLES(EAGLSurfaceDesc* surface)
{
	if( (surface->msaaFramebuffer || surface->targetFramebuffer) && surface->depthbuffer )
	{
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->depthbuffer) );
		surface->depthbuffer = 0;
	}

	if(surface->targetRT)
	{
		GLES_CHK( glDeleteTextures(1, &surface->targetRT) );
		surface->targetRT = 0;
	}

	if(surface->targetFramebuffer)
	{
		GLES_CHK( glDeleteFramebuffersOES(1, &surface->targetFramebuffer) );
		surface->targetFramebuffer = 0;
	}

	if(surface->msaaRenderbuffer)
	{
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->msaaRenderbuffer) );
		surface->msaaRenderbuffer = 0;
	}

	if(surface->msaaFramebuffer)
	{
		GLES_CHK( glDeleteFramebuffersOES(1, &surface->msaaFramebuffer) );
		surface->msaaFramebuffer = 0;
	}
}
void CreateSurfaceGLES(EAGLSurfaceDesc* surface)
{
	GLuint oldRenderbuffer;
	GLES_CHK( glGetIntegerv(GL_RENDERBUFFER_BINDING_OES, (GLint *) &oldRenderbuffer) );

	DestroySurfaceGLES(surface);

	InitEAGLLayer(surface->eaglLayer, surface->use32bitColor);

	GLES_CHK( glGenRenderbuffersOES(1, &surface->renderbuffer) );
	GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->renderbuffer) );

	if( !AllocateRenderBufferStorageFromEAGLLayer(surface->eaglLayer) )
	{
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->renderbuffer) );
		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_BINDING_OES, oldRenderbuffer) );

		printf_console("FAILED allocating render buffer storage from gles context\n");
		return;
	}

	GLES_CHK( glGenFramebuffersOES(1, &surface->framebuffer) );

	UNITY_DBG_LOG ("glBindFramebufferOES(GL_FRAMEBUFFER_OES, %d) :: AppCtrl\n", surface->framebuffer);
	GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->framebuffer) );

	gDefaultFBO = surface->framebuffer;

	UNITY_DBG_LOG ("glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, %d) :: AppCtrl\n", surface->renderbuffer);
	GLES_CHK( glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, surface->renderbuffer) );

	CreateSurfaceMultisampleBuffersGLES(surface);
}
void DestroySurfaceGLES(EAGLSurfaceDesc* surface)
{
	if( surface->renderbuffer )
	{
		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->renderbuffer) );
		DeallocateRenderBufferStorageFromEAGLLayer();

		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, 0) );
		GLES_CHK( glDeleteRenderbuffersOES(1, &surface->renderbuffer) );

		surface->renderbuffer = 0;
	}

	if( surface->framebuffer )
	{
		GLES_CHK( glDeleteFramebuffersOES(1, &surface->framebuffer) );
		surface->framebuffer = 0;
	}

	DestroySurfaceMultisampleBuffersGLES(surface);
}
void AfterPresentSurfaceGLES(EAGLSurfaceDesc* surface)
{
	if(surface->use32bitColor != UnityUse32bitDisplayBuffer())
	{
		surface->use32bitColor = UnityUse32bitDisplayBuffer();
		CreateSurfaceGLES(surface);
		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->systemRenderbuffer) );
	}

	if(NeedRecreateRenderingSurfaceGLES(surface))
	{
		UnityGetRenderingResolution(&surface->targetW, &surface->targetH);
		surface->msaaSamples = UnityGetDesiredMSAASampleCount(MSAA_DEFAULT_SAMPLE_COUNT);

		CreateRenderingSurfaceGLES(surface);
	}
}
extern "C" bool UnityResolveMSAA(GLuint destFBO, GLuint colorTex, GLuint colorBuf, GLuint depthTex, GLuint depthBuf)
{
#if GL_APPLE_framebuffer_multisample
	if (_surface.msaaSamples > 0 && _supportsMSAA && destFBO!=_surface.msaaFramebuffer && destFBO!=_surface.framebuffer)
	{
		Profiler_StartMSAAResolve();

		GLint oldFBO;
		GLES_CHK( glGetIntegerv (GL_FRAMEBUFFER_BINDING_OES, &oldFBO) );

		UNITY_DBG_LOG ("UnityResolveMSAA: samples=%i msaaFBO=%i destFBO=%i colorTex=%i colorRB=%i depthTex=%i depthRB=%i\n", _surface.msaaSamples, _surface.msaaFramebuffer, destFBO, colorTex, colorBuf, depthTex, depthBuf);
		UNITY_DBG_LOG ("  bind dest as DRAW FBO and textures/buffers into it\n");

		GLES_CHK( glBindFramebufferOES (GL_DRAW_FRAMEBUFFER_APPLE, destFBO) );
		if (colorTex)
			GLES_CHK( glFramebufferTexture2DOES( GL_DRAW_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0 ) );
		else if (colorBuf)
			GLES_CHK( glFramebufferRenderbufferOES (GL_DRAW_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuf) );

		if (depthTex)
			GLES_CHK( glFramebufferTexture2DOES( GL_DRAW_FRAMEBUFFER_APPLE, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0 ) );
		else if (depthBuf)
			GLES_CHK( glFramebufferRenderbufferOES (GL_DRAW_FRAMEBUFFER_APPLE, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuf) );

		UNITY_DBG_LOG ("  bind msaa as READ FBO\n");
		GLES_CHK( glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, _surface.msaaFramebuffer) );

		UNITY_DBG_LOG ("  glResolveMultisampleFramebufferAPPLE ();\n");
		GLES_CHK( glResolveMultisampleFramebufferAPPLE() );

		GLES_CHK( glBindFramebufferOES (GL_FRAMEBUFFER_OES, oldFBO) );

		Profiler_EndMSAAResolve();
		return true;
	}
	#endif
	return false;
}
void PreparePresentSurfaceGLES(EAGLSurfaceDesc* surface)
{
#if GL_APPLE_framebuffer_multisample
	if( surface->msaaSamples > 0 && _supportsMSAA )
	{
		Profiler_StartMSAAResolve();

		UNITY_DBG_LOG ("  ResolveMSAA: samples=%i msaaFBO=%i destFBO=%i\n", surface->msaaSamples, surface->msaaFramebuffer, surface->framebuffer);
		GLES_CHK( glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, surface->msaaFramebuffer) );
		GLES_CHK( glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, surface->framebuffer) );

		GLES_CHK( glResolveMultisampleFramebufferAPPLE() );

		Profiler_EndMSAAResolve();
	}
#endif

	// update screenshot here
	if( UnityIsCaptureScreenshotRequested() )
	{
		GLint curfb = 0;
		GLES_CHK( glGetIntegerv(GL_FRAMEBUFFER_BINDING, &curfb) );
		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->framebuffer) );
		UnityCaptureScreenshot();
		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, curfb) );
	}

#if GL_EXT_discard_framebuffer
	if( _supportsDiscard )
	{
		GLenum attachments[3];
		int discardCount = 0;
		if (surface->msaaSamples > 1 && _supportsMSAA)
			attachments[discardCount++] = GL_COLOR_ATTACHMENT0_OES;

		if (surface->depthFormat)
			attachments[discardCount++] = GL_DEPTH_ATTACHMENT_OES;

		attachments[discardCount++] = GL_STENCIL_ATTACHMENT_OES;

		GLenum target = (surface->msaaSamples > 1 && _supportsMSAA) ? GL_READ_FRAMEBUFFER_APPLE: GL_FRAMEBUFFER_OES;
		if (discardCount > 0)
		{
			GLES_CHK( glDiscardFramebufferEXT(target, discardCount, attachments) );
		}
	}
#endif
}
void CreateSurfaceMultisampleBuffersGLES(EAGLSurfaceDesc* surface)
{
	UNITY_DBG_LOG ("CreateSurfaceMultisampleBuffers: samples=%i\n", surface->msaaSamples);
	GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->renderbuffer) );

	UNITY_DBG_LOG ("glBindFramebufferOES(GL_FRAMEBUFFER_OES, %d) :: AppCtrl\n", surface->framebuffer);
	GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->framebuffer) );

	gDefaultFBO = surface->framebuffer;

	DestroySurfaceMultisampleBuffersGLES(surface);

	GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->renderbuffer) );

	UNITY_DBG_LOG ("glBindFramebufferOES(GL_FRAMEBUFFER_OES, %d) :: AppCtrl\n", surface->framebuffer);
	GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->framebuffer) );

#if GL_APPLE_framebuffer_multisample
	if(surface->msaaSamples > 1)
	{
		GLES_CHK( glGenRenderbuffersOES(1, &surface->msaaRenderbuffer) );
		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->msaaRenderbuffer) );

		GLES_CHK( glGenFramebuffersOES(1, &surface->msaaFramebuffer) );

		UNITY_DBG_LOG ("glBindFramebufferOES(GL_FRAMEBUFFER_OES, %d) :: AppCtrl\n", surface->msaaFramebuffer);
		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->msaaFramebuffer) );

		gDefaultFBO = surface->msaaFramebuffer;

		GLES_CHK( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, surface->msaaSamples, surface->format, surface->w, surface->h) );
		GLES_CHK( glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, surface->msaaRenderbuffer) );

		if(surface->depthFormat)
		{
			GLES_CHK( glGenRenderbuffersOES(1, &surface->msaaDepthbuffer) );
			GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->msaaDepthbuffer) );
			GLES_CHK( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, surface->msaaSamples, GL_DEPTH_COMPONENT16_OES, surface->w, surface->h) );
			GLES_CHK( glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, surface->msaaDepthbuffer) );
		}
	}
	else
#endif
	{
		if(surface->depthFormat)
		{
			GLES_CHK( glGenRenderbuffersOES(1, &surface->depthbuffer) );
			GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->depthbuffer) );
			GLES_CHK( glRenderbufferStorageOES(GL_RENDERBUFFER_OES, surface->depthFormat, surface->w, surface->h) );

			UNITY_DBG_LOG ("glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, %d) :: AppCtrl\n", surface->depthbuffer);
			GLES_CHK( glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, surface->depthbuffer) );
		}
	}
}
void PreparePresentSurfaceGLES(EAGLSurfaceDesc* surface)
{
#if GL_APPLE_framebuffer_multisample
	if( surface->msaaSamples > 1 && _supportsMSAA )
	{
		Profiler_StartMSAAResolve();

		GLuint drawFB = surface->targetFramebuffer ? surface->targetFramebuffer : surface->systemFramebuffer;

		GLES_CHK( glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, surface->msaaFramebuffer) );
		GLES_CHK( glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, drawFB) );
		GLES_CHK( glResolveMultisampleFramebufferAPPLE() );

		Profiler_EndMSAAResolve();
	}
#endif

	// update screenshot from target FBO to get requested resolution
	if( UnityIsCaptureScreenshotRequested() )
	{
		GLint target = surface->targetFramebuffer ? surface->targetFramebuffer : surface->systemFramebuffer;

		GLint curfb = 0;
		GLES_CHK( glGetIntegerv(GL_FRAMEBUFFER_BINDING, &curfb) );

		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, target) );
		UnityCaptureScreenshot();
		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, curfb) );
	}


	if( surface->targetFramebuffer )
	{
		gDefaultFBO = surface->systemFramebuffer;
		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, gDefaultFBO) );

		UnityBlitToSystemFB(surface->targetRT, surface->targetW, surface->targetH, surface->systemW, surface->systemH);

		gDefaultFBO = surface->msaaFramebuffer ? surface->msaaFramebuffer : surface->targetFramebuffer;
		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, gDefaultFBO) );
	}

#if GL_EXT_discard_framebuffer
	if( _supportsDiscard )
	{
		GLenum	discardAttach[] = {GL_COLOR_ATTACHMENT0_OES, GL_DEPTH_ATTACHMENT_OES, GL_STENCIL_ATTACHMENT_OES};

		if( surface->msaaFramebuffer )
			GLES_CHK( glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 3, discardAttach) );

		if(surface->targetFramebuffer)
		{
			GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->targetFramebuffer) );
			GLES_CHK( glDiscardFramebufferEXT(GL_FRAMEBUFFER_OES, 3, discardAttach) );
		}

		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->systemFramebuffer) );
		GLES_CHK( glDiscardFramebufferEXT(GL_FRAMEBUFFER_OES, 2, &discardAttach[1]) );

		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, gDefaultFBO) );
	}
#endif
}
void CreateRenderingSurfaceGLES(EAGLSurfaceDesc* surface)
{
	gDefaultFBO = surface->systemFramebuffer;

	GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->systemFramebuffer) );
	GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->systemRenderbuffer) );

	DestroyRenderingSurfaceGLES(surface);

	GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->systemFramebuffer) );
	GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->systemRenderbuffer) );

	if( surface->targetW != surface->systemW || surface->targetH != surface->systemH )
	{
		GLES_CHK( glGenTextures(1, &surface->targetRT) );
		GLES_CHK( glBindTexture(GL_TEXTURE_2D, surface->targetRT) );
        GLES_CHK( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GLES_UPSCALE_FILTER) );
        GLES_CHK( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GLES_UPSCALE_FILTER) );
        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) );

        GLenum fmt  = surface->use32bitColor ? GL_RGBA : GL_RGB;
        GLenum type = surface->use32bitColor ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT_5_6_5;
        GLES_CHK( glTexImage2D(GL_TEXTURE_2D, 0, fmt, surface->targetW, surface->targetH, 0, fmt, type, 0) );

		GLES_CHK( glGenFramebuffersOES(1, &surface->targetFramebuffer) );
		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->targetFramebuffer) );
		GLES_CHK( glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, surface->targetRT, 0) );

		GLES_CHK( glBindTexture(GL_TEXTURE_2D, 0) );
		gDefaultFBO = surface->targetFramebuffer;
	}

#if GL_APPLE_framebuffer_multisample
	if(_supportsMSAA && surface->msaaSamples > 1)
	{
		GLES_CHK( glGenRenderbuffersOES(1, &surface->msaaRenderbuffer) );
		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->msaaRenderbuffer) );

		GLES_CHK( glGenFramebuffersOES(1, &surface->msaaFramebuffer) );
		GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, surface->msaaFramebuffer) );

		GLES_CHK( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, surface->msaaSamples, surface->format, surface->targetW, surface->targetH) );
		GLES_CHK( glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, surface->msaaRenderbuffer) );

		gDefaultFBO = surface->msaaFramebuffer;
	}
#endif

	GLES_CHK( glBindFramebufferOES(GL_FRAMEBUFFER_OES, gDefaultFBO) );
	if(surface->depthFormat != 0)
	{
		GLES_CHK( glGenRenderbuffersOES(1, &surface->depthbuffer) );
		GLES_CHK( glBindRenderbufferOES(GL_RENDERBUFFER_OES, surface->depthbuffer) );

		bool needMSAA = GL_APPLE_framebuffer_multisample && (surface->msaaSamples > 1);

	#if GL_APPLE_framebuffer_multisample
		if(needMSAA)
			GLES_CHK( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, surface->msaaSamples, surface->depthFormat, surface->targetW, surface->targetH) );
	#endif

		if(!needMSAA)
			GLES_CHK( glRenderbufferStorageOES(GL_RENDERBUFFER_OES, surface->depthFormat, surface->targetW, surface->targetH) );

		GLES_CHK( glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, surface->depthbuffer) );
	}
}