diff options
-rw-r--r-- | cc/BUILD.gn | 2 | ||||
-rw-r--r-- | cc/cc.gyp | 1 | ||||
-rw-r--r-- | cc/cc_tests.gyp | 1 | ||||
-rw-r--r-- | cc/raster/pixel_buffer_tile_task_worker_pool.cc | 18 | ||||
-rw-r--r-- | cc/raster/tile_task_worker_pool_unittest.cc | 4 | ||||
-rw-r--r-- | cc/resources/resource.h | 33 | ||||
-rw-r--r-- | cc/resources/resource_format.cc | 62 | ||||
-rw-r--r-- | cc/resources/resource_format.h | 11 | ||||
-rw-r--r-- | cc/resources/resource_pool.cc | 24 | ||||
-rw-r--r-- | cc/resources/resource_pool_unittest.cc | 4 | ||||
-rw-r--r-- | cc/resources/resource_provider.cc | 19 | ||||
-rw-r--r-- | cc/resources/resource_provider.h | 55 | ||||
-rw-r--r-- | cc/resources/resource_util.h | 242 | ||||
-rw-r--r-- | cc/resources/resource_util_unittest.cc | 167 | ||||
-rw-r--r-- | cc/resources/scoped_resource_unittest.cc | 6 | ||||
-rw-r--r-- | cc/resources/video_resource_updater.cc | 18 | ||||
-rw-r--r-- | cc/tiles/tile.cc | 4 | ||||
-rw-r--r-- | cc/tiles/tile_manager.cc | 5 | ||||
-rw-r--r-- | cc/tiles/tile_manager_unittest.cc | 4 |
19 files changed, 541 insertions, 139 deletions
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index e421db1..92e5917 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn @@ -394,6 +394,7 @@ component("cc") { "resources/resource_pool.h", "resources/resource_provider.cc", "resources/resource_provider.h", + "resources/resource_util.h", "resources/returned_resource.h", "resources/scoped_resource.cc", "resources/scoped_resource.h", @@ -798,6 +799,7 @@ test("cc_unittests") { "resources/platform_color_unittest.cc", "resources/resource_pool_unittest.cc", "resources/resource_provider_unittest.cc", + "resources/resource_util_unittest.cc", "resources/scoped_resource_unittest.cc", "resources/video_resource_updater_unittest.cc", "scheduler/begin_frame_source_unittest.cc", @@ -451,6 +451,7 @@ 'resources/resource_pool.h', 'resources/resource_provider.cc', 'resources/resource_provider.h', + 'resources/resource_util.h', 'resources/returned_resource.h', 'resources/scoped_resource.cc', 'resources/scoped_resource.h', diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 5dfd42a..61ba1cf 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -97,6 +97,7 @@ 'resources/platform_color_unittest.cc', 'resources/resource_pool_unittest.cc', 'resources/resource_provider_unittest.cc', + 'resources/resource_util_unittest.cc', 'resources/scoped_resource_unittest.cc', 'resources/video_resource_updater_unittest.cc', 'scheduler/begin_frame_source_unittest.cc', diff --git a/cc/raster/pixel_buffer_tile_task_worker_pool.cc b/cc/raster/pixel_buffer_tile_task_worker_pool.cc index df79519..2d2524b 100644 --- a/cc/raster/pixel_buffer_tile_task_worker_pool.cc +++ b/cc/raster/pixel_buffer_tile_task_worker_pool.cc @@ -436,9 +436,9 @@ void PixelBufferTileTaskWorkerPool::CheckForCompletedUploads() { DCHECK(state_it != raster_task_states_.end()); RasterTaskState& state = *state_it; - // We can use UncheckedMemorySizeBytes here, since these tasks come from - // tiles, the size of which is controlled by the compositor. - bytes_pending_upload_ -= Resource::UncheckedMemorySizeBytes( + // We can use UncheckedSizeInBytes here, since these tasks come from tiles, + // the size of which is controlled by the compositor. + bytes_pending_upload_ -= ResourceUtil::UncheckedSizeInBytes<size_t>( task->resource()->size(), task->resource()->format()); task->WillComplete(); @@ -545,9 +545,9 @@ void PixelBufferTileTaskWorkerPool::ScheduleMoreTasks() { // but if it's the only task allow it to complete no matter what its size, // to prevent starvation of the task queue. size_t new_bytes_pending_upload = bytes_pending_upload; - // We can use UncheckedMemorySizeBytes here, since these tasks come from - // tiles, the size of which is controlled by the compositor. - new_bytes_pending_upload += Resource::UncheckedMemorySizeBytes( + // We can use UncheckedSizeInBytes here, since these tasks come from tiles, + // the size of which is controlled by the compositor. + new_bytes_pending_upload += ResourceUtil::UncheckedSizeInBytes<size_t>( task->resource()->size(), task->resource()->format()); if (new_bytes_pending_upload > max_bytes_pending_upload_ && bytes_pending_upload) { @@ -712,9 +712,9 @@ void PixelBufferTileTaskWorkerPool::CheckForCompletedRasterizerTasks() { resource_provider_->BeginSetPixels(raster_task->resource()->id()); has_performed_uploads_since_last_flush_ = true; - // We can use UncheckedMemorySizeBytes here, since these tasks come from - // tiles, the size of which is controlled by the compositor. - bytes_pending_upload_ += Resource::UncheckedMemorySizeBytes( + // We can use UncheckedSizeInBytes here, since these tasks come from tiles, + // the size of which is controlled by the compositor. + bytes_pending_upload_ += ResourceUtil::UncheckedSizeInBytes<size_t>( raster_task->resource()->size(), raster_task->resource()->format()); raster_tasks_with_pending_upload_.push_back(raster_task); state.type = RasterTaskState::UPLOADING; diff --git a/cc/raster/tile_task_worker_pool_unittest.cc b/cc/raster/tile_task_worker_pool_unittest.cc index 76e5fe4..fa36474 100644 --- a/cc/raster/tile_task_worker_pool_unittest.cc +++ b/cc/raster/tile_task_worker_pool_unittest.cc @@ -406,8 +406,8 @@ TEST_P(TileTaskWorkerPoolTest, LargeResources) { ScopedResource::Create(resource_provider_.get())); resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888); - EXPECT_GE(Resource::UncheckedMemorySizeBytes(resource->size(), - resource->format()), + EXPECT_GE(ResourceUtil::UncheckedSizeInBytes<size_t>(resource->size(), + resource->format()), kMaxTransferBufferUsageBytes); } diff --git a/cc/resources/resource.h b/cc/resources/resource.h index f9138fb..549a314 100644 --- a/cc/resources/resource.h +++ b/cc/resources/resource.h @@ -5,9 +5,9 @@ #ifndef CC_RESOURCES_RESOURCE_H_ #define CC_RESOURCES_RESOURCE_H_ -#include "base/numerics/safe_math.h" #include "cc/base/cc_export.h" #include "cc/resources/resource_provider.h" +#include "cc/resources/resource_util.h" #include "ui/gfx/geometry/size.h" namespace cc { @@ -24,37 +24,6 @@ class CC_EXPORT Resource { gfx::Size size() const { return size_; } ResourceFormat format() const { return format_; } - // Return true if the call to UncheckedMemorySizeBytes would return a value - // that fits in a size_t. - static bool VerifySizeInBytes(const gfx::Size& size, ResourceFormat format) { - base::CheckedNumeric<size_t> checked_value = BitsPerPixel(format); - checked_value *= size.width(); - checked_value *= size.height(); - if (!checked_value.IsValid()) - return false; - size_t value = checked_value.ValueOrDie(); - if ((value % 8) != 0) - return false; - return true; - } - - static size_t CheckedMemorySizeBytes(const gfx::Size& size, - ResourceFormat format) { - DCHECK(VerifySizeInBytes(size, format)); - base::CheckedNumeric<size_t> checked_value = BitsPerPixel(format); - checked_value *= size.width(); - checked_value *= size.height(); - checked_value /= 8; - return checked_value.ValueOrDie(); - } - - inline static size_t UncheckedMemorySizeBytes(const gfx::Size& size, - ResourceFormat format) { - DCHECK(VerifySizeInBytes(size, format)); - return static_cast<size_t>(BitsPerPixel(format)) * size.width() * - size.height() / 8; - } - protected: void set_id(ResourceId id) { id_ = id; } void set_dimensions(const gfx::Size& size, ResourceFormat format) { diff --git a/cc/resources/resource_format.cc b/cc/resources/resource_format.cc index 45581b8..ce9fe7d 100644 --- a/cc/resources/resource_format.cc +++ b/cc/resources/resource_format.cc @@ -4,6 +4,9 @@ #include "cc/resources/resource_format.h" +#include "third_party/khronos/GLES2/gl2.h" +#include "third_party/khronos/GLES2/gl2ext.h" + namespace cc { SkColorType ResourceFormatToSkColorType(ResourceFormat format) { @@ -25,4 +28,63 @@ SkColorType ResourceFormatToSkColorType(ResourceFormat format) { return kN32_SkColorType; } +int BitsPerPixel(ResourceFormat format) { + switch (format) { + case BGRA_8888: + case RGBA_8888: + return 32; + case RGBA_4444: + case RGB_565: + return 16; + case ALPHA_8: + case LUMINANCE_8: + case RED_8: + return 8; + case ETC1: + return 4; + } + NOTREACHED(); + return 0; +} + +GLenum GLDataType(ResourceFormat format) { + DCHECK_LE(format, RESOURCE_FORMAT_MAX); + static const GLenum format_gl_data_type[] = { + GL_UNSIGNED_BYTE, // RGBA_8888 + GL_UNSIGNED_SHORT_4_4_4_4, // RGBA_4444 + GL_UNSIGNED_BYTE, // BGRA_8888 + GL_UNSIGNED_BYTE, // ALPHA_8 + GL_UNSIGNED_BYTE, // LUMINANCE_8 + GL_UNSIGNED_SHORT_5_6_5, // RGB_565, + GL_UNSIGNED_BYTE, // ETC1 + GL_UNSIGNED_BYTE // RED_8 + }; + static_assert(arraysize(format_gl_data_type) == (RESOURCE_FORMAT_MAX + 1), + "format_gl_data_type does not handle all cases."); + + return format_gl_data_type[format]; +} + +GLenum GLDataFormat(ResourceFormat format) { + DCHECK_LE(format, RESOURCE_FORMAT_MAX); + static const GLenum format_gl_data_format[] = { + GL_RGBA, // RGBA_8888 + GL_RGBA, // RGBA_4444 + GL_BGRA_EXT, // BGRA_8888 + GL_ALPHA, // ALPHA_8 + GL_LUMINANCE, // LUMINANCE_8 + GL_RGB, // RGB_565 + GL_ETC1_RGB8_OES, // ETC1 + GL_RED_EXT // RED_8 + }; + static_assert(arraysize(format_gl_data_format) == (RESOURCE_FORMAT_MAX + 1), + "format_gl_data_format does not handle all cases."); + + return format_gl_data_format[format]; +} + +GLenum GLInternalFormat(ResourceFormat format) { + return GLDataFormat(format); +} + } // namespace cc diff --git a/cc/resources/resource_format.h b/cc/resources/resource_format.h index d785ab7..d910bb0 100644 --- a/cc/resources/resource_format.h +++ b/cc/resources/resource_format.h @@ -6,8 +6,14 @@ #define CC_RESOURCES_RESOURCE_FORMAT_H_ #include "base/logging.h" +#include "cc/base/cc_export.h" #include "third_party/skia/include/core/SkBitmap.h" +// TODO(prashant.n): Including third_party/khronos/GLES2/gl2.h causes +// redefinition errors as macros/functions defined in it conflict with +// macros/functions defined in ui/gl/gl_bindings.h. (http://crbug.com/512833). +typedef unsigned int GLenum; + namespace cc { // Keep in sync with arrays below. @@ -25,6 +31,11 @@ enum ResourceFormat { SkColorType ResourceFormatToSkColorType(ResourceFormat format); +CC_EXPORT int BitsPerPixel(ResourceFormat format); +CC_EXPORT GLenum GLDataType(ResourceFormat format); +CC_EXPORT GLenum GLDataFormat(ResourceFormat format); +CC_EXPORT GLenum GLInternalFormat(ResourceFormat format); + } // namespace cc #endif // CC_RESOURCES_RESOURCE_FORMAT_H_ diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc index 7b65d34..300c48f 100644 --- a/cc/resources/resource_pool.cc +++ b/cc/resources/resource_pool.cc @@ -5,6 +5,7 @@ #include "cc/resources/resource_pool.h" #include "cc/resources/resource_provider.h" +#include "cc/resources/resource_util.h" #include "cc/resources/scoped_resource.h" namespace cc { @@ -48,7 +49,7 @@ scoped_ptr<ScopedResource> ResourcePool::AcquireResource( unused_resources_.erase(it); unused_memory_usage_bytes_ -= - Resource::UncheckedMemorySizeBytes(size, format); + ResourceUtil::UncheckedSizeInBytes<size_t>(size, format); return make_scoped_ptr(resource); } @@ -56,9 +57,10 @@ scoped_ptr<ScopedResource> ResourcePool::AcquireResource( ScopedResource::Create(resource_provider_); resource->AllocateManaged(size, target_, format); - DCHECK(Resource::VerifySizeInBytes(resource->size(), resource->format())); - memory_usage_bytes_ += - Resource::UncheckedMemorySizeBytes(resource->size(), resource->format()); + DCHECK(ResourceUtil::VerifySizeInBytes<size_t>(resource->size(), + resource->format())); + memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( + resource->size(), resource->format()); ++resource_count_; return resource.Pass(); } @@ -78,8 +80,8 @@ scoped_ptr<ScopedResource> ResourcePool::TryAcquireResourceWithContentId( DCHECK(resource_provider_->CanLockForWrite(resource->id())); unused_resources_.erase(it); - unused_memory_usage_bytes_ -= - Resource::UncheckedMemorySizeBytes(resource->size(), resource->format()); + unused_memory_usage_bytes_ -= ResourceUtil::UncheckedSizeInBytes<size_t>( + resource->size(), resource->format()); return make_scoped_ptr(resource); } @@ -112,7 +114,7 @@ void ResourcePool::ReduceResourceUsage() { // memory is necessarily returned to the OS. ScopedResource* resource = unused_resources_.front().resource; unused_resources_.pop_front(); - unused_memory_usage_bytes_ -= Resource::UncheckedMemorySizeBytes( + unused_memory_usage_bytes_ -= ResourceUtil::UncheckedSizeInBytes<size_t>( resource->size(), resource->format()); DeleteResource(resource); } @@ -129,8 +131,8 @@ bool ResourcePool::ResourceUsageTooHigh() { } void ResourcePool::DeleteResource(ScopedResource* resource) { - size_t resource_bytes = - Resource::UncheckedMemorySizeBytes(resource->size(), resource->format()); + size_t resource_bytes = ResourceUtil::UncheckedSizeInBytes<size_t>( + resource->size(), resource->format()); memory_usage_bytes_ -= resource_bytes; --resource_count_; delete resource; @@ -160,8 +162,8 @@ void ResourcePool::CheckBusyResources(bool wait_if_needed) { void ResourcePool::DidFinishUsingResource(ScopedResource* resource, uint64_t content_id) { - unused_memory_usage_bytes_ += - Resource::UncheckedMemorySizeBytes(resource->size(), resource->format()); + unused_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( + resource->size(), resource->format()); unused_resources_.push_back(PoolResource(resource, content_id)); } diff --git a/cc/resources/resource_pool_unittest.cc b/cc/resources/resource_pool_unittest.cc index 96e7591..035b19e 100644 --- a/cc/resources/resource_pool_unittest.cc +++ b/cc/resources/resource_pool_unittest.cc @@ -4,6 +4,7 @@ #include "cc/resources/resource_pool.h" +#include "cc/resources/resource_util.h" #include "cc/resources/scoped_resource.h" #include "cc/test/fake_output_surface.h" #include "cc/test/fake_output_surface_client.h" @@ -54,7 +55,8 @@ TEST_F(ResourcePoolTest, AccountingSingleResource) { gfx::Size size(100, 100); ResourceFormat format = RGBA_8888; - size_t resource_bytes = Resource::UncheckedMemorySizeBytes(size, format); + size_t resource_bytes = + ResourceUtil::UncheckedSizeInBytes<size_t>(size, format); scoped_ptr<ScopedResource> resource = resource_pool_->AcquireResource(size, format); diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc index 9f68066..30f3391f 100644 --- a/cc/resources/resource_provider.cc +++ b/cc/resources/resource_provider.cc @@ -14,8 +14,8 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/trace_event/trace_event.h" -#include "cc/base/math_util.h" #include "cc/resources/platform_color.h" +#include "cc/resources/resource_util.h" #include "cc/resources/returned_resource.h" #include "cc/resources/shared_bitmap_manager.h" #include "cc/resources/transferable_resource.h" @@ -724,13 +724,10 @@ void ResourceProvider::CopyToResource(ResourceId id, gl->BindTexture(GL_TEXTURE_2D, resource->gl_id); if (resource->format == ETC1) { - base::CheckedNumeric<int> num_bytes = BitsPerPixel(ETC1); - num_bytes *= image_size.width(); - num_bytes *= image_size.height(); - num_bytes /= 8; + int image_bytes = ResourceUtil::CheckedSizeInBytes<int>(image_size, ETC1); gl->CompressedTexImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(ETC1), image_size.width(), image_size.height(), 0, - num_bytes.ValueOrDie(), image); + image_bytes, image); } else { gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_size.width(), image_size.height(), GLDataFormat(resource->format), @@ -1531,12 +1528,10 @@ void ResourceProvider::AcquirePixelBuffer(ResourceId id) { resource->gl_pixel_buffer_id = buffer_id_allocator_->NextId(); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); - unsigned bytes_per_pixel = BitsPerPixel(resource->format) / 8; - gl->BufferData( - GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, - resource->size.height() * - MathUtil::RoundUp(bytes_per_pixel * resource->size.width(), 4u), - NULL, GL_DYNAMIC_DRAW); + size_t resource_bytes = ResourceUtil::UncheckedSizeInBytesAligned<size_t>( + resource->size, resource->format); + gl->BufferData(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource_bytes, NULL, + GL_DYNAMIC_DRAW); gl->BindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h index fbed140..44d555a 100644 --- a/cc/resources/resource_provider.h +++ b/cc/resources/resource_provider.h @@ -608,61 +608,6 @@ class CC_EXPORT ResourceProvider { DISALLOW_COPY_AND_ASSIGN(ResourceProvider); }; -// TODO(epenner): Move these format conversions to resource_format.h -// once that builds on mac (npapi.h currently #includes OpenGL.h). -inline int BitsPerPixel(ResourceFormat format) { - switch (format) { - case BGRA_8888: - case RGBA_8888: - return 32; - case RGBA_4444: - case RGB_565: - return 16; - case ALPHA_8: - case LUMINANCE_8: - case RED_8: - return 8; - case ETC1: - return 4; - } - NOTREACHED(); - return 0; -} - -inline GLenum GLDataType(ResourceFormat format) { - DCHECK_LE(format, RESOURCE_FORMAT_MAX); - static const unsigned format_gl_data_type[RESOURCE_FORMAT_MAX + 1] = { - GL_UNSIGNED_BYTE, // RGBA_8888 - GL_UNSIGNED_SHORT_4_4_4_4, // RGBA_4444 - GL_UNSIGNED_BYTE, // BGRA_8888 - GL_UNSIGNED_BYTE, // ALPHA_8 - GL_UNSIGNED_BYTE, // LUMINANCE_8 - GL_UNSIGNED_SHORT_5_6_5, // RGB_565, - GL_UNSIGNED_BYTE, // ETC1 - GL_UNSIGNED_BYTE // RED_8 - }; - return format_gl_data_type[format]; -} - -inline GLenum GLDataFormat(ResourceFormat format) { - DCHECK_LE(format, RESOURCE_FORMAT_MAX); - static const unsigned format_gl_data_format[RESOURCE_FORMAT_MAX + 1] = { - GL_RGBA, // RGBA_8888 - GL_RGBA, // RGBA_4444 - GL_BGRA_EXT, // BGRA_8888 - GL_ALPHA, // ALPHA_8 - GL_LUMINANCE, // LUMINANCE_8 - GL_RGB, // RGB_565 - GL_ETC1_RGB8_OES, // ETC1 - GL_RED_EXT // RED_8 - }; - return format_gl_data_format[format]; -} - -inline GLenum GLInternalFormat(ResourceFormat format) { - return GLDataFormat(format); -} - } // namespace cc #endif // CC_RESOURCES_RESOURCE_PROVIDER_H_ diff --git a/cc/resources/resource_util.h b/cc/resources/resource_util.h new file mode 100644 index 0000000..0953fbb --- /dev/null +++ b/cc/resources/resource_util.h @@ -0,0 +1,242 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RESOURCES_RESOURCE_UTIL_H_ +#define CC_RESOURCES_RESOURCE_UTIL_H_ + +#include <limits> + +#include "base/numerics/safe_math.h" +#include "cc/base/cc_export.h" +#include "cc/base/math_util.h" +#include "cc/resources/resource_format.h" +#include "ui/gfx/geometry/size.h" + +namespace cc { + +class CC_EXPORT ResourceUtil { + public: + // Returns true if the width is valid and fits in bytes, false otherwise. + template <typename T> + static bool VerifyWidthInBytes(int width, ResourceFormat format); + // Returns true if the size is valid and fits in bytes, false otherwise. + template <typename T> + static bool VerifySizeInBytes(const gfx::Size& size, ResourceFormat format); + + // Dies with a CRASH() if the width can not be represented as a positive + // number of bytes. + template <typename T> + static T CheckedWidthInBytes(int width, ResourceFormat format); + // Dies with a CRASH() if the size can not be represented as a positive + // number of bytes. + template <typename T> + static T CheckedSizeInBytes(const gfx::Size& size, ResourceFormat format); + + // Returns the width in bytes but may overflow or return 0. Only do this for + // computing widths for sizes that have already been checked. + template <typename T> + static T UncheckedWidthInBytes(int width, ResourceFormat format); + // Returns the size in bytes but may overflow or return 0. Only do this for + // sizes that have already been checked. + template <typename T> + static T UncheckedSizeInBytes(const gfx::Size& size, ResourceFormat format); + // Returns the width in bytes aligned but may overflow or return 0. Only do + // this for computing widths for sizes that have already been checked. + template <typename T> + static T UncheckedWidthInBytesAligned(int width, ResourceFormat format); + // Returns the size in bytes aligned but may overflow or return 0. Only do + // this for sizes that have already been checked. + template <typename T> + static T UncheckedSizeInBytesAligned(const gfx::Size& size, + ResourceFormat format); + + private: + // TODO(prashant.n): Replace IsSameType with std::is_same once C++11 is used + // on all platforms. + template <typename T, typename U> + struct IsSameType { + static const bool value = false; + }; + + template <typename T> + struct IsSameType<T, T> { + static const bool value = true; + }; + + template <typename T> + static inline void VerifyType(); + + template <typename T> + static bool VerifyRoundUp(T value, T round_up); + + template <typename T> + static bool VerifyFitsInBytesInternal(int width, + int height, + ResourceFormat format, + bool verify_size, + bool aligned); + + template <typename T> + static T BytesInternal(int width, + int height, + ResourceFormat format, + bool verify_size, + bool aligned); + + DISALLOW_COPY_AND_ASSIGN(ResourceUtil); +}; + +template <typename T> +bool ResourceUtil::VerifyWidthInBytes(int width, ResourceFormat format) { + VerifyType<T>(); + return VerifyFitsInBytesInternal<T>(width, 0, format, false, false); +} + +template <typename T> +bool ResourceUtil::VerifySizeInBytes(const gfx::Size& size, + ResourceFormat format) { + VerifyType<T>(); + return VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, + false); +} + +template <typename T> +T ResourceUtil::CheckedWidthInBytes(int width, ResourceFormat format) { + VerifyType<T>(); + DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, false)); + base::CheckedNumeric<T> checked_value = BitsPerPixel(format); + checked_value *= width; + // TODO(prashant.n): Move the value roundup overflow to MathUtil class. + // http://crbug.com/515566 + CHECK(VerifyRoundUp(checked_value.ValueOrDie(), static_cast<T>(8))); + checked_value = MathUtil::RoundUp<T>(checked_value.ValueOrDie(), 8); + checked_value /= 8; + return checked_value.ValueOrDie(); +} + +template <typename T> +T ResourceUtil::CheckedSizeInBytes(const gfx::Size& size, + ResourceFormat format) { + VerifyType<T>(); + DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, + false)); + base::CheckedNumeric<T> checked_value = BitsPerPixel(format); + checked_value *= size.width(); + CHECK(VerifyRoundUp(checked_value.ValueOrDie(), static_cast<T>(8))); + checked_value = MathUtil::RoundUp<T>(checked_value.ValueOrDie(), 8); + checked_value /= 8; + checked_value *= size.height(); + return checked_value.ValueOrDie(); +} + +template <typename T> +T ResourceUtil::UncheckedWidthInBytes(int width, ResourceFormat format) { + VerifyType<T>(); + DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, false)); + return BytesInternal<T>(width, 0, format, false, false); +} + +template <typename T> +T ResourceUtil::UncheckedSizeInBytes(const gfx::Size& size, + ResourceFormat format) { + VerifyType<T>(); + DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, + false)); + return BytesInternal<T>(size.width(), size.height(), format, true, false); +} + +template <typename T> +T ResourceUtil::UncheckedWidthInBytesAligned(int width, ResourceFormat format) { + VerifyType<T>(); + DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, true)); + return BytesInternal<T>(width, 0, format, false, true); +} + +template <typename T> +T ResourceUtil::UncheckedSizeInBytesAligned(const gfx::Size& size, + ResourceFormat format) { + VerifyType<T>(); + DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, + true)); + return BytesInternal<T>(size.width(), size.height(), format, true, true); +} + +template <typename T> +void ResourceUtil::VerifyType() { + static_assert( + std::numeric_limits<T>::is_integer && !IsSameType<T, bool>::value, + "T must be non-bool integer type. Preferred type is size_t."); +} + +template <typename T> +bool ResourceUtil::VerifyRoundUp(T value, T round_up) { + if (round_up == 0) + return false; + if (value == round_up) + return true; + if ((std::numeric_limits<T>::max() - (value - (value % round_up))) >= + round_up) + return true; + return false; +} + +template <typename T> +bool ResourceUtil::VerifyFitsInBytesInternal(int width, + int height, + ResourceFormat format, + bool verify_size, + bool aligned) { + base::CheckedNumeric<T> checked_value = BitsPerPixel(format); + checked_value *= width; + if (!checked_value.IsValid()) + return false; + + // Roundup bits to byte (8 bits) boundary. If width is 3 and BitsPerPixel is + // 4, then it should return 16, so that row pixels do not get truncated. + DCHECK(VerifyRoundUp(checked_value.ValueOrDie(), static_cast<T>(8))); + checked_value = MathUtil::RoundUp<T>(checked_value.ValueOrDie(), 8); + + // If aligned is true, bytes are aligned on 4-bytes boundaries for upload + // performance, assuming that GL_PACK_ALIGNMENT or GL_UNPACK_ALIGNMENT have + // not changed from default. + if (aligned) { + checked_value /= 8; + if (!checked_value.IsValid()) + return false; + checked_value = MathUtil::RoundUp<T>(checked_value.ValueOrDie(), 4); + checked_value *= 8; + } + + if (verify_size) + checked_value *= height; + if (!checked_value.IsValid()) + return false; + T value = checked_value.ValueOrDie(); + if ((value % 8) != 0) + return false; + return true; +} + +template <typename T> +T ResourceUtil::BytesInternal(int width, + int height, + ResourceFormat format, + bool verify_size, + bool aligned) { + T bytes = BitsPerPixel(format); + bytes *= width; + DCHECK(VerifyRoundUp(bytes, static_cast<T>(8))); + bytes = MathUtil::RoundUp<T>(bytes, 8); + bytes /= 8; + if (aligned) + bytes = MathUtil::RoundUp<T>(bytes, 4); + if (verify_size) + bytes *= height; + + return bytes; +} + +} // namespace cc + +#endif // CC_RESOURCES_RESOURCE_UTIL_H_ diff --git a/cc/resources/resource_util_unittest.cc b/cc/resources/resource_util_unittest.cc new file mode 100644 index 0000000..1fabc3b --- /dev/null +++ b/cc/resources/resource_util_unittest.cc @@ -0,0 +1,167 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "cc/resources/resource_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +struct TestFormat { + ResourceFormat format; + size_t expected_bytes; + size_t expected_bytes_aligned; +}; + +// Modify this constant as per TestFormat variables defined in following tests. +const int kTestFormats = 4; + +class ResourceUtilTest : public testing::Test { + public: + void TestVerifyWidthInBytes(int width, const TestFormat* test_formats) { + for (int i = 0; i < kTestFormats; ++i) { + EXPECT_TRUE(ResourceUtil::VerifyWidthInBytes<size_t>( + width, test_formats[i].format)); + } + } + + void TestCheckedWidthInBytes(int width, const TestFormat* test_formats) { + for (int i = 0; i < kTestFormats; ++i) { + size_t bytes = ResourceUtil::CheckedWidthInBytes<size_t>( + width, test_formats[i].format); + EXPECT_EQ(bytes, test_formats[i].expected_bytes); + } + } + + void TestUncheckedWidthInBytes(int width, const TestFormat* test_formats) { + for (int i = 0; i < kTestFormats; ++i) { + size_t bytes = ResourceUtil::UncheckedWidthInBytes<size_t>( + width, test_formats[i].format); + EXPECT_EQ(bytes, test_formats[i].expected_bytes); + } + } + + void TestUncheckedWidthInBytesAligned(int width, + const TestFormat* test_formats) { + for (int i = 0; i < kTestFormats; ++i) { + size_t bytes = ResourceUtil::UncheckedWidthInBytesAligned<size_t>( + width, test_formats[i].format); + EXPECT_EQ(bytes, test_formats[i].expected_bytes_aligned); + } + } + + void TestVerifySizeInBytes(const gfx::Size& size, + const TestFormat* test_formats) { + for (int i = 0; i < kTestFormats; ++i) { + EXPECT_TRUE(ResourceUtil::VerifySizeInBytes<size_t>( + size, test_formats[i].format)); + } + } + + void TestCheckedSizeInBytes(const gfx::Size& size, + const TestFormat* test_formats) { + for (int i = 0; i < kTestFormats; ++i) { + size_t bytes = ResourceUtil::CheckedSizeInBytes<size_t>( + size, test_formats[i].format); + EXPECT_EQ(bytes, test_formats[i].expected_bytes); + } + } + + void TestUncheckedSizeInBytes(const gfx::Size& size, + const TestFormat* test_formats) { + for (int i = 0; i < kTestFormats; ++i) { + size_t bytes = ResourceUtil::UncheckedSizeInBytes<size_t>( + size, test_formats[i].format); + EXPECT_EQ(bytes, test_formats[i].expected_bytes); + } + } + + void TestUncheckedSizeInBytesAligned(const gfx::Size& size, + const TestFormat* test_formats) { + for (int i = 0; i < kTestFormats; ++i) { + size_t bytes = ResourceUtil::UncheckedSizeInBytesAligned<size_t>( + size, test_formats[i].format); + EXPECT_EQ(bytes, test_formats[i].expected_bytes_aligned); + } + } +}; + +TEST_F(ResourceUtilTest, WidthInBytes) { + // Check bytes for even width. + int width = 10; + TestFormat test_formats[] = { + {RGBA_8888, 40, 40}, // for 32 bits + {RGBA_4444, 20, 20}, // for 16 bits + {ALPHA_8, 10, 12}, // for 8 bits + {ETC1, 5, 8} // for 4 bits + }; + + TestVerifyWidthInBytes(width, test_formats); + TestCheckedWidthInBytes(width, test_formats); + TestUncheckedWidthInBytes(width, test_formats); + TestUncheckedWidthInBytesAligned(width, test_formats); + + // Check bytes for odd width. + int width_odd = 11; + TestFormat test_formats_odd[] = { + {RGBA_8888, 44, 44}, // for 32 bits + {RGBA_4444, 22, 24}, // for 16 bits + {ALPHA_8, 11, 12}, // for 8 bits + {ETC1, 6, 8} // for 4 bits + }; + + TestVerifyWidthInBytes(width_odd, test_formats_odd); + TestCheckedWidthInBytes(width_odd, test_formats_odd); + TestUncheckedWidthInBytes(width_odd, test_formats_odd); + TestUncheckedWidthInBytesAligned(width_odd, test_formats_odd); +} + +TEST_F(ResourceUtilTest, SizeInBytes) { + // Check bytes for even size. + gfx::Size size(10, 10); + TestFormat test_formats[] = { + {RGBA_8888, 400, 400}, // for 32 bits + {RGBA_4444, 200, 200}, // for 16 bits + {ALPHA_8, 100, 120}, // for 8 bits + {ETC1, 50, 80} // for 4 bits + }; + + TestVerifySizeInBytes(size, test_formats); + TestCheckedSizeInBytes(size, test_formats); + TestUncheckedSizeInBytes(size, test_formats); + TestUncheckedSizeInBytesAligned(size, test_formats); + + // Check bytes for odd size. + gfx::Size size_odd(11, 11); + TestFormat test_formats_odd[] = { + {RGBA_8888, 484, 484}, // for 32 bits + {RGBA_4444, 242, 264}, // for 16 bits + {ALPHA_8, 121, 132}, // for 8 bits + {ETC1, 66, 88} // for 4 bits + }; + + TestVerifySizeInBytes(size_odd, test_formats_odd); + TestCheckedSizeInBytes(size_odd, test_formats_odd); + TestUncheckedSizeInBytes(size_odd, test_formats_odd); + TestUncheckedSizeInBytesAligned(size_odd, test_formats_odd); +} + +TEST_F(ResourceUtilTest, WidthInBytesOverflow) { + int width = 10; + // 10 * 16 = 160 bits, overflows in char, but fits in unsigned char. + EXPECT_FALSE(ResourceUtil::VerifyWidthInBytes<signed char>(width, RGBA_4444)); + EXPECT_TRUE( + ResourceUtil::VerifyWidthInBytes<unsigned char>(width, RGBA_4444)); +} + +TEST_F(ResourceUtilTest, SizeInBytesOverflow) { + gfx::Size size(10, 10); + // 10 * 16 * 10 = 1600 bits, overflows in char, but fits in int. + EXPECT_FALSE(ResourceUtil::VerifySizeInBytes<signed char>(size, RGBA_4444)); + EXPECT_TRUE(ResourceUtil::VerifySizeInBytes<int>(size, RGBA_4444)); +} + +} // namespace +} // namespace cc diff --git a/cc/resources/scoped_resource_unittest.cc b/cc/resources/scoped_resource_unittest.cc index 197fa1f3..cb41bf6 100644 --- a/cc/resources/scoped_resource_unittest.cc +++ b/cc/resources/scoped_resource_unittest.cc @@ -31,8 +31,8 @@ TEST(ScopedResourceTest, NewScopedResource) { // New scoped textures do not have a size yet. EXPECT_EQ(gfx::Size(), texture->size()); - EXPECT_EQ(0u, Resource::UncheckedMemorySizeBytes(texture->size(), - texture->format())); + EXPECT_EQ(0u, ResourceUtil::UncheckedSizeInBytes<size_t>(texture->size(), + texture->format())); } TEST(ScopedResourceTest, CreateScopedResource) { @@ -51,7 +51,7 @@ TEST(ScopedResourceTest, CreateScopedResource) { // The texture has an allocated byte-size now. size_t expected_bytes = 30 * 30 * 4; - EXPECT_EQ(expected_bytes, Resource::UncheckedMemorySizeBytes( + EXPECT_EQ(expected_bytes, ResourceUtil::UncheckedSizeInBytes<size_t>( texture->size(), texture->format())); EXPECT_LT(0u, texture->id()); diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc index dd5606e..88e8360 100644 --- a/cc/resources/video_resource_updater.cc +++ b/cc/resources/video_resource_updater.cc @@ -305,16 +305,18 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( gfx::Size resource_size_pixels = plane_resource.resource_size; // The |video_stride_pixels| is the width of the video frame we are // uploading (including non-frame data to fill in the stride). - size_t video_stride_pixels = video_frame->stride(i); + int video_stride_pixels = video_frame->stride(i); - size_t bytes_per_pixel = BitsPerPixel(plane_resource.resource_format) / 8; + size_t bytes_per_row = ResourceUtil::UncheckedWidthInBytes<size_t>( + resource_size_pixels.width(), plane_resource.resource_format); // Use 4-byte row alignment (OpenGL default) for upload performance. // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. - size_t upload_image_stride = MathUtil::RoundUp<size_t>( - bytes_per_pixel * resource_size_pixels.width(), 4u); + size_t upload_image_stride = MathUtil::RoundUp<size_t>(bytes_per_row, 4u); const uint8_t* pixels; - if (upload_image_stride == video_stride_pixels * bytes_per_pixel) { + size_t video_bytes_per_row = ResourceUtil::UncheckedWidthInBytes<size_t>( + video_stride_pixels, plane_resource.resource_format); + if (upload_image_stride == video_bytes_per_row) { pixels = video_frame->data(i); } else { // Avoid malloc for each frame/plane if possible. @@ -324,9 +326,9 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( upload_pixels_.resize(needed_size); for (int row = 0; row < resource_size_pixels.height(); ++row) { uint8_t* dst = &upload_pixels_[upload_image_stride * row]; - const uint8_t* src = video_frame->data(i) + - bytes_per_pixel * video_stride_pixels * row; - memcpy(dst, src, resource_size_pixels.width() * bytes_per_pixel); + const uint8_t* src = + video_frame->data(i) + (video_bytes_per_row * row); + memcpy(dst, src, bytes_per_row); } pixels = &upload_pixels_[0]; } diff --git a/cc/tiles/tile.cc b/cc/tiles/tile.cc index 6052e5e..ad50dd1 100644 --- a/cc/tiles/tile.cc +++ b/cc/tiles/tile.cc @@ -71,8 +71,8 @@ size_t Tile::GPUMemoryUsageInBytes() const { if (draw_info_.resource_) { // We can use UncheckedSizeInBytes, since the tile size is determined by the // compositor. - return Resource::UncheckedMemorySizeBytes(draw_info_.resource_->size(), - draw_info_.resource_->format()); + return ResourceUtil::UncheckedSizeInBytes<size_t>( + draw_info_.resource_->size(), draw_info_.resource_->format()); } return 0; } diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc index 23d2eca..545866e 100644 --- a/cc/tiles/tile_manager.cc +++ b/cc/tiles/tile_manager.cc @@ -1045,9 +1045,10 @@ TileManager::MemoryUsage::MemoryUsage(size_t memory_bytes, TileManager::MemoryUsage TileManager::MemoryUsage::FromConfig( const gfx::Size& size, ResourceFormat format) { - // We can use UncheckedMemorySizeBytes here since this is used with a tile + // We can use UncheckedSizeInBytes here since this is used with a tile // size which is determined by the compositor (it's at most max texture size). - return MemoryUsage(Resource::UncheckedMemorySizeBytes(size, format), 1); + return MemoryUsage(ResourceUtil::UncheckedSizeInBytes<size_t>(size, format), + 1); } // static diff --git a/cc/tiles/tile_manager_unittest.cc b/cc/tiles/tile_manager_unittest.cc index 891d082..27ef65d 100644 --- a/cc/tiles/tile_manager_unittest.cc +++ b/cc/tiles/tile_manager_unittest.cc @@ -1386,8 +1386,8 @@ TEST_F(TileManagerTilePriorityQueueTest, EXPECT_EQ(RGBA_8888, host_impl_.resource_provider()->best_texture_format()); ManagedMemoryPolicy policy = host_impl_.ActualManagedMemoryPolicy(); - policy.bytes_limit_when_visible = - Resource::UncheckedMemorySizeBytes(gfx::Size(256, 256), RGBA_8888); + policy.bytes_limit_when_visible = ResourceUtil::UncheckedSizeInBytes<size_t>( + gfx::Size(256, 256), RGBA_8888); host_impl_.SetMemoryPolicy(policy); EXPECT_FALSE(host_impl_.is_likely_to_require_a_draw()); |