diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-09-18 14:34:04 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-10-04 11:15:27 +0000 |
commit | e6430e577f105ad8813c92e75c54660c4985026e (patch) | |
tree | 88115e5d1fb471fea807111924dcccbeadbf9e4f /chromium/components/viz/common | |
parent | 53d399fe6415a96ea6986ec0d402a9c07da72453 (diff) | |
download | qtwebengine-chromium-e6430e577f105ad8813c92e75c54660c4985026e.tar.gz |
BASELINE: Update Chromium to 61.0.3163.99
Change-Id: I8452f34574d88ca2b27af9bd56fc9ff3f16b1367
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/viz/common')
63 files changed, 7873 insertions, 0 deletions
diff --git a/chromium/components/viz/common/BUILD.gn b/chromium/components/viz/common/BUILD.gn new file mode 100644 index 00000000000..84f40f15662 --- /dev/null +++ b/chromium/components/viz/common/BUILD.gn @@ -0,0 +1,134 @@ +# Copyright 2017 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. + +import("//components/viz/viz.gni") +import("//testing/test.gni") + +viz_component("common") { + output_name = "viz_common" + + defines = [ "VIZ_COMMON_IMPLEMENTATION" ] + + sources = [ + "display/renderer_settings.cc", + "display/renderer_settings.h", + "gl_helper.cc", + "gl_helper.h", + "gl_helper_readback_support.cc", + "gl_helper_readback_support.h", + "gl_helper_scaling.cc", + "gl_helper_scaling.h", + "gpu/context_cache_controller.cc", + "gpu/context_cache_controller.h", + "gpu/context_provider.cc", + "gpu/context_provider.h", + "gpu/in_process_context_provider.cc", + "gpu/in_process_context_provider.h", + "hit_test/aggregated_hit_test_region.h", + "quads/resource_format.h", + "quads/shared_bitmap.cc", + "quads/shared_bitmap.h", + "quads/texture_mailbox.cc", + "quads/texture_mailbox.h", + "resources/buffer_to_texture_target_map.cc", + "resources/buffer_to_texture_target_map.h", + "resources/platform_color.h", + "resources/resource_format_utils.cc", + "resources/resource_format_utils.h", + "resources/resource_settings.cc", + "resources/resource_settings.h", + "resources/shared_bitmap_manager.h", + "surfaces/frame_sink_id.cc", + "surfaces/frame_sink_id.h", + "surfaces/frame_sink_id_allocator.h", + "surfaces/local_surface_id.cc", + "surfaces/local_surface_id.h", + "surfaces/local_surface_id_allocator.cc", + "surfaces/local_surface_id_allocator.h", + "surfaces/sequence_surface_reference_factory.cc", + "surfaces/sequence_surface_reference_factory.h", + "surfaces/surface_id.cc", + "surfaces/surface_id.h", + "surfaces/surface_info.h", + "surfaces/surface_reference_factory.h", + "surfaces/surface_reference_owner.h", + "surfaces/surface_sequence.h", + "surfaces/surface_sequence_generator.cc", + "surfaces/surface_sequence_generator.h", + "viz_common_export.h", + ] + + deps = [ + "//base", + "//gpu", + "//gpu/command_buffer/client:gles2_implementation", + "//gpu/command_buffer/client:gles2_interface", + "//gpu/command_buffer/service", + "//gpu/ipc:gl_in_process_context", + "//gpu/skia_bindings:skia_bindings", + "//mojo/public/cpp/bindings", + "//skia", + "//ui/gfx:color_space", + "//ui/gfx:geometry_skia", + "//ui/gfx/geometry", + ] + + public_deps = [ + "//gpu/command_buffer/client", + "//gpu/command_buffer/common", + "//mojo/public/cpp/bindings", + ] +} + +viz_source_set("unit_tests") { + testonly = true + sources = [ + "gl_helper_unittest.cc", + "resources/buffer_to_texture_target_map_unittest.cc", + "resources/platform_color_unittest.cc", + "surfaces/surface_sequence_generator_unittest.cc", + "yuv_readback_unittest.cc", + ] + + deps = [ + ":common", + "//base/test:test_support", + "//gpu/command_buffer/client:gles2_implementation", + "//gpu/command_buffer/client:gles2_interface", + "//gpu/ipc:gl_in_process_context", + "//gpu/ipc/common:surface_handle_type", + "//media", + "//testing/gtest", + ] +} + +# Microbenchmark to measure performance of GLHelper code, for use in +# debugging, profiling, and optimizing. +viz_test("viz_benchmark") { + sources = [ + "gl_helper_benchmark.cc", + ] + + configs = [ + "//build/config/compiler:no_size_t_to_int_warning", + "//third_party/khronos:khronos_headers", + ] + + deps = [ + ":common", + "//base", + "//base/test:test_support", + "//components/test:run_all_unittests", + "//gpu/command_buffer/client", + "//gpu/command_buffer/client:gles2_implementation", + "//gpu/ipc:gl_in_process_context", + "//testing/gmock", + "//testing/gtest", + "//ui/gfx", + ] + + data_deps = [ + "//third_party/mesa:osmesa", + ] +} diff --git a/chromium/components/viz/common/DEPS b/chromium/components/viz/common/DEPS new file mode 100644 index 00000000000..4efdc7698ad --- /dev/null +++ b/chromium/components/viz/common/DEPS @@ -0,0 +1,24 @@ +specific_include_rules = { + # DEPS for GLHelper and friends which are in the root common/ directory. + "(yuv_readback|gl_helper).*\.(cc|h)": [ + "+gpu/GLES2", + "+gpu/command_buffer/client", + "+gpu/command_buffer/common", + "+gpu/command_buffer/service", + "+gpu/ipc/common", + "+mojo/public/cpp/bindings", + "+ui/gfx/geometry", + "+services/ui/gpu/interfaces", + "+third_party/skia", + ], + ".*_unittest\.cc": [ + "+gpu/ipc/gl_in_process_context.h", + "+media/base", + "+ui/gfx", + "+ui/gl", + ], + ".*_benchmark\.cc": [ + "+gpu/ipc/gl_in_process_context.h", + "+ui/gfx", + ], +} diff --git a/chromium/components/viz/common/README.md b/chromium/components/viz/common/README.md new file mode 100644 index 00000000000..d2e8fd6f3d9 --- /dev/null +++ b/chromium/components/viz/common/README.md @@ -0,0 +1,4 @@ +This directory contains common code used by implementations of client/, host/, +and/or service/. It also contains common code used by the users of these +components (i.e. code outside of //components/viz that use code in client/, +host/, and/or service/ can also use this code). diff --git a/chromium/components/viz/common/display/renderer_settings.cc b/chromium/components/viz/common/display/renderer_settings.cc new file mode 100644 index 00000000000..643e6fdd23d --- /dev/null +++ b/chromium/components/viz/common/display/renderer_settings.cc @@ -0,0 +1,15 @@ +// Copyright 2014 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 "components/viz/common/display/renderer_settings.h" + +namespace viz { + +RendererSettings::RendererSettings() = default; + +RendererSettings::RendererSettings(const RendererSettings& other) = default; + +RendererSettings::~RendererSettings() = default; + +} // namespace viz diff --git a/chromium/components/viz/common/display/renderer_settings.h b/chromium/components/viz/common/display/renderer_settings.h new file mode 100644 index 00000000000..c7fb57c2540 --- /dev/null +++ b/chromium/components/viz/common/display/renderer_settings.h @@ -0,0 +1,42 @@ +// Copyright 2014 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 COMPONENTS_VIZ_COMMON_DISPLAY_RENDERER_SETTINGS_H_ +#define COMPONENTS_VIZ_COMMON_DISPLAY_RENDERER_SETTINGS_H_ + +#include <stddef.h> + +#include "components/viz/common/resources/resource_settings.h" +#include "components/viz/common/viz_common_export.h" + +namespace viz { + +class VIZ_COMMON_EXPORT RendererSettings { + public: + RendererSettings(); + RendererSettings(const RendererSettings& other); + ~RendererSettings(); + + ResourceSettings resource_settings; + bool allow_antialiasing = true; + bool force_antialiasing = false; + bool force_blending_with_shaders = false; + bool partial_swap_enabled = false; + bool finish_rendering_on_resize = false; + bool should_clear_root_render_pass = true; + bool release_overlay_resources_after_gpu_query = false; + bool gl_composited_overlay_candidate_quad_border = false; + bool show_overdraw_feedback = false; + bool enable_color_correct_rendering = false; + int highp_threshold_min = 0; + + // Determines whether we disallow non-exact matches when finding resources + // in ResourcePool. Only used for layout or pixel tests, as non-deterministic + // resource sizes can lead to floating point error and noise in these tests. + bool disallow_non_exact_resource_reuse = false; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_DISPLAY_RENDERER_SETTINGS_H_ diff --git a/chromium/components/viz/common/gl_helper.cc b/chromium/components/viz/common/gl_helper.cc new file mode 100644 index 00000000000..003db7b762e --- /dev/null +++ b/chromium/components/viz/common/gl_helper.cc @@ -0,0 +1,1237 @@ +// Copyright (c) 2012 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 "components/viz/common/gl_helper.h" + +#include <stddef.h> +#include <stdint.h> + +#include <queue> +#include <string> + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/string_util.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "components/viz/common/gl_helper_readback_support.h" +#include "components/viz/common/gl_helper_scaling.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "gpu/command_buffer/client/context_support.h" +#include "gpu/command_buffer/common/mailbox.h" +#include "gpu/command_buffer/common/mailbox_holder.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +using gpu::gles2::GLES2Interface; + +namespace { + +class ScopedFlush { + public: + explicit ScopedFlush(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} + + ~ScopedFlush() { gl_->Flush(); } + + private: + gpu::gles2::GLES2Interface* gl_; + + DISALLOW_COPY_AND_ASSIGN(ScopedFlush); +}; + +// Helper class for allocating and holding an RGBA texture of a given +// size and an associated framebuffer. +class TextureFrameBufferPair { + public: + TextureFrameBufferPair(GLES2Interface* gl, gfx::Size size) + : texture_(gl), framebuffer_(gl), size_(size) { + viz::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + viz::ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder( + gl, framebuffer_); + gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture_, 0); + } + + GLuint texture() const { return texture_.id(); } + GLuint framebuffer() const { return framebuffer_.id(); } + gfx::Size size() const { return size_; } + + private: + viz::ScopedTexture texture_; + viz::ScopedFramebuffer framebuffer_; + gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(TextureFrameBufferPair); +}; + +// Helper class for holding a scaler, a texture for the output of that +// scaler and an associated frame buffer. This is inteded to be used +// when the output of a scaler is to be sent to a readback. +class ScalerHolder { + public: + ScalerHolder(GLES2Interface* gl, viz::GLHelper::ScalerInterface* scaler) + : texture_and_framebuffer_(gl, scaler->DstSize()), scaler_(scaler) {} + + void Scale(GLuint src_texture) { + scaler_->Scale(src_texture, texture_and_framebuffer_.texture()); + } + + viz::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); } + TextureFrameBufferPair* texture_and_framebuffer() { + return &texture_and_framebuffer_; + } + GLuint texture() const { return texture_and_framebuffer_.texture(); } + + private: + TextureFrameBufferPair texture_and_framebuffer_; + std::unique_ptr<viz::GLHelper::ScalerInterface> scaler_; + + DISALLOW_COPY_AND_ASSIGN(ScalerHolder); +}; + +} // namespace + +namespace viz { +typedef GLHelperReadbackSupport::FormatSupport FormatSupport; + +// Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates +// the data needed for it. +class GLHelper::CopyTextureToImpl + : public base::SupportsWeakPtr<GLHelper::CopyTextureToImpl> { + public: + CopyTextureToImpl(GLES2Interface* gl, + gpu::ContextSupport* context_support, + GLHelper* helper) + : gl_(gl), + context_support_(context_support), + helper_(helper), + flush_(gl), + max_draw_buffers_(0) { + const GLubyte* extensions = gl_->GetString(GL_EXTENSIONS); + if (!extensions) + return; + std::string extensions_string = + " " + std::string(reinterpret_cast<const char*>(extensions)) + " "; + if (extensions_string.find(" GL_EXT_draw_buffers ") != std::string::npos) { + gl_->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_); + } + } + ~CopyTextureToImpl() { CancelRequests(); } + + GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token) { + return helper_->ConsumeMailboxToTexture(mailbox, sync_token); + } + + void CropScaleReadbackAndCleanTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback<void(bool)>& callback, + GLHelper::ScalerQuality quality); + + void ReadbackTextureSync(GLuint texture, + const gfx::Rect& src_rect, + unsigned char* out, + SkColorType format); + + void ReadbackTextureAsync(GLuint texture, + const gfx::Size& dst_size, + unsigned char* out, + SkColorType color_type, + const base::Callback<void(bool)>& callback); + + // Reads back bytes from the currently bound frame buffer. + // Note that dst_size is specified in bytes, not pixels. + void ReadbackAsync(const gfx::Size& dst_size, + size_t bytes_per_row, // generally dst_size.width() * 4 + size_t row_stride_bytes, // generally dst_size.width() * 4 + unsigned char* out, + GLenum format, + GLenum type, + size_t bytes_per_pixel, + const base::Callback<void(bool)>& callback); + + void ReadbackPlane(TextureFrameBufferPair* source, + int row_stride_bytes, + unsigned char* data, + int size_shift, + const gfx::Rect& paste_rect, + ReadbackSwizzle swizzle, + const base::Callback<void(bool)>& callback); + + GLuint CopyAndScaleTexture(GLuint texture, + const gfx::Size& src_size, + const gfx::Size& dst_size, + bool vertically_flip_texture, + GLHelper::ScalerQuality quality); + + ReadbackYUVInterface* CreateReadbackPipelineYUV( + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + bool use_mrt); + + // Returns the maximum number of draw buffers available, + // 0 if GL_EXT_draw_buffers is not available. + GLint MaxDrawBuffers() const { return max_draw_buffers_; } + + FormatSupport GetReadbackConfig(SkColorType color_type, + bool can_swizzle, + GLenum* format, + GLenum* type, + size_t* bytes_per_pixel); + + private: + // A single request to CropScaleReadbackAndCleanTexture. + // The main thread can cancel the request, before it's handled by the helper + // thread, by resetting the texture and pixels fields. Alternatively, the + // thread marks that it handles the request by resetting the pixels field + // (meaning it guarantees that the callback with be called). + // In either case, the callback must be called exactly once, and the texture + // must be deleted by the main thread gl. + struct Request { + Request(const gfx::Size& size_, + size_t bytes_per_row_, + size_t row_stride_bytes_, + unsigned char* pixels_, + const base::Callback<void(bool)>& callback_) + : done(false), + size(size_), + bytes_per_row(bytes_per_row_), + row_stride_bytes(row_stride_bytes_), + pixels(pixels_), + callback(callback_), + buffer(0), + query(0) {} + + bool done; + bool result; + gfx::Size size; + size_t bytes_per_row; + size_t row_stride_bytes; + unsigned char* pixels; + base::Callback<void(bool)> callback; + GLuint buffer; + GLuint query; + }; + + // We must take care to call the callbacks last, as they may + // end up destroying the gl_helper and make *this invalid. + // We stick the finished requests in a stack object that calls + // the callbacks when it goes out of scope. + class FinishRequestHelper { + public: + FinishRequestHelper() {} + ~FinishRequestHelper() { + while (!requests_.empty()) { + Request* request = requests_.front(); + requests_.pop(); + request->callback.Run(request->result); + delete request; + } + } + void Add(Request* r) { requests_.push(r); } + + private: + std::queue<Request*> requests_; + DISALLOW_COPY_AND_ASSIGN(FinishRequestHelper); + }; + + // A readback pipeline that also converts the data to YUV before + // reading it back. + class ReadbackYUVImpl : public ReadbackYUVInterface { + public: + ReadbackYUVImpl(GLES2Interface* gl, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + ReadbackSwizzle swizzle); + + void ReadbackYUV(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const gfx::Rect& target_visible_rect, + int y_plane_row_stride_bytes, + unsigned char* y_plane_data, + int u_plane_row_stride_bytes, + unsigned char* u_plane_data, + int v_plane_row_stride_bytes, + unsigned char* v_plane_data, + const gfx::Point& paste_location, + const base::Callback<void(bool)>& callback) override; + + ScalerInterface* scaler() override { return scaler_.scaler(); } + + private: + GLES2Interface* gl_; + CopyTextureToImpl* copy_impl_; + gfx::Size dst_size_; + ReadbackSwizzle swizzle_; + ScalerHolder scaler_; + ScalerHolder y_; + ScalerHolder u_; + ScalerHolder v_; + + DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl); + }; + + // A readback pipeline that also converts the data to YUV before + // reading it back. This one uses Multiple Render Targets, which + // may not be supported on all platforms. + class ReadbackYUV_MRT : public ReadbackYUVInterface { + public: + ReadbackYUV_MRT(GLES2Interface* gl, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + ReadbackSwizzle swizzle); + + void ReadbackYUV(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const gfx::Rect& target_visible_rect, + int y_plane_row_stride_bytes, + unsigned char* y_plane_data, + int u_plane_row_stride_bytes, + unsigned char* u_plane_data, + int v_plane_row_stride_bytes, + unsigned char* v_plane_data, + const gfx::Point& paste_location, + const base::Callback<void(bool)>& callback) override; + + ScalerInterface* scaler() override { return scaler_.scaler(); } + + private: + GLES2Interface* gl_; + CopyTextureToImpl* copy_impl_; + gfx::Size dst_size_; + GLHelper::ScalerQuality quality_; + ReadbackSwizzle swizzle_; + ScalerHolder scaler_; + std::unique_ptr<GLHelperScaling::ShaderInterface> pass1_shader_; + std::unique_ptr<GLHelperScaling::ShaderInterface> pass2_shader_; + TextureFrameBufferPair y_; + ScopedTexture uv_; + TextureFrameBufferPair u_; + TextureFrameBufferPair v_; + + DISALLOW_COPY_AND_ASSIGN(ReadbackYUV_MRT); + }; + + // Copies the block of pixels specified with |src_subrect| from |src_texture|, + // scales it to |dst_size|, writes it into a texture, and returns its ID. + // |src_size| is the size of |src_texture|. + GLuint ScaleTexture(GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + SkColorType color_type, + GLHelper::ScalerQuality quality); + + // Converts each four consecutive pixels of the source texture into one pixel + // in the result texture with each pixel channel representing the grayscale + // color of one of the four original pixels: + // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X1X2X3X4 + // The resulting texture is still an RGBA texture (which is ~4 times narrower + // than the original). If rendered directly, it wouldn't show anything useful, + // but the data in it can be used to construct a grayscale image. + // |encoded_texture_size| is the exact size of the resulting RGBA texture. It + // is equal to src_size.width()/4 rounded upwards. Some channels in the last + // pixel ((-src_size.width()) % 4) to be exact) are padding and don't contain + // useful data. + // If swizzle is set to true, the transformed pixels are reordered: + // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X3X2X1X4. + GLuint EncodeTextureAsGrayscale(GLuint src_texture, + const gfx::Size& src_size, + gfx::Size* const encoded_texture_size, + bool vertically_flip_texture, + bool swizzle); + + static void nullcallback(bool success) {} + void ReadbackDone(Request* request, size_t bytes_per_pixel); + void FinishRequest(Request* request, + bool result, + FinishRequestHelper* helper); + void CancelRequests(); + + static const float kRGBtoYColorWeights[]; + static const float kRGBtoUColorWeights[]; + static const float kRGBtoVColorWeights[]; + static const float kRGBtoGrayscaleColorWeights[]; + + GLES2Interface* gl_; + gpu::ContextSupport* context_support_; + GLHelper* helper_; + + // A scoped flush that will ensure all resource deletions are flushed when + // this object is destroyed. Must be declared before other Scoped* fields. + ScopedFlush flush_; + + std::queue<Request*> request_queue_; + GLint max_draw_buffers_; +}; + +GLHelper::ScalerInterface* GLHelper::CreateScaler(ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle) { + InitScalerImpl(); + return scaler_impl_->CreateScaler(quality, src_size, src_subrect, dst_size, + vertically_flip_texture, swizzle); +} + +GLuint GLHelper::CopyTextureToImpl::ScaleTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + SkColorType color_type, + GLHelper::ScalerQuality quality) { + GLuint dst_texture = 0u; + gl_->GenTextures(1, &dst_texture); + { + GLenum format = GL_RGBA, type = GL_UNSIGNED_BYTE; + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture); + + // Use GL_RGBA for destination/temporary texture unless we're working with + // 16-bit data + if (color_type == kRGB_565_SkColorType) { + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + } + + gl_->TexImage2D(GL_TEXTURE_2D, 0, format, dst_size.width(), + dst_size.height(), 0, format, type, NULL); + } + std::unique_ptr<ScalerInterface> scaler( + helper_->CreateScaler(quality, src_size, src_subrect, dst_size, + vertically_flip_texture, swizzle)); + scaler->Scale(src_texture, dst_texture); + return dst_texture; +} + +GLuint GLHelper::CopyTextureToImpl::EncodeTextureAsGrayscale( + GLuint src_texture, + const gfx::Size& src_size, + gfx::Size* const encoded_texture_size, + bool vertically_flip_texture, + bool swizzle) { + GLuint dst_texture = 0u; + gl_->GenTextures(1, &dst_texture); + // The size of the encoded texture. + *encoded_texture_size = + gfx::Size((src_size.width() + 3) / 4, src_size.height()); + { + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, encoded_texture_size->width(), + encoded_texture_size->height(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + } + + helper_->InitScalerImpl(); + std::unique_ptr<ScalerInterface> grayscale_scaler( + helper_->scaler_impl_.get()->CreatePlanarScaler( + src_size, + gfx::Rect(0, 0, (src_size.width() + 3) & ~3, src_size.height()), + *encoded_texture_size, vertically_flip_texture, swizzle, + kRGBtoGrayscaleColorWeights)); + grayscale_scaler->Scale(src_texture, dst_texture); + return dst_texture; +} + +void GLHelper::CopyTextureToImpl::ReadbackAsync( + const gfx::Size& dst_size, + size_t bytes_per_row, + size_t row_stride_bytes, + unsigned char* out, + GLenum format, + GLenum type, + size_t bytes_per_pixel, + const base::Callback<void(bool)>& callback) { + TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::ReadbackAsync"); + Request* request = + new Request(dst_size, bytes_per_row, row_stride_bytes, out, callback); + request_queue_.push(request); + request->buffer = 0u; + + gl_->GenBuffers(1, &request->buffer); + gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); + gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, + bytes_per_pixel * dst_size.GetArea(), NULL, GL_STREAM_READ); + + request->query = 0u; + gl_->GenQueriesEXT(1, &request->query); + gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, request->query); + gl_->ReadPixels(0, 0, dst_size.width(), dst_size.height(), format, type, + NULL); + gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM); + gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); + context_support_->SignalQuery( + request->query, base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), + request, bytes_per_pixel)); +} + +void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback<void(bool)>& callback, + GLHelper::ScalerQuality quality) { + GLenum format, type; + size_t bytes_per_pixel; + SkColorType readback_color_type = out_color_type; + // Single-component textures are not supported by all GPUs, so we implement + // kAlpha_8_SkColorType support here via a special encoding (see below) using + // a 32-bit texture to represent an 8-bit image. + // Thus we use generic 32-bit readback in this case. + if (out_color_type == kAlpha_8_SkColorType) { + readback_color_type = kRGBA_8888_SkColorType; + } + + FormatSupport supported = GetReadbackConfig(readback_color_type, true, + &format, &type, &bytes_per_pixel); + + if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { + callback.Run(false); + return; + } + + GLuint texture = src_texture; + + // Scale texture if needed + // Optimization: SCALER_QUALITY_FAST is just a single bilinear pass, which we + // can do just as well in EncodeTextureAsGrayscale, which we will do if + // out_color_type is kAlpha_8_SkColorType, so let's skip the scaling step + // in that case. + bool scale_texture = out_color_type != kAlpha_8_SkColorType || + quality != GLHelper::SCALER_QUALITY_FAST; + if (scale_texture) { + // Don't swizzle during the scale step for kAlpha_8_SkColorType. + // We will swizzle in the encode step below if needed. + bool scale_swizzle = out_color_type == kAlpha_8_SkColorType + ? false + : supported == GLHelperReadbackSupport::SWIZZLE; + texture = ScaleTexture( + src_texture, src_size, src_subrect, dst_size, true, scale_swizzle, + out_color_type == kAlpha_8_SkColorType ? kN32_SkColorType + : out_color_type, + quality); + DCHECK(texture); + } + + gfx::Size readback_texture_size = dst_size; + // Encode texture to grayscale if needed. + if (out_color_type == kAlpha_8_SkColorType) { + // Do the vertical flip here if we haven't already done it when we scaled + // the texture. + bool encode_as_grayscale_vertical_flip = !scale_texture; + // EncodeTextureAsGrayscale by default creates a texture which should be + // read back as RGBA, so need to swizzle if the readback format is BGRA. + bool encode_as_grayscale_swizzle = format == GL_BGRA_EXT; + GLuint tmp_texture = EncodeTextureAsGrayscale( + texture, dst_size, &readback_texture_size, + encode_as_grayscale_vertical_flip, encode_as_grayscale_swizzle); + // If the scaled texture was created - delete it + if (scale_texture) + gl_->DeleteTextures(1, &texture); + texture = tmp_texture; + DCHECK(texture); + } + + // Readback the pixels of the resulting texture + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, + dst_framebuffer); + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + texture, 0); + + size_t bytes_per_row = out_color_type == kAlpha_8_SkColorType + ? dst_size.width() + : dst_size.width() * bytes_per_pixel; + + ReadbackAsync(readback_texture_size, bytes_per_row, bytes_per_row, out, + format, type, bytes_per_pixel, callback); + gl_->DeleteTextures(1, &texture); +} + +void GLHelper::CopyTextureToImpl::ReadbackTextureSync(GLuint texture, + const gfx::Rect& src_rect, + unsigned char* out, + SkColorType color_type) { + GLenum format, type; + size_t bytes_per_pixel; + FormatSupport supported = + GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel); + if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { + return; + } + + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, + dst_framebuffer); + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + texture, 0); + gl_->ReadPixels(src_rect.x(), src_rect.y(), src_rect.width(), + src_rect.height(), format, type, out); +} + +void GLHelper::CopyTextureToImpl::ReadbackTextureAsync( + GLuint texture, + const gfx::Size& dst_size, + unsigned char* out, + SkColorType color_type, + const base::Callback<void(bool)>& callback) { + GLenum format, type; + size_t bytes_per_pixel; + FormatSupport supported = + GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel); + if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { + callback.Run(false); + return; + } + + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, + dst_framebuffer); + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + texture, 0); + ReadbackAsync(dst_size, dst_size.width() * bytes_per_pixel, + dst_size.width() * bytes_per_pixel, out, format, type, + bytes_per_pixel, callback); +} + +GLuint GLHelper::CopyTextureToImpl::CopyAndScaleTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Size& dst_size, + bool vertically_flip_texture, + GLHelper::ScalerQuality quality) { + return ScaleTexture(src_texture, src_size, gfx::Rect(src_size), dst_size, + vertically_flip_texture, false, + kRGBA_8888_SkColorType, // GL_RGBA + quality); +} + +void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request, + size_t bytes_per_pixel) { + TRACE_EVENT0("gpu.capture", + "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete"); + finished_request->done = true; + + FinishRequestHelper finish_request_helper; + + // We process transfer requests in the order they were received, regardless + // of the order we get the callbacks in. + while (!request_queue_.empty()) { + Request* request = request_queue_.front(); + if (!request->done) { + break; + } + + bool result = false; + if (request->buffer != 0) { + gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); + unsigned char* data = static_cast<unsigned char*>(gl_->MapBufferCHROMIUM( + GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY)); + if (data) { + result = true; + if (request->bytes_per_row == request->size.width() * bytes_per_pixel && + request->bytes_per_row == request->row_stride_bytes) { + memcpy(request->pixels, data, + request->size.GetArea() * bytes_per_pixel); + } else { + unsigned char* out = request->pixels; + for (int y = 0; y < request->size.height(); y++) { + memcpy(out, data, request->bytes_per_row); + out += request->row_stride_bytes; + data += request->size.width() * bytes_per_pixel; + } + } + gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM); + } + gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); + } + FinishRequest(request, result, &finish_request_helper); + } +} + +void GLHelper::CopyTextureToImpl::FinishRequest( + Request* request, + bool result, + FinishRequestHelper* finish_request_helper) { + TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::FinishRequest"); + DCHECK(request_queue_.front() == request); + request_queue_.pop(); + request->result = result; + ScopedFlush flush(gl_); + if (request->query != 0) { + gl_->DeleteQueriesEXT(1, &request->query); + request->query = 0; + } + if (request->buffer != 0) { + gl_->DeleteBuffers(1, &request->buffer); + request->buffer = 0; + } + finish_request_helper->Add(request); +} + +void GLHelper::CopyTextureToImpl::CancelRequests() { + FinishRequestHelper finish_request_helper; + while (!request_queue_.empty()) { + Request* request = request_queue_.front(); + FinishRequest(request, false, &finish_request_helper); + } +} + +FormatSupport GLHelper::CopyTextureToImpl::GetReadbackConfig( + SkColorType color_type, + bool can_swizzle, + GLenum* format, + GLenum* type, + size_t* bytes_per_pixel) { + return helper_->readback_support_->GetReadbackConfig( + color_type, can_swizzle, format, type, bytes_per_pixel); +} + +GLHelper::GLHelper(GLES2Interface* gl, gpu::ContextSupport* context_support) + : gl_(gl), + context_support_(context_support), + readback_support_(new GLHelperReadbackSupport(gl)) {} + +GLHelper::~GLHelper() {} + +void GLHelper::CropScaleReadbackAndCleanTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback<void(bool)>& callback, + GLHelper::ScalerQuality quality) { + InitCopyTextToImpl(); + copy_texture_to_impl_->CropScaleReadbackAndCleanTexture( + src_texture, src_size, src_subrect, dst_size, out, out_color_type, + callback, quality); +} + +void GLHelper::CropScaleReadbackAndCleanMailbox( + const gpu::Mailbox& src_mailbox, + const gpu::SyncToken& sync_token, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback<void(bool)>& callback, + GLHelper::ScalerQuality quality) { + GLuint mailbox_texture = ConsumeMailboxToTexture(src_mailbox, sync_token); + CropScaleReadbackAndCleanTexture(mailbox_texture, src_size, src_subrect, + dst_size, out, out_color_type, callback, + quality); + gl_->DeleteTextures(1, &mailbox_texture); +} + +void GLHelper::ReadbackTextureSync(GLuint texture, + const gfx::Rect& src_rect, + unsigned char* out, + SkColorType format) { + InitCopyTextToImpl(); + copy_texture_to_impl_->ReadbackTextureSync(texture, src_rect, out, format); +} + +void GLHelper::ReadbackTextureAsync( + GLuint texture, + const gfx::Size& dst_size, + unsigned char* out, + SkColorType color_type, + const base::Callback<void(bool)>& callback) { + InitCopyTextToImpl(); + copy_texture_to_impl_->ReadbackTextureAsync(texture, dst_size, out, + color_type, callback); +} + +GLuint GLHelper::CopyTexture(GLuint texture, const gfx::Size& size) { + InitCopyTextToImpl(); + return copy_texture_to_impl_->CopyAndScaleTexture( + texture, size, size, false, GLHelper::SCALER_QUALITY_FAST); +} + +GLuint GLHelper::CopyAndScaleTexture(GLuint texture, + const gfx::Size& src_size, + const gfx::Size& dst_size, + bool vertically_flip_texture, + ScalerQuality quality) { + InitCopyTextToImpl(); + return copy_texture_to_impl_->CopyAndScaleTexture( + texture, src_size, dst_size, vertically_flip_texture, quality); +} + +GLuint GLHelper::CompileShaderFromSource(const GLchar* source, GLenum type) { + GLuint shader = gl_->CreateShader(type); + GLint length = base::checked_cast<GLint>(strlen(source)); + gl_->ShaderSource(shader, 1, &source, &length); + gl_->CompileShader(shader); + GLint compile_status = 0; + gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + GLint log_length = 0; + gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + if (log_length) { + std::unique_ptr<GLchar[]> log(new GLchar[log_length]); + GLsizei returned_log_length = 0; + gl_->GetShaderInfoLog(shader, log_length, &returned_log_length, + log.get()); + LOG(ERROR) << std::string(log.get(), returned_log_length); + } + gl_->DeleteShader(shader); + return 0; + } + return shader; +} + +void GLHelper::InitCopyTextToImpl() { + // Lazily initialize |copy_texture_to_impl_| + if (!copy_texture_to_impl_) + copy_texture_to_impl_.reset( + new CopyTextureToImpl(gl_, context_support_, this)); +} + +void GLHelper::InitScalerImpl() { + // Lazily initialize |scaler_impl_| + if (!scaler_impl_) + scaler_impl_.reset(new GLHelperScaling(gl_, this)); +} + +GLint GLHelper::MaxDrawBuffers() { + InitCopyTextToImpl(); + return copy_texture_to_impl_->MaxDrawBuffers(); +} + +void GLHelper::CopySubBufferDamage(GLenum target, + GLuint texture, + GLuint previous_texture, + const SkRegion& new_damage, + const SkRegion& old_damage) { + SkRegion region(old_damage); + if (region.op(new_damage, SkRegion::kDifference_Op)) { + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, + dst_framebuffer); + gl_->BindTexture(target, texture); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, + previous_texture, 0); + for (SkRegion::Iterator it(region); !it.done(); it.next()) { + const SkIRect& rect = it.rect(); + gl_->CopyTexSubImage2D(target, 0, rect.x(), rect.y(), rect.x(), rect.y(), + rect.width(), rect.height()); + } + gl_->BindTexture(target, 0); + gl_->Flush(); + } +} + +GLuint GLHelper::CreateTexture() { + GLuint texture = 0u; + gl_->GenTextures(1, &texture); + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return texture; +} + +void GLHelper::DeleteTexture(GLuint texture_id) { + gl_->DeleteTextures(1, &texture_id); +} + +void GLHelper::GenerateSyncToken(gpu::SyncToken* sync_token) { + const uint64_t fence_sync = gl_->InsertFenceSyncCHROMIUM(); + gl_->ShallowFlushCHROMIUM(); + gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token->GetData()); +} + +void GLHelper::WaitSyncToken(const gpu::SyncToken& sync_token) { + gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); +} + +gpu::MailboxHolder GLHelper::ProduceMailboxHolderFromTexture( + GLuint texture_id) { + gpu::Mailbox mailbox; + gl_->GenMailboxCHROMIUM(mailbox.name); + gl_->ProduceTextureDirectCHROMIUM(texture_id, GL_TEXTURE_2D, mailbox.name); + + gpu::SyncToken sync_token; + GenerateSyncToken(&sync_token); + + return gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D); +} + +GLuint GLHelper::ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token) { + if (mailbox.IsZero()) + return 0; + if (sync_token.HasData()) + WaitSyncToken(sync_token); + GLuint texture = + gl_->CreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); + return texture; +} + +void GLHelper::ResizeTexture(GLuint texture, const gfx::Size& size) { + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, + GL_RGB, GL_UNSIGNED_BYTE, NULL); +} + +void GLHelper::CopyTextureSubImage(GLuint texture, const gfx::Rect& rect) { + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); + gl_->CopyTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.x(), + rect.y(), rect.width(), rect.height()); +} + +void GLHelper::CopyTextureFullImage(GLuint texture, const gfx::Size& size) { + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); + gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size.width(), + size.height(), 0); +} + +void GLHelper::Flush() { + gl_->Flush(); +} + +void GLHelper::InsertOrderingBarrier() { + gl_->OrderingBarrierCHROMIUM(); +} + +void GLHelper::CopyTextureToImpl::ReadbackPlane( + TextureFrameBufferPair* source, + int row_stride_bytes, + unsigned char* data, + int size_shift, + const gfx::Rect& paste_rect, + ReadbackSwizzle swizzle, + const base::Callback<void(bool)>& callback) { + gl_->BindFramebuffer(GL_FRAMEBUFFER, source->framebuffer()); + const size_t offset = row_stride_bytes * (paste_rect.y() >> size_shift) + + (paste_rect.x() >> size_shift); + ReadbackAsync(source->size(), paste_rect.width() >> size_shift, + row_stride_bytes, data + offset, + (swizzle == kSwizzleBGRA) ? GL_BGRA_EXT : GL_RGBA, + GL_UNSIGNED_BYTE, 4, callback); +} + +const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = { + 0.257f, 0.504f, 0.098f, 0.0625f}; +const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = { + -0.148f, -0.291f, 0.439f, 0.5f}; +const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = { + 0.439f, -0.368f, -0.071f, 0.5f}; +const float GLHelper::CopyTextureToImpl::kRGBtoGrayscaleColorWeights[] = { + 0.213f, 0.715f, 0.072f, 0.0f}; + +// YUV readback constructors. Initiates the main scaler pipeline and +// one planar scaler for each of the Y, U and V planes. +GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl( + GLES2Interface* gl, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + ReadbackSwizzle swizzle) + : gl_(gl), + copy_impl_(copy_impl), + dst_size_(dst_size), + swizzle_(swizzle), + scaler_(gl, + scaler_impl->CreateScaler(quality, + src_size, + src_subrect, + dst_size, + flip_vertically, + false)), + y_(gl, + scaler_impl->CreatePlanarScaler( + dst_size, + gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()), + gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), + false, + (swizzle == kSwizzleBGRA), + kRGBtoYColorWeights)), + u_(gl, + scaler_impl->CreatePlanarScaler( + dst_size, + gfx::Rect(0, + 0, + (dst_size.width() + 7) & ~7, + (dst_size.height() + 1) & ~1), + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), + false, + (swizzle == kSwizzleBGRA), + kRGBtoUColorWeights)), + v_(gl, + scaler_impl->CreatePlanarScaler( + dst_size, + gfx::Rect(0, + 0, + (dst_size.width() + 7) & ~7, + (dst_size.height() + 1) & ~1), + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), + false, + (swizzle == kSwizzleBGRA), + kRGBtoVColorWeights)) { + DCHECK(!(dst_size.width() & 1)); + DCHECK(!(dst_size.height() & 1)); +} + +void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV( + const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const gfx::Rect& target_visible_rect, + int y_plane_row_stride_bytes, + unsigned char* y_plane_data, + int u_plane_row_stride_bytes, + unsigned char* u_plane_data, + int v_plane_row_stride_bytes, + unsigned char* v_plane_data, + const gfx::Point& paste_location, + const base::Callback<void(bool)>& callback) { + DCHECK(!(paste_location.x() & 1)); + DCHECK(!(paste_location.y() & 1)); + + GLuint mailbox_texture = + copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token); + + // Scale texture to right size. + scaler_.Scale(mailbox_texture); + gl_->DeleteTextures(1, &mailbox_texture); + + // Convert the scaled texture in to Y, U and V planes. + y_.Scale(scaler_.texture()); + u_.Scale(scaler_.texture()); + v_.Scale(scaler_.texture()); + + const gfx::Rect paste_rect(paste_location, dst_size_); + if (!target_visible_rect.Contains(paste_rect)) { + LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!"; + callback.Run(false); + return; + } + + // Read back planes, one at a time. Keep the video frame alive while doing the + // readback. + copy_impl_->ReadbackPlane(y_.texture_and_framebuffer(), + y_plane_row_stride_bytes, y_plane_data, 0, + paste_rect, swizzle_, base::Bind(&nullcallback)); + copy_impl_->ReadbackPlane(u_.texture_and_framebuffer(), + u_plane_row_stride_bytes, u_plane_data, 1, + paste_rect, swizzle_, base::Bind(&nullcallback)); + copy_impl_->ReadbackPlane(v_.texture_and_framebuffer(), + v_plane_row_stride_bytes, v_plane_data, 1, + paste_rect, swizzle_, callback); + gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); +} + +// YUV readback constructors. Initiates the main scaler pipeline and +// one planar scaler for each of the Y, U and V planes. +GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV_MRT( + GLES2Interface* gl, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + ReadbackSwizzle swizzle) + : gl_(gl), + copy_impl_(copy_impl), + dst_size_(dst_size), + quality_(quality), + swizzle_(swizzle), + scaler_(gl, + scaler_impl->CreateScaler(quality, + src_size, + src_subrect, + dst_size, + false, + false)), + pass1_shader_(scaler_impl->CreateYuvMrtShader( + dst_size, + gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()), + gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), + flip_vertically, + (swizzle == kSwizzleBGRA), + GLHelperScaling::SHADER_YUV_MRT_PASS1)), + pass2_shader_(scaler_impl->CreateYuvMrtShader( + gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), + gfx::Rect(0, 0, (dst_size.width() + 7) / 8 * 2, dst_size.height()), + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), + false, + (swizzle == kSwizzleBGRA), + GLHelperScaling::SHADER_YUV_MRT_PASS2)), + y_(gl, gfx::Size((dst_size.width() + 3) / 4, dst_size.height())), + uv_(gl), + u_(gl, + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)), + v_(gl, + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)) { + DCHECK(!(dst_size.width() & 1)); + DCHECK(!(dst_size.height() & 1)); + + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, uv_); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (dst_size.width() + 3) / 4, + dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); +} + +void GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV( + const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const gfx::Rect& target_visible_rect, + int y_plane_row_stride_bytes, + unsigned char* y_plane_data, + int u_plane_row_stride_bytes, + unsigned char* u_plane_data, + int v_plane_row_stride_bytes, + unsigned char* v_plane_data, + const gfx::Point& paste_location, + const base::Callback<void(bool)>& callback) { + DCHECK(!(paste_location.x() & 1)); + DCHECK(!(paste_location.y() & 1)); + + GLuint mailbox_texture = + copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token); + + GLuint texture; + if (quality_ == GLHelper::SCALER_QUALITY_FAST) { + // Optimization: SCALER_QUALITY_FAST is just a single bilinear + // pass, which pass1_shader_ can do just as well, so let's skip + // the actual scaling in that case. + texture = mailbox_texture; + } else { + // Scale texture to right size. + scaler_.Scale(mailbox_texture); + texture = scaler_.texture(); + } + + std::vector<GLuint> outputs(2); + // Convert the scaled texture in to Y, U and V planes. + outputs[0] = y_.texture(); + outputs[1] = uv_; + pass1_shader_->Execute(texture, outputs); + + gl_->DeleteTextures(1, &mailbox_texture); + + outputs[0] = u_.texture(); + outputs[1] = v_.texture(); + pass2_shader_->Execute(uv_, outputs); + + const gfx::Rect paste_rect(paste_location, dst_size_); + if (!target_visible_rect.Contains(paste_rect)) { + LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!"; + callback.Run(false); + return; + } + + // Read back planes, one at a time. + copy_impl_->ReadbackPlane(&y_, y_plane_row_stride_bytes, y_plane_data, 0, + paste_rect, swizzle_, base::Bind(&nullcallback)); + copy_impl_->ReadbackPlane(&u_, u_plane_row_stride_bytes, u_plane_data, 1, + paste_rect, swizzle_, base::Bind(&nullcallback)); + copy_impl_->ReadbackPlane(&v_, v_plane_row_stride_bytes, v_plane_data, 1, + paste_rect, swizzle_, callback); + gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); +} + +bool GLHelper::IsReadbackConfigSupported(SkColorType color_type) { + DCHECK(readback_support_.get()); + GLenum format, type; + size_t bytes_per_pixel; + FormatSupport support = readback_support_->GetReadbackConfig( + color_type, false, &format, &type, &bytes_per_pixel); + + return (support == GLHelperReadbackSupport::SUPPORTED); +} + +ReadbackYUVInterface* GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV( + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + bool use_mrt) { + helper_->InitScalerImpl(); + // Just query if the best readback configuration needs a swizzle In + // ReadbackPlane() we will choose GL_RGBA/GL_BGRA_EXT based on swizzle + GLenum format, type; + size_t bytes_per_pixel; + FormatSupport supported = GetReadbackConfig(kRGBA_8888_SkColorType, true, + &format, &type, &bytes_per_pixel); + DCHECK((format == GL_RGBA || format == GL_BGRA_EXT) && + type == GL_UNSIGNED_BYTE); + + ReadbackSwizzle swizzle = kSwizzleNone; + if (supported == GLHelperReadbackSupport::SWIZZLE) + swizzle = kSwizzleBGRA; + + if (max_draw_buffers_ >= 2 && use_mrt) { + return new ReadbackYUV_MRT(gl_, this, helper_->scaler_impl_.get(), quality, + src_size, src_subrect, dst_size, flip_vertically, + swizzle); + } + return new ReadbackYUVImpl(gl_, this, helper_->scaler_impl_.get(), quality, + src_size, src_subrect, dst_size, flip_vertically, + swizzle); +} + +ReadbackYUVInterface* GLHelper::CreateReadbackPipelineYUV( + ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + bool use_mrt) { + InitCopyTextToImpl(); + return copy_texture_to_impl_->CreateReadbackPipelineYUV( + quality, src_size, src_subrect, dst_size, flip_vertically, use_mrt); +} + +} // namespace viz diff --git a/chromium/components/viz/common/gl_helper.h b/chromium/components/viz/common/gl_helper.h new file mode 100644 index 00000000000..931b8549fed --- /dev/null +++ b/chromium/components/viz/common/gl_helper.h @@ -0,0 +1,383 @@ +// Copyright (c) 2012 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 COMPONENTS_VIZ_COMMON_GL_HELPER_H_ +#define COMPONENTS_VIZ_COMMON_GL_HELPER_H_ + +#include <memory> + +#include "base/atomicops.h" +#include "base/callback.h" +#include "base/macros.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/common/mailbox_holder.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace gfx { +class Point; +class Rect; +class Size; +} // namespace gfx + +namespace gpu { +class ContextSupport; +struct Mailbox; +} // namespace gpu + +class SkRegion; + +namespace viz { + +class GLHelperScaling; + +class ScopedGLuint { + public: + typedef void (gpu::gles2::GLES2Interface::*GenFunc)(GLsizei n, GLuint* ids); + typedef void (gpu::gles2::GLES2Interface::*DeleteFunc)(GLsizei n, + const GLuint* ids); + ScopedGLuint(gpu::gles2::GLES2Interface* gl, + GenFunc gen_func, + DeleteFunc delete_func) + : gl_(gl), id_(0u), delete_func_(delete_func) { + (gl_->*gen_func)(1, &id_); + } + + operator GLuint() const { return id_; } + + GLuint id() const { return id_; } + + ~ScopedGLuint() { + if (id_ != 0) { + (gl_->*delete_func_)(1, &id_); + } + } + + private: + gpu::gles2::GLES2Interface* gl_; + GLuint id_; + DeleteFunc delete_func_; + + DISALLOW_COPY_AND_ASSIGN(ScopedGLuint); +}; + +class ScopedBuffer : public ScopedGLuint { + public: + explicit ScopedBuffer(gpu::gles2::GLES2Interface* gl) + : ScopedGLuint(gl, + &gpu::gles2::GLES2Interface::GenBuffers, + &gpu::gles2::GLES2Interface::DeleteBuffers) {} +}; + +class ScopedFramebuffer : public ScopedGLuint { + public: + explicit ScopedFramebuffer(gpu::gles2::GLES2Interface* gl) + : ScopedGLuint(gl, + &gpu::gles2::GLES2Interface::GenFramebuffers, + &gpu::gles2::GLES2Interface::DeleteFramebuffers) {} +}; + +class ScopedTexture : public ScopedGLuint { + public: + explicit ScopedTexture(gpu::gles2::GLES2Interface* gl) + : ScopedGLuint(gl, + &gpu::gles2::GLES2Interface::GenTextures, + &gpu::gles2::GLES2Interface::DeleteTextures) {} +}; + +template <GLenum Target> +class ScopedBinder { + public: + typedef void (gpu::gles2::GLES2Interface::*BindFunc)(GLenum target, + GLuint id); + ScopedBinder(gpu::gles2::GLES2Interface* gl, GLuint id, BindFunc bind_func) + : gl_(gl), bind_func_(bind_func) { + (gl_->*bind_func_)(Target, id); + } + + virtual ~ScopedBinder() { (gl_->*bind_func_)(Target, 0); } + + private: + gpu::gles2::GLES2Interface* gl_; + BindFunc bind_func_; + + DISALLOW_COPY_AND_ASSIGN(ScopedBinder); +}; + +template <GLenum Target> +class ScopedBufferBinder : ScopedBinder<Target> { + public: + ScopedBufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id) + : ScopedBinder<Target>(gl, id, &gpu::gles2::GLES2Interface::BindBuffer) {} +}; + +template <GLenum Target> +class ScopedFramebufferBinder : ScopedBinder<Target> { + public: + ScopedFramebufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id) + : ScopedBinder<Target>(gl, + id, + &gpu::gles2::GLES2Interface::BindFramebuffer) {} +}; + +template <GLenum Target> +class ScopedTextureBinder : ScopedBinder<Target> { + public: + ScopedTextureBinder(gpu::gles2::GLES2Interface* gl, GLuint id) + : ScopedBinder<Target>(gl, id, &gpu::gles2::GLES2Interface::BindTexture) { + } +}; + +class ReadbackYUVInterface; +class GLHelperReadbackSupport; + +// Provides higher level operations on top of the gpu::gles2::GLES2Interface +// interfaces. +class VIZ_COMMON_EXPORT GLHelper { + public: + GLHelper(gpu::gles2::GLES2Interface* gl, + gpu::ContextSupport* context_support); + ~GLHelper(); + + enum ScalerQuality { + // Bilinear single pass, fastest possible. + SCALER_QUALITY_FAST = 1, + + // Bilinear upscale + N * 50% bilinear downscales. + // This is still fast enough for most purposes and + // Image quality is nearly as good as the BEST option. + SCALER_QUALITY_GOOD = 2, + + // Bicubic upscale + N * 50% bicubic downscales. + // Produces very good quality scaled images, but it's + // 2-8x slower than the "GOOD" quality, so it's not always + // worth it. + SCALER_QUALITY_BEST = 3, + }; + + // Copies the block of pixels specified with |src_subrect| from |src_texture|, + // scales it to |dst_size|, and writes it into |out|. + // |src_size| is the size of |src_texture|. The result is in |out_color_type| + // format and is potentially flipped vertically to make it a correct image + // representation. |callback| is invoked with the copy result when the copy + // operation has completed. + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + void CropScaleReadbackAndCleanTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback<void(bool)>& callback, + GLHelper::ScalerQuality quality); + + // Copies the block of pixels specified with |src_subrect| from |src_mailbox|, + // scales it to |dst_size|, and writes it into |out|. + // |src_size| is the size of |src_mailbox|. The result is in |out_color_type| + // format and is potentially flipped vertically to make it a correct image + // representation. |callback| is invoked with the copy result when the copy + // operation has completed. + // Note that the texture bound to src_mailbox will have the min/mag filter set + // to GL_LINEAR and wrap_s/t set to CLAMP_TO_EDGE in this call. src_mailbox is + // assumed to be GL_TEXTURE_2D. + void CropScaleReadbackAndCleanMailbox( + const gpu::Mailbox& src_mailbox, + const gpu::SyncToken& sync_token, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback<void(bool)>& callback, + GLHelper::ScalerQuality quality); + + // Copies the texture data out of |texture| into |out|. |size| is the + // size of the texture. No post processing is applied to the pixels. The + // texture is assumed to have a format of GL_RGBA with a pixel type of + // GL_UNSIGNED_BYTE. This is a blocking call that calls glReadPixels on the + // current OpenGL context. + void ReadbackTextureSync(GLuint texture, + const gfx::Rect& src_rect, + unsigned char* out, + SkColorType format); + + void ReadbackTextureAsync(GLuint texture, + const gfx::Size& dst_size, + unsigned char* out, + SkColorType color_type, + const base::Callback<void(bool)>& callback); + + // Creates a copy of the specified texture. |size| is the size of the texture. + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + GLuint CopyTexture(GLuint texture, const gfx::Size& size); + + // Creates a scaled copy of the specified texture. |src_size| is the size of + // the texture and |dst_size| is the size of the resulting copy. + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + GLuint CopyAndScaleTexture(GLuint texture, + const gfx::Size& src_size, + const gfx::Size& dst_size, + bool vertically_flip_texture, + ScalerQuality quality); + + // Returns the shader compiled from the source. + GLuint CompileShaderFromSource(const GLchar* source, GLenum type); + + // Copies all pixels from |previous_texture| into |texture| that are + // inside the region covered by |old_damage| but not part of |new_damage|. + void CopySubBufferDamage(GLenum target, + GLuint texture, + GLuint previous_texture, + const SkRegion& new_damage, + const SkRegion& old_damage); + + // Simply creates a texture. + GLuint CreateTexture(); + // Deletes a texture. + void DeleteTexture(GLuint texture_id); + + // Inserts a fence sync, flushes, and generates a sync token. + void GenerateSyncToken(gpu::SyncToken* sync_token); + + // Wait for the sync token before executing further GL commands. + void WaitSyncToken(const gpu::SyncToken& sync_token); + + // Creates a mailbox holder that is attached to the given texture id, with a + // sync point to wait on before using the mailbox. Returns a holder with an + // empty mailbox on failure. + // Note the texture is assumed to be GL_TEXTURE_2D. + gpu::MailboxHolder ProduceMailboxHolderFromTexture(GLuint texture_id); + + // Creates a texture and consumes a mailbox into it. Returns 0 on failure. + // Note the mailbox is assumed to be GL_TEXTURE_2D. + GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token); + + // Resizes the texture's size to |size|. + void ResizeTexture(GLuint texture, const gfx::Size& size); + + // Copies the framebuffer data given in |rect| to |texture|. + void CopyTextureSubImage(GLuint texture, const gfx::Rect& rect); + + // Copies the all framebuffer data to |texture|. |size| specifies the + // size of the framebuffer. + void CopyTextureFullImage(GLuint texture, const gfx::Size& size); + + // Flushes GL commands. + void Flush(); + + // Force commands in the current command buffer to be executed before commands + // in other command buffers from the same process (ie channel to the GPU + // process). + void InsertOrderingBarrier(); + + // A scaler will cache all intermediate textures and programs + // needed to scale from a specified size to a destination size. + // If the source or destination sizes changes, you must create + // a new scaler. + class ScalerInterface { + public: + ScalerInterface() {} + virtual ~ScalerInterface() {} + + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + virtual void Scale(GLuint source_texture, GLuint dest_texture) = 0; + virtual const gfx::Size& SrcSize() = 0; + virtual const gfx::Rect& SrcSubrect() = 0; + virtual const gfx::Size& DstSize() = 0; + }; + + // Note that the quality may be adjusted down if texture + // allocations fail or hardware doesn't support the requtested + // quality. Note that ScalerQuality enum is arranged in + // numerical order for simplicity. + ScalerInterface* CreateScaler(ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle); + + // Create a readback pipeline that will scale a subsection of the source + // texture, then convert it to YUV422 planar form and then read back that. + // This reduces the amount of memory read from GPU to CPU memory by a factor + // 2.6, which can be quite handy since readbacks have very limited speed + // on some platforms. All values in |dst_size| must be a multiple of two. If + // |use_mrt| is true, the pipeline will try to optimize the YUV conversion + // using the multi-render-target extension. |use_mrt| should only be set to + // false for testing. + ReadbackYUVInterface* CreateReadbackPipelineYUV(ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + bool use_mrt); + + // Returns the maximum number of draw buffers available, + // 0 if GL_EXT_draw_buffers is not available. + GLint MaxDrawBuffers(); + + // Checks whether the readbback is supported for texture with the + // matching config. This doesnt check for cross format readbacks. + bool IsReadbackConfigSupported(SkColorType texture_format); + + protected: + class CopyTextureToImpl; + + // Creates |copy_texture_to_impl_| if NULL. + void InitCopyTextToImpl(); + // Creates |scaler_impl_| if NULL. + void InitScalerImpl(); + + enum ReadbackSwizzle { kSwizzleNone = 0, kSwizzleBGRA }; + + gpu::gles2::GLES2Interface* gl_; + gpu::ContextSupport* context_support_; + std::unique_ptr<CopyTextureToImpl> copy_texture_to_impl_; + std::unique_ptr<GLHelperScaling> scaler_impl_; + std::unique_ptr<GLHelperReadbackSupport> readback_support_; + + private: + DISALLOW_COPY_AND_ASSIGN(GLHelper); +}; + +// Similar to a ScalerInterface, a yuv readback pipeline will +// cache a scaler and all intermediate textures and frame buffers +// needed to scale, crop, letterbox and read back a texture from +// the GPU into CPU-accessible RAM. A single readback pipeline +// can handle multiple outstanding readbacks at the same time, but +// if the source or destination sizes change, you'll need to create +// a new readback pipeline. +class ReadbackYUVInterface { + public: + ReadbackYUVInterface() {} + virtual ~ReadbackYUVInterface() {} + + // Note that |target| must use YV12 format. |paste_location| specifies where + // the captured pixels that are read back will be placed in the video frame. + // The region defined by the |paste_location| and the |dst_size| specified in + // the call to CreateReadbackPipelineYUV() must be fully contained within + // |target->visible_rect()|. + virtual void ReadbackYUV(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const gfx::Rect& target_visible_rect, + int y_plane_row_stride_bytes, + unsigned char* y_plane_data, + int u_plane_row_stride_bytes, + unsigned char* u_plane_data, + int v_plane_row_stride_bytes, + unsigned char* v_plane_data, + const gfx::Point& paste_location, + const base::Callback<void(bool)>& callback) = 0; + virtual GLHelper::ScalerInterface* scaler() = 0; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_GL_HELPER_H_ diff --git a/chromium/components/viz/common/gl_helper_benchmark.cc b/chromium/components/viz/common/gl_helper_benchmark.cc new file mode 100644 index 00000000000..59c3a73e537 --- /dev/null +++ b/chromium/components/viz/common/gl_helper_benchmark.cc @@ -0,0 +1,252 @@ +// Copyright (c) 2012 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. + +// This file looks like a unit test, but it contains benchmarks and test +// utilities intended for manual evaluation of the scalers in +// gl_helper*. These tests produce output in the form of files and printouts, +// but cannot really "fail". There is no point in making these tests part +// of any test automation run. + +#include <stddef.h> +#include <stdio.h> +#include <cmath> +#include <string> +#include <vector> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES2/gl2extchromium.h> + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/strings/stringprintf.h" +#include "base/test/scoped_task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "components/viz/common/gl_helper.h" +#include "components/viz/common/gl_helper_scaling.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "gpu/command_buffer/client/shared_memory_limits.h" +#include "gpu/ipc/gl_in_process_context.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkTypes.h" +#include "ui/gfx/codec/png_codec.h" + +namespace viz { + +namespace { + +GLHelper::ScalerQuality kQualities[] = { + GLHelper::SCALER_QUALITY_BEST, GLHelper::SCALER_QUALITY_GOOD, + GLHelper::SCALER_QUALITY_FAST, +}; + +const char* const kQualityNames[] = { + "best", "good", "fast", +}; + +} // namespace + +class GLHelperBenchmark : public testing::Test { + protected: + void SetUp() override { + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = 8; + attributes.depth_size = 24; + attributes.red_size = 8; + attributes.green_size = 8; + attributes.blue_size = 8; + attributes.stencil_size = 8; + attributes.samples = 4; + attributes.sample_buffers = 1; + attributes.bind_generates_resource = false; + attributes.gpu_preference = gl::PreferDiscreteGpu; + + context_.reset( + gpu::GLInProcessContext::Create(nullptr, /* service */ + nullptr, /* surface */ + true, /* offscreen */ + gpu::kNullSurfaceHandle, /* window */ + nullptr, /* share_context */ + attributes, gpu::SharedMemoryLimits(), + nullptr, /* gpu_memory_buffer_manager */ + nullptr, /* image_factory */ + base::ThreadTaskRunnerHandle::Get())); + gl_ = context_->GetImplementation(); + gpu::ContextSupport* support = context_->GetImplementation(); + + helper_.reset(new GLHelper(gl_, support)); + helper_scaling_.reset(new GLHelperScaling(gl_, helper_.get())); + } + + void TearDown() override { + helper_scaling_.reset(NULL); + helper_.reset(NULL); + context_.reset(NULL); + } + + void LoadPngFileToSkBitmap(const base::FilePath& filename, SkBitmap* bitmap) { + std::string compressed; + base::ReadFileToString(base::MakeAbsoluteFilePath(filename), &compressed); + ASSERT_TRUE(compressed.size()); + ASSERT_TRUE(gfx::PNGCodec::Decode( + reinterpret_cast<const unsigned char*>(compressed.data()), + compressed.size(), bitmap)); + } + + // Save the image to a png file. Used to create the initial test files. + void SaveToFile(SkBitmap* bitmap, const base::FilePath& filename) { + std::vector<unsigned char> compressed; + ASSERT_TRUE(gfx::PNGCodec::Encode( + static_cast<unsigned char*>(bitmap->getPixels()), + gfx::PNGCodec::FORMAT_BGRA, + gfx::Size(bitmap->width(), bitmap->height()), + static_cast<int>(bitmap->rowBytes()), true, + std::vector<gfx::PNGCodec::Comment>(), &compressed)); + ASSERT_TRUE(compressed.size()); + FILE* f = base::OpenFile(filename, "wb"); + ASSERT_TRUE(f); + ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f), + compressed.size()); + base::CloseFile(f); + } + + base::test::ScopedTaskEnvironment scoped_task_environment_; + std::unique_ptr<gpu::GLInProcessContext> context_; + gpu::gles2::GLES2Interface* gl_; + std::unique_ptr<GLHelper> helper_; + std::unique_ptr<GLHelperScaling> helper_scaling_; + std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; +}; + +TEST_F(GLHelperBenchmark, ScaleBenchmark) { + int output_sizes[] = {1920, 1080, 1249, 720, // Output size on pixel + 256, 144}; + int input_sizes[] = {3200, 2040, 2560, 1476, // Pixel tab size + 1920, 1080, 1280, 720, 800, 480, 256, 144}; + + for (size_t q = 0; q < arraysize(kQualities); q++) { + for (size_t outsize = 0; outsize < arraysize(output_sizes); outsize += 2) { + for (size_t insize = 0; insize < arraysize(input_sizes); insize += 2) { + uint32_t src_texture; + gl_->GenTextures(1, &src_texture); + uint32_t dst_texture; + gl_->GenTextures(1, &dst_texture); + uint32_t framebuffer; + gl_->GenFramebuffers(1, &framebuffer); + const gfx::Size src_size(input_sizes[insize], input_sizes[insize + 1]); + const gfx::Size dst_size(output_sizes[outsize], + output_sizes[outsize + 1]); + SkBitmap input; + input.allocN32Pixels(src_size.width(), src_size.height()); + + SkBitmap output_pixels; + output_pixels.allocN32Pixels(dst_size.width(), dst_size.height()); + + gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); + gl_->BindTexture(GL_TEXTURE_2D, dst_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dst_size.width(), + dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + gl_->BindTexture(GL_TEXTURE_2D, src_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(), + src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, + input.getPixels()); + + gfx::Rect src_subrect(0, 0, src_size.width(), src_size.height()); + std::unique_ptr<GLHelper::ScalerInterface> scaler(helper_->CreateScaler( + kQualities[q], src_size, src_subrect, dst_size, false, false)); + // Scale once beforehand before we start measuring. + scaler->Scale(src_texture, dst_texture); + gl_->Finish(); + + base::TimeTicks start_time = base::TimeTicks::Now(); + int iterations = 0; + base::TimeTicks end_time; + while (true) { + for (int i = 0; i < 50; i++) { + iterations++; + scaler->Scale(src_texture, dst_texture); + gl_->Flush(); + } + gl_->Finish(); + end_time = base::TimeTicks::Now(); + if (iterations > 2000) { + break; + } + if ((end_time - start_time).InMillisecondsF() > 1000) { + break; + } + } + gl_->DeleteTextures(1, &dst_texture); + gl_->DeleteTextures(1, &src_texture); + gl_->DeleteFramebuffers(1, &framebuffer); + + std::string name; + name = base::StringPrintf("scale_%dx%d_to_%dx%d_%s", src_size.width(), + src_size.height(), dst_size.width(), + dst_size.height(), kQualityNames[q]); + + float ms = (end_time - start_time).InMillisecondsF() / iterations; + VLOG(0) << base::StringPrintf("*RESULT gpu_scale_time: %s=%.2f ms\n", + name.c_str(), ms); + } + } + } +} + +// This is more of a test utility than a test. +// Put an PNG image called "testimage.png" in your +// current directory, then run this test. It will +// create testoutput_Q_P.png, where Q is the scaling +// mode and P is the scaling percentage taken from +// the table below. +TEST_F(GLHelperBenchmark, DISABLED_ScaleTestImage) { + int percents[] = { + 230, 180, 150, 110, 90, 70, 50, 49, 40, 20, 10, + }; + + SkBitmap input; + LoadPngFileToSkBitmap(base::FilePath(FILE_PATH_LITERAL("testimage.png")), + &input); + + uint32_t framebuffer; + gl_->GenFramebuffers(1, &framebuffer); + uint32_t src_texture; + gl_->GenTextures(1, &src_texture); + const gfx::Size src_size(input.width(), input.height()); + gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); + gl_->BindTexture(GL_TEXTURE_2D, src_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(), + src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, + input.getPixels()); + + for (size_t q = 0; q < arraysize(kQualities); q++) { + for (size_t p = 0; p < arraysize(percents); p++) { + const gfx::Size dst_size(input.width() * percents[p] / 100, + input.height() * percents[p] / 100); + uint32_t dst_texture = helper_->CopyAndScaleTexture( + src_texture, src_size, dst_size, false, kQualities[q]); + + SkBitmap output_pixels; + output_pixels.allocN32Pixels(dst_size.width(), dst_size.height()); + + helper_->ReadbackTextureSync( + dst_texture, gfx::Rect(0, 0, dst_size.width(), dst_size.height()), + static_cast<unsigned char*>(output_pixels.getPixels()), + kN32_SkColorType); + gl_->DeleteTextures(1, &dst_texture); + std::string filename = base::StringPrintf("testoutput_%s_%d.ppm", + kQualityNames[q], percents[p]); + VLOG(0) << "Writing " << filename; + SaveToFile(&output_pixels, base::FilePath::FromUTF8Unsafe(filename)); + } + } + gl_->DeleteTextures(1, &src_texture); + gl_->DeleteFramebuffers(1, &framebuffer); +} + +} // namespace viz diff --git a/chromium/components/viz/common/gl_helper_readback_support.cc b/chromium/components/viz/common/gl_helper_readback_support.cc new file mode 100644 index 00000000000..6c8161937d6 --- /dev/null +++ b/chromium/components/viz/common/gl_helper_readback_support.cc @@ -0,0 +1,172 @@ +// Copyright 2014 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 "components/viz/common/gl_helper_readback_support.h" +#include "base/logging.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "third_party/skia/include/core/SkImageInfo.h" + +namespace viz { + +GLHelperReadbackSupport::GLHelperReadbackSupport(gpu::gles2::GLES2Interface* gl) + : gl_(gl) { + InitializeReadbackSupport(); +} + +GLHelperReadbackSupport::~GLHelperReadbackSupport() {} + +void GLHelperReadbackSupport::InitializeReadbackSupport() { + // We are concerned about 16, 32-bit formats only. The below are the most + // used 16, 32-bit formats. In future if any new format support is needed + // that should be added here. Initialize the array with + // GLHelperReadbackSupport::NOT_SUPPORTED as we dont know the supported + // formats yet. + for (int i = 0; i <= kLastEnum_SkColorType; ++i) { + format_support_table_[i] = GLHelperReadbackSupport::NOT_SUPPORTED; + } + // TODO(sikugu): kAlpha_8_SkColorType support check is failing on mesa. + // See crbug.com/415667. + CheckForReadbackSupport(kRGB_565_SkColorType); + CheckForReadbackSupport(kARGB_4444_SkColorType); + CheckForReadbackSupport(kRGBA_8888_SkColorType); + CheckForReadbackSupport(kBGRA_8888_SkColorType); + // Further any formats, support should be checked here. +} + +void GLHelperReadbackSupport::CheckForReadbackSupport( + SkColorType texture_format) { + bool supports_format = false; + switch (texture_format) { + case kRGB_565_SkColorType: + supports_format = SupportsFormat(GL_RGB, GL_UNSIGNED_SHORT_5_6_5); + break; + case kRGBA_8888_SkColorType: + // This is the baseline, assume always true. + supports_format = true; + break; + case kBGRA_8888_SkColorType: + supports_format = SupportsFormat(GL_BGRA_EXT, GL_UNSIGNED_BYTE); + break; + case kARGB_4444_SkColorType: + supports_format = false; + break; + default: + NOTREACHED(); + supports_format = false; + break; + } + DCHECK((int)texture_format <= (int)kLastEnum_SkColorType); + format_support_table_[texture_format] = + supports_format ? GLHelperReadbackSupport::SUPPORTED + : GLHelperReadbackSupport::NOT_SUPPORTED; +} + +void GLHelperReadbackSupport::GetAdditionalFormat(GLenum format, + GLenum type, + GLenum* format_out, + GLenum* type_out) { + for (unsigned int i = 0; i < format_cache_.size(); i++) { + if (format_cache_[i].format == format && format_cache_[i].type == type) { + *format_out = format_cache_[i].read_format; + *type_out = format_cache_[i].read_type; + return; + } + } + + const int kTestSize = 64; + ScopedTexture dst_texture(gl_); + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_->TexImage2D(GL_TEXTURE_2D, 0, format, kTestSize, kTestSize, 0, format, + type, NULL); + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, + dst_framebuffer); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + dst_texture, 0); + GLint format_tmp = 0, type_tmp = 0; + gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &format_tmp); + gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type_tmp); + *format_out = format_tmp; + *type_out = type_tmp; + + struct FormatCacheEntry entry = {format, type, *format_out, *type_out}; + format_cache_.push_back(entry); +} + +bool GLHelperReadbackSupport::SupportsFormat(GLenum format, GLenum type) { + // GLES2.0 Specification says this pairing is always supported + // with additional format from GL_IMPLEMENTATION_COLOR_READ_FORMAT/TYPE + if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) + return true; + + bool supports_format = false; + GLenum ext_format = 0, ext_type = 0; + GetAdditionalFormat(format, type, &ext_format, &ext_type); + if ((ext_format == format) && (ext_type == type)) { + supports_format = true; + } + return supports_format; +} + +GLHelperReadbackSupport::FormatSupport +GLHelperReadbackSupport::GetReadbackConfig(SkColorType color_type, + bool can_swizzle, + GLenum* format, + GLenum* type, + size_t* bytes_per_pixel) { + DCHECK(format && type && bytes_per_pixel); + *bytes_per_pixel = 4; + *type = GL_UNSIGNED_BYTE; + GLenum new_format = 0, new_type = 0; + switch (color_type) { + case kRGB_565_SkColorType: + if (format_support_table_[color_type] == + GLHelperReadbackSupport::SUPPORTED) { + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + *bytes_per_pixel = 2; + return GLHelperReadbackSupport::SUPPORTED; + } + break; + case kRGBA_8888_SkColorType: + *format = GL_RGBA; + if (can_swizzle) { + // If GL_BGRA_EXT is advertised as the readback format through + // GL_IMPLEMENTATION_COLOR_READ_FORMAT then assume it is preferred by + // the implementation for performance. + GetAdditionalFormat(*format, *type, &new_format, &new_type); + + if (new_format == GL_BGRA_EXT && new_type == GL_UNSIGNED_BYTE) { + *format = GL_BGRA_EXT; + return GLHelperReadbackSupport::SWIZZLE; + } + } + return GLHelperReadbackSupport::SUPPORTED; + case kBGRA_8888_SkColorType: + *format = GL_BGRA_EXT; + if (format_support_table_[color_type] == + GLHelperReadbackSupport::SUPPORTED) + return GLHelperReadbackSupport::SUPPORTED; + + if (can_swizzle) { + *format = GL_RGBA; + return GLHelperReadbackSupport::SWIZZLE; + } + + break; + case kARGB_4444_SkColorType: + return GLHelperReadbackSupport::NOT_SUPPORTED; + default: + NOTREACHED(); + break; + } + + return GLHelperReadbackSupport::NOT_SUPPORTED; +} + +} // namespace viz diff --git a/chromium/components/viz/common/gl_helper_readback_support.h b/chromium/components/viz/common/gl_helper_readback_support.h new file mode 100644 index 00000000000..0bb76d59517 --- /dev/null +++ b/chromium/components/viz/common/gl_helper_readback_support.h @@ -0,0 +1,75 @@ +// Copyright 2014 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 COMPONENTS_VIZ_COMMON_GL_HELPER_READBACK_SUPPORT_H_ +#define COMPONENTS_VIZ_COMMON_GL_HELPER_READBACK_SUPPORT_H_ + +#include <stddef.h> + +#include <vector> + +#include "components/viz/common/gl_helper.h" + +namespace viz { + +class GLHelperReadbackSupport { + public: + enum FormatSupport { SUPPORTED, SWIZZLE, NOT_SUPPORTED }; + + explicit GLHelperReadbackSupport(gpu::gles2::GLES2Interface* gl); + + ~GLHelperReadbackSupport(); + + // For a given color type retrieve whether readback is supported and if so + // how it should be performed. The |format|, |type| and |bytes_per_pixel| are + // the values that should be used with glReadPixels to facilitate the + // readback. If |can_swizzle| is true then this method will return SWIZZLE if + // the data needs to be swizzled before using the returned |format| otherwise + // the method will return SUPPORTED to indicate that readback is permitted of + // this color othewise NOT_SUPPORTED will be returned. This method always + // overwrites the out values irrespective of the return value. + FormatSupport GetReadbackConfig(SkColorType color_type, + bool can_swizzle, + GLenum* format, + GLenum* type, + size_t* bytes_per_pixel); + // Provides the additional readback format/type pairing for a render target + // of a given format/type pairing + void GetAdditionalFormat(GLenum format, + GLenum type, + GLenum* format_out, + GLenum* type_out); + + private: + struct FormatCacheEntry { + GLenum format; + GLenum type; + GLenum read_format; + GLenum read_type; + }; + + // This populates the format_support_table with the list of supported + // formats. + void InitializeReadbackSupport(); + + // This api is called once per format and it is done in the + // InitializeReadbackSupport. We should not use this any where + // except the InitializeReadbackSupport.Calling this at other places + // can distrub the state of normal gl operations. + void CheckForReadbackSupport(SkColorType texture_format); + + // Helper functions for checking the supported texture formats. + // Avoid using this API in between texture operations, as this does some + // teture opertions (bind, attach) internally. + bool SupportsFormat(GLenum format, GLenum type); + + FormatSupport format_support_table_[kLastEnum_SkColorType + 1]; + + gpu::gles2::GLES2Interface* gl_; + std::vector<struct FormatCacheEntry> format_cache_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_GL_HELPER_READBACK_SUPPORT_H_ diff --git a/chromium/components/viz/common/gl_helper_scaling.cc b/chromium/components/viz/common/gl_helper_scaling.cc new file mode 100644 index 00000000000..e394202df35 --- /dev/null +++ b/chromium/components/viz/common/gl_helper_scaling.cc @@ -0,0 +1,882 @@ +// Copyright (c) 2012 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 "components/viz/common/gl_helper_scaling.h" + +#include <stddef.h> + +#include <deque> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "gpu/command_buffer/client/gles2_interface.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +using gpu::gles2::GLES2Interface; + +namespace viz { + +GLHelperScaling::GLHelperScaling(GLES2Interface* gl, GLHelper* helper) + : gl_(gl), helper_(helper), vertex_attributes_buffer_(gl_) { + InitBuffer(); +} + +GLHelperScaling::~GLHelperScaling() {} + +// Used to keep track of a generated shader program. The program +// is passed in as text through Setup and is used by calling +// UseProgram() with the right parameters. Note that |gl_| +// and |helper_| are assumed to live longer than this program. +class ShaderProgram : public base::RefCounted<ShaderProgram> { + public: + ShaderProgram(GLES2Interface* gl, GLHelper* helper) + : gl_(gl), + helper_(helper), + program_(gl_->CreateProgram()), + position_location_(-1), + texcoord_location_(-1), + src_subrect_location_(-1), + src_pixelsize_location_(-1), + dst_pixelsize_location_(-1), + scaling_vector_location_(-1), + color_weights_location_(-1) {} + + // Compile shader program. + void Setup(const GLchar* vertex_shader_text, + const GLchar* fragment_shader_text); + + // UseProgram must be called with GL_TEXTURE_2D bound to the + // source texture and GL_ARRAY_BUFFER bound to a vertex + // attribute buffer. + void UseProgram(const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool scale_x, + bool flip_y, + GLfloat color_weights[4]); + + bool Initialized() const { return position_location_ != -1; } + + private: + friend class base::RefCounted<ShaderProgram>; + ~ShaderProgram() { gl_->DeleteProgram(program_); } + + GLES2Interface* gl_; + GLHelper* helper_; + + // A program for copying a source texture into a destination texture. + GLuint program_; + + // The location of the position in the program. + GLint position_location_; + // The location of the texture coordinate in the program. + GLint texcoord_location_; + // The location of the source texture in the program. + GLint texture_location_; + // The location of the texture coordinate of + // the sub-rectangle in the program. + GLint src_subrect_location_; + // Location of size of source image in pixels. + GLint src_pixelsize_location_; + // Location of size of destination image in pixels. + GLint dst_pixelsize_location_; + // Location of vector for scaling direction. + GLint scaling_vector_location_; + // Location of color weights. + GLint color_weights_location_; + + DISALLOW_COPY_AND_ASSIGN(ShaderProgram); +}; + +// Implementation of a single stage in a scaler pipeline. If the pipeline has +// multiple stages, it calls Scale() on the subscaler, then further scales the +// output. Caches textures and framebuffers to avoid allocating/deleting +// them once per frame, which can be expensive on some drivers. +class ScalerImpl : public GLHelper::ScalerInterface, + public GLHelperScaling::ShaderInterface { + public: + // |gl| and |copy_impl| are expected to live longer than this object. + // |src_size| is the size of the input texture in pixels. + // |dst_size| is the size of the output texutre in pixels. + // |src_subrect| is the portion of the src to copy to the output texture. + // If |scale_x| is true, we are scaling along the X axis, otherwise Y. + // If we are scaling in both X and Y, |scale_x| is ignored. + // If |vertically_flip_texture| is true, output will be upside-down. + // If |swizzle| is true, RGBA will be transformed into BGRA. + // |color_weights| are only used together with SHADER_PLANAR to specify + // how to convert RGB colors into a single value. + ScalerImpl(GLES2Interface* gl, + GLHelperScaling* scaler_helper, + const GLHelperScaling::ScalerStage& scaler_stage, + ScalerImpl* subscaler, + const float* color_weights) + : gl_(gl), + scaler_helper_(scaler_helper), + spec_(scaler_stage), + intermediate_texture_(0), + dst_framebuffer_(gl), + subscaler_(subscaler) { + if (color_weights) { + color_weights_[0] = color_weights[0]; + color_weights_[1] = color_weights[1]; + color_weights_[2] = color_weights[2]; + color_weights_[3] = color_weights[3]; + } else { + color_weights_[0] = 0.0; + color_weights_[1] = 0.0; + color_weights_[2] = 0.0; + color_weights_[3] = 0.0; + } + shader_program_ = + scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle); + + if (subscaler_) { + intermediate_texture_ = 0u; + gl_->GenTextures(1, &intermediate_texture_); + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, + intermediate_texture_); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spec_.src_size.width(), + spec_.src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, + NULL); + } + } + + ~ScalerImpl() override { + if (intermediate_texture_) { + gl_->DeleteTextures(1, &intermediate_texture_); + } + } + + // GLHelperShader::ShaderInterface implementation. + void Execute(GLuint source_texture, + const std::vector<GLuint>& dest_textures) override { + if (subscaler_) { + subscaler_->Scale(source_texture, intermediate_texture_); + source_texture = intermediate_texture_; + } + + ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder( + gl_, dst_framebuffer_); + DCHECK_GT(dest_textures.size(), 0U); + auto num_dest_textures = static_cast<GLsizei>(dest_textures.size()); + auto buffers = base::MakeUnique<GLenum[]>(num_dest_textures); + for (GLsizei t = 0; t < num_dest_textures; t++) { + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dest_textures[t]); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + t, + GL_TEXTURE_2D, dest_textures[t], 0); + buffers[t] = GL_COLOR_ATTACHMENT0 + t; + } + ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, source_texture); + + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder( + gl_, scaler_helper_->vertex_attributes_buffer_); + shader_program_->UseProgram(spec_.src_size, spec_.src_subrect, + spec_.dst_size, spec_.scale_x, + spec_.vertically_flip_texture, color_weights_); + gl_->Viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height()); + + if (num_dest_textures > 1) { + DCHECK_LE(num_dest_textures, scaler_helper_->helper_->MaxDrawBuffers()); + gl_->DrawBuffersEXT(num_dest_textures, buffers.get()); + } + // Conduct texture mapping by drawing a quad composed of two triangles. + gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); + if (dest_textures.size() > 1) { + // Set the draw buffers back to not confuse others. + gl_->DrawBuffersEXT(1, &buffers[0]); + } + } + + // GLHelper::ScalerInterface implementation. + void Scale(GLuint source_texture, GLuint dest_texture) override { + std::vector<GLuint> tmp(1); + tmp[0] = dest_texture; + Execute(source_texture, tmp); + } + + const gfx::Size& SrcSize() override { + if (subscaler_) { + return subscaler_->SrcSize(); + } + return spec_.src_size; + } + const gfx::Rect& SrcSubrect() override { + if (subscaler_) { + return subscaler_->SrcSubrect(); + } + return spec_.src_subrect; + } + const gfx::Size& DstSize() override { return spec_.dst_size; } + + private: + GLES2Interface* gl_; + GLHelperScaling* scaler_helper_; + GLHelperScaling::ScalerStage spec_; + GLfloat color_weights_[4]; + GLuint intermediate_texture_; + scoped_refptr<ShaderProgram> shader_program_; + ScopedFramebuffer dst_framebuffer_; + std::unique_ptr<ScalerImpl> subscaler_; +}; + +GLHelperScaling::ScalerStage::ScalerStage(ShaderType shader_, + gfx::Size src_size_, + gfx::Rect src_subrect_, + gfx::Size dst_size_, + bool scale_x_, + bool vertically_flip_texture_, + bool swizzle_) + : shader(shader_), + src_size(src_size_), + src_subrect(src_subrect_), + dst_size(dst_size_), + scale_x(scale_x_), + vertically_flip_texture(vertically_flip_texture_), + swizzle(swizzle_) {} + +GLHelperScaling::ScalerStage::ScalerStage(const ScalerStage& other) = default; + +// The important inputs for this function is |x_ops| and +// |y_ops|. They represent scaling operations to be done +// on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST, +// then we will interpret these scale operations literally and we'll +// create one scaler stage for each ScaleOp. However, if |quality| +// is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations +// by combining two or more ScaleOps in to a single scaler stage. +// Normally we process ScaleOps from |y_ops| first and |x_ops| after +// all |y_ops| are processed, but sometimes we can combine one or more +// operation from both queues essentially for free. This is the reason +// why |x_ops| and |y_ops| aren't just one single queue. +void GLHelperScaling::ConvertScalerOpsToScalerStages( + GLHelper::ScalerQuality quality, + gfx::Size src_size, + gfx::Rect src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + std::deque<GLHelperScaling::ScaleOp>* x_ops, + std::deque<GLHelperScaling::ScaleOp>* y_ops, + std::vector<ScalerStage>* scaler_stages) { + while (!x_ops->empty() || !y_ops->empty()) { + gfx::Size intermediate_size = src_subrect.size(); + std::deque<ScaleOp>* current_queue = NULL; + + if (!y_ops->empty()) { + current_queue = y_ops; + } else { + current_queue = x_ops; + } + + ShaderType current_shader = SHADER_BILINEAR; + switch (current_queue->front().scale_factor) { + case 0: + if (quality == GLHelper::SCALER_QUALITY_BEST) { + current_shader = SHADER_BICUBIC_UPSCALE; + } + break; + case 2: + if (quality == GLHelper::SCALER_QUALITY_BEST) { + current_shader = SHADER_BICUBIC_HALF_1D; + } + break; + case 3: + DCHECK(quality != GLHelper::SCALER_QUALITY_BEST); + current_shader = SHADER_BILINEAR3; + break; + default: + NOTREACHED(); + } + bool scale_x = current_queue->front().scale_x; + current_queue->front().UpdateSize(&intermediate_size); + current_queue->pop_front(); + + // Optimization: Sometimes we can combine 2-4 scaling operations into + // one operation. + if (quality == GLHelper::SCALER_QUALITY_GOOD) { + if (!current_queue->empty() && current_shader == SHADER_BILINEAR) { + // Combine two steps in the same dimension. + current_queue->front().UpdateSize(&intermediate_size); + current_queue->pop_front(); + current_shader = SHADER_BILINEAR2; + if (!current_queue->empty()) { + // Combine three steps in the same dimension. + current_queue->front().UpdateSize(&intermediate_size); + current_queue->pop_front(); + current_shader = SHADER_BILINEAR4; + } + } + // Check if we can combine some steps in the other dimension as well. + // Since all shaders currently use GL_LINEAR, we can easily scale up + // or scale down by exactly 2x at the same time as we do another + // operation. Currently, the following mergers are supported: + // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down) + // * 2 bilinear Y-passes with 2 bilinear X-passes + // * 1 bilinear Y-pass with N bilinear X-pass + // * N bilinear Y-passes with 1 bilinear X-pass (down only) + // Measurements indicate that generalizing this for 3x3 and 4x4 + // makes it slower on some platforms, such as the Pixel. + if (!scale_x && x_ops->size() > 0 && x_ops->front().scale_factor <= 2) { + int x_passes = 0; + if (current_shader == SHADER_BILINEAR2 && x_ops->size() >= 2) { + // 2y + 2x passes + x_passes = 2; + current_shader = SHADER_BILINEAR2X2; + } else if (current_shader == SHADER_BILINEAR) { + // 1y + Nx passes + scale_x = true; + switch (x_ops->size()) { + case 0: + NOTREACHED(); + case 1: + if (x_ops->front().scale_factor == 3) { + current_shader = SHADER_BILINEAR3; + } + x_passes = 1; + break; + case 2: + x_passes = 2; + current_shader = SHADER_BILINEAR2; + break; + default: + x_passes = 3; + current_shader = SHADER_BILINEAR4; + break; + } + } else if (x_ops->front().scale_factor == 2) { + // Ny + 1x-downscale + x_passes = 1; + } + + for (int i = 0; i < x_passes; i++) { + x_ops->front().UpdateSize(&intermediate_size); + x_ops->pop_front(); + } + } + } + + scaler_stages->push_back(ScalerStage(current_shader, src_size, src_subrect, + intermediate_size, scale_x, + vertically_flip_texture, swizzle)); + src_size = intermediate_size; + src_subrect = gfx::Rect(intermediate_size); + vertically_flip_texture = false; + swizzle = false; + } +} + +void GLHelperScaling::ComputeScalerStages( + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + std::vector<ScalerStage>* scaler_stages) { + if (quality == GLHelper::SCALER_QUALITY_FAST || + src_subrect.size() == dst_size) { + scaler_stages->push_back(ScalerStage(SHADER_BILINEAR, src_size, src_subrect, + dst_size, false, + vertically_flip_texture, swizzle)); + return; + } + + std::deque<GLHelperScaling::ScaleOp> x_ops, y_ops; + GLHelperScaling::ScaleOp::AddOps(src_subrect.width(), dst_size.width(), true, + quality == GLHelper::SCALER_QUALITY_GOOD, + &x_ops); + GLHelperScaling::ScaleOp::AddOps( + src_subrect.height(), dst_size.height(), false, + quality == GLHelper::SCALER_QUALITY_GOOD, &y_ops); + + ConvertScalerOpsToScalerStages(quality, src_size, src_subrect, dst_size, + vertically_flip_texture, swizzle, &x_ops, + &y_ops, scaler_stages); +} + +GLHelper::ScalerInterface* GLHelperScaling::CreateScaler( + GLHelper::ScalerQuality quality, + gfx::Size src_size, + gfx::Rect src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle) { + std::vector<ScalerStage> scaler_stages; + ComputeScalerStages(quality, src_size, src_subrect, dst_size, + vertically_flip_texture, swizzle, &scaler_stages); + + ScalerImpl* ret = NULL; + for (unsigned int i = 0; i < scaler_stages.size(); i++) { + ret = new ScalerImpl(gl_, this, scaler_stages[i], ret, NULL); + } + return ret; +} + +GLHelper::ScalerInterface* GLHelperScaling::CreatePlanarScaler( + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + const float color_weights[4]) { + ScalerStage stage(SHADER_PLANAR, src_size, src_subrect, dst_size, true, + vertically_flip_texture, swizzle); + return new ScalerImpl(gl_, this, stage, NULL, color_weights); +} + +GLHelperScaling::ShaderInterface* GLHelperScaling::CreateYuvMrtShader( + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + ShaderType shader) { + DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2); + ScalerStage stage(shader, src_size, src_subrect, dst_size, true, + vertically_flip_texture, swizzle); + return new ScalerImpl(gl_, this, stage, NULL, NULL); +} + +const GLfloat GLHelperScaling::kVertexAttributes[] = { + -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0 + 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1 + -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2 + 1.0f, 1.0f, 1.0f, 1.0f, +}; // vertex 3 + +void GLHelperScaling::InitBuffer() { + ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(gl_, + vertex_attributes_buffer_); + gl_->BufferData(GL_ARRAY_BUFFER, sizeof(kVertexAttributes), kVertexAttributes, + GL_STATIC_DRAW); +} + +scoped_refptr<ShaderProgram> GLHelperScaling::GetShaderProgram(ShaderType type, + bool swizzle) { + ShaderProgramKeyType key(type, swizzle); + scoped_refptr<ShaderProgram>& cache_entry(shader_programs_[key]); + if (!cache_entry.get()) { + cache_entry = new ShaderProgram(gl_, helper_); + std::basic_string<GLchar> vertex_program; + std::basic_string<GLchar> fragment_program; + std::basic_string<GLchar> vertex_header; + std::basic_string<GLchar> fragment_directives; + std::basic_string<GLchar> fragment_header; + std::basic_string<GLchar> shared_variables; + + vertex_header.append( + "precision highp float;\n" + "attribute vec2 a_position;\n" + "attribute vec2 a_texcoord;\n" + "uniform vec4 src_subrect;\n"); + + fragment_header.append( + "precision mediump float;\n" + "uniform sampler2D s_texture;\n"); + + vertex_program.append( + " gl_Position = vec4(a_position, 0.0, 1.0);\n" + " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n"); + + switch (type) { + case SHADER_BILINEAR: + shared_variables.append("varying vec2 v_texcoord;\n"); + vertex_program.append(" v_texcoord = texcoord;\n"); + fragment_program.append( + " gl_FragColor = texture2D(s_texture, v_texcoord);\n"); + break; + + case SHADER_BILINEAR2: + // This is equivialent to two passes of the BILINEAR shader above. + // It can be used to scale an image down 1.0x-2.0x in either dimension, + // or exactly 4x. + shared_variables.append( + "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 4.0;\n" + " v_texcoords.xy = texcoord + step;\n" + " v_texcoords.zw = texcoord - step;\n"); + + fragment_program.append( + " gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n" + " texture2D(s_texture, v_texcoords.zw)) / 2.0;\n"); + break; + + case SHADER_BILINEAR3: + // This is kind of like doing 1.5 passes of the BILINEAR shader. + // It can be used to scale an image down 1.5x-3.0x, or exactly 6x. + shared_variables.append( + "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad + "varying vec2 v_texcoords2;\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 3.0;\n" + " v_texcoords1.xy = texcoord + step;\n" + " v_texcoords1.zw = texcoord;\n" + " v_texcoords2 = texcoord - step;\n"); + fragment_program.append( + " gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n" + " texture2D(s_texture, v_texcoords1.zw) +\n" + " texture2D(s_texture, v_texcoords2)) / 3.0;\n"); + break; + + case SHADER_BILINEAR4: + // This is equivialent to three passes of the BILINEAR shader above, + // It can be used to scale an image down 2.0x-4.0x or exactly 8x. + shared_variables.append("varying vec4 v_texcoords[2];\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 8.0;\n" + " v_texcoords[0].xy = texcoord - step * 3.0;\n" + " v_texcoords[0].zw = texcoord - step;\n" + " v_texcoords[1].xy = texcoord + step;\n" + " v_texcoords[1].zw = texcoord + step * 3.0;\n"); + fragment_program.append( + " gl_FragColor = (\n" + " texture2D(s_texture, v_texcoords[0].xy) +\n" + " texture2D(s_texture, v_texcoords[0].zw) +\n" + " texture2D(s_texture, v_texcoords[1].xy) +\n" + " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); + break; + + case SHADER_BILINEAR2X2: + // This is equivialent to four passes of the BILINEAR shader above. + // Two in each dimension. It can be used to scale an image down + // 1.0x-2.0x in both X and Y directions. Or, it could be used to + // scale an image down by exactly 4x in both dimensions. + shared_variables.append("varying vec4 v_texcoords[2];\n"); + vertex_header.append("uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n" + " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n" + " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n" + " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n" + " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n"); + fragment_program.append( + " gl_FragColor = (\n" + " texture2D(s_texture, v_texcoords[0].xy) +\n" + " texture2D(s_texture, v_texcoords[0].zw) +\n" + " texture2D(s_texture, v_texcoords[1].xy) +\n" + " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); + break; + + case SHADER_BICUBIC_HALF_1D: + // This scales down texture by exactly half in one dimension. + // directions in one pass. We use bilinear lookup to reduce + // the number of texture reads from 8 to 4 + shared_variables.append( + "const float CenterDist = 99.0 / 140.0;\n" + "const float LobeDist = 11.0 / 4.0;\n" + "const float CenterWeight = 35.0 / 64.0;\n" + "const float LobeWeight = -3.0 / 64.0;\n" + "varying vec4 v_texcoords[2];\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 src_pixelsize;\n"); + vertex_program.append( + " vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n" + " v_texcoords[0].xy = texcoord - LobeDist * step;\n" + " v_texcoords[0].zw = texcoord - CenterDist * step;\n" + " v_texcoords[1].xy = texcoord + CenterDist * step;\n" + " v_texcoords[1].zw = texcoord + LobeDist * step;\n"); + fragment_program.append( + " gl_FragColor = \n" + // Lobe pixels + " (texture2D(s_texture, v_texcoords[0].xy) +\n" + " texture2D(s_texture, v_texcoords[1].zw)) *\n" + " LobeWeight +\n" + // Center pixels + " (texture2D(s_texture, v_texcoords[0].zw) +\n" + " texture2D(s_texture, v_texcoords[1].xy)) *\n" + " CenterWeight;\n"); + break; + + case SHADER_BICUBIC_UPSCALE: + // When scaling up, we need 4 texture reads, but we can + // save some instructions because will know in which range of + // the bicubic function each call call to the bicubic function + // will be in. + // Also, when sampling the bicubic function like this, the sum + // is always exactly one, so we can skip normalization as well. + shared_variables.append("varying vec2 v_texcoord;\n"); + vertex_program.append(" v_texcoord = texcoord;\n"); + fragment_header.append( + "uniform vec2 src_pixelsize;\n" + "uniform vec2 scaling_vector;\n" + "const float a = -0.5;\n" + // This function is equivialent to calling the bicubic + // function with x-1, x, 1-x and 2-x + // (assuming 0 <= x < 1) + "vec4 filt4(float x) {\n" + " return vec4(x * x * x, x * x, x, 1) *\n" + " mat4( a, -2.0 * a, a, 0.0,\n" + " a + 2.0, -a - 3.0, 0.0, 1.0,\n" + " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n" + " -a, a, 0.0, 0.0);\n" + "}\n" + "mat4 pixels_x(vec2 pos, vec2 step) {\n" + " return mat4(\n" + " texture2D(s_texture, pos - step),\n" + " texture2D(s_texture, pos),\n" + " texture2D(s_texture, pos + step),\n" + " texture2D(s_texture, pos + step * 2.0));\n" + "}\n"); + fragment_program.append( + " vec2 pixel_pos = v_texcoord * src_pixelsize - \n" + " scaling_vector / 2.0;\n" + " float frac = fract(dot(pixel_pos, scaling_vector));\n" + " vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n" + " vec2 step = scaling_vector / src_pixelsize;\n" + " gl_FragColor = pixels_x(base, step) * filt4(frac);\n"); + break; + + case SHADER_PLANAR: + // Converts four RGBA pixels into one pixel. Each RGBA + // pixel will be dot-multiplied with the color weights and + // then placed into a component of the output. This is used to + // convert RGBA textures into Y, U and V textures. We do this + // because single-component textures are not renderable on all + // architectures. + shared_variables.append("varying vec4 v_texcoords[2];\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 4.0;\n" + " v_texcoords[0].xy = texcoord - step * 1.5;\n" + " v_texcoords[0].zw = texcoord - step * 0.5;\n" + " v_texcoords[1].xy = texcoord + step * 0.5;\n" + " v_texcoords[1].zw = texcoord + step * 1.5;\n"); + fragment_header.append("uniform vec4 color_weights;\n"); + fragment_program.append( + " gl_FragColor = color_weights * mat4(\n" + " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n"); + break; + + case SHADER_YUV_MRT_PASS1: + // RGB24 to YV12 in two passes; writing two 8888 targets each pass. + // + // YV12 is full-resolution luma and half-resolution blue/red chroma. + // + // (original) + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // | + // | (y plane) (temporary) + // | YYYY YYYY UUVV UUVV + // +--> { YYYY YYYY + UUVV UUVV } + // YYYY YYYY UUVV UUVV + // First YYYY YYYY UUVV UUVV + // pass YYYY YYYY UUVV UUVV + // YYYY YYYY UUVV UUVV + // | + // | (u plane) (v plane) + // Second | UUUU VVVV + // pass +--> { UUUU + VVVV } + // UUUU VVVV + // + shared_variables.append("varying vec4 v_texcoords[2];\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 4.0;\n" + " v_texcoords[0].xy = texcoord - step * 1.5;\n" + " v_texcoords[0].zw = texcoord - step * 0.5;\n" + " v_texcoords[1].xy = texcoord + step * 0.5;\n" + " v_texcoords[1].zw = texcoord + step * 1.5;\n"); + fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n"); + fragment_header.append( + "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n" + "const float kYBias = 0.0625;\n" + // Divide U and V by two to compensate for averaging below. + "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n" + "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n" + "const float kUVBias = 0.5;\n"); + fragment_program.append( + " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n" + " vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n" + " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n" + " vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n" + " vec3 pixel12 = pixel1 + pixel2;\n" + " vec3 pixel34 = pixel3 + pixel4;\n" + " gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n" + " dot(pixel2, kRGBtoY),\n" + " dot(pixel3, kRGBtoY),\n" + " dot(pixel4, kRGBtoY)) + kYBias;\n" + " gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n" + " dot(pixel34, kRGBtoU),\n" + " dot(pixel12, kRGBtoV),\n" + " dot(pixel34, kRGBtoV)) + kUVBias;\n"); + break; + + case SHADER_YUV_MRT_PASS2: + // We're just sampling two pixels and unswizzling them. There's + // no need to do vertical scaling with math, since bilinear + // interpolation in the sampler takes care of that. + shared_variables.append("varying vec4 v_texcoords;\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 2.0;\n" + " v_texcoords.xy = texcoord - step * 0.5;\n" + " v_texcoords.zw = texcoord + step * 0.5;\n"); + fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n"); + fragment_program.append( + " vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n" + " vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n" + " gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n" + " gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n"); + break; + } + if (swizzle) { + switch (type) { + case SHADER_YUV_MRT_PASS1: + fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n"); + break; + case SHADER_YUV_MRT_PASS2: + fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n"); + fragment_program.append(" gl_FragData[1] = gl_FragData[1].bgra;\n"); + break; + default: + fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n"); + break; + } + } + + vertex_program = vertex_header + shared_variables + "void main() {\n" + + vertex_program + "}\n"; + + fragment_program = fragment_directives + fragment_header + + shared_variables + "void main() {\n" + fragment_program + + "}\n"; + + cache_entry->Setup(vertex_program.c_str(), fragment_program.c_str()); + } + return cache_entry; +} + +void ShaderProgram::Setup(const GLchar* vertex_shader_text, + const GLchar* fragment_shader_text) { + // Shaders to map the source texture to |dst_texture_|. + GLuint vertex_shader = + helper_->CompileShaderFromSource(vertex_shader_text, GL_VERTEX_SHADER); + if (vertex_shader == 0) + return; + + gl_->AttachShader(program_, vertex_shader); + gl_->DeleteShader(vertex_shader); + + GLuint fragment_shader = helper_->CompileShaderFromSource( + fragment_shader_text, GL_FRAGMENT_SHADER); + if (fragment_shader == 0) + return; + gl_->AttachShader(program_, fragment_shader); + gl_->DeleteShader(fragment_shader); + + gl_->LinkProgram(program_); + + GLint link_status = 0; + gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status); + if (!link_status) + return; + + position_location_ = gl_->GetAttribLocation(program_, "a_position"); + texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord"); + texture_location_ = gl_->GetUniformLocation(program_, "s_texture"); + src_subrect_location_ = gl_->GetUniformLocation(program_, "src_subrect"); + src_pixelsize_location_ = gl_->GetUniformLocation(program_, "src_pixelsize"); + dst_pixelsize_location_ = gl_->GetUniformLocation(program_, "dst_pixelsize"); + scaling_vector_location_ = + gl_->GetUniformLocation(program_, "scaling_vector"); + color_weights_location_ = gl_->GetUniformLocation(program_, "color_weights"); + // The only reason fetching these attribute locations should fail is + // if the context was spontaneously lost (i.e., because the GPU + // process crashed, perhaps deliberately for testing). + DCHECK(Initialized() || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR); +} + +void ShaderProgram::UseProgram(const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool scale_x, + bool flip_y, + GLfloat color_weights[4]) { + gl_->UseProgram(program_); + + // OpenGL defines the last parameter to VertexAttribPointer as type + // "const GLvoid*" even though it is actually an offset into the buffer + // object's data store and not a pointer to the client's address space. + const void* offsets[2] = {0, + reinterpret_cast<const void*>(2 * sizeof(GLfloat))}; + + gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE, + 4 * sizeof(GLfloat), offsets[0]); + gl_->EnableVertexAttribArray(position_location_); + + gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE, + 4 * sizeof(GLfloat), offsets[1]); + gl_->EnableVertexAttribArray(texcoord_location_); + + gl_->Uniform1i(texture_location_, 0); + + // Convert |src_subrect| to texture coordinates. + GLfloat src_subrect_texcoord[] = { + static_cast<float>(src_subrect.x()) / src_size.width(), + static_cast<float>(src_subrect.y()) / src_size.height(), + static_cast<float>(src_subrect.width()) / src_size.width(), + static_cast<float>(src_subrect.height()) / src_size.height(), + }; + if (flip_y) { + src_subrect_texcoord[1] += src_subrect_texcoord[3]; + src_subrect_texcoord[3] *= -1.0; + } + gl_->Uniform4fv(src_subrect_location_, 1, src_subrect_texcoord); + + gl_->Uniform2f(src_pixelsize_location_, src_size.width(), src_size.height()); + gl_->Uniform2f(dst_pixelsize_location_, static_cast<float>(dst_size.width()), + static_cast<float>(dst_size.height())); + + gl_->Uniform2f(scaling_vector_location_, scale_x ? 1.0 : 0.0, + scale_x ? 0.0 : 1.0); + gl_->Uniform4fv(color_weights_location_, 1, color_weights); +} + +} // namespace viz diff --git a/chromium/components/viz/common/gl_helper_scaling.h b/chromium/components/viz/common/gl_helper_scaling.h new file mode 100644 index 00000000000..a37ea919ee5 --- /dev/null +++ b/chromium/components/viz/common/gl_helper_scaling.h @@ -0,0 +1,208 @@ +// Copyright (c) 2013 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 COMPONENTS_VIZ_COMMON_GL_HELPER_SCALING_H_ +#define COMPONENTS_VIZ_COMMON_GL_HELPER_SCALING_H_ + +#include <deque> +#include <map> +#include <vector> + +#include "base/macros.h" +#include "components/viz/common/gl_helper.h" +#include "components/viz/common/viz_common_export.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace viz { + +class ShaderProgram; +class ScalerImpl; +class GLHelperTest; + +// Implements GPU texture scaling methods. +// Note that you should probably not use this class directly. +// See gl_helper.cc::CreateScaler instead. +class VIZ_COMMON_EXPORT GLHelperScaling { + public: + enum ShaderType { + SHADER_BILINEAR, + SHADER_BILINEAR2, + SHADER_BILINEAR3, + SHADER_BILINEAR4, + SHADER_BILINEAR2X2, + SHADER_BICUBIC_UPSCALE, + SHADER_BICUBIC_HALF_1D, + SHADER_PLANAR, + SHADER_YUV_MRT_PASS1, + SHADER_YUV_MRT_PASS2, + }; + + // Similar to ScalerInterface, but can generate multiple outputs. + // Used for YUV conversion in gl_helper.c + class ShaderInterface { + public: + ShaderInterface() {} + virtual ~ShaderInterface() {} + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + virtual void Execute(GLuint source_texture, + const std::vector<GLuint>& dest_textures) = 0; + }; + + using ShaderProgramKeyType = std::pair<ShaderType, bool>; + + GLHelperScaling(gpu::gles2::GLES2Interface* gl, GLHelper* helper); + ~GLHelperScaling(); + void InitBuffer(); + + GLHelper::ScalerInterface* CreateScaler(GLHelper::ScalerQuality quality, + gfx::Size src_size, + gfx::Rect src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle); + + GLHelper::ScalerInterface* CreatePlanarScaler(const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + const float color_weights[4]); + + ShaderInterface* CreateYuvMrtShader(const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + ShaderType shader); + + private: + // A ScaleOp represents a pass in a scaler pipeline, in one dimension. + // Note that when quality is GOOD, multiple scaler passes will be + // combined into one operation for increased performance. + // Exposed in the header file for testing purposes. + struct ScaleOp { + ScaleOp(int factor, bool x, int size) + : scale_factor(factor), scale_x(x), scale_size(size) {} + + // Calculate a set of ScaleOp needed to convert an image of size + // |src| into an image of size |dst|. If |scale_x| is true, then + // the calculations are for the X axis of the image, otherwise Y. + // If |allow3| is true, we can use a SHADER_BILINEAR3 to replace + // a scale up and scale down with a 3-tap bilinear scale. + // The calculated ScaleOps are added to |ops|. + static void AddOps(int src, + int dst, + bool scale_x, + bool allow3, + std::deque<ScaleOp>* ops) { + int num_downscales = 0; + if (allow3 && dst * 3 >= src && dst * 2 < src) { + // Technically, this should be a scale up and then a + // scale down, but it makes the optimization code more + // complicated. + ops->push_back(ScaleOp(3, scale_x, dst)); + return; + } + while ((dst << num_downscales) < src) { + num_downscales++; + } + if ((dst << num_downscales) != src) { + ops->push_back(ScaleOp(0, scale_x, dst << num_downscales)); + } + while (num_downscales) { + num_downscales--; + ops->push_back(ScaleOp(2, scale_x, dst << num_downscales)); + } + } + + // Update |size| to its new size. Before calling this function + // |size| should be the size of the input image. After calling it, + // |size| will be the size of the image after this particular + // scaling operation. + void UpdateSize(gfx::Size* subrect) { + if (scale_x) { + subrect->set_width(scale_size); + } else { + subrect->set_height(scale_size); + } + } + + // A scale factor of 0 means upscale + // 2 means 50% scale + // 3 means 33% scale, etc. + int scale_factor; + bool scale_x; // Otherwise y + int scale_size; // Size to scale to. + }; + + // Full specification for a single scaling stage. + struct ScalerStage { + ScalerStage(ShaderType shader_, + gfx::Size src_size_, + gfx::Rect src_subrect_, + gfx::Size dst_size_, + bool scale_x_, + bool vertically_flip_texture_, + bool swizzle_); + ScalerStage(const ScalerStage& other); + ShaderType shader; + gfx::Size src_size; + gfx::Rect src_subrect; + gfx::Size dst_size; + bool scale_x; + bool vertically_flip_texture; + bool swizzle; + }; + + // Compute a vector of scaler stages for a particular + // set of input/output parameters. + void ComputeScalerStages(GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + std::vector<ScalerStage>* scaler_stages); + + // Take two queues of ScaleOp structs and generate a + // vector of scaler stages. This is the second half of + // ComputeScalerStages. + void ConvertScalerOpsToScalerStages( + GLHelper::ScalerQuality quality, + gfx::Size src_size, + gfx::Rect src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + std::deque<GLHelperScaling::ScaleOp>* x_ops, + std::deque<GLHelperScaling::ScaleOp>* y_ops, + std::vector<ScalerStage>* scaler_stages); + + scoped_refptr<ShaderProgram> GetShaderProgram(ShaderType type, bool swizzle); + + // Interleaved array of 2-dimentional vertex positions (x, y) and + // 2-dimentional texture coordinates (s, t). + static const GLfloat kVertexAttributes[]; + + gpu::gles2::GLES2Interface* gl_; + GLHelper* helper_; + + // The buffer that holds the vertices and the texture coordinates data for + // drawing a quad. + ScopedBuffer vertex_attributes_buffer_; + + std::map<ShaderProgramKeyType, scoped_refptr<ShaderProgram>> shader_programs_; + + friend class ShaderProgram; + friend class ScalerImpl; + friend class GLHelperBenchmark; + friend class GLHelperTest; + DISALLOW_COPY_AND_ASSIGN(GLHelperScaling); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_GL_HELPER_SCALING_H_ diff --git a/chromium/components/viz/common/gl_helper_unittest.cc b/chromium/components/viz/common/gl_helper_unittest.cc new file mode 100644 index 00000000000..34192b4837c --- /dev/null +++ b/chromium/components/viz/common/gl_helper_unittest.cc @@ -0,0 +1,1425 @@ +// Copyright 2016 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 <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <cmath> +#include <string> +#include <vector> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES2/gl2extchromium.h> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/memory/ref_counted_memory.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "components/viz/common/gl_helper.h" +#include "components/viz/common/gl_helper_readback_support.h" +#include "components/viz/common/gl_helper_scaling.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "gpu/command_buffer/client/shared_memory_limits.h" +#include "gpu/ipc/gl_in_process_context.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkTypes.h" + +#if !defined(OS_ANDROID) + +namespace viz { + +GLHelper::ScalerQuality kQualities[] = { + GLHelper::SCALER_QUALITY_BEST, GLHelper::SCALER_QUALITY_GOOD, + GLHelper::SCALER_QUALITY_FAST, +}; + +const char* kQualityNames[] = { + "best", "good", "fast", +}; + +class GLHelperTest : public testing::Test { + protected: + void SetUp() override { + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = 8; + attributes.depth_size = 24; + attributes.red_size = 8; + attributes.green_size = 8; + attributes.blue_size = 8; + attributes.stencil_size = 8; + attributes.samples = 4; + attributes.sample_buffers = 1; + attributes.bind_generates_resource = false; + + context_.reset( + gpu::GLInProcessContext::Create(nullptr, /* service */ + nullptr, /* surface */ + true, /* offscreen */ + gpu::kNullSurfaceHandle, /* window */ + nullptr, /* share_context */ + attributes, gpu::SharedMemoryLimits(), + nullptr, /* gpu_memory_buffer_manager */ + nullptr, /* image_factory */ + base::ThreadTaskRunnerHandle::Get())); + gl_ = context_->GetImplementation(); + gpu::ContextSupport* support = context_->GetImplementation(); + + helper_.reset(new GLHelper(gl_, support)); + helper_scaling_.reset(new GLHelperScaling(gl_, helper_.get())); + } + + void TearDown() override { + helper_scaling_.reset(nullptr); + helper_.reset(nullptr); + context_.reset(nullptr); + } + + // Bicubic filter kernel function. + static float Bicubic(float x) { + const float a = -0.5; + x = std::abs(x); + float x2 = x * x; + float x3 = x2 * x; + if (x <= 1) { + return (a + 2) * x3 - (a + 3) * x2 + 1; + } else if (x < 2) { + return a * x3 - 5 * a * x2 + 8 * a * x - 4 * a; + } else { + return 0.0f; + } + } + + // Look up a single channel value. Works for 4-channel and single channel + // bitmaps. Clamp x/y. + int Channel(SkBitmap* pixels, int x, int y, int c) { + if (pixels->bytesPerPixel() == 4) { + uint32_t* data = + pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)), + std::max(0, std::min(y, pixels->height() - 1))); + return (*data) >> (c * 8) & 0xff; + } else { + DCHECK_EQ(pixels->bytesPerPixel(), 1); + DCHECK_EQ(c, 0); + return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)), + std::max(0, std::min(y, pixels->height() - 1))); + } + } + + // Set a single channel value. Works for 4-channel and single channel + // bitmaps. Clamp x/y. + void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) { + DCHECK_GE(x, 0); + DCHECK_GE(y, 0); + DCHECK_LT(x, pixels->width()); + DCHECK_LT(y, pixels->height()); + if (pixels->bytesPerPixel() == 4) { + uint32_t* data = pixels->getAddr32(x, y); + v = std::max(0, std::min(v, 255)); + *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8)); + } else { + DCHECK_EQ(pixels->bytesPerPixel(), 1); + DCHECK_EQ(c, 0); + uint8_t* data = pixels->getAddr8(x, y); + v = std::max(0, std::min(v, 255)); + *data = v; + } + } + + // Print all the R, G, B or A values from an SkBitmap in a + // human-readable format. + void PrintChannel(SkBitmap* pixels, int c) { + for (int y = 0; y < pixels->height(); y++) { + std::string formatted; + for (int x = 0; x < pixels->width(); x++) { + formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c))); + } + LOG(ERROR) << formatted; + } + } + + // Print out the individual steps of a scaler pipeline. + std::string PrintStages( + const std::vector<GLHelperScaling::ScalerStage>& scaler_stages) { + std::string ret; + for (size_t i = 0; i < scaler_stages.size(); i++) { + ret.append(base::StringPrintf( + "%dx%d -> %dx%d ", scaler_stages[i].src_size.width(), + scaler_stages[i].src_size.height(), scaler_stages[i].dst_size.width(), + scaler_stages[i].dst_size.height())); + bool xy_matters = false; + switch (scaler_stages[i].shader) { + case GLHelperScaling::SHADER_BILINEAR: + ret.append("bilinear"); + break; + case GLHelperScaling::SHADER_BILINEAR2: + ret.append("bilinear2"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_BILINEAR3: + ret.append("bilinear3"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_BILINEAR4: + ret.append("bilinear4"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_BILINEAR2X2: + ret.append("bilinear2x2"); + break; + case GLHelperScaling::SHADER_BICUBIC_UPSCALE: + ret.append("bicubic upscale"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_BICUBIC_HALF_1D: + ret.append("bicubic 1/2"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_PLANAR: + ret.append("planar"); + break; + case GLHelperScaling::SHADER_YUV_MRT_PASS1: + ret.append("rgb2yuv pass 1"); + break; + case GLHelperScaling::SHADER_YUV_MRT_PASS2: + ret.append("rgb2yuv pass 2"); + break; + } + + if (xy_matters) { + if (scaler_stages[i].scale_x) { + ret.append(" X"); + } else { + ret.append(" Y"); + } + } + ret.append("\n"); + } + return ret; + } + + bool CheckScale(double scale, int samples, bool already_scaled) { + // 1:1 is valid if there is one sample. + if (samples == 1 && scale == 1.0) { + return true; + } + // Is it an exact down-scale (50%, 25%, etc.?) + if (scale == 2.0 * samples) { + return true; + } + // Upscales, only valid if we haven't already scaled in this dimension. + if (!already_scaled) { + // Is it a valid bilinear upscale? + if (samples == 1 && scale <= 1.0) { + return true; + } + // Multi-sample upscale-downscale combination? + if (scale > samples / 2.0 && scale < samples) { + return true; + } + } + return false; + } + + // Make sure that the stages of the scaler pipeline are sane. + void ValidateScalerStages( + GLHelper::ScalerQuality quality, + const std::vector<GLHelperScaling::ScalerStage>& scaler_stages, + const gfx::Size& dst_size, + const std::string& message) { + bool previous_error = HasFailure(); + // First, check that the input size for each stage is equal to + // the output size of the previous stage. + for (size_t i = 1; i < scaler_stages.size(); i++) { + EXPECT_EQ(scaler_stages[i - 1].dst_size.width(), + scaler_stages[i].src_size.width()); + EXPECT_EQ(scaler_stages[i - 1].dst_size.height(), + scaler_stages[i].src_size.height()); + EXPECT_EQ(scaler_stages[i].src_subrect.x(), 0); + EXPECT_EQ(scaler_stages[i].src_subrect.y(), 0); + EXPECT_EQ(scaler_stages[i].src_subrect.width(), + scaler_stages[i].src_size.width()); + EXPECT_EQ(scaler_stages[i].src_subrect.height(), + scaler_stages[i].src_size.height()); + } + + // Check the output size matches the destination of the last stage + EXPECT_EQ(scaler_stages.back().dst_size.width(), dst_size.width()); + EXPECT_EQ(scaler_stages.back().dst_size.height(), dst_size.height()); + + // Used to verify that up-scales are not attempted after some + // other scale. + bool scaled_x = false; + bool scaled_y = false; + + for (size_t i = 0; i < scaler_stages.size(); i++) { + // Note: 2.0 means scaling down by 50% + double x_scale = + static_cast<double>(scaler_stages[i].src_subrect.width()) / + static_cast<double>(scaler_stages[i].dst_size.width()); + double y_scale = + static_cast<double>(scaler_stages[i].src_subrect.height()) / + static_cast<double>(scaler_stages[i].dst_size.height()); + + int x_samples = 0; + int y_samples = 0; + + // Codify valid scale operations. + switch (scaler_stages[i].shader) { + case GLHelperScaling::SHADER_PLANAR: + case GLHelperScaling::SHADER_YUV_MRT_PASS1: + case GLHelperScaling::SHADER_YUV_MRT_PASS2: + EXPECT_TRUE(false) << "Invalid shader."; + break; + + case GLHelperScaling::SHADER_BILINEAR: + if (quality != GLHelper::SCALER_QUALITY_FAST) { + x_samples = 1; + y_samples = 1; + } + break; + case GLHelperScaling::SHADER_BILINEAR2: + x_samples = 2; + y_samples = 1; + break; + case GLHelperScaling::SHADER_BILINEAR3: + x_samples = 3; + y_samples = 1; + break; + case GLHelperScaling::SHADER_BILINEAR4: + x_samples = 4; + y_samples = 1; + break; + case GLHelperScaling::SHADER_BILINEAR2X2: + x_samples = 2; + y_samples = 2; + break; + case GLHelperScaling::SHADER_BICUBIC_UPSCALE: + if (scaler_stages[i].scale_x) { + EXPECT_LT(x_scale, 1.0); + EXPECT_EQ(y_scale, 1.0); + } else { + EXPECT_EQ(x_scale, 1.0); + EXPECT_LT(y_scale, 1.0); + } + break; + case GLHelperScaling::SHADER_BICUBIC_HALF_1D: + if (scaler_stages[i].scale_x) { + EXPECT_EQ(x_scale, 2.0); + EXPECT_EQ(y_scale, 1.0); + } else { + EXPECT_EQ(x_scale, 1.0); + EXPECT_EQ(y_scale, 2.0); + } + break; + } + + if (!scaler_stages[i].scale_x) { + std::swap(x_samples, y_samples); + } + + if (x_samples) { + EXPECT_TRUE(CheckScale(x_scale, x_samples, scaled_x)) + << "x_scale = " << x_scale; + } + if (y_samples) { + EXPECT_TRUE(CheckScale(y_scale, y_samples, scaled_y)) + << "y_scale = " << y_scale; + } + + if (x_scale != 1.0) { + scaled_x = true; + } + if (y_scale != 1.0) { + scaled_y = true; + } + } + + if (HasFailure() && !previous_error) { + LOG(ERROR) << "Invalid scaler stages: " << message; + LOG(ERROR) << "Scaler stages:"; + LOG(ERROR) << PrintStages(scaler_stages); + } + } + + // Compares two bitmaps taking color types into account. Checks whether each + // component of each pixel is no more than |maxdiff| apart. If bitmaps are not + // similar enough, prints out |truth|, |other|, |source|, |scaler_stages| + // and |message|. + void Compare(SkBitmap* truth, + SkBitmap* other, + int maxdiff, + SkBitmap* source, + const std::vector<GLHelperScaling::ScalerStage>& scaler_stages, + std::string message) { + EXPECT_EQ(truth->width(), other->width()); + EXPECT_EQ(truth->height(), other->height()); + bool swizzle = (truth->colorType() == kRGBA_8888_SkColorType && + other->colorType() == kBGRA_8888_SkColorType) || + (truth->colorType() == kBGRA_8888_SkColorType && + other->colorType() == kRGBA_8888_SkColorType); + EXPECT_TRUE(swizzle || truth->colorType() == other->colorType()); + int bpp = truth->bytesPerPixel(); + for (int x = 0; x < truth->width(); x++) { + for (int y = 0; y < truth->height(); y++) { + for (int c = 0; c < bpp; c++) { + int a = Channel(truth, x, y, c); + // swizzle when comparing if needed + int b = swizzle && (c == 0 || c == 2) + ? Channel(other, x, y, (c + 2) & 2) + : Channel(other, x, y, c); + EXPECT_NEAR(a, b, maxdiff) + << " x=" << x << " y=" << y << " c=" << c << " " << message; + if (std::abs(a - b) > maxdiff) { + LOG(ERROR) << "-------expected--------"; + for (int i = 0; i < bpp; i++) { + LOG(ERROR) << "Channel " << i << ":"; + PrintChannel(truth, i); + } + LOG(ERROR) << "-------actual--------"; + for (int i = 0; i < bpp; i++) { + LOG(ERROR) << "Channel " << i << ":"; + PrintChannel(other, i); + } + if (source) { + LOG(ERROR) << "-------original--------"; + for (int i = 0; i < source->bytesPerPixel(); i++) { + LOG(ERROR) << "Channel " << i << ":"; + PrintChannel(source, i); + } + } + LOG(ERROR) << "-----Scaler stages------"; + LOG(ERROR) << PrintStages(scaler_stages); + return; + } + } + } + } + } + + // Get a single R, G, B or A value as a float. + float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) { + return Channel(pixels, x, y, c) / 255.0; + } + + // Works like a GL_LINEAR lookup on an SkBitmap. + float Bilinear(SkBitmap* pixels, float x, float y, int c) { + x -= 0.5; + y -= 0.5; + int base_x = static_cast<int>(floorf(x)); + int base_y = static_cast<int>(floorf(y)); + x -= base_x; + y -= base_y; + return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) + + ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) + + ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y + + ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y); + } + + // Encodes an RGBA bitmap to grayscale. + // Reference implementation for + // GLHelper::CopyToTextureImpl::EncodeTextureAsGrayscale. + void EncodeToGrayscaleSlow(SkBitmap* input, SkBitmap* output) { + const float kRGBtoGrayscaleColorWeights[3] = {0.213f, 0.715f, 0.072f}; + CHECK_EQ(kAlpha_8_SkColorType, output->colorType()); + CHECK_EQ(input->width(), output->width()); + CHECK_EQ(input->height(), output->height()); + CHECK_EQ(input->colorType(), kRGBA_8888_SkColorType); + + for (int dst_y = 0; dst_y < output->height(); dst_y++) { + for (int dst_x = 0; dst_x < output->width(); dst_x++) { + float c0 = ChannelAsFloat(input, dst_x, dst_y, 0); + float c1 = ChannelAsFloat(input, dst_x, dst_y, 1); + float c2 = ChannelAsFloat(input, dst_x, dst_y, 2); + float value = c0 * kRGBtoGrayscaleColorWeights[0] + + c1 * kRGBtoGrayscaleColorWeights[1] + + c2 * kRGBtoGrayscaleColorWeights[2]; + SetChannel(output, dst_x, dst_y, 0, + static_cast<int>(value * 255.0f + 0.5f)); + } + } + } + + // Very slow bicubic / bilinear scaler for reference. + void ScaleSlow(SkBitmap* input, + SkBitmap* output, + GLHelper::ScalerQuality quality) { + float xscale = static_cast<float>(input->width()) / output->width(); + float yscale = static_cast<float>(input->height()) / output->height(); + float clamped_xscale = xscale < 1.0 ? 1.0 : 1.0 / xscale; + float clamped_yscale = yscale < 1.0 ? 1.0 : 1.0 / yscale; + for (int dst_y = 0; dst_y < output->height(); dst_y++) { + for (int dst_x = 0; dst_x < output->width(); dst_x++) { + for (int channel = 0; channel < 4; channel++) { + float dst_x_in_src = (dst_x + 0.5f) * xscale; + float dst_y_in_src = (dst_y + 0.5f) * yscale; + + float value = 0.0f; + float sum = 0.0f; + switch (quality) { + case GLHelper::SCALER_QUALITY_BEST: + for (int src_y = -10; src_y < input->height() + 10; ++src_y) { + float coeff_y = + Bicubic((src_y + 0.5f - dst_y_in_src) * clamped_yscale); + if (coeff_y == 0.0f) { + continue; + } + for (int src_x = -10; src_x < input->width() + 10; ++src_x) { + float coeff = + coeff_y * + Bicubic((src_x + 0.5f - dst_x_in_src) * clamped_xscale); + if (coeff == 0.0f) { + continue; + } + sum += coeff; + float c = ChannelAsFloat(input, src_x, src_y, channel); + value += c * coeff; + } + } + break; + + case GLHelper::SCALER_QUALITY_GOOD: { + int xshift = 0, yshift = 0; + while ((output->width() << xshift) < input->width()) { + xshift++; + } + while ((output->height() << yshift) < input->height()) { + yshift++; + } + int xmag = 1 << xshift; + int ymag = 1 << yshift; + if (xmag == 4 && output->width() * 3 >= input->width()) { + xmag = 3; + } + if (ymag == 4 && output->height() * 3 >= input->height()) { + ymag = 3; + } + for (int x = 0; x < xmag; x++) { + for (int y = 0; y < ymag; y++) { + value += Bilinear( + input, (dst_x * xmag + x + 0.5) * xscale / xmag, + (dst_y * ymag + y + 0.5) * yscale / ymag, channel); + sum += 1.0; + } + } + break; + } + + case GLHelper::SCALER_QUALITY_FAST: + value = Bilinear(input, dst_x_in_src, dst_y_in_src, channel); + sum = 1.0; + } + value /= sum; + SetChannel(output, dst_x, dst_y, channel, + static_cast<int>(value * 255.0f + 0.5f)); + } + } + } + } + + void FlipSKBitmap(SkBitmap* bitmap) { + int bpp = bitmap->bytesPerPixel(); + DCHECK(bpp == 4 || bpp == 1); + int top_line = 0; + int bottom_line = bitmap->height() - 1; + while (top_line < bottom_line) { + for (int x = 0; x < bitmap->width(); x++) { + bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line), + *bitmap->getAddr32(x, bottom_line)) + : std::swap(*bitmap->getAddr8(x, top_line), + *bitmap->getAddr8(x, bottom_line)); + } + top_line++; + bottom_line--; + } + } + + // Swaps red and blue channels in each pixel in a 32-bit bitmap. + void SwizzleSKBitmap(SkBitmap* bitmap) { + int bpp = bitmap->bytesPerPixel(); + DCHECK_EQ(bpp, 4); + for (int y = 0; y < bitmap->height(); y++) { + for (int x = 0; x < bitmap->width(); x++) { + // Swap channels 0 and 2 (red and blue) + int c0 = Channel(bitmap, x, y, 0); + int c2 = Channel(bitmap, x, y, 2); + SetChannel(bitmap, x, y, 2, c0); + SetChannel(bitmap, x, y, 0, c2); + } + } + } + + // gl_helper scales recursively, so we'll need to do that + // in the reference implementation too. + void ScaleSlowRecursive(SkBitmap* input, + SkBitmap* output, + GLHelper::ScalerQuality quality) { + if (quality == GLHelper::SCALER_QUALITY_FAST || + quality == GLHelper::SCALER_QUALITY_GOOD) { + ScaleSlow(input, output, quality); + return; + } + + float xscale = static_cast<float>(output->width()) / input->width(); + + // This corresponds to all the operations we can do directly. + float yscale = static_cast<float>(output->height()) / input->height(); + if ((xscale == 1.0f && yscale == 1.0f) || + (xscale == 0.5f && yscale == 1.0f) || + (xscale == 1.0f && yscale == 0.5f) || + (xscale >= 1.0f && yscale == 1.0f) || + (xscale == 1.0f && yscale >= 1.0f)) { + ScaleSlow(input, output, quality); + return; + } + + // Now we break the problem down into smaller pieces, using the + // operations available. + int xtmp = input->width(); + int ytmp = input->height(); + + if (output->height() != input->height()) { + ytmp = output->height(); + while (ytmp < input->height() && ytmp * 2 != input->height()) { + ytmp += ytmp; + } + } else { + xtmp = output->width(); + while (xtmp < input->width() && xtmp * 2 != input->width()) { + xtmp += xtmp; + } + } + + SkBitmap tmp; + tmp.allocN32Pixels(xtmp, ytmp); + + ScaleSlowRecursive(input, &tmp, quality); + ScaleSlowRecursive(&tmp, output, quality); + } + + // Creates an RGBA SkBitmap + std::unique_ptr<SkBitmap> CreateTestBitmap(int width, + int height, + int test_pattern) { + std::unique_ptr<SkBitmap> bitmap(new SkBitmap); + bitmap->allocPixels(SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, + kPremul_SkAlphaType)); + + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + switch (test_pattern) { + case 0: // Smooth test pattern + SetChannel(bitmap.get(), x, y, 0, x * 10); + SetChannel(bitmap.get(), x, y, 0, y == 0 ? x * 50 : x * 10); + SetChannel(bitmap.get(), x, y, 1, y * 10); + SetChannel(bitmap.get(), x, y, 2, (x + y) * 10); + SetChannel(bitmap.get(), x, y, 3, 255); + break; + case 1: // Small blocks + SetChannel(bitmap.get(), x, y, 0, x & 1 ? 255 : 0); + SetChannel(bitmap.get(), x, y, 1, y & 1 ? 255 : 0); + SetChannel(bitmap.get(), x, y, 2, (x + y) & 1 ? 255 : 0); + SetChannel(bitmap.get(), x, y, 3, 255); + break; + case 2: // Medium blocks + SetChannel(bitmap.get(), x, y, 0, 10 + x / 2 * 50); + SetChannel(bitmap.get(), x, y, 1, 10 + y / 3 * 50); + SetChannel(bitmap.get(), x, y, 2, (x + y) / 5 * 50 + 5); + SetChannel(bitmap.get(), x, y, 3, 255); + break; + } + } + } + return bitmap; + } + + // Binds texture and framebuffer and loads the bitmap pixels into the texture. + void BindTextureAndFrameBuffer(GLuint texture, + GLuint framebuffer, + SkBitmap* bitmap, + int width, + int height) { + gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); + gl_->BindTexture(GL_TEXTURE_2D, texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, bitmap->getPixels()); + } + + // Create a test image, transform it using + // GLHelper::CropScaleReadbackAndCleanTexture and a reference implementation + // and compare the results. + void TestCropScaleReadbackAndCleanTexture(int xsize, + int ysize, + int scaled_xsize, + int scaled_ysize, + int test_pattern, + SkColorType out_color_type, + bool swizzle, + size_t quality_index) { + DCHECK(out_color_type == kAlpha_8_SkColorType || + out_color_type == kRGBA_8888_SkColorType || + out_color_type == kBGRA_8888_SkColorType); + GLuint src_texture; + gl_->GenTextures(1, &src_texture); + GLuint framebuffer; + gl_->GenFramebuffers(1, &framebuffer); + std::unique_ptr<SkBitmap> input_pixels = + CreateTestBitmap(xsize, ysize, test_pattern); + BindTextureAndFrameBuffer(src_texture, framebuffer, input_pixels.get(), + xsize, ysize); + + std::string message = base::StringPrintf( + "input size: %dx%d " + "output size: %dx%d " + "pattern: %d , quality: %s, " + "out_color_type: %d", + xsize, ysize, scaled_xsize, scaled_ysize, test_pattern, + kQualityNames[quality_index], out_color_type); + + // Transform the bitmap using GLHelper::CropScaleReadbackAndCleanTexture. + SkBitmap output_pixels; + output_pixels.allocPixels(SkImageInfo::Make( + scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType)); + base::RunLoop run_loop; + gfx::Size encoded_texture_size; + helper_->CropScaleReadbackAndCleanTexture( + src_texture, gfx::Size(xsize, ysize), gfx::Rect(xsize, ysize), + gfx::Size(scaled_xsize, scaled_ysize), + static_cast<unsigned char*>(output_pixels.getPixels()), out_color_type, + base::Bind(&callcallback, run_loop.QuitClosure()), + kQualities[quality_index]); + run_loop.Run(); + // CropScaleReadbackAndCleanTexture flips the pixels. Flip them back. + FlipSKBitmap(&output_pixels); + + // If the bitmap shouldn't have changed - compare against input. + if (xsize == scaled_xsize && ysize == scaled_ysize && + out_color_type != kAlpha_8_SkColorType) { + const std::vector<GLHelperScaling::ScalerStage> dummy_stages; + Compare(input_pixels.get(), &output_pixels, 0, nullptr, dummy_stages, + message + " comparing against input"); + return; + } + + // Now transform the bitmap using the reference implementation. + SkBitmap scaled_pixels; + scaled_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize, + kRGBA_8888_SkColorType, + kPremul_SkAlphaType)); + SkBitmap truth_pixels; + // Step 1: Scale + ScaleSlowRecursive(input_pixels.get(), &scaled_pixels, + kQualities[quality_index]); + // Step 2: Encode to grayscale if needed. + if (out_color_type == kAlpha_8_SkColorType) { + truth_pixels.allocPixels(SkImageInfo::Make( + scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType)); + EncodeToGrayscaleSlow(&scaled_pixels, &truth_pixels); + } else { + truth_pixels = scaled_pixels; + } + + // Now compare the results. + const std::vector<GLHelperScaling::ScalerStage> dummy_stages; + Compare(&truth_pixels, &output_pixels, 2, input_pixels.get(), dummy_stages, + message + " comparing against transformed/scaled"); + + gl_->DeleteTextures(1, &src_texture); + gl_->DeleteFramebuffers(1, &framebuffer); + } + + // Scaling test: Create a test image, scale it using GLHelperScaling + // and a reference implementation and compare the results. + void TestScale(int xsize, + int ysize, + int scaled_xsize, + int scaled_ysize, + int test_pattern, + size_t quality_index, + bool flip) { + GLuint src_texture; + gl_->GenTextures(1, &src_texture); + GLuint framebuffer; + gl_->GenFramebuffers(1, &framebuffer); + std::unique_ptr<SkBitmap> input_pixels = + CreateTestBitmap(xsize, ysize, test_pattern); + BindTextureAndFrameBuffer(src_texture, framebuffer, input_pixels.get(), + xsize, ysize); + + std::string message = base::StringPrintf( + "input size: %dx%d " + "output size: %dx%d " + "pattern: %d quality: %s", + xsize, ysize, scaled_xsize, scaled_ysize, test_pattern, + kQualityNames[quality_index]); + + std::vector<GLHelperScaling::ScalerStage> stages; + helper_scaling_->ComputeScalerStages( + kQualities[quality_index], gfx::Size(xsize, ysize), + gfx::Rect(0, 0, xsize, ysize), gfx::Size(scaled_xsize, scaled_ysize), + flip, false, &stages); + ValidateScalerStages(kQualities[quality_index], stages, + gfx::Size(scaled_xsize, scaled_ysize), message); + + GLuint dst_texture = helper_->CopyAndScaleTexture( + src_texture, gfx::Size(xsize, ysize), + gfx::Size(scaled_xsize, scaled_ysize), flip, kQualities[quality_index]); + + SkBitmap output_pixels; + output_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize, + kRGBA_8888_SkColorType, + kPremul_SkAlphaType)); + + helper_->ReadbackTextureSync( + dst_texture, gfx::Rect(0, 0, scaled_xsize, scaled_ysize), + static_cast<unsigned char*>(output_pixels.getPixels()), + kRGBA_8888_SkColorType); + if (flip) { + // Flip the pixels back. + FlipSKBitmap(&output_pixels); + } + + // If the bitmap shouldn't have changed - compare against input. + if (xsize == scaled_xsize && ysize == scaled_ysize) { + Compare(input_pixels.get(), &output_pixels, 0, nullptr, stages, + message + " comparing against input"); + return; + } + + // Now scale the bitmap using the reference implementation. + SkBitmap truth_pixels; + truth_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize, + kRGBA_8888_SkColorType, + kPremul_SkAlphaType)); + ScaleSlowRecursive(input_pixels.get(), &truth_pixels, + kQualities[quality_index]); + Compare(&truth_pixels, &output_pixels, 2, input_pixels.get(), stages, + message + " comparing against scaled"); + + gl_->DeleteTextures(1, &src_texture); + gl_->DeleteTextures(1, &dst_texture); + gl_->DeleteFramebuffers(1, &framebuffer); + } + + // Create a scaling pipeline and check that it is made up of + // valid scaling operations. + void TestScalerPipeline(size_t quality, + int xsize, + int ysize, + int dst_xsize, + int dst_ysize) { + std::vector<GLHelperScaling::ScalerStage> stages; + helper_scaling_->ComputeScalerStages( + kQualities[quality], gfx::Size(xsize, ysize), + gfx::Rect(0, 0, xsize, ysize), gfx::Size(dst_xsize, dst_ysize), false, + false, &stages); + ValidateScalerStages(kQualities[quality], stages, + gfx::Size(dst_xsize, dst_ysize), + base::StringPrintf("input size: %dx%d " + "output size: %dx%d " + "quality: %s", + xsize, ysize, dst_xsize, dst_ysize, + kQualityNames[quality])); + } + + // Create a scaling pipeline and make sure that the steps + // are exactly the steps we expect. + void CheckPipeline(GLHelper::ScalerQuality quality, + int xsize, + int ysize, + int dst_xsize, + int dst_ysize, + const std::string& description) { + std::vector<GLHelperScaling::ScalerStage> stages; + helper_scaling_->ComputeScalerStages( + quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize), + gfx::Size(dst_xsize, dst_ysize), false, false, &stages); + ValidateScalerStages(GLHelper::SCALER_QUALITY_GOOD, stages, + gfx::Size(dst_xsize, dst_ysize), ""); + EXPECT_EQ(PrintStages(stages), description); + } + + static void callcallback(const base::Callback<void()>& callback, + bool result) { + callback.Run(); + } + + void DrawGridToBitmap(int w, + int h, + SkColor background_color, + SkColor grid_color, + int grid_pitch, + int grid_width, + const SkBitmap& bmp) { + ASSERT_GT(grid_pitch, 0); + ASSERT_GT(grid_width, 0); + ASSERT_NE(background_color, grid_color); + + for (int y = 0; y < h; ++y) { + bool y_on_grid = ((y % grid_pitch) < grid_width); + + for (int x = 0; x < w; ++x) { + bool on_grid = (y_on_grid || ((x % grid_pitch) < grid_width)); + + if (bmp.colorType() == kRGBA_8888_SkColorType || + bmp.colorType() == kBGRA_8888_SkColorType) { + *bmp.getAddr32(x, y) = (on_grid ? grid_color : background_color); + } else if (bmp.colorType() == kRGB_565_SkColorType) { + *bmp.getAddr16(x, y) = (on_grid ? grid_color : background_color); + } + } + } + } + + void DrawCheckerToBitmap(int w, + int h, + SkColor color1, + SkColor color2, + int rect_w, + int rect_h, + const SkBitmap& bmp) { + ASSERT_GT(rect_w, 0); + ASSERT_GT(rect_h, 0); + ASSERT_NE(color1, color2); + + for (int y = 0; y < h; ++y) { + bool y_bit = (((y / rect_h) & 0x1) == 0); + + for (int x = 0; x < w; ++x) { + bool x_bit = (((x / rect_w) & 0x1) == 0); + + bool use_color2 = (x_bit != y_bit); // xor + if (bmp.colorType() == kRGBA_8888_SkColorType || + bmp.colorType() == kBGRA_8888_SkColorType) { + *bmp.getAddr32(x, y) = (use_color2 ? color2 : color1); + } else if (bmp.colorType() == kRGB_565_SkColorType) { + *bmp.getAddr16(x, y) = (use_color2 ? color2 : color1); + } + } + } + } + + bool ColorComponentsClose(SkColor component1, + SkColor component2, + SkColorType color_type) { + int c1 = static_cast<int>(component1); + int c2 = static_cast<int>(component2); + bool result = false; + switch (color_type) { + case kRGBA_8888_SkColorType: + case kBGRA_8888_SkColorType: + result = (std::abs(c1 - c2) == 0); + break; + case kRGB_565_SkColorType: + result = (std::abs(c1 - c2) <= 7); + break; + default: + break; + } + return result; + } + + bool ColorsClose(SkColor color1, SkColor color2, SkColorType color_type) { + bool red = ColorComponentsClose(SkColorGetR(color1), SkColorGetR(color2), + color_type); + bool green = ColorComponentsClose(SkColorGetG(color1), SkColorGetG(color2), + color_type); + bool blue = ColorComponentsClose(SkColorGetB(color1), SkColorGetB(color2), + color_type); + bool alpha = ColorComponentsClose(SkColorGetA(color1), SkColorGetA(color2), + color_type); + if (color_type == kRGB_565_SkColorType) { + return red && blue && green; + } + return red && blue && green && alpha; + } + + bool IsEqual(const SkBitmap& bmp1, const SkBitmap& bmp2) { + if (bmp1.isNull() && bmp2.isNull()) + return true; + if (bmp1.width() != bmp2.width() || bmp1.height() != bmp2.height()) { + LOG(ERROR) << "Bitmap geometry check failure"; + return false; + } + if (bmp1.colorType() != bmp2.colorType()) + return false; + + if (!bmp1.getPixels() || !bmp2.getPixels()) { + LOG(ERROR) << "Empty Bitmap!"; + return false; + } + for (int y = 0; y < bmp1.height(); ++y) { + for (int x = 0; x < bmp1.width(); ++x) { + if (!ColorsClose(bmp1.getColor(x, y), bmp2.getColor(x, y), + bmp1.colorType())) { + LOG(ERROR) << "Bitmap color comparision failure"; + return false; + } + } + } + return true; + } + + void BindAndAttachTextureWithPixels(GLuint src_texture, + SkColorType color_type, + const gfx::Size& src_size, + const SkBitmap& input_pixels) { + gl_->BindTexture(GL_TEXTURE_2D, src_texture); + GLenum format = 0; + switch (color_type) { + case kBGRA_8888_SkColorType: + format = GL_BGRA_EXT; + break; + case kRGBA_8888_SkColorType: + format = GL_RGBA; + break; + case kRGB_565_SkColorType: + format = GL_RGB; + break; + default: + NOTREACHED(); + } + GLenum type = (color_type == kRGB_565_SkColorType) ? GL_UNSIGNED_SHORT_5_6_5 + : GL_UNSIGNED_BYTE; + gl_->TexImage2D(GL_TEXTURE_2D, 0, format, src_size.width(), + src_size.height(), 0, format, type, + input_pixels.getPixels()); + } + + void ReadBackTexture(GLuint src_texture, + const gfx::Size& src_size, + unsigned char* pixels, + SkColorType color_type, + bool async) { + if (async) { + base::RunLoop run_loop; + helper_->ReadbackTextureAsync( + src_texture, src_size, pixels, color_type, + base::Bind(&callcallback, run_loop.QuitClosure())); + run_loop.Run(); + } else { + helper_->ReadbackTextureSync(src_texture, gfx::Rect(src_size), pixels, + color_type); + } + } + // Test basic format readback. + bool TestTextureFormatReadback(const gfx::Size& src_size, + SkColorType color_type, + bool async) { + SkImageInfo info = SkImageInfo::Make(src_size.width(), src_size.height(), + color_type, kPremul_SkAlphaType); + if (!helper_->IsReadbackConfigSupported(color_type)) { + LOG(INFO) << "Skipping test format not supported" << color_type; + return true; + } + GLuint src_texture; + gl_->GenTextures(1, &src_texture); + SkBitmap input_pixels; + input_pixels.allocPixels(info); + // Test Pattern-1, Fill with Plain color pattern. + // Erase the input bitmap with red color. + input_pixels.eraseColor(SK_ColorRED); + BindAndAttachTextureWithPixels(src_texture, color_type, src_size, + input_pixels); + SkBitmap output_pixels; + output_pixels.allocPixels(info); + // Initialize the output bitmap with Green color. + // When the readback is over output bitmap should have the red color. + output_pixels.eraseColor(SK_ColorGREEN); + uint8_t* pixels = static_cast<uint8_t*>(output_pixels.getPixels()); + ReadBackTexture(src_texture, src_size, pixels, color_type, async); + bool result = IsEqual(input_pixels, output_pixels); + if (!result) { + LOG(ERROR) << "Bitmap comparision failure Pattern-1"; + return false; + } + const int rect_w = 10, rect_h = 4, src_grid_pitch = 10, src_grid_width = 4; + const SkColor color1 = SK_ColorRED, color2 = SK_ColorBLUE; + // Test Pattern-2, Fill with Grid Pattern. + DrawGridToBitmap(src_size.width(), src_size.height(), color2, color1, + src_grid_pitch, src_grid_width, input_pixels); + BindAndAttachTextureWithPixels(src_texture, color_type, src_size, + input_pixels); + ReadBackTexture(src_texture, src_size, pixels, color_type, async); + result = IsEqual(input_pixels, output_pixels); + if (!result) { + LOG(ERROR) << "Bitmap comparision failure Pattern-2"; + return false; + } + // Test Pattern-3, Fill with CheckerBoard Pattern. + DrawCheckerToBitmap(src_size.width(), src_size.height(), color1, color2, + rect_w, rect_h, input_pixels); + BindAndAttachTextureWithPixels(src_texture, color_type, src_size, + input_pixels); + ReadBackTexture(src_texture, src_size, pixels, color_type, async); + result = IsEqual(input_pixels, output_pixels); + if (!result) { + LOG(ERROR) << "Bitmap comparision failure Pattern-3"; + return false; + } + gl_->DeleteTextures(1, &src_texture); + if (HasFailure()) { + return false; + } + return true; + } + + void TestAddOps(int src, int dst, bool scale_x, bool allow3) { + std::deque<GLHelperScaling::ScaleOp> ops; + GLHelperScaling::ScaleOp::AddOps(src, dst, scale_x, allow3, &ops); + // Scale factor 3 is a special case. + // It is currently only allowed by itself. + if (allow3 && dst * 3 >= src && dst * 2 < src) { + EXPECT_EQ(ops[0].scale_factor, 3); + EXPECT_EQ(ops.size(), 1U); + EXPECT_EQ(ops[0].scale_x, scale_x); + EXPECT_EQ(ops[0].scale_size, dst); + return; + } + + for (size_t i = 0; i < ops.size(); i++) { + EXPECT_EQ(ops[i].scale_x, scale_x); + if (i == 0) { + // Only the first op is allowed to be a scale up. + // (Scaling up *after* scaling down would make it fuzzy.) + EXPECT_TRUE(ops[0].scale_factor == 0 || ops[0].scale_factor == 2); + } else { + // All other operations must be 50% downscales. + EXPECT_EQ(ops[i].scale_factor, 2); + } + } + // Check that the scale factors make sense and add up. + int tmp = dst; + for (int i = static_cast<int>(ops.size() - 1); i >= 0; i--) { + EXPECT_EQ(tmp, ops[i].scale_size); + if (ops[i].scale_factor == 0) { + EXPECT_EQ(i, 0); + EXPECT_GT(tmp, src); + tmp = src; + } else { + tmp *= ops[i].scale_factor; + } + } + EXPECT_EQ(tmp, src); + } + + void CheckPipeline2(int xsize, + int ysize, + int dst_xsize, + int dst_ysize, + const std::string& description) { + std::vector<GLHelperScaling::ScalerStage> stages; + helper_scaling_->ConvertScalerOpsToScalerStages( + GLHelper::SCALER_QUALITY_GOOD, gfx::Size(xsize, ysize), + gfx::Rect(0, 0, xsize, ysize), gfx::Size(dst_xsize, dst_ysize), false, + false, &x_ops_, &y_ops_, &stages); + EXPECT_EQ(x_ops_.size(), 0U); + EXPECT_EQ(y_ops_.size(), 0U); + ValidateScalerStages(GLHelper::SCALER_QUALITY_GOOD, stages, + gfx::Size(dst_xsize, dst_ysize), ""); + EXPECT_EQ(PrintStages(stages), description); + } + + void CheckOptimizationsTest() { + // Basic upscale. X and Y should be combined into one pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000)); + CheckPipeline2(1024, 768, 2000, 2000, "1024x768 -> 2000x2000 bilinear\n"); + + // X scaled 1/2, Y upscaled, should still be one pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000)); + CheckPipeline2(1024, 768, 512, 2000, "1024x768 -> 512x2000 bilinear\n"); + + // X upscaled, Y scaled 1/2, one bilinear pass + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384)); + CheckPipeline2(1024, 768, 2000, 384, "1024x768 -> 2000x384 bilinear\n"); + + // X scaled 1/2, Y scaled 1/2, one bilinear pass + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384)); + CheckPipeline2(1024, 768, 512, 384, "1024x768 -> 512x384 bilinear\n"); + + // X scaled 1/2, Y scaled to 60%, one bilinear2 pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + CheckPipeline2(100, 100, 50, 60, "100x100 -> 50x60 bilinear2 Y\n"); + + // X scaled to 60%, Y scaled 1/2, one bilinear2 pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 50)); + CheckPipeline2(100, 100, 60, 50, "100x100 -> 60x50 bilinear2 X\n"); + + // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + CheckPipeline2(100, 100, 60, 60, "100x100 -> 60x60 bilinear2x2\n"); + + // X scaled to 40%, Y scaled 40%, two bilinear3 passes. + x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40)); + y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40)); + CheckPipeline2(100, 100, 40, 40, + "100x100 -> 100x40 bilinear3 Y\n" + "100x40 -> 40x40 bilinear3 X\n"); + + // X scaled to 60%, Y scaled 40% + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40)); + CheckPipeline2(100, 100, 60, 40, + "100x100 -> 100x40 bilinear3 Y\n" + "100x40 -> 60x40 bilinear2 X\n"); + + // X scaled to 40%, Y scaled 60% + x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + CheckPipeline2(100, 100, 40, 60, + "100x100 -> 100x60 bilinear2 Y\n" + "100x60 -> 40x60 bilinear3 X\n"); + + // X scaled to 30%, Y scaled 30% + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 30)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); + CheckPipeline2(100, 100, 30, 30, + "100x100 -> 100x30 bilinear4 Y\n" + "100x30 -> 30x30 bilinear4 X\n"); + + // X scaled to 50%, Y scaled 30% + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); + CheckPipeline2(100, 100, 50, 30, "100x100 -> 50x30 bilinear4 Y\n"); + + // X scaled to 150%, Y scaled 30% + // Note that we avoid combinding X and Y passes + // as that would probably be LESS efficient here. + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 150)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); + CheckPipeline2(100, 100, 150, 30, + "100x100 -> 100x30 bilinear4 Y\n" + "100x30 -> 150x30 bilinear\n"); + + // X scaled to 1%, Y scaled 1% + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 128)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 64)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 32)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 16)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 8)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 4)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 2)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 1)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 128)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 64)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 32)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 16)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 8)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 4)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 2)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 1)); + CheckPipeline2(100, 100, 1, 1, + "100x100 -> 100x32 bilinear4 Y\n" + "100x32 -> 100x4 bilinear4 Y\n" + "100x4 -> 64x1 bilinear2x2\n" + "64x1 -> 8x1 bilinear4 X\n" + "8x1 -> 1x1 bilinear4 X\n"); + } + + std::unique_ptr<gpu::GLInProcessContext> context_; + gpu::gles2::GLES2Interface* gl_; + std::unique_ptr<GLHelper> helper_; + std::unique_ptr<GLHelperScaling> helper_scaling_; + std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; +}; + +class GLHelperPixelTest : public GLHelperTest { + private: + gl::DisableNullDrawGLBindings enable_pixel_output_; +}; + +TEST_F(GLHelperTest, RGBASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kRGBA_8888_SkColorType, false); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, BGRASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kBGRA_8888_SkColorType, false); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, RGB565SyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kRGB_565_SkColorType, false); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, RGBAASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kRGBA_8888_SkColorType, true); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, BGRAASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kBGRA_8888_SkColorType, true); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, RGB565ASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kRGB_565_SkColorType, true); + EXPECT_EQ(result, true); +} + +int kRGBReadBackSizes[] = {3, 6, 16}; + +class GLHelperPixelReadbackTest + : public GLHelperPixelTest, + public ::testing::WithParamInterface<std::tr1::tuple<unsigned int, + unsigned int, + unsigned int, + unsigned int, + unsigned int>> {}; + +// Per pixel tests, all sizes are small so that we can print +// out the generated bitmaps. +TEST_P(GLHelperPixelReadbackTest, ScaleTest) { + unsigned int q_index = std::tr1::get<0>(GetParam()); + unsigned int x = std::tr1::get<1>(GetParam()); + unsigned int y = std::tr1::get<2>(GetParam()); + unsigned int dst_x = std::tr1::get<3>(GetParam()); + unsigned int dst_y = std::tr1::get<4>(GetParam()); + + for (int flip = 0; flip <= 1; flip++) { + for (int pattern = 0; pattern < 3; pattern++) { + TestScale(kRGBReadBackSizes[x], kRGBReadBackSizes[y], + kRGBReadBackSizes[dst_x], kRGBReadBackSizes[dst_y], pattern, + q_index, flip == 1); + if (HasFailure()) { + return; + } + } + } +} + +// Per pixel tests, all sizes are small so that we can print +// out the generated bitmaps. +TEST_P(GLHelperPixelReadbackTest, CropScaleReadbackAndCleanTextureTest) { + unsigned int q_index = std::tr1::get<0>(GetParam()); + unsigned int x = std::tr1::get<1>(GetParam()); + unsigned int y = std::tr1::get<2>(GetParam()); + unsigned int dst_x = std::tr1::get<3>(GetParam()); + unsigned int dst_y = std::tr1::get<4>(GetParam()); + + const SkColorType kColorTypes[] = { + kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType}; + for (size_t color_type = 0; color_type < arraysize(kColorTypes); + color_type++) { + for (int pattern = 0; pattern < 3; pattern++) { + TestCropScaleReadbackAndCleanTexture( + kRGBReadBackSizes[x], kRGBReadBackSizes[y], kRGBReadBackSizes[dst_x], + kRGBReadBackSizes[dst_y], pattern, kColorTypes[color_type], false, + q_index); + if (HasFailure()) + return; + } + } +} + +INSTANTIATE_TEST_CASE_P( + , + GLHelperPixelReadbackTest, + ::testing::Combine( + ::testing::Range<unsigned int>(0, arraysize(kQualities)), + ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)), + ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)), + ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)), + ::testing::Range<unsigned int>(0, arraysize(kRGBReadBackSizes)))); + +// Validate that all scaling generates valid pipelines. +TEST_F(GLHelperTest, ValidateScalerPipelines) { + int sizes[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096}; + for (size_t q = 0; q < arraysize(kQualities); q++) { + for (size_t x = 0; x < arraysize(sizes); x++) { + for (size_t y = 0; y < arraysize(sizes); y++) { + for (size_t dst_x = 0; dst_x < arraysize(sizes); dst_x++) { + for (size_t dst_y = 0; dst_y < arraysize(sizes); dst_y++) { + TestScalerPipeline(q, sizes[x], sizes[y], sizes[dst_x], + sizes[dst_y]); + if (HasFailure()) { + return; + } + } + } + } + } + } +} + +// Make sure we don't create overly complicated pipelines +// for a few common use cases. +TEST_F(GLHelperTest, CheckSpecificPipelines) { + // Upscale should be single pass. + CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 1024, 700, 1280, 720, + "1024x700 -> 1280x720 bilinear\n"); + // Slight downscale should use BILINEAR2X2. + CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 1280, 720, 1024, 700, + "1280x720 -> 1024x700 bilinear2x2\n"); + // Most common tab capture pipeline on the Pixel. + // Should be using two BILINEAR3 passes. + CheckPipeline(GLHelper::SCALER_QUALITY_GOOD, 2560, 1476, 1249, 720, + "2560x1476 -> 2560x720 bilinear3 Y\n" + "2560x720 -> 1249x720 bilinear3 X\n"); +} + +TEST_F(GLHelperTest, ScalerOpTest) { + for (int allow3 = 0; allow3 <= 1; allow3++) { + for (int dst = 1; dst < 2049; dst += 1 + (dst >> 3)) { + for (int src = 1; src < 2049; src++) { + TestAddOps(src, dst, allow3 == 1, (src & 1) == 1); + if (HasFailure()) { + LOG(ERROR) << "Failed for src=" << src << " dst=" << dst + << " allow3=" << allow3; + return; + } + } + } + } +} + +TEST_F(GLHelperTest, CheckOptimizations) { + // Test in baseclass since it is friends with GLHelperScaling + CheckOptimizationsTest(); +} + +} // namespace viz + +#endif // OS_ANDROID diff --git a/chromium/components/viz/common/gpu/DEPS b/chromium/components/viz/common/gpu/DEPS new file mode 100644 index 00000000000..945aba3fc2b --- /dev/null +++ b/chromium/components/viz/common/gpu/DEPS @@ -0,0 +1,12 @@ +include_rules = [ + "+cc/output", + "+cc/resources", + "+gpu/command_buffer", + "+gpu/GLES2/gl2extchromium.h", + "+gpu/ipc", + "+gpu/skia_bindings", + "+ui/gfx", + "+third_party/khronos/GLES2/gl2.h", + "+third_party/khronos/GLES2/gl2ext.h", + "+third_party/skia/include/gpu", +] diff --git a/chromium/components/viz/common/gpu/README.md b/chromium/components/viz/common/gpu/README.md new file mode 100644 index 00000000000..f05f2922908 --- /dev/null +++ b/chromium/components/viz/common/gpu/README.md @@ -0,0 +1,9 @@ +# gpu/ + +This directory contains viz APIs for access to the gpu services. + +## ContextProvider + +The primary interface to control access to the gpu and lifetime of client-side +gpu control structures (such as the GLES2Implementation that gives access to +the command buffer). diff --git a/chromium/components/viz/common/gpu/context_cache_controller.cc b/chromium/components/viz/common/gpu/context_cache_controller.cc new file mode 100644 index 00000000000..d49abb72890 --- /dev/null +++ b/chromium/components/viz/common/gpu/context_cache_controller.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2016 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 "components/viz/common/gpu/context_cache_controller.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/synchronization/lock.h" +#include "gpu/command_buffer/client/context_support.h" +#include "third_party/skia/include/gpu/GrContext.h" + +namespace viz { +namespace { +static const int kIdleCleanupDelaySeconds = 1; +} // namespace + +ContextCacheController::ScopedToken::ScopedToken() = default; + +ContextCacheController::ScopedToken::~ScopedToken() { + DCHECK(released_); +} + +void ContextCacheController::ScopedToken::Release() { + DCHECK(!released_); + released_ = true; +} + +ContextCacheController::ContextCacheController( + gpu::ContextSupport* context_support, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : context_support_(context_support), + task_runner_(std::move(task_runner)), + weak_factory_(this) { + // The |weak_factory_| can only be used from a single thread. We + // create/destroy this class and run callbacks on a single thread, but we + // want to be able to post callbacks from multiple threads. We need a weak + // ptr to post callbacks, so acquire one here, while we're on the right + // thread. + weak_ptr_ = weak_factory_.GetWeakPtr(); +} + +ContextCacheController::~ContextCacheController() = default; + +void ContextCacheController::SetGrContext(GrContext* gr_context) { + gr_context_ = gr_context; +} + +void ContextCacheController::SetLock(base::Lock* lock) { + context_lock_ = lock; +} + +std::unique_ptr<ContextCacheController::ScopedVisibility> +ContextCacheController::ClientBecameVisible() { + if (context_lock_) + context_lock_->AssertAcquired(); + + bool became_visible = num_clients_visible_ == 0; + ++num_clients_visible_; + + if (became_visible) + context_support_->SetAggressivelyFreeResources(false); + + return base::WrapUnique(new ScopedVisibility()); +} + +void ContextCacheController::ClientBecameNotVisible( + std::unique_ptr<ScopedVisibility> scoped_visibility) { + DCHECK(scoped_visibility); + scoped_visibility->Release(); + + if (context_lock_) + context_lock_->AssertAcquired(); + + DCHECK_GT(num_clients_visible_, 0u); + --num_clients_visible_; + + if (num_clients_visible_ == 0) { + // We are freeing resources now - cancel any pending idle callbacks. + InvalidatePendingIdleCallbacks(); + + if (gr_context_) + gr_context_->freeGpuResources(); + context_support_->SetAggressivelyFreeResources(true); + } +} + +std::unique_ptr<ContextCacheController::ScopedBusy> +ContextCacheController::ClientBecameBusy() { + if (context_lock_) + context_lock_->AssertAcquired(); + + ++num_clients_busy_; + // We are busy, cancel any pending idle callbacks. + InvalidatePendingIdleCallbacks(); + + return base::WrapUnique(new ScopedBusy()); +} + +void ContextCacheController::ClientBecameNotBusy( + std::unique_ptr<ScopedBusy> scoped_busy) { + DCHECK(scoped_busy); + scoped_busy->Release(); + + if (context_lock_) + context_lock_->AssertAcquired(); + + DCHECK_GT(num_clients_busy_, 0u); + --num_clients_busy_; + + // If we have become idle and we are visible, queue a task to drop resources + // after a delay. If are not visible, we have already dropped resources. + if (num_clients_busy_ == 0 && num_clients_visible_ > 0 && task_runner_) { + // If we already have a callback pending, don't post a new one. The pending + // callback will handle posting a new callback itself. This prevents us from + // flooding the system with tasks. + if (!callback_pending_) { + { + base::AutoLock hold(current_idle_generation_lock_); + PostIdleCallback(current_idle_generation_); + } + callback_pending_ = true; + } + } +} + +void ContextCacheController::PostIdleCallback( + uint32_t current_idle_generation) const { + task_runner_->PostDelayedTask( + FROM_HERE, + base::BindOnce(&ContextCacheController::OnIdle, weak_ptr_, + current_idle_generation), + base::TimeDelta::FromSeconds(kIdleCleanupDelaySeconds)); +} + +void ContextCacheController::InvalidatePendingIdleCallbacks() { + base::AutoLock hold(current_idle_generation_lock_); + ++current_idle_generation_; +} + +void ContextCacheController::OnIdle(uint32_t idle_generation) { + // First check if we should run our idle callback at all. If we have become + // busy since scheduling, just schedule another idle callback and return. + { + base::AutoLock hold(current_idle_generation_lock_); + if (current_idle_generation_ != idle_generation) { + PostIdleCallback(current_idle_generation_); + return; + } + } + + // Try to acquire the context lock - if we can't acquire it then we've become + // busy since checking |current_idle_generation_| above. In this case, just + // re-post our idle callback and return. + if (context_lock_ && !context_lock_->Try()) { + base::AutoLock hold(current_idle_generation_lock_); + PostIdleCallback(current_idle_generation_); + return; + } + + if (gr_context_) + gr_context_->freeGpuResources(); + + // Toggle SetAggressivelyFreeResources to drop command buffer data. + context_support_->SetAggressivelyFreeResources(true); + context_support_->SetAggressivelyFreeResources(false); + + callback_pending_ = false; + + if (context_lock_) + context_lock_->Release(); +} + +} // namespace viz diff --git a/chromium/components/viz/common/gpu/context_cache_controller.h b/chromium/components/viz/common/gpu/context_cache_controller.h new file mode 100644 index 00000000000..feea7f33b3d --- /dev/null +++ b/chromium/components/viz/common/gpu/context_cache_controller.h @@ -0,0 +1,110 @@ +// Copyright (c) 2016 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 COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_ +#define COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_ + +#include <cstdint> +#include <memory> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "components/viz/common/viz_common_export.h" + +class GrContext; + +namespace base { +class Lock; +} + +namespace gpu { +class ContextSupport; +} + +namespace viz { + +// ContextCacheController manages clearing cached data on ContextProvider when +// appropriate. Currently, cache clearing is triggered when the Context +// provider transitions from Visible to Not Visible, or from Busy to Idle. As a +// ContextProvider may have multiple clients, ContextCacheController tracks +// visibility and idle status across all clients and only cleans up when +// appropriate. +class VIZ_COMMON_EXPORT ContextCacheController { + public: + class VIZ_COMMON_EXPORT ScopedToken { + public: + ~ScopedToken(); + + private: + friend class ContextCacheController; + ScopedToken(); + void Release(); + + bool released_ = false; + }; + using ScopedVisibility = ScopedToken; + using ScopedBusy = ScopedToken; + + ContextCacheController( + gpu::ContextSupport* context_support, + scoped_refptr<base::SingleThreadTaskRunner> task_runner); + virtual ~ContextCacheController(); + + void SetGrContext(GrContext* gr_context); + void SetLock(base::Lock* lock); + + // Clients of the owning ContextProvider should call this function when they + // become visible. The returned ScopedVisibility pointer must be passed back + // to ClientBecameNotVisible or it will DCHECK in its destructor. + virtual std::unique_ptr<ScopedVisibility> ClientBecameVisible(); + + // When a client becomes not visible (either due to a visibility change or + // because it is being deleted), it must pass back any ScopedVisibility + // pointers it owns via this function. + virtual void ClientBecameNotVisible( + std::unique_ptr<ScopedVisibility> scoped_visibility); + + // Clients of the owning ContextProvider may call this function when they + // become busy. The returned ScopedBusy pointer must be passed back + // to ClientBecameNotBusy or it will DCHECK in its destructor. + std::unique_ptr<ScopedBusy> ClientBecameBusy(); + + // When a client becomes not busy, it must pass back any ScopedBusy + // pointers it owns via this function. + void ClientBecameNotBusy(std::unique_ptr<ScopedBusy> scoped_busy); + + private: + void OnIdle(uint32_t idle_generation); + void PostIdleCallback(uint32_t current_idle_generation) const; + void InvalidatePendingIdleCallbacks(); + + gpu::ContextSupport* context_support_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + GrContext* gr_context_ = nullptr; + + // If set, |context_lock_| must be held before accessing any member within + // the idle callback. Exceptions to this are |current_idle_generation_|, + // which has its own lock, and weak_ptr_ and task_runner_, which may be + // accessed from multiple threads without locking. + base::Lock* context_lock_ = nullptr; + + uint32_t num_clients_visible_ = 0; + uint32_t num_clients_busy_ = 0; + bool callback_pending_ = false; + + // |current_idle_generation_lock_| must be held when accessing + // |current_idle_generation_|. |current_idle_generation_lock_| must never be + // held while acquiring |context_lock_|. + base::Lock current_idle_generation_lock_; + uint32_t current_idle_generation_ = 0; + + base::WeakPtr<ContextCacheController> weak_ptr_; + base::WeakPtrFactory<ContextCacheController> weak_factory_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_GPU_CONTEXT_CACHE_CONTROLLER_H_ diff --git a/chromium/components/viz/common/gpu/context_provider.cc b/chromium/components/viz/common/gpu/context_provider.cc new file mode 100644 index 00000000000..b54a4303698 --- /dev/null +++ b/chromium/components/viz/common/gpu/context_provider.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2016 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 "components/viz/common/gpu/context_provider.h" + +namespace viz { + +ContextProvider::ScopedContextLock::ScopedContextLock( + ContextProvider* context_provider) + : context_provider_(context_provider), + context_lock_(*context_provider_->GetLock()) { + // Allow current thread to use |context_provider_|. + context_provider_->DetachFromThread(); + busy_ = context_provider_->CacheController()->ClientBecameBusy(); +} + +ContextProvider::ScopedContextLock::~ScopedContextLock() { + // Let ContextCacheController know we are no longer busy. + context_provider_->CacheController()->ClientBecameNotBusy(std::move(busy_)); + + // Allow usage by thread for which |context_provider_| is bound to. + context_provider_->DetachFromThread(); +} + +} // namespace viz diff --git a/chromium/components/viz/common/gpu/context_provider.h b/chromium/components/viz/common/gpu/context_provider.h new file mode 100644 index 00000000000..35cc27fa1c4 --- /dev/null +++ b/chromium/components/viz/common/gpu/context_provider.h @@ -0,0 +1,104 @@ +// Copyright (c) 2013 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 COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_ +#define COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_ + +#include <stddef.h> +#include <stdint.h> + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "components/viz/common/gpu/context_cache_controller.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/command_buffer/common/capabilities.h" + +class GrContext; + +namespace base { +class Lock; +} + +namespace gpu { +class ContextSupport; +namespace gles2 { +class GLES2Interface; +} +} // namespace gpu + +namespace viz { + +class VIZ_COMMON_EXPORT ContextProvider + : public base::RefCountedThreadSafe<ContextProvider> { + public: + // Hold an instance of this lock while using a context across multiple + // threads. This only works for ContextProviders that will return a valid + // lock from GetLock(), so is not always supported. Most use of + // ContextProvider should be single-thread only on the thread that + // BindToCurrentThread is run on. + class VIZ_COMMON_EXPORT ScopedContextLock { + public: + explicit ScopedContextLock(ContextProvider* context_provider); + ~ScopedContextLock(); + + gpu::gles2::GLES2Interface* ContextGL() { + return context_provider_->ContextGL(); + } + + private: + ContextProvider* const context_provider_; + base::AutoLock context_lock_; + std::unique_ptr<ContextCacheController::ScopedBusy> busy_; + }; + + // Bind the 3d context to the current thread. This should be called before + // accessing the contexts. Calling it more than once should have no effect. + // Once this function has been called, the class should only be accessed + // from the same thread unless the function has some explicitly specified + // rules for access on a different thread. See SetupLockOnMainThread(), which + // can be used to provide access from multiple threads. + virtual bool BindToCurrentThread() = 0; + + virtual gpu::gles2::GLES2Interface* ContextGL() = 0; + virtual gpu::ContextSupport* ContextSupport() = 0; + virtual class GrContext* GrContext() = 0; + virtual ContextCacheController* CacheController() = 0; + + // Invalidates the cached OpenGL state in GrContext. + // See skia GrContext::resetContext for details. + virtual void InvalidateGrContext(uint32_t state) = 0; + + // Returns the capabilities of the currently bound 3d context. + virtual gpu::Capabilities ContextCapabilities() = 0; + + // Sets a callback to be called when the context is lost. This should be + // called from the same thread that the context is bound to. To avoid races, + // it should be called before BindToCurrentThread(). + // Implementation note: Implementations must avoid post-tasking the provided + // |lost_context_callback| directly as clients expect the method to not be + // called once they call SetLostContextCallback() again with a different + // callback. + typedef base::Closure LostContextCallback; + virtual void SetLostContextCallback( + const LostContextCallback& lost_context_callback) = 0; + + // Below are helper methods for ScopedContextLock. Use that instead of calling + // these directly. + // + // Detaches debugging thread checkers to allow use of the provider from the + // current thread. This can be called on any thread. + virtual void DetachFromThread() {} + // Returns the lock that should be held if using this context from multiple + // threads. This can be called on any thread. + virtual base::Lock* GetLock() = 0; + + protected: + friend class base::RefCountedThreadSafe<ContextProvider>; + virtual ~ContextProvider() {} +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_GPU_CONTEXT_PROVIDER_H_ diff --git a/chromium/components/viz/common/gpu/in_process_context_provider.cc b/chromium/components/viz/common/gpu/in_process_context_provider.cc new file mode 100644 index 00000000000..bd20e2548eb --- /dev/null +++ b/chromium/components/viz/common/gpu/in_process_context_provider.cc @@ -0,0 +1,154 @@ +// Copyright 2016 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 "components/viz/common/gpu/in_process_context_provider.h" + +#include <stdint.h> + +#include "base/lazy_instance.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/viz/common/resources/platform_color.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" +#include "gpu/command_buffer/service/framebuffer_completeness_cache.h" +#include "gpu/command_buffer/service/gpu_preferences.h" +#include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/shader_translator_cache.h" +#include "gpu/command_buffer/service/sync_point_manager.h" +#include "gpu/ipc/common/surface_handle.h" +#include "gpu/ipc/gl_in_process_context.h" +#include "gpu/ipc/gpu_in_process_thread_service.h" +#include "gpu/ipc/in_process_command_buffer.h" +#include "gpu/skia_bindings/grcontext_for_gles2_interface.h" +#include "third_party/khronos/GLES2/gl2.h" +#include "third_party/khronos/GLES2/gl2ext.h" +#include "third_party/skia/include/gpu/GrContext.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" +#include "ui/gfx/native_widget_types.h" + +namespace viz { + +namespace { + +gpu::gles2::ContextCreationAttribHelper CreateAttributes() { + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = -1; + attributes.depth_size = 0; + attributes.stencil_size = 8; + attributes.samples = 0; + attributes.sample_buffers = 0; + attributes.fail_if_major_perf_caveat = false; + attributes.bind_generates_resource = false; + return attributes; +} + +std::unique_ptr<gpu::GLInProcessContext> CreateInProcessContext( + scoped_refptr<gpu::InProcessCommandBuffer::Service> service, + const gpu::gles2::ContextCreationAttribHelper& attributes, + gpu::SurfaceHandle widget, + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, + gpu::ImageFactory* image_factory, + const gpu::SharedMemoryLimits& limits, + gpu::GLInProcessContext* shared_context) { + const bool is_offscreen = widget == gpu::kNullSurfaceHandle; + return base::WrapUnique(gpu::GLInProcessContext::Create( + service, nullptr, is_offscreen, widget, shared_context, attributes, + limits, gpu_memory_buffer_manager, image_factory, + base::ThreadTaskRunnerHandle::Get())); +} + +} // namespace + +InProcessContextProvider::InProcessContextProvider( + scoped_refptr<gpu::InProcessCommandBuffer::Service> service, + gpu::SurfaceHandle widget, + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, + gpu::ImageFactory* image_factory, + const gpu::SharedMemoryLimits& limits, + InProcessContextProvider* shared_context) + : attributes_(CreateAttributes()), + context_(CreateInProcessContext( + service, + attributes_, + widget, + gpu_memory_buffer_manager, + image_factory, + limits, + (shared_context ? shared_context->context_.get() : nullptr))) { + cache_controller_.reset(new ContextCacheController( + context_->GetImplementation(), base::ThreadTaskRunnerHandle::Get())); +} + +InProcessContextProvider::~InProcessContextProvider() = default; + +bool InProcessContextProvider::BindToCurrentThread() { + return !!context_; +} + +gpu::gles2::GLES2Interface* InProcessContextProvider::ContextGL() { + return context_->GetImplementation(); +} + +gpu::ContextSupport* InProcessContextProvider::ContextSupport() { + return context_->GetImplementation(); +} + +class GrContext* InProcessContextProvider::GrContext() { + if (gr_context_) + return gr_context_->get(); + + gr_context_.reset(new skia_bindings::GrContextForGLES2Interface( + ContextGL(), ContextCapabilities())); + return gr_context_->get(); +} + +ContextCacheController* InProcessContextProvider::CacheController() { + return cache_controller_.get(); +} + +void InProcessContextProvider::InvalidateGrContext(uint32_t state) { + if (gr_context_) + gr_context_->ResetContext(state); +} + +base::Lock* InProcessContextProvider::GetLock() { + return &context_lock_; +} + +gpu::Capabilities InProcessContextProvider::ContextCapabilities() { + return context_->GetCapabilities(); +} + +void InProcessContextProvider::SetLostContextCallback( + const LostContextCallback& lost_context_callback) { + // This code lives in the GPU process and so this would go away + // if the context is lost? +} + +uint32_t InProcessContextProvider::GetCopyTextureInternalFormat() { + if (attributes_.alpha_size > 0) + return GL_RGBA; + DCHECK_NE(attributes_.red_size, 0); + DCHECK_NE(attributes_.green_size, 0); + DCHECK_NE(attributes_.blue_size, 0); + return GL_RGB; +} + +void InProcessContextProvider::SetSwapBuffersCompletionCallback( + const gpu::InProcessCommandBuffer::SwapBuffersCompletionCallback& + callback) { + context_->SetSwapBuffersCompletionCallback(callback); +} + +void InProcessContextProvider::SetUpdateVSyncParametersCallback( + const gpu::InProcessCommandBuffer::UpdateVSyncParametersCallback& + callback) { + context_->SetUpdateVSyncParametersCallback(callback); +} + +} // namespace viz diff --git a/chromium/components/viz/common/gpu/in_process_context_provider.h b/chromium/components/viz/common/gpu/in_process_context_provider.h new file mode 100644 index 00000000000..493191fc389 --- /dev/null +++ b/chromium/components/viz/common/gpu/in_process_context_provider.h @@ -0,0 +1,83 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_ +#define COMPONENTS_VIZ_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_ + +#include <stdint.h> + +#include <memory> + +#include "base/compiler_specific.h" +#include "base/synchronization/lock.h" +#include "components/viz/common/gpu/context_cache_controller.h" +#include "components/viz/common/gpu/context_provider.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" +#include "gpu/ipc/in_process_command_buffer.h" +#include "ui/gfx/native_widget_types.h" + +class GrContext; + +namespace gpu { +class GLInProcessContext; +class GpuMemoryBufferManager; +class ImageFactory; +struct SharedMemoryLimits; +} // namespace gpu + +namespace skia_bindings { +class GrContextForGLES2Interface; +} + +namespace viz { + +class VIZ_COMMON_EXPORT InProcessContextProvider + : public NON_EXPORTED_BASE(ContextProvider) { + public: + InProcessContextProvider( + scoped_refptr<gpu::InProcessCommandBuffer::Service> service, + gpu::SurfaceHandle widget, + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, + gpu::ImageFactory* image_factory, + const gpu::SharedMemoryLimits& limits, + InProcessContextProvider* shared_context); + + bool BindToCurrentThread() override; + gpu::gles2::GLES2Interface* ContextGL() override; + gpu::ContextSupport* ContextSupport() override; + class GrContext* GrContext() override; + ContextCacheController* CacheController() override; + void InvalidateGrContext(uint32_t state) override; + base::Lock* GetLock() override; + gpu::Capabilities ContextCapabilities() override; + void SetLostContextCallback( + const LostContextCallback& lost_context_callback) override; + + uint32_t GetCopyTextureInternalFormat(); + + void SetSwapBuffersCompletionCallback( + const gpu::InProcessCommandBuffer::SwapBuffersCompletionCallback& + callback); + + void SetUpdateVSyncParametersCallback( + const gpu::InProcessCommandBuffer::UpdateVSyncParametersCallback& + callback); + + protected: + friend class base::RefCountedThreadSafe<InProcessContextProvider>; + ~InProcessContextProvider() override; + + private: + const gpu::gles2::ContextCreationAttribHelper attributes_; + + base::Lock context_lock_; + std::unique_ptr<gpu::GLInProcessContext> context_; + std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_; + std::unique_ptr<ContextCacheController> cache_controller_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_GPU_IN_PROCESS_CONTEXT_PROVIDER_H_ diff --git a/chromium/components/viz/common/hit_test/DEPS b/chromium/components/viz/common/hit_test/DEPS new file mode 100644 index 00000000000..b49b8636b9b --- /dev/null +++ b/chromium/components/viz/common/hit_test/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+ui/gfx/geometry", + "+ui/gfx/transform.h", +]
\ No newline at end of file diff --git a/chromium/components/viz/common/hit_test/aggregated_hit_test_region.h b/chromium/components/viz/common/hit_test/aggregated_hit_test_region.h new file mode 100644 index 00000000000..b90bfb19a6e --- /dev/null +++ b/chromium/components/viz/common/hit_test/aggregated_hit_test_region.h @@ -0,0 +1,49 @@ +// Copyright 2017 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 COMPONENTS_VIZ_COMMON_HIT_TEST_AGGREGATED_HIT_TEST_REGION_H_ +#define COMPONENTS_VIZ_COMMON_HIT_TEST_AGGREGATED_HIT_TEST_REGION_H_ + +#include <stdint.h> + +#include "components/viz/common/surfaces/surface_id.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/transform.h" + +namespace viz { + +// A AggregatedHitTestRegion element with child_count of kEndOfList indicates +// the last element and end of the list. +constexpr int kEndOfList = -1; + +// An array of AggregatedHitTestRegion elements is used to define the +// aggregated hit-test data for the Display. +// +// It is designed to be in shared memory so that the viz service can +// write the hit_test data, and the viz host can read without +// process hops. +struct AggregatedHitTestRegion { + // The FrameSinkId corresponding to this region. Events that match + // are routed to this surface. + FrameSinkId frame_sink_id; + + // Flags to indicate the type of region as defined for + // mojom::HitTestRegion + uint32_t flags; + + // The rectangle that defines the region in parent region's coordinate space. + gfx::Rect rect; + + // The transform applied to the rect in parent region's coordinate space. + gfx::Transform transform; + + // The number of children including their children below this entry. + // If this element is not matched then child_count elements can be skipped + // to move to the next entry. + int child_count; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_HIT_TEST_AGGREGATED_HIT_TEST_REGION_H_ diff --git a/chromium/components/viz/common/quads/DEPS b/chromium/components/viz/common/quads/DEPS new file mode 100644 index 00000000000..766ac6bb3c0 --- /dev/null +++ b/chromium/components/viz/common/quads/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+gpu/command_buffer/common", + "+mojo/public/cpp/bindings", + "+ui/gfx", +] diff --git a/chromium/components/viz/common/quads/README.md b/chromium/components/viz/common/quads/README.md new file mode 100644 index 00000000000..c6295e1c7da --- /dev/null +++ b/chromium/components/viz/common/quads/README.md @@ -0,0 +1,8 @@ +# quads/ + +This directory contains the transport data structures for submitting frames +from the viz client to the service, and returning data back to the client. + +The root of the structure for submitting frames is CompositorFrame. +Viz-specific types that are included in the CompositorFrame are all part +of this directory, including RenderPass, DrawQuads and SharedQuadState. diff --git a/chromium/components/viz/common/quads/resource_format.h b/chromium/components/viz/common/quads/resource_format.h new file mode 100644 index 00000000000..4ae7b0675fb --- /dev/null +++ b/chromium/components/viz/common/quads/resource_format.h @@ -0,0 +1,35 @@ +// Copyright 2013 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 COMPONENTS_VIZ_COMMON_QUADS_RESOURCE_FORMAT_H_ +#define COMPONENTS_VIZ_COMMON_QUADS_RESOURCE_FORMAT_H_ + +#include "base/logging.h" +#include "ui/gfx/buffer_types.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 viz { + +// Keep in sync with arrays below. +enum ResourceFormat { + RGBA_8888, + RGBA_4444, + BGRA_8888, + ALPHA_8, + LUMINANCE_8, + RGB_565, + ETC1, + RED_8, + LUMINANCE_F16, + RGBA_F16, + RESOURCE_FORMAT_MAX = RGBA_F16, +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_QUADS_RESOURCE_FORMAT_H_ diff --git a/chromium/components/viz/common/quads/shared_bitmap.cc b/chromium/components/viz/common/quads/shared_bitmap.cc new file mode 100644 index 00000000000..c17074bd391 --- /dev/null +++ b/chromium/components/viz/common/quads/shared_bitmap.cc @@ -0,0 +1,82 @@ +// Copyright 2013 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 "components/viz/common/quads/shared_bitmap.h" + +#include <stddef.h> +#include <stdint.h> + +#include "base/logging.h" +#include "base/memory/shared_memory_handle.h" +#include "base/numerics/safe_math.h" +#include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" + +namespace viz { + +SharedBitmap::SharedBitmap(uint8_t* pixels, + const SharedBitmapId& id, + uint32_t sequence_number) + : pixels_(pixels), id_(id), sequence_number_(sequence_number) {} + +SharedBitmap::~SharedBitmap() {} + +// static +bool SharedBitmap::SizeInBytes(const gfx::Size& size, size_t* size_in_bytes) { + if (size.IsEmpty()) + return false; + base::CheckedNumeric<size_t> s = 4; + s *= size.width(); + s *= size.height(); + if (!s.IsValid()) + return false; + *size_in_bytes = s.ValueOrDie(); + return true; +} + +// static +size_t SharedBitmap::CheckedSizeInBytes(const gfx::Size& size) { + CHECK(!size.IsEmpty()); + base::CheckedNumeric<size_t> s = 4; + s *= size.width(); + s *= size.height(); + return s.ValueOrDie(); +} + +// static +size_t SharedBitmap::UncheckedSizeInBytes(const gfx::Size& size) { + DCHECK(VerifySizeInBytes(size)); + size_t s = 4; + s *= size.width(); + s *= size.height(); + return s; +} + +// static +bool SharedBitmap::VerifySizeInBytes(const gfx::Size& size) { + if (size.IsEmpty()) + return false; + base::CheckedNumeric<size_t> s = 4; + s *= size.width(); + s *= size.height(); + return s.IsValid(); +} + +// static +SharedBitmapId SharedBitmap::GenerateId() { + SharedBitmapId id; + // Needs cryptographically-secure random numbers. + base::RandBytes(id.name, sizeof(id.name)); + return id; +} + +base::trace_event::MemoryAllocatorDumpGuid GetSharedBitmapGUIDForTracing( + const SharedBitmapId& bitmap_id) { + auto bitmap_id_hex = base::HexEncode(bitmap_id.name, sizeof(bitmap_id.name)); + return base::trace_event::MemoryAllocatorDumpGuid( + base::StringPrintf("sharedbitmap-x-process/%s", bitmap_id_hex.c_str())); +} + +} // namespace viz diff --git a/chromium/components/viz/common/quads/shared_bitmap.h b/chromium/components/viz/common/quads/shared_bitmap.h new file mode 100644 index 00000000000..0d5fa83a48e --- /dev/null +++ b/chromium/components/viz/common/quads/shared_bitmap.h @@ -0,0 +1,77 @@ +// Copyright 2013 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 COMPONENTS_VIZ_COMMON_QUADS_SHARED_BITMAP_H_ +#define COMPONENTS_VIZ_COMMON_QUADS_SHARED_BITMAP_H_ + +#include <stddef.h> +#include <stdint.h> + +#include "base/hash.h" +#include "base/macros.h" +#include "base/trace_event/memory_allocator_dump.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/command_buffer/common/mailbox.h" +#include "ui/gfx/geometry/size.h" + +namespace base { +class SharedMemoryHandle; +} + +namespace viz { +using SharedBitmapId = gpu::Mailbox; + +struct SharedBitmapIdHash { + size_t operator()(const SharedBitmapId& id) const { + return base::Hash(reinterpret_cast<const char*>(id.name), sizeof(id.name)); + } +}; + +VIZ_COMMON_EXPORT base::trace_event::MemoryAllocatorDumpGuid +GetSharedBitmapGUIDForTracing(const SharedBitmapId& bitmap_id); + +class VIZ_COMMON_EXPORT SharedBitmap { + public: + SharedBitmap(uint8_t* pixels, + const SharedBitmapId& id, + uint32_t sequence_number); + + virtual ~SharedBitmap(); + + uint8_t* pixels() { return pixels_; } + + const SharedBitmapId& id() { return id_; } + + // The sequence number that ClientSharedBitmapManager assigned to this + // SharedBitmap. + uint32_t sequence_number() const { return sequence_number_; } + + // Returns the shared memory's handle when the back end is base::SharedMemory. + // Otherwise, this returns an invalid handle. + virtual base::SharedMemoryHandle GetSharedMemoryHandle() const = 0; + + // Returns true if the size is valid and false otherwise. + static bool SizeInBytes(const gfx::Size& size, size_t* size_in_bytes); + // Dies with a CRASH() if the size can not be represented as a positive number + // of bytes. + static size_t CheckedSizeInBytes(const gfx::Size& size); + // Returns the size in bytes but may overflow or return 0. Only do this for + // sizes that have already been checked. + static size_t UncheckedSizeInBytes(const gfx::Size& size); + // Returns true if the size is valid and false otherwise. + static bool VerifySizeInBytes(const gfx::Size& size); + + static SharedBitmapId GenerateId(); + + private: + uint8_t* pixels_; + SharedBitmapId id_; + const uint32_t sequence_number_; + + DISALLOW_COPY_AND_ASSIGN(SharedBitmap); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_QUADS_SHARED_BITMAP_H_ diff --git a/chromium/components/viz/common/quads/texture_mailbox.cc b/chromium/components/viz/common/quads/texture_mailbox.cc new file mode 100644 index 00000000000..6b04c11493e --- /dev/null +++ b/chromium/components/viz/common/quads/texture_mailbox.cc @@ -0,0 +1,101 @@ +// Copyright 2013 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 "components/viz/common/quads/texture_mailbox.h" + +#include <stddef.h> +#include <stdint.h> + +#include "base/logging.h" +#include "components/viz/common/quads/shared_bitmap.h" + +namespace viz { + +TextureMailbox::TextureMailbox() : shared_bitmap_(NULL) {} + +TextureMailbox::TextureMailbox(const TextureMailbox& other) = default; + +TextureMailbox::TextureMailbox(const gpu::MailboxHolder& mailbox_holder) + : mailbox_holder_(mailbox_holder), + shared_bitmap_(NULL), + is_overlay_candidate_(false), +#if defined(OS_ANDROID) + is_backed_by_surface_texture_(false), + wants_promotion_hint_(false), +#endif + secure_output_only_(false), + nearest_neighbor_(false) { +} + +TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + uint32_t target) + : mailbox_holder_(mailbox, sync_token, target), + shared_bitmap_(NULL), + is_overlay_candidate_(false), +#if defined(OS_ANDROID) + is_backed_by_surface_texture_(false), + wants_promotion_hint_(false), +#endif + secure_output_only_(false), + nearest_neighbor_(false) { +} + +TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + uint32_t target, + const gfx::Size& size_in_pixels, + bool is_overlay_candidate, + bool secure_output_only) + : mailbox_holder_(mailbox, sync_token, target), + shared_bitmap_(nullptr), + size_in_pixels_(size_in_pixels), + is_overlay_candidate_(is_overlay_candidate), +#if defined(OS_ANDROID) + is_backed_by_surface_texture_(false), + wants_promotion_hint_(false), +#endif + secure_output_only_(secure_output_only), + nearest_neighbor_(false) { + DCHECK(!is_overlay_candidate || !size_in_pixels.IsEmpty()); +} + +TextureMailbox::TextureMailbox(SharedBitmap* shared_bitmap, + const gfx::Size& size_in_pixels) + : shared_bitmap_(shared_bitmap), + size_in_pixels_(size_in_pixels), + is_overlay_candidate_(false), +#if defined(OS_ANDROID) + is_backed_by_surface_texture_(false), + wants_promotion_hint_(false), +#endif + secure_output_only_(false), + nearest_neighbor_(false) { + // If an embedder of cc gives an invalid TextureMailbox, we should crash + // here to identify the offender. + CHECK(SharedBitmap::VerifySizeInBytes(size_in_pixels_)); +} + +TextureMailbox::~TextureMailbox() {} + +bool TextureMailbox::Equals(const TextureMailbox& other) const { + if (other.IsTexture()) { + return IsTexture() && !memcmp(mailbox_holder_.mailbox.name, + other.mailbox_holder_.mailbox.name, + sizeof(mailbox_holder_.mailbox.name)); + } else if (other.IsSharedMemory()) { + return IsSharedMemory() && (shared_bitmap_ == other.shared_bitmap_); + } + + DCHECK(!other.IsValid()); + return !IsValid(); +} + +size_t TextureMailbox::SharedMemorySizeInBytes() const { + // UncheckedSizeInBytes is okay because we VerifySizeInBytes in the + // constructor and the field is immutable. + return SharedBitmap::UncheckedSizeInBytes(size_in_pixels_); +} + +} // namespace viz diff --git a/chromium/components/viz/common/quads/texture_mailbox.h b/chromium/components/viz/common/quads/texture_mailbox.h new file mode 100644 index 00000000000..3c24f3bb9dc --- /dev/null +++ b/chromium/components/viz/common/quads/texture_mailbox.h @@ -0,0 +1,123 @@ +// Copyright 2013 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 COMPONENTS_VIZ_COMMON_QUADS_TEXTURE_MAILBOX_H_ +#define COMPONENTS_VIZ_COMMON_QUADS_TEXTURE_MAILBOX_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <string> + +#include "base/memory/shared_memory.h" +#include "build/build_config.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/command_buffer/common/mailbox_holder.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "ui/gfx/color_space.h" +#include "ui/gfx/geometry/size.h" + +namespace cc { +namespace mojom { +class TextureMailboxDataView; +} +} // namespace cc + +namespace viz { + +class SharedBitmap; + +// TODO(skaslev, danakj) Rename this class more apropriately since now it +// can hold a shared memory resource as well as a texture mailbox. +class VIZ_COMMON_EXPORT TextureMailbox { + public: + TextureMailbox(); + TextureMailbox(const TextureMailbox& other); + explicit TextureMailbox(const gpu::MailboxHolder& mailbox_holder); + TextureMailbox(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + uint32_t target); + TextureMailbox(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + uint32_t target, + const gfx::Size& size_in_pixels, + bool is_overlay_candidate, + bool secure_output_only); + TextureMailbox(SharedBitmap* shared_bitmap, const gfx::Size& size_in_pixels); + + ~TextureMailbox(); + + bool IsValid() const { return IsTexture() || IsSharedMemory(); } + bool IsTexture() const { return !mailbox_holder_.mailbox.IsZero(); } + bool IsSharedMemory() const { return shared_bitmap_ != NULL; } + bool HasSyncToken() const { return mailbox_holder_.sync_token.HasData(); } + + int8_t* GetSyncTokenData() { return mailbox_holder_.sync_token.GetData(); } + + bool Equals(const TextureMailbox&) const; + + const gpu::Mailbox& mailbox() const { return mailbox_holder_.mailbox; } + const int8_t* name() const { return mailbox().name; } + uint32_t target() const { return mailbox_holder_.texture_target; } + const gpu::SyncToken& sync_token() const { + return mailbox_holder_.sync_token; + } + void set_sync_token(const gpu::SyncToken& sync_token) { + mailbox_holder_.sync_token = sync_token; + } + + bool is_overlay_candidate() const { return is_overlay_candidate_; } + void set_is_overlay_candidate(bool overlay_candidate) { + is_overlay_candidate_ = overlay_candidate; + } + bool secure_output_only() const { return secure_output_only_; } + bool nearest_neighbor() const { return nearest_neighbor_; } + void set_nearest_neighbor(bool nearest_neighbor) { + nearest_neighbor_ = nearest_neighbor; + } + const gfx::ColorSpace& color_space() const { return color_space_; } + void set_color_space(const gfx::ColorSpace& color_space) { + color_space_ = color_space; + } + + // This is valid if allow_overlau() or IsSharedMemory() is true. + gfx::Size size_in_pixels() const { return size_in_pixels_; } + + SharedBitmap* shared_bitmap() const { return shared_bitmap_; } + size_t SharedMemorySizeInBytes() const; + +#if defined(OS_ANDROID) + bool is_backed_by_surface_texture() const { + return is_backed_by_surface_texture_; + } + + void set_is_backed_by_surface_texture(bool value) { + is_backed_by_surface_texture_ = value; + } + + bool wants_promotion_hint() const { return wants_promotion_hint_; } + + void set_wants_promotion_hint(bool value) { wants_promotion_hint_ = value; } +#endif + + private: + friend struct mojo::StructTraits<cc::mojom::TextureMailboxDataView, + TextureMailbox>; + + gpu::MailboxHolder mailbox_holder_; + SharedBitmap* shared_bitmap_; + gfx::Size size_in_pixels_; + bool is_overlay_candidate_; +#if defined(OS_ANDROID) + bool is_backed_by_surface_texture_; + bool wants_promotion_hint_; +#endif + bool secure_output_only_; + bool nearest_neighbor_; + gfx::ColorSpace color_space_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_QUADS_TEXTURE_MAILBOX_H_ diff --git a/chromium/components/viz/common/resources/DEPS b/chromium/components/viz/common/resources/DEPS new file mode 100644 index 00000000000..ce3dad47826 --- /dev/null +++ b/chromium/components/viz/common/resources/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+gpu/GLES2", + "+third_party/khronos/GLES2", + "+third_party/skia", + "+ui/gfx", +] diff --git a/chromium/components/viz/common/resources/README.md b/chromium/components/viz/common/resources/README.md new file mode 100644 index 00000000000..70939e6867e --- /dev/null +++ b/chromium/components/viz/common/resources/README.md @@ -0,0 +1,7 @@ +# resources/ + +This directory contains abstractions and helpers to allocate and use both +gpu and software resources. + +This code provides means to decide on texture formats, including compressed +formats, or classes to manage the lifetime and (re)-use of resources. diff --git a/chromium/components/viz/common/resources/buffer_to_texture_target_map.cc b/chromium/components/viz/common/resources/buffer_to_texture_target_map.cc new file mode 100644 index 00000000000..83877f4e1fb --- /dev/null +++ b/chromium/components/viz/common/resources/buffer_to_texture_target_map.cc @@ -0,0 +1,72 @@ +// Copyright 2016 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 "components/viz/common/resources/buffer_to_texture_target_map.h" + +#include <vector> + +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "gpu/GLES2/gl2extchromium.h" + +namespace viz { + +BufferToTextureTargetMap StringToBufferToTextureTargetMap( + const std::string& str) { + BufferToTextureTargetMap map; + std::vector<std::string> entries = + base::SplitString(str, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + for (const auto& entry : entries) { + std::vector<std::string> fields = base::SplitString( + entry, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + CHECK_EQ(fields.size(), 3u); + uint32_t usage = 0; + uint32_t format = 0; + uint32_t target = 0; + bool succeeded = base::StringToUint(fields[0], &usage) && + base::StringToUint(fields[1], &format) && + base::StringToUint(fields[2], &target); + CHECK(succeeded); + CHECK_LE(usage, static_cast<uint32_t>(gfx::BufferUsage::LAST)); + CHECK_LE(format, static_cast<uint32_t>(gfx::BufferFormat::LAST)); + map.insert(BufferToTextureTargetMap::value_type( + BufferToTextureTargetKey(static_cast<gfx::BufferUsage>(usage), + static_cast<gfx::BufferFormat>(format)), + target)); + } + return map; +} + +std::string BufferToTextureTargetMapToString( + const BufferToTextureTargetMap& map) { + std::string str; + for (const auto& entry : map) { + if (!str.empty()) + str += ";"; + str += base::UintToString(static_cast<uint32_t>(entry.first.first)); + str += ","; + str += base::UintToString(static_cast<uint32_t>(entry.first.second)); + str += ","; + str += base::UintToString(entry.second); + } + return str; +} + +BufferToTextureTargetMap DefaultBufferToTextureTargetMapForTesting() { + BufferToTextureTargetMap image_targets; + for (int usage_idx = 0; usage_idx <= static_cast<int>(gfx::BufferUsage::LAST); + ++usage_idx) { + gfx::BufferUsage usage = static_cast<gfx::BufferUsage>(usage_idx); + for (int format_idx = 0; + format_idx <= static_cast<int>(gfx::BufferFormat::LAST); + ++format_idx) { + gfx::BufferFormat format = static_cast<gfx::BufferFormat>(format_idx); + image_targets.insert(BufferToTextureTargetMap::value_type( + BufferToTextureTargetKey(usage, format), GL_TEXTURE_2D)); + } + } + + return image_targets; +} + +} // namespace viz diff --git a/chromium/components/viz/common/resources/buffer_to_texture_target_map.h b/chromium/components/viz/common/resources/buffer_to_texture_target_map.h new file mode 100644 index 00000000000..27539475287 --- /dev/null +++ b/chromium/components/viz/common/resources/buffer_to_texture_target_map.h @@ -0,0 +1,37 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_RESOURCES_BUFFER_TO_TEXTURE_TARGET_MAP_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_BUFFER_TO_TEXTURE_TARGET_MAP_H_ + +#include <map> +#include <string> + +#include "components/viz/common/viz_common_export.h" +#include "ui/gfx/buffer_types.h" + +namespace viz { +// A map of GPU Memory Buffer usage/format to GL texture target. +using BufferToTextureTargetKey = std::pair<gfx::BufferUsage, gfx::BufferFormat>; +using BufferToTextureTargetMap = std::map<BufferToTextureTargetKey, uint32_t>; + +// Converts a serialized ImageTextureTargetsMap back to the runtime format. +// Serialization takes the form: +// "usage,format,target;usage,format,target;...;usage,format,target" +VIZ_COMMON_EXPORT BufferToTextureTargetMap +StringToBufferToTextureTargetMap(const std::string& str); + +// Converts an ImageTextureTargetsMap to a string representation of the format: +// "usage,format,target;usage,format,target;...;usage,format,target" +VIZ_COMMON_EXPORT std::string BufferToTextureTargetMapToString( + const BufferToTextureTargetMap& map); + +// Returns a default-initialized BufferToTextureTargetsMap where every entry +// maps to GL_TEXTURE_2D. +VIZ_COMMON_EXPORT BufferToTextureTargetMap +DefaultBufferToTextureTargetMapForTesting(); + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_RESOURCES_BUFFER_TO_TEXTURE_TARGET_MAP_H_ diff --git a/chromium/components/viz/common/resources/buffer_to_texture_target_map_unittest.cc b/chromium/components/viz/common/resources/buffer_to_texture_target_map_unittest.cc new file mode 100644 index 00000000000..f037b998412 --- /dev/null +++ b/chromium/components/viz/common/resources/buffer_to_texture_target_map_unittest.cc @@ -0,0 +1,37 @@ +// Copyright 2016 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 "components/viz/common/resources/buffer_to_texture_target_map.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/buffer_types.h" + +namespace viz { +namespace { + +// Ensures that a map populated with various values can be serialized to/from +// string successfully. +TEST(BufferToTextureTargetMapTest, SerializeRoundTrip) { + BufferToTextureTargetMap test_map; + uint32_t next_value = 0; + for (int usage_idx = 0; usage_idx <= static_cast<int>(gfx::BufferUsage::LAST); + ++usage_idx) { + gfx::BufferUsage usage = static_cast<gfx::BufferUsage>(usage_idx); + for (int format_idx = 0; + format_idx <= static_cast<int>(gfx::BufferFormat::LAST); + ++format_idx) { + gfx::BufferFormat format = static_cast<gfx::BufferFormat>(format_idx); + test_map.insert(BufferToTextureTargetMap::value_type( + BufferToTextureTargetKey(usage, format), next_value++)); + } + } + + std::string serialized_map = BufferToTextureTargetMapToString(test_map); + BufferToTextureTargetMap deserialized_map = + StringToBufferToTextureTargetMap(serialized_map); + EXPECT_EQ(test_map, deserialized_map); +} + +} // namespace +} // namespace viz diff --git a/chromium/components/viz/common/resources/platform_color.h b/chromium/components/viz/common/resources/platform_color.h new file mode 100644 index 00000000000..505b6d43269 --- /dev/null +++ b/chromium/components/viz/common/resources/platform_color.h @@ -0,0 +1,77 @@ +// Copyright 2011 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 COMPONENTS_VIZ_COMMON_RESOURCES_PLATFORM_COLOR_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_PLATFORM_COLOR_H_ + +#include "base/logging.h" +#include "base/macros.h" +#include "components/viz/common/quads/resource_format.h" +#include "third_party/skia/include/core/SkTypes.h" + +namespace viz { + +class PlatformColor { + public: + enum SourceDataFormat { SOURCE_FORMAT_RGBA8, SOURCE_FORMAT_BGRA8 }; + + static SourceDataFormat Format() { + return SK_B32_SHIFT ? SOURCE_FORMAT_RGBA8 : SOURCE_FORMAT_BGRA8; + } + + // Returns the most efficient texture format for this platform. + static ResourceFormat BestTextureFormat() { + switch (Format()) { + case SOURCE_FORMAT_BGRA8: + return BGRA_8888; + case SOURCE_FORMAT_RGBA8: + return RGBA_8888; + } + NOTREACHED(); + return RGBA_8888; + } + + // Returns the most efficient supported texture format for this platform. + static ResourceFormat BestSupportedTextureFormat(bool supports_bgra8888) { + switch (Format()) { + case SOURCE_FORMAT_BGRA8: + return (supports_bgra8888) ? BGRA_8888 : RGBA_8888; + case SOURCE_FORMAT_RGBA8: + return RGBA_8888; + } + NOTREACHED(); + return RGBA_8888; + } + + // Return true if the given 32bpp resource format has the same component order + // as the platform color data format. + static bool SameComponentOrder(ResourceFormat format) { + switch (format) { + case RGBA_8888: + return Format() == SOURCE_FORMAT_RGBA8; + case BGRA_8888: + return Format() == SOURCE_FORMAT_BGRA8; + case ALPHA_8: + case LUMINANCE_8: + case RGB_565: + case RGBA_4444: + case ETC1: + case RED_8: + case LUMINANCE_F16: + case RGBA_F16: + NOTREACHED(); + return false; + } + + NOTREACHED(); + return false; + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformColor); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_RESOURCES_PLATFORM_COLOR_H_ diff --git a/chromium/components/viz/common/resources/platform_color_unittest.cc b/chromium/components/viz/common/resources/platform_color_unittest.cc new file mode 100644 index 00000000000..f801217673b --- /dev/null +++ b/chromium/components/viz/common/resources/platform_color_unittest.cc @@ -0,0 +1,42 @@ +// 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 "components/viz/common/resources/platform_color.h" + +#include <stddef.h> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace viz { +namespace { + +// Verify SameComponentOrder on this platform. +TEST(PlatformColorTest, SameComponentOrder) { + bool rgba = !!SK_B32_SHIFT; + + for (size_t i = 0; i <= RESOURCE_FORMAT_MAX; ++i) { + ResourceFormat format = static_cast<ResourceFormat>(i); + switch (format) { + case RGBA_8888: + EXPECT_EQ(rgba, PlatformColor::SameComponentOrder(format)); + break; + case BGRA_8888: + EXPECT_NE(rgba, PlatformColor::SameComponentOrder(format)); + break; + // The following formats are not platform colors. + case ALPHA_8: + case LUMINANCE_8: + case RGB_565: + case RGBA_4444: + case ETC1: + case RED_8: + case LUMINANCE_F16: + case RGBA_F16: + break; + } + } +} + +} // namespace +} // namespace viz diff --git a/chromium/components/viz/common/resources/resource_format_utils.cc b/chromium/components/viz/common/resources/resource_format_utils.cc new file mode 100644 index 00000000000..f15ddc72682 --- /dev/null +++ b/chromium/components/viz/common/resources/resource_format_utils.cc @@ -0,0 +1,177 @@ +// Copyright 2016 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 "components/viz/common/resources/resource_format_utils.h" + +#include "third_party/khronos/GLES2/gl2.h" +#include "third_party/khronos/GLES2/gl2ext.h" +#include "ui/gfx/gpu_memory_buffer.h" + +namespace viz { + +SkColorType ResourceFormatToClosestSkColorType(ResourceFormat format) { + // Use kN32_SkColorType if there is no corresponding SkColorType. + switch (format) { + case RGBA_4444: + return kARGB_4444_SkColorType; + case RGBA_8888: + case BGRA_8888: + return kN32_SkColorType; + case ALPHA_8: + return kAlpha_8_SkColorType; + case RGB_565: + return kRGB_565_SkColorType; + case LUMINANCE_8: + return kGray_8_SkColorType; + case ETC1: + case RED_8: + case LUMINANCE_F16: + return kN32_SkColorType; + case RGBA_F16: + return kRGBA_F16_SkColorType; + } + NOTREACHED(); + return kN32_SkColorType; +} + +int BitsPerPixel(ResourceFormat format) { + switch (format) { + case RGBA_F16: + return 64; + case BGRA_8888: + case RGBA_8888: + return 32; + case RGBA_4444: + case RGB_565: + case LUMINANCE_F16: + 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 + GL_HALF_FLOAT_OES, // LUMINANCE_F16 + GL_HALF_FLOAT_OES, // RGBA_F16 + }; + 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 + GL_LUMINANCE, // LUMINANCE_F16 + GL_RGBA, // RGBA_F16 + }; + 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) { + // In GLES2, the internal format must match the texture format. (It no longer + // is true in GLES3, however it still holds for the BGRA extension.) + return GLDataFormat(format); +} + +GLenum GLCopyTextureInternalFormat(ResourceFormat format) { + // In GLES2, valid formats for glCopyTexImage2D are: GL_ALPHA, GL_LUMINANCE, + // GL_LUMINANCE_ALPHA, GL_RGB, or GL_RGBA. + // Extensions typically used for glTexImage2D do not also work for + // glCopyTexImage2D. For instance GL_BGRA_EXT may not be used for + // anything but gl(Sub)TexImage2D: + // https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt + DCHECK_LE(format, RESOURCE_FORMAT_MAX); + static const GLenum format_gl_data_format[] = { + GL_RGBA, // RGBA_8888 + GL_RGBA, // RGBA_4444 + GL_RGBA, // BGRA_8888 + GL_ALPHA, // ALPHA_8 + GL_LUMINANCE, // LUMINANCE_8 + GL_RGB, // RGB_565 + GL_RGB, // ETC1 + GL_LUMINANCE, // RED_8 + GL_LUMINANCE, // LUMINANCE_F16 + GL_RGBA, // RGBA_F16 + }; + 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]; +} + +gfx::BufferFormat BufferFormat(ResourceFormat format) { + switch (format) { + case BGRA_8888: + return gfx::BufferFormat::BGRA_8888; + case RED_8: + return gfx::BufferFormat::R_8; + case RGBA_4444: + return gfx::BufferFormat::RGBA_4444; + case RGBA_8888: + return gfx::BufferFormat::RGBA_8888; + case ETC1: + return gfx::BufferFormat::ETC1; + case RGBA_F16: + return gfx::BufferFormat::RGBA_F16; + case ALPHA_8: + case LUMINANCE_8: + case RGB_565: + case LUMINANCE_F16: + break; + } + NOTREACHED(); + return gfx::BufferFormat::RGBA_8888; +} + +bool IsResourceFormatCompressed(ResourceFormat format) { + return format == ETC1; +} + +bool DoesResourceFormatSupportAlpha(ResourceFormat format) { + switch (format) { + case RGBA_4444: + case RGBA_8888: + case BGRA_8888: + case ALPHA_8: + case RGBA_F16: + return true; + case LUMINANCE_8: + case RGB_565: + case ETC1: + case RED_8: + case LUMINANCE_F16: + return false; + } + NOTREACHED(); + return false; +} + +} // namespace viz diff --git a/chromium/components/viz/common/resources/resource_format_utils.h b/chromium/components/viz/common/resources/resource_format_utils.h new file mode 100644 index 00000000000..935ccc8796e --- /dev/null +++ b/chromium/components/viz/common/resources/resource_format_utils.h @@ -0,0 +1,27 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_ + +#include "components/viz/common/quads/resource_format.h" +#include "components/viz/common/viz_common_export.h" +#include "third_party/skia/include/core/SkImageInfo.h" + +namespace viz { + +VIZ_COMMON_EXPORT SkColorType +ResourceFormatToClosestSkColorType(ResourceFormat format); +VIZ_COMMON_EXPORT int BitsPerPixel(ResourceFormat format); +VIZ_COMMON_EXPORT GLenum GLDataType(ResourceFormat format); +VIZ_COMMON_EXPORT GLenum GLDataFormat(ResourceFormat format); +VIZ_COMMON_EXPORT GLenum GLInternalFormat(ResourceFormat format); +VIZ_COMMON_EXPORT GLenum GLCopyTextureInternalFormat(ResourceFormat format); +VIZ_COMMON_EXPORT gfx::BufferFormat BufferFormat(ResourceFormat format); +VIZ_COMMON_EXPORT bool IsResourceFormatCompressed(ResourceFormat format); +VIZ_COMMON_EXPORT bool DoesResourceFormatSupportAlpha(ResourceFormat format); + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_ diff --git a/chromium/components/viz/common/resources/resource_settings.cc b/chromium/components/viz/common/resources/resource_settings.cc new file mode 100644 index 00000000000..2519c0c2f57 --- /dev/null +++ b/chromium/components/viz/common/resources/resource_settings.cc @@ -0,0 +1,18 @@ +// Copyright 2017 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 "components/viz/common/resources/resource_settings.h" + +namespace viz { + +ResourceSettings::ResourceSettings() = default; + +ResourceSettings::ResourceSettings(const ResourceSettings& other) = default; + +ResourceSettings::~ResourceSettings() = default; + +ResourceSettings& ResourceSettings::operator=(const ResourceSettings& other) = + default; + +} // namespace viz diff --git a/chromium/components/viz/common/resources/resource_settings.h b/chromium/components/viz/common/resources/resource_settings.h new file mode 100644 index 00000000000..31ab9d006d0 --- /dev/null +++ b/chromium/components/viz/common/resources/resource_settings.h @@ -0,0 +1,29 @@ +// Copyright 2017 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 COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_SETTINGS_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_SETTINGS_H_ + +#include "components/viz/common/resources/buffer_to_texture_target_map.h" +#include "components/viz/common/viz_common_export.h" + +namespace viz { + +// ResourceSettings contains all the settings that are needed to create a +// ResourceProvider. +class VIZ_COMMON_EXPORT ResourceSettings { + public: + ResourceSettings(); + ResourceSettings(const ResourceSettings& other); + ResourceSettings& operator=(const ResourceSettings& other); + ~ResourceSettings(); + + size_t texture_id_allocation_chunk_size = 64; + bool use_gpu_memory_buffer_resources = false; + BufferToTextureTargetMap buffer_to_texture_target_map; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_SETTINGS_H_ diff --git a/chromium/components/viz/common/resources/shared_bitmap_manager.h b/chromium/components/viz/common/resources/shared_bitmap_manager.h new file mode 100644 index 00000000000..630f0d852a0 --- /dev/null +++ b/chromium/components/viz/common/resources/shared_bitmap_manager.h @@ -0,0 +1,33 @@ +// Copyright 2013 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 COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_ + +#include <memory> + +#include "base/macros.h" +#include "components/viz/common/quads/shared_bitmap.h" +#include "ui/gfx/geometry/size.h" + +namespace viz { + +class SharedBitmapManager { + public: + SharedBitmapManager() {} + virtual ~SharedBitmapManager() {} + + virtual std::unique_ptr<SharedBitmap> AllocateSharedBitmap( + const gfx::Size&) = 0; + virtual std::unique_ptr<SharedBitmap> GetSharedBitmapFromId( + const gfx::Size&, + const SharedBitmapId&) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(SharedBitmapManager); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_ diff --git a/chromium/components/viz/common/surfaces/DEPS b/chromium/components/viz/common/surfaces/DEPS new file mode 100644 index 00000000000..826db9ece7e --- /dev/null +++ b/chromium/components/viz/common/surfaces/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+mojo/public/cpp/bindings", + "+ui/gfx/geometry", +] diff --git a/chromium/components/viz/common/surfaces/frame_sink_id.cc b/chromium/components/viz/common/surfaces/frame_sink_id.cc new file mode 100644 index 00000000000..2b12f1b8942 --- /dev/null +++ b/chromium/components/viz/common/surfaces/frame_sink_id.cc @@ -0,0 +1,19 @@ +// Copyright 2016 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 "components/viz/common/surfaces/frame_sink_id.h" + +#include "base/strings/stringprintf.h" + +namespace viz { + +std::string FrameSinkId::ToString() const { + return base::StringPrintf("FrameSinkId(%d, %d)", client_id_, sink_id_); +} + +std::ostream& operator<<(std::ostream& out, const FrameSinkId& frame_sink_id) { + return out << frame_sink_id.ToString(); +} + +} // namespace viz diff --git a/chromium/components/viz/common/surfaces/frame_sink_id.h b/chromium/components/viz/common/surfaces/frame_sink_id.h new file mode 100644 index 00000000000..30319d7b3eb --- /dev/null +++ b/chromium/components/viz/common/surfaces/frame_sink_id.h @@ -0,0 +1,64 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_H_ + +#include <stdint.h> + +#include <iosfwd> +#include <string> +#include <tuple> + +#include "base/hash.h" +#include "components/viz/common/viz_common_export.h" + +namespace viz { + +class VIZ_COMMON_EXPORT FrameSinkId { + public: + constexpr FrameSinkId() : client_id_(0), sink_id_(0) {} + + constexpr FrameSinkId(const FrameSinkId& other) + : client_id_(other.client_id_), sink_id_(other.sink_id_) {} + + constexpr FrameSinkId(uint32_t client_id, uint32_t sink_id) + : client_id_(client_id), sink_id_(sink_id) {} + + constexpr bool is_valid() const { return client_id_ != 0 || sink_id_ != 0; } + + constexpr uint32_t client_id() const { return client_id_; } + + constexpr uint32_t sink_id() const { return sink_id_; } + + bool operator==(const FrameSinkId& other) const { + return client_id_ == other.client_id_ && sink_id_ == other.sink_id_; + } + + bool operator!=(const FrameSinkId& other) const { return !(*this == other); } + + bool operator<(const FrameSinkId& other) const { + return std::tie(client_id_, sink_id_) < + std::tie(other.client_id_, other.sink_id_); + } + + size_t hash() const { return base::HashInts(client_id_, sink_id_); } + + std::string ToString() const; + + private: + uint32_t client_id_; + uint32_t sink_id_; +}; + +VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out, + const FrameSinkId& frame_sink_id); + +struct FrameSinkIdHash { + size_t operator()(const FrameSinkId& key) const { return key.hash(); } +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_H_ diff --git a/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h b/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h new file mode 100644 index 00000000000..3594dab8748 --- /dev/null +++ b/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h @@ -0,0 +1,32 @@ +// Copyright 2017 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 COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_ALLOCATOR_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_ALLOCATOR_H_ + +#include "components/viz/common/surfaces/frame_sink_id.h" + +namespace viz { + +// This class generates FrameSinkId with a fixed client_id and an +// incrementally-increasing sink_id. +class FrameSinkIdAllocator { + public: + constexpr explicit FrameSinkIdAllocator(uint32_t client_id) + : client_id_(client_id), next_sink_id_(1u) {} + + FrameSinkId NextFrameSinkId() { + return FrameSinkId(client_id_, next_sink_id_++); + } + + private: + const uint32_t client_id_; + uint32_t next_sink_id_; + + DISALLOW_COPY_AND_ASSIGN(FrameSinkIdAllocator); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_FRAME_SINK_ID_ALLOCATOR_H_ diff --git a/chromium/components/viz/common/surfaces/local_surface_id.cc b/chromium/components/viz/common/surfaces/local_surface_id.cc new file mode 100644 index 00000000000..67fa5750092 --- /dev/null +++ b/chromium/components/viz/common/surfaces/local_surface_id.cc @@ -0,0 +1,21 @@ +// Copyright 2017 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 "components/viz/common/surfaces/local_surface_id.h" + +#include "base/strings/stringprintf.h" + +namespace viz { + +std::string LocalSurfaceId::ToString() const { + return base::StringPrintf("LocalSurfaceId(%d, %s" PRIu64 ")", local_id_, + nonce_.ToString().c_str()); +} + +std::ostream& operator<<(std::ostream& out, + const LocalSurfaceId& local_surface_id) { + return out << local_surface_id.ToString(); +} + +} // namespace viz diff --git a/chromium/components/viz/common/surfaces/local_surface_id.h b/chromium/components/viz/common/surfaces/local_surface_id.h new file mode 100644 index 00000000000..450386c6479 --- /dev/null +++ b/chromium/components/viz/common/surfaces/local_surface_id.h @@ -0,0 +1,85 @@ +// Copyright 2017 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 COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_H_ + +#include <inttypes.h> + +#include <iosfwd> +#include <string> +#include <tuple> + +#include "base/hash.h" +#include "base/unguessable_token.h" +#include "components/viz/common/viz_common_export.h" +#include "mojo/public/cpp/bindings/struct_traits.h" + +namespace cc { +namespace mojom { +class LocalSurfaceIdDataView; +} +} // namespace cc + +namespace viz { + +class VIZ_COMMON_EXPORT LocalSurfaceId { + public: + constexpr LocalSurfaceId() : local_id_(0) {} + + constexpr LocalSurfaceId(const LocalSurfaceId& other) + : local_id_(other.local_id_), nonce_(other.nonce_) {} + + constexpr LocalSurfaceId(uint32_t local_id, + const base::UnguessableToken& nonce) + : local_id_(local_id), nonce_(nonce) {} + + constexpr bool is_valid() const { + return local_id_ != 0 && !nonce_.is_empty(); + } + + constexpr uint32_t local_id() const { return local_id_; } + + constexpr const base::UnguessableToken& nonce() const { return nonce_; } + + bool operator==(const LocalSurfaceId& other) const { + return local_id_ == other.local_id_ && nonce_ == other.nonce_; + } + + bool operator!=(const LocalSurfaceId& other) const { + return !(*this == other); + } + + bool operator<(const LocalSurfaceId& other) const { + return std::tie(local_id_, nonce_) < + std::tie(other.local_id_, other.nonce_); + } + + size_t hash() const { + DCHECK(is_valid()) << ToString(); + return base::HashInts( + local_id_, static_cast<uint64_t>(base::UnguessableTokenHash()(nonce_))); + } + + std::string ToString() const; + + private: + friend struct mojo::StructTraits<cc::mojom::LocalSurfaceIdDataView, + LocalSurfaceId>; + + uint32_t local_id_; + base::UnguessableToken nonce_; +}; + +VIZ_COMMON_EXPORT std::ostream& operator<<( + std::ostream& out, + const LocalSurfaceId& local_surface_id); + +struct LocalSurfaceIdHash { + size_t operator()(const LocalSurfaceId& key) const { return key.hash(); } +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_H_ diff --git a/chromium/components/viz/common/surfaces/local_surface_id_allocator.cc b/chromium/components/viz/common/surfaces/local_surface_id_allocator.cc new file mode 100644 index 00000000000..16660338d68 --- /dev/null +++ b/chromium/components/viz/common/surfaces/local_surface_id_allocator.cc @@ -0,0 +1,24 @@ +// Copyright 2017 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 "components/viz/common/surfaces/local_surface_id_allocator.h" + +#include <stdint.h> + +#include "base/rand_util.h" +#include "base/unguessable_token.h" + +namespace viz { + +LocalSurfaceIdAllocator::LocalSurfaceIdAllocator() : next_id_(1u) {} + +LocalSurfaceIdAllocator::~LocalSurfaceIdAllocator() {} + +LocalSurfaceId LocalSurfaceIdAllocator::GenerateId() { + LocalSurfaceId id(next_id_, base::UnguessableToken::Create()); + next_id_++; + return id; +} + +} // namespace viz diff --git a/chromium/components/viz/common/surfaces/local_surface_id_allocator.h b/chromium/components/viz/common/surfaces/local_surface_id_allocator.h new file mode 100644 index 00000000000..bc01fd5aea0 --- /dev/null +++ b/chromium/components/viz/common/surfaces/local_surface_id_allocator.h @@ -0,0 +1,34 @@ +// Copyright 2017 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 COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_ALLOCATOR_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_ALLOCATOR_H_ + +#include <stdint.h> + +#include "base/macros.h" +#include "components/viz/common/surfaces/surface_id.h" +#include "components/viz/common/viz_common_export.h" + +namespace viz { + +// This is a helper class for generating local surface IDs for a single +// FrameSink. This is not threadsafe, to use from multiple threads wrap this +// class in a mutex. +class VIZ_COMMON_EXPORT LocalSurfaceIdAllocator { + public: + LocalSurfaceIdAllocator(); + ~LocalSurfaceIdAllocator(); + + LocalSurfaceId GenerateId(); + + private: + uint32_t next_id_; + + DISALLOW_COPY_AND_ASSIGN(LocalSurfaceIdAllocator); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_LOCAL_SURFACE_ID_ALLOCATOR_H_ diff --git a/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.cc b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.cc new file mode 100644 index 00000000000..7faf30ec2ea --- /dev/null +++ b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.cc @@ -0,0 +1,23 @@ +// Copyright 2016 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 "components/viz/common/surfaces/sequence_surface_reference_factory.h" + +#include "base/bind.h" +#include "base/memory/ptr_util.h" + +#include "components/viz/common/surfaces/surface_sequence.h" + +namespace viz { + +base::Closure SequenceSurfaceReferenceFactory::CreateReference( + SurfaceReferenceOwner* owner, + const SurfaceId& surface_id) const { + auto seq = owner->GetSurfaceSequenceGenerator()->CreateSurfaceSequence(); + RequireSequence(surface_id, seq); + return base::Bind(&SequenceSurfaceReferenceFactory::SatisfySequence, this, + seq); +} + +} // namespace viz diff --git a/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h new file mode 100644 index 00000000000..93fa9534bf2 --- /dev/null +++ b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h @@ -0,0 +1,37 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_SURFACES_SEQUENCE_SURFACE_REFERENCE_FACTORY_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_SEQUENCE_SURFACE_REFERENCE_FACTORY_H_ + +#include "components/viz/common/surfaces/surface_reference_factory.h" +#include "components/viz/common/surfaces/surface_sequence.h" +#include "components/viz/common/viz_common_export.h" + +namespace viz { + +// A surface reference factory that uses SurfaceSequence. +class VIZ_COMMON_EXPORT SequenceSurfaceReferenceFactory + : public NON_EXPORTED_BASE(SurfaceReferenceFactory) { + public: + SequenceSurfaceReferenceFactory() = default; + + // SurfaceReferenceFactory implementation: + base::Closure CreateReference(SurfaceReferenceOwner* owner, + const SurfaceId& surface_id) const override; + + protected: + ~SequenceSurfaceReferenceFactory() override = default; + + private: + virtual void RequireSequence(const SurfaceId& surface_id, + const SurfaceSequence& sequence) const = 0; + virtual void SatisfySequence(const SurfaceSequence& sequence) const = 0; + + DISALLOW_COPY_AND_ASSIGN(SequenceSurfaceReferenceFactory); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_SEQUENCE_SURFACE_REFERENCE_FACTORY_H_ diff --git a/chromium/components/viz/common/surfaces/surface_id.cc b/chromium/components/viz/common/surfaces/surface_id.cc new file mode 100644 index 00000000000..a9068c2ab7d --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_id.cc @@ -0,0 +1,21 @@ +// Copyright 2016 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 "components/viz/common/surfaces/surface_id.h" + +#include "base/strings/stringprintf.h" + +namespace viz { + +std::string SurfaceId::ToString() const { + return base::StringPrintf("SurfaceId(%s, %s)", + frame_sink_id_.ToString().c_str(), + local_surface_id_.ToString().c_str()); +} + +std::ostream& operator<<(std::ostream& out, const SurfaceId& surface_id) { + return out << surface_id.ToString(); +} + +} // namespace viz diff --git a/chromium/components/viz/common/surfaces/surface_id.h b/chromium/components/viz/common/surfaces/surface_id.h new file mode 100644 index 00000000000..b008abaf288 --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_id.h @@ -0,0 +1,88 @@ +// Copyright 2014 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 COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_ID_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_ID_H_ + +#include <stdint.h> + +#include <iosfwd> +#include <string> + +#include "base/format_macros.h" +#include "base/hash.h" +#include "components/viz/common/surfaces/frame_sink_id.h" +#include "components/viz/common/surfaces/local_surface_id.h" +#include "components/viz/common/viz_common_export.h" +#include "mojo/public/cpp/bindings/struct_traits.h" + +namespace cc { +namespace mojom { +class SurfaceIdDataView; +} +} // namespace cc + +namespace viz { + +class VIZ_COMMON_EXPORT SurfaceId { + public: + constexpr SurfaceId() = default; + + constexpr SurfaceId(const SurfaceId& other) = default; + + // A SurfaceId consists of two components: FrameSinkId and LocalSurfaceId. + // A |frame_sink_id| consists of two components; one is allocated by the + // display compositor service and one is allocated by the client. The + // |frame_sink_id| uniquely identifies a FrameSink (and frame source). + // A |local_surface_id| is a sequentially allocated ID generated by the frame + // source that uniquely identifies a sequential set of frames of the same size + // and device scale factor. + constexpr SurfaceId(const FrameSinkId& frame_sink_id, + const LocalSurfaceId& local_surface_id) + : frame_sink_id_(frame_sink_id), local_surface_id_(local_surface_id) {} + + bool is_valid() const { + return frame_sink_id_.is_valid() && local_surface_id_.is_valid(); + } + + size_t hash() const { + return base::HashInts(static_cast<uint64_t>(frame_sink_id_.hash()), + static_cast<uint64_t>(local_surface_id_.hash())); + } + + const FrameSinkId& frame_sink_id() const { return frame_sink_id_; } + + const LocalSurfaceId& local_surface_id() const { return local_surface_id_; } + + std::string ToString() const; + + bool operator==(const SurfaceId& other) const { + return frame_sink_id_ == other.frame_sink_id_ && + local_surface_id_ == other.local_surface_id_; + } + + bool operator!=(const SurfaceId& other) const { return !(*this == other); } + + bool operator<(const SurfaceId& other) const { + return std::tie(frame_sink_id_, local_surface_id_) < + std::tie(other.frame_sink_id_, other.local_surface_id_); + } + + private: + friend struct mojo::StructTraits<cc::mojom::SurfaceIdDataView, SurfaceId>; + + FrameSinkId frame_sink_id_; + LocalSurfaceId local_surface_id_; +}; + +VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out, + const SurfaceId& surface_id); + +struct SurfaceIdHash { + size_t operator()(const SurfaceId& key) const { return key.hash(); } +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_ID_H_ diff --git a/chromium/components/viz/common/surfaces/surface_info.h b/chromium/components/viz/common/surfaces/surface_info.h new file mode 100644 index 00000000000..1e21fda57d8 --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_info.h @@ -0,0 +1,63 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_INFO_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_INFO_H_ + +#include "components/viz/common/surfaces/surface_id.h" +#include "ui/gfx/geometry/size.h" + +namespace IPC { +template <class T> +struct ParamTraits; +} // namespace IPC + +namespace cc { +namespace mojom { +class SurfaceInfoDataView; +} +} // namespace cc + +namespace viz { + +// This class contains information about the surface that is being embedded. +class SurfaceInfo { + public: + SurfaceInfo() = default; + SurfaceInfo(const SurfaceId& id, + float device_scale_factor, + const gfx::Size& size_in_pixels) + : id_(id), + device_scale_factor_(device_scale_factor), + size_in_pixels_(size_in_pixels) {} + + bool is_valid() const { + return id_.is_valid() && device_scale_factor_ != 0 && + !size_in_pixels_.IsEmpty(); + } + + bool operator==(const SurfaceInfo& info) const { + return id_ == info.id() && + device_scale_factor_ == info.device_scale_factor() && + size_in_pixels_ == info.size_in_pixels(); + } + + bool operator!=(const SurfaceInfo& info) const { return !(*this == info); } + + const SurfaceId& id() const { return id_; } + float device_scale_factor() const { return device_scale_factor_; } + const gfx::Size& size_in_pixels() const { return size_in_pixels_; } + + private: + friend struct mojo::StructTraits<cc::mojom::SurfaceInfoDataView, SurfaceInfo>; + friend struct IPC::ParamTraits<SurfaceInfo>; + + SurfaceId id_; + float device_scale_factor_ = 1.f; + gfx::Size size_in_pixels_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_INFO_H_ diff --git a/chromium/components/viz/common/surfaces/surface_reference_factory.h b/chromium/components/viz/common/surfaces/surface_reference_factory.h new file mode 100644 index 00000000000..03e5808f24e --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_reference_factory.h @@ -0,0 +1,39 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_FACTORY_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_FACTORY_H_ + +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" +#include "components/viz/common/surfaces/surface_id.h" +#include "components/viz/common/surfaces/surface_reference_owner.h" + +namespace viz { + +// Confusingly, SurfaceReferenceFactory is only used to create SurfaceSequences. +// TODO(kylechar): Delete all usage of SurfaceReferenceFactory when surface +// references are enabled by default. +class SurfaceReferenceFactory + : public base::RefCountedThreadSafe<SurfaceReferenceFactory> { + public: + // Creates a reference to the surface with the given surface id and returns + // a closure that must be called exactly once to remove the reference. + virtual base::Closure CreateReference(SurfaceReferenceOwner* owner, + const SurfaceId& surface_id) const = 0; + + SurfaceReferenceFactory() = default; + + protected: + virtual ~SurfaceReferenceFactory() = default; + + private: + friend class base::RefCountedThreadSafe<SurfaceReferenceFactory>; + + DISALLOW_COPY_AND_ASSIGN(SurfaceReferenceFactory); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_FACTORY_H_ diff --git a/chromium/components/viz/common/surfaces/surface_reference_owner.h b/chromium/components/viz/common/surfaces/surface_reference_owner.h new file mode 100644 index 00000000000..c966fb9fad1 --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_reference_owner.h @@ -0,0 +1,23 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_OWNER_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_OWNER_H_ + +#include "components/viz/common/surfaces/surface_sequence_generator.h" + +namespace viz { + +// Implementations of this interface can be passed to +// SurfaceReferenceFactory::CreateReference as the reference owner. +class SurfaceReferenceOwner { + public: + virtual ~SurfaceReferenceOwner() {} + + virtual SurfaceSequenceGenerator* GetSurfaceSequenceGenerator() = 0; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_REFERENCE_OWNER_H_ diff --git a/chromium/components/viz/common/surfaces/surface_sequence.h b/chromium/components/viz/common/surfaces/surface_sequence.h new file mode 100644 index 00000000000..1b5bda93d54 --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_sequence.h @@ -0,0 +1,53 @@ +// Copyright 2014 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 COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <tuple> + +#include "base/hash.h" +#include "components/viz/common/surfaces/frame_sink_id.h" + +namespace viz { + +// A per-surface-namespace sequence number that's used to coordinate +// dependencies between frames. A sequence number may be satisfied once, and +// may be depended on once. +struct SurfaceSequence { + SurfaceSequence() : sequence(0u) {} + SurfaceSequence(const FrameSinkId& frame_sink_id, uint32_t sequence) + : frame_sink_id(frame_sink_id), sequence(sequence) {} + bool is_valid() const { return frame_sink_id.is_valid() && sequence > 0u; } + + FrameSinkId frame_sink_id; + uint32_t sequence; +}; + +inline bool operator==(const SurfaceSequence& a, const SurfaceSequence& b) { + return a.frame_sink_id == b.frame_sink_id && a.sequence == b.sequence; +} + +inline bool operator!=(const SurfaceSequence& a, const SurfaceSequence& b) { + return !(a == b); +} + +inline bool operator<(const SurfaceSequence& a, const SurfaceSequence& b) { + return std::tie(a.frame_sink_id, a.sequence) < + std::tie(b.frame_sink_id, b.sequence); +} + +struct SurfaceSequenceHash { + size_t operator()(SurfaceSequence key) const { + return base::HashInts(static_cast<uint64_t>(key.frame_sink_id.hash()), + key.sequence); + } +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_H_ diff --git a/chromium/components/viz/common/surfaces/surface_sequence_generator.cc b/chromium/components/viz/common/surfaces/surface_sequence_generator.cc new file mode 100644 index 00000000000..f3c351e53f9 --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_sequence_generator.cc @@ -0,0 +1,19 @@ +// Copyright 2016 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 "components/viz/common/surfaces/surface_sequence_generator.h" +#include "components/viz/common/surfaces/surface_sequence.h" + +namespace viz { + +SurfaceSequenceGenerator::SurfaceSequenceGenerator() + : next_surface_sequence_(1u) {} + +SurfaceSequenceGenerator::~SurfaceSequenceGenerator() = default; + +SurfaceSequence SurfaceSequenceGenerator::CreateSurfaceSequence() { + return SurfaceSequence(frame_sink_id_, next_surface_sequence_++); +} + +} // namespace viz diff --git a/chromium/components/viz/common/surfaces/surface_sequence_generator.h b/chromium/components/viz/common/surfaces/surface_sequence_generator.h new file mode 100644 index 00000000000..1bed7d5baf7 --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_sequence_generator.h @@ -0,0 +1,41 @@ +// Copyright 2016 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 COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_GENERATOR_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_GENERATOR_H_ + +#include <stdint.h> + +#include <tuple> + +#include "base/macros.h" + +#include "components/viz/common/surfaces/frame_sink_id.h" +#include "components/viz/common/surfaces/surface_sequence.h" +#include "components/viz/common/viz_common_export.h" + +namespace viz { + +// Generates unique surface sequences for a surface client id. +class VIZ_COMMON_EXPORT SurfaceSequenceGenerator { + public: + SurfaceSequenceGenerator(); + ~SurfaceSequenceGenerator(); + + void set_frame_sink_id(const FrameSinkId& frame_sink_id) { + frame_sink_id_ = frame_sink_id; + } + + SurfaceSequence CreateSurfaceSequence(); + + private: + FrameSinkId frame_sink_id_; + uint32_t next_surface_sequence_; + + DISALLOW_COPY_AND_ASSIGN(SurfaceSequenceGenerator); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_SEQUENCE_GENERATOR_H_ diff --git a/chromium/components/viz/common/surfaces/surface_sequence_generator_unittest.cc b/chromium/components/viz/common/surfaces/surface_sequence_generator_unittest.cc new file mode 100644 index 00000000000..5d1913cc0d4 --- /dev/null +++ b/chromium/components/viz/common/surfaces/surface_sequence_generator_unittest.cc @@ -0,0 +1,24 @@ +// Copyright 2016 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 "components/viz/common/surfaces/surface_sequence_generator.h" + +#include "components/viz/common/surfaces/surface_sequence.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace viz { +namespace { + +static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1); + +TEST(SurfaceSequenceGeneratorTest, Basic) { + SurfaceSequenceGenerator generator; + generator.set_frame_sink_id(kArbitraryFrameSinkId); + SurfaceSequence sequence1 = generator.CreateSurfaceSequence(); + SurfaceSequence sequence2 = generator.CreateSurfaceSequence(); + EXPECT_NE(sequence1, sequence2); +} + +} // namespace +} // namespace viz diff --git a/chromium/components/viz/common/viz_common_export.h b/chromium/components/viz/common/viz_common_export.h new file mode 100644 index 00000000000..9e58b1327c9 --- /dev/null +++ b/chromium/components/viz/common/viz_common_export.h @@ -0,0 +1,29 @@ +// Copyright 2017 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 COMPONENTS_VIZ_COMMON_VIZ_COMMON_EXPORT_H_ +#define COMPONENTS_VIZ_COMMON_VIZ_COMMON_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(VIZ_COMMON_IMPLEMENTATION) +#define VIZ_COMMON_EXPORT __declspec(dllexport) +#else +#define VIZ_COMMON_EXPORT __declspec(dllimport) +#endif // defined(VIZ_COMMON_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(VIZ_COMMON_IMPLEMENTATION) +#define VIZ_COMMON_EXPORT __attribute__((visibility("default"))) +#else +#define VIZ_COMMON_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define VIZ_COMMON_EXPORT +#endif + +#endif // COMPONENTS_VIZ_COMMON_VIZ_COMMON_EXPORT_H_ diff --git a/chromium/components/viz/common/yuv_readback_unittest.cc b/chromium/components/viz/common/yuv_readback_unittest.cc new file mode 100644 index 00000000000..3a92257ae3a --- /dev/null +++ b/chromium/components/viz/common/yuv_readback_unittest.cc @@ -0,0 +1,558 @@ +// Copyright 2016 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/json/json_reader.h" +#include "base/memory/ref_counted_memory.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/trace_event/trace_event.h" +#include "build/build_config.h" +#include "components/viz/common/gl_helper.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "gpu/command_buffer/client/shared_memory_limits.h" +#include "gpu/ipc/common/surface_handle.h" +#include "gpu/ipc/gl_in_process_context.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" + +#if !defined(OS_ANDROID) + +namespace viz { + +namespace { +int kYUVReadbackSizes[] = {2, 4, 14}; +} + +class YUVReadbackTest : public testing::Test { + protected: + void SetUp() override { + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = 8; + attributes.depth_size = 24; + attributes.red_size = 8; + attributes.green_size = 8; + attributes.blue_size = 8; + attributes.stencil_size = 8; + attributes.samples = 4; + attributes.sample_buffers = 1; + attributes.bind_generates_resource = false; + + context_.reset( + gpu::GLInProcessContext::Create(nullptr, /* service */ + nullptr, /* surface */ + true, /* offscreen */ + gpu::kNullSurfaceHandle, /* window */ + nullptr, /* share_context */ + attributes, gpu::SharedMemoryLimits(), + nullptr, /* gpu_memory_buffer_manager */ + nullptr, /* image_factory */ + base::ThreadTaskRunnerHandle::Get())); + gl_ = context_->GetImplementation(); + gpu::ContextSupport* support = context_->GetImplementation(); + + helper_.reset(new GLHelper(gl_, support)); + } + + void TearDown() override { + helper_.reset(NULL); + context_.reset(NULL); + } + + void StartTracing(const std::string& filter) { + base::trace_event::TraceLog::GetInstance()->SetEnabled( + base::trace_event::TraceConfig(filter, + base::trace_event::RECORD_UNTIL_FULL), + base::trace_event::TraceLog::RECORDING_MODE); + } + + static void TraceDataCB( + const base::Callback<void()>& callback, + std::string* output, + const scoped_refptr<base::RefCountedString>& json_events_str, + bool has_more_events) { + if (output->size() > 1 && !json_events_str->data().empty()) { + output->append(","); + } + output->append(json_events_str->data()); + if (!has_more_events) { + callback.Run(); + } + } + + // End tracing, return tracing data in a simple map + // of event name->counts. + void EndTracing(std::map<std::string, int>* event_counts) { + std::string json_data = "["; + base::trace_event::TraceLog::GetInstance()->SetDisabled(); + base::RunLoop run_loop; + base::trace_event::TraceLog::GetInstance()->Flush( + base::Bind(&YUVReadbackTest::TraceDataCB, run_loop.QuitClosure(), + base::Unretained(&json_data))); + run_loop.Run(); + json_data.append("]"); + + std::string error_msg; + std::unique_ptr<base::Value> trace_data = + base::JSONReader::ReadAndReturnError(json_data, 0, NULL, &error_msg); + CHECK(trace_data) << "JSON parsing failed (" << error_msg + << ") JSON data:" << std::endl + << json_data; + + base::ListValue* list; + CHECK(trace_data->GetAsList(&list)); + for (size_t i = 0; i < list->GetSize(); i++) { + base::Value* item = NULL; + if (list->Get(i, &item)) { + base::DictionaryValue* dict; + CHECK(item->GetAsDictionary(&dict)); + std::string name; + CHECK(dict->GetString("name", &name)); + std::string trace_type; + CHECK(dict->GetString("ph", &trace_type)); + // Count all except END traces, as they come in BEGIN/END pairs. + if (trace_type != "E" && trace_type != "e") + (*event_counts)[name]++; + VLOG(1) << "trace name: " << name; + } + } + } + + // Look up a single channel value. Works for 4-channel and single channel + // bitmaps. Clamp x/y. + int Channel(SkBitmap* pixels, int x, int y, int c) { + if (pixels->bytesPerPixel() == 4) { + uint32_t* data = + pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)), + std::max(0, std::min(y, pixels->height() - 1))); + return (*data) >> (c * 8) & 0xff; + } else { + DCHECK_EQ(pixels->bytesPerPixel(), 1); + DCHECK_EQ(c, 0); + return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)), + std::max(0, std::min(y, pixels->height() - 1))); + } + } + + // Set a single channel value. Works for 4-channel and single channel + // bitmaps. Clamp x/y. + void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) { + DCHECK_GE(x, 0); + DCHECK_GE(y, 0); + DCHECK_LT(x, pixels->width()); + DCHECK_LT(y, pixels->height()); + if (pixels->bytesPerPixel() == 4) { + uint32_t* data = pixels->getAddr32(x, y); + v = std::max(0, std::min(v, 255)); + *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8)); + } else { + DCHECK_EQ(pixels->bytesPerPixel(), 1); + DCHECK_EQ(c, 0); + uint8_t* data = pixels->getAddr8(x, y); + v = std::max(0, std::min(v, 255)); + *data = v; + } + } + + // Print all the R, G, B or A values from an SkBitmap in a + // human-readable format. + void PrintChannel(SkBitmap* pixels, int c) { + for (int y = 0; y < pixels->height(); y++) { + std::string formatted; + for (int x = 0; x < pixels->width(); x++) { + formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c))); + } + LOG(ERROR) << formatted; + } + } + + // Get a single R, G, B or A value as a float. + float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) { + return Channel(pixels, x, y, c) / 255.0; + } + + // Works like a GL_LINEAR lookup on an SkBitmap. + float Bilinear(SkBitmap* pixels, float x, float y, int c) { + x -= 0.5; + y -= 0.5; + int base_x = static_cast<int>(floorf(x)); + int base_y = static_cast<int>(floorf(y)); + x -= base_x; + y -= base_y; + return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) + + ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) + + ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y + + ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y); + } + + void FlipSKBitmap(SkBitmap* bitmap) { + int bpp = bitmap->bytesPerPixel(); + DCHECK(bpp == 4 || bpp == 1); + int top_line = 0; + int bottom_line = bitmap->height() - 1; + while (top_line < bottom_line) { + for (int x = 0; x < bitmap->width(); x++) { + bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line), + *bitmap->getAddr32(x, bottom_line)) + : std::swap(*bitmap->getAddr8(x, top_line), + *bitmap->getAddr8(x, bottom_line)); + } + top_line++; + bottom_line--; + } + } + + // Note: Left/Right means Top/Bottom when used for Y dimension. + enum Margin { + MarginLeft, + MarginMiddle, + MarginRight, + MarginInvalid, + }; + + static Margin NextMargin(Margin m) { + switch (m) { + case MarginLeft: + return MarginMiddle; + case MarginMiddle: + return MarginRight; + case MarginRight: + return MarginInvalid; + default: + return MarginInvalid; + } + } + + int compute_margin(int insize, int outsize, Margin m) { + int available = outsize - insize; + switch (m) { + default: + EXPECT_TRUE(false) << "This should not happen."; + return 0; + case MarginLeft: + return 0; + case MarginMiddle: + return (available / 2) & ~1; + case MarginRight: + return available; + } + } + + // Convert 0.0 - 1.0 to 0 - 255 + int float_to_byte(float v) { + int ret = static_cast<int>(floorf(v * 255.0f + 0.5f)); + if (ret < 0) { + return 0; + } + if (ret > 255) { + return 255; + } + return ret; + } + + static void callcallback(const base::Callback<void()>& callback, + bool result) { + callback.Run(); + } + + void PrintPlane(unsigned char* plane, int xsize, int stride, int ysize) { + for (int y = 0; y < ysize; y++) { + std::string formatted; + for (int x = 0; x < xsize; x++) { + formatted.append(base::StringPrintf("%3d, ", plane[y * stride + x])); + } + LOG(ERROR) << formatted << " (" << (plane + y * stride) << ")"; + } + } + + // Compare two planes make sure that each component of each pixel + // is no more than |maxdiff| apart. + void ComparePlane(unsigned char* truth, + int truth_stride, + unsigned char* other, + int other_stride, + int maxdiff, + int xsize, + int ysize, + SkBitmap* source, + std::string message) { + for (int x = 0; x < xsize; x++) { + for (int y = 0; y < ysize; y++) { + int a = other[y * other_stride + x]; + int b = truth[y * truth_stride + x]; + EXPECT_NEAR(a, b, maxdiff) + << " x=" << x << " y=" << y << " " << message; + if (std::abs(a - b) > maxdiff) { + LOG(ERROR) << "-------expected--------"; + PrintPlane(truth, xsize, truth_stride, ysize); + LOG(ERROR) << "-------actual--------"; + PrintPlane(other, xsize, other_stride, ysize); + if (source) { + LOG(ERROR) << "-------before yuv conversion: red--------"; + PrintChannel(source, 0); + LOG(ERROR) << "-------before yuv conversion: green------"; + PrintChannel(source, 1); + LOG(ERROR) << "-------before yuv conversion: blue-------"; + PrintChannel(source, 2); + } + return; + } + } + } + } + + // YUV readback test. Create a test pattern, convert to YUV + // with reference implementation and compare to what gl_helper + // returns. + void TestYUVReadback(int xsize, + int ysize, + int output_xsize, + int output_ysize, + int xmargin, + int ymargin, + int test_pattern, + bool flip, + bool use_mrt, + GLHelper::ScalerQuality quality) { + GLuint src_texture; + gl_->GenTextures(1, &src_texture); + SkBitmap input_pixels; + input_pixels.allocN32Pixels(xsize, ysize); + + for (int x = 0; x < xsize; ++x) { + for (int y = 0; y < ysize; ++y) { + switch (test_pattern) { + case 0: // Smooth test pattern + SetChannel(&input_pixels, x, y, 0, x * 10); + SetChannel(&input_pixels, x, y, 1, y * 10); + SetChannel(&input_pixels, x, y, 2, (x + y) * 10); + SetChannel(&input_pixels, x, y, 3, 255); + break; + case 1: // Small blocks + SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 3, 255); + break; + case 2: // Medium blocks + SetChannel(&input_pixels, x, y, 0, 10 + x / 2 * 50); + SetChannel(&input_pixels, x, y, 1, 10 + y / 3 * 50); + SetChannel(&input_pixels, x, y, 2, (x + y) / 5 * 50 + 5); + SetChannel(&input_pixels, x, y, 3, 255); + break; + } + } + } + + gl_->BindTexture(GL_TEXTURE_2D, src_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsize, ysize, 0, GL_RGBA, + GL_UNSIGNED_BYTE, input_pixels.getPixels()); + + gpu::Mailbox mailbox; + gl_->GenMailboxCHROMIUM(mailbox.name); + EXPECT_FALSE(mailbox.IsZero()); + gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); + const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM(); + gl_->ShallowFlushCHROMIUM(); + + gpu::SyncToken sync_token; + gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); + + std::string message = base::StringPrintf( + "input size: %dx%d " + "output size: %dx%d " + "margin: %dx%d " + "pattern: %d %s %s", + xsize, ysize, output_xsize, output_ysize, xmargin, ymargin, + test_pattern, flip ? "flip" : "noflip", flip ? "mrt" : "nomrt"); + std::unique_ptr<ReadbackYUVInterface> yuv_reader( + helper_->CreateReadbackPipelineYUV( + quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize), + gfx::Size(xsize, ysize), flip, use_mrt)); + + scoped_refptr<media::VideoFrame> output_frame = + media::VideoFrame::CreateFrame( + media::PIXEL_FORMAT_YV12, + // The coded size of the output frame is rounded up to the next + // 16-byte boundary. This tests that the readback is being + // positioned inside the frame's visible region, and not dependent + // on its coded size. + gfx::Size((output_xsize + 15) & ~15, (output_ysize + 15) & ~15), + gfx::Rect(0, 0, output_xsize, output_ysize), + gfx::Size(output_xsize, output_ysize), + base::TimeDelta::FromSeconds(0)); + scoped_refptr<media::VideoFrame> truth_frame = + media::VideoFrame::CreateFrame( + media::PIXEL_FORMAT_YV12, gfx::Size(output_xsize, output_ysize), + gfx::Rect(0, 0, output_xsize, output_ysize), + gfx::Size(output_xsize, output_ysize), + base::TimeDelta::FromSeconds(0)); + + base::RunLoop run_loop; + yuv_reader->ReadbackYUV(mailbox, sync_token, output_frame->visible_rect(), + output_frame->stride(media::VideoFrame::kYPlane), + output_frame->data(media::VideoFrame::kYPlane), + output_frame->stride(media::VideoFrame::kUPlane), + output_frame->data(media::VideoFrame::kUPlane), + output_frame->stride(media::VideoFrame::kVPlane), + output_frame->data(media::VideoFrame::kVPlane), + gfx::Point(xmargin, ymargin), + base::Bind(&callcallback, run_loop.QuitClosure())); + + const gfx::Rect paste_rect(gfx::Point(xmargin, ymargin), + gfx::Size(xsize, ysize)); + media::LetterboxYUV(output_frame.get(), paste_rect); + run_loop.Run(); + + if (flip) { + FlipSKBitmap(&input_pixels); + } + + unsigned char* Y = truth_frame->visible_data(media::VideoFrame::kYPlane); + unsigned char* U = truth_frame->visible_data(media::VideoFrame::kUPlane); + unsigned char* V = truth_frame->visible_data(media::VideoFrame::kVPlane); + int32_t y_stride = truth_frame->stride(media::VideoFrame::kYPlane); + int32_t u_stride = truth_frame->stride(media::VideoFrame::kUPlane); + int32_t v_stride = truth_frame->stride(media::VideoFrame::kVPlane); + memset(Y, 0x00, y_stride * output_ysize); + memset(U, 0x80, u_stride * output_ysize / 2); + memset(V, 0x80, v_stride * output_ysize / 2); + + const float kRGBtoYColorWeights[] = {0.257f, 0.504f, 0.098f, 0.0625f}; + const float kRGBtoUColorWeights[] = {-0.148f, -0.291f, 0.439f, 0.5f}; + const float kRGBtoVColorWeights[] = {0.439f, -0.368f, -0.071f, 0.5f}; + + for (int y = 0; y < ysize; y++) { + for (int x = 0; x < xsize; x++) { + Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte( + ChannelAsFloat(&input_pixels, x, y, 0) * kRGBtoYColorWeights[0] + + ChannelAsFloat(&input_pixels, x, y, 1) * kRGBtoYColorWeights[1] + + ChannelAsFloat(&input_pixels, x, y, 2) * kRGBtoYColorWeights[2] + + kRGBtoYColorWeights[3]); + } + } + + for (int y = 0; y < ysize / 2; y++) { + for (int x = 0; x < xsize / 2; x++) { + U[(y + ymargin / 2) * u_stride + x + xmargin / 2] = + float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * + kRGBtoUColorWeights[0] + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * + kRGBtoUColorWeights[1] + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * + kRGBtoUColorWeights[2] + + kRGBtoUColorWeights[3]); + V[(y + ymargin / 2) * v_stride + x + xmargin / 2] = + float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * + kRGBtoVColorWeights[0] + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * + kRGBtoVColorWeights[1] + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * + kRGBtoVColorWeights[2] + + kRGBtoVColorWeights[3]); + } + } + + ComparePlane( + Y, y_stride, output_frame->visible_data(media::VideoFrame::kYPlane), + output_frame->stride(media::VideoFrame::kYPlane), 2, output_xsize, + output_ysize, &input_pixels, message + " Y plane"); + ComparePlane( + U, u_stride, output_frame->visible_data(media::VideoFrame::kUPlane), + output_frame->stride(media::VideoFrame::kUPlane), 2, output_xsize / 2, + output_ysize / 2, &input_pixels, message + " U plane"); + ComparePlane( + V, v_stride, output_frame->visible_data(media::VideoFrame::kVPlane), + output_frame->stride(media::VideoFrame::kVPlane), 2, output_xsize / 2, + output_ysize / 2, &input_pixels, message + " V plane"); + + gl_->DeleteTextures(1, &src_texture); + } + + std::unique_ptr<gpu::GLInProcessContext> context_; + gpu::gles2::GLES2Interface* gl_; + std::unique_ptr<GLHelper> helper_; + gl::DisableNullDrawGLBindings enable_pixel_output_; +}; + +TEST_F(YUVReadbackTest, YUVReadbackOptTest) { + // This test uses the gpu.service/gpu_decoder tracing events to detect how + // many scaling passes are actually performed by the YUV readback pipeline. + StartTracing(TRACE_DISABLED_BY_DEFAULT( + "gpu.service") "," TRACE_DISABLED_BY_DEFAULT("gpu_decoder")); + + TestYUVReadback(800, 400, 800, 400, 0, 0, 1, false, true, + GLHelper::SCALER_QUALITY_FAST); + + std::map<std::string, int> event_counts; + EndTracing(&event_counts); + int draw_buffer_calls = event_counts["kDrawBuffersEXTImmediate"]; + int draw_arrays_calls = event_counts["kDrawArrays"]; + VLOG(1) << "Draw buffer calls: " << draw_buffer_calls; + VLOG(1) << "DrawArrays calls: " << draw_arrays_calls; + + if (draw_buffer_calls) { + // When using MRT, the YUV readback code should only + // execute two draw arrays, and scaling should be integrated + // into those two calls since we are using the FAST scalign + // quality. + EXPECT_EQ(2, draw_arrays_calls); + } else { + // When not using MRT, there are three passes for the YUV, + // and one for the scaling. + EXPECT_EQ(4, draw_arrays_calls); + } +} + +class YUVReadbackPixelTest + : public YUVReadbackTest, + public ::testing::WithParamInterface< + std::tr1::tuple<bool, bool, unsigned int, unsigned int>> {}; + +TEST_P(YUVReadbackPixelTest, Test) { + bool flip = std::tr1::get<0>(GetParam()); + bool use_mrt = std::tr1::get<1>(GetParam()); + unsigned int x = std::tr1::get<2>(GetParam()); + unsigned int y = std::tr1::get<3>(GetParam()); + + for (unsigned int ox = x; ox < arraysize(kYUVReadbackSizes); ox++) { + for (unsigned int oy = y; oy < arraysize(kYUVReadbackSizes); oy++) { + // If output is a subsection of the destination frame, (letterbox) + // then try different variations of where the subsection goes. + for (Margin xm = x < ox ? MarginLeft : MarginRight; xm <= MarginRight; + xm = NextMargin(xm)) { + for (Margin ym = y < oy ? MarginLeft : MarginRight; ym <= MarginRight; + ym = NextMargin(ym)) { + for (int pattern = 0; pattern < 3; pattern++) { + TestYUVReadback( + kYUVReadbackSizes[x], kYUVReadbackSizes[y], + kYUVReadbackSizes[ox], kYUVReadbackSizes[oy], + compute_margin(kYUVReadbackSizes[x], kYUVReadbackSizes[ox], xm), + compute_margin(kYUVReadbackSizes[y], kYUVReadbackSizes[oy], ym), + pattern, flip, use_mrt, GLHelper::SCALER_QUALITY_GOOD); + if (HasFailure()) { + return; + } + } + } + } + } + } +} + +// First argument is intentionally empty. +INSTANTIATE_TEST_CASE_P( + , + YUVReadbackPixelTest, + ::testing::Combine( + ::testing::Bool(), + ::testing::Bool(), + ::testing::Range<unsigned int>(0, arraysize(kYUVReadbackSizes)), + ::testing::Range<unsigned int>(0, arraysize(kYUVReadbackSizes)))); + +} // namespace viz + +#endif |