1 // Copyright 2014 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/software_output_device_win.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/memory/unsafe_shared_memory_region.h"
11 #include "base/trace_event/trace_event.h"
12 #include "base/win/windows_version.h"
13 #include "components/viz/common/display/use_layered_window.h"
14 #include "components/viz/common/resources/resource_sizes.h"
15 #include "mojo/public/cpp/bindings/pending_remote.h"
16 #include "mojo/public/cpp/system/platform_handle.h"
17 #include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h"
18 #include "skia/ext/platform_canvas.h"
19 #include "skia/ext/skia_utils_win.h"
20 #include "ui/gfx/gdi_util.h"
21 #include "ui/gfx/skia_util.h"
22 #include "ui/gfx/win/hwnd_util.h"
23 #include "ui/gl/vsync_provider_win.h"
24 
25 namespace viz {
26 
SoftwareOutputDeviceWinBase(HWND hwnd)27 SoftwareOutputDeviceWinBase::SoftwareOutputDeviceWinBase(HWND hwnd)
28     : hwnd_(hwnd) {
29   vsync_provider_ = std::make_unique<gl::VSyncProviderWin>(hwnd);
30 }
31 
~SoftwareOutputDeviceWinBase()32 SoftwareOutputDeviceWinBase::~SoftwareOutputDeviceWinBase() {
33   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
34   DCHECK(!in_paint_);
35 }
36 
Resize(const gfx::Size & viewport_pixel_size,float scale_factor)37 void SoftwareOutputDeviceWinBase::Resize(const gfx::Size& viewport_pixel_size,
38                                          float scale_factor) {
39   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
40   DCHECK(!in_paint_);
41 
42   if (viewport_pixel_size_ == viewport_pixel_size)
43     return;
44 
45   viewport_pixel_size_ = viewport_pixel_size;
46   ResizeDelegated();
47 }
48 
BeginPaint(const gfx::Rect & damage_rect)49 SkCanvas* SoftwareOutputDeviceWinBase::BeginPaint(
50     const gfx::Rect& damage_rect) {
51   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
52   DCHECK(!in_paint_);
53 
54   damage_rect_ = damage_rect;
55   in_paint_ = true;
56   return BeginPaintDelegated();
57 }
58 
EndPaint()59 void SoftwareOutputDeviceWinBase::EndPaint() {
60   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
61   DCHECK(in_paint_);
62 
63   in_paint_ = false;
64 
65   gfx::Rect intersected_damage_rect = damage_rect_;
66   intersected_damage_rect.Intersect(gfx::Rect(viewport_pixel_size_));
67   if (intersected_damage_rect.IsEmpty())
68     return;
69 
70   EndPaintDelegated(intersected_damage_rect);
71 }
72 
SoftwareOutputDeviceWinDirect(HWND hwnd,OutputDeviceBacking * backing)73 SoftwareOutputDeviceWinDirect::SoftwareOutputDeviceWinDirect(
74     HWND hwnd,
75     OutputDeviceBacking* backing)
76     : SoftwareOutputDeviceWinBase(hwnd), backing_(backing) {
77   backing_->RegisterClient(this);
78 }
79 
~SoftwareOutputDeviceWinDirect()80 SoftwareOutputDeviceWinDirect::~SoftwareOutputDeviceWinDirect() {
81   backing_->UnregisterClient(this);
82 }
83 
ResizeDelegated()84 void SoftwareOutputDeviceWinDirect::ResizeDelegated() {
85   canvas_.reset();
86   backing_->ClientResized();
87 }
88 
BeginPaintDelegated()89 SkCanvas* SoftwareOutputDeviceWinDirect::BeginPaintDelegated() {
90   if (!canvas_) {
91     // Share pixel backing with other SoftwareOutputDeviceWinDirect instances.
92     // All work happens on the same thread so this is safe.
93     base::UnsafeSharedMemoryRegion* region =
94         backing_->GetSharedMemoryRegion(viewport_pixel_size_);
95     if (region && region->IsValid()) {
96       canvas_ = skia::CreatePlatformCanvasWithSharedSection(
97           viewport_pixel_size_.width(), viewport_pixel_size_.height(), true,
98           region->GetPlatformHandle(), skia::CRASH_ON_FAILURE);
99     }
100   }
101   return canvas_.get();
102 }
103 
EndPaintDelegated(const gfx::Rect & damage_rect)104 void SoftwareOutputDeviceWinDirect::EndPaintDelegated(
105     const gfx::Rect& damage_rect) {
106   if (!canvas_)
107     return;
108 
109   HDC dib_dc = skia::GetNativeDrawingContext(canvas_.get());
110   HDC hdc = ::GetDC(hwnd());
111   RECT src_rect = damage_rect.ToRECT();
112   skia::CopyHDC(dib_dc, hdc, damage_rect.x(), damage_rect.y(),
113                 canvas_->imageInfo().isOpaque(), src_rect,
114                 canvas_->getTotalMatrix());
115 
116   ::ReleaseDC(hwnd(), hdc);
117 }
118 
GetViewportPixelSize() const119 const gfx::Size& SoftwareOutputDeviceWinDirect::GetViewportPixelSize() const {
120   return viewport_pixel_size_;
121 }
122 
ReleaseCanvas()123 void SoftwareOutputDeviceWinDirect::ReleaseCanvas() {
124   canvas_.reset();
125 }
126 
SoftwareOutputDeviceWinProxy(HWND hwnd,mojo::PendingRemote<mojom::LayeredWindowUpdater> layered_window_updater)127 SoftwareOutputDeviceWinProxy::SoftwareOutputDeviceWinProxy(
128     HWND hwnd,
129     mojo::PendingRemote<mojom::LayeredWindowUpdater> layered_window_updater)
130     : SoftwareOutputDeviceWinBase(hwnd),
131       layered_window_updater_(std::move(layered_window_updater)) {
132   DCHECK(layered_window_updater_.is_bound());
133 }
134 
135 SoftwareOutputDeviceWinProxy::~SoftwareOutputDeviceWinProxy() = default;
136 
OnSwapBuffers(SwapBuffersCallback swap_ack_callback)137 void SoftwareOutputDeviceWinProxy::OnSwapBuffers(
138     SwapBuffersCallback swap_ack_callback) {
139   DCHECK(swap_ack_callback_.is_null());
140 
141   // We aren't waiting on DrawAck() and can immediately run the callback.
142   if (!waiting_on_draw_ack_) {
143     task_runner_->PostTask(
144         FROM_HERE,
145         base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_));
146     return;
147   }
148 
149   swap_ack_callback_ =
150       base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_);
151 }
152 
ResizeDelegated()153 void SoftwareOutputDeviceWinProxy::ResizeDelegated() {
154   canvas_.reset();
155 
156   size_t required_bytes;
157   if (!ResourceSizes::MaybeSizeInBytes(
158           viewport_pixel_size_, ResourceFormat::RGBA_8888, &required_bytes)) {
159     DLOG(ERROR) << "Invalid viewport size " << viewport_pixel_size_.ToString();
160     return;
161   }
162 
163   base::UnsafeSharedMemoryRegion region =
164       base::UnsafeSharedMemoryRegion::Create(required_bytes);
165   if (!region.IsValid()) {
166     DLOG(ERROR) << "Failed to allocate " << required_bytes << " bytes";
167     return;
168   }
169 
170   // The SkCanvas maps shared memory on creation and unmaps on destruction.
171   canvas_ = skia::CreatePlatformCanvasWithSharedSection(
172       viewport_pixel_size_.width(), viewport_pixel_size_.height(), true,
173       region.GetPlatformHandle(), skia::CRASH_ON_FAILURE);
174 
175   // Transfer region ownership to the browser process.
176   layered_window_updater_->OnAllocatedSharedMemory(viewport_pixel_size_,
177                                                    std::move(region));
178 }
179 
BeginPaintDelegated()180 SkCanvas* SoftwareOutputDeviceWinProxy::BeginPaintDelegated() {
181   return canvas_.get();
182 }
183 
EndPaintDelegated(const gfx::Rect & damage_rect)184 void SoftwareOutputDeviceWinProxy::EndPaintDelegated(
185     const gfx::Rect& damage_rect) {
186   DCHECK(!waiting_on_draw_ack_);
187 
188   if (!canvas_)
189     return;
190 
191   layered_window_updater_->Draw(base::BindOnce(
192       &SoftwareOutputDeviceWinProxy::DrawAck, base::Unretained(this)));
193   waiting_on_draw_ack_ = true;
194 
195   TRACE_EVENT_ASYNC_BEGIN0("viz", "SoftwareOutputDeviceWinProxy::Draw", this);
196 }
197 
DrawAck()198 void SoftwareOutputDeviceWinProxy::DrawAck() {
199   DCHECK(waiting_on_draw_ack_);
200 
201   TRACE_EVENT_ASYNC_END0("viz", "SoftwareOutputDeviceWinProxy::Draw", this);
202 
203   waiting_on_draw_ack_ = false;
204 
205   // It's possible the display compositor won't call SwapBuffers() so there will
206   // be no callback to run.
207   if (swap_ack_callback_)
208     std::move(swap_ack_callback_).Run();
209 }
210 
CreateSoftwareOutputDeviceWin(HWND hwnd,OutputDeviceBacking * backing,mojom::DisplayClient * display_client)211 std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWin(
212     HWND hwnd,
213     OutputDeviceBacking* backing,
214     mojom::DisplayClient* display_client) {
215   if (NeedsToUseLayerWindow(hwnd)) {
216     DCHECK(display_client);
217 
218     // Setup mojom::LayeredWindowUpdater implementation in the browser process
219     // to draw to the HWND.
220     mojo::PendingRemote<mojom::LayeredWindowUpdater> layered_window_updater;
221     display_client->CreateLayeredWindowUpdater(
222         layered_window_updater.InitWithNewPipeAndPassReceiver());
223 
224     return std::make_unique<SoftwareOutputDeviceWinProxy>(
225         hwnd, std::move(layered_window_updater));
226   } else {
227     return std::make_unique<SoftwareOutputDeviceWinDirect>(hwnd, backing);
228   }
229 }
230 
231 }  // namespace viz
232