xbmc
GLonDX.h
1 /*
2  * Copyright (C) 2005-2019 Team Kodi
3  * This file is part of Kodi - https://kodi.tv
4  *
5  * SPDX-License-Identifier: GPL-2.0-or-later
6  * See LICENSES/README.md for more information.
7  */
8 
9 #pragma once
10 
11 #ifdef __cplusplus
12 
13 #if defined(WIN32) && defined(HAS_ANGLE)
14 
15 #include <angle_gl.h>
16 #include <d3d11.h>
17 #include <d3dcompiler.h>
18 #include <kodi/AddonBase.h>
19 #include <kodi/gui/General.h>
20 #include <wrl/client.h>
21 
22 #include <EGL/egl.h>
23 #include <EGL/eglext.h>
24 
25 #pragma comment(lib, "d3dcompiler.lib")
26 #ifndef GL_CLIENT_VERSION
27 #define GL_CLIENT_VERSION 3
28 #endif
29 
30 namespace kodi
31 {
32 namespace gui
33 {
34 namespace gl
35 {
36 
37 class ATTR_DLL_LOCAL CGLonDX : public kodi::gui::IRenderHelper
38 {
39 public:
40  explicit CGLonDX() : m_pContext(reinterpret_cast<ID3D11DeviceContext*>(kodi::gui::GetHWContext()))
41  {
42  }
43  ~CGLonDX() override { destruct(); }
44 
45  bool Init() override
46  {
47  EGLint egl_display_attrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE,
48  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
49  EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
50  EGL_DONT_CARE,
51  EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
52  EGL_DONT_CARE,
53  EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
54  EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
55  EGL_NONE};
56  EGLint egl_config_attrs[] = {EGL_RED_SIZE,
57  8,
58  EGL_GREEN_SIZE,
59  8,
60  EGL_BLUE_SIZE,
61  8,
62  EGL_ALPHA_SIZE,
63  8,
64  EGL_BIND_TO_TEXTURE_RGBA,
65  EGL_TRUE,
66  EGL_RENDERABLE_TYPE,
67  GL_CLIENT_VERSION == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT,
68  EGL_SURFACE_TYPE,
69  EGL_PBUFFER_BIT,
70  EGL_NONE};
71  EGLint egl_context_attrs[] = {EGL_CONTEXT_CLIENT_VERSION, GL_CLIENT_VERSION, EGL_NONE};
72 
73  m_eglDisplay =
74  eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, egl_display_attrs);
75  if (m_eglDisplay == EGL_NO_DISPLAY)
76  {
77  Log(ADDON_LOG_ERROR, "GLonDX: unable to get EGL display (%s)", eglGetErrorString());
78  return false;
79  }
80 
81  if (eglInitialize(m_eglDisplay, nullptr, nullptr) != EGL_TRUE)
82  {
83  Log(ADDON_LOG_ERROR, "GLonDX: unable to init EGL display (%s)", eglGetErrorString());
84  return false;
85  }
86 
87  EGLint numConfigs = 0;
88  if (eglChooseConfig(m_eglDisplay, egl_config_attrs, &m_eglConfig, 1, &numConfigs) != EGL_TRUE ||
89  numConfigs == 0)
90  {
91  Log(ADDON_LOG_ERROR, "GLonDX: unable to get EGL config (%s)", eglGetErrorString());
92  return false;
93  }
94 
95  m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, nullptr, egl_context_attrs);
96  if (m_eglContext == EGL_NO_CONTEXT)
97  {
98  Log(ADDON_LOG_ERROR, "GLonDX: unable to create EGL context (%s)", eglGetErrorString());
99  return false;
100  }
101 
102  if (!createD3DResources())
103  return false;
104 
105  if (eglMakeCurrent(m_eglDisplay, m_eglBuffer, m_eglBuffer, m_eglContext) != EGL_TRUE)
106  {
107  Log(ADDON_LOG_ERROR, "GLonDX: unable to make current EGL (%s)", eglGetErrorString());
108  return false;
109  }
110  return true;
111  }
112 
113  void CheckGL(ID3D11DeviceContext* device)
114  {
115  if (m_pContext != device)
116  {
117  m_pSRView = nullptr;
118  m_pVShader = nullptr;
119  m_pPShader = nullptr;
120  m_pContext = device;
121 
122  if (m_eglBuffer != EGL_NO_SURFACE)
123  {
124  eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
125  eglDestroySurface(m_eglDisplay, m_eglBuffer);
126  m_eglBuffer = EGL_NO_SURFACE;
127  }
128 
129  // create new resources
130  if (!createD3DResources())
131  return;
132 
133  eglMakeCurrent(m_eglDisplay, m_eglBuffer, m_eglBuffer, m_eglContext);
134  }
135  }
136 
137  void Begin() override
138  {
139  // confirm on begin D3D context is correct
140  CheckGL(reinterpret_cast<ID3D11DeviceContext*>(kodi::gui::GetHWContext()));
141 
142  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
143  glClear(GL_COLOR_BUFFER_BIT);
144  }
145 
146  void End() override
147  {
148  glFlush();
149 
150  // set our primitive shaders
151  m_pContext->VSSetShader(m_pVShader.Get(), nullptr, 0);
152  m_pContext->PSSetShader(m_pPShader.Get(), nullptr, 0);
153  m_pContext->PSSetShaderResources(0, 1, m_pSRView.GetAddressOf());
154  // draw texture
155  m_pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
156  m_pContext->IASetVertexBuffers(0, 0, nullptr, nullptr, nullptr);
157  m_pContext->IASetInputLayout(nullptr);
158  m_pContext->Draw(4, 0);
159  // unset shaders
160  m_pContext->PSSetShader(nullptr, nullptr, 0);
161  m_pContext->VSSetShader(nullptr, nullptr, 0);
162  // unbind our view
163  ID3D11ShaderResourceView* views[1] = {};
164  m_pContext->PSSetShaderResources(0, 1, views);
165  }
166 
167 private:
168  enum ShaderType
169  {
170  VERTEX_SHADER,
171  PIXEL_SHADER
172  };
173 
174  bool createD3DResources()
175  {
176  HANDLE sharedHandle;
177  Microsoft::WRL::ComPtr<ID3D11Device> pDevice;
178  Microsoft::WRL::ComPtr<ID3D11RenderTargetView> pRTView;
179  Microsoft::WRL::ComPtr<ID3D11Resource> pRTResource;
180  Microsoft::WRL::ComPtr<ID3D11Texture2D> pRTTexture;
181  Microsoft::WRL::ComPtr<ID3D11Texture2D> pOffScreenTexture;
182  Microsoft::WRL::ComPtr<IDXGIResource> dxgiResource;
183 
184  m_pContext->GetDevice(&pDevice);
185  m_pContext->OMGetRenderTargets(1, &pRTView, nullptr);
186  if (!pRTView)
187  return false;
188 
189  pRTView->GetResource(&pRTResource);
190  if (FAILED(pRTResource.As(&pRTTexture)))
191  return false;
192 
193  D3D11_TEXTURE2D_DESC texDesc;
194  pRTTexture->GetDesc(&texDesc);
195  texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
196  texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
197  texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
198  if (FAILED(pDevice->CreateTexture2D(&texDesc, nullptr, &pOffScreenTexture)))
199  {
200  Log(ADDON_LOG_ERROR, "GLonDX: unable to create intermediate texture");
201  return false;
202  }
203 
204  CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(pOffScreenTexture.Get(),
205  D3D11_SRV_DIMENSION_TEXTURE2D);
206  if (FAILED(pDevice->CreateShaderResourceView(pOffScreenTexture.Get(), &srvDesc, &m_pSRView)))
207  {
208  Log(ADDON_LOG_ERROR, "GLonDX: unable to create shader view");
209  return false;
210  }
211 
212  if (FAILED(pOffScreenTexture.As(&dxgiResource)) ||
213  FAILED(dxgiResource->GetSharedHandle(&sharedHandle)))
214  {
215  Log(ADDON_LOG_ERROR, "GLonDX: unable get shared handle for texture");
216  return false;
217  }
218 
219  // initiate simple shaders
220  if (FAILED(d3dCreateShader(VERTEX_SHADER, vs_out_shader_text, &m_pVShader)))
221  {
222  Log(ADDON_LOG_ERROR, "GLonDX: unable to create vertex shader view");
223  return false;
224  }
225 
226  if (FAILED(d3dCreateShader(PIXEL_SHADER, ps_out_shader_text, &m_pPShader)))
227  {
228  Log(ADDON_LOG_ERROR, "GLonDX: unable to create pixel shader view");
229  return false;
230  }
231 
232  // create EGL buffer from D3D shared texture
233  EGLint egl_buffer_attrs[] = {EGL_WIDTH,
234  static_cast<EGLint>(texDesc.Width),
235  EGL_HEIGHT,
236  static_cast<EGLint>(texDesc.Height),
237  EGL_TEXTURE_TARGET,
238  EGL_TEXTURE_2D,
239  EGL_TEXTURE_FORMAT,
240  EGL_TEXTURE_RGBA,
241  EGL_NONE};
242 
243  m_eglBuffer =
244  eglCreatePbufferFromClientBuffer(m_eglDisplay, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
245  sharedHandle, m_eglConfig, egl_buffer_attrs);
246 
247  if (m_eglBuffer == EGL_NO_SURFACE)
248  {
249  Log(ADDON_LOG_ERROR, "GLonDX: unable to create EGL buffer (%s)", eglGetErrorString());
250  return false;
251  }
252  return true;
253  }
254 
255  HRESULT d3dCreateShader(ShaderType shaderType,
256  const std::string& source,
257  IUnknown** ppShader) const
258  {
259  Microsoft::WRL::ComPtr<ID3DBlob> pBlob;
260  Microsoft::WRL::ComPtr<ID3DBlob> pErrors;
261 
262  auto hr = D3DCompile(source.c_str(), source.length(), nullptr, nullptr, nullptr, "main",
263  shaderType == PIXEL_SHADER ? "ps_4_0" : "vs_4_0", 0, 0, &pBlob, &pErrors);
264 
265  if (SUCCEEDED(hr))
266  {
267  Microsoft::WRL::ComPtr<ID3D11Device> pDevice;
268  m_pContext->GetDevice(&pDevice);
269 
270  if (shaderType == PIXEL_SHADER)
271  {
272  hr = pDevice->CreatePixelShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), nullptr,
273  reinterpret_cast<ID3D11PixelShader**>(ppShader));
274  }
275  else
276  {
277  hr = pDevice->CreateVertexShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), nullptr,
278  reinterpret_cast<ID3D11VertexShader**>(ppShader));
279  }
280 
281  if (FAILED(hr))
282  {
283  Log(ADDON_LOG_ERROR, "GLonDX: unable to create %s shader",
284  shaderType == PIXEL_SHADER ? "pixel" : "vertex");
285  }
286  }
287  else
288  {
289  Log(ADDON_LOG_ERROR, "GLonDX: unable to compile shader (%s)", pErrors->GetBufferPointer());
290  }
291  return hr;
292  }
293 
294  static const char* eglGetErrorString()
295  {
296 #define CASE_STR(value) \
297  case value: \
298  return #value
299  switch (eglGetError())
300  {
301  CASE_STR(EGL_SUCCESS);
302  CASE_STR(EGL_NOT_INITIALIZED);
303  CASE_STR(EGL_BAD_ACCESS);
304  CASE_STR(EGL_BAD_ALLOC);
305  CASE_STR(EGL_BAD_ATTRIBUTE);
306  CASE_STR(EGL_BAD_CONTEXT);
307  CASE_STR(EGL_BAD_CONFIG);
308  CASE_STR(EGL_BAD_CURRENT_SURFACE);
309  CASE_STR(EGL_BAD_DISPLAY);
310  CASE_STR(EGL_BAD_SURFACE);
311  CASE_STR(EGL_BAD_MATCH);
312  CASE_STR(EGL_BAD_PARAMETER);
313  CASE_STR(EGL_BAD_NATIVE_PIXMAP);
314  CASE_STR(EGL_BAD_NATIVE_WINDOW);
315  CASE_STR(EGL_CONTEXT_LOST);
316  default:
317  return "Unknown";
318  }
319 #undef CASE_STR
320  }
321 
322  void destruct()
323  {
324  if (m_eglDisplay != EGL_NO_DISPLAY)
325  {
326  eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
327 
328  if (m_eglBuffer != EGL_NO_SURFACE)
329  {
330  eglDestroySurface(m_eglDisplay, m_eglBuffer);
331  m_eglBuffer = EGL_NO_SURFACE;
332  }
333 
334  if (m_eglContext != EGL_NO_CONTEXT)
335  {
336  eglDestroyContext(m_eglDisplay, m_eglContext);
337  m_eglContext = EGL_NO_CONTEXT;
338  }
339 
340  eglTerminate(m_eglDisplay);
341  m_eglDisplay = EGL_NO_DISPLAY;
342  }
343 
344  m_pSRView = nullptr;
345  m_pVShader = nullptr;
346  m_pPShader = nullptr;
347  m_pContext = nullptr;
348  }
349 
350  EGLConfig m_eglConfig = EGL_NO_CONFIG_KHR;
351  EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
352  EGLContext m_eglContext = EGL_NO_CONTEXT;
353  EGLSurface m_eglBuffer = EGL_NO_SURFACE;
354 
355  ID3D11DeviceContext* m_pContext = nullptr; // don't hold context
356  Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_pSRView = nullptr;
357  Microsoft::WRL::ComPtr<ID3D11VertexShader> m_pVShader = nullptr;
358  Microsoft::WRL::ComPtr<ID3D11PixelShader> m_pPShader = nullptr;
359 
360 #define TO_STRING(...) #__VA_ARGS__
361  std::string vs_out_shader_text = TO_STRING(void main(uint id
362  : SV_VertexId, out float2 tex
363  : TEXCOORD0, out float4 pos
364  : SV_POSITION) {
365  tex = float2(id % 2, (id % 4) >> 1);
366  pos = float4((tex.x - 0.5f) * 2, -(tex.y - 0.5f) * 2, 0, 1);
367  });
368 
369  std::string ps_out_shader_text = TO_STRING(
370  Texture2D texMain : register(t0);
371  SamplerState Sampler
372  {
373  Filter = MIN_MAG_MIP_LINEAR;
374  AddressU = CLAMP;
375  AddressV = CLAMP;
376  Comparison = NEVER;
377  };
378 
379  float4 main(in float2 tex : TEXCOORD0) : SV_TARGET
380  {
381  return texMain.Sample(Sampler, tex);
382  });
383 #undef TO_STRING
384 }; /* class CGLonDX */
385 
386 } /* namespace gl */
387 
388 using CRenderHelper = gl::CGLonDX;
389 } /* namespace gui */
390 } /* namespace kodi */
391 
392 #else /* defined(WIN32) && defined(HAS_ANGLE) */
393 #pragma message("WARNING: GLonDX.h only be available on Windows by use of Angle as depend!")
394 #endif /* defined(WIN32) && defined(HAS_ANGLE) */
395 
396 #endif /* __cplusplus */
Definition: XHandle.h:21
3 : To report error messages in the log file.
Definition: addon_base.h:193
int main(int argc, char **argv)
main()
Definition: example.c:273
Definition: LibInputPointer.h:13