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