Add Sad Tab resources to the iOS build.
[chromium-blink-merge.git] / cc / texture_uploader.cc
blob078c08e61db7a48ac4a45f5a4b1253aee3348f06
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.
5 #include "config.h"
7 #include "cc/texture_uploader.h"
9 #include <algorithm>
10 #include <vector>
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>
23 namespace {
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
39 namespace cc {
41 TextureUploader::Query::Query(WebKit::WebGraphicsContext3D* context)
42 : m_context(context)
43 , m_queryId(0)
44 , m_value(0)
45 , m_hasValue(false)
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()
58 m_hasValue = false;
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);
72 return !available;
75 unsigned TextureUploader::Query::value()
77 if (!m_hasValue) {
78 m_context->getQueryObjectuivEXT(m_queryId, GL_QUERY_RESULT_EXT, &m_value);
79 m_hasValue = true;
81 return 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,
97 bool useShallowFlush)
98 : m_context(context)
99 , m_numBlockingTextureUploads(0)
100 , m_useMapTexSubImage(useMapTexSubImage)
101 , m_subImageSize(0)
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()
115 processQueries();
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())
124 continue;
126 m_numBlockingTextureUploads--;
127 (*it)->markAsNonBlocking();
130 DCHECK(!m_numBlockingTextureUploads);
133 double TextureUploader::estimatedTexturesPerSecond()
135 processQueries();
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);
141 return *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,
163 GLenum format,
164 const gfx::Size& size)
166 CHECK(image_rect.Contains(source_rect));
168 bool isFullUpload = dest_offset.IsZero() && source_rect.size() == size;
170 if (isFullUpload)
171 beginQuery();
173 if (m_useMapTexSubImage) {
174 uploadWithMapTexSubImage(
175 image, image_rect, source_rect, dest_offset, format);
176 } else {
177 uploadWithTexSubImage(
178 image, image_rect, source_rect, dest_offset, format);
181 if (isFullUpload)
182 endQuery();
184 m_numTextureUploadsSinceLastFlush++;
185 if (m_numTextureUploadsSinceLastFlush >= textureUploadFlushPeriod)
186 flush();
189 void TextureUploader::flush() {
190 if (!m_numTextureUploadsSinceLastFlush)
191 return;
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,
203 GLenum format)
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()];
237 } else {
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,
256 dest_offset.x(),
257 dest_offset.y(),
258 source_rect.width(),
259 source_rect.height(),
260 format,
261 GL_UNSIGNED_BYTE,
262 pixel_source);
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,
269 GLenum format)
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,
303 dest_offset.x(),
304 dest_offset.y(),
305 source_rect.width(),
306 source_rect.height(),
307 format,
308 GL_UNSIGNED_BYTE,
309 GL_WRITE_ONLY));
311 if (!pixel_dest) {
312 uploadWithTexSubImage(
313 image, image_rect, source_rect, dest_offset, format);
314 return;
317 unsigned int bytes_per_pixel = Texture::bytesPerPixel(format);
319 if (image_rect.width() == source_rect.width() && !offset.x()) {
320 memcpy(pixel_dest,
321 &image[offset.y() * image_rect.width() * bytes_per_pixel],
322 image_rect.width() * source_rect.height() * bytes_per_pixel);
323 } else {
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())
340 break;
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());
360 } // namespace cc