summaryrefslogtreecommitdiffstats
path: root/xbmc/rendering/gles
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/rendering/gles
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/rendering/gles')
-rw-r--r--xbmc/rendering/gles/CMakeLists.txt11
-rw-r--r--xbmc/rendering/gles/GLESShader.cpp178
-rw-r--r--xbmc/rendering/gles/GLESShader.h65
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.cpp669
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.h143
-rw-r--r--xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp72
-rw-r--r--xbmc/rendering/gles/ScreenshotSurfaceGLES.h22
7 files changed, 1160 insertions, 0 deletions
diff --git a/xbmc/rendering/gles/CMakeLists.txt b/xbmc/rendering/gles/CMakeLists.txt
new file mode 100644
index 0000000..74cfe63
--- /dev/null
+++ b/xbmc/rendering/gles/CMakeLists.txt
@@ -0,0 +1,11 @@
+if(OPENGLES_FOUND)
+ set(SOURCES RenderSystemGLES.cpp
+ ScreenshotSurfaceGLES.cpp
+ GLESShader.cpp)
+
+ set(HEADERS RenderSystemGLES.h
+ ScreenshotSurfaceGLES.h
+ GLESShader.h)
+
+ core_add_library(rendering_gles)
+endif()
diff --git a/xbmc/rendering/gles/GLESShader.cpp b/xbmc/rendering/gles/GLESShader.cpp
new file mode 100644
index 0000000..3964f4b
--- /dev/null
+++ b/xbmc/rendering/gles/GLESShader.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GLESShader.h"
+
+#include "ServiceBroker.h"
+#include "rendering/MatrixGL.h"
+#include "rendering/RenderSystem.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+using namespace Shaders;
+
+CGLESShader::CGLESShader(const char* shader, const std::string& prefix)
+{
+ m_proj = nullptr;
+ m_model = nullptr;
+ m_clipPossible = false;
+
+ VertexShader()->LoadSource("gles_shader.vert");
+ PixelShader()->LoadSource(shader, prefix);
+}
+
+CGLESShader::CGLESShader(const char* vshader, const char* fshader, const std::string& prefix)
+{
+ m_proj = nullptr;
+ m_model = nullptr;
+ m_clipPossible = false;
+
+ VertexShader()->LoadSource(vshader, prefix);
+ PixelShader()->LoadSource(fshader, prefix);
+}
+
+void CGLESShader::OnCompiledAndLinked()
+{
+ // This is called after CompileAndLink()
+
+ // Variables passed directly to the Fragment shader
+ m_hTex0 = glGetUniformLocation(ProgramHandle(), "m_samp0");
+ m_hTex1 = glGetUniformLocation(ProgramHandle(), "m_samp1");
+ m_hUniCol = glGetUniformLocation(ProgramHandle(), "m_unicol");
+ m_hField = glGetUniformLocation(ProgramHandle(), "m_field");
+ m_hStep = glGetUniformLocation(ProgramHandle(), "m_step");
+ m_hContrast = glGetUniformLocation(ProgramHandle(), "m_contrast");
+ m_hBrightness = glGetUniformLocation(ProgramHandle(), "m_brightness");
+
+ // Variables passed directly to the Vertex shader
+ m_hProj = glGetUniformLocation(ProgramHandle(), "m_proj");
+ m_hModel = glGetUniformLocation(ProgramHandle(), "m_model");
+ m_hCoord0Matrix = glGetUniformLocation(ProgramHandle(), "m_coord0Matrix");
+
+ // Vertex attributes
+ m_hPos = glGetAttribLocation(ProgramHandle(), "m_attrpos");
+ m_hCol = glGetAttribLocation(ProgramHandle(), "m_attrcol");
+ m_hCord0 = glGetAttribLocation(ProgramHandle(), "m_attrcord0");
+ m_hCord1 = glGetAttribLocation(ProgramHandle(), "m_attrcord1");
+
+ // It's okay to do this only one time. Textures units never change.
+ glUseProgram( ProgramHandle() );
+ glUniform1i(m_hTex0, 0);
+ glUniform1i(m_hTex1, 1);
+ glUniform4f(m_hUniCol, 1.0, 1.0, 1.0, 1.0);
+
+ const float identity[16] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+ };
+ glUniformMatrix4fv(m_hCoord0Matrix, 1, GL_FALSE, identity);
+
+ glUseProgram( 0 );
+}
+
+bool CGLESShader::OnEnabled()
+{
+ // This is called after glUseProgram()
+
+ const GLfloat *projMatrix = glMatrixProject.Get();
+ const GLfloat *modelMatrix = glMatrixModview.Get();
+ glUniformMatrix4fv(m_hProj, 1, GL_FALSE, projMatrix);
+ glUniformMatrix4fv(m_hModel, 1, GL_FALSE, modelMatrix);
+
+ const TransformMatrix &guiMatrix = CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIMatrix();
+ CRect viewPort; // absolute positions of corners
+ CServiceBroker::GetRenderSystem()->GetViewPort(viewPort);
+
+ /* glScissor operates in window coordinates. In order that we can use it to
+ * perform clipping, we must ensure that there is an independent linear
+ * transformation from the coordinate system used by CGraphicContext::ClipRect
+ * to window coordinates, separately for X and Y (in other words, no
+ * rotation or shear is introduced at any stage). To do, this, we need to
+ * check that zeros are present in the following locations:
+ *
+ * GUI matrix:
+ * / * 0 * * \
+ * | 0 * * * |
+ * \ 0 0 * * /
+ * ^ TransformMatrix::TransformX/Y/ZCoord are only ever called with
+ * input z = 0, so this column doesn't matter
+ * Model-view matrix:
+ * / * 0 0 * \
+ * | 0 * 0 * |
+ * | 0 0 * * |
+ * \ * * * * / <- eye w has no influence on window x/y (last column below
+ * is either 0 or ignored)
+ * Projection matrix:
+ * / * 0 0 0 \
+ * | 0 * 0 0 |
+ * | * * * * | <- normalised device coordinate z has no influence on window x/y
+ * \ 0 0 * 0 /
+ *
+ * Some of these zeros are not strictly required to ensure this, but they tend
+ * to be zeroed in the common case, so by checking for zeros here, we simplify
+ * the calculation of the window x/y coordinates further down the line.
+ *
+ * (Minor detail: we don't quite deal in window coordinates as defined by
+ * OpenGL, because CRenderSystemGLES::SetScissors flips the Y axis. But all
+ * that's needed to handle that is an effective negation at the stage where
+ * Y is in normalised device coordinates.)
+ */
+ m_clipPossible = guiMatrix.m[0][1] == 0 &&
+ guiMatrix.m[1][0] == 0 &&
+ guiMatrix.m[2][0] == 0 &&
+ guiMatrix.m[2][1] == 0 &&
+ modelMatrix[0+1*4] == 0 &&
+ modelMatrix[0+2*4] == 0 &&
+ modelMatrix[1+0*4] == 0 &&
+ modelMatrix[1+2*4] == 0 &&
+ modelMatrix[2+0*4] == 0 &&
+ modelMatrix[2+1*4] == 0 &&
+ projMatrix[0+1*4] == 0 &&
+ projMatrix[0+2*4] == 0 &&
+ projMatrix[0+3*4] == 0 &&
+ projMatrix[1+0*4] == 0 &&
+ projMatrix[1+2*4] == 0 &&
+ projMatrix[1+3*4] == 0 &&
+ projMatrix[3+0*4] == 0 &&
+ projMatrix[3+1*4] == 0 &&
+ projMatrix[3+3*4] == 0;
+
+ m_clipXFactor = 0.0;
+ m_clipXOffset = 0.0;
+ m_clipYFactor = 0.0;
+ m_clipYOffset = 0.0;
+
+ if (m_clipPossible)
+ {
+ m_clipXFactor = guiMatrix.m[0][0] * modelMatrix[0+0*4] * projMatrix[0+0*4];
+ m_clipXOffset = (guiMatrix.m[0][3] * modelMatrix[0+0*4] + modelMatrix[0+3*4]) * projMatrix[0+0*4];
+ m_clipYFactor = guiMatrix.m[1][1] * modelMatrix[1+1*4] * projMatrix[1+1*4];
+ m_clipYOffset = (guiMatrix.m[1][3] * modelMatrix[1+1*4] + modelMatrix[1+3*4]) * projMatrix[1+1*4];
+ float clipW = (guiMatrix.m[2][3] * modelMatrix[2+2*4] + modelMatrix[2+3*4]) * projMatrix[3+2*4];
+ float xMult = (viewPort.x2 - viewPort.x1) / (2 * clipW);
+ float yMult = (viewPort.y1 - viewPort.y2) / (2 * clipW); // correct for inverted window coordinate scheme
+ m_clipXFactor = m_clipXFactor * xMult;
+ m_clipXOffset = m_clipXOffset * xMult + (viewPort.x2 + viewPort.x1) / 2;
+ m_clipYFactor = m_clipYFactor * yMult;
+ m_clipYOffset = m_clipYOffset * yMult + (viewPort.y2 + viewPort.y1) / 2;
+ }
+
+ glUniform1f(m_hBrightness, 0.0f);
+ glUniform1f(m_hContrast, 1.0f);
+
+ return true;
+}
+
+void CGLESShader::Free()
+{
+ // Do Cleanup here
+ CGLSLShaderProgram::Free();
+}
+
diff --git a/xbmc/rendering/gles/GLESShader.h b/xbmc/rendering/gles/GLESShader.h
new file mode 100644
index 0000000..8ce31e5
--- /dev/null
+++ b/xbmc/rendering/gles/GLESShader.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "guilib/Shader.h"
+
+#include <string>
+
+class CGLESShader : public Shaders::CGLSLShaderProgram
+{
+public:
+ CGLESShader(const char* shader, const std::string& prefix);
+ CGLESShader(const char* vshader, const char* fshader, const std::string& prefix);
+ void OnCompiledAndLinked() override;
+ bool OnEnabled() override;
+ void Free();
+
+ GLint GetPosLoc() { return m_hPos; }
+ GLint GetColLoc() { return m_hCol; }
+ GLint GetCord0Loc() { return m_hCord0; }
+ GLint GetCord1Loc() { return m_hCord1; }
+ GLint GetUniColLoc() { return m_hUniCol; }
+ GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; }
+ GLint GetFieldLoc() { return m_hField; }
+ GLint GetStepLoc() { return m_hStep; }
+ GLint GetContrastLoc() { return m_hContrast; }
+ GLint GetBrightnessLoc() { return m_hBrightness; }
+ GLint GetModelLoc() { return m_hModel; }
+ bool HardwareClipIsPossible() { return m_clipPossible; }
+ GLfloat GetClipXFactor() { return m_clipXFactor; }
+ GLfloat GetClipXOffset() { return m_clipXOffset; }
+ GLfloat GetClipYFactor() { return m_clipYFactor; }
+ GLfloat GetClipYOffset() { return m_clipYOffset; }
+
+protected:
+ GLint m_hTex0 = 0;
+ GLint m_hTex1 = 0;
+ GLint m_hUniCol = 0;
+ GLint m_hProj = 0;
+ GLint m_hModel = 0;
+ GLint m_hPos = 0;
+ GLint m_hCol = 0;
+ GLint m_hCord0 = 0;
+ GLint m_hCord1 = 0;
+ GLint m_hCoord0Matrix = 0;
+ GLint m_hField = 0;
+ GLint m_hStep = 0;
+ GLint m_hContrast = 0;
+ GLint m_hBrightness = 0;
+
+ const GLfloat *m_proj;
+ const GLfloat *m_model;
+
+ bool m_clipPossible;
+ GLfloat m_clipXFactor;
+ GLfloat m_clipXOffset;
+ GLfloat m_clipYFactor;
+ GLfloat m_clipYOffset;
+};
diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp
new file mode 100644
index 0000000..d8557ff
--- /dev/null
+++ b/xbmc/rendering/gles/RenderSystemGLES.cpp
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "RenderSystemGLES.h"
+
+#include "guilib/DirtyRegion.h"
+#include "guilib/GUITextureGLES.h"
+#include "rendering/MatrixGL.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/GLUtils.h"
+#include "utils/MathUtils.h"
+#include "utils/SystemInfo.h"
+#include "utils/TimeUtils.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#if defined(TARGET_LINUX)
+#include "utils/EGLUtils.h"
+#endif
+
+using namespace std::chrono_literals;
+
+CRenderSystemGLES::CRenderSystemGLES()
+ : CRenderSystemBase()
+{
+}
+
+bool CRenderSystemGLES::InitRenderSystem()
+{
+ GLint maxTextureSize;
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+
+ m_maxTextureSize = maxTextureSize;
+
+ // Get the GLES version number
+ m_RenderVersionMajor = 0;
+ m_RenderVersionMinor = 0;
+
+ const char* ver = (const char*)glGetString(GL_VERSION);
+ if (ver != 0)
+ {
+ sscanf(ver, "%d.%d", &m_RenderVersionMajor, &m_RenderVersionMinor);
+ if (!m_RenderVersionMajor)
+ sscanf(ver, "%*s %*s %d.%d", &m_RenderVersionMajor, &m_RenderVersionMinor);
+ m_RenderVersion = ver;
+ }
+
+ // Get our driver vendor and renderer
+ const char *tmpVendor = (const char*) glGetString(GL_VENDOR);
+ m_RenderVendor.clear();
+ if (tmpVendor != NULL)
+ m_RenderVendor = tmpVendor;
+
+ const char *tmpRenderer = (const char*) glGetString(GL_RENDERER);
+ m_RenderRenderer.clear();
+ if (tmpRenderer != NULL)
+ m_RenderRenderer = tmpRenderer;
+
+ m_RenderExtensions = " ";
+
+ const char *tmpExtensions = (const char*) glGetString(GL_EXTENSIONS);
+ if (tmpExtensions != NULL)
+ {
+ m_RenderExtensions += tmpExtensions;
+ }
+
+ m_RenderExtensions += " ";
+
+#if defined(GL_KHR_debug) && defined(TARGET_LINUX)
+ if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_openGlDebugging)
+ {
+ if (IsExtSupported("GL_KHR_debug"))
+ {
+ auto glDebugMessageCallback = CEGLUtils::GetRequiredProcAddress<PFNGLDEBUGMESSAGECALLBACKKHRPROC>("glDebugMessageCallbackKHR");
+ auto glDebugMessageControl = CEGLUtils::GetRequiredProcAddress<PFNGLDEBUGMESSAGECONTROLKHRPROC>("glDebugMessageControlKHR");
+
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
+ glDebugMessageCallback(KODI::UTILS::GL::GlErrorCallback, nullptr);
+
+ // ignore shader compilation information
+ glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER_KHR, GL_DEBUG_TYPE_OTHER_KHR, GL_DONT_CARE, 0, nullptr, GL_FALSE);
+
+ CLog::Log(LOGDEBUG, "OpenGL(ES): debugging enabled");
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "OpenGL(ES): debugging requested but the required extension isn't available (GL_KHR_debug)");
+ }
+ }
+#endif
+
+ LogGraphicsInfo();
+
+ m_bRenderCreated = true;
+
+ InitialiseShaders();
+
+ CGUITextureGLES::Register();
+
+ return true;
+}
+
+bool CRenderSystemGLES::ResetRenderSystem(int width, int height)
+{
+ m_width = width;
+ m_height = height;
+
+ glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
+ CalculateMaxTexturesize();
+
+ CRect rect( 0, 0, width, height );
+ SetViewPort( rect );
+
+ glEnable(GL_SCISSOR_TEST);
+
+ glMatrixProject.Clear();
+ glMatrixProject->LoadIdentity();
+ glMatrixProject->Ortho(0.0f, width-1, height-1, 0.0f, -1.0f, 1.0f);
+ glMatrixProject.Load();
+
+ glMatrixModview.Clear();
+ glMatrixModview->LoadIdentity();
+ glMatrixModview.Load();
+
+ glMatrixTexture.Clear();
+ glMatrixTexture->LoadIdentity();
+ glMatrixTexture.Load();
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glEnable(GL_BLEND); // Turn Blending On
+ glDisable(GL_DEPTH_TEST);
+
+ return true;
+}
+
+bool CRenderSystemGLES::DestroyRenderSystem()
+{
+ ResetScissors();
+ CDirtyRegionList dirtyRegions;
+ CDirtyRegion dirtyWindow(CServiceBroker::GetWinSystem()->GetGfxContext().GetViewWindow());
+ dirtyRegions.push_back(dirtyWindow);
+
+ ClearBuffers(0);
+ glFinish();
+ PresentRenderImpl(true);
+
+ ReleaseShaders();
+ m_bRenderCreated = false;
+
+ return true;
+}
+
+bool CRenderSystemGLES::BeginRender()
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ bool useLimited = CServiceBroker::GetWinSystem()->UseLimitedColor();
+
+ if (m_limitedColorRange != useLimited)
+ {
+ ReleaseShaders();
+ InitialiseShaders();
+ }
+
+ m_limitedColorRange = useLimited;
+
+ return true;
+}
+
+bool CRenderSystemGLES::EndRender()
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ return true;
+}
+
+bool CRenderSystemGLES::ClearBuffers(UTILS::COLOR::Color color)
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ float r = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::R, color) / 255.0f;
+ float g = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::G, color) / 255.0f;
+ float b = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::B, color) / 255.0f;
+ float a = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::A, color) / 255.0f;
+
+ glClearColor(r, g, b, a);
+
+ GLbitfield flags = GL_COLOR_BUFFER_BIT;
+ glClear(flags);
+
+ return true;
+}
+
+bool CRenderSystemGLES::IsExtSupported(const char* extension) const
+{
+ if (strcmp( extension, "GL_EXT_framebuffer_object" ) == 0)
+ {
+ // GLES has FBO as a core element, not an extension!
+ return true;
+ }
+ else
+ {
+ std::string name;
+ name = " ";
+ name += extension;
+ name += " ";
+
+ return m_RenderExtensions.find(name) != std::string::npos;
+ }
+}
+
+void CRenderSystemGLES::PresentRender(bool rendered, bool videoLayer)
+{
+ SetVSync(true);
+
+ if (!m_bRenderCreated)
+ return;
+
+ PresentRenderImpl(rendered);
+
+ // if video is rendered to a separate layer, we should not block this thread
+ if (!rendered && !videoLayer)
+ KODI::TIME::Sleep(40ms);
+}
+
+void CRenderSystemGLES::SetVSync(bool enable)
+{
+ if (m_bVsyncInit)
+ return;
+
+ if (!m_bRenderCreated)
+ return;
+
+ if (enable)
+ CLog::Log(LOGINFO, "GLES: Enabling VSYNC");
+ else
+ CLog::Log(LOGINFO, "GLES: Disabling VSYNC");
+
+ m_bVsyncInit = true;
+
+ SetVSyncImpl(enable);
+}
+
+void CRenderSystemGLES::CaptureStateBlock()
+{
+ if (!m_bRenderCreated)
+ return;
+
+ glMatrixProject.Push();
+ glMatrixModview.Push();
+ glMatrixTexture.Push();
+
+ glDisable(GL_SCISSOR_TEST); // fixes FBO corruption on Macs
+ glActiveTexture(GL_TEXTURE0);
+//! @todo - NOTE: Only for Screensavers & Visualisations
+// glColor3f(1.0, 1.0, 1.0);
+}
+
+void CRenderSystemGLES::ApplyStateBlock()
+{
+ if (!m_bRenderCreated)
+ return;
+
+ glMatrixProject.PopLoad();
+ glMatrixModview.PopLoad();
+ glMatrixTexture.PopLoad();
+ glActiveTexture(GL_TEXTURE0);
+ glEnable(GL_BLEND);
+ glEnable(GL_SCISSOR_TEST);
+ glClear(GL_DEPTH_BUFFER_BIT);
+}
+
+void CRenderSystemGLES::SetCameraPosition(const CPoint &camera, int screenWidth, int screenHeight, float stereoFactor)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ CPoint offset = camera - CPoint(screenWidth*0.5f, screenHeight*0.5f);
+
+ float w = (float)m_viewPort[2]*0.5f;
+ float h = (float)m_viewPort[3]*0.5f;
+
+ glMatrixModview->LoadIdentity();
+ glMatrixModview->Translatef(-(w + offset.x - stereoFactor), +(h + offset.y), 0);
+ glMatrixModview->LookAt(0.0f, 0.0f, -2.0f * h, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f);
+ glMatrixModview.Load();
+
+ glMatrixProject->LoadIdentity();
+ glMatrixProject->Frustum( (-w - offset.x)*0.5f, (w - offset.x)*0.5f, (-h + offset.y)*0.5f, (h + offset.y)*0.5f, h, 100*h);
+ glMatrixProject.Load();
+}
+
+void CRenderSystemGLES::Project(float &x, float &y, float &z)
+{
+ GLfloat coordX, coordY, coordZ;
+ if (CMatrixGL::Project(x, y, z, glMatrixModview.Get(), glMatrixProject.Get(), m_viewPort, &coordX, &coordY, &coordZ))
+ {
+ x = coordX;
+ y = (float)(m_viewPort[1] + m_viewPort[3] - coordY);
+ z = 0;
+ }
+}
+
+void CRenderSystemGLES::CalculateMaxTexturesize()
+{
+ // GLES cannot do PROXY textures to determine maximum size,
+ CLog::Log(LOGINFO, "GLES: Maximum texture width: {}", m_maxTextureSize);
+}
+
+void CRenderSystemGLES::GetViewPort(CRect& viewPort)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ viewPort.x1 = m_viewPort[0];
+ viewPort.y1 = m_height - m_viewPort[1] - m_viewPort[3];
+ viewPort.x2 = m_viewPort[0] + m_viewPort[2];
+ viewPort.y2 = viewPort.y1 + m_viewPort[3];
+}
+
+void CRenderSystemGLES::SetViewPort(const CRect& viewPort)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ glScissor((GLint) viewPort.x1, (GLint) (m_height - viewPort.y1 - viewPort.Height()), (GLsizei) viewPort.Width(), (GLsizei) viewPort.Height());
+ glViewport((GLint) viewPort.x1, (GLint) (m_height - viewPort.y1 - viewPort.Height()), (GLsizei) viewPort.Width(), (GLsizei) viewPort.Height());
+ m_viewPort[0] = viewPort.x1;
+ m_viewPort[1] = m_height - viewPort.y1 - viewPort.Height();
+ m_viewPort[2] = viewPort.Width();
+ m_viewPort[3] = viewPort.Height();
+}
+
+bool CRenderSystemGLES::ScissorsCanEffectClipping()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->HardwareClipIsPossible();
+
+ return false;
+}
+
+CRect CRenderSystemGLES::ClipRectToScissorRect(const CRect &rect)
+{
+ if (!m_pShader[m_method])
+ return CRect();
+ float xFactor = m_pShader[m_method]->GetClipXFactor();
+ float xOffset = m_pShader[m_method]->GetClipXOffset();
+ float yFactor = m_pShader[m_method]->GetClipYFactor();
+ float yOffset = m_pShader[m_method]->GetClipYOffset();
+ return CRect(rect.x1 * xFactor + xOffset,
+ rect.y1 * yFactor + yOffset,
+ rect.x2 * xFactor + xOffset,
+ rect.y2 * yFactor + yOffset);
+}
+
+void CRenderSystemGLES::SetScissors(const CRect &rect)
+{
+ if (!m_bRenderCreated)
+ return;
+ GLint x1 = MathUtils::round_int(static_cast<double>(rect.x1));
+ GLint y1 = MathUtils::round_int(static_cast<double>(rect.y1));
+ GLint x2 = MathUtils::round_int(static_cast<double>(rect.x2));
+ GLint y2 = MathUtils::round_int(static_cast<double>(rect.y2));
+ glScissor(x1, m_height - y2, x2-x1, y2-y1);
+}
+
+void CRenderSystemGLES::ResetScissors()
+{
+ SetScissors(CRect(0, 0, (float)m_width, (float)m_height));
+}
+
+void CRenderSystemGLES::InitialiseShaders()
+{
+ std::string defines;
+ m_limitedColorRange = CServiceBroker::GetWinSystem()->UseLimitedColor();
+ if (m_limitedColorRange)
+ {
+ defines += "#define KODI_LIMITED_RANGE 1\n";
+ }
+
+ m_pShader[ShaderMethodGLES::SM_DEFAULT] =
+ std::make_unique<CGLESShader>("gles_shader.vert", "gles_shader_default.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_DEFAULT]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_DEFAULT]->Free();
+ m_pShader[ShaderMethodGLES::SM_DEFAULT].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_default.frag - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGLES::SM_TEXTURE] =
+ std::make_unique<CGLESShader>("gles_shader_texture.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_TEXTURE]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_texture.frag - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGLES::SM_MULTI] =
+ std::make_unique<CGLESShader>("gles_shader_multi.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_MULTI]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_MULTI]->Free();
+ m_pShader[ShaderMethodGLES::SM_MULTI].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_multi.frag - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGLES::SM_FONTS] =
+ std::make_unique<CGLESShader>("gles_shader_fonts.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_FONTS]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_FONTS]->Free();
+ m_pShader[ShaderMethodGLES::SM_FONTS].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_fonts.frag - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOBLEND] =
+ std::make_unique<CGLESShader>("gles_shader_texture_noblend.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_TEXTURE_NOBLEND]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOBLEND]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOBLEND].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_texture_noblend.frag - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGLES::SM_MULTI_BLENDCOLOR] =
+ std::make_unique<CGLESShader>("gles_shader_multi_blendcolor.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_MULTI_BLENDCOLOR]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_MULTI_BLENDCOLOR]->Free();
+ m_pShader[ShaderMethodGLES::SM_MULTI_BLENDCOLOR].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_multi_blendcolor.frag - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA] =
+ std::make_unique<CGLESShader>("gles_shader_rgba.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_rgba.frag - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BLENDCOLOR] =
+ std::make_unique<CGLESShader>("gles_shader_rgba_blendcolor.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BLENDCOLOR]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BLENDCOLOR]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BLENDCOLOR].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_rgba_blendcolor.frag - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB] =
+ std::make_unique<CGLESShader>("gles_shader_rgba_bob.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_rgba_bob.frag - compile and link failed");
+ }
+
+ if (IsExtSupported("GL_OES_EGL_image_external"))
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_OES] =
+ std::make_unique<CGLESShader>("gles_shader_rgba_oes.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_OES]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_OES]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_OES].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_rgba_oes.frag - compile and link failed");
+ }
+
+
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB_OES] =
+ std::make_unique<CGLESShader>("gles_shader_rgba_bob_oes.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB_OES]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB_OES]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB_OES].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_rgba_bob_oes.frag - compile and link failed");
+ }
+ }
+
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOALPHA] =
+ std::make_unique<CGLESShader>("gles_shader_texture_noalpha.frag", defines);
+ if (!m_pShader[ShaderMethodGLES::SM_TEXTURE_NOALPHA]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOALPHA]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOALPHA].reset();
+ CLog::Log(LOGERROR, "GUI Shader gles_shader_texture_noalpha.frag - compile and link failed");
+ }
+}
+
+void CRenderSystemGLES::ReleaseShaders()
+{
+ if (m_pShader[ShaderMethodGLES::SM_DEFAULT])
+ m_pShader[ShaderMethodGLES::SM_DEFAULT]->Free();
+ m_pShader[ShaderMethodGLES::SM_DEFAULT].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_TEXTURE])
+ m_pShader[ShaderMethodGLES::SM_TEXTURE]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_MULTI])
+ m_pShader[ShaderMethodGLES::SM_MULTI]->Free();
+ m_pShader[ShaderMethodGLES::SM_MULTI].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_FONTS])
+ m_pShader[ShaderMethodGLES::SM_FONTS]->Free();
+ m_pShader[ShaderMethodGLES::SM_FONTS].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_TEXTURE_NOBLEND])
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOBLEND]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOBLEND].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_MULTI_BLENDCOLOR])
+ m_pShader[ShaderMethodGLES::SM_MULTI_BLENDCOLOR]->Free();
+ m_pShader[ShaderMethodGLES::SM_MULTI_BLENDCOLOR].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA])
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BLENDCOLOR])
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BLENDCOLOR]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BLENDCOLOR].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB])
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_OES])
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_OES]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_OES].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB_OES])
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB_OES]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_RGBA_BOB_OES].reset();
+
+ if (m_pShader[ShaderMethodGLES::SM_TEXTURE_NOALPHA])
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOALPHA]->Free();
+ m_pShader[ShaderMethodGLES::SM_TEXTURE_NOALPHA].reset();
+}
+
+void CRenderSystemGLES::EnableGUIShader(ShaderMethodGLES method)
+{
+ m_method = method;
+ if (m_pShader[m_method])
+ {
+ m_pShader[m_method]->Enable();
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "Invalid GUI Shader selected - {}", method);
+ }
+}
+
+void CRenderSystemGLES::DisableGUIShader()
+{
+ if (m_pShader[m_method])
+ {
+ m_pShader[m_method]->Disable();
+ }
+ m_method = ShaderMethodGLES::SM_DEFAULT;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetPos()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetPosLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetCol()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetColLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetCoord0()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetCord0Loc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetCoord1()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetCord1Loc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetUniCol()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetUniColLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetCoord0Matrix()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetCoord0MatrixLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetField()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetFieldLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetStep()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetStepLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetContrast()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetContrastLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGLES::GUIShaderGetBrightness()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetBrightnessLoc();
+
+ return -1;
+}
+
+bool CRenderSystemGLES::SupportsStereo(RENDER_STEREO_MODE mode) const
+{
+ return CRenderSystemBase::SupportsStereo(mode);
+}
+
+GLint CRenderSystemGLES::GUIShaderGetModel()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetModelLoc();
+
+ return -1;
+}
diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h
new file mode 100644
index 0000000..e0cd72b
--- /dev/null
+++ b/xbmc/rendering/gles/RenderSystemGLES.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "GLESShader.h"
+#include "rendering/RenderSystem.h"
+#include "utils/ColorUtils.h"
+#include "utils/Map.h"
+
+#include <map>
+
+#include <fmt/format.h>
+
+#include "system_gl.h"
+
+enum class ShaderMethodGLES
+{
+ SM_DEFAULT,
+ SM_TEXTURE,
+ SM_MULTI,
+ SM_FONTS,
+ SM_TEXTURE_NOBLEND,
+ SM_MULTI_BLENDCOLOR,
+ SM_TEXTURE_RGBA,
+ SM_TEXTURE_RGBA_OES,
+ SM_TEXTURE_RGBA_BLENDCOLOR,
+ SM_TEXTURE_RGBA_BOB,
+ SM_TEXTURE_RGBA_BOB_OES,
+ SM_TEXTURE_NOALPHA,
+ SM_MAX
+};
+
+template<>
+struct fmt::formatter<ShaderMethodGLES> : fmt::formatter<std::string_view>
+{
+ template<typename FormatContext>
+ constexpr auto format(const ShaderMethodGLES& shaderMethod, FormatContext& ctx)
+ {
+ const auto it = ShaderMethodGLESMap.find(shaderMethod);
+ if (it == ShaderMethodGLESMap.cend())
+ throw std::range_error("no string mapping found for shader method");
+
+ return fmt::formatter<string_view>::format(it->second, ctx);
+ }
+
+private:
+ static constexpr auto ShaderMethodGLESMap = make_map<ShaderMethodGLES, std::string_view>({
+ {ShaderMethodGLES::SM_DEFAULT, "default"},
+ {ShaderMethodGLES::SM_TEXTURE, "texture"},
+ {ShaderMethodGLES::SM_MULTI, "multi"},
+ {ShaderMethodGLES::SM_FONTS, "fonts"},
+ {ShaderMethodGLES::SM_TEXTURE_NOBLEND, "texture no blending"},
+ {ShaderMethodGLES::SM_MULTI_BLENDCOLOR, "multi blend colour"},
+ {ShaderMethodGLES::SM_TEXTURE_RGBA, "texure rgba"},
+ {ShaderMethodGLES::SM_TEXTURE_RGBA_OES, "texture rgba OES"},
+ {ShaderMethodGLES::SM_TEXTURE_RGBA_BLENDCOLOR, "texture rgba blend colour"},
+ {ShaderMethodGLES::SM_TEXTURE_RGBA_BOB, "texture rgba bob"},
+ {ShaderMethodGLES::SM_TEXTURE_RGBA_BOB_OES, "texture rgba bob OES"},
+ {ShaderMethodGLES::SM_TEXTURE_NOALPHA, "texture no alpha"},
+ });
+
+ static_assert(static_cast<size_t>(ShaderMethodGLES::SM_MAX) == ShaderMethodGLESMap.size(),
+ "ShaderMethodGLESMap doesn't match the size of ShaderMethodGLES, did you forget to "
+ "add/remove a mapping?");
+};
+
+class CRenderSystemGLES : public CRenderSystemBase
+{
+public:
+ CRenderSystemGLES();
+ ~CRenderSystemGLES() override = default;
+
+ bool InitRenderSystem() override;
+ bool DestroyRenderSystem() override;
+ bool ResetRenderSystem(int width, int height) override;
+
+ bool BeginRender() override;
+ bool EndRender() override;
+ void PresentRender(bool rendered, bool videoLayer) override;
+ bool ClearBuffers(UTILS::COLOR::Color color) override;
+ bool IsExtSupported(const char* extension) const override;
+
+ void SetVSync(bool vsync);
+ void ResetVSync() { m_bVsyncInit = false; }
+
+ void SetViewPort(const CRect& viewPort) override;
+ void GetViewPort(CRect& viewPort) override;
+
+ bool ScissorsCanEffectClipping() override;
+ CRect ClipRectToScissorRect(const CRect &rect) override;
+ void SetScissors(const CRect& rect) override;
+ void ResetScissors() override;
+
+ void CaptureStateBlock() override;
+ void ApplyStateBlock() override;
+
+ void SetCameraPosition(const CPoint &camera, int screenWidth, int screenHeight, float stereoFactor = 0.0f) override;
+
+ bool SupportsStereo(RENDER_STEREO_MODE mode) const override;
+
+ void Project(float &x, float &y, float &z) override;
+
+ std::string GetShaderPath(const std::string &filename) override { return "GLES/2.0/"; }
+
+ void InitialiseShaders();
+ void ReleaseShaders();
+ void EnableGUIShader(ShaderMethodGLES method);
+ void DisableGUIShader();
+
+ GLint GUIShaderGetPos();
+ GLint GUIShaderGetCol();
+ GLint GUIShaderGetCoord0();
+ GLint GUIShaderGetCoord1();
+ GLint GUIShaderGetUniCol();
+ GLint GUIShaderGetCoord0Matrix();
+ GLint GUIShaderGetField();
+ GLint GUIShaderGetStep();
+ GLint GUIShaderGetContrast();
+ GLint GUIShaderGetBrightness();
+ GLint GUIShaderGetModel();
+
+protected:
+ virtual void SetVSyncImpl(bool enable) = 0;
+ virtual void PresentRenderImpl(bool rendered) = 0;
+ void CalculateMaxTexturesize();
+
+ bool m_bVsyncInit{false};
+ int m_width;
+ int m_height;
+
+ std::string m_RenderExtensions;
+
+ std::map<ShaderMethodGLES, std::unique_ptr<CGLESShader>> m_pShader;
+ ShaderMethodGLES m_method = ShaderMethodGLES::SM_DEFAULT;
+
+ GLint m_viewPort[4];
+};
diff --git a/xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp b/xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp
new file mode 100644
index 0000000..3c290ec
--- /dev/null
+++ b/xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ScreenshotSurfaceGLES.h"
+
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "utils/Screenshot.h"
+#include "windowing/GraphicContext.h"
+
+#include <mutex>
+#include <vector>
+
+#include "system_gl.h"
+
+void CScreenshotSurfaceGLES::Register()
+{
+ CScreenShot::Register(CScreenshotSurfaceGLES::CreateSurface);
+}
+
+std::unique_ptr<IScreenshotSurface> CScreenshotSurfaceGLES::CreateSurface()
+{
+ return std::unique_ptr<CScreenshotSurfaceGLES>(new CScreenshotSurfaceGLES());
+}
+
+bool CScreenshotSurfaceGLES::Capture()
+{
+ CWinSystemBase* winsystem = CServiceBroker::GetWinSystem();
+ if (!winsystem)
+ return false;
+
+ CGUIComponent* gui = CServiceBroker::GetGUI();
+ if (!gui)
+ return false;
+
+ std::unique_lock<CCriticalSection> lock(winsystem->GetGfxContext());
+ gui->GetWindowManager().Render();
+
+ //get current viewport
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ m_width = viewport[2] - viewport[0];
+ m_height = viewport[3] - viewport[1];
+ m_stride = m_width * 4;
+ std::vector<uint8_t> surface(m_stride * m_height);
+
+ //read pixels from the backbuffer
+ glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(surface.data()));
+
+ //make a new buffer and copy the read image to it with the Y axis inverted
+ m_buffer = new unsigned char[m_stride * m_height];
+ for (int y = 0; y < m_height; y++)
+ {
+ // we need to save in BGRA order so XOR Swap RGBA -> BGRA
+ unsigned char* swap_pixels = surface.data() + (m_height - y - 1) * m_stride;
+ for (int x = 0; x < m_width; x++, swap_pixels += 4)
+ {
+ std::swap(swap_pixels[0], swap_pixels[2]);
+ }
+
+ memcpy(m_buffer + y * m_stride, surface.data() + (m_height - y - 1) * m_stride, m_stride);
+ }
+
+ return m_buffer != nullptr;
+}
diff --git a/xbmc/rendering/gles/ScreenshotSurfaceGLES.h b/xbmc/rendering/gles/ScreenshotSurfaceGLES.h
new file mode 100644
index 0000000..1ca1730
--- /dev/null
+++ b/xbmc/rendering/gles/ScreenshotSurfaceGLES.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "utils/IScreenshotSurface.h"
+
+#include <memory>
+
+class CScreenshotSurfaceGLES : public IScreenshotSurface
+{
+public:
+ static void Register();
+ static std::unique_ptr<IScreenshotSurface> CreateSurface();
+
+ bool Capture() override;
+};