Commit 0173c183 authored by Tom Anderson's avatar Tom Anderson Committed by Commit Bot

Reland "Use shared memory to send images to X server"

This is a reland of a752710d

Original change's description:
> Use shared memory to send images to X server
>
> When playing back a 1440p 60fps video without hardware acceleration, ~15% of
> frames would be dropped. With this change, I'm not getting any dropped frames.
>
> Background:
> The bottleneck is in SoftwareOutputDeviceX11::EndPaint, which (for 4K video) was
> taking ~0.06s per frame, which was limiting the output to ~15fps. It was taking
> so long because we were sending each frame to the X server over the wire. With
> the shared memory approach from the above CL, SoftwareOutputDeviceX11::EndPaint
> takes only ~0.00002s.
>
> Summary of changes:
> * Switch usage of XPutImage() to XShmPutImage() when possible.
>   * Shm segment is recycled across frames.
>   * Segment has a 1.5x growth policy to give windows room to resize without
>     having to reallocate for each frame.
>   * Segment has space for 2 frames. It was experimentally found that bumping
>     this up to 3 didn't give significant gains.
> * X server sends a completion event when XShmPutImage() is finished, so we
>   cannot recycle the memory until getting this event.
>   * Event processing happens on the GPU main thread, but processing needs to
>     happen on the compositor thread, so forwarding the event is necessary. This
>     is implemented in XShmImagePool.
>
> BUG=991633
>
> Change-Id: Ia7782cb9e21d0a93d3250c118e4ba76025354e40
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1693737
> Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
> Reviewed-by: kylechar <kylechar@chromium.org>
> Reviewed-by: Robert Kroeger <rjkroege@chromium.org>
> Reviewed-by: Kenneth Russell <kbr@chromium.org>
> Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#691321}

R=jorgelo
TBR=rjkroege,kbr,kylechar

Bug: 991633
Change-Id: Id0533ea8ba4de45ca2242ee979197798a818826d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1775500Reviewed-by: default avatarThomas Anderson <thomasanderson@chromium.org>
Reviewed-by: default avatarJorge Lucangeli Obes <jorgelo@chromium.org>
Auto-Submit: Thomas Anderson <thomasanderson@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#693202}
parent db43b7c1
......@@ -6,6 +6,8 @@
#include <stdint.h>
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
......
......@@ -4,6 +4,8 @@
#include "components/viz/service/display/software_output_device.h"
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/threading/sequenced_task_runner_handle.h"
......@@ -56,4 +58,8 @@ void SoftwareOutputDevice::OnSwapBuffers(
viewport_pixel_size_));
}
int SoftwareOutputDevice::MaxFramesPending() const {
return 1;
}
} // namespace viz
......@@ -73,6 +73,8 @@ class VIZ_SERVICE_EXPORT SoftwareOutputDevice {
// completes.
virtual void OnSwapBuffers(SwapBuffersCallback swap_ack_callback);
virtual int MaxFramesPending() const;
protected:
scoped_refptr<base::SequencedTaskRunner> task_runner_;
SoftwareOutputDeviceClient* client_ = nullptr;
......
......@@ -243,7 +243,10 @@ OutputSurfaceProviderImpl::CreateSoftwareOutputDeviceForPlatform(
return std::make_unique<SoftwareOutputDeviceOzone>(
std::move(platform_window_surface), std::move(surface_ozone));
#elif defined(USE_X11)
return std::make_unique<SoftwareOutputDeviceX11>(surface_handle);
return std::make_unique<SoftwareOutputDeviceX11>(
surface_handle, gpu_service_impl_->in_host_process()
? nullptr
: gpu_service_impl_->main_runner());
#endif
}
......
......@@ -8,9 +8,13 @@
#include <stdint.h>
#include <string.h>
#include <memory>
#include <utility>
#include "base/macros.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "ui/base/x/x11_shm_image_pool.h"
#include "ui/base/x/x11_util.h"
#include "ui/base/x/x11_util_internal.h"
#include "ui/gfx/x/x11.h"
......@@ -21,6 +25,8 @@ namespace viz {
namespace {
constexpr int kMaxFramesPending = 2;
class ScopedPixmap {
public:
ScopedPixmap(XDisplay* display, Pixmap pixmap)
......@@ -39,24 +45,21 @@ class ScopedPixmap {
DISALLOW_COPY_AND_ASSIGN(ScopedPixmap);
};
struct XImageDeleter {
void operator()(XImage* image) const { XDestroyImage(image); }
};
} // namespace
// Draw |data| over |widget|'s parent-relative background, and write the
// resulting image to |widget|. Returns true on success.
bool CompositeBitmap(XDisplay* display,
XID widget,
int x,
int y,
int width,
int height,
int depth,
GC gc,
const void* data) {
// static
bool SoftwareOutputDeviceX11::CompositeBitmap(XDisplay* display,
XID widget,
int x,
int y,
int width,
int height,
int depth,
GC gc,
const void* data) {
XClearArea(display, widget, x, y, width, height, false);
std::unique_ptr<XImage, XImageDeleter> bg;
ui::XScopedImage bg;
{
gfx::X11ErrorTracker ignore_x_errors;
bg.reset(
......@@ -111,9 +114,8 @@ bool CompositeBitmap(XDisplay* display,
return true;
}
} // namespace
SoftwareOutputDeviceX11::SoftwareOutputDeviceX11(gfx::AcceleratedWidget widget)
SoftwareOutputDeviceX11::SoftwareOutputDeviceX11(gfx::AcceleratedWidget widget,
base::TaskRunner* task_runner)
: widget_(widget), display_(gfx::GetXDisplay()), gc_(nullptr) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
......@@ -122,6 +124,14 @@ SoftwareOutputDeviceX11::SoftwareOutputDeviceX11(gfx::AcceleratedWidget widget)
LOG(ERROR) << "XGetWindowAttributes failed for window " << widget_;
return;
}
shm_pool_ = base::MakeRefCounted<ui::XShmImagePool>(
task_runner_.get(), task_runner, display_, widget_, attributes_.visual,
attributes_.depth, kMaxFramesPending);
shm_pool_->Initialize();
// TODO(thomasanderson): Avoid going through the X11 server to plumb this
// property in.
ui::GetIntProperty(widget_, "CHROMIUM_COMPOSITE_WINDOW", &composite_);
}
......@@ -129,21 +139,58 @@ SoftwareOutputDeviceX11::~SoftwareOutputDeviceX11() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
XFreeGC(display_, gc_);
shm_pool_->Teardown();
}
void SoftwareOutputDeviceX11::EndPaint() {
void SoftwareOutputDeviceX11::Resize(const gfx::Size& pixel_size,
float scale_factor) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
SoftwareOutputDevice::EndPaint();
if (!surface_)
// Fallback to the non-shm codepath when |composite_| is true, which only
// happens for status icon windows that are typically 16x16px. It's possible
// to add a shm codepath, but it wouldn't be buying much since it would only
// affect windows that are tiny and infrequently updated.
if (!composite_ && shm_pool_->Resize(pixel_size)) {
viewport_pixel_size_ = pixel_size;
return;
}
SoftwareOutputDevice::Resize(pixel_size, scale_factor);
}
SkCanvas* SoftwareOutputDeviceX11::BeginPaint(const gfx::Rect& damage_rect) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
damage_rect_ = damage_rect;
if (shm_pool_->Ready())
return new SkCanvas(shm_pool_->CurrentBitmap());
return SoftwareOutputDevice::BeginPaint(damage_rect);
}
void SoftwareOutputDeviceX11::EndPaint() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
SoftwareOutputDevice::EndPaint();
gfx::Rect rect = damage_rect_;
rect.Intersect(gfx::Rect(viewport_pixel_size_));
if (rect.IsEmpty())
return;
if (shm_pool_->Ready()) {
// TODO(thomasanderson): Investigate direct rendering with DRI3 to avoid any
// unnecessary X11 IPC or buffer copying.
if (XShmPutImage(display_, widget_, gc_, shm_pool_->CurrentImage(),
rect.x(), rect.y(), rect.x(), rect.y(), rect.width(),
rect.height(), x11::True)) {
XFlush(display_);
}
}
if (!surface_)
return;
if (composite_) {
SkPixmap pixmap;
surface_->peekPixels(&pixmap);
......@@ -208,7 +255,6 @@ void SoftwareOutputDeviceX11::EndPaint() {
XRenderFreePicture(display_, dest_picture);
XFreePixmap(display_, pixmap);
} else {
// TODO(jbauman): Switch to XShmPutImage since it's async.
SkPixmap pixmap;
surface_->peekPixels(&pixmap);
gfx::PutARGBImage(display_, attributes_.visual, attributes_.depth, widget_,
......@@ -225,4 +271,16 @@ void SoftwareOutputDeviceX11::EndPaint() {
XFlush(display_);
}
void SoftwareOutputDeviceX11::OnSwapBuffers(
SwapBuffersCallback swap_ack_callback) {
if (shm_pool_->Ready())
return shm_pool_->SwapBuffers(std::move(swap_ack_callback));
return SoftwareOutputDevice::OnSwapBuffers(std::move(swap_ack_callback));
}
int SoftwareOutputDeviceX11::MaxFramesPending() const {
return kMaxFramesPending;
}
} // namespace viz
......@@ -6,6 +6,7 @@
#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SOFTWARE_OUTPUT_DEVICE_X11_H_
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/threading/thread_checker.h"
#include "components/viz/service/display/software_output_device.h"
#include "components/viz/service/viz_service_export.h"
......@@ -13,24 +14,50 @@
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_types.h"
namespace ui {
class XShmImagePool;
}
namespace viz {
class VIZ_SERVICE_EXPORT SoftwareOutputDeviceX11 : public SoftwareOutputDevice {
public:
explicit SoftwareOutputDeviceX11(gfx::AcceleratedWidget widget);
explicit SoftwareOutputDeviceX11(gfx::AcceleratedWidget widget,
base::TaskRunner* task_runner);
~SoftwareOutputDeviceX11() override;
private:
// Draw |data| over |widget|'s parent-relative background, and write the
// resulting image to |widget|. Returns true on success.
static bool CompositeBitmap(XDisplay* display,
XID widget,
int x,
int y,
int width,
int height,
int depth,
GC gc,
const void* data);
// SoftwareOutputDevice:
void Resize(const gfx::Size& pixel_size, float scale_factor) override;
SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override;
void EndPaint() override;
void OnSwapBuffers(SwapBuffersCallback swap_ack_callback) override;
int MaxFramesPending() const override;
private:
gfx::AcceleratedWidget widget_;
XDisplay* display_;
GC gc_;
XWindowAttributes attributes_;
// If nonzero, indicates that the widget should be drawn over its
// parent-relative background.
int composite_ = 0;
scoped_refptr<ui::XShmImagePool> shm_pool_;
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceX11);
......
......@@ -24,7 +24,9 @@ namespace viz {
SoftwareOutputSurface::SoftwareOutputSurface(
std::unique_ptr<SoftwareOutputDevice> software_device)
: OutputSurface(std::move(software_device)) {}
: OutputSurface(std::move(software_device)) {
capabilities_.max_frames_pending = software_device_->MaxFramesPending();
}
SoftwareOutputSurface::~SoftwareOutputSurface() = default;
......@@ -67,10 +69,7 @@ void SoftwareOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, swap_time);
}
DCHECK(stored_latency_info_.empty())
<< "A second frame is not expected to "
<< "arrive before the previous latency info is processed.";
stored_latency_info_ = std::move(frame.latency_info);
stored_latency_info_.emplace(std::move(frame.latency_info));
software_device()->OnSwapBuffers(
base::BindOnce(&SoftwareOutputSurface::SwapBuffersCallback,
......@@ -110,10 +109,12 @@ uint32_t SoftwareOutputSurface::GetFramebufferCopyTextureFormat() {
void SoftwareOutputSurface::SwapBuffersCallback(base::TimeTicks swap_time,
const gfx::Size& pixel_size) {
latency_tracker_.OnGpuSwapBuffersCompleted(stored_latency_info_);
client_->DidFinishLatencyInfo(stored_latency_info_);
std::vector<ui::LatencyInfo>().swap(stored_latency_info_);
auto& latency_info = stored_latency_info_.front();
latency_tracker_.OnGpuSwapBuffersCompleted(latency_info);
client_->DidFinishLatencyInfo(latency_info);
std::vector<ui::LatencyInfo>().swap(latency_info);
client_->DidReceiveSwapBuffersAck({swap_time, swap_time});
stored_latency_info_.pop();
base::TimeTicks now = base::TimeTicks::Now();
base::TimeDelta interval_to_next_refresh =
......
......@@ -6,6 +6,8 @@
#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SOFTWARE_OUTPUT_SURFACE_H_
#include <memory>
#include <queue>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "components/viz/common/display/update_vsync_parameters_callback.h"
......@@ -64,7 +66,7 @@ class VIZ_SERVICE_EXPORT SoftwareOutputSurface : public OutputSurface {
base::TimeTicks refresh_timebase_;
base::TimeDelta refresh_interval_ = BeginFrameArgs::DefaultInterval();
std::vector<ui::LatencyInfo> stored_latency_info_;
std::queue<std::vector<ui::LatencyInfo>> stored_latency_info_;
ui::LatencyTracker latency_tracker_;
#if defined(USE_X11)
......
......@@ -5,6 +5,9 @@
#ifndef COMPONENTS_VIZ_SERVICE_GL_GPU_SERVICE_IMPL_H_
#define COMPONENTS_VIZ_SERVICE_GL_GPU_SERVICE_IMPL_H_
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_refptr.h"
......@@ -225,6 +228,8 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
}
gpu::Scheduler* scheduler() { return scheduler_.get(); }
base::TaskRunner* main_runner() { return main_runner_.get(); }
gpu::GpuWatchdogThread* watchdog_thread() { return watchdog_thread_.get(); }
const gpu::GpuFeatureInfo& gpu_feature_info() const {
......
......@@ -216,7 +216,7 @@ GpuProcessTransportFactory::CreateSoftwareOutputDevice(
scoped_refptr<base::SequencedTaskRunner> task_runner) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kHeadless))
return base::WrapUnique(new viz::SoftwareOutputDevice);
return std::make_unique<viz::SoftwareOutputDevice>();
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if defined(USE_OZONE)
......@@ -230,10 +230,11 @@ GpuProcessTransportFactory::CreateSoftwareOutputDevice(
return std::make_unique<viz::SoftwareOutputDeviceOzone>(
std::move(platform_window_surface), std::move(surface_ozone));
#elif defined(USE_X11)
return std::make_unique<viz::SoftwareOutputDeviceX11>(widget);
return std::make_unique<viz::SoftwareOutputDeviceX11>(
widget, base::ThreadTaskRunnerHandle::Get().get());
#else
NOTREACHED();
return std::unique_ptr<viz::SoftwareOutputDevice>();
return nullptr;
#endif
}
......
......@@ -845,7 +845,8 @@ bool SyscallSets::IsSystemVSemaphores(int sysno) {
}
#endif
#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
defined(__aarch64__) || \
(defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS))
// These give a lot of ambient authority and bypass the setuid sandbox.
bool SyscallSets::IsSystemVSharedMemory(int sysno) {
......
......@@ -75,7 +75,8 @@ class SANDBOX_EXPORT SyscallSets {
(defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS))
static bool IsSystemVSemaphores(int sysno);
#endif
#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
defined(__aarch64__) || \
(defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS))
// These give a lot of ambient authority and bypass the setuid sandbox.
static bool IsSystemVSharedMemory(int sysno);
......
......@@ -1422,5 +1422,293 @@
#define __NR_memfd_create 356
#endif
#if !defined(__NR_bpf)
#define __NR_bpf 357
#endif
#if !defined(__NR_execveat)
#define __NR_execveat 358
#endif
#if !defined(__NR_socket)
#define __NR_socket 359
#endif
#if !defined(__NR_socketpair)
#define __NR_socketpair 360
#endif
#if !defined(__NR_bind)
#define __NR_bind 361
#endif
#if !defined(__NR_connect)
#define __NR_connect 362
#endif
#if !defined(__NR_listen)
#define __NR_listen 363
#endif
#if !defined(__NR_accept4)
#define __NR_accept4 364
#endif
#if !defined(__NR_getsockopt)
#define __NR_getsockopt 365
#endif
#if !defined(__NR_setsockopt)
#define __NR_setsockopt 366
#endif
#if !defined(__NR_getsockname)
#define __NR_getsockname 367
#endif
#if !defined(__NR_getpeername)
#define __NR_getpeername 368
#endif
#if !defined(__NR_sendto)
#define __NR_sendto 369
#endif
#if !defined(__NR_sendmsg)
#define __NR_sendmsg 370
#endif
#if !defined(__NR_recvfrom)
#define __NR_recvfrom 371
#endif
#if !defined(__NR_recvmsg)
#define __NR_recvmsg 372
#endif
#if !defined(__NR_shutdown)
#define __NR_shutdown 373
#endif
#if !defined(__NR_userfaultfd)
#define __NR_userfaultfd 374
#endif
#if !defined(__NR_membarrier)
#define __NR_membarrier 375
#endif
#if !defined(__NR_mlock2)
#define __NR_mlock2 376
#endif
#if !defined(__NR_copy_file_range)
#define __NR_copy_file_range 377
#endif
#if !defined(__NR_preadv2)
#define __NR_preadv2 378
#endif
#if !defined(__NR_pwritev2)
#define __NR_pwritev2 379
#endif
#if !defined(__NR_pkey_mprotect)
#define __NR_pkey_mprotect 380
#endif
#if !defined(__NR_pkey_alloc)
#define __NR_pkey_alloc 381
#endif
#if !defined(__NR_pkey_free)
#define __NR_pkey_free 382
#endif
#if !defined(__NR_statx)
#define __NR_statx 383
#endif
#if !defined(__NR_arch_prctl)
#define __NR_arch_prctl 384
#endif
#if !defined(__NR_io_pgetevents)
#define __NR_io_pgetevents 385
#endif
#if !defined(__NR_rseq)
#define __NR_rseq 386
#endif
#if !defined(__NR_semget)
#define __NR_semget 393
#endif
#if !defined(__NR_semctl)
#define __NR_semctl 394
#endif
#if !defined(__NR_shmget)
#define __NR_shmget 395
#endif
#if !defined(__NR_shmctl)
#define __NR_shmctl 396
#endif
#if !defined(__NR_shmat)
#define __NR_shmat 397
#endif
#if !defined(__NR_shmdt)
#define __NR_shmdt 398
#endif
#if !defined(__NR_msgget)
#define __NR_msgget 399
#endif
#if !defined(__NR_msgsnd)
#define __NR_msgsnd 400
#endif
#if !defined(__NR_msgrcv)
#define __NR_msgrcv 401
#endif
#if !defined(__NR_msgctl)
#define __NR_msgctl 402
#endif
#if !defined(__NR_clock_gettime64)
#define __NR_clock_gettime64 403
#endif
#if !defined(__NR_clock_settime64)
#define __NR_clock_settime64 404
#endif
#if !defined(__NR_clock_adjtime64)
#define __NR_clock_adjtime64 405
#endif
#if !defined(__NR_clock_getres_time64)
#define __NR_clock_getres_time64 406
#endif
#if !defined(__NR_clock_nanosleep_time64)
#define __NR_clock_nanosleep_time64 407
#endif
#if !defined(__NR_timer_gettime64)
#define __NR_timer_gettime64 408
#endif
#if !defined(__NR_timer_settime64)
#define __NR_timer_settime64 409
#endif
#if !defined(__NR_timerfd_gettime64)
#define __NR_timerfd_gettime64 410
#endif
#if !defined(__NR_timerfd_settime64)
#define __NR_timerfd_settime64 411
#endif
#if !defined(__NR_utimensat_time64)
#define __NR_utimensat_time64 412
#endif
#if !defined(__NR_pselect6_time64)
#define __NR_pselect6_time64 413
#endif
#if !defined(__NR_ppoll_time64)
#define __NR_ppoll_time64 414
#endif
#if !defined(__NR_io_pgetevents_time64)
#define __NR_io_pgetevents_time64 416
#endif
#if !defined(__NR_recvmmsg_time64)
#define __NR_recvmmsg_time64 417
#endif
#if !defined(__NR_mq_timedsend_time64)
#define __NR_mq_timedsend_time64 418
#endif
#if !defined(__NR_mq_timedreceive_time64)
#define __NR_mq_timedreceive_time64 419
#endif
#if !defined(__NR_semtimedop_time64)
#define __NR_semtimedop_time64 420
#endif
#if !defined(__NR_rt_sigtimedwait_time64)
#define __NR_rt_sigtimedwait_time64 421
#endif
#if !defined(__NR_futex_time64)
#define __NR_futex_time64 422
#endif
#if !defined(__NR_sched_rr_get_interval_time64)
#define __NR_sched_rr_get_interval_time64 423
#endif
#if !defined(__NR_pidfd_send_signal)
#define __NR_pidfd_send_signal 424
#endif
#if !defined(__NR_io_uring_setup)
#define __NR_io_uring_setup 425
#endif
#if !defined(__NR_io_uring_enter)
#define __NR_io_uring_enter 426
#endif
#if !defined(__NR_io_uring_register)
#define __NR_io_uring_register 427
#endif
#if !defined(__NR_open_tree)
#define __NR_open_tree 428
#endif
#if !defined(__NR_move_mount)
#define __NR_move_mount 429
#endif
#if !defined(__NR_fsopen)
#define __NR_fsopen 430
#endif
#if !defined(__NR_fsconfig)
#define __NR_fsconfig 431
#endif
#if !defined(__NR_fsmount)
#define __NR_fsmount 432
#endif
#if !defined(__NR_fspick)
#define __NR_fspick 433
#endif
#if !defined(__NR_pidfd_open)
#define __NR_pidfd_open 434
#endif
#if !defined(__NR_clone3)
#define __NR_clone3 435
#endif
#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_
......@@ -23,11 +23,11 @@
#include "services/service_manager/sandbox/linux/sandbox_linux.h"
#include "services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h"
using sandbox::SyscallSets;
using sandbox::bpf_dsl::Allow;
using sandbox::bpf_dsl::ResultExpr;
using sandbox::bpf_dsl::Trap;
using sandbox::syscall_broker::BrokerProcess;
using sandbox::SyscallSets;
namespace service_manager {
......@@ -65,6 +65,11 @@ ResultExpr GpuProcessPolicy::EvaluateSyscall(int sysno) const {
if (SyscallSets::IsEventFd(sysno))
return Allow();
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_X11)
if (SyscallSets::IsSystemVSharedMemory(sysno))
return Allow();
#endif
auto* broker_process = SandboxLinux::GetInstance()->broker_process();
if (broker_process->IsSyscallAllowed(sysno)) {
return Trap(BrokerProcess::SIGSYS_Handler, broker_process);
......
......@@ -27,6 +27,14 @@ jumbo_component("x") {
"x11_window.h",
]
if (use_x11) {
# Not supported on Ozone for now.
sources += [
"x11_shm_image_pool.cc",
"x11_shm_image_pool.h",
]
}
configs += [
"//build/config/linux:x11",
"//build/config/linux:xrandr",
......
// Copyright 2019 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/base/x/x11_shm_image_pool.h"
#include <sys/ipc.h>
#include <sys/shm.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/gfx/geometry/rect.h"
namespace ui {
namespace {
constexpr int kMinImageAreaForShmem = 256;
// When resizing a segment, the new segment size is calculated as
// new_size = target_size * kShmResizeThreshold
// so that target_size has room to grow before another resize is necessary. We
// also want target_size to have room to shrink, so we avoid resizing until
// shrink_size = target_size / kShmResizeThreshold
// Given these equations, shrink_size is
// shrink_size = new_size / kShmResizeThreshold ^ 2
// new_size is recorded in SoftwareOutputDeviceX11::shm_size_, so we need to
// divide by kShmResizeThreshold twice to get the shrink threshold.
constexpr float kShmResizeThreshold = 1.5f;
constexpr float kShmResizeShrinkThreshold =
1.0f / (kShmResizeThreshold * kShmResizeThreshold);
} // namespace
XShmImagePool::FrameState::FrameState() = default;
XShmImagePool::FrameState::~FrameState() = default;
XShmImagePool::SwapClosure::SwapClosure() = default;
XShmImagePool::SwapClosure::~SwapClosure() = default;
XShmImagePool::XShmImagePool(base::TaskRunner* host_task_runner,
base::TaskRunner* event_task_runner,
XDisplay* display,
XID drawable,
Visual* visual,
int depth,
std::size_t frames_pending)
: host_task_runner_(host_task_runner),
event_task_runner_(event_task_runner),
display_(display),
drawable_(drawable),
visual_(visual),
depth_(depth),
frame_states_(frames_pending) {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
}
bool XShmImagePool::Resize(const gfx::Size& pixel_size) {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
if (pixel_size == pixel_size_)
return true;
auto cleanup_fn = [](XShmImagePool* x) { x->Cleanup(); };
std::unique_ptr<XShmImagePool, decltype(cleanup_fn)> cleanup{this,
cleanup_fn};
if (!event_task_runner_)
return false;
if (!ui::QueryShmSupport())
return false;
if (pixel_size.width() <= 0 || pixel_size.height() <= 0 ||
pixel_size.GetArea() <= kMinImageAreaForShmem) {
return false;
}
std::size_t needed_frame_bytes;
for (std::size_t i = 0; i < frame_states_.size(); ++i) {
FrameState& state = frame_states_[i];
state.image.reset(XShmCreateImage(display_, visual_, depth_, ZPixmap,
nullptr, &shminfo_, pixel_size.width(),
pixel_size.height()));
if (!state.image)
return false;
std::size_t current_frame_bytes =
state.image->bytes_per_line * state.image->height;
if (i == 0)
needed_frame_bytes = current_frame_bytes;
else
DCHECK_EQ(current_frame_bytes, needed_frame_bytes);
}
if (needed_frame_bytes > frame_bytes_ ||
needed_frame_bytes < frame_bytes_ * kShmResizeShrinkThreshold) {
// Resize.
Cleanup();
frame_bytes_ = needed_frame_bytes * kShmResizeThreshold;
shminfo_.shmid = shmget(IPC_PRIVATE, frame_bytes_ * frame_states_.size(),
IPC_CREAT | SHM_R | SHM_W);
if (shminfo_.shmid < 0)
return false;
shminfo_.shmaddr = reinterpret_cast<char*>(shmat(shminfo_.shmid, 0, 0));
if (shminfo_.shmaddr == reinterpret_cast<char*>(-1)) {
shmctl(shminfo_.shmid, IPC_RMID, 0);
return false;
}
#if defined(OS_LINUX)
// On Linux, a shmid can still be attached after IPC_RMID if otherwise kept
// alive. Detach before XShmAttach to prevent a memory leak in case the
// process dies.
shmctl(shminfo_.shmid, IPC_RMID, 0);
#endif
DCHECK(!shmem_attached_to_server_);
if (!XShmAttach(display_, &shminfo_))
return false;
shmem_attached_to_server_ = true;
#if !defined(OS_LINUX)
// The Linux-specific shmctl behavior above may not be portable, so we're
// forced to do IPC_RMID after the server has attached to the segment.
// XShmAttach is asynchronous, so we must also sync.
XSync(display_, x11::False);
shmctl(shminfo_.shmid, IPC_RMID, 0);
#endif
// If this class ever needs to use XShmGetImage(), this needs to be
// changed to read-write.
shminfo_.readOnly = true;
}
for (std::size_t i = 0; i < frame_states_.size(); ++i) {
FrameState& state = frame_states_[i];
const std::size_t offset = i * needed_frame_bytes;
#ifndef NDEBUG
state.offset = offset;
#endif
state.image->data = shminfo_.shmaddr + offset;
SkImageInfo image_info = SkImageInfo::Make(
state.image->width, state.image->height,
state.image->byte_order == LSBFirst ? kBGRA_8888_SkColorType
: kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
state.bitmap = SkBitmap();
if (!state.bitmap.installPixels(image_info, state.image->data,
state.image->bytes_per_line)) {
return false;
}
}
pixel_size_ = pixel_size;
cleanup.release();
return true;
}
bool XShmImagePool::Ready() {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
return shmem_attached_to_server_;
}
SkBitmap& XShmImagePool::CurrentBitmap() {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
return frame_states_[current_frame_index_].bitmap;
}
XImage* XShmImagePool::CurrentImage() {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
return frame_states_[current_frame_index_].image.get();
}
void XShmImagePool::SwapBuffers(
base::OnceCallback<void(const gfx::Size&)> callback) {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
swap_closures_.emplace();
SwapClosure& swap_closure = swap_closures_.back();
swap_closure.closure = base::BindOnce(std::move(callback), pixel_size_);
#ifndef NDEBUG
swap_closure.shmseg = shminfo_.shmseg;
swap_closure.offset = frame_states_[current_frame_index_].offset;
#endif
current_frame_index_ = (current_frame_index_ + 1) % frame_states_.size();
}
void XShmImagePool::Initialize() {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
if (event_task_runner_)
event_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&XShmImagePool::InitializeOnGpu, this));
}
void XShmImagePool::Teardown() {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
if (event_task_runner_)
event_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&XShmImagePool::TeardownOnGpu, this));
}
XShmImagePool::~XShmImagePool() {
Cleanup();
#ifndef NDEBUG
DCHECK(!dispatcher_registered_);
#endif
}
void XShmImagePool::InitializeOnGpu() {
DCHECK(event_task_runner_->RunsTasksInCurrentSequence());
ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
#ifndef NDEBUG
dispatcher_registered_ = true;
#endif
}
void XShmImagePool::TeardownOnGpu() {
DCHECK(event_task_runner_->RunsTasksInCurrentSequence());
ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
#ifndef NDEBUG
dispatcher_registered_ = false;
#endif
}
void XShmImagePool::Cleanup() {
if (shminfo_.shmaddr)
shmdt(shminfo_.shmaddr);
if (shmem_attached_to_server_) {
XShmDetach(display_, &shminfo_);
shmem_attached_to_server_ = false;
}
shminfo_ = {};
frame_bytes_ = 0;
pixel_size_ = gfx::Size();
current_frame_index_ = 0;
}
void XShmImagePool::DispatchShmCompletionEvent(XShmCompletionEvent event) {
DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
DCHECK(!swap_closures_.empty());
SwapClosure& swap_ack = swap_closures_.front();
#ifndef NDEBUG
DCHECK_EQ(event.shmseg, swap_ack.shmseg);
DCHECK_EQ(event.offset, swap_ack.offset);
#endif
std::move(swap_ack.closure).Run();
swap_closures_.pop();
}
bool XShmImagePool::CanDispatchEvent(const ui::PlatformEvent& event) {
DCHECK(event_task_runner_->RunsTasksInCurrentSequence());
XEvent* xevent = event;
if (xevent->type != ui::ShmEventBase() + ShmCompletion)
return false;
XShmCompletionEvent* shm_event =
reinterpret_cast<XShmCompletionEvent*>(xevent);
return shm_event->drawable == drawable_;
}
uint32_t XShmImagePool::DispatchEvent(const ui::PlatformEvent& event) {
DCHECK(event_task_runner_->RunsTasksInCurrentSequence());
XShmCompletionEvent* shm_event =
reinterpret_cast<XShmCompletionEvent*>(event);
host_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&XShmImagePool::DispatchShmCompletionEvent,
this, *shm_event));
return ui::POST_DISPATCH_STOP_PROPAGATION;
}
} // namespace ui
// Copyright 2019 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.
#ifndef UI_BASE_X_X11_SHM_IMAGE_POOL_H_
#define UI_BASE_X_X11_SHM_IMAGE_POOL_H_
#include <cstring>
#include <queue>
#include <vector>
#include "base/callback_forward.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/task_runner.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/x/x11.h"
namespace ui {
class COMPONENT_EXPORT(UI_BASE_X) XShmImagePool
: public PlatformEventDispatcher,
public base::RefCountedThreadSafe<XShmImagePool> {
public:
XShmImagePool(base::TaskRunner* host_task_runner,
base::TaskRunner* event_task_runner,
XDisplay* display,
XID drawable,
Visual* visual,
int depth,
std::size_t max_frames_pending);
bool Resize(const gfx::Size& pixel_size);
// Is XSHM supported by the server and are the shared buffers ready for use?
bool Ready();
// Obtain state for the current frame.
SkBitmap& CurrentBitmap();
XImage* CurrentImage();
// Switch to the next cached frame. CurrentBitmap() and CurrentImage() will
// change to reflect the new frame.
void SwapBuffers(base::OnceCallback<void(const gfx::Size&)> callback);
// Part of setup and teardown must be done on the event task runner. Posting
// the tasks cannot be done in the constructor/destructor because because this
// would cause subtle problems with the reference count for this object. So
// Initialize() must be called after constructing and Teardown() must be
// called before destructing.
void Initialize();
void Teardown();
private:
friend class base::RefCountedThreadSafe<XShmImagePool>;
struct FrameState {
FrameState();
~FrameState();
XScopedImage image;
SkBitmap bitmap;
#ifndef NDEBUG
std::size_t offset = 0;
#endif
};
struct SwapClosure {
SwapClosure();
~SwapClosure();
base::OnceClosure closure;
#ifndef NDEBUG
ShmSeg shmseg;
std::size_t offset;
#endif
};
~XShmImagePool() override;
void InitializeOnGpu();
void TeardownOnGpu();
void Cleanup();
void DispatchShmCompletionEvent(XShmCompletionEvent event);
// PlatformEventDispatcher:
bool CanDispatchEvent(const PlatformEvent& event) override;
uint32_t DispatchEvent(const PlatformEvent& event) override;
base::TaskRunner* const host_task_runner_;
base::TaskRunner* const event_task_runner_;
XDisplay* const display_;
const XID drawable_;
Visual* const visual_;
const int depth_;
gfx::Size pixel_size_;
std::size_t frame_bytes_ = 0;
XShmSegmentInfo shminfo_{};
bool shmem_attached_to_server_ = false;
std::vector<FrameState> frame_states_;
std::size_t current_frame_index_ = 0;
std::queue<SwapClosure> swap_closures_;
#ifndef NDEBUG
bool dispatcher_registered_ = false;
#endif
DISALLOW_COPY_AND_ASSIGN(XShmImagePool);
};
} // namespace ui
#endif // UI_BASE_X_X11_SHM_IMAGE_POOL_H_
......@@ -15,7 +15,6 @@
#include <bitset>
#include <list>
#include <map>
#include <memory>
#include <utility>
#include <vector>
......@@ -287,10 +286,23 @@ bool QueryRenderSupport(Display* dpy) {
// We don't care about the version of Xrender since all the features which
// we use are included in every version.
static bool render_supported = XRenderQueryExtension(dpy, &dummy, &dummy);
return render_supported;
}
bool QueryShmSupport() {
int major;
int minor;
x11::Bool pixmaps;
static bool supported =
XShmQueryVersion(gfx::GetXDisplay(), &major, &minor, &pixmaps);
return supported;
}
int ShmEventBase() {
static int event_base = XShmGetEventBase(gfx::GetXDisplay());
return event_base;
}
::Cursor CreateReffedCustomXCursor(XcursorImage* image) {
return XCustomCursorCache::GetInstance()->InstallCustomCursor(image);
}
......@@ -1310,6 +1322,10 @@ void XScopedCursor::reset(::Cursor cursor) {
cursor_ = cursor;
}
void XImageDeleter::operator()(XImage* image) const {
XDestroyImage(image);
}
namespace test {
const XcursorImage* GetCachedXcursorImage(::Cursor cursor) {
......
......@@ -13,6 +13,7 @@
#include <stddef.h>
#include <memory>
#include <string>
#include <vector>
......@@ -26,7 +27,6 @@
#include "ui/gfx/icc_profile.h"
#include "ui/gfx/x/x11_types.h"
typedef unsigned long XSharedMemoryId; // ShmSeg in the X headers.
typedef unsigned long Cursor;
namespace gfx {
......@@ -49,6 +49,12 @@ COMPONENT_EXPORT(UI_BASE_X) bool IsXInput2Available();
// Return true iff the display supports Xrender
COMPONENT_EXPORT(UI_BASE_X) bool QueryRenderSupport(XDisplay* dpy);
// Return true iff the display supports MIT-SHM.
COMPONENT_EXPORT(UI_BASE_X) bool QueryShmSupport();
// Returns the first event ID for the MIT-SHM extension, if available.
COMPONENT_EXPORT(UI_BASE_X) int ShmEventBase();
// Creates a custom X cursor from the image. This takes ownership of image. The
// caller must not free/modify the image. The refcount of the newly created
// cursor is set to 1.
......@@ -357,6 +363,11 @@ class COMPONENT_EXPORT(UI_BASE_X) XScopedCursor {
DISALLOW_COPY_AND_ASSIGN(XScopedCursor);
};
struct COMPONENT_EXPORT(UI_BASE_X) XImageDeleter {
void operator()(XImage* image) const;
};
using XScopedImage = std::unique_ptr<XImage, XImageDeleter>;
namespace test {
// Returns the cached XcursorImage for |cursor|.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment