1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/viz/service/display_embedder/skia_output_device_x11.h"
6 
7 #include <utility>
8 
9 #include "base/logging.h"
10 #include "base/memory/ref_counted_memory.h"
11 #include "third_party/skia/include/core/SkSurface.h"
12 #include "third_party/skia/include/gpu/GrBackendSurface.h"
13 #include "third_party/skia/include/gpu/vk/GrVkTypes.h"
14 #include "ui/base/x/x11_util.h"
15 #include "ui/gfx/geometry/rect.h"
16 #include "ui/gfx/x/xproto.h"
17 
18 namespace viz {
19 
20 namespace {
21 
CreateGC(x11::Connection * connection,x11::Window window)22 x11::GraphicsContext CreateGC(x11::Connection* connection, x11::Window window) {
23   auto gc = connection->GenerateId<x11::GraphicsContext>();
24   connection->CreateGC({gc, window});
25   return gc;
26 }
27 
28 }  // namespace
29 
SkiaOutputDeviceX11(scoped_refptr<gpu::SharedContextState> context_state,gfx::AcceleratedWidget widget,gpu::MemoryTracker * memory_tracker,DidSwapBufferCompleteCallback did_swap_buffer_complete_callback)30 SkiaOutputDeviceX11::SkiaOutputDeviceX11(
31     scoped_refptr<gpu::SharedContextState> context_state,
32     gfx::AcceleratedWidget widget,
33     gpu::MemoryTracker* memory_tracker,
34     DidSwapBufferCompleteCallback did_swap_buffer_complete_callback)
35     : SkiaOutputDeviceOffscreen(context_state,
36                                 gfx::SurfaceOrigin::kTopLeft,
37                                 true /* has_alpha */,
38                                 memory_tracker,
39                                 did_swap_buffer_complete_callback),
40       connection_(x11::Connection::Get()),
41       window_(static_cast<x11::Window>(widget)),
42       gc_(CreateGC(connection_, window_)) {
43   if (auto attributes = connection_->GetWindowAttributes({window_}).Sync()) {
44     visual_ = attributes->visual;
45   } else {
46     LOG(FATAL) << "Failed to get attributes for window "
47                << static_cast<uint32_t>(window_);
48   }
49 
50   // |capabilities_| should be set by SkiaOutputDeviceOffscreen.
51   DCHECK_EQ(capabilities_.output_surface_origin, gfx::SurfaceOrigin::kTopLeft);
52   DCHECK(capabilities_.supports_post_sub_buffer);
53 }
54 
~SkiaOutputDeviceX11()55 SkiaOutputDeviceX11::~SkiaOutputDeviceX11() {
56   connection_->FreeGC({gc_});
57 }
58 
Reshape(const gfx::Size & size,float device_scale_factor,const gfx::ColorSpace & color_space,gfx::BufferFormat format,gfx::OverlayTransform transform)59 bool SkiaOutputDeviceX11::Reshape(const gfx::Size& size,
60                                   float device_scale_factor,
61                                   const gfx::ColorSpace& color_space,
62                                   gfx::BufferFormat format,
63                                   gfx::OverlayTransform transform) {
64   if (!SkiaOutputDeviceOffscreen::Reshape(size, device_scale_factor,
65                                           color_space, format, transform)) {
66     return false;
67   }
68   auto ii =
69       SkImageInfo::MakeN32(size.width(), size.height(), kOpaque_SkAlphaType);
70   std::vector<uint8_t> mem(ii.computeMinByteSize());
71   pixels_ = base::RefCountedBytes::TakeVector(&mem);
72   return true;
73 }
74 
SwapBuffers(BufferPresentedCallback feedback,std::vector<ui::LatencyInfo> latency_info)75 void SkiaOutputDeviceX11::SwapBuffers(
76     BufferPresentedCallback feedback,
77     std::vector<ui::LatencyInfo> latency_info) {
78   return PostSubBuffer(
79       gfx::Rect(0, 0, sk_surface_->width(), sk_surface_->height()),
80       std::move(feedback), std::move(latency_info));
81 }
82 
PostSubBuffer(const gfx::Rect & rect,BufferPresentedCallback feedback,std::vector<ui::LatencyInfo> latency_info)83 void SkiaOutputDeviceX11::PostSubBuffer(
84     const gfx::Rect& rect,
85     BufferPresentedCallback feedback,
86     std::vector<ui::LatencyInfo> latency_info) {
87   StartSwapBuffers(std::move(feedback));
88   if (!rect.IsEmpty()) {
89     auto ii =
90         SkImageInfo::MakeN32(rect.width(), rect.height(), kOpaque_SkAlphaType);
91     DCHECK_GE(pixels_->size(), ii.computeMinByteSize());
92     SkPixmap sk_pixmap(ii, pixels_->data(), ii.minRowBytes());
93     bool result = sk_surface_->readPixels(sk_pixmap, rect.x(), rect.y());
94     LOG_IF(FATAL, !result) << "Failed to read pixels from offscreen SkSurface.";
95 
96     // TODO(penghuang): Switch to XShmPutImage.
97     ui::DrawPixmap(x11::Connection::Get(), visual_, window_, gc_, sk_pixmap,
98                    0 /* src_x */, 0 /* src_y */, rect.x() /* dst_x */,
99                    rect.y() /* dst_y */, rect.width(), rect.height());
100 
101     connection_->Flush();
102   }
103   FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK),
104                     gfx::Size(sk_surface_->width(), sk_surface_->height()),
105                     std::move(latency_info));
106 }
107 
108 }  // namespace viz
109