blob: e16a2b4ff890143b0e2be2da316a58abcecbe049 [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 "components/viz/service/display_embedder/output_presenter_x11.h"
extern "C" {
#include <X11/xshmfence.h>
}
#include <algorithm>
#include <utility>
#include "base/memory/ref_counted.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/types/pass_key.h"
#include "build/build_config.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/service/display_embedder/skia_output_surface_dependency.h"
#include "gpu/command_buffer/service/external_vk_image_backing.h"
#include "gpu/command_buffer/service/external_vk_image_skia_representation.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_image.h"
#include "gpu/vulkan/vulkan_util.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/dri3.h"
#include "ui/gfx/x/future.h"
#include "ui/gfx/x/present.h"
#define VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO_MESA (VkStructureType)1000001002
struct wsi_image_create_info {
VkStructureType sType;
const void* pNext;
bool scanout;
};
namespace viz {
namespace {
std::vector<VkSemaphore> ToVkSemaphores(
std::vector<GrBackendSemaphore>& semaphores) {
std::vector<VkSemaphore> vk_semaphores(semaphores.size());
for (size_t i = 0; i < semaphores.size(); ++i)
vk_semaphores[i] = semaphores[i].vkSemaphore();
return vk_semaphores;
}
class Fence {
public:
static std::unique_ptr<Fence> Create(x11::Pixmap pixmap) {
base::ScopedFD fence_fd(xshmfence_alloc_shm());
if (!fence_fd.is_valid()) {
DLOG(ERROR) << "xshmfence_alloc_shm() failed!";
return {};
}
auto* shm_fence = xshmfence_map_shm(fence_fd.get());
if (!shm_fence) {
DLOG(ERROR) << "xshmfence_map_shm() failed!";
return {};
}
auto* connection = x11::Connection::Get();
auto* dri3 = &connection->dri3();
auto fence = connection->GenerateId<x11::Sync::Fence>();
dri3->FenceFromFD(
{pixmap, static_cast<uint32_t>(fence), 1, std::move(fence_fd)});
return std::make_unique<Fence>(base::PassKey<Fence>(), shm_fence, fence);
}
Fence(base::PassKey<Fence>, xshmfence* shm_fence, x11::Sync::Fence fence)
: shm_fence_(shm_fence), fence_(fence) {}
~Fence() {
auto* connection = x11::Connection::Get();
xshmfence_unmap_shm(shm_fence_);
connection->sync().DestroyFence({fence_});
}
void Reset() { xshmfence_reset(shm_fence_); }
void Wait() { xshmfence_await(shm_fence_); }
x11::Sync::Fence fence() const { return fence_; }
private:
xshmfence* const shm_fence_;
const x11::Sync::Fence fence_;
};
class PresenterImageX11 : public OutputPresenter::Image {
public:
PresenterImageX11();
~PresenterImageX11() override;
bool Initialize(x11::Window window,
gpu::SharedImageFactory* factory,
gpu::SharedImageRepresentationFactory* representation_factory,
SkiaOutputSurfaceDependency* deps,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
ResourceFormat format,
int depth,
uint32_t shared_image_usage,
const std::vector<std::vector<uint64_t>>& modifier_vectors,
scoped_refptr<base::SingleThreadTaskRunner> x11_task_runner);
// OutputPresenterX11::Image:
void BeginPresent() final;
void EndPresent() final;
int GetPresentCount() const final;
void OnContextLost() final;
class OnX11 : public base::RefCountedThreadSafe<OnX11> {
public:
OnX11(gpu::VulkanDeviceQueue* device_queue, VkFence vk_fence);
void Initialize(x11::Dri3::PixmapFromBuffersRequest request);
void WaitForVkFence();
VkFence vk_fence() const { return vk_fence_; }
x11::Pixmap pixmap() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return pixmap_;
}
Fence* idle_fence() const { return idle_fence_.get(); }
bool busy() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return busy_;
}
void set_busy(bool busy) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
busy_ = busy;
}
private:
friend base::RefCountedThreadSafe<OnX11>;
~OnX11();
gpu::VulkanDeviceQueue* const device_queue_;
const VkFence vk_fence_;
x11::Pixmap pixmap_ GUARDED_BY_CONTEXT(thread_checker_){};
std::unique_ptr<Fence> idle_fence_;
bool busy_ GUARDED_BY_CONTEXT(thread_checker_) = false;
THREAD_CHECKER(thread_checker_);
};
scoped_refptr<OnX11> on_x11() const { return on_x11_; }
private:
scoped_refptr<base::SingleThreadTaskRunner> x11_task_runner_;
std::unique_ptr<gpu::SharedImageRepresentationSkia::ScopedReadAccess>
scoped_read_access_;
int present_count_ = 0;
gpu::VulkanDeviceQueue* device_queue_ = nullptr;
std::vector<GrBackendSemaphore> end_read_semaphores_;
scoped_refptr<OnX11> on_x11_;
};
PresenterImageX11::OnX11::OnX11(gpu::VulkanDeviceQueue* device_queue,
VkFence vk_fence)
: device_queue_(device_queue), vk_fence_(vk_fence) {
DETACH_FROM_THREAD(thread_checker_);
}
PresenterImageX11::OnX11::~OnX11() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (vk_fence_ != VK_NULL_HANDLE)
vkDestroyFence(device_queue_->GetVulkanDevice(), vk_fence_, nullptr);
if (pixmap_ != x11::Pixmap::None)
x11::Connection::Get()->FreePixmap({pixmap_});
}
void PresenterImageX11::OnX11::Initialize(
x11::Dri3::PixmapFromBuffersRequest request) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto* connection = x11::Connection::Get();
auto* dri3 = &connection->dri3();
pixmap_ = connection->GenerateId<x11::Pixmap>();
request.pixmap = pixmap_;
dri3->PixmapFromBuffers(request);
idle_fence_ = Fence::Create(pixmap_);
CHECK(idle_fence_);
}
void PresenterImageX11::OnX11::WaitForVkFence() {
auto result = vkWaitForFences(device_queue_->GetVulkanDevice(), 1, &vk_fence_,
VK_TRUE, UINT64_MAX);
DCHECK_EQ(result, VK_SUCCESS);
}
PresenterImageX11::PresenterImageX11() = default;
PresenterImageX11::~PresenterImageX11() {
if (on_x11_)
x11_task_runner_->ReleaseSoon(FROM_HERE, std::move(on_x11_));
}
bool PresenterImageX11::Initialize(
x11::Window window,
gpu::SharedImageFactory* factory,
gpu::SharedImageRepresentationFactory* representation_factory,
SkiaOutputSurfaceDependency* deps,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
ResourceFormat format,
int depth,
uint32_t shared_image_usage,
const std::vector<std::vector<uint64_t>>& modifier_vectors,
scoped_refptr<base::SingleThreadTaskRunner> x11_task_runner) {
DCHECK(format == RGBA_8888 || format == BGRA_8888);
device_queue_ = deps->GetVulkanContextProvider()->GetDeviceQueue();
x11_task_runner_ = std::move(x11_task_runner);
// OutputPresenterX11 only supports MESA vulkan driver.
switch (device_queue_->vk_physical_device_driver_properties().driverID) {
case VK_DRIVER_ID_MESA_RADV:
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
break;
default:
return false;
}
const auto vk_format = ToVkFormat(format);
std::unique_ptr<gpu::VulkanImage> vulkan_image;
if (modifier_vectors.empty()) {
// If DRM modifier is not supported, lagecy wsi_image_create_info will be
// used for scanout.
wsi_image_create_info create_info = {
.sType = VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO_MESA,
.scanout = true,
};
vulkan_image = gpu::VulkanImage::CreateWithExternalMemory(
device_queue_, size, vk_format, shared_image_usage, /*flags=*/0,
VK_IMAGE_TILING_OPTIMAL, &create_info);
} else {
for (auto& modifiers : modifier_vectors) {
vulkan_image = gpu::VulkanImage::CreateWithExternalMemoryAndModifiers(
device_queue_, size, vk_format, modifiers, shared_image_usage,
/*flags=*/0);
if (vulkan_image)
break;
}
}
if (!vulkan_image)
return false;
// Destroy the |vulkan_image| when this function returns. The |vulkan_image|
// will be imported as a SharedImage, and the original |vulkan_image| will
// not be used after returning of this function.
// TODO(penghuang): maybe creating the shared image directly.
base::ScopedClosureRunner vulkan_image_destructor(base::BindOnce(
&gpu::VulkanImage::Destroy, base::Unretained(vulkan_image.get())));
const auto& layouts = vulkan_image->layouts();
gfx::GpuMemoryBufferHandle gmb_handle;
gmb_handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP;
gmb_handle.native_pixmap_handle.modifier = vulkan_image->modifier();
for (size_t i = 0; i < vulkan_image->plane_count(); ++i) {
gmb_handle.native_pixmap_handle.planes.emplace_back(
layouts[i].rowPitch, layouts[i].offset, layouts[i].size,
vulkan_image->GetMemoryFd());
}
auto mailbox = gpu::Mailbox::GenerateForSharedImage();
if (!factory->CreateSharedImage(
mailbox, 0, std::move(gmb_handle), BufferFormat(format),
deps->GetSurfaceHandle(), size, color_space, kTopLeft_GrSurfaceOrigin,
kPremul_SkAlphaType, shared_image_usage)) {
DLOG(ERROR) << "CreateSharedImage failed.";
return false;
}
if (!Image::Initialize(factory, representation_factory, mailbox, deps))
return false;
VkFenceCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
VkFence vk_fence = VK_NULL_HANDLE;
if (vkCreateFence(device_queue_->GetVulkanDevice(), &create_info, nullptr,
&vk_fence) != VK_SUCCESS)
return false;
// The ownership of |vk_fence| is passed to OnX11 which will destroy it.
on_x11_ = base::MakeRefCounted<OnX11>(device_queue_, vk_fence);
std::vector<base::ScopedFD> fds(vulkan_image->plane_count());
for (size_t i = 0; i < vulkan_image->plane_count(); ++i)
fds[i] = vulkan_image->GetMemoryFd();
x11::Dri3::PixmapFromBuffersRequest request = {
.window = window,
.width = size.width(),
.height = size.height(),
.stride0 = layouts[0].rowPitch,
.offset0 = layouts[0].offset,
.stride1 = layouts[1].rowPitch,
.offset1 = layouts[1].offset,
.stride2 = layouts[2].rowPitch,
.offset2 = layouts[2].offset,
.stride3 = layouts[3].rowPitch,
.offset3 = layouts[3].offset,
.depth = depth,
.bpp = 32,
.modifier = vulkan_image->modifier(),
.buffers = std::move(fds)};
x11_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&OnX11::Initialize, on_x11_, std::move(request)));
return true;
}
void PresenterImageX11::BeginPresent() {
if (++present_count_ != 1) {
DCHECK(scoped_read_access_);
return;
}
DCHECK(!sk_surface());
DCHECK(!scoped_read_access_);
std::vector<GrBackendSemaphore> begin_read_semaphores;
scoped_read_access_ = skia_representation()->BeginScopedReadAccess(
&begin_read_semaphores, &end_read_semaphores_);
DCHECK(scoped_read_access_);
VkFence vk_fence = on_x11_->vk_fence();
vkResetFences(device_queue_->GetVulkanDevice(), 1, &vk_fence);
auto vk_semaphores = ToVkSemaphores(begin_read_semaphores);
if (!gpu::SubmitWaitVkSemaphores(device_queue_->GetVulkanQueue(),
vk_semaphores, vk_fence)) {
NOTREACHED();
}
}
void PresenterImageX11::EndPresent() {
DCHECK(present_count_);
if (--present_count_)
return;
auto vk_semaphores = ToVkSemaphores(end_read_semaphores_);
end_read_semaphores_.clear();
// Wait on the idle_fence on GPU main, after it is released, we can reuse the
// image safely.
on_x11_->idle_fence()->Wait();
gpu::SubmitSignalVkSemaphores(device_queue_->GetVulkanQueue(), vk_semaphores);
scoped_read_access_.reset();
}
int PresenterImageX11::GetPresentCount() const {
return present_count_;
}
void PresenterImageX11::OnContextLost() {}
constexpr size_t kNumberOfBuffers = 3;
constexpr size_t kMaxPendingFrames = 2;
} // namespace
class OutputPresenterX11::OnX11 : public x11::EventObserver {
public:
explicit OnX11(x11::Window window);
~OnX11() override;
void Initialize();
void PostSubBuffer(scoped_refptr<PresenterImageX11::OnX11> image,
const gfx::Rect& rect,
SwapCompletionCallback completion_callback,
BufferPresentedCallback presentation_callback);
private:
// x11::EventObserver implementations:
void OnEvent(const x11::Event& event) final;
bool OnCompleteNotifyEvent(const x11::Present::CompleteNotifyEvent* event);
bool OnIdleNotifyEvent(const x11::Present::IdleNotifyEvent* event);
const x11::Window window_ GUARDED_BY_CONTEXT(thread_checker_);
// For executing task on GPU main thread.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_
GUARDED_BY_CONTEXT(thread_checker_);
std::unique_ptr<ui::X11EventSource> event_source_
GUARDED_BY_CONTEXT(thread_checker_);
uint32_t event_id_ GUARDED_BY_CONTEXT(thread_checker_) = 0;
uint64_t last_target_msc_ GUARDED_BY_CONTEXT(thread_checker_) = 0;
uint64_t last_present_msc_ GUARDED_BY_CONTEXT(thread_checker_) = 0;
// Image in present queue.
base::circular_deque<scoped_refptr<PresenterImageX11::OnX11>> present_images_
GUARDED_BY_CONTEXT(thread_checker_);
// Callbacks wait for X11 CompleteNotifyEvent
base::circular_deque<SwapCompletionCallback> swap_completion_callbacks_
GUARDED_BY_CONTEXT(thread_checker_);
base::circular_deque<BufferPresentedCallback> presentation_callbacks_
GUARDED_BY_CONTEXT(thread_checker_);
THREAD_CHECKER(thread_checker_);
};
OutputPresenterX11::OnX11::OnX11(x11::Window window)
: window_(window), task_runner_(base::ThreadTaskRunnerHandle::Get()) {
DETACH_FROM_THREAD(thread_checker_);
}
OutputPresenterX11::OnX11::~OnX11() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto* connection = x11::Connection::Get();
auto* present = &connection->present();
present->SelectInput({static_cast<x11::Present::Event>(event_id_), window_,
x11::Present::EventMask::NoEvent});
connection->RemoveEventObserver(this);
}
void OutputPresenterX11::OnX11::Initialize() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto* connection = x11::Connection::Get();
event_source_ = std::make_unique<ui::X11EventSource>(connection);
connection->AddEventObserver(this);
auto* present = &connection->present();
event_id_ = connection->GenerateId<uint32_t>();
constexpr auto kEventMasks = x11::Present::EventMask::CompleteNotify |
x11::Present::EventMask::IdleNotify;
present->SelectInput(
{static_cast<x11::Present::Event>(event_id_), window_, kEventMasks});
}
void OutputPresenterX11::OnX11::PostSubBuffer(
scoped_refptr<PresenterImageX11::OnX11> image,
const gfx::Rect& rect,
SwapCompletionCallback completion_callback,
BufferPresentedCallback presentation_callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!image->busy());
image->set_busy(true);
// Wait for VKFence passed before sending pixmap to Xserver to present.
image->WaitForVkFence();
last_target_msc_ = std::max(last_present_msc_, last_target_msc_) + 1;
x11::Connection::Get()->present().Pixmap({
.window = window_,
.pixmap = image->pixmap(),
.idle_fence = image->idle_fence()->fence(),
.target_msc = last_target_msc_,
});
DCHECK_LT(present_images_.size(), kNumberOfBuffers);
present_images_.push_back(image);
swap_completion_callbacks_.push_back(std::move(completion_callback));
presentation_callbacks_.push_back(std::move(presentation_callback));
}
void OutputPresenterX11::OnX11::OnEvent(const x11::Event& event) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (event.window() != window_)
return;
if (auto* e = event.As<x11::Present::CompleteNotifyEvent>())
OnCompleteNotifyEvent(e);
else if (auto* e = event.As<x11::Present::IdleNotifyEvent>())
OnIdleNotifyEvent(e);
}
bool OutputPresenterX11::OnX11::OnCompleteNotifyEvent(
const x11::Present::CompleteNotifyEvent* event) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_LE(last_present_msc_, event->msc);
last_present_msc_ = event->msc;
auto timestamp =
base::TimeTicks() + base::TimeDelta::FromMicroseconds(event->ust);
// Assume the refresh rate is 60 Hz for now.
// TODO(penghuang): query refresh rate from Xserver.
constexpr auto kInterval = base::TimeDelta::FromMicroseconds(
base::Time::kMicrosecondsPerSecond / 60);
uint32_t flags = gfx::PresentationFeedback::kVSync;
if (event->mode == x11::Present::CompleteMode::Flip)
flags |= gfx::PresentationFeedback::kZeroCopy;
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(swap_completion_callbacks_.front()),
gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK)));
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(presentation_callbacks_.front()),
gfx::PresentationFeedback(timestamp, kInterval, flags)));
swap_completion_callbacks_.pop_front();
presentation_callbacks_.pop_front();
return true;
}
bool OutputPresenterX11::OnX11::OnIdleNotifyEvent(
const x11::Present::IdleNotifyEvent* event) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto& image : present_images_) {
if (image->pixmap() == event->pixmap) {
DCHECK(image->busy());
image->set_busy(false);
break;
}
}
// Remove idle images at the beginning of the |present_images_|.
while (!present_images_.empty()) {
auto& image = present_images_.front();
if (image->busy())
break;
present_images_.pop_front();
}
return true;
}
// static
const uint32_t OutputPresenterX11::kDefaultSharedImageUsage =
gpu::SHARED_IMAGE_USAGE_SCANOUT | gpu::SHARED_IMAGE_USAGE_DISPLAY |
gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
// static
std::unique_ptr<OutputPresenterX11> OutputPresenterX11::Create(
SkiaOutputSurfaceDependency* deps,
gpu::SharedImageFactory* factory,
gpu::SharedImageRepresentationFactory* representation_factory) {
auto presenter = std::make_unique<OutputPresenterX11>(deps, factory,
representation_factory);
if (!presenter->Initialize())
presenter.reset();
return presenter;
}
OutputPresenterX11::OutputPresenterX11(
SkiaOutputSurfaceDependency* deps,
gpu::SharedImageFactory* factory,
gpu::SharedImageRepresentationFactory* representation_factory,
uint32_t shared_image_usage)
: dependency_(deps),
shared_image_factory_(factory),
shared_image_representation_factory_(representation_factory),
shared_image_usage_(shared_image_usage) {}
OutputPresenterX11::~OutputPresenterX11() {
if (on_x11_)
x11_thread_->task_runner()->DeleteSoon(FROM_HERE, std::move(on_x11_));
// The dtor of |x11_thread_| will be blocked until all post tasks are
// finished.
x11_thread_.reset();
}
bool OutputPresenterX11::Initialize() {
if (!dependency_->IsUsingVulkan())
return false;
auto* device_queue = dependency_->GetSharedContextState()
->vk_context_provider()
->GetDeviceQueue();
// OutputPresenterX11 only supports MESA vulkan driver.
switch (device_queue->vk_physical_device_driver_properties().driverID) {
case VK_DRIVER_ID_MESA_RADV:
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
break;
default:
return false;
}
auto* connection = x11::Connection::Get();
auto* present = &connection->present();
if (!present->present())
return false;
auto* dri3 = &connection->dri3();
if (!dri3->present())
return false;
auto* sync = &connection->sync();
if (!sync->present())
return false;
auto window = static_cast<x11::Window>(dependency_->GetSurfaceHandle());
auto geometry = connection->GetGeometry({window}).Sync();
depth_ = geometry->depth;
const bool support_modifier =
gfx::HasExtension(device_queue->enabled_extensions(),
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
if (auto modifiers = dri3->GetSupportedModifiers(
{static_cast<uint32_t>(window), depth_, 32})
.Sync()) {
if (!modifiers->window_modifiers.empty())
modifier_vectors_.push_back(std::move(modifiers->window_modifiers));
if (!modifiers->screen_modifiers.empty())
modifier_vectors_.push_back(std::move(modifiers->screen_modifiers));
}
// If Xserver require pixmap with modifier, but Vulkan doesn't support
// modifier, we cannot use X11 present.
if (!modifier_vectors_.empty() && !support_modifier)
return false;
// Without modifier, Xserver can only handle BGRA format.
supports_rgba_ = !modifier_vectors_.empty();
x11_thread_ = std::make_unique<base::Thread>("OutputPresenterX11");
bool result = x11_thread_->StartWithOptions(
base::Thread::Options(base::MessagePumpType::UI, 0));
CHECK(result);
on_x11_ = std::make_unique<OnX11>(window);
x11_thread_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&OutputPresenterX11::OnX11::Initialize,
base::Unretained(on_x11_.get())));
return true;
}
void OutputPresenterX11::InitializeCapabilities(
OutputSurface::Capabilities* capabilities) {
capabilities->number_of_buffers = kNumberOfBuffers;
capabilities->max_frames_pending = kMaxPendingFrames;
capabilities->supports_post_sub_buffer = true;
capabilities->supports_commit_overlay_planes = false;
// Set supports_surfaceless to enable overlays.
capabilities->supports_surfaceless = true;
// We expect origin of buffers is at top left.
capabilities->output_surface_origin = gfx::SurfaceOrigin::kTopLeft;
// TODO(https://crbug.com/1108406): only add supported formats base on
// platform, driver, etc.
capabilities->sk_color_types[static_cast<int>(gfx::BufferFormat::RGBA_8888)] =
supports_rgba_ ? kRGBA_8888_SkColorType : kBGRA_8888_SkColorType;
capabilities->sk_color_types[static_cast<int>(gfx::BufferFormat::BGRA_8888)] =
kBGRA_8888_SkColorType;
}
bool OutputPresenterX11::Reshape(const gfx::Size& size,
float device_scale_factor,
const gfx::ColorSpace& color_space,
gfx::BufferFormat format,
gfx::OverlayTransform transform) {
DCHECK(format == gfx::BufferFormat::RGBA_8888 ||
format == gfx::BufferFormat::BGRA_8888);
switch (format) {
case gfx::BufferFormat::RGBA_8888:
image_format_ = supports_rgba_ ? RGBA_8888 : BGRA_8888;
break;
case gfx::BufferFormat::BGRA_8888:
image_format_ = BGRA_8888;
break;
default:
NOTREACHED();
}
return true;
}
std::vector<std::unique_ptr<OutputPresenter::Image>>
OutputPresenterX11::AllocateImages(gfx::ColorSpace color_space,
gfx::Size image_size,
size_t num_images) {
DCHECK_EQ(num_images, kNumberOfBuffers);
auto window = static_cast<x11::Window>(dependency_->GetSurfaceHandle());
std::vector<std::unique_ptr<Image>> images(num_images);
for (size_t i = 0; i < num_images; ++i) {
auto image = std::make_unique<PresenterImageX11>();
if (!image->Initialize(window, shared_image_factory_,
shared_image_representation_factory_, dependency_,
image_size, color_space, image_format_, depth_,
shared_image_usage_, modifier_vectors_,
x11_thread_->task_runner())) {
DLOG(ERROR) << "Failed to initialize image.";
return {};
}
images[i] = std::move(image);
}
return images;
}
void OutputPresenterX11::SwapBuffers(
SwapCompletionCallback completion_callback,
BufferPresentedCallback presentation_callback) {
NOTIMPLEMENTED();
}
void OutputPresenterX11::PostSubBuffer(
const gfx::Rect& rect,
SwapCompletionCallback completion_callback,
BufferPresentedCallback presentation_callback) {
DCHECK(scheduled_image_);
auto image = static_cast<PresenterImageX11*>(scheduled_image_)->on_x11();
// Reset the |idle_fence()| which will be released when X11 is done with the
// pixmap.
image->idle_fence()->Reset();
x11_thread_->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&OutputPresenterX11::OnX11::PostSubBuffer,
base::Unretained(on_x11_.get()), std::move(image), rect,
std::move(completion_callback),
std::move(presentation_callback)));
scheduled_image_ = nullptr;
}
void OutputPresenterX11::SchedulePrimaryPlane(
const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane,
Image* image,
bool is_submitted) {
DCHECK(!scheduled_image_);
scheduled_image_ = image;
}
void OutputPresenterX11::CommitOverlayPlanes(
SwapCompletionCallback completion_callback,
BufferPresentedCallback presentation_callback) {
NOTIMPLEMENTED();
}
void OutputPresenterX11::ScheduleOverlays(
SkiaOutputSurface::OverlayList overlays,
std::vector<ScopedOverlayAccess*> accesses) {
NOTIMPLEMENTED();
}
} // namespace viz