summaryrefslogtreecommitdiffstats
path: root/xbmc/rendering
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/rendering')
-rw-r--r--xbmc/rendering/CMakeLists.txt27
-rw-r--r--xbmc/rendering/MatrixGL.cpp261
-rw-r--r--xbmc/rendering/MatrixGL.h85
-rw-r--r--xbmc/rendering/MatrixGL.neon.cpp47
-rw-r--r--xbmc/rendering/RenderSystem.cpp110
-rw-r--r--xbmc/rendering/RenderSystem.h98
-rw-r--r--xbmc/rendering/RenderSystemTypes.h35
-rw-r--r--xbmc/rendering/dx/CMakeLists.txt11
-rw-r--r--xbmc/rendering/dx/DeviceResources.cpp1361
-rw-r--r--xbmc/rendering/dx/DeviceResources.h187
-rw-r--r--xbmc/rendering/dx/DirectXHelper.h198
-rw-r--r--xbmc/rendering/dx/RenderContext.h31
-rw-r--r--xbmc/rendering/dx/RenderSystemDX.cpp706
-rw-r--r--xbmc/rendering/dx/RenderSystemDX.h106
-rw-r--r--xbmc/rendering/dx/ScreenshotSurfaceWindows.cpp110
-rw-r--r--xbmc/rendering/dx/ScreenshotSurfaceWindows.h22
-rw-r--r--xbmc/rendering/gl/CMakeLists.txt12
-rw-r--r--xbmc/rendering/gl/GLShader.cpp162
-rw-r--r--xbmc/rendering/gl/GLShader.h55
-rw-r--r--xbmc/rendering/gl/RenderSystemGL.cpp795
-rw-r--r--xbmc/rendering/gl/RenderSystemGL.h139
-rw-r--r--xbmc/rendering/gl/ScreenshotSurfaceGL.cpp65
-rw-r--r--xbmc/rendering/gl/ScreenshotSurfaceGL.h22
-rw-r--r--xbmc/rendering/gles/CMakeLists.txt11
-rw-r--r--xbmc/rendering/gles/GLESShader.cpp178
-rw-r--r--xbmc/rendering/gles/GLESShader.h65
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.cpp669
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.h143
-rw-r--r--xbmc/rendering/gles/ScreenshotSurfaceGLES.cpp72
-rw-r--r--xbmc/rendering/gles/ScreenshotSurfaceGLES.h22
30 files changed, 5805 insertions, 0 deletions
diff --git a/xbmc/rendering/CMakeLists.txt b/xbmc/rendering/CMakeLists.txt
new file mode 100644
index 0000000..c212a96
--- /dev/null
+++ b/xbmc/rendering/CMakeLists.txt
@@ -0,0 +1,27 @@
+set(SOURCES RenderSystem.cpp)
+
+set(HEADERS RenderSystem.h
+ RenderSystemTypes.h)
+
+if(OPENGL_FOUND OR OPENGLES_FOUND)
+ list(APPEND SOURCES MatrixGL.cpp)
+ list(APPEND HEADERS MatrixGL.h)
+
+ if(ARCH MATCHES arm AND ENABLE_NEON)
+ list(APPEND SOURCES MatrixGL.neon.cpp)
+ if(NOT DEFINED NEON_FLAGS)
+ set_source_files_properties(MatrixGL.neon.cpp PROPERTIES COMPILE_OPTIONS -mfpu=neon)
+ endif()
+ endif()
+endif()
+
+core_add_library(rendering)
+if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore)
+ if(HAVE_SSE)
+ target_compile_options(${CORE_LIBRARY} PRIVATE -msse)
+ endif()
+ if(HAVE_SSE2)
+ target_compile_options(${CORE_LIBRARY} PRIVATE -msse2)
+ endif()
+endif()
+
diff --git a/xbmc/rendering/MatrixGL.cpp b/xbmc/rendering/MatrixGL.cpp
new file mode 100644
index 0000000..6e90383
--- /dev/null
+++ b/xbmc/rendering/MatrixGL.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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 "MatrixGL.h"
+
+#include "ServiceBroker.h"
+#include "utils/TransformMatrix.h"
+
+#if defined(HAS_NEON) && !defined(__LP64__)
+#include "utils/CPUInfo.h"
+void Matrix4Mul(float* src_mat_1, const float* src_mat_2);
+#endif
+
+#include <cmath>
+
+CMatrixGLStack glMatrixModview = CMatrixGLStack();
+CMatrixGLStack glMatrixProject = CMatrixGLStack();
+CMatrixGLStack glMatrixTexture = CMatrixGLStack();
+
+CMatrixGL::CMatrixGL(const TransformMatrix &src) noexcept
+{
+ for(int i = 0; i < 3; i++)
+ for(int j = 0; j < 4; j++)
+ m_pMatrix[j * 4 + i] = src.m[i][j];
+
+ m_pMatrix[3] = 0.0f;
+ m_pMatrix[7] = 0.0f;
+ m_pMatrix[11] = 0.0f;
+ m_pMatrix[15] = 1.0f;
+}
+
+void CMatrixGL::LoadIdentity()
+{
+ m_pMatrix[0] = 1.0f; m_pMatrix[4] = 0.0f; m_pMatrix[8] = 0.0f; m_pMatrix[12] = 0.0f;
+ m_pMatrix[1] = 0.0f; m_pMatrix[5] = 1.0f; m_pMatrix[9] = 0.0f; m_pMatrix[13] = 0.0f;
+ m_pMatrix[2] = 0.0f; m_pMatrix[6] = 0.0f; m_pMatrix[10] = 1.0f; m_pMatrix[14] = 0.0f;
+ m_pMatrix[3] = 0.0f; m_pMatrix[7] = 0.0f; m_pMatrix[11] = 0.0f; m_pMatrix[15] = 1.0f;
+}
+
+void CMatrixGL::Ortho(GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+{
+ GLfloat u = 2.0f / (r - l);
+ GLfloat v = 2.0f / (t - b);
+ GLfloat w = -2.0f / (f - n);
+ GLfloat x = - (r + l) / (r - l);
+ GLfloat y = - (t + b) / (t - b);
+ GLfloat z = - (f + n) / (f - n);
+ const CMatrixGL matrix{ u, 0.0f, 0.0f, 0.0f,
+ 0.0f, v, 0.0f, 0.0f,
+ 0.0f, 0.0f, w, 0.0f,
+ x, y, z, 1.0f};
+ MultMatrixf(matrix);
+}
+
+void CMatrixGL::Ortho2D(GLfloat l, GLfloat r, GLfloat b, GLfloat t)
+{
+ GLfloat u = 2.0f / (r - l);
+ GLfloat v = 2.0f / (t - b);
+ GLfloat x = - (r + l) / (r - l);
+ GLfloat y = - (t + b) / (t - b);
+ const CMatrixGL matrix{ u, 0.0f, 0.0f, 0.0f,
+ 0.0f, v, 0.0f, 0.0f,
+ 0.0f, 0.0f,-1.0f, 0.0f,
+ x, y, 0.0f, 1.0f};
+ MultMatrixf(matrix);
+}
+
+void CMatrixGL::Frustum(GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+{
+ GLfloat u = (2.0f * n) / (r - l);
+ GLfloat v = (2.0f * n) / (t - b);
+ GLfloat w = (r + l) / (r - l);
+ GLfloat x = (t + b) / (t - b);
+ GLfloat y = - (f + n) / (f - n);
+ GLfloat z = - (2.0f * f * n) / (f - n);
+ const CMatrixGL matrix{ u, 0.0f, 0.0f, 0.0f,
+ 0.0f, v, 0.0f, 0.0f,
+ w, x, y,-1.0f,
+ 0.0f, 0.0f, z, 0.0f};
+ MultMatrixf(matrix);
+}
+
+void CMatrixGL::Translatef(GLfloat x, GLfloat y, GLfloat z)
+{
+ const CMatrixGL matrix{1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ x, y, z, 1.0f};
+ MultMatrixf(matrix);
+}
+
+void CMatrixGL::Scalef(GLfloat x, GLfloat y, GLfloat z)
+{
+ const CMatrixGL matrix{ x, 0.0f, 0.0f, 0.0f,
+ 0.0f, y, 0.0f, 0.0f,
+ 0.0f, 0.0f, z, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f};
+ MultMatrixf(matrix);
+}
+
+void CMatrixGL::Rotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+{
+ GLfloat modulus = std::sqrt((x*x)+(y*y)+(z*z));
+ if (modulus != 0.0f)
+ {
+ x /= modulus;
+ y /= modulus;
+ z /= modulus;
+ }
+ GLfloat cosine = std::cos(angle);
+ GLfloat sine = std::sin(angle);
+ GLfloat cos1 = 1 - cosine;
+ GLfloat a = (x*x*cos1) + cosine;
+ GLfloat b = (x*y*cos1) - (z*sine);
+ GLfloat c = (x*z*cos1) + (y*sine);
+ GLfloat d = (y*x*cos1) + (z*sine);
+ GLfloat e = (y*y*cos1) + cosine;
+ GLfloat f = (y*z*cos1) - (x*sine);
+ GLfloat g = (z*x*cos1) - (y*sine);
+ GLfloat h = (z*y*cos1) + (x*sine);
+ GLfloat i = (z*z*cos1) + cosine;
+ const CMatrixGL matrix{ a, d, g, 0.0f,
+ b, e, h, 0.0f,
+ c, f, i, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f};
+ MultMatrixf(matrix);
+}
+
+void CMatrixGL::MultMatrixf(const CMatrixGL &matrix) noexcept
+{
+#if defined(HAS_NEON) && !defined(__LP64__)
+ if ((CServiceBroker::GetCPUInfo()->GetCPUFeatures() & CPU_FEATURE_NEON) == CPU_FEATURE_NEON)
+ {
+ Matrix4Mul(m_pMatrix, matrix.m_pMatrix);
+ return;
+ }
+#endif
+ GLfloat a = (matrix.m_pMatrix[0] * m_pMatrix[0]) + (matrix.m_pMatrix[1] * m_pMatrix[4]) + (matrix.m_pMatrix[2] * m_pMatrix[8]) + (matrix.m_pMatrix[3] * m_pMatrix[12]);
+ GLfloat b = (matrix.m_pMatrix[0] * m_pMatrix[1]) + (matrix.m_pMatrix[1] * m_pMatrix[5]) + (matrix.m_pMatrix[2] * m_pMatrix[9]) + (matrix.m_pMatrix[3] * m_pMatrix[13]);
+ GLfloat c = (matrix.m_pMatrix[0] * m_pMatrix[2]) + (matrix.m_pMatrix[1] * m_pMatrix[6]) + (matrix.m_pMatrix[2] * m_pMatrix[10]) + (matrix.m_pMatrix[3] * m_pMatrix[14]);
+ GLfloat d = (matrix.m_pMatrix[0] * m_pMatrix[3]) + (matrix.m_pMatrix[1] * m_pMatrix[7]) + (matrix.m_pMatrix[2] * m_pMatrix[11]) + (matrix.m_pMatrix[3] * m_pMatrix[15]);
+ GLfloat e = (matrix.m_pMatrix[4] * m_pMatrix[0]) + (matrix.m_pMatrix[5] * m_pMatrix[4]) + (matrix.m_pMatrix[6] * m_pMatrix[8]) + (matrix.m_pMatrix[7] * m_pMatrix[12]);
+ GLfloat f = (matrix.m_pMatrix[4] * m_pMatrix[1]) + (matrix.m_pMatrix[5] * m_pMatrix[5]) + (matrix.m_pMatrix[6] * m_pMatrix[9]) + (matrix.m_pMatrix[7] * m_pMatrix[13]);
+ GLfloat g = (matrix.m_pMatrix[4] * m_pMatrix[2]) + (matrix.m_pMatrix[5] * m_pMatrix[6]) + (matrix.m_pMatrix[6] * m_pMatrix[10]) + (matrix.m_pMatrix[7] * m_pMatrix[14]);
+ GLfloat h = (matrix.m_pMatrix[4] * m_pMatrix[3]) + (matrix.m_pMatrix[5] * m_pMatrix[7]) + (matrix.m_pMatrix[6] * m_pMatrix[11]) + (matrix.m_pMatrix[7] * m_pMatrix[15]);
+ GLfloat i = (matrix.m_pMatrix[8] * m_pMatrix[0]) + (matrix.m_pMatrix[9] * m_pMatrix[4]) + (matrix.m_pMatrix[10] * m_pMatrix[8]) + (matrix.m_pMatrix[11] * m_pMatrix[12]);
+ GLfloat j = (matrix.m_pMatrix[8] * m_pMatrix[1]) + (matrix.m_pMatrix[9] * m_pMatrix[5]) + (matrix.m_pMatrix[10] * m_pMatrix[9]) + (matrix.m_pMatrix[11] * m_pMatrix[13]);
+ GLfloat k = (matrix.m_pMatrix[8] * m_pMatrix[2]) + (matrix.m_pMatrix[9] * m_pMatrix[6]) + (matrix.m_pMatrix[10] * m_pMatrix[10]) + (matrix.m_pMatrix[11] * m_pMatrix[14]);
+ GLfloat l = (matrix.m_pMatrix[8] * m_pMatrix[3]) + (matrix.m_pMatrix[9] * m_pMatrix[7]) + (matrix.m_pMatrix[10] * m_pMatrix[11]) + (matrix.m_pMatrix[11] * m_pMatrix[15]);
+ GLfloat m = (matrix.m_pMatrix[12] * m_pMatrix[0]) + (matrix.m_pMatrix[13] * m_pMatrix[4]) + (matrix.m_pMatrix[14] * m_pMatrix[8]) + (matrix.m_pMatrix[15] * m_pMatrix[12]);
+ GLfloat n = (matrix.m_pMatrix[12] * m_pMatrix[1]) + (matrix.m_pMatrix[13] * m_pMatrix[5]) + (matrix.m_pMatrix[14] * m_pMatrix[9]) + (matrix.m_pMatrix[15] * m_pMatrix[13]);
+ GLfloat o = (matrix.m_pMatrix[12] * m_pMatrix[2]) + (matrix.m_pMatrix[13] * m_pMatrix[6]) + (matrix.m_pMatrix[14] * m_pMatrix[10]) + (matrix.m_pMatrix[15] * m_pMatrix[14]);
+ GLfloat p = (matrix.m_pMatrix[12] * m_pMatrix[3]) + (matrix.m_pMatrix[13] * m_pMatrix[7]) + (matrix.m_pMatrix[14] * m_pMatrix[11]) + (matrix.m_pMatrix[15] * m_pMatrix[15]);
+ m_pMatrix[0] = a; m_pMatrix[4] = e; m_pMatrix[8] = i; m_pMatrix[12] = m;
+ m_pMatrix[1] = b; m_pMatrix[5] = f; m_pMatrix[9] = j; m_pMatrix[13] = n;
+ m_pMatrix[2] = c; m_pMatrix[6] = g; m_pMatrix[10] = k; m_pMatrix[14] = o;
+ m_pMatrix[3] = d; m_pMatrix[7] = h; m_pMatrix[11] = l; m_pMatrix[15] = p;
+}
+
+// gluLookAt implementation taken from Mesa3D
+void CMatrixGL::LookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez, GLfloat centerx, GLfloat centery, GLfloat centerz, GLfloat upx, GLfloat upy, GLfloat upz)
+{
+ GLfloat forward[3], side[3], up[3];
+
+ forward[0] = centerx - eyex;
+ forward[1] = centery - eyey;
+ forward[2] = centerz - eyez;
+
+ up[0] = upx;
+ up[1] = upy;
+ up[2] = upz;
+
+ GLfloat tmp = std::sqrt(forward[0]*forward[0] + forward[1]*forward[1] + forward[2]*forward[2]);
+ if (tmp != 0.0f)
+ {
+ forward[0] /= tmp;
+ forward[1] /= tmp;
+ forward[2] /= tmp;
+ }
+
+ side[0] = forward[1]*up[2] - forward[2]*up[1];
+ side[1] = forward[2]*up[0] - forward[0]*up[2];
+ side[2] = forward[0]*up[1] - forward[1]*up[0];
+
+ tmp = std::sqrt(side[0]*side[0] + side[1]*side[1] + side[2]*side[2]);
+ if (tmp != 0.0f)
+ {
+ side[0] /= tmp;
+ side[1] /= tmp;
+ side[2] /= tmp;
+ }
+
+ up[0] = side[1]*forward[2] - side[2]*forward[1];
+ up[1] = side[2]*forward[0] - side[0]*forward[2];
+ up[2] = side[0]*forward[1] - side[1]*forward[0];
+
+ const CMatrixGL matrix{
+ side[0], up[0], -forward[0], 0.0f,
+ side[1], up[1], -forward[1], 0.0f,
+ side[2], up[2], -forward[2], 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f,
+ };
+
+ MultMatrixf(matrix);
+ Translatef(-eyex, -eyey, -eyez);
+}
+
+static void __gluMultMatrixVecf(const GLfloat matrix[16], const GLfloat in[4], GLfloat out[4])
+{
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ out[i] = in[0] * matrix[0*4+i] +
+ in[1] * matrix[1*4+i] +
+ in[2] * matrix[2*4+i] +
+ in[3] * matrix[3*4+i];
+ }
+}
+
+// gluProject implementation taken from Mesa3D
+bool CMatrixGL::Project(GLfloat objx, GLfloat objy, GLfloat objz, const GLfloat modelMatrix[16], const GLfloat projMatrix[16], const GLint viewport[4], GLfloat* winx, GLfloat* winy, GLfloat* winz)
+{
+ GLfloat in[4];
+ GLfloat out[4];
+
+ in[0]=objx;
+ in[1]=objy;
+ in[2]=objz;
+ in[3]=1.0;
+ __gluMultMatrixVecf(modelMatrix, in, out);
+ __gluMultMatrixVecf(projMatrix, out, in);
+ if (in[3] == 0.0f)
+ return false;
+ in[0] /= in[3];
+ in[1] /= in[3];
+ in[2] /= in[3];
+ /* Map x, y and z to range 0-1 */
+ in[0] = in[0] * 0.5f + 0.5f;
+ in[1] = in[1] * 0.5f + 0.5f;
+ in[2] = in[2] * 0.5f + 0.5f;
+
+ /* Map x,y to viewport */
+ in[0] = in[0] * viewport[2] + viewport[0];
+ in[1] = in[1] * viewport[3] + viewport[1];
+
+ *winx=in[0];
+ *winy=in[1];
+ *winz=in[2];
+ return true;
+}
+
+void CMatrixGLStack::Load()
+{
+
+}
diff --git a/xbmc/rendering/MatrixGL.h b/xbmc/rendering/MatrixGL.h
new file mode 100644
index 0000000..333da2f
--- /dev/null
+++ b/xbmc/rendering/MatrixGL.h
@@ -0,0 +1,85 @@
+/*
+ * 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 <stack>
+
+#include "system_gl.h"
+
+class TransformMatrix;
+
+class CMatrixGL
+{
+public:
+ CMatrixGL() = default;
+
+ constexpr CMatrixGL(GLfloat x0, GLfloat x1, GLfloat x2, GLfloat x3,
+ GLfloat x4, GLfloat x5, GLfloat x6, GLfloat x7,
+ GLfloat x8, GLfloat x9, GLfloat x10, GLfloat x11,
+ GLfloat x12, GLfloat x13, GLfloat x14, GLfloat x15)
+ :m_pMatrix{x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15} {}
+
+ CMatrixGL(const TransformMatrix &src) noexcept;
+
+ operator const float*() const { return m_pMatrix; }
+
+ void LoadIdentity();
+ void Ortho(GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f);
+ void Ortho2D(GLfloat l, GLfloat r, GLfloat b, GLfloat t);
+ void Frustum(GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f);
+ void Translatef(GLfloat x, GLfloat y, GLfloat z);
+ void Scalef(GLfloat x, GLfloat y, GLfloat z);
+ void Rotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+ void MultMatrixf(const CMatrixGL &matrix) noexcept;
+ void LookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez, GLfloat centerx, GLfloat centery, GLfloat centerz, GLfloat upx, GLfloat upy, GLfloat upz);
+
+ static bool Project(GLfloat objx, GLfloat objy, GLfloat objz, const GLfloat modelMatrix[16], const GLfloat projMatrix[16], const GLint viewport[4], GLfloat* winx, GLfloat* winy, GLfloat* winz);
+
+private:
+ /* alignas(16) allows better SIMD optimizations (e.g. SSE2 benefits
+ a lot from this) */
+ alignas(16) GLfloat m_pMatrix[16];
+};
+
+class CMatrixGLStack
+{
+public:
+ void Push()
+ {
+ m_stack.push(m_current);
+ }
+
+ void Clear()
+ {
+ m_stack = std::stack<CMatrixGL>();
+ }
+
+ void Pop()
+ {
+ if(!m_stack.empty())
+ {
+ m_current = m_stack.top();
+ m_stack.pop();
+ }
+ }
+
+ void Load();
+ void PopLoad() { Pop(); Load(); }
+
+ CMatrixGL& Get() { return m_current; }
+ CMatrixGL* operator->() { return &m_current; }
+
+private:
+ std::stack<CMatrixGL> m_stack;
+ CMatrixGL m_current;
+};
+
+extern CMatrixGLStack glMatrixModview;
+extern CMatrixGLStack glMatrixProject;
+extern CMatrixGLStack glMatrixTexture;
diff --git a/xbmc/rendering/MatrixGL.neon.cpp b/xbmc/rendering/MatrixGL.neon.cpp
new file mode 100644
index 0000000..3f3f33c
--- /dev/null
+++ b/xbmc/rendering/MatrixGL.neon.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+
+void Matrix4Mul(float* src_mat_1, const float* src_mat_2)
+{
+ asm volatile (
+ // Store A & B leaving room at top of registers for result (q0-q3)
+ "vldmia %0, { q4-q7 } \n\t"
+ "vldmia %1, { q8-q11 } \n\t"
+
+ // result = first column of B x first row of A
+ "vmul.f32 q0, q8, d8[0]\n\t"
+ "vmul.f32 q1, q8, d10[0]\n\t"
+ "vmul.f32 q2, q8, d12[0]\n\t"
+ "vmul.f32 q3, q8, d14[0]\n\t"
+
+ // result += second column of B x second row of A
+ "vmla.f32 q0, q9, d8[1]\n\t"
+ "vmla.f32 q1, q9, d10[1]\n\t"
+ "vmla.f32 q2, q9, d12[1]\n\t"
+ "vmla.f32 q3, q9, d14[1]\n\t"
+
+ // result += third column of B x third row of A
+ "vmla.f32 q0, q10, d9[0]\n\t"
+ "vmla.f32 q1, q10, d11[0]\n\t"
+ "vmla.f32 q2, q10, d13[0]\n\t"
+ "vmla.f32 q3, q10, d15[0]\n\t"
+
+ // result += last column of B x last row of A
+ "vmla.f32 q0, q11, d9[1]\n\t"
+ "vmla.f32 q1, q11, d11[1]\n\t"
+ "vmla.f32 q2, q11, d13[1]\n\t"
+ "vmla.f32 q3, q11, d15[1]\n\t"
+
+ // output = result registers
+ "vstmia %1, { q0-q3 }"
+ : //no output
+ : "r" (src_mat_2), "r" (src_mat_1) // input - note *value* of pointer doesn't change
+ : "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11" //clobber
+ );
+}
diff --git a/xbmc/rendering/RenderSystem.cpp b/xbmc/rendering/RenderSystem.cpp
new file mode 100644
index 0000000..3f10562
--- /dev/null
+++ b/xbmc/rendering/RenderSystem.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "RenderSystem.h"
+
+#include "Util.h"
+#include "guilib/GUIFontManager.h"
+#include "guilib/GUIImage.h"
+#include "guilib/GUILabelControl.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+
+CRenderSystemBase::CRenderSystemBase()
+{
+ m_bRenderCreated = false;
+ m_bVSync = true;
+ m_maxTextureSize = 2048;
+ m_RenderVersionMajor = 0;
+ m_RenderVersionMinor = 0;
+ m_minDXTPitch = 0;
+}
+
+CRenderSystemBase::~CRenderSystemBase() = default;
+
+void CRenderSystemBase::GetRenderVersion(unsigned int& major, unsigned int& minor) const
+{
+ major = m_RenderVersionMajor;
+ minor = m_RenderVersionMinor;
+}
+
+bool CRenderSystemBase::SupportsNPOT(bool dxt) const
+{
+ if (dxt)
+ return false;
+
+ return true;
+}
+
+bool CRenderSystemBase::SupportsStereo(RENDER_STEREO_MODE mode) const
+{
+ switch(mode)
+ {
+ case RENDER_STEREO_MODE_OFF:
+ case RENDER_STEREO_MODE_SPLIT_HORIZONTAL:
+ case RENDER_STEREO_MODE_SPLIT_VERTICAL:
+ case RENDER_STEREO_MODE_MONO:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void CRenderSystemBase::ShowSplash(const std::string& message)
+{
+ if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_splashImage && !(m_splashImage || !message.empty()))
+ return;
+
+ if (!m_splashImage)
+ {
+ m_splashImage = std::unique_ptr<CGUIImage>(new CGUIImage(0, 0, 0, 0, CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(),
+ CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight(), CTextureInfo(CUtil::GetSplashPath())));
+ m_splashImage->SetAspectRatio(CAspectRatio::AR_SCALE);
+ }
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().lock();
+ CServiceBroker::GetWinSystem()->GetGfxContext().Clear();
+
+ RESOLUTION_INFO res = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetRenderingResolution(res, true);
+
+ //render splash image
+ BeginRender();
+
+ m_splashImage->AllocResources();
+ m_splashImage->Render();
+ m_splashImage->FreeResources();
+
+ if (!message.empty())
+ {
+ if (!m_splashMessageLayout)
+ {
+ auto messageFont = g_fontManager.LoadTTF("__splash__", "arial.ttf", 0xFFFFFFFF, 0, 20, FONT_STYLE_NORMAL, false, 1.0f, 1.0f, &res);
+ if (messageFont)
+ m_splashMessageLayout = std::unique_ptr<CGUITextLayout>(new CGUITextLayout(messageFont, true, 0));
+ }
+
+ if (m_splashMessageLayout)
+ {
+ m_splashMessageLayout->Update(message, 1150, false, true);
+ float textWidth, textHeight;
+ m_splashMessageLayout->GetTextExtent(textWidth, textHeight);
+
+ int width = CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth();
+ int height = CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight();
+ float y = height - textHeight - 100;
+ m_splashMessageLayout->RenderOutline(width/2, y, 0, 0xFF000000, XBFONT_CENTER_X, width);
+ }
+ }
+
+ //show it on screen
+ EndRender();
+ CServiceBroker::GetWinSystem()->GetGfxContext().unlock();
+ CServiceBroker::GetWinSystem()->GetGfxContext().Flip(true, false);
+}
+
diff --git a/xbmc/rendering/RenderSystem.h b/xbmc/rendering/RenderSystem.h
new file mode 100644
index 0000000..0c3d6e0
--- /dev/null
+++ b/xbmc/rendering/RenderSystem.h
@@ -0,0 +1,98 @@
+/*
+ * 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 "RenderSystemTypes.h"
+#include "utils/ColorUtils.h"
+#include "utils/Geometry.h"
+
+#include <memory>
+#include <string>
+
+/*
+ * CRenderSystemBase interface allows us to create the rendering engine we use.
+ * We currently have two engines: OpenGL and DirectX
+ * This interface is very basic since a lot of the actual details will go in to the derived classes
+ */
+
+class CGUIImage;
+class CGUITextLayout;
+
+class CRenderSystemBase
+{
+public:
+ CRenderSystemBase();
+ virtual ~CRenderSystemBase();
+
+ virtual bool InitRenderSystem() = 0;
+ virtual bool DestroyRenderSystem() = 0;
+ virtual bool ResetRenderSystem(int width, int height) = 0;
+
+ virtual bool BeginRender() = 0;
+ virtual bool EndRender() = 0;
+ virtual void PresentRender(bool rendered, bool videoLayer) = 0;
+ virtual bool ClearBuffers(UTILS::COLOR::Color color) = 0;
+ virtual bool IsExtSupported(const char* extension) const = 0;
+
+ virtual void SetViewPort(const CRect& viewPort) = 0;
+ virtual void GetViewPort(CRect& viewPort) = 0;
+ virtual void RestoreViewPort() {}
+
+ virtual bool ScissorsCanEffectClipping() { return false; }
+ virtual CRect ClipRectToScissorRect(const CRect &rect) { return CRect(); }
+ virtual void SetScissors(const CRect &rect) = 0;
+ virtual void ResetScissors() = 0;
+
+ virtual void CaptureStateBlock() = 0;
+ virtual void ApplyStateBlock() = 0;
+
+ virtual void SetCameraPosition(const CPoint &camera, int screenWidth, int screenHeight, float stereoFactor = 0.f) = 0;
+ virtual void SetStereoMode(RENDER_STEREO_MODE mode, RENDER_STEREO_VIEW view)
+ {
+ m_stereoMode = mode;
+ m_stereoView = view;
+ }
+
+ /**
+ * Project (x,y,z) 3d scene coordinates to (x,y) 2d screen coordinates
+ */
+ virtual void Project(float &x, float &y, float &z) { }
+
+ virtual std::string GetShaderPath(const std::string &filename) { return ""; }
+
+ void GetRenderVersion(unsigned int& major, unsigned int& minor) const;
+ const std::string& GetRenderVendor() const { return m_RenderVendor; }
+ const std::string& GetRenderRenderer() const { return m_RenderRenderer; }
+ const std::string& GetRenderVersionString() const { return m_RenderVersion; }
+ virtual bool SupportsNPOT(bool dxt) const;
+ virtual bool SupportsStereo(RENDER_STEREO_MODE mode) const;
+ unsigned int GetMaxTextureSize() const { return m_maxTextureSize; }
+ unsigned int GetMinDXTPitch() const { return m_minDXTPitch; }
+
+ virtual void ShowSplash(const std::string& message);
+
+protected:
+ bool m_bRenderCreated;
+ bool m_bVSync;
+ unsigned int m_maxTextureSize;
+ unsigned int m_minDXTPitch;
+
+ std::string m_RenderRenderer;
+ std::string m_RenderVendor;
+ std::string m_RenderVersion;
+ int m_RenderVersionMinor;
+ int m_RenderVersionMajor;
+ RENDER_STEREO_VIEW m_stereoView = RENDER_STEREO_VIEW_OFF;
+ RENDER_STEREO_MODE m_stereoMode = RENDER_STEREO_MODE_OFF;
+ bool m_limitedColorRange = false;
+
+ std::unique_ptr<CGUIImage> m_splashImage;
+ std::unique_ptr<CGUITextLayout> m_splashMessageLayout;
+};
+
diff --git a/xbmc/rendering/RenderSystemTypes.h b/xbmc/rendering/RenderSystemTypes.h
new file mode 100644
index 0000000..c818183
--- /dev/null
+++ b/xbmc/rendering/RenderSystemTypes.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017-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
+
+enum RENDER_STEREO_VIEW
+{
+ RENDER_STEREO_VIEW_OFF,
+ RENDER_STEREO_VIEW_LEFT,
+ RENDER_STEREO_VIEW_RIGHT,
+};
+
+enum RENDER_STEREO_MODE
+{
+ RENDER_STEREO_MODE_OFF,
+ RENDER_STEREO_MODE_SPLIT_HORIZONTAL,
+ RENDER_STEREO_MODE_SPLIT_VERTICAL,
+ RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN,
+ RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA,
+ RENDER_STEREO_MODE_ANAGLYPH_YELLOW_BLUE,
+ RENDER_STEREO_MODE_INTERLACED,
+ RENDER_STEREO_MODE_CHECKERBOARD,
+ RENDER_STEREO_MODE_HARDWAREBASED,
+ RENDER_STEREO_MODE_MONO,
+ RENDER_STEREO_MODE_COUNT,
+
+ // Pseudo modes
+ RENDER_STEREO_MODE_AUTO = 100,
+ RENDER_STEREO_MODE_UNDEFINED = 999,
+};
diff --git a/xbmc/rendering/dx/CMakeLists.txt b/xbmc/rendering/dx/CMakeLists.txt
new file mode 100644
index 0000000..7466907
--- /dev/null
+++ b/xbmc/rendering/dx/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(SOURCES DeviceResources.cpp
+ RenderSystemDX.cpp
+ ScreenshotSurfaceWindows.cpp)
+
+set(HEADERS DeviceResources.h
+ DirectXHelper.h
+ RenderContext.h
+ RenderSystemDX.h
+ ScreenshotSurfaceWindows.h)
+
+core_add_library(rendering_dx)
diff --git a/xbmc/rendering/dx/DeviceResources.cpp b/xbmc/rendering/dx/DeviceResources.cpp
new file mode 100644
index 0000000..b3ae890
--- /dev/null
+++ b/xbmc/rendering/dx/DeviceResources.cpp
@@ -0,0 +1,1361 @@
+/*
+ * 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 "DeviceResources.h"
+
+#include "DirectXHelper.h"
+#include "RenderContext.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "messaging/ApplicationMessenger.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/SystemInfo.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#include "platform/win32/CharsetConverter.h"
+#include "platform/win32/WIN32Util.h"
+
+#ifdef TARGET_WINDOWS_STORE
+#include <winrt/Windows.Graphics.Display.Core.h>
+
+extern "C"
+{
+#include <libavutil/rational.h>
+}
+#endif
+
+#ifdef _DEBUG
+#include <dxgidebug.h>
+#pragma comment(lib, "dxgi.lib")
+#endif // _DEBUG
+
+using namespace DirectX;
+using namespace Microsoft::WRL;
+using namespace Concurrency;
+namespace winrt
+{
+ using namespace Windows::Foundation;
+}
+
+#ifdef _DEBUG
+#define breakOnDebug __debugbreak()
+#else
+#define breakOnDebug
+#endif
+#define LOG_HR(hr) \
+ CLog::LogF(LOGERROR, "function call at line {} ends with error: {}", __LINE__, \
+ DX::GetErrorDescription(hr));
+#define CHECK_ERR() if (FAILED(hr)) { LOG_HR(hr); breakOnDebug; return; }
+#define RETURN_ERR(ret) if (FAILED(hr)) { LOG_HR(hr); breakOnDebug; return (##ret); }
+
+bool DX::DeviceResources::CBackBuffer::Acquire(ID3D11Texture2D* pTexture)
+{
+ if (!pTexture)
+ return false;
+
+ D3D11_TEXTURE2D_DESC desc;
+ pTexture->GetDesc(&desc);
+
+ m_width = desc.Width;
+ m_height = desc.Height;
+ m_format = desc.Format;
+ m_usage = desc.Usage;
+
+ m_texture = pTexture;
+ return true;
+}
+
+std::shared_ptr<DX::DeviceResources> DX::DeviceResources::Get()
+{
+ static std::shared_ptr<DeviceResources> sDeviceResources(new DeviceResources);
+ return sDeviceResources;
+}
+
+// Constructor for DeviceResources.
+DX::DeviceResources::DeviceResources()
+ : m_screenViewport()
+ , m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1)
+ , m_outputSize()
+ , m_logicalSize()
+ , m_dpi(DisplayMetrics::Dpi100)
+ , m_effectiveDpi(DisplayMetrics::Dpi100)
+ , m_deviceNotify(nullptr)
+ , m_stereoEnabled(false)
+ , m_bDeviceCreated(false)
+ , m_IsHDROutput(false)
+ , m_IsTransferPQ(false)
+{
+}
+
+DX::DeviceResources::~DeviceResources() = default;
+
+void DX::DeviceResources::Release()
+{
+ if (!m_bDeviceCreated)
+ return;
+
+ ReleaseBackBuffer();
+ OnDeviceLost(true);
+ DestroySwapChain();
+
+ m_adapter = nullptr;
+ m_dxgiFactory = nullptr;
+ m_output = nullptr;
+ m_deferrContext = nullptr;
+ m_d3dContext = nullptr;
+ m_d3dDevice = nullptr;
+ m_bDeviceCreated = false;
+#ifdef _DEBUG
+ if (m_d3dDebug)
+ {
+ m_d3dDebug->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL);
+ m_d3dDebug = nullptr;
+ }
+#endif
+}
+
+void DX::DeviceResources::GetOutput(IDXGIOutput** ppOutput) const
+{
+ ComPtr<IDXGIOutput> pOutput;
+ if (!m_swapChain || FAILED(m_swapChain->GetContainingOutput(pOutput.GetAddressOf())) || !pOutput)
+ m_output.As(&pOutput);
+ *ppOutput = pOutput.Detach();
+}
+
+void DX::DeviceResources::GetAdapterDesc(DXGI_ADAPTER_DESC* desc) const
+{
+ if (m_adapter)
+ m_adapter->GetDesc(desc);
+
+ // GetDesc() returns VendorId == 0 in Xbox however, we need to know that
+ // GPU is AMD to apply workarounds in DXVA.cpp CheckCompatibility() same as desktop
+ if (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::Xbox)
+ desc->VendorId = PCIV_AMD;
+}
+
+void DX::DeviceResources::GetDisplayMode(DXGI_MODE_DESC* mode) const
+{
+ DXGI_OUTPUT_DESC outDesc;
+ ComPtr<IDXGIOutput> pOutput;
+ DXGI_SWAP_CHAIN_DESC scDesc;
+
+ if (!m_swapChain)
+ return;
+
+ m_swapChain->GetDesc(&scDesc);
+
+ GetOutput(pOutput.GetAddressOf());
+ pOutput->GetDesc(&outDesc);
+
+ // desktop coords depend on DPI
+ mode->Width = DX::ConvertDipsToPixels(outDesc.DesktopCoordinates.right - outDesc.DesktopCoordinates.left, m_dpi);
+ mode->Height = DX::ConvertDipsToPixels(outDesc.DesktopCoordinates.bottom - outDesc.DesktopCoordinates.top, m_dpi);
+ mode->Format = scDesc.BufferDesc.Format;
+ mode->Scaling = scDesc.BufferDesc.Scaling;
+ mode->ScanlineOrdering = scDesc.BufferDesc.ScanlineOrdering;
+
+#ifdef TARGET_WINDOWS_DESKTOP
+ DEVMODEW sDevMode = {};
+ sDevMode.dmSize = sizeof(sDevMode);
+
+ // EnumDisplaySettingsW is only one way to detect current refresh rate
+ if (EnumDisplaySettingsW(outDesc.DeviceName, ENUM_CURRENT_SETTINGS, &sDevMode))
+ {
+ int i = (((sDevMode.dmDisplayFrequency + 1) % 24) == 0 || ((sDevMode.dmDisplayFrequency + 1) % 30) == 0) ? 1 : 0;
+ mode->RefreshRate.Numerator = (sDevMode.dmDisplayFrequency + i) * 1000;
+ mode->RefreshRate.Denominator = 1000 + i;
+ if (sDevMode.dmDisplayFlags & DM_INTERLACED)
+ {
+ mode->RefreshRate.Numerator *= 2;
+ mode->ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST; // guessing
+ }
+ else
+ mode->ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
+ }
+#else
+ using namespace winrt::Windows::Graphics::Display::Core;
+
+ auto hdmiInfo = HdmiDisplayInformation::GetForCurrentView();
+ if (hdmiInfo) // Xbox only
+ {
+ auto currentMode = hdmiInfo.GetCurrentDisplayMode();
+ AVRational refresh = av_d2q(currentMode.RefreshRate(), 60000);
+ mode->RefreshRate.Numerator = refresh.num;
+ mode->RefreshRate.Denominator = refresh.den;
+ }
+#endif
+}
+
+void DX::DeviceResources::SetViewPort(D3D11_VIEWPORT& viewPort) const
+{
+ // convert logical viewport to real
+ D3D11_VIEWPORT realViewPort =
+ {
+ viewPort.TopLeftX,
+ viewPort.TopLeftY,
+ viewPort.Width,
+ viewPort.Height,
+ viewPort.MinDepth,
+ viewPort.MinDepth
+ };
+
+ m_deferrContext->RSSetViewports(1, &realViewPort);
+}
+
+bool DX::DeviceResources::SetFullScreen(bool fullscreen, RESOLUTION_INFO& res)
+{
+ if (!m_bDeviceCreated || !m_swapChain)
+ return false;
+
+ critical_section::scoped_lock lock(m_criticalSection);
+
+ BOOL bFullScreen;
+ m_swapChain->GetFullscreenState(&bFullScreen, nullptr);
+
+ CLog::LogF(LOGDEBUG, "switching from {}({:.0f} x {:.0f}) to {}({} x {})",
+ bFullScreen ? "fullscreen " : "", m_outputSize.Width, m_outputSize.Height,
+ fullscreen ? "fullscreen " : "", res.iWidth, res.iHeight);
+
+ bool recreate = m_stereoEnabled != (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED);
+ if (!!bFullScreen && !fullscreen)
+ {
+ CLog::LogF(LOGDEBUG, "switching to windowed");
+ recreate |= SUCCEEDED(m_swapChain->SetFullscreenState(false, nullptr));
+ }
+ else if (fullscreen)
+ {
+ const bool isResValid = res.iWidth > 0 && res.iHeight > 0 && res.fRefreshRate > 0.f;
+ if (isResValid)
+ {
+ DXGI_MODE_DESC currentMode = {};
+ GetDisplayMode(&currentMode);
+ DXGI_SWAP_CHAIN_DESC scDesc;
+ m_swapChain->GetDesc(&scDesc);
+
+ bool is_interlaced = scDesc.BufferDesc.ScanlineOrdering > DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
+ float refreshRate = res.fRefreshRate;
+ if (res.dwFlags & D3DPRESENTFLAG_INTERLACED)
+ refreshRate *= 2;
+
+ if (currentMode.Width != res.iWidth
+ || currentMode.Height != res.iHeight
+ || DX::RationalToFloat(currentMode.RefreshRate) != refreshRate
+ || is_interlaced != (res.dwFlags & D3DPRESENTFLAG_INTERLACED ? true : false)
+ // force resolution change for stereo mode
+ // some drivers unable to create stereo swapchain if mode does not match @23.976
+ || CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED)
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__ ": changing display mode to {}x{}@{:0.3f}", res.iWidth,
+ res.iHeight, res.fRefreshRate,
+ res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+
+ int refresh = static_cast<int>(res.fRefreshRate);
+ int i = (refresh + 1) % 24 == 0 || (refresh + 1) % 30 == 0 ? 1 : 0;
+
+ currentMode.Width = res.iWidth;
+ currentMode.Height = res.iHeight;
+ currentMode.RefreshRate.Numerator = (refresh + i) * 1000;
+ currentMode.RefreshRate.Denominator = 1000 + i;
+ if (res.dwFlags & D3DPRESENTFLAG_INTERLACED)
+ {
+ currentMode.RefreshRate.Numerator *= 2;
+ currentMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST; // guessing;
+ }
+ // sometimes the OS silently brings Kodi out of full screen mode
+ // in this case switching a resolution has no any effect and
+ // we have to enter into full screen mode before switching
+ if (!bFullScreen)
+ {
+ ComPtr<IDXGIOutput> pOutput;
+ GetOutput(pOutput.GetAddressOf());
+
+ CLog::LogF(LOGDEBUG, "fixup fullscreen mode before switching resolution");
+ recreate |= SUCCEEDED(m_swapChain->SetFullscreenState(true, pOutput.Get()));
+ m_swapChain->GetFullscreenState(&bFullScreen, nullptr);
+ }
+ bool resized = SUCCEEDED(m_swapChain->ResizeTarget(&currentMode));
+ if (resized)
+ {
+ // some system doesn't inform windowing about desktop size changes
+ // so we have to change output size before resizing buffers
+ m_outputSize.Width = static_cast<float>(currentMode.Width);
+ m_outputSize.Height = static_cast<float>(currentMode.Height);
+ }
+ recreate |= resized;
+ }
+ }
+ if (!bFullScreen)
+ {
+ ComPtr<IDXGIOutput> pOutput;
+ GetOutput(pOutput.GetAddressOf());
+
+ CLog::LogF(LOGDEBUG, "switching to fullscreen");
+ recreate |= SUCCEEDED(m_swapChain->SetFullscreenState(true, pOutput.Get()));
+ }
+ }
+
+ // resize backbuffer to proper handle fullscreen/stereo transition
+ if (recreate)
+ ResizeBuffers();
+
+ CLog::LogF(LOGDEBUG, "switching done.");
+
+ return recreate;
+}
+
+// Configures resources that don't depend on the Direct3D device.
+void DX::DeviceResources::CreateDeviceIndependentResources()
+{
+}
+
+// Configures the Direct3D device, and stores handles to it and the device context.
+void DX::DeviceResources::CreateDeviceResources()
+{
+ CLog::LogF(LOGDEBUG, "creating DirectX 11 device.");
+
+ CreateFactory();
+
+ UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+#if defined(_DEBUG)
+ if (DX::SdkLayersAvailable())
+ {
+ // If the project is in a debug build, enable debugging via SDK Layers with this flag.
+ creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
+ }
+#endif
+
+ // This array defines the set of DirectX hardware feature levels this app will support.
+ // Note the ordering should be preserved.
+ // Don't forget to declare your application's minimum required feature level in its
+ // description. All applications are assumed to support 9.1 unless otherwise stated.
+ std::vector<D3D_FEATURE_LEVEL> featureLevels;
+ if (CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10))
+ {
+ featureLevels.push_back(D3D_FEATURE_LEVEL_12_1);
+ featureLevels.push_back(D3D_FEATURE_LEVEL_12_0);
+ }
+ if (CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin8))
+ featureLevels.push_back(D3D_FEATURE_LEVEL_11_1);
+ featureLevels.push_back(D3D_FEATURE_LEVEL_11_0);
+ featureLevels.push_back(D3D_FEATURE_LEVEL_10_1);
+ featureLevels.push_back(D3D_FEATURE_LEVEL_10_0);
+ featureLevels.push_back(D3D_FEATURE_LEVEL_9_3);
+ featureLevels.push_back(D3D_FEATURE_LEVEL_9_2);
+ featureLevels.push_back(D3D_FEATURE_LEVEL_9_1);
+
+ // Create the Direct3D 11 API device object and a corresponding context.
+ ComPtr<ID3D11Device> device;
+ ComPtr<ID3D11DeviceContext> context;
+
+ D3D_DRIVER_TYPE drivertType = m_adapter != nullptr ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;
+ HRESULT hr = D3D11CreateDevice(
+ m_adapter.Get(), // Create a device on specified adapter.
+ drivertType, // Create a device using scepcified driver.
+ nullptr, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
+ creationFlags, // Set debug and Direct2D compatibility flags.
+ featureLevels.data(), // List of feature levels this app can support.
+ featureLevels.size(), // Size of the list above.
+ D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
+ &device, // Returns the Direct3D device created.
+ &m_d3dFeatureLevel, // Returns feature level of device created.
+ &context // Returns the device immediate context.
+ );
+
+ if (FAILED(hr))
+ {
+ CLog::LogF(LOGERROR, "unable to create hardware device, trying to create WARP devices then.");
+ hr = D3D11CreateDevice(
+ nullptr,
+ D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device.
+ nullptr,
+ creationFlags,
+ featureLevels.data(),
+ featureLevels.size(),
+ D3D11_SDK_VERSION,
+ &device,
+ &m_d3dFeatureLevel,
+ &context
+ );
+ if (FAILED(hr))
+ {
+ CLog::LogF(LOGFATAL, "unable to create WARP device. Rendering in not possible.");
+ CHECK_ERR();
+ }
+ }
+
+ // Store pointers to the Direct3D 11.1 API device and immediate context.
+ hr = device.As(&m_d3dDevice); CHECK_ERR();
+
+ // Check shared textures support
+ CheckNV12SharedTexturesSupport();
+
+#ifdef _DEBUG
+ if (SUCCEEDED(m_d3dDevice.As(&m_d3dDebug)))
+ {
+ ComPtr<ID3D11InfoQueue> d3dInfoQueue;
+ if (SUCCEEDED(m_d3dDebug.As(&d3dInfoQueue)))
+ {
+ std::vector<D3D11_MESSAGE_ID> hide =
+ {
+ D3D11_MESSAGE_ID_GETVIDEOPROCESSORFILTERRANGE_UNSUPPORTED, // avoid GETVIDEOPROCESSORFILTERRANGE_UNSUPPORTED (dx bug)
+ D3D11_MESSAGE_ID_DEVICE_RSSETSCISSORRECTS_NEGATIVESCISSOR // avoid warning for some labels out of screen
+ // Add more message IDs here as needed
+ };
+
+ D3D11_INFO_QUEUE_FILTER filter = {};
+ filter.DenyList.NumIDs = hide.size();
+ filter.DenyList.pIDList = hide.data();
+ d3dInfoQueue->AddStorageFilterEntries(&filter);
+ }
+ }
+#endif
+
+ hr = context.As(&m_d3dContext); CHECK_ERR();
+ hr = m_d3dDevice->CreateDeferredContext1(0, &m_deferrContext); CHECK_ERR();
+
+ if (!m_adapter)
+ {
+ ComPtr<IDXGIDevice1> dxgiDevice;
+ ComPtr<IDXGIAdapter> adapter;
+ hr = m_d3dDevice.As(&dxgiDevice); CHECK_ERR();
+ hr = dxgiDevice->GetAdapter(&adapter); CHECK_ERR();
+ hr = adapter.As(&m_adapter); CHECK_ERR();
+ }
+
+ DXGI_ADAPTER_DESC aDesc;
+ m_adapter->GetDesc(&aDesc);
+
+ CLog::LogF(LOGINFO, "device is created on adapter '{}' with {}",
+ KODI::PLATFORM::WINDOWS::FromW(aDesc.Description),
+ GetFeatureLevelDescription(m_d3dFeatureLevel));
+
+ CheckDXVA2SharedDecoderSurfaces();
+
+ m_bDeviceCreated = true;
+}
+
+void DX::DeviceResources::ReleaseBackBuffer()
+{
+ CLog::LogF(LOGDEBUG, "release buffers.");
+
+ m_backBufferTex.Release();
+ m_d3dDepthStencilView = nullptr;
+ if (m_deferrContext)
+ {
+ // Clear the previous window size specific context.
+ ID3D11RenderTargetView* nullViews[] = { nullptr, nullptr, nullptr, nullptr };
+ m_deferrContext->OMSetRenderTargets(4, nullViews, nullptr);
+ FinishCommandList(false);
+
+ m_deferrContext->Flush();
+ m_d3dContext->Flush();
+ }
+}
+
+void DX::DeviceResources::CreateBackBuffer()
+{
+ if (!m_bDeviceCreated || !m_swapChain)
+ return;
+
+ CLog::LogF(LOGDEBUG, "create buffers.");
+
+ // Get swap chain back buffer.
+ ComPtr<ID3D11Texture2D> backBuffer;
+ HRESULT hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)); CHECK_ERR();
+
+ // Create back buffer texture from swap chain texture
+ if (!m_backBufferTex.Acquire(backBuffer.Get()))
+ {
+ CLog::LogF(LOGERROR, "failed to create render target.");
+ return;
+ }
+
+ // Create a depth stencil view for use with 3D rendering if needed.
+ CD3D11_TEXTURE2D_DESC depthStencilDesc(
+ DXGI_FORMAT_D24_UNORM_S8_UINT,
+ lround(m_outputSize.Width),
+ lround(m_outputSize.Height),
+ 1, // This depth stencil view has only one texture.
+ 1, // Use a single mipmap level.
+ D3D11_BIND_DEPTH_STENCIL
+ );
+
+ ComPtr<ID3D11Texture2D> depthStencil;
+ hr = m_d3dDevice->CreateTexture2D(
+ &depthStencilDesc,
+ nullptr,
+ &depthStencil
+ ); CHECK_ERR();
+
+ CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
+ hr = m_d3dDevice->CreateDepthStencilView(
+ depthStencil.Get(),
+ &depthStencilViewDesc,
+ &m_d3dDepthStencilView
+ ); CHECK_ERR();
+
+ // Set the 3D rendering viewport to target the entire window.
+ m_screenViewport = CD3D11_VIEWPORT(
+ 0.0f,
+ 0.0f,
+ m_outputSize.Width,
+ m_outputSize.Height
+ );
+
+ m_deferrContext->RSSetViewports(1, &m_screenViewport);
+}
+
+HRESULT DX::DeviceResources::CreateSwapChain(DXGI_SWAP_CHAIN_DESC1& desc, DXGI_SWAP_CHAIN_FULLSCREEN_DESC& fsDesc, IDXGISwapChain1** ppSwapChain) const
+{
+ HRESULT hr;
+#ifdef TARGET_WINDOWS_DESKTOP
+ hr = m_dxgiFactory->CreateSwapChainForHwnd(
+ m_d3dDevice.Get(),
+ m_window,
+ &desc,
+ &fsDesc,
+ nullptr,
+ ppSwapChain
+ ); RETURN_ERR(hr);
+ hr = m_dxgiFactory->MakeWindowAssociation(m_window, /*DXGI_MWA_NO_WINDOW_CHANGES |*/ DXGI_MWA_NO_ALT_ENTER);
+#else
+ hr = m_dxgiFactory->CreateSwapChainForCoreWindow(
+ m_d3dDevice.Get(),
+ winrt::get_unknown(m_coreWindow),
+ &desc,
+ nullptr,
+ ppSwapChain
+ ); RETURN_ERR(hr);
+#endif
+ return hr;
+}
+
+void DX::DeviceResources::DestroySwapChain()
+{
+ if (!m_swapChain)
+ return;
+
+ BOOL bFullcreen = 0;
+ m_swapChain->GetFullscreenState(&bFullcreen, nullptr);
+ if (!!bFullcreen)
+ m_swapChain->SetFullscreenState(false, nullptr); // mandatory before releasing swapchain
+ m_swapChain = nullptr;
+ m_deferrContext->Flush();
+ m_d3dContext->Flush();
+ m_IsTransferPQ = false;
+}
+
+void DX::DeviceResources::ResizeBuffers()
+{
+ if (!m_bDeviceCreated)
+ return;
+
+ CLog::LogF(LOGDEBUG, "resize buffers.");
+
+ bool bHWStereoEnabled = RENDER_STEREO_MODE_HARDWAREBASED ==
+ CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode();
+ bool windowed = true;
+ HRESULT hr = E_FAIL;
+ DXGI_SWAP_CHAIN_DESC1 scDesc = {};
+
+ if (m_swapChain)
+ {
+ BOOL bFullcreen = 0;
+ m_swapChain->GetFullscreenState(&bFullcreen, nullptr);
+ if (!!bFullcreen)
+ windowed = false;
+
+ m_swapChain->GetDesc1(&scDesc);
+ if ((scDesc.Stereo == TRUE) != bHWStereoEnabled) // check if swapchain needs to be recreated
+ DestroySwapChain();
+ }
+
+ if (m_swapChain) // If the swap chain already exists, resize it.
+ {
+ m_swapChain->GetDesc1(&scDesc);
+ hr = m_swapChain->ResizeBuffers(scDesc.BufferCount, lround(m_outputSize.Width),
+ lround(m_outputSize.Height), scDesc.Format,
+ windowed ? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
+
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
+ {
+ // If the device was removed for any reason, a new device and swap chain will need to be created.
+ HandleDeviceLost(hr == DXGI_ERROR_DEVICE_REMOVED);
+
+ // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
+ // and correctly set up the new device.
+ return;
+ }
+ else if (hr == DXGI_ERROR_INVALID_CALL)
+ {
+ // Called when Windows HDR is toggled externally to Kodi.
+ // Is forced to re-create swap chain to avoid crash.
+ CreateWindowSizeDependentResources();
+ return;
+ }
+ CHECK_ERR();
+ }
+ else // Otherwise, create a new one using the same adapter as the existing Direct3D device.
+ {
+ HDR_STATUS hdrStatus = CWIN32Util::GetWindowsHDRStatus();
+ const bool isHdrEnabled = (hdrStatus == HDR_STATUS::HDR_ON);
+ bool use10bit = (hdrStatus != HDR_STATUS::HDR_UNSUPPORTED);
+
+// Xbox needs 10 bit swapchain to output true 4K resolution
+#ifdef TARGET_WINDOWS_DESKTOP
+ DXGI_ADAPTER_DESC ad = {};
+ GetAdapterDesc(&ad);
+
+ // Some AMD graphics has issues with 10 bit in SDR.
+ // Enabled by default only in Intel and NVIDIA with latest drivers/hardware
+ if (m_d3dFeatureLevel < D3D_FEATURE_LEVEL_12_1 || ad.VendorId == PCIV_AMD)
+ use10bit = false;
+#endif
+
+ // 0 = Auto | 1 = Never | 2 = Always
+ int use10bitSetting = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ CSettings::SETTING_VIDEOSCREEN_10BITSURFACES);
+
+ if (use10bitSetting == 1)
+ use10bit = false;
+ else if (use10bitSetting == 2)
+ use10bit = true;
+
+ DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
+ swapChainDesc.Width = lround(m_outputSize.Width);
+ swapChainDesc.Height = lround(m_outputSize.Height);
+ swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ swapChainDesc.Stereo = bHWStereoEnabled;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+#ifdef TARGET_WINDOWS_DESKTOP
+ swapChainDesc.BufferCount = 6; // HDR 60 fps needs 6 buffers to avoid frame drops
+#else
+ swapChainDesc.BufferCount = 3; // Xbox don't like 6 backbuffers (3 is fine even for 4K 60 fps)
+#endif
+ // FLIP_DISCARD improves performance (needed in some systems for 4K HDR 60 fps)
+ swapChainDesc.SwapEffect = CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10)
+ ? DXGI_SWAP_EFFECT_FLIP_DISCARD
+ : DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ swapChainDesc.Flags = windowed ? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+ swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+ swapChainDesc.SampleDesc.Count = 1;
+ swapChainDesc.SampleDesc.Quality = 0;
+
+ DXGI_SWAP_CHAIN_FULLSCREEN_DESC scFSDesc = {}; // unused for uwp
+ scFSDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ scFSDesc.Windowed = windowed;
+
+ ComPtr<IDXGISwapChain1> swapChain;
+ if (m_d3dFeatureLevel >= D3D_FEATURE_LEVEL_11_0 && !bHWStereoEnabled &&
+ (isHdrEnabled || use10bit))
+ {
+ swapChainDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
+ hr = CreateSwapChain(swapChainDesc, scFSDesc, &swapChain);
+ if (FAILED(hr))
+ {
+ CLog::LogF(LOGWARNING, "creating 10bit swapchain failed, fallback to 8bit.");
+ swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ }
+ }
+
+ if (!swapChain)
+ hr = CreateSwapChain(swapChainDesc, scFSDesc, &swapChain);
+
+ if (FAILED(hr) && bHWStereoEnabled)
+ {
+ // switch to stereo mode failed, create mono swapchain
+ CLog::LogF(LOGERROR, "creating stereo swap chain failed with error.");
+ CLog::LogF(LOGINFO, "fallback to monoscopic mode.");
+
+ swapChainDesc.Stereo = false;
+ bHWStereoEnabled = false;
+
+ hr = CreateSwapChain(swapChainDesc, scFSDesc, &swapChain); CHECK_ERR();
+
+ // fallback to split_horizontal mode.
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoMode(
+ RENDER_STEREO_MODE_SPLIT_HORIZONTAL);
+ }
+
+ if (FAILED(hr))
+ {
+ CLog::LogF(LOGERROR, "unable to create swapchain.");
+ return;
+ }
+
+ m_IsHDROutput = (swapChainDesc.Format == DXGI_FORMAT_R10G10B10A2_UNORM) && isHdrEnabled;
+
+ const int bits = (swapChainDesc.Format == DXGI_FORMAT_R10G10B10A2_UNORM) ? 10 : 8;
+ std::string flip =
+ (swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) ? "discard" : "sequential";
+
+ CLog::LogF(LOGINFO, "{} bit swapchain is used with {} flip {} buffers and {} output", bits,
+ swapChainDesc.BufferCount, flip, m_IsHDROutput ? "HDR" : "SDR");
+
+ hr = swapChain.As(&m_swapChain); CHECK_ERR();
+ m_stereoEnabled = bHWStereoEnabled;
+
+ // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
+ // ensures that the application will only render after each VSync, minimizing power consumption.
+ ComPtr<IDXGIDevice1> dxgiDevice;
+ hr = m_d3dDevice.As(&dxgiDevice); CHECK_ERR();
+ dxgiDevice->SetMaximumFrameLatency(1);
+ }
+
+ CLog::LogF(LOGDEBUG, "end resize buffers.");
+}
+
+// These resources need to be recreated every time the window size is changed.
+void DX::DeviceResources::CreateWindowSizeDependentResources()
+{
+ ReleaseBackBuffer();
+
+ DestroySwapChain();
+
+ if (!m_dxgiFactory->IsCurrent()) // HDR toggling requires re-create factory
+ CreateFactory();
+
+ UpdateRenderTargetSize();
+ ResizeBuffers();
+
+ CreateBackBuffer();
+}
+
+// Determine the dimensions of the render target and whether it will be scaled down.
+void DX::DeviceResources::UpdateRenderTargetSize()
+{
+ m_effectiveDpi = m_dpi;
+
+ // To improve battery life on high resolution devices, render to a smaller render target
+ // and allow the GPU to scale the output when it is presented.
+ if (!DisplayMetrics::SupportHighResolutions && m_dpi > DisplayMetrics::DpiThreshold)
+ {
+ float width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_dpi);
+ float height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_dpi);
+
+ // When the device is in portrait orientation, height > width. Compare the
+ // larger dimension against the width threshold and the smaller dimension
+ // against the height threshold.
+ if (std::max(width, height) > DisplayMetrics::WidthThreshold && std::min(width, height) > DisplayMetrics::HeightThreshold)
+ {
+ // To scale the app we change the effective DPI. Logical size does not change.
+ m_effectiveDpi /= 2.0f;
+ }
+ }
+
+ // Calculate the necessary render target size in pixels.
+ m_outputSize.Width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_effectiveDpi);
+ m_outputSize.Height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_effectiveDpi);
+
+ // Prevent zero size DirectX content from being created.
+ m_outputSize.Width = std::max(m_outputSize.Width, 1.f);
+ m_outputSize.Height = std::max(m_outputSize.Height, 1.f);
+}
+
+void DX::DeviceResources::Register(ID3DResource* resource)
+{
+ critical_section::scoped_lock lock(m_resourceSection);
+ m_resources.push_back(resource);
+}
+
+void DX::DeviceResources::Unregister(ID3DResource* resource)
+{
+ critical_section::scoped_lock lock(m_resourceSection);
+ std::vector<ID3DResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
+ if (i != m_resources.end())
+ m_resources.erase(i);
+}
+
+void DX::DeviceResources::FinishCommandList(bool bExecute) const
+{
+ if (m_d3dContext == m_deferrContext)
+ return;
+
+ ComPtr<ID3D11CommandList> pCommandList;
+ if (FAILED(m_deferrContext->FinishCommandList(true, &pCommandList)))
+ {
+ CLog::LogF(LOGERROR, "failed to finish command queue.");
+ return;
+ }
+
+ if (bExecute)
+ m_d3dContext->ExecuteCommandList(pCommandList.Get(), false);
+}
+
+// This method is called in the event handler for the SizeChanged event.
+void DX::DeviceResources::SetLogicalSize(float width, float height)
+{
+ if
+#if defined(TARGET_WINDOWS_DESKTOP)
+ (!m_window)
+#else
+ (!m_coreWindow)
+#endif
+ return;
+
+ CLog::LogF(LOGDEBUG, "receive changing logical size to {:f} x {:f}", width, height);
+
+ if (m_logicalSize.Width != width || m_logicalSize.Height != height)
+ {
+ CLog::LogF(LOGDEBUG, "change logical size to {:f} x {:f}", width, height);
+
+ m_logicalSize = winrt::Size(width, height);
+
+ UpdateRenderTargetSize();
+ ResizeBuffers();
+ }
+}
+
+// This method is called in the event handler for the DpiChanged event.
+void DX::DeviceResources::SetDpi(float dpi)
+{
+ dpi = std::max(dpi, DisplayMetrics::Dpi100);
+ if (dpi != m_dpi)
+ m_dpi = dpi;
+}
+
+// This method is called in the event handler for the DisplayContentsInvalidated event.
+void DX::DeviceResources::ValidateDevice()
+{
+ // The D3D Device is no longer valid if the default adapter changed since the device
+ // was created or if the device has been removed.
+
+ // First, get the information for the default adapter from when the device was created.
+ ComPtr<IDXGIDevice1> dxgiDevice;
+ m_d3dDevice.As(&dxgiDevice);
+
+ ComPtr<IDXGIAdapter> deviceAdapter;
+ dxgiDevice->GetAdapter(&deviceAdapter);
+
+ ComPtr<IDXGIFactory2> dxgiFactory;
+ deviceAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory));
+
+ DXGI_ADAPTER_DESC1 previousDesc;
+ {
+ ComPtr<IDXGIAdapter1> previousDefaultAdapter;
+ dxgiFactory->EnumAdapters1(0, &previousDefaultAdapter);
+
+ previousDefaultAdapter->GetDesc1(&previousDesc);
+ }
+
+ // Next, get the information for the current default adapter.
+ DXGI_ADAPTER_DESC1 currentDesc;
+ {
+ ComPtr<IDXGIFactory1> currentFactory;
+ CreateDXGIFactory1(IID_PPV_ARGS(&currentFactory));
+
+ ComPtr<IDXGIAdapter1> currentDefaultAdapter;
+ currentFactory->EnumAdapters1(0, &currentDefaultAdapter);
+
+ currentDefaultAdapter->GetDesc1(&currentDesc);
+ }
+ // If the adapter LUIDs don't match, or if the device reports that it has been removed,
+ // a new D3D device must be created.
+ HRESULT hr = m_d3dDevice->GetDeviceRemovedReason();
+ if ( previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart
+ || previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart
+ || FAILED(hr))
+ {
+ // Release references to resources related to the old device.
+ dxgiDevice = nullptr;
+ deviceAdapter = nullptr;
+ dxgiFactory = nullptr;
+
+ // Create a new device and swap chain.
+ HandleDeviceLost(hr == DXGI_ERROR_DEVICE_REMOVED);
+ }
+}
+
+void DX::DeviceResources::OnDeviceLost(bool removed)
+{
+ auto pGUI = CServiceBroker::GetGUI();
+ if (pGUI)
+ pGUI->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_LOST);
+
+ // tell any shared resources
+ for (auto res : m_resources)
+ {
+ // the most of resources like textures and buffers try to
+ // receive and save their status from current device.
+ // `removed` means that we have no possibility
+ // to use the device anymore, tell all resources about this.
+ res->OnDestroyDevice(removed);
+ }
+}
+
+void DX::DeviceResources::OnDeviceRestored()
+{
+ // tell any shared resources
+ for (auto res : m_resources)
+ res->OnCreateDevice();
+
+ auto pGUI = CServiceBroker::GetGUI();
+ if (pGUI)
+ pGUI->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_RESET);
+}
+
+// Recreate all device resources and set them back to the current state.
+void DX::DeviceResources::HandleDeviceLost(bool removed)
+{
+ bool backbuferExists = m_backBufferTex.Get() != nullptr;
+
+ OnDeviceLost(removed);
+ if (m_deviceNotify != nullptr)
+ m_deviceNotify->OnDXDeviceLost();
+
+ if (backbuferExists)
+ ReleaseBackBuffer();
+
+ DestroySwapChain();
+
+ CreateDeviceResources();
+ UpdateRenderTargetSize();
+ ResizeBuffers();
+
+ if (backbuferExists)
+ CreateBackBuffer();
+
+ if (m_deviceNotify != nullptr)
+ m_deviceNotify->OnDXDeviceRestored();
+ OnDeviceRestored();
+
+ if (removed)
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
+ "ReloadSkin");
+}
+
+bool DX::DeviceResources::Begin()
+{
+ HRESULT hr = m_swapChain->Present(0, DXGI_PRESENT_TEST);
+
+ // If the device was removed either by a disconnection or a driver upgrade, we
+ // must recreate all device resources.
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
+ {
+ HandleDeviceLost(hr == DXGI_ERROR_DEVICE_REMOVED);
+ }
+ else
+ {
+ // not fatal errors
+ if (hr == DXGI_ERROR_INVALID_CALL)
+ {
+ CreateWindowSizeDependentResources();
+ }
+ }
+
+ m_deferrContext->OMSetRenderTargets(1, m_backBufferTex.GetAddressOfRTV(), m_d3dDepthStencilView.Get());
+
+ return true;
+}
+
+// Present the contents of the swap chain to the screen.
+void DX::DeviceResources::Present()
+{
+ FinishCommandList();
+
+ // The first argument instructs DXGI to block until VSync, putting the application
+ // to sleep until the next VSync. This ensures we don't waste any cycles rendering
+ // frames that will never be displayed to the screen.
+ DXGI_PRESENT_PARAMETERS parameters = {};
+ HRESULT hr = m_swapChain->Present1(1, 0, &parameters);
+
+ // If the device was removed either by a disconnection or a driver upgrade, we
+ // must recreate all device resources.
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
+ {
+ HandleDeviceLost(hr == DXGI_ERROR_DEVICE_REMOVED);
+ }
+ else
+ {
+ // not fatal errors
+ if (hr == DXGI_ERROR_INVALID_CALL)
+ {
+ CreateWindowSizeDependentResources();
+ }
+ }
+
+ if (m_d3dContext == m_deferrContext)
+ {
+ m_deferrContext->OMSetRenderTargets(1, m_backBufferTex.GetAddressOfRTV(), m_d3dDepthStencilView.Get());
+ }
+}
+
+void DX::DeviceResources::ClearDepthStencil() const
+{
+ m_deferrContext->ClearDepthStencilView(m_d3dDepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0, 0);
+}
+
+void DX::DeviceResources::ClearRenderTarget(ID3D11RenderTargetView* pRTView, float color[4]) const
+{
+ m_deferrContext->ClearRenderTargetView(pRTView, color);
+}
+
+void DX::DeviceResources::HandleOutputChange(const std::function<bool(DXGI_OUTPUT_DESC)>& cmpFunc)
+{
+ DXGI_ADAPTER_DESC currentDesc = {};
+ DXGI_ADAPTER_DESC foundDesc = {};
+
+ ComPtr<IDXGIFactory1> factory;
+ if (m_adapter)
+ m_adapter->GetDesc(&currentDesc);
+
+ CreateDXGIFactory1(IID_IDXGIFactory1, &factory);
+
+ ComPtr<IDXGIAdapter1> adapter;
+ for (int i = 0; factory->EnumAdapters1(i, adapter.ReleaseAndGetAddressOf()) != DXGI_ERROR_NOT_FOUND; i++)
+ {
+ adapter->GetDesc(&foundDesc);
+ ComPtr<IDXGIOutput> output;
+ for (int j = 0; adapter->EnumOutputs(j, output.ReleaseAndGetAddressOf()) != DXGI_ERROR_NOT_FOUND; j++)
+ {
+ DXGI_OUTPUT_DESC outputDesc;
+ output->GetDesc(&outputDesc);
+ if (cmpFunc(outputDesc))
+ {
+ output.As(&m_output);
+ // check if adapter is changed
+ if (currentDesc.AdapterLuid.HighPart != foundDesc.AdapterLuid.HighPart
+ || currentDesc.AdapterLuid.LowPart != foundDesc.AdapterLuid.LowPart)
+ {
+ // adapter is changed
+ m_adapter = adapter;
+ CLog::LogF(LOGDEBUG, "selected {} adapter. ",
+ KODI::PLATFORM::WINDOWS::FromW(foundDesc.Description));
+ // (re)init hooks into new driver
+ Windowing()->InitHooks(output.Get());
+ // recreate d3d11 device on new adapter
+ if (m_d3dDevice)
+ HandleDeviceLost(false);
+ }
+ return;
+ }
+ }
+ }
+}
+
+bool DX::DeviceResources::CreateFactory()
+{
+ HRESULT hr;
+#if defined(_DEBUG) && defined(TARGET_WINDOWS_STORE)
+ bool debugDXGI = false;
+ {
+ ComPtr<IDXGIInfoQueue> dxgiInfoQueue;
+ if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(dxgiInfoQueue.GetAddressOf()))))
+ {
+ debugDXGI = true;
+
+ hr = CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, IID_PPV_ARGS(m_dxgiFactory.ReleaseAndGetAddressOf())); RETURN_ERR(false);
+
+ dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true);
+ dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true);
+ }
+ }
+
+ if (!debugDXGI)
+#endif
+ hr = CreateDXGIFactory1(IID_PPV_ARGS(m_dxgiFactory.ReleaseAndGetAddressOf())); RETURN_ERR(false);
+
+ return true;
+}
+
+void DX::DeviceResources::SetMonitor(HMONITOR monitor)
+{
+ HandleOutputChange([monitor](DXGI_OUTPUT_DESC outputDesc) {
+ return outputDesc.Monitor == monitor;
+ });
+}
+
+void DX::DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify)
+{
+ m_deviceNotify = deviceNotify;
+}
+
+HMONITOR DX::DeviceResources::GetMonitor() const
+{
+ if (m_swapChain)
+ {
+ ComPtr<IDXGIOutput> output;
+ GetOutput(output.GetAddressOf());
+ if (output)
+ {
+ DXGI_OUTPUT_DESC desc;
+ output->GetDesc(&desc);
+ return desc.Monitor;
+ }
+ }
+ return nullptr;
+}
+
+bool DX::DeviceResources::IsStereoAvailable() const
+{
+ if (m_dxgiFactory)
+ return m_dxgiFactory->IsWindowedStereoEnabled();
+
+ return false;
+}
+
+void DX::DeviceResources::CheckNV12SharedTexturesSupport()
+{
+ if (m_d3dFeatureLevel < D3D_FEATURE_LEVEL_10_0 ||
+ CSysInfo::GetWindowsDeviceFamily() != CSysInfo::Desktop)
+ return;
+
+ D3D11_FEATURE_DATA_D3D11_OPTIONS4 op4 = {};
+ HRESULT hr = m_d3dDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS4, &op4, sizeof(op4));
+ m_NV12SharedTexturesSupport = SUCCEEDED(hr) && !!op4.ExtendedNV12SharedTextureSupported;
+ CLog::LogF(LOGINFO, "extended NV12 shared textures is{}supported",
+ m_NV12SharedTexturesSupport ? " " : " NOT ");
+}
+
+void DX::DeviceResources::CheckDXVA2SharedDecoderSurfaces()
+{
+ if (CSysInfo::GetWindowsDeviceFamily() != CSysInfo::Desktop)
+ return;
+
+ VideoDriverInfo driver = GetVideoDriverVersion();
+
+ if (!m_NV12SharedTexturesSupport)
+ return;
+
+ DXGI_ADAPTER_DESC ad = {};
+ GetAdapterDesc(&ad);
+
+ m_DXVA2SharedDecoderSurfaces =
+ ad.VendorId == PCIV_Intel ||
+ (ad.VendorId == PCIV_NVIDIA && driver.valid && driver.majorVersion >= 465) ||
+ (ad.VendorId == PCIV_AMD && driver.valid && driver.majorVersion >= 30 &&
+ m_d3dFeatureLevel >= D3D_FEATURE_LEVEL_12_1);
+
+ CLog::LogF(LOGINFO, "DXVA2 shared decoder surfaces is{}supported",
+ m_DXVA2SharedDecoderSurfaces ? " " : " NOT ");
+}
+
+VideoDriverInfo DX::DeviceResources::GetVideoDriverVersion()
+{
+ DXGI_ADAPTER_DESC ad = {};
+ GetAdapterDesc(&ad);
+
+ VideoDriverInfo driver = CWIN32Util::GetVideoDriverInfo(ad.VendorId, ad.Description);
+
+ if (ad.VendorId == PCIV_NVIDIA)
+ CLog::LogF(LOGINFO, "video driver version is {} {}.{} ({})", GetGFXProviderName(ad.VendorId),
+ driver.majorVersion, driver.minorVersion, driver.version);
+ else
+ CLog::LogF(LOGINFO, "video driver version is {} {}", GetGFXProviderName(ad.VendorId),
+ driver.version);
+
+ return driver;
+}
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+// This method is called when the window (WND) is created (or re-created).
+void DX::DeviceResources::SetWindow(HWND window)
+{
+ m_window = window;
+
+ CreateDeviceIndependentResources();
+ CreateDeviceResources();
+}
+#elif defined(TARGET_WINDOWS_STORE)
+// This method is called when the CoreWindow is created (or re-created).
+void DX::DeviceResources::SetWindow(const winrt::Windows::UI::Core::CoreWindow& window)
+{
+ using namespace winrt::Windows::UI::Core;
+ using namespace winrt::Windows::Graphics::Display;
+
+ m_coreWindow = window;
+ auto dispatcher = m_coreWindow.Dispatcher();
+ DispatchedHandler handler([&]()
+ {
+ auto coreWindow = CoreWindow::GetForCurrentThread();
+ m_logicalSize = winrt::Size(coreWindow.Bounds().Width, coreWindow.Bounds().Height);
+ m_dpi = DisplayInformation::GetForCurrentView().LogicalDpi();
+ SetWindowPos(coreWindow.Bounds());
+ });
+ if (dispatcher.HasThreadAccess())
+ handler();
+ else
+ dispatcher.RunAsync(CoreDispatcherPriority::High, handler).get();
+
+ CreateDeviceIndependentResources();
+ CreateDeviceResources();
+ // we have to call this because we will not get initial WM_SIZE
+ CreateWindowSizeDependentResources();
+}
+
+void DX::DeviceResources::SetWindowPos(winrt::Rect rect)
+{
+ int centerX = rect.X + rect.Width / 2;
+ int centerY = rect.Y + rect.Height / 2;
+
+ HandleOutputChange([centerX, centerY](DXGI_OUTPUT_DESC outputDesc) {
+ // DesktopCoordinates depends on the DPI of the desktop
+ return outputDesc.DesktopCoordinates.left <= centerX && outputDesc.DesktopCoordinates.right >= centerX
+ && outputDesc.DesktopCoordinates.top <= centerY && outputDesc.DesktopCoordinates.bottom >= centerY;
+ });
+}
+
+// Call this method when the app suspends. It provides a hint to the driver that the app
+// is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
+void DX::DeviceResources::Trim() const
+{
+ ComPtr<IDXGIDevice3> dxgiDevice;
+ m_d3dDevice.As(&dxgiDevice);
+
+ dxgiDevice->Trim();
+}
+
+#endif
+
+void DX::DeviceResources::SetHdrMetaData(DXGI_HDR_METADATA_HDR10& hdr10) const
+{
+ ComPtr<IDXGISwapChain4> swapChain4;
+
+ if (!m_swapChain)
+ return;
+
+ if (SUCCEEDED(m_swapChain.As(&swapChain4)))
+ {
+ if (SUCCEEDED(swapChain4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(hdr10), &hdr10)))
+ {
+ CLog::LogF(LOGDEBUG,
+ "(raw) RP {} {} | GP {} {} | BP {} {} | WP {} {} | Max ML {} | min ML "
+ "{} | Max CLL {} | Max FALL {}",
+ hdr10.RedPrimary[0], hdr10.RedPrimary[1], hdr10.GreenPrimary[0],
+ hdr10.GreenPrimary[1], hdr10.BluePrimary[0], hdr10.BluePrimary[1],
+ hdr10.WhitePoint[0], hdr10.WhitePoint[1], hdr10.MaxMasteringLuminance,
+ hdr10.MinMasteringLuminance, hdr10.MaxContentLightLevel,
+ hdr10.MaxFrameAverageLightLevel);
+
+ constexpr double FACTOR_1 = 50000.0;
+ constexpr double FACTOR_2 = 10000.0;
+ const double RP_0 = static_cast<double>(hdr10.RedPrimary[0]) / FACTOR_1;
+ const double RP_1 = static_cast<double>(hdr10.RedPrimary[1]) / FACTOR_1;
+ const double GP_0 = static_cast<double>(hdr10.GreenPrimary[0]) / FACTOR_1;
+ const double GP_1 = static_cast<double>(hdr10.GreenPrimary[1]) / FACTOR_1;
+ const double BP_0 = static_cast<double>(hdr10.BluePrimary[0]) / FACTOR_1;
+ const double BP_1 = static_cast<double>(hdr10.BluePrimary[1]) / FACTOR_1;
+ const double WP_0 = static_cast<double>(hdr10.WhitePoint[0]) / FACTOR_1;
+ const double WP_1 = static_cast<double>(hdr10.WhitePoint[1]) / FACTOR_1;
+ const double Max_ML = static_cast<double>(hdr10.MaxMasteringLuminance) / FACTOR_2;
+ const double min_ML = static_cast<double>(hdr10.MinMasteringLuminance) / FACTOR_2;
+
+ CLog::LogF(LOGINFO,
+ "RP {:.3f} {:.3f} | GP {:.3f} {:.3f} | BP {:.3f} {:.3f} | WP {:.3f} "
+ "{:.3f} | Max ML {:.0f} | min ML {:.4f} | Max CLL {} | Max FALL {}",
+ RP_0, RP_1, GP_0, GP_1, BP_0, BP_1, WP_0, WP_1, Max_ML, min_ML,
+ hdr10.MaxContentLightLevel, hdr10.MaxFrameAverageLightLevel);
+ }
+ else
+ {
+ CLog::LogF(LOGERROR, "DXGI SetHDRMetaData failed");
+ }
+ }
+}
+
+void DX::DeviceResources::SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace)
+{
+ ComPtr<IDXGISwapChain3> swapChain3;
+
+ if (!m_swapChain)
+ return;
+
+ if (SUCCEEDED(m_swapChain.As(&swapChain3)))
+ {
+ if (SUCCEEDED(swapChain3->SetColorSpace1(colorSpace)))
+ {
+ m_IsTransferPQ = (colorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
+
+ CLog::LogF(LOGDEBUG, "DXGI SetColorSpace1 success");
+ }
+ else
+ {
+ CLog::LogF(LOGERROR, "DXGI SetColorSpace1 failed");
+ }
+ }
+}
+
+HDR_STATUS DX::DeviceResources::ToggleHDR()
+{
+ DXGI_MODE_DESC md = {};
+ GetDisplayMode(&md);
+
+ DX::Windowing()->SetTogglingHDR(true);
+ DX::Windowing()->SetAlteringWindow(true);
+
+ // Toggle display HDR
+ HDR_STATUS hdrStatus = CWIN32Util::ToggleWindowsHDR(md);
+
+ // Kill swapchain
+ if (m_swapChain && hdrStatus != HDR_STATUS::HDR_TOGGLE_FAILED)
+ {
+ CLog::LogF(LOGDEBUG, "Re-create swapchain due HDR <-> SDR switch");
+ DestroySwapChain();
+ }
+
+ DX::Windowing()->SetAlteringWindow(false);
+
+ // Re-create swapchain
+ if (hdrStatus != HDR_STATUS::HDR_TOGGLE_FAILED)
+ {
+ CreateWindowSizeDependentResources();
+
+ DX::Windowing()->NotifyAppFocusChange(true);
+ }
+
+ return hdrStatus;
+}
+
+void DX::DeviceResources::ApplyDisplaySettings()
+{
+ CLog::LogF(LOGDEBUG, "Re-create swapchain due Display Settings changed");
+
+ DestroySwapChain();
+ CreateWindowSizeDependentResources();
+}
+
+DEBUG_INFO_RENDER DX::DeviceResources::GetDebugInfo() const
+{
+ if (!m_swapChain)
+ return {};
+
+ DXGI_SWAP_CHAIN_DESC1 desc = {};
+ m_swapChain->GetDesc1(&desc);
+
+ DXGI_MODE_DESC md = {};
+ GetDisplayMode(&md);
+
+ const int bits = (desc.Format == DXGI_FORMAT_R10G10B10A2_UNORM) ? 10 : 8;
+ const int max = (desc.Format == DXGI_FORMAT_R10G10B10A2_UNORM) ? 1024 : 256;
+ const int range_min = DX::Windowing()->UseLimitedColor() ? (max * 16) / 256 : 0;
+ const int range_max = DX::Windowing()->UseLimitedColor() ? (max * 235) / 256 : max - 1;
+
+ DEBUG_INFO_RENDER info;
+
+ info.renderFlags = StringUtils::Format(
+ "Swapchain: {} buffers, flip {}, {}, EOTF: {} (Windows HDR {})", desc.BufferCount,
+ (desc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) ? "discard" : "sequential",
+ Windowing()->IsFullScreen()
+ ? ((desc.Flags == DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) ? "fullscreen exclusive"
+ : "fullscreen windowed")
+ : "windowed screen",
+ m_IsTransferPQ ? "PQ" : "SDR", m_IsHDROutput ? "on" : "off");
+
+ info.videoOutput = StringUtils::Format(
+ "Surfaces: {}x{}{} @ {:.3f} Hz, pixel: {} {}-bit, range: {} ({}-{})", desc.Width, desc.Height,
+ (md.ScanlineOrdering > DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE) ? "i" : "p",
+ static_cast<double>(md.RefreshRate.Numerator) /
+ static_cast<double>(md.RefreshRate.Denominator),
+ (desc.Format == DXGI_FORMAT_R10G10B10A2_UNORM) ? "R10G10B10A2" : "B8G8R8A8", bits,
+ DX::Windowing()->UseLimitedColor() ? "limited" : "full", range_min, range_max);
+
+ return info;
+}
diff --git a/xbmc/rendering/dx/DeviceResources.h b/xbmc/rendering/dx/DeviceResources.h
new file mode 100644
index 0000000..d458902
--- /dev/null
+++ b/xbmc/rendering/dx/DeviceResources.h
@@ -0,0 +1,187 @@
+/*
+ * 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 "DirectXHelper.h"
+#include "HDRStatus.h"
+#include "guilib/D3DResource.h"
+
+#include <functional>
+#include <memory>
+
+#include <concrt.h>
+#include <dxgi1_5.h>
+#include <wrl.h>
+#include <wrl/client.h>
+
+struct RESOLUTION_INFO;
+struct DEBUG_INFO_RENDER;
+struct VideoDriverInfo;
+
+namespace DX
+{
+ interface IDeviceNotify
+ {
+ virtual void OnDXDeviceLost() = 0;
+ virtual void OnDXDeviceRestored() = 0;
+ };
+
+ // Controls all the DirectX device resources.
+ class DeviceResources
+ {
+ public:
+ static std::shared_ptr<DX::DeviceResources> Get();
+
+ DeviceResources();
+ virtual ~DeviceResources();
+ void Release();
+
+ void ValidateDevice();
+ void HandleDeviceLost(bool removed);
+ bool Begin();
+ void Present();
+
+ // The size of the render target, in pixels.
+ winrt::Windows::Foundation::Size GetOutputSize() const { return m_outputSize; }
+ // The size of the render target, in dips.
+ winrt::Windows::Foundation::Size GetLogicalSize() const { return m_logicalSize; }
+ void SetLogicalSize(float width, float height);
+ float GetDpi() const { return m_effectiveDpi; }
+ void SetDpi(float dpi);
+
+ // D3D Accessors.
+ bool HasValidDevice() const { return m_bDeviceCreated; }
+ ID3D11Device1* GetD3DDevice() const { return m_d3dDevice.Get(); }
+ ID3D11DeviceContext1* GetD3DContext() const { return m_deferrContext.Get(); }
+ ID3D11DeviceContext1* GetImmediateContext() const { return m_d3dContext.Get(); }
+ IDXGISwapChain1* GetSwapChain() const { return m_swapChain.Get(); }
+ IDXGIFactory2* GetIDXGIFactory2() const { return m_dxgiFactory.Get(); }
+ IDXGIAdapter1* GetAdapter() const { return m_adapter.Get(); }
+ ID3D11DepthStencilView* GetDSV() const { return m_d3dDepthStencilView.Get(); }
+ D3D_FEATURE_LEVEL GetDeviceFeatureLevel() const { return m_d3dFeatureLevel; }
+ CD3DTexture& GetBackBuffer() { return m_backBufferTex; }
+
+ void GetOutput(IDXGIOutput** ppOutput) const;
+ void GetAdapterDesc(DXGI_ADAPTER_DESC *desc) const;
+ void GetDisplayMode(DXGI_MODE_DESC *mode) const;
+
+ D3D11_VIEWPORT GetScreenViewport() const { return m_screenViewport; }
+ void SetViewPort(D3D11_VIEWPORT& viewPort) const;
+
+ void ReleaseBackBuffer();
+ void CreateBackBuffer();
+ void ResizeBuffers();
+
+ bool SetFullScreen(bool fullscreen, RESOLUTION_INFO& res);
+
+ // Apply display settings changes
+ void ApplyDisplaySettings();
+
+ // HDR display support
+ HDR_STATUS ToggleHDR();
+ void SetHdrMetaData(DXGI_HDR_METADATA_HDR10& hdr10) const;
+ void SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace);
+ bool IsHDROutput() const { return m_IsHDROutput; }
+ bool IsTransferPQ() const { return m_IsTransferPQ; }
+
+ // DX resources registration
+ void Register(ID3DResource *resource);
+ void Unregister(ID3DResource *resource);
+
+ void FinishCommandList(bool bExecute = true) const;
+ void ClearDepthStencil() const;
+ void ClearRenderTarget(ID3D11RenderTargetView* pRTView, float color[4]) const;
+ void RegisterDeviceNotify(IDeviceNotify* deviceNotify);
+
+ bool IsStereoAvailable() const;
+ bool IsStereoEnabled() const { return m_stereoEnabled; }
+ void SetStereoIdx(byte idx) { m_backBufferTex.SetViewIdx(idx); }
+
+ void SetMonitor(HMONITOR monitor);
+ HMONITOR GetMonitor() const;
+#if defined(TARGET_WINDOWS_DESKTOP)
+ void SetWindow(HWND window);
+#elif defined(TARGET_WINDOWS_STORE)
+ void Trim() const;
+ void SetWindow(const winrt::Windows::UI::Core::CoreWindow& window);
+ void SetWindowPos(winrt::Windows::Foundation::Rect rect);
+#endif // TARGET_WINDOWS_STORE
+ bool IsNV12SharedTexturesSupported() const { return m_NV12SharedTexturesSupport; }
+ bool IsDXVA2SharedDecoderSurfaces() const { return m_DXVA2SharedDecoderSurfaces; }
+
+ // Gets debug info from swapchain
+ DEBUG_INFO_RENDER GetDebugInfo() const;
+
+ private:
+ class CBackBuffer : public CD3DTexture
+ {
+ public:
+ CBackBuffer() : CD3DTexture() {}
+ void SetViewIdx(unsigned idx) { m_viewIdx = idx; }
+ bool Acquire(ID3D11Texture2D* pTexture);
+ };
+
+ HRESULT CreateSwapChain(DXGI_SWAP_CHAIN_DESC1 &desc, DXGI_SWAP_CHAIN_FULLSCREEN_DESC &fsDesc, IDXGISwapChain1 **ppSwapChain) const;
+ void DestroySwapChain();
+ void CreateDeviceIndependentResources();
+ void CreateDeviceResources();
+ void CreateWindowSizeDependentResources();
+ void UpdateRenderTargetSize();
+ void OnDeviceLost(bool removed);
+ void OnDeviceRestored();
+ void HandleOutputChange(const std::function<bool(DXGI_OUTPUT_DESC)>& cmpFunc);
+ bool CreateFactory();
+ void CheckNV12SharedTexturesSupport();
+ VideoDriverInfo GetVideoDriverVersion();
+ void CheckDXVA2SharedDecoderSurfaces();
+
+ HWND m_window{ nullptr };
+#if defined(TARGET_WINDOWS_STORE)
+ winrt::Windows::UI::Core::CoreWindow m_coreWindow = nullptr;
+#endif
+ Microsoft::WRL::ComPtr<IDXGIFactory2> m_dxgiFactory;
+ Microsoft::WRL::ComPtr<IDXGIAdapter1> m_adapter;
+ Microsoft::WRL::ComPtr<IDXGIOutput1> m_output;
+
+ Microsoft::WRL::ComPtr<ID3D11Device1> m_d3dDevice;
+ Microsoft::WRL::ComPtr<ID3D11DeviceContext1> m_d3dContext;
+ Microsoft::WRL::ComPtr<ID3D11DeviceContext1> m_deferrContext;
+ Microsoft::WRL::ComPtr<IDXGISwapChain1> m_swapChain;
+#ifdef _DEBUG
+ Microsoft::WRL::ComPtr<ID3D11Debug> m_d3dDebug;
+#endif
+
+ CBackBuffer m_backBufferTex;
+ Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_d3dDepthStencilView;
+ D3D11_VIEWPORT m_screenViewport;
+
+ // Cached device properties.
+ D3D_FEATURE_LEVEL m_d3dFeatureLevel;
+ winrt::Windows::Foundation::Size m_outputSize;
+ winrt::Windows::Foundation::Size m_logicalSize;
+ float m_dpi;
+
+ // This is the DPI that will be reported back to the app. It takes into account whether the app supports high resolution screens or not.
+ float m_effectiveDpi;
+ // The IDeviceNotify can be held directly as it owns the DeviceResources.
+ IDeviceNotify* m_deviceNotify;
+
+ // scritical section
+ Concurrency::critical_section m_criticalSection;
+ Concurrency::critical_section m_resourceSection;
+ std::vector<ID3DResource*> m_resources;
+
+ bool m_stereoEnabled;
+ bool m_bDeviceCreated;
+ bool m_IsHDROutput;
+ bool m_IsTransferPQ;
+ bool m_NV12SharedTexturesSupport{false};
+ bool m_DXVA2SharedDecoderSurfaces{false};
+ };
+}
diff --git a/xbmc/rendering/dx/DirectXHelper.h b/xbmc/rendering/dx/DirectXHelper.h
new file mode 100644
index 0000000..bb51ca6
--- /dev/null
+++ b/xbmc/rendering/dx/DirectXHelper.h
@@ -0,0 +1,198 @@
+/*
+ * 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 "commons/Exception.h"
+#include "dxerr.h"
+
+#include "platform/win32/CharsetConverter.h"
+
+#include <d3d11_4.h>
+#include <ppltasks.h> // For create_task
+
+enum PCI_Vendors
+{
+ PCIV_AMD = 0x1002,
+ PCIV_NVIDIA = 0x10DE,
+ PCIV_Intel = 0x8086,
+};
+
+namespace DX
+{
+#define RATIONAL_TO_FLOAT(rational) ((rational.Denominator != 0) ? \
+ static_cast<float>(rational.Numerator) / static_cast<float>(rational.Denominator) : 0.0f)
+
+ namespace DisplayMetrics
+ {
+ // High resolution displays can require a lot of GPU and battery power to render.
+ // High resolution phones, for example, may suffer from poor battery life if
+ // games attempt to render at 60 frames per second at full fidelity.
+ // The decision to render at full fidelity across all platforms and form factors
+ // should be deliberate.
+ static const bool SupportHighResolutions = true;
+
+ // The default thresholds that define a "high resolution" display. If the thresholds
+ // are exceeded and SupportHighResolutions is false, the dimensions will be scaled
+ // by 50%.
+ static const float Dpi100 = 96.0f; // 100% of standard desktop display.
+ static const float DpiThreshold = 192.0f; // 200% of standard desktop display.
+ static const float WidthThreshold = 1920.0f; // 1080p width.
+ static const float HeightThreshold = 1080.0f; // 1080p height.
+ };
+
+ inline void BreakIfFailed(HRESULT hr)
+ {
+ if (FAILED(hr))
+ {
+ // Set a breakpoint on this line to catch Win32 API errors.
+#if _DEBUG && !defined(TARGET_WINDOWS_STORE)
+ DebugBreak();
+#endif
+ throw new XbmcCommons::UncheckedException(__FUNCTION__, "Unhandled error");
+ }
+ }
+
+ // Converts a length in device-independent pixels (DIPs) to a length in physical pixels.
+ inline float ConvertDipsToPixels(float dips, float dpi)
+ {
+ static const float dipsPerInch = DisplayMetrics::Dpi100;
+ return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer.
+ }
+
+ inline float ConvertPixelsToDips(float pixels, float dpi)
+ {
+ static const float dipsPerInch = DisplayMetrics::Dpi100;
+ return floorf(pixels / (dpi / dipsPerInch) + 0.5f); // Round to nearest integer.
+ }
+
+ inline float RationalToFloat(DXGI_RATIONAL rational)
+ {
+ return RATIONAL_TO_FLOAT(rational);
+ }
+
+ inline void GetRefreshRatio(uint32_t refresh, uint32_t *num, uint32_t *den)
+ {
+ int i = (((refresh + 1) % 24) == 0 || ((refresh + 1) % 30) == 0) ? 1 : 0;
+ *num = (refresh + i) * 1000;
+ *den = 1000 + i;
+ }
+
+ inline std::string GetErrorDescription(HRESULT hr)
+ {
+ using namespace KODI::PLATFORM::WINDOWS;
+
+ WCHAR buff[2048];
+ DXGetErrorDescriptionW(hr, buff, 2048);
+
+ return FromW(StringUtils::Format(L"{:X} - {} ({})", hr, DXGetErrorStringW(hr), buff));
+ }
+
+ inline std::string GetFeatureLevelDescription(D3D_FEATURE_LEVEL featureLevel)
+ {
+ uint32_t fl_major = (featureLevel & 0xF000u) >> 12;
+ uint32_t fl_minor = (featureLevel & 0x0F00u) >> 8;
+
+ return StringUtils::Format("D3D_FEATURE_LEVEL_{}_{}", fl_major, fl_minor);
+ }
+
+ inline std::string GetGFXProviderName(UINT vendorId)
+ {
+ std::string name;
+ switch (vendorId)
+ {
+ case PCIV_AMD:
+ name = "AMD";
+ break;
+ case PCIV_Intel:
+ name = "Intel";
+ break;
+ case PCIV_NVIDIA:
+ name = "NVIDIA";
+ break;
+ }
+
+ return name;
+ }
+
+ template <typename T> struct SizeGen
+ {
+ SizeGen<T>() { Width = Height = 0; }
+ SizeGen<T>(T width, T height) { Width = width; Height = height; }
+
+ bool operator !=(const SizeGen<T> &size) const
+ {
+ return Width != size.Width || Height != size.Height;
+ }
+
+ const SizeGen<T> &operator -=(const SizeGen<T> &size)
+ {
+ Width -= size.Width;
+ Height -= size.Height;
+ return *this;
+ };
+
+ const SizeGen<T> &operator +=(const SizeGen<T> &size)
+ {
+ Width += size.Width;
+ Height += size.Height;
+ return *this;
+ };
+
+ const SizeGen<T> &operator -=(const T &size)
+ {
+ Width -= size;
+ Height -= size;
+ return *this;
+ };
+
+ const SizeGen<T> &operator +=(const T &size)
+ {
+ Width += size;
+ Height += size;
+ return *this;
+ };
+
+ T Width, Height;
+ };
+
+#if defined(_DEBUG)
+ // Check for SDK Layer support.
+ inline bool SdkLayersAvailable()
+ {
+ HRESULT hr = D3D11CreateDevice(
+ nullptr,
+ D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device.
+ nullptr,
+ D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers.
+ nullptr, // Any feature level will do.
+ 0,
+ D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
+ nullptr, // No need to keep the D3D device reference.
+ nullptr, // No need to know the feature level.
+ nullptr // No need to keep the D3D device context reference.
+ );
+
+ return SUCCEEDED(hr);
+ }
+#endif
+}
+
+#ifdef TARGET_WINDOWS_DESKTOP
+namespace winrt
+{
+ namespace Windows
+ {
+ namespace Foundation
+ {
+ typedef DX::SizeGen<float> Size;
+ typedef DX::SizeGen<int> SizeInt;
+ }
+ }
+}
+#endif
diff --git a/xbmc/rendering/dx/RenderContext.h b/xbmc/rendering/dx/RenderContext.h
new file mode 100644
index 0000000..9878691
--- /dev/null
+++ b/xbmc/rendering/dx/RenderContext.h
@@ -0,0 +1,31 @@
+/*
+ * 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
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+#include "windowing/windows/WinSystemWin32DX.h"
+#elif defined(TARGET_WINDOWS_STORE)
+#include "windowing/win10/WinSystemWin10DX.h"
+#endif
+#include "ServiceBroker.h"
+
+namespace DX
+{
+#if defined(TARGET_WINDOWS_DESKTOP)
+ __inline CWinSystemWin32DX* Windowing()
+ {
+ return dynamic_cast<CWinSystemWin32DX*>(CServiceBroker::GetRenderSystem());
+ }
+#elif defined(TARGET_WINDOWS_STORE)
+ __inline CWinSystemWin10DX* Windowing()
+ {
+ return dynamic_cast<CWinSystemWin10DX*>(CServiceBroker::GetRenderSystem());
+ }
+#endif
+}
diff --git a/xbmc/rendering/dx/RenderSystemDX.cpp b/xbmc/rendering/dx/RenderSystemDX.cpp
new file mode 100644
index 0000000..cb1b0d5
--- /dev/null
+++ b/xbmc/rendering/dx/RenderSystemDX.cpp
@@ -0,0 +1,706 @@
+/*
+ * 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 "RenderSystemDX.h"
+
+#include "application/Application.h"
+
+#include <mutex>
+#if defined(TARGET_WINDOWS_DESKTOP)
+#include "cores/RetroPlayer/process/windows/RPProcessInfoWin.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPWinRenderer.h"
+#endif
+#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
+#include "cores/VideoPlayer/DVDCodecs/Video/DXVA.h"
+#if defined(TARGET_WINDOWS_STORE)
+#include "cores/VideoPlayer/Process/windows/ProcessInfoWin10.h"
+#else
+#include "cores/VideoPlayer/Process/windows/ProcessInfoWin.h"
+#endif
+#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
+#include "cores/VideoPlayer/VideoRenderers/WinRenderer.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
+#include "guilib/D3DResource.h"
+#include "guilib/GUIShaderDX.h"
+#include "guilib/GUITextureD3D.h"
+#include "guilib/GUIWindowManager.h"
+#include "utils/MathUtils.h"
+#include "utils/log.h"
+
+#include <DirectXPackedVector.h>
+
+extern "C" {
+#include <libavutil/pixfmt.h>
+}
+
+using namespace KODI;
+using namespace DirectX;
+using namespace DirectX::PackedVector;
+using namespace Microsoft::WRL;
+using namespace std::chrono_literals;
+
+CRenderSystemDX::CRenderSystemDX() : CRenderSystemBase()
+ , m_interlaced(false)
+{
+ m_bVSync = true;
+
+ memset(&m_viewPort, 0, sizeof m_viewPort);
+ memset(&m_scissor, 0, sizeof m_scissor);
+}
+
+CRenderSystemDX::~CRenderSystemDX() = default;
+
+bool CRenderSystemDX::InitRenderSystem()
+{
+ m_bVSync = true;
+
+ // check various device capabilities
+ CheckDeviceCaps();
+
+ if (!CreateStates() || !InitGUIShader())
+ return false;
+
+ m_bRenderCreated = true;
+ m_deviceResources->RegisterDeviceNotify(this);
+
+ // register platform dependent objects
+#if defined(TARGET_WINDOWS_STORE)
+ VIDEOPLAYER::CProcessInfoWin10::Register();
+#else
+ VIDEOPLAYER::CProcessInfoWin::Register();
+#endif
+ CDVDFactoryCodec::ClearHWAccels();
+ DXVA::CDecoder::Register();
+ VIDEOPLAYER::CRendererFactory::ClearRenderer();
+ CWinRenderer::Register();
+#if defined(TARGET_WINDOWS_DESKTOP)
+ RETRO::CRPProcessInfoWin::Register();
+ RETRO::CRPProcessInfoWin::RegisterRendererFactory(new RETRO::CWinRendererFactory);
+#endif
+ m_viewPort = m_deviceResources->GetScreenViewport();
+ RestoreViewPort();
+
+ auto outputSize = m_deviceResources->GetOutputSize();
+ // set camera to center of screen
+ CPoint camPoint = { outputSize.Width * 0.5f, outputSize.Height * 0.5f };
+ SetCameraPosition(camPoint, outputSize.Width, outputSize.Height);
+
+ DXGI_ADAPTER_DESC AIdentifier = {};
+ m_deviceResources->GetAdapterDesc(&AIdentifier);
+ m_RenderRenderer = KODI::PLATFORM::WINDOWS::FromW(AIdentifier.Description);
+ uint32_t version = 0;
+ for (uint32_t decimal = m_deviceResources->GetDeviceFeatureLevel() >> 8, round = 0; decimal > 0; decimal >>= 4, ++round)
+ version += (decimal % 16) * std::pow(10, round);
+ m_RenderVersion = StringUtils::Format("{:.1f}", static_cast<float>(version) / 10.0f);
+
+ CGUITextureD3D::Register();
+
+ return true;
+}
+
+void CRenderSystemDX::OnResize()
+{
+ if (!m_bRenderCreated)
+ return;
+
+ auto outputSize = m_deviceResources->GetOutputSize();
+
+ // set camera to center of screen
+ CPoint camPoint = { outputSize.Width * 0.5f, outputSize.Height * 0.5f };
+ SetCameraPosition(camPoint, outputSize.Width, outputSize.Height);
+
+ CheckInterlacedStereoView();
+}
+
+bool CRenderSystemDX::IsFormatSupport(DXGI_FORMAT format, unsigned int usage) const
+{
+ ComPtr<ID3D11Device1> pD3DDev = m_deviceResources->GetD3DDevice();
+ UINT supported;
+ pD3DDev->CheckFormatSupport(format, &supported);
+ return (supported & usage) != 0;
+}
+
+bool CRenderSystemDX::DestroyRenderSystem()
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+
+ if (m_pGUIShader)
+ {
+ m_pGUIShader->End();
+ delete m_pGUIShader;
+ m_pGUIShader = nullptr;
+ }
+ m_rightEyeTex.Release();
+ m_BlendEnableState = nullptr;
+ m_BlendDisableState = nullptr;
+ m_RSScissorDisable = nullptr;
+ m_RSScissorEnable = nullptr;
+ m_depthStencilState = nullptr;
+
+ return true;
+}
+
+void CRenderSystemDX::CheckInterlacedStereoView()
+{
+ RENDER_STEREO_MODE stereoMode = CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode();
+
+ if ( m_rightEyeTex.Get()
+ && RENDER_STEREO_MODE_INTERLACED != stereoMode
+ && RENDER_STEREO_MODE_CHECKERBOARD != stereoMode)
+ {
+ m_rightEyeTex.Release();
+ }
+
+ if ( !m_rightEyeTex.Get()
+ && ( RENDER_STEREO_MODE_INTERLACED == stereoMode
+ || RENDER_STEREO_MODE_CHECKERBOARD == stereoMode))
+ {
+ const auto outputSize = m_deviceResources->GetOutputSize();
+ DXGI_FORMAT texFormat = m_deviceResources->GetBackBuffer().GetFormat();
+ if (!m_rightEyeTex.Create(outputSize.Width, outputSize.Height, 1, D3D11_USAGE_DEFAULT, texFormat))
+ {
+ CLog::Log(LOGERROR, "{} - Failed to create right eye buffer.", __FUNCTION__);
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoMode(RENDER_STEREO_MODE_SPLIT_HORIZONTAL); // try fallback to split horizontal
+ }
+ else
+ m_deviceResources->Unregister(&m_rightEyeTex); // we will handle its health
+ }
+}
+
+bool CRenderSystemDX::CreateStates()
+{
+ auto m_pD3DDev = m_deviceResources->GetD3DDevice();
+ auto m_pContext = m_deviceResources->GetD3DContext();
+
+ if (!m_pD3DDev)
+ return false;
+
+ m_BlendEnableState = nullptr;
+ m_BlendDisableState = nullptr;
+
+ // Initialize the description of the stencil state.
+ D3D11_DEPTH_STENCIL_DESC depthStencilDesc = {};
+
+ // Set up the description of the stencil state.
+ depthStencilDesc.DepthEnable = false;
+ depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
+ depthStencilDesc.DepthFunc = D3D11_COMPARISON_NEVER;
+ depthStencilDesc.StencilEnable = false;
+ depthStencilDesc.StencilReadMask = 0xFF;
+ depthStencilDesc.StencilWriteMask = 0xFF;
+
+ // Stencil operations if pixel is front-facing.
+ depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
+ depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
+ depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
+ depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
+
+ // Stencil operations if pixel is back-facing.
+ depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
+ depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
+ depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
+ depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
+
+ // Create the depth stencil state.
+ HRESULT hr = m_pD3DDev->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);
+ if(FAILED(hr))
+ return false;
+
+ // Set the depth stencil state.
+ m_pContext->OMSetDepthStencilState(m_depthStencilState.Get(), 0);
+
+ D3D11_RASTERIZER_DESC rasterizerState;
+ rasterizerState.CullMode = D3D11_CULL_NONE;
+ rasterizerState.FillMode = D3D11_FILL_SOLID;// DEBUG - D3D11_FILL_WIREFRAME
+ rasterizerState.FrontCounterClockwise = false;
+ rasterizerState.DepthBias = 0;
+ rasterizerState.DepthBiasClamp = 0.0f;
+ rasterizerState.DepthClipEnable = true;
+ rasterizerState.SlopeScaledDepthBias = 0.0f;
+ rasterizerState.ScissorEnable = false;
+ rasterizerState.MultisampleEnable = false;
+ rasterizerState.AntialiasedLineEnable = false;
+
+ if (FAILED(m_pD3DDev->CreateRasterizerState(&rasterizerState, &m_RSScissorDisable)))
+ return false;
+
+ rasterizerState.ScissorEnable = true;
+ if (FAILED(m_pD3DDev->CreateRasterizerState(&rasterizerState, &m_RSScissorEnable)))
+ return false;
+
+ m_pContext->RSSetState(m_RSScissorDisable.Get()); // by default
+
+ D3D11_BLEND_DESC blendState = {};
+ blendState.RenderTarget[0].BlendEnable = true;
+ blendState.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; // D3D11_BLEND_SRC_ALPHA;
+ blendState.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; // D3D11_BLEND_INV_SRC_ALPHA;
+ blendState.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
+ blendState.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+ blendState.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
+ blendState.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+ blendState.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
+
+ m_pD3DDev->CreateBlendState(&blendState, &m_BlendEnableState);
+
+ blendState.RenderTarget[0].BlendEnable = false;
+ m_pD3DDev->CreateBlendState(&blendState, &m_BlendDisableState);
+
+ // by default
+ m_pContext->OMSetBlendState(m_BlendEnableState.Get(), nullptr, 0xFFFFFFFF);
+ m_BlendEnabled = true;
+
+ return true;
+}
+
+void CRenderSystemDX::PresentRender(bool rendered, bool videoLayer)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ if ( rendered
+ && ( m_stereoMode == RENDER_STEREO_MODE_INTERLACED
+ || m_stereoMode == RENDER_STEREO_MODE_CHECKERBOARD))
+ {
+ auto m_pContext = m_deviceResources->GetD3DContext();
+
+ // all views prepared, let's merge them before present
+ m_pContext->OMSetRenderTargets(1, m_deviceResources->GetBackBuffer().GetAddressOfRTV(), m_deviceResources->GetDSV());
+
+ auto outputSize = m_deviceResources->GetOutputSize();
+ CRect destRect = { 0.0f, 0.0f, float(outputSize.Width), float(outputSize.Height) };
+
+ SHADER_METHOD method = RENDER_STEREO_MODE_INTERLACED == m_stereoMode
+ ? SHADER_METHOD_RENDER_STEREO_INTERLACED_RIGHT
+ : SHADER_METHOD_RENDER_STEREO_CHECKERBOARD_RIGHT;
+ SetAlphaBlendEnable(true);
+ CD3DTexture::DrawQuad(destRect, 0, &m_rightEyeTex, nullptr, method);
+ CD3DHelper::PSClearShaderResources(m_pContext);
+ }
+
+ // time for decoder that may require the context
+ {
+ std::unique_lock<CCriticalSection> lock(m_decoderSection);
+ XbmcThreads::EndTime<> timer;
+ timer.Set(5ms);
+ while (!m_decodingTimer.IsTimePast() && !timer.IsTimePast())
+ {
+ m_decodingEvent.wait(lock, 1ms);
+ }
+ }
+
+ PresentRenderImpl(rendered);
+}
+
+void CRenderSystemDX::RequestDecodingTime()
+{
+ std::unique_lock<CCriticalSection> lock(m_decoderSection);
+ m_decodingTimer.Set(3ms);
+}
+
+void CRenderSystemDX::ReleaseDecodingTime()
+{
+ std::unique_lock<CCriticalSection> lock(m_decoderSection);
+ m_decodingTimer.SetExpired();
+ m_decodingEvent.notify();
+}
+
+bool CRenderSystemDX::BeginRender()
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ m_limitedColorRange = CServiceBroker::GetWinSystem()->UseLimitedColor();
+ m_inScene = m_deviceResources->Begin();
+ return m_inScene;
+}
+
+bool CRenderSystemDX::EndRender()
+{
+ m_inScene = false;
+
+ if (!m_bRenderCreated)
+ return false;
+
+ return true;
+}
+
+bool CRenderSystemDX::ClearBuffers(UTILS::COLOR::Color color)
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ float fColor[4];
+ CD3DHelper::XMStoreColor(fColor, color);
+ ID3D11RenderTargetView* pRTView = m_deviceResources->GetBackBuffer().GetRenderTarget();
+
+ if ( m_stereoMode != RENDER_STEREO_MODE_OFF
+ && m_stereoMode != RENDER_STEREO_MODE_MONO)
+ {
+ // if stereo anaglyph/tab/sbs, data was cleared when left view was rendered
+ if (m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ {
+ // execute command's queue
+ m_deviceResources->FinishCommandList();
+
+ // do not clear RT for anaglyph modes
+ if ( m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA
+ || m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN
+ || m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_YELLOW_BLUE)
+ {
+ pRTView = nullptr;
+ }
+ // for interlaced/checkerboard clear view for right texture
+ else if (m_stereoMode == RENDER_STEREO_MODE_INTERLACED
+ || m_stereoMode == RENDER_STEREO_MODE_CHECKERBOARD)
+ {
+ pRTView = m_rightEyeTex.GetRenderTarget();
+ }
+ }
+ }
+
+ if (pRTView == nullptr)
+ return true;
+
+ auto outputSize = m_deviceResources->GetOutputSize();
+ CRect clRect(0.0f, 0.0f,
+ static_cast<float>(outputSize.Width),
+ static_cast<float>(outputSize.Height));
+
+ // Unlike Direct3D 9, D3D11 ClearRenderTargetView always clears full extent of the resource view.
+ // Viewport and scissor settings are not applied. So clear RT by drawing full sized rect with clear color
+ if (m_ScissorsEnabled && m_scissor != clRect)
+ {
+ bool alphaEnabled = m_BlendEnabled;
+ if (alphaEnabled)
+ SetAlphaBlendEnable(false);
+
+ CGUITextureD3D::DrawQuad(clRect, color);
+
+ if (alphaEnabled)
+ SetAlphaBlendEnable(true);
+ }
+ else
+ m_deviceResources->ClearRenderTarget(pRTView, fColor);
+
+ m_deviceResources->ClearDepthStencil();
+ return true;
+}
+
+void CRenderSystemDX::CaptureStateBlock()
+{
+ if (!m_bRenderCreated)
+ return;
+}
+
+void CRenderSystemDX::ApplyStateBlock()
+{
+ if (!m_bRenderCreated)
+ return;
+
+ auto m_pContext = m_deviceResources->GetD3DContext();
+
+ m_pContext->RSSetState(m_ScissorsEnabled ? m_RSScissorEnable.Get() : m_RSScissorDisable.Get());
+ m_pContext->OMSetDepthStencilState(m_depthStencilState.Get(), 0);
+ float factors[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_pContext->OMSetBlendState(m_BlendEnabled ? m_BlendEnableState.Get() : m_BlendDisableState.Get(), factors, 0xFFFFFFFF);
+
+ m_pGUIShader->ApplyStateBlock();
+}
+
+void CRenderSystemDX::SetCameraPosition(const CPoint &camera, int screenWidth, int screenHeight, float stereoFactor)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ // grab the viewport dimensions and location
+ float w = m_viewPort.Width * 0.5f;
+ float h = m_viewPort.Height * 0.5f;
+
+ XMFLOAT2 offset = XMFLOAT2(camera.x - screenWidth*0.5f, camera.y - screenHeight*0.5f);
+
+ // world view. Until this is moved onto the GPU (via a vertex shader for instance), we set it to the identity here.
+ m_pGUIShader->SetWorld(XMMatrixIdentity());
+
+ // Initialize the view matrix camera view.
+ // Multiply the Y coord by -1 then translate so that everything is relative to the camera position.
+ XMMATRIX flipY = XMMatrixScaling(1.0, -1.0f, 1.0f);
+ XMMATRIX translate = XMMatrixTranslation(-(w + offset.x - stereoFactor), -(h + offset.y), 2 * h);
+ m_pGUIShader->SetView(XMMatrixMultiply(translate, flipY));
+
+ // projection onto screen space
+ m_pGUIShader->SetProjection(XMMatrixPerspectiveOffCenterLH((-w - offset.x)*0.5f, (w - offset.x)*0.5f, (-h + offset.y)*0.5f, (h + offset.y)*0.5f, h, 100 * h));
+}
+
+void CRenderSystemDX::Project(float &x, float &y, float &z)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ m_pGUIShader->Project(x, y, z);
+}
+
+CRect CRenderSystemDX::GetBackBufferRect()
+{
+ auto outputSize = m_deviceResources->GetOutputSize();
+ return CRect(0.f, 0.f, static_cast<float>(outputSize.Width), static_cast<float>(outputSize.Height));
+}
+
+void CRenderSystemDX::GetViewPort(CRect& viewPort)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ viewPort.x1 = m_viewPort.TopLeftX;
+ viewPort.y1 = m_viewPort.TopLeftY;
+ viewPort.x2 = m_viewPort.TopLeftX + m_viewPort.Width;
+ viewPort.y2 = m_viewPort.TopLeftY + m_viewPort.Height;
+}
+
+void CRenderSystemDX::SetViewPort(const CRect& viewPort)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ m_viewPort.MinDepth = 0.0f;
+ m_viewPort.MaxDepth = 1.0f;
+ m_viewPort.TopLeftX = viewPort.x1;
+ m_viewPort.TopLeftY = viewPort.y1;
+ m_viewPort.Width = viewPort.x2 - viewPort.x1;
+ m_viewPort.Height = viewPort.y2 - viewPort.y1;
+
+ m_deviceResources->SetViewPort(m_viewPort);
+ m_pGUIShader->SetViewPort(m_viewPort);
+}
+
+void CRenderSystemDX::RestoreViewPort()
+{
+ if (!m_bRenderCreated)
+ return;
+
+ m_deviceResources->SetViewPort(m_viewPort);
+ m_pGUIShader->SetViewPort(m_viewPort);
+}
+
+bool CRenderSystemDX::ScissorsCanEffectClipping()
+{
+ if (!m_bRenderCreated)
+ return false;
+
+ return m_pGUIShader != nullptr && m_pGUIShader->HardwareClipIsPossible();
+}
+
+CRect CRenderSystemDX::ClipRectToScissorRect(const CRect &rect)
+{
+ if (!m_bRenderCreated)
+ return CRect();
+
+ float xFactor = m_pGUIShader->GetClipXFactor();
+ float xOffset = m_pGUIShader->GetClipXOffset();
+ float yFactor = m_pGUIShader->GetClipYFactor();
+ float yOffset = m_pGUIShader->GetClipYOffset();
+
+ return CRect(rect.x1 * xFactor + xOffset,
+ rect.y1 * yFactor + yOffset,
+ rect.x2 * xFactor + xOffset,
+ rect.y2 * yFactor + yOffset);
+}
+
+void CRenderSystemDX::SetScissors(const CRect& rect)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ auto m_pContext = m_deviceResources->GetD3DContext();
+
+ m_scissor = rect;
+ CD3D11_RECT scissor(MathUtils::round_int(rect.x1)
+ , MathUtils::round_int(rect.y1)
+ , MathUtils::round_int(rect.x2)
+ , MathUtils::round_int(rect.y2));
+
+ m_pContext->RSSetScissorRects(1, &scissor);
+ m_pContext->RSSetState(m_RSScissorEnable.Get());
+ m_ScissorsEnabled = true;
+}
+
+void CRenderSystemDX::ResetScissors()
+{
+ if (!m_bRenderCreated)
+ return;
+
+ auto m_pContext = m_deviceResources->GetD3DContext();
+ auto outputSize = m_deviceResources->GetOutputSize();
+
+ m_scissor.SetRect(0.0f, 0.0f,
+ static_cast<float>(outputSize.Width),
+ static_cast<float>(outputSize.Height));
+
+ m_pContext->RSSetState(m_RSScissorDisable.Get());
+ m_ScissorsEnabled = false;
+}
+
+void CRenderSystemDX::OnDXDeviceLost()
+{
+ CRenderSystemDX::DestroyRenderSystem();
+}
+
+void CRenderSystemDX::OnDXDeviceRestored()
+{
+ CRenderSystemDX::InitRenderSystem();
+}
+
+void CRenderSystemDX::SetStereoMode(RENDER_STEREO_MODE mode, RENDER_STEREO_VIEW view)
+{
+ CRenderSystemBase::SetStereoMode(mode, view);
+
+ if (!m_bRenderCreated)
+ return;
+
+ auto m_pContext = m_deviceResources->GetD3DContext();
+
+ UINT writeMask = D3D11_COLOR_WRITE_ENABLE_ALL;
+ if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_RED_CYAN)
+ {
+ if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
+ writeMask = D3D11_COLOR_WRITE_ENABLE_RED;
+ else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ writeMask = D3D11_COLOR_WRITE_ENABLE_BLUE | D3D11_COLOR_WRITE_ENABLE_GREEN;
+ }
+ if(m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_GREEN_MAGENTA)
+ {
+ if(m_stereoView == RENDER_STEREO_VIEW_LEFT)
+ writeMask = D3D11_COLOR_WRITE_ENABLE_GREEN;
+ else if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ writeMask = D3D11_COLOR_WRITE_ENABLE_BLUE | D3D11_COLOR_WRITE_ENABLE_RED;
+ }
+ if (m_stereoMode == RENDER_STEREO_MODE_ANAGLYPH_YELLOW_BLUE)
+ {
+ if (m_stereoView == RENDER_STEREO_VIEW_LEFT)
+ writeMask = D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN;
+ else if (m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ writeMask = D3D11_COLOR_WRITE_ENABLE_BLUE;
+ }
+ if ( RENDER_STEREO_MODE_INTERLACED == m_stereoMode
+ || RENDER_STEREO_MODE_CHECKERBOARD == m_stereoMode)
+ {
+ if (m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ {
+ m_pContext->OMSetRenderTargets(1, m_rightEyeTex.GetAddressOfRTV(), m_deviceResources->GetDSV());
+ }
+ }
+ else if (RENDER_STEREO_MODE_HARDWAREBASED == m_stereoMode)
+ {
+ m_deviceResources->SetStereoIdx(m_stereoView == RENDER_STEREO_VIEW_RIGHT ? 1 : 0);
+
+ m_pContext->OMSetRenderTargets(1, m_deviceResources->GetBackBuffer().GetAddressOfRTV(), m_deviceResources->GetDSV());
+ }
+
+ auto m_pD3DDev = m_deviceResources->GetD3DDevice();
+
+ D3D11_BLEND_DESC desc;
+ m_BlendEnableState->GetDesc(&desc);
+ // update blend state
+ if (desc.RenderTarget[0].RenderTargetWriteMask != writeMask)
+ {
+ m_BlendDisableState = nullptr;
+ m_BlendEnableState = nullptr;
+
+ desc.RenderTarget[0].RenderTargetWriteMask = writeMask;
+ m_pD3DDev->CreateBlendState(&desc, &m_BlendEnableState);
+
+ desc.RenderTarget[0].BlendEnable = false;
+ m_pD3DDev->CreateBlendState(&desc, &m_BlendDisableState);
+
+ float blendFactors[] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_pContext->OMSetBlendState(m_BlendEnabled ? m_BlendEnableState.Get() : m_BlendDisableState.Get(), blendFactors, 0xFFFFFFFF);
+ }
+}
+
+bool CRenderSystemDX::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:
+ case RENDER_STEREO_MODE_CHECKERBOARD:
+ return true;
+ case RENDER_STEREO_MODE_HARDWAREBASED:
+ return m_deviceResources->IsStereoAvailable();
+ default:
+ return CRenderSystemBase::SupportsStereo(mode);
+ }
+}
+
+void CRenderSystemDX::FlushGPU() const
+{
+ if (!m_bRenderCreated)
+ return;
+
+ m_deviceResources->FinishCommandList();
+ m_deviceResources->GetImmediateContext()->Flush();
+}
+
+bool CRenderSystemDX::InitGUIShader()
+{
+ delete m_pGUIShader;
+ m_pGUIShader = nullptr;
+
+ m_pGUIShader = new CGUIShaderDX();
+ if (!m_pGUIShader->Initialize())
+ {
+ CLog::LogF(LOGERROR, "Failed to initialize GUI shader.");
+ return false;
+ }
+
+ m_pGUIShader->ApplyStateBlock();
+ return true;
+}
+
+void CRenderSystemDX::SetAlphaBlendEnable(bool enable)
+{
+ if (!m_bRenderCreated)
+ return;
+
+ m_deviceResources->GetD3DContext()->OMSetBlendState(enable ? m_BlendEnableState.Get() : m_BlendDisableState.Get(), nullptr, 0xFFFFFFFF);
+ m_BlendEnabled = enable;
+}
+
+CD3DTexture& CRenderSystemDX::GetBackBuffer()
+{
+ if (m_stereoView == RENDER_STEREO_VIEW_RIGHT && m_rightEyeTex.Get())
+ return m_rightEyeTex;
+
+ return m_deviceResources->GetBackBuffer();
+}
+
+void CRenderSystemDX::CheckDeviceCaps()
+{
+ const auto feature_level = m_deviceResources->GetDeviceFeatureLevel();
+ if (feature_level < D3D_FEATURE_LEVEL_9_3)
+ m_maxTextureSize = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ else if (feature_level < D3D_FEATURE_LEVEL_10_0)
+ m_maxTextureSize = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ else if (feature_level < D3D_FEATURE_LEVEL_11_0)
+ m_maxTextureSize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ else
+ // 11_x and greater feature level. Limit this size to avoid memory overheads
+ m_maxTextureSize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION >> 1;
+}
+
+bool CRenderSystemDX::SupportsNPOT(bool dxt) const
+{
+ // MSDN says:
+ // At feature levels 9_1, 9_2 and 9_3, the display device supports the use
+ // of 2D textures with dimensions that are not powers of two under two conditions:
+ // 1) only one MIP-map level for each texture can be created - we are using both 1 and 0 mipmap levels
+ // 2) no wrap sampler modes for textures are allowed - we are using clamp everywhere
+ // At feature levels 10_0, 10_1 and 11_0, the display device unconditionally supports the use of 2D textures with dimensions that are not powers of two.
+ // taking in account first condition we setup caps NPOT for FE > 9.x only
+ return m_deviceResources->GetDeviceFeatureLevel() > D3D_FEATURE_LEVEL_9_3 ? true : false;
+}
diff --git a/xbmc/rendering/dx/RenderSystemDX.h b/xbmc/rendering/dx/RenderSystemDX.h
new file mode 100644
index 0000000..9a18e22
--- /dev/null
+++ b/xbmc/rendering/dx/RenderSystemDX.h
@@ -0,0 +1,106 @@
+/*
+ * 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 "DeviceResources.h"
+#include "rendering/RenderSystem.h"
+#include "threads/Condition.h"
+#include "threads/CriticalSection.h"
+#include "threads/SystemClock.h"
+#include "utils/ColorUtils.h"
+
+#include <wrl/client.h>
+
+class ID3DResource;
+class CGUIShaderDX;
+enum AVPixelFormat;
+enum AVPixelFormat;
+
+class CRenderSystemDX : public CRenderSystemBase, DX::IDeviceNotify
+{
+public:
+ CRenderSystemDX();
+ virtual ~CRenderSystemDX();
+
+ // CRenderBase overrides
+ bool InitRenderSystem() override;
+ bool DestroyRenderSystem() override;
+ bool BeginRender() override;
+ bool EndRender() override;
+ void PresentRender(bool rendered, bool videoLayer) override;
+ bool ClearBuffers(UTILS::COLOR::Color color) override;
+ void SetViewPort(const CRect& viewPort) override;
+ void GetViewPort(CRect& viewPort) override;
+ void RestoreViewPort() override;
+ CRect ClipRectToScissorRect(const CRect &rect) override;
+ bool ScissorsCanEffectClipping() 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.f) override;
+ void SetStereoMode(RENDER_STEREO_MODE mode, RENDER_STEREO_VIEW view) override;
+ bool SupportsStereo(RENDER_STEREO_MODE mode) const override;
+ void Project(float &x, float &y, float &z) override;
+ bool SupportsNPOT(bool dxt) const override;
+
+ // IDeviceNotify overrides
+ void OnDXDeviceLost() override;
+ void OnDXDeviceRestored() override;
+
+ // CRenderSystemDX methods
+ CGUIShaderDX* GetGUIShader() const { return m_pGUIShader; }
+ bool Interlaced() const { return m_interlaced; }
+ bool IsFormatSupport(DXGI_FORMAT format, unsigned int usage) const;
+ CRect GetBackBufferRect();
+ CD3DTexture& GetBackBuffer();
+
+ void FlushGPU() const;
+ void RequestDecodingTime();
+ void ReleaseDecodingTime();
+ void SetAlphaBlendEnable(bool enable);
+
+ // empty overrides
+ bool IsExtSupported(const char* extension) const override { return false; }
+ bool ResetRenderSystem(int width, int height) override { return true; }
+
+protected:
+ virtual void PresentRenderImpl(bool rendered) = 0;
+
+ bool CreateStates();
+ bool InitGUIShader();
+ void OnResize();
+ void CheckInterlacedStereoView(void);
+ void CheckDeviceCaps();
+
+ CCriticalSection m_resourceSection;
+ CCriticalSection m_decoderSection;
+
+ // our adapter could change as we go
+ bool m_interlaced;
+ bool m_inScene{ false }; ///< True if we're in a BeginScene()/EndScene() block
+ bool m_BlendEnabled{ false };
+ bool m_ScissorsEnabled{ false };
+ D3D11_VIEWPORT m_viewPort;
+ CRect m_scissor;
+ CGUIShaderDX* m_pGUIShader{ nullptr };
+ Microsoft::WRL::ComPtr<ID3D11DepthStencilState> m_depthStencilState;
+ Microsoft::WRL::ComPtr<ID3D11BlendState> m_BlendEnableState;
+ Microsoft::WRL::ComPtr<ID3D11BlendState> m_BlendDisableState;
+ Microsoft::WRL::ComPtr<ID3D11RasterizerState> m_RSScissorDisable;
+ Microsoft::WRL::ComPtr<ID3D11RasterizerState> m_RSScissorEnable;
+ // stereo interlaced/checkerboard intermediate target
+ CD3DTexture m_rightEyeTex;
+
+ XbmcThreads::EndTime<> m_decodingTimer;
+ XbmcThreads::ConditionVariable m_decodingEvent;
+
+ std::shared_ptr<DX::DeviceResources> m_deviceResources;
+};
+
diff --git a/xbmc/rendering/dx/ScreenshotSurfaceWindows.cpp b/xbmc/rendering/dx/ScreenshotSurfaceWindows.cpp
new file mode 100644
index 0000000..7a62ad6
--- /dev/null
+++ b/xbmc/rendering/dx/ScreenshotSurfaceWindows.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "ScreenshotSurfaceWindows.h"
+
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "rendering/dx/DeviceResources.h"
+#include "utils/Screenshot.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#include <mutex>
+
+#include <wrl/client.h>
+
+using namespace Microsoft::WRL;
+
+void CScreenshotSurfaceWindows::Register()
+{
+ CScreenShot::Register(CScreenshotSurfaceWindows::CreateSurface);
+}
+
+std::unique_ptr<IScreenshotSurface> CScreenshotSurfaceWindows::CreateSurface()
+{
+ return std::unique_ptr<CScreenshotSurfaceWindows>(new CScreenshotSurfaceWindows());
+}
+
+bool CScreenshotSurfaceWindows::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();
+
+ auto deviceResources = DX::DeviceResources::Get();
+ deviceResources->FinishCommandList();
+
+ ComPtr<ID3D11DeviceContext> pImdContext = deviceResources->GetImmediateContext();
+ ComPtr<ID3D11Device> pDevice = deviceResources->GetD3DDevice();
+ CD3DTexture& backbuffer = deviceResources->GetBackBuffer();
+ if (!backbuffer.Get())
+ return false;
+
+ D3D11_TEXTURE2D_DESC desc = {};
+ backbuffer.GetDesc(&desc);
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.BindFlags = 0;
+
+ ComPtr<ID3D11Texture2D> pCopyTexture = nullptr;
+ if (SUCCEEDED(pDevice->CreateTexture2D(&desc, nullptr, &pCopyTexture)))
+ {
+ // take copy
+ pImdContext->CopyResource(pCopyTexture.Get(), backbuffer.Get());
+
+ D3D11_MAPPED_SUBRESOURCE res;
+ if (SUCCEEDED(pImdContext->Map(pCopyTexture.Get(), 0, D3D11_MAP_READ, 0, &res)))
+ {
+ m_width = desc.Width;
+ m_height = desc.Height;
+ m_stride = res.RowPitch;
+ m_buffer = new unsigned char[m_height * m_stride];
+ if (desc.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
+ {
+ // convert R10G10B10A2 -> B8G8R8A8
+ for (int y = 0; y < m_height; y++)
+ {
+ uint32_t* pixels10 = reinterpret_cast<uint32_t*>(static_cast<uint8_t*>(res.pData) + y * res.RowPitch);
+ uint8_t* pixels8 = m_buffer + y * m_stride;
+
+ for (int x = 0; x < m_width; x++, pixels10++, pixels8 += 4)
+ {
+ // actual bit per channel is A2B10G10R10
+ uint32_t pixel = *pixels10;
+ // R
+ pixels8[2] = static_cast<uint8_t>((pixel & 0x3FF) * 255 / 1023);
+ // G
+ pixel >>= 10;
+ pixels8[1] = static_cast<uint8_t>((pixel & 0x3FF) * 255 / 1023);
+ // B
+ pixel >>= 10;
+ pixels8[0] = static_cast<uint8_t>((pixel & 0x3FF) * 255 / 1023);
+ // A
+ pixels8[3] = 0xFF;
+ }
+ }
+ }
+ else
+ memcpy(m_buffer, res.pData, m_height * m_stride);
+ pImdContext->Unmap(pCopyTexture.Get(), 0);
+ }
+ else
+ CLog::LogF(LOGERROR, "MAP_READ failed.");
+ }
+
+ return m_buffer != nullptr;
+}
diff --git a/xbmc/rendering/dx/ScreenshotSurfaceWindows.h b/xbmc/rendering/dx/ScreenshotSurfaceWindows.h
new file mode 100644
index 0000000..e492e44
--- /dev/null
+++ b/xbmc/rendering/dx/ScreenshotSurfaceWindows.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 CScreenshotSurfaceWindows : public IScreenshotSurface
+{
+public:
+ static void Register();
+ static std::unique_ptr<IScreenshotSurface> CreateSurface();
+
+ bool Capture() override;
+};
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;
+};