From be933ef2241d79558f91796cc5b3a161f72ebf9c Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 19 Oct 2020 00:52:24 +0200 Subject: sync with upstream --- .../include/kodi/gui/gl/CMakeLists.txt | 7 + xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h | 112 ++++ .../kodi-dev-kit/include/kodi/gui/gl/GLonDX.h | 389 ++++++++++++++ .../kodi-dev-kit/include/kodi/gui/gl/Shader.h | 571 +++++++++++++++++++++ 4 files changed, 1079 insertions(+) create mode 100644 xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h create mode 100644 xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GLonDX.h create mode 100644 xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/Shader.h (limited to 'xbmc/addons/kodi-dev-kit/include/kodi/gui/gl') diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/CMakeLists.txt new file mode 100644 index 0000000..844902d --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/CMakeLists.txt @@ -0,0 +1,7 @@ +set(HEADERS GL.h + GLonDX.h + Shader.h) + +if(NOT ENABLE_STATIC_LIBS) + core_add_library(addons_kodi-dev-kit_include_kodi_gui_gl) +endif() diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h new file mode 100644 index 0000000..16d43e3 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GL.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2005-2019 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 + +#ifdef __cplusplus + +//============================================================================== +/// @defgroup cpp_kodi_gui_helpers_gl OpenGL helpers +/// @ingroup cpp_kodi_gui_helpers +/// @brief **Auxiliary functions for Open GL**\n +/// This group includes help for definitions, functions, and classes for +/// OpenGL. +/// +/// To use OpenGL for your system, add the @ref GL.h "#include ". +/// +/// The @ref HAS_GL is declared if Open GL is required and @ref HAS_GLES if Open GL +/// Embedded Systems (ES) is required, with ES the version is additionally given +/// in the definition, this can be "2" or "3". +/// +/// +///----------------------------------------------------------------------------- +/// +/// Following @ref GL_TYPE_STRING define can be used, for example, to manage +/// different folders for GL and GLES and make the selection easier. +/// This are on OpenGL "GL" and on Open GL|ES "GLES". +/// +/// **Example:** +/// ~~~~~~~~~~~~~~~~~{.cpp} +/// kodi::GetAddonPath("resources/shaders/" GL_TYPE_STRING "/frag.glsl"); +/// ~~~~~~~~~~~~~~~~~ +/// +/// +///---------------------------------------------------------------------------- +/// +/// In addition, @ref BUFFER_OFFSET is declared in it which can be used to give an +/// offset on the array to GL. +/// +/// **Example:** +/// ~~~~~~~~~~~~~~~~~{.cpp} +/// const struct PackedVertex { +/// float position[3]; // Position x, y, z +/// float color[4]; // Color r, g, b, a +/// } vertices[3] = { +/// { { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, +/// { { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, +/// { { 0.0f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } } +/// }; +/// +/// glVertexAttribPointer(m_aPosition, 3, GL_FLOAT, GL_FALSE, sizeof(PackedVertex), BUFFER_OFFSET(offsetof(PackedVertex, position))); +/// glEnableVertexAttribArray(m_aPosition); +/// +/// glVertexAttribPointer(m_aColor, 4, GL_FLOAT, GL_FALSE, sizeof(PackedVertex), BUFFER_OFFSET(offsetof(PackedVertex, color))); +/// glEnableVertexAttribArray(m_aColor); +/// ~~~~~~~~~~~~~~~~~ + +#if HAS_GL +#define GL_TYPE_STRING "GL" +// always define GL_GLEXT_PROTOTYPES before include gl headers +#if !defined(GL_GLEXT_PROTOTYPES) +#define GL_GLEXT_PROTOTYPES +#endif +#if defined(TARGET_LINUX) +#include +#include +#elif defined(TARGET_FREEBSD) +#include +#elif defined(TARGET_DARWIN) +#include +#include +#elif defined(WIN32) +#error Use of GL under Windows is not possible +#endif +#elif HAS_GLES >= 2 +#define GL_TYPE_STRING "GLES" +#if defined(WIN32) +#if defined(HAS_ANGLE) +#include +#else +#error Use of GLES only be available under Windows by the use of angle +#endif +#elif defined(TARGET_DARWIN) +#if HAS_GLES == 3 +#include +#include +#else +#include +#include +#endif +#else +#if HAS_GLES == 3 +#include +#include +#else +#include +#include +#endif +#endif +#endif + +#ifndef BUFFER_OFFSET +/// @ingroup cpp_kodi_gui_helpers_gl +/// @brief To give a offset number as pointer value. +#define BUFFER_OFFSET(i) ((char*)nullptr + (i)) +#endif + +#endif /* __cplusplus */ diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GLonDX.h b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GLonDX.h new file mode 100644 index 0000000..4dd97af --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/GLonDX.h @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2005-2019 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 + +#ifdef __cplusplus + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "d3dcompiler.lib") +#ifndef GL_CLIENT_VERSION +#define GL_CLIENT_VERSION 3 +#endif + +namespace kodi +{ +namespace gui +{ +namespace gl +{ + +class ATTRIBUTE_HIDDEN CGLonDX : public kodi::gui::IRenderHelper +{ +public: + explicit CGLonDX() : m_pContext(reinterpret_cast(kodi::gui::GetHWContext())) + { + } + ~CGLonDX() override { destruct(); } + + bool Init() override + { + EGLint egl_display_attrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, + EGL_DONT_CARE, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, + EGL_DONT_CARE, + EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, + EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE, + EGL_NONE}; + EGLint egl_config_attrs[] = {EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, + EGL_BIND_TO_TEXTURE_RGBA, + EGL_TRUE, + EGL_RENDERABLE_TYPE, + GL_CLIENT_VERSION == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, + EGL_PBUFFER_BIT, + EGL_NONE}; + EGLint egl_context_attrs[] = {EGL_CONTEXT_CLIENT_VERSION, GL_CLIENT_VERSION, EGL_NONE}; + + m_eglDisplay = + eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, egl_display_attrs); + if (m_eglDisplay == EGL_NO_DISPLAY) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to get EGL display (%s)", eglGetErrorString()); + return false; + } + + if (eglInitialize(m_eglDisplay, nullptr, nullptr) != EGL_TRUE) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to init EGL display (%s)", eglGetErrorString()); + return false; + } + + EGLint numConfigs = 0; + if (eglChooseConfig(m_eglDisplay, egl_config_attrs, &m_eglConfig, 1, &numConfigs) != EGL_TRUE || + numConfigs == 0) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to get EGL config (%s)", eglGetErrorString()); + return false; + } + + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, nullptr, egl_context_attrs); + if (m_eglContext == EGL_NO_CONTEXT) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create EGL context (%s)", eglGetErrorString()); + return false; + } + + if (!createD3DResources()) + return false; + + if (eglMakeCurrent(m_eglDisplay, m_eglBuffer, m_eglBuffer, m_eglContext) != EGL_TRUE) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to make current EGL (%s)", eglGetErrorString()); + return false; + } + return true; + } + + void CheckGL(ID3D11DeviceContext* device) + { + if (m_pContext != device) + { + m_pSRView = nullptr; + m_pVShader = nullptr; + m_pPShader = nullptr; + m_pContext = device; + + if (m_eglBuffer != EGL_NO_SURFACE) + { + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(m_eglDisplay, m_eglBuffer); + m_eglBuffer = EGL_NO_SURFACE; + } + + // create new resources + if (!createD3DResources()) + return; + + eglMakeCurrent(m_eglDisplay, m_eglBuffer, m_eglBuffer, m_eglContext); + } + } + + void Begin() override + { + // confirm on begin D3D context is correct + CheckGL(reinterpret_cast(kodi::gui::GetHWContext())); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + + void End() override + { + glFlush(); + + // set our primitive shaders + m_pContext->VSSetShader(m_pVShader.Get(), nullptr, 0); + m_pContext->PSSetShader(m_pPShader.Get(), nullptr, 0); + m_pContext->PSSetShaderResources(0, 1, m_pSRView.GetAddressOf()); + // draw texture + m_pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + m_pContext->IASetVertexBuffers(0, 0, nullptr, nullptr, nullptr); + m_pContext->IASetInputLayout(nullptr); + m_pContext->Draw(4, 0); + // unset shaders + m_pContext->PSSetShader(nullptr, nullptr, 0); + m_pContext->VSSetShader(nullptr, nullptr, 0); + // unbind our view + ID3D11ShaderResourceView* views[1] = {}; + m_pContext->PSSetShaderResources(0, 1, views); + } + +private: + enum ShaderType + { + VERTEX_SHADER, + PIXEL_SHADER + }; + + bool createD3DResources() + { + HANDLE sharedHandle; + Microsoft::WRL::ComPtr pDevice; + Microsoft::WRL::ComPtr pRTView; + Microsoft::WRL::ComPtr pRTResource; + Microsoft::WRL::ComPtr pRTTexture; + Microsoft::WRL::ComPtr pOffScreenTexture; + Microsoft::WRL::ComPtr dxgiResource; + + m_pContext->GetDevice(&pDevice); + m_pContext->OMGetRenderTargets(1, &pRTView, nullptr); + if (!pRTView) + return false; + + pRTView->GetResource(&pRTResource); + if (FAILED(pRTResource.As(&pRTTexture))) + return false; + + D3D11_TEXTURE2D_DESC texDesc; + pRTTexture->GetDesc(&texDesc); + texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + if (FAILED(pDevice->CreateTexture2D(&texDesc, nullptr, &pOffScreenTexture))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create intermediate texture"); + return false; + } + + CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(pOffScreenTexture.Get(), + D3D11_SRV_DIMENSION_TEXTURE2D); + if (FAILED(pDevice->CreateShaderResourceView(pOffScreenTexture.Get(), &srvDesc, &m_pSRView))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create shader view"); + return false; + } + + if (FAILED(pOffScreenTexture.As(&dxgiResource)) || + FAILED(dxgiResource->GetSharedHandle(&sharedHandle))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable get shared handle for texture"); + return false; + } + + // initiate simple shaders + if (FAILED(d3dCreateShader(VERTEX_SHADER, vs_out_shader_text, &m_pVShader))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create vertex shader view"); + return false; + } + + if (FAILED(d3dCreateShader(PIXEL_SHADER, ps_out_shader_text, &m_pPShader))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create pixel shader view"); + return false; + } + + // create EGL buffer from D3D shared texture + EGLint egl_buffer_attrs[] = {EGL_WIDTH, + static_cast(texDesc.Width), + EGL_HEIGHT, + static_cast(texDesc.Height), + EGL_TEXTURE_TARGET, + EGL_TEXTURE_2D, + EGL_TEXTURE_FORMAT, + EGL_TEXTURE_RGBA, + EGL_NONE}; + + m_eglBuffer = + eglCreatePbufferFromClientBuffer(m_eglDisplay, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, + sharedHandle, m_eglConfig, egl_buffer_attrs); + + if (m_eglBuffer == EGL_NO_SURFACE) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create EGL buffer (%s)", eglGetErrorString()); + return false; + } + return true; + } + + HRESULT d3dCreateShader(ShaderType shaderType, + const std::string& source, + IUnknown** ppShader) const + { + Microsoft::WRL::ComPtr pBlob; + Microsoft::WRL::ComPtr pErrors; + + auto hr = D3DCompile(source.c_str(), source.length(), nullptr, nullptr, nullptr, "main", + shaderType == PIXEL_SHADER ? "ps_4_0" : "vs_4_0", 0, 0, &pBlob, &pErrors); + + if (SUCCEEDED(hr)) + { + Microsoft::WRL::ComPtr pDevice; + m_pContext->GetDevice(&pDevice); + + if (shaderType == PIXEL_SHADER) + { + hr = pDevice->CreatePixelShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), nullptr, + reinterpret_cast(ppShader)); + } + else + { + hr = pDevice->CreateVertexShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), nullptr, + reinterpret_cast(ppShader)); + } + + if (FAILED(hr)) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create %s shader", + shaderType == PIXEL_SHADER ? "pixel" : "vertex"); + } + } + else + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to compile shader (%s)", pErrors->GetBufferPointer()); + } + return hr; + } + + static const char* eglGetErrorString() + { +#define CASE_STR(value) \ + case value: \ + return #value + switch (eglGetError()) + { + CASE_STR(EGL_SUCCESS); + CASE_STR(EGL_NOT_INITIALIZED); + CASE_STR(EGL_BAD_ACCESS); + CASE_STR(EGL_BAD_ALLOC); + CASE_STR(EGL_BAD_ATTRIBUTE); + CASE_STR(EGL_BAD_CONTEXT); + CASE_STR(EGL_BAD_CONFIG); + CASE_STR(EGL_BAD_CURRENT_SURFACE); + CASE_STR(EGL_BAD_DISPLAY); + CASE_STR(EGL_BAD_SURFACE); + CASE_STR(EGL_BAD_MATCH); + CASE_STR(EGL_BAD_PARAMETER); + CASE_STR(EGL_BAD_NATIVE_PIXMAP); + CASE_STR(EGL_BAD_NATIVE_WINDOW); + CASE_STR(EGL_CONTEXT_LOST); + default: + return "Unknown"; + } +#undef CASE_STR + } + + void destruct() + { + if (m_eglDisplay != EGL_NO_DISPLAY) + { + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (m_eglBuffer != EGL_NO_SURFACE) + { + eglDestroySurface(m_eglDisplay, m_eglBuffer); + m_eglBuffer = EGL_NO_SURFACE; + } + + if (m_eglContext != EGL_NO_CONTEXT) + { + eglDestroyContext(m_eglDisplay, m_eglContext); + m_eglContext = EGL_NO_CONTEXT; + } + + eglTerminate(m_eglDisplay); + m_eglDisplay = EGL_NO_DISPLAY; + } + + m_pSRView = nullptr; + m_pVShader = nullptr; + m_pPShader = nullptr; + m_pContext = nullptr; + } + + EGLConfig m_eglConfig = EGL_NO_CONFIG_KHR; + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + EGLContext m_eglContext = EGL_NO_CONTEXT; + EGLSurface m_eglBuffer = EGL_NO_SURFACE; + + ID3D11DeviceContext* m_pContext = nullptr; // don't hold context + Microsoft::WRL::ComPtr m_pSRView = nullptr; + Microsoft::WRL::ComPtr m_pVShader = nullptr; + Microsoft::WRL::ComPtr m_pPShader = nullptr; + +#define TO_STRING(...) #__VA_ARGS__ + std::string vs_out_shader_text = TO_STRING(void main(uint id + : SV_VertexId, out float2 tex + : TEXCOORD0, out float4 pos + : SV_POSITION) { + tex = float2(id % 2, (id % 4) >> 1); + pos = float4((tex.x - 0.5f) * 2, -(tex.y - 0.5f) * 2, 0, 1); + }); + + std::string ps_out_shader_text = TO_STRING( + Texture2D texMain : register(t0); + SamplerState Sampler + { + Filter = MIN_MAG_MIP_LINEAR; + AddressU = CLAMP; + AddressV = CLAMP; + Comparison = NEVER; + }; + + float4 main(in float2 tex : TEXCOORD0) : SV_TARGET + { + return texMain.Sample(Sampler, tex); + }); +#undef TO_STRING +}; /* class CGLonDX */ + +} /* namespace gl */ + +using CRenderHelper = gl::CGLonDX; +} /* namespace gui */ +} /* namespace kodi */ + +#endif /* __cplusplus */ diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/Shader.h b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/Shader.h new file mode 100644 index 0000000..bf6d48c --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/include/kodi/gui/gl/Shader.h @@ -0,0 +1,571 @@ +/* + * Copyright (C) 2005-2019 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 "GL.h" + +#ifdef __cplusplus + +#include +#include +#include + +#include +#include + +#define LOG_SIZE 1024 +#define GLchar char + +namespace kodi +{ +namespace gui +{ +namespace gl +{ + +//======================================================================== +/// CShader - base class +class ATTRIBUTE_HIDDEN CShader +{ +public: + CShader() = default; + virtual ~CShader() = default; + virtual bool Compile(const std::string& extraBegin = "", const std::string& extraEnd = "") = 0; + virtual void Free() = 0; + virtual GLuint Handle() = 0; + + bool LoadSource(const std::string& file) + { + char buffer[16384]; + + kodi::vfs::CFile source; + if (!source.OpenFile(file)) + { + kodi::Log(ADDON_LOG_ERROR, "CShader::%s: Failed to open file '%s'", __FUNCTION__, + file.c_str()); + return false; + } + size_t len = source.Read(buffer, sizeof(buffer)); + m_source.assign(buffer); + m_source[len] = 0; + source.Close(); + return true; + } + + bool OK() const { return m_compiled; } + +protected: + std::string m_source; + std::string m_lastLog; + bool m_compiled = false; +}; +//------------------------------------------------------------------------ + +//======================================================================== +/// CVertexShader +class ATTRIBUTE_HIDDEN CVertexShader : public CShader +{ +public: + CVertexShader() = default; + ~CVertexShader() override { Free(); } + + void Free() override + { + if (m_vertexShader) + glDeleteShader(m_vertexShader); + m_vertexShader = 0; + } + + bool Compile(const std::string& extraBegin = "", const std::string& extraEnd = "") override + { + GLint params[4]; + + Free(); + + m_vertexShader = glCreateShader(GL_VERTEX_SHADER); + + GLsizei count = 0; + const char* sources[3]; + if (!extraBegin.empty()) + sources[count++] = extraBegin.c_str(); + if (!m_source.empty()) + sources[count++] = m_source.c_str(); + if (!extraEnd.empty()) + sources[count++] = extraEnd.c_str(); + + glShaderSource(m_vertexShader, count, sources, nullptr); + glCompileShader(m_vertexShader); + glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, params); + if (params[0] != GL_TRUE) + { + GLchar log[LOG_SIZE]; + glGetShaderInfoLog(m_vertexShader, LOG_SIZE, nullptr, log); + kodi::Log(ADDON_LOG_ERROR, "CVertexShader::%s: %s", __FUNCTION__, log); + fprintf(stderr, "CVertexShader::%s: %s\n", __FUNCTION__, log); + m_lastLog = log; + m_compiled = false; + } + else + { + GLchar log[LOG_SIZE]; + glGetShaderInfoLog(m_vertexShader, LOG_SIZE, nullptr, log); + m_lastLog = log; + m_compiled = true; + } + return m_compiled; + } + + GLuint Handle() override { return m_vertexShader; } + +protected: + GLuint m_vertexShader = 0; +}; +//------------------------------------------------------------------------ + +//======================================================================== +/// CPixelShader +class ATTRIBUTE_HIDDEN CPixelShader : public CShader +{ +public: + CPixelShader() = default; + ~CPixelShader() { Free(); } + void Free() override + { + if (m_pixelShader) + glDeleteShader(m_pixelShader); + m_pixelShader = 0; + } + + bool Compile(const std::string& extraBegin = "", const std::string& extraEnd = "") override + { + GLint params[4]; + + Free(); + + m_pixelShader = glCreateShader(GL_FRAGMENT_SHADER); + + GLsizei count = 0; + const char* sources[3]; + if (!extraBegin.empty()) + sources[count++] = extraBegin.c_str(); + if (!m_source.empty()) + sources[count++] = m_source.c_str(); + if (!extraEnd.empty()) + sources[count++] = extraEnd.c_str(); + + glShaderSource(m_pixelShader, count, sources, 0); + glCompileShader(m_pixelShader); + glGetShaderiv(m_pixelShader, GL_COMPILE_STATUS, params); + if (params[0] != GL_TRUE) + { + GLchar log[LOG_SIZE]; + glGetShaderInfoLog(m_pixelShader, LOG_SIZE, nullptr, log); + kodi::Log(ADDON_LOG_ERROR, "CPixelShader::%s: %s", __FUNCTION__, log); + fprintf(stderr, "CPixelShader::%s: %s\n", __FUNCTION__, log); + m_lastLog = log; + m_compiled = false; + } + else + { + GLchar log[LOG_SIZE]; + glGetShaderInfoLog(m_pixelShader, LOG_SIZE, nullptr, log); + m_lastLog = log; + m_compiled = true; + } + return m_compiled; + } + + GLuint Handle() override { return m_pixelShader; } + +protected: + GLuint m_pixelShader = 0; +}; +//------------------------------------------------------------------------ + +//============================================================================ +/// @defgroup cpp_kodi_gui_helpers_gl_CShaderProgram GL Shader Program +/// @ingroup cpp_kodi_gui_helpers_gl +/// @brief @cpp_class{ kodi::gui::gl::CShaderProgram } +/// **Class to manage an OpenGL shader program**\n +/// With this class the used GL shader code can be defined on the GPU and +/// its variables can be managed between CPU and GPU. +/// +/// It has the header @ref Shader.h "#include " +/// be included to enjoy it. +/// +/// ---------------------------------------------------------------------------- +/// +/// Example: +/// +/// ~~~~~~~~~~~~~{.cpp} +/// +/// #include +/// ... +/// +/// class ATTRIBUTE_HIDDEN CExample +/// : ..., +/// public kodi::gui::gl::CShaderProgram +/// { +/// public: +/// CExample() = default; +/// +/// bool Start(); +/// void Render(); +/// +/// // override functions for kodi::gui::gl::CShaderProgram +/// void OnCompiledAndLinked() override; +/// bool OnEnabled() override; +/// +/// private: +/// ... +/// GLint m_aPosition = -1; +/// GLint m_aColor = -1; +/// }; +/// +/// bool CExample::Start() +/// { +/// // Define shaders and load +/// std::string fraqShader = kodi::GetAddonPath("resources/shaders/" GL_TYPE_STRING "/glsl.frag"); +/// std::string vertShader = kodi::GetAddonPath("resources/shaders/" GL_TYPE_STRING "/glsl.vert"); +/// if (!LoadShaderFiles(vertShader, fraqShader) || !CompileAndLink()) +/// return false; +/// +/// ... +/// return true; +/// } +/// +/// ... +/// +/// void CExample::Render() +/// { +/// ... +/// +/// EnableShader(); +/// ... +/// DO WORK +/// ... +/// DisableShader(); +/// } +/// +/// void CExample::OnCompiledAndLinked() +/// { +/// ... +/// DO YOUR WORK HERE FOR WHAT IS ONE TIME REQUIRED DURING COMPILE OF SHADER, E.G.: +/// +/// m_aPosition = glGetAttribLocation(ProgramHandle(), "a_position"); +/// m_aColor = glGetAttribLocation(ProgramHandle(), "a_color"); +/// } +/// +/// bool OnEnabled() override +/// { +/// ... +/// DO YOUR WORK HERE FOR WHAT REQUIRED DURING ENABLE OF SHADER +/// ... +/// return true; +/// } +/// +/// ADDONCREATOR(CExample); +/// ~~~~~~~~~~~~~ +/// +class ATTRIBUTE_HIDDEN CShaderProgram +{ +public: + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief Construct a new shader. + /// + /// Load must be done later with @ref LoadShaderFiles. + /// + CShaderProgram() = default; + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief Construct a new shader and load defined shader files. + /// + /// @param[in] vert Path to used GL vertext shader + /// @param[in] frag Path to used GL fragment shader + /// + CShaderProgram(const std::string& vert, const std::string& frag) { LoadShaderFiles(vert, frag); } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief Destructor. + /// + virtual ~CShaderProgram() { ShaderFree(); } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief To load manually the needed shader files. + /// + /// @param[in] vert Path to used GL vertext shader + /// @param[in] frag Path to used GL fragment shader + /// + /// + /// @note The use of the files is optional, but it must either be passed over + /// here or via @ref CompileAndLink, or both of the source code. + /// + bool LoadShaderFiles(const std::string& vert, const std::string& frag) + { + if (!kodi::vfs::FileExists(vert) || !m_pVP.LoadSource(vert)) + { + kodi::Log(ADDON_LOG_ERROR, "%s: Failed to load '%s'", __func__, vert.c_str()); + return false; + } + + if (!kodi::vfs::FileExists(frag) || !m_pFP.LoadSource(frag)) + { + kodi::Log(ADDON_LOG_ERROR, "%s: Failed to load '%s'", __func__, frag.c_str()); + return false; + } + + return true; + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief To compile and link the shader to the GL interface. + /// + /// Optionally, additional source code can be transferred here, or it can be + /// used independently without any files + /// + /// @param[in] vertexExtraBegin [opt] To additionally add vextex source + /// code to the beginning of the loaded file + /// source code + /// @param[in] vertexExtraEnd [opt] To additionally add vextex source + /// code to the end of the loaded file + /// source code + /// @param[in] fragmentExtraBegin [opt] To additionally add fragment source + /// code to the beginning of the loaded file + /// source code + /// @param[in] fragmentExtraEnd [opt] To additionally add fragment source + /// code to the end of the loaded file + /// source code + /// @return true if compile was successed + /// + /// + /// @note In the case of a compile error, it will be written once into the Kodi + /// log and in addition to the console output to quickly detect the errors when + /// writing the damage. + /// + /// + bool CompileAndLink(const std::string& vertexExtraBegin = "", + const std::string& vertexExtraEnd = "", + const std::string& fragmentExtraBegin = "", + const std::string& fragmentExtraEnd = "") + { + GLint params[4]; + + // free resources + ShaderFree(); + m_ok = false; + + // compiled vertex shader + if (!m_pVP.Compile(vertexExtraBegin, vertexExtraEnd)) + { + kodi::Log(ADDON_LOG_ERROR, "GL: Error compiling vertex shader"); + return false; + } + + // compile pixel shader + if (!m_pFP.Compile(fragmentExtraBegin, fragmentExtraEnd)) + { + m_pVP.Free(); + kodi::Log(ADDON_LOG_ERROR, "GL: Error compiling fragment shader"); + return false; + } + + // create program object + m_shaderProgram = glCreateProgram(); + if (!m_shaderProgram) + { + kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: Failed to create GL program", __FUNCTION__); + ShaderFree(); + return false; + } + + // attach the vertex shader + glAttachShader(m_shaderProgram, m_pVP.Handle()); + glAttachShader(m_shaderProgram, m_pFP.Handle()); + + // link the program + glLinkProgram(m_shaderProgram); + glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, params); + if (params[0] != GL_TRUE) + { + GLchar log[LOG_SIZE]; + glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, nullptr, log); + kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: %s", __FUNCTION__, log); + fprintf(stderr, "CShaderProgram::%s: %s@n", __FUNCTION__, log); + ShaderFree(); + return false; + } + + m_validated = false; + m_ok = true; + OnCompiledAndLinked(); + return true; + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief To activate the shader and use it on the GPU. + /// + /// @return true if enable was successfull done + /// + /// + /// @note During this call, the @ref OnEnabled stored in the child is also + /// called + /// + bool EnableShader() + { + if (ShaderOK()) + { + glUseProgram(m_shaderProgram); + if (OnEnabled()) + { + if (!m_validated) + { + // validate the program + GLint params[4]; + glValidateProgram(m_shaderProgram); + glGetProgramiv(m_shaderProgram, GL_VALIDATE_STATUS, params); + if (params[0] != GL_TRUE) + { + GLchar log[LOG_SIZE]; + glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, nullptr, log); + kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: %s", __FUNCTION__, log); + fprintf(stderr, "CShaderProgram::%s: %s\n", __FUNCTION__, log); + } + m_validated = true; + } + return true; + } + else + { + glUseProgram(0); + return false; + } + return true; + } + return false; + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief To deactivate the shader use on the GPU. + /// + void DisableShader() + { + if (ShaderOK()) + { + glUseProgram(0); + OnDisabled(); + } + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief Used to check if shader has been loaded before. + /// + /// @return true if enable was successfull done + /// + /// @note The CompileAndLink call sets these values + /// + ATTRIBUTE_FORCEINLINE bool ShaderOK() const { return m_ok; } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief To get the vertex shader class used by Kodi at the addon. + /// + /// @return pointer to vertex shader class + /// + ATTRIBUTE_FORCEINLINE CVertexShader& VertexShader() { return m_pVP; } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief To get the fragment shader class used by Kodi at the addon. + /// + /// @return pointer to fragment shader class + /// + ATTRIBUTE_FORCEINLINE CPixelShader& PixelShader() { return m_pFP; } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief Used to get the definition created in the OpenGL itself. + /// + /// @return GLuint of GL shader program handler + /// + ATTRIBUTE_FORCEINLINE GLuint ProgramHandle() { return m_shaderProgram; } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @defgroup cpp_kodi_gui_helpers_gl_CShaderProgram_child Child Functions + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram + /// @brief @cpp_class{ kodi::gui::gl::CShaderProgram child functions } + /// + /// Functions that are added by parent in the child + /// @{ + //========================================================================== + /// + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram_child + /// @brief Mandatory child function to set the necessary CPU to GPU data + /// + virtual void OnCompiledAndLinked(){}; + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram_child + /// @brief Optional function to exchange data between CPU and GPU while + /// activating the shader + /// + /// @return true if enable was successfull done + /// + virtual bool OnEnabled() { return true; }; + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_gui_helpers_gl_CShaderProgram_child + /// @brief Optional child function that may have to be performed when + /// switching off the shader + virtual void OnDisabled(){}; + //-------------------------------------------------------------------------- + /// @} + +private: + void ShaderFree() + { + if (m_shaderProgram) + glDeleteProgram(m_shaderProgram); + m_shaderProgram = 0; + m_ok = false; + } + + CVertexShader m_pVP; + CPixelShader m_pFP; + GLuint m_shaderProgram = 0; + bool m_ok = false; + bool m_validated = false; +}; +//------------------------------------------------------------------------ + +} /* namespace gl */ +} /* namespace gui */ +} /* namespace kodi */ + +#endif /* __cplusplus */ -- cgit v1.2.3