| // Copyright 2018 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_device_backing.h" |
| |
| #include <algorithm> |
| |
| #include "base/debug/alias.h" |
| #include "base/logging.h" |
| #include "base/memory/shared_memory.h" |
| #include "base/stl_util.h" |
| #include "components/viz/common/resources/resource_sizes.h" |
| |
| namespace viz { |
| namespace { |
| |
| // If a window is larger than this in bytes, don't even try to create a backing |
| // bitmap for it. |
| constexpr size_t kMaxBitmapSizeBytes = 4 * (16384 * 8192); |
| |
| // Finds the size in bytes to hold |viewport_size| pixels. If |viewport_size| is |
| // a valid size this will return true and |out_bytes| will contain the size in |
| // bytes. If |viewport_size| is not a valid size then this will return false. |
| bool GetViewportSizeInBytes(const gfx::Size& viewport_size, size_t* out_bytes) { |
| size_t bytes; |
| if (!ResourceSizes::MaybeSizeInBytes(viewport_size, RGBA_8888, &bytes)) |
| return false; |
| if (bytes > kMaxBitmapSizeBytes) |
| return false; |
| *out_bytes = bytes; |
| return true; |
| } |
| |
| } // namespace |
| |
| OutputDeviceBacking::OutputDeviceBacking() = default; |
| |
| OutputDeviceBacking::~OutputDeviceBacking() { |
| DCHECK(clients_.empty()); |
| } |
| |
| void OutputDeviceBacking::ClientResized() { |
| // If the max viewport size doesn't change then nothing here changes. |
| if (GetMaxViewportBytes() == created_shm_bytes_) |
| return; |
| |
| // Otherwise we need to allocate a new shared memory region and clients |
| // should re-request it. |
| for (auto* client : clients_) |
| client->ReleaseCanvas(); |
| |
| region_ = {}; |
| created_shm_bytes_ = 0; |
| } |
| |
| void OutputDeviceBacking::RegisterClient(Client* client) { |
| clients_.push_back(client); |
| } |
| |
| void OutputDeviceBacking::UnregisterClient(Client* client) { |
| DCHECK(base::Contains(clients_, client)); |
| base::Erase(clients_, client); |
| ClientResized(); |
| } |
| |
| base::UnsafeSharedMemoryRegion* OutputDeviceBacking::GetSharedMemoryRegion( |
| const gfx::Size& viewport_size) { |
| // If |viewport_size| is empty or too big don't try to allocate SharedMemory. |
| size_t viewport_bytes; |
| if (!GetViewportSizeInBytes(viewport_size, &viewport_bytes)) |
| return nullptr; |
| |
| // Allocate a new SharedMemory segment that can fit the largest viewport. |
| if (!region_.IsValid()) { |
| size_t max_viewport_bytes = GetMaxViewportBytes(); |
| DCHECK_LE(viewport_bytes, max_viewport_bytes); |
| |
| base::debug::Alias(&max_viewport_bytes); |
| region_ = base::UnsafeSharedMemoryRegion::Create(max_viewport_bytes); |
| if (!region_.IsValid()) { |
| LOG(ERROR) << "Shared memory region create failed on " |
| << max_viewport_bytes << " bytes"; |
| return nullptr; |
| } |
| created_shm_bytes_ = max_viewport_bytes; |
| } else { |
| // Clients must call Resize() for new |viewport_size|. |
| DCHECK_LE(viewport_bytes, created_shm_bytes_); |
| } |
| |
| return ®ion_; |
| } |
| |
| size_t OutputDeviceBacking::GetMaxViewportBytes() { |
| // Minimum byte size is 1 because creating a 0-byte-long SharedMemory fails. |
| size_t max_bytes = 1; |
| for (auto* client : clients_) { |
| size_t current_bytes; |
| if (!GetViewportSizeInBytes(client->GetViewportPixelSize(), ¤t_bytes)) |
| continue; |
| max_bytes = std::max(max_bytes, current_bytes); |
| } |
| return max_bytes; |
| } |
| |
| } // namespace viz |