Add entry to histograms.xml for OverlyLargeOriginLength.
[chromium-blink-merge.git] / cc / scheduler / texture_uploader.cc
blob54d351527d690821ea0a7c33c1ca93707a64a937
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 "cc/scheduler/texture_uploader.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/debug/trace_event.h"
11 #include "base/metrics/histogram.h"
12 #include "cc/base/util.h"
13 #include "cc/resources/prioritized_resource.h"
14 #include "cc/resources/resource.h"
15 #include "gpu/GLES2/gl2extchromium.h"
16 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.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"
22 namespace {
24 // How many previous uploads to use when predicting future throughput.
25 static const size_t kUploadHistorySizeMax = 1000;
26 static const size_t kUploadHistorySizeInitial = 100;
28 // Global estimated number of textures per second to maintain estimates across
29 // subsequent instances of TextureUploader.
30 // More than one thread will not access this variable, so we do not need to
31 // synchronize access.
32 static const double kDefaultEstimatedTexturesPerSecond = 48.0 * 60.0;
34 // Flush interval when performing texture uploads.
35 static const size_t kTextureUploadFlushPeriod = 4;
37 } // anonymous namespace
39 namespace cc {
41 TextureUploader::Query::Query(blink::WebGraphicsContext3D* context)
42 : context_(context),
43 query_id_(0),
44 value_(0),
45 has_value_(false),
46 is_non_blocking_(false) {
47 query_id_ = context_->createQueryEXT();
50 TextureUploader::Query::~Query() { context_->deleteQueryEXT(query_id_); }
52 void TextureUploader::Query::Begin() {
53 has_value_ = false;
54 is_non_blocking_ = false;
55 context_->beginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, query_id_);
58 void TextureUploader::Query::End() {
59 context_->endQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
62 bool TextureUploader::Query::IsPending() {
63 unsigned available = 1;
64 context_->getQueryObjectuivEXT(
65 query_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
66 return !available;
69 unsigned TextureUploader::Query::Value() {
70 if (!has_value_) {
71 context_->getQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &value_);
72 has_value_ = true;
74 return value_;
77 TextureUploader::TextureUploader(blink::WebGraphicsContext3D* context,
78 bool use_map_tex_sub_image,
79 bool use_shallow_flush)
80 : context_(context),
81 num_blocking_texture_uploads_(0),
82 use_map_tex_sub_image_(use_map_tex_sub_image),
83 sub_image_size_(0),
84 use_shallow_flush_(use_shallow_flush),
85 num_texture_uploads_since_last_flush_(0) {
86 for (size_t i = kUploadHistorySizeInitial; i > 0; i--)
87 textures_per_second_history_.insert(kDefaultEstimatedTexturesPerSecond);
90 TextureUploader::~TextureUploader() {}
92 size_t TextureUploader::NumBlockingUploads() {
93 ProcessQueries();
94 return num_blocking_texture_uploads_;
97 void TextureUploader::MarkPendingUploadsAsNonBlocking() {
98 for (ScopedPtrDeque<Query>::iterator it = pending_queries_.begin();
99 it != pending_queries_.end();
100 ++it) {
101 if ((*it)->is_non_blocking())
102 continue;
104 num_blocking_texture_uploads_--;
105 (*it)->mark_as_non_blocking();
108 DCHECK(!num_blocking_texture_uploads_);
111 double TextureUploader::EstimatedTexturesPerSecond() {
112 ProcessQueries();
114 // Use the median as our estimate.
115 std::multiset<double>::iterator median = textures_per_second_history_.begin();
116 std::advance(median, textures_per_second_history_.size() / 2);
117 return *median;
120 void TextureUploader::BeginQuery() {
121 if (available_queries_.empty())
122 available_queries_.push_back(Query::Create(context_));
124 available_queries_.front()->Begin();
127 void TextureUploader::EndQuery() {
128 available_queries_.front()->End();
129 pending_queries_.push_back(available_queries_.take_front());
130 num_blocking_texture_uploads_++;
133 void TextureUploader::Upload(const uint8* image,
134 gfx::Rect image_rect,
135 gfx::Rect source_rect,
136 gfx::Vector2d dest_offset,
137 ResourceFormat format,
138 gfx::Size size) {
139 CHECK(image_rect.Contains(source_rect));
141 bool is_full_upload = dest_offset.IsZero() && source_rect.size() == size;
143 if (is_full_upload)
144 BeginQuery();
146 if (format == ETC1) {
147 // ETC1 does not support subimage uploads.
148 DCHECK(is_full_upload);
149 UploadWithTexImageETC1(image, size);
150 } else {
151 if (use_map_tex_sub_image_) {
152 UploadWithMapTexSubImage(
153 image, image_rect, source_rect, dest_offset, format);
154 } else {
155 UploadWithTexSubImage(
156 image, image_rect, source_rect, dest_offset, format);
160 if (is_full_upload)
161 EndQuery();
163 num_texture_uploads_since_last_flush_++;
164 if (num_texture_uploads_since_last_flush_ >= kTextureUploadFlushPeriod)
165 Flush();
168 void TextureUploader::Flush() {
169 if (!num_texture_uploads_since_last_flush_)
170 return;
172 if (use_shallow_flush_)
173 context_->shallowFlushCHROMIUM();
175 num_texture_uploads_since_last_flush_ = 0;
178 void TextureUploader::ReleaseCachedQueries() {
179 ProcessQueries();
180 available_queries_.clear();
183 void TextureUploader::UploadWithTexSubImage(const uint8* image,
184 gfx::Rect image_rect,
185 gfx::Rect source_rect,
186 gfx::Vector2d dest_offset,
187 ResourceFormat format) {
188 TRACE_EVENT0("cc", "TextureUploader::UploadWithTexSubImage");
190 // Early-out if this is a no-op, and assert that |image| be valid if this is
191 // not a no-op.
192 if (source_rect.IsEmpty())
193 return;
194 DCHECK(image);
196 // Offset from image-rect to source-rect.
197 gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
199 const uint8* pixel_source;
200 unsigned bytes_per_pixel = BitsPerPixel(format) / 8;
201 // Use 4-byte row alignment (OpenGL default) for upload performance.
202 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
203 unsigned upload_image_stride =
204 RoundUp(bytes_per_pixel * source_rect.width(), 4u);
206 if (upload_image_stride == image_rect.width() * bytes_per_pixel &&
207 !offset.x()) {
208 pixel_source = &image[image_rect.width() * bytes_per_pixel * offset.y()];
209 } else {
210 size_t needed_size = upload_image_stride * source_rect.height();
211 if (sub_image_size_ < needed_size) {
212 sub_image_.reset(new uint8[needed_size]);
213 sub_image_size_ = needed_size;
215 // Strides not equal, so do a row-by-row memcpy from the
216 // paint results into a temp buffer for uploading.
217 for (int row = 0; row < source_rect.height(); ++row)
218 memcpy(&sub_image_[upload_image_stride * row],
219 &image[bytes_per_pixel *
220 (offset.x() + (offset.y() + row) * image_rect.width())],
221 source_rect.width() * bytes_per_pixel);
223 pixel_source = &sub_image_[0];
226 context_->texSubImage2D(GL_TEXTURE_2D,
228 dest_offset.x(),
229 dest_offset.y(),
230 source_rect.width(),
231 source_rect.height(),
232 GLDataFormat(format),
233 GLDataType(format),
234 pixel_source);
237 void TextureUploader::UploadWithMapTexSubImage(const uint8* image,
238 gfx::Rect image_rect,
239 gfx::Rect source_rect,
240 gfx::Vector2d dest_offset,
241 ResourceFormat format) {
242 TRACE_EVENT0("cc", "TextureUploader::UploadWithMapTexSubImage");
244 // Early-out if this is a no-op, and assert that |image| be valid if this is
245 // not a no-op.
246 if (source_rect.IsEmpty())
247 return;
248 DCHECK(image);
249 // Compressed textures have no implementation of mapTexSubImage.
250 DCHECK_NE(ETC1, format);
252 // Offset from image-rect to source-rect.
253 gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
255 unsigned bytes_per_pixel = BitsPerPixel(format) / 8;
256 // Use 4-byte row alignment (OpenGL default) for upload performance.
257 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
258 unsigned upload_image_stride =
259 RoundUp(bytes_per_pixel * source_rect.width(), 4u);
261 // Upload tile data via a mapped transfer buffer
262 uint8* pixel_dest = static_cast<uint8*>(
263 context_->mapTexSubImage2DCHROMIUM(GL_TEXTURE_2D,
265 dest_offset.x(),
266 dest_offset.y(),
267 source_rect.width(),
268 source_rect.height(),
269 GLDataFormat(format),
270 GLDataType(format),
271 GL_WRITE_ONLY));
273 if (!pixel_dest) {
274 UploadWithTexSubImage(image, image_rect, source_rect, dest_offset, format);
275 return;
278 if (upload_image_stride == image_rect.width() * bytes_per_pixel &&
279 !offset.x()) {
280 memcpy(pixel_dest,
281 &image[image_rect.width() * bytes_per_pixel * offset.y()],
282 source_rect.height() * image_rect.width() * bytes_per_pixel);
283 } else {
284 // Strides not equal, so do a row-by-row memcpy from the
285 // paint results into the pixel_dest.
286 for (int row = 0; row < source_rect.height(); ++row) {
287 memcpy(&pixel_dest[upload_image_stride * row],
288 &image[bytes_per_pixel *
289 (offset.x() + (offset.y() + row) * image_rect.width())],
290 source_rect.width() * bytes_per_pixel);
294 context_->unmapTexSubImage2DCHROMIUM(pixel_dest);
297 void TextureUploader::UploadWithTexImageETC1(const uint8* image,
298 gfx::Size size) {
299 TRACE_EVENT0("cc", "TextureUploader::UploadWithTexImageETC1");
300 DCHECK_EQ(0, size.width() % 4);
301 DCHECK_EQ(0, size.height() % 4);
303 context_->compressedTexImage2D(GL_TEXTURE_2D,
305 GLInternalFormat(ETC1),
306 size.width(),
307 size.height(),
309 Resource::MemorySizeBytes(size, ETC1),
310 image);
313 void TextureUploader::ProcessQueries() {
314 while (!pending_queries_.empty()) {
315 if (pending_queries_.front()->IsPending())
316 break;
318 unsigned us_elapsed = pending_queries_.front()->Value();
319 UMA_HISTOGRAM_CUSTOM_COUNTS(
320 "Renderer4.TextureGpuUploadTimeUS", us_elapsed, 0, 100000, 50);
322 // Clamp the queries to saner values in case the queries fail.
323 us_elapsed = std::max(1u, us_elapsed);
324 us_elapsed = std::min(15000u, us_elapsed);
326 if (!pending_queries_.front()->is_non_blocking())
327 num_blocking_texture_uploads_--;
329 // Remove the min and max value from our history and insert the new one.
330 double textures_per_second = 1.0 / (us_elapsed * 1e-6);
331 if (textures_per_second_history_.size() >= kUploadHistorySizeMax) {
332 textures_per_second_history_.erase(textures_per_second_history_.begin());
333 textures_per_second_history_.erase(--textures_per_second_history_.end());
335 textures_per_second_history_.insert(textures_per_second);
337 available_queries_.push_back(pending_queries_.take_front());
341 } // namespace cc