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.
7 #include "cc/texture_uploader.h"
12 #include "base/debug/alias.h"
13 #include "base/debug/trace_event.h"
14 #include "base/metrics/histogram.h"
15 #include "cc/texture.h"
16 #include "cc/prioritized_resource.h"
17 #include "third_party/khronos/GLES2/gl2.h"
18 #include "third_party/khronos/GLES2/gl2ext.h"
19 #include "ui/gfx/rect.h"
20 #include "ui/gfx/vector2d.h"
21 #include <public/WebGraphicsContext3D.h>
25 // How many previous uploads to use when predicting future throughput.
26 static const size_t uploadHistorySizeMax
= 1000;
27 static const size_t uploadHistorySizeInitial
= 100;
29 // Global estimated number of textures per second to maintain estimates across
30 // subsequent instances of TextureUploader.
31 // More than one thread will not access this variable, so we do not need to synchronize access.
32 static const double defaultEstimatedTexturesPerSecond
= 48.0 * 60.0;
34 // Flush interval when performing texture uploads.
35 const int textureUploadFlushPeriod
= 4;
37 } // anonymous namespace
41 TextureUploader::Query::Query(WebKit::WebGraphicsContext3D
* context
)
46 , m_isNonBlocking(false)
48 m_queryId
= m_context
->createQueryEXT();
51 TextureUploader::Query::~Query()
53 m_context
->deleteQueryEXT(m_queryId
);
56 void TextureUploader::Query::begin()
59 m_isNonBlocking
= false;
60 m_context
->beginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM
, m_queryId
);
63 void TextureUploader::Query::end()
65 m_context
->endQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM
);
68 bool TextureUploader::Query::isPending()
70 unsigned available
= 1;
71 m_context
->getQueryObjectuivEXT(m_queryId
, GL_QUERY_RESULT_AVAILABLE_EXT
, &available
);
75 unsigned TextureUploader::Query::value()
78 m_context
->getQueryObjectuivEXT(m_queryId
, GL_QUERY_RESULT_EXT
, &m_value
);
84 void TextureUploader::Query::markAsNonBlocking()
86 m_isNonBlocking
= true;
89 bool TextureUploader::Query::isNonBlocking()
91 return m_isNonBlocking
;
94 TextureUploader::TextureUploader(
95 WebKit::WebGraphicsContext3D
* context
,
96 bool useMapTexSubImage
,
99 , m_numBlockingTextureUploads(0)
100 , m_useMapTexSubImage(useMapTexSubImage
)
102 , m_useShallowFlush(useShallowFlush
)
103 , m_numTextureUploadsSinceLastFlush(0)
105 for (size_t i
= uploadHistorySizeInitial
; i
> 0; i
--)
106 m_texturesPerSecondHistory
.insert(defaultEstimatedTexturesPerSecond
);
109 TextureUploader::~TextureUploader()
113 size_t TextureUploader::numBlockingUploads()
116 return m_numBlockingTextureUploads
;
119 void TextureUploader::markPendingUploadsAsNonBlocking()
121 for (ScopedPtrDeque
<Query
>::iterator it
= m_pendingQueries
.begin();
122 it
!= m_pendingQueries
.end(); ++it
) {
123 if ((*it
)->isNonBlocking())
126 m_numBlockingTextureUploads
--;
127 (*it
)->markAsNonBlocking();
130 DCHECK(!m_numBlockingTextureUploads
);
133 double TextureUploader::estimatedTexturesPerSecond()
137 // Use the median as our estimate.
138 std::multiset
<double>::iterator median
= m_texturesPerSecondHistory
.begin();
139 std::advance(median
, m_texturesPerSecondHistory
.size() / 2);
140 TRACE_COUNTER1("cc", "estimatedTexturesPerSecond", *median
);
144 void TextureUploader::beginQuery()
146 if (m_availableQueries
.isEmpty())
147 m_availableQueries
.append(Query::create(m_context
));
149 m_availableQueries
.first()->begin();
152 void TextureUploader::endQuery()
154 m_availableQueries
.first()->end();
155 m_pendingQueries
.append(m_availableQueries
.takeFirst());
156 m_numBlockingTextureUploads
++;
159 void TextureUploader::upload(const uint8
* image
,
160 const gfx::Rect
& image_rect
,
161 const gfx::Rect
& source_rect
,
162 const gfx::Vector2d
& dest_offset
,
164 const gfx::Size
& size
)
166 CHECK(image_rect
.Contains(source_rect
));
168 bool isFullUpload
= dest_offset
.IsZero() && source_rect
.size() == size
;
173 if (m_useMapTexSubImage
) {
174 uploadWithMapTexSubImage(
175 image
, image_rect
, source_rect
, dest_offset
, format
);
177 uploadWithTexSubImage(
178 image
, image_rect
, source_rect
, dest_offset
, format
);
184 m_numTextureUploadsSinceLastFlush
++;
185 if (m_numTextureUploadsSinceLastFlush
>= textureUploadFlushPeriod
)
189 void TextureUploader::flush() {
190 if (!m_numTextureUploadsSinceLastFlush
)
193 if (m_useShallowFlush
)
194 m_context
->shallowFlushCHROMIUM();
196 m_numTextureUploadsSinceLastFlush
= 0;
199 void TextureUploader::uploadWithTexSubImage(const uint8
* image
,
200 const gfx::Rect
& image_rect
,
201 const gfx::Rect
& source_rect
,
202 const gfx::Vector2d
& dest_offset
,
205 // Instrumentation to debug issue 156107
206 int source_rect_x
= source_rect
.x();
207 int source_rect_y
= source_rect
.y();
208 int source_rect_width
= source_rect
.width();
209 int source_rect_height
= source_rect
.height();
210 int image_rect_x
= image_rect
.x();
211 int image_rect_y
= image_rect
.y();
212 int image_rect_width
= image_rect
.width();
213 int image_rect_height
= image_rect
.height();
214 int dest_offset_x
= dest_offset
.x();
215 int dest_offset_y
= dest_offset
.y();
216 base::debug::Alias(&image
);
217 base::debug::Alias(&source_rect_x
);
218 base::debug::Alias(&source_rect_y
);
219 base::debug::Alias(&source_rect_width
);
220 base::debug::Alias(&source_rect_height
);
221 base::debug::Alias(&image_rect_x
);
222 base::debug::Alias(&image_rect_y
);
223 base::debug::Alias(&image_rect_width
);
224 base::debug::Alias(&image_rect_height
);
225 base::debug::Alias(&dest_offset_x
);
226 base::debug::Alias(&dest_offset_y
);
227 TRACE_EVENT0("cc", "TextureUploader::uploadWithTexSubImage");
229 // Offset from image-rect to source-rect.
230 gfx::Vector2d
offset(source_rect
.origin() - image_rect
.origin());
232 const uint8
* pixel_source
;
233 unsigned int bytes_per_pixel
= Texture::bytesPerPixel(format
);
235 if (image_rect
.width() == source_rect
.width() && !offset
.x()) {
236 pixel_source
= &image
[bytes_per_pixel
* offset
.y() * image_rect
.width()];
238 size_t needed_size
= source_rect
.width() * source_rect
.height() * bytes_per_pixel
;
239 if (m_subImageSize
< needed_size
) {
240 m_subImage
.reset(new uint8
[needed_size
]);
241 m_subImageSize
= needed_size
;
243 // Strides not equal, so do a row-by-row memcpy from the
244 // paint results into a temp buffer for uploading.
245 for (int row
= 0; row
< source_rect
.height(); ++row
)
246 memcpy(&m_subImage
[source_rect
.width() * bytes_per_pixel
* row
],
247 &image
[bytes_per_pixel
* (offset
.x() +
248 (offset
.y() + row
) * image_rect
.width())],
249 source_rect
.width() * bytes_per_pixel
);
251 pixel_source
= &m_subImage
[0];
254 m_context
->texSubImage2D(GL_TEXTURE_2D
,
259 source_rect
.height(),
265 void TextureUploader::uploadWithMapTexSubImage(const uint8
* image
,
266 const gfx::Rect
& image_rect
,
267 const gfx::Rect
& source_rect
,
268 const gfx::Vector2d
& dest_offset
,
271 // Instrumentation to debug issue 156107
272 int source_rect_x
= source_rect
.x();
273 int source_rect_y
= source_rect
.y();
274 int source_rect_width
= source_rect
.width();
275 int source_rect_height
= source_rect
.height();
276 int image_rect_x
= image_rect
.x();
277 int image_rect_y
= image_rect
.y();
278 int image_rect_width
= image_rect
.width();
279 int image_rect_height
= image_rect
.height();
280 int dest_offset_x
= dest_offset
.x();
281 int dest_offset_y
= dest_offset
.y();
282 base::debug::Alias(&image
);
283 base::debug::Alias(&source_rect_x
);
284 base::debug::Alias(&source_rect_y
);
285 base::debug::Alias(&source_rect_width
);
286 base::debug::Alias(&source_rect_height
);
287 base::debug::Alias(&image_rect_x
);
288 base::debug::Alias(&image_rect_y
);
289 base::debug::Alias(&image_rect_width
);
290 base::debug::Alias(&image_rect_height
);
291 base::debug::Alias(&dest_offset_x
);
292 base::debug::Alias(&dest_offset_y
);
294 TRACE_EVENT0("cc", "TextureUploader::uploadWithMapTexSubImage");
296 // Offset from image-rect to source-rect.
297 gfx::Vector2d
offset(source_rect
.origin() - image_rect
.origin());
299 // Upload tile data via a mapped transfer buffer
300 uint8
* pixel_dest
= static_cast<uint8
*>(
301 m_context
->mapTexSubImage2DCHROMIUM(GL_TEXTURE_2D
,
306 source_rect
.height(),
312 uploadWithTexSubImage(
313 image
, image_rect
, source_rect
, dest_offset
, format
);
317 unsigned int bytes_per_pixel
= Texture::bytesPerPixel(format
);
319 if (image_rect
.width() == source_rect
.width() && !offset
.x()) {
321 &image
[offset
.y() * image_rect
.width() * bytes_per_pixel
],
322 image_rect
.width() * source_rect
.height() * bytes_per_pixel
);
324 // Strides not equal, so do a row-by-row memcpy from the
325 // paint results into the pixelDest
326 for (int row
= 0; row
< source_rect
.height(); ++row
)
327 memcpy(&pixel_dest
[source_rect
.width() * row
* bytes_per_pixel
],
328 &image
[bytes_per_pixel
* (offset
.x() +
329 (offset
.y() + row
) * image_rect
.width())],
330 source_rect
.width() * bytes_per_pixel
);
333 m_context
->unmapTexSubImage2DCHROMIUM(pixel_dest
);
336 void TextureUploader::processQueries()
338 while (!m_pendingQueries
.isEmpty()) {
339 if (m_pendingQueries
.first()->isPending())
342 unsigned usElapsed
= m_pendingQueries
.first()->value();
343 HISTOGRAM_CUSTOM_COUNTS("Renderer4.TextureGpuUploadTimeUS", usElapsed
, 0, 100000, 50);
345 if (!m_pendingQueries
.first()->isNonBlocking())
346 m_numBlockingTextureUploads
--;
348 // Remove the min and max value from our history and insert the new one.
349 double texturesPerSecond
= 1.0 / (usElapsed
* 1e-6);
350 if (m_texturesPerSecondHistory
.size() >= uploadHistorySizeMax
) {
351 m_texturesPerSecondHistory
.erase(m_texturesPerSecondHistory
.begin());
352 m_texturesPerSecondHistory
.erase(--m_texturesPerSecondHistory
.end());
354 m_texturesPerSecondHistory
.insert(texturesPerSecond
);
356 m_availableQueries
.append(m_pendingQueries
.takeFirst());