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"
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"
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
41 TextureUploader::Query::Query(blink::WebGraphicsContext3D
* context
)
46 is_non_blocking_(false) {
47 query_id_
= context_
->createQueryEXT();
50 TextureUploader::Query::~Query() { context_
->deleteQueryEXT(query_id_
); }
52 void TextureUploader::Query::Begin() {
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
);
69 unsigned TextureUploader::Query::Value() {
71 context_
->getQueryObjectuivEXT(query_id_
, GL_QUERY_RESULT_EXT
, &value_
);
77 TextureUploader::TextureUploader(blink::WebGraphicsContext3D
* context
,
78 bool use_map_tex_sub_image
,
79 bool use_shallow_flush
)
81 num_blocking_texture_uploads_(0),
82 use_map_tex_sub_image_(use_map_tex_sub_image
),
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() {
94 return num_blocking_texture_uploads_
;
97 void TextureUploader::MarkPendingUploadsAsNonBlocking() {
98 for (ScopedPtrDeque
<Query
>::iterator it
= pending_queries_
.begin();
99 it
!= pending_queries_
.end();
101 if ((*it
)->is_non_blocking())
104 num_blocking_texture_uploads_
--;
105 (*it
)->mark_as_non_blocking();
108 DCHECK(!num_blocking_texture_uploads_
);
111 double TextureUploader::EstimatedTexturesPerSecond() {
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);
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
,
139 CHECK(image_rect
.Contains(source_rect
));
141 bool is_full_upload
= dest_offset
.IsZero() && source_rect
.size() == size
;
146 if (format
== ETC1
) {
147 // ETC1 does not support subimage uploads.
148 DCHECK(is_full_upload
);
149 UploadWithTexImageETC1(image
, size
);
151 if (use_map_tex_sub_image_
) {
152 UploadWithMapTexSubImage(
153 image
, image_rect
, source_rect
, dest_offset
, format
);
155 UploadWithTexSubImage(
156 image
, image_rect
, source_rect
, dest_offset
, format
);
163 num_texture_uploads_since_last_flush_
++;
164 if (num_texture_uploads_since_last_flush_
>= kTextureUploadFlushPeriod
)
168 void TextureUploader::Flush() {
169 if (!num_texture_uploads_since_last_flush_
)
172 if (use_shallow_flush_
)
173 context_
->shallowFlushCHROMIUM();
175 num_texture_uploads_since_last_flush_
= 0;
178 void TextureUploader::ReleaseCachedQueries() {
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
192 if (source_rect
.IsEmpty())
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
&&
208 pixel_source
= &image
[image_rect
.width() * bytes_per_pixel
* offset
.y()];
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
,
231 source_rect
.height(),
232 GLDataFormat(format
),
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
246 if (source_rect
.IsEmpty())
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
,
268 source_rect
.height(),
269 GLDataFormat(format
),
274 UploadWithTexSubImage(image
, image_rect
, source_rect
, dest_offset
, format
);
278 if (upload_image_stride
== image_rect
.width() * bytes_per_pixel
&&
281 &image
[image_rect
.width() * bytes_per_pixel
* offset
.y()],
282 source_rect
.height() * image_rect
.width() * bytes_per_pixel
);
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
,
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
),
309 Resource::MemorySizeBytes(size
, ETC1
),
313 void TextureUploader::ProcessQueries() {
314 while (!pending_queries_
.empty()) {
315 if (pending_queries_
.front()->IsPending())
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());