| // Copyright 2018 The Chromium Embedded Framework Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cef/libcef/browser/gpu/external_texture_manager.h" |
| |
| #include "gpu/command_buffer/service/service_utils.h" |
| #include "third_party/khronos/EGL/egl.h" |
| #include "third_party/khronos/EGL/eglext.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context_egl.h" |
| #include "ui/gl/gl_image.h" |
| #include "ui/gl/gl_surface_egl.h" |
| #include "ui/gl/init/gl_factory.h" |
| |
| #if defined(OS_WIN) |
| #include <d3d11_1.h> |
| #include "ui/gl/gl_angle_util_win.h" |
| #include "ui/gl/gl_image_dxgi.h" |
| #endif |
| |
| #ifndef EGL_ANGLE_d3d_texture_client_buffer |
| #define EGL_ANGLE_d3d_texture_client_buffer 1 |
| #define EGL_D3D_TEXTURE_ANGLE 0x33A3 |
| #endif |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| namespace { |
| |
| #if defined(OS_WIN) |
| |
| class GLImageDXGISharedHandle : public gl::GLImageDXGI { |
| public: |
| GLImageDXGISharedHandle(const gfx::Size& size) |
| : GLImageDXGI(size, nullptr), |
| handle_((HANDLE)0), |
| surface_(EGL_NO_SURFACE), |
| texture_id_(0) {} |
| |
| void* share_handle() const { return handle_; } |
| |
| bool Initialize() { |
| Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = |
| gl::QueryD3D11DeviceObjectFromANGLE(); |
| if (!d3d11_device) { |
| return false; |
| } |
| |
| Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device1; |
| HRESULT hr = d3d11_device.As(&d3d11_device1); |
| if (FAILED(hr)) { |
| return false; |
| } |
| |
| D3D11_TEXTURE2D_DESC td = {0}; |
| td.ArraySize = 1; |
| td.CPUAccessFlags = 0; |
| td.Format = DXGI_FORMAT_B8G8R8A8_UNORM; |
| td.Width = GetSize().width(); |
| td.Height = GetSize().height(); |
| td.MipLevels = 1; |
| td.SampleDesc.Count = 1; |
| td.SampleDesc.Quality = 0; |
| td.Usage = D3D11_USAGE_DEFAULT; |
| td.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; |
| td.MiscFlags = 0; |
| |
| hr = d3d11_device1->CreateTexture2D(&td, nullptr, texture_.GetAddressOf()); |
| if (FAILED(hr)) { |
| return false; |
| } |
| |
| // Create a staging texture that will not be a render-target, but will be |
| // shared. We could make the render target directly shareable, but the |
| // staged copy is safer for synchronization and less problematic |
| td.BindFlags = D3D11_BIND_SHADER_RESOURCE; |
| td.MiscFlags = D3D11_RESOURCE_MISC_SHARED; |
| hr = d3d11_device1->CreateTexture2D(&td, nullptr, |
| staging_texture_.GetAddressOf()); |
| if (FAILED(hr)) { |
| return false; |
| } |
| |
| // If using a staging texture ... then we need the shared handle for that |
| Microsoft::WRL::ComPtr<IDXGIResource> dxgi_res; |
| if (staging_texture_.Get()) { |
| hr = staging_texture_.As(&dxgi_res); |
| } else { |
| hr = texture_.As(&dxgi_res); |
| } |
| if (SUCCEEDED(hr)) { |
| dxgi_res->GetSharedHandle(&handle_); |
| } |
| |
| return true; |
| } |
| |
| void Lock() { |
| // In the future a keyed mutex could be utilized here. |
| } |
| |
| void Unlock() { |
| if (staging_texture_.Get() && texture_.Get()) { |
| Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device; |
| staging_texture_->GetDevice(&d3d11_device); |
| if (d3d11_device.Get()) { |
| Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_ctx; |
| d3d11_device->GetImmediateContext(&d3d11_ctx); |
| if (d3d11_ctx.Get()) { |
| d3d11_ctx->CopyResource(staging_texture_.Get(), texture_.Get()); |
| } |
| } |
| } |
| } |
| |
| void SetSurface(EGLSurface surface, GLuint texture_id) { |
| surface_ = surface; |
| texture_id_ = texture_id; |
| } |
| |
| EGLSurface surface() const { return surface_; } |
| |
| GLuint texture_id() const { return texture_id_; } |
| |
| protected: |
| ~GLImageDXGISharedHandle() override {} |
| |
| private: |
| HANDLE handle_; |
| Microsoft::WRL::ComPtr<ID3D11Texture2D> staging_texture_; |
| EGLSurface surface_; |
| GLuint texture_id_; |
| }; |
| |
| #endif // defined(OS_WIN) |
| |
| } // namespace |
| |
| ExternalTextureManager::ExternalTextureManager() {} |
| |
| ExternalTextureManager::~ExternalTextureManager() {} |
| |
| void* ExternalTextureManager::CreateTexture(GLuint texture_id, |
| uint32_t width, |
| uint32_t height, |
| TextureManager* tex_man) { |
| void* share_handle = nullptr; |
| |
| #if defined(OS_WIN) |
| EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay(); |
| if (egl_display == EGL_NO_DISPLAY) { |
| return nullptr; |
| } |
| |
| EGLContext curContext = eglGetCurrentContext(); |
| if (curContext == EGL_NO_CONTEXT) { |
| return nullptr; |
| } |
| |
| gfx::Size size(width, height); |
| scoped_refptr<gl::GLImage> image; |
| void* texture = nullptr; |
| |
| GLImageDXGISharedHandle* dxgi_image = new GLImageDXGISharedHandle(size); |
| if (!dxgi_image->Initialize()) { |
| return nullptr; |
| } |
| image = dxgi_image; |
| share_handle = dxgi_image->share_handle(); |
| texture = dxgi_image->texture().Get(); |
| |
| if (!image) { // this check seems unnecessary |
| return nullptr; |
| } |
| |
| EGLint numConfigs = 0; |
| EGLint configAttrs[] = { |
| EGL_RENDERABLE_TYPE, |
| EGL_OPENGL_ES3_BIT, // must remain in this position for ES2 fallback |
| EGL_SURFACE_TYPE, |
| EGL_PBUFFER_BIT, |
| EGL_BUFFER_SIZE, |
| 32, |
| EGL_RED_SIZE, |
| 8, |
| EGL_GREEN_SIZE, |
| 8, |
| EGL_BLUE_SIZE, |
| 8, |
| EGL_ALPHA_SIZE, |
| 8, |
| EGL_DEPTH_SIZE, |
| 0, |
| EGL_STENCIL_SIZE, |
| 0, |
| EGL_SAMPLE_BUFFERS, |
| 0, |
| EGL_NONE}; |
| |
| EGLConfig config = nullptr; |
| if (eglChooseConfig(egl_display, configAttrs, &config, 1, &numConfigs) != |
| EGL_TRUE) { |
| return nullptr; |
| } |
| |
| EGLSurface surface = EGL_NO_SURFACE; |
| EGLint surfAttrs[] = {EGL_WIDTH, |
| width, |
| EGL_HEIGHT, |
| height, |
| EGL_TEXTURE_TARGET, |
| EGL_TEXTURE_2D, |
| EGL_TEXTURE_FORMAT, |
| EGL_TEXTURE_RGBA, |
| EGL_NONE}; |
| |
| surface = eglCreatePbufferFromClientBuffer(egl_display, EGL_D3D_TEXTURE_ANGLE, |
| texture, config, surfAttrs); |
| if (surface == EGL_NO_SURFACE) { |
| // fallback to ES2 - it could be that we're running on older hardware |
| // and ES3 isn't available |
| |
| // EGL_RENDERABLE_TYPE is the bit at configAttrs[0] |
| configAttrs[1] = EGL_OPENGL_ES2_BIT; |
| config = nullptr; |
| if (eglChooseConfig(egl_display, configAttrs, &config, 1, &numConfigs) == |
| EGL_TRUE) { |
| surface = eglCreatePbufferFromClientBuffer( |
| egl_display, EGL_D3D_TEXTURE_ANGLE, texture, config, surfAttrs); |
| } |
| |
| // still no surface? we're done |
| if (surface == EGL_NO_SURFACE) { |
| return nullptr; |
| } |
| } |
| |
| dxgi_image->SetSurface(surface, texture_id); |
| |
| surfaceMap_[share_handle] = image; |
| |
| EGLSurface drawSurface = eglGetCurrentSurface(EGL_DRAW); |
| EGLSurface readSurface = eglGetCurrentSurface(EGL_READ); |
| |
| eglMakeCurrent(egl_display, surface, surface, curContext); |
| |
| if (eglBindTexImage(egl_display, surface, EGL_BACK_BUFFER)) { |
| if (tex_man) { |
| TextureRef* texture_ref = tex_man->GetTexture(texture_id); |
| tex_man->SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, |
| height, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, |
| gfx::Rect(size)); |
| tex_man->SetLevelImage(texture_ref, GL_TEXTURE_2D, 0, image.get(), |
| Texture::BOUND); |
| } |
| } |
| |
| eglMakeCurrent(egl_display, drawSurface, readSurface, curContext); |
| |
| #endif // defined(OS_WIN) |
| |
| return share_handle; |
| } |
| |
| void ExternalTextureManager::LockTexture(void* handle) { |
| #if defined(OS_WIN) |
| auto const img = surfaceMap_.find(handle); |
| if (img != surfaceMap_.end()) { |
| GLImageDXGISharedHandle* dxgi_image = |
| reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get()); |
| dxgi_image->Lock(); |
| } |
| #endif // defined(OS_WIN) |
| } |
| |
| void ExternalTextureManager::UnlockTexture(void* handle) { |
| #if defined(OS_WIN) |
| auto const img = surfaceMap_.find(handle); |
| if (img != surfaceMap_.end()) { |
| GLImageDXGISharedHandle* dxgi_image = |
| reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get()); |
| dxgi_image->Unlock(); |
| } |
| #endif // defined(OS_WIN) |
| } |
| |
| void ExternalTextureManager::DeleteTexture(void* handle, |
| TextureManager* tex_man) { |
| #if defined(OS_WIN) |
| EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay(); |
| if (egl_display == EGL_NO_DISPLAY) { |
| return; |
| } |
| auto const img = surfaceMap_.find(handle); |
| if (img == surfaceMap_.end()) { |
| return; |
| } |
| |
| EGLSurface surface = EGL_NO_SURFACE; |
| GLuint texture_id = 0; |
| |
| GLImageDXGISharedHandle* dxgi_image = |
| reinterpret_cast<GLImageDXGISharedHandle*>(img->second.get()); |
| surface = dxgi_image->surface(); |
| texture_id = dxgi_image->texture_id(); |
| |
| if (surface != EGL_NO_SURFACE) { |
| EGLContext curContext = eglGetCurrentContext(); |
| if (curContext != EGL_NO_CONTEXT) { |
| EGLSurface drawSurface = eglGetCurrentSurface(EGL_DRAW); |
| EGLSurface readSurface = eglGetCurrentSurface(EGL_READ); |
| |
| eglMakeCurrent(egl_display, surface, surface, curContext); |
| |
| TextureRef* texture_ref = nullptr; |
| if (tex_man) { |
| texture_ref = tex_man->GetTexture(texture_id); |
| } |
| |
| eglReleaseTexImage(egl_display, surface, EGL_BACK_BUFFER); |
| |
| if (tex_man && texture_ref) { |
| tex_man->SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, |
| 0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect()); |
| tex_man->SetLevelImage(texture_ref, GL_TEXTURE_2D, 0, nullptr, |
| Texture::UNBOUND); |
| } |
| |
| eglMakeCurrent(egl_display, drawSurface, readSurface, curContext); |
| |
| eglDestroySurface(egl_display, surface); |
| } |
| } |
| surfaceMap_.erase(img); |
| #endif // defined(OS_WIN) |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |