Home | History | Annotate | Download | only in scheduler
      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/alias.h"
     11 #include "base/debug/trace_event.h"
     12 #include "base/metrics/histogram.h"
     13 #include "cc/base/util.h"
     14 #include "cc/resources/prioritized_resource.h"
     15 #include "cc/resources/resource.h"
     16 #include "gpu/GLES2/gl2extchromium.h"
     17 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
     18 #include "third_party/khronos/GLES2/gl2.h"
     19 #include "third_party/khronos/GLES2/gl2ext.h"
     20 #include "ui/gfx/rect.h"
     21 #include "ui/gfx/vector2d.h"
     22 
     23 namespace {
     24 
     25 // How many previous uploads to use when predicting future throughput.
     26 static const size_t kUploadHistorySizeMax = 1000;
     27 static const size_t kUploadHistorySizeInitial = 100;
     28 
     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
     32 // synchronize access.
     33 static const double kDefaultEstimatedTexturesPerSecond = 48.0 * 60.0;
     34 
     35 // Flush interval when performing texture uploads.
     36 static const size_t kTextureUploadFlushPeriod = 4;
     37 
     38 }  // anonymous namespace
     39 
     40 namespace cc {
     41 
     42 TextureUploader::Query::Query(WebKit::WebGraphicsContext3D* context)
     43     : context_(context),
     44       query_id_(0),
     45       value_(0),
     46       has_value_(false),
     47       is_non_blocking_(false) {
     48   query_id_ = context_->createQueryEXT();
     49 }
     50 
     51 TextureUploader::Query::~Query() { context_->deleteQueryEXT(query_id_); }
     52 
     53 void TextureUploader::Query::Begin() {
     54   has_value_ = false;
     55   is_non_blocking_ = false;
     56   context_->beginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, query_id_);
     57 }
     58 
     59 void TextureUploader::Query::End() {
     60   context_->endQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
     61 }
     62 
     63 bool TextureUploader::Query::IsPending() {
     64   unsigned available = 1;
     65   context_->getQueryObjectuivEXT(
     66       query_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
     67   return !available;
     68 }
     69 
     70 unsigned TextureUploader::Query::Value() {
     71   if (!has_value_) {
     72     context_->getQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &value_);
     73     has_value_ = true;
     74   }
     75   return value_;
     76 }
     77 
     78 TextureUploader::TextureUploader(WebKit::WebGraphicsContext3D* context,
     79                                  bool use_map_tex_sub_image,
     80                                  bool use_shallow_flush)
     81     : context_(context),
     82       num_blocking_texture_uploads_(0),
     83       use_map_tex_sub_image_(use_map_tex_sub_image),
     84       sub_image_size_(0),
     85       use_shallow_flush_(use_shallow_flush),
     86       num_texture_uploads_since_last_flush_(0) {
     87   for (size_t i = kUploadHistorySizeInitial; i > 0; i--)
     88     textures_per_second_history_.insert(kDefaultEstimatedTexturesPerSecond);
     89 }
     90 
     91 TextureUploader::~TextureUploader() {}
     92 
     93 size_t TextureUploader::NumBlockingUploads() {
     94   ProcessQueries();
     95   return num_blocking_texture_uploads_;
     96 }
     97 
     98 void TextureUploader::MarkPendingUploadsAsNonBlocking() {
     99   for (ScopedPtrDeque<Query>::iterator it = pending_queries_.begin();
    100        it != pending_queries_.end();
    101        ++it) {
    102     if ((*it)->is_non_blocking())
    103       continue;
    104 
    105     num_blocking_texture_uploads_--;
    106     (*it)->mark_as_non_blocking();
    107   }
    108 
    109   DCHECK(!num_blocking_texture_uploads_);
    110 }
    111 
    112 double TextureUploader::EstimatedTexturesPerSecond() {
    113   ProcessQueries();
    114 
    115   // Use the median as our estimate.
    116   std::multiset<double>::iterator median = textures_per_second_history_.begin();
    117   std::advance(median, textures_per_second_history_.size() / 2);
    118   return *median;
    119 }
    120 
    121 void TextureUploader::BeginQuery() {
    122   if (available_queries_.empty())
    123     available_queries_.push_back(Query::Create(context_));
    124 
    125   available_queries_.front()->Begin();
    126 }
    127 
    128 void TextureUploader::EndQuery() {
    129   available_queries_.front()->End();
    130   pending_queries_.push_back(available_queries_.take_front());
    131   num_blocking_texture_uploads_++;
    132 }
    133 
    134 void TextureUploader::Upload(const uint8* image,
    135                              gfx::Rect image_rect,
    136                              gfx::Rect source_rect,
    137                              gfx::Vector2d dest_offset,
    138                              GLenum format,
    139                              gfx::Size size) {
    140   CHECK(image_rect.Contains(source_rect));
    141 
    142   bool is_full_upload = dest_offset.IsZero() && source_rect.size() == size;
    143 
    144   if (is_full_upload)
    145     BeginQuery();
    146 
    147   if (use_map_tex_sub_image_) {
    148     UploadWithMapTexSubImage(
    149         image, image_rect, source_rect, dest_offset, format);
    150   } else {
    151     UploadWithTexSubImage(image, image_rect, source_rect, dest_offset, format);
    152   }
    153 
    154   if (is_full_upload)
    155     EndQuery();
    156 
    157   num_texture_uploads_since_last_flush_++;
    158   if (num_texture_uploads_since_last_flush_ >= kTextureUploadFlushPeriod)
    159     Flush();
    160 }
    161 
    162 void TextureUploader::Flush() {
    163   if (!num_texture_uploads_since_last_flush_)
    164     return;
    165 
    166   if (use_shallow_flush_)
    167     context_->shallowFlushCHROMIUM();
    168 
    169   num_texture_uploads_since_last_flush_ = 0;
    170 }
    171 
    172 void TextureUploader::ReleaseCachedQueries() {
    173   ProcessQueries();
    174   available_queries_.clear();
    175 }
    176 
    177 void TextureUploader::UploadWithTexSubImage(const uint8* image,
    178                                             gfx::Rect image_rect,
    179                                             gfx::Rect source_rect,
    180                                             gfx::Vector2d dest_offset,
    181                                             GLenum format) {
    182   // Instrumentation to debug issue 156107
    183   int source_rect_x = source_rect.x();
    184   int source_rect_y = source_rect.y();
    185   int source_rect_width = source_rect.width();
    186   int source_rect_height = source_rect.height();
    187   int image_rect_x = image_rect.x();
    188   int image_rect_y = image_rect.y();
    189   int image_rect_width = image_rect.width();
    190   int image_rect_height = image_rect.height();
    191   int dest_offset_x = dest_offset.x();
    192   int dest_offset_y = dest_offset.y();
    193   base::debug::Alias(&image);
    194   base::debug::Alias(&source_rect_x);
    195   base::debug::Alias(&source_rect_y);
    196   base::debug::Alias(&source_rect_width);
    197   base::debug::Alias(&source_rect_height);
    198   base::debug::Alias(&image_rect_x);
    199   base::debug::Alias(&image_rect_y);
    200   base::debug::Alias(&image_rect_width);
    201   base::debug::Alias(&image_rect_height);
    202   base::debug::Alias(&dest_offset_x);
    203   base::debug::Alias(&dest_offset_y);
    204   TRACE_EVENT0("cc", "TextureUploader::UploadWithTexSubImage");
    205 
    206   // Offset from image-rect to source-rect.
    207   gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
    208 
    209   const uint8* pixel_source;
    210   unsigned int bytes_per_pixel = Resource::BytesPerPixel(format);
    211   // Use 4-byte row alignment (OpenGL default) for upload performance.
    212   // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
    213   unsigned int upload_image_stride =
    214       RoundUp(bytes_per_pixel * source_rect.width(), 4u);
    215 
    216   if (upload_image_stride == image_rect.width() * bytes_per_pixel &&
    217       !offset.x()) {
    218     pixel_source = &image[image_rect.width() * bytes_per_pixel * offset.y()];
    219   } else {
    220     size_t needed_size = upload_image_stride * source_rect.height();
    221     if (sub_image_size_ < needed_size) {
    222       sub_image_.reset(new uint8[needed_size]);
    223       sub_image_size_ = needed_size;
    224     }
    225     // Strides not equal, so do a row-by-row memcpy from the
    226     // paint results into a temp buffer for uploading.
    227     for (int row = 0; row < source_rect.height(); ++row)
    228       memcpy(&sub_image_[upload_image_stride * row],
    229              &image[bytes_per_pixel *
    230                  (offset.x() + (offset.y() + row) * image_rect.width())],
    231              source_rect.width() * bytes_per_pixel);
    232 
    233     pixel_source = &sub_image_[0];
    234   }
    235 
    236   context_->texSubImage2D(GL_TEXTURE_2D,
    237                           0,
    238                           dest_offset.x(),
    239                           dest_offset.y(),
    240                           source_rect.width(),
    241                           source_rect.height(),
    242                           format,
    243                           GL_UNSIGNED_BYTE,
    244                           pixel_source);
    245 }
    246 
    247 void TextureUploader::UploadWithMapTexSubImage(const uint8* image,
    248                                                gfx::Rect image_rect,
    249                                                gfx::Rect source_rect,
    250                                                gfx::Vector2d dest_offset,
    251                                                GLenum format) {
    252   // Instrumentation to debug issue 156107
    253   int source_rect_x = source_rect.x();
    254   int source_rect_y = source_rect.y();
    255   int source_rect_width = source_rect.width();
    256   int source_rect_height = source_rect.height();
    257   int image_rect_x = image_rect.x();
    258   int image_rect_y = image_rect.y();
    259   int image_rect_width = image_rect.width();
    260   int image_rect_height = image_rect.height();
    261   int dest_offset_x = dest_offset.x();
    262   int dest_offset_y = dest_offset.y();
    263   base::debug::Alias(&image);
    264   base::debug::Alias(&source_rect_x);
    265   base::debug::Alias(&source_rect_y);
    266   base::debug::Alias(&source_rect_width);
    267   base::debug::Alias(&source_rect_height);
    268   base::debug::Alias(&image_rect_x);
    269   base::debug::Alias(&image_rect_y);
    270   base::debug::Alias(&image_rect_width);
    271   base::debug::Alias(&image_rect_height);
    272   base::debug::Alias(&dest_offset_x);
    273   base::debug::Alias(&dest_offset_y);
    274 
    275   TRACE_EVENT0("cc", "TextureUploader::UploadWithMapTexSubImage");
    276 
    277   // Offset from image-rect to source-rect.
    278   gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
    279 
    280   unsigned int bytes_per_pixel = Resource::BytesPerPixel(format);
    281   // Use 4-byte row alignment (OpenGL default) for upload performance.
    282   // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
    283   unsigned int upload_image_stride =
    284       RoundUp(bytes_per_pixel * source_rect.width(), 4u);
    285 
    286   // Upload tile data via a mapped transfer buffer
    287   uint8* pixel_dest = static_cast<uint8*>(
    288       context_->mapTexSubImage2DCHROMIUM(GL_TEXTURE_2D,
    289                                          0,
    290                                          dest_offset.x(),
    291                                          dest_offset.y(),
    292                                          source_rect.width(),
    293                                          source_rect.height(),
    294                                          format,
    295                                          GL_UNSIGNED_BYTE,
    296                                          GL_WRITE_ONLY));
    297 
    298   if (!pixel_dest) {
    299     UploadWithTexSubImage(image, image_rect, source_rect, dest_offset, format);
    300     return;
    301   }
    302 
    303   if (upload_image_stride == image_rect.width() * bytes_per_pixel &&
    304       !offset.x()) {
    305     memcpy(pixel_dest,
    306            &image[image_rect.width() * bytes_per_pixel * offset.y()],
    307            source_rect.height() * image_rect.width() * bytes_per_pixel);
    308   } else {
    309     // Strides not equal, so do a row-by-row memcpy from the
    310     // paint results into the pixel_dest.
    311     for (int row = 0; row < source_rect.height(); ++row) {
    312       memcpy(&pixel_dest[upload_image_stride * row],
    313              &image[bytes_per_pixel *
    314                  (offset.x() + (offset.y() + row) * image_rect.width())],
    315              source_rect.width() * bytes_per_pixel);
    316     }
    317   }
    318 
    319   context_->unmapTexSubImage2DCHROMIUM(pixel_dest);
    320 }
    321 
    322 void TextureUploader::ProcessQueries() {
    323   while (!pending_queries_.empty()) {
    324     if (pending_queries_.front()->IsPending())
    325       break;
    326 
    327     unsigned us_elapsed = pending_queries_.front()->Value();
    328     UMA_HISTOGRAM_CUSTOM_COUNTS(
    329         "Renderer4.TextureGpuUploadTimeUS", us_elapsed, 0, 100000, 50);
    330 
    331     // Clamp the queries to saner values in case the queries fail.
    332     us_elapsed = std::max(1u, us_elapsed);
    333     us_elapsed = std::min(15000u, us_elapsed);
    334 
    335     if (!pending_queries_.front()->is_non_blocking())
    336       num_blocking_texture_uploads_--;
    337 
    338     // Remove the min and max value from our history and insert the new one.
    339     double textures_per_second = 1.0 / (us_elapsed * 1e-6);
    340     if (textures_per_second_history_.size() >= kUploadHistorySizeMax) {
    341       textures_per_second_history_.erase(textures_per_second_history_.begin());
    342       textures_per_second_history_.erase(--textures_per_second_history_.end());
    343     }
    344     textures_per_second_history_.insert(textures_per_second);
    345 
    346     available_queries_.push_back(pending_queries_.take_front());
    347   }
    348 }
    349 
    350 }  // namespace cc
    351