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