Bladeren bron

Merge branch 'master' of github.com:bkaradzic/bgfx

bkaradzic 13 jaren geleden
bovenliggende
commit
5edeb765fe
6 gewijzigde bestanden met toevoegingen van 367 en 39 verwijderingen
  1. 8 1
      README.md
  2. 114 0
      src/glcontext_wgl.cpp
  3. 1 1
      src/glimports.h
  4. 2 1
      src/renderer_d3d11.cpp
  5. 229 34
      src/renderer_gl.cpp
  6. 13 2
      src/renderer_gl.h

+ 8 - 1
README.md

@@ -65,6 +65,9 @@ point to DirectX SDK directory.
 
 	setx DXSDK_DIR <path to DirectX SDK directory>
 
+If you're building with Visual Studio 2008, you'll need TR1 support from:  
+[Visual C++ 2008 Feature Pack Release](https://www.microsoft.com/en-us/download/details.aspx?id=6922)
+
 If you're building with MinGW/TDM compiler on Windows make DirectX SDK
 directory link to directory without spaces in the path.
 
@@ -199,6 +202,9 @@ cross-platform shaders.
 
 ### Texture Compiler (texturec)
 
+This tool doesn't currently exist. Use nvdxt, or any other tool that produces
+DDS textures for now.
+
 ### Geometry Compiler (geometryc)
 
 Converts Wavefront .obj mesh file to format optimal for using with bgfx.
@@ -208,11 +214,12 @@ Todo
 
  - Multiple render targets.
  - BlendFuncSeparate and BlendEquationSeparate.
- - Copy from texture to texture.
+ - Blit between textures.
  - Occlusion queries.
  - iOS platforms.
  - DX11: MSAA.
  - GL: MSAA.
+ - Fullscreen mode.
 
 Notice
 ------

+ 114 - 0
src/glcontext_wgl.cpp

@@ -16,6 +16,9 @@ namespace bgfx
 	PFNWGLMAKECURRENTPROC wglMakeCurrent;
 	PFNWGLCREATECONTEXTPROC wglCreateContext;
 	PFNWGLDELETECONTEXTPROC wglDeleteContext;
+	PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB;
+	PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
+	PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
 
 #	define GL_IMPORT(_optional, _proto, _func) _proto _func
 #		include "glimports.h"
@@ -80,6 +83,117 @@ namespace bgfx
 		result = wglMakeCurrent(m_hdc, m_context);
 		BGFX_FATAL(0 != result, Fatal::UnableToInitialize, "wglMakeCurrent failed!");
 
+		wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
+		wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
+		wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
+
+		if (NULL != wglGetExtensionsStringARB)
+		{
+			BX_TRACE("WGL extensions:");
+			const char* extensions = (const char*)wglGetExtensionsStringARB(m_hdc);
+			if (NULL != extensions)
+			{
+				char name[1024];
+				const char* pos = extensions;
+				const char* end = extensions + strlen(extensions);
+				while (pos < end)
+				{
+					uint32_t len;
+					const char* space = strchr(pos, ' ');
+					if (NULL != space)
+					{
+						len = uint32_min(sizeof(name), (uint32_t)(space - pos) );
+					}
+					else
+					{
+						len = uint32_min(sizeof(name), (uint32_t)strlen(pos) );
+					}
+
+					strncpy(name, pos, len);
+					name[len] = '\0';
+
+					BX_TRACE("\t%s", name);
+
+					pos += len+1;
+				}
+			}
+		}
+
+#if 0
+		// An application can only set the pixel format of a window one time.
+		// Once a window's pixel format is set, it cannot be changed.
+		// MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/dd369049%28v=vs.85%29.aspx
+		if (NULL != wglChoosePixelFormatARB
+		&&  NULL != wglCreateContextAttribsARB)
+		{
+			int32_t attrs[] =
+			{
+				WGL_SAMPLE_BUFFERS_ARB, 0,
+				WGL_SAMPLES_ARB, 0,
+				WGL_SUPPORT_OPENGL_ARB, true,
+				WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
+				WGL_DRAW_TO_WINDOW_ARB, true,
+				WGL_DOUBLE_BUFFER_ARB, true,
+				WGL_RED_BITS_ARB, 8,
+				WGL_BLUE_BITS_ARB, 8,
+				WGL_GREEN_BITS_ARB, 8,
+				WGL_ALPHA_BITS_ARB, 8,
+				WGL_DEPTH_BITS_ARB, 24,
+				WGL_STENCIL_BITS_ARB, 8,
+				0
+			};
+
+			uint32_t numFormats = 0;
+			do 
+			{
+				result = wglChoosePixelFormatARB(m_hdc, attrs, NULL, 1, &pixelFormat, &numFormats);
+				if (0 == result
+				||  0 == numFormats)
+				{
+					attrs[3] >>= 1;
+					attrs[1] = attrs[3] == 0 ? 0 : 1;
+				}
+
+			} while (0 == numFormats);
+
+			DescribePixelFormat(m_hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+
+			BX_TRACE("Pixel format:\n"
+				"\tiPixelType %d\n"
+				"\tcColorBits %d\n"
+				"\tcAlphaBits %d\n"
+				"\tcDepthBits %d\n"
+				"\tcStencilBits %d\n"
+				, pfd.iPixelType
+				, pfd.cColorBits
+				, pfd.cAlphaBits
+				, pfd.cDepthBits
+				, pfd.cStencilBits
+				);
+
+			result = SetPixelFormat(m_hdc, pixelFormat, &pfd);
+			BGFX_FATAL(0 != result, Fatal::UnableToInitialize, "SetPixelFormat failed (last err: 0x%08x)!", GetLastError() );
+
+			wglMakeCurrent(m_hdc, NULL);
+			wglDeleteContext(m_context);
+
+			const int32_t contextAttrs[] =
+			{
+				WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
+				WGL_CONTEXT_MINOR_VERSION_ARB, 1,
+#if BGFX_CONFIG_DEBUG
+				WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
+#endif // BGFX_CONFIG_DEBUG
+				0
+			};
+
+			m_context = wglCreateContextAttribsARB(m_hdc, 0, contextAttrs);
+
+			result = wglMakeCurrent(m_hdc, m_context);
+			BGFX_FATAL(0 != result, Fatal::UnableToInitialize, "wglMakeCurrent failed!");
+		}
+#endif // 0
+
 		import();
 	}
 

+ 1 - 1
src/glimports.h

@@ -119,7 +119,7 @@ GL_IMPORT(true,  PFNGLGETPROGRAMBINARYPROC,               glGetProgramBinary);
 GL_IMPORT(true,  PFNGLPROGRAMBINARYPROC,                  glProgramBinary);
 GL_IMPORT(true,  PFNGLPROGRAMPARAMETERIPROC,              glProgramParameteri);
 
-GL_IMPORT(true,  PFNGLBLITFRAMEBUFFEREXTPROC,             glBlitFramebufferEXT);
+GL_IMPORT(true,  PFNGLBLITFRAMEBUFFERPROC,                glBlitFramebuffer);
 
 GL_IMPORT(true,  PFNGLQUERYCOUNTERPROC,                   glQueryCounter);
 GL_IMPORT(true,  PFNGLGETQUERYOBJECTI64VPROC,             glGetQueryObjecti64v);

+ 2 - 1
src/renderer_d3d11.cpp

@@ -452,7 +452,8 @@ namespace bgfx
 		{
 			if (NULL != m_swapChain)
 			{
-				DX_CHECK(m_swapChain->Present(0, 0) );
+				uint32_t syncInterval = !!(m_flags & BGFX_RESET_VSYNC);
+				DX_CHECK(m_swapChain->Present(syncInterval, 0) );
 			}
 		}
 

+ 229 - 34
src/renderer_gl.cpp

@@ -34,6 +34,7 @@ namespace bgfx
 			EXT_texture_sRGB,
 			ARB_texture_swizzle,
 			EXT_texture_swizzle,
+			ARB_texture_multisample,
 			OES_standard_derivatives,
 			ARB_get_program_binary,
 			OES_get_program_binary,
@@ -85,6 +86,7 @@ namespace bgfx
 		{ "GL_EXT_texture_sRGB",                  false, true },
 		{ "GL_ARB_texture_swizzle",               false, true },
 		{ "GL_EXT_texture_swizzle",               false, true },
+		{ "GL_ARB_texture_multisample",           false, true },
 		{ "GL_OES_standard_derivatives",          false, true },
 		{ "GL_ARB_get_program_binary",            false, true },
 		{ "GL_OES_get_program_binary",            false, false },
@@ -155,16 +157,20 @@ namespace bgfx
 	struct RendererContext
 	{
 		RendererContext()
-			: m_capture(NULL)
+			: m_rtMsaa(false)
+			, m_capture(NULL)
 			, m_captureSize(0)
 			, m_maxAnisotropy(0.0f)
+			, m_maxMsaa(0)
 			, m_vaoSupport(false)
 			, m_programBinarySupport(false)
 			, m_textureSwizzleSupport(false)
 			, m_flip(false)
 			, m_postSwapBuffers(NULL)
 			, m_hash( (BX_PLATFORM_WINDOWS<<1) | BX_ARCH_64BIT)
+			, m_backBufferFbo(0)
 		{
+			m_rt.idx = invalidHandle;
 			memset(&m_resolution, 0, sizeof(m_resolution) );
 		}
 
@@ -178,12 +184,108 @@ namespace bgfx
 				m_textVideoMem.clear();
 
 				m_resolution = _resolution;
-				setRenderContextSize(_resolution.m_width, _resolution.m_height);
+
+				uint32_t msaa = 1<<( (m_resolution.m_flags&BGFX_RESET_MSAA_MASK)>>BGFX_RESET_MSAA_SHIFT);
+				msaa = uint32_min(m_maxMsaa, msaa == 0 ? 0 : 1<<msaa);
+				setRenderContextSize(_resolution.m_width, _resolution.m_height, msaa);
 				updateCapture();
 			}
 		}
 
-		void setRenderContextSize(uint32_t _width, uint32_t _height)
+		uint32_t setRenderTarget(RenderTargetHandle _rt, uint32_t _height, bool _msaa = true)
+		{
+			if (m_rt.idx != invalidHandle
+			&&  m_rt.idx != _rt.idx
+			&&  m_rtMsaa)
+			{
+				RenderTarget& renderTarget = m_renderTargets[m_rt.idx];
+				if (0 != renderTarget.m_fbo[1])
+				{
+					renderTarget.resolve();
+				}
+			}
+
+			if (_rt.idx == invalidHandle)
+			{
+				GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_backBufferFbo) );
+			}
+			else
+			{
+				RenderTarget& renderTarget = m_renderTargets[_rt.idx];
+				GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, renderTarget.m_fbo[0]) );
+				_height = renderTarget.m_height;
+			}
+
+			m_rt = _rt;
+			m_rtMsaa = _msaa;
+
+			return _height;
+		}
+
+		void createMsaaFbo(uint32_t _width, uint32_t _height, uint32_t _msaa)
+		{
+#if BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+			if (1 < _msaa)
+			{
+				GL_CHECK(glGenFramebuffers(1, &m_backBufferFbo) );
+				GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_backBufferFbo) );
+				GL_CHECK(glGenRenderbuffers(3, m_backBufferRbos) );
+				GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, m_backBufferRbos[0]) );
+				GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, _msaa, GL_RGBA8, _width, _height) );
+				GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, m_backBufferRbos[1]) );
+				GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, _msaa, GL_DEPTH24_STENCIL8, _width, _height) );
+				GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_backBufferRbos[0]) );
+				GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_backBufferRbos[1]) );
+
+				BX_CHECK(GL_FRAMEBUFFER_COMPLETE ==  glCheckFramebufferStatus(GL_FRAMEBUFFER)
+					, "glCheckFramebufferStatus failed 0x%08x"
+					, glCheckFramebufferStatus(GL_FRAMEBUFFER)
+					);
+
+				GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_backBufferFbo) );
+			}
+#endif // BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+		}
+
+		void destroyMsaaFbo()
+		{
+#if BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+			if (0 != m_backBufferFbo)
+			{
+				GL_CHECK(glDeleteFramebuffers(1, &m_backBufferFbo) );
+				GL_CHECK(glDeleteRenderbuffers(3, m_backBufferRbos) );
+				m_backBufferFbo = 0;
+			}
+#endif // BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+		}
+
+		void blitMsaaFbo()
+		{
+#if BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+			if (0 != m_backBufferFbo)
+			{
+				GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0) );
+				GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, m_backBufferFbo) );
+				GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) );
+				uint32_t width = m_resolution.m_width;
+				uint32_t height = m_resolution.m_height;
+				GL_CHECK(glBlitFramebuffer(0
+					, 0
+					, width
+					, height
+					, 0
+					, 0
+					, width
+					, height
+					, GL_COLOR_BUFFER_BIT
+					, GL_LINEAR
+					) );
+				GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0) );
+			}
+#endif // BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+		}
+
+		void setRenderContextSize(uint32_t _width, uint32_t _height, uint32_t _msaa = 0)
 		{
 			if (_width != 0
 			||  _height != 0)
@@ -194,7 +296,11 @@ namespace bgfx
 				}
 				else
 				{
+					destroyMsaaFbo();
+
 					m_glctx.resize(_width, _height);
+
+					createMsaaFbo(_width, _height, _msaa);
 				}
 			}
 
@@ -328,11 +434,14 @@ namespace bgfx
 		VaoCache m_vaoCache;
 
 		TextVideoMem m_textVideoMem;
+		RenderTargetHandle m_rt;
+		bool m_rtMsaa;
 
 		Resolution m_resolution;
 		void* m_capture;
 		uint32_t m_captureSize;
 		float m_maxAnisotropy;
+		int32_t m_maxMsaa;
 		bool m_vaoSupport;
 		bool m_programBinarySupport;
 		bool m_textureSwizzleSupport;
@@ -341,6 +450,8 @@ namespace bgfx
 		PostSwapBuffersFn m_postSwapBuffers;
 		uint64_t m_hash;
 
+		GLuint m_backBufferFbo;
+		GLuint m_backBufferRbos[2];
 		GlContext m_glctx;
 	};
 
@@ -1303,11 +1414,12 @@ namespace bgfx
 	{
 		GLenum internalFormat = /*_fp ? GL_RGBA16F_ARB :*/ GL_RGBA;
 		GLenum type = /*_fp ? GL_HALF_FLOAT_ARB :*/ GL_UNSIGNED_BYTE;
-		m_target = /*0 != _depth ? GL_TEXTURE_3D :*/ GL_TEXTURE_2D;
+		m_target = GL_TEXTURE_2D;
 
 		GL_CHECK(glGenTextures(1, &m_id) );
 		BX_CHECK(0 != m_id, "Failed to generate texture id.");
 		GL_CHECK(glBindTexture(m_target, m_id) );
+
 		GL_CHECK(glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, _min) );
 		GL_CHECK(glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, _mag) );
 		GL_CHECK(glTexParameteri(m_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) );
@@ -1474,8 +1586,9 @@ namespace bgfx
 
 		m_width = _width;
 		m_height = _height;
+		uint32_t msaa = (_flags&BGFX_RENDER_TARGET_MSAA_MASK)>>BGFX_RENDER_TARGET_MSAA_SHIFT;
+		m_msaa = uint32_min(s_renderCtx.m_maxMsaa, msaa == 0 ? 0 : 1<<msaa);
 
-//		m_msaa = s_msaa[(m_flags&BGFX_RENDER_TARGET_MSAA_MASK)>>BGFX_RENDER_TARGET_MSAA_SHIFT];
 		uint32_t colorFormat = (_flags&BGFX_RENDER_TARGET_COLOR_MASK)>>BGFX_RENDER_TARGET_COLOR_SHIFT;
 		uint32_t depthFormat = (_flags&BGFX_RENDER_TARGET_DEPTH_MASK)>>BGFX_RENDER_TARGET_DEPTH_SHIFT;
 		GLenum minFilter = s_textureFilter[(_textureFlags&BGFX_TEXTURE_MIN_MASK)>>BGFX_TEXTURE_MIN_SHIFT];
@@ -1491,11 +1604,35 @@ namespace bgfx
 		{
 			m_depth.createDepth(_width, _height);
 		}
-#endif // 
+#endif //
 
-		GL_CHECK(glGenFramebuffers(1, &m_fbo) );
-		BX_CHECK(0 != m_fbo, "Failed to generate framebuffer id.");
-		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_fbo) );
+#if BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+		if (0 < colorFormat
+		&&  0 != m_msaa)
+		{
+			GL_CHECK(glGenFramebuffers(2, m_fbo) );
+			GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_fbo[0]) );
+
+			GL_CHECK(glGenRenderbuffers(1, &m_colorRbo) );
+			BX_CHECK(0 != m_colorRbo, "Failed to generate color renderbuffer id.");
+			GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, m_colorRbo) );
+			GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_msaa, GL_RGBA8, _width, _height) );
+			GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0) );
+
+			GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER
+				, GL_COLOR_ATTACHMENT0
+				, GL_RENDERBUFFER
+				, m_colorRbo
+				) );
+
+			GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_fbo[1]) );
+		}
+		else
+#endif // BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+		{
+			GL_CHECK(glGenFramebuffers(1, m_fbo) );
+			GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_fbo[0]) );
+		}
 
 		if (0 < colorFormat)
 		{
@@ -1507,8 +1644,15 @@ namespace bgfx
 							) );
 		}
 
+		BX_CHECK(GL_FRAMEBUFFER_COMPLETE ==  glCheckFramebufferStatus(GL_FRAMEBUFFER)
+			, "glCheckFramebufferStatus failed 0x%08x"
+			, glCheckFramebufferStatus(GL_FRAMEBUFFER)
+			);
+
 		if (0 < depthFormat)
 		{
+			GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_fbo[0]) );
+
 			if (0 < colorFormat)
 			{
 #if BGFX_CONFIG_RENDERER_OPENGL
@@ -1517,16 +1661,26 @@ namespace bgfx
 				GLenum depthComponent = GL_DEPTH_COMPONENT16;
 #endif // BGFX_CONFIG_RENDERER_OPENGL
 
-				GL_CHECK(glGenRenderbuffers(1, &m_rbo) );
-				BX_CHECK(0 != m_rbo, "Failed to generate renderbuffer id.");
-				GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, m_rbo) );
-				GL_CHECK(glRenderbufferStorage(GL_RENDERBUFFER, depthComponent, _width, _height) );
+				GL_CHECK(glGenRenderbuffers(1, &m_depthRbo) );
+				BX_CHECK(0 != m_depthRbo, "Failed to generate renderbuffer id.");
+				GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, m_depthRbo) );
+
+#if BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+				if (0 != m_msaa)
+				{
+					GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_msaa, depthComponent, _width, _height) );
+				}
+				else
+#endif // BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+				{
+					GL_CHECK(glRenderbufferStorage(GL_RENDERBUFFER, depthComponent, _width, _height) );
+				}
 				GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0) );
 
 				GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER
 					, GL_DEPTH_ATTACHMENT
 					, GL_RENDERBUFFER
-					, m_rbo
+					, m_depthRbo
 					) );
 			}
 			else
@@ -1538,29 +1692,59 @@ namespace bgfx
 					, 0
 					) );
 			}
-		}
 
-		BX_CHECK(GL_FRAMEBUFFER_COMPLETE ==  glCheckFramebufferStatus(GL_FRAMEBUFFER)
+			BX_CHECK(GL_FRAMEBUFFER_COMPLETE ==  glCheckFramebufferStatus(GL_FRAMEBUFFER)
 				, "glCheckFramebufferStatus failed 0x%08x"
 				, glCheckFramebufferStatus(GL_FRAMEBUFFER)
 				);
+		}
 
-		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0) );
+		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, s_renderCtx.m_backBufferFbo) );
 	}
 
 	void RenderTarget::destroy()
 	{
-		GL_CHECK(glDeleteFramebuffers(1, &m_fbo) );
+		GL_CHECK(glDeleteFramebuffers(0 == m_fbo[1] ? 1 : 2, m_fbo) );
+		memset(m_fbo, 0, sizeof(m_fbo) );
 
-		if (0 != m_rbo)
+		if (0 != m_colorRbo)
 		{
-			GL_CHECK(glDeleteRenderbuffers(1, &m_rbo) );
+			GL_CHECK(glDeleteRenderbuffers(1, &m_colorRbo) );
+			m_colorRbo = 0;
+		}
+
+		if (0 != m_depthRbo)
+		{
+			GL_CHECK(glDeleteRenderbuffers(1, &m_depthRbo) );
+			m_depthRbo = 0;
 		}
 
 		m_color.destroy();
 		m_depth.destroy();
 	}
 
+	void RenderTarget::resolve()
+	{
+#if BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+		BX_CHECK(0 != m_fbo[1], "Can resolve without two framebuffers.");
+
+		GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo[0]) );
+		GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[1]) );
+		GL_CHECK(glBlitFramebuffer(0
+				, 0
+				, m_width
+				, m_height
+				, 0
+				, 0
+				, m_width
+				, m_height
+				, GL_COLOR_BUFFER_BIT
+				, GL_LINEAR
+				) );
+		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, s_renderCtx.m_backBufferFbo) );
+#endif // BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+	}
+
 	void ConstantBuffer::commit()
 	{
 		reset();
@@ -1845,6 +2029,13 @@ namespace bgfx
 				glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &s_renderCtx.m_maxAnisotropy);
 			}
 
+#if BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+			if (s_extension[Extension::ARB_texture_multisample].m_supported)
+			{
+				glGetIntegerv(GL_MAX_SAMPLES, &s_renderCtx.m_maxMsaa);
+			}
+#endif // BGFX_CONFIG_RENDERER_OPENGL|BGFX_CONFIG_RENDERER_OPENGLES3
+
 			if (s_extension[Extension::EXT_texture_format_BGRA8888].m_supported
 			||  s_extension[Extension::EXT_bgra].m_supported)
 			{
@@ -2038,7 +2229,7 @@ namespace bgfx
 			GL_CHECK(glBindVertexArray(0) );
 		}
 
-		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0) );
+		GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, s_renderCtx.m_backBufferFbo) );
 
 		s_renderCtx.updateResolution(m_render->m_resolution);
 
@@ -2123,18 +2314,7 @@ namespace bgfx
 					if (m_render->m_rt[view].idx != rt.idx)
 					{
 						rt = m_render->m_rt[view];
-
-						if (rt.idx == invalidHandle)
-						{
-							GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0) );
-							height = m_render->m_resolution.m_height;
-						}
-						else
-						{
-							RenderTarget& renderTarget = s_renderCtx.m_renderTargets[rt.idx];
-							GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, renderTarget.m_fbo) );
-							height = renderTarget.m_height;
-						}
+						height = s_renderCtx.setRenderTarget(rt, m_render->m_resolution.m_height);
 					}
 
 					Rect& rect = m_render->m_rect[view];
@@ -2234,7 +2414,8 @@ namespace bgfx
 
 				if ( (BGFX_STATE_CULL_MASK|BGFX_STATE_DEPTH_WRITE|BGFX_STATE_DEPTH_TEST_MASK
 				     |BGFX_STATE_ALPHA_MASK|BGFX_STATE_RGB_WRITE|BGFX_STATE_BLEND_MASK
-					 |BGFX_STATE_ALPHA_REF_MASK|BGFX_STATE_PT_MASK|BGFX_STATE_POINT_SIZE_MASK) & changedFlags)
+					 |BGFX_STATE_ALPHA_REF_MASK|BGFX_STATE_PT_MASK|BGFX_STATE_POINT_SIZE_MASK
+					 |BGFX_STATE_MSAA) & changedFlags)
 				{
 					if (BGFX_STATE_CULL_MASK & changedFlags)
 					{
@@ -2297,6 +2478,18 @@ namespace bgfx
 						float pointSize = (float)(uint32_max(1, (newFlags&BGFX_STATE_POINT_SIZE_MASK)>>BGFX_STATE_POINT_SIZE_SHIFT) );
 						GL_CHECK(glPointSize(pointSize) );
 					}
+
+					if (BGFX_STATE_MSAA & changedFlags)
+					{
+						if (BGFX_STATE_MSAA & newFlags)
+						{
+							GL_CHECK(glEnable(GL_MULTISAMPLE) );
+						}
+						else
+						{
+							GL_CHECK(glDisable(GL_MULTISAMPLE) );
+						}
+					}
 #endif // BGFX_CONFIG_RENDERER_OPENGL
 
 					if ( (BGFX_STATE_ALPHA_WRITE|BGFX_STATE_RGB_WRITE) & changedFlags)
@@ -2761,6 +2954,8 @@ namespace bgfx
 			}
 		}
 
+		s_renderCtx.blitMsaaFbo();
+
 		int64_t now = bx::getHPCounter();
 		elapsed += now;
 

+ 13 - 2
src/renderer_gl.h

@@ -481,15 +481,26 @@ namespace bgfx
 
 	struct RenderTarget
 	{
+		RenderTarget()
+			: m_width(0)
+			, m_height(0)
+			, m_msaa(0)
+		{
+			memset(m_fbo, 0, sizeof(m_fbo) );
+		}
+
 		void create(uint16_t _width, uint16_t _height, uint32_t _flags, uint32_t _textureFlags);
 		void destroy();
+		void resolve();
 
 		GLsizei m_width;
 		GLsizei m_height;
+		uint32_t m_msaa;
 		Texture m_color;
 		Texture m_depth;
-		GLuint m_fbo;
-		GLuint m_rbo;
+		GLuint m_fbo[2];
+		GLuint m_colorRbo;
+		GLuint m_depthRbo;
 	};
 
 	struct Program