/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SharedSurfaceANGLE.h" #include "GLContextEGL.h" using namespace mozilla::gfx; namespace mozilla { namespace gl { SurfaceFactory_ANGLEShareHandle* SurfaceFactory_ANGLEShareHandle::Create(GLContext* gl, ID3D10Device1* d3d, const SurfaceCaps& caps) { GLLibraryEGL* egl = &sEGLLibrary; if (!egl) return nullptr; if (!egl->IsExtensionSupported( GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle)) { return nullptr; } return new SurfaceFactory_ANGLEShareHandle(gl, egl, d3d, caps); } EGLDisplay SharedSurface_ANGLEShareHandle::Display() { return mEGL->Display(); } SharedSurface_ANGLEShareHandle::~SharedSurface_ANGLEShareHandle() { mEGL->fDestroySurface(Display(), mPBuffer); } void SharedSurface_ANGLEShareHandle::LockProdImpl() { GLContextEGL::Cast(mGL)->SetEGLSurfaceOverride(mPBuffer); } void SharedSurface_ANGLEShareHandle::UnlockProdImpl() { } void SharedSurface_ANGLEShareHandle::Fence() { mGL->fFinish(); } bool SharedSurface_ANGLEShareHandle::WaitSync() { // Since we glFinish in Fence(), we're always going to be resolved here. return true; } static void FillPBufferAttribs_ByBits(nsTArray& aAttrs, int redBits, int greenBits, int blueBits, int alphaBits, int depthBits, int stencilBits) { aAttrs.Clear(); #if defined(A1) || defined(A2) #error The temp-macro names we want are already defined. #endif #define A1(_x) do { aAttrs.AppendElement(_x); } while (0) #define A2(_x,_y) do { A1(_x); A1(_y); } while (0) A2(LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT); A2(LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PBUFFER_BIT); A2(LOCAL_EGL_RED_SIZE, redBits); A2(LOCAL_EGL_GREEN_SIZE, greenBits); A2(LOCAL_EGL_BLUE_SIZE, blueBits); A2(LOCAL_EGL_ALPHA_SIZE, alphaBits); A2(LOCAL_EGL_DEPTH_SIZE, depthBits); A2(LOCAL_EGL_STENCIL_SIZE, stencilBits); A1(LOCAL_EGL_NONE); #undef A1 #undef A2 } static void FillPBufferAttribs_BySizes(nsTArray& attribs, bool bpp16, bool hasAlpha, int depthBits, int stencilBits) { int red = 0; int green = 0; int blue = 0; int alpha = 0; if (bpp16) { if (hasAlpha) { red = green = blue = alpha = 4; } else { red = 5; green = 6; blue = 5; } } else { red = green = blue = 8; if (hasAlpha) alpha = 8; } FillPBufferAttribs_ByBits(attribs, red, green, blue, alpha, depthBits, stencilBits); } static EGLConfig ChooseConfig(GLContext* gl, GLLibraryEGL* egl, const SurfaceCaps& caps) { MOZ_ASSERT(egl); MOZ_ASSERT(caps.color); // We might want 24-bit depth, but we're only (fairly) sure to get 16-bit. int depthBits = caps.depth ? 16 : 0; int stencilBits = caps.stencil ? 8 : 0; // Ok, now we have everything. nsTArray attribs(32); FillPBufferAttribs_BySizes(attribs, caps.bpp16, caps.alpha, depthBits, stencilBits); // Time to try to get this config: EGLConfig configs[64]; int numConfigs = sizeof(configs)/sizeof(EGLConfig); int foundConfigs = 0; if (!egl->fChooseConfig(egl->Display(), attribs.Elements(), configs, numConfigs, &foundConfigs) || !foundConfigs) { NS_WARNING("No configs found for the requested formats."); return EGL_NO_CONFIG; } // TODO: Pick a config progamatically instead of hoping that // the first config will be minimally matching our request. EGLConfig config = configs[0]; if (gl->DebugMode()) { egl->DumpEGLConfig(config); } return config; } // Returns EGL_NO_SURFACE on error. static EGLSurface CreatePBufferSurface(GLLibraryEGL* egl, EGLDisplay display, EGLConfig config, const gfx::IntSize& size) { EGLint attribs[] = { LOCAL_EGL_WIDTH, size.width, LOCAL_EGL_HEIGHT, size.height, LOCAL_EGL_NONE }; EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs); return surface; } SharedSurface_ANGLEShareHandle* SharedSurface_ANGLEShareHandle::Create(GLContext* gl, ID3D10Device1* d3d, EGLContext context, EGLConfig config, const gfx::IntSize& size, bool hasAlpha) { GLLibraryEGL* egl = &sEGLLibrary; MOZ_ASSERT(egl); MOZ_ASSERT(egl->IsExtensionSupported( GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle)); if (!context || !config) return nullptr; EGLDisplay display = egl->Display(); EGLSurface pbuffer = CreatePBufferSurface(egl, display, config, size); if (!pbuffer) return nullptr; // Declare everything before 'goto's. HANDLE shareHandle = nullptr; nsRefPtr texture; nsRefPtr srv; // On failure, goto CleanUpIfFailed. // If |failed|, CleanUpIfFailed will clean up and return null. bool failed = true; HRESULT hr; // Off to the races! if (!egl->fQuerySurfacePointerANGLE( display, pbuffer, LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, &shareHandle)) { NS_ERROR("Failed to grab ShareHandle for PBuffer!"); goto CleanUpIfFailed; } // Ok, we have a valid PBuffer with ShareHandle. // Let's attach it to D3D. hr = d3d->OpenSharedResource(shareHandle, __uuidof(ID3D10Texture2D), getter_AddRefs(texture)); if (FAILED(hr)) { NS_ERROR("Failed to open shared resource!"); goto CleanUpIfFailed; } hr = d3d->CreateShaderResourceView(texture, nullptr, getter_AddRefs(srv)); if (FAILED(hr)) { NS_ERROR("Failed to create SRV!"); goto CleanUpIfFailed; } failed = false; CleanUpIfFailed: if (failed) { NS_WARNING("CleanUpIfFailed"); egl->fDestroySurface(egl->Display(), pbuffer); return nullptr; } return new SharedSurface_ANGLEShareHandle(gl, egl, size, hasAlpha, context, pbuffer, texture, srv); } SurfaceFactory_ANGLEShareHandle::SurfaceFactory_ANGLEShareHandle(GLContext* gl, GLLibraryEGL* egl, ID3D10Device1* d3d, const SurfaceCaps& caps) : SurfaceFactory_GL(gl, SharedSurfaceType::EGLSurfaceANGLE, caps) , mProdGL(gl) , mEGL(egl) , mConsD3D(d3d) { mConfig = ChooseConfig(mProdGL, mEGL, mReadCaps); mContext = GLContextEGL::Cast(mProdGL)->GetEGLContext(); MOZ_ASSERT(mConfig && mContext); } } /* namespace gl */ } /* namespace mozilla */