1 // Copyright 2018 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/output_device_backing.h"
6 
7 #include <algorithm>
8 
9 #include "base/debug/alias.h"
10 #include "base/logging.h"
11 #include "base/memory/unsafe_shared_memory_region.h"
12 #include "base/stl_util.h"
13 #include "components/viz/common/resources/resource_sizes.h"
14 
15 namespace viz {
16 namespace {
17 
18 // If a window is larger than this in bytes, don't even try to create a backing
19 // bitmap for it.
20 constexpr size_t kMaxBitmapSizeBytes = 4 * (16384 * 8192);
21 
22 // Finds the size in bytes to hold |viewport_size| pixels. If |viewport_size| is
23 // a valid size this will return true and |out_bytes| will contain the size in
24 // bytes. If |viewport_size| is not a valid size then this will return false.
GetViewportSizeInBytes(const gfx::Size & viewport_size,size_t * out_bytes)25 bool GetViewportSizeInBytes(const gfx::Size& viewport_size, size_t* out_bytes) {
26   size_t bytes;
27   if (!ResourceSizes::MaybeSizeInBytes(viewport_size, RGBA_8888, &bytes))
28     return false;
29   if (bytes > kMaxBitmapSizeBytes)
30     return false;
31   *out_bytes = bytes;
32   return true;
33 }
34 
35 }  // namespace
36 
37 OutputDeviceBacking::OutputDeviceBacking() = default;
38 
~OutputDeviceBacking()39 OutputDeviceBacking::~OutputDeviceBacking() {
40   DCHECK(clients_.empty());
41 }
42 
ClientResized()43 void OutputDeviceBacking::ClientResized() {
44   // If the max viewport size doesn't change then nothing here changes.
45   if (GetMaxViewportBytes() == created_shm_bytes_)
46     return;
47 
48   // Otherwise we need to allocate a new shared memory region and clients
49   // should re-request it.
50   for (auto* client : clients_)
51     client->ReleaseCanvas();
52 
53   region_ = {};
54   created_shm_bytes_ = 0;
55 }
56 
RegisterClient(Client * client)57 void OutputDeviceBacking::RegisterClient(Client* client) {
58   clients_.push_back(client);
59 }
60 
UnregisterClient(Client * client)61 void OutputDeviceBacking::UnregisterClient(Client* client) {
62   DCHECK(base::Contains(clients_, client));
63   base::Erase(clients_, client);
64   ClientResized();
65 }
66 
GetSharedMemoryRegion(const gfx::Size & viewport_size)67 base::UnsafeSharedMemoryRegion* OutputDeviceBacking::GetSharedMemoryRegion(
68     const gfx::Size& viewport_size) {
69   // If |viewport_size| is empty or too big don't try to allocate SharedMemory.
70   size_t viewport_bytes;
71   if (!GetViewportSizeInBytes(viewport_size, &viewport_bytes))
72     return nullptr;
73 
74   // Allocate a new SharedMemory segment that can fit the largest viewport.
75   if (!region_.IsValid()) {
76     size_t max_viewport_bytes = GetMaxViewportBytes();
77     DCHECK_LE(viewport_bytes, max_viewport_bytes);
78 
79     base::debug::Alias(&max_viewport_bytes);
80     region_ = base::UnsafeSharedMemoryRegion::Create(max_viewport_bytes);
81     if (!region_.IsValid()) {
82       LOG(ERROR) << "Shared memory region create failed on "
83                  << max_viewport_bytes << " bytes";
84       return nullptr;
85     }
86     created_shm_bytes_ = max_viewport_bytes;
87   } else {
88     // Clients must call Resize() for new |viewport_size|.
89     DCHECK_LE(viewport_bytes, created_shm_bytes_);
90   }
91 
92   return &region_;
93 }
94 
GetMaxViewportBytes()95 size_t OutputDeviceBacking::GetMaxViewportBytes() {
96   // Minimum byte size is 1 because creating a 0-byte-long SharedMemory fails.
97   size_t max_bytes = 1;
98   for (auto* client : clients_) {
99     size_t current_bytes;
100     if (!GetViewportSizeInBytes(client->GetViewportPixelSize(), &current_bytes))
101       continue;
102     max_bytes = std::max(max_bytes, current_bytes);
103   }
104   return max_bytes;
105 }
106 
107 }  // namespace viz
108