| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "cc/scheduler/texture_uploader.h" | 5 #include "cc/scheduler/texture_uploader.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
| 11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
| 12 #include "cc/base/util.h" | 12 #include "cc/base/util.h" |
| 13 #include "cc/resources/prioritized_resource.h" | 13 #include "cc/resources/prioritized_resource.h" |
| 14 #include "cc/resources/resource.h" | 14 #include "cc/resources/resource.h" |
| 15 #include "gpu/GLES2/gl2extchromium.h" | 15 #include "gpu/GLES2/gl2extchromium.h" |
| 16 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" | 16 #include "gpu/command_buffer/client/gles2_interface.h" |
| 17 #include "third_party/khronos/GLES2/gl2.h" | 17 #include "third_party/khronos/GLES2/gl2.h" |
| 18 #include "third_party/khronos/GLES2/gl2ext.h" | 18 #include "third_party/khronos/GLES2/gl2ext.h" |
| 19 #include "ui/gfx/rect.h" | 19 #include "ui/gfx/rect.h" |
| 20 #include "ui/gfx/vector2d.h" | 20 #include "ui/gfx/vector2d.h" |
| 21 | 21 |
| 22 using gpu::gles2::GLES2Interface; |
| 23 |
| 22 namespace { | 24 namespace { |
| 23 | 25 |
| 24 // How many previous uploads to use when predicting future throughput. | 26 // How many previous uploads to use when predicting future throughput. |
| 25 static const size_t kUploadHistorySizeMax = 1000; | 27 static const size_t kUploadHistorySizeMax = 1000; |
| 26 static const size_t kUploadHistorySizeInitial = 100; | 28 static const size_t kUploadHistorySizeInitial = 100; |
| 27 | 29 |
| 28 // Global estimated number of textures per second to maintain estimates across | 30 // Global estimated number of textures per second to maintain estimates across |
| 29 // subsequent instances of TextureUploader. | 31 // subsequent instances of TextureUploader. |
| 30 // More than one thread will not access this variable, so we do not need to | 32 // More than one thread will not access this variable, so we do not need to |
| 31 // synchronize access. | 33 // synchronize access. |
| 32 static const double kDefaultEstimatedTexturesPerSecond = 48.0 * 60.0; | 34 static const double kDefaultEstimatedTexturesPerSecond = 48.0 * 60.0; |
| 33 | 35 |
| 34 // Flush interval when performing texture uploads. | 36 // Flush interval when performing texture uploads. |
| 35 static const size_t kTextureUploadFlushPeriod = 4; | 37 static const size_t kTextureUploadFlushPeriod = 4; |
| 36 | 38 |
| 37 } // anonymous namespace | 39 } // anonymous namespace |
| 38 | 40 |
| 39 namespace cc { | 41 namespace cc { |
| 40 | 42 |
| 41 TextureUploader::Query::Query(blink::WebGraphicsContext3D* context) | 43 TextureUploader::Query::Query(GLES2Interface* gl) |
| 42 : context_(context), | 44 : gl_(gl), |
| 43 query_id_(0), | 45 query_id_(0), |
| 44 value_(0), | 46 value_(0), |
| 45 has_value_(false), | 47 has_value_(false), |
| 46 is_non_blocking_(false) { | 48 is_non_blocking_(false) { |
| 47 query_id_ = context_->createQueryEXT(); | 49 gl_->GenQueriesEXT(1, &query_id_); |
| 48 } | 50 } |
| 49 | 51 |
| 50 TextureUploader::Query::~Query() { context_->deleteQueryEXT(query_id_); } | 52 TextureUploader::Query::~Query() { gl_->DeleteQueriesEXT(1, &query_id_); } |
| 51 | 53 |
| 52 void TextureUploader::Query::Begin() { | 54 void TextureUploader::Query::Begin() { |
| 53 has_value_ = false; | 55 has_value_ = false; |
| 54 is_non_blocking_ = false; | 56 is_non_blocking_ = false; |
| 55 context_->beginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, query_id_); | 57 gl_->BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, query_id_); |
| 56 } | 58 } |
| 57 | 59 |
| 58 void TextureUploader::Query::End() { | 60 void TextureUploader::Query::End() { |
| 59 context_->endQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM); | 61 gl_->EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM); |
| 60 } | 62 } |
| 61 | 63 |
| 62 bool TextureUploader::Query::IsPending() { | 64 bool TextureUploader::Query::IsPending() { |
| 63 unsigned available = 1; | 65 unsigned available = 1; |
| 64 context_->getQueryObjectuivEXT( | 66 gl_->GetQueryObjectuivEXT( |
| 65 query_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available); | 67 query_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available); |
| 66 return !available; | 68 return !available; |
| 67 } | 69 } |
| 68 | 70 |
| 69 unsigned TextureUploader::Query::Value() { | 71 unsigned TextureUploader::Query::Value() { |
| 70 if (!has_value_) { | 72 if (!has_value_) { |
| 71 context_->getQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &value_); | 73 gl_->GetQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &value_); |
| 72 has_value_ = true; | 74 has_value_ = true; |
| 73 } | 75 } |
| 74 return value_; | 76 return value_; |
| 75 } | 77 } |
| 76 | 78 |
| 77 TextureUploader::TextureUploader(blink::WebGraphicsContext3D* context) | 79 TextureUploader::TextureUploader(GLES2Interface* gl) |
| 78 : context_(context), | 80 : gl_(gl), |
| 79 num_blocking_texture_uploads_(0), | 81 num_blocking_texture_uploads_(0), |
| 80 sub_image_size_(0), | 82 sub_image_size_(0), |
| 81 num_texture_uploads_since_last_flush_(0) { | 83 num_texture_uploads_since_last_flush_(0) { |
| 82 for (size_t i = kUploadHistorySizeInitial; i > 0; i--) | 84 for (size_t i = kUploadHistorySizeInitial; i > 0; i--) |
| 83 textures_per_second_history_.insert(kDefaultEstimatedTexturesPerSecond); | 85 textures_per_second_history_.insert(kDefaultEstimatedTexturesPerSecond); |
| 84 } | 86 } |
| 85 | 87 |
| 86 TextureUploader::~TextureUploader() {} | 88 TextureUploader::~TextureUploader() {} |
| 87 | 89 |
| 88 size_t TextureUploader::NumBlockingUploads() { | 90 size_t TextureUploader::NumBlockingUploads() { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 108 ProcessQueries(); | 110 ProcessQueries(); |
| 109 | 111 |
| 110 // Use the median as our estimate. | 112 // Use the median as our estimate. |
| 111 std::multiset<double>::iterator median = textures_per_second_history_.begin(); | 113 std::multiset<double>::iterator median = textures_per_second_history_.begin(); |
| 112 std::advance(median, textures_per_second_history_.size() / 2); | 114 std::advance(median, textures_per_second_history_.size() / 2); |
| 113 return *median; | 115 return *median; |
| 114 } | 116 } |
| 115 | 117 |
| 116 void TextureUploader::BeginQuery() { | 118 void TextureUploader::BeginQuery() { |
| 117 if (available_queries_.empty()) | 119 if (available_queries_.empty()) |
| 118 available_queries_.push_back(Query::Create(context_)); | 120 available_queries_.push_back(Query::Create(gl_)); |
| 119 | 121 |
| 120 available_queries_.front()->Begin(); | 122 available_queries_.front()->Begin(); |
| 121 } | 123 } |
| 122 | 124 |
| 123 void TextureUploader::EndQuery() { | 125 void TextureUploader::EndQuery() { |
| 124 available_queries_.front()->End(); | 126 available_queries_.front()->End(); |
| 125 pending_queries_.push_back(available_queries_.take_front()); | 127 pending_queries_.push_back(available_queries_.take_front()); |
| 126 num_blocking_texture_uploads_++; | 128 num_blocking_texture_uploads_++; |
| 127 } | 129 } |
| 128 | 130 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 153 | 155 |
| 154 num_texture_uploads_since_last_flush_++; | 156 num_texture_uploads_since_last_flush_++; |
| 155 if (num_texture_uploads_since_last_flush_ >= kTextureUploadFlushPeriod) | 157 if (num_texture_uploads_since_last_flush_ >= kTextureUploadFlushPeriod) |
| 156 Flush(); | 158 Flush(); |
| 157 } | 159 } |
| 158 | 160 |
| 159 void TextureUploader::Flush() { | 161 void TextureUploader::Flush() { |
| 160 if (!num_texture_uploads_since_last_flush_) | 162 if (!num_texture_uploads_since_last_flush_) |
| 161 return; | 163 return; |
| 162 | 164 |
| 163 context_->shallowFlushCHROMIUM(); | 165 gl_->ShallowFlushCHROMIUM(); |
| 164 | 166 |
| 165 num_texture_uploads_since_last_flush_ = 0; | 167 num_texture_uploads_since_last_flush_ = 0; |
| 166 } | 168 } |
| 167 | 169 |
| 168 void TextureUploader::ReleaseCachedQueries() { | 170 void TextureUploader::ReleaseCachedQueries() { |
| 169 ProcessQueries(); | 171 ProcessQueries(); |
| 170 available_queries_.clear(); | 172 available_queries_.clear(); |
| 171 } | 173 } |
| 172 | 174 |
| 173 void TextureUploader::UploadWithTexSubImage(const uint8* image, | 175 void TextureUploader::UploadWithTexSubImage(const uint8* image, |
| (...skipping 26 matching lines...) Expand all Loading... |
| 200 size_t needed_size = upload_image_stride * source_rect.height(); | 202 size_t needed_size = upload_image_stride * source_rect.height(); |
| 201 if (sub_image_size_ < needed_size) { | 203 if (sub_image_size_ < needed_size) { |
| 202 sub_image_.reset(new uint8[needed_size]); | 204 sub_image_.reset(new uint8[needed_size]); |
| 203 sub_image_size_ = needed_size; | 205 sub_image_size_ = needed_size; |
| 204 } | 206 } |
| 205 // Strides not equal, so do a row-by-row memcpy from the | 207 // Strides not equal, so do a row-by-row memcpy from the |
| 206 // paint results into a temp buffer for uploading. | 208 // paint results into a temp buffer for uploading. |
| 207 for (int row = 0; row < source_rect.height(); ++row) | 209 for (int row = 0; row < source_rect.height(); ++row) |
| 208 memcpy(&sub_image_[upload_image_stride * row], | 210 memcpy(&sub_image_[upload_image_stride * row], |
| 209 &image[bytes_per_pixel * | 211 &image[bytes_per_pixel * |
| 210 (offset.x() + (offset.y() + row) * image_rect.width())], | 212 (offset.x() + (offset.y() + row) * image_rect.width())], |
| 211 source_rect.width() * bytes_per_pixel); | 213 source_rect.width() * bytes_per_pixel); |
| 212 | 214 |
| 213 pixel_source = &sub_image_[0]; | 215 pixel_source = &sub_image_[0]; |
| 214 } | 216 } |
| 215 | 217 |
| 216 context_->texSubImage2D(GL_TEXTURE_2D, | 218 gl_->TexSubImage2D(GL_TEXTURE_2D, |
| 217 0, | 219 0, |
| 218 dest_offset.x(), | 220 dest_offset.x(), |
| 219 dest_offset.y(), | 221 dest_offset.y(), |
| 220 source_rect.width(), | 222 source_rect.width(), |
| 221 source_rect.height(), | 223 source_rect.height(), |
| 222 GLDataFormat(format), | 224 GLDataFormat(format), |
| 223 GLDataType(format), | 225 GLDataType(format), |
| 224 pixel_source); | 226 pixel_source); |
| 225 } | 227 } |
| 226 | 228 |
| 227 void TextureUploader::UploadWithMapTexSubImage(const uint8* image, | 229 void TextureUploader::UploadWithMapTexSubImage(const uint8* image, |
| 228 gfx::Rect image_rect, | 230 gfx::Rect image_rect, |
| 229 gfx::Rect source_rect, | 231 gfx::Rect source_rect, |
| 230 gfx::Vector2d dest_offset, | 232 gfx::Vector2d dest_offset, |
| 231 ResourceFormat format) { | 233 ResourceFormat format) { |
| 232 TRACE_EVENT0("cc", "TextureUploader::UploadWithMapTexSubImage"); | 234 TRACE_EVENT0("cc", "TextureUploader::UploadWithMapTexSubImage"); |
| 233 | 235 |
| 234 // Early-out if this is a no-op, and assert that |image| be valid if this is | 236 // Early-out if this is a no-op, and assert that |image| be valid if this is |
| 235 // not a no-op. | 237 // not a no-op. |
| 236 if (source_rect.IsEmpty()) | 238 if (source_rect.IsEmpty()) |
| 237 return; | 239 return; |
| 238 DCHECK(image); | 240 DCHECK(image); |
| 239 // Compressed textures have no implementation of mapTexSubImage. | 241 // Compressed textures have no implementation of mapTexSubImage. |
| 240 DCHECK_NE(ETC1, format); | 242 DCHECK_NE(ETC1, format); |
| 241 | 243 |
| 242 // Offset from image-rect to source-rect. | 244 // Offset from image-rect to source-rect. |
| 243 gfx::Vector2d offset(source_rect.origin() - image_rect.origin()); | 245 gfx::Vector2d offset(source_rect.origin() - image_rect.origin()); |
| 244 | 246 |
| 245 unsigned bytes_per_pixel = BitsPerPixel(format) / 8; | 247 unsigned bytes_per_pixel = BitsPerPixel(format) / 8; |
| 246 // Use 4-byte row alignment (OpenGL default) for upload performance. | 248 // Use 4-byte row alignment (OpenGL default) for upload performance. |
| 247 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. | 249 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. |
| 248 unsigned upload_image_stride = | 250 unsigned upload_image_stride = |
| 249 RoundUp(bytes_per_pixel * source_rect.width(), 4u); | 251 RoundUp(bytes_per_pixel * source_rect.width(), 4u); |
| 250 | 252 |
| 251 // Upload tile data via a mapped transfer buffer | 253 // Upload tile data via a mapped transfer buffer |
| 252 uint8* pixel_dest = static_cast<uint8*>( | 254 uint8* pixel_dest = |
| 253 context_->mapTexSubImage2DCHROMIUM(GL_TEXTURE_2D, | 255 static_cast<uint8*>(gl_->MapTexSubImage2DCHROMIUM(GL_TEXTURE_2D, |
| 254 0, | 256 0, |
| 255 dest_offset.x(), | 257 dest_offset.x(), |
| 256 dest_offset.y(), | 258 dest_offset.y(), |
| 257 source_rect.width(), | 259 source_rect.width(), |
| 258 source_rect.height(), | 260 source_rect.height(), |
| 259 GLDataFormat(format), | 261 GLDataFormat(format), |
| 260 GLDataType(format), | 262 GLDataType(format), |
| 261 GL_WRITE_ONLY)); | 263 GL_WRITE_ONLY)); |
| 262 | 264 |
| 263 if (!pixel_dest) { | 265 if (!pixel_dest) { |
| 264 UploadWithTexSubImage(image, image_rect, source_rect, dest_offset, format); | 266 UploadWithTexSubImage(image, image_rect, source_rect, dest_offset, format); |
| 265 return; | 267 return; |
| 266 } | 268 } |
| 267 | 269 |
| 268 if (upload_image_stride == image_rect.width() * bytes_per_pixel && | 270 if (upload_image_stride == image_rect.width() * bytes_per_pixel && |
| 269 !offset.x()) { | 271 !offset.x()) { |
| 270 memcpy(pixel_dest, | 272 memcpy(pixel_dest, |
| 271 &image[image_rect.width() * bytes_per_pixel * offset.y()], | 273 &image[image_rect.width() * bytes_per_pixel * offset.y()], |
| 272 source_rect.height() * image_rect.width() * bytes_per_pixel); | 274 source_rect.height() * image_rect.width() * bytes_per_pixel); |
| 273 } else { | 275 } else { |
| 274 // Strides not equal, so do a row-by-row memcpy from the | 276 // Strides not equal, so do a row-by-row memcpy from the |
| 275 // paint results into the pixel_dest. | 277 // paint results into the pixel_dest. |
| 276 for (int row = 0; row < source_rect.height(); ++row) { | 278 for (int row = 0; row < source_rect.height(); ++row) { |
| 277 memcpy(&pixel_dest[upload_image_stride * row], | 279 memcpy(&pixel_dest[upload_image_stride * row], |
| 278 &image[bytes_per_pixel * | 280 &image[bytes_per_pixel * |
| 279 (offset.x() + (offset.y() + row) * image_rect.width())], | 281 (offset.x() + (offset.y() + row) * image_rect.width())], |
| 280 source_rect.width() * bytes_per_pixel); | 282 source_rect.width() * bytes_per_pixel); |
| 281 } | 283 } |
| 282 } | 284 } |
| 283 | 285 |
| 284 context_->unmapTexSubImage2DCHROMIUM(pixel_dest); | 286 gl_->UnmapTexSubImage2DCHROMIUM(pixel_dest); |
| 285 } | 287 } |
| 286 | 288 |
| 287 void TextureUploader::UploadWithTexImageETC1(const uint8* image, | 289 void TextureUploader::UploadWithTexImageETC1(const uint8* image, |
| 288 gfx::Size size) { | 290 gfx::Size size) { |
| 289 TRACE_EVENT0("cc", "TextureUploader::UploadWithTexImageETC1"); | 291 TRACE_EVENT0("cc", "TextureUploader::UploadWithTexImageETC1"); |
| 290 DCHECK_EQ(0, size.width() % 4); | 292 DCHECK_EQ(0, size.width() % 4); |
| 291 DCHECK_EQ(0, size.height() % 4); | 293 DCHECK_EQ(0, size.height() % 4); |
| 292 | 294 |
| 293 context_->compressedTexImage2D(GL_TEXTURE_2D, | 295 gl_->CompressedTexImage2D(GL_TEXTURE_2D, |
| 294 0, | 296 0, |
| 295 GLInternalFormat(ETC1), | 297 GLInternalFormat(ETC1), |
| 296 size.width(), | 298 size.width(), |
| 297 size.height(), | 299 size.height(), |
| 298 0, | 300 0, |
| 299 Resource::MemorySizeBytes(size, ETC1), | 301 Resource::MemorySizeBytes(size, ETC1), |
| 300 image); | 302 image); |
| 301 } | 303 } |
| 302 | 304 |
| 303 void TextureUploader::ProcessQueries() { | 305 void TextureUploader::ProcessQueries() { |
| 304 while (!pending_queries_.empty()) { | 306 while (!pending_queries_.empty()) { |
| 305 if (pending_queries_.front()->IsPending()) | 307 if (pending_queries_.front()->IsPending()) |
| 306 break; | 308 break; |
| 307 | 309 |
| 308 unsigned us_elapsed = pending_queries_.front()->Value(); | 310 unsigned us_elapsed = pending_queries_.front()->Value(); |
| 309 UMA_HISTOGRAM_CUSTOM_COUNTS( | 311 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 310 "Renderer4.TextureGpuUploadTimeUS", us_elapsed, 0, 100000, 50); | 312 "Renderer4.TextureGpuUploadTimeUS", us_elapsed, 0, 100000, 50); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 322 textures_per_second_history_.erase(textures_per_second_history_.begin()); | 324 textures_per_second_history_.erase(textures_per_second_history_.begin()); |
| 323 textures_per_second_history_.erase(--textures_per_second_history_.end()); | 325 textures_per_second_history_.erase(--textures_per_second_history_.end()); |
| 324 } | 326 } |
| 325 textures_per_second_history_.insert(textures_per_second); | 327 textures_per_second_history_.insert(textures_per_second); |
| 326 | 328 |
| 327 available_queries_.push_back(pending_queries_.take_front()); | 329 available_queries_.push_back(pending_queries_.take_front()); |
| 328 } | 330 } |
| 329 } | 331 } |
| 330 | 332 |
| 331 } // namespace cc | 333 } // namespace cc |
| OLD | NEW |