• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cef/libcef/browser/gpu/external_texture_manager.h"
6 
7 #include "gpu/command_buffer/service/service_utils.h"
8 #include "third_party/khronos/EGL/egl.h"
9 #include "third_party/khronos/EGL/eglext.h"
10 #include "ui/gl/gl_bindings.h"
11 #include "ui/gl/gl_context_egl.h"
12 #include "ui/gl/gl_image.h"
13 #include "ui/gl/gl_surface_egl.h"
14 #include "ui/gl/init/gl_factory.h"
15 
16 #if BUILDFLAG(IS_WIN)
17 #include <d3d11_1.h>
18 #include "ui/gl/gl_angle_util_win.h"
19 #include "ui/gl/gl_image_dxgi.h"
20 #endif
21 
22 #ifndef EGL_ANGLE_d3d_texture_client_buffer
23 #define EGL_ANGLE_d3d_texture_client_buffer 1
24 #define EGL_D3D_TEXTURE_ANGLE 0x33A3
25 #endif
26 
27 namespace gpu {
28 namespace gles2 {
29 
30 namespace {
31 
32 #if BUILDFLAG(IS_WIN)
33 
34 class GLImageDXGISharedHandle : public gl::GLImageDXGI {
35  public:
GLImageDXGISharedHandle(const gfx::Size & size)36   GLImageDXGISharedHandle(const gfx::Size& size)
37       : GLImageDXGI(size, nullptr),
38         handle_((HANDLE)0),
39         surface_(EGL_NO_SURFACE),
40         texture_id_(0) {}
41 
share_handle() const42   void* share_handle() const { return handle_; }
43 
Initialize()44   bool Initialize() {
45     Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
46         gl::QueryD3D11DeviceObjectFromANGLE();
47     if (!d3d11_device) {
48       return false;
49     }
50 
51     Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device1;
52     HRESULT hr = d3d11_device.As(&d3d11_device1);
53     if (FAILED(hr)) {
54       return false;
55     }
56 
57     D3D11_TEXTURE2D_DESC td = {0};
58     td.ArraySize = 1;
59     td.CPUAccessFlags = 0;
60     td.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
61     td.Width = GetSize().width();
62     td.Height = GetSize().height();
63     td.MipLevels = 1;
64     td.SampleDesc.Count = 1;
65     td.SampleDesc.Quality = 0;
66     td.Usage = D3D11_USAGE_DEFAULT;
67     td.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
68     td.MiscFlags = 0;
69 
70     hr = d3d11_device1->CreateTexture2D(&td, nullptr, texture_.GetAddressOf());
71     if (FAILED(hr)) {
72       return false;
73     }
74 
75     // Create a staging texture that will not be a render-target, but will be
76     // shared.  We could make the render target directly shareable, but the
77     // staged copy is safer for synchronization and less problematic
78     td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
79     td.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
80     hr = d3d11_device1->CreateTexture2D(&td, nullptr,
81                                         staging_texture_.GetAddressOf());
82     if (FAILED(hr)) {
83       return false;
84     }
85 
86     // If using a staging texture ... then we need the shared handle for that
87     Microsoft::WRL::ComPtr<IDXGIResource> dxgi_res;
88     if (staging_texture_.Get()) {
89       hr = staging_texture_.As(&dxgi_res);
90     } else {
91       hr = texture_.As(&dxgi_res);
92     }
93     if (SUCCEEDED(hr)) {
94       dxgi_res->GetSharedHandle(&handle_);
95     }
96 
97     return true;
98   }
99 
Lock()100   void Lock() {
101     // In the future a keyed mutex could be utilized here.
102   }
103 
Unlock()104   void Unlock() {
105     if (staging_texture_.Get() && texture_.Get()) {
106       Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device;
107       staging_texture_->GetDevice(&d3d11_device);
108       if (d3d11_device.Get()) {
109         Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_ctx;
110         d3d11_device->GetImmediateContext(&d3d11_ctx);
111         if (d3d11_ctx.Get()) {
112           d3d11_ctx->CopyResource(staging_texture_.Get(), texture_.Get());
113         }
114       }
115     }
116   }
117 
SetSurface(EGLSurface surface,GLuint texture_id)118   void SetSurface(EGLSurface surface, GLuint texture_id) {
119     surface_ = surface;
120     texture_id_ = texture_id;
121   }
122 
surface() const123   EGLSurface surface() const { return surface_; }
124 
texture_id() const125   GLuint texture_id() const { return texture_id_; }
126 
127  protected:
~GLImageDXGISharedHandle()128   ~GLImageDXGISharedHandle() override {}
129 
130  private:
131   HANDLE handle_;
132   Microsoft::WRL::ComPtr<ID3D11Texture2D> staging_texture_;
133   EGLSurface surface_;
134   GLuint texture_id_;
135 };
136 
137 #endif  // BUILDFLAG(IS_WIN)
138 
139 }  // namespace
140 
ExternalTextureManager()141 ExternalTextureManager::ExternalTextureManager() {}
142 
~ExternalTextureManager()143 ExternalTextureManager::~ExternalTextureManager() {}
144 
CreateTexture(GLuint texture_id,uint32_t width,uint32_t height,TextureManager * tex_man)145 void* ExternalTextureManager::CreateTexture(GLuint texture_id,
146                                             uint32_t width,
147                                             uint32_t height,
148                                             TextureManager* tex_man) {
149   void* share_handle = nullptr;
150 
151 #if BUILDFLAG(IS_WIN)
152   EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
153   if (egl_display == EGL_NO_DISPLAY) {
154     return nullptr;
155   }
156 
157   EGLContext curContext = eglGetCurrentContext();
158   if (curContext == EGL_NO_CONTEXT) {
159     return nullptr;
160   }
161 
162   gfx::Size size(width, height);
163   scoped_refptr<gl::GLImage> image;
164   void* texture = nullptr;
165 
166   GLImageDXGISharedHandle* dxgi_image = new GLImageDXGISharedHandle(size);
167   if (!dxgi_image->Initialize()) {
168     return nullptr;
169   }
170   image = dxgi_image;
171   share_handle = dxgi_image->share_handle();
172   texture = dxgi_image->texture().Get();
173 
174   if (!image) {  // this check seems unnecessary
175     return nullptr;
176   }
177 
178   EGLint numConfigs = 0;
179   EGLint configAttrs[] = {
180       EGL_RENDERABLE_TYPE,
181       EGL_OPENGL_ES3_BIT,  // must remain in this position for ES2 fallback
182       EGL_SURFACE_TYPE,
183       EGL_PBUFFER_BIT,
184       EGL_BUFFER_SIZE,
185       32,
186       EGL_RED_SIZE,
187       8,
188       EGL_GREEN_SIZE,
189       8,
190       EGL_BLUE_SIZE,
191       8,
192       EGL_ALPHA_SIZE,
193       8,
194       EGL_DEPTH_SIZE,
195       0,
196       EGL_STENCIL_SIZE,
197       0,
198       EGL_SAMPLE_BUFFERS,
199       0,
200       EGL_NONE};
201 
202   EGLConfig config = nullptr;
203   if (eglChooseConfig(egl_display, configAttrs, &config, 1, &numConfigs) !=
204       EGL_TRUE) {
205     return nullptr;
206   }
207 
208   EGLSurface surface = EGL_NO_SURFACE;
209   EGLint surfAttrs[] = {EGL_WIDTH,
210                         width,
211                         EGL_HEIGHT,
212                         height,
213                         EGL_TEXTURE_TARGET,
214                         EGL_TEXTURE_2D,
215                         EGL_TEXTURE_FORMAT,
216                         EGL_TEXTURE_RGBA,
217                         EGL_NONE};
218 
219   surface = eglCreatePbufferFromClientBuffer(egl_display, EGL_D3D_TEXTURE_ANGLE,
220                                              texture, config, surfAttrs);
221   if (surface == EGL_NO_SURFACE) {
222     // fallback to ES2 - it could be that we're running on older hardware
223     // and ES3 isn't available
224 
225     // EGL_RENDERABLE_TYPE is the bit at configAttrs[0]
226     configAttrs[1] = EGL_OPENGL_ES2_BIT;
227     config = nullptr;
228     if (eglChooseConfig(egl_display, configAttrs, &config, 1, &numConfigs) ==
229         EGL_TRUE) {
230       surface = eglCreatePbufferFromClientBuffer(
231           egl_display, EGL_D3D_TEXTURE_ANGLE, texture, config, surfAttrs);
232     }
233 
234     // still no surface? we're done
235     if (surface == EGL_NO_SURFACE) {
236       return nullptr;
237     }
238   }
239 
240   dxgi_image->SetSurface(surface, texture_id);
241 
242   surfaceMap_[share_handle] = image;
243 
244   EGLSurface drawSurface = eglGetCurrentSurface(EGL_DRAW);
245   EGLSurface readSurface = eglGetCurrentSurface(EGL_READ);
246 
247   eglMakeCurrent(egl_display, surface, surface, curContext);
248 
249   if (eglBindTexImage(egl_display, surface, EGL_BACK_BUFFER)) {
250     if (tex_man) {
251       TextureRef* texture_ref = tex_man->GetTexture(texture_id);
252       tex_man->SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_BGRA_EXT, width,
253                             height, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE,
254                             gfx::Rect(size));
255       tex_man->SetLevelImage(texture_ref, GL_TEXTURE_2D, 0, image.get(),
256                              Texture::BOUND);
257     }
258   }
259 
260   eglMakeCurrent(egl_display, drawSurface, readSurface, curContext);
261 
262 #endif  // BUILDFLAG(IS_WIN)
263 
264   return share_handle;
265 }
266 
LockTexture(void * handle)267 void ExternalTextureManager::LockTexture(void* handle) {
268 #if BUILDFLAG(IS_WIN)
269   auto const img = surfaceMap_.find(handle);
270   if (img != surfaceMap_.end()) {
271     GLImageDXGISharedHandle* dxgi_image =
272         reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get());
273     dxgi_image->Lock();
274   }
275 #endif  // BUILDFLAG(IS_WIN)
276 }
277 
UnlockTexture(void * handle)278 void ExternalTextureManager::UnlockTexture(void* handle) {
279 #if BUILDFLAG(IS_WIN)
280   auto const img = surfaceMap_.find(handle);
281   if (img != surfaceMap_.end()) {
282     GLImageDXGISharedHandle* dxgi_image =
283         reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get());
284     dxgi_image->Unlock();
285   }
286 #endif  // BUILDFLAG(IS_WIN)
287 }
288 
DeleteTexture(void * handle,TextureManager * tex_man)289 void ExternalTextureManager::DeleteTexture(void* handle,
290                                            TextureManager* tex_man) {
291 #if BUILDFLAG(IS_WIN)
292   EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
293   if (egl_display == EGL_NO_DISPLAY) {
294     return;
295   }
296   auto const img = surfaceMap_.find(handle);
297   if (img == surfaceMap_.end()) {
298     return;
299   }
300 
301   EGLSurface surface = EGL_NO_SURFACE;
302   GLuint texture_id = 0;
303 
304   GLImageDXGISharedHandle* dxgi_image =
305       reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get());
306   surface = dxgi_image->surface();
307   texture_id = dxgi_image->texture_id();
308 
309   if (surface != EGL_NO_SURFACE) {
310     EGLContext curContext = eglGetCurrentContext();
311     if (curContext != EGL_NO_CONTEXT) {
312       EGLSurface drawSurface = eglGetCurrentSurface(EGL_DRAW);
313       EGLSurface readSurface = eglGetCurrentSurface(EGL_READ);
314 
315       eglMakeCurrent(egl_display, surface, surface, curContext);
316 
317       TextureRef* texture_ref = nullptr;
318       if (tex_man) {
319         texture_ref = tex_man->GetTexture(texture_id);
320       }
321 
322       eglReleaseTexImage(egl_display, surface, EGL_BACK_BUFFER);
323 
324       if (tex_man && texture_ref) {
325         tex_man->SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1,
326                               0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
327         tex_man->SetLevelImage(texture_ref, GL_TEXTURE_2D, 0, nullptr,
328                                Texture::UNBOUND);
329       }
330 
331       eglMakeCurrent(egl_display, drawSurface, readSurface, curContext);
332 
333       eglDestroySurface(egl_display, surface);
334     }
335   }
336   surfaceMap_.erase(img);
337 #endif  // BUILDFLAG(IS_WIN)
338 }
339 
340 }  // namespace gles2
341 }  // namespace gpu
342