// Copyright (c) 2012 The Chromium 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 "ui/gl/gl_surface_glx.h" #include #include "base/command_line.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/cancellation_flag.h" #include "base/synchronization/lock.h" #include "base/threading/thread.h" #include "base/threading/thread_checker.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "ui/events/platform/platform_event_source.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_connection.h" #include "ui/gfx/x/x11_types.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface_presentation_helper.h" #include "ui/gl/gl_visual_picker_glx.h" #include "ui/gl/sync_control_vsync_provider.h" namespace gl { namespace { bool g_glx_context_create = false; bool g_glx_create_context_robustness_supported = false; bool g_glx_create_context_profile_supported = false; bool g_glx_create_context_profile_es2_supported = false; bool g_glx_texture_from_pixmap_supported = false; bool g_glx_oml_sync_control_supported = false; // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML // always fails even though GLX_OML_sync_control is reported as being supported. bool g_glx_get_msc_rate_oml_supported = false; bool g_glx_ext_swap_control_supported = false; bool g_glx_mesa_swap_control_supported = false; bool g_glx_sgi_video_sync_supported = false; // A 24-bit RGB visual and colormap to use when creating offscreen surfaces. Visual* g_visual = nullptr; int g_depth = CopyFromParent; Colormap g_colormap = CopyFromParent; GLXFBConfig GetConfigForWindow(Display* display, gfx::AcceleratedWidget window) { DCHECK(window != 0); // This code path is expensive, but we only take it when // attempting to use GLX_ARB_create_context_robustness, in which // case we need a GLXFBConfig for the window in order to create a // context for it. // // TODO(kbr): this is not a reliable code path. On platforms which // support it, we should use glXChooseFBConfig in the browser // process to choose the FBConfig and from there the X Visual to // use when creating the window in the first place. Then we can // pass that FBConfig down rather than attempting to reconstitute // it. XWindowAttributes attributes; if (!XGetWindowAttributes(display, window, &attributes)) { LOG(ERROR) << "XGetWindowAttributes failed for window " << window << "."; return nullptr; } int visual_id = XVisualIDFromVisual(attributes.visual); int num_elements = 0; gfx::XScopedPtr configs( glXGetFBConfigs(display, DefaultScreen(display), &num_elements)); if (!configs.get()) { LOG(ERROR) << "glXGetFBConfigs failed."; return nullptr; } if (!num_elements) { LOG(ERROR) << "glXGetFBConfigs returned 0 elements."; return nullptr; } bool found = false; int i; for (i = 0; i < num_elements; ++i) { int value; if (glXGetFBConfigAttrib(display, configs.get()[i], GLX_VISUAL_ID, &value)) { LOG(ERROR) << "glXGetFBConfigAttrib failed."; return nullptr; } if (value == visual_id) { found = true; break; } } if (found) { return configs.get()[i]; } return nullptr; } bool CreateDummyWindow(Display* display) { DCHECK(display); gfx::AcceleratedWidget parent_window = XRootWindow(display, DefaultScreen(display)); gfx::AcceleratedWidget window = XCreateWindow(display, parent_window, 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, 0, nullptr); if (!window) { LOG(ERROR) << "XCreateWindow failed"; return false; } GLXFBConfig config = GetConfigForWindow(display, window); if (!config) { LOG(ERROR) << "Failed to get GLXConfig"; XDestroyWindow(display, window); return false; } GLXWindow glx_window = glXCreateWindow(display, config, window, nullptr); if (!glx_window) { LOG(ERROR) << "glXCreateWindow failed"; XDestroyWindow(display, window); return false; } glXDestroyWindow(display, glx_window); XDestroyWindow(display, window); return true; } class OMLSyncControlVSyncProvider : public SyncControlVSyncProvider { public: explicit OMLSyncControlVSyncProvider(GLXWindow glx_window) : SyncControlVSyncProvider(), glx_window_(glx_window) {} ~OMLSyncControlVSyncProvider() override {} protected: bool GetSyncValues(int64_t* system_time, int64_t* media_stream_counter, int64_t* swap_buffer_counter) override { return glXGetSyncValuesOML(gfx::GetXDisplay(), glx_window_, system_time, media_stream_counter, swap_buffer_counter); } bool GetMscRate(int32_t* numerator, int32_t* denominator) override { if (!g_glx_get_msc_rate_oml_supported) return false; if (!glXGetMscRateOML(gfx::GetXDisplay(), glx_window_, numerator, denominator)) { // Once glXGetMscRateOML has been found to fail, don't try again, // since each failing call may spew an error message. g_glx_get_msc_rate_oml_supported = false; return false; } return true; } bool IsHWClock() const override { return true; } private: GLXWindow glx_window_; DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider); }; class SGIVideoSyncThread : public base::Thread, public base::RefCounted { public: static scoped_refptr Create() { if (!g_video_sync_thread) { g_video_sync_thread = new SGIVideoSyncThread(); g_video_sync_thread->Start(); } return g_video_sync_thread; } private: friend class base::RefCounted; SGIVideoSyncThread() : base::Thread("SGI_video_sync") { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } ~SGIVideoSyncThread() override { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); g_video_sync_thread = nullptr; Stop(); } static SGIVideoSyncThread* g_video_sync_thread; THREAD_CHECKER(thread_checker_); DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread); }; class SGIVideoSyncProviderThreadShim { public: explicit SGIVideoSyncProviderThreadShim(gfx::AcceleratedWidget parent_window) : parent_window_(parent_window), window_(0), glx_window_(0), task_runner_(base::ThreadTaskRunnerHandle::Get()), cancel_vsync_flag_(), vsync_lock_() { // This ensures that creation of |parent_window_| has occured when this shim // is executing in the same thread as the call to create |parent_window_|. XSync(gfx::GetXDisplay(), x11::False); } virtual ~SGIVideoSyncProviderThreadShim() { if (glx_window_) glXDestroyWindow(display_, glx_window_); if (window_) XDestroyWindow(display_, window_); } base::CancellationFlag* cancel_vsync_flag() { return &cancel_vsync_flag_; } base::Lock* vsync_lock() { return &vsync_lock_; } void Initialize() { DCHECK(display_); window_ = XCreateWindow(display_, parent_window_, 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, 0, nullptr); if (!window_) { LOG(ERROR) << "video_sync: XCreateWindow failed"; return; } GLXFBConfig config = GetConfigForWindow(display_, window_); if (!config) { LOG(ERROR) << "video_sync: Failed to get GLXConfig"; return; } glx_window_ = glXCreateWindow(display_, config, window_, nullptr); if (!glx_window_) { LOG(ERROR) << "video_sync: glXCreateWindow failed"; return; } // Create the context only once for all vsync providers. if (!context_) { context_ = glXCreateNewContext(display_, config, GLX_RGBA_TYPE, nullptr, x11::True); if (!context_) LOG(ERROR) << "video_sync: glXCreateNewContext failed"; } } void GetVSyncParameters( const gfx::VSyncProvider::UpdateVSyncCallback& callback) { base::TimeTicks now; { // Don't allow |window_| destruction while we're probing vsync. base::AutoLock locked(vsync_lock_); if (!context_ || cancel_vsync_flag_.IsSet()) return; glXMakeContextCurrent(display_, glx_window_, glx_window_, context_); unsigned int retrace_count = 0; if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0) return; TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD); now = base::TimeTicks::Now(); glXMakeContextCurrent(display_, 0, 0, nullptr); } const base::TimeDelta kDefaultInterval = base::TimeDelta::FromSeconds(1) / 60; task_runner_->PostTask(FROM_HERE, base::BindOnce(callback, now, kDefaultInterval)); } private: // For initialization of display_ in GLSurface::InitializeOneOff before // the sandbox goes up. friend class gl::GLSurfaceGLX; // We only need one Display and GLXContext because we only use one thread for // SGI_video_sync. The display is created in GLSurfaceGLX::InitializeOneOff // and the context is created the first time a vsync provider is initialized. static Display* display_; static GLXContext context_; gfx::AcceleratedWidget parent_window_; gfx::AcceleratedWidget window_; GLXWindow glx_window_; scoped_refptr task_runner_; base::CancellationFlag cancel_vsync_flag_; base::Lock vsync_lock_; DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim); }; class SGIVideoSyncVSyncProvider : public gfx::VSyncProvider, public base::SupportsWeakPtr { public: explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget parent_window) : vsync_thread_(SGIVideoSyncThread::Create()), shim_(new SGIVideoSyncProviderThreadShim(parent_window)), cancel_vsync_flag_(shim_->cancel_vsync_flag()), vsync_lock_(shim_->vsync_lock()) { vsync_thread_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&SGIVideoSyncProviderThreadShim::Initialize, base::Unretained(shim_.get()))); } ~SGIVideoSyncVSyncProvider() override { { base::AutoLock locked(*vsync_lock_); cancel_vsync_flag_->Set(); } // Hand-off |shim_| to be deleted on the |vsync_thread_|. vsync_thread_->task_runner()->DeleteSoon(FROM_HERE, shim_.release()); } void GetVSyncParameters( const gfx::VSyncProvider::UpdateVSyncCallback& callback) override { // Only one outstanding request per surface. if (!pending_callback_) { DCHECK(callback); pending_callback_ = callback; vsync_thread_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&SGIVideoSyncProviderThreadShim::GetVSyncParameters, base::Unretained(shim_.get()), base::BindRepeating( &SGIVideoSyncVSyncProvider::PendingCallbackRunner, AsWeakPtr()))); } } bool GetVSyncParametersIfAvailable(base::TimeTicks* timebase, base::TimeDelta* interval) override { return false; } bool SupportGetVSyncParametersIfAvailable() const override { return false; } bool IsHWClock() const override { return false; } private: void PendingCallbackRunner(const base::TimeTicks timebase, const base::TimeDelta interval) { DCHECK(pending_callback_); std::move(pending_callback_).Run(timebase, interval); } scoped_refptr vsync_thread_; // Thread shim through which the sync provider is accessed on |vsync_thread_|. std::unique_ptr shim_; gfx::VSyncProvider::UpdateVSyncCallback pending_callback_; // Raw pointers to sync primitives owned by the shim_. // These will only be referenced before we post a task to destroy // the shim_, so they are safe to access. base::CancellationFlag* cancel_vsync_flag_; base::Lock* vsync_lock_; DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider); }; SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = nullptr; // In order to take advantage of GLX_SGI_video_sync, we need a display // for use on a separate thread. We must allocate this before the sandbox // goes up (rather than on-demand when we start the thread). Display* SGIVideoSyncProviderThreadShim::display_ = nullptr; GLXContext SGIVideoSyncProviderThreadShim::context_ = 0; } // namespace bool GLSurfaceGLX::initialized_ = false; GLSurfaceGLX::GLSurfaceGLX() {} bool GLSurfaceGLX::InitializeOneOff() { if (initialized_) return true; // http://crbug.com/245466 setenv("force_s3tc_enable", "true", 1); // SGIVideoSyncProviderShim (if instantiated) will issue X commands on // it's own thread. gfx::InitializeThreadedX11(); if (!gfx::GetXDisplay()) { LOG(ERROR) << "XOpenDisplay failed."; return false; } int major, minor; if (!glXQueryVersion(gfx::GetXDisplay(), &major, &minor)) { LOG(ERROR) << "glxQueryVersion failed"; return false; } if (major == 1 && minor < 3) { LOG(ERROR) << "GLX 1.3 or later is required."; return false; } const XVisualInfo& visual_info = gl::GLVisualPickerGLX::GetInstance()->system_visual(); g_visual = visual_info.visual; g_depth = visual_info.depth; g_colormap = XCreateColormap(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()), visual_info.visual, AllocNone); // We create a dummy unmapped window for both the main Display and the video // sync Display so that the Nvidia driver can initialize itself before the // sandbox is set up. // Unfortunately some fds e.g. /dev/nvidia0 are cached per thread and because // we can't start threads before the sandbox is set up, these are accessed // through the broker process. See GpuProcessPolicy::InitGpuBrokerProcess. if (!CreateDummyWindow(gfx::GetXDisplay())) { LOG(ERROR) << "CreateDummyWindow(gfx::GetXDisplay()) failed"; return false; } initialized_ = true; return true; } // static bool GLSurfaceGLX::InitializeExtensionSettingsOneOff() { if (!initialized_) return false; #ifdef TOOLKIT_QT g_driver_glx.InitializeExtensionBindings(g_driver_glx.GetPlatformExtensions()); #else g_driver_glx.InitializeExtensionBindings(); #endif g_glx_context_create = HasGLXExtension("GLX_ARB_create_context"); g_glx_create_context_robustness_supported = HasGLXExtension("GLX_ARB_create_context_robustness"); g_glx_create_context_profile_supported = HasGLXExtension("GLX_ARB_create_context_profile"); g_glx_create_context_profile_es2_supported = HasGLXExtension("GLX_ARB_create_context_es2_profile"); g_glx_texture_from_pixmap_supported = HasGLXExtension("GLX_EXT_texture_from_pixmap"); g_glx_oml_sync_control_supported = HasGLXExtension("GLX_OML_sync_control"); g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported; g_glx_ext_swap_control_supported = HasGLXExtension("GLX_EXT_swap_control"); g_glx_mesa_swap_control_supported = HasGLXExtension("GLX_MESA_swap_control"); g_glx_sgi_video_sync_supported = HasGLXExtension("GLX_SGI_video_sync"); if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported) { Display* video_sync_display = gfx::OpenNewXDisplay(); if (!video_sync_display) { LOG(ERROR) << "Could not open video sync display"; return false; } if (!CreateDummyWindow(video_sync_display)) { LOG(ERROR) << "CreateDummyWindow(video_sync_display) failed"; return false; } SGIVideoSyncProviderThreadShim::display_ = video_sync_display; } return true; } // static void GLSurfaceGLX::ShutdownOneOff() { initialized_ = false; g_glx_context_create = false; g_glx_create_context_robustness_supported = false; g_glx_create_context_profile_supported = false; g_glx_create_context_profile_es2_supported = false; g_glx_texture_from_pixmap_supported = false; g_glx_oml_sync_control_supported = false; g_glx_get_msc_rate_oml_supported = false; g_glx_ext_swap_control_supported = false; g_glx_mesa_swap_control_supported = false; g_glx_sgi_video_sync_supported = false; g_visual = nullptr; g_depth = CopyFromParent; g_colormap = CopyFromParent; } // static const char* GLSurfaceGLX::GetGLXExtensions() { return glXQueryExtensionsString(gfx::GetXDisplay(), 0); } // static bool GLSurfaceGLX::HasGLXExtension(const char* name) { return ExtensionsContain(GetGLXExtensions(), name); } // static bool GLSurfaceGLX::IsCreateContextSupported() { return g_glx_context_create; } // static bool GLSurfaceGLX::IsCreateContextRobustnessSupported() { return g_glx_create_context_robustness_supported; } // static bool GLSurfaceGLX::IsCreateContextProfileSupported() { return g_glx_create_context_profile_supported; } // static bool GLSurfaceGLX::IsCreateContextES2ProfileSupported() { return g_glx_create_context_profile_es2_supported; } // static bool GLSurfaceGLX::IsTextureFromPixmapSupported() { return g_glx_texture_from_pixmap_supported; } // static bool GLSurfaceGLX::IsEXTSwapControlSupported() { return g_glx_ext_swap_control_supported; } // static bool GLSurfaceGLX::IsMESASwapControlSupported() { return g_glx_mesa_swap_control_supported; } // static bool GLSurfaceGLX::IsOMLSyncControlSupported() { return g_glx_oml_sync_control_supported; } void* GLSurfaceGLX::GetDisplay() { return gfx::GetXDisplay(); } GLSurfaceGLX::~GLSurfaceGLX() {} NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window) : parent_window_(window), window_(0), glx_window_(0), config_(nullptr), visual_id_(CopyFromParent) {} bool NativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) { XWindowAttributes attributes; if (!XGetWindowAttributes(gfx::GetXDisplay(), parent_window_, &attributes)) { LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_ << "."; return false; } size_ = gfx::Size(attributes.width, attributes.height); visual_id_ = XVisualIDFromVisual(attributes.visual); // Create a child window, with a CopyFromParent visual (to avoid inducing // extra blits in the driver), that we can resize exactly in Resize(), // correctly ordered with GL, so that we don't have invalid transient states. // See https://crbug.com/326995. XSetWindowAttributes swa; memset(&swa, 0, sizeof(swa)); swa.background_pixmap = 0; swa.bit_gravity = NorthWestGravity; window_ = XCreateWindow(gfx::GetXDisplay(), parent_window_, 0, 0, size_.width(), size_.height(), 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixmap | CWBitGravity, &swa); if (!window_) { LOG(ERROR) << "XCreateWindow failed"; return false; } XMapWindow(gfx::GetXDisplay(), window_); RegisterEvents(); XFlush(gfx::GetXDisplay()); GetConfig(); if (!config_) { LOG(ERROR) << "Failed to get GLXConfig"; return false; } glx_window_ = glXCreateWindow(gfx::GetXDisplay(), config_, window_, NULL); if (!glx_window_) { LOG(ERROR) << "glXCreateWindow failed"; return false; } if (g_glx_oml_sync_control_supported) { vsync_provider_ = std::make_unique(glx_window_); presentation_helper_ = std::make_unique(vsync_provider_.get()); } else if (g_glx_sgi_video_sync_supported) { vsync_provider_ = std::make_unique(parent_window_); presentation_helper_ = std::make_unique(vsync_provider_.get()); } else { // Assume a refresh rate of 59.9 Hz, which will cause us to skip // 1 frame every 10 seconds on a 60Hz monitor, but will prevent us // from blocking the GPU service due to back pressure. This would still // encounter backpressure on a <60Hz monitor, but hopefully that is // not common. const base::TimeTicks kDefaultTimebase; const base::TimeDelta kDefaultInterval = base::TimeDelta::FromSeconds(1) / 59.9; vsync_provider_ = std::make_unique( kDefaultTimebase, kDefaultInterval); presentation_helper_ = std::make_unique( kDefaultTimebase, kDefaultInterval); } return true; } void NativeViewGLSurfaceGLX::Destroy() { presentation_helper_ = nullptr; vsync_provider_ = nullptr; if (glx_window_) { glXDestroyWindow(gfx::GetXDisplay(), glx_window_); glx_window_ = 0; } if (window_) { UnregisterEvents(); XDestroyWindow(gfx::GetXDisplay(), window_); window_ = 0; XFlush(gfx::GetXDisplay()); } } bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size, float scale_factor, ColorSpace color_space, bool has_alpha) { size_ = size; glXWaitGL(); XResizeWindow(gfx::GetXDisplay(), window_, size.width(), size.height()); glXWaitX(); return true; } bool NativeViewGLSurfaceGLX::IsOffscreen() { return false; } gfx::SwapResult NativeViewGLSurfaceGLX::SwapBuffers( const PresentationCallback& callback) { TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers", "width", GetSize().width(), "height", GetSize().height()); GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers( presentation_helper_.get(), callback); glXSwapBuffers(gfx::GetXDisplay(), GetDrawableHandle()); return scoped_swap_buffers.result(); } gfx::Size NativeViewGLSurfaceGLX::GetSize() { return size_; } void* NativeViewGLSurfaceGLX::GetHandle() { return reinterpret_cast(GetDrawableHandle()); } bool NativeViewGLSurfaceGLX::SupportsPresentationCallback() { return true; } bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() { return g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer; } void* NativeViewGLSurfaceGLX::GetConfig() { if (!config_) config_ = GetConfigForWindow(gfx::GetXDisplay(), window_); return config_; } GLSurfaceFormat NativeViewGLSurfaceGLX::GetFormat() { return GLSurfaceFormat(); } unsigned long NativeViewGLSurfaceGLX::GetCompatibilityKey() { return visual_id_; } gfx::SwapResult NativeViewGLSurfaceGLX::PostSubBuffer( int x, int y, int width, int height, const PresentationCallback& callback) { DCHECK(g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer); GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers( presentation_helper_.get(), callback); glXCopySubBufferMESA(gfx::GetXDisplay(), GetDrawableHandle(), x, y, width, height); return scoped_swap_buffers.result(); } bool NativeViewGLSurfaceGLX::OnMakeCurrent(GLContext* context) { presentation_helper_->OnMakeCurrent(context, this); return GLSurfaceGLX::OnMakeCurrent(context); } gfx::VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() { return vsync_provider_.get(); } NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() { Destroy(); } void NativeViewGLSurfaceGLX::ForwardExposeEvent(XEvent* event) { XEvent forwarded_event = *event; forwarded_event.xexpose.window = parent_window_; XSendEvent(gfx::GetXDisplay(), parent_window_, x11::False, ExposureMask, &forwarded_event); XFlush(gfx::GetXDisplay()); } bool NativeViewGLSurfaceGLX::CanHandleEvent(XEvent* event) { return event->type == Expose && event->xexpose.window == static_cast(window_); } GLXDrawable NativeViewGLSurfaceGLX::GetDrawableHandle() const { return glx_window_; } UnmappedNativeViewGLSurfaceGLX::UnmappedNativeViewGLSurfaceGLX( const gfx::Size& size) : size_(size), config_(nullptr), window_(0), glx_window_(0) { // Ensure that we don't create a window with zero size. if (size_.GetArea() == 0) size_.SetSize(1, 1); } bool UnmappedNativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) { DCHECK(!window_); gfx::AcceleratedWidget parent_window = DefaultRootWindow(gfx::GetXDisplay()); XSetWindowAttributes attrs; attrs.border_pixel = 0; attrs.colormap = g_colormap; window_ = XCreateWindow( gfx::GetXDisplay(), parent_window, 0, 0, size_.width(), size_.height(), 0, g_depth, InputOutput, g_visual, CWBorderPixel | CWColormap, &attrs); if (!window_) { LOG(ERROR) << "XCreateWindow failed"; return false; } GetConfig(); if (!config_) { LOG(ERROR) << "Failed to get GLXConfig"; return false; } glx_window_ = glXCreateWindow(gfx::GetXDisplay(), config_, window_, NULL); if (!glx_window_) { LOG(ERROR) << "glXCreateWindow failed"; return false; } return true; } void UnmappedNativeViewGLSurfaceGLX::Destroy() { config_ = nullptr; if (glx_window_) { glXDestroyWindow(gfx::GetXDisplay(), glx_window_); glx_window_ = 0; } if (window_) { XDestroyWindow(gfx::GetXDisplay(), window_); window_ = 0; } } bool UnmappedNativeViewGLSurfaceGLX::IsOffscreen() { return true; } gfx::SwapResult UnmappedNativeViewGLSurfaceGLX::SwapBuffers( const PresentationCallback& callback) { NOTREACHED() << "Attempted to call SwapBuffers on an unmapped window."; return gfx::SwapResult::SWAP_FAILED; } gfx::Size UnmappedNativeViewGLSurfaceGLX::GetSize() { return size_; } void* UnmappedNativeViewGLSurfaceGLX::GetHandle() { return reinterpret_cast(glx_window_); } void* UnmappedNativeViewGLSurfaceGLX::GetConfig() { if (!config_) config_ = GetConfigForWindow(gfx::GetXDisplay(), window_); return config_; } GLSurfaceFormat UnmappedNativeViewGLSurfaceGLX::GetFormat() { return GLSurfaceFormat(); } unsigned long UnmappedNativeViewGLSurfaceGLX::GetCompatibilityKey() { return XVisualIDFromVisual(g_visual); } UnmappedNativeViewGLSurfaceGLX::~UnmappedNativeViewGLSurfaceGLX() { Destroy(); } } // namespace gl