1 // Copyright 2016 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 "third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h"
6
7 #include "base/single_thread_task_runner.h"
8 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
9 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
10 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
11 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
12 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
13 #include "third_party/blink/renderer/platform/wtf/wtf.h"
14
15 namespace {
16
17 typedef HashMap<int, blink::OffscreenCanvasPlaceholder*> PlaceholderIdMap;
18
placeholderRegistry()19 PlaceholderIdMap& placeholderRegistry() {
20 DEFINE_STATIC_LOCAL(PlaceholderIdMap, s_placeholderRegistry, ());
21 return s_placeholderRegistry;
22 }
23
releaseFrameToDispatcher(base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,scoped_refptr<blink::CanvasResource> oldImage,viz::ResourceId resourceId)24 void releaseFrameToDispatcher(
25 base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,
26 scoped_refptr<blink::CanvasResource> oldImage,
27 viz::ResourceId resourceId) {
28 oldImage = nullptr; // Needed to unref'ed on the right thread
29 if (dispatcher) {
30 dispatcher->ReclaimResource(resourceId);
31 }
32 }
33
SetSuspendAnimation(base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,bool suspend)34 void SetSuspendAnimation(
35 base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,
36 bool suspend) {
37 if (dispatcher) {
38 dispatcher->SetSuspendAnimation(suspend);
39 }
40 }
41
UpdateDispatcherFilterQuality(base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,SkFilterQuality filter)42 void UpdateDispatcherFilterQuality(
43 base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,
44 SkFilterQuality filter) {
45 if (dispatcher) {
46 dispatcher->SetFilterQuality(filter);
47 }
48 }
49
50 } // unnamed namespace
51
52 namespace blink {
53
~OffscreenCanvasPlaceholder()54 OffscreenCanvasPlaceholder::~OffscreenCanvasPlaceholder() {
55 UnregisterPlaceholderCanvas();
56 }
57
SetOffscreenCanvasResource(scoped_refptr<CanvasResource> new_frame,viz::ResourceId resource_id)58 void OffscreenCanvasPlaceholder::SetOffscreenCanvasResource(
59 scoped_refptr<CanvasResource> new_frame,
60 viz::ResourceId resource_id) {
61 DCHECK(IsOffscreenCanvasRegistered());
62 DCHECK(new_frame);
63 ReleaseOffscreenCanvasFrame();
64 placeholder_frame_ = std::move(new_frame);
65 placeholder_frame_resource_id_ = resource_id;
66
67 if (animation_state_ == kShouldSuspendAnimation) {
68 bool success = PostSetSuspendAnimationToOffscreenCanvasThread(true);
69 DCHECK(success);
70 animation_state_ = kSuspendedAnimation;
71 } else if (animation_state_ == kShouldActivateAnimation) {
72 bool success = PostSetSuspendAnimationToOffscreenCanvasThread(false);
73 DCHECK(success);
74 animation_state_ = kActiveAnimation;
75 }
76 }
77
SetOffscreenCanvasDispatcher(base::WeakPtr<CanvasResourceDispatcher> dispatcher,scoped_refptr<base::SingleThreadTaskRunner> task_runner)78 void OffscreenCanvasPlaceholder::SetOffscreenCanvasDispatcher(
79 base::WeakPtr<CanvasResourceDispatcher> dispatcher,
80 scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
81 DCHECK(IsOffscreenCanvasRegistered());
82 frame_dispatcher_ = std::move(dispatcher);
83 frame_dispatcher_task_runner_ = std::move(task_runner);
84 // The UpdateOffscreenCanvasFilterQuality could be called to change the filter
85 // quality before this function. We need to first apply the filter changes to
86 // the corresponding offscreen canvas.
87 if (filter_quality_) {
88 SkFilterQuality quality = filter_quality_.value();
89 filter_quality_ = base::nullopt;
90 UpdateOffscreenCanvasFilterQuality(quality);
91 }
92 }
93
ReleaseOffscreenCanvasFrame()94 void OffscreenCanvasPlaceholder::ReleaseOffscreenCanvasFrame() {
95 DCHECK(IsOffscreenCanvasRegistered());
96 if (!placeholder_frame_)
97 return;
98
99 DCHECK(frame_dispatcher_task_runner_);
100 placeholder_frame_->Transfer();
101 PostCrossThreadTask(
102 *frame_dispatcher_task_runner_, FROM_HERE,
103 CrossThreadBindOnce(releaseFrameToDispatcher, frame_dispatcher_,
104 std::move(placeholder_frame_),
105 placeholder_frame_resource_id_));
106 }
107
UpdateOffscreenCanvasFilterQuality(SkFilterQuality filter_quality)108 void OffscreenCanvasPlaceholder::UpdateOffscreenCanvasFilterQuality(
109 SkFilterQuality filter_quality) {
110 DCHECK(IsOffscreenCanvasRegistered());
111 if (!frame_dispatcher_task_runner_) {
112 filter_quality_ = filter_quality;
113 return;
114 }
115
116 if (filter_quality_ == filter_quality)
117 return;
118
119 filter_quality_ = filter_quality;
120 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
121 Thread::Current()->GetTaskRunner();
122 if (task_runner == frame_dispatcher_task_runner_) {
123 UpdateDispatcherFilterQuality(frame_dispatcher_, filter_quality);
124 } else {
125 PostCrossThreadTask(*frame_dispatcher_task_runner_, FROM_HERE,
126 CrossThreadBindOnce(UpdateDispatcherFilterQuality,
127 frame_dispatcher_, filter_quality));
128 }
129 }
130
SetSuspendOffscreenCanvasAnimation(bool suspend)131 void OffscreenCanvasPlaceholder::SetSuspendOffscreenCanvasAnimation(
132 bool suspend) {
133 switch (animation_state_) {
134 case kActiveAnimation:
135 if (suspend) {
136 if (PostSetSuspendAnimationToOffscreenCanvasThread(suspend)) {
137 animation_state_ = kSuspendedAnimation;
138 } else {
139 animation_state_ = kShouldSuspendAnimation;
140 }
141 }
142 break;
143 case kSuspendedAnimation:
144 if (!suspend) {
145 if (PostSetSuspendAnimationToOffscreenCanvasThread(suspend)) {
146 animation_state_ = kActiveAnimation;
147 } else {
148 animation_state_ = kShouldActivateAnimation;
149 }
150 }
151 break;
152 case kShouldSuspendAnimation:
153 if (!suspend) {
154 animation_state_ = kActiveAnimation;
155 }
156 break;
157 case kShouldActivateAnimation:
158 if (suspend) {
159 animation_state_ = kSuspendedAnimation;
160 }
161 break;
162 default:
163 NOTREACHED();
164 }
165 }
166
167 OffscreenCanvasPlaceholder*
GetPlaceholderCanvasById(unsigned placeholder_id)168 OffscreenCanvasPlaceholder::GetPlaceholderCanvasById(unsigned placeholder_id) {
169 PlaceholderIdMap::iterator it = placeholderRegistry().find(placeholder_id);
170 if (it == placeholderRegistry().end())
171 return nullptr;
172 return it->value;
173 }
174
RegisterPlaceholderCanvas(unsigned placeholder_id)175 void OffscreenCanvasPlaceholder::RegisterPlaceholderCanvas(
176 unsigned placeholder_id) {
177 DCHECK(!placeholderRegistry().Contains(placeholder_id));
178 DCHECK(!IsOffscreenCanvasRegistered());
179 placeholderRegistry().insert(placeholder_id, this);
180 placeholder_id_ = placeholder_id;
181 }
182
UnregisterPlaceholderCanvas()183 void OffscreenCanvasPlaceholder::UnregisterPlaceholderCanvas() {
184 if (!IsOffscreenCanvasRegistered())
185 return;
186 DCHECK(placeholderRegistry().find(placeholder_id_)->value == this);
187 placeholderRegistry().erase(placeholder_id_);
188 placeholder_id_ = kNoPlaceholderId;
189 }
190
PostSetSuspendAnimationToOffscreenCanvasThread(bool suspend)191 bool OffscreenCanvasPlaceholder::PostSetSuspendAnimationToOffscreenCanvasThread(
192 bool suspend) {
193 if (!frame_dispatcher_task_runner_)
194 return false;
195 PostCrossThreadTask(
196 *frame_dispatcher_task_runner_, FROM_HERE,
197 CrossThreadBindOnce(SetSuspendAnimation, frame_dispatcher_, suspend));
198 return true;
199 }
200
201 } // namespace blink
202