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 "config.h" | |
6 | |
7 #include "cc/texture_update_controller.h" | |
8 | |
9 #include "base/debug/trace_event.h" | |
10 #include "cc/prioritized_texture.h" | |
11 #include "cc/proxy.h" | |
12 #include "cc/resource_provider.h" | |
13 #include "cc/texture_copier.h" | |
14 #include "third_party/khronos/GLES2/gl2.h" | |
15 #include "third_party/skia/include/gpu/SkGpuDevice.h" | |
16 #include <limits> | |
17 #include <public/WebGraphicsContext3D.h> | |
18 #include <public/WebSharedGraphicsContext3D.h> | |
19 #include <wtf/CurrentTime.h> | |
20 | |
21 using WebKit::WebGraphicsContext3D; | |
22 using WebKit::WebSharedGraphicsContext3D; | |
23 | |
24 namespace { | |
25 | |
26 // Number of partial updates we allow. | |
27 const size_t partialTextureUpdatesMax = 12; | |
28 | |
29 // Measured in seconds. | |
30 const double textureUpdateTickRate = 0.004; | |
31 | |
32 // Measured in seconds. | |
33 const double uploaderBusyTickRate = 0.001; | |
34 | |
35 // Flush interval when performing texture uploads. | |
36 const int textureUploadFlushPeriod = 4; | |
37 | |
38 // Number of blocking update intervals to allow. | |
39 const size_t maxBlockingUpdateIntervals = 4; | |
40 | |
41 scoped_ptr<SkCanvas> createAcceleratedCanvas( | |
42 GrContext* grContext, cc::IntSize canvasSize, unsigned textureId) | |
43 { | |
44 GrPlatformTextureDesc textureDesc; | |
45 textureDesc.fFlags = kRenderTarget_GrPlatformTextureFlag; | |
46 textureDesc.fWidth = canvasSize.width(); | |
47 textureDesc.fHeight = canvasSize.height(); | |
48 textureDesc.fConfig = kSkia8888_GrPixelConfig; | |
49 textureDesc.fTextureHandle = textureId; | |
50 SkAutoTUnref<GrTexture> target( | |
51 grContext->createPlatformTexture(textureDesc)); | |
52 SkAutoTUnref<SkDevice> device(new SkGpuDevice(grContext, target.get())); | |
53 return make_scoped_ptr(new SkCanvas(device.get())); | |
54 } | |
55 | |
56 } // namespace | |
57 | |
58 namespace cc { | |
59 | |
60 size_t TextureUpdateController::maxPartialTextureUpdates() | |
61 { | |
62 return partialTextureUpdatesMax; | |
63 } | |
64 | |
65 size_t TextureUpdateController::maxFullUpdatesPerTick( | |
66 ResourceProvider* resourceProvider) | |
67 { | |
68 double texturesPerSecond = resourceProvider->estimatedUploadsPerSecond(); | |
69 size_t texturesPerTick = floor(textureUpdateTickRate * texturesPerSecond); | |
70 return texturesPerTick ? texturesPerTick : 1; | |
71 } | |
72 | |
73 TextureUpdateController::TextureUpdateController(TextureUpdateControllerClient*
client, Thread* thread, scoped_ptr<TextureUpdateQueue> queue, ResourceProvider*
resourceProvider) | |
74 : m_client(client) | |
75 , m_timer(new Timer(thread, this)) | |
76 , m_queue(queue.Pass()) | |
77 , m_resourceProvider(resourceProvider) | |
78 , m_textureUpdatesPerTick(maxFullUpdatesPerTick(resourceProvider)) | |
79 , m_firstUpdateAttempt(true) | |
80 { | |
81 } | |
82 | |
83 TextureUpdateController::~TextureUpdateController() | |
84 { | |
85 } | |
86 | |
87 void TextureUpdateController::performMoreUpdates( | |
88 base::TimeTicks timeLimit) | |
89 { | |
90 m_timeLimit = timeLimit; | |
91 | |
92 // Update already in progress. | |
93 if (m_timer->isActive()) | |
94 return; | |
95 | |
96 // Call updateMoreTexturesNow() directly unless it's the first update | |
97 // attempt. This ensures that we empty the update queue in a finite | |
98 // amount of time. | |
99 if (m_firstUpdateAttempt) { | |
100 // Post a 0-delay task when no updates were left. When it runs, | |
101 // readyToFinalizeTextureUpdates() will be called. | |
102 if (!updateMoreTexturesIfEnoughTimeRemaining()) | |
103 m_timer->startOneShot(0); | |
104 | |
105 m_firstUpdateAttempt = false; | |
106 } else | |
107 updateMoreTexturesNow(); | |
108 } | |
109 | |
110 void TextureUpdateController::discardUploadsToEvictedResources() | |
111 { | |
112 m_queue->clearUploadsToEvictedResources(); | |
113 } | |
114 | |
115 void TextureUpdateController::updateTexture(ResourceUpdate update) | |
116 { | |
117 if (update.picture) { | |
118 PrioritizedTexture* texture = update.texture; | |
119 IntRect pictureRect = update.content_rect; | |
120 IntRect sourceRect = update.source_rect; | |
121 IntSize destOffset = update.dest_offset; | |
122 | |
123 texture->acquireBackingTexture(m_resourceProvider); | |
124 DCHECK(texture->haveBackingTexture()); | |
125 | |
126 DCHECK(m_resourceProvider->resourceType(texture->resourceId()) == | |
127 ResourceProvider::GLTexture); | |
128 | |
129 WebGraphicsContext3D* paintContext = Proxy::hasImplThread() ? | |
130 WebSharedGraphicsContext3D::compositorThreadContext() : | |
131 WebSharedGraphicsContext3D::mainThreadContext(); | |
132 GrContext* paintGrContext = Proxy::hasImplThread() ? | |
133 WebSharedGraphicsContext3D::compositorThreadGrContext() : | |
134 WebSharedGraphicsContext3D::mainThreadGrContext(); | |
135 | |
136 // Flush the context in which the backing texture is created so that it | |
137 // is available in other shared contexts. It is important to do here | |
138 // because the backing texture is created in one context while it is | |
139 // being written to in another. | |
140 m_resourceProvider->flush(); | |
141 ResourceProvider::ScopedWriteLockGL lock( | |
142 m_resourceProvider, texture->resourceId()); | |
143 | |
144 // Make sure ganesh uses the correct GL context. | |
145 paintContext->makeContextCurrent(); | |
146 | |
147 // Create an accelerated canvas to draw on. | |
148 scoped_ptr<SkCanvas> canvas = createAcceleratedCanvas( | |
149 paintGrContext, texture->size(), lock.textureId()); | |
150 | |
151 // The compositor expects the textures to be upside-down so it can flip | |
152 // the final composited image. Ganesh renders the image upright so we | |
153 // need to do a y-flip. | |
154 canvas->translate(0.0, texture->size().height()); | |
155 canvas->scale(1.0, -1.0); | |
156 // Clip to the destination on the texture that must be updated. | |
157 canvas->clipRect(SkRect::MakeXYWH(destOffset.width(), | |
158 destOffset.height(), | |
159 sourceRect.width(), | |
160 sourceRect.height())); | |
161 // Translate the origin of pictureRect to destOffset. | |
162 // Note that destOffset is defined relative to sourceRect. | |
163 canvas->translate( | |
164 pictureRect.x() - sourceRect.x() + destOffset.width(), | |
165 pictureRect.y() - sourceRect.y() + destOffset.height()); | |
166 canvas->drawPicture(*update.picture); | |
167 | |
168 // Flush ganesh context so that all the rendered stuff appears on the | |
169 // texture. | |
170 paintGrContext->flush(); | |
171 | |
172 // Flush the GL context so rendering results from this context are | |
173 // visible in the compositor's context. | |
174 paintContext->flush(); | |
175 } | |
176 | |
177 if (update.bitmap) { | |
178 update.bitmap->lockPixels(); | |
179 update.texture->upload( | |
180 m_resourceProvider, | |
181 static_cast<const uint8_t*>(update.bitmap->getPixels()), | |
182 update.content_rect, | |
183 update.source_rect, | |
184 update.dest_offset); | |
185 update.bitmap->unlockPixels(); | |
186 } | |
187 } | |
188 | |
189 void TextureUpdateController::finalize() | |
190 { | |
191 size_t uploadCount = 0; | |
192 while (m_queue->fullUploadSize()) { | |
193 if (!(uploadCount % textureUploadFlushPeriod) && uploadCount) | |
194 m_resourceProvider->shallowFlushIfSupported(); | |
195 | |
196 updateTexture(m_queue->takeFirstFullUpload()); | |
197 uploadCount++; | |
198 } | |
199 | |
200 while (m_queue->partialUploadSize()) { | |
201 if (!(uploadCount % textureUploadFlushPeriod) && uploadCount) | |
202 m_resourceProvider->shallowFlushIfSupported(); | |
203 | |
204 updateTexture(m_queue->takeFirstPartialUpload()); | |
205 uploadCount++; | |
206 } | |
207 | |
208 if (uploadCount) | |
209 m_resourceProvider->shallowFlushIfSupported(); | |
210 | |
211 if (m_queue->copySize()) { | |
212 TextureCopier* copier = m_resourceProvider->textureCopier(); | |
213 while (m_queue->copySize()) | |
214 copier->copyTexture(m_queue->takeFirstCopy()); | |
215 | |
216 // If we've performed any texture copies, we need to insert a flush | |
217 // here into the compositor context before letting the main thread | |
218 // proceed as it may make draw calls to the source texture of one of | |
219 // our copy operations. | |
220 copier->flush(); | |
221 } | |
222 } | |
223 | |
224 void TextureUpdateController::onTimerFired() | |
225 { | |
226 ResourceProvider::debugNotifyEnterZone(0xB000000); | |
227 if (!updateMoreTexturesIfEnoughTimeRemaining()) | |
228 m_client->readyToFinalizeTextureUpdates(); | |
229 ResourceProvider::debugNotifyLeaveZone(); | |
230 } | |
231 | |
232 base::TimeTicks TextureUpdateController::now() const | |
233 { | |
234 return base::TimeTicks::Now(); | |
235 } | |
236 | |
237 base::TimeDelta TextureUpdateController::updateMoreTexturesTime() const | |
238 { | |
239 return base::TimeDelta::FromMilliseconds(textureUpdateTickRate * 1000); | |
240 } | |
241 | |
242 size_t TextureUpdateController::updateMoreTexturesSize() const | |
243 { | |
244 return m_textureUpdatesPerTick; | |
245 } | |
246 | |
247 size_t TextureUpdateController::maxBlockingUpdates() const | |
248 { | |
249 return updateMoreTexturesSize() * maxBlockingUpdateIntervals; | |
250 } | |
251 | |
252 bool TextureUpdateController::updateMoreTexturesIfEnoughTimeRemaining() | |
253 { | |
254 // Blocking uploads will increase when we're too aggressive in our upload | |
255 // time estimate. We use a different timeout here to prevent unnecessary | |
256 // amounts of idle time when blocking uploads have reached the max. | |
257 if (m_resourceProvider->numBlockingUploads() >= maxBlockingUpdates()) { | |
258 m_timer->startOneShot(uploaderBusyTickRate); | |
259 return true; | |
260 } | |
261 | |
262 if (!m_queue->fullUploadSize()) | |
263 return false; | |
264 | |
265 bool hasTimeRemaining = m_timeLimit.is_null() || | |
266 this->now() < m_timeLimit - updateMoreTexturesTime(); | |
267 if (hasTimeRemaining) | |
268 updateMoreTexturesNow(); | |
269 | |
270 return true; | |
271 } | |
272 | |
273 void TextureUpdateController::updateMoreTexturesNow() | |
274 { | |
275 size_t uploads = std::min( | |
276 m_queue->fullUploadSize(), updateMoreTexturesSize()); | |
277 m_timer->startOneShot( | |
278 updateMoreTexturesTime().InSecondsF() / updateMoreTexturesSize() * | |
279 uploads); | |
280 | |
281 if (!uploads) | |
282 return; | |
283 | |
284 size_t uploadCount = 0; | |
285 while (m_queue->fullUploadSize() && uploadCount < uploads) { | |
286 if (!(uploadCount % textureUploadFlushPeriod) && uploadCount) | |
287 m_resourceProvider->shallowFlushIfSupported(); | |
288 updateTexture(m_queue->takeFirstFullUpload()); | |
289 uploadCount++; | |
290 } | |
291 m_resourceProvider->shallowFlushIfSupported(); | |
292 } | |
293 | |
294 } // namespace cc | |
OLD | NEW |