blob: 5b89bfdaad60e0b470cbe9ff049960a5e6f5329e [file] [log] [blame]
// Copyright 2020 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/gfx/linux/gpu_memory_buffer_support_x11.h"
#include <fcntl.h>
#include <xcb/xcb.h>
#include <memory>
#include "base/containers/contains.h"
#include "base/debug/crash_logging.h"
#include "base/posix/eintr_wrapper.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/buffer_usage_util.h"
#include "ui/gfx/linux/drm_util_linux.h"
#include "ui/gfx/linux/gbm_buffer.h"
#include "ui/gfx/linux/gbm_device.h"
#include "ui/gfx/linux/gbm_util.h"
#include "ui/gfx/linux/gbm_wrapper.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/dri3.h"
#include "ui/gfx/x/future.h"
namespace ui {
namespace {
// Obtain an authenticated DRM fd from X11 and create a GbmDevice with it.
std::unique_ptr<ui::GbmDevice> CreateX11GbmDevice() {
auto* connection = x11::Connection::Get();
// |connection| may be nullptr in headless mode.
if (!connection)
return nullptr;
auto& dri3 = connection->dri3();
if (!dri3.present())
return nullptr;
// Let the X11 server know the DRI3 client version. This is required to use
// the DRI3 extension. We don't care about the returned server version because
// we only use features from the original DRI3 interface.
dri3.QueryVersion({x11::Dri3::major_version, x11::Dri3::minor_version});
// Obtain an authenticated DRM fd.
auto reply = dri3.Open({connection->default_root(), 0}).Sync();
if (!reply)
return nullptr;
base::ScopedFD fd(HANDLE_EINTR(dup(reply->device_fd.get())));
if (!fd.is_valid())
return nullptr;
if (HANDLE_EINTR(fcntl(fd.get(), F_SETFD, FD_CLOEXEC)) == -1)
return nullptr;
return ui::CreateGbmDevice(fd.release());
}
std::vector<gfx::BufferUsageAndFormat> CreateSupportedConfigList(
ui::GbmDevice* device) {
if (!device)
return {};
std::vector<gfx::BufferUsageAndFormat> configs;
for (gfx::BufferUsage usage : {
gfx::BufferUsage::GPU_READ,
gfx::BufferUsage::SCANOUT,
gfx::BufferUsage::SCANOUT_CPU_READ_WRITE,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
}) {
for (gfx::BufferFormat format : {
gfx::BufferFormat::R_8,
gfx::BufferFormat::RG_88,
gfx::BufferFormat::RGBA_8888,
gfx::BufferFormat::RGBX_8888,
gfx::BufferFormat::BGRA_8888,
gfx::BufferFormat::BGRX_8888,
gfx::BufferFormat::BGRA_1010102,
// On some Intel setups calling gbm_bo_create() with this format
// results in a crash caused by an integer-divide-by-zero.
// TODO(thomasanderson): Enable this format.
// gfx::BufferFormat::RGBA_1010102,
gfx::BufferFormat::BGR_565,
gfx::BufferFormat::YUV_420_BIPLANAR,
gfx::BufferFormat::YVU_420,
gfx::BufferFormat::P010,
}) {
// At least on mesa/amdgpu, gbm_device_is_format_supported() lies. Test
// format support by creating a buffer directly. Use a 2x2 buffer so that
// YUV420 formats get properly tested.
if (device->CreateBuffer(GetFourCCFormatFromBufferFormat(format),
gfx::Size(2, 2), BufferUsageToGbmFlags(usage))) {
configs.push_back(gfx::BufferUsageAndFormat(usage, format));
}
}
}
return configs;
}
} // namespace
// static
GpuMemoryBufferSupportX11* GpuMemoryBufferSupportX11::GetInstance() {
static base::NoDestructor<GpuMemoryBufferSupportX11> instance;
return instance.get();
}
GpuMemoryBufferSupportX11::GpuMemoryBufferSupportX11()
: device_(CreateX11GbmDevice()),
supported_configs_(CreateSupportedConfigList(device_.get())) {}
GpuMemoryBufferSupportX11::~GpuMemoryBufferSupportX11() = default;
std::unique_ptr<GbmBuffer> GpuMemoryBufferSupportX11::CreateBuffer(
gfx::BufferFormat format,
const gfx::Size& size,
gfx::BufferUsage usage) {
DCHECK(device_);
DCHECK(base::Contains(supported_configs_,
gfx::BufferUsageAndFormat(usage, format)));
static base::debug::CrashKeyString* crash_key_string =
base::debug::AllocateCrashKeyString("buffer_usage_and_format",
base::debug::CrashKeySize::Size64);
std::string buffer_usage_and_format = gfx::BufferFormatToString(format) +
std::string(",") +
gfx::BufferUsageToString(usage);
base::debug::ScopedCrashKeyString scoped_crash_key(
crash_key_string, buffer_usage_and_format.c_str());
return device_->CreateBuffer(GetFourCCFormatFromBufferFormat(format), size,
BufferUsageToGbmFlags(usage));
}
} // namespace ui