diff options
Diffstat (limited to '')
-rw-r--r-- | xbmc/rendering/gl/CMakeLists.txt | 12 | ||||
-rw-r--r-- | xbmc/rendering/gl/GLShader.cpp | 162 | ||||
-rw-r--r-- | xbmc/rendering/gl/GLShader.h | 55 | ||||
-rw-r--r-- | xbmc/rendering/gl/RenderSystemGL.cpp | 795 | ||||
-rw-r--r-- | xbmc/rendering/gl/RenderSystemGL.h | 139 | ||||
-rw-r--r-- | xbmc/rendering/gl/ScreenshotSurfaceGL.cpp | 65 | ||||
-rw-r--r-- | xbmc/rendering/gl/ScreenshotSurfaceGL.h | 22 | ||||
-rw-r--r-- | xbmc/rendering/gles/CMakeLists.txt | 11 | ||||
-rw-r--r-- | xbmc/rendering/gles/GLESShader.cpp | 178 | ||||
-rw-r--r-- | xbmc/rendering/gles/GLESShader.h | 65 | ||||
-rw-r--r-- | xbmc/rendering/gles/RenderSystemGLES.cpp | 669 | ||||
-rw-r--r-- | xbmc/rendering/gles/RenderSystemGLES.h | 143 | ||||
-rw-r--r-- | xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp | 72 | ||||
-rw-r--r-- | xbmc/rendering/gles/ScreenshotSurfaceGLES.h | 22 |
14 files changed, 2410 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; +}; 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; +}; |