blob: b3148aa0b2beb7c5e09172ed08bedd6ec273c861 [file] [log] [blame]
// 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 <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/synchronization/atomic_flag.h"
#include "base/synchronization/lock.h"
#include "base/task/single_thread_task_runner.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/base/x/visual_picker_glx.h"
#include "ui/base/x/x11_display_util.h"
#include "ui/base/x/x11_util.h"
#include "ui/base/x/x11_xrandr_interval_only_vsync_provider.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/x/xproto_util.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface_presentation_helper.h"
#include "ui/gl/glx_util.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_robustness_video_memory_purge_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.
x11::VisualId g_visual{};
int g_depth = static_cast<int>(x11::WindowClass::CopyFromParent);
x11::ColorMap g_colormap{};
bool CreateDummyWindow(x11::Connection* conn) {
DCHECK(conn);
auto parent_window = conn->default_root();
auto window = conn->GenerateId<x11::Window>();
auto create_window = conn->CreateWindow(x11::CreateWindowRequest{
.wid = window,
.parent = parent_window,
.width = 1,
.height = 1,
.c_class = x11::WindowClass::InputOutput,
});
if (create_window.Sync().error) {
LOG(ERROR) << "Failed to create window";
return false;
}
GLXFBConfig config = GetFbConfigForWindow(conn, window);
if (!config) {
LOG(ERROR) << "Failed to get GLXConfig";
conn->DestroyWindow({window});
return false;
}
GLXWindow glx_window = glXCreateWindow(
conn->GetXlibDisplay(), config, static_cast<uint32_t>(window), nullptr);
if (!glx_window) {
LOG(ERROR) << "glXCreateWindow failed";
conn->DestroyWindow({window});
return false;
}
glXDestroyWindow(conn->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
glx_window);
conn->DestroyWindow({window});
return true;
}
class OMLSyncControlVSyncProvider : public SyncControlVSyncProvider {
public:
explicit OMLSyncControlVSyncProvider(GLXWindow glx_window)
: SyncControlVSyncProvider(), glx_window_(glx_window) {}
OMLSyncControlVSyncProvider(const OMLSyncControlVSyncProvider&) = delete;
OMLSyncControlVSyncProvider& operator=(const OMLSyncControlVSyncProvider&) =
delete;
~OMLSyncControlVSyncProvider() override = default;
protected:
bool GetSyncValues(int64_t* system_time,
int64_t* media_stream_counter,
int64_t* swap_buffer_counter) override {
x11::Connection::Get()->Flush();
return glXGetSyncValuesOML(x11::Connection::Get()->GetXlibDisplay(),
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(x11::Connection::Get()->GetXlibDisplay(), 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_;
};
class SGIVideoSyncThread : public base::Thread,
public base::RefCounted<SGIVideoSyncThread> {
public:
// Create a connection to the X server for use on g_video_sync_thread before
// the sandbox starts.
static bool InitializeBeforeSandboxStarts() {
auto* connection = GetConnectionImpl();
if (!connection || !connection->Ready())
return false;
if (!CreateDummyWindow(connection)) {
LOG(ERROR) << "CreateDummyWindow(display) failed";
return false;
}
connection->DetachFromSequence();
return true;
}
static scoped_refptr<SGIVideoSyncThread> Create() {
if (!g_video_sync_thread) {
g_video_sync_thread = new SGIVideoSyncThread();
g_video_sync_thread->Start();
}
return g_video_sync_thread;
}
SGIVideoSyncThread(const SGIVideoSyncThread&) = delete;
SGIVideoSyncThread& operator=(const SGIVideoSyncThread&) = delete;
x11::Connection* GetConnection() {
DCHECK(task_runner()->BelongsToCurrentThread());
return GetConnectionImpl();
}
void MaybeCreateGLXContext(GLXFBConfig config) {
DCHECK(task_runner()->BelongsToCurrentThread());
if (!context_) {
context_ = glXCreateNewContext(
GetConnection()->GetXlibDisplay(x11::XlibDisplayType::kSyncing),
config, GLX_RGBA_TYPE, nullptr, true);
}
LOG_IF(ERROR, !context_) << "video_sync: glXCreateNewContext failed";
}
// Destroy |context_| on the thread where it is used.
void CleanUp() override {
DCHECK(task_runner()->BelongsToCurrentThread());
if (context_)
glXDestroyContext(
GetConnection()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
context_);
// Release the connection from this thread's sequence so that a new
// SGIVideoSyncThread can reuse the connection. The connection must be
// reused since it can only be created before sandbox initialization.
GetConnection()->DetachFromSequence();
}
GLXContext GetGLXContext() {
DCHECK(task_runner()->BelongsToCurrentThread());
return context_;
}
private:
friend class base::RefCounted<SGIVideoSyncThread>;
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 x11::Connection* GetConnectionImpl() {
if (!g_connection)
g_connection = x11::Connection::Get()->Clone().release();
return g_connection;
}
static SGIVideoSyncThread* g_video_sync_thread;
static x11::Connection* g_connection;
GLXContext context_ = nullptr;
THREAD_CHECKER(thread_checker_);
};
class SGIVideoSyncProviderThreadShim {
public:
SGIVideoSyncProviderThreadShim(gfx::AcceleratedWidget parent_window,
SGIVideoSyncThread* vsync_thread)
: parent_window_(parent_window),
vsync_thread_(vsync_thread),
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_|.
x11::Connection::Get()->Sync();
}
SGIVideoSyncProviderThreadShim(const SGIVideoSyncProviderThreadShim&) =
delete;
SGIVideoSyncProviderThreadShim& operator=(
const SGIVideoSyncProviderThreadShim&) = delete;
~SGIVideoSyncProviderThreadShim() {
auto* connection = vsync_thread_->GetConnection();
if (glx_window_) {
glXDestroyWindow(
connection->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
glx_window_);
}
if (window_ != x11::Window::None)
connection->DestroyWindow({window_});
}
base::AtomicFlag* cancel_vsync_flag() { return &cancel_vsync_flag_; }
base::Lock* vsync_lock() { return &vsync_lock_; }
void Initialize() {
auto* connection = vsync_thread_->GetConnection();
DCHECK(connection);
auto window = connection->GenerateId<x11::Window>();
auto req = connection->CreateWindow(x11::CreateWindowRequest{
.wid = window,
.parent = static_cast<x11::Window>(parent_window_),
.width = 1,
.height = 1,
.c_class = x11::WindowClass::InputOutput,
});
if (req.Sync().error) {
LOG(ERROR) << "video_sync: XCreateWindow failed";
return;
}
window_ = window;
GLXFBConfig config = GetFbConfigForWindow(connection, window_);
if (!config) {
LOG(ERROR) << "video_sync: Failed to get GLXConfig";
return;
}
glx_window_ = glXCreateWindow(
connection->GetXlibDisplay(x11::XlibDisplayType::kSyncing), config,
static_cast<uint32_t>(window_), nullptr);
if (!glx_window_) {
LOG(ERROR) << "video_sync: glXCreateWindow failed";
return;
}
vsync_thread_->MaybeCreateGLXContext(config);
}
void GetVSyncParameters(gfx::VSyncProvider::UpdateVSyncCallback callback) {
// Don't allow |window_| destruction while we're probing vsync.
base::AutoLock locked(vsync_lock_);
if (!vsync_thread_->GetGLXContext() || cancel_vsync_flag_.IsSet())
return;
base::TimeDelta interval = ui::GetPrimaryDisplayRefreshIntervalFromXrandr();
auto* connection = vsync_thread_->GetConnection();
glXMakeContextCurrent(
connection->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
glx_window_, glx_window_, vsync_thread_->GetGLXContext());
unsigned int retrace_count = 0;
if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
return;
base::TimeTicks now = base::TimeTicks::Now();
TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
glXMakeContextCurrent(
connection->GetXlibDisplay(x11::XlibDisplayType::kFlushing), 0, 0,
nullptr);
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), now, interval));
}
private:
gfx::AcceleratedWidget parent_window_;
SGIVideoSyncThread* vsync_thread_;
x11::Window window_ = x11::Window::None;
GLXWindow glx_window_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
base::AtomicFlag cancel_vsync_flag_;
base::Lock vsync_lock_;
};
class SGIVideoSyncVSyncProvider
: public gfx::VSyncProvider,
public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
public:
explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget parent_window)
: vsync_thread_(SGIVideoSyncThread::Create()),
shim_(new SGIVideoSyncProviderThreadShim(parent_window,
vsync_thread_.get())),
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(const SGIVideoSyncVSyncProvider&) = delete;
SGIVideoSyncVSyncProvider& operator=(const SGIVideoSyncVSyncProvider&) =
delete;
~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(
gfx::VSyncProvider::UpdateVSyncCallback callback) override {
// Only one outstanding request per surface.
if (!pending_callback_) {
DCHECK(callback);
pending_callback_ = std::move(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<SGIVideoSyncThread> vsync_thread_;
// Thread shim through which the sync provider is accessed on |vsync_thread_|.
std::unique_ptr<SGIVideoSyncProviderThreadShim> 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::AtomicFlag* cancel_vsync_flag_;
base::Lock* vsync_lock_;
};
SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = nullptr;
x11::Connection* SGIVideoSyncThread::g_connection = nullptr;
} // namespace
bool GLSurfaceGLX::initialized_ = false;
GLSurfaceGLX::GLSurfaceGLX() = default;
bool GLSurfaceGLX::InitializeOneOff() {
if (initialized_)
return true;
// http://crbug.com/245466
setenv("force_s3tc_enable", "true", 1);
if (!x11::Connection::Get()->Ready()) {
LOG(ERROR) << "Could not open X11 connection.";
return false;
}
int major = 0, minor = 0;
if (!glXQueryVersion(x11::Connection::Get()->GetXlibDisplay(), &major,
&minor)) {
LOG(ERROR) << "glxQueryVersion failed";
return false;
}
if (major == 1 && minor < 3) {
LOG(ERROR) << "GLX 1.3 or later is required.";
return false;
}
auto* visual_picker = ui::VisualPickerGlx::GetInstance();
auto visual_id = visual_picker->rgba_visual();
if (visual_id == x11::VisualId{})
visual_id = visual_picker->system_visual();
g_visual = visual_id;
auto* connection = x11::Connection::Get();
g_depth = connection->GetVisualInfoFromId(visual_id)->format->depth;
g_colormap = connection->GenerateId<x11::ColorMap>();
connection->CreateColormap({x11::ColormapAlloc::None, g_colormap,
connection->default_root(), g_visual});
// 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(x11::Connection::Get())) {
LOG(ERROR) << "CreateDummyWindow() failed";
return false;
}
initialized_ = true;
return true;
}
// static
bool GLSurfaceGLX::InitializeExtensionSettingsOneOff() {
if (!initialized_)
return false;
g_driver_glx.InitializeExtensionBindings();
g_glx_context_create = HasGLXExtension("GLX_ARB_create_context");
g_glx_create_context_robustness_supported =
HasGLXExtension("GLX_ARB_create_context_robustness");
g_glx_robustness_video_memory_purge_supported =
HasGLXExtension("GLX_NV_robustness_video_memory_purge");
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) {
if (!SGIVideoSyncThread::InitializeBeforeSandboxStarts())
return false;
}
return true;
}
// static
void GLSurfaceGLX::ShutdownOneOff() {
initialized_ = false;
g_glx_context_create = false;
g_glx_create_context_robustness_supported = false;
g_glx_robustness_video_memory_purge_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 = {};
g_depth = static_cast<int>(x11::WindowClass::CopyFromParent);
g_colormap = {};
}
// static
std::string GLSurfaceGLX::QueryGLXExtensions() {
auto* connection = x11::Connection::Get();
const int screen = connection ? connection->DefaultScreenId() : 0;
const char* extensions =
glXQueryExtensionsString(connection->GetXlibDisplay(), screen);
if (extensions)
return std::string(extensions);
return "";
}
// static
const char* GLSurfaceGLX::GetGLXExtensions() {
static base::NoDestructor<std::string> glx_extensions("");
if (glx_extensions->empty()) {
*glx_extensions = QueryGLXExtensions();
}
return glx_extensions->c_str();
}
// 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::IsRobustnessVideoMemoryPurgeSupported() {
return g_glx_robustness_video_memory_purge_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 x11::Connection::Get()->GetXlibDisplay();
}
GLSurfaceGLX::~GLSurfaceGLX() = default;
NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
: parent_window_(window),
window_(x11::Window::None),
glx_window_(),
config_(nullptr),
has_swapped_buffers_(false) {}
bool NativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) {
auto* conn = x11::Connection::Get();
auto parent = static_cast<x11::Window>(parent_window_);
auto attributes_req = conn->GetWindowAttributes({parent});
auto geometry_req = conn->GetGeometry(parent);
conn->Flush();
auto attributes = attributes_req.Sync();
auto geometry = geometry_req.Sync();
if (!attributes || !geometry) {
LOG(ERROR) << "GetGeometry/GetWindowAttribues failed for window "
<< static_cast<uint32_t>(parent_window_) << ".";
return false;
}
size_ = gfx::Size(geometry->width, geometry->height);
window_ = conn->GenerateId<x11::Window>();
x11::CreateWindowRequest req{
.depth = static_cast<uint8_t>(g_depth),
.wid = window_,
.parent = static_cast<x11::Window>(parent_window_),
.width = static_cast<uint16_t>(size_.width()),
.height = static_cast<uint16_t>(size_.height()),
.c_class = x11::WindowClass::InputOutput,
.visual = g_visual,
.background_pixmap = x11::Pixmap::None,
.border_pixel = 0,
.bit_gravity = x11::Gravity::NorthWest,
.colormap = g_colormap,
};
if (ui::IsCompositingManagerPresent() && attributes->visual == g_visual) {
// When parent and child are using the same visual, the back buffer will be
// shared between parent and child. If WM compositing is enabled, we set
// child's background pixel to ARGB(0,0,0,0), so ARGB(0,0,0,0) will be
// filled to the shared buffer, when the child window is mapped. It can
// avoid an annoying flash when the child window is mapped below.
// If WM compositing is disabled, we don't set the background pixel, so
// nothing will be draw when the child window is mapped.
req.background_pixel = 0; // ARGB(0,0,0,0) for compositing WM
}
conn->CreateWindow(req);
conn->MapWindow({window_});
RegisterEvents();
conn->Sync();
GetConfig();
if (!config_) {
LOG(ERROR) << "Failed to get GLXConfig";
return false;
}
glx_window_ = static_cast<x11::Glx::Window>(
glXCreateWindow(conn->GetXlibDisplay(x11::XlibDisplayType::kSyncing),
config_, static_cast<uint32_t>(window_), nullptr));
if (!GetDrawableHandle()) {
LOG(ERROR) << "glXCreateWindow failed";
return false;
}
if (g_glx_oml_sync_control_supported) {
vsync_provider_ = std::make_unique<OMLSyncControlVSyncProvider>(
static_cast<GLXWindow>(glx_window_));
presentation_helper_ =
std::make_unique<GLSurfacePresentationHelper>(vsync_provider_.get());
} else if (g_glx_sgi_video_sync_supported) {
vsync_provider_ =
std::make_unique<SGIVideoSyncVSyncProvider>(parent_window_);
presentation_helper_ =
std::make_unique<GLSurfacePresentationHelper>(vsync_provider_.get());
} else {
vsync_provider_ = std::make_unique<ui::XrandrIntervalOnlyVSyncProvider>();
presentation_helper_ = std::make_unique<GLSurfacePresentationHelper>(
base::TimeTicks(), ui::GetPrimaryDisplayRefreshIntervalFromXrandr());
}
return true;
}
void NativeViewGLSurfaceGLX::Destroy() {
presentation_helper_ = nullptr;
vsync_provider_ = nullptr;
if (GetDrawableHandle()) {
glXDestroyWindow(
x11::Connection::Get()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
GetDrawableHandle());
glx_window_ = {};
}
if (window_ != x11::Window::None) {
UnregisterEvents();
x11::Connection::Get()->DestroyWindow({window_});
window_ = x11::Window::None;
x11::Connection::Get()->Flush();
}
}
bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size,
float scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha) {
size_ = size;
glXWaitGL();
x11::Connection::Get()->ConfigureWindow(
{.window = window_, .width = size.width(), .height = size.height()});
glXWaitX();
return true;
}
bool NativeViewGLSurfaceGLX::IsOffscreen() {
return false;
}
gfx::SwapResult NativeViewGLSurfaceGLX::SwapBuffers(
PresentationCallback callback) {
TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers", "width",
GetSize().width(), "height", GetSize().height());
GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers(
presentation_helper_.get(), std::move(callback));
auto* connection = x11::Connection::Get();
glXSwapBuffers(connection->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
GetDrawableHandle());
// We need to restore the background pixel that we set to WhitePixel on
// views::DesktopWindowTreeHostX11::InitX11Window back to None for the
// XWindow associated to this surface after the first SwapBuffers has
// happened, to avoid showing a weird white background while resizing.
if (!has_swapped_buffers_) {
connection->ChangeWindowAttributes({
.window = static_cast<x11::Window>(parent_window_),
.background_pixmap = x11::Pixmap::None,
});
has_swapped_buffers_ = true;
}
return scoped_swap_buffers.result();
}
gfx::Size NativeViewGLSurfaceGLX::GetSize() {
return size_;
}
void* NativeViewGLSurfaceGLX::GetHandle() {
return reinterpret_cast<void*>(GetDrawableHandle());
}
bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
return g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
}
void* NativeViewGLSurfaceGLX::GetConfig() {
if (!config_)
config_ = GetFbConfigForWindow(x11::Connection::Get(), window_);
return config_;
}
GLSurfaceFormat NativeViewGLSurfaceGLX::GetFormat() {
return GLSurfaceFormat();
}
gfx::SwapResult NativeViewGLSurfaceGLX::PostSubBuffer(
int x,
int y,
int width,
int height,
PresentationCallback callback) {
DCHECK(g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers(
presentation_helper_.get(), std::move(callback));
glXCopySubBufferMESA(
x11::Connection::Get()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
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();
}
void NativeViewGLSurfaceGLX::SetVSyncEnabled(bool enabled) {
DCHECK(GLContext::GetCurrent() && GLContext::GetCurrent()->IsCurrent(this));
int interval = enabled ? 1 : 0;
if (GLSurfaceGLX::IsEXTSwapControlSupported()) {
glXSwapIntervalEXT(
x11::Connection::Get()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
GetDrawableHandle(), interval);
} else if (GLSurfaceGLX::IsMESASwapControlSupported()) {
glXSwapIntervalMESA(interval);
} else if (interval == 0) {
LOG(WARNING)
<< "Could not disable vsync: driver does not support swap control";
}
}
NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
Destroy();
}
void NativeViewGLSurfaceGLX::ForwardExposeEvent(const x11::Event& event) {
auto forwarded_event = *event.As<x11::ExposeEvent>();
auto window = static_cast<x11::Window>(parent_window_);
forwarded_event.window = window;
x11::SendEvent(forwarded_event, window, x11::EventMask::Exposure);
x11::Connection::Get()->Flush();
}
bool NativeViewGLSurfaceGLX::CanHandleEvent(const x11::Event& x11_event) {
auto* expose = x11_event.As<x11::ExposeEvent>();
return expose && expose->window == static_cast<x11::Window>(window_);
}
uint32_t NativeViewGLSurfaceGLX::GetDrawableHandle() const {
return static_cast<uint32_t>(glx_window_);
}
UnmappedNativeViewGLSurfaceGLX::UnmappedNativeViewGLSurfaceGLX(
const gfx::Size& size)
: size_(size), config_(nullptr), window_(x11::Window::None), glx_window_() {
// 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_EQ(window_, x11::Window::None);
auto parent_window = ui::GetX11RootWindow();
auto* conn = x11::Connection::Get();
window_ = conn->GenerateId<x11::Window>();
conn->CreateWindow(x11::CreateWindowRequest{
.depth = static_cast<uint8_t>(g_depth),
.wid = window_,
.parent = parent_window,
.width = static_cast<uint16_t>(size_.width()),
.height = static_cast<uint16_t>(size_.height()),
.c_class = x11::WindowClass::InputOutput,
.visual = g_visual,
.border_pixel = 0,
.colormap = g_colormap,
})
.Sync();
GetConfig();
if (!config_) {
LOG(ERROR) << "Failed to get GLXConfig";
return false;
}
glx_window_ = static_cast<x11::Glx::Window>(
glXCreateWindow(conn->GetXlibDisplay(x11::XlibDisplayType::kSyncing),
config_, static_cast<uint32_t>(window_), nullptr));
if (glx_window_ == x11::Glx::Window{}) {
LOG(ERROR) << "glXCreateWindow failed";
return false;
}
return true;
}
void UnmappedNativeViewGLSurfaceGLX::Destroy() {
config_ = nullptr;
if (glx_window_ != x11::Glx::Window{}) {
glXDestroyWindow(
x11::Connection::Get()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
static_cast<uint32_t>(glx_window_));
glx_window_ = {};
}
if (window_ != x11::Window::None) {
x11::Connection::Get()->DestroyWindow({window_});
window_ = x11::Window::None;
}
}
bool UnmappedNativeViewGLSurfaceGLX::IsOffscreen() {
return true;
}
gfx::SwapResult UnmappedNativeViewGLSurfaceGLX::SwapBuffers(
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<void*>(glx_window_);
}
void* UnmappedNativeViewGLSurfaceGLX::GetConfig() {
if (!config_)
config_ = GetFbConfigForWindow(x11::Connection::Get(), window_);
return config_;
}
GLSurfaceFormat UnmappedNativeViewGLSurfaceGLX::GetFormat() {
return GLSurfaceFormat();
}
UnmappedNativeViewGLSurfaceGLX::~UnmappedNativeViewGLSurfaceGLX() {
Destroy();
}
} // namespace gl