- add sources.
[platform/framework/web/crosswalk.git] / src / cc / scheduler / texture_uploader.cc
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/scheduler/texture_uploader.h"
6
7 #include <algorithm>
8 #include <vector>
9
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"
21
22 namespace {
23
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;
27
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;
33
34 // Flush interval when performing texture uploads.
35 static const size_t kTextureUploadFlushPeriod = 4;
36
37 }  // anonymous namespace
38
39 namespace cc {
40
41 TextureUploader::Query::Query(WebKit::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();
48 }
49
50 TextureUploader::Query::~Query() { context_->deleteQueryEXT(query_id_); }
51
52 void TextureUploader::Query::Begin() {
53   has_value_ = false;
54   is_non_blocking_ = false;
55   context_->beginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, query_id_);
56 }
57
58 void TextureUploader::Query::End() {
59   context_->endQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
60 }
61
62 bool TextureUploader::Query::IsPending() {
63   unsigned available = 1;
64   context_->getQueryObjectuivEXT(
65       query_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
66   return !available;
67 }
68
69 unsigned TextureUploader::Query::Value() {
70   if (!has_value_) {
71     context_->getQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &value_);
72     has_value_ = true;
73   }
74   return value_;
75 }
76
77 TextureUploader::TextureUploader(WebKit::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);
88 }
89
90 TextureUploader::~TextureUploader() {}
91
92 size_t TextureUploader::NumBlockingUploads() {
93   ProcessQueries();
94   return num_blocking_texture_uploads_;
95 }
96
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;
103
104     num_blocking_texture_uploads_--;
105     (*it)->mark_as_non_blocking();
106   }
107
108   DCHECK(!num_blocking_texture_uploads_);
109 }
110
111 double TextureUploader::EstimatedTexturesPerSecond() {
112   ProcessQueries();
113
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;
118 }
119
120 void TextureUploader::BeginQuery() {
121   if (available_queries_.empty())
122     available_queries_.push_back(Query::Create(context_));
123
124   available_queries_.front()->Begin();
125 }
126
127 void TextureUploader::EndQuery() {
128   available_queries_.front()->End();
129   pending_queries_.push_back(available_queries_.take_front());
130   num_blocking_texture_uploads_++;
131 }
132
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));
140
141   bool is_full_upload = dest_offset.IsZero() && source_rect.size() == size;
142
143   if (is_full_upload)
144     BeginQuery();
145
146   if (format == ETC1) {
147     // ETC1 does not support subimage uploads.
148     DCHECK(is_full_upload);
149     UploadWithTexImageETC1(image, size);
150     return;
151   }
152
153   if (use_map_tex_sub_image_) {
154     UploadWithMapTexSubImage(
155         image, image_rect, source_rect, dest_offset, format);
156   } else {
157     UploadWithTexSubImage(image, image_rect, source_rect, dest_offset, format);
158   }
159
160   if (is_full_upload)
161     EndQuery();
162
163   num_texture_uploads_since_last_flush_++;
164   if (num_texture_uploads_since_last_flush_ >= kTextureUploadFlushPeriod)
165     Flush();
166 }
167
168 void TextureUploader::Flush() {
169   if (!num_texture_uploads_since_last_flush_)
170     return;
171
172   if (use_shallow_flush_)
173     context_->shallowFlushCHROMIUM();
174
175   num_texture_uploads_since_last_flush_ = 0;
176 }
177
178 void TextureUploader::ReleaseCachedQueries() {
179   ProcessQueries();
180   available_queries_.clear();
181 }
182
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");
189
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);
195
196   // Offset from image-rect to source-rect.
197   gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
198
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);
205
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;
214     }
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);
222
223     pixel_source = &sub_image_[0];
224   }
225
226   context_->texSubImage2D(GL_TEXTURE_2D,
227                           0,
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);
235 }
236
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");
243
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);
251
252   // Offset from image-rect to source-rect.
253   gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
254
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);
260
261   // Upload tile data via a mapped transfer buffer
262   uint8* pixel_dest = static_cast<uint8*>(
263       context_->mapTexSubImage2DCHROMIUM(GL_TEXTURE_2D,
264                                          0,
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));
272
273   if (!pixel_dest) {
274     UploadWithTexSubImage(image, image_rect, source_rect, dest_offset, format);
275     return;
276   }
277
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);
291     }
292   }
293
294   context_->unmapTexSubImage2DCHROMIUM(pixel_dest);
295 }
296
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);
302
303   context_->compressedTexImage2D(GL_TEXTURE_2D,
304                                  0,
305                                  GLInternalFormat(ETC1),
306                                  size.width(),
307                                  size.height(),
308                                  0,
309                                  Resource::MemorySizeBytes(size, ETC1),
310                                  image);
311 }
312
313 void TextureUploader::ProcessQueries() {
314   while (!pending_queries_.empty()) {
315     if (pending_queries_.front()->IsPending())
316       break;
317
318     unsigned us_elapsed = pending_queries_.front()->Value();
319     UMA_HISTOGRAM_CUSTOM_COUNTS(
320         "Renderer4.TextureGpuUploadTimeUS", us_elapsed, 0, 100000, 50);
321
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);
325
326     if (!pending_queries_.front()->is_non_blocking())
327       num_blocking_texture_uploads_--;
328
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());
334     }
335     textures_per_second_history_.insert(textures_per_second);
336
337     available_queries_.push_back(pending_queries_.take_front());
338   }
339 }
340
341 }  // namespace cc