blob: 08cd7da62b7914f3346d7420205e07dc2d163efb [file] [log] [blame]
// 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