diff --git a/source/common/engine/renderstyle.h b/source/common/engine/renderstyle.h
index 70ad5d561..7d0a7b877 100644
--- a/source/common/engine/renderstyle.h
+++ b/source/common/engine/renderstyle.h
@@ -51,10 +51,12 @@ enum ETexMode
 	TM_OPAQUE,			// (r, g, b, 1)
 	TM_INVERSE,			// (1-r, 1-g, 1-b, a)
 	TM_ALPHATEXTURE,		// (1, 1, 1, r)
-	TM_CLAMPY,			// (r, g, b, (t >= 0.0 && t <= 1.0)? a:0)
-	TM_INVERTOPAQUE,	// (1-r, 1-g, 1-b, 1)
+	TM_INVERTOPAQUE = 6,	// (1-r, 1-g, 1-b, 1)
 	TM_FOGLAYER,		// (renders a fog layer in the shape of the active texture)
 	TM_FIXEDCOLORMAP = TM_FOGLAYER,	// repurposes the objectcolor uniforms to render a fixed colormap range. (Same constant because they cannot be used in the same context.
+
+	TM_CLAMPY = 0x1000,			// (r, g, b, (t >= 0.0 && t <= 1.0)? a:0)
+
 };
 
 // Legacy render styles
diff --git a/source/common/platform/posix/cocoa/i_video.mm b/source/common/platform/posix/cocoa/i_video.mm
index b9b1be72e..a306433b8 100644
--- a/source/common/platform/posix/cocoa/i_video.mm
+++ b/source/common/platform/posix/cocoa/i_video.mm
@@ -52,8 +52,11 @@
 #include "v_text.h"
 #include "version.h"
 #include "printf.h"
-
 #include "gl_framebuffer.h"
+#ifdef HAVE_GLES2
+#include "gles_framebuffer.h"
+#endif
+
 #ifdef HAVE_VULKAN
 #include "vulkan/system/vk_framebuffer.h"
 #endif
@@ -462,7 +465,12 @@ public:
 
 		if (fb == nullptr)
 		{
-			fb = new OpenGLRenderer::OpenGLFrameBuffer(0, vid_fullscreen);
+#ifdef HAVE_GLES2
+			if( (Args->CheckParm ("-gles2_renderer")) || (vid_preferbackend == 3) )
+				fb = new OpenGLESRenderer::OpenGLFrameBuffer(0, vid_fullscreen);
+			else
+#endif
+				fb = new OpenGLRenderer::OpenGLFrameBuffer(0, vid_fullscreen);
 		}
 
 		fb->SetWindow(ms_window);
diff --git a/source/common/platform/posix/sdl/sdlglvideo.cpp b/source/common/platform/posix/sdl/sdlglvideo.cpp
index 4f7dc6e88..57c46e908 100644
--- a/source/common/platform/posix/sdl/sdlglvideo.cpp
+++ b/source/common/platform/posix/sdl/sdlglvideo.cpp
@@ -50,7 +50,10 @@
 
 #include "gl_renderer.h"
 #include "gl_framebuffer.h"
-
+#ifdef HAVE_GLES2
+#include "gles_framebuffer.h"
+#endif
+ 
 #ifdef HAVE_VULKAN
 #include "vulkan/system/vk_framebuffer.h"
 #endif
@@ -468,7 +471,12 @@ DFrameBuffer *SDLVideo::CreateFrameBuffer ()
 #endif
 	if (fb == nullptr)
 	{
-		fb = new OpenGLRenderer::OpenGLFrameBuffer(0, vid_fullscreen);
+#ifdef HAVE_GLES2
+		if( (Args->CheckParm ("-gles2_renderer")) || (vid_preferbackend == 3) )
+			fb = new OpenGLESRenderer::OpenGLFrameBuffer(0, vid_fullscreen);
+		else
+#endif
+			fb = new OpenGLRenderer::OpenGLFrameBuffer(0, vid_fullscreen);
 	}
 
 	return fb;
diff --git a/source/common/platform/win32/gl_sysfb.cpp b/source/common/platform/win32/gl_sysfb.cpp
index e373c3d3b..17822d213 100644
--- a/source/common/platform/win32/gl_sysfb.cpp
+++ b/source/common/platform/win32/gl_sysfb.cpp
@@ -71,7 +71,7 @@ PFNWGLSWAPINTERVALEXTPROC myWglSwapIntervalExtProc;
 
 SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen) : SystemBaseFrameBuffer(hMonitor, fullscreen)
 {
-	if (!static_cast<Win32GLVideo *>(Video)->InitHardware(Window, 0))
+	if (!static_cast<Win32GLVideo*>(Video)->InitHardware(Window, 0))
 	{
 		I_FatalError("Unable to initialize OpenGL");
 		return;
diff --git a/source/common/platform/win32/i_specialpaths.cpp b/source/common/platform/win32/i_specialpaths.cpp
index 3078f59c1..bbd3c4976 100644
--- a/source/common/platform/win32/i_specialpaths.cpp
+++ b/source/common/platform/win32/i_specialpaths.cpp
@@ -74,16 +74,36 @@ bool UseKnownFolders()
 	{
 		return !iswritable;
 	}
-	std::wstring testpath = progdir.WideString() + L"writest";
-	file = CreateFile(testpath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
-		CREATE_ALWAYS,
-		FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
-	if (file != INVALID_HANDLE_VALUE)
+	// Consider 'Program Files' read only without actually checking.
+	bool found = false;
+	for (auto p : { L"ProgramFiles", L"ProgramFiles(x86)" })
 	{
-		CloseHandle(file);
-		if (!batchrun) Printf("Using program directory for storage\n");
-		iswritable = true;
-		return false;
+		wchar_t buffer1[256];
+		if (GetEnvironmentVariable(p, buffer1, 256))
+		{
+			FString envpath(buffer1);
+			FixPathSeperator(envpath);
+			if (progdir.MakeLower().IndexOf(envpath.MakeLower()) == 0)
+			{
+				found = true;
+				break;
+			}
+		}
+	}
+
+	if (!found)
+	{
+		std::wstring testpath = progdir.WideString() + L"writest";
+		file = CreateFile(testpath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
+			CREATE_ALWAYS,
+			FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
+		if (file != INVALID_HANDLE_VALUE)
+		{
+			CloseHandle(file);
+			if (!batchrun) Printf("Using program directory for storage\n");
+			iswritable = true;
+			return false;
+		}
 	}
 	if (!batchrun) Printf("Using known folders for storage\n");
 	iswritable = false;
diff --git a/source/common/platform/win32/i_system.cpp b/source/common/platform/win32/i_system.cpp
index 72930a560..103b26f31 100644
--- a/source/common/platform/win32/i_system.cpp
+++ b/source/common/platform/win32/i_system.cpp
@@ -510,7 +510,6 @@ BOOL CALLBACK IWADBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa
 		char	szString[256];
 
 		// Check the current video settings.
-		//SendDlgItemMessage( hDlg, vid_renderer ? IDC_WELCOME_OPENGL : IDC_WELCOME_SOFTWARE, BM_SETCHECK, BST_CHECKED, 0 );
 		SendDlgItemMessage( hDlg, IDC_WELCOME_FULLSCREEN, BM_SETCHECK, vid_fullscreen ? BST_CHECKED : BST_UNCHECKED, 0 );
 		switch (vid_preferbackend)
 		{
@@ -520,6 +519,11 @@ BOOL CALLBACK IWADBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa
 		case 2:
 			SendDlgItemMessage( hDlg, IDC_WELCOME_VULKAN3, BM_SETCHECK, BST_CHECKED, 0 );
 			break;
+#ifdef HAVE_GLES2
+		case 3:
+			SendDlgItemMessage( hDlg, IDC_WELCOME_VULKAN4, BM_SETCHECK, BST_CHECKED, 0 );
+			break;
+#endif			
 		default:
 			SendDlgItemMessage( hDlg, IDC_WELCOME_VULKAN1, BM_SETCHECK, BST_CHECKED, 0 );
 			break;
@@ -574,6 +578,11 @@ BOOL CALLBACK IWADBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa
 			SetQueryIWad(hDlg);
 			// [SP] Upstreamed from Zandronum
 			vid_fullscreen = SendDlgItemMessage( hDlg, IDC_WELCOME_FULLSCREEN, BM_GETCHECK, 0, 0 ) == BST_CHECKED;
+#ifdef HAVE_GLES2
+			if (SendDlgItemMessage(hDlg, IDC_WELCOME_VULKAN4, BM_GETCHECK, 0, 0) == BST_CHECKED)
+				vid_preferbackend = 3;
+			else 
+#endif
 			if (SendDlgItemMessage(hDlg, IDC_WELCOME_VULKAN3, BM_GETCHECK, 0, 0) == BST_CHECKED)
 				vid_preferbackend = 2;
 			else if (SendDlgItemMessage(hDlg, IDC_WELCOME_VULKAN2, BM_GETCHECK, 0, 0) == BST_CHECKED)
diff --git a/source/common/platform/win32/resource.h b/source/common/platform/win32/resource.h
index f07a37083..d00ee9bd9 100644
--- a/source/common/platform/win32/resource.h
+++ b/source/common/platform/win32/resource.h
@@ -97,6 +97,7 @@
 #define IDC_RADIO3                      1086
 #define IDC_WELCOME_VULKAN3             1086
 #define IDCE_ROOM                       1087
+#define IDC_WELCOME_VULKAN4             1187
 #define IDCS_ROOM                       1088
 #define IDCE_ROOMHF                     1089
 #define IDCS_ROOMHF                     1090
diff --git a/source/common/platform/win32/win32basevideo.cpp b/source/common/platform/win32/win32basevideo.cpp
index d406a5d43..fc2ae67c4 100644
--- a/source/common/platform/win32/win32basevideo.cpp
+++ b/source/common/platform/win32/win32basevideo.cpp
@@ -48,7 +48,7 @@
 #include "win32basevideo.h"
 #include "cmdlib.h"
 
-CVAR(Int, vid_adapter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+CVAR(Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
 
 //==========================================================================
 //
@@ -69,6 +69,12 @@ Win32BaseVideo::Win32BaseVideo()
 //
 //==========================================================================
 
+HMONITOR GetPrimaryMonitorHandle()
+{
+	const POINT ptZero = { 0, 0 };
+	return MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
+}
+
 struct MonitorEnumState
 {
 	int curIdx;
@@ -116,24 +122,29 @@ void Win32BaseVideo::GetDisplayDeviceName()
 	mes.curIdx = 1;
 	mes.hFoundMonitor = nullptr;
 
-	// Could also use EnumDisplayDevices, I guess. That might work.
-	if (EnumDisplayMonitors(0, 0, &GetDisplayDeviceNameMonitorEnumProc, LPARAM(&mes)))
+	if (vid_adapter == 0)
 	{
-		if (mes.hFoundMonitor)
+		mes.hFoundMonitor = GetPrimaryMonitorHandle();
+	}
+
+	// Could also use EnumDisplayDevices, I guess. That might work.
+	else EnumDisplayMonitors(0, 0, &GetDisplayDeviceNameMonitorEnumProc, LPARAM(&mes));
+
+	if (mes.hFoundMonitor)
+	{
+		MONITORINFOEXA mi;
+
+		mi.cbSize = sizeof mi;
+
+		if (GetMonitorInfoA(mes.hFoundMonitor, &mi))
 		{
-			MONITORINFOEXA mi;
+			strcpy(m_DisplayDeviceBuffer, mi.szDevice);
+			m_DisplayDeviceName = m_DisplayDeviceBuffer;
 
-			mi.cbSize = sizeof mi;
-
-			if (GetMonitorInfoA(mes.hFoundMonitor, &mi))
-			{
-				strcpy(m_DisplayDeviceBuffer, mi.szDevice);
-				m_DisplayDeviceName = m_DisplayDeviceBuffer;
-
-				m_hMonitor = mes.hFoundMonitor;
-			}
+			m_hMonitor = mes.hFoundMonitor;
 		}
 	}
+
 }
 
 //==========================================================================
diff --git a/source/common/platform/win32/win32glvideo.cpp b/source/common/platform/win32/win32glvideo.cpp
index e41d29b39..abc9768cc 100644
--- a/source/common/platform/win32/win32glvideo.cpp
+++ b/source/common/platform/win32/win32glvideo.cpp
@@ -53,6 +53,9 @@
 #include "win32glvideo.h"
 
 #include "gl_framebuffer.h"
+#ifdef HAVE_GLES2
+#include "gles_framebuffer.h"
+#endif
 
 extern "C" {
 HGLRC zd_wglCreateContext(HDC Arg1);
@@ -62,6 +65,7 @@ PROC zd_wglGetProcAddress(LPCSTR name);
 }
 
 EXTERN_CVAR(Int, vid_adapter)
+EXTERN_CVAR(Int, vid_preferbackend)
 EXTERN_CVAR(Bool, vid_hdr)
 
 CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
@@ -105,7 +109,13 @@ DFrameBuffer *Win32GLVideo::CreateFrameBuffer()
 {
 	SystemGLFrameBuffer *fb;
 
-	fb = new OpenGLRenderer::OpenGLFrameBuffer(m_hMonitor, vid_fullscreen);
+#ifdef HAVE_GLES2
+	if ((Args->CheckParm("-gles2_renderer")) || (vid_preferbackend == 3) )
+		fb = new OpenGLESRenderer::OpenGLFrameBuffer(m_hMonitor, vid_fullscreen);
+	else
+#endif
+		fb = new OpenGLRenderer::OpenGLFrameBuffer(m_hMonitor, vid_fullscreen);
+
 	return fb;
 }
 
diff --git a/source/common/rendering/gl/gl_buffers.cpp b/source/common/rendering/gl/gl_buffers.cpp
index 9b49d431a..47970b401 100644
--- a/source/common/rendering/gl/gl_buffers.cpp
+++ b/source/common/rendering/gl/gl_buffers.cpp
@@ -170,6 +170,29 @@ void GLBuffer::Resize(size_t newsize)
 	}
 }
 
+void GLBuffer::GPUDropSync()
+{
+	if (mGLSync != NULL)
+	{
+		glDeleteSync(mGLSync);
+	}
+
+	mGLSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+void GLBuffer::GPUWaitSync()
+{
+	GLenum status = glClientWaitSync(mGLSync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000 * 1000 * 50); // Wait for a max of 50ms...
+	
+	if (status != GL_ALREADY_SIGNALED && status != GL_CONDITION_SATISFIED)
+	{
+		//Printf("Error on glClientWaitSync: %d\n", status);
+	}
+	
+	glDeleteSync(mGLSync);
+
+	mGLSync = NULL;
+}
 
 //===========================================================================
 //
diff --git a/source/common/rendering/gl/gl_buffers.h b/source/common/rendering/gl/gl_buffers.h
index 950556ff9..0141ece5f 100644
--- a/source/common/rendering/gl/gl_buffers.h
+++ b/source/common/rendering/gl/gl_buffers.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "buffers.h"
+#include "gl_load.h"
 
 #ifdef _MSC_VER
 // silence bogus warning C4250: 'GLVertexBuffer': inherits 'GLBuffer::GLBuffer::SetData' via dominance
@@ -19,6 +20,7 @@ protected:
 	int mAllocationSize = 0;
 	bool mPersistent = false;
 	bool nomap = true;
+	GLsync mGLSync = 0;
 
 	GLBuffer(int usetype);
 	~GLBuffer();
@@ -29,6 +31,9 @@ protected:
 	void Resize(size_t newsize) override;
 	void *Lock(unsigned int size) override;
 	void Unlock() override;
+
+	void GPUDropSync();
+	void GPUWaitSync();
 public:
 	void Bind();
 };
diff --git a/source/common/rendering/gl/gl_framebuffer.cpp b/source/common/rendering/gl/gl_framebuffer.cpp
index b116e9577..668965a11 100644
--- a/source/common/rendering/gl/gl_framebuffer.cpp
+++ b/source/common/rendering/gl/gl_framebuffer.cpp
@@ -65,6 +65,7 @@ EXTERN_CVAR (Bool, vid_vsync)
 EXTERN_CVAR(Bool, r_drawvoxels)
 EXTERN_CVAR(Int, gl_tonemap)
 EXTERN_CVAR(Bool, cl_capfps)
+EXTERN_CVAR(Int, gl_pipeline_depth);
 
 void gl_LoadExtensions();
 void gl_PrintStartupLog();
@@ -133,6 +134,9 @@ void OpenGLFrameBuffer::InitializeState()
 
 	gl_LoadExtensions();
 
+	mPipelineNbr = clamp(*gl_pipeline_depth, 1, HW_MAX_PIPELINE_BUFFERS);
+	mPipelineType = gl_pipeline_depth > 0;
+
 	// Move some state to the framebuffer object for easier access.
 	hwcaps = gl.flags;
 	glslversion = gl.glslversion;
@@ -164,10 +168,10 @@ void OpenGLFrameBuffer::InitializeState()
 
 	SetViewportRects(nullptr);
 
-	mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight());
+	mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight(), screen->mPipelineNbr);
 	mSkyData = new FSkyVertexBuffer;
-	mViewpoints = new HWViewpointBuffer;
-	mLights = new FLightBuffer();
+	mViewpoints = new HWViewpointBuffer(screen->mPipelineNbr);
+	mLights = new FLightBuffer(screen->mPipelineNbr);
 	GLRenderer = new FGLRenderer(this);
 	GLRenderer->Initialize(GetWidth(), GetHeight());
 	static_cast<GLDataBuffer*>(mLights->GetBuffer())->BindBase();
@@ -256,10 +260,25 @@ void OpenGLFrameBuffer::Swap()
 	bool swapbefore = gl_finishbeforeswap && camtexcount == 0;
 	Finish.Reset();
 	Finish.Clock();
-	if (swapbefore) glFinish();
-	FPSLimit();
-	SwapBuffers();
-	if (!swapbefore) glFinish();
+	if (gl_pipeline_depth < 1)
+	{
+		if (swapbefore) glFinish();
+		FPSLimit();
+		SwapBuffers();
+		if (!swapbefore) glFinish();
+	}
+	else
+	{
+		mVertexData->DropSync();
+
+		FPSLimit();
+		SwapBuffers();
+
+		mVertexData->NextPipelineBuffer();
+		mVertexData->WaitSync();
+
+		RenderState()->SetVertexBuffer(screen->mVertexData); // Needed for Raze because it does not reset it
+	}
 	Finish.Unclock();
 	camtexcount = 0;
 	FHardwareTexture::UnbindAll();
diff --git a/source/common/rendering/gl/gl_renderstate.cpp b/source/common/rendering/gl/gl_renderstate.cpp
index 64e8cc4d1..b8272e58d 100644
--- a/source/common/rendering/gl/gl_renderstate.cpp
+++ b/source/common/rendering/gl/gl_renderstate.cpp
@@ -204,7 +204,7 @@ bool FGLRenderState::ApplyShader()
 		size_t start, size;
 		index = screen->mLights->GetBinding(index, &start, &size);
 
-		if (start != mLastMappedLightIndex)
+		if (start != mLastMappedLightIndex || screen->mPipelineNbr > 1) // If multiple buffers always bind
 		{
 			mLastMappedLightIndex = start;
 			static_cast<GLDataBuffer*>(screen->mLights->GetBuffer())->BindRange(nullptr, start, size);
diff --git a/source/common/rendering/gl/gl_shader.cpp b/source/common/rendering/gl/gl_shader.cpp
index 5694b7ef0..2ef56a288 100644
--- a/source/common/rendering/gl/gl_shader.cpp
+++ b/source/common/rendering/gl/gl_shader.cpp
@@ -382,7 +382,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 			vp_comb = "#version 430 core\n#define SHADER_STORAGE_LIGHTS\n";
 	}
 
-	if (gl.flags & RFL_SHADER_STORAGE_BUFFER)
+	if ((gl.flags & RFL_SHADER_STORAGE_BUFFER) && screen->allowSSBO())
 	{
 		vp_comb << "#define SUPPORTS_SHADOWMAPS\n";
 	}
diff --git a/source/common/rendering/gles/EGL/egl.h b/source/common/rendering/gles/EGL/egl.h
new file mode 100644
index 000000000..5a2729121
--- /dev/null
+++ b/source/common/rendering/gles/EGL/egl.h
@@ -0,0 +1,303 @@
+#ifndef __egl_h_
+#define __egl_h_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** Copyright (c) 2013-2014 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+/*
+** This header is generated from the Khronos OpenGL / OpenGL ES XML
+** API Registry. The current version of the Registry, generator scripts
+** used to make the header, and the header can be found at
+**   http://www.opengl.org/registry/
+**
+** Khronos $Revision: 29318 $ on $Date: 2015-01-02 03:16:10 -0800 (Fri, 02 Jan 2015) $
+*/
+
+#include <EGL/eglplatform.h>
+
+/* Generated on date 20150102 */
+
+/* Generated C header for:
+ * API: egl
+ * Versions considered: .*
+ * Versions emitted: .*
+ * Default extensions included: None
+ * Additional extensions included: _nomatch_^
+ * Extensions removed: _nomatch_^
+ */
+
+#ifndef EGL_VERSION_1_0
+#define EGL_VERSION_1_0 1
+typedef unsigned int EGLBoolean;
+typedef void *EGLDisplay;
+#include <KHR/khrplatform.h>
+#include <EGL/eglplatform.h>
+typedef void *EGLConfig;
+typedef void *EGLSurface;
+typedef void *EGLContext;
+typedef void (*__eglMustCastToProperFunctionPointerType)(void);
+#define EGL_ALPHA_SIZE                    0x3021
+#define EGL_BAD_ACCESS                    0x3002
+#define EGL_BAD_ALLOC                     0x3003
+#define EGL_BAD_ATTRIBUTE                 0x3004
+#define EGL_BAD_CONFIG                    0x3005
+#define EGL_BAD_CONTEXT                   0x3006
+#define EGL_BAD_CURRENT_SURFACE           0x3007
+#define EGL_BAD_DISPLAY                   0x3008
+#define EGL_BAD_MATCH                     0x3009
+#define EGL_BAD_NATIVE_PIXMAP             0x300A
+#define EGL_BAD_NATIVE_WINDOW             0x300B
+#define EGL_BAD_PARAMETER                 0x300C
+#define EGL_BAD_SURFACE                   0x300D
+#define EGL_BLUE_SIZE                     0x3022
+#define EGL_BUFFER_SIZE                   0x3020
+#define EGL_CONFIG_CAVEAT                 0x3027
+#define EGL_CONFIG_ID                     0x3028
+#define EGL_CORE_NATIVE_ENGINE            0x305B
+#define EGL_DEPTH_SIZE                    0x3025
+#define EGL_DONT_CARE                     ((EGLint)-1)
+#define EGL_DRAW                          0x3059
+#define EGL_EXTENSIONS                    0x3055
+#define EGL_FALSE                         0
+#define EGL_GREEN_SIZE                    0x3023
+#define EGL_HEIGHT                        0x3056
+#define EGL_LARGEST_PBUFFER               0x3058
+#define EGL_LEVEL                         0x3029
+#define EGL_MAX_PBUFFER_HEIGHT            0x302A
+#define EGL_MAX_PBUFFER_PIXELS            0x302B
+#define EGL_MAX_PBUFFER_WIDTH             0x302C
+#define EGL_NATIVE_RENDERABLE             0x302D
+#define EGL_NATIVE_VISUAL_ID              0x302E
+#define EGL_NATIVE_VISUAL_TYPE            0x302F
+#define EGL_NONE                          0x3038
+#define EGL_NON_CONFORMANT_CONFIG         0x3051
+#define EGL_NOT_INITIALIZED               0x3001
+#define EGL_NO_CONTEXT                    ((EGLContext)0)
+#define EGL_NO_DISPLAY                    ((EGLDisplay)0)
+#define EGL_NO_SURFACE                    ((EGLSurface)0)
+#define EGL_PBUFFER_BIT                   0x0001
+#define EGL_PIXMAP_BIT                    0x0002
+#define EGL_READ                          0x305A
+#define EGL_RED_SIZE                      0x3024
+#define EGL_SAMPLES                       0x3031
+#define EGL_SAMPLE_BUFFERS                0x3032
+#define EGL_SLOW_CONFIG                   0x3050
+#define EGL_STENCIL_SIZE                  0x3026
+#define EGL_SUCCESS                       0x3000
+#define EGL_SURFACE_TYPE                  0x3033
+#define EGL_TRANSPARENT_BLUE_VALUE        0x3035
+#define EGL_TRANSPARENT_GREEN_VALUE       0x3036
+#define EGL_TRANSPARENT_RED_VALUE         0x3037
+#define EGL_TRANSPARENT_RGB               0x3052
+#define EGL_TRANSPARENT_TYPE              0x3034
+#define EGL_TRUE                          1
+#define EGL_VENDOR                        0x3053
+#define EGL_VERSION                       0x3054
+#define EGL_WIDTH                         0x3057
+#define EGL_WINDOW_BIT                    0x0004
+EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
+EGLAPI EGLContext EGLAPIENTRY eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferSurface (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePixmapSurface (EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface (EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext (EGLDisplay dpy, EGLContext ctx);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface (EGLDisplay dpy, EGLSurface surface);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigAttrib (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+EGLAPI EGLDisplay EGLAPIENTRY eglGetCurrentDisplay (void);
+EGLAPI EGLSurface EGLAPIENTRY eglGetCurrentSurface (EGLint readdraw);
+EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay (EGLNativeDisplayType display_id);
+EGLAPI EGLint EGLAPIENTRY eglGetError (void);
+EGLAPI __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddress (const char *procname);
+EGLAPI EGLBoolean EGLAPIENTRY eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor);
+EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext (EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value);
+EGLAPI const char *EGLAPIENTRY eglQueryString (EGLDisplay dpy, EGLint name);
+EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers (EGLDisplay dpy, EGLSurface surface);
+EGLAPI EGLBoolean EGLAPIENTRY eglTerminate (EGLDisplay dpy);
+EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL (void);
+EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative (EGLint engine);
+#endif /* EGL_VERSION_1_0 */
+
+#ifndef EGL_VERSION_1_1
+#define EGL_VERSION_1_1 1
+#define EGL_BACK_BUFFER                   0x3084
+#define EGL_BIND_TO_TEXTURE_RGB           0x3039
+#define EGL_BIND_TO_TEXTURE_RGBA          0x303A
+#define EGL_CONTEXT_LOST                  0x300E
+#define EGL_MIN_SWAP_INTERVAL             0x303B
+#define EGL_MAX_SWAP_INTERVAL             0x303C
+#define EGL_MIPMAP_TEXTURE                0x3082
+#define EGL_MIPMAP_LEVEL                  0x3083
+#define EGL_NO_TEXTURE                    0x305C
+#define EGL_TEXTURE_2D                    0x305F
+#define EGL_TEXTURE_FORMAT                0x3080
+#define EGL_TEXTURE_RGB                   0x305D
+#define EGL_TEXTURE_RGBA                  0x305E
+#define EGL_TEXTURE_TARGET                0x3081
+EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval (EGLDisplay dpy, EGLint interval);
+#endif /* EGL_VERSION_1_1 */
+
+#ifndef EGL_VERSION_1_2
+#define EGL_VERSION_1_2 1
+typedef unsigned int EGLenum;
+typedef void *EGLClientBuffer;
+#define EGL_ALPHA_FORMAT                  0x3088
+#define EGL_ALPHA_FORMAT_NONPRE           0x308B
+#define EGL_ALPHA_FORMAT_PRE              0x308C
+#define EGL_ALPHA_MASK_SIZE               0x303E
+#define EGL_BUFFER_PRESERVED              0x3094
+#define EGL_BUFFER_DESTROYED              0x3095
+#define EGL_CLIENT_APIS                   0x308D
+#define EGL_COLORSPACE                    0x3087
+#define EGL_COLORSPACE_sRGB               0x3089
+#define EGL_COLORSPACE_LINEAR             0x308A
+#define EGL_COLOR_BUFFER_TYPE             0x303F
+#define EGL_CONTEXT_CLIENT_TYPE           0x3097
+#define EGL_DISPLAY_SCALING               10000
+#define EGL_HORIZONTAL_RESOLUTION         0x3090
+#define EGL_LUMINANCE_BUFFER              0x308F
+#define EGL_LUMINANCE_SIZE                0x303D
+#define EGL_OPENGL_ES_BIT                 0x0001
+#define EGL_OPENVG_BIT                    0x0002
+#define EGL_OPENGL_ES_API                 0x30A0
+#define EGL_OPENVG_API                    0x30A1
+#define EGL_OPENVG_IMAGE                  0x3096
+#define EGL_PIXEL_ASPECT_RATIO            0x3092
+#define EGL_RENDERABLE_TYPE               0x3040
+#define EGL_RENDER_BUFFER                 0x3086
+#define EGL_RGB_BUFFER                    0x308E
+#define EGL_SINGLE_BUFFER                 0x3085
+#define EGL_SWAP_BEHAVIOR                 0x3093
+#define EGL_UNKNOWN                       ((EGLint)-1)
+#define EGL_VERTICAL_RESOLUTION           0x3091
+EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI (EGLenum api);
+EGLAPI EGLenum EGLAPIENTRY eglQueryAPI (void);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread (void);
+EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient (void);
+#endif /* EGL_VERSION_1_2 */
+
+#ifndef EGL_VERSION_1_3
+#define EGL_VERSION_1_3 1
+#define EGL_CONFORMANT                    0x3042
+#define EGL_CONTEXT_CLIENT_VERSION        0x3098
+#define EGL_MATCH_NATIVE_PIXMAP           0x3041
+#define EGL_OPENGL_ES2_BIT                0x0004
+#define EGL_VG_ALPHA_FORMAT               0x3088
+#define EGL_VG_ALPHA_FORMAT_NONPRE        0x308B
+#define EGL_VG_ALPHA_FORMAT_PRE           0x308C
+#define EGL_VG_ALPHA_FORMAT_PRE_BIT       0x0040
+#define EGL_VG_COLORSPACE                 0x3087
+#define EGL_VG_COLORSPACE_sRGB            0x3089
+#define EGL_VG_COLORSPACE_LINEAR          0x308A
+#define EGL_VG_COLORSPACE_LINEAR_BIT      0x0020
+#endif /* EGL_VERSION_1_3 */
+
+#ifndef EGL_VERSION_1_4
+#define EGL_VERSION_1_4 1
+#define EGL_DEFAULT_DISPLAY               ((EGLNativeDisplayType)0)
+#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT   0x0200
+#define EGL_MULTISAMPLE_RESOLVE           0x3099
+#define EGL_MULTISAMPLE_RESOLVE_DEFAULT   0x309A
+#define EGL_MULTISAMPLE_RESOLVE_BOX       0x309B
+#define EGL_OPENGL_API                    0x30A2
+#define EGL_OPENGL_BIT                    0x0008
+#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT   0x0400
+EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void);
+#endif /* EGL_VERSION_1_4 */
+
+#ifndef EGL_VERSION_1_5
+#define EGL_VERSION_1_5 1
+typedef void *EGLSync;
+typedef intptr_t EGLAttrib;
+typedef khronos_utime_nanoseconds_t EGLTime;
+typedef void *EGLImage;
+#define EGL_CONTEXT_MAJOR_VERSION         0x3098
+#define EGL_CONTEXT_MINOR_VERSION         0x30FB
+#define EGL_CONTEXT_OPENGL_PROFILE_MASK   0x30FD
+#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD
+#define EGL_NO_RESET_NOTIFICATION         0x31BE
+#define EGL_LOSE_CONTEXT_ON_RESET         0x31BF
+#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
+#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002
+#define EGL_CONTEXT_OPENGL_DEBUG          0x31B0
+#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1
+#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS  0x31B2
+#define EGL_OPENGL_ES3_BIT                0x00000040
+#define EGL_CL_EVENT_HANDLE               0x309C
+#define EGL_SYNC_CL_EVENT                 0x30FE
+#define EGL_SYNC_CL_EVENT_COMPLETE        0x30FF
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE  0x30F0
+#define EGL_SYNC_TYPE                     0x30F7
+#define EGL_SYNC_STATUS                   0x30F1
+#define EGL_SYNC_CONDITION                0x30F8
+#define EGL_SIGNALED                      0x30F2
+#define EGL_UNSIGNALED                    0x30F3
+#define EGL_SYNC_FLUSH_COMMANDS_BIT       0x0001
+#define EGL_FOREVER                       0xFFFFFFFFFFFFFFFFull
+#define EGL_TIMEOUT_EXPIRED               0x30F5
+#define EGL_CONDITION_SATISFIED           0x30F6
+#define EGL_NO_SYNC                       ((EGLSync)0)
+#define EGL_SYNC_FENCE                    0x30F9
+#define EGL_GL_COLORSPACE                 0x309D
+#define EGL_GL_COLORSPACE_SRGB            0x3089
+#define EGL_GL_COLORSPACE_LINEAR          0x308A
+#define EGL_GL_RENDERBUFFER               0x30B9
+#define EGL_GL_TEXTURE_2D                 0x30B1
+#define EGL_GL_TEXTURE_LEVEL              0x30BC
+#define EGL_GL_TEXTURE_3D                 0x30B2
+#define EGL_GL_TEXTURE_ZOFFSET            0x30BD
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
+#define EGL_IMAGE_PRESERVED               0x30D2
+#define EGL_NO_IMAGE                      ((EGLImage)0)
+EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync);
+EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttrib (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
+EGLAPI EGLImage EGLAPIENTRY eglCreateImage (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImage (EGLDisplay dpy, EGLImage image);
+EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplay (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#endif /* EGL_VERSION_1_5 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/common/rendering/gles/EGL/eglext.h b/source/common/rendering/gles/EGL/eglext.h
new file mode 100644
index 000000000..c8c93773b
--- /dev/null
+++ b/source/common/rendering/gles/EGL/eglext.h
@@ -0,0 +1,826 @@
+#ifndef __eglext_h_
+#define __eglext_h_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** Copyright (c) 2013-2014 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+/*
+** This header is generated from the Khronos OpenGL / OpenGL ES XML
+** API Registry. The current version of the Registry, generator scripts
+** used to make the header, and the header can be found at
+**   http://www.opengl.org/registry/
+**
+** Khronos $Revision: 29318 $ on $Date: 2015-01-02 03:16:10 -0800 (Fri, 02 Jan 2015) $
+*/
+
+#include <EGL/eglplatform.h>
+
+#define EGL_EGLEXT_VERSION 20150102
+
+/* Generated C header for:
+ * API: egl
+ * Versions considered: .*
+ * Versions emitted: _nomatch_^
+ * Default extensions included: egl
+ * Additional extensions included: _nomatch_^
+ * Extensions removed: _nomatch_^
+ */
+
+#ifndef EGL_KHR_cl_event
+#define EGL_KHR_cl_event 1
+#define EGL_CL_EVENT_HANDLE_KHR           0x309C
+#define EGL_SYNC_CL_EVENT_KHR             0x30FE
+#define EGL_SYNC_CL_EVENT_COMPLETE_KHR    0x30FF
+#endif /* EGL_KHR_cl_event */
+
+#ifndef EGL_KHR_cl_event2
+#define EGL_KHR_cl_event2 1
+typedef void *EGLSyncKHR;
+typedef intptr_t EGLAttribKHR;
+typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESYNC64KHRPROC) (EGLDisplay dpy, EGLenum type, const EGLAttribKHR *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateSync64KHR (EGLDisplay dpy, EGLenum type, const EGLAttribKHR *attrib_list);
+#endif
+#endif /* EGL_KHR_cl_event2 */
+
+#ifndef EGL_KHR_client_get_all_proc_addresses
+#define EGL_KHR_client_get_all_proc_addresses 1
+#endif /* EGL_KHR_client_get_all_proc_addresses */
+
+#ifndef EGL_KHR_config_attribs
+#define EGL_KHR_config_attribs 1
+#define EGL_CONFORMANT_KHR                0x3042
+#define EGL_VG_COLORSPACE_LINEAR_BIT_KHR  0x0020
+#define EGL_VG_ALPHA_FORMAT_PRE_BIT_KHR   0x0040
+#endif /* EGL_KHR_config_attribs */
+
+#ifndef EGL_KHR_create_context
+#define EGL_KHR_create_context 1
+#define EGL_CONTEXT_MAJOR_VERSION_KHR     0x3098
+#define EGL_CONTEXT_MINOR_VERSION_KHR     0x30FB
+#define EGL_CONTEXT_FLAGS_KHR             0x30FC
+#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD
+#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD
+#define EGL_NO_RESET_NOTIFICATION_KHR     0x31BE
+#define EGL_LOSE_CONTEXT_ON_RESET_KHR     0x31BF
+#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR  0x00000001
+#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
+#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
+#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
+#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
+#define EGL_OPENGL_ES3_BIT_KHR            0x00000040
+#endif /* EGL_KHR_create_context */
+
+#ifndef EGL_KHR_fence_sync
+#define EGL_KHR_fence_sync 1
+#ifdef KHRONOS_SUPPORT_INT64
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR 0x30F0
+#define EGL_SYNC_CONDITION_KHR            0x30F8
+#define EGL_SYNC_FENCE_KHR                0x30F9
+#endif /* KHRONOS_SUPPORT_INT64 */
+#endif /* EGL_KHR_fence_sync */
+
+#ifndef EGL_KHR_get_all_proc_addresses
+#define EGL_KHR_get_all_proc_addresses 1
+#endif /* EGL_KHR_get_all_proc_addresses */
+
+#ifndef EGL_KHR_gl_colorspace
+#define EGL_KHR_gl_colorspace 1
+#define EGL_GL_COLORSPACE_KHR             0x309D
+#define EGL_GL_COLORSPACE_SRGB_KHR        0x3089
+#define EGL_GL_COLORSPACE_LINEAR_KHR      0x308A
+#endif /* EGL_KHR_gl_colorspace */
+
+#ifndef EGL_KHR_gl_renderbuffer_image
+#define EGL_KHR_gl_renderbuffer_image 1
+#define EGL_GL_RENDERBUFFER_KHR           0x30B9
+#endif /* EGL_KHR_gl_renderbuffer_image */
+
+#ifndef EGL_KHR_gl_texture_2D_image
+#define EGL_KHR_gl_texture_2D_image 1
+#define EGL_GL_TEXTURE_2D_KHR             0x30B1
+#define EGL_GL_TEXTURE_LEVEL_KHR          0x30BC
+#endif /* EGL_KHR_gl_texture_2D_image */
+
+#ifndef EGL_KHR_gl_texture_3D_image
+#define EGL_KHR_gl_texture_3D_image 1
+#define EGL_GL_TEXTURE_3D_KHR             0x30B2
+#define EGL_GL_TEXTURE_ZOFFSET_KHR        0x30BD
+#endif /* EGL_KHR_gl_texture_3D_image */
+
+#ifndef EGL_KHR_gl_texture_cubemap_image
+#define EGL_KHR_gl_texture_cubemap_image 1
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR 0x30B3
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR 0x30B4
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR 0x30B5
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR 0x30B6
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR 0x30B7
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR 0x30B8
+#endif /* EGL_KHR_gl_texture_cubemap_image */
+
+#ifndef EGL_KHR_image
+#define EGL_KHR_image 1
+typedef void *EGLImageKHR;
+#define EGL_NATIVE_PIXMAP_KHR             0x30B0
+#define EGL_NO_IMAGE_KHR                  ((EGLImageKHR)0)
+typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImageKHR (EGLDisplay dpy, EGLImageKHR image);
+#endif
+#endif /* EGL_KHR_image */
+
+#ifndef EGL_KHR_image_base
+#define EGL_KHR_image_base 1
+#define EGL_IMAGE_PRESERVED_KHR           0x30D2
+#endif /* EGL_KHR_image_base */
+
+#ifndef EGL_KHR_image_pixmap
+#define EGL_KHR_image_pixmap 1
+#endif /* EGL_KHR_image_pixmap */
+
+#ifndef EGL_KHR_lock_surface
+#define EGL_KHR_lock_surface 1
+#define EGL_READ_SURFACE_BIT_KHR          0x0001
+#define EGL_WRITE_SURFACE_BIT_KHR         0x0002
+#define EGL_LOCK_SURFACE_BIT_KHR          0x0080
+#define EGL_OPTIMAL_FORMAT_BIT_KHR        0x0100
+#define EGL_MATCH_FORMAT_KHR              0x3043
+#define EGL_FORMAT_RGB_565_EXACT_KHR      0x30C0
+#define EGL_FORMAT_RGB_565_KHR            0x30C1
+#define EGL_FORMAT_RGBA_8888_EXACT_KHR    0x30C2
+#define EGL_FORMAT_RGBA_8888_KHR          0x30C3
+#define EGL_MAP_PRESERVE_PIXELS_KHR       0x30C4
+#define EGL_LOCK_USAGE_HINT_KHR           0x30C5
+#define EGL_BITMAP_POINTER_KHR            0x30C6
+#define EGL_BITMAP_PITCH_KHR              0x30C7
+#define EGL_BITMAP_ORIGIN_KHR             0x30C8
+#define EGL_BITMAP_PIXEL_RED_OFFSET_KHR   0x30C9
+#define EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR 0x30CA
+#define EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR  0x30CB
+#define EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR 0x30CC
+#define EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR 0x30CD
+#define EGL_LOWER_LEFT_KHR                0x30CE
+#define EGL_UPPER_LEFT_KHR                0x30CF
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLLOCKSURFACEKHRPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNLOCKSURFACEKHRPROC) (EGLDisplay dpy, EGLSurface surface);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglLockSurfaceKHR (EGLDisplay dpy, EGLSurface surface, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglUnlockSurfaceKHR (EGLDisplay dpy, EGLSurface surface);
+#endif
+#endif /* EGL_KHR_lock_surface */
+
+#ifndef EGL_KHR_lock_surface2
+#define EGL_KHR_lock_surface2 1
+#define EGL_BITMAP_PIXEL_SIZE_KHR         0x3110
+#endif /* EGL_KHR_lock_surface2 */
+
+#ifndef EGL_KHR_lock_surface3
+#define EGL_KHR_lock_surface3 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSURFACE64KHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLAttribKHR *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface64KHR (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLAttribKHR *value);
+#endif
+#endif /* EGL_KHR_lock_surface3 */
+
+#ifndef EGL_KHR_partial_update
+#define EGL_KHR_partial_update 1
+#define EGL_BUFFER_AGE_KHR                0x313D
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETDAMAGEREGIONKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSetDamageRegionKHR (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#endif
+#endif /* EGL_KHR_partial_update */
+
+#ifndef EGL_KHR_platform_android
+#define EGL_KHR_platform_android 1
+#define EGL_PLATFORM_ANDROID_KHR          0x3141
+#endif /* EGL_KHR_platform_android */
+
+#ifndef EGL_KHR_platform_gbm
+#define EGL_KHR_platform_gbm 1
+#define EGL_PLATFORM_GBM_KHR              0x31D7
+#endif /* EGL_KHR_platform_gbm */
+
+#ifndef EGL_KHR_platform_wayland
+#define EGL_KHR_platform_wayland 1
+#define EGL_PLATFORM_WAYLAND_KHR          0x31D8
+#endif /* EGL_KHR_platform_wayland */
+
+#ifndef EGL_KHR_platform_x11
+#define EGL_KHR_platform_x11 1
+#define EGL_PLATFORM_X11_KHR              0x31D5
+#define EGL_PLATFORM_X11_SCREEN_KHR       0x31D6
+#endif /* EGL_KHR_platform_x11 */
+
+#ifndef EGL_KHR_reusable_sync
+#define EGL_KHR_reusable_sync 1
+typedef khronos_utime_nanoseconds_t EGLTimeKHR;
+#ifdef KHRONOS_SUPPORT_INT64
+#define EGL_SYNC_STATUS_KHR               0x30F1
+#define EGL_SIGNALED_KHR                  0x30F2
+#define EGL_UNSIGNALED_KHR                0x30F3
+#define EGL_TIMEOUT_EXPIRED_KHR           0x30F5
+#define EGL_CONDITION_SATISFIED_KHR       0x30F6
+#define EGL_SYNC_TYPE_KHR                 0x30F7
+#define EGL_SYNC_REUSABLE_KHR             0x30FA
+#define EGL_SYNC_FLUSH_COMMANDS_BIT_KHR   0x0001
+#define EGL_FOREVER_KHR                   0xFFFFFFFFFFFFFFFFull
+#define EGL_NO_SYNC_KHR                   ((EGLSyncKHR)0)
+typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync);
+typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSIGNALSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateSyncKHR (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySyncKHR (EGLDisplay dpy, EGLSyncKHR sync);
+EGLAPI EGLint EGLAPIENTRY eglClientWaitSyncKHR (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
+EGLAPI EGLBoolean EGLAPIENTRY eglSignalSyncKHR (EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttribKHR (EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value);
+#endif
+#endif /* KHRONOS_SUPPORT_INT64 */
+#endif /* EGL_KHR_reusable_sync */
+
+#ifndef EGL_KHR_stream
+#define EGL_KHR_stream 1
+typedef void *EGLStreamKHR;
+typedef khronos_uint64_t EGLuint64KHR;
+#ifdef KHRONOS_SUPPORT_INT64
+#define EGL_NO_STREAM_KHR                 ((EGLStreamKHR)0)
+#define EGL_CONSUMER_LATENCY_USEC_KHR     0x3210
+#define EGL_PRODUCER_FRAME_KHR            0x3212
+#define EGL_CONSUMER_FRAME_KHR            0x3213
+#define EGL_STREAM_STATE_KHR              0x3214
+#define EGL_STREAM_STATE_CREATED_KHR      0x3215
+#define EGL_STREAM_STATE_CONNECTING_KHR   0x3216
+#define EGL_STREAM_STATE_EMPTY_KHR        0x3217
+#define EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR 0x3218
+#define EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR 0x3219
+#define EGL_STREAM_STATE_DISCONNECTED_KHR 0x321A
+#define EGL_BAD_STREAM_KHR                0x321B
+#define EGL_BAD_STATE_KHR                 0x321C
+typedef EGLStreamKHR (EGLAPIENTRYP PFNEGLCREATESTREAMKHRPROC) (EGLDisplay dpy, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSTREAMKHRPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMATTRIBKHRPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLint value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSTREAMKHRPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLint *value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSTREAMU64KHRPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLuint64KHR *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLStreamKHR EGLAPIENTRY eglCreateStreamKHR (EGLDisplay dpy, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroyStreamKHR (EGLDisplay dpy, EGLStreamKHR stream);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamAttribKHR (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLint value);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryStreamKHR (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLint *value);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryStreamu64KHR (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLuint64KHR *value);
+#endif
+#endif /* KHRONOS_SUPPORT_INT64 */
+#endif /* EGL_KHR_stream */
+
+#ifndef EGL_KHR_stream_consumer_gltexture
+#define EGL_KHR_stream_consumer_gltexture 1
+#ifdef EGL_KHR_stream
+#define EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR 0x321E
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERACQUIREKHRPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERRELEASEKHRPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerGLTextureExternalKHR (EGLDisplay dpy, EGLStreamKHR stream);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerAcquireKHR (EGLDisplay dpy, EGLStreamKHR stream);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerReleaseKHR (EGLDisplay dpy, EGLStreamKHR stream);
+#endif
+#endif /* EGL_KHR_stream */
+#endif /* EGL_KHR_stream_consumer_gltexture */
+
+#ifndef EGL_KHR_stream_cross_process_fd
+#define EGL_KHR_stream_cross_process_fd 1
+typedef int EGLNativeFileDescriptorKHR;
+#ifdef EGL_KHR_stream
+#define EGL_NO_FILE_DESCRIPTOR_KHR        ((EGLNativeFileDescriptorKHR)(-1))
+typedef EGLNativeFileDescriptorKHR (EGLAPIENTRYP PFNEGLGETSTREAMFILEDESCRIPTORKHRPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+typedef EGLStreamKHR (EGLAPIENTRYP PFNEGLCREATESTREAMFROMFILEDESCRIPTORKHRPROC) (EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLNativeFileDescriptorKHR EGLAPIENTRY eglGetStreamFileDescriptorKHR (EGLDisplay dpy, EGLStreamKHR stream);
+EGLAPI EGLStreamKHR EGLAPIENTRY eglCreateStreamFromFileDescriptorKHR (EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor);
+#endif
+#endif /* EGL_KHR_stream */
+#endif /* EGL_KHR_stream_cross_process_fd */
+
+#ifndef EGL_KHR_stream_fifo
+#define EGL_KHR_stream_fifo 1
+#ifdef EGL_KHR_stream
+#define EGL_STREAM_FIFO_LENGTH_KHR        0x31FC
+#define EGL_STREAM_TIME_NOW_KHR           0x31FD
+#define EGL_STREAM_TIME_CONSUMER_KHR      0x31FE
+#define EGL_STREAM_TIME_PRODUCER_KHR      0x31FF
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSTREAMTIMEKHRPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLTimeKHR *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryStreamTimeKHR (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLTimeKHR *value);
+#endif
+#endif /* EGL_KHR_stream */
+#endif /* EGL_KHR_stream_fifo */
+
+#ifndef EGL_KHR_stream_producer_aldatalocator
+#define EGL_KHR_stream_producer_aldatalocator 1
+#ifdef EGL_KHR_stream
+#endif /* EGL_KHR_stream */
+#endif /* EGL_KHR_stream_producer_aldatalocator */
+
+#ifndef EGL_KHR_stream_producer_eglsurface
+#define EGL_KHR_stream_producer_eglsurface 1
+#ifdef EGL_KHR_stream
+#define EGL_STREAM_BIT_KHR                0x0800
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC) (EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream, const EGLint *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLSurface EGLAPIENTRY eglCreateStreamProducerSurfaceKHR (EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream, const EGLint *attrib_list);
+#endif
+#endif /* EGL_KHR_stream */
+#endif /* EGL_KHR_stream_producer_eglsurface */
+
+#ifndef EGL_KHR_surfaceless_context
+#define EGL_KHR_surfaceless_context 1
+#endif /* EGL_KHR_surfaceless_context */
+
+#ifndef EGL_KHR_swap_buffers_with_damage
+#define EGL_KHR_swap_buffers_with_damage 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#endif
+#endif /* EGL_KHR_swap_buffers_with_damage */
+
+#ifndef EGL_KHR_vg_parent_image
+#define EGL_KHR_vg_parent_image 1
+#define EGL_VG_PARENT_IMAGE_KHR           0x30BA
+#endif /* EGL_KHR_vg_parent_image */
+
+#ifndef EGL_KHR_wait_sync
+#define EGL_KHR_wait_sync 1
+typedef EGLint (EGLAPIENTRYP PFNEGLWAITSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLint EGLAPIENTRY eglWaitSyncKHR (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags);
+#endif
+#endif /* EGL_KHR_wait_sync */
+
+#ifndef EGL_ANDROID_blob_cache
+#define EGL_ANDROID_blob_cache 1
+typedef khronos_ssize_t EGLsizeiANDROID;
+typedef void (*EGLSetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, const void *value, EGLsizeiANDROID valueSize);
+typedef EGLsizeiANDROID (*EGLGetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, void *value, EGLsizeiANDROID valueSize);
+typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSANDROIDPROC) (EGLDisplay dpy, EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI void EGLAPIENTRY eglSetBlobCacheFuncsANDROID (EGLDisplay dpy, EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get);
+#endif
+#endif /* EGL_ANDROID_blob_cache */
+
+#ifndef EGL_ANDROID_framebuffer_target
+#define EGL_ANDROID_framebuffer_target 1
+#define EGL_FRAMEBUFFER_TARGET_ANDROID    0x3147
+#endif /* EGL_ANDROID_framebuffer_target */
+
+#ifndef EGL_ANDROID_image_native_buffer
+#define EGL_ANDROID_image_native_buffer 1
+#define EGL_NATIVE_BUFFER_ANDROID         0x3140
+#endif /* EGL_ANDROID_image_native_buffer */
+
+#ifndef EGL_ANDROID_native_fence_sync
+#define EGL_ANDROID_native_fence_sync 1
+#define EGL_SYNC_NATIVE_FENCE_ANDROID     0x3144
+#define EGL_SYNC_NATIVE_FENCE_FD_ANDROID  0x3145
+#define EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID 0x3146
+#define EGL_NO_NATIVE_FENCE_FD_ANDROID    -1
+typedef EGLint (EGLAPIENTRYP PFNEGLDUPNATIVEFENCEFDANDROIDPROC) (EGLDisplay dpy, EGLSyncKHR sync);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLint EGLAPIENTRY eglDupNativeFenceFDANDROID (EGLDisplay dpy, EGLSyncKHR sync);
+#endif
+#endif /* EGL_ANDROID_native_fence_sync */
+
+#ifndef EGL_ANDROID_recordable
+#define EGL_ANDROID_recordable 1
+#define EGL_RECORDABLE_ANDROID            0x3142
+#endif /* EGL_ANDROID_recordable */
+
+#ifndef EGL_ANGLE_d3d_share_handle_client_buffer
+#define EGL_ANGLE_d3d_share_handle_client_buffer 1
+#define EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200
+#endif /* EGL_ANGLE_d3d_share_handle_client_buffer */
+
+#ifndef EGL_ANGLE_query_surface_pointer
+#define EGL_ANGLE_query_surface_pointer 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSURFACEPOINTERANGLEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, void **value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurfacePointerANGLE (EGLDisplay dpy, EGLSurface surface, EGLint attribute, void **value);
+#endif
+#endif /* EGL_ANGLE_query_surface_pointer */
+
+#ifndef EGL_ANGLE_surface_d3d_texture_2d_share_handle
+#define EGL_ANGLE_surface_d3d_texture_2d_share_handle 1
+#endif /* EGL_ANGLE_surface_d3d_texture_2d_share_handle */
+
+#ifndef EGL_ANGLE_window_fixed_size
+#define EGL_ANGLE_window_fixed_size 1
+#define EGL_FIXED_SIZE_ANGLE              0x3201
+#endif /* EGL_ANGLE_window_fixed_size */
+
+#ifndef EGL_ARM_pixmap_multisample_discard
+#define EGL_ARM_pixmap_multisample_discard 1
+#define EGL_DISCARD_SAMPLES_ARM           0x3286
+#endif /* EGL_ARM_pixmap_multisample_discard */
+
+#ifndef EGL_EXT_buffer_age
+#define EGL_EXT_buffer_age 1
+#define EGL_BUFFER_AGE_EXT                0x313D
+#endif /* EGL_EXT_buffer_age */
+
+#ifndef EGL_EXT_client_extensions
+#define EGL_EXT_client_extensions 1
+#endif /* EGL_EXT_client_extensions */
+
+#ifndef EGL_EXT_create_context_robustness
+#define EGL_EXT_create_context_robustness 1
+#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF
+#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT 0x3138
+#define EGL_NO_RESET_NOTIFICATION_EXT     0x31BE
+#define EGL_LOSE_CONTEXT_ON_RESET_EXT     0x31BF
+#endif /* EGL_EXT_create_context_robustness */
+
+#ifndef EGL_EXT_device_base
+#define EGL_EXT_device_base 1
+typedef void *EGLDeviceEXT;
+#define EGL_NO_DEVICE_EXT                 ((EGLDeviceEXT)(0))
+#define EGL_BAD_DEVICE_EXT                0x322B
+#define EGL_DEVICE_EXT                    0x322C
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDEVICEATTRIBEXTPROC) (EGLDeviceEXT device, EGLint attribute, EGLAttrib *value);
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYDEVICESTRINGEXTPROC) (EGLDeviceEXT device, EGLint name);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDEVICESEXTPROC) (EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDISPLAYATTRIBEXTPROC) (EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDeviceAttribEXT (EGLDeviceEXT device, EGLint attribute, EGLAttrib *value);
+EGLAPI const char *EGLAPIENTRY eglQueryDeviceStringEXT (EGLDeviceEXT device, EGLint name);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDevicesEXT (EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribEXT (EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#endif
+#endif /* EGL_EXT_device_base */
+
+#ifndef EGL_EXT_device_drm
+#define EGL_EXT_device_drm 1
+#define EGL_DRM_DEVICE_FILE_EXT           0x3233
+#endif /* EGL_EXT_device_drm */
+
+#ifndef EGL_EXT_device_openwf
+#define EGL_EXT_device_openwf 1
+#define EGL_OPENWF_DEVICE_ID_EXT          0x3237
+#endif /* EGL_EXT_device_openwf */
+
+#ifndef EGL_EXT_image_dma_buf_import
+#define EGL_EXT_image_dma_buf_import 1
+#define EGL_LINUX_DMA_BUF_EXT             0x3270
+#define EGL_LINUX_DRM_FOURCC_EXT          0x3271
+#define EGL_DMA_BUF_PLANE0_FD_EXT         0x3272
+#define EGL_DMA_BUF_PLANE0_OFFSET_EXT     0x3273
+#define EGL_DMA_BUF_PLANE0_PITCH_EXT      0x3274
+#define EGL_DMA_BUF_PLANE1_FD_EXT         0x3275
+#define EGL_DMA_BUF_PLANE1_OFFSET_EXT     0x3276
+#define EGL_DMA_BUF_PLANE1_PITCH_EXT      0x3277
+#define EGL_DMA_BUF_PLANE2_FD_EXT         0x3278
+#define EGL_DMA_BUF_PLANE2_OFFSET_EXT     0x3279
+#define EGL_DMA_BUF_PLANE2_PITCH_EXT      0x327A
+#define EGL_YUV_COLOR_SPACE_HINT_EXT      0x327B
+#define EGL_SAMPLE_RANGE_HINT_EXT         0x327C
+#define EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT 0x327D
+#define EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT 0x327E
+#define EGL_ITU_REC601_EXT                0x327F
+#define EGL_ITU_REC709_EXT                0x3280
+#define EGL_ITU_REC2020_EXT               0x3281
+#define EGL_YUV_FULL_RANGE_EXT            0x3282
+#define EGL_YUV_NARROW_RANGE_EXT          0x3283
+#define EGL_YUV_CHROMA_SITING_0_EXT       0x3284
+#define EGL_YUV_CHROMA_SITING_0_5_EXT     0x3285
+#endif /* EGL_EXT_image_dma_buf_import */
+
+#ifndef EGL_EXT_multiview_window
+#define EGL_EXT_multiview_window 1
+#define EGL_MULTIVIEW_VIEW_COUNT_EXT      0x3134
+#endif /* EGL_EXT_multiview_window */
+
+#ifndef EGL_EXT_output_base
+#define EGL_EXT_output_base 1
+typedef void *EGLOutputLayerEXT;
+typedef void *EGLOutputPortEXT;
+#define EGL_NO_OUTPUT_LAYER_EXT           ((EGLOutputLayerEXT)0)
+#define EGL_NO_OUTPUT_PORT_EXT            ((EGLOutputPortEXT)0)
+#define EGL_BAD_OUTPUT_LAYER_EXT          0x322D
+#define EGL_BAD_OUTPUT_PORT_EXT           0x322E
+#define EGL_SWAP_INTERVAL_EXT             0x322F
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETOUTPUTLAYERSEXTPROC) (EGLDisplay dpy, const EGLAttrib *attrib_list, EGLOutputLayerEXT *layers, EGLint max_layers, EGLint *num_layers);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETOUTPUTPORTSEXTPROC) (EGLDisplay dpy, const EGLAttrib *attrib_list, EGLOutputPortEXT *ports, EGLint max_ports, EGLint *num_ports);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLOUTPUTLAYERATTRIBEXTPROC) (EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint attribute, EGLAttrib value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYOUTPUTLAYERATTRIBEXTPROC) (EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint attribute, EGLAttrib *value);
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYOUTPUTLAYERSTRINGEXTPROC) (EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint name);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLOUTPUTPORTATTRIBEXTPROC) (EGLDisplay dpy, EGLOutputPortEXT port, EGLint attribute, EGLAttrib value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYOUTPUTPORTATTRIBEXTPROC) (EGLDisplay dpy, EGLOutputPortEXT port, EGLint attribute, EGLAttrib *value);
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYOUTPUTPORTSTRINGEXTPROC) (EGLDisplay dpy, EGLOutputPortEXT port, EGLint name);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglGetOutputLayersEXT (EGLDisplay dpy, const EGLAttrib *attrib_list, EGLOutputLayerEXT *layers, EGLint max_layers, EGLint *num_layers);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetOutputPortsEXT (EGLDisplay dpy, const EGLAttrib *attrib_list, EGLOutputPortEXT *ports, EGLint max_ports, EGLint *num_ports);
+EGLAPI EGLBoolean EGLAPIENTRY eglOutputLayerAttribEXT (EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint attribute, EGLAttrib value);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryOutputLayerAttribEXT (EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint attribute, EGLAttrib *value);
+EGLAPI const char *EGLAPIENTRY eglQueryOutputLayerStringEXT (EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint name);
+EGLAPI EGLBoolean EGLAPIENTRY eglOutputPortAttribEXT (EGLDisplay dpy, EGLOutputPortEXT port, EGLint attribute, EGLAttrib value);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryOutputPortAttribEXT (EGLDisplay dpy, EGLOutputPortEXT port, EGLint attribute, EGLAttrib *value);
+EGLAPI const char *EGLAPIENTRY eglQueryOutputPortStringEXT (EGLDisplay dpy, EGLOutputPortEXT port, EGLint name);
+#endif
+#endif /* EGL_EXT_output_base */
+
+#ifndef EGL_EXT_output_drm
+#define EGL_EXT_output_drm 1
+#define EGL_DRM_CRTC_EXT                  0x3234
+#define EGL_DRM_PLANE_EXT                 0x3235
+#define EGL_DRM_CONNECTOR_EXT             0x3236
+#endif /* EGL_EXT_output_drm */
+
+#ifndef EGL_EXT_output_openwf
+#define EGL_EXT_output_openwf 1
+#define EGL_OPENWF_PIPELINE_ID_EXT        0x3238
+#define EGL_OPENWF_PORT_ID_EXT            0x3239
+#endif /* EGL_EXT_output_openwf */
+
+#ifndef EGL_EXT_platform_base
+#define EGL_EXT_platform_base 1
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLint *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplayEXT (EGLenum platform, void *native_display, const EGLint *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurfaceEXT (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurfaceEXT (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLint *attrib_list);
+#endif
+#endif /* EGL_EXT_platform_base */
+
+#ifndef EGL_EXT_platform_device
+#define EGL_EXT_platform_device 1
+#define EGL_PLATFORM_DEVICE_EXT           0x313F
+#endif /* EGL_EXT_platform_device */
+
+#ifndef EGL_EXT_platform_wayland
+#define EGL_EXT_platform_wayland 1
+#define EGL_PLATFORM_WAYLAND_EXT          0x31D8
+#endif /* EGL_EXT_platform_wayland */
+
+#ifndef EGL_EXT_platform_x11
+#define EGL_EXT_platform_x11 1
+#define EGL_PLATFORM_X11_EXT              0x31D5
+#define EGL_PLATFORM_X11_SCREEN_EXT       0x31D6
+#endif /* EGL_EXT_platform_x11 */
+
+#ifndef EGL_EXT_protected_surface
+#define EGL_EXT_protected_surface 1
+#define EGL_PROTECTED_CONTENT_EXT         0x32C0
+#endif /* EGL_EXT_protected_surface */
+
+#ifndef EGL_EXT_stream_consumer_egloutput
+#define EGL_EXT_stream_consumer_egloutput 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLOutputLayerEXT layer);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerOutputEXT (EGLDisplay dpy, EGLStreamKHR stream, EGLOutputLayerEXT layer);
+#endif
+#endif /* EGL_EXT_stream_consumer_egloutput */
+
+#ifndef EGL_EXT_swap_buffers_with_damage
+#define EGL_EXT_swap_buffers_with_damage 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#endif
+#endif /* EGL_EXT_swap_buffers_with_damage */
+
+#ifndef EGL_HI_clientpixmap
+#define EGL_HI_clientpixmap 1
+struct EGLClientPixmapHI {
+    void  *pData;
+    EGLint iWidth;
+    EGLint iHeight;
+    EGLint iStride;
+};
+#define EGL_CLIENT_PIXMAP_POINTER_HI      0x8F74
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPIXMAPSURFACEHIPROC) (EGLDisplay dpy, EGLConfig config, struct EGLClientPixmapHI *pixmap);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePixmapSurfaceHI (EGLDisplay dpy, EGLConfig config, struct EGLClientPixmapHI *pixmap);
+#endif
+#endif /* EGL_HI_clientpixmap */
+
+#ifndef EGL_HI_colorformats
+#define EGL_HI_colorformats 1
+#define EGL_COLOR_FORMAT_HI               0x8F70
+#define EGL_COLOR_RGB_HI                  0x8F71
+#define EGL_COLOR_RGBA_HI                 0x8F72
+#define EGL_COLOR_ARGB_HI                 0x8F73
+#endif /* EGL_HI_colorformats */
+
+#ifndef EGL_IMG_context_priority
+#define EGL_IMG_context_priority 1
+#define EGL_CONTEXT_PRIORITY_LEVEL_IMG    0x3100
+#define EGL_CONTEXT_PRIORITY_HIGH_IMG     0x3101
+#define EGL_CONTEXT_PRIORITY_MEDIUM_IMG   0x3102
+#define EGL_CONTEXT_PRIORITY_LOW_IMG      0x3103
+#endif /* EGL_IMG_context_priority */
+
+#ifndef EGL_MESA_drm_image
+#define EGL_MESA_drm_image 1
+#define EGL_DRM_BUFFER_FORMAT_MESA        0x31D0
+#define EGL_DRM_BUFFER_USE_MESA           0x31D1
+#define EGL_DRM_BUFFER_FORMAT_ARGB32_MESA 0x31D2
+#define EGL_DRM_BUFFER_MESA               0x31D3
+#define EGL_DRM_BUFFER_STRIDE_MESA        0x31D4
+#define EGL_DRM_BUFFER_USE_SCANOUT_MESA   0x00000001
+#define EGL_DRM_BUFFER_USE_SHARE_MESA     0x00000002
+typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEDRMIMAGEMESAPROC) (EGLDisplay dpy, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLEXPORTDRMIMAGEMESAPROC) (EGLDisplay dpy, EGLImageKHR image, EGLint *name, EGLint *handle, EGLint *stride);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLImageKHR EGLAPIENTRY eglCreateDRMImageMESA (EGLDisplay dpy, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglExportDRMImageMESA (EGLDisplay dpy, EGLImageKHR image, EGLint *name, EGLint *handle, EGLint *stride);
+#endif
+#endif /* EGL_MESA_drm_image */
+
+#ifndef EGL_MESA_platform_gbm
+#define EGL_MESA_platform_gbm 1
+#define EGL_PLATFORM_GBM_MESA             0x31D7
+#endif /* EGL_MESA_platform_gbm */
+
+#ifndef EGL_NOK_swap_region
+#define EGL_NOK_swap_region 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSREGIONNOKPROC) (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersRegionNOK (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects);
+#endif
+#endif /* EGL_NOK_swap_region */
+
+#ifndef EGL_NOK_swap_region2
+#define EGL_NOK_swap_region2 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSREGION2NOKPROC) (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersRegion2NOK (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects);
+#endif
+#endif /* EGL_NOK_swap_region2 */
+
+#ifndef EGL_NOK_texture_from_pixmap
+#define EGL_NOK_texture_from_pixmap 1
+#define EGL_Y_INVERTED_NOK                0x307F
+#endif /* EGL_NOK_texture_from_pixmap */
+
+#ifndef EGL_NV_3dvision_surface
+#define EGL_NV_3dvision_surface 1
+#define EGL_AUTO_STEREO_NV                0x3136
+#endif /* EGL_NV_3dvision_surface */
+
+#ifndef EGL_NV_coverage_sample
+#define EGL_NV_coverage_sample 1
+#define EGL_COVERAGE_BUFFERS_NV           0x30E0
+#define EGL_COVERAGE_SAMPLES_NV           0x30E1
+#endif /* EGL_NV_coverage_sample */
+
+#ifndef EGL_NV_coverage_sample_resolve
+#define EGL_NV_coverage_sample_resolve 1
+#define EGL_COVERAGE_SAMPLE_RESOLVE_NV    0x3131
+#define EGL_COVERAGE_SAMPLE_RESOLVE_DEFAULT_NV 0x3132
+#define EGL_COVERAGE_SAMPLE_RESOLVE_NONE_NV 0x3133
+#endif /* EGL_NV_coverage_sample_resolve */
+
+#ifndef EGL_NV_cuda_event
+#define EGL_NV_cuda_event 1
+#define EGL_CUDA_EVENT_HANDLE_NV          0x323B
+#define EGL_SYNC_CUDA_EVENT_NV            0x323C
+#define EGL_SYNC_CUDA_EVENT_COMPLETE_NV   0x323D
+#endif /* EGL_NV_cuda_event */
+
+#ifndef EGL_NV_depth_nonlinear
+#define EGL_NV_depth_nonlinear 1
+#define EGL_DEPTH_ENCODING_NV             0x30E2
+#define EGL_DEPTH_ENCODING_NONE_NV        0
+#define EGL_DEPTH_ENCODING_NONLINEAR_NV   0x30E3
+#endif /* EGL_NV_depth_nonlinear */
+
+#ifndef EGL_NV_device_cuda
+#define EGL_NV_device_cuda 1
+#define EGL_CUDA_DEVICE_NV                0x323A
+#endif /* EGL_NV_device_cuda */
+
+#ifndef EGL_NV_native_query
+#define EGL_NV_native_query 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYNATIVEDISPLAYNVPROC) (EGLDisplay dpy, EGLNativeDisplayType *display_id);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYNATIVEWINDOWNVPROC) (EGLDisplay dpy, EGLSurface surf, EGLNativeWindowType *window);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYNATIVEPIXMAPNVPROC) (EGLDisplay dpy, EGLSurface surf, EGLNativePixmapType *pixmap);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryNativeDisplayNV (EGLDisplay dpy, EGLNativeDisplayType *display_id);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryNativeWindowNV (EGLDisplay dpy, EGLSurface surf, EGLNativeWindowType *window);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryNativePixmapNV (EGLDisplay dpy, EGLSurface surf, EGLNativePixmapType *pixmap);
+#endif
+#endif /* EGL_NV_native_query */
+
+#ifndef EGL_NV_post_convert_rounding
+#define EGL_NV_post_convert_rounding 1
+#endif /* EGL_NV_post_convert_rounding */
+
+#ifndef EGL_NV_post_sub_buffer
+#define EGL_NV_post_sub_buffer 1
+#define EGL_POST_SUB_BUFFER_SUPPORTED_NV  0x30BE
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLPOSTSUBBUFFERNVPROC) (EGLDisplay dpy, EGLSurface surface, EGLint x, EGLint y, EGLint width, EGLint height);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglPostSubBufferNV (EGLDisplay dpy, EGLSurface surface, EGLint x, EGLint y, EGLint width, EGLint height);
+#endif
+#endif /* EGL_NV_post_sub_buffer */
+
+#ifndef EGL_NV_stream_sync
+#define EGL_NV_stream_sync 1
+#define EGL_SYNC_NEW_FRAME_NV             0x321F
+typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESTREAMSYNCNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum type, const EGLint *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateStreamSyncNV (EGLDisplay dpy, EGLStreamKHR stream, EGLenum type, const EGLint *attrib_list);
+#endif
+#endif /* EGL_NV_stream_sync */
+
+#ifndef EGL_NV_sync
+#define EGL_NV_sync 1
+typedef void *EGLSyncNV;
+typedef khronos_utime_nanoseconds_t EGLTimeNV;
+#ifdef KHRONOS_SUPPORT_INT64
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE_NV 0x30E6
+#define EGL_SYNC_STATUS_NV                0x30E7
+#define EGL_SIGNALED_NV                   0x30E8
+#define EGL_UNSIGNALED_NV                 0x30E9
+#define EGL_SYNC_FLUSH_COMMANDS_BIT_NV    0x0001
+#define EGL_FOREVER_NV                    0xFFFFFFFFFFFFFFFFull
+#define EGL_ALREADY_SIGNALED_NV           0x30EA
+#define EGL_TIMEOUT_EXPIRED_NV            0x30EB
+#define EGL_CONDITION_SATISFIED_NV        0x30EC
+#define EGL_SYNC_TYPE_NV                  0x30ED
+#define EGL_SYNC_CONDITION_NV             0x30EE
+#define EGL_SYNC_FENCE_NV                 0x30EF
+#define EGL_NO_SYNC_NV                    ((EGLSyncNV)0)
+typedef EGLSyncNV (EGLAPIENTRYP PFNEGLCREATEFENCESYNCNVPROC) (EGLDisplay dpy, EGLenum condition, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCNVPROC) (EGLSyncNV sync);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLFENCENVPROC) (EGLSyncNV sync);
+typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCNVPROC) (EGLSyncNV sync, EGLint flags, EGLTimeNV timeout);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSIGNALSYNCNVPROC) (EGLSyncNV sync, EGLenum mode);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBNVPROC) (EGLSyncNV sync, EGLint attribute, EGLint *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLSyncNV EGLAPIENTRY eglCreateFenceSyncNV (EGLDisplay dpy, EGLenum condition, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySyncNV (EGLSyncNV sync);
+EGLAPI EGLBoolean EGLAPIENTRY eglFenceNV (EGLSyncNV sync);
+EGLAPI EGLint EGLAPIENTRY eglClientWaitSyncNV (EGLSyncNV sync, EGLint flags, EGLTimeNV timeout);
+EGLAPI EGLBoolean EGLAPIENTRY eglSignalSyncNV (EGLSyncNV sync, EGLenum mode);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttribNV (EGLSyncNV sync, EGLint attribute, EGLint *value);
+#endif
+#endif /* KHRONOS_SUPPORT_INT64 */
+#endif /* EGL_NV_sync */
+
+#ifndef EGL_NV_system_time
+#define EGL_NV_system_time 1
+typedef khronos_utime_nanoseconds_t EGLuint64NV;
+#ifdef KHRONOS_SUPPORT_INT64
+typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC) (void);
+typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMENVPROC) (void);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLuint64NV EGLAPIENTRY eglGetSystemTimeFrequencyNV (void);
+EGLAPI EGLuint64NV EGLAPIENTRY eglGetSystemTimeNV (void);
+#endif
+#endif /* KHRONOS_SUPPORT_INT64 */
+#endif /* EGL_NV_system_time */
+
+#ifndef EGL_TIZEN_image_native_buffer
+#define EGL_TIZEN_image_native_buffer 1
+#define EGL_NATIVE_BUFFER_TIZEN           0x32A0
+#endif /* EGL_TIZEN_image_native_buffer */
+
+#ifndef EGL_TIZEN_image_native_surface
+#define EGL_TIZEN_image_native_surface 1
+#define EGL_NATIVE_SURFACE_TIZEN          0x32A1
+#endif /* EGL_TIZEN_image_native_surface */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/common/rendering/gles/EGL/eglplatform.h b/source/common/rendering/gles/EGL/eglplatform.h
new file mode 100644
index 000000000..d79f322e2
--- /dev/null
+++ b/source/common/rendering/gles/EGL/eglplatform.h
@@ -0,0 +1,125 @@
+#ifndef __eglplatform_h_
+#define __eglplatform_h_
+
+/*
+** Copyright (c) 2007-2013 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+/* Platform-specific types and definitions for egl.h
+ * $Revision: 23432 $ on $Date: 2013-10-09 00:57:24 -0700 (Wed, 09 Oct 2013) $
+ *
+ * Adopters may modify khrplatform.h and this file to suit their platform.
+ * You are encouraged to submit all modifications to the Khronos group so that
+ * they can be included in future versions of this file.  Please submit changes
+ * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla)
+ * by filing a bug against product "EGL" component "Registry".
+ */
+
+#include <KHR/khrplatform.h>
+
+/* Macros used in EGL function prototype declarations.
+ *
+ * EGL functions should be prototyped as:
+ *
+ * EGLAPI return-type EGLAPIENTRY eglFunction(arguments);
+ * typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments);
+ *
+ * KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h
+ */
+
+#ifndef EGLAPI
+#define EGLAPI KHRONOS_APICALL
+#endif
+
+#ifndef EGLAPIENTRY
+#define EGLAPIENTRY  KHRONOS_APIENTRY
+#endif
+#define EGLAPIENTRYP EGLAPIENTRY*
+
+/* The types NativeDisplayType, NativeWindowType, and NativePixmapType
+ * are aliases of window-system-dependent types, such as X Display * or
+ * Windows Device Context. They must be defined in platform-specific
+ * code below. The EGL-prefixed versions of Native*Type are the same
+ * types, renamed in EGL 1.3 so all types in the API start with "EGL".
+ *
+ * Khronos STRONGLY RECOMMENDS that you use the default definitions
+ * provided below, since these changes affect both binary and source
+ * portability of applications using EGL running on different EGL
+ * implementations.
+ */
+
+#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <windows.h>
+
+typedef HDC     EGLNativeDisplayType;
+typedef HBITMAP EGLNativePixmapType;
+typedef HWND    EGLNativeWindowType;
+
+#elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
+
+typedef int   EGLNativeDisplayType;
+typedef void *EGLNativeWindowType;
+typedef void *EGLNativePixmapType;
+
+#elif defined(__ANDROID__) || defined(ANDROID)
+
+#include <android/native_window.h>
+
+struct egl_native_pixmap_t;
+
+typedef struct ANativeWindow*           EGLNativeWindowType;
+typedef struct egl_native_pixmap_t*     EGLNativePixmapType;
+typedef void*                           EGLNativeDisplayType;
+
+#elif defined(__unix__) || defined(__APPLE__)
+
+/* X11 (tentative)  */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef Display *EGLNativeDisplayType;
+typedef Pixmap   EGLNativePixmapType;
+typedef Window   EGLNativeWindowType;
+
+#else
+#error "Platform not recognized"
+#endif
+
+/* EGL 1.2 types, renamed for consistency in EGL 1.3 */
+typedef EGLNativeDisplayType NativeDisplayType;
+typedef EGLNativePixmapType  NativePixmapType;
+typedef EGLNativeWindowType  NativeWindowType;
+
+
+/* Define EGLint. This must be a signed integral type large enough to contain
+ * all legal attribute names and values passed into and out of EGL, whether
+ * their type is boolean, bitmask, enumerant (symbolic constant), integer,
+ * handle, or other.  While in general a 32-bit integer will suffice, if
+ * handles are 64 bit types, then EGLint should be defined as a signed 64-bit
+ * integer type.
+ */
+typedef khronos_int32_t EGLint;
+
+#endif /* __eglplatform_h */
diff --git a/source/common/rendering/gles/glad/include/EGL/eglplatform.h b/source/common/rendering/gles/glad/include/EGL/eglplatform.h
new file mode 100644
index 000000000..d79f322e2
--- /dev/null
+++ b/source/common/rendering/gles/glad/include/EGL/eglplatform.h
@@ -0,0 +1,125 @@
+#ifndef __eglplatform_h_
+#define __eglplatform_h_
+
+/*
+** Copyright (c) 2007-2013 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+/* Platform-specific types and definitions for egl.h
+ * $Revision: 23432 $ on $Date: 2013-10-09 00:57:24 -0700 (Wed, 09 Oct 2013) $
+ *
+ * Adopters may modify khrplatform.h and this file to suit their platform.
+ * You are encouraged to submit all modifications to the Khronos group so that
+ * they can be included in future versions of this file.  Please submit changes
+ * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla)
+ * by filing a bug against product "EGL" component "Registry".
+ */
+
+#include <KHR/khrplatform.h>
+
+/* Macros used in EGL function prototype declarations.
+ *
+ * EGL functions should be prototyped as:
+ *
+ * EGLAPI return-type EGLAPIENTRY eglFunction(arguments);
+ * typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments);
+ *
+ * KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h
+ */
+
+#ifndef EGLAPI
+#define EGLAPI KHRONOS_APICALL
+#endif
+
+#ifndef EGLAPIENTRY
+#define EGLAPIENTRY  KHRONOS_APIENTRY
+#endif
+#define EGLAPIENTRYP EGLAPIENTRY*
+
+/* The types NativeDisplayType, NativeWindowType, and NativePixmapType
+ * are aliases of window-system-dependent types, such as X Display * or
+ * Windows Device Context. They must be defined in platform-specific
+ * code below. The EGL-prefixed versions of Native*Type are the same
+ * types, renamed in EGL 1.3 so all types in the API start with "EGL".
+ *
+ * Khronos STRONGLY RECOMMENDS that you use the default definitions
+ * provided below, since these changes affect both binary and source
+ * portability of applications using EGL running on different EGL
+ * implementations.
+ */
+
+#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <windows.h>
+
+typedef HDC     EGLNativeDisplayType;
+typedef HBITMAP EGLNativePixmapType;
+typedef HWND    EGLNativeWindowType;
+
+#elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
+
+typedef int   EGLNativeDisplayType;
+typedef void *EGLNativeWindowType;
+typedef void *EGLNativePixmapType;
+
+#elif defined(__ANDROID__) || defined(ANDROID)
+
+#include <android/native_window.h>
+
+struct egl_native_pixmap_t;
+
+typedef struct ANativeWindow*           EGLNativeWindowType;
+typedef struct egl_native_pixmap_t*     EGLNativePixmapType;
+typedef void*                           EGLNativeDisplayType;
+
+#elif defined(__unix__) || defined(__APPLE__)
+
+/* X11 (tentative)  */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef Display *EGLNativeDisplayType;
+typedef Pixmap   EGLNativePixmapType;
+typedef Window   EGLNativeWindowType;
+
+#else
+#error "Platform not recognized"
+#endif
+
+/* EGL 1.2 types, renamed for consistency in EGL 1.3 */
+typedef EGLNativeDisplayType NativeDisplayType;
+typedef EGLNativePixmapType  NativePixmapType;
+typedef EGLNativeWindowType  NativeWindowType;
+
+
+/* Define EGLint. This must be a signed integral type large enough to contain
+ * all legal attribute names and values passed into and out of EGL, whether
+ * their type is boolean, bitmask, enumerant (symbolic constant), integer,
+ * handle, or other.  While in general a 32-bit integer will suffice, if
+ * handles are 64 bit types, then EGLint should be defined as a signed 64-bit
+ * integer type.
+ */
+typedef khronos_int32_t EGLint;
+
+#endif /* __eglplatform_h */
diff --git a/source/common/rendering/gles/glad/include/KHR/khrplatform.h b/source/common/rendering/gles/glad/include/KHR/khrplatform.h
new file mode 100644
index 000000000..dd22d9270
--- /dev/null
+++ b/source/common/rendering/gles/glad/include/KHR/khrplatform.h
@@ -0,0 +1,290 @@
+#ifndef __khrplatform_h_
+#define __khrplatform_h_
+
+/*
+** Copyright (c) 2008-2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+/* Khronos platform-specific types and definitions.
+ *
+ * The master copy of khrplatform.h is maintained in the Khronos EGL
+ * Registry repository at https://github.com/KhronosGroup/EGL-Registry
+ * The last semantic modification to khrplatform.h was at commit ID:
+ *      67a3e0864c2d75ea5287b9f3d2eb74a745936692
+ *
+ * Adopters may modify this file to suit their platform. Adopters are
+ * encouraged to submit platform specific modifications to the Khronos
+ * group so that they can be included in future versions of this file.
+ * Please submit changes by filing pull requests or issues on
+ * the EGL Registry repository linked above.
+ *
+ *
+ * See the Implementer's Guidelines for information about where this file
+ * should be located on your system and for more details of its use:
+ *    http://www.khronos.org/registry/implementers_guide.pdf
+ *
+ * This file should be included as
+ *        #include <KHR/khrplatform.h>
+ * by Khronos client API header files that use its types and defines.
+ *
+ * The types in khrplatform.h should only be used to define API-specific types.
+ *
+ * Types defined in khrplatform.h:
+ *    khronos_int8_t              signed   8  bit
+ *    khronos_uint8_t             unsigned 8  bit
+ *    khronos_int16_t             signed   16 bit
+ *    khronos_uint16_t            unsigned 16 bit
+ *    khronos_int32_t             signed   32 bit
+ *    khronos_uint32_t            unsigned 32 bit
+ *    khronos_int64_t             signed   64 bit
+ *    khronos_uint64_t            unsigned 64 bit
+ *    khronos_intptr_t            signed   same number of bits as a pointer
+ *    khronos_uintptr_t           unsigned same number of bits as a pointer
+ *    khronos_ssize_t             signed   size
+ *    khronos_usize_t             unsigned size
+ *    khronos_float_t             signed   32 bit floating point
+ *    khronos_time_ns_t           unsigned 64 bit time in nanoseconds
+ *    khronos_utime_nanoseconds_t unsigned time interval or absolute time in
+ *                                         nanoseconds
+ *    khronos_stime_nanoseconds_t signed time interval in nanoseconds
+ *    khronos_boolean_enum_t      enumerated boolean type. This should
+ *      only be used as a base type when a client API's boolean type is
+ *      an enum. Client APIs which use an integer or other type for
+ *      booleans cannot use this as the base type for their boolean.
+ *
+ * Tokens defined in khrplatform.h:
+ *
+ *    KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
+ *
+ *    KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
+ *    KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
+ *
+ * Calling convention macros defined in this file:
+ *    KHRONOS_APICALL
+ *    KHRONOS_APIENTRY
+ *    KHRONOS_APIATTRIBUTES
+ *
+ * These may be used in function prototypes as:
+ *
+ *      KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
+ *                                  int arg1,
+ *                                  int arg2) KHRONOS_APIATTRIBUTES;
+ */
+
+#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
+#   define KHRONOS_STATIC 1
+#endif
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APICALL
+ *-------------------------------------------------------------------------
+ * This precedes the return type of the function in the function prototype.
+ */
+#if defined(KHRONOS_STATIC)
+    /* If the preprocessor constant KHRONOS_STATIC is defined, make the
+     * header compatible with static linking. */
+#   define KHRONOS_APICALL
+#elif defined(_WIN32)
+#   define KHRONOS_APICALL __declspec(dllimport)
+#elif defined (__SYMBIAN32__)
+#   define KHRONOS_APICALL IMPORT_C
+#elif defined(__ANDROID__)
+#   define KHRONOS_APICALL __attribute__((visibility("default")))
+#else
+#   define KHRONOS_APICALL
+#endif
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APIENTRY
+ *-------------------------------------------------------------------------
+ * This follows the return type of the function  and precedes the function
+ * name in the function prototype.
+ */
+#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
+    /* Win32 but not WinCE */
+#   define KHRONOS_APIENTRY __stdcall
+#else
+#   define KHRONOS_APIENTRY
+#endif
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APIATTRIBUTES
+ *-------------------------------------------------------------------------
+ * This follows the closing parenthesis of the function prototype arguments.
+ */
+#if defined (__ARMCC_2__)
+#define KHRONOS_APIATTRIBUTES __softfp
+#else
+#define KHRONOS_APIATTRIBUTES
+#endif
+
+/*-------------------------------------------------------------------------
+ * basic type definitions
+ *-----------------------------------------------------------------------*/
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
+
+
+/*
+ * Using <stdint.h>
+ */
+#include <stdint.h>
+typedef int32_t                 khronos_int32_t;
+typedef uint32_t                khronos_uint32_t;
+typedef int64_t                 khronos_int64_t;
+typedef uint64_t                khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#elif defined(__VMS ) || defined(__sgi)
+
+/*
+ * Using <inttypes.h>
+ */
+#include <inttypes.h>
+typedef int32_t                 khronos_int32_t;
+typedef uint32_t                khronos_uint32_t;
+typedef int64_t                 khronos_int64_t;
+typedef uint64_t                khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
+
+/*
+ * Win32
+ */
+typedef __int32                 khronos_int32_t;
+typedef unsigned __int32        khronos_uint32_t;
+typedef __int64                 khronos_int64_t;
+typedef unsigned __int64        khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#elif defined(__sun__) || defined(__digital__)
+
+/*
+ * Sun or Digital
+ */
+typedef int                     khronos_int32_t;
+typedef unsigned int            khronos_uint32_t;
+#if defined(__arch64__) || defined(_LP64)
+typedef long int                khronos_int64_t;
+typedef unsigned long int       khronos_uint64_t;
+#else
+typedef long long int           khronos_int64_t;
+typedef unsigned long long int  khronos_uint64_t;
+#endif /* __arch64__ */
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#elif 0
+
+/*
+ * Hypothetical platform with no float or int64 support
+ */
+typedef int                     khronos_int32_t;
+typedef unsigned int            khronos_uint32_t;
+#define KHRONOS_SUPPORT_INT64   0
+#define KHRONOS_SUPPORT_FLOAT   0
+
+#else
+
+/*
+ * Generic fallback
+ */
+#include <stdint.h>
+typedef int32_t                 khronos_int32_t;
+typedef uint32_t                khronos_uint32_t;
+typedef int64_t                 khronos_int64_t;
+typedef uint64_t                khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#endif
+
+
+/*
+ * Types that are (so far) the same on all platforms
+ */
+typedef signed   char          khronos_int8_t;
+typedef unsigned char          khronos_uint8_t;
+typedef signed   short int     khronos_int16_t;
+typedef unsigned short int     khronos_uint16_t;
+
+/*
+ * Types that differ between LLP64 and LP64 architectures - in LLP64,
+ * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
+ * to be the only LLP64 architecture in current use.
+ */
+#ifdef _WIN64
+typedef signed   long long int khronos_intptr_t;
+typedef unsigned long long int khronos_uintptr_t;
+typedef signed   long long int khronos_ssize_t;
+typedef unsigned long long int khronos_usize_t;
+#else
+typedef signed   long  int     khronos_intptr_t;
+typedef unsigned long  int     khronos_uintptr_t;
+typedef signed   long  int     khronos_ssize_t;
+typedef unsigned long  int     khronos_usize_t;
+#endif
+
+#if KHRONOS_SUPPORT_FLOAT
+/*
+ * Float type
+ */
+typedef          float         khronos_float_t;
+#endif
+
+#if KHRONOS_SUPPORT_INT64
+/* Time types
+ *
+ * These types can be used to represent a time interval in nanoseconds or
+ * an absolute Unadjusted System Time.  Unadjusted System Time is the number
+ * of nanoseconds since some arbitrary system event (e.g. since the last
+ * time the system booted).  The Unadjusted System Time is an unsigned
+ * 64 bit value that wraps back to 0 every 584 years.  Time intervals
+ * may be either signed or unsigned.
+ */
+typedef khronos_uint64_t       khronos_utime_nanoseconds_t;
+typedef khronos_int64_t        khronos_stime_nanoseconds_t;
+#endif
+
+/*
+ * Dummy value used to pad enum types to 32 bits.
+ */
+#ifndef KHRONOS_MAX_ENUM
+#define KHRONOS_MAX_ENUM 0x7FFFFFFF
+#endif
+
+/*
+ * Enumerated boolean type
+ *
+ * Values other than zero should be considered to be true.  Therefore
+ * comparisons should not be made against KHRONOS_TRUE.
+ */
+typedef enum {
+    KHRONOS_FALSE = 0,
+    KHRONOS_TRUE  = 1,
+    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
+} khronos_boolean_enum_t;
+
+#endif /* __khrplatform_h_ */
diff --git a/source/common/rendering/gles/glad/include/glad/glad.h b/source/common/rendering/gles/glad/include/glad/glad.h
new file mode 100644
index 000000000..5a7c1505e
--- /dev/null
+++ b/source/common/rendering/gles/glad/include/glad/glad.h
@@ -0,0 +1,874 @@
+/*
+
+    OpenGL ES loader generated by glad 0.1.34 on Fri Mar 19 17:04:24 2021.
+
+    Language/Generator: C/C++
+    Specification: gl
+    APIs: gles2=2.0
+    Profile: compatibility
+    Extensions:
+        
+    Loader: True
+    Local files: False
+    Omit khrplatform: False
+    Reproducible: False
+
+    Commandline:
+        --profile="compatibility" --api="gles2=2.0" --generator="c" --spec="gl" --extensions=""
+    Online:
+        https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gles2%3D2.0
+*/
+
+
+#ifndef __glad_h_
+#define __glad_h_
+
+#ifdef __gl2_h_
+#error OpenGL ES 2 header already included, remove this include, glad already provides it
+#endif
+#define __gl2_h_
+
+#ifdef __gl3_h_
+#error OpenGL ES 3 header already included, remove this include, glad already provides it
+#endif
+#define __gl3_h_
+
+#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
+#define APIENTRY __stdcall
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+#ifndef APIENTRYP
+#define APIENTRYP APIENTRY *
+#endif
+
+#ifndef GLAPIENTRY
+#define GLAPIENTRY APIENTRY
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct gladGLversionStruct {
+    int major;
+    int minor;
+};
+
+typedef void* (* GLADloadproc)(const char *name);
+
+#ifndef GLAPI
+# if defined(GLAD_GLAPI_EXPORT)
+#  if defined(_WIN32) || defined(__CYGWIN__)
+#   if defined(GLAD_GLAPI_EXPORT_BUILD)
+#    if defined(__GNUC__)
+#     define GLAPI __attribute__ ((dllexport)) extern
+#    else
+#     define GLAPI __declspec(dllexport) extern
+#    endif
+#   else
+#    if defined(__GNUC__)
+#     define GLAPI __attribute__ ((dllimport)) extern
+#    else
+#     define GLAPI __declspec(dllimport) extern
+#    endif
+#   endif
+#  elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)
+#   define GLAPI __attribute__ ((visibility ("default"))) extern
+#  else
+#   define GLAPI extern
+#  endif
+# else
+#  define GLAPI extern
+# endif
+#endif
+
+GLAPI struct gladGLversionStruct GLVersion;
+GLAPI int gladLoadGLES2Loader(GLADloadproc);
+
+#include <KHR/khrplatform.h>
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef void GLvoid;
+typedef khronos_int8_t GLbyte;
+typedef khronos_uint8_t GLubyte;
+typedef khronos_int16_t GLshort;
+typedef khronos_uint16_t GLushort;
+typedef int GLint;
+typedef unsigned int GLuint;
+typedef khronos_int32_t GLclampx;
+typedef int GLsizei;
+typedef khronos_float_t GLfloat;
+typedef khronos_float_t GLclampf;
+typedef double GLdouble;
+typedef double GLclampd;
+typedef void *GLeglClientBufferEXT;
+typedef void *GLeglImageOES;
+typedef char GLchar;
+typedef char GLcharARB;
+#ifdef __APPLE__
+typedef void *GLhandleARB;
+#else
+typedef unsigned int GLhandleARB;
+#endif
+typedef khronos_uint16_t GLhalf;
+typedef khronos_uint16_t GLhalfARB;
+typedef khronos_int32_t GLfixed;
+typedef khronos_intptr_t GLintptr;
+typedef khronos_intptr_t GLintptrARB;
+typedef khronos_ssize_t GLsizeiptr;
+typedef khronos_ssize_t GLsizeiptrARB;
+typedef khronos_int64_t GLint64;
+typedef khronos_int64_t GLint64EXT;
+typedef khronos_uint64_t GLuint64;
+typedef khronos_uint64_t GLuint64EXT;
+typedef struct __GLsync *GLsync;
+struct _cl_context;
+struct _cl_event;
+typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);
+typedef unsigned short GLhalfNV;
+typedef GLintptr GLvdpauSurfaceNV;
+typedef void (APIENTRY *GLVULKANPROCNV)(void);
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_STENCIL_BUFFER_BIT 0x00000400
+#define GL_COLOR_BUFFER_BIT 0x00004000
+#define GL_FALSE 0
+#define GL_TRUE 1
+#define GL_POINTS 0x0000
+#define GL_LINES 0x0001
+#define GL_LINE_LOOP 0x0002
+#define GL_LINE_STRIP 0x0003
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_TRIANGLE_FAN 0x0006
+#define GL_ZERO 0
+#define GL_ONE 1
+#define GL_SRC_COLOR 0x0300
+#define GL_ONE_MINUS_SRC_COLOR 0x0301
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_ALPHA 0x0304
+#define GL_ONE_MINUS_DST_ALPHA 0x0305
+#define GL_DST_COLOR 0x0306
+#define GL_ONE_MINUS_DST_COLOR 0x0307
+#define GL_SRC_ALPHA_SATURATE 0x0308
+#define GL_FUNC_ADD 0x8006
+#define GL_BLEND_EQUATION 0x8009
+#define GL_BLEND_EQUATION_RGB 0x8009
+#define GL_BLEND_EQUATION_ALPHA 0x883D
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+#define GL_BLEND_DST_RGB 0x80C8
+#define GL_BLEND_SRC_RGB 0x80C9
+#define GL_BLEND_DST_ALPHA 0x80CA
+#define GL_BLEND_SRC_ALPHA 0x80CB
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_BLEND_COLOR 0x8005
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_ARRAY_BUFFER_BINDING 0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STATIC_DRAW 0x88E4
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_BUFFER_SIZE 0x8764
+#define GL_BUFFER_USAGE 0x8765
+#define GL_CURRENT_VERTEX_ATTRIB 0x8626
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_FRONT_AND_BACK 0x0408
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_CULL_FACE 0x0B44
+#define GL_BLEND 0x0BE2
+#define GL_DITHER 0x0BD0
+#define GL_STENCIL_TEST 0x0B90
+#define GL_DEPTH_TEST 0x0B71
+#define GL_SCISSOR_TEST 0x0C11
+#define GL_POLYGON_OFFSET_FILL 0x8037
+#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
+#define GL_SAMPLE_COVERAGE 0x80A0
+#define GL_NO_ERROR 0
+#define GL_INVALID_ENUM 0x0500
+#define GL_INVALID_VALUE 0x0501
+#define GL_INVALID_OPERATION 0x0502
+#define GL_OUT_OF_MEMORY 0x0505
+#define GL_CW 0x0900
+#define GL_CCW 0x0901
+#define GL_LINE_WIDTH 0x0B21
+#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
+#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
+#define GL_CULL_FACE_MODE 0x0B45
+#define GL_FRONT_FACE 0x0B46
+#define GL_DEPTH_RANGE 0x0B70
+#define GL_DEPTH_WRITEMASK 0x0B72
+#define GL_DEPTH_CLEAR_VALUE 0x0B73
+#define GL_DEPTH_FUNC 0x0B74
+#define GL_STENCIL_CLEAR_VALUE 0x0B91
+#define GL_STENCIL_FUNC 0x0B92
+#define GL_STENCIL_FAIL 0x0B94
+#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
+#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96
+#define GL_STENCIL_REF 0x0B97
+#define GL_STENCIL_VALUE_MASK 0x0B93
+#define GL_STENCIL_WRITEMASK 0x0B98
+#define GL_STENCIL_BACK_FUNC 0x8800
+#define GL_STENCIL_BACK_FAIL 0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define GL_STENCIL_BACK_REF 0x8CA3
+#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4
+#define GL_STENCIL_BACK_WRITEMASK 0x8CA5
+#define GL_VIEWPORT 0x0BA2
+#define GL_SCISSOR_BOX 0x0C10
+#define GL_COLOR_CLEAR_VALUE 0x0C22
+#define GL_COLOR_WRITEMASK 0x0C23
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_PACK_ALIGNMENT 0x0D05
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_MAX_VIEWPORT_DIMS 0x0D3A
+#define GL_SUBPIXEL_BITS 0x0D50
+#define GL_RED_BITS 0x0D52
+#define GL_GREEN_BITS 0x0D53
+#define GL_BLUE_BITS 0x0D54
+#define GL_ALPHA_BITS 0x0D55
+#define GL_DEPTH_BITS 0x0D56
+#define GL_STENCIL_BITS 0x0D57
+#define GL_POLYGON_OFFSET_UNITS 0x2A00
+#define GL_POLYGON_OFFSET_FACTOR 0x8038
+#define GL_TEXTURE_BINDING_2D 0x8069
+#define GL_SAMPLE_BUFFERS 0x80A8
+#define GL_SAMPLES 0x80A9
+#define GL_SAMPLE_COVERAGE_VALUE 0x80AA
+#define GL_SAMPLE_COVERAGE_INVERT 0x80AB
+#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3
+#define GL_DONT_CARE 0x1100
+#define GL_FASTEST 0x1101
+#define GL_NICEST 0x1102
+#define GL_GENERATE_MIPMAP_HINT 0x8192
+#define GL_BYTE 0x1400
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_SHORT 0x1402
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_INT 0x1404
+#define GL_UNSIGNED_INT 0x1405
+#define GL_FLOAT 0x1406
+#define GL_FIXED 0x140C
+#define GL_DEPTH_COMPONENT 0x1902
+#define GL_ALPHA 0x1906
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_LUMINANCE 0x1909
+#define GL_LUMINANCE_ALPHA 0x190A
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_MAX_VERTEX_ATTRIBS 0x8869
+#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
+#define GL_MAX_VARYING_VECTORS 0x8DFC
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
+#define GL_SHADER_TYPE 0x8B4F
+#define GL_DELETE_STATUS 0x8B80
+#define GL_LINK_STATUS 0x8B82
+#define GL_VALIDATE_STATUS 0x8B83
+#define GL_ATTACHED_SHADERS 0x8B85
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#define GL_CURRENT_PROGRAM 0x8B8D
+#define GL_NEVER 0x0200
+#define GL_LESS 0x0201
+#define GL_EQUAL 0x0202
+#define GL_LEQUAL 0x0203
+#define GL_GREATER 0x0204
+#define GL_NOTEQUAL 0x0205
+#define GL_GEQUAL 0x0206
+#define GL_ALWAYS 0x0207
+#define GL_KEEP 0x1E00
+#define GL_REPLACE 0x1E01
+#define GL_INCR 0x1E02
+#define GL_DECR 0x1E03
+#define GL_INVERT 0x150A
+#define GL_INCR_WRAP 0x8507
+#define GL_DECR_WRAP 0x8508
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+#define GL_EXTENSIONS 0x1F03
+#define GL_NEAREST 0x2600
+#define GL_LINEAR 0x2601
+#define GL_NEAREST_MIPMAP_NEAREST 0x2700
+#define GL_LINEAR_MIPMAP_NEAREST 0x2701
+#define GL_NEAREST_MIPMAP_LINEAR 0x2702
+#define GL_LINEAR_MIPMAP_LINEAR 0x2703
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+#define GL_TEXTURE 0x1702
+#define GL_TEXTURE_CUBE_MAP 0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE1 0x84C1
+#define GL_TEXTURE2 0x84C2
+#define GL_TEXTURE3 0x84C3
+#define GL_TEXTURE4 0x84C4
+#define GL_TEXTURE5 0x84C5
+#define GL_TEXTURE6 0x84C6
+#define GL_TEXTURE7 0x84C7
+#define GL_TEXTURE8 0x84C8
+#define GL_TEXTURE9 0x84C9
+#define GL_TEXTURE10 0x84CA
+#define GL_TEXTURE11 0x84CB
+#define GL_TEXTURE12 0x84CC
+#define GL_TEXTURE13 0x84CD
+#define GL_TEXTURE14 0x84CE
+#define GL_TEXTURE15 0x84CF
+#define GL_TEXTURE16 0x84D0
+#define GL_TEXTURE17 0x84D1
+#define GL_TEXTURE18 0x84D2
+#define GL_TEXTURE19 0x84D3
+#define GL_TEXTURE20 0x84D4
+#define GL_TEXTURE21 0x84D5
+#define GL_TEXTURE22 0x84D6
+#define GL_TEXTURE23 0x84D7
+#define GL_TEXTURE24 0x84D8
+#define GL_TEXTURE25 0x84D9
+#define GL_TEXTURE26 0x84DA
+#define GL_TEXTURE27 0x84DB
+#define GL_TEXTURE28 0x84DC
+#define GL_TEXTURE29 0x84DD
+#define GL_TEXTURE30 0x84DE
+#define GL_TEXTURE31 0x84DF
+#define GL_ACTIVE_TEXTURE 0x84E0
+#define GL_REPEAT 0x2901
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_MIRRORED_REPEAT 0x8370
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A
+#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_SHADER_SOURCE_LENGTH 0x8B88
+#define GL_SHADER_COMPILER 0x8DFA
+#define GL_SHADER_BINARY_FORMATS 0x8DF8
+#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9
+#define GL_LOW_FLOAT 0x8DF0
+#define GL_MEDIUM_FLOAT 0x8DF1
+#define GL_HIGH_FLOAT 0x8DF2
+#define GL_LOW_INT 0x8DF3
+#define GL_MEDIUM_INT 0x8DF4
+#define GL_HIGH_INT 0x8DF5
+#define GL_FRAMEBUFFER 0x8D40
+#define GL_RENDERBUFFER 0x8D41
+#define GL_RGBA4 0x8056
+#define GL_RGB5_A1 0x8057
+#define GL_RGB565 0x8D62
+#define GL_DEPTH_COMPONENT16 0x81A5
+#define GL_STENCIL_INDEX8 0x8D48
+#define GL_RENDERBUFFER_WIDTH 0x8D42
+#define GL_RENDERBUFFER_HEIGHT 0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44
+#define GL_RENDERBUFFER_RED_SIZE 0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#define GL_DEPTH_ATTACHMENT 0x8D00
+#define GL_STENCIL_ATTACHMENT 0x8D20
+#define GL_NONE 0
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
+#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+#define GL_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_RENDERBUFFER_BINDING 0x8CA7
+#define GL_MAX_RENDERBUFFER_SIZE 0x84E8
+#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506
+#ifndef GL_ES_VERSION_2_0
+#define GL_ES_VERSION_2_0 1
+GLAPI int GLAD_GL_ES_VERSION_2_0;
+typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture);
+GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture;
+#define glActiveTexture glad_glActiveTexture
+typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader);
+GLAPI PFNGLATTACHSHADERPROC glad_glAttachShader;
+#define glAttachShader glad_glAttachShader
+typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name);
+GLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;
+#define glBindAttribLocation glad_glBindAttribLocation
+typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
+GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer;
+#define glBindBuffer glad_glBindBuffer
+typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);
+GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;
+#define glBindFramebuffer glad_glBindFramebuffer
+typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer);
+GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;
+#define glBindRenderbuffer glad_glBindRenderbuffer
+typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture);
+GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture;
+#define glBindTexture glad_glBindTexture
+typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+GLAPI PFNGLBLENDCOLORPROC glad_glBlendColor;
+#define glBlendColor glad_glBlendColor
+typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode);
+GLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation;
+#define glBlendEquation glad_glBlendEquation
+typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha);
+GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;
+#define glBlendEquationSeparate glad_glBlendEquationSeparate
+typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor);
+GLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc;
+#define glBlendFunc glad_glBlendFunc
+typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
+GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;
+#define glBlendFuncSeparate glad_glBlendFuncSeparate
+typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
+GLAPI PFNGLBUFFERDATAPROC glad_glBufferData;
+#define glBufferData glad_glBufferData
+typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
+GLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;
+#define glBufferSubData glad_glBufferSubData
+typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target);
+GLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;
+#define glCheckFramebufferStatus glad_glCheckFramebufferStatus
+typedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask);
+GLAPI PFNGLCLEARPROC glad_glClear;
+#define glClear glad_glClear
+typedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+GLAPI PFNGLCLEARCOLORPROC glad_glClearColor;
+#define glClearColor glad_glClearColor
+typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC)(GLfloat d);
+GLAPI PFNGLCLEARDEPTHFPROC glad_glClearDepthf;
+#define glClearDepthf glad_glClearDepthf
+typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s);
+GLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil;
+#define glClearStencil glad_glClearStencil
+typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+GLAPI PFNGLCOLORMASKPROC glad_glColorMask;
+#define glColorMask glad_glColorMask
+typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);
+GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;
+#define glCompileShader glad_glCompileShader
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);
+GLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;
+#define glCompressedTexImage2D glad_glCompressedTexImage2D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);
+GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;
+#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D
+typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+GLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;
+#define glCopyTexImage2D glad_glCopyTexImage2D
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;
+#define glCopyTexSubImage2D glad_glCopyTexSubImage2D
+typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void);
+GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;
+#define glCreateProgram glad_glCreateProgram
+typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type);
+GLAPI PFNGLCREATESHADERPROC glad_glCreateShader;
+#define glCreateShader glad_glCreateShader
+typedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode);
+GLAPI PFNGLCULLFACEPROC glad_glCullFace;
+#define glCullFace glad_glCullFace
+typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers);
+GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;
+#define glDeleteBuffers glad_glDeleteBuffers
+typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers);
+GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;
+#define glDeleteFramebuffers glad_glDeleteFramebuffers
+typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program);
+GLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;
+#define glDeleteProgram glad_glDeleteProgram
+typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers);
+GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;
+#define glDeleteRenderbuffers glad_glDeleteRenderbuffers
+typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader);
+GLAPI PFNGLDELETESHADERPROC glad_glDeleteShader;
+#define glDeleteShader glad_glDeleteShader
+typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures);
+GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures;
+#define glDeleteTextures glad_glDeleteTextures
+typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func);
+GLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc;
+#define glDepthFunc glad_glDepthFunc
+typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag);
+GLAPI PFNGLDEPTHMASKPROC glad_glDepthMask;
+#define glDepthMask glad_glDepthMask
+typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f);
+GLAPI PFNGLDEPTHRANGEFPROC glad_glDepthRangef;
+#define glDepthRangef glad_glDepthRangef
+typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader);
+GLAPI PFNGLDETACHSHADERPROC glad_glDetachShader;
+#define glDetachShader glad_glDetachShader
+typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap);
+GLAPI PFNGLDISABLEPROC glad_glDisable;
+#define glDisable glad_glDisable
+typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);
+GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;
+#define glDisableVertexAttribArray glad_glDisableVertexAttribArray
+typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count);
+GLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays;
+#define glDrawArrays glad_glDrawArrays
+typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices);
+GLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements;
+#define glDrawElements glad_glDrawElements
+typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap);
+GLAPI PFNGLENABLEPROC glad_glEnable;
+#define glEnable glad_glEnable
+typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);
+GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;
+#define glEnableVertexAttribArray glad_glEnableVertexAttribArray
+typedef void (APIENTRYP PFNGLFINISHPROC)(void);
+GLAPI PFNGLFINISHPROC glad_glFinish;
+#define glFinish glad_glFinish
+typedef void (APIENTRYP PFNGLFLUSHPROC)(void);
+GLAPI PFNGLFLUSHPROC glad_glFlush;
+#define glFlush glad_glFlush
+typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;
+#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+GLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;
+#define glFramebufferTexture2D glad_glFramebufferTexture2D
+typedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode);
+GLAPI PFNGLFRONTFACEPROC glad_glFrontFace;
+#define glFrontFace glad_glFrontFace
+typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers);
+GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers;
+#define glGenBuffers glad_glGenBuffers
+typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target);
+GLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;
+#define glGenerateMipmap glad_glGenerateMipmap
+typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers);
+GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;
+#define glGenFramebuffers glad_glGenFramebuffers
+typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers);
+GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;
+#define glGenRenderbuffers glad_glGenRenderbuffers
+typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures);
+GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures;
+#define glGenTextures glad_glGenTextures
+typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
+GLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;
+#define glGetActiveAttrib glad_glGetActiveAttrib
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
+GLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;
+#define glGetActiveUniform glad_glGetActiveUniform
+typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders);
+GLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;
+#define glGetAttachedShaders glad_glGetAttachedShaders
+typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name);
+GLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;
+#define glGetAttribLocation glad_glGetAttribLocation
+typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data);
+GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv;
+#define glGetBooleanv glad_glGetBooleanv
+typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;
+#define glGetBufferParameteriv glad_glGetBufferParameteriv
+typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void);
+GLAPI PFNGLGETERRORPROC glad_glGetError;
+#define glGetError glad_glGetError
+typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data);
+GLAPI PFNGLGETFLOATVPROC glad_glGetFloatv;
+#define glGetFloatv glad_glGetFloatv
+typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params);
+GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;
+#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv
+typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data);
+GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv;
+#define glGetIntegerv glad_glGetIntegerv
+typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params);
+GLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;
+#define glGetProgramiv glad_glGetProgramiv
+typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+GLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;
+#define glGetProgramInfoLog glad_glGetProgramInfoLog
+typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;
+#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv
+typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params);
+GLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv;
+#define glGetShaderiv glad_glGetShaderiv
+typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+GLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;
+#define glGetShaderInfoLog glad_glGetShaderInfoLog
+typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision);
+GLAPI PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;
+#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat
+typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source);
+GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;
+#define glGetShaderSource glad_glGetShaderSource
+typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name);
+GLAPI PFNGLGETSTRINGPROC glad_glGetString;
+#define glGetString glad_glGetString
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params);
+GLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;
+#define glGetTexParameterfv glad_glGetTexParameterfv
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;
+#define glGetTexParameteriv glad_glGetTexParameteriv
+typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params);
+GLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;
+#define glGetUniformfv glad_glGetUniformfv
+typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params);
+GLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;
+#define glGetUniformiv glad_glGetUniformiv
+typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name);
+GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;
+#define glGetUniformLocation glad_glGetUniformLocation
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params);
+GLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;
+#define glGetVertexAttribfv glad_glGetVertexAttribfv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params);
+GLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;
+#define glGetVertexAttribiv glad_glGetVertexAttribiv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer);
+GLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;
+#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv
+typedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode);
+GLAPI PFNGLHINTPROC glad_glHint;
+#define glHint glad_glHint
+typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer);
+GLAPI PFNGLISBUFFERPROC glad_glIsBuffer;
+#define glIsBuffer glad_glIsBuffer
+typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap);
+GLAPI PFNGLISENABLEDPROC glad_glIsEnabled;
+#define glIsEnabled glad_glIsEnabled
+typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer);
+GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;
+#define glIsFramebuffer glad_glIsFramebuffer
+typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program);
+GLAPI PFNGLISPROGRAMPROC glad_glIsProgram;
+#define glIsProgram glad_glIsProgram
+typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer);
+GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;
+#define glIsRenderbuffer glad_glIsRenderbuffer
+typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader);
+GLAPI PFNGLISSHADERPROC glad_glIsShader;
+#define glIsShader glad_glIsShader
+typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture);
+GLAPI PFNGLISTEXTUREPROC glad_glIsTexture;
+#define glIsTexture glad_glIsTexture
+typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width);
+GLAPI PFNGLLINEWIDTHPROC glad_glLineWidth;
+#define glLineWidth glad_glLineWidth
+typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program);
+GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram;
+#define glLinkProgram glad_glLinkProgram
+typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param);
+GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei;
+#define glPixelStorei glad_glPixelStorei
+typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units);
+GLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;
+#define glPolygonOffset glad_glPolygonOffset
+typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
+GLAPI PFNGLREADPIXELSPROC glad_glReadPixels;
+#define glReadPixels glad_glReadPixels
+typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC)(void);
+GLAPI PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;
+#define glReleaseShaderCompiler glad_glReleaseShaderCompiler
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;
+#define glRenderbufferStorage glad_glRenderbufferStorage
+typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert);
+GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;
+#define glSampleCoverage glad_glSampleCoverage
+typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI PFNGLSCISSORPROC glad_glScissor;
+#define glScissor glad_glScissor
+typedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length);
+GLAPI PFNGLSHADERBINARYPROC glad_glShaderBinary;
+#define glShaderBinary glad_glShaderBinary
+typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
+GLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource;
+#define glShaderSource glad_glShaderSource
+typedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask);
+GLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc;
+#define glStencilFunc glad_glStencilFunc
+typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask);
+GLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;
+#define glStencilFuncSeparate glad_glStencilFuncSeparate
+typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask);
+GLAPI PFNGLSTENCILMASKPROC glad_glStencilMask;
+#define glStencilMask glad_glStencilMask
+typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask);
+GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;
+#define glStencilMaskSeparate glad_glStencilMaskSeparate
+typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass);
+GLAPI PFNGLSTENCILOPPROC glad_glStencilOp;
+#define glStencilOp glad_glStencilOp
+typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
+GLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;
+#define glStencilOpSeparate glad_glStencilOpSeparate
+typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
+GLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D;
+#define glTexImage2D glad_glTexImage2D
+typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param);
+GLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf;
+#define glTexParameterf glad_glTexParameterf
+typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params);
+GLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;
+#define glTexParameterfv glad_glTexParameterfv
+typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);
+GLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri;
+#define glTexParameteri glad_glTexParameteri
+typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params);
+GLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;
+#define glTexParameteriv glad_glTexParameteriv
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
+GLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;
+#define glTexSubImage2D glad_glTexSubImage2D
+typedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0);
+GLAPI PFNGLUNIFORM1FPROC glad_glUniform1f;
+#define glUniform1f glad_glUniform1f
+typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv;
+#define glUniform1fv glad_glUniform1fv
+typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0);
+GLAPI PFNGLUNIFORM1IPROC glad_glUniform1i;
+#define glUniform1i glad_glUniform1i
+typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv;
+#define glUniform1iv glad_glUniform1iv
+typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1);
+GLAPI PFNGLUNIFORM2FPROC glad_glUniform2f;
+#define glUniform2f glad_glUniform2f
+typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv;
+#define glUniform2fv glad_glUniform2fv
+typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1);
+GLAPI PFNGLUNIFORM2IPROC glad_glUniform2i;
+#define glUniform2i glad_glUniform2i
+typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv;
+#define glUniform2iv glad_glUniform2iv
+typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
+GLAPI PFNGLUNIFORM3FPROC glad_glUniform3f;
+#define glUniform3f glad_glUniform3f
+typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv;
+#define glUniform3fv glad_glUniform3fv
+typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2);
+GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i;
+#define glUniform3i glad_glUniform3i
+typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv;
+#define glUniform3iv glad_glUniform3iv
+typedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
+GLAPI PFNGLUNIFORM4FPROC glad_glUniform4f;
+#define glUniform4f glad_glUniform4f
+typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv;
+#define glUniform4fv glad_glUniform4fv
+typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
+GLAPI PFNGLUNIFORM4IPROC glad_glUniform4i;
+#define glUniform4i glad_glUniform4i
+typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv;
+#define glUniform4iv glad_glUniform4iv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;
+#define glUniformMatrix2fv glad_glUniformMatrix2fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;
+#define glUniformMatrix3fv glad_glUniformMatrix3fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;
+#define glUniformMatrix4fv glad_glUniformMatrix4fv
+typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program);
+GLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram;
+#define glUseProgram glad_glUseProgram
+typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program);
+GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;
+#define glValidateProgram glad_glValidateProgram
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x);
+GLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;
+#define glVertexAttrib1f glad_glVertexAttrib1f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v);
+GLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;
+#define glVertexAttrib1fv glad_glVertexAttrib1fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y);
+GLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;
+#define glVertexAttrib2f glad_glVertexAttrib2f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v);
+GLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;
+#define glVertexAttrib2fv glad_glVertexAttrib2fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z);
+GLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;
+#define glVertexAttrib3f glad_glVertexAttrib3f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v);
+GLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;
+#define glVertexAttrib3fv glad_glVertexAttrib3fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+GLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;
+#define glVertexAttrib4f glad_glVertexAttrib4f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v);
+GLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;
+#define glVertexAttrib4fv glad_glVertexAttrib4fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
+GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;
+#define glVertexAttribPointer glad_glVertexAttribPointer
+typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI PFNGLVIEWPORTPROC glad_glViewport;
+#define glViewport glad_glViewport
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/common/rendering/gles/glad/include/glad/glad_egl.h b/source/common/rendering/gles/glad/include/glad/glad_egl.h
new file mode 100644
index 000000000..fbc5b8630
--- /dev/null
+++ b/source/common/rendering/gles/glad/include/glad/glad_egl.h
@@ -0,0 +1,267 @@
+/*
+
+    EGL loader generated by glad 0.1.34 on Sat Feb 20 22:54:45 2021.
+
+    Language/Generator: C/C++
+    Specification: egl
+    APIs: egl=1.4
+    Profile: -
+    Extensions:
+        
+    Loader: True
+    Local files: False
+    Omit khrplatform: False
+    Reproducible: False
+
+    Commandline:
+        --api="egl=1.4" --generator="c" --spec="egl" --extensions=""
+    Online:
+        https://glad.dav1d.de/#language=c&specification=egl&loader=on&api=egl%3D1.4
+*/
+
+
+#ifndef __glad_egl_h_
+
+#ifdef __egl_h_
+#error EGL header already included, remove this include, glad already provides it
+#endif
+
+#define __glad_egl_h_
+#define __egl_h_
+
+#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
+#define APIENTRY __stdcall
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+#ifndef APIENTRYP
+#define APIENTRYP APIENTRY *
+#endif
+#ifndef GLAPI
+#define GLAPI extern
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* (* GLADloadproc)(const char *name);
+
+GLAPI int gladLoadEGL(void);
+GLAPI int gladLoadEGLLoader(GLADloadproc);
+
+#include <KHR/khrplatform.h>
+#include <EGL/eglplatform.h>
+
+#define EGL_CAST(X,V) (X)(V)
+
+struct AHardwareBuffer;
+struct wl_buffer;
+struct wl_display;
+struct wl_resource;
+typedef unsigned int EGLBoolean;
+typedef unsigned int EGLenum;
+typedef intptr_t EGLAttribKHR;
+typedef intptr_t EGLAttrib;
+typedef void *EGLClientBuffer;
+typedef void *EGLConfig;
+typedef void *EGLContext;
+typedef void *EGLDeviceEXT;
+typedef void *EGLDisplay;
+typedef void *EGLImage;
+typedef void *EGLImageKHR;
+typedef void *EGLLabelKHR;
+typedef void *EGLObjectKHR;
+typedef void *EGLOutputLayerEXT;
+typedef void *EGLOutputPortEXT;
+typedef void *EGLStreamKHR;
+typedef void *EGLSurface;
+typedef void *EGLSync;
+typedef void *EGLSyncKHR;
+typedef void *EGLSyncNV;
+typedef void (*__eglMustCastToProperFunctionPointerType)(void);
+typedef khronos_utime_nanoseconds_t EGLTimeKHR;
+typedef khronos_utime_nanoseconds_t EGLTime;
+typedef khronos_utime_nanoseconds_t EGLTimeNV;
+typedef khronos_utime_nanoseconds_t EGLuint64NV;
+typedef khronos_uint64_t EGLuint64KHR;
+typedef khronos_stime_nanoseconds_t EGLnsecsANDROID;
+typedef int EGLNativeFileDescriptorKHR;
+typedef khronos_ssize_t EGLsizeiANDROID;
+typedef void (*EGLSetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, const void *value, EGLsizeiANDROID valueSize);
+typedef EGLsizeiANDROID (*EGLGetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, void *value, EGLsizeiANDROID valueSize);
+struct EGLClientPixmapHI {
+    void  *pData;
+    EGLint iWidth;
+    EGLint iHeight;
+    EGLint iStride;
+};
+typedef void (APIENTRY *EGLDEBUGPROCKHR)(EGLenum error,const char *command,EGLint messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char* message);
+#define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC
+#define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC
+#define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC
+#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC
+#define EGL_ALPHA_SIZE 0x3021
+#define EGL_BAD_ACCESS 0x3002
+#define EGL_BAD_ALLOC 0x3003
+#define EGL_BAD_ATTRIBUTE 0x3004
+#define EGL_BAD_CONFIG 0x3005
+#define EGL_BAD_CONTEXT 0x3006
+#define EGL_BAD_CURRENT_SURFACE 0x3007
+#define EGL_BAD_DISPLAY 0x3008
+#define EGL_BAD_MATCH 0x3009
+#define EGL_BAD_NATIVE_PIXMAP 0x300A
+#define EGL_BAD_NATIVE_WINDOW 0x300B
+#define EGL_BAD_PARAMETER 0x300C
+#define EGL_BAD_SURFACE 0x300D
+#define EGL_BLUE_SIZE 0x3022
+#define EGL_BUFFER_SIZE 0x3020
+#define EGL_CONFIG_CAVEAT 0x3027
+#define EGL_CONFIG_ID 0x3028
+#define EGL_CORE_NATIVE_ENGINE 0x305B
+#define EGL_DEPTH_SIZE 0x3025
+#define EGL_DONT_CARE EGL_CAST(EGLint,-1)
+#define EGL_DRAW 0x3059
+#define EGL_EXTENSIONS 0x3055
+#define EGL_FALSE 0
+#define EGL_GREEN_SIZE 0x3023
+#define EGL_HEIGHT 0x3056
+#define EGL_LARGEST_PBUFFER 0x3058
+#define EGL_LEVEL 0x3029
+#define EGL_MAX_PBUFFER_HEIGHT 0x302A
+#define EGL_MAX_PBUFFER_PIXELS 0x302B
+#define EGL_MAX_PBUFFER_WIDTH 0x302C
+#define EGL_NATIVE_RENDERABLE 0x302D
+#define EGL_NATIVE_VISUAL_ID 0x302E
+#define EGL_NATIVE_VISUAL_TYPE 0x302F
+#define EGL_NONE 0x3038
+#define EGL_NON_CONFORMANT_CONFIG 0x3051
+#define EGL_NOT_INITIALIZED 0x3001
+#define EGL_NO_CONTEXT EGL_CAST(EGLContext,0)
+#define EGL_NO_DISPLAY EGL_CAST(EGLDisplay,0)
+#define EGL_NO_SURFACE EGL_CAST(EGLSurface,0)
+#define EGL_PBUFFER_BIT 0x0001
+#define EGL_PIXMAP_BIT 0x0002
+#define EGL_READ 0x305A
+#define EGL_RED_SIZE 0x3024
+#define EGL_SAMPLES 0x3031
+#define EGL_SAMPLE_BUFFERS 0x3032
+#define EGL_SLOW_CONFIG 0x3050
+#define EGL_STENCIL_SIZE 0x3026
+#define EGL_SUCCESS 0x3000
+#define EGL_SURFACE_TYPE 0x3033
+#define EGL_TRANSPARENT_BLUE_VALUE 0x3035
+#define EGL_TRANSPARENT_GREEN_VALUE 0x3036
+#define EGL_TRANSPARENT_RED_VALUE 0x3037
+#define EGL_TRANSPARENT_RGB 0x3052
+#define EGL_TRANSPARENT_TYPE 0x3034
+#define EGL_TRUE 1
+#define EGL_VENDOR 0x3053
+#define EGL_VERSION 0x3054
+#define EGL_WIDTH 0x3057
+#define EGL_WINDOW_BIT 0x0004
+#define EGL_BACK_BUFFER 0x3084
+#define EGL_BIND_TO_TEXTURE_RGB 0x3039
+#define EGL_BIND_TO_TEXTURE_RGBA 0x303A
+#define EGL_CONTEXT_LOST 0x300E
+#define EGL_MIN_SWAP_INTERVAL 0x303B
+#define EGL_MAX_SWAP_INTERVAL 0x303C
+#define EGL_MIPMAP_TEXTURE 0x3082
+#define EGL_MIPMAP_LEVEL 0x3083
+#define EGL_NO_TEXTURE 0x305C
+#define EGL_TEXTURE_2D 0x305F
+#define EGL_TEXTURE_FORMAT 0x3080
+#define EGL_TEXTURE_RGB 0x305D
+#define EGL_TEXTURE_RGBA 0x305E
+#define EGL_TEXTURE_TARGET 0x3081
+#define EGL_ALPHA_FORMAT 0x3088
+#define EGL_ALPHA_FORMAT_NONPRE 0x308B
+#define EGL_ALPHA_FORMAT_PRE 0x308C
+#define EGL_ALPHA_MASK_SIZE 0x303E
+#define EGL_BUFFER_PRESERVED 0x3094
+#define EGL_BUFFER_DESTROYED 0x3095
+#define EGL_CLIENT_APIS 0x308D
+#define EGL_COLORSPACE 0x3087
+#define EGL_COLORSPACE_sRGB 0x3089
+#define EGL_COLORSPACE_LINEAR 0x308A
+#define EGL_COLOR_BUFFER_TYPE 0x303F
+#define EGL_CONTEXT_CLIENT_TYPE 0x3097
+#define EGL_DISPLAY_SCALING 10000
+#define EGL_HORIZONTAL_RESOLUTION 0x3090
+#define EGL_LUMINANCE_BUFFER 0x308F
+#define EGL_LUMINANCE_SIZE 0x303D
+#define EGL_OPENGL_ES_BIT 0x0001
+#define EGL_OPENVG_BIT 0x0002
+#define EGL_OPENGL_ES_API 0x30A0
+#define EGL_OPENVG_API 0x30A1
+#define EGL_OPENVG_IMAGE 0x3096
+#define EGL_PIXEL_ASPECT_RATIO 0x3092
+#define EGL_RENDERABLE_TYPE 0x3040
+#define EGL_RENDER_BUFFER 0x3086
+#define EGL_RGB_BUFFER 0x308E
+#define EGL_SINGLE_BUFFER 0x3085
+#define EGL_SWAP_BEHAVIOR 0x3093
+#define EGL_UNKNOWN EGL_CAST(EGLint,-1)
+#define EGL_VERTICAL_RESOLUTION 0x3091
+#define EGL_CONFORMANT 0x3042
+#define EGL_CONTEXT_CLIENT_VERSION 0x3098
+#define EGL_MATCH_NATIVE_PIXMAP 0x3041
+#define EGL_OPENGL_ES2_BIT 0x0004
+#define EGL_VG_ALPHA_FORMAT 0x3088
+#define EGL_VG_ALPHA_FORMAT_NONPRE 0x308B
+#define EGL_VG_ALPHA_FORMAT_PRE 0x308C
+#define EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040
+#define EGL_VG_COLORSPACE 0x3087
+#define EGL_VG_COLORSPACE_sRGB 0x3089
+#define EGL_VG_COLORSPACE_LINEAR 0x308A
+#define EGL_VG_COLORSPACE_LINEAR_BIT 0x0020
+#define EGL_DEFAULT_DISPLAY EGL_CAST(EGLNativeDisplayType,0)
+#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200
+#define EGL_MULTISAMPLE_RESOLVE 0x3099
+#define EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A
+#define EGL_MULTISAMPLE_RESOLVE_BOX 0x309B
+#define EGL_OPENGL_API 0x30A2
+#define EGL_OPENGL_BIT 0x0008
+#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400
+EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
+EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
+EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list);
+EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx);
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface);
+EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
+EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+EGLDisplay eglGetCurrentDisplay(void);
+EGLSurface eglGetCurrentSurface(EGLint readdraw);
+EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
+EGLint eglGetError(void);
+__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname);
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
+EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value);
+const char *eglQueryString(EGLDisplay dpy, EGLint name);
+EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);
+EGLBoolean eglTerminate(EGLDisplay dpy);
+EGLBoolean eglWaitGL(void);
+EGLBoolean eglWaitNative(EGLint engine);
+EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
+EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval);
+EGLBoolean eglBindAPI(EGLenum api);
+EGLenum eglQueryAPI(void);
+EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
+EGLBoolean eglReleaseThread(void);
+EGLBoolean eglWaitClient(void);
+EGLContext eglGetCurrentContext(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/common/rendering/gles/glad/src/glad.c b/source/common/rendering/gles/glad/src/glad.c
new file mode 100644
index 000000000..9da9f0ed1
--- /dev/null
+++ b/source/common/rendering/gles/glad/src/glad.c
@@ -0,0 +1,475 @@
+/*
+
+    OpenGL ES loader generated by glad 0.1.34 on Fri Mar 19 17:04:24 2021.
+
+    Language/Generator: C/C++
+    Specification: gl
+    APIs: gles2=2.0
+    Profile: compatibility
+    Extensions:
+        
+    Loader: True
+    Local files: False
+    Omit khrplatform: False
+    Reproducible: False
+
+    Commandline:
+        --profile="compatibility" --api="gles2=2.0" --generator="c" --spec="gl" --extensions=""
+    Online:
+        https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gles2%3D2.0
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glad/glad.h>
+
+struct gladGLversionStruct GLVersion = { 0, 0 };
+
+#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
+#define _GLAD_IS_SOME_NEW_VERSION 1
+#endif
+
+static int max_loaded_major;
+static int max_loaded_minor;
+
+static const char *exts = NULL;
+static int num_exts_i = 0;
+static char **exts_i = NULL;
+
+static int get_exts(void) {
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+    if(max_loaded_major < 3) {
+#endif
+        exts = (const char *)glGetString(GL_EXTENSIONS);
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+    } else {
+        unsigned int index;
+
+        num_exts_i = 0;
+        glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i);
+        if (num_exts_i > 0) {
+            exts_i = (char **)malloc((size_t)num_exts_i * (sizeof *exts_i));
+        }
+
+        if (exts_i == NULL) {
+            return 0;
+        }
+
+        for(index = 0; index < (unsigned)num_exts_i; index++) {
+            const char *gl_str_tmp = (const char*)glGetStringi(GL_EXTENSIONS, index);
+            size_t len = strlen(gl_str_tmp);
+
+            char *local_str = (char*)malloc((len+1) * sizeof(char));
+            if(local_str != NULL) {
+                memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char));
+            }
+            exts_i[index] = local_str;
+        }
+    }
+#endif
+    return 1;
+}
+
+static void free_exts(void) {
+    if (exts_i != NULL) {
+        int index;
+        for(index = 0; index < num_exts_i; index++) {
+            free((char *)exts_i[index]);
+        }
+        free((void *)exts_i);
+        exts_i = NULL;
+    }
+}
+
+static int has_ext(const char *ext) {
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+    if(max_loaded_major < 3) {
+#endif
+        const char *extensions;
+        const char *loc;
+        const char *terminator;
+        extensions = exts;
+        if(extensions == NULL || ext == NULL) {
+            return 0;
+        }
+
+        while(1) {
+            loc = strstr(extensions, ext);
+            if(loc == NULL) {
+                return 0;
+            }
+
+            terminator = loc + strlen(ext);
+            if((loc == extensions || *(loc - 1) == ' ') &&
+                (*terminator == ' ' || *terminator == '\0')) {
+                return 1;
+            }
+            extensions = terminator;
+        }
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+    } else {
+        int index;
+        if(exts_i == NULL) return 0;
+        for(index = 0; index < num_exts_i; index++) {
+            const char *e = exts_i[index];
+
+            if(exts_i[index] != NULL && strcmp(e, ext) == 0) {
+                return 1;
+            }
+        }
+    }
+#endif
+
+    return 0;
+}
+int GLAD_GL_ES_VERSION_2_0 = 0;
+PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL;
+PFNGLATTACHSHADERPROC glad_glAttachShader = NULL;
+PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL;
+PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL;
+PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL;
+PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL;
+PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;
+PFNGLBLENDCOLORPROC glad_glBlendColor = NULL;
+PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL;
+PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL;
+PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL;
+PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL;
+PFNGLBUFFERDATAPROC glad_glBufferData = NULL;
+PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL;
+PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL;
+PFNGLCLEARPROC glad_glClear = NULL;
+PFNGLCLEARCOLORPROC glad_glClearColor = NULL;
+PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL;
+PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;
+PFNGLCOLORMASKPROC glad_glColorMask = NULL;
+PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL;
+PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL;
+PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL;
+PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL;
+PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL;
+PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL;
+PFNGLCREATESHADERPROC glad_glCreateShader = NULL;
+PFNGLCULLFACEPROC glad_glCullFace = NULL;
+PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL;
+PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL;
+PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL;
+PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL;
+PFNGLDELETESHADERPROC glad_glDeleteShader = NULL;
+PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL;
+PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL;
+PFNGLDEPTHMASKPROC glad_glDepthMask = NULL;
+PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;
+PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
+PFNGLDISABLEPROC glad_glDisable = NULL;
+PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL;
+PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL;
+PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL;
+PFNGLENABLEPROC glad_glEnable = NULL;
+PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL;
+PFNGLFINISHPROC glad_glFinish = NULL;
+PFNGLFLUSHPROC glad_glFlush = NULL;
+PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL;
+PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL;
+PFNGLFRONTFACEPROC glad_glFrontFace = NULL;
+PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL;
+PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL;
+PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL;
+PFNGLGENTEXTURESPROC glad_glGenTextures = NULL;
+PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;
+PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL;
+PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL;
+PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL;
+PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL;
+PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL;
+PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL;
+PFNGLGETERRORPROC glad_glGetError = NULL;
+PFNGLGETFLOATVPROC glad_glGetFloatv = NULL;
+PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL;
+PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL;
+PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL;
+PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;
+PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL;
+PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL;
+PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL;
+PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL;
+PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL;
+PFNGLGETSTRINGPROC glad_glGetString = NULL;
+PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL;
+PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL;
+PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL;
+PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL;
+PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL;
+PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL;
+PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL;
+PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL;
+PFNGLHINTPROC glad_glHint = NULL;
+PFNGLISBUFFERPROC glad_glIsBuffer = NULL;
+PFNGLISENABLEDPROC glad_glIsEnabled = NULL;
+PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL;
+PFNGLISPROGRAMPROC glad_glIsProgram = NULL;
+PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL;
+PFNGLISSHADERPROC glad_glIsShader = NULL;
+PFNGLISTEXTUREPROC glad_glIsTexture = NULL;
+PFNGLLINEWIDTHPROC glad_glLineWidth = NULL;
+PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL;
+PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL;
+PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;
+PFNGLREADPIXELSPROC glad_glReadPixels = NULL;
+PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL;
+PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL;
+PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL;
+PFNGLSCISSORPROC glad_glScissor = NULL;
+PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL;
+PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;
+PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;
+PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;
+PFNGLSTENCILMASKPROC glad_glStencilMask = NULL;
+PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL;
+PFNGLSTENCILOPPROC glad_glStencilOp = NULL;
+PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL;
+PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL;
+PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL;
+PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL;
+PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL;
+PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL;
+PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL;
+PFNGLUNIFORM1FPROC glad_glUniform1f = NULL;
+PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL;
+PFNGLUNIFORM1IPROC glad_glUniform1i = NULL;
+PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL;
+PFNGLUNIFORM2FPROC glad_glUniform2f = NULL;
+PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL;
+PFNGLUNIFORM2IPROC glad_glUniform2i = NULL;
+PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL;
+PFNGLUNIFORM3FPROC glad_glUniform3f = NULL;
+PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL;
+PFNGLUNIFORM3IPROC glad_glUniform3i = NULL;
+PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL;
+PFNGLUNIFORM4FPROC glad_glUniform4f = NULL;
+PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL;
+PFNGLUNIFORM4IPROC glad_glUniform4i = NULL;
+PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL;
+PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL;
+PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL;
+PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL;
+PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL;
+PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL;
+PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL;
+PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL;
+PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL;
+PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL;
+PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL;
+PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL;
+PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL;
+PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL;
+PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL;
+PFNGLVIEWPORTPROC glad_glViewport = NULL;
+static void load_GL_ES_VERSION_2_0(GLADloadproc load) {
+	if(!GLAD_GL_ES_VERSION_2_0) return;
+	glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture");
+	glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader");
+	glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation");
+	glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer");
+	glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer");
+	glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer");
+	glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture");
+	glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor");
+	glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation");
+	glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate");
+	glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc");
+	glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate");
+	glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData");
+	glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData");
+	glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus");
+	glad_glClear = (PFNGLCLEARPROC)load("glClear");
+	glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor");
+	glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC)load("glClearDepthf");
+	glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil");
+	glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask");
+	glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader");
+	glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D");
+	glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D");
+	glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D");
+	glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D");
+	glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram");
+	glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader");
+	glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
+	glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers");
+	glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers");
+	glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram");
+	glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers");
+	glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader");
+	glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures");
+	glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc");
+	glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask");
+	glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC)load("glDepthRangef");
+	glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader");
+	glad_glDisable = (PFNGLDISABLEPROC)load("glDisable");
+	glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray");
+	glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays");
+	glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements");
+	glad_glEnable = (PFNGLENABLEPROC)load("glEnable");
+	glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray");
+	glad_glFinish = (PFNGLFINISHPROC)load("glFinish");
+	glad_glFlush = (PFNGLFLUSHPROC)load("glFlush");
+	glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer");
+	glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D");
+	glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace");
+	glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers");
+	glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap");
+	glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers");
+	glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers");
+	glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures");
+	glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib");
+	glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform");
+	glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders");
+	glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation");
+	glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv");
+	glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv");
+	glad_glGetError = (PFNGLGETERRORPROC)load("glGetError");
+	glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv");
+	glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv");
+	glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv");
+	glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv");
+	glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog");
+	glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv");
+	glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv");
+	glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog");
+	glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)load("glGetShaderPrecisionFormat");
+	glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource");
+	glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+	glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv");
+	glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv");
+	glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv");
+	glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv");
+	glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation");
+	glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv");
+	glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv");
+	glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv");
+	glad_glHint = (PFNGLHINTPROC)load("glHint");
+	glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer");
+	glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled");
+	glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer");
+	glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram");
+	glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer");
+	glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader");
+	glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture");
+	glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth");
+	glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram");
+	glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei");
+	glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset");
+	glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels");
+	glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)load("glReleaseShaderCompiler");
+	glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage");
+	glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage");
+	glad_glScissor = (PFNGLSCISSORPROC)load("glScissor");
+	glad_glShaderBinary = (PFNGLSHADERBINARYPROC)load("glShaderBinary");
+	glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource");
+	glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc");
+	glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate");
+	glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask");
+	glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate");
+	glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp");
+	glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate");
+	glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D");
+	glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf");
+	glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv");
+	glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri");
+	glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv");
+	glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D");
+	glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f");
+	glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv");
+	glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i");
+	glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv");
+	glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f");
+	glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv");
+	glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i");
+	glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv");
+	glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f");
+	glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv");
+	glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i");
+	glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv");
+	glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f");
+	glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv");
+	glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i");
+	glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv");
+	glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv");
+	glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv");
+	glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv");
+	glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram");
+	glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram");
+	glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f");
+	glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv");
+	glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f");
+	glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv");
+	glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f");
+	glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv");
+	glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f");
+	glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv");
+	glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer");
+	glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport");
+}
+static int find_extensionsGLES2(void) {
+	if (!get_exts()) return 0;
+	(void)&has_ext;
+	free_exts();
+	return 1;
+}
+
+static void find_coreGLES2(void) {
+
+    /* Thank you @elmindreda
+     * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176
+     * https://github.com/glfw/glfw/blob/master/src/context.c#L36
+     */
+    int i, major, minor;
+
+    const char* version;
+    const char* prefixes[] = {
+        "OpenGL ES-CM ",
+        "OpenGL ES-CL ",
+        "OpenGL ES ",
+        NULL
+    };
+
+    version = (const char*) glGetString(GL_VERSION);
+    if (!version) return;
+
+    for (i = 0;  prefixes[i];  i++) {
+        const size_t length = strlen(prefixes[i]);
+        if (strncmp(version, prefixes[i], length) == 0) {
+            version += length;
+            break;
+        }
+    }
+
+/* PR #18 */
+#ifdef _MSC_VER
+    sscanf_s(version, "%d.%d", &major, &minor);
+#else
+    sscanf(version, "%d.%d", &major, &minor);
+#endif
+
+    GLVersion.major = major; GLVersion.minor = minor;
+    max_loaded_major = major; max_loaded_minor = minor;
+	GLAD_GL_ES_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2;
+	if (GLVersion.major > 2 || (GLVersion.major >= 2 && GLVersion.minor >= 0)) {
+		max_loaded_major = 2;
+		max_loaded_minor = 0;
+	}
+}
+
+int gladLoadGLES2Loader(GLADloadproc load) {
+	GLVersion.major = 0; GLVersion.minor = 0;
+	glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+	if(glGetString == NULL) return 0;
+	if(glGetString(GL_VERSION) == NULL) return 0;
+	find_coreGLES2();
+	load_GL_ES_VERSION_2_0(load);
+
+	if (!find_extensionsGLES2()) return 0;
+	return GLVersion.major != 0 || GLVersion.minor != 0;
+}
+
diff --git a/source/common/rendering/gles/glad/src/glad_egl.c b/source/common/rendering/gles/glad/src/glad_egl.c
new file mode 100644
index 000000000..3c939401c
--- /dev/null
+++ b/source/common/rendering/gles/glad/src/glad_egl.c
@@ -0,0 +1,45 @@
+/*
+
+    EGL loader generated by glad 0.1.34 on Sat Feb 20 22:54:45 2021.
+
+    Language/Generator: C/C++
+    Specification: egl
+    APIs: egl=1.4
+    Profile: -
+    Extensions:
+        
+    Loader: True
+    Local files: False
+    Omit khrplatform: False
+    Reproducible: False
+
+    Commandline:
+        --api="egl=1.4" --generator="c" --spec="egl" --extensions=""
+    Online:
+        https://glad.dav1d.de/#language=c&specification=egl&loader=on&api=egl%3D1.4
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glad/glad_egl.h>
+
+int gladLoadEGL(void) {
+    return gladLoadEGLLoader((GLADloadproc)eglGetProcAddress);
+}
+
+static int find_extensionsEGL(void) {
+	return 1;
+}
+
+static void find_coreEGL(void) {
+}
+
+int gladLoadEGLLoader(GLADloadproc load) {
+	(void) load;
+	find_coreEGL();
+
+	if (!find_extensionsEGL()) return 0;
+	return 1;
+}
+
diff --git a/source/common/rendering/gles/gles_buffers.cpp b/source/common/rendering/gles/gles_buffers.cpp
new file mode 100644
index 000000000..61296f362
--- /dev/null
+++ b/source/common/rendering/gles/gles_buffers.cpp
@@ -0,0 +1,307 @@
+/*
+** gl_buffers.cpp
+** Low level vertex buffer class
+**
+**---------------------------------------------------------------------------
+** Copyright 2018-2020 Christoph Oelckers
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+**/
+
+#include <algorithm>
+#include "gles_system.h"
+#include "gles_buffers.h"
+#include "gles_renderstate.h"
+#include "v_video.h"
+#include "flatvertices.h"
+
+namespace OpenGLESRenderer
+{
+
+//==========================================================================
+//
+// basic buffer implementation
+//
+//==========================================================================
+
+static inline void InvalidateBufferState()
+{
+	gl_RenderState.ResetVertexBuffer();	// force rebinding of buffers on next Apply call.
+}
+
+GLBuffer::GLBuffer(int usetype)
+	: mUseType(usetype)
+{
+	if ((usetype == GL_ARRAY_BUFFER) || (usetype == GL_ELEMENT_ARRAY_BUFFER))
+	{
+		glGenBuffers(1, &mBufferId);
+		isData = false;
+	}
+	else
+	{
+		isData = true;
+	}
+}
+
+GLBuffer::~GLBuffer()
+{
+	if (mBufferId != 0)
+	{
+		if (gles.useMappedBuffers)
+		{
+			glBindBuffer(mUseType, mBufferId);
+			glUnmapBuffer(mUseType);
+		}
+		glBindBuffer(mUseType, 0);
+		glDeleteBuffers(1, &mBufferId);
+	}
+	
+	if (memory)
+		delete[] memory;
+}
+
+void GLBuffer::Bind()
+{
+	if (!isData)
+	{
+		glBindBuffer(mUseType, mBufferId);
+	}
+}
+
+
+void GLBuffer::SetData(size_t size, const void* data, bool staticdata)
+{
+	if (isData || !gles.useMappedBuffers)
+	{
+		if (memory)
+			delete[] memory;
+
+		memory = (char*)(new uint64_t[size / 8 + 16]);
+
+		if (data)
+			memcpy(memory, data, size);
+	}
+
+	if (!isData)
+	{
+		Bind();
+		glBufferData(mUseType, size, data, staticdata ? GL_STATIC_DRAW : GL_STREAM_DRAW);
+	}
+
+	if (!isData && gles.useMappedBuffers)
+	{
+		map = 0;
+	}
+	else
+	{
+		map = memory;
+	}
+
+	buffersize = size;
+	InvalidateBufferState();
+}
+
+void GLBuffer::SetSubData(size_t offset, size_t size, const void *data)
+{
+	Bind();
+	
+	memcpy(memory + offset, data, size);
+	
+	if (!isData)
+	{
+		glBufferSubData(mUseType, offset, size, data);
+	}
+}
+
+void GLBuffer::Upload(size_t start, size_t size)
+{
+	if (!gles.useMappedBuffers)
+	{
+		Bind();
+		
+		if(size)
+			glBufferSubData(mUseType, start, size, memory + start);
+	}
+}
+
+void GLBuffer::Map()
+{
+	if (!isData && gles.useMappedBuffers)
+	{
+		Bind();
+		map = (FFlatVertex*)glMapBufferRange(mUseType, 0, buffersize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
+	}
+	else
+	{
+		map = memory;
+	}
+	InvalidateBufferState();
+}
+
+void GLBuffer::Unmap()
+{
+	if (!isData && gles.useMappedBuffers)
+	{
+		Bind();
+		glUnmapBuffer(mUseType);
+		InvalidateBufferState();
+	}
+}
+
+void *GLBuffer::Lock(unsigned int size)
+{
+	// This initializes this buffer as a static object with no data.
+	SetData(size, nullptr, true);
+	if (!isData && gles.useMappedBuffers)
+	{
+		return glMapBufferRange(mUseType, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
+	}
+	else
+	{
+		return map;
+	}
+}
+
+void GLBuffer::Unlock()
+{
+	if (!isData)
+	{
+		if (gles.useMappedBuffers)
+		{
+			Bind();
+			glUnmapBuffer(mUseType);
+			InvalidateBufferState();
+		}
+		else
+		{
+			Bind();
+			glBufferData(mUseType, buffersize, map, GL_STATIC_DRAW);
+			InvalidateBufferState();
+		}
+	}
+}
+
+void GLBuffer::Resize(size_t newsize)
+{
+	assert(!nomap);	// only mappable buffers can be resized. 
+	if (newsize > buffersize && !nomap)
+	{
+		/*
+		// reallocate the buffer with twice the size
+		unsigned int oldbuffer = mBufferId;
+
+		// first unmap the old buffer
+		Bind();
+		glUnmapBuffer(mUseType);
+
+		glGenBuffers(1, &mBufferId);
+		SetData(newsize, nullptr, false);
+		glBindBuffer(GL_COPY_READ_BUFFER, oldbuffer);
+
+		// copy contents and delete the old buffer.
+		glCopyBufferSubData(GL_COPY_READ_BUFFER, mUseType, 0, 0, buffersize);
+		glBindBuffer(GL_COPY_READ_BUFFER, 0);
+		glDeleteBuffers(1, &oldbuffer);
+		buffersize = newsize;
+		InvalidateBufferState();
+		*/
+	}
+}
+
+
+//===========================================================================
+//
+// Vertex buffer implementation
+//
+//===========================================================================
+
+void GLVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
+{
+	static int VFmtToGLFmt[] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_UNSIGNED_BYTE, GL_FLOAT }; // TODO Fix last entry GL_INT_2_10_10_10_REV, normals for models will be broken
+	static uint8_t VFmtToSize[] = {4, 3, 2, 1, 4, 4};
+	
+	mStride = stride;
+	mNumBindingPoints = numBindingPoints;
+	
+	for(int i = 0; i < numAttributes; i++)
+	{
+		if (attrs[i].location >= 0 && attrs[i].location < VATTR_MAX)
+		{
+			auto & attrinf = mAttributeInfo[attrs[i].location];
+			attrinf.format = VFmtToGLFmt[attrs[i].format];
+			attrinf.size = VFmtToSize[attrs[i].format];
+			attrinf.offset = attrs[i].offset;
+			attrinf.bindingpoint = attrs[i].binding;
+		}
+	}
+}
+
+void GLVertexBuffer::Bind(int *offsets)
+{
+	int i = 0;
+
+	// This is what gets called from RenderState.Apply. It shouldn't be called anywhere else if the render state is in use
+	GLBuffer::Bind();
+	for(auto &attrinf : mAttributeInfo)
+	{
+		if (attrinf.size == 0)
+		{
+			glDisableVertexAttribArray(i);
+		}
+		else
+		{
+			glEnableVertexAttribArray(i);
+			size_t ofs = offsets == nullptr ? attrinf.offset : attrinf.offset + mStride * offsets[attrinf.bindingpoint];
+			glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.format != GL_FLOAT, (GLsizei)mStride, (void*)(intptr_t)ofs);
+		}
+		i++;
+	}
+}
+
+void GLDataBuffer::BindRange(FRenderState *state, size_t start, size_t length)
+{
+	if (mBindingPoint == 3)// VIEWPOINT_BINDINGPOINT
+	{
+		static_cast<FGLRenderState*>(state)->ApplyViewport(memory + start);
+	}
+	else
+	{
+		//glBindBufferRange(mUseType, mBindingPoint, mBufferId, start, length);
+	} 
+}
+
+void GLDataBuffer::BindBase()
+{
+	//glBindBufferBase(mUseType, mBindingPoint, mBufferId);
+}
+
+
+GLVertexBuffer::GLVertexBuffer() : GLBuffer(GL_ARRAY_BUFFER) {}
+GLIndexBuffer::GLIndexBuffer() : GLBuffer(GL_ELEMENT_ARRAY_BUFFER) {}
+GLDataBuffer::GLDataBuffer(int bindingpoint, bool is_ssbo) : GLBuffer(0), mBindingPoint(bindingpoint) {}
+
+}
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_buffers.h b/source/common/rendering/gles/gles_buffers.h
new file mode 100644
index 000000000..de51ff31b
--- /dev/null
+++ b/source/common/rendering/gles/gles_buffers.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "buffers.h"
+
+#ifdef _MSC_VER
+// silence bogus warning C4250: 'GLVertexBuffer': inherits 'GLBuffer::GLBuffer::SetData' via dominance
+// According to internet infos, the warning is erroneously emitted in this case.
+#pragma warning(disable:4250) 
+#endif
+
+namespace OpenGLESRenderer
+{
+
+class GLBuffer : virtual public IBuffer
+{
+protected:
+	const int mUseType;
+	unsigned int mBufferId = 0;
+	int mAllocationSize = 0;
+	bool mPersistent = false;
+	bool nomap = true;
+
+	bool isData = false;
+	char *memory = nullptr;
+
+	GLBuffer(int usetype);
+	~GLBuffer();
+	void SetData(size_t size, const void *data, bool staticdata) override;
+	void SetSubData(size_t offset, size_t size, const void *data) override;
+	void Map() override;
+	void Unmap() override;
+	void Resize(size_t newsize) override;
+	void *Lock(unsigned int size) override;
+	void Unlock() override;
+public:
+	void Bind();
+	void Upload(size_t start, size_t end);
+};
+
+
+class GLVertexBuffer : public IVertexBuffer, public GLBuffer
+{
+	// If this could use the modern (since GL 4.3) binding system, things would be simpler... :(
+	struct GLVertexBufferAttribute
+	{
+		int bindingpoint;
+		int format;
+		int size;
+		int offset;
+	};
+
+	int mNumBindingPoints;
+	GLVertexBufferAttribute mAttributeInfo[VATTR_MAX] = {};	// Thanks to OpenGL's state system this needs to contain info about every attribute that may ever be in use throughout the entire renderer.
+	size_t mStride = 0;
+
+public:
+	GLVertexBuffer();
+	void SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs) override;
+	void Bind(int *offsets);
+};
+
+class GLIndexBuffer : public IIndexBuffer, public GLBuffer
+{
+public:
+
+	GLIndexBuffer();
+};
+
+class GLDataBuffer : public IDataBuffer, public GLBuffer
+{
+	int mBindingPoint;
+public:
+	GLDataBuffer(int bindingpoint, bool is_ssbo);
+	void BindRange(FRenderState* state, size_t start, size_t length);
+	void BindBase();
+};
+
+}
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_framebuffer.cpp b/source/common/rendering/gles/gles_framebuffer.cpp
new file mode 100644
index 000000000..658a8aebf
--- /dev/null
+++ b/source/common/rendering/gles/gles_framebuffer.cpp
@@ -0,0 +1,494 @@
+/*
+** gl_framebuffer.cpp
+** Implementation of the non-hardware specific parts of the
+** OpenGL frame buffer
+**
+**---------------------------------------------------------------------------
+** Copyright 2010-2020 Christoph Oelckers
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/ 
+
+#include "gles_system.h"
+#include "v_video.h"
+#include "m_png.h"
+#include "templates.h"
+#include "i_time.h"
+
+#include "gles_framebuffer.h"
+#include "gles_renderer.h"
+#include "gles_samplers.h"
+#include "gles_renderbuffers.h"
+#include "hw_clock.h"
+#include "hw_vrmodes.h"
+#include "hw_skydome.h"
+#include "hw_viewpointbuffer.h"
+#include "hw_lightbuffer.h"
+#include "gles_shaderprogram.h"
+#include "r_videoscale.h"
+#include "gles_buffers.h"
+#include "gles_postprocessstate.h"
+#include "v_draw.h"
+#include "printf.h"
+#include "gles_hwtexture.h"
+
+#include "flatvertices.h"
+#include "hw_cvars.h"
+
+EXTERN_CVAR (Bool, vid_vsync)
+EXTERN_CVAR(Bool, r_drawvoxels)
+EXTERN_CVAR(Int, gl_tonemap)
+EXTERN_CVAR(Bool, cl_capfps)
+EXTERN_CVAR(Int, gl_pipeline_depth);
+
+EXTERN_CVAR(Bool, gl_sort_textures);
+
+void Draw2D(F2DDrawer *drawer, FRenderState &state);
+
+extern bool vid_hdr_active;
+
+namespace OpenGLESRenderer
+{
+	FGLRenderer *GLRenderer;
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, bool fullscreen) : 
+	Super(hMonitor, fullscreen) 
+{
+	// SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver.
+	// If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed!
+	Super::SetVSync(vid_vsync);
+	FHardwareTexture::InitGlobalState();
+
+	// Make sure all global variables tracking OpenGL context state are reset..
+	gl_RenderState.Reset();
+
+	GLRenderer = nullptr;
+}
+
+OpenGLFrameBuffer::~OpenGLFrameBuffer()
+{
+	PPResource::ResetAll();
+
+	if (mVertexData != nullptr) delete mVertexData;
+	if (mSkyData != nullptr) delete mSkyData;
+	if (mViewpoints != nullptr) delete mViewpoints;
+	if (mLights != nullptr) delete mLights;
+	mShadowMap.Reset();
+
+	if (GLRenderer)
+	{
+		delete GLRenderer;
+		GLRenderer = nullptr;
+	}
+}
+
+//==========================================================================
+//
+// Initializes the GL renderer
+//
+//==========================================================================
+
+void OpenGLFrameBuffer::InitializeState()
+{
+	static bool first=true;
+
+	mPipelineNbr = gl_pipeline_depth == 0? std::min(4, HW_MAX_PIPELINE_BUFFERS) : clamp(*gl_pipeline_depth, 1, HW_MAX_PIPELINE_BUFFERS);
+	mPipelineType = 1;
+
+	InitGLES();
+
+	// Move some state to the framebuffer object for easier access.
+	hwcaps = gles.flags;
+	vendorstring = gles.vendorstring;
+
+	glDepthFunc(GL_LESS);
+
+	glEnable(GL_DITHER);
+	glDisable(GL_CULL_FACE);
+	glDisable(GL_POLYGON_OFFSET_FILL);
+	
+	glEnable(GL_BLEND);
+
+	glDisable(GL_DEPTH_TEST);
+
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+	glClearDepthf(1.0f);
+
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	SetViewportRects(nullptr);
+
+	mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight(), mPipelineNbr);
+	mSkyData = new FSkyVertexBuffer;
+	mViewpoints = new HWViewpointBuffer(mPipelineNbr);
+	mLights = new FLightBuffer(mPipelineNbr);
+	GLRenderer = new FGLRenderer(this);
+	GLRenderer->Initialize(GetWidth(), GetHeight());
+	static_cast<GLDataBuffer*>(mLights->GetBuffer())->BindBase();
+}
+
+//==========================================================================
+//
+// Updates the screen
+//
+//==========================================================================
+
+void OpenGLFrameBuffer::Update()
+{
+	twoD.Reset();
+	Flush3D.Reset();
+
+	Flush3D.Clock();
+	GLRenderer->Flush();
+	Flush3D.Unclock();
+
+	Swap();
+	Super::Update();
+}
+
+void OpenGLFrameBuffer::CopyScreenToBuffer(int width, int height, uint8_t* scr)
+{
+	IntRect bounds;
+	bounds.left = 0;
+	bounds.top = 0;
+	bounds.width = width;
+	bounds.height = height;
+	GLRenderer->CopyToBackbuffer(&bounds, false);
+
+	// strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers
+	glFinish();
+	glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, scr);
+}
+
+//===========================================================================
+//
+// Camera texture rendering
+//
+//===========================================================================
+
+void OpenGLFrameBuffer::RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect &)> renderFunc)
+{
+	GLRenderer->StartOffscreen();
+	GLRenderer->BindToFrameBuffer(tex);
+
+	IntRect bounds;
+	bounds.left = bounds.top = 0;
+	bounds.width = FHardwareTexture::GetTexDimension(tex->GetWidth());
+	bounds.height = FHardwareTexture::GetTexDimension(tex->GetHeight());
+
+	renderFunc(bounds);
+	GLRenderer->EndOffscreen();
+
+	tex->SetUpdated(true);
+	static_cast<OpenGLFrameBuffer*>(screen)->camtexcount++;
+}
+
+//===========================================================================
+//
+// 
+//
+//===========================================================================
+
+const char* OpenGLFrameBuffer::DeviceName() const 
+{
+	return gles.modelstring;
+}
+
+//==========================================================================
+//
+// Swap the buffers
+//
+//==========================================================================
+
+
+void OpenGLFrameBuffer::Swap()
+{
+	
+	Finish.Reset();
+	Finish.Clock();
+
+
+	FPSLimit();
+	SwapBuffers();
+	
+	mVertexData->NextPipelineBuffer();
+
+	RenderState()->SetVertexBuffer(screen->mVertexData); // Needed for Raze because it does not reset it
+
+	Finish.Unclock();
+	camtexcount = 0;
+	FHardwareTexture::UnbindAll();
+	gl_RenderState.ClearLastMaterial();
+}
+
+//==========================================================================
+//
+// Enable/disable vertical sync
+//
+//==========================================================================
+
+void OpenGLFrameBuffer::SetVSync(bool vsync)
+{
+	Super::SetVSync(vsync);
+}
+
+//===========================================================================
+//
+//
+//===========================================================================
+
+void OpenGLFrameBuffer::SetTextureFilterMode()
+{
+	if (GLRenderer != nullptr && GLRenderer->mSamplerManager != nullptr) GLRenderer->mSamplerManager->SetTextureFilterMode();
+}
+
+IHardwareTexture *OpenGLFrameBuffer::CreateHardwareTexture(int numchannels) 
+{ 
+	return new FHardwareTexture(numchannels);
+}
+
+void OpenGLFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation)
+{
+	if (mat->Source()->GetUseType() == ETextureType::SWCanvas) return;
+
+	int flags = mat->GetScaleFlags();
+	int numLayers = mat->NumLayers();
+	MaterialLayerInfo* layer;
+	auto base = static_cast<FHardwareTexture*>(mat->GetLayer(0, translation, &layer));
+
+	if (base->BindOrCreate(layer->layerTexture, 0, CLAMP_NONE, translation, layer->scaleFlags))
+	{
+		for (int i = 1; i < numLayers; i++)
+		{
+			auto systex = static_cast<FHardwareTexture*>(mat->GetLayer(i, 0, &layer));
+			systex->BindOrCreate(layer->layerTexture, i, CLAMP_NONE, 0, layer->scaleFlags);
+		}
+	}
+	// unbind everything. 
+	FHardwareTexture::UnbindAll();
+	gl_RenderState.ClearLastMaterial();
+}
+
+IVertexBuffer *OpenGLFrameBuffer::CreateVertexBuffer()
+{ 
+	return new GLVertexBuffer; 
+}
+
+IIndexBuffer *OpenGLFrameBuffer::CreateIndexBuffer()
+{ 
+	return new GLIndexBuffer; 
+}
+
+IDataBuffer *OpenGLFrameBuffer::CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize)
+{
+	return new GLDataBuffer(bindingpoint, ssbo);
+}
+
+
+void OpenGLFrameBuffer::SetViewportRects(IntRect *bounds)
+{
+	Super::SetViewportRects(bounds);
+	if (!bounds)
+	{
+		auto vrmode = VRMode::GetVRMode(true);
+		vrmode->AdjustViewport(this);
+	}
+}
+
+
+FRenderState* OpenGLFrameBuffer::RenderState()
+{
+	return &gl_RenderState;
+}
+
+
+void OpenGLFrameBuffer::FirstEye()
+{
+	//GLRenderer->mBuffers->CurrentEye() = 0;  // always begin at zero, in case eye count changed
+}
+
+void OpenGLFrameBuffer::NextEye(int eyecount)
+{
+	//GLRenderer->mBuffers->NextEye(eyecount);
+}
+
+void OpenGLFrameBuffer::SetSceneRenderTarget(bool useSSAO)
+{
+#ifndef NO_RENDER_BUFFER
+	GLRenderer->mBuffers->BindSceneFB(useSSAO);
+#endif
+}
+
+
+
+void OpenGLFrameBuffer::WaitForCommands(bool finish)
+{
+	glFinish();
+}
+
+
+//===========================================================================
+//
+// 
+//
+//===========================================================================
+
+void OpenGLFrameBuffer::BeginFrame()
+{
+	SetViewportRects(nullptr);
+	if (GLRenderer != nullptr)
+		GLRenderer->BeginFrame();
+}
+
+//===========================================================================
+// 
+//	Takes a screenshot
+//
+//===========================================================================
+
+TArray<uint8_t> OpenGLFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma)
+{
+	const auto &viewport = mOutputLetterbox;
+
+	// Grab what is in the back buffer.
+	// We cannot rely on SCREENWIDTH/HEIGHT here because the output may have been scaled.
+	TArray<uint8_t> pixels;
+	pixels.Resize(viewport.width * viewport.height * 3);
+	glPixelStorei(GL_PACK_ALIGNMENT, 1);
+	glReadPixels(viewport.left, viewport.top, viewport.width, viewport.height, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]);
+	glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+	// Copy to screenshot buffer:
+	int w = SCREENWIDTH;
+	int h = SCREENHEIGHT;
+
+	TArray<uint8_t> ScreenshotBuffer(w * h * 3, true);
+
+	float rcpWidth = 1.0f / w;
+	float rcpHeight = 1.0f / h;
+	for (int y = 0; y < h; y++)
+	{
+		for (int x = 0; x < w; x++)
+		{
+			float u = (x + 0.5f) * rcpWidth;
+			float v = (y + 0.5f) * rcpHeight;
+			int sx = u * viewport.width;
+			int sy = v * viewport.height;
+			int sindex = (sx + sy * viewport.width) * 3;
+			int dindex = (x + (h - y - 1) * w) * 3;
+			ScreenshotBuffer[dindex] = pixels[sindex];
+			ScreenshotBuffer[dindex + 1] = pixels[sindex + 1];
+			ScreenshotBuffer[dindex + 2] = pixels[sindex + 2];
+		}
+	}
+
+	pitch = w * 3;
+	color_type = SS_RGB;
+
+	// Screenshot should not use gamma correction if it was already applied to rendered image
+	gamma = 1;
+	if (vid_hdr_active && vid_fullscreen)
+		gamma *= 2.2f;
+	return ScreenshotBuffer;
+}
+
+//===========================================================================
+// 
+// 2D drawing
+//
+//===========================================================================
+
+void OpenGLFrameBuffer::Draw2D()
+{
+	if (GLRenderer != nullptr)
+	{
+		GLRenderer->mBuffers->BindCurrentFB();
+		::Draw2D(twod, gl_RenderState);
+	}
+}
+
+void OpenGLFrameBuffer::PostProcessScene(bool swscene, int fixedcm, float flash, const std::function<void()> &afterBloomDrawEndScene2D)
+{
+	//if (!swscene) GLRenderer->mBuffers->BlitSceneToTexture(); // Copy the resulting scene to the current post process texture
+	GLRenderer->PostProcessScene(fixedcm, flash, afterBloomDrawEndScene2D);
+}
+
+//==========================================================================
+//
+// OpenGLFrameBuffer :: WipeStartScreen
+//
+// Called before the current screen has started rendering. This needs to
+// save what was drawn the previous frame so that it can be animated into
+// what gets drawn this frame.
+//
+//==========================================================================
+
+FTexture *OpenGLFrameBuffer::WipeStartScreen()
+{
+	const auto &viewport = screen->mScreenViewport;
+
+	auto tex = new FWrapperTexture(viewport.width, viewport.height, 1);
+	tex->GetSystemTexture()->CreateTexture(nullptr, viewport.width, viewport.height, 0, false, "WipeStartScreen");
+	glFinish();
+	static_cast<FHardwareTexture*>(tex->GetSystemTexture())->Bind(0, false);
+
+	GLRenderer->mBuffers->BindCurrentFB();
+	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewport.left, viewport.top, viewport.width, viewport.height);
+	return tex;
+}
+
+//==========================================================================
+//
+// OpenGLFrameBuffer :: WipeEndScreen
+//
+// The screen we want to animate to has just been drawn.
+//
+//==========================================================================
+
+FTexture *OpenGLFrameBuffer::WipeEndScreen()
+{
+	GLRenderer->Flush();
+	const auto &viewport = screen->mScreenViewport;
+	auto tex = new FWrapperTexture(viewport.width, viewport.height, 1);
+	tex->GetSystemTexture()->CreateTexture(NULL, viewport.width, viewport.height, 0, false, "WipeEndScreen");
+	glFinish();
+	static_cast<FHardwareTexture*>(tex->GetSystemTexture())->Bind(0, false);
+	GLRenderer->mBuffers->BindCurrentFB();
+	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewport.left, viewport.top, viewport.width, viewport.height);
+	return tex;
+}
+
+}
diff --git a/source/common/rendering/gles/gles_framebuffer.h b/source/common/rendering/gles/gles_framebuffer.h
new file mode 100644
index 000000000..25626c659
--- /dev/null
+++ b/source/common/rendering/gles/gles_framebuffer.h
@@ -0,0 +1,73 @@
+#ifndef __GLES_FRAMEBUFFER
+#define __GLES_FRAMEBUFFER
+
+#include "gl_sysfb.h"
+#include "m_png.h"
+
+#include <memory>
+
+namespace OpenGLESRenderer
+{
+
+class FHardwareTexture;
+class FGLDebug;
+
+class OpenGLFrameBuffer : public SystemGLFrameBuffer
+{
+	typedef SystemGLFrameBuffer Super;
+
+	void RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect &)> renderFunc) override;
+
+public:
+
+	explicit OpenGLFrameBuffer() {}
+	OpenGLFrameBuffer(void *hMonitor, bool fullscreen) ;
+	~OpenGLFrameBuffer();
+
+	void InitializeState() override;
+	void Update() override;
+
+	void FirstEye() override;
+	void NextEye(int eyecount) override;
+	void SetSceneRenderTarget(bool useSSAO) override;
+	void WaitForCommands(bool finish) override;
+	void CopyScreenToBuffer(int width, int height, uint8_t* buffer) override;
+	bool FlipSavePic() const override { return true; }
+
+	FRenderState* RenderState() override;
+
+	const char* DeviceName() const override;
+	void SetTextureFilterMode() override;
+	IHardwareTexture *CreateHardwareTexture(int numchannels) override;
+	void PrecacheMaterial(FMaterial *mat, int translation) override;
+	void BeginFrame() override;
+	void SetViewportRects(IntRect *bounds) override;
+	IVertexBuffer *CreateVertexBuffer() override;
+	IIndexBuffer *CreateIndexBuffer() override;
+	IDataBuffer *CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize) override;
+
+	// Retrieves a buffer containing image data for a screenshot.
+	// Hint: Pitch can be negative for upside-down images, in which case buffer
+	// points to the last row in the buffer, which will be the first row output.
+	virtual TArray<uint8_t> GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma) override;
+
+	void Swap();
+	bool IsHWGammaActive() const { return HWGammaActive; }
+
+	void SetVSync(bool vsync) override;
+
+	void Draw2D() override;
+	void PostProcessScene(bool swscene, int fixedcm, float flash, const std::function<void()> &afterBloomDrawEndScene2D) override;
+
+	bool HWGammaActive = false;			// Are we using hardware or software gamma?
+	std::shared_ptr<FGLDebug> mDebug;	// Debug API
+    
+    FTexture *WipeStartScreen() override;
+    FTexture *WipeEndScreen() override;
+
+	int camtexcount = 0;
+};
+
+}
+
+#endif //__GL_FRAMEBUFFER
diff --git a/source/common/rendering/gles/gles_hwtexture.cpp b/source/common/rendering/gles/gles_hwtexture.cpp
new file mode 100644
index 000000000..5ce937b10
--- /dev/null
+++ b/source/common/rendering/gles/gles_hwtexture.cpp
@@ -0,0 +1,299 @@
+/*
+** gl_hwtexture.cpp
+** GL texture abstraction
+**
+**---------------------------------------------------------------------------
+** Copyright 2019 Christoph Oelckers
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+**
+*/
+
+#include "gles_system.h"
+#include "templates.h"
+#include "c_cvars.h"
+#include "hw_material.h"
+
+#include "hw_cvars.h"
+#include "gles_renderer.h"
+#include "gles_samplers.h"
+#include "gles_renderstate.h"
+#include "gles_hwtexture.h"
+
+namespace OpenGLESRenderer
+{
+
+
+TexFilter_s TexFilter[] = {
+	{GL_NEAREST,					GL_NEAREST,		false},
+	{GL_NEAREST_MIPMAP_NEAREST,		GL_NEAREST,		true},
+	{GL_LINEAR,						GL_LINEAR,		false},
+	{GL_LINEAR_MIPMAP_NEAREST,		GL_LINEAR,		true},
+	{GL_LINEAR_MIPMAP_LINEAR,		GL_LINEAR,		true},
+	{GL_NEAREST_MIPMAP_LINEAR,		GL_NEAREST,		true},
+	{GL_LINEAR_MIPMAP_LINEAR,		GL_NEAREST,		true},
+};
+
+//===========================================================================
+// 
+//	Static texture data
+//
+//===========================================================================
+unsigned int FHardwareTexture::lastbound[FHardwareTexture::MAX_TEXTURES];
+
+//===========================================================================
+// 
+//	Loads the texture image into the hardware
+//
+// NOTE: For some strange reason I was unable to find the source buffer
+// should be one line higher than the actual texture. I got extremely
+// strange crashes deep inside the GL driver when I didn't do it!
+//
+//===========================================================================
+
+unsigned int FHardwareTexture::CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, const char *name)
+{
+	int rh,rw;
+	int texformat = GL_RGBA;// TexFormat[gl_texture_format];
+	bool deletebuffer=false;
+
+	/*
+	if (forcenocompression)
+	{
+		texformat = GL_RGBA8;
+	}
+	*/
+	bool firstCall = glTexID == 0;
+	if (firstCall)
+	{
+		glGenTextures(1, &glTexID);
+	}
+
+	int textureBinding = UINT_MAX;
+	if (texunit == -1)	glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
+	if (texunit > 0) glActiveTexture(GL_TEXTURE0+texunit);
+	if (texunit >= 0) lastbound[texunit] = glTexID;
+	glBindTexture(GL_TEXTURE_2D, glTexID);
+
+
+	rw = GetTexDimension(w);
+	rh = GetTexDimension(h);
+
+	if (!buffer)
+	{
+		// The texture must at least be initialized if no data is present.
+		mipmapped = false;
+		buffer=(unsigned char *)calloc(4,rw * (rh+1));
+		deletebuffer=true;
+		//texheight=-h;	
+	}
+	else
+	{
+		if (rw < w || rh < h)
+		{
+			// The texture is larger than what the hardware can handle so scale it down.
+			unsigned char * scaledbuffer=(unsigned char *)calloc(4,rw * (rh+1));
+			if (scaledbuffer)
+			{
+				Resize(w, h, rw, rh, buffer, scaledbuffer);
+				deletebuffer=true;
+				buffer=scaledbuffer;
+			}
+		}
+
+
+	}
+	// store the physical size.
+
+	int sourcetype;
+
+
+#if USE_GLES2
+	sourcetype = GL_BGRA;
+	texformat = GL_BGRA;
+#else
+	sourcetype = GL_BGRA;
+	texformat = GL_RGBA;
+#endif
+	glTexImage2D(GL_TEXTURE_2D, 0, texformat, rw, rh, 0, sourcetype, GL_UNSIGNED_BYTE, buffer);
+
+	if (deletebuffer && buffer) free(buffer);
+
+	if (mipmap && TexFilter[gl_texture_filter].mipmapping)
+	{
+		glGenerateMipmap(GL_TEXTURE_2D);
+		mipmapped = true;
+	}
+	
+	if (texunit > 0) glActiveTexture(GL_TEXTURE0);
+	else if (texunit == -1) glBindTexture(GL_TEXTURE_2D, textureBinding);
+	return glTexID;
+}
+
+
+
+//===========================================================================
+// 
+//	Destroys the texture
+//
+//===========================================================================
+FHardwareTexture::~FHardwareTexture() 
+{ 
+	if (glTexID != 0) glDeleteTextures(1, &glTexID);
+}
+
+
+//===========================================================================
+// 
+//	Binds this patch
+//
+//===========================================================================
+unsigned int FHardwareTexture::Bind(int texunit, bool needmipmap)
+{
+	if (glTexID != 0)
+	{
+		if (lastbound[texunit] == glTexID) return glTexID;
+		lastbound[texunit] = glTexID;
+		if (texunit != 0) glActiveTexture(GL_TEXTURE0 + texunit);
+		glBindTexture(GL_TEXTURE_2D, glTexID);
+		// Check if we need mipmaps on a texture that was creted without them.
+		if (needmipmap && !mipmapped && TexFilter[gl_texture_filter].mipmapping)
+		{
+			glGenerateMipmap(GL_TEXTURE_2D);
+			mipmapped = true;
+		}
+		if (texunit != 0) glActiveTexture(GL_TEXTURE0);
+		return glTexID;
+	}
+	return 0;
+}
+
+void FHardwareTexture::Unbind(int texunit)
+{
+	if (lastbound[texunit] != 0)
+	{
+		if (texunit != 0) glActiveTexture(GL_TEXTURE0+texunit);
+		glBindTexture(GL_TEXTURE_2D, 0);
+		if (texunit != 0) glActiveTexture(GL_TEXTURE0);
+		lastbound[texunit] = 0;
+	}
+}
+
+void FHardwareTexture::UnbindAll()
+{
+	for(int texunit = 0; texunit < 16; texunit++)
+	{
+		Unbind(texunit);
+	}
+}
+
+//===========================================================================
+// 
+//	Creates a depth buffer for this texture
+//
+//===========================================================================
+
+int FHardwareTexture::GetDepthBuffer(int width, int height)
+{
+	if (glDepthID == 0)
+	{
+		glGenRenderbuffers(1, &glDepthID);
+		glBindRenderbuffer(GL_RENDERBUFFER, glDepthID);
+		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 
+			GetTexDimension(width), GetTexDimension(height));
+		glBindRenderbuffer(GL_RENDERBUFFER, 0);
+	}
+	return glDepthID;
+}
+
+
+//===========================================================================
+// 
+//	Binds this texture's surfaces to the current framrbuffer
+//
+//===========================================================================
+
+void FHardwareTexture::BindToFrameBuffer(int width, int height)
+{
+	width = GetTexDimension(width);
+	height = GetTexDimension(height);
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glTexID, 0);
+	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, GetDepthBuffer(width, height));
+	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, GetDepthBuffer(width, height));
+}
+
+
+//===========================================================================
+// 
+//	Binds a texture to the renderer
+//
+//===========================================================================
+
+bool FHardwareTexture::BindOrCreate(FTexture *tex, int texunit, int clampmode, int translation, int flags)
+{
+	int usebright = false;
+
+	bool needmipmap = (clampmode <= CLAMP_XY) && !forcenofilter;
+
+	// Bind it to the system.
+	if (!Bind(texunit, needmipmap))
+	{
+		if (flags & CTF_Indexed)
+		{
+			glTextureBytes = 1;
+			forcenofilter = true;
+			needmipmap = false;
+		}
+		int w = 0, h = 0;
+
+		// Create this texture
+		
+		FTextureBuffer texbuffer;
+
+		if (!tex->isHardwareCanvas())
+		{
+			texbuffer = tex->CreateTexBuffer(translation, flags | CTF_ProcessData);
+			w = texbuffer.mWidth;
+			h = texbuffer.mHeight;
+		}
+		else
+		{
+			w = tex->GetWidth();
+			h = tex->GetHeight();
+		}
+		if (!CreateTexture(texbuffer.mBuffer, w, h, texunit, needmipmap, "FHardwareTexture.BindOrCreate"))
+		{
+			// could not create texture
+			return false;
+		}
+	}
+	if (forcenofilter && clampmode <= CLAMP_XY) clampmode += CLAMP_NOFILTER - CLAMP_NONE;
+	GLRenderer->mSamplerManager->Bind(texunit, clampmode, 255);
+	return true;
+}
+
+}
diff --git a/source/common/rendering/gles/gles_hwtexture.h b/source/common/rendering/gles/gles_hwtexture.h
new file mode 100644
index 000000000..8ff673de7
--- /dev/null
+++ b/source/common/rendering/gles/gles_hwtexture.h
@@ -0,0 +1,79 @@
+#pragma once
+class FBitmap;
+class FTexture;
+
+#include "tarray.h"
+#include "hw_ihwtexture.h"
+
+
+#ifdef LoadImage
+#undef LoadImage
+#endif
+
+#define SHADED_TEXTURE -1
+#define DIRECT_PALETTE -2
+
+#include "tarray.h"
+#include "gles_system.h"
+#include "hw_ihwtexture.h"
+
+class FCanvasTexture;
+
+namespace OpenGLESRenderer
+{
+
+class FHardwareTexture : public IHardwareTexture
+{
+public:
+
+	static unsigned int lastbound[MAX_TEXTURES];
+
+	static int GetTexDimension(int value)
+	{
+		if (value > gles.max_texturesize) return gles.max_texturesize;
+		return value;
+	}
+
+	static void InitGlobalState() { for (int i = 0; i < MAX_TEXTURES; i++) lastbound[i] = 0; }
+
+private:
+
+	bool forcenofilter;
+
+	unsigned int glTexID = 0;
+	unsigned int glDepthID = 0;	// only used by camera textures
+	int glTextureBytes;
+	bool mipmapped = false;
+
+	int GetDepthBuffer(int w, int h);
+
+public:
+	FHardwareTexture(int numchannels = 4, bool disablefilter = false)
+	{
+		forcenofilter = disablefilter;
+		glTextureBytes = numchannels;
+	}
+
+	~FHardwareTexture();
+
+	static void Unbind(int texunit);
+	static void UnbindAll();
+
+	void BindToFrameBuffer(int w, int h);
+
+	unsigned int Bind(int texunit, bool needmipmap);
+	bool BindOrCreate(FTexture* tex, int texunit, int clampmode, int translation, int flags);
+
+	void AllocateBuffer(int w, int h, int texelsize) {} // Not used
+	uint8_t* MapBuffer() { return 0; }					// Not used
+
+	unsigned int CreateTexture(unsigned char* buffer, int w, int h, int texunit, bool mipmap, const char* name);
+	unsigned int GetTextureHandle()
+	{
+		return glTexID;
+	}
+
+	int numChannels() { return glTextureBytes; }
+};
+
+}
diff --git a/source/common/rendering/gles/gles_postprocess.cpp b/source/common/rendering/gles/gles_postprocess.cpp
new file mode 100644
index 000000000..a618c3b2e
--- /dev/null
+++ b/source/common/rendering/gles/gles_postprocess.cpp
@@ -0,0 +1,225 @@
+/*
+**  Postprocessing framework
+**  Copyright (c) 2016-2020 Magnus Norddahl
+**
+**  This software is provided 'as-is', without any express or implied
+**  warranty.  In no event will the authors be held liable for any damages
+**  arising from the use of this software.
+**
+**  Permission is granted to anyone to use this software for any purpose,
+**  including commercial applications, and to alter it and redistribute it
+**  freely, subject to the following restrictions:
+**
+**  1. The origin of this software must not be misrepresented; you must not
+**     claim that you wrote the original software. If you use this software
+**     in a product, an acknowledgment in the product documentation would be
+**     appreciated but is not required.
+**  2. Altered source versions must be plainly marked as such, and must not be
+**     misrepresented as being the original software.
+**  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "gles_system.h"
+#include "m_png.h"
+#include "gles_buffers.h"
+#include "gles_framebuffer.h"
+#include "gles_renderbuffers.h"
+#include "gles_renderer.h"
+#include "gles_postprocessstate.h"
+#include "gles_shaderprogram.h"
+#include "hwrenderer/postprocessing/hw_postprocess.h"
+#include "hwrenderer/postprocessing/hw_postprocess_cvars.h"
+#include "flatvertices.h"
+#include "r_videoscale.h"
+#include "v_video.h"
+#include "templates.h"
+#include "hw_vrmodes.h"
+#include "v_draw.h"
+
+extern bool vid_hdr_active;
+
+
+namespace OpenGLESRenderer
+{
+
+int gl_dither_bpc = -1;
+
+void FGLRenderer::RenderScreenQuad()
+{
+	auto buffer = static_cast<GLVertexBuffer *>(screen->mVertexData->GetBufferObjects().first);
+	buffer->Bind(nullptr);
+	glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 3);
+}
+
+void FGLRenderer::PostProcessScene(int fixedcm, float flash, const std::function<void()> &afterBloomDrawEndScene2D)
+{
+#ifndef NO_RENDER_BUFFER
+	mBuffers->BindCurrentFB();
+#endif
+	if (afterBloomDrawEndScene2D) afterBloomDrawEndScene2D();
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// Copies the rendered screen to its final destination
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::Flush()
+{
+	CopyToBackbuffer(nullptr, true);
+}
+
+//-----------------------------------------------------------------------------
+//
+// Gamma correct while copying to frame buffer
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::CopyToBackbuffer(const IntRect *bounds, bool applyGamma)
+{
+#ifdef NO_RENDER_BUFFER
+	mBuffers->BindOutputFB();
+#endif
+	screen->Draw2D();	// draw all pending 2D stuff before copying the buffer
+	twod->Clear();
+
+	FGLPostProcessState savedState;
+	savedState.SaveTextureBindings(2);
+
+	mBuffers->BindOutputFB();
+
+	IntRect box;
+	if (bounds)
+	{
+		box = *bounds;
+	}
+	else
+	{
+		ClearBorders();
+		box = screen->mOutputLetterbox;
+	}
+
+	mBuffers->BindCurrentTexture(0);
+#ifndef NO_RENDER_BUFFER
+	DrawPresentTexture(box, applyGamma);
+#endif
+}
+
+void FGLRenderer::DrawPresentTexture(const IntRect &box, bool applyGamma)
+{
+	glViewport(box.left, box.top, box.width, box.height);
+
+	mBuffers->BindDitherTexture(1);
+
+	glActiveTexture(GL_TEXTURE0);
+	if (ViewportLinearScale())
+	{
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	}
+	else
+	{
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	}
+
+	mPresentShader->Bind();
+	if (!applyGamma || framebuffer->IsHWGammaActive())
+	{
+		mPresentShader->Uniforms->InvGamma = 1.0f;
+		mPresentShader->Uniforms->Contrast = 1.0f;
+		mPresentShader->Uniforms->Brightness = 0.0f;
+		mPresentShader->Uniforms->Saturation = 1.0f;
+	}
+	else
+	{
+		mPresentShader->Uniforms->InvGamma = 1.0f / clamp<float>(vid_gamma, 0.1f, 4.f);
+		mPresentShader->Uniforms->Contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
+		mPresentShader->Uniforms->Brightness = clamp<float>(vid_brightness, -0.8f, 0.8f);
+		mPresentShader->Uniforms->Saturation = clamp<float>(vid_saturation, -15.0f, 15.f);
+		mPresentShader->Uniforms->GrayFormula = static_cast<int>(gl_satformula);
+	}
+	if (vid_hdr_active && framebuffer->IsFullscreen())
+	{
+		// Full screen exclusive mode treats a rgba16f frame buffer as linear.
+		// It probably will eventually in desktop mode too, but the DWM doesn't seem to support that.
+		mPresentShader->Uniforms->HdrMode = 1;
+		mPresentShader->Uniforms->ColorScale = (gl_dither_bpc == -1) ? 1023.0f : (float)((1 << gl_dither_bpc) - 1);
+	}
+	else
+	{
+		mPresentShader->Uniforms->HdrMode = 0;
+		mPresentShader->Uniforms->ColorScale = (gl_dither_bpc == -1) ? 255.0f : (float)((1 << gl_dither_bpc) - 1);
+	}
+	mPresentShader->Uniforms->Scale = { screen->mScreenViewport.width / (float)mBuffers->GetWidth(), screen->mScreenViewport.height / (float)mBuffers->GetHeight() };
+	mPresentShader->Uniforms->Offset = { 0.0f, 0.0f };
+	mPresentShader->Uniforms.SetData();
+
+	
+	for (int n = 0; n < mPresentShader->Uniforms.mFields.size(); n++)
+	{
+		int index = -1;
+		UniformFieldDesc desc = mPresentShader->Uniforms.mFields[n];
+		int loc = mPresentShader->Uniforms.UniformLocation[n];
+		switch (desc.Type)
+		{
+		case UniformType::Int:
+			glUniform1i(loc, *((GLint*)(((char*)(&mPresentShader->Uniforms)) + desc.Offset)));
+			break;
+		case UniformType::Float:
+			glUniform1f(loc, *((GLfloat*)(((char*)(&mPresentShader->Uniforms)) + desc.Offset)));
+			break;
+		case UniformType::Vec2:
+			glUniform2fv(loc,1 , ((GLfloat*)(((char*)(&mPresentShader->Uniforms)) + desc.Offset)));
+			break;
+		}
+	}
+	
+	RenderScreenQuad();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Fills the black bars around the screen letterbox
+//
+//-----------------------------------------------------------------------------
+
+void FGLRenderer::ClearBorders()
+{
+	const auto &box = screen->mOutputLetterbox;
+
+	int clientWidth = framebuffer->GetClientWidth();
+	int clientHeight = framebuffer->GetClientHeight();
+	if (clientWidth == 0 || clientHeight == 0)
+		return;
+
+	glViewport(0, 0, clientWidth, clientHeight);
+	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+	glEnable(GL_SCISSOR_TEST);
+	if (box.top > 0)
+	{
+		glScissor(0, 0, clientWidth, box.top);
+		glClear(GL_COLOR_BUFFER_BIT);
+	}
+	if (clientHeight - box.top - box.height > 0)
+	{
+		glScissor(0, box.top + box.height, clientWidth, clientHeight - box.top - box.height);
+		glClear(GL_COLOR_BUFFER_BIT);
+	}
+	if (box.left > 0)
+	{
+		glScissor(0, box.top, box.left, box.height);
+		glClear(GL_COLOR_BUFFER_BIT);
+	}
+	if (clientWidth - box.left - box.width > 0)
+	{
+		glScissor(box.left + box.width, box.top, clientWidth - box.left - box.width, box.height);
+		glClear(GL_COLOR_BUFFER_BIT);
+	}
+	glDisable(GL_SCISSOR_TEST);
+}
+
+}
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_postprocessstate.cpp b/source/common/rendering/gles/gles_postprocessstate.cpp
new file mode 100644
index 000000000..216406125
--- /dev/null
+++ b/source/common/rendering/gles/gles_postprocessstate.cpp
@@ -0,0 +1,118 @@
+/*
+**  Postprocessing framework
+**  Copyright (c) 2016-2020 Magnus Norddahl
+**
+**  This software is provided 'as-is', without any express or implied
+**  warranty.  In no event will the authors be held liable for any damages
+**  arising from the use of this software.
+**
+**  Permission is granted to anyone to use this software for any purpose,
+**  including commercial applications, and to alter it and redistribute it
+**  freely, subject to the following restrictions:
+**
+**  1. The origin of this software must not be misrepresented; you must not
+**     claim that you wrote the original software. If you use this software
+**     in a product, an acknowledgment in the product documentation would be
+**     appreciated but is not required.
+**  2. Altered source versions must be plainly marked as such, and must not be
+**     misrepresented as being the original software.
+**  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "templates.h"
+#include "gles_system.h"
+#include "gles_postprocessstate.h"
+
+namespace OpenGLESRenderer
+{
+
+//-----------------------------------------------------------------------------
+//
+// Saves state modified by post processing shaders
+//
+//-----------------------------------------------------------------------------
+
+FGLPostProcessState::FGLPostProcessState()
+{
+	glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
+	glActiveTexture(GL_TEXTURE0);
+	SaveTextureBindings(1);
+
+	glGetBooleanv(GL_BLEND, &blendEnabled);
+	glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled);
+	glGetBooleanv(GL_DEPTH_TEST, &depthEnabled);
+	glGetIntegerv(GL_CURRENT_PROGRAM, &currentProgram);
+	glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRgb);
+	glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha);
+	glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRgb);
+	glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha);
+	glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRgb);
+	glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha);
+
+	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_SCISSOR_TEST);
+	glDisable(GL_BLEND);
+}
+
+void FGLPostProcessState::SaveTextureBindings(unsigned int numUnits)
+{
+	while (textureBinding.Size() < numUnits)
+	{
+		unsigned int i = textureBinding.Size();
+
+		GLint texture;
+		glActiveTexture(GL_TEXTURE0 + i);
+		glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture);
+		glBindTexture(GL_TEXTURE_2D, 0);
+		textureBinding.Push(texture);
+	}
+	glActiveTexture(GL_TEXTURE0);
+}
+
+//-----------------------------------------------------------------------------
+//
+// Restores state at the end of post processing
+//
+//-----------------------------------------------------------------------------
+
+FGLPostProcessState::~FGLPostProcessState()
+{
+	if (blendEnabled)
+		glEnable(GL_BLEND);
+	else
+		glDisable(GL_BLEND);
+
+	if (scissorEnabled)
+		glEnable(GL_SCISSOR_TEST);
+	else
+		glDisable(GL_SCISSOR_TEST);
+
+	if (depthEnabled)
+		glEnable(GL_DEPTH_TEST);
+	else
+		glDisable(GL_DEPTH_TEST);
+
+
+	glBlendEquationSeparate(blendEquationRgb, blendEquationAlpha);
+	glBlendFuncSeparate(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha);
+
+	glUseProgram(currentProgram);
+
+	// Fully unbind to avoid incomplete texture warnings from Nvidia's driver when gl_debug_level 4 is active
+	for (unsigned int i = 0; i < textureBinding.Size(); i++)
+	{
+		glActiveTexture(GL_TEXTURE0 + i);
+		glBindTexture(GL_TEXTURE_2D, 0);
+	}
+
+
+	for (unsigned int i = 0; i < textureBinding.Size(); i++)
+	{
+		glActiveTexture(GL_TEXTURE0 + i);
+		glBindTexture(GL_TEXTURE_2D, textureBinding[i]);
+	}
+
+	glActiveTexture(activeTex);
+}
+
+}
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_postprocessstate.h b/source/common/rendering/gles/gles_postprocessstate.h
new file mode 100644
index 000000000..1a5ec760f
--- /dev/null
+++ b/source/common/rendering/gles/gles_postprocessstate.h
@@ -0,0 +1,40 @@
+#ifndef __GL_POSTPROCESSSTATE_H
+#define __GL_POSTPROCESSSTATE_H
+
+#include <string.h>
+#include "matrix.h"
+#include "c_cvars.h"
+
+namespace OpenGLESRenderer
+{
+
+class FGLPostProcessState
+{
+public:
+	FGLPostProcessState();
+	~FGLPostProcessState();
+
+	void SaveTextureBindings(unsigned int numUnits);
+
+private:
+	FGLPostProcessState(const FGLPostProcessState &) = delete;
+	FGLPostProcessState &operator=(const FGLPostProcessState &) = delete;
+
+	GLint activeTex;
+	TArray<GLint> textureBinding;
+	TArray<GLint> samplerBinding;
+	GLboolean blendEnabled;
+	GLboolean scissorEnabled;
+	GLboolean depthEnabled;
+	GLboolean multisampleEnabled;
+	GLint currentProgram;
+	GLint blendEquationRgb;
+	GLint blendEquationAlpha;
+	GLint blendSrcRgb;
+	GLint blendSrcAlpha;
+	GLint blendDestRgb;
+	GLint blendDestAlpha;
+};
+
+}
+#endif
diff --git a/source/common/rendering/gles/gles_renderbuffers.cpp b/source/common/rendering/gles/gles_renderbuffers.cpp
new file mode 100644
index 000000000..91f8ce462
--- /dev/null
+++ b/source/common/rendering/gles/gles_renderbuffers.cpp
@@ -0,0 +1,433 @@
+/*
+**  Postprocessing framework
+**  Copyright (c) 2016-2020 Magnus Norddahl
+**
+**  This software is provided 'as-is', without any express or implied
+**  warranty.  In no event will the authors be held liable for any damages
+**  arising from the use of this software.
+**
+**  Permission is granted to anyone to use this software for any purpose,
+**  including commercial applications, and to alter it and redistribute it
+**  freely, subject to the following restrictions:
+**
+**  1. The origin of this software must not be misrepresented; you must not
+**     claim that you wrote the original software. If you use this software
+**     in a product, an acknowledgment in the product documentation would be
+**     appreciated but is not required.
+**  2. Altered source versions must be plainly marked as such, and must not be
+**     misrepresented as being the original software.
+**  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "gles_system.h"
+#include "v_video.h"
+#include "printf.h"
+#include "hw_cvars.h"
+#include "gles_renderer.h"
+#include "gles_renderbuffers.h"
+#include "gles_postprocessstate.h"
+#include "gles_shaderprogram.h"
+#include "gles_buffers.h"
+#include "templates.h"
+#include <random>
+
+EXTERN_CVAR(Int, gl_debug_level)
+
+namespace OpenGLESRenderer
+{
+
+	//==========================================================================
+	//
+	// Initialize render buffers and textures used in rendering passes
+	//
+	//==========================================================================
+
+	FGLRenderBuffers::FGLRenderBuffers()
+	{
+	
+	}
+
+	//==========================================================================
+	//
+	// Free render buffer resources
+	//
+	//==========================================================================
+
+	FGLRenderBuffers::~FGLRenderBuffers()
+	{
+		ClearScene();
+		
+		DeleteTexture(mDitherTexture);
+	}
+
+	void FGLRenderBuffers::ClearScene()
+	{
+		DeleteFrameBuffer(mSceneFB);
+		DeleteRenderBuffer(mSceneDepthStencilBuf);
+		DeleteRenderBuffer(mSceneStencilBuf);
+	}
+
+	void FGLRenderBuffers::DeleteTexture(PPGLTexture& tex)
+	{
+		if (tex.handle != 0)
+			glDeleteTextures(1, &tex.handle);
+		tex.handle = 0;
+	}
+
+	void FGLRenderBuffers::DeleteRenderBuffer(PPGLRenderBuffer& buf)
+	{
+		if (buf.handle != 0)
+			glDeleteRenderbuffers(1, &buf.handle);
+		buf.handle = 0;
+	}
+
+	void FGLRenderBuffers::DeleteFrameBuffer(PPGLFrameBuffer& fb)
+	{
+		if (fb.handle != 0)
+			glDeleteFramebuffers(1, &fb.handle);
+		fb.handle = 0;
+	}
+
+	//==========================================================================
+	//
+	// Makes sure all render buffers have sizes suitable for rending at the
+	// specified resolution
+	//
+	//==========================================================================
+
+	void FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHeight)
+	{
+		if (width <= 0 || height <= 0)
+			I_FatalError("Requested invalid render buffer sizes: screen = %dx%d", width, height);
+
+		GLint activeTex;
+		GLint textureBinding;
+		glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
+		glActiveTexture(GL_TEXTURE0);
+		glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
+
+		if (width != mWidth || height != mHeight)
+			CreatePipeline(width, height);
+
+		if (width != mWidth || height != mHeight )
+			CreateScene(width, height);
+
+		mWidth = width;
+		mHeight = height;
+		mSceneWidth = sceneWidth;
+		mSceneHeight = sceneHeight;
+
+		glBindTexture(GL_TEXTURE_2D, textureBinding);
+		glActiveTexture(activeTex);
+		glBindRenderbuffer(GL_RENDERBUFFER, 0);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+		if (FailedCreate)
+		{
+			ClearScene();
+			mWidth = 0;
+			mHeight = 0;
+			mSceneWidth = 0;
+			mSceneHeight = 0;
+			I_FatalError("Unable to create render buffers.");
+		}
+	}
+
+	//==========================================================================
+	//
+	// Creates the scene buffers
+	//
+	//==========================================================================
+
+	void FGLRenderBuffers::CreateScene(int width, int height)
+	{
+		ClearScene();
+		if(gles.depthStencilAvailable)
+			mSceneDepthStencilBuf = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
+		else
+		{
+			mSceneDepthStencilBuf = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH_COMPONENT16, width, height);
+			mSceneStencilBuf = CreateRenderBuffer("SceneStencil", GL_STENCIL_INDEX8, width, height);
+		}
+		mSceneFB= CreateFrameBuffer("SceneFB", mSceneTex, mSceneDepthStencilBuf, mSceneStencilBuf);
+	}
+
+	//==========================================================================
+	//
+	// Creates the buffers needed for post processing steps
+	//
+	//==========================================================================
+
+	void FGLRenderBuffers::CreatePipeline(int width, int height)
+	{
+		mSceneTex = Create2DTexture("PipelineTexture", GL_RGBA, width, height);
+	}
+
+	
+
+	//==========================================================================
+	//
+	// Creates a 2D texture defaulting to linear filtering and clamp to edge
+	//
+	//==========================================================================
+
+	PPGLTexture FGLRenderBuffers::Create2DTexture(const char* name, GLuint format, int width, int height, const void* data)
+	{
+		PPGLTexture tex;
+		tex.Width = width;
+		tex.Height = height;
+		glGenTextures(1, &tex.handle);
+		glBindTexture(GL_TEXTURE_2D, tex.handle);
+	
+		GLenum dataformat = 0, datatype = 0;
+		/*
+		switch (format)
+		{
+		case GL_RGBA:				dataformat = GL_RGBA; datatype = GL_UNSIGNED_BYTE; break;
+
+		case GL_RGBA16:				dataformat = GL_RGBA; datatype = GL_UNSIGNED_SHORT; break;
+		case GL_RGBA16F:			dataformat = GL_RGBA; datatype = GL_FLOAT; break;
+		case GL_RGBA32F:			dataformat = GL_RGBA; datatype = GL_FLOAT; break;
+		case GL_RGBA16_SNORM:		dataformat = GL_RGBA; datatype = GL_SHORT; break;
+		case GL_R32F:				dataformat = GL_RED; datatype = GL_FLOAT; break;
+		case GL_R16F:				dataformat = GL_RED; datatype = GL_FLOAT; break;
+		case GL_RG32F:				dataformat = GL_RG; datatype = GL_FLOAT; break;
+		case GL_RG16F:				dataformat = GL_RG; datatype = GL_FLOAT; break;
+		case GL_RGB10_A2:			dataformat = GL_RGBA; datatype = GL_UNSIGNED_INT_10_10_10_2; break;
+		case GL_DEPTH_COMPONENT24:	dataformat = GL_DEPTH_COMPONENT; datatype = GL_FLOAT; break;
+
+		case GL_STENCIL_INDEX8:		dataformat = GL_STENCIL_INDEX8_OES; datatype = GL_INT; break;
+		case GL_DEPTH24_STENCIL8_OES:	dataformat = GL_DEPTH_STENCIL_OES; datatype = GL_UNSIGNED_INT_24_8; break;
+		default: I_FatalError("Unknown format passed to FGLRenderBuffers.Create2DTexture");
+		}
+	*/
+		format = GL_RGBA;
+		dataformat = GL_RGBA;
+		datatype = GL_UNSIGNED_BYTE;
+
+		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, dataformat, datatype, data);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
+		return tex;
+	}
+
+	//==========================================================================
+	//
+	// Creates a render buffer
+	//
+	//==========================================================================
+
+	PPGLRenderBuffer FGLRenderBuffers::CreateRenderBuffer(const char* name, GLuint format, int width, int height)
+	{
+		PPGLRenderBuffer buf;
+		glGenRenderbuffers(1, &buf.handle);
+		glBindRenderbuffer(GL_RENDERBUFFER, buf.handle);
+	
+		glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
+		return buf;
+	}
+
+	//==========================================================================
+	//
+	// Creates a frame buffer
+	//
+	//==========================================================================
+
+	PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char* name, PPGLTexture colorbuffer)
+	{
+		PPGLFrameBuffer fb;
+		glGenFramebuffers(1, &fb.handle);
+		glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
+	
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer.handle, 0);
+		if (CheckFrameBufferCompleteness())
+			ClearFrameBuffer(false, false);
+		return fb;
+	}
+
+	PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char* name, PPGLTexture colorbuffer, PPGLRenderBuffer depthstencil, PPGLRenderBuffer stencil)
+	{
+		PPGLFrameBuffer fb;
+		glGenFramebuffers(1, &fb.handle);
+		glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
+		
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer.handle, 0);
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthstencil.handle);
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gles.depthStencilAvailable ? depthstencil.handle : stencil.handle);
+		if (CheckFrameBufferCompleteness())
+			ClearFrameBuffer(true, true);
+		return fb;
+	}
+
+	PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char* name, PPGLRenderBuffer colorbuffer, PPGLRenderBuffer depthstencil, PPGLRenderBuffer stencil)
+	{
+		PPGLFrameBuffer fb;
+		glGenFramebuffers(1, &fb.handle);
+		glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
+	
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer.handle);
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthstencil.handle);
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gles.depthStencilAvailable ? depthstencil.handle : stencil.handle);
+		if (CheckFrameBufferCompleteness())
+			ClearFrameBuffer(true, true);
+		return fb;
+	}
+
+	PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char* name, PPGLTexture colorbuffer0, PPGLTexture colorbuffer1, PPGLTexture colorbuffer2, PPGLTexture depthstencil, PPGLRenderBuffer stencil)
+	{
+		PPGLFrameBuffer fb;
+		glGenFramebuffers(1, &fb.handle);
+		glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
+
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer0.handle, 0);
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthstencil.handle);
+		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gles.depthStencilAvailable ? depthstencil.handle : stencil.handle);
+	
+		if (CheckFrameBufferCompleteness())
+			ClearFrameBuffer(true, true);
+		return fb;
+	}
+
+	//==========================================================================
+	//
+	// Verifies that the frame buffer setup is valid
+	//
+	//==========================================================================
+
+	bool FGLRenderBuffers::CheckFrameBufferCompleteness()
+	{
+		GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+		if (result == GL_FRAMEBUFFER_COMPLETE)
+			return true;
+
+		bool FailedCreate = true;
+
+
+		FString error = "glCheckFramebufferStatus failed: ";
+		switch (result)
+		{
+		default: error.AppendFormat("error code %d", (int)result); break;
+		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break;
+		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break;
+		case GL_FRAMEBUFFER_UNSUPPORTED: error << "GL_FRAMEBUFFER_UNSUPPORTED"; break;
+		}
+		Printf("%s\n", error.GetChars());
+	
+
+		return false;
+	}
+
+	//==========================================================================
+	//
+	// Clear frame buffer to make sure it never contains uninitialized data
+	//
+	//==========================================================================
+
+	void FGLRenderBuffers::ClearFrameBuffer(bool stencil, bool depth)
+	{
+		GLboolean scissorEnabled;
+		GLint stencilValue;
+		GLfloat depthValue;
+		glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled);
+		glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilValue);
+		glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthValue);
+		glDisable(GL_SCISSOR_TEST);
+		glClearColor(0.0, 0.0, 0.0, 0.0);
+		glClearDepthf(0.0f);
+		glClearStencil(0);
+		GLenum flags = GL_COLOR_BUFFER_BIT;
+		if (stencil)
+			flags |= GL_STENCIL_BUFFER_BIT;
+		if (depth)
+			flags |= GL_DEPTH_BUFFER_BIT;
+		glClear(flags);
+		glClearStencil(stencilValue);
+		glClearDepthf(depthValue);
+		if (scissorEnabled)
+			glEnable(GL_SCISSOR_TEST);
+	}
+
+
+	void FGLRenderBuffers::BindDitherTexture(int texunit)
+	{
+		if (!mDitherTexture)
+		{
+			static const float data[64] =
+			{
+				 .0078125, .2578125, .1328125, .3828125, .0234375, .2734375, .1484375, .3984375,
+				 .7578125, .5078125, .8828125, .6328125, .7734375, .5234375, .8984375, .6484375,
+				 .0703125, .3203125, .1953125, .4453125, .0859375, .3359375, .2109375, .4609375,
+				 .8203125, .5703125, .9453125, .6953125, .8359375, .5859375, .9609375, .7109375,
+				 .0390625, .2890625, .1640625, .4140625, .0546875, .3046875, .1796875, .4296875,
+				 .7890625, .5390625, .9140625, .6640625, .8046875, .5546875, .9296875, .6796875,
+				 .1015625, .3515625, .2265625, .4765625, .1171875, .3671875, .2421875, .4921875,
+				 .8515625, .6015625, .9765625, .7265625, .8671875, .6171875, .9921875, .7421875,
+			};
+
+			glActiveTexture(GL_TEXTURE0 + texunit);
+			mDitherTexture = Create2DTexture("DitherTexture", GL_RGBA, 8, 8, data);
+		}
+		mDitherTexture.Bind(1, GL_NEAREST, GL_REPEAT);
+	}
+
+	
+	//==========================================================================
+	//
+	// Makes the scene frame buffer active (multisample, depth, stecil, etc.)
+	//
+	//==========================================================================
+
+	void FGLRenderBuffers::BindSceneFB(bool sceneData)
+	{
+		glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB.handle);
+	}
+
+	//==========================================================================
+	//
+	// Binds the current scene/effect/hud texture to the specified texture unit
+	//
+	//==========================================================================
+
+	void FGLRenderBuffers::BindCurrentTexture(int index, int filter, int wrap)
+	{
+		mSceneTex.Bind(index, filter, wrap);
+	}
+
+	//==========================================================================
+	//
+	// Makes the frame buffer for the current texture active 
+	//
+	//==========================================================================
+
+	void FGLRenderBuffers::BindCurrentFB()
+	{
+#ifndef NO_RENDER_BUFFER
+		mSceneFB.Bind();
+#endif
+	}
+
+	//==========================================================================
+	//
+	// Makes the screen frame buffer active
+	//
+	//==========================================================================
+
+	void FGLRenderBuffers::BindOutputFB()
+	{
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	}
+
+	//==========================================================================
+	//
+	// Returns true if render buffers are supported and should be used
+	//
+	//==========================================================================
+
+	bool FGLRenderBuffers::FailedCreate = false;
+
+
+
+
+}  // namespace OpenGLRenderer
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_renderbuffers.h b/source/common/rendering/gles/gles_renderbuffers.h
new file mode 100644
index 000000000..ce052e26f
--- /dev/null
+++ b/source/common/rendering/gles/gles_renderbuffers.h
@@ -0,0 +1,152 @@
+
+#pragma once
+
+#include "hwrenderer/postprocessing/hw_postprocess.h"
+
+namespace OpenGLESRenderer
+{
+
+class FGLRenderBuffers;
+
+class PPGLTexture
+{
+public:
+	void Bind(int index, int filter = GL_NEAREST, int wrap = GL_CLAMP_TO_EDGE)
+	{
+		glActiveTexture(GL_TEXTURE0 + index);
+		glBindTexture(GL_TEXTURE_2D, handle);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
+	}
+
+	int Width = -1;
+	int Height = -1;
+
+	explicit operator bool() const { return handle != 0; }
+
+private:
+	GLuint handle = 0;
+
+	friend class FGLRenderBuffers;
+	friend class PPGLTextureBackend;
+};
+
+class PPGLFrameBuffer
+{
+public:
+	void Bind()
+	{
+		glBindFramebuffer(GL_FRAMEBUFFER, handle);
+	}
+
+	explicit operator bool() const { return handle != 0; }
+
+private:
+	GLuint handle = 0;
+
+	friend class FGLRenderBuffers;
+	friend class PPGLTextureBackend;
+};
+
+class PPGLRenderBuffer
+{
+private:
+	GLuint handle = 0;
+
+	explicit operator bool() const { return handle != 0; }
+
+	friend class FGLRenderBuffers;
+};
+
+class PPGLTextureBackend : public PPTextureBackend
+{
+public:
+	~PPGLTextureBackend()
+	{
+		if (Tex.handle != 0)
+		{
+			glDeleteTextures(1, &Tex.handle);
+			Tex.handle = 0;
+		}
+		if (FB.handle != 0)
+		{
+			glDeleteFramebuffers(1, &FB.handle);
+			FB.handle = 0;
+		}
+	}
+
+	PPGLTexture Tex;
+	PPGLFrameBuffer FB;
+};
+
+class FShaderProgram;
+
+
+class FGLRenderBuffers
+{
+public:
+	FGLRenderBuffers();
+	~FGLRenderBuffers();
+
+	void Setup(int width, int height, int sceneWidth, int sceneHeight);
+
+	void BindSceneFB(bool sceneData);
+
+
+	void BindCurrentTexture(int index, int filter = GL_NEAREST, int wrap = GL_CLAMP_TO_EDGE);
+	void BindCurrentFB();
+	void BindNextFB();
+	void NextTexture();
+
+
+	void BindOutputFB();
+
+	void BindDitherTexture(int texunit);
+
+	int GetWidth() const { return mWidth; }
+	int GetHeight() const { return mHeight; }
+
+	int GetSceneWidth() const { return mSceneWidth; }
+	int GetSceneHeight() const { return mSceneHeight; }
+
+private:
+	void ClearScene();
+	
+	void CreateScene(int width, int height);
+	void CreatePipeline(int width, int height);
+
+	PPGLTexture Create2DTexture(const char *name, GLuint format, int width, int height, const void *data = nullptr);
+	PPGLRenderBuffer CreateRenderBuffer(const char *name, GLuint format, int width, int height);
+	PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer);
+	PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer, PPGLRenderBuffer depthstencil, PPGLRenderBuffer stencil);
+	PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLRenderBuffer colorbuffer, PPGLRenderBuffer depthstencil, PPGLRenderBuffer stencil);
+	PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer0, PPGLTexture colorbuffer1, PPGLTexture colorbuffer2, PPGLTexture depthstencil, PPGLRenderBuffer stencil);
+	bool CheckFrameBufferCompleteness();
+	void ClearFrameBuffer(bool stencil, bool depth);
+	void DeleteTexture(PPGLTexture &handle);
+	void DeleteRenderBuffer(PPGLRenderBuffer &handle);
+	void DeleteFrameBuffer(PPGLFrameBuffer &handle);
+
+	int mWidth = 0;
+	int mHeight = 0;
+	int mSceneWidth = 0;
+	int mSceneHeight = 0;
+
+	// Buffers for the scene
+	PPGLTexture mSceneDepthStencilTex;
+	PPGLTexture mSceneTex;
+	PPGLRenderBuffer mSceneDepthStencilBuf;
+	PPGLRenderBuffer mSceneStencilBuf; // This is only use when combined depth-stencil is not avaliable
+	PPGLFrameBuffer mSceneFB;
+	bool mSceneUsesTextures = false;
+
+	PPGLTexture mDitherTexture;
+
+	static bool FailedCreate;
+
+	friend class GLPPRenderState;
+};
+
+}
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_renderer.cpp b/source/common/rendering/gles/gles_renderer.cpp
new file mode 100644
index 000000000..b3b79ddae
--- /dev/null
+++ b/source/common/rendering/gles/gles_renderer.cpp
@@ -0,0 +1,159 @@
+/*
+** gl_renderer.cpp
+** Renderer interface
+**
+**---------------------------------------------------------------------------
+** Copyright 2005-2020 Christoph Oelckers
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/ 
+
+#include "gles_system.h"
+#include "files.h"
+#include "v_video.h"
+#include "m_png.h"
+#include "filesystem.h"
+#include "i_time.h"
+#include "cmdlib.h"
+#include "version.h"
+#include "gles_framebuffer.h"
+#include "hw_cvars.h"
+#include "gles_renderer.h"
+#include "gles_hwtexture.h"
+#include "gles_renderstate.h"
+#include "gles_samplers.h"
+#include "gles_renderbuffers.h"
+#include "gles_shaderprogram.h"
+#include "flatvertices.h"
+#include "hw_lightbuffer.h"
+#include "r_videoscale.h"
+#include "model.h"
+#include "gles_postprocessstate.h"
+#include "gles_buffers.h"
+#include "texturemanager.h"
+
+EXTERN_CVAR(Int, screenblocks)
+
+namespace OpenGLESRenderer
+{
+
+//===========================================================================
+// 
+// Renderer interface
+//
+//===========================================================================
+
+//-----------------------------------------------------------------------------
+//
+// Initialize
+//
+//-----------------------------------------------------------------------------
+
+FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) 
+{
+	framebuffer = fb;
+}
+
+void FGLRenderer::Initialize(int width, int height)
+{
+	mScreenBuffers = new FGLRenderBuffers();
+	mBuffers = mScreenBuffers;
+	mPresentShader = new FPresentShader();
+	
+	mFBID = 0;
+	mOldFBID = 0;
+
+	mShaderManager = new FShaderManager;
+	mSamplerManager = new FSamplerManager;
+}
+
+FGLRenderer::~FGLRenderer() 
+{
+	FlushModels();
+	TexMan.FlushAll();
+	if (mShaderManager != nullptr) delete mShaderManager;
+	if (mFBID != 0) glDeleteFramebuffers(1, &mFBID);
+	
+	if (mBuffers) delete mBuffers;
+	if (mPresentShader) delete mPresentShader;
+}
+
+//===========================================================================
+// 
+//
+//
+//===========================================================================
+
+bool FGLRenderer::StartOffscreen()
+{
+	bool firstBind = (mFBID == 0);
+	if (mFBID == 0)
+		glGenFramebuffers(1, &mFBID);
+	glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mOldFBID);
+	glBindFramebuffer(GL_FRAMEBUFFER, mFBID);
+	return true;
+}
+
+//===========================================================================
+// 
+//
+//
+//===========================================================================
+
+void FGLRenderer::EndOffscreen()
+{
+	glBindFramebuffer(GL_FRAMEBUFFER, mOldFBID); 
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::BindToFrameBuffer(FTexture *tex)
+{
+	auto BaseLayer = static_cast<FHardwareTexture*>(tex->GetHardwareTexture(0, 0));
+	// must create the hardware texture first
+	BaseLayer->BindOrCreate(tex, 0, 0, 0, 0);
+	FHardwareTexture::Unbind(0);
+	gl_RenderState.ClearLastMaterial();
+	BaseLayer->BindToFrameBuffer(tex->GetWidth(), tex->GetHeight());
+}
+
+//===========================================================================
+//
+//
+//
+//===========================================================================
+
+void FGLRenderer::BeginFrame()
+{
+	mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
+}
+
+}
diff --git a/source/common/rendering/gles/gles_renderer.h b/source/common/rendering/gles/gles_renderer.h
new file mode 100644
index 000000000..a19690029
--- /dev/null
+++ b/source/common/rendering/gles/gles_renderer.h
@@ -0,0 +1,106 @@
+#ifndef __GL_RENDERER_H
+#define __GL_RENDERER_H
+
+#include "v_video.h"
+#include "vectors.h"
+#include "matrix.h"
+#include "gles_renderbuffers.h"
+#include <functional>
+
+#ifdef _MSC_VER
+#pragma warning(disable:4244)
+#endif
+
+struct particle_t;
+class FCanvasTexture;
+class FFlatVertexBuffer;
+class FSkyVertexBuffer;
+class HWPortal;
+class FLightBuffer;
+class DPSprite;
+class FGLRenderBuffers;
+class FGL2DDrawer;
+class SWSceneDrawer;
+class HWViewpointBuffer;
+struct FRenderViewpoint;
+
+namespace OpenGLESRenderer
+{
+	class FHardwareTexture;
+	class FShaderManager;
+	class FSamplerManager;
+	class OpenGLFrameBuffer;
+	class FPresentShaderBase;
+	class FPresentShader;
+	class FPresent3DCheckerShader;
+	class FPresent3DColumnShader;
+	class FPresent3DRowShader;
+	class FShadowMapShader;
+
+class FGLRenderer
+{
+public:
+
+	OpenGLFrameBuffer *framebuffer;
+	int mMirrorCount = 0;
+	int mPlaneMirrorCount = 0;
+	FShaderManager *mShaderManager = nullptr;
+	FSamplerManager* mSamplerManager = nullptr;
+	unsigned int mFBID = 0;
+	unsigned int mStencilValue = 0;
+
+	int mOldFBID = 0;
+
+	FGLRenderBuffers *mBuffers = nullptr;
+	FGLRenderBuffers *mScreenBuffers = nullptr;
+	FPresentShader *mPresentShader = nullptr;
+
+	//FRotator mAngles;
+
+	FGLRenderer(OpenGLFrameBuffer *fb);
+	~FGLRenderer() ;
+
+	void Initialize(int width, int height);
+
+	void ClearBorders();
+
+	void PresentStereo();
+	void RenderScreenQuad();
+	void PostProcessScene(int fixedcm, float flash, const std::function<void()> &afterBloomDrawEndScene2D);
+	
+	void CopyToBackbuffer(const IntRect *bounds, bool applyGamma);
+	void DrawPresentTexture(const IntRect &box, bool applyGamma);
+	void Flush();
+	void BeginFrame();
+    
+	bool StartOffscreen();
+	void EndOffscreen();
+
+	void BindToFrameBuffer(FTexture* tex);
+
+private:
+
+	bool QuadStereoCheckInitialRenderContextState();
+	void PresentAnaglyph(bool r, bool g, bool b);
+	void PresentSideBySide(int);
+	void PresentTopBottom();
+	void prepareInterleavedPresent(FPresentShaderBase& shader);
+	void PresentColumnInterleaved();
+	void PresentRowInterleaved();
+	void PresentCheckerInterleaved();
+	void PresentQuadStereo();
+
+};
+
+struct TexFilter_s
+{
+	int minfilter;
+	int magfilter;
+	bool mipmapping;
+} ;
+
+
+extern FGLRenderer *GLRenderer;
+
+}
+#endif
diff --git a/source/common/rendering/gles/gles_renderstate.cpp b/source/common/rendering/gles/gles_renderstate.cpp
new file mode 100644
index 000000000..dfd9652ad
--- /dev/null
+++ b/source/common/rendering/gles/gles_renderstate.cpp
@@ -0,0 +1,727 @@
+// 
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2009-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_renderstate.cpp
+** Render state maintenance
+**
+*/
+
+#include "templates.h"
+#include "gles_system.h"
+#include "hw_cvars.h"
+#include "flatvertices.h"
+#include "gles_shader.h"
+#include "gles_renderer.h"
+#include "hw_lightbuffer.h"
+#include "gles_renderbuffers.h"
+#include "gles_hwtexture.h"
+#include "gles_buffers.h"
+#include "hw_clock.h"
+#include "printf.h"
+
+#include "hwrenderer/data/hw_viewpointbuffer.h"
+
+namespace OpenGLESRenderer
+{
+
+FGLRenderState gl_RenderState;
+
+static VSMatrix identityMatrix(1);
+
+static void matrixToGL(const VSMatrix &mat, int loc)
+{
+	glUniformMatrix4fv(loc, 1, false, (float*)&mat);
+}
+
+//==========================================================================
+//
+// This only gets called once upon setup.
+// With OpenGL the state is persistent and cannot be cleared, once set up.
+//
+//==========================================================================
+
+void FGLRenderState::Reset()
+{
+	FRenderState::Reset();
+	mVertexBuffer = mCurrentVertexBuffer = nullptr;
+	mGlossiness = 0.0f;
+	mSpecularLevel = 0.0f;
+	mShaderTimer = 0.0f;
+
+	stRenderStyle = DefaultRenderStyle();
+	stSrcBlend = stDstBlend = -1;
+	stBlendEquation = -1;
+	stAlphaTest = 0;
+	mLastDepthClamp = true;
+
+	mEffectState = 0;
+	activeShader = nullptr;
+
+	mCurrentVertexBuffer = nullptr;
+	mCurrentVertexOffsets[0] = mVertexOffsets[0] = 0;
+	mCurrentIndexBuffer = nullptr;
+
+}
+
+//==========================================================================
+//
+// Apply shader settings
+//
+//==========================================================================
+
+bool FGLRenderState::ApplyShader()
+{
+	static const float nulvec[] = { 0.f, 0.f, 0.f, 0.f };
+	
+	// Need to calc light data now in order to select correct shader
+	float* lightPtr = NULL;
+	int modLights = 0;
+	int subLights = 0;
+	int addLights = 0;
+
+	if (mLightIndex >= 0)
+	{
+		lightPtr = ((float*)screen->mLights->GetBuffer()->Memory());
+		lightPtr += ((int64_t)mLightIndex * 4);
+		//float array[64];
+		//memcpy(array, ptr, 4 * 64);
+
+		// Calculate how much light data there is to upload, this is stored in the first 4 floats
+		modLights = int(lightPtr[1]) / LIGHT_VEC4_NUM;
+		subLights = (int(lightPtr[2]) - int(lightPtr[1])) / LIGHT_VEC4_NUM;
+		addLights = (int(lightPtr[3]) - int(lightPtr[2])) / LIGHT_VEC4_NUM;
+
+		// Here we limit the number of lights, but dont' change the light data so priority has to be mod, sub then add
+		if (modLights > gles.maxlights)
+			modLights = gles.maxlights;
+
+		if (modLights + subLights > gles.maxlights)
+			subLights = gles.maxlights - modLights;
+
+		if (modLights + subLights + addLights > gles.maxlights)
+			addLights = gles.maxlights - modLights - subLights;
+
+
+		// Skip passed the first 4 floats so the upload below only contains light data
+		lightPtr += 4;
+	}
+
+	ShaderFlavourData flavour;
+
+	flavour.textureMode = (mTextureMode == TM_NORMAL && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode);
+	
+	if (flavour.textureMode == -1)
+		flavour.textureMode = 0;
+
+	flavour.texFlags = mTextureModeFlags; if (!mBrightmapEnabled) flavour.texFlags &= ~(TEXF_Brightmap | TEXF_Glowmap);
+	flavour.texFlags >>= 16; //Move flags to start of word
+
+	flavour.blendFlags = (int)(mStreamData.uTextureAddColor.a + 0.01);
+
+	flavour.twoDFog = false;
+	flavour.fogEnabled = false;
+	flavour.fogEquationRadial = false;
+	flavour.colouredFog = false;
+
+	flavour.fogEquationRadial = (gl_fogmode == 2);
+	
+	flavour.twoDFog = false;
+	flavour.fogEnabled = false;
+	flavour.colouredFog = false;
+
+	if (mFogEnabled)
+	{
+		if (mFogEnabled == 2)
+		{
+			flavour.twoDFog = true;
+		}
+		else if (gl_fogmode)
+		{
+			flavour.fogEnabled = true;
+
+			if ((GetFogColor() & 0xffffff) != 0)
+				flavour.colouredFog = true;
+		}
+	}
+
+	flavour.doDesaturate = mStreamData.uDesaturationFactor != 0;
+	flavour.useULightLevel = (mLightParms[3] >= 0); //#define uLightLevel uLightAttr.a
+
+	// Yes create shaders for all combinations of active lights to avoid more branches
+	flavour.dynLightsMod = (modLights > 0);
+	flavour.dynLightsSub = (subLights > 0);
+	flavour.dynLightsAdd = (addLights > 0);
+
+	flavour.useObjectColor2 = (mStreamData.uObjectColor2.a > 0);
+	flavour.useGlowTopColor = mGlowEnabled && (mStreamData.uGlowTopColor[3] > 0);
+	flavour.useGlowBottomColor = mGlowEnabled && (mStreamData.uGlowBottomColor[3] > 0);
+	
+	flavour.useColorMap = (mColorMapSpecial >= CM_FIRSTSPECIALCOLORMAP) || (mColorMapFlash != 1);
+
+	flavour.buildLighting = (mHwUniforms->mPalLightLevels >> 16) == 5; // Build engine mode
+	flavour.bandedSwLight = !!(mHwUniforms->mPalLightLevels & 0xFF);
+
+#ifdef NPOT_EMULATION
+	flavour.npotEmulation = (mStreamData.uNpotEmulation.Y != 0);
+#endif
+
+	if (mSpecialEffect > EFF_NONE)
+	{
+		activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect, mPassType, flavour);
+	}
+	else
+	{
+		activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : SHADER_NoTexture, mAlphaThreshold >= 0.f, mPassType);
+
+		activeShader->Bind(flavour);
+	}
+
+	
+	if (mHwUniforms)
+	{
+		//matrixToGL(mHwUniforms->mProjectionMatrix, activeShader->cur->ProjectionMatrix_index);
+		activeShader->cur->muProjectionMatrix.Set(&mHwUniforms->mProjectionMatrix);
+		activeShader->cur->muViewMatrix.Set(&mHwUniforms->mViewMatrix);
+		activeShader->cur->muNormalViewMatrix.Set(&mHwUniforms->mNormalViewMatrix);
+
+		//matrixToGL(mHwUniforms->mViewMatrix, activeShader->cur->ViewMatrix_index);
+		//matrixToGL(mHwUniforms->mNormalViewMatrix, activeShader->cur->NormalViewMatrix_index);
+
+		activeShader->cur->muCameraPos.Set(&mHwUniforms->mCameraPos.X);
+		activeShader->cur->muClipLine.Set(&mHwUniforms->mClipLine.X);
+
+		activeShader->cur->muGlobVis.Set(mHwUniforms->mGlobVis);
+
+		activeShader->cur->muPalLightLevels.Set(mHwUniforms->mPalLightLevels & 0xFF); // JUST pass the pal levels, clear the top bits
+		activeShader->cur->muViewHeight.Set(mHwUniforms->mViewHeight);
+		activeShader->cur->muClipHeight.Set(mHwUniforms->mClipHeight);
+		activeShader->cur->muClipHeightDirection.Set(mHwUniforms->mClipHeightDirection);
+		//activeShader->cur->muShadowmapFilter.Set(mHwUniforms->mShadowmapFilter);
+	}
+
+	glVertexAttrib4fv(VATTR_COLOR, &mStreamData.uVertexColor.X);
+	glVertexAttrib4fv(VATTR_NORMAL, &mStreamData.uVertexNormal.X);
+
+	activeShader->cur->muDesaturation.Set(mStreamData.uDesaturationFactor);
+	//activeShader->cur->muFogEnabled.Set(fogset);
+
+	int f = mTextureModeFlags;
+	if (!mBrightmapEnabled) f &= ~(TEXF_Brightmap | TEXF_Glowmap);
+	//activeShader->cur->muTextureMode.Set((mTextureMode == TM_NORMAL && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode) | f);
+	activeShader->cur->muLightParms.Set(mLightParms);
+	activeShader->cur->muFogColor.Set(mStreamData.uFogColor);
+	activeShader->cur->muObjectColor.Set(mStreamData.uObjectColor);
+	activeShader->cur->muDynLightColor.Set(&mStreamData.uDynLightColor.X);
+	activeShader->cur->muInterpolationFactor.Set(mStreamData.uInterpolationFactor);
+	activeShader->cur->muTimer.Set((double)(screen->FrameTime - firstFrame) * (double)mShaderTimer / 1000.);
+	activeShader->cur->muAlphaThreshold.Set(mAlphaThreshold);
+	activeShader->cur->muClipSplit.Set(mClipSplit);
+	activeShader->cur->muSpecularMaterial.Set(mGlossiness, mSpecularLevel);
+	activeShader->cur->muAddColor.Set(mStreamData.uAddColor);
+	activeShader->cur->muTextureAddColor.Set(mStreamData.uTextureAddColor);
+	activeShader->cur->muTextureModulateColor.Set(mStreamData.uTextureModulateColor);
+	activeShader->cur->muTextureBlendColor.Set(mStreamData.uTextureBlendColor);
+	activeShader->cur->muDetailParms.Set(&mStreamData.uDetailParms.X);
+#ifdef NPOT_EMULATION
+	activeShader->cur->muNpotEmulation.Set(&mStreamData.uNpotEmulation.X);
+#endif
+
+	if (flavour.useColorMap)
+	{
+		if (mColorMapSpecial < CM_FIRSTSPECIALCOLORMAP || mColorMapSpecial >= CM_MAXCOLORMAP)
+		{
+			activeShader->cur->muFixedColormapStart.Set( 0,0,0, mColorMapFlash );
+			activeShader->cur->muFixedColormapRange.Set( 0,0,0, 1.f );
+		}
+		else
+		{
+			FSpecialColormap* scm = &SpecialColormaps[mColorMapSpecial - CM_FIRSTSPECIALCOLORMAP];
+
+			//uniforms.MapStart = { scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], flash };
+			activeShader->cur->muFixedColormapStart.Set( scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], mColorMapFlash );
+			activeShader->cur->muFixedColormapRange.Set( scm->ColorizeEnd[0] - scm->ColorizeStart[0],
+				scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f );
+		}
+	}
+
+	if (mGlowEnabled || activeShader->cur->currentglowstate)
+	{
+		activeShader->cur->muGlowTopColor.Set(&mStreamData.uGlowTopColor.X);
+		activeShader->cur->muGlowBottomColor.Set(&mStreamData.uGlowBottomColor.X);
+		activeShader->cur->muGlowTopPlane.Set(&mStreamData.uGlowTopPlane.X);
+		activeShader->cur->muGlowBottomPlane.Set(&mStreamData.uGlowBottomPlane.X);
+		activeShader->cur->currentglowstate = mGlowEnabled;
+	}
+
+	if (mGradientEnabled || activeShader->cur->currentgradientstate)
+	{
+		activeShader->cur->muObjectColor2.Set(mStreamData.uObjectColor2);
+		activeShader->cur->muGradientTopPlane.Set(&mStreamData.uGradientTopPlane.X);
+		activeShader->cur->muGradientBottomPlane.Set(&mStreamData.uGradientBottomPlane.X);
+		activeShader->cur->currentgradientstate = mGradientEnabled;
+	}
+
+	if (mSplitEnabled || activeShader->cur->currentsplitstate)
+	{
+		activeShader->cur->muSplitTopPlane.Set(&mStreamData.uSplitTopPlane.X);
+		activeShader->cur->muSplitBottomPlane.Set(&mStreamData.uSplitBottomPlane.X);
+		activeShader->cur->currentsplitstate = mSplitEnabled;
+	}
+
+
+	if (mTextureMatrixEnabled)
+	{
+		matrixToGL(mTextureMatrix, activeShader->cur->texturematrix_index);
+		activeShader->cur->currentTextureMatrixState = true;
+	}
+	else if (activeShader->cur->currentTextureMatrixState)
+	{
+		activeShader->cur->currentTextureMatrixState = false;
+		matrixToGL(identityMatrix, activeShader->cur->texturematrix_index);
+	}
+
+	if (mModelMatrixEnabled)
+	{
+		matrixToGL(mModelMatrix, activeShader->cur->modelmatrix_index);
+		VSMatrix norm;
+		norm.computeNormalMatrix(mModelMatrix);
+		matrixToGL(norm, activeShader->cur->normalmodelmatrix_index);
+		activeShader->cur->currentModelMatrixState = true;
+	}
+	else if (activeShader->cur->currentModelMatrixState)
+	{
+		activeShader->cur->currentModelMatrixState = false;
+		matrixToGL(identityMatrix, activeShader->cur->modelmatrix_index);
+		matrixToGL(identityMatrix, activeShader->cur->normalmodelmatrix_index);
+	}
+
+	// Upload the light data
+	if (mLightIndex >= 0)
+	{
+		int totalLights = modLights + subLights + addLights;
+
+		// Calculate the total number of vec4s we need
+		int totalVectors = totalLights * LIGHT_VEC4_NUM;
+		
+		// TODO!!! If there are too many lights we need to remove some of the lights and modify the data
+		// At the moment the shader will just try to read off the end of the array...
+		if (totalVectors > gles.numlightvectors)
+			totalVectors = gles.numlightvectors;
+
+		glUniform4fv(activeShader->cur->lights_index, totalVectors, lightPtr);
+
+		int range[4] = { 0, 
+			modLights * LIGHT_VEC4_NUM, 
+			(modLights + subLights) * LIGHT_VEC4_NUM, 
+			(modLights + subLights + addLights) * LIGHT_VEC4_NUM };
+
+		activeShader->cur->muLightRange.Set(range);
+	}
+	
+	return true;
+}
+
+
+//==========================================================================
+//
+// Apply State
+//
+//==========================================================================
+
+void FGLRenderState::ApplyState()
+{
+	if (mRenderStyle != stRenderStyle)
+	{
+		ApplyBlendMode();
+		stRenderStyle = mRenderStyle;
+	}
+
+	if (mSplitEnabled != stSplitEnabled)
+	{
+		/*
+		if (mSplitEnabled)
+		{
+			glEnable(GL_CLIP_DISTANCE3);
+			glEnable(GL_CLIP_DISTANCE4);
+		}
+		else
+		{
+			glDisable(GL_CLIP_DISTANCE3);
+			glDisable(GL_CLIP_DISTANCE4);
+		}
+		*/
+		stSplitEnabled = mSplitEnabled;
+	}
+
+	if (mMaterial.mChanged)
+	{
+		ApplyMaterial(mMaterial.mMaterial, mMaterial.mClampMode, mMaterial.mTranslation, mMaterial.mOverrideShader);
+		mMaterial.mChanged = false;
+	}
+
+	if (mBias.mChanged)
+	{
+		if (mBias.mFactor == 0 && mBias.mUnits == 0)
+		{
+			glDisable(GL_POLYGON_OFFSET_FILL);
+		}
+		else
+		{
+			glEnable(GL_POLYGON_OFFSET_FILL);
+		}
+		glPolygonOffset(mBias.mFactor, mBias.mUnits);
+		mBias.mChanged = false;
+	}
+}
+
+void FGLRenderState::ApplyBuffers()
+{
+	if (mVertexBuffer != mCurrentVertexBuffer || mVertexOffsets[0] != mCurrentVertexOffsets[0] || mVertexOffsets[1] != mCurrentVertexOffsets[1])
+	{
+		assert(mVertexBuffer != nullptr);
+		static_cast<GLVertexBuffer*>(mVertexBuffer)->Bind(mVertexOffsets);
+		mCurrentVertexBuffer = mVertexBuffer;
+		mCurrentVertexOffsets[0] = mVertexOffsets[0];
+		mCurrentVertexOffsets[1] = mVertexOffsets[1];
+	}
+	if (mIndexBuffer != mCurrentIndexBuffer)
+	{
+		if (mIndexBuffer) static_cast<GLIndexBuffer*>(mIndexBuffer)->Bind();
+		mCurrentIndexBuffer = mIndexBuffer;
+	}
+}
+
+void FGLRenderState::Apply()
+{
+	ApplyState();
+	ApplyBuffers();
+	ApplyShader();
+}
+
+//===========================================================================
+// 
+//	Binds a texture to the renderer
+//
+//===========================================================================
+
+void FGLRenderState::ApplyMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader)
+{
+	if (mat->Source()->isHardwareCanvas())
+	{
+		mTempTM = TM_OPAQUE;
+	}
+	else
+	{
+		mTempTM = TM_NORMAL;
+	}
+	auto tex = mat->Source();
+	mEffectState = overrideshader >= 0 ? overrideshader : mat->GetShaderIndex();
+	mShaderTimer = tex->GetShaderSpeed();
+	SetSpecular(tex->GetGlossiness(), tex->GetSpecularLevel());
+	if (tex->isHardwareCanvas()) static_cast<FCanvasTexture*>(tex->GetTexture())->NeedUpdate();
+
+	clampmode = tex->GetClampMode(clampmode);
+	
+	// avoid rebinding the same texture multiple times.
+	if (mat == lastMaterial && lastClamp == clampmode && translation == lastTranslation) return;
+	lastMaterial = mat;
+	lastClamp = clampmode;
+	lastTranslation = translation;
+
+	int usebright = false;
+	int maxbound = 0;
+
+	int numLayers = mat->NumLayers();
+	MaterialLayerInfo* layer;
+	auto base = static_cast<FHardwareTexture*>(mat->GetLayer(0, translation, &layer));
+
+	if (base->BindOrCreate(tex->GetTexture(), 0, clampmode, translation, layer->scaleFlags))
+	{
+		if (!(layer->scaleFlags & CTF_Indexed))
+		{
+			for (int i = 1; i < numLayers; i++)
+			{
+				auto systex = static_cast<FHardwareTexture*>(mat->GetLayer(i, 0, &layer));
+				// fixme: Upscale flags must be disabled for certain layers.
+				systex->BindOrCreate(layer->layerTexture, i, clampmode, 0, layer->scaleFlags);
+				maxbound = i;
+			}
+		}
+		else
+		{
+			for (int i = 1; i < 3; i++)
+			{
+				auto systex = static_cast<FHardwareTexture*>(mat->GetLayer(i, translation, &layer));
+				systex->Bind(i, false);
+				maxbound = i;
+			}
+		}
+	}
+	// unbind everything from the last texture that's still active
+	for (int i = maxbound + 1; i <= maxBoundMaterial; i++)
+	{
+		FHardwareTexture::Unbind(i);
+		maxBoundMaterial = maxbound;
+	}
+}
+
+//==========================================================================
+//
+// Apply blend mode from RenderStyle
+//
+//==========================================================================
+
+void FGLRenderState::ApplyBlendMode()
+{
+	static int blendstyles[] = { GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA };
+	static int renderops[] = { 0, GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, -1, -1, -1, -1,
+		-1, -1, -1, -1, -1, -1, -1, -1 };
+
+	int srcblend = blendstyles[mRenderStyle.SrcAlpha%STYLEALPHA_MAX];
+	int dstblend = blendstyles[mRenderStyle.DestAlpha%STYLEALPHA_MAX];
+	int blendequation = renderops[mRenderStyle.BlendOp & 15];
+
+	if (blendequation == -1)	// This was a fuzz style.
+	{
+		srcblend = GL_DST_COLOR;
+		dstblend = GL_ONE_MINUS_SRC_ALPHA;
+		blendequation = GL_FUNC_ADD;
+	}
+
+	// Checks must be disabled until all draw code has been converted.
+	if (srcblend != stSrcBlend || dstblend != stDstBlend)
+	{
+		stSrcBlend = srcblend;
+		stDstBlend = dstblend;
+		glBlendFunc(srcblend, dstblend);
+	}
+	if (blendequation != stBlendEquation)
+	{
+		stBlendEquation = blendequation;
+		glBlendEquation(blendequation);
+	}
+
+}
+
+//==========================================================================
+//
+// API dependent draw calls
+//
+//==========================================================================
+
+static int dt2gl[] = { GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP };
+
+void FGLRenderState::Draw(int dt, int index, int count, bool apply)
+{
+		if (apply)
+		{
+			Apply();
+		}
+	drawcalls.Clock();
+	glDrawArrays(dt2gl[dt], index, count);
+	drawcalls.Unclock();
+}
+
+void FGLRenderState::DrawIndexed(int dt, int index, int count, bool apply)
+{
+	if (apply)
+	{
+		Apply();
+	}
+	drawcalls.Clock();
+	glDrawElements(dt2gl[dt], count, GL_UNSIGNED_INT, (void*)(intptr_t)(index * sizeof(uint32_t)));
+	drawcalls.Unclock();
+}
+
+void FGLRenderState::SetDepthMask(bool on)
+{
+	glDepthMask(on);
+}
+
+void FGLRenderState::SetDepthFunc(int func)
+{
+	static int df2gl[] = { GL_LESS, GL_LEQUAL, GL_ALWAYS };
+	glDepthFunc(df2gl[func]);
+}
+
+void FGLRenderState::SetDepthRange(float min, float max)
+{
+	glDepthRangef(min, max);
+}
+
+void FGLRenderState::SetColorMask(bool r, bool g, bool b, bool a)
+{
+	glColorMask(r, g, b, a);
+}
+
+void FGLRenderState::SetStencil(int offs, int op, int flags = -1)
+{
+	static int op2gl[] = { GL_KEEP, GL_INCR, GL_DECR };
+
+	glStencilFunc(GL_EQUAL, screen->stencilValue + offs, ~0);		// draw sky into stencil
+	glStencilOp(GL_KEEP, GL_KEEP, op2gl[op]);		// this stage doesn't modify the stencil
+
+	if (flags != -1)
+	{
+		bool cmon = !(flags & SF_ColorMaskOff);
+		glColorMask(cmon, cmon, cmon, cmon);						// don't write to the graphics buffer
+		glDepthMask(!(flags & SF_DepthMaskOff));
+	}
+}
+
+void FGLRenderState::ToggleState(int state, bool on)
+{
+	if (on)
+	{
+		glEnable(state);
+	}
+	else
+	{
+		glDisable(state);
+	}
+}
+
+void FGLRenderState::SetCulling(int mode)
+{
+	if (mode != Cull_None)
+	{
+		glEnable(GL_CULL_FACE);
+		glFrontFace(mode == Cull_CCW ? GL_CCW : GL_CW);
+	}
+	else
+	{
+		glDisable(GL_CULL_FACE);
+	}
+}
+
+void FGLRenderState::EnableClipDistance(int num, bool state)
+{
+
+}
+
+void FGLRenderState::Clear(int targets)
+{
+	// This always clears to default values.
+	int gltarget = 0;
+	if (targets & CT_Depth)
+	{
+		gltarget |= GL_DEPTH_BUFFER_BIT;
+
+		glClearDepthf(1);
+	}
+	if (targets & CT_Stencil)
+	{
+		gltarget |= GL_STENCIL_BUFFER_BIT;
+		glClearStencil(0);
+	}
+	if (targets & CT_Color)
+	{
+		gltarget |= GL_COLOR_BUFFER_BIT;
+		glClearColor(screen->mSceneClearColor[0], screen->mSceneClearColor[1], screen->mSceneClearColor[2], screen->mSceneClearColor[3]);
+	}
+	glClear(gltarget);
+}
+
+void FGLRenderState::EnableStencil(bool on)
+{
+	ToggleState(GL_STENCIL_TEST, on);
+}
+
+void FGLRenderState::SetScissor(int x, int y, int w, int h)
+{
+	if (w > -1)
+	{
+		glEnable(GL_SCISSOR_TEST);
+		glScissor(x, y, w, h);
+	}
+	else
+	{
+		glDisable(GL_SCISSOR_TEST);
+	}
+}
+
+void FGLRenderState::SetViewport(int x, int y, int w, int h)
+{
+	glViewport(x, y, w, h);
+}
+
+void FGLRenderState::EnableDepthTest(bool on)
+{
+	ToggleState(GL_DEPTH_TEST, on);
+}
+
+void FGLRenderState::EnableMultisampling(bool on)
+{
+
+}
+
+void FGLRenderState::EnableLineSmooth(bool on)
+{
+
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+void FGLRenderState::ClearScreen()
+{
+
+	screen->mViewpoints->Set2D(*this, SCREENWIDTH, SCREENHEIGHT);
+	SetColor(0, 0, 0);
+	Apply();
+
+	glDisable(GL_DEPTH_TEST);
+
+	glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4);
+
+	glEnable(GL_DEPTH_TEST);
+
+}
+
+
+
+//==========================================================================
+//
+// Below are less frequently altrered state settings which do not get
+// buffered by the state object, but set directly instead.
+//
+//==========================================================================
+
+bool FGLRenderState::SetDepthClamp(bool on)
+{
+	bool res = mLastDepthClamp;
+	/*
+	if (!on) glDisable(GL_DEPTH_CLAMP);
+	else glEnable(GL_DEPTH_CLAMP);
+	*/
+	mLastDepthClamp = on;
+	return res;
+}
+void FGLRenderState::ApplyViewport(void* data)
+{	
+	mHwUniforms = reinterpret_cast<HWViewpointUniforms*>(static_cast<uint8_t*>(data));
+
+}
+}
diff --git a/source/common/rendering/gles/gles_renderstate.h b/source/common/rendering/gles/gles_renderstate.h
new file mode 100644
index 000000000..7b7768541
--- /dev/null
+++ b/source/common/rendering/gles/gles_renderstate.h
@@ -0,0 +1,152 @@
+// 
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2009-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+
+#ifndef __GL_RENDERSTATE_H
+#define __GL_RENDERSTATE_H
+
+#include <algorithm>
+#include <string.h>
+#include "matrix.h"
+#include "hw_renderstate.h"
+#include "hw_material.h"
+#include "c_cvars.h"
+#include "hwrenderer/data/hw_viewpointuniforms.h"
+
+namespace OpenGLESRenderer
+{
+
+class FShader;
+struct HWSectorPlane;
+
+class FGLRenderState final : public FRenderState
+{
+	uint8_t mLastDepthClamp : 1;
+
+	float mGlossiness, mSpecularLevel;
+	float mShaderTimer;
+
+	int mEffectState;
+	int mTempTM = TM_NORMAL;
+
+	FRenderStyle stRenderStyle;
+	int stSrcBlend, stDstBlend;
+	bool stAlphaTest;
+	bool stSplitEnabled;
+	int stBlendEquation;
+
+	FShader *activeShader;
+
+	int mNumDrawBuffers = 1;
+
+	bool ApplyShader();
+	void ApplyState();
+
+	// Texture binding state
+	FMaterial *lastMaterial = nullptr;
+	int lastClamp = 0;
+	int lastTranslation = 0;
+	int maxBoundMaterial = -1;
+	size_t mLastMappedLightIndex = SIZE_MAX;
+
+	IVertexBuffer *mCurrentVertexBuffer;
+	int mCurrentVertexOffsets[2];	// one per binding point
+	IIndexBuffer *mCurrentIndexBuffer;
+
+	HWViewpointUniforms* mHwUniforms = nullptr;
+
+public:
+
+	FGLRenderState()
+	{
+		Reset();
+	}
+
+	void Reset();
+
+	void ClearLastMaterial()
+	{
+		lastMaterial = nullptr;
+	}
+
+	void ApplyMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader);
+
+	void Apply();
+	void ApplyBuffers();
+	void ApplyBlendMode();
+
+	void ResetVertexBuffer()
+	{
+		// forces rebinding with the next 'apply' call.
+		mCurrentVertexBuffer = nullptr;
+		mCurrentIndexBuffer = nullptr;
+	}
+
+	void SetSpecular(float glossiness, float specularLevel)
+	{
+		mGlossiness = glossiness;
+		mSpecularLevel = specularLevel;
+	}
+
+	void EnableDrawBuffers(int count, bool apply = false) override
+	{
+		/*
+		count = std::min(count, 3);
+		if (mNumDrawBuffers != count)
+		{
+			static GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
+			glDrawBuffers(count, buffers);
+			mNumDrawBuffers = count;
+		}
+		if (apply) Apply();
+		*/
+	}
+
+	void ToggleState(int state, bool on);
+
+	void ClearScreen() override;
+	void Draw(int dt, int index, int count, bool apply = true) override;
+	void DrawIndexed(int dt, int index, int count, bool apply = true) override;
+
+	bool SetDepthClamp(bool on) override;
+	void SetDepthMask(bool on) override;
+	void SetDepthFunc(int func) override;
+	void SetDepthRange(float min, float max) override;
+	void SetColorMask(bool r, bool g, bool b, bool a) override;
+	void SetStencil(int offs, int op, int flags) override;
+	void SetCulling(int mode) override;
+	void EnableClipDistance(int num, bool state) override;
+	void Clear(int targets) override;
+	void EnableStencil(bool on) override;
+	void SetScissor(int x, int y, int w, int h) override;
+	void SetViewport(int x, int y, int w, int h) override;
+	void EnableDepthTest(bool on) override;
+	void EnableMultisampling(bool on) override;
+	void EnableLineSmooth(bool on) override;
+
+	void ApplyViewport(void* data);
+};
+
+extern FGLRenderState gl_RenderState;
+
+}
+
+#endif
diff --git a/source/common/rendering/gles/gles_samplers.cpp b/source/common/rendering/gles/gles_samplers.cpp
new file mode 100644
index 000000000..1e3b16d2d
--- /dev/null
+++ b/source/common/rendering/gles/gles_samplers.cpp
@@ -0,0 +1,178 @@
+/*
+** gl_samplers.cpp
+**
+** Texture sampler handling
+**
+**---------------------------------------------------------------------------
+** Copyright 2015-2019 Christoph Oelckers
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**---------------------------------------------------------------------------
+**
+*/
+#include "gles_system.h"
+#include "c_cvars.h"
+
+#include "hw_cvars.h"
+
+#include "gles_renderer.h"
+#include "gles_samplers.h"
+#include "hw_material.h"
+#include "i_interface.h"
+
+namespace OpenGLESRenderer
+{
+
+extern TexFilter_s TexFilter[];
+
+
+FSamplerManager::FSamplerManager()
+{
+	SetTextureFilterMode();
+}
+
+FSamplerManager::~FSamplerManager()
+{
+
+}
+
+void FSamplerManager::UnbindAll()
+{
+	
+}
+	
+uint8_t FSamplerManager::Bind(int texunit, int num, int lastval)
+{
+
+	int filter = sysCallbacks.DisableTextureFilter && sysCallbacks.DisableTextureFilter() ? 0 : gl_texture_filter;
+
+	glActiveTexture(GL_TEXTURE0 + texunit);
+	switch (num)
+	{
+	case CLAMP_NONE:
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+		if (lastval >= CLAMP_XY_NOMIP)
+		{
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[filter].minfilter);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+		}
+		break;
+
+	case CLAMP_X:
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+		if (lastval >= CLAMP_XY_NOMIP)
+		{
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[filter].minfilter);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+		}
+		break;
+
+	case CLAMP_Y:
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		if (lastval >= CLAMP_XY_NOMIP)
+		{
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[filter].minfilter);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+		}
+		break;
+
+	case CLAMP_XY:
+		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 (lastval >= CLAMP_XY_NOMIP)
+		{
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[filter].minfilter);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+		}
+		break;
+
+	case CLAMP_XY_NOMIP:
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[filter].magfilter);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+		break;
+
+	case CLAMP_NOFILTER:
+	case CLAMP_NOFILTER_X:
+	case CLAMP_NOFILTER_Y:
+	case CLAMP_NOFILTER_XY:
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		break;
+
+	case CLAMP_CAMTEX:
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[filter].magfilter);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+		break;
+	}
+	glActiveTexture(GL_TEXTURE0);
+	return 255;
+}
+
+	
+void FSamplerManager::SetTextureFilterMode()
+{
+	/*
+	GLRenderer->FlushTextures();
+
+	GLint bounds[IHardwareTexture::MAX_TEXTURES];
+	
+	// Unbind all
+	for(int i = IHardwareTexture::MAX_TEXTURES-1; i >= 0; i--)
+	{
+		glActiveTexture(GL_TEXTURE0 + i);
+		glGetIntegerv(GL_SAMPLER_BINDING, &bounds[i]);
+		glBindSampler(i, 0);
+	}
+
+	int filter = sysCallbacks.DisableTextureFilter && sysCallbacks.DisableTextureFilter() ? 0 : gl_texture_filter;
+
+	for (int i = 0; i < 4; i++)
+	{
+		glSamplerParameteri(mSamplers[i], GL_TEXTURE_MIN_FILTER, TexFilter[filter].minfilter);
+		glSamplerParameteri(mSamplers[i], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+		glSamplerParameterf(mSamplers[i], GL_TEXTURE_MAX_ANISOTROPY_EXT, filter > 0? gl_texture_filter_anisotropic : 1.0);
+	}
+	glSamplerParameteri(mSamplers[CLAMP_XY_NOMIP], GL_TEXTURE_MIN_FILTER, TexFilter[filter].magfilter);
+	glSamplerParameteri(mSamplers[CLAMP_XY_NOMIP], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+	glSamplerParameteri(mSamplers[CLAMP_CAMTEX], GL_TEXTURE_MIN_FILTER, TexFilter[filter].magfilter);
+	glSamplerParameteri(mSamplers[CLAMP_CAMTEX], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
+	for(int i = 0; i < IHardwareTexture::MAX_TEXTURES; i++)
+	{
+		glBindSampler(i, bounds[i]);
+	}
+	*/
+}
+
+
+}
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_samplers.h b/source/common/rendering/gles/gles_samplers.h
new file mode 100644
index 000000000..541b409b7
--- /dev/null
+++ b/source/common/rendering/gles/gles_samplers.h
@@ -0,0 +1,27 @@
+#ifndef __GLES_SAMPLERS_H
+#define __GLES_SAMPLERS_H
+
+#include "gles_hwtexture.h"
+#include "textures.h"
+
+namespace OpenGLESRenderer
+{
+
+
+class FSamplerManager
+{
+	void UnbindAll();
+
+public:
+
+	FSamplerManager();
+	~FSamplerManager();
+
+	uint8_t Bind(int texunit, int num, int lastval);
+	void SetTextureFilterMode();
+
+};
+
+}
+#endif
+
diff --git a/source/common/rendering/gles/gles_shader.cpp b/source/common/rendering/gles/gles_shader.cpp
new file mode 100644
index 000000000..4587fd84a
--- /dev/null
+++ b/source/common/rendering/gles/gles_shader.cpp
@@ -0,0 +1,952 @@
+// 
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2004-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+/*
+** gl_shader.cpp
+**
+** GLSL shader handling
+**
+*/
+
+#include "gles_system.h"
+#include "c_cvars.h"
+#include "v_video.h"
+#include "filesystem.h"
+#include "engineerrors.h"
+#include "cmdlib.h"
+#include "md5.h"
+#include "gles_shader.h"
+#include "hw_shaderpatcher.h"
+#include "shaderuniforms.h"
+#include "hw_viewpointuniforms.h"
+#include "hw_lightbuffer.h"
+#include "i_specialpaths.h"
+#include "printf.h"
+#include "version.h"
+
+#include "matrix.h"
+#include "gles_renderer.h"
+#include <map>
+#include <memory>
+
+namespace OpenGLESRenderer
+{
+
+CVAR(Int, gles_glsl_precision, 2, 0); // 0 = low, 1 = medium, 2 = high
+
+FString GetGLSLPrecision()
+{
+	FString str = "precision highp int;\n \
+		           precision highp float;\n";
+
+	if (gles_glsl_precision == 0)
+		str.Substitute("highp", "lowp");
+	else if (gles_glsl_precision == 1)
+		str.Substitute("highp", "mediump");
+
+	return str;
+}
+
+struct ProgramBinary
+{
+	uint32_t format;
+	TArray<uint8_t> data;
+};
+
+static const char *ShaderMagic = "ZDSC";
+
+static std::map<FString, std::unique_ptr<ProgramBinary>> ShaderCache; // Not a TMap because it doesn't support unique_ptr move semantics
+
+bool IsShaderCacheActive()
+{
+	static bool active = true;
+	static bool firstcall = true;
+
+	if (firstcall)
+	{
+		const char *vendor = (const char *)glGetString(GL_VENDOR);
+		active = !(strstr(vendor, "Intel") == nullptr);
+		firstcall = false;
+	}
+	return active;
+}
+
+static FString CalcProgramBinaryChecksum(const FString &vertex, const FString &fragment)
+{
+	const GLubyte *vendor = glGetString(GL_VENDOR);
+	const GLubyte *renderer = glGetString(GL_RENDERER);
+	const GLubyte *version = glGetString(GL_VERSION);
+
+	uint8_t digest[16];
+	MD5Context md5;
+	md5.Update(vendor, (unsigned int)strlen((const char*)vendor));
+	md5.Update(renderer, (unsigned int)strlen((const char*)renderer));
+	md5.Update(version, (unsigned int)strlen((const char*)version));
+	md5.Update((const uint8_t *)vertex.GetChars(), (unsigned int)vertex.Len());
+	md5.Update((const uint8_t *)fragment.GetChars(), (unsigned int)fragment.Len());
+	md5.Final(digest);
+
+	char hexdigest[33];
+	for (int i = 0; i < 16; i++)
+	{
+		int v = digest[i] >> 4;
+		hexdigest[i * 2] = v < 10 ? ('0' + v) : ('a' + v - 10);
+		v = digest[i] & 15;
+		hexdigest[i * 2 + 1] = v < 10 ? ('0' + v) : ('a' + v - 10);
+	}
+	hexdigest[32] = 0;
+	return hexdigest;
+}
+
+static FString CreateProgramCacheName(bool create)
+{
+	FString path = M_GetCachePath(create);
+	if (create) CreatePath(path);
+	path << "/shadercache.zdsc";
+	return path;
+}
+
+static void LoadShaders()
+{
+	static bool loaded = false;
+	if (loaded)
+		return;
+	loaded = true;
+
+	try
+	{
+		FString path = CreateProgramCacheName(false);
+		FileReader fr;
+		if (!fr.OpenFile(path))
+			I_Error("Could not open shader file");
+
+		char magic[4];
+		fr.Read(magic, 4);
+		if (memcmp(magic, ShaderMagic, 4) != 0)
+			I_Error("Not a shader cache file");
+
+		uint32_t count = fr.ReadUInt32();
+		if (count > 512)
+			I_Error("Too many shaders cached");
+
+		for (uint32_t i = 0; i < count; i++)
+		{
+			char hexdigest[33];
+			if (fr.Read(hexdigest, 32) != 32)
+				I_Error("Read error");
+			hexdigest[32] = 0;
+
+			std::unique_ptr<ProgramBinary> binary(new ProgramBinary());
+			binary->format = fr.ReadUInt32();
+			uint32_t size = fr.ReadUInt32();
+			if (size > 1024 * 1024)
+				I_Error("Shader too big, probably file corruption");
+
+			binary->data.Resize(size);
+			if (fr.Read(binary->data.Data(), binary->data.Size()) != binary->data.Size())
+				I_Error("Read error");
+
+			ShaderCache[hexdigest] = std::move(binary);
+		}
+	}
+	catch (...)
+	{
+		ShaderCache.clear();
+	}
+}
+
+static void SaveShaders()
+{
+	FString path = CreateProgramCacheName(true);
+	std::unique_ptr<FileWriter> fw(FileWriter::Open(path));
+	if (fw)
+	{
+		uint32_t count = (uint32_t)ShaderCache.size();
+		fw->Write(ShaderMagic, 4);
+		fw->Write(&count, sizeof(uint32_t));
+		for (const auto &it : ShaderCache)
+		{
+			uint32_t size = it.second->data.Size();
+			fw->Write(it.first.GetChars(), 32);
+			fw->Write(&it.second->format, sizeof(uint32_t));
+			fw->Write(&size, sizeof(uint32_t));
+			fw->Write(it.second->data.Data(), it.second->data.Size());
+		}
+	}
+}
+
+TArray<uint8_t> LoadCachedProgramBinary(const FString &vertex, const FString &fragment, uint32_t &binaryFormat)
+{
+	LoadShaders();
+
+	auto it = ShaderCache.find(CalcProgramBinaryChecksum(vertex, fragment));
+	if (it != ShaderCache.end())
+	{
+		binaryFormat = it->second->format;
+		return it->second->data;
+	}
+	else
+	{
+		binaryFormat = 0;
+		return {};
+	}
+}
+
+void SaveCachedProgramBinary(const FString &vertex, const FString &fragment, const TArray<uint8_t> &binary, uint32_t binaryFormat)
+{
+	auto &entry = ShaderCache[CalcProgramBinaryChecksum(vertex, fragment)];
+	entry.reset(new ProgramBinary());
+	entry->format = binaryFormat;
+	entry->data = binary;
+
+	SaveShaders();
+}
+
+bool FShader::Configure(const char* name, const char* vert_prog_lump, const char* fragprog, const char* fragprog2, const char* light_fragprog, const char* defines)
+{
+	mVertProg = vert_prog_lump;
+	mFragProg = fragprog;
+	mFragProg2 = fragprog2;
+	mLightProg = light_fragprog;
+	mDefinesBase = defines;
+
+	return true;
+}
+
+void FShader::LoadVariant()
+{
+	//mDefinesBase
+	Load(mName.GetChars(), mVertProg, mFragProg, mFragProg2, mLightProg, mDefinesBase);
+}
+
+bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * frag_prog_lump_, const char * proc_prog_lump_, const char * light_fragprog_, const char * defines)
+{
+	ShaderVariantData* shaderData = new ShaderVariantData();
+
+	FString vert_prog_lump = vert_prog_lump_;
+	FString frag_prog_lump = frag_prog_lump_;
+	FString	proc_prog_lump = proc_prog_lump_;
+	FString light_fragprog = light_fragprog_;
+
+	vert_prog_lump.Substitute("shaders/", "shaders_gles/");
+	frag_prog_lump.Substitute("shaders/", "shaders_gles/");
+	proc_prog_lump.Substitute("shaders/", "shaders_gles/");
+	light_fragprog.Substitute("shaders/", "shaders_gles/");
+
+	//light_fragprog.Substitute("material_pbr", "material_normal");
+
+	if(light_fragprog.Len())
+		light_fragprog = "shaders_gles/glsl/material_normal.fp"; // NOTE: Always use normal material for now, ignore others
+
+
+	static char buffer[10000];
+	FString error;
+
+	FString i_data = GetGLSLPrecision();
+
+	i_data += R"(
+
+		// light buffers
+		uniform vec4 lights[MAXIMUM_LIGHT_VECTORS];
+		
+		uniform	mat4 ProjectionMatrix;
+		uniform	mat4 ViewMatrix;
+		uniform	mat4 NormalViewMatrix;
+
+		uniform	vec4 uCameraPos;
+		uniform	vec4 uClipLine;
+
+		uniform	float uGlobVis;			// uGlobVis = R_GetGlobVis(r_visibility) / 32.0
+		uniform	int uPalLightLevels;	
+		uniform	int uViewHeight;		// Software fuzz scaling
+		uniform	float uClipHeight;
+		uniform	float uClipHeightDirection;
+		uniform	int uShadowmapFilter;
+
+		uniform int uTextureMode;
+		uniform vec2 uClipSplit;
+		uniform float uAlphaThreshold;
+
+		// colors
+		uniform vec4 uObjectColor;
+		uniform vec4 uObjectColor2;
+		uniform vec4 uDynLightColor;
+		uniform vec4 uAddColor;
+		uniform vec4 uTextureBlendColor;
+		uniform vec4 uTextureModulateColor;
+		uniform vec4 uTextureAddColor;
+		uniform vec4 uFogColor;
+		uniform float uDesaturationFactor;
+		uniform float uInterpolationFactor;
+
+		// Glowing walls stuff
+		uniform vec4 uGlowTopPlane;
+		uniform vec4 uGlowTopColor;
+		uniform vec4 uGlowBottomPlane;
+		uniform vec4 uGlowBottomColor;
+
+		uniform vec4 uGradientTopPlane;
+		uniform vec4 uGradientBottomPlane;
+
+		uniform vec4 uSplitTopPlane;
+		uniform vec4 uSplitBottomPlane;
+
+		uniform vec4 uDetailParms;
+		// Lighting + Fog
+		uniform vec4 uLightAttr;
+		#define uLightLevel uLightAttr.a
+		#define uFogDensity uLightAttr.b
+		#define uLightFactor uLightAttr.g
+		#define uLightDist uLightAttr.r
+		//uniform int uFogEnabled;
+
+		// dynamic lights
+		uniform ivec4 uLightRange;
+
+		// Blinn glossiness and specular level
+		uniform vec2 uSpecularMaterial;
+
+		// matrices
+		uniform mat4 ModelMatrix;
+		uniform mat4 NormalModelMatrix;
+		uniform mat4 TextureMatrix;
+
+		uniform vec4 uFixedColormapStart;
+		uniform vec4 uFixedColormapRange;
+
+		// textures
+		uniform sampler2D tex;
+		uniform sampler2D ShadowMap;
+		uniform sampler2D texture2;
+		uniform sampler2D texture3;
+		uniform sampler2D texture4;
+		uniform sampler2D texture5;
+		uniform sampler2D texture6;
+		uniform sampler2D texture7;
+		uniform sampler2D texture8;
+		uniform sampler2D texture9;
+		uniform sampler2D texture10;
+		uniform sampler2D texture11;
+
+		// timer data
+		uniform float timer;
+
+		// material types
+		#if defined(SPECULAR)
+		#define normaltexture texture2
+		#define speculartexture texture3
+		#define brighttexture texture4
+		#define detailtexture texture5
+		#define glowtexture texture6
+		#elif defined(PBR)
+		#define normaltexture texture2
+		#define metallictexture texture3
+		#define roughnesstexture texture4
+		#define aotexture texture5
+		#define brighttexture texture6
+		#define detailtexture texture7
+		#define glowtexture texture8
+		#else
+		#define brighttexture texture2
+		#define detailtexture texture3
+		#define glowtexture texture4
+		#endif
+	)";
+	
+#ifdef NPOT_EMULATION
+	i_data += "#define NPOT_EMULATION\nuniform vec2 uNpotEmulation;\n";
+#endif
+
+	int vp_lump = fileSystem.CheckNumForFullName(vert_prog_lump, 0);
+	if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump.GetChars());
+	FileData vp_data = fileSystem.ReadFile(vp_lump);
+
+	int fp_lump = fileSystem.CheckNumForFullName(frag_prog_lump, 0);
+	if (fp_lump == -1) I_Error("Unable to load '%s'", frag_prog_lump.GetChars());
+	FileData fp_data = fileSystem.ReadFile(fp_lump);
+
+
+
+//
+// The following code uses GetChars on the strings to get rid of terminating 0 characters. Do not remove or the code may break!
+//
+	FString vp_comb;
+
+	assert(screen->mLights != NULL);
+
+	bool lightbuffertype = screen->mLights->GetBufferType();
+	unsigned int lightbuffersize = screen->mLights->GetBlockSize();
+
+	vp_comb.Format("#version 100\n#define NUM_UBO_LIGHTS %d\n#define NO_CLIPDISTANCE_SUPPORT\n", lightbuffersize);
+
+	FString fp_comb = vp_comb;
+	vp_comb << defines << i_data.GetChars();
+	fp_comb << "$placeholder$\n" << defines << i_data.GetChars();
+
+	vp_comb << "#line 1\n";
+	fp_comb << "#line 1\n";
+
+	vp_comb << RemoveLayoutLocationDecl(vp_data.GetString(), "out").GetChars() << "\n";
+	fp_comb << RemoveLayoutLocationDecl(fp_data.GetString(), "in").GetChars() << "\n";
+	FString placeholder = "\n";
+
+	if (proc_prog_lump.Len())
+	{
+		fp_comb << "#line 1\n";
+
+		if (*proc_prog_lump != '#')
+		{
+			int pp_lump = fileSystem.CheckNumForFullName(proc_prog_lump);
+			if (pp_lump == -1) I_Error("Unable to load '%s'", proc_prog_lump.GetChars());
+			FileData pp_data = fileSystem.ReadFile(pp_lump);
+
+			if (pp_data.GetString().IndexOf("ProcessMaterial") < 0 && pp_data.GetString().IndexOf("SetupMaterial") < 0)
+			{
+				// this looks like an old custom hardware shader.
+
+				if (pp_data.GetString().IndexOf("GetTexCoord") >= 0)
+				{
+					int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultmat2.fp", 0);
+					if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultmat2.fp");
+					FileData pl_data = fileSystem.ReadFile(pl_lump);
+					fp_comb << "\n" << pl_data.GetString().GetChars();
+				}
+				else
+				{
+					int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultmat.fp", 0);
+					if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultmat.fp");
+					FileData pl_data = fileSystem.ReadFile(pl_lump);
+					fp_comb << "\n" << pl_data.GetString().GetChars();
+
+					if (pp_data.GetString().IndexOf("ProcessTexel") < 0)
+					{
+						// this looks like an even older custom hardware shader.
+						// We need to replace the ProcessTexel call to make it work.
+
+						fp_comb.Substitute("material.Base = ProcessTexel();", "material.Base = Process(vec4(1.0));");
+					}
+				}
+
+				if (pp_data.GetString().IndexOf("ProcessLight") >= 0)
+				{
+					// The ProcessLight signatured changed. Forward to the old one.
+					fp_comb << "\nvec4 ProcessLight(vec4 color);\n";
+					fp_comb << "\nvec4 ProcessLight(Material material, vec4 color) { return ProcessLight(color); }\n";
+				}
+			}
+
+			fp_comb << RemoveLegacyUserUniforms(pp_data.GetString()).GetChars();
+			fp_comb.Substitute("gl_TexCoord[0]", "vTexCoord");	// fix old custom shaders.
+
+			if (pp_data.GetString().IndexOf("ProcessLight") < 0)
+			{
+				int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultlight.fp", 0);
+				if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultlight.fp");
+				FileData pl_data = fileSystem.ReadFile(pl_lump);
+				fp_comb << "\n" << pl_data.GetString().GetChars();
+			}
+
+			// ProcessMaterial must be considered broken because it requires the user to fill in data they possibly cannot know all about.
+			if (pp_data.GetString().IndexOf("ProcessMaterial") >= 0 && pp_data.GetString().IndexOf("SetupMaterial") < 0)
+			{
+				// This reactivates the old logic and disables all features that cannot be supported with that method.
+				placeholder << "#define LEGACY_USER_SHADER\n";
+			}
+		}
+		else
+		{
+			// Proc_prog_lump is not a lump name but the source itself (from generated shaders)
+			fp_comb << proc_prog_lump.GetChars() + 1;
+		}
+	}
+	fp_comb.Substitute("$placeholder$", placeholder);
+
+	if (light_fragprog.Len())
+	{
+		int pp_lump = fileSystem.CheckNumForFullName(light_fragprog, 0);
+		if (pp_lump == -1) I_Error("Unable to load '%s'", light_fragprog.GetChars());
+		FileData pp_data = fileSystem.ReadFile(pp_lump);
+		fp_comb << pp_data.GetString().GetChars() << "\n";
+	}
+
+	if (gles.flags & RFL_NO_CLIP_PLANES)
+	{
+		// On ATI's GL3 drivers we have to disable gl_ClipDistance because it's hopelessly broken.
+		// This will cause some glitches and regressions but is the only way to avoid total display garbage.
+		vp_comb.Substitute("gl_ClipDistance", "//");
+	}
+
+	shaderData->hShader = glCreateProgram();
+	
+	uint32_t binaryFormat = 0;
+	TArray<uint8_t> binary;
+	if (IsShaderCacheActive())
+		binary = LoadCachedProgramBinary(vp_comb, fp_comb, binaryFormat);
+
+	bool linked = false;
+
+	if (!linked)
+	{
+		shaderData->hVertProg = glCreateShader(GL_VERTEX_SHADER);
+		shaderData->hFragProg = glCreateShader(GL_FRAGMENT_SHADER);
+
+		int vp_size = (int)vp_comb.Len();
+		int fp_size = (int)fp_comb.Len();
+
+		const char *vp_ptr = vp_comb.GetChars();
+		const char *fp_ptr = fp_comb.GetChars();
+
+		glShaderSource(shaderData->hVertProg, 1, &vp_ptr, &vp_size);
+		glShaderSource(shaderData->hFragProg, 1, &fp_ptr, &fp_size);
+
+		glCompileShader(shaderData->hVertProg);
+		glCompileShader(shaderData->hFragProg);
+
+		glAttachShader(shaderData->hShader, shaderData->hVertProg);
+		glAttachShader(shaderData->hShader, shaderData->hFragProg);
+
+
+		glBindAttribLocation(shaderData->hShader, VATTR_VERTEX, "aPosition");
+		glBindAttribLocation(shaderData->hShader, VATTR_TEXCOORD, "aTexCoord");
+		glBindAttribLocation(shaderData->hShader, VATTR_COLOR, "aColor");
+		glBindAttribLocation(shaderData->hShader, VATTR_VERTEX2, "aVertex2");
+		glBindAttribLocation(shaderData->hShader, VATTR_NORMAL, "aNormal");
+		glBindAttribLocation(shaderData->hShader, VATTR_NORMAL2, "aNormal2");
+
+
+		glLinkProgram(shaderData->hShader);
+
+		glGetShaderInfoLog(shaderData->hVertProg, 10000, NULL, buffer);
+		if (*buffer)
+		{
+			error << "Vertex shader:\n" << buffer << "\n";
+		}
+		glGetShaderInfoLog(shaderData->hFragProg, 10000, NULL, buffer);
+		if (*buffer)
+		{
+			error << "Fragment shader:\n" << buffer << "\n";
+		}
+
+		glGetProgramInfoLog(shaderData->hShader, 10000, NULL, buffer);
+		if (*buffer)
+		{
+			error << "Linking:\n" << buffer << "\n";
+		}
+		GLint status = 0;
+		glGetProgramiv(shaderData->hShader, GL_LINK_STATUS, &status);
+		linked = (status == GL_TRUE);
+
+		if (!linked)
+		{
+			// only print message if there's an error.
+			I_Error("Init Shader '%s':\n%s\n", name, error.GetChars());
+		}
+	}
+	else
+	{
+		shaderData->hVertProg = 0;
+		shaderData->hFragProg = 0;
+	}
+
+	shaderData->muProjectionMatrix.Init(shaderData->hShader, "ProjectionMatrix");
+	shaderData->muViewMatrix.Init(shaderData->hShader, "ViewMatrix");
+	shaderData->muNormalViewMatrix.Init(shaderData->hShader, "NormalViewMatrix");
+
+	//shaderData->ProjectionMatrix_index = glGetUniformLocation(shaderData->hShader, "ProjectionMatrix");
+	//shaderData->ViewMatrix_index = glGetUniformLocation(shaderData->hShader, "ViewMatrix");
+	//shaderData->NormalViewMatrix_index = glGetUniformLocation(shaderData->hShader, "NormalViewMatrix");
+
+	shaderData->muCameraPos.Init(shaderData->hShader, "uCameraPos");
+	shaderData->muClipLine.Init(shaderData->hShader, "uClipLine");
+
+	shaderData->muGlobVis.Init(shaderData->hShader, "uGlobVis");
+	shaderData->muPalLightLevels.Init(shaderData->hShader, "uPalLightLevels");
+	shaderData->muViewHeight.Init(shaderData->hShader, "uViewHeight");
+	shaderData->muClipHeight.Init(shaderData->hShader, "uClipHeight");
+	shaderData->muClipHeightDirection.Init(shaderData->hShader, "uClipHeightDirection");
+	shaderData->muShadowmapFilter.Init(shaderData->hShader, "uShadowmapFilter");
+
+	////
+
+	shaderData->muDesaturation.Init(shaderData->hShader, "uDesaturationFactor");
+	shaderData->muFogEnabled.Init(shaderData->hShader, "uFogEnabled");
+	shaderData->muTextureMode.Init(shaderData->hShader, "uTextureMode");
+	shaderData->muLightParms.Init(shaderData->hShader, "uLightAttr");
+	shaderData->muClipSplit.Init(shaderData->hShader, "uClipSplit");
+	shaderData->muLightRange.Init(shaderData->hShader, "uLightRange");
+	shaderData->muFogColor.Init(shaderData->hShader, "uFogColor");
+	shaderData->muDynLightColor.Init(shaderData->hShader, "uDynLightColor");
+	shaderData->muObjectColor.Init(shaderData->hShader, "uObjectColor");
+	shaderData->muObjectColor2.Init(shaderData->hShader, "uObjectColor2");
+	shaderData->muGlowBottomColor.Init(shaderData->hShader, "uGlowBottomColor");
+	shaderData->muGlowTopColor.Init(shaderData->hShader, "uGlowTopColor");
+	shaderData->muGlowBottomPlane.Init(shaderData->hShader, "uGlowBottomPlane");
+	shaderData->muGlowTopPlane.Init(shaderData->hShader, "uGlowTopPlane");
+	shaderData->muGradientBottomPlane.Init(shaderData->hShader, "uGradientBottomPlane");
+	shaderData->muGradientTopPlane.Init(shaderData->hShader, "uGradientTopPlane");
+	shaderData->muSplitBottomPlane.Init(shaderData->hShader, "uSplitBottomPlane");
+	shaderData->muSplitTopPlane.Init(shaderData->hShader, "uSplitTopPlane");
+	shaderData->muDetailParms.Init(shaderData->hShader, "uDetailParms");
+	shaderData->muInterpolationFactor.Init(shaderData->hShader, "uInterpolationFactor");
+	shaderData->muAlphaThreshold.Init(shaderData->hShader, "uAlphaThreshold");
+	shaderData->muSpecularMaterial.Init(shaderData->hShader, "uSpecularMaterial");
+	shaderData->muAddColor.Init(shaderData->hShader, "uAddColor");
+	shaderData->muTextureAddColor.Init(shaderData->hShader, "uTextureAddColor");
+	shaderData->muTextureModulateColor.Init(shaderData->hShader, "uTextureModulateColor");
+	shaderData->muTextureBlendColor.Init(shaderData->hShader, "uTextureBlendColor");
+	shaderData->muTimer.Init(shaderData->hShader, "timer");
+#ifdef NPOT_EMULATION
+	shaderData->muNpotEmulation.Init(shaderData->hShader, "uNpotEmulation");
+#endif
+	shaderData->muFixedColormapStart.Init(shaderData->hShader, "uFixedColormapStart");
+	shaderData->muFixedColormapRange.Init(shaderData->hShader, "uFixedColormapRange");
+
+	shaderData->lights_index = glGetUniformLocation(shaderData->hShader, "lights");
+	shaderData->modelmatrix_index = glGetUniformLocation(shaderData->hShader, "ModelMatrix");
+	shaderData->texturematrix_index = glGetUniformLocation(shaderData->hShader, "TextureMatrix");
+	shaderData->normalmodelmatrix_index = glGetUniformLocation(shaderData->hShader, "NormalModelMatrix");
+
+
+	glUseProgram(shaderData->hShader);
+
+	// set up other texture units (if needed by the shader)
+	for (int i = 2; i<16; i++)
+	{
+		char stringbuf[20];
+		mysnprintf(stringbuf, 20, "texture%d", i);
+		int tempindex = glGetUniformLocation(shaderData->hShader, stringbuf);
+		if (tempindex > 0) glUniform1i(tempindex, i - 1);
+	}
+
+	int shadowmapindex = glGetUniformLocation(shaderData->hShader, "ShadowMap");
+	if (shadowmapindex > 0) glUniform1i(shadowmapindex, 16);
+
+	glUseProgram(0);
+
+	cur = shaderData;
+
+	return linked;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FShader::~FShader()
+{
+	/*
+	glDeleteProgram(hShader);
+	if (hVertProg != 0)
+		glDeleteShader(hVertProg);
+	if (hFragProg != 0)
+		glDeleteShader(hFragProg);
+	*/
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+bool FShader::Bind(ShaderFlavourData& flavour)
+{
+	uint32_t tag = CreateShaderTag(flavour);
+
+	auto pos = variants.find(tag);
+
+	if (pos == variants.end())
+	{
+		FString variantConfig = "\n";
+
+		variantConfig.AppendFormat("#define MAXIMUM_LIGHT_VECTORS %d\n", gles.numlightvectors);
+		variantConfig.AppendFormat("#define DEF_TEXTURE_MODE %d\n", flavour.textureMode);
+		variantConfig.AppendFormat("#define DEF_TEXTURE_FLAGS %d\n", flavour.texFlags);
+		variantConfig.AppendFormat("#define DEF_BLEND_FLAGS %d\n", flavour.blendFlags);
+		variantConfig.AppendFormat("#define DEF_FOG_2D %d\n", flavour.twoDFog);
+		variantConfig.AppendFormat("#define DEF_FOG_ENABLED %d\n", flavour.fogEnabled);
+		variantConfig.AppendFormat("#define DEF_FOG_RADIAL %d\n", flavour.fogEquationRadial);
+		variantConfig.AppendFormat("#define DEF_FOG_COLOURED %d\n", flavour.colouredFog);
+		variantConfig.AppendFormat("#define DEF_USE_U_LIGHT_LEVEL %d\n", flavour.useULightLevel);
+
+		variantConfig.AppendFormat("#define DEF_DO_DESATURATE %d\n", flavour.doDesaturate);
+		
+		variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_MOD %d\n", flavour.dynLightsMod);
+		variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_SUB %d\n", flavour.dynLightsSub);
+		variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_ADD %d\n", flavour.dynLightsAdd);
+
+		variantConfig.AppendFormat("#define DEF_USE_OBJECT_COLOR_2 %d\n", flavour.useObjectColor2);
+		variantConfig.AppendFormat("#define DEF_USE_GLOW_TOP_COLOR %d\n", flavour.useGlowTopColor);
+		variantConfig.AppendFormat("#define DEF_USE_GLOW_BOTTOM_COLOR %d\n", flavour.useGlowBottomColor);
+		
+		variantConfig.AppendFormat("#define DEF_USE_COLOR_MAP %d\n", flavour.useColorMap);
+		variantConfig.AppendFormat("#define DEF_BUILD_LIGHTING %d\n", flavour.buildLighting);
+		variantConfig.AppendFormat("#define DEF_BANDED_SW_LIGHTING %d\n", flavour.bandedSwLight);
+
+		variantConfig.AppendFormat("#define USE_GLSL_V100 %d\n", gles.forceGLSLv100);
+
+#ifdef NPOT_EMULATION
+		variantConfig.AppendFormat("#define DEF_NPOT_EMULATION %d\n", flavour.npotEmulation);
+#endif
+
+		// Printf("Shader: %s, %08x %s", mFragProg2.GetChars(), tag, variantConfig.GetChars());
+
+		Load(mName.GetChars(), mVertProg, mFragProg, mFragProg2, mLightProg, mDefinesBase + variantConfig);
+
+		if (variants.insert(std::make_pair(tag, cur)).second == false)
+		{
+			Printf("ERROR INSERTING");
+		}
+	}
+	else
+	{
+		cur = pos->second;
+	}
+
+	GLRenderer->mShaderManager->SetActiveShader(this->cur);
+	return true;
+}
+
+//==========================================================================
+//
+// Since all shaders are REQUIRED, any error here needs to be fatal
+//
+//==========================================================================
+
+FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType)
+{
+	FString defines;
+	defines += shaderdefines;
+	// this can't be in the shader code due to ATI strangeness.
+	if (!usediscard) defines += "#define NO_ALPHATEST\n";
+
+	FShader *shader = NULL;
+	try
+	{
+		shader = new FShader(ShaderName);
+		if (!shader->Configure(ShaderName, "shaders_gles/glsl/main.vp", "shaders_gles/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars()))
+		{
+			I_FatalError("Unable to load shader %s\n", ShaderName);
+		}
+	}
+	catch(CRecoverableError &err)
+	{
+		if (shader != NULL) delete shader;
+		shader = NULL;
+		I_FatalError("Unable to load shader %s:\n%s\n", ShaderName, err.GetMessage());
+	}
+	return shader;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FShaderManager::FShaderManager()
+{
+	for (int passType = 0; passType < MAX_PASS_TYPES; passType++)
+		mPassShaders.Push(new FShaderCollection((EPassType)passType));
+}
+
+FShaderManager::~FShaderManager()
+{
+	glUseProgram(0);
+	mActiveShader = NULL;
+
+	for (auto collection : mPassShaders)
+		delete collection;
+}
+
+void FShaderManager::SetActiveShader(FShader::ShaderVariantData *sh)
+{
+	if (mActiveShader != sh)
+	{
+		glUseProgram(sh!= NULL? sh->GetHandle() : 0);
+		mActiveShader = sh;
+	}
+}
+
+FShader *FShaderManager::BindEffect(int effect, EPassType passType, ShaderFlavourData &flavour)
+{
+	if (passType < mPassShaders.Size())
+		return mPassShaders[passType]->BindEffect(effect, flavour);
+	else
+		return nullptr;
+}
+
+FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType)
+{
+	if (passType < mPassShaders.Size())
+		return mPassShaders[passType]->Get(eff, alphateston);
+	else
+		return nullptr;
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FShaderCollection::FShaderCollection(EPassType passType)
+{
+	CompileShaders(passType);
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FShaderCollection::~FShaderCollection()
+{
+	Clean();
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FShaderCollection::CompileShaders(EPassType passType)
+{
+	mMaterialShaders.Clear();
+	mMaterialShadersNAT.Clear();
+	for (int i = 0; i < MAX_EFFECTS; i++)
+	{
+		mEffectShaders[i] = NULL;
+	}
+
+	for(int i=0;defaultshaders[i].ShaderName != NULL;i++)
+	{
+		FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, passType);
+		mMaterialShaders.Push(shc);
+		if (i < SHADER_NoTexture)
+		{
+			FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, passType);
+			mMaterialShadersNAT.Push(shc);
+		}
+	}
+
+#if 0
+	for(unsigned i = 0; i < usershaders.Size(); i++)
+	{
+		FString name = ExtractFileBase(usershaders[i].shader);
+		FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines;
+		FShader *shc = Compile(name, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, passType);
+		mMaterialShaders.Push(shc);
+	}
+#endif
+
+	for(int i=0;i<MAX_EFFECTS;i++)
+	{
+		FShader *eff = new FShader(effectshaders[i].ShaderName);
+		if (!eff->Configure(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].fp1,
+						effectshaders[i].fp2, effectshaders[i].fp3, effectshaders[i].defines))
+		{
+			delete eff;
+		}
+		else mEffectShaders[i] = eff;
+	}
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FShaderCollection::Clean()
+{
+	for (unsigned int i = 0; i < mMaterialShadersNAT.Size(); i++)
+	{
+		if (mMaterialShadersNAT[i] != NULL) delete mMaterialShadersNAT[i];
+	}
+	for (unsigned int i = 0; i < mMaterialShaders.Size(); i++)
+	{
+		if (mMaterialShaders[i] != NULL) delete mMaterialShaders[i];
+	}
+	for (int i = 0; i < MAX_EFFECTS; i++)
+	{
+		if (mEffectShaders[i] != NULL) delete mEffectShaders[i];
+		mEffectShaders[i] = NULL;
+	}
+	mMaterialShaders.Clear();
+	mMaterialShadersNAT.Clear();
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+int FShaderCollection::Find(const char * shn)
+{
+	FName sfn = shn;
+
+	for(unsigned int i=0;i<mMaterialShaders.Size();i++)
+	{
+		if (mMaterialShaders[i]->mName == sfn)
+		{
+			return i;
+		}
+	}
+	return -1;
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FShader *FShaderCollection::BindEffect(int effect, ShaderFlavourData& flavour)
+{
+	if (effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[effect] != NULL)
+	{
+		mEffectShaders[effect]->Bind(flavour);
+		return mEffectShaders[effect];
+	}
+	return NULL;
+}
+
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void gl_DestroyUserShaders()
+{
+	// todo
+}
+
+}
diff --git a/source/common/rendering/gles/gles_shader.h b/source/common/rendering/gles/gles_shader.h
new file mode 100644
index 000000000..7f07c8615
--- /dev/null
+++ b/source/common/rendering/gles/gles_shader.h
@@ -0,0 +1,484 @@
+// 
+//---------------------------------------------------------------------------
+//
+// Copyright(C) 2004-2016 Christoph Oelckers
+// All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see http://www.gnu.org/licenses/
+//
+//--------------------------------------------------------------------------
+//
+
+#ifndef __GL_SHADERS_H__
+#define __GL_SHADERS_H__
+
+#include <map>
+
+#include "gles_renderstate.h"
+#include "name.h"
+
+extern bool gl_shaderactive;
+
+struct HWViewpointUniforms;
+
+namespace OpenGLESRenderer
+{
+	class FShaderCollection;
+
+//==========================================================================
+//
+//
+//==========================================================================
+
+class FUniform1i
+{
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar *name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+	}
+
+	void Set(int newvalue)
+	{
+		glUniform1i(mIndex, newvalue);
+	}
+};
+
+class FBufferedUniform1i
+{
+	int mBuffer;
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar *name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+		mBuffer = 0;
+	}
+
+	void Set(int newvalue)
+	{
+		if (newvalue != mBuffer)
+		{
+			mBuffer = newvalue;
+			glUniform1i(mIndex, newvalue);
+		}
+	}
+};
+
+class FBufferedUniform4i
+{
+	int mBuffer[4];
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar *name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+		memset(mBuffer, 0, sizeof(mBuffer));
+	}
+
+	void Set(const int *newvalue)
+	{
+		if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
+		{
+			memcpy(mBuffer, newvalue, sizeof(mBuffer));
+			glUniform4iv(mIndex, 1, newvalue);
+		}
+	}
+};
+
+class FBufferedUniform1f
+{
+	float mBuffer;
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar *name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+		mBuffer = 0;
+	}
+
+	void Set(float newvalue)
+	{
+		if (newvalue != mBuffer)
+		{
+			mBuffer = newvalue;
+			glUniform1f(mIndex, newvalue);
+		}
+	}
+};
+
+class FBufferedUniform2f
+{
+	float mBuffer[2];
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar *name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+		memset(mBuffer, 0, sizeof(mBuffer));
+	}
+
+	void Set(const float *newvalue)
+	{
+		if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
+		{
+			memcpy(mBuffer, newvalue, sizeof(mBuffer));
+			glUniform2fv(mIndex, 1, newvalue);
+		}
+	}
+
+	void Set(float f1, float f2)
+	{
+		if (mBuffer[0] != f1 || mBuffer[1] != f2)
+		{
+			mBuffer[0] = f1;
+			mBuffer[1] = f2;
+			glUniform2fv(mIndex, 1, mBuffer);
+		}
+	}
+
+};
+
+class FBufferedUniform4f
+{
+	float mBuffer[4];
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar *name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+		memset(mBuffer, 0, sizeof(mBuffer));
+	}
+
+	void Set(const float *newvalue)
+	{
+		if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
+		{
+			memcpy(mBuffer, newvalue, sizeof(mBuffer));
+			glUniform4fv(mIndex, 1, newvalue);
+		}
+	}
+};
+
+class FUniform4f
+{
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar *name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+	}
+
+	void Set(const float *newvalue)
+	{
+		glUniform4fv(mIndex, 1, newvalue);
+	}
+
+	void Set(float a, float b, float c, float d)
+	{
+		glUniform4f(mIndex, a, b, c, d);
+	}
+
+	void Set(PalEntry newvalue)
+	{
+		glUniform4f(mIndex, newvalue.r / 255.f, newvalue.g / 255.f, newvalue.b / 255.f, newvalue.a / 255.f);
+	}
+
+};
+
+class FBufferedUniformPE
+{
+	FVector4PalEntry mBuffer;
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar *name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+		mBuffer = 0;
+	}
+
+	void Set(const FVector4PalEntry &newvalue)
+	{
+		if (newvalue != mBuffer)
+		{
+			mBuffer = newvalue;
+			glUniform4f(mIndex, newvalue.r, newvalue.g, newvalue.b, newvalue.a);
+		}
+	}
+};
+
+class FBufferedUniformMat4fv
+{
+	VSMatrix mBuffer;
+	int mIndex;
+
+public:
+	void Init(GLuint hShader, const GLchar* name)
+	{
+		mIndex = glGetUniformLocation(hShader, name);
+		mBuffer = 0;
+	}
+
+	void Set(const VSMatrix* newvalue)
+	{
+		//if (memcmp(newvalue, &mBuffer, sizeof(mBuffer))) // Breaks 2D menu for some reason..
+		{
+			mBuffer = *newvalue;
+			glUniformMatrix4fv(mIndex, 1, false, (float*)newvalue);
+		}
+	}
+};
+
+class ShaderFlavourData
+{
+public:
+	int textureMode;
+	int texFlags;
+	int blendFlags;
+	bool twoDFog;
+	bool fogEnabled;
+	bool fogEquationRadial;
+	bool colouredFog;
+	bool doDesaturate;
+	bool dynLightsMod;
+	bool dynLightsSub;
+	bool dynLightsAdd;
+	bool useULightLevel;
+	bool useObjectColor2;
+	bool useGlowTopColor;
+	bool useGlowBottomColor;
+	bool useColorMap;
+
+	bool buildLighting;
+	bool bandedSwLight;
+
+#ifdef NPOT_EMULATION
+	bool npotEmulation;
+#endif
+};
+
+class FShader
+{
+	friend class FShaderCollection;
+	friend class FGLRenderState;
+	
+	FName mName;
+
+	FString mVertProg;
+	FString mFragProg;
+	FString mFragProg2;
+	FString mLightProg;
+	FString mDefinesBase;
+
+	/////
+public: class ShaderVariantData
+	{
+	public:
+
+		unsigned int hShader = 0;
+		unsigned int hVertProg = 0;
+		unsigned int hFragProg = 0;
+
+		//int ProjectionMatrix_index = 0;
+		//int ViewMatrix_index = 0;
+		//int NormalViewMatrix_index = 0;
+
+		FBufferedUniformMat4fv muProjectionMatrix;
+		FBufferedUniformMat4fv muViewMatrix;
+		FBufferedUniformMat4fv muNormalViewMatrix;
+
+		FUniform4f muCameraPos;
+		FUniform4f muClipLine;
+
+		FBufferedUniform1f muGlobVis;
+		FBufferedUniform1i muPalLightLevels;
+		FBufferedUniform1i muViewHeight;
+		FBufferedUniform1f muClipHeight;
+		FBufferedUniform1f muClipHeightDirection;
+		FBufferedUniform1i muShadowmapFilter;
+		/////
+
+		FBufferedUniform1f muDesaturation;
+		FBufferedUniform1i muFogEnabled;
+		FBufferedUniform1i muTextureMode;
+		FBufferedUniform4f muLightParms;
+		FBufferedUniform2f muClipSplit;
+		FBufferedUniform4i muLightRange;
+		FBufferedUniformPE muFogColor;
+		FBufferedUniform4f muDynLightColor;
+		FBufferedUniformPE muObjectColor;
+		FBufferedUniformPE muObjectColor2;
+		FBufferedUniformPE muAddColor;
+		FBufferedUniformPE muTextureBlendColor;
+		FBufferedUniformPE muTextureModulateColor;
+		FBufferedUniformPE muTextureAddColor;
+		FUniform4f muGlowBottomColor;
+		FUniform4f muGlowTopColor;
+		FUniform4f muGlowBottomPlane;
+		FUniform4f muGlowTopPlane;
+		FUniform4f muGradientBottomPlane;
+		FUniform4f muGradientTopPlane;
+		FUniform4f muSplitBottomPlane;
+		FUniform4f muSplitTopPlane;
+		FUniform4f muDetailParms;
+		FBufferedUniform1f muInterpolationFactor;
+		FBufferedUniform1f muAlphaThreshold;
+		FBufferedUniform2f muSpecularMaterial;
+		FBufferedUniform1f muTimer;
+#ifdef NPOT_EMULATION
+		FBufferedUniform2f muNpotEmulation;
+#endif
+		FUniform4f muFixedColormapStart;
+		FUniform4f muFixedColormapRange;
+
+
+		int lights_index = 0;
+		int modelmatrix_index = 0;
+		int normalmodelmatrix_index = 0;
+		int texturematrix_index = 0;
+
+		int currentglowstate = 0;
+		int currentgradientstate = 0;
+		int currentsplitstate = 0;
+		int currentcliplinestate = 0;
+		int currentfixedcolormap = 0;
+		bool currentTextureMatrixState = true;// by setting the matrix state to 'true' it is guaranteed to be set the first time the render state gets applied.
+		bool currentModelMatrixState = true;
+
+		unsigned int GetHandle() const { return hShader; }
+	};
+
+	std::map<uint32_t, ShaderVariantData*> variants;
+
+	ShaderVariantData* cur = 0;
+
+public:
+	FShader(const char *name)
+		: mName(name)
+	{
+		
+	}
+
+	~FShader();
+
+	bool Load(const char * name, const char * vert_prog_lump, const char * fragprog, const char * fragprog2, const char * light_fragprog, const char *defines);
+	bool Configure(const char* name, const char* vert_prog_lump, const char* fragprog, const char* fragprog2, const char* light_fragprog, const char* defines);
+
+	void LoadVariant();
+
+
+	uint32_t CreateShaderTag(ShaderFlavourData &flavour)
+	{
+		uint32_t tag = 0;
+		tag |= (flavour.textureMode & 0x7);
+		
+		tag |= (flavour.texFlags & 7) << 3;
+		
+		tag |= (flavour.blendFlags & 7) << 6;
+
+		tag |= (flavour.twoDFog & 1) << 7;
+		tag |= (flavour.fogEnabled & 1) << 8;
+		tag |= (flavour.fogEquationRadial & 1) << 9;
+		tag |= (flavour.colouredFog & 1) << 10;
+
+		tag |= (flavour.doDesaturate & 1) << 11;
+
+		tag |= (flavour.dynLightsMod & 1) << 12;
+		tag |= (flavour.dynLightsSub & 1) << 13;
+		tag |= (flavour.dynLightsAdd & 1) << 14;
+		tag |= (flavour.useULightLevel & 1) << 15;
+		tag |= (flavour.useObjectColor2 & 1) << 16;
+		tag |= (flavour.useGlowTopColor & 1) << 17;
+		tag |= (flavour.useGlowBottomColor & 1) << 18;
+		tag |= (flavour.useColorMap & 1) << 19;
+		tag |= (flavour.buildLighting & 1) << 20;
+		tag |= (flavour.bandedSwLight & 1) << 21;
+
+#ifdef NPOT_EMULATION
+		tag |= (flavour.npotEmulation & 1) << 22;
+#endif
+		return tag;
+	}
+
+	bool Bind(ShaderFlavourData& flavour);
+
+	
+};
+
+//==========================================================================
+//
+// The global shader manager
+//
+//==========================================================================
+class FShaderManager
+{
+public:
+	FShaderManager();
+	~FShaderManager();
+
+	FShader *BindEffect(int effect, EPassType passType, ShaderFlavourData& flavour);
+	FShader *Get(unsigned int eff, bool alphateston, EPassType passType);
+
+	void SetActiveShader(FShader::ShaderVariantData *sh);
+private:
+
+	FShader::ShaderVariantData *mActiveShader = nullptr;
+	TArray<FShaderCollection*> mPassShaders;
+
+	friend class FShader;
+};
+
+class FShaderCollection
+{
+	TArray<FShader*> mMaterialShaders;
+	TArray<FShader*> mMaterialShadersNAT;
+	FShader *mEffectShaders[MAX_EFFECTS];
+
+	void Clean();
+	void CompileShaders(EPassType passType);
+	
+public:
+	FShaderCollection(EPassType passType);
+	~FShaderCollection();
+	FShader *Compile(const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType);
+	int Find(const char *mame);
+	FShader *BindEffect(int effect, ShaderFlavourData& flavour);
+
+	FShader *Get(unsigned int eff, bool alphateston)
+	{
+		// indices 0-2 match the warping modes, 3 no texture, the following are custom
+		if (!alphateston && eff <= 2)
+		{
+			return mMaterialShadersNAT[eff];	// Non-alphatest shaders are only created for default, warp1+2 and brightmap. The rest won't get used anyway
+		}
+		if (eff < mMaterialShaders.Size())
+		{
+			return mMaterialShaders[eff];
+		}
+		return NULL;
+	}
+};
+
+}
+#endif
+
diff --git a/source/common/rendering/gles/gles_shaderprogram.cpp b/source/common/rendering/gles/gles_shaderprogram.cpp
new file mode 100644
index 000000000..26906f57a
--- /dev/null
+++ b/source/common/rendering/gles/gles_shaderprogram.cpp
@@ -0,0 +1,293 @@
+/*
+**  Postprocessing framework
+**  Copyright (c) 2016-2020 Magnus Norddahl
+**
+**  This software is provided 'as-is', without any express or implied
+**  warranty.  In no event will the authors be held liable for any damages
+**  arising from the use of this software.
+**
+**  Permission is granted to anyone to use this software for any purpose,
+**  including commercial applications, and to alter it and redistribute it
+**  freely, subject to the following restrictions:
+**
+**  1. The origin of this software must not be misrepresented; you must not
+**     claim that you wrote the original software. If you use this software
+**     in a product, an acknowledgment in the product documentation would be
+**     appreciated but is not required.
+**  2. Altered source versions must be plainly marked as such, and must not be
+**     misrepresented as being the original software.
+**  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "gles_system.h"
+#include "v_video.h"
+#include "hw_cvars.h"
+#include "gles_shaderprogram.h"
+#include "hw_shaderpatcher.h"
+#include "filesystem.h"
+#include "printf.h"
+
+namespace OpenGLESRenderer
+{
+
+FString GetGLSLPrecision();
+
+bool IsShaderCacheActive();
+TArray<uint8_t> LoadCachedProgramBinary(const FString &vertex, const FString &fragment, uint32_t &binaryFormat);
+void SaveCachedProgramBinary(const FString &vertex, const FString &fragment, const TArray<uint8_t> &binary, uint32_t binaryFormat);
+
+FShaderProgram::FShaderProgram()
+{
+	for (int i = 0; i < NumShaderTypes; i++)
+		mShaders[i] = 0;
+}
+
+//==========================================================================
+//
+// Free shader program resources
+//
+//==========================================================================
+
+FShaderProgram::~FShaderProgram()
+{
+	if (mProgram != 0)
+		glDeleteProgram(mProgram);
+
+	for (int i = 0; i < NumShaderTypes; i++)
+	{
+		if (mShaders[i] != 0)
+			glDeleteShader(mShaders[i]);
+	}
+}
+
+//==========================================================================
+//
+// Creates an OpenGL shader object for the specified type of shader
+//
+//==========================================================================
+
+void FShaderProgram::CreateShader(ShaderType type)
+{
+	GLenum gltype = 0;
+	switch (type)
+	{
+	default:
+	case Vertex: gltype = GL_VERTEX_SHADER; break;
+	case Fragment: gltype = GL_FRAGMENT_SHADER; break;
+	}
+	mShaders[type] = glCreateShader(gltype);
+}
+
+//==========================================================================
+//
+// Compiles a shader and attaches it the program object
+//
+//==========================================================================
+
+void FShaderProgram::Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion)
+{
+	int lump = fileSystem.CheckNumForFullName(lumpName);
+	if (lump == -1) I_FatalError("Unable to load '%s'", lumpName);
+	FString code = fileSystem.ReadFile(lump).GetString().GetChars();
+	Compile(type, lumpName, code, defines, maxGlslVersion);
+}
+
+void FShaderProgram::Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion)
+{
+	mShaderNames[type] = name;
+	mShaderSources[type] = PatchShader(type, code, defines, maxGlslVersion);
+}
+
+void FShaderProgram::CompileShader(ShaderType type)
+{
+	CreateShader(type);
+
+	const auto &handle = mShaders[type];
+
+	
+	const FString &patchedCode = mShaderSources[type];
+	int lengths[1] = { (int)patchedCode.Len() };
+	const char *sources[1] = { patchedCode.GetChars() };
+	glShaderSource(handle, 1, sources, lengths);
+
+	glCompileShader(handle);
+
+	GLint status = 0;
+	glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
+	if (status == GL_FALSE)
+	{
+		I_FatalError("Compile Shader '%s':\n%s\n", mShaderNames[type].GetChars(), GetShaderInfoLog(handle).GetChars());
+	}
+	else
+	{
+		if (mProgram == 0)
+			mProgram = glCreateProgram();
+		glAttachShader(mProgram, handle);
+	}
+}
+
+//==========================================================================
+//
+// Links a program with the compiled shaders
+//
+//==========================================================================
+
+void FShaderProgram::Link(const char *name)
+{
+
+	uint32_t binaryFormat = 0;
+	TArray<uint8_t> binary;
+	if (IsShaderCacheActive())
+		binary = LoadCachedProgramBinary(mShaderSources[Vertex], mShaderSources[Fragment], binaryFormat);
+
+	bool loadedFromBinary = false;
+
+	if (!loadedFromBinary)
+	{
+		CompileShader(Vertex);
+		CompileShader(Fragment);
+
+		glLinkProgram(mProgram);
+
+		GLint status = 0;
+		glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
+		if (status == GL_FALSE)
+		{
+			I_FatalError("Link Shader '%s':\n%s\n", name, GetProgramInfoLog(mProgram).GetChars());
+		}
+	}
+
+	// This is only for old OpenGL which didn't allow to set the binding from within the shader.
+	if (screen->glslversion < 4.20)
+	{
+		glUseProgram(mProgram);
+		for (auto &uni : samplerstobind)
+		{
+			auto index = glGetUniformLocation(mProgram, uni.first);
+			if (index >= 0)
+			{
+				glUniform1i(index, uni.second);
+			}
+		}
+	}
+	samplerstobind.Clear();
+	samplerstobind.ShrinkToFit();
+}
+
+//==========================================================================
+//
+// Set uniform buffer location (only useful for GL 3.3)
+//
+//==========================================================================
+
+void FShaderProgram::SetUniformBufferLocation(int index, const char *name)
+{
+
+}
+
+//==========================================================================
+//
+// Makes the shader the active program
+//
+//==========================================================================
+
+void FShaderProgram::Bind()
+{
+	glUseProgram(mProgram);
+}
+
+//==========================================================================
+//
+// Returns the shader info log (warnings and compile errors)
+//
+//==========================================================================
+
+FString FShaderProgram::GetShaderInfoLog(GLuint handle)
+{
+	static char buffer[10000];
+	GLsizei length = 0;
+	buffer[0] = 0;
+	glGetShaderInfoLog(handle, 10000, &length, buffer);
+	return FString(buffer);
+}
+
+//==========================================================================
+//
+// Returns the program info log (warnings and compile errors)
+//
+//==========================================================================
+
+FString FShaderProgram::GetProgramInfoLog(GLuint handle)
+{
+	static char buffer[10000];
+	GLsizei length = 0;
+	buffer[0] = 0;
+	glGetProgramInfoLog(handle, 10000, &length, buffer);
+	return FString(buffer);
+}
+
+//==========================================================================
+//
+// Patches a shader to be compatible with the version of OpenGL in use
+//
+//==========================================================================
+
+FString FShaderProgram::PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion)
+{
+	FString patchedCode;
+
+	patchedCode.AppendFormat("#version %d\n", 100); // Set to GLES2 
+
+	patchedCode += GetGLSLPrecision();
+
+
+	if (defines)
+		patchedCode << defines;
+
+
+	patchedCode << "#line 1\n";
+	patchedCode << RemoveLayoutLocationDecl(code, type == Vertex ? "out" : "in");
+
+	if (maxGlslVersion < 420)
+	{
+		// Here we must strip out all layout(binding) declarations for sampler uniforms and store them in 'samplerstobind' which can then be processed by the link function.
+		patchedCode = RemoveSamplerBindings(patchedCode, samplerstobind);
+	}
+
+	return patchedCode;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void FPresentShaderBase::Init(const char * vtx_shader_name, const char * program_name)
+{
+	FString prolog = Uniforms.CreateDeclaration("Uniforms", PresentUniforms::Desc());
+
+	mShader.reset(new FShaderProgram());
+	mShader->Compile(FShaderProgram::Vertex, "shaders_gles/pp/screenquad.vp", prolog, 330);
+	mShader->Compile(FShaderProgram::Fragment, vtx_shader_name, prolog, 330);
+	mShader->Link(program_name);
+	mShader->Bind();
+	Uniforms.Init();
+
+	Uniforms.UniformLocation.resize(Uniforms.mFields.size());
+
+	for (int n = 0; n < Uniforms.mFields.size(); n++)
+	{
+		int index = -1;
+		UniformFieldDesc desc = Uniforms.mFields[n];
+		index = glGetUniformLocation(mShader->mProgram, desc.Name);
+		Uniforms.UniformLocation[n] = index;
+	}
+}
+
+void FPresentShader::Bind()
+{
+	if (!mShader)
+	{
+		Init("shaders_gles/pp/present.fp", "shaders_gles/pp/present");
+	}
+	mShader->Bind();
+}
+
+}
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_shaderprogram.h b/source/common/rendering/gles/gles_shaderprogram.h
new file mode 100644
index 000000000..a8dc97428
--- /dev/null
+++ b/source/common/rendering/gles/gles_shaderprogram.h
@@ -0,0 +1,159 @@
+
+#pragma once
+
+#include "gles_system.h"
+#include "gles_shader.h"
+#include "hwrenderer/postprocessing/hw_postprocess.h"
+
+namespace OpenGLESRenderer
+{
+
+class FShaderProgram : public PPShaderBackend
+{
+public:
+	FShaderProgram();
+	~FShaderProgram();
+
+	enum ShaderType
+	{
+		Vertex,
+		Fragment,
+		NumShaderTypes
+	};
+
+	void Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion);
+	void Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion);
+	void Link(const char *name);
+	void SetUniformBufferLocation(int index, const char *name);
+
+	void Bind();
+
+	GLuint Handle() { return mProgram; }
+	//explicit operator bool() const { return mProgram != 0; }
+
+	std::unique_ptr<IDataBuffer> Uniforms;
+	GLuint mProgram = 0;
+private:
+	FShaderProgram(const FShaderProgram &) = delete;
+	FShaderProgram &operator=(const FShaderProgram &) = delete;
+
+	void CompileShader(ShaderType type);
+	FString PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion);
+
+	void CreateShader(ShaderType type);
+	FString GetShaderInfoLog(GLuint handle);
+	FString GetProgramInfoLog(GLuint handle);
+
+
+	GLuint mShaders[NumShaderTypes];
+	FString mShaderSources[NumShaderTypes];
+	FString mShaderNames[NumShaderTypes];
+	TArray<std::pair<FString, int>> samplerstobind;
+};
+
+template<typename T>
+class ShaderUniformsGles
+{
+public:
+	ShaderUniformsGles()
+	{
+		memset(&Values, 0, sizeof(Values));
+	}
+
+	~ShaderUniformsGles()
+	{
+		if (mBuffer != nullptr)
+			delete mBuffer;
+	}
+
+	FString CreateDeclaration(const char* name, const std::vector<UniformFieldDesc>& fields)
+	{
+		mFields = fields;
+		FString decl;
+		decl += "\n";
+		for (size_t i = 0; i < fields.size(); i++)
+		{
+			decl.AppendFormat("\tuniform %s %s;\n", GetTypeStr(fields[i].Type), fields[i].Name);
+		}
+		decl += "\n";
+		return decl;
+	}
+
+	void Init()
+	{
+		if (mBuffer == nullptr)
+			mBuffer = screen->CreateDataBuffer(-1, false, false);
+	}
+
+	void SetData()
+	{
+		if (mBuffer != nullptr)
+			mBuffer->SetData(sizeof(T), &Values);
+	}
+
+	IDataBuffer* GetBuffer() const
+	{
+		// OpenGL needs to mess around with this in ways that should not be part of the interface.
+		return mBuffer;
+	}
+
+	T* operator->() { return &Values; }
+	const T* operator->() const { return &Values; }
+
+	T Values;
+
+	std::vector<UniformFieldDesc> mFields;
+	std::vector<int> UniformLocation;
+
+private:
+	ShaderUniformsGles(const ShaderUniformsGles&) = delete;
+	ShaderUniformsGles& operator=(const ShaderUniformsGles&) = delete;
+
+	IDataBuffer* mBuffer = nullptr;
+
+private:
+	static const char* GetTypeStr(UniformType type)
+	{
+		switch (type)
+		{
+		default:
+		case UniformType::Int: return "int";
+		case UniformType::UInt: return "uint";
+		case UniformType::Float: return "float";
+		case UniformType::Vec2: return "vec2";
+		case UniformType::Vec3: return "vec3";
+		case UniformType::Vec4: return "vec4";
+		case UniformType::IVec2: return "ivec2";
+		case UniformType::IVec3: return "ivec3";
+		case UniformType::IVec4: return "ivec4";
+		case UniformType::UVec2: return "uvec2";
+		case UniformType::UVec3: return "uvec3";
+		case UniformType::UVec4: return "uvec4";
+		case UniformType::Mat4: return "mat4";
+		}
+	}
+};
+
+
+class FPresentShaderBase
+{
+public:
+	virtual ~FPresentShaderBase() {}
+	virtual void Bind() = 0;
+
+	ShaderUniformsGles<PresentUniforms> Uniforms;
+
+protected:
+	virtual void Init(const char * vtx_shader_name, const char * program_name);
+	std::unique_ptr<FShaderProgram> mShader;
+};
+
+class FPresentShader : public FPresentShaderBase
+{
+public:
+	void Bind() override;
+
+};
+
+
+}
\ No newline at end of file
diff --git a/source/common/rendering/gles/gles_system.cpp b/source/common/rendering/gles/gles_system.cpp
new file mode 100644
index 000000000..f58051e64
--- /dev/null
+++ b/source/common/rendering/gles/gles_system.cpp
@@ -0,0 +1,188 @@
+
+
+#include "gles_system.h"
+#include "tarray.h"
+#include "v_video.h"
+#include "printf.h"
+
+CVAR(Bool, gles_use_mapped_buffer, false, 0);
+CVAR(Bool, gles_force_glsl_v100, false, 0);
+CVAR(Int, gles_max_lights_per_surface, 32, 0);
+EXTERN_CVAR(Bool, gl_customshader);
+
+
+#if USE_GLES2
+
+PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRange = NULL;
+PFNGLUNMAPBUFFEROESPROC glUnmapBuffer = NULL;
+#ifdef __ANDROID__
+#include <dlfcn.h>
+
+static void* LoadGLES2Proc(const char* name)
+{
+	static void *glesLib = NULL;
+
+	if(!glesLib)
+	{
+		int flags = RTLD_LOCAL | RTLD_NOW;
+
+		glesLib = dlopen("libGLESv2_CM.so", flags);
+		if(!glesLib)
+		{
+			glesLib = dlopen("libGLESv2.so", flags);
+		}
+	}
+
+	void * ret = NULL;
+	ret =  dlsym(glesLib, name);
+
+	if(!ret)
+	{
+		//LOGI("Failed to load: %s", name);
+	}
+	else
+	{
+		//LOGI("Loaded %s func OK", name);
+	}
+
+	return ret;
+}
+
+#elif defined _WIN32
+
+#include <windows.h>
+
+static HMODULE opengl32dll;
+static PROC(WINAPI* getprocaddress)(LPCSTR name);
+
+static void* LoadGLES2Proc(const char* name)
+{
+
+	HINSTANCE hGetProcIDDLL = LoadLibraryA("libGLESv2.dll");
+	
+	int error =	GetLastError();
+
+	void* addr = GetProcAddress(hGetProcIDDLL, name);
+	if (!addr)
+	{
+		//exit(1);
+	}
+	else
+	{
+		return addr;
+	}
+}
+
+#endif
+
+#endif // USE_GLES2
+
+static TArray<FString>  m_Extensions;
+
+
+static void CollectExtensions()
+{
+	const char* supported = (char*)glGetString(GL_EXTENSIONS);
+
+	if (nullptr != supported)
+	{
+		char* extensions = new char[strlen(supported) + 1];
+		strcpy(extensions, supported);
+
+		char* extension = strtok(extensions, " ");
+
+		while (extension)
+		{
+			m_Extensions.Push(FString(extension));
+			extension = strtok(nullptr, " ");
+		}
+
+		delete[] extensions;
+	}
+}
+
+
+static bool CheckExtension(const char* ext)
+{
+	for (unsigned int i = 0; i < m_Extensions.Size(); ++i)
+	{
+		if (m_Extensions[i].CompareNoCase(ext) == 0) return true;
+	}
+
+	return false;
+}
+
+namespace OpenGLESRenderer
+{
+	RenderContextGLES gles;
+
+	void InitGLES()
+	{
+
+#if USE_GLES2
+
+		if (!gladLoadGLES2Loader(&LoadGLES2Proc))
+		{
+			exit(-1);
+		}
+
+		glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC)LoadGLES2Proc("glMapBufferRange");
+		glUnmapBuffer = (PFNGLUNMAPBUFFEROESPROC)LoadGLES2Proc("glUnmapBuffer");
+
+#else
+		static bool first = true;
+
+		if (first)
+		{
+			if (ogl_LoadFunctions() == ogl_LOAD_FAILED)
+			{
+				//I_FatalError("Failed to load OpenGL functions.");
+			}
+		}
+		GLuint vao;
+		glGenVertexArrays(1, &vao);
+		glBindVertexArray(vao);
+#endif
+		CollectExtensions();
+
+		Printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR));
+		Printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
+		Printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
+		Printf("GL_SHADING_LANGUAGE_VERSION: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
+		Printf("GL_EXTENSIONS:\n");
+		for (unsigned i = 0; i < m_Extensions.Size(); i++)
+		{
+			Printf(" %s\n", m_Extensions[i].GetChars());
+		}
+
+
+		gles.flags = RFL_NO_CLIP_PLANES;
+
+		gles.useMappedBuffers = gles_use_mapped_buffer;
+		gles.forceGLSLv100 = gles_force_glsl_v100;
+		gles.maxlights = gles_max_lights_per_surface;
+
+		gles.modelstring = (char*)glGetString(GL_RENDERER);
+		gles.vendorstring = (char*)glGetString(GL_VENDOR);
+	
+		gl_customshader = false;
+
+		GLint maxTextureSize[1];
+		glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize);
+
+		gles.max_texturesize = maxTextureSize[0];
+		
+		Printf("GL_MAX_TEXTURE_SIZE: %d\n", gles.max_texturesize);
+
+#if USE_GLES2
+		gles.depthStencilAvailable = CheckExtension("GL_OES_packed_depth_stencil");
+		gles.npotAvailable = CheckExtension("GL_OES_texture_npot");
+#else
+		gles.depthStencilAvailable = true;
+		gles.npotAvailable = true;
+		gles.useMappedBuffers = true;
+#endif
+		
+		gles.numlightvectors = (gles.maxlights * LIGHT_VEC4_NUM);
+	}
+}
diff --git a/source/common/rendering/gles/gles_system.h b/source/common/rendering/gles/gles_system.h
new file mode 100644
index 000000000..021499362
--- /dev/null
+++ b/source/common/rendering/gles/gles_system.h
@@ -0,0 +1,91 @@
+#ifndef __GLES_SYSTEM_H
+#define __GLES_SYSTEM_H
+
+#include <math.h>
+#include <float.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <signal.h>
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
+#include <malloc.h>
+#endif
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define USE_GLES2 0
+
+#if (USE_GLES2)
+	#include "glad/glad.h"
+
+// Below are used extensions for GLES
+typedef void* (APIENTRYP PFNGLMAPBUFFERRANGEEXTPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+GLAPI PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRange;
+
+typedef GLboolean(APIENTRYP PFNGLUNMAPBUFFEROESPROC)(GLenum target);
+GLAPI PFNGLUNMAPBUFFEROESPROC glUnmapBuffer;
+
+#define GL_DEPTH24_STENCIL8               0x88F0
+#define GL_MAP_PERSISTENT_BIT             0x0040
+#define GL_MAP_READ_BIT                   0x0001
+#define GL_MAP_WRITE_BIT                  0x0002
+#define GL_MAP_UNSYNCHRONIZED_BIT         0x0020
+#define GL_MAP_INVALIDATE_BUFFER_BIT      0x0008
+#define GL_BGRA                           0x80E1
+
+#else
+	#include "gl_load/gl_load.h"
+#endif
+
+#if defined(__APPLE__)
+	#include <OpenGL/OpenGL.h>
+#endif
+
+// This is the number of vec4s make up the light data
+#define LIGHT_VEC4_NUM 4
+
+//#define NO_RENDER_BUFFER
+
+//#define NPOT_EMULATION
+
+namespace OpenGLESRenderer
+{
+	struct RenderContextGLES
+	{
+		unsigned int flags;
+		unsigned int maxlights;
+		unsigned int numlightvectors;
+		bool useMappedBuffers;
+		bool depthStencilAvailable;
+		bool npotAvailable;
+		bool forceGLSLv100;
+		int max_texturesize;
+		char* vendorstring;
+		char* modelstring;
+	};
+
+	extern RenderContextGLES gles;
+
+	void InitGLES();
+}
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4244)     // MIPS
+#pragma warning(disable : 4136)     // X86
+#pragma warning(disable : 4051)     // ALPHA
+
+#pragma warning(disable : 4018)     // signed/unsigned mismatch
+#pragma warning(disable : 4305)     // truncate from double to float
+#endif
+
+#endif //__GL_PCH_H
diff --git a/source/common/rendering/hwrenderer/data/buffers.h b/source/common/rendering/hwrenderer/data/buffers.h
index 8ef57174e..7f4402c1c 100644
--- a/source/common/rendering/hwrenderer/data/buffers.h
+++ b/source/common/rendering/hwrenderer/data/buffers.h
@@ -5,6 +5,14 @@
 
 class FRenderState;
 
+#ifdef __ANDROID__
+#define HW_MAX_PIPELINE_BUFFERS 8
+#define HW_BLOCK_SSBO 1
+#else
+// On desktop this is only useful fpr letting the GPU run in parallel with the playsim and for that 2 buffers are enough.
+#define HW_MAX_PIPELINE_BUFFERS 2
+#endif
+
 // The low level code needs to know which attributes exist.
 // OpenGL needs to change the state of all of them per buffer binding.
 // VAOs are mostly useless for this because they lump buffer and binding state together which the model code does not want.
@@ -54,10 +62,15 @@ public:
 	virtual void *Lock(unsigned int size) = 0;
 	virtual void Unlock() = 0;
 	virtual void Resize(size_t newsize) = 0;
+
+	virtual void Upload(size_t start, size_t size) {} // For unmappable buffers
+
 	virtual void Map() {}		// Only needed by old OpenGL but this needs to be in the interface.
 	virtual void Unmap() {}
 	void *Memory() { return map; }
 	size_t Size() { return buffersize; }
+	virtual void GPUDropSync() {}
+	virtual void GPUWaitSync() {}
 };
 
 class IVertexBuffer : virtual public IBuffer
diff --git a/source/common/rendering/hwrenderer/data/flatvertices.cpp b/source/common/rendering/hwrenderer/data/flatvertices.cpp
index 5bc37bcb7..9a34b45ff 100644
--- a/source/common/rendering/hwrenderer/data/flatvertices.cpp
+++ b/source/common/rendering/hwrenderer/data/flatvertices.cpp
@@ -45,7 +45,8 @@
 //
 //==========================================================================
 
-FFlatVertexBuffer::FFlatVertexBuffer(int width, int height)
+FFlatVertexBuffer::FFlatVertexBuffer(int width, int height, int pipelineNbr):
+	mPipelineNbr(pipelineNbr)
 {
 	vbo_shadowdata.Resize(NUM_RESERVED);
 
@@ -78,19 +79,27 @@ FFlatVertexBuffer::FFlatVertexBuffer(int width, int height)
 	vbo_shadowdata[18].Set(32767.0f, -32767.0f, 32767.0f, 0, 0);
 	vbo_shadowdata[19].Set(32767.0f, -32767.0f, -32767.0f, 0, 0);
 
-	mVertexBuffer = screen->CreateVertexBuffer();
 	mIndexBuffer = screen->CreateIndexBuffer();
 	int data[4] = {};
 	mIndexBuffer->SetData(4, data); // On Vulkan this may not be empty, so set some dummy defaults to avoid crashes.
 
-	unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex);
-	mVertexBuffer->SetData(bytesize, nullptr, false);
 
-	static const FVertexBufferAttribute format[] = {
-		{ 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(FFlatVertex, x) },
-		{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FFlatVertex, u) }
-	};
-	mVertexBuffer->SetFormat(1, 2, sizeof(FFlatVertex), format);
+	for (int n = 0; n < mPipelineNbr; n++)
+	{
+		mVertexBufferPipeline[n] = screen->CreateVertexBuffer();
+
+		unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex);
+		mVertexBufferPipeline[n]->SetData(bytesize, nullptr, false);
+
+		static const FVertexBufferAttribute format[] = {
+			{ 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(FFlatVertex, x) },
+			{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FFlatVertex, u) }
+		};
+
+		mVertexBufferPipeline[n]->SetFormat(1, 2, sizeof(FFlatVertex), format);
+	}
+
+	mVertexBuffer = mVertexBufferPipeline[mPipelinePos];
 
 	mIndex = mCurIndex = NUM_RESERVED;
 	mNumReserved = NUM_RESERVED;
@@ -105,8 +114,12 @@ FFlatVertexBuffer::FFlatVertexBuffer(int width, int height)
 
 FFlatVertexBuffer::~FFlatVertexBuffer()
 {
+	for (int n = 0; n < mPipelineNbr; n++)
+	{
+		delete mVertexBufferPipeline[n];
+	}
+
 	delete mIndexBuffer;
-	delete mVertexBuffer;
 	mIndexBuffer = nullptr;
 	mVertexBuffer = nullptr;
 }
@@ -153,8 +166,17 @@ std::pair<FFlatVertex *, unsigned int> FFlatVertexBuffer::AllocVertices(unsigned
 
 void FFlatVertexBuffer::Copy(int start, int count)
 {
-	Map();
-	memcpy(GetBuffer(start), &vbo_shadowdata[0], count * sizeof(FFlatVertex));
-	Unmap();
+	IVertexBuffer* old = mVertexBuffer;
+
+	for (int n = 0; n < mPipelineNbr; n++)
+	{
+		mVertexBuffer = mVertexBufferPipeline[n];
+		Map();
+		memcpy(GetBuffer(start), &vbo_shadowdata[0], count * sizeof(FFlatVertex));
+		Unmap();
+		mVertexBuffer->Upload(start * sizeof(FFlatVertex), count * sizeof(FFlatVertex));
+	}
+
+	mVertexBuffer = old;
 }
 
diff --git a/source/common/rendering/hwrenderer/data/flatvertices.h b/source/common/rendering/hwrenderer/data/flatvertices.h
index d28339ee7..619128a33 100644
--- a/source/common/rendering/hwrenderer/data/flatvertices.h
+++ b/source/common/rendering/hwrenderer/data/flatvertices.h
@@ -45,13 +45,20 @@ public:
 	TArray<FFlatVertex> vbo_shadowdata;
 	TArray<uint32_t> ibo_data;
 
-	IVertexBuffer *mVertexBuffer;
+	int mPipelineNbr;
+	int mPipelinePos = 0;
+
+	IVertexBuffer* mVertexBuffer;
+	IVertexBuffer *mVertexBufferPipeline[HW_MAX_PIPELINE_BUFFERS];
 	IIndexBuffer *mIndexBuffer;
 
+	
+
 	unsigned int mIndex;
 	std::atomic<unsigned int> mCurIndex;
 	unsigned int mNumReserved;
 
+	unsigned int mMapStart;
 
 	static const unsigned int BUFFER_SIZE = 2000000;
 	static const unsigned int BUFFER_SIZE_TO_USE = BUFFER_SIZE-500;
@@ -68,7 +75,7 @@ public:
 		NUM_RESERVED = 20
 	};
 
-	FFlatVertexBuffer(int width, int height);
+	FFlatVertexBuffer(int width, int height, int pipelineNbr = 1);
 	~FFlatVertexBuffer();
 
 	void OutputResized(int width, int height);
@@ -97,16 +104,40 @@ public:
 		mCurIndex = mIndex;
 	}
 
+	void NextPipelineBuffer()
+	{
+		mPipelinePos++;
+		mPipelinePos %= mPipelineNbr;
+
+		mVertexBuffer = mVertexBufferPipeline[mPipelinePos];
+	}
+
 	void Map()
 	{
+		mMapStart = mCurIndex;
 		mVertexBuffer->Map();
 	}
 
 	void Unmap()
 	{
 		mVertexBuffer->Unmap();
+		mVertexBuffer->Upload(mMapStart * sizeof(FFlatVertex), (mCurIndex - mMapStart) * sizeof(FFlatVertex));
 	}
 
+	void DropSync()
+	{
+		mVertexBuffer->GPUDropSync();
+	}
+
+	void WaitSync()
+	{
+		mVertexBuffer->GPUWaitSync();
+	}
+
+	int GetPipelinePos() 
+	{ 
+		return mPipelinePos; 
+	}
 };
 
 #endif
diff --git a/source/common/rendering/hwrenderer/data/hw_cvars.cpp b/source/common/rendering/hwrenderer/data/hw_cvars.cpp
index 6faf39e0d..5c1b64359 100644
--- a/source/common/rendering/hwrenderer/data/hw_cvars.cpp
+++ b/source/common/rendering/hwrenderer/data/hw_cvars.cpp
@@ -69,11 +69,6 @@ CUSTOM_CVAR(Bool, gl_plane_reflection, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
 	gl_plane_reflection_i = self;
 }
 
-CUSTOM_CVAR(Bool, gl_render_precise, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
-{
-	gl_seamless=self;
-}
-
 CUSTOM_CVARD(Float, vid_gamma, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts gamma component of gamma ramp")
 {
 	if (self < 0) self = 1;
diff --git a/source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp b/source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp
index 2783ab669..e0283d58e 100644
--- a/source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp
+++ b/source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp
@@ -33,7 +33,8 @@ static const int ELEMENTS_PER_LIGHT = 4;			// each light needs 4 vec4's.
 static const int ELEMENT_SIZE = (4*sizeof(float));
 
 
-FLightBuffer::FLightBuffer()
+FLightBuffer::FLightBuffer(int pipelineNbr):
+	mPipelineNbr(pipelineNbr)
 {
 	int maxNumberOfLights = 80000;
 	
@@ -43,7 +44,7 @@ FLightBuffer::FLightBuffer()
 	// Hack alert: On Intel's GL driver SSBO's perform quite worse than UBOs.
 	// We only want to disable using SSBOs for lights but not disable the feature entirely.
 	// Note that using an uniform buffer here will limit the number of lights per surface so it isn't done for NVidia and AMD.
-	if (screen->IsVulkan() || screen->IsPoly() || ((screen->hwcaps & RFL_SHADER_STORAGE_BUFFER) && !strstr(screen->vendorstring, "Intel")))
+	if (screen->IsVulkan() || screen->IsPoly() || ((screen->hwcaps & RFL_SHADER_STORAGE_BUFFER) && screen->allowSSBO() && !strstr(screen->vendorstring, "Intel")))
 	{
 		mBufferType = true;
 		mBlockAlign = 0;
@@ -56,11 +57,15 @@ FLightBuffer::FLightBuffer()
 		mBlockSize = screen->maxuniformblock / ELEMENT_SIZE;
 		mBlockAlign = screen->uniformblockalignment / ELEMENT_SIZE;
 		mMaxUploadSize = (mBlockSize - mBlockAlign);
-		mByteSize += screen->maxuniformblock;	// to avoid mapping beyond the end of the buffer.
+		
+		//mByteSize += screen->maxuniformblock;	// to avoid mapping beyond the end of the buffer. REMOVED this...This can try to allocate 100's of MB..
 	}
 
-	mBuffer = screen->CreateDataBuffer(LIGHTBUF_BINDINGPOINT, mBufferType, false);
-	mBuffer->SetData(mByteSize, nullptr, false);
+	for (int n = 0; n < mPipelineNbr; n++)
+	{
+		mBufferPipeline[n] = screen->CreateDataBuffer(LIGHTBUF_BINDINGPOINT, mBufferType, false);
+		mBufferPipeline[n]->SetData(mByteSize, nullptr, false);
+	}
 
 	Clear();
 }
@@ -73,6 +78,11 @@ FLightBuffer::~FLightBuffer()
 void FLightBuffer::Clear()
 {
 	mIndex = 0;
+
+	mPipelinePos++;
+	mPipelinePos %= mPipelineNbr;
+
+	mBuffer = mBufferPipeline[mPipelinePos];
 }
 
 int FLightBuffer::UploadLights(FDynLightData &data)
diff --git a/source/common/rendering/hwrenderer/data/hw_lightbuffer.h b/source/common/rendering/hwrenderer/data/hw_lightbuffer.h
index 2e5849346..441dc55ff 100644
--- a/source/common/rendering/hwrenderer/data/hw_lightbuffer.h
+++ b/source/common/rendering/hwrenderer/data/hw_lightbuffer.h
@@ -12,6 +12,9 @@ class FRenderState;
 class FLightBuffer
 {
 	IDataBuffer *mBuffer;
+	IDataBuffer* mBufferPipeline[HW_MAX_PIPELINE_BUFFERS];
+	int mPipelineNbr;
+	int mPipelinePos = 0;
 
 	bool mBufferType;
     std::atomic<unsigned int> mIndex;
@@ -25,7 +28,7 @@ class FLightBuffer
 
 public:
 
-	FLightBuffer();
+	FLightBuffer(int pipelineNbr = 1);
 	~FLightBuffer();
 	void Clear();
 	int UploadLights(FDynLightData &data);
diff --git a/source/common/rendering/hwrenderer/data/hw_renderstate.h b/source/common/rendering/hwrenderer/data/hw_renderstate.h
index 4751cba0b..040a51113 100644
--- a/source/common/rendering/hwrenderer/data/hw_renderstate.h
+++ b/source/common/rendering/hwrenderer/data/hw_renderstate.h
@@ -226,6 +226,10 @@ protected:
 	float mAlphaThreshold;
 	float mClipSplit[2];
 
+
+	int mColorMapSpecial;
+	float mColorMapFlash;
+
 	StreamData mStreamData = {};
 	PalEntry mFogColor;
 
@@ -278,6 +282,9 @@ public:
 		mBias.Reset();
 		mPassType = NORMAL_PASS;
 
+		mColorMapSpecial = 0;
+		mColorMapFlash = 1;
+
 		mVertexBuffer = nullptr;
 		mVertexOffsets[0] = mVertexOffsets[1] = 0;
 		mIndexBuffer = nullptr;
@@ -342,7 +349,7 @@ public:
 		mTextureMode = mode;
 	}
 
-	void SetTextureMode(FRenderStyle style)
+	void SetTextureMode(FRenderStyle style, bool clampy = false)
 	{
 		if (style.Flags & STYLEF_RedIsAlpha)
 		{
@@ -356,6 +363,7 @@ public:
 		{
 			SetTextureMode(TM_INVERSE);
 		}
+		if (clampy) mTextureMode |= TM_CLAMPY;
 	}
 
 	int GetTextureMode()
@@ -676,6 +684,12 @@ public:
 		return mPassType;
 	}
 
+	void SetSpecialColormap(int cm, float flash)
+	{
+		mColorMapSpecial = cm;
+		mColorMapFlash = flash;
+	}
+
 	// API-dependent render interface
 
 	// Draw commands
diff --git a/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.cpp b/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.cpp
index a265e7d11..c37306b8a 100644
--- a/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.cpp
+++ b/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.cpp
@@ -33,13 +33,19 @@
 
 static const int INITIAL_BUFFER_SIZE = 100;	// 100 viewpoints per frame should nearly always be enough
 
-HWViewpointBuffer::HWViewpointBuffer()
+HWViewpointBuffer::HWViewpointBuffer(int pipelineNbr):
+	mPipelineNbr(pipelineNbr)
 {
 	mBufferSize = INITIAL_BUFFER_SIZE;
 	mBlockAlign = ((sizeof(HWViewpointUniforms) / screen->uniformblockalignment) + 1) * screen->uniformblockalignment;
 	mByteSize = mBufferSize * mBlockAlign;
-	mBuffer = screen->CreateDataBuffer(VIEWPOINT_BINDINGPOINT, false, true);
-	mBuffer->SetData(mByteSize, nullptr, false);
+
+	for (int n = 0; n < mPipelineNbr; n++)
+	{
+		mBufferPipeline[n] = screen->CreateDataBuffer(VIEWPOINT_BINDINGPOINT, false, true);
+		mBufferPipeline[n]->SetData(mByteSize, nullptr, false);
+	}
+
 	Clear();
 	mLastMappedIndex = UINT_MAX;
 	mClipPlaneInfo.Push(0);
@@ -57,7 +63,10 @@ void HWViewpointBuffer::CheckSize()
 	{
 		mBufferSize *= 2;
 		mByteSize *= 2;
-		mBuffer->Resize(mByteSize);
+		for (int n = 0; n < mPipelineNbr; n++)
+		{
+			mBufferPipeline[n]->Resize(mByteSize);
+		}
 		m2DHeight = m2DWidth = -1;
 	}
 }
@@ -89,9 +98,14 @@ void HWViewpointBuffer::Set2D(FRenderState &di, int width, int height, int pll)
 
 		matrices.mProjectionMatrix.ortho(0, (float)width, (float)height, 0, -1.0f, 1.0f);
 		matrices.CalcDependencies();
-		mBuffer->Map();
-		memcpy(mBuffer->Memory(), &matrices, sizeof(matrices));
-		mBuffer->Unmap();
+		
+		for (int n = 0; n < mPipelineNbr; n++)
+		{
+			mBufferPipeline[n]->Map();
+			memcpy(mBufferPipeline[n]->Memory(), &matrices, sizeof(matrices));
+			mBufferPipeline[n]->Unmap();
+		}
+
 		m2DWidth = width;
 		m2DHeight = height;
 		mLastMappedIndex = -1;
@@ -115,5 +129,10 @@ void HWViewpointBuffer::Clear()
 	// Index 0 is reserved for the 2D projection.
 	mUploadIndex = 1;
 	mClipPlaneInfo.Resize(1);
+
+	mPipelinePos++;
+	mPipelinePos %= mPipelineNbr;
+
+	mBuffer = mBufferPipeline[mPipelinePos];
 }
 
diff --git a/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.h b/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.h
index 42c096704..0fbd46dfa 100644
--- a/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.h
+++ b/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.h
@@ -8,6 +8,9 @@ class FRenderState;
 class HWViewpointBuffer
 {
 	IDataBuffer *mBuffer;
+	IDataBuffer* mBufferPipeline[HW_MAX_PIPELINE_BUFFERS];
+	int mPipelineNbr;
+	int mPipelinePos = 0;
 
 	unsigned int mBufferSize;
 	unsigned int mBlockAlign;
@@ -24,7 +27,7 @@ class HWViewpointBuffer
 
 public:
 
-	HWViewpointBuffer();
+	HWViewpointBuffer(int pipelineNbr = 1);
 	~HWViewpointBuffer();
 	void Clear();
 	int Bind(FRenderState &di, unsigned int index);
diff --git a/source/common/rendering/v_video.cpp b/source/common/rendering/v_video.cpp
index 815b73a6b..0d00c4c2e 100644
--- a/source/common/rendering/v_video.cpp
+++ b/source/common/rendering/v_video.cpp
@@ -72,6 +72,13 @@ CVAR(Int, win_w, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
 CVAR(Int, win_h, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
 CVAR(Bool, win_maximized, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
 
+// 0 means 'no pipelining' for non GLES2 and 4 elements for GLES2
+CUSTOM_CVAR(Int, gl_pipeline_depth, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
+{
+	if (self < 0 || self >= HW_MAX_PIPELINE_BUFFERS) self = 0;
+	Printf("Changing the pipeline depth requires a restart for " GAMENAME ".\n");
+}
+
 CUSTOM_CVAR(Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
 {
 	if (self < GameTicRate && self != 0)
@@ -92,6 +99,11 @@ CUSTOM_CVAR(Int, vid_preferbackend, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_N
 
 	switch(self)
 	{
+#ifdef HAVE_GLES2
+	case 3:
+		Printf("Selecting OpenGLES 2.0 backend...\n");
+		break;
+#endif
 	case 2:
 		Printf("Selecting SoftPoly backend...\n");
 		break;
diff --git a/source/common/rendering/v_video.h b/source/common/rendering/v_video.h
index c9124e05f..b78afd4b0 100644
--- a/source/common/rendering/v_video.h
+++ b/source/common/rendering/v_video.h
@@ -43,6 +43,7 @@
 #include "v_2ddrawer.h"
 #include "intrect.h"
 #include "hw_shadowmap.h"
+#include "buffers.h"
 
 
 struct FPortalSceneState;
@@ -148,6 +149,9 @@ public:
 	IntRect mOutputLetterbox;
 	float mSceneClearColor[4]{ 0,0,0,255 };
 
+	int mPipelineNbr = 1;						// Number of HW buffers to pipeline
+	int mPipelineType = 0;
+	
 public:
 	DFrameBuffer (int width=1, int height=1);
 	virtual ~DFrameBuffer();
@@ -158,6 +162,14 @@ public:
 	{
 		mShadowMap.SetAABBTree(tree);
 	}
+	bool allowSSBO()
+	{
+#ifndef HW_BLOCK_SSBO
+		return true;
+#else
+		return mPipelineType == 0;
+#endif
+	}
 
 	virtual DCanvas* GetCanvas() { return nullptr; }
 
diff --git a/source/common/rendering/vulkan/system/vk_framebuffer.cpp b/source/common/rendering/vulkan/system/vk_framebuffer.cpp
index 5bce01224..4d08212ec 100644
--- a/source/common/rendering/vulkan/system/vk_framebuffer.cpp
+++ b/source/common/rendering/vulkan/system/vk_framebuffer.cpp
@@ -209,15 +209,21 @@ void VulkanFrameBuffer::Update()
 	Super::Update();
 }
 
-void VulkanFrameBuffer::DeleteFrameObjects()
+void VulkanFrameBuffer::DeleteFrameObjects(bool uploadOnly)
 {
-	FrameDeleteList.Images.clear();
-	FrameDeleteList.ImageViews.clear();
-	FrameDeleteList.Framebuffers.clear();
-	FrameDeleteList.Buffers.clear();
-	FrameDeleteList.Descriptors.clear();
-	FrameDeleteList.DescriptorPools.clear();
-	FrameDeleteList.CommandBuffers.clear();
+	FrameTextureUpload.Buffers.clear();
+	FrameTextureUpload.TotalSize = 0;
+
+	if (!uploadOnly)
+	{
+		FrameDeleteList.Images.clear();
+		FrameDeleteList.ImageViews.clear();
+		FrameDeleteList.Framebuffers.clear();
+		FrameDeleteList.Buffers.clear();
+		FrameDeleteList.Descriptors.clear();
+		FrameDeleteList.DescriptorPools.clear();
+		FrameDeleteList.CommandBuffers.clear();
+	}
 }
 
 void VulkanFrameBuffer::FlushCommands(VulkanCommandBuffer **commands, size_t count, bool finish, bool lastsubmit)
@@ -251,11 +257,12 @@ void VulkanFrameBuffer::FlushCommands(VulkanCommandBuffer **commands, size_t cou
 	mNextSubmit++;
 }
 
-void VulkanFrameBuffer::FlushCommands(bool finish, bool lastsubmit)
+void VulkanFrameBuffer::FlushCommands(bool finish, bool lastsubmit, bool uploadOnly)
 {
-	mRenderState->EndRenderPass();
+	if (!uploadOnly)
+		mRenderState->EndRenderPass();
 
-	if (mDrawCommands || mTransferCommands)
+	if ((!uploadOnly && mDrawCommands) || mTransferCommands)
 	{
 		VulkanCommandBuffer *commands[2];
 		size_t count = 0;
@@ -267,7 +274,7 @@ void VulkanFrameBuffer::FlushCommands(bool finish, bool lastsubmit)
 			FrameDeleteList.CommandBuffers.push_back(std::move(mTransferCommands));
 		}
 
-		if (mDrawCommands)
+		if (!uploadOnly && mDrawCommands)
 		{
 			mDrawCommands->end();
 			commands[count++] = mDrawCommands.get();
@@ -280,7 +287,7 @@ void VulkanFrameBuffer::FlushCommands(bool finish, bool lastsubmit)
 	}
 }
 
-void VulkanFrameBuffer::WaitForCommands(bool finish)
+void VulkanFrameBuffer::WaitForCommands(bool finish, bool uploadOnly)
 {
 	if (finish)
 	{
@@ -292,7 +299,7 @@ void VulkanFrameBuffer::WaitForCommands(bool finish)
 			mPostprocess->DrawPresentTexture(mOutputLetterbox, true, false);
 	}
 
-	FlushCommands(finish, true);
+	FlushCommands(finish, true, uploadOnly);
 
 	if (finish)
 	{
@@ -310,7 +317,7 @@ void VulkanFrameBuffer::WaitForCommands(bool finish)
 		vkResetFences(device->device, numWaitFences, mSubmitWaitFences);
 	}
 
-	DeleteFrameObjects();
+	DeleteFrameObjects(uploadOnly);
 	mNextSubmit = 0;
 
 	if (finish)
diff --git a/source/common/rendering/vulkan/system/vk_framebuffer.h b/source/common/rendering/vulkan/system/vk_framebuffer.h
index 9c5e5bf2d..365278acb 100644
--- a/source/common/rendering/vulkan/system/vk_framebuffer.h
+++ b/source/common/rendering/vulkan/system/vk_framebuffer.h
@@ -37,7 +37,7 @@ public:
 	VkRenderBuffers *GetBuffers() { return mActiveRenderBuffers; }
 	FRenderState* RenderState() override;
 
-	void FlushCommands(bool finish, bool lastsubmit = false);
+	void FlushCommands(bool finish, bool lastsubmit = false, bool uploadOnly = false);
 
 	unsigned int GetLightBufferBlockSize() const;
 
@@ -64,6 +64,12 @@ public:
 		std::vector<std::unique_ptr<VulkanCommandBuffer>> CommandBuffers;
 	} FrameDeleteList;
 
+	struct
+	{
+		std::vector<std::unique_ptr<VulkanBuffer>> Buffers;
+		size_t TotalSize = 0;
+	} FrameTextureUpload;
+
 	VulkanFrameBuffer(void *hMonitor, bool fullscreen, VulkanDevice *dev);
 	~VulkanFrameBuffer();
 	bool IsVulkan() override { return true; }
@@ -103,7 +109,8 @@ public:
 
 	void Draw2D() override;
 
-	void WaitForCommands(bool finish) override;
+	void WaitForCommands(bool finish) override { WaitForCommands(finish, false); }
+	void WaitForCommands(bool finish, bool uploadOnly);
 
 	void PushGroup(const FString &name);
 	void PopGroup();
@@ -116,7 +123,7 @@ private:
 	void PrintStartupLog();
 	void CreateFanToTrisIndexBuffer();
 	void CopyScreenToBuffer(int w, int h, uint8_t *data) override;
-	void DeleteFrameObjects();
+	void DeleteFrameObjects(bool uploadOnly = false);
 	void FlushCommands(VulkanCommandBuffer **commands, size_t count, bool finish, bool lastsubmit);
 
 	std::unique_ptr<VkShaderManager> mShaderManager;
diff --git a/source/common/rendering/vulkan/textures/vk_hwtexture.cpp b/source/common/rendering/vulkan/textures/vk_hwtexture.cpp
index 4360fc06e..276f9fd09 100644
--- a/source/common/rendering/vulkan/textures/vk_hwtexture.cpp
+++ b/source/common/rendering/vulkan/textures/vk_hwtexture.cpp
@@ -203,9 +203,14 @@ void VkHardwareTexture::CreateTexture(int w, int h, int pixelsize, VkFormat form
 	region.imageExtent.height = h;
 	cmdbuffer->copyBufferToImage(stagingBuffer->buffer, mImage.Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
 
-	fb->FrameDeleteList.Buffers.push_back(std::move(stagingBuffer));
+	fb->FrameTextureUpload.Buffers.push_back(std::move(stagingBuffer));
 
 	if (mipmap) mImage.GenerateMipmaps(cmdbuffer);
+
+	// If we queued more than 64 MB of data already: wait until the uploads finish before continuing
+	fb->FrameTextureUpload.TotalSize += totalSize;
+	if (fb->FrameTextureUpload.TotalSize > 64 * 1024 * 1024)
+		fb->WaitForCommands(false, true);
 }
 
 int VkHardwareTexture::GetMipLevels(int w, int h)
diff --git a/source/common/textures/hw_material.cpp b/source/common/textures/hw_material.cpp
index a157cbc07..3f149d728 100644
--- a/source/common/textures/hw_material.cpp
+++ b/source/common/textures/hw_material.cpp
@@ -29,6 +29,10 @@
 #include "c_cvars.h"
 #include "v_video.h"
 
+
+CVAR(Bool, gl_customshader, true, 0);
+
+
 static IHardwareTexture* (*layercallback)(int layer, int translation);
 TArray<UserShaderDesc> usershaders;
 
@@ -124,17 +128,20 @@ FMaterial::FMaterial(FGameTexture * tx, int scaleflags)
 		}
 
 		auto index = tx->GetShaderIndex();
-		if (index >= FIRST_USER_SHADER)
+		if (gl_customshader)
 		{
-			const UserShaderDesc &usershader = usershaders[index - FIRST_USER_SHADER];
-			if (usershader.shaderType == mShaderIndex) // Only apply user shader if it matches the expected material
+			if (index >= FIRST_USER_SHADER)
 			{
-				for (auto &texture : tx->CustomShaderTextures)
+				const UserShaderDesc& usershader = usershaders[index - FIRST_USER_SHADER];
+				if (usershader.shaderType == mShaderIndex) // Only apply user shader if it matches the expected material
 				{
-					if (texture == nullptr) continue;
-					mTextureLayers.Push({ texture.get(), 0 });	// scalability should be user-definable.
+					for (auto& texture : tx->CustomShaderTextures)
+					{
+						if (texture == nullptr) continue;
+						mTextureLayers.Push({ texture.get(), 0 });	// scalability should be user-definable.
+					}
+					mShaderIndex = index;
 				}
-				mShaderIndex = index;
 			}
 		}
 	}
diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp
index aa499eb8b..62627931d 100644
--- a/wadsrc/static/shaders/glsl/main.fp
+++ b/wadsrc/static/shaders/glsl/main.fp
@@ -161,10 +161,11 @@ const int Tex_Blend_Hardlight = 4;
 vec4 getTexel(vec2 st)
 {
 	vec4 texel = texture(tex, st);
+	
 	//
 	// Apply texture modes
 	//
-	switch (uTextureMode & 0xffff)
+	switch (uTextureMode & 0xfff)
 	{
 		case 1:	// TM_STENCIL
 			texel.rgb = vec3(1.0,1.0,1.0);
@@ -185,13 +186,6 @@ vec4 getTexel(vec2 st)
 			break;
 		}
 			
-		case 5:	// TM_CLAMPY
-			if (st.t < 0.0 || st.t > 1.0)
-			{
-				texel.a = 0.0;
-			}
-			break;
-			
 		case 6: // TM_OPAQUEINVERSE
 			texel = vec4(1.0-texel.r, 1.0-texel.b, 1.0-texel.g, 1.0);
 			break;
@@ -200,10 +194,17 @@ vec4 getTexel(vec2 st)
 			return texel;
 
 	}
+	if ((uTextureMode & 0x1000) != 0) // TM_CLAMPY
+	{
+		if (st.t < 0.0 || st.t > 1.0)
+		{
+			texel.a = 0.0;
+		}
+	}
 	
 	// Apply the texture modification colors.
 	int blendflags = int(uTextureAddColor.a);	// this alpha is unused otherwise
-	if ((blendflags &  0x3fff) != 0)			// keep the upper flags for other things.
+	if (blendflags != 0)	
 	{
 		// only apply the texture manipulation if it contains something.
 		texel = ApplyTextureManipulation(texel, blendflags);
@@ -567,7 +568,7 @@ void SetMaterialProps(inout Material material, vec2 texCoord)
 // OpenGL doesn't care, but Vulkan pukes all over the place if these texture samplings are included in no-texture shaders, even though never called.
 #ifndef NO_LAYERS
 	if ((uTextureMode & TEXF_Brightmap) != 0)
-		material.Bright = texture(brighttexture, texCoord.st);
+		material.Bright = desaturate(texture(brighttexture, texCoord.st));
 		
 	if ((uTextureMode & TEXF_Detailmap) != 0)
 	{
@@ -576,7 +577,7 @@ void SetMaterialProps(inout Material material, vec2 texCoord)
 	}
 	
 	if ((uTextureMode & TEXF_Glowmap) != 0)
-		material.Glow = texture(glowtexture, texCoord.st);
+		material.Glow = desaturate(texture(glowtexture, texCoord.st));
 #endif
 }
 
@@ -631,7 +632,7 @@ vec4 getLightColor(Material material, float fogdist, float fogfactor)
 	color = min(color, 1.0);
 
 	// these cannot be safely applied by the legacy format where the implementation cannot guarantee that the values are set.
-#ifndef LEGACY_USER_SHADER
+#if !defined LEGACY_USER_SHADER && !defined NO_LAYERS
 	//
 	// apply glow 
 	//
diff --git a/wadsrc/static/shaders/glsl/main.vp b/wadsrc/static/shaders/glsl/main.vp
index 86da9868a..a410d5a4d 100644
--- a/wadsrc/static/shaders/glsl/main.vp
+++ b/wadsrc/static/shaders/glsl/main.vp
@@ -80,13 +80,13 @@ void main()
 
 		#ifdef HAS_UNIFORM_VERTEX_DATA
 			if ((useVertexData & 2) == 0)
-				vWorldNormal = NormalModelMatrix * vec4(uVertexNormal.xyz, 1.0);
+				vWorldNormal = NormalModelMatrix * vec4(normalize(uVertexNormal.xyz), 1.0);
 			else
 				vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0);
 		#else
 			vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0);
 		#endif
-		vEyeNormal = NormalViewMatrix * vWorldNormal;
+		vEyeNormal = NormalViewMatrix * vec4(normalize(vWorldNormal.xyz), 1.0);
 	#endif
 	
 	#ifdef SPHEREMAP
diff --git a/wadsrc/static/shaders/pp/present.fp b/wadsrc/static/shaders/pp/present.fp
index 7069d0fae..c10625883 100644
--- a/wadsrc/static/shaders/pp/present.fp
+++ b/wadsrc/static/shaders/pp/present.fp
@@ -31,7 +31,7 @@ vec4 Dither(vec4 c)
 
 vec4 sRGBtoLinear(vec4 c)
 {
-	return vec4(mix(pow((c.rgb + 0.055) / 1.055, vec3(2.4)), c.rgb / 12.92, step(c.rgb, vec3(0.04045))), c.a);
+	return vec4(mix(c.rgb / 12.92, pow((c.rgb + 0.055) / 1.055, vec3(2.4)), step(c.rgb, vec3(0.04045))), c.a);
 }
 
 vec4 ApplyHdrMode(vec4 c)
diff --git a/wadsrc/static/zscript/engine/ui/menu/search/menu.zs b/wadsrc/static/zscript/engine/ui/menu/search/menu.zs
index 46efc0c6a..17a8a21d3 100644
--- a/wadsrc/static/zscript/engine/ui/menu/search/menu.zs
+++ b/wadsrc/static/zscript/engine/ui/menu/search/menu.zs
@@ -31,7 +31,8 @@ class os_Menu : OptionMenu
 
 		addSearchField(text);
 
-		bool found = listOptions(mDesc, "MainMenu", query, "", isAnyTermMatches);
+		Dictionary searchedMenus = Dictionary.Create();
+		bool found = listOptions(mDesc, "MainMenu", query, "", isAnyTermMatches, searchedMenus);
 
 		if (!found) { addNoResultsItem(mDesc); }
 
@@ -54,21 +55,29 @@ class os_Menu : OptionMenu
 	                                string   menuName,
 	                                os_Query query,
 	                                string   path,
-	                                bool     isAnyTermMatches)
+	                                bool     isAnyTermMatches,
+	                                Dictionary searchedMenus)
 	{
+		if (searchedMenus.At(menuName).length() > 0)
+		{
+			return false;
+		}
+
+		searchedMenus.Insert(menuName, "1");
+
 		let desc         = MenuDescriptor.GetDescriptor(menuName);
 		let listMenuDesc = ListMenuDescriptor(desc);
 
 		if (listMenuDesc)
 		{
-			return listOptionsListMenu(listMenuDesc, targetDesc, query, path, isAnyTermMatches);
+			return listOptionsListMenu(listMenuDesc, targetDesc, query, path, isAnyTermMatches, searchedMenus);
 		}
 
 		let optionMenuDesc = OptionMenuDescriptor(desc);
 
 		if (optionMenuDesc)
 		{
-			return listOptionsOptionMenu(optionMenuDesc, targetDesc, query, path, isAnyTermMatches);
+			return listOptionsOptionMenu(optionMenuDesc, targetDesc, query, path, isAnyTermMatches, searchedMenus);
 		}
 
 		return false;
@@ -78,7 +87,8 @@ class os_Menu : OptionMenu
 	                                        OptionMenuDescriptor targetDesc,
 	                                        os_Query query,
 	                                        string   path,
-	                                        bool     isAnyTermMatches)
+	                                        bool     isAnyTermMatches,
+	                                        Dictionary searchedMenus)
 	{
 		int  nItems = sourceDesc.mItems.size();
 		bool found  = false;
@@ -92,7 +102,7 @@ class os_Menu : OptionMenu
 				? makePath(path, StringTable.Localize(textItem.mText))
 				: path;
 
-			found |= listOptions(targetDesc, actionN, query, newPath, isAnyTermMatches);
+			found |= listOptions(targetDesc, actionN, query, newPath, isAnyTermMatches, searchedMenus);
 		}
 
 		return found;
@@ -102,7 +112,8 @@ class os_Menu : OptionMenu
 	                                          OptionMenuDescriptor targetDesc,
 	                                          os_Query query,
 	                                          string   path,
-	                                          bool     isAnyTermMatches)
+	                                          bool     isAnyTermMatches,
+	                                          Dictionary searchedMenus)
 	{
 		if (sourceDesc == targetDesc) { return false; }
 
@@ -152,7 +163,7 @@ class os_Menu : OptionMenu
 			{
 				string newPath = makePath(path, label);
 
-				found |= listOptions(targetDesc, item.GetAction(), query, newPath, isAnyTermMatches);
+				found |= listOptions(targetDesc, item.GetAction(), query, newPath, isAnyTermMatches, searchedMenus);
 			}
 		}
 
diff --git a/wadsrc/static/zscript/engine/ui/menu/textentermenu.zs b/wadsrc/static/zscript/engine/ui/menu/textentermenu.zs
index 2f179d0d7..6fc7abe02 100644
--- a/wadsrc/static/zscript/engine/ui/menu/textentermenu.zs
+++ b/wadsrc/static/zscript/engine/ui/menu/textentermenu.zs
@@ -365,7 +365,7 @@ class TextEnterMenu : Menu
 					else if (ch == 8 || ch == 0)
 					{
 						// Draw the backspace and end "characters".
-						String str = ch == 8 ? "BS" : "ED";
+						String str = ch == 8 ? "←" : "↲";
 						screen.DrawText(displayFont, colr,
 							xx + cell_width/2 - displayFont.StringWidth(str)*CleanXfac_1/2,
 							yy + top_padding, str, DTA_CleanNoMove_1, true);