summaryrefslogtreecommitdiffstats
path: root/xbmc/rendering/gl
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/rendering/gl')
-rw-r--r--xbmc/rendering/gl/CMakeLists.txt12
-rw-r--r--xbmc/rendering/gl/GLShader.cpp162
-rw-r--r--xbmc/rendering/gl/GLShader.h55
-rw-r--r--xbmc/rendering/gl/RenderSystemGL.cpp795
-rw-r--r--xbmc/rendering/gl/RenderSystemGL.h139
-rw-r--r--xbmc/rendering/gl/ScreenshotSurfaceGL.cpp65
-rw-r--r--xbmc/rendering/gl/ScreenshotSurfaceGL.h22
7 files changed, 1250 insertions, 0 deletions
diff --git a/xbmc/rendering/gl/CMakeLists.txt b/xbmc/rendering/gl/CMakeLists.txt
new file mode 100644
index 0000000..6aadafa
--- /dev/null
+++ b/xbmc/rendering/gl/CMakeLists.txt
@@ -0,0 +1,12 @@
+if(OPENGL_FOUND)
+ set(SOURCES RenderSystemGL.cpp
+ ScreenshotSurfaceGL.cpp
+ GLShader.cpp)
+
+ set(HEADERS RenderSystemGL.h
+ ScreenshotSurfaceGL.h
+ GLShader.h)
+
+ core_add_library(rendering_gl)
+endif()
+
diff --git a/xbmc/rendering/gl/GLShader.cpp b/xbmc/rendering/gl/GLShader.cpp
new file mode 100644
index 0000000..e91af97
--- /dev/null
+++ b/xbmc/rendering/gl/GLShader.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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 "GLShader.h"
+
+#include "ServiceBroker.h"
+#include "rendering/MatrixGL.h"
+#include "rendering/RenderSystem.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+using namespace Shaders;
+
+CGLShader::CGLShader(const char* shader, const std::string& prefix)
+{
+ m_proj = nullptr;
+ m_model = nullptr;
+ m_clipPossible = false;
+
+ VertexShader()->LoadSource("gl_shader_vert.glsl");
+ PixelShader()->LoadSource(shader, prefix);
+}
+
+CGLShader::CGLShader(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 CGLShader::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");
+
+ // Variables passed directly to the Vertex shader
+ m_hProj = glGetUniformLocation(ProgramHandle(), "m_proj");
+ m_hModel = glGetUniformLocation(ProgramHandle(), "m_model");
+
+ // 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);
+
+ glUseProgram(0);
+}
+
+bool CGLShader::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;
+ }
+
+ return true;
+}
+
+void CGLShader::Free()
+{
+ // Do Cleanup here
+ CGLSLShaderProgram::Free();
+}
diff --git a/xbmc/rendering/gl/GLShader.h b/xbmc/rendering/gl/GLShader.h
new file mode 100644
index 0000000..d5b82f3
--- /dev/null
+++ b/xbmc/rendering/gl/GLShader.h
@@ -0,0 +1,55 @@
+/*
+ * 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 CGLShader : public Shaders::CGLSLShaderProgram
+{
+public:
+ CGLShader(const char* shader, const std::string& prefix);
+ CGLShader(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 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;
+
+ const GLfloat *m_proj = nullptr;
+ const GLfloat *m_model = nullptr;
+
+ bool m_clipPossible = false;
+ GLfloat m_clipXFactor;
+ GLfloat m_clipXOffset;
+ GLfloat m_clipYFactor;
+ GLfloat m_clipYOffset;
+};
diff --git a/xbmc/rendering/gl/RenderSystemGL.cpp b/xbmc/rendering/gl/RenderSystemGL.cpp
new file mode 100644
index 0000000..46a354c
--- /dev/null
+++ b/xbmc/rendering/gl/RenderSystemGL.cpp
@@ -0,0 +1,795 @@
+/*
+ * 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 "RenderSystemGL.h"
+
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "guilib/GUITextureGL.h"
+#include "rendering/MatrixGL.h"
+#include "utils/FileUtils.h"
+#include "utils/GLUtils.h"
+#include "utils/MathUtils.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+#include "windowing/WinSystem.h"
+
+using namespace std::chrono_literals;
+
+CRenderSystemGL::CRenderSystemGL() : CRenderSystemBase()
+{
+}
+
+CRenderSystemGL::~CRenderSystemGL() = default;
+
+bool CRenderSystemGL::InitRenderSystem()
+{
+ m_bVSync = false;
+ m_bVsyncInit = false;
+ m_maxTextureSize = 2048;
+
+ // Get the GL 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);
+ m_RenderVersion = ver;
+ }
+
+ CLog::Log(LOGINFO, "CRenderSystemGL::{} - Version: {}, Major: {}, Minor: {}", __FUNCTION__, ver,
+ m_RenderVersionMajor, m_RenderVersionMinor);
+
+ m_RenderExtensions = " ";
+ if (m_RenderVersionMajor > 3 ||
+ (m_RenderVersionMajor == 3 && m_RenderVersionMinor >= 2))
+ {
+ GLint n = 0;
+ glGetIntegerv(GL_NUM_EXTENSIONS, &n);
+ if (n > 0)
+ {
+ GLint i;
+ for (i = 0; i < n; i++)
+ {
+ m_RenderExtensions += (const char*) glGetStringi(GL_EXTENSIONS, i);
+ m_RenderExtensions += " ";
+ }
+ }
+ }
+ else
+ {
+ auto extensions = (const char*) glGetString(GL_EXTENSIONS);
+ if (extensions)
+ {
+ m_RenderExtensions += extensions;
+ }
+ }
+ m_RenderExtensions += " ";
+
+ ver = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
+ if (ver)
+ {
+ sscanf(ver, "%d.%d", &m_glslMajor, &m_glslMinor);
+ }
+ else
+ {
+ m_glslMajor = 1;
+ m_glslMinor = 0;
+ }
+
+ LogGraphicsInfo();
+
+ // 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_bRenderCreated = true;
+
+ if (m_RenderVersionMajor > 3 ||
+ (m_RenderVersionMajor == 3 && m_RenderVersionMinor >= 2))
+ {
+ glGenVertexArrays(1, &m_vertexArray);
+ glBindVertexArray(m_vertexArray);
+ }
+
+ InitialiseShaders();
+
+ CGUITextureGL::Register();
+
+ return true;
+}
+
+bool CRenderSystemGL::ResetRenderSystem(int width, int height)
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ m_width = width;
+ m_height = height;
+
+ if (m_RenderVersionMajor > 3 ||
+ (m_RenderVersionMajor == 3 && m_RenderVersionMinor >= 2))
+ {
+ glBindVertexArray(0);
+ glDeleteVertexArrays(1, &m_vertexArray);
+ glGenVertexArrays(1, &m_vertexArray);
+ glBindVertexArray(m_vertexArray);
+ }
+
+ ReleaseShaders();
+ InitialiseShaders();
+
+ 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();
+
+ if (IsExtSupported("GL_ARB_multitexture"))
+ {
+ //clear error flags
+ ResetGLErrors();
+
+ GLint maxtex;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxtex);
+
+ //some sanity checks
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ CLog::Log(LOGERROR, "ResetRenderSystem() GL_MAX_TEXTURE_IMAGE_UNITS returned error {}",
+ (int)error);
+ maxtex = 3;
+ }
+ else if (maxtex < 1 || maxtex > 32)
+ {
+ CLog::Log(LOGERROR,
+ "ResetRenderSystem() GL_MAX_TEXTURE_IMAGE_UNITS returned invalid value {}",
+ (int)maxtex);
+ maxtex = 3;
+ }
+
+ //reset texture matrix for all textures
+ for (GLint i = 0; i < maxtex; i++)
+ {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glMatrixTexture.Load();
+ }
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glEnable(GL_BLEND); // Turn Blending On
+ glDisable(GL_DEPTH_TEST);
+
+ return true;
+}
+
+bool CRenderSystemGL::DestroyRenderSystem()
+{
+ if (m_vertexArray != GL_NONE)
+ {
+ glDeleteVertexArrays(1, &m_vertexArray);
+ }
+
+ ReleaseShaders();
+ m_bRenderCreated = false;
+
+ return true;
+}
+
+bool CRenderSystemGL::BeginRender()
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ bool useLimited = CServiceBroker::GetWinSystem()->UseLimitedColor();
+
+ if (m_limitedColorRange != useLimited)
+ {
+ ReleaseShaders();
+ InitialiseShaders();
+ }
+
+ m_limitedColorRange = useLimited;
+ return true;
+}
+
+bool CRenderSystemGL::EndRender()
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ return true;
+}
+
+bool CRenderSystemGL::ClearBuffers(UTILS::COLOR::Color color)
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ /* clear is not affected by stipple pattern, so we can only clear on first frame */
+ if(m_stereoMode == RENDER_STEREO_MODE_INTERLACED && m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ return true;
+
+ 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 CRenderSystemGL::IsExtSupported(const char* extension) const
+{
+ if (m_RenderVersionMajor > 3 ||
+ (m_RenderVersionMajor == 3 && m_RenderVersionMinor >= 2))
+ {
+ if (strcmp( extension, "GL_EXT_framebuffer_object") == 0)
+ {
+ return true;
+ }
+ if (strcmp( extension, "GL_ARB_texture_non_power_of_two") == 0)
+ {
+ return true;
+ }
+ }
+
+ std::string name;
+ name = " ";
+ name += extension;
+ name += " ";
+
+ return m_RenderExtensions.find(name) != std::string::npos;
+}
+
+bool CRenderSystemGL::SupportsNPOT(bool dxt) const
+{
+ return true;
+}
+
+void CRenderSystemGL::PresentRender(bool rendered, bool videoLayer)
+{
+ SetVSync(true);
+
+ if (!m_bRenderCreated)
+ return;
+
+ PresentRenderImpl(rendered);
+
+ if (!rendered)
+ KODI::TIME::Sleep(40ms);
+}
+
+void CRenderSystemGL::SetVSync(bool enable)
+{
+ if (m_bVSync == enable && m_bVsyncInit == true)
+ return;
+
+ if (!m_bRenderCreated)
+ return;
+
+ if (enable)
+ CLog::Log(LOGINFO, "GL: Enabling VSYNC");
+ else
+ CLog::Log(LOGINFO, "GL: Disabling VSYNC");
+
+ m_bVSync = enable;
+ m_bVsyncInit = true;
+
+ SetVSyncImpl(enable);
+}
+
+void CRenderSystemGL::CaptureStateBlock()
+{
+ if (!m_bRenderCreated)
+ return;
+
+ glMatrixProject.Push();
+ glMatrixModview.Push();
+ glMatrixTexture.Push();
+
+ glDisable(GL_SCISSOR_TEST); // fixes FBO corruption on Macs
+ glActiveTexture(GL_TEXTURE0);
+}
+
+void CRenderSystemGL::ApplyStateBlock()
+{
+ if (!m_bRenderCreated)
+ return;
+
+ glBindVertexArray(m_vertexArray);
+
+ glViewport(m_viewPort[0], m_viewPort[1], m_viewPort[2], m_viewPort[3]);
+
+ glMatrixProject.PopLoad();
+ glMatrixModview.PopLoad();
+ glMatrixTexture.PopLoad();
+
+ glActiveTexture(GL_TEXTURE0);
+ glEnable(GL_BLEND);
+ glEnable(GL_SCISSOR_TEST);
+}
+
+void CRenderSystemGL::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 CRenderSystemGL::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 CRenderSystemGL::CalculateMaxTexturesize()
+{
+ GLint width = 256;
+
+ // reset any previous GL errors
+ ResetGLErrors();
+
+ // max out at 2^(8+8)
+ for (int i = 0 ; i<8 ; i++)
+ {
+ glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, width, width, 0, GL_BGRA,
+ GL_UNSIGNED_BYTE, NULL);
+ glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
+ &width);
+
+ // GMA950 on OS X sets error instead
+ if (width == 0 || (glGetError() != GL_NO_ERROR) )
+ break;
+
+ m_maxTextureSize = width;
+ width *= 2;
+ if (width > 65536) // have an upper limit in case driver acts stupid
+ {
+ CLog::Log(LOGERROR, "GL: Could not determine maximum texture width, falling back to 2048");
+ m_maxTextureSize = 2048;
+ break;
+ }
+ }
+
+ CLog::Log(LOGINFO, "GL: Maximum texture width: {}", m_maxTextureSize);
+}
+
+void CRenderSystemGL::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 CRenderSystemGL::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 CRenderSystemGL::ScissorsCanEffectClipping()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->HardwareClipIsPossible();
+
+ return false;
+}
+
+CRect CRenderSystemGL::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 CRenderSystemGL::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 CRenderSystemGL::ResetScissors()
+{
+ SetScissors(CRect(0, 0, (float)m_width, (float)m_height));
+}
+
+void CRenderSystemGL::GetGLSLVersion(int& major, int& minor)
+{
+ major = m_glslMajor;
+ minor = m_glslMinor;
+}
+
+void CRenderSystemGL::ResetGLErrors()
+{
+ int count = 0;
+ while (glGetError() != GL_NO_ERROR)
+ {
+ count++;
+ if (count >= 100)
+ {
+ CLog::Log(
+ LOGWARNING,
+ "CRenderSystemGL::ResetGLErrors glGetError didn't return GL_NO_ERROR after {} iterations",
+ count);
+ break;
+ }
+ }
+}
+static const GLubyte stipple_3d[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00,
+};
+
+void CRenderSystemGL::SetStereoMode(RENDER_STEREO_MODE mode, RENDER_STEREO_VIEW view)
+{
+ CRenderSystemBase::SetStereoMode(mode, view);
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDrawBuffer(GL_BACK);
+
+ if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN)
+ {
+ if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
+ glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
+ else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
+ if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA)
+ {
+ if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
+ glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
+ else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ glColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_TRUE);
+ }
+ if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_YELLOW_BLUE)
+ {
+ if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
+ glColorMask(GL_TRUE, GL_TRUE, GL_FALSE, GL_TRUE);
+ else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE);
+ }
+
+ if(m_stereoMode == RENDER_STEREO_MODE_INTERLACED)
+ {
+ glEnable(GL_POLYGON_STIPPLE);
+ if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
+ glPolygonStipple(stipple_3d);
+ else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ glPolygonStipple(stipple_3d+4);
+ }
+
+ if(m_stereoMode == RENDER_STEREO_MODE_HARDWAREBASED)
+ {
+ if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
+ glDrawBuffer(GL_BACK_LEFT);
+ else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ glDrawBuffer(GL_BACK_RIGHT);
+ }
+
+}
+
+bool CRenderSystemGL::SupportsStereo(RENDER_STEREO_MODE mode) const
+{
+ switch(mode)
+ {
+ case RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN:
+ case RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA:
+ case RENDER_STEREO_MODE_ANAGLYPH_YELLOW_BLUE:
+ case RENDER_STEREO_MODE_INTERLACED:
+ return true;
+ case RENDER_STEREO_MODE_HARDWAREBASED: {
+ //This is called by setting init, at which point GL is not inited
+ //luckily if GL doesn't support this, it will just behave as if
+ //it was not in effect.
+ //GLboolean stereo = GL_FALSE;
+ //glGetBooleanv(GL_STEREO, &stereo);
+ //return stereo == GL_TRUE ? true : false;
+ return true;
+ }
+ default:
+ return CRenderSystemBase::SupportsStereo(mode);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// shaders
+// -----------------------------------------------------------------------------
+void CRenderSystemGL::InitialiseShaders()
+{
+ std::string defines;
+ m_limitedColorRange = CServiceBroker::GetWinSystem()->UseLimitedColor();
+ if (m_limitedColorRange)
+ {
+ defines += "#define KODI_LIMITED_RANGE 1\n";
+ }
+
+ m_pShader[ShaderMethodGL::SM_DEFAULT] = std::make_unique<CGLShader>(
+ "gl_shader_vert_default.glsl", "gl_shader_frag_default.glsl", defines);
+ if (!m_pShader[ShaderMethodGL::SM_DEFAULT]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGL::SM_DEFAULT]->Free();
+ m_pShader[ShaderMethodGL::SM_DEFAULT].reset();
+ CLog::Log(LOGERROR, "GUI Shader gl_shader_frag_default.glsl - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGL::SM_TEXTURE] =
+ std::make_unique<CGLShader>("gl_shader_frag_texture.glsl", defines);
+ if (!m_pShader[ShaderMethodGL::SM_TEXTURE]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGL::SM_TEXTURE]->Free();
+ m_pShader[ShaderMethodGL::SM_TEXTURE].reset();
+ CLog::Log(LOGERROR, "GUI Shader gl_shader_frag_texture.glsl - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGL::SM_TEXTURE_LIM] =
+ std::make_unique<CGLShader>("gl_shader_frag_texture_lim.glsl", defines);
+ if (!m_pShader[ShaderMethodGL::SM_TEXTURE_LIM]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGL::SM_TEXTURE_LIM]->Free();
+ m_pShader[ShaderMethodGL::SM_TEXTURE_LIM].reset();
+ CLog::Log(LOGERROR, "GUI Shader gl_shader_frag_texture_lim.glsl - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGL::SM_MULTI] =
+ std::make_unique<CGLShader>("gl_shader_frag_multi.glsl", defines);
+ if (!m_pShader[ShaderMethodGL::SM_MULTI]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGL::SM_MULTI]->Free();
+ m_pShader[ShaderMethodGL::SM_MULTI].reset();
+ CLog::Log(LOGERROR, "GUI Shader gl_shader_frag_multi.glsl - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGL::SM_FONTS] =
+ std::make_unique<CGLShader>("gl_shader_frag_fonts.glsl", defines);
+ if (!m_pShader[ShaderMethodGL::SM_FONTS]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGL::SM_FONTS]->Free();
+ m_pShader[ShaderMethodGL::SM_FONTS].reset();
+ CLog::Log(LOGERROR, "GUI Shader gl_shader_frag_fonts.glsl - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGL::SM_TEXTURE_NOBLEND] =
+ std::make_unique<CGLShader>("gl_shader_frag_texture_noblend.glsl", defines);
+ if (!m_pShader[ShaderMethodGL::SM_TEXTURE_NOBLEND]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGL::SM_TEXTURE_NOBLEND]->Free();
+ m_pShader[ShaderMethodGL::SM_TEXTURE_NOBLEND].reset();
+ CLog::Log(LOGERROR, "GUI Shader gl_shader_frag_texture_noblend.glsl - compile and link failed");
+ }
+
+ m_pShader[ShaderMethodGL::SM_MULTI_BLENDCOLOR] =
+ std::make_unique<CGLShader>("gl_shader_frag_multi_blendcolor.glsl", defines);
+ if (!m_pShader[ShaderMethodGL::SM_MULTI_BLENDCOLOR]->CompileAndLink())
+ {
+ m_pShader[ShaderMethodGL::SM_MULTI_BLENDCOLOR]->Free();
+ m_pShader[ShaderMethodGL::SM_MULTI_BLENDCOLOR].reset();
+ CLog::Log(LOGERROR, "GUI Shader gl_shader_frag_multi_blendcolor.glsl - compile and link failed");
+ }
+}
+
+void CRenderSystemGL::ReleaseShaders()
+{
+ if (m_pShader[ShaderMethodGL::SM_DEFAULT])
+ m_pShader[ShaderMethodGL::SM_DEFAULT]->Free();
+ m_pShader[ShaderMethodGL::SM_DEFAULT].reset();
+
+ if (m_pShader[ShaderMethodGL::SM_TEXTURE])
+ m_pShader[ShaderMethodGL::SM_TEXTURE]->Free();
+ m_pShader[ShaderMethodGL::SM_TEXTURE].reset();
+
+ if (m_pShader[ShaderMethodGL::SM_TEXTURE_LIM])
+ m_pShader[ShaderMethodGL::SM_TEXTURE_LIM]->Free();
+ m_pShader[ShaderMethodGL::SM_TEXTURE_LIM].reset();
+
+ if (m_pShader[ShaderMethodGL::SM_MULTI])
+ m_pShader[ShaderMethodGL::SM_MULTI]->Free();
+ m_pShader[ShaderMethodGL::SM_MULTI].reset();
+
+ if (m_pShader[ShaderMethodGL::SM_FONTS])
+ m_pShader[ShaderMethodGL::SM_FONTS]->Free();
+ m_pShader[ShaderMethodGL::SM_FONTS].reset();
+
+ if (m_pShader[ShaderMethodGL::SM_TEXTURE_NOBLEND])
+ m_pShader[ShaderMethodGL::SM_TEXTURE_NOBLEND]->Free();
+ m_pShader[ShaderMethodGL::SM_TEXTURE_NOBLEND].reset();
+
+ if (m_pShader[ShaderMethodGL::SM_MULTI_BLENDCOLOR])
+ m_pShader[ShaderMethodGL::SM_MULTI_BLENDCOLOR]->Free();
+ m_pShader[ShaderMethodGL::SM_MULTI_BLENDCOLOR].reset();
+}
+
+void CRenderSystemGL::EnableShader(ShaderMethodGL method)
+{
+ m_method = method;
+ if (m_pShader[m_method])
+ {
+ m_pShader[m_method]->Enable();
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "Invalid GUI Shader selected {}", method);
+ }
+}
+
+void CRenderSystemGL::DisableShader()
+{
+ if (m_pShader[m_method])
+ {
+ m_pShader[m_method]->Disable();
+ }
+ m_method = ShaderMethodGL::SM_DEFAULT;
+}
+
+GLint CRenderSystemGL::ShaderGetPos()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetPosLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGL::ShaderGetCol()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetColLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGL::ShaderGetCoord0()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetCord0Loc();
+
+ return -1;
+}
+
+GLint CRenderSystemGL::ShaderGetCoord1()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetCord1Loc();
+
+ return -1;
+}
+
+GLint CRenderSystemGL::ShaderGetUniCol()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetUniColLoc();
+
+ return -1;
+}
+
+GLint CRenderSystemGL::ShaderGetModel()
+{
+ if (m_pShader[m_method])
+ return m_pShader[m_method]->GetModelLoc();
+
+ return -1;
+}
+
+std::string CRenderSystemGL::GetShaderPath(const std::string &filename)
+{
+ std::string path = "GL/1.2/";
+
+ if (m_glslMajor >= 4)
+ {
+ std::string file = "special://xbmc/system/shaders/GL/4.0/" + filename;
+ const CURL pathToUrl(file);
+ if (CFileUtils::Exists(pathToUrl.Get()))
+ return "GL/4.0/";
+ }
+ if (m_glslMajor >= 2 || (m_glslMajor == 1 && m_glslMinor >= 50))
+ path = "GL/1.5/";
+
+ return path;
+}
diff --git a/xbmc/rendering/gl/RenderSystemGL.h b/xbmc/rendering/gl/RenderSystemGL.h
new file mode 100644
index 0000000..191c97f
--- /dev/null
+++ b/xbmc/rendering/gl/RenderSystemGL.h
@@ -0,0 +1,139 @@
+/*
+ * 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 "GLShader.h"
+#include "rendering/RenderSystem.h"
+#include "utils/ColorUtils.h"
+#include "utils/Map.h"
+
+#include <map>
+#include <memory>
+
+#include <fmt/format.h>
+
+#include "system_gl.h"
+
+enum class ShaderMethodGL
+{
+ SM_DEFAULT = 0,
+ SM_TEXTURE,
+ SM_TEXTURE_LIM,
+ SM_MULTI,
+ SM_FONTS,
+ SM_TEXTURE_NOBLEND,
+ SM_MULTI_BLENDCOLOR,
+ SM_MAX
+};
+
+template<>
+struct fmt::formatter<ShaderMethodGL> : fmt::formatter<std::string_view>
+{
+ template<typename FormatContext>
+ constexpr auto format(const ShaderMethodGL& shaderMethod, FormatContext& ctx)
+ {
+ const auto it = ShaderMethodGLMap.find(shaderMethod);
+ if (it == ShaderMethodGLMap.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 ShaderMethodGLMap = make_map<ShaderMethodGL, std::string_view>({
+ {ShaderMethodGL::SM_DEFAULT, "default"},
+ {ShaderMethodGL::SM_TEXTURE, "texture"},
+ {ShaderMethodGL::SM_TEXTURE_LIM, "texture limited"},
+ {ShaderMethodGL::SM_MULTI, "multi"},
+ {ShaderMethodGL::SM_FONTS, "fonts"},
+ {ShaderMethodGL::SM_TEXTURE_NOBLEND, "texture no blending"},
+ {ShaderMethodGL::SM_MULTI_BLENDCOLOR, "multi blend colour"},
+ });
+
+ static_assert(static_cast<size_t>(ShaderMethodGL::SM_MAX) == ShaderMethodGLMap.size(),
+ "ShaderMethodGLMap doesn't match the size of ShaderMethodGL, did you forget to "
+ "add/remove a mapping?");
+};
+
+class CRenderSystemGL : public CRenderSystemBase
+{
+public:
+ CRenderSystemGL();
+ ~CRenderSystemGL() override;
+ 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;
+
+ void SetStereoMode(RENDER_STEREO_MODE mode, RENDER_STEREO_VIEW view) override;
+ bool SupportsStereo(RENDER_STEREO_MODE mode) const override;
+ bool SupportsNPOT(bool dxt) const override;
+
+ void Project(float &x, float &y, float &z) override;
+
+ std::string GetShaderPath(const std::string &filename) override;
+
+ void GetGLVersion(int& major, int& minor);
+ void GetGLSLVersion(int& major, int& minor);
+
+ void ResetGLErrors();
+
+ // shaders
+ void EnableShader(ShaderMethodGL method);
+ void DisableShader();
+ GLint ShaderGetPos();
+ GLint ShaderGetCol();
+ GLint ShaderGetCoord0();
+ GLint ShaderGetCoord1();
+ GLint ShaderGetUniCol();
+ GLint ShaderGetModel();
+
+protected:
+ virtual void SetVSyncImpl(bool enable) = 0;
+ virtual void PresentRenderImpl(bool rendered) = 0;
+ void CalculateMaxTexturesize();
+ void InitialiseShaders();
+ void ReleaseShaders();
+
+ bool m_bVsyncInit = false;
+ int m_width;
+ int m_height;
+
+ std::string m_RenderExtensions;
+
+ int m_glslMajor = 0;
+ int m_glslMinor = 0;
+
+ GLint m_viewPort[4];
+
+ std::map<ShaderMethodGL, std::unique_ptr<CGLShader>> m_pShader;
+ ShaderMethodGL m_method = ShaderMethodGL::SM_DEFAULT;
+ GLuint m_vertexArray = GL_NONE;
+};
diff --git a/xbmc/rendering/gl/ScreenshotSurfaceGL.cpp b/xbmc/rendering/gl/ScreenshotSurfaceGL.cpp
new file mode 100644
index 0000000..022611b
--- /dev/null
+++ b/xbmc/rendering/gl/ScreenshotSurfaceGL.cpp
@@ -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.
+ */
+
+#include "ScreenshotSurfaceGL.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 CScreenshotSurfaceGL::Register()
+{
+ CScreenShot::Register(CScreenshotSurfaceGL::CreateSurface);
+}
+
+std::unique_ptr<IScreenshotSurface> CScreenshotSurfaceGL::CreateSurface()
+{
+ return std::unique_ptr<CScreenshotSurfaceGL>(new CScreenshotSurfaceGL());
+}
+
+bool CScreenshotSurfaceGL::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();
+
+ glReadBuffer(GL_BACK);
+
+ // 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_BGRA, 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++)
+ 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/gl/ScreenshotSurfaceGL.h b/xbmc/rendering/gl/ScreenshotSurfaceGL.h
new file mode 100644
index 0000000..01f0590
--- /dev/null
+++ b/xbmc/rendering/gl/ScreenshotSurfaceGL.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 CScreenshotSurfaceGL : public IScreenshotSurface
+{
+public:
+ static void Register();
+ static std::unique_ptr<IScreenshotSurface> CreateSurface();
+
+ bool Capture() override;
+};