OLD | NEW |
| (Empty) |
1 // Copyright 2012 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 "cc/resource_update_controller.h" | |
6 | |
7 #include <limits> | |
8 | |
9 #include "base/debug/trace_event.h" | |
10 #include "cc/base/thread.h" | |
11 #include "cc/output/context_provider.h" | |
12 #include "cc/output/texture_copier.h" | |
13 #include "cc/prioritized_resource.h" | |
14 #include "cc/resource_provider.h" | |
15 #include "skia/ext/refptr.h" | |
16 #include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3
D.h" | |
17 #include "third_party/khronos/GLES2/gl2.h" | |
18 #include "third_party/skia/include/gpu/SkGpuDevice.h" | |
19 | |
20 using WebKit::WebGraphicsContext3D; | |
21 | |
22 namespace { | |
23 | |
24 // Number of partial updates we allow. | |
25 const size_t kPartialTextureUpdatesMax = 12; | |
26 | |
27 // Measured in seconds. | |
28 const double kTextureUpdateTickRate = 0.004; | |
29 | |
30 // Measured in seconds. | |
31 const double kUploaderBusyTickRate = 0.001; | |
32 | |
33 // Number of blocking update intervals to allow. | |
34 const size_t kMaxBlockingUpdateIntervals = 4; | |
35 | |
36 skia::RefPtr<SkCanvas> CreateAcceleratedCanvas( | |
37 GrContext* gr_context, gfx::Size canvasSize, unsigned textureId) { | |
38 GrBackendTextureDesc texture_desc; | |
39 texture_desc.fFlags = kRenderTarget_GrBackendTextureFlag; | |
40 texture_desc.fWidth = canvasSize.width(); | |
41 texture_desc.fHeight = canvasSize.height(); | |
42 texture_desc.fConfig = kSkia8888_GrPixelConfig; | |
43 texture_desc.fTextureHandle = textureId; | |
44 skia::RefPtr<GrTexture> target = | |
45 skia::AdoptRef(gr_context->wrapBackendTexture(texture_desc)); | |
46 skia::RefPtr<SkDevice> device = | |
47 skia::AdoptRef(new SkGpuDevice(gr_context, target.get())); | |
48 return skia::AdoptRef(new SkCanvas(device.get())); | |
49 } | |
50 | |
51 } // namespace | |
52 | |
53 namespace cc { | |
54 | |
55 size_t ResourceUpdateController::MaxPartialTextureUpdates() { | |
56 return kPartialTextureUpdatesMax; | |
57 } | |
58 | |
59 size_t ResourceUpdateController::MaxFullUpdatesPerTick( | |
60 ResourceProvider* resource_provider) { | |
61 double textures_per_second = resource_provider->EstimatedUploadsPerSecond(); | |
62 size_t textures_per_tick = | |
63 floor(kTextureUpdateTickRate * textures_per_second); | |
64 return textures_per_tick ? textures_per_tick : 1; | |
65 } | |
66 | |
67 ResourceUpdateController::ResourceUpdateController( | |
68 ResourceUpdateControllerClient* client, | |
69 Thread* thread, | |
70 scoped_ptr<ResourceUpdateQueue> queue, | |
71 ResourceProvider* resource_provider) | |
72 : client_(client), | |
73 queue_(queue.Pass()), | |
74 resource_provider_(resource_provider), | |
75 texture_updates_per_tick_(MaxFullUpdatesPerTick(resource_provider)), | |
76 first_update_attempt_(true), | |
77 thread_(thread), | |
78 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | |
79 task_posted_(false) {} | |
80 | |
81 ResourceUpdateController::~ResourceUpdateController() {} | |
82 | |
83 void ResourceUpdateController::PerformMoreUpdates( | |
84 base::TimeTicks timeLimit) { | |
85 time_limit_ = timeLimit; | |
86 | |
87 // Update already in progress. | |
88 if (task_posted_) | |
89 return; | |
90 | |
91 // Call UpdateMoreTexturesNow() directly unless it's the first update | |
92 // attempt. This ensures that we empty the update queue in a finite | |
93 // amount of time. | |
94 if (!first_update_attempt_) | |
95 UpdateMoreTexturesNow(); | |
96 | |
97 // Post a 0-delay task when no updates were left. When it runs, | |
98 // ReadyToFinalizeTextureUpdates() will be called. | |
99 if (!UpdateMoreTexturesIfEnoughTimeRemaining()) { | |
100 task_posted_ = true; | |
101 thread_->PostTask( | |
102 base::Bind(&ResourceUpdateController::OnTimerFired, | |
103 weak_factory_.GetWeakPtr())); | |
104 } | |
105 | |
106 first_update_attempt_ = false; | |
107 } | |
108 | |
109 void ResourceUpdateController::DiscardUploadsToEvictedResources() { | |
110 queue_->clearUploadsToEvictedResources(); | |
111 } | |
112 | |
113 void ResourceUpdateController::UpdateTexture(ResourceUpdate update) { | |
114 if (update.picture) { | |
115 PrioritizedResource* texture = update.texture; | |
116 gfx::Rect picture_rect = update.content_rect; | |
117 gfx::Rect source_rect = update.source_rect; | |
118 gfx::Vector2d dest_offset = update.dest_offset; | |
119 | |
120 texture->acquireBackingTexture(resource_provider_); | |
121 DCHECK(texture->haveBackingTexture()); | |
122 | |
123 DCHECK(resource_provider_->GetResourceType(texture->resourceId()) == | |
124 ResourceProvider::GLTexture); | |
125 | |
126 cc::ContextProvider* offscreen_contexts = | |
127 resource_provider_->offscreen_context_provider(); | |
128 | |
129 ResourceProvider::ScopedWriteLockGL lock( | |
130 resource_provider_, texture->resourceId()); | |
131 | |
132 // Flush the compositor context to ensure that textures there are available | |
133 // in the shared context. Do this after locking/creating the compositor | |
134 // texture. | |
135 resource_provider_->Flush(); | |
136 | |
137 // Make sure skia uses the correct GL context. | |
138 offscreen_contexts->Context3d()->makeContextCurrent(); | |
139 | |
140 // Create an accelerated canvas to draw on. | |
141 skia::RefPtr<SkCanvas> canvas = CreateAcceleratedCanvas( | |
142 offscreen_contexts->GrContext(), texture->size(), lock.texture_id()); | |
143 | |
144 // The compositor expects the textures to be upside-down so it can flip | |
145 // the final composited image. Ganesh renders the image upright so we | |
146 // need to do a y-flip. | |
147 canvas->translate(0.0, texture->size().height()); | |
148 canvas->scale(1.0, -1.0); | |
149 // Clip to the destination on the texture that must be updated. | |
150 canvas->clipRect(SkRect::MakeXYWH(dest_offset.x(), | |
151 dest_offset.y(), | |
152 source_rect.width(), | |
153 source_rect.height())); | |
154 // Translate the origin of picture_rect to dest_offset. | |
155 // Note that dest_offset is defined relative to source_rect. | |
156 canvas->translate( | |
157 picture_rect.x() - source_rect.x() + dest_offset.x(), | |
158 picture_rect.y() - source_rect.y() + dest_offset.y()); | |
159 canvas->drawPicture(*update.picture); | |
160 | |
161 // Flush skia context so that all the rendered stuff appears on the | |
162 // texture. | |
163 offscreen_contexts->GrContext()->flush(); | |
164 | |
165 // Flush the GL context so rendering results from this context are | |
166 // visible in the compositor's context. | |
167 offscreen_contexts->Context3d()->flush(); | |
168 | |
169 // Use the compositor's GL context again. | |
170 resource_provider_->GraphicsContext3D()->makeContextCurrent(); | |
171 } | |
172 | |
173 if (update.bitmap) { | |
174 update.bitmap->lockPixels(); | |
175 update.texture->setPixels( | |
176 resource_provider_, | |
177 static_cast<const uint8_t*>(update.bitmap->getPixels()), | |
178 update.content_rect, | |
179 update.source_rect, | |
180 update.dest_offset); | |
181 update.bitmap->unlockPixels(); | |
182 } | |
183 } | |
184 | |
185 void ResourceUpdateController::Finalize() { | |
186 while (queue_->fullUploadSize()) | |
187 UpdateTexture(queue_->takeFirstFullUpload()); | |
188 | |
189 while (queue_->partialUploadSize()) | |
190 UpdateTexture(queue_->takeFirstPartialUpload()); | |
191 | |
192 resource_provider_->FlushUploads(); | |
193 | |
194 if (queue_->copySize()) { | |
195 TextureCopier* copier = resource_provider_->texture_copier(); | |
196 while (queue_->copySize()) | |
197 copier->CopyTexture(queue_->takeFirstCopy()); | |
198 | |
199 // If we've performed any texture copies, we need to insert a flush | |
200 // here into the compositor context before letting the main thread | |
201 // proceed as it may make draw calls to the source texture of one of | |
202 // our copy operations. | |
203 copier->Flush(); | |
204 } | |
205 } | |
206 | |
207 void ResourceUpdateController::OnTimerFired() { | |
208 task_posted_ = false; | |
209 if (!UpdateMoreTexturesIfEnoughTimeRemaining()) | |
210 client_->ReadyToFinalizeTextureUpdates(); | |
211 } | |
212 | |
213 base::TimeTicks ResourceUpdateController::Now() const { | |
214 return base::TimeTicks::Now(); | |
215 } | |
216 | |
217 base::TimeDelta ResourceUpdateController::UpdateMoreTexturesTime() const { | |
218 return base::TimeDelta::FromMilliseconds(kTextureUpdateTickRate * 1000); | |
219 } | |
220 | |
221 size_t ResourceUpdateController::UpdateMoreTexturesSize() const { | |
222 return texture_updates_per_tick_; | |
223 } | |
224 | |
225 size_t ResourceUpdateController::MaxBlockingUpdates() const { | |
226 return UpdateMoreTexturesSize() * kMaxBlockingUpdateIntervals; | |
227 } | |
228 | |
229 base::TimeDelta ResourceUpdateController::PendingUpdateTime() const { | |
230 base::TimeDelta update_one_resource_time = | |
231 UpdateMoreTexturesTime() / UpdateMoreTexturesSize(); | |
232 return update_one_resource_time * resource_provider_->NumBlockingUploads(); | |
233 } | |
234 | |
235 bool ResourceUpdateController::UpdateMoreTexturesIfEnoughTimeRemaining() { | |
236 while (resource_provider_->NumBlockingUploads() < MaxBlockingUpdates()) { | |
237 if (!queue_->fullUploadSize()) | |
238 return false; | |
239 | |
240 if (!time_limit_.is_null()) { | |
241 // Estimated completion time of all pending updates. | |
242 base::TimeTicks completion_time = Now() + PendingUpdateTime(); | |
243 | |
244 // Time remaining based on current completion estimate. | |
245 base::TimeDelta time_remaining = time_limit_ - completion_time; | |
246 | |
247 if (time_remaining < UpdateMoreTexturesTime()) | |
248 return true; | |
249 } | |
250 | |
251 UpdateMoreTexturesNow(); | |
252 } | |
253 | |
254 task_posted_ = true; | |
255 thread_->PostDelayedTask( | |
256 base::Bind(&ResourceUpdateController::OnTimerFired, | |
257 weak_factory_.GetWeakPtr()), | |
258 base::TimeDelta::FromMilliseconds(kUploaderBusyTickRate * 1000)); | |
259 return true; | |
260 } | |
261 | |
262 void ResourceUpdateController::UpdateMoreTexturesNow() { | |
263 size_t uploads = std::min( | |
264 queue_->fullUploadSize(), UpdateMoreTexturesSize()); | |
265 | |
266 if (!uploads) | |
267 return; | |
268 | |
269 while (queue_->fullUploadSize() && uploads--) | |
270 UpdateTexture(queue_->takeFirstFullUpload()); | |
271 | |
272 resource_provider_->FlushUploads(); | |
273 } | |
274 | |
275 } // namespace cc | |
OLD | NEW |