// Copyright 2014 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 "cef/libcef/browser/osr/software_output_device_proxy.h" #include "base/memory/shared_memory.h" #include "components/viz/common/resources/resource_sizes.h" #include "mojo/public/cpp/base/shared_memory_utils.h" #include "mojo/public/cpp/system/platform_handle.h" #include "skia/ext/platform_canvas.h" #include "third_party/skia/include/core/SkCanvas.h" #include "ui/gfx/skia_util.h" #if defined(OS_WIN) #include #include "skia/ext/skia_utils_win.h" #include "ui/gfx/gdi_util.h" #include "ui/gfx/win/hwnd_util.h" #endif namespace viz { SoftwareOutputDeviceProxy::~SoftwareOutputDeviceProxy() = default; SoftwareOutputDeviceProxy::SoftwareOutputDeviceProxy( mojom::LayeredWindowUpdaterPtr layered_window_updater) : layered_window_updater_(std::move(layered_window_updater)) { DCHECK(layered_window_updater_.is_bound()); } void SoftwareOutputDeviceProxy::OnSwapBuffers( SwapBuffersCallback swap_ack_callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(swap_ack_callback_.is_null()); // We aren't waiting on DrawAck() and can immediately run the callback. if (!waiting_on_draw_ack_) { task_runner_->PostTask( FROM_HERE, base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_)); return; } swap_ack_callback_ = base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_); } void SoftwareOutputDeviceProxy::Resize(const gfx::Size& viewport_pixel_size, float scale_factor) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!in_paint_); if (viewport_pixel_size_ == viewport_pixel_size) return; viewport_pixel_size_ = viewport_pixel_size; canvas_.reset(); size_t required_bytes; if (!ResourceSizes::MaybeSizeInBytes( viewport_pixel_size_, ResourceFormat::RGBA_8888, &required_bytes)) { DLOG(ERROR) << "Invalid viewport size " << viewport_pixel_size_.ToString(); return; } base::UnsafeSharedMemoryRegion region = base::UnsafeSharedMemoryRegion::Create(required_bytes); if (!region.IsValid()) { DLOG(ERROR) << "Failed to allocate " << required_bytes << " bytes"; return; } #if !defined(OS_WIN) auto shm = mojo::CreateReadOnlySharedMemoryRegion(required_bytes); if (!shm.IsValid()) { DLOG(ERROR) << "Failed to allocate " << required_bytes << " bytes"; return; } shm_ = region.Map(); if (!shm_.IsValid()) { DLOG(ERROR) << "Failed to map " << required_bytes << " bytes"; return; } canvas_ = skia::CreatePlatformCanvasWithPixels( viewport_pixel_size_.width(), viewport_pixel_size_.height(), false, static_cast(shm_.memory()), skia::CRASH_ON_FAILURE); #else canvas_ = skia::CreatePlatformCanvasWithSharedSection( viewport_pixel_size_.width(), viewport_pixel_size_.height(), false, region.GetPlatformHandle(), skia::CRASH_ON_FAILURE); #endif // Transfer region ownership to the browser process. layered_window_updater_->OnAllocatedSharedMemory(viewport_pixel_size_, std::move(region)); } SkCanvas* SoftwareOutputDeviceProxy::BeginPaint(const gfx::Rect& damage_rect) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!in_paint_); damage_rect_ = damage_rect; in_paint_ = true; return canvas_.get(); } void SoftwareOutputDeviceProxy::EndPaint() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(in_paint_); DCHECK(!waiting_on_draw_ack_); in_paint_ = false; gfx::Rect intersected_damage_rect = damage_rect_; intersected_damage_rect.Intersect(gfx::Rect(viewport_pixel_size_)); if (intersected_damage_rect.IsEmpty()) return; if (!canvas_) return; layered_window_updater_->Draw( damage_rect_, base::BindOnce(&SoftwareOutputDeviceProxy::DrawAck, base::Unretained(this))); waiting_on_draw_ack_ = true; TRACE_EVENT_ASYNC_BEGIN0("viz", "SoftwareOutputDeviceProxy::Draw", this); } void SoftwareOutputDeviceProxy::DrawAck() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(waiting_on_draw_ack_); DCHECK(!swap_ack_callback_.is_null()); TRACE_EVENT_ASYNC_END0("viz", "SoftwareOutputDeviceProxy::Draw", this); waiting_on_draw_ack_ = false; std::move(swap_ack_callback_).Run(); } } // namespace viz