diff options
Diffstat (limited to 'chromium/components/viz/common')
56 files changed, 3503 insertions, 103 deletions
diff --git a/chromium/components/viz/common/BUILD.gn b/chromium/components/viz/common/BUILD.gn index 84f40f15662..95ac2e7fdaf 100644 --- a/chromium/components/viz/common/BUILD.gn +++ b/chromium/components/viz/common/BUILD.gn @@ -5,6 +5,27 @@ import("//components/viz/viz.gni") import("//testing/test.gni") +viz_component("resource_format") { + output_name = "viz_resource_format" + + defines = [ "VIZ_RESOURCE_FORMAT_IMPLEMENTATION" ] + + sources = [ + "resources/resource_format.h", + "resources/resource_format_utils.cc", + "resources/resource_format_utils.h", + "viz_resource_format_export.h", + ] + + configs = [ "//third_party/khronos:khronos_headers" ] + + deps = [ + "//base", + "//skia", + "//ui/gfx:buffer_types", + ] +} + viz_component("common") { output_name = "viz_common" @@ -13,6 +34,12 @@ viz_component("common") { sources = [ "display/renderer_settings.cc", "display/renderer_settings.h", + "frame_sinks/begin_frame_args.cc", + "frame_sinks/begin_frame_args.h", + "frame_sinks/begin_frame_source.cc", + "frame_sinks/begin_frame_source.h", + "frame_sinks/delay_based_time_source.cc", + "frame_sinks/delay_based_time_source.h", "gl_helper.cc", "gl_helper.h", "gl_helper_readback_support.cc", @@ -25,20 +52,33 @@ viz_component("common") { "gpu/context_provider.h", "gpu/in_process_context_provider.cc", "gpu/in_process_context_provider.h", + "gpu/vulkan_context_provider.h", + "gpu/vulkan_in_process_context_provider.cc", + "gpu/vulkan_in_process_context_provider.h", "hit_test/aggregated_hit_test_region.h", - "quads/resource_format.h", + "quads/copy_output_request.cc", + "quads/copy_output_request.h", + "quads/copy_output_result.cc", + "quads/copy_output_result.h", + "quads/release_callback.h", "quads/shared_bitmap.cc", "quads/shared_bitmap.h", + "quads/shared_quad_state.cc", + "quads/shared_quad_state.h", + "quads/single_release_callback.cc", + "quads/single_release_callback.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_id.h", "resources/resource_settings.cc", "resources/resource_settings.h", + "resources/returned_resource.h", "resources/shared_bitmap_manager.h", + "resources/transferable_resource.cc", + "resources/transferable_resource.h", "surfaces/frame_sink_id.cc", "surfaces/frame_sink_id.h", "surfaces/frame_sink_id_allocator.h", @@ -48,6 +88,8 @@ viz_component("common") { "surfaces/local_surface_id_allocator.h", "surfaces/sequence_surface_reference_factory.cc", "surfaces/sequence_surface_reference_factory.h", + "surfaces/stub_surface_reference_factory.cc", + "surfaces/stub_surface_reference_factory.h", "surfaces/surface_id.cc", "surfaces/surface_id.h", "surfaces/surface_info.h", @@ -56,10 +98,18 @@ viz_component("common") { "surfaces/surface_sequence.h", "surfaces/surface_sequence_generator.cc", "surfaces/surface_sequence_generator.h", + "switches.cc", + "switches.h", + "traced_value.cc", + "traced_value.h", "viz_common_export.h", ] deps = [ + # TODO(staraz): cc/base was added because SharedQuadState includes + # cc::MathUtil. Remove it once cc/base/math_util* are moved to viz. + "//cc/base", + "//base", "//gpu", "//gpu/command_buffer/client:gles2_implementation", @@ -67,6 +117,7 @@ viz_component("common") { "//gpu/command_buffer/service", "//gpu/ipc:gl_in_process_context", "//gpu/skia_bindings:skia_bindings", + "//gpu/vulkan:features", "//mojo/public/cpp/bindings", "//skia", "//ui/gfx:color_space", @@ -75,6 +126,7 @@ viz_component("common") { ] public_deps = [ + ":resource_format", "//gpu/command_buffer/client", "//gpu/command_buffer/common", "//mojo/public/cpp/bindings", @@ -84,6 +136,8 @@ viz_component("common") { viz_source_set("unit_tests") { testonly = true sources = [ + "frame_sinks/begin_frame_args_unittest.cc", + "frame_sinks/delay_based_time_source_unittest.cc", "gl_helper_unittest.cc", "resources/buffer_to_texture_target_map_unittest.cc", "resources/platform_color_unittest.cc", @@ -94,6 +148,7 @@ viz_source_set("unit_tests") { deps = [ ":common", "//base/test:test_support", + "//components/viz/test:test_support", "//gpu/command_buffer/client:gles2_implementation", "//gpu/command_buffer/client:gles2_interface", "//gpu/ipc:gl_in_process_context", diff --git a/chromium/components/viz/common/DEPS b/chromium/components/viz/common/DEPS index 4efdc7698ad..7732773b1b8 100644 --- a/chromium/components/viz/common/DEPS +++ b/chromium/components/viz/common/DEPS @@ -7,18 +7,16 @@ specific_include_rules = { "+gpu/command_buffer/service", "+gpu/ipc/common", "+mojo/public/cpp/bindings", - "+ui/gfx/geometry", "+services/ui/gpu/interfaces", "+third_party/skia", ], ".*_unittest\.cc": [ + "+components/viz/test", "+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 deleted file mode 100644 index d2e8fd6f3d9..00000000000 --- a/chromium/components/viz/common/README.md +++ /dev/null @@ -1,4 +0,0 @@ -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.h b/chromium/components/viz/common/display/renderer_settings.h index c7fb57c2540..28b529d304e 100644 --- a/chromium/components/viz/common/display/renderer_settings.h +++ b/chromium/components/viz/common/display/renderer_settings.h @@ -29,12 +29,15 @@ class VIZ_COMMON_EXPORT RendererSettings { bool gl_composited_overlay_candidate_quad_border = false; bool show_overdraw_feedback = false; bool enable_color_correct_rendering = false; + bool use_skia_renderer = 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; + + int slow_down_compositing_scale_factor = 1; }; } // namespace viz diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_args.cc b/chromium/components/viz/common/frame_sinks/begin_frame_args.cc new file mode 100644 index 00000000000..a955809fdb1 --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/begin_frame_args.cc @@ -0,0 +1,130 @@ +// 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/frame_sinks/begin_frame_args.h" + +#include "base/trace_event/trace_event_argument.h" + +namespace viz { + +const char* BeginFrameArgs::TypeToString(BeginFrameArgsType type) { + switch (type) { + case BeginFrameArgs::INVALID: + return "INVALID"; + case BeginFrameArgs::NORMAL: + return "NORMAL"; + case BeginFrameArgs::MISSED: + return "MISSED"; + case BeginFrameArgs::BEGIN_FRAME_ARGS_TYPE_MAX: + return "BEGIN_FRAME_ARGS_TYPE_MAX"; + } + NOTREACHED(); + return "???"; +} + +constexpr uint64_t BeginFrameArgs::kInvalidFrameNumber; +constexpr uint64_t BeginFrameArgs::kStartingFrameNumber; + +BeginFrameArgs::BeginFrameArgs() + : frame_time(base::TimeTicks()), + deadline(base::TimeTicks()), + interval(base::TimeDelta::FromMicroseconds(-1)), + sequence_number(kInvalidFrameNumber), + source_id(0), + type(BeginFrameArgs::INVALID), + on_critical_path(true) {} + +BeginFrameArgs::BeginFrameArgs(uint32_t source_id, + uint64_t sequence_number, + base::TimeTicks frame_time, + base::TimeTicks deadline, + base::TimeDelta interval, + BeginFrameArgs::BeginFrameArgsType type) + : frame_time(frame_time), + deadline(deadline), + interval(interval), + sequence_number(sequence_number), + source_id(source_id), + type(type), + on_critical_path(true) { + DCHECK_LE(kStartingFrameNumber, sequence_number); +} + +BeginFrameArgs BeginFrameArgs::Create(BeginFrameArgs::CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, + base::TimeTicks frame_time, + base::TimeTicks deadline, + base::TimeDelta interval, + BeginFrameArgs::BeginFrameArgsType type) { + DCHECK_NE(type, BeginFrameArgs::INVALID); + DCHECK_NE(type, BeginFrameArgs::BEGIN_FRAME_ARGS_TYPE_MAX); +#ifdef NDEBUG + return BeginFrameArgs(source_id, sequence_number, frame_time, deadline, + interval, type); +#else + BeginFrameArgs args = BeginFrameArgs(source_id, sequence_number, frame_time, + deadline, interval, type); + args.created_from = location; + return args; +#endif +} + +std::unique_ptr<base::trace_event::ConvertableToTraceFormat> +BeginFrameArgs::AsValue() const { + std::unique_ptr<base::trace_event::TracedValue> state( + new base::trace_event::TracedValue()); + AsValueInto(state.get()); + return std::move(state); +} + +void BeginFrameArgs::AsValueInto(base::trace_event::TracedValue* state) const { + state->SetString("type", "BeginFrameArgs"); + state->SetString("subtype", TypeToString(type)); + state->SetInteger("source_id", source_id); + state->SetInteger("sequence_number", sequence_number); + state->SetDouble("frame_time_us", frame_time.since_origin().InMicroseconds()); + state->SetDouble("deadline_us", deadline.since_origin().InMicroseconds()); + state->SetDouble("interval_us", interval.InMicroseconds()); +#ifndef NDEBUG + state->SetString("created_from", created_from.ToString()); +#endif + state->SetBoolean("on_critical_path", on_critical_path); +} + +// This is a hard-coded deadline adjustment that assumes 60Hz, to be used in +// cases where a good estimated draw time is not known. Using 1/3 of the vsync +// as the default adjustment gives the Browser the last 1/3 of a frame to +// produce output, the Renderer Impl thread the middle 1/3 of a frame to produce +// ouput, and the Renderer Main thread the first 1/3 of a frame to produce +// output. +base::TimeDelta BeginFrameArgs::DefaultEstimatedParentDrawTime() { + return base::TimeDelta::FromMicroseconds(16666 / 3); +} + +base::TimeDelta BeginFrameArgs::DefaultInterval() { + return base::TimeDelta::FromMicroseconds(16666); +} + +BeginFrameAck::BeginFrameAck() + : sequence_number(BeginFrameArgs::kInvalidFrameNumber), + source_id(0), + has_damage(false) {} + +BeginFrameAck::BeginFrameAck(uint32_t source_id, + uint64_t sequence_number, + bool has_damage) + : sequence_number(sequence_number), + source_id(source_id), + has_damage(has_damage) { + DCHECK_LT(BeginFrameArgs::kInvalidFrameNumber, sequence_number); +} + +// static +BeginFrameAck BeginFrameAck::CreateManualAckWithDamage() { + return BeginFrameAck(BeginFrameArgs::kManualSourceId, + BeginFrameArgs::kStartingFrameNumber, true); +} + +} // namespace viz diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_args.h b/chromium/components/viz/common/frame_sinks/begin_frame_args.h new file mode 100644 index 00000000000..72175015936 --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/begin_frame_args.h @@ -0,0 +1,145 @@ +// 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_FRAME_SINKS_BEGIN_FRAME_ARGS_H_ +#define COMPONENTS_VIZ_COMMON_FRAME_SINKS_BEGIN_FRAME_ARGS_H_ + +#include <stdint.h> +#include <memory> + +#include "base/location.h" +#include "base/memory/ref_counted.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/viz/common/viz_common_export.h" + +namespace base { +namespace trace_event { +class ConvertableToTraceFormat; +class TracedValue; +} // namespace trace_event +} // namespace base + +/** + * In debug builds we trace the creation origin of BeginFrameArgs objects. We + * reuse the tracked_objects::Location system to do that. + * + * However, in release builds we don't want this as it doubles the size of the + * BeginFrameArgs object. As well it adds a number of largish strings to the + * binary. Despite the argument being unused, most compilers are unable to + * optimise it away even when unused. Instead we use the BEGINFRAME_FROM_HERE + * macro to prevent the data even getting referenced. + */ +#ifdef NDEBUG +#define BEGINFRAME_FROM_HERE nullptr +#else +#define BEGINFRAME_FROM_HERE FROM_HERE +#endif + +namespace viz { + +struct VIZ_COMMON_EXPORT BeginFrameArgs { + enum BeginFrameArgsType { + INVALID, + NORMAL, + MISSED, + // Not a real type, but used by the IPC system. Should always remain the + // *last* value in this enum. + BEGIN_FRAME_ARGS_TYPE_MAX, + }; + static const char* TypeToString(BeginFrameArgsType type); + + static constexpr uint32_t kStartingSourceId = 0; + // |source_id| for BeginFrameArgs not created by a BeginFrameSource. Used to + // avoid sequence number conflicts of BeginFrameArgs manually fed to an + // observer with those fed to the observer by the its BeginFrameSource. + static constexpr uint32_t kManualSourceId = UINT32_MAX; + + static constexpr uint64_t kInvalidFrameNumber = 0; + static constexpr uint64_t kStartingFrameNumber = 1; + + // Creates an invalid set of values. + BeginFrameArgs(); + +#ifdef NDEBUG + typedef const void* CreationLocation; +#else + typedef const tracked_objects::Location& CreationLocation; + tracked_objects::Location created_from; +#endif + + // You should be able to find all instances where a BeginFrame has been + // created by searching for "BeginFrameArgs::Create". + // The location argument should **always** be BEGINFRAME_FROM_HERE macro. + static BeginFrameArgs Create(CreationLocation location, + uint32_t source_id, + uint64_t sequence_number, + base::TimeTicks frame_time, + base::TimeTicks deadline, + base::TimeDelta interval, + BeginFrameArgsType type); + + // This is the default delta that will be used to adjust the deadline when + // proper draw-time estimations are not yet available. + static base::TimeDelta DefaultEstimatedParentDrawTime(); + + // This is the default interval to use to avoid sprinkling the code with + // magic numbers. + static base::TimeDelta DefaultInterval(); + + bool IsValid() const { return interval >= base::TimeDelta(); } + + std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; + void AsValueInto(base::trace_event::TracedValue* dict) const; + + base::TimeTicks frame_time; + base::TimeTicks deadline; + base::TimeDelta interval; + + // |source_id| and |sequence_number| identify a BeginFrame within a single + // process and are set by the original BeginFrameSource that created the + // BeginFrameArgs. When |source_id| of consecutive BeginFrameArgs changes, + // observers should expect the continuity of |sequence_number| to break. + uint64_t sequence_number; + uint32_t source_id; // |source_id| after |sequence_number| for packing. + + BeginFrameArgsType type; + bool on_critical_path; + + private: + BeginFrameArgs(uint32_t source_id, + uint64_t sequence_number, + base::TimeTicks frame_time, + base::TimeTicks deadline, + base::TimeDelta interval, + BeginFrameArgsType type); +}; + +// Sent by a BeginFrameObserver as acknowledgment of completing a BeginFrame. +struct VIZ_COMMON_EXPORT BeginFrameAck { + BeginFrameAck(); + BeginFrameAck(uint32_t source_id, uint64_t sequence_number, bool has_damage); + + // Creates a BeginFrameAck for a manual BeginFrame. Used when clients produce + // a CompositorFrame without prior BeginFrame, e.g. for synchronous drawing. + static BeginFrameAck CreateManualAckWithDamage(); + + // Sequence number of the BeginFrame that is acknowledged. + uint64_t sequence_number; + + // Source identifier of the BeginFrame that is acknowledged. The + // BeginFrameSource that receives the acknowledgment uses this to discard + // BeginFrameAcks for BeginFrames sent by a different source. Such a situation + // may occur when the BeginFrameSource of the observer changes while a + // BeginFrame from the old source is still in flight. + uint32_t source_id; // |source_id| after above fields for packing. + + // |true| if the observer has produced damage (e.g. sent a CompositorFrame or + // damaged a surface) as part of responding to the BeginFrame. + bool has_damage; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_FRAME_SINKS_BEGIN_FRAME_ARGS_H_ diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_args_unittest.cc b/chromium/components/viz/common/frame_sinks/begin_frame_args_unittest.cc new file mode 100644 index 00000000000..50f898bb15a --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/begin_frame_args_unittest.cc @@ -0,0 +1,125 @@ +// 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 <string> + +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/test/begin_frame_args_test.h" +#include "testing/gtest/include/gtest/gtest-spi.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace viz { +namespace { + +constexpr base::TimeDelta k1Usec = base::TimeDelta::FromMicroseconds(1); +constexpr base::TimeDelta k2Usec = base::TimeDelta::FromMicroseconds(2); +constexpr base::TimeDelta k3Usec = base::TimeDelta::FromMicroseconds(3); + +TEST(BeginFrameArgsTest, Helpers) { + // Quick create methods work + BeginFrameArgs args0 = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + EXPECT_TRUE(args0.IsValid()) << args0; + + BeginFrameArgs args1 = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1, 0, 0, -1); + EXPECT_FALSE(args1.IsValid()) << args1; + + BeginFrameArgs args2 = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 10, 1, 2, 3); + EXPECT_TRUE(args2.IsValid()) << args2; + EXPECT_EQ(123u, args2.source_id); + EXPECT_EQ(10u, args2.sequence_number); + EXPECT_EQ(k1Usec, args2.frame_time.since_origin()); + EXPECT_EQ(k2Usec, args2.deadline.since_origin()); + EXPECT_EQ(k3Usec, args2.interval); + EXPECT_EQ(BeginFrameArgs::NORMAL, args2.type); + + BeginFrameArgs args4 = CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, 234, 20, 1, 2, 3, BeginFrameArgs::MISSED); + EXPECT_TRUE(args4.IsValid()) << args4; + EXPECT_EQ(234u, args4.source_id); + EXPECT_EQ(20u, args4.sequence_number); + EXPECT_EQ(k1Usec, args4.frame_time.since_origin()); + EXPECT_EQ(k2Usec, args4.deadline.since_origin()); + EXPECT_EQ(k3Usec, args4.interval); + EXPECT_EQ(BeginFrameArgs::MISSED, args4.type); + + // operator== + EXPECT_EQ( + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 20, 4, 5, 6), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 20, 4, 5, 6)); + + EXPECT_NONFATAL_FAILURE( + EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9, BeginFrameArgs::MISSED), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9)), + ""); + + EXPECT_NONFATAL_FAILURE( + EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 4, + 5, 6), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9)), + ""); + + EXPECT_NONFATAL_FAILURE( + EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 40, 7, + 8, 9)), + ""); + + EXPECT_NONFATAL_FAILURE( + EXPECT_EQ(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 123, 30, 7, + 8, 9), + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 234, 30, 7, + 8, 9)), + ""); + + // operator<< + std::stringstream out1; + out1 << args1; + EXPECT_EQ("BeginFrameArgs(NORMAL, 0, 1, 0, 0, -1us)", out1.str()); + std::stringstream out2; + out2 << args2; + EXPECT_EQ("BeginFrameArgs(NORMAL, 123, 10, 1, 2, 3us)", out2.str()); + + // PrintTo + EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 0, 1, 0, 0, -1us)"), + ::testing::PrintToString(args1)); + EXPECT_EQ(std::string("BeginFrameArgs(NORMAL, 123, 10, 1, 2, 3us)"), + ::testing::PrintToString(args2)); +} + +TEST(BeginFrameArgsTest, Create) { + // BeginFrames are not valid by default + BeginFrameArgs args1; + EXPECT_FALSE(args1.IsValid()) << args1; + EXPECT_TRUE(args1.on_critical_path); + + BeginFrameArgs args2 = BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, 123, 10, base::TimeTicks() + k1Usec, + base::TimeTicks() + k2Usec, k3Usec, BeginFrameArgs::NORMAL); + EXPECT_TRUE(args2.IsValid()) << args2; + EXPECT_EQ(123u, args2.source_id) << args2; + EXPECT_EQ(10u, args2.sequence_number) << args2; + EXPECT_EQ(k1Usec, args2.frame_time.since_origin()) << args2; + EXPECT_EQ(k2Usec, args2.deadline.since_origin()) << args2; + EXPECT_EQ(k3Usec, args2.interval) << args2; + EXPECT_EQ(BeginFrameArgs::NORMAL, args2.type) << args2; +} + +#ifndef NDEBUG +TEST(BeginFrameArgsTest, Location) { + tracked_objects::Location expected_location = BEGINFRAME_FROM_HERE; + + BeginFrameArgs args = CreateBeginFrameArgsForTesting(expected_location, 0, 1); + EXPECT_EQ(expected_location.ToString(), args.created_from.ToString()); +} +#endif + +} // namespace +} // namespace viz diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source.cc b/chromium/components/viz/common/frame_sinks/begin_frame_source.cc new file mode 100644 index 00000000000..7b3729259de --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/begin_frame_source.cc @@ -0,0 +1,354 @@ +// 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/frame_sinks/begin_frame_source.h" + +#include <stddef.h> + +#include "base/atomic_sequence_num.h" +#include "base/auto_reset.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_argument.h" +#include "components/viz/common/frame_sinks/delay_based_time_source.h" + +namespace viz { + +namespace { +// kDoubleTickDivisor prevents the SyntheticBFS from sending BeginFrames too +// often to an observer. +static const double kDoubleTickDivisor = 2.0; +} // namespace + +// BeginFrameObserverBase ------------------------------------------------- +BeginFrameObserverBase::BeginFrameObserverBase() = default; + +BeginFrameObserverBase::~BeginFrameObserverBase() = default; + +const BeginFrameArgs& BeginFrameObserverBase::LastUsedBeginFrameArgs() const { + return last_begin_frame_args_; +} + +void BeginFrameObserverBase::OnBeginFrame(const BeginFrameArgs& args) { + DCHECK(args.IsValid()); + DCHECK(args.frame_time >= last_begin_frame_args_.frame_time); + DCHECK(args.sequence_number > last_begin_frame_args_.sequence_number || + args.source_id != last_begin_frame_args_.source_id) + << "current " << args.AsValue()->ToString() << ", last " + << last_begin_frame_args_.AsValue()->ToString(); + bool used = OnBeginFrameDerivedImpl(args); + if (used) { + last_begin_frame_args_ = args; + } else { + ++dropped_begin_frame_args_; + } +} + +void BeginFrameObserverBase::AsValueInto( + base::trace_event::TracedValue* state) const { + state->SetInteger("dropped_begin_frame_args", dropped_begin_frame_args_); + + state->BeginDictionary("last_begin_frame_args"); + last_begin_frame_args_.AsValueInto(state); + state->EndDictionary(); +} + +// BeginFrameSource ------------------------------------------------------- +namespace { +static base::AtomicSequenceNumber g_next_source_id; +} // namespace + +BeginFrameSource::BeginFrameSource() : source_id_(g_next_source_id.GetNext()) {} + +BeginFrameSource::~BeginFrameSource() = default; + +void BeginFrameSource::AsValueInto( + base::trace_event::TracedValue* state) const { + state->SetInteger("source_id", source_id_); +} + +// StubBeginFrameSource --------------------------------------------------- +bool StubBeginFrameSource::IsThrottled() const { + return true; +} + +// SyntheticBeginFrameSource ---------------------------------------------- +SyntheticBeginFrameSource::~SyntheticBeginFrameSource() = default; + +// BackToBackBeginFrameSource --------------------------------------------- +BackToBackBeginFrameSource::BackToBackBeginFrameSource( + std::unique_ptr<DelayBasedTimeSource> time_source) + : time_source_(std::move(time_source)), + next_sequence_number_(BeginFrameArgs::kStartingFrameNumber), + weak_factory_(this) { + time_source_->SetClient(this); + // The time_source_ ticks immediately, so we SetActive(true) for a single + // tick when we need it, and keep it as SetActive(false) otherwise. + time_source_->SetTimebaseAndInterval(base::TimeTicks(), base::TimeDelta()); +} + +BackToBackBeginFrameSource::~BackToBackBeginFrameSource() = default; + +void BackToBackBeginFrameSource::AddObserver(BeginFrameObserver* obs) { + DCHECK(obs); + DCHECK(observers_.find(obs) == observers_.end()); + observers_.insert(obs); + pending_begin_frame_observers_.insert(obs); + obs->OnBeginFrameSourcePausedChanged(false); + time_source_->SetActive(true); +} + +void BackToBackBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) { + DCHECK(obs); + DCHECK(observers_.find(obs) != observers_.end()); + observers_.erase(obs); + pending_begin_frame_observers_.erase(obs); + if (pending_begin_frame_observers_.empty()) + time_source_->SetActive(false); +} + +void BackToBackBeginFrameSource::DidFinishFrame(BeginFrameObserver* obs) { + if (observers_.find(obs) != observers_.end()) { + pending_begin_frame_observers_.insert(obs); + time_source_->SetActive(true); + } +} + +bool BackToBackBeginFrameSource::IsThrottled() const { + return false; +} + +void BackToBackBeginFrameSource::OnTimerTick() { + base::TimeTicks frame_time = time_source_->LastTickTime(); + base::TimeDelta default_interval = BeginFrameArgs::DefaultInterval(); + BeginFrameArgs args = BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time, + frame_time + default_interval, default_interval, BeginFrameArgs::NORMAL); + next_sequence_number_++; + + // This must happen after getting the LastTickTime() from the time source. + time_source_->SetActive(false); + + std::unordered_set<BeginFrameObserver*> pending_observers; + pending_observers.swap(pending_begin_frame_observers_); + DCHECK(!pending_observers.empty()); + for (BeginFrameObserver* obs : pending_observers) + obs->OnBeginFrame(args); +} + +// DelayBasedBeginFrameSource --------------------------------------------- +DelayBasedBeginFrameSource::DelayBasedBeginFrameSource( + std::unique_ptr<DelayBasedTimeSource> time_source) + : time_source_(std::move(time_source)), + next_sequence_number_(BeginFrameArgs::kStartingFrameNumber) { + time_source_->SetClient(this); +} + +DelayBasedBeginFrameSource::~DelayBasedBeginFrameSource() = default; + +void DelayBasedBeginFrameSource::OnUpdateVSyncParameters( + base::TimeTicks timebase, + base::TimeDelta interval) { + if (!authoritative_interval_.is_zero()) { + interval = authoritative_interval_; + } else if (interval.is_zero()) { + // TODO(brianderson): We should not be receiving 0 intervals. + interval = BeginFrameArgs::DefaultInterval(); + } + + last_timebase_ = timebase; + time_source_->SetTimebaseAndInterval(timebase, interval); +} + +void DelayBasedBeginFrameSource::SetAuthoritativeVSyncInterval( + base::TimeDelta interval) { + authoritative_interval_ = interval; + OnUpdateVSyncParameters(last_timebase_, interval); +} + +BeginFrameArgs DelayBasedBeginFrameSource::CreateBeginFrameArgs( + base::TimeTicks frame_time) { + uint64_t sequence_number = next_sequence_number_++; + return BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, source_id(), sequence_number, frame_time, + time_source_->NextTickTime(), time_source_->Interval(), + BeginFrameArgs::NORMAL); +} + +void DelayBasedBeginFrameSource::AddObserver(BeginFrameObserver* obs) { + DCHECK(obs); + DCHECK(observers_.find(obs) == observers_.end()); + + observers_.insert(obs); + obs->OnBeginFrameSourcePausedChanged(false); + time_source_->SetActive(true); + + // Missed args should correspond to |last_begin_frame_args_| (particularly, + // have the same sequence number) if |last_begin_frame_args_| still correspond + // to the last time the time source should have ticked. This may not be the + // case if the time source was inactive before AddObserver() was called. In + // such a case, we create new args with a new sequence number only if + // sufficient time has passed since the last tick. + base::TimeTicks last_or_missed_tick_time = + time_source_->NextTickTime() - time_source_->Interval(); + if (!last_begin_frame_args_.IsValid() || + last_or_missed_tick_time > + last_begin_frame_args_.frame_time + + last_begin_frame_args_.interval / kDoubleTickDivisor) { + last_begin_frame_args_ = CreateBeginFrameArgs(last_or_missed_tick_time); + } + BeginFrameArgs missed_args = last_begin_frame_args_; + missed_args.type = BeginFrameArgs::MISSED; + + BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); + if (!last_args.IsValid() || + (missed_args.frame_time > + last_args.frame_time + missed_args.interval / kDoubleTickDivisor)) { + DCHECK(missed_args.sequence_number > last_args.sequence_number || + missed_args.source_id != last_args.source_id) + << "missed " << missed_args.AsValue()->ToString() << ", last " + << last_args.AsValue()->ToString(); + obs->OnBeginFrame(missed_args); + } +} + +void DelayBasedBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) { + DCHECK(obs); + DCHECK(observers_.find(obs) != observers_.end()); + + observers_.erase(obs); + if (observers_.empty()) + time_source_->SetActive(false); +} + +bool DelayBasedBeginFrameSource::IsThrottled() const { + return true; +} + +void DelayBasedBeginFrameSource::OnTimerTick() { + last_begin_frame_args_ = CreateBeginFrameArgs(time_source_->LastTickTime()); + std::unordered_set<BeginFrameObserver*> observers(observers_); + for (auto* obs : observers) { + BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); + if (!last_args.IsValid() || + (last_begin_frame_args_.frame_time > + last_args.frame_time + + last_begin_frame_args_.interval / kDoubleTickDivisor)) { + obs->OnBeginFrame(last_begin_frame_args_); + } + } +} + +// ExternalBeginFrameSource ----------------------------------------------- +ExternalBeginFrameSource::ExternalBeginFrameSource( + ExternalBeginFrameSourceClient* client) + : client_(client) { + DCHECK(client_); +} + +ExternalBeginFrameSource::~ExternalBeginFrameSource() = default; + +void ExternalBeginFrameSource::AsValueInto( + base::trace_event::TracedValue* state) const { + BeginFrameSource::AsValueInto(state); + + state->SetBoolean("paused", paused_); + state->SetInteger("num_observers", observers_.size()); + + state->BeginDictionary("last_begin_frame_args"); + last_begin_frame_args_.AsValueInto(state); + state->EndDictionary(); +} + +void ExternalBeginFrameSource::AddObserver(BeginFrameObserver* obs) { + DCHECK(obs); + DCHECK(observers_.find(obs) == observers_.end()); + + bool observers_was_empty = observers_.empty(); + observers_.insert(obs); + obs->OnBeginFrameSourcePausedChanged(paused_); + if (observers_was_empty) + client_->OnNeedsBeginFrames(true); + + // Send a MISSED begin frame if necessary. + BeginFrameArgs missed_args = GetMissedBeginFrameArgs(obs); + if (missed_args.IsValid()) { + DCHECK_EQ(BeginFrameArgs::MISSED, missed_args.type); + obs->OnBeginFrame(missed_args); + } +} + +void ExternalBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) { + DCHECK(obs); + DCHECK(observers_.find(obs) != observers_.end()); + + observers_.erase(obs); + if (observers_.empty()) + client_->OnNeedsBeginFrames(false); +} + +bool ExternalBeginFrameSource::IsThrottled() const { + return true; +} + +void ExternalBeginFrameSource::OnSetBeginFrameSourcePaused(bool paused) { + if (paused_ == paused) + return; + paused_ = paused; + std::unordered_set<BeginFrameObserver*> observers(observers_); + for (auto* obs : observers) + obs->OnBeginFrameSourcePausedChanged(paused_); +} + +void ExternalBeginFrameSource::OnBeginFrame(const BeginFrameArgs& args) { + // Ignore out of order begin frames because of layer tree frame sink being + // recreated. + if (last_begin_frame_args_.IsValid() && + (args.frame_time <= last_begin_frame_args_.frame_time || + (args.source_id == last_begin_frame_args_.source_id && + args.sequence_number <= last_begin_frame_args_.sequence_number))) + return; + + last_begin_frame_args_ = args; + std::unordered_set<BeginFrameObserver*> observers(observers_); + for (auto* obs : observers) { + // It is possible that the source in which |args| originate changes, or that + // our hookup to this source changes, so we have to check for continuity. + // See also https://crbug.com/690127 for what may happen without this check. + const BeginFrameArgs& last_args = obs->LastUsedBeginFrameArgs(); + if (!last_args.IsValid() || (args.frame_time > last_args.frame_time)) { + DCHECK((args.source_id != last_args.source_id) || + (args.sequence_number > last_args.sequence_number)) + << "current " << args.AsValue()->ToString() << ", last " + << last_args.AsValue()->ToString(); + obs->OnBeginFrame(args); + } + } +} + +BeginFrameArgs ExternalBeginFrameSource::GetMissedBeginFrameArgs( + BeginFrameObserver* obs) { + if (!last_begin_frame_args_.IsValid()) + return BeginFrameArgs(); + + const BeginFrameArgs& last_args = obs->LastUsedBeginFrameArgs(); + if (last_args.IsValid() && + last_begin_frame_args_.frame_time <= last_args.frame_time) { + return BeginFrameArgs(); + } + + DCHECK((last_begin_frame_args_.source_id != last_args.source_id) || + (last_begin_frame_args_.sequence_number > last_args.sequence_number)) + << "current " << last_begin_frame_args_.AsValue()->ToString() << ", last " + << last_args.AsValue()->ToString(); + BeginFrameArgs missed_args = last_begin_frame_args_; + missed_args.type = BeginFrameArgs::MISSED; + return missed_args; +} + +} // namespace viz diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source.h b/chromium/components/viz/common/frame_sinks/begin_frame_source.h new file mode 100644 index 00000000000..8c6c46201fe --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/begin_frame_source.h @@ -0,0 +1,275 @@ +// 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_FRAME_SINKS_BEGIN_FRAME_SOURCE_H_ +#define COMPONENTS_VIZ_COMMON_FRAME_SINKS_BEGIN_FRAME_SOURCE_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <set> +#include <string> + +#include "base/logging.h" +#include "base/macros.h" +#include "base/trace_event/trace_event.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/common/frame_sinks/delay_based_time_source.h" + +namespace viz { + +// (Pure) Interface for observing BeginFrame messages from BeginFrameSource +// objects. +class VIZ_COMMON_EXPORT BeginFrameObserver { + public: + virtual ~BeginFrameObserver() {} + + // The |args| given to OnBeginFrame is guaranteed to have + // |args|.IsValid()==true. If |args|.source_id did not change between + // invocations, |args|.sequence_number is guaranteed to be be strictly greater + // than the previous call. Further, |args|.frame_time is guaranteed to be + // greater than or equal to the previous call even if the source_id changes. + // + // Side effects: This function can (and most of the time *will*) change the + // return value of the LastUsedBeginFrameArgs method. See the documentation + // on that method for more information. + // + // The observer is required call BeginFrameSource::DidFinishFrame() as soon as + // it has completed handling the BeginFrame. + virtual void OnBeginFrame(const BeginFrameArgs& args) = 0; + + // Returns the last BeginFrameArgs used by the observer. This method's + // return value is affected by the OnBeginFrame method! + // + // - Before the first call of OnBeginFrame, this method should return a + // BeginFrameArgs on which IsValid() returns false. + // + // - If the |args| passed to OnBeginFrame is (or *will be*) used, then + // LastUsedBeginFrameArgs return value should become the |args| given to + // OnBeginFrame. + // + // - If the |args| passed to OnBeginFrame is dropped, then + // LastUsedBeginFrameArgs return value should *not* change. + // + // These requirements are designed to allow chaining and nesting of + // BeginFrameObservers which filter the incoming BeginFrame messages while + // preventing "double dropping" and other bad side effects. + virtual const BeginFrameArgs& LastUsedBeginFrameArgs() const = 0; + + virtual void OnBeginFrameSourcePausedChanged(bool paused) = 0; +}; + +// Simple base class which implements a BeginFrameObserver which checks the +// incoming values meet the BeginFrameObserver requirements and implements the +// required LastUsedBeginFrameArgs behaviour. +// +// Users of this class should; +// - Implement the OnBeginFrameDerivedImpl function. +// - Recommended (but not required) to call +// BeginFrameObserverBase::OnValueInto in their overridden OnValueInto +// function. +class VIZ_COMMON_EXPORT BeginFrameObserverBase : public BeginFrameObserver { + public: + BeginFrameObserverBase(); + ~BeginFrameObserverBase() override; + + // BeginFrameObserver + + // Traces |args| and DCHECK |args| satisfies pre-conditions then calls + // OnBeginFrameDerivedImpl and updates the last_begin_frame_args_ value on + // true. + void OnBeginFrame(const BeginFrameArgs& args) override; + const BeginFrameArgs& LastUsedBeginFrameArgs() const override; + + protected: + // Return true if the given argument is (or will be) used. + virtual bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) = 0; + + void AsValueInto(base::trace_event::TracedValue* state) const; + + BeginFrameArgs last_begin_frame_args_; + int64_t dropped_begin_frame_args_ = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(BeginFrameObserverBase); +}; + +// Interface for a class which produces BeginFrame calls to a +// BeginFrameObserver. +// +// BeginFrame calls *normally* occur just after a vsync interrupt when input +// processing has been finished and provide information about the time values +// of the vsync times. *However*, these values can be heavily modified or even +// plain made up (when no vsync signal is available or vsync throttling is +// turned off). See the BeginFrameObserver for information about the guarantees +// all BeginFrameSources *must* provide. +class VIZ_COMMON_EXPORT BeginFrameSource { + public: + BeginFrameSource(); + virtual ~BeginFrameSource(); + + // Returns an identifier for this BeginFrameSource. Guaranteed unique within a + // process, but not across processes. This is used to create BeginFrames that + // originate at this source. Note that BeginFrameSources may pass on + // BeginFrames created by other sources, with different IDs. + uint32_t source_id() const { return source_id_; } + + // BeginFrameObservers use DidFinishFrame to provide back pressure to a frame + // source about frame processing (rather than toggling SetNeedsBeginFrames + // every frame). For example, the BackToBackFrameSource uses them to make sure + // only one frame is pending at a time. + virtual void DidFinishFrame(BeginFrameObserver* obs) = 0; + + // Add/Remove an observer from the source. When no observers are added the BFS + // should shut down its timers, disable vsync, etc. + virtual void AddObserver(BeginFrameObserver* obs) = 0; + virtual void RemoveObserver(BeginFrameObserver* obs) = 0; + + // Returns false if the begin frame source will just continue to produce + // begin frames without waiting. + virtual bool IsThrottled() const = 0; + + virtual void AsValueInto(base::trace_event::TracedValue* state) const; + + private: + uint32_t source_id_; + + DISALLOW_COPY_AND_ASSIGN(BeginFrameSource); +}; + +// A BeginFrameSource that does nothing. +class VIZ_COMMON_EXPORT StubBeginFrameSource : public BeginFrameSource { + public: + void DidFinishFrame(BeginFrameObserver* obs) override {} + void AddObserver(BeginFrameObserver* obs) override {} + void RemoveObserver(BeginFrameObserver* obs) override {} + bool IsThrottled() const override; +}; + +// A frame source which ticks itself independently. +class VIZ_COMMON_EXPORT SyntheticBeginFrameSource : public BeginFrameSource { + public: + ~SyntheticBeginFrameSource() override; + + virtual void OnUpdateVSyncParameters(base::TimeTicks timebase, + base::TimeDelta interval) = 0; + // This overrides any past or future interval from updating vsync parameters. + virtual void SetAuthoritativeVSyncInterval(base::TimeDelta interval) = 0; +}; + +// A frame source which calls BeginFrame (at the next possible time) as soon as +// an observer acknowledges the prior BeginFrame. +class VIZ_COMMON_EXPORT BackToBackBeginFrameSource + : public SyntheticBeginFrameSource, + public DelayBasedTimeSourceClient { + public: + explicit BackToBackBeginFrameSource( + std::unique_ptr<DelayBasedTimeSource> time_source); + ~BackToBackBeginFrameSource() override; + + // BeginFrameSource implementation. + void AddObserver(BeginFrameObserver* obs) override; + void RemoveObserver(BeginFrameObserver* obs) override; + void DidFinishFrame(BeginFrameObserver* obs) override; + bool IsThrottled() const override; + + // SyntheticBeginFrameSource implementation. + void OnUpdateVSyncParameters(base::TimeTicks timebase, + base::TimeDelta interval) override {} + void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override {} + + // DelayBasedTimeSourceClient implementation. + void OnTimerTick() override; + + private: + std::unique_ptr<DelayBasedTimeSource> time_source_; + std::unordered_set<BeginFrameObserver*> observers_; + std::unordered_set<BeginFrameObserver*> pending_begin_frame_observers_; + uint64_t next_sequence_number_; + base::WeakPtrFactory<BackToBackBeginFrameSource> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(BackToBackBeginFrameSource); +}; + +// A frame source which is locked to an external parameters provides from a +// vsync source and generates BeginFrameArgs for it. +class VIZ_COMMON_EXPORT DelayBasedBeginFrameSource + : public SyntheticBeginFrameSource, + public DelayBasedTimeSourceClient { + public: + explicit DelayBasedBeginFrameSource( + std::unique_ptr<DelayBasedTimeSource> time_source); + ~DelayBasedBeginFrameSource() override; + + // BeginFrameSource implementation. + void AddObserver(BeginFrameObserver* obs) override; + void RemoveObserver(BeginFrameObserver* obs) override; + void DidFinishFrame(BeginFrameObserver* obs) override {} + bool IsThrottled() const override; + + // SyntheticBeginFrameSource implementation. + void OnUpdateVSyncParameters(base::TimeTicks timebase, + base::TimeDelta interval) override; + void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override; + + // DelayBasedTimeSourceClient implementation. + void OnTimerTick() override; + + private: + BeginFrameArgs CreateBeginFrameArgs(base::TimeTicks frame_time); + + std::unique_ptr<DelayBasedTimeSource> time_source_; + std::unordered_set<BeginFrameObserver*> observers_; + base::TimeTicks last_timebase_; + base::TimeDelta authoritative_interval_; + BeginFrameArgs last_begin_frame_args_; + uint64_t next_sequence_number_; + + DISALLOW_COPY_AND_ASSIGN(DelayBasedBeginFrameSource); +}; + +class VIZ_COMMON_EXPORT ExternalBeginFrameSourceClient { + public: + // Only called when changed. Assumed false by default. + virtual void OnNeedsBeginFrames(bool needs_begin_frames) = 0; +}; + +// A BeginFrameSource that is only ticked manually. Usually the endpoint +// of messages from some other thread/process that send OnBeginFrame and +// receive SetNeedsBeginFrame messages. This turns such messages back into +// an observable BeginFrameSource. +class VIZ_COMMON_EXPORT ExternalBeginFrameSource : public BeginFrameSource { + public: + // Client lifetime must be preserved by owner past the lifetime of this class. + explicit ExternalBeginFrameSource(ExternalBeginFrameSourceClient* client); + ~ExternalBeginFrameSource() override; + + // BeginFrameSource implementation. + void AddObserver(BeginFrameObserver* obs) override; + void RemoveObserver(BeginFrameObserver* obs) override; + void DidFinishFrame(BeginFrameObserver* obs) override {} + bool IsThrottled() const override; + void AsValueInto(base::trace_event::TracedValue* state) const override; + + void OnSetBeginFrameSourcePaused(bool paused); + void OnBeginFrame(const BeginFrameArgs& args); + + protected: + // Called on AddObserver and gets missed BeginFrameArgs for the given + // observer. The missed BeginFrame is sent only if the returned + // BeginFrameArgs is valid. + virtual BeginFrameArgs GetMissedBeginFrameArgs(BeginFrameObserver* obs); + + BeginFrameArgs last_begin_frame_args_; + std::unordered_set<BeginFrameObserver*> observers_; + ExternalBeginFrameSourceClient* client_; + bool paused_ = false; + + private: + DISALLOW_COPY_AND_ASSIGN(ExternalBeginFrameSource); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_FRAME_SINKS_BEGIN_FRAME_SOURCE_H_ diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc b/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc new file mode 100644 index 00000000000..0b646241e63 --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc @@ -0,0 +1,610 @@ +// 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. + +#include "components/viz/common/frame_sinks/begin_frame_source.h" + +#include <stdint.h> + +#include "base/memory/ptr_util.h" +#include "base/test/test_simple_task_runner.h" +#include "components/viz/test/begin_frame_args_test.h" +#include "components/viz/test/begin_frame_source_test.h" +#include "components/viz/test/fake_delay_based_time_source.h" +#include "components/viz/test/ordered_simple_task_runner.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::NiceMock; +using testing::_; + +namespace viz { +namespace { + +// Returns a fake TimeTicks based on the given microsecond offset. +base::TimeTicks TicksFromMicroseconds(int64_t micros) { + return base::TimeTicks() + base::TimeDelta::FromMicroseconds(micros); +} + +// BeginFrameSource testing ---------------------------------------------------- +TEST(BeginFrameSourceTest, SourceIdsAreUnique) { + StubBeginFrameSource source1; + StubBeginFrameSource source2; + StubBeginFrameSource source3; + EXPECT_NE(source1.source_id(), source2.source_id()); + EXPECT_NE(source1.source_id(), source3.source_id()); + EXPECT_NE(source2.source_id(), source3.source_id()); +} + +// BackToBackBeginFrameSource testing +// ------------------------------------------ +class BackToBackBeginFrameSourceTest : public ::testing::Test { + protected: + static const int64_t kDeadline; + static const int64_t kInterval; + + void SetUp() override { + now_src_.reset(new base::SimpleTestTickClock()); + now_src_->Advance(base::TimeDelta::FromMicroseconds(1000)); + task_runner_ = + make_scoped_refptr(new OrderedSimpleTaskRunner(now_src_.get(), false)); + std::unique_ptr<FakeDelayBasedTimeSource> time_source( + new FakeDelayBasedTimeSource(now_src_.get(), task_runner_.get())); + delay_based_time_source_ = time_source.get(); + source_.reset(new BackToBackBeginFrameSource(std::move(time_source))); + obs_ = base::WrapUnique(new ::testing::NiceMock<MockBeginFrameObserver>); + } + + void TearDown() override { obs_.reset(); } + + std::unique_ptr<base::SimpleTestTickClock> now_src_; + scoped_refptr<OrderedSimpleTaskRunner> task_runner_; + std::unique_ptr<BackToBackBeginFrameSource> source_; + std::unique_ptr<MockBeginFrameObserver> obs_; + FakeDelayBasedTimeSource* delay_based_time_source_; // Owned by |now_src_|. +}; + +const int64_t BackToBackBeginFrameSourceTest::kDeadline = + BeginFrameArgs::DefaultInterval().InMicroseconds(); + +const int64_t BackToBackBeginFrameSourceTest::kInterval = + BeginFrameArgs::DefaultInterval().InMicroseconds(); + +TEST_F(BackToBackBeginFrameSourceTest, AddObserverSendsBeginFrame) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + EXPECT_TRUE(task_runner_->HasPendingTasks()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); + + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100, + 1100 + kDeadline, kInterval); + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(obs_.get()); + task_runner_->RunPendingTasks(); +} + +TEST_F(BackToBackBeginFrameSourceTest, + RemoveObserverThenDidFinishFrameProducesNoFrame) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); + + source_->RemoveObserver(obs_.get()); + source_->DidFinishFrame(obs_.get()); + + // Verify no BeginFrame is sent to |obs_|. There is a pending task in the + // task_runner_ as a BeginFrame was posted, but it gets aborted since |obs_| + // is removed. + task_runner_->RunPendingTasks(); + EXPECT_FALSE(task_runner_->HasPendingTasks()); +} + +TEST_F(BackToBackBeginFrameSourceTest, + DidFinishFrameThenRemoveObserverProducesNoFrame) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(obs_.get()); + source_->RemoveObserver(obs_.get()); + + EXPECT_TRUE(task_runner_->HasPendingTasks()); + task_runner_->RunPendingTasks(); +} + +TEST_F(BackToBackBeginFrameSourceTest, + TogglingObserverThenDidFinishFrameProducesCorrectFrame) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->RemoveObserver(obs_.get()); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(10)); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(10)); + source_->DidFinishFrame(obs_.get()); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(10)); + // The begin frame is posted at the time when the observer was added, + // so it ignores changes to "now" afterward. + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1110, + 1110 + kDeadline, kInterval); + EXPECT_TRUE(task_runner_->HasPendingTasks()); + task_runner_->RunPendingTasks(); +} + +TEST_F(BackToBackBeginFrameSourceTest, + DidFinishFrameThenTogglingObserverProducesCorrectFrame) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(obs_.get()); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(10)); + source_->RemoveObserver(obs_.get()); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(10)); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(10)); + // Ticks at the time at which the observer was added, ignoring the + // last change to "now". + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1120, + 1120 + kDeadline, kInterval); + EXPECT_TRUE(task_runner_->HasPendingTasks()); + task_runner_->RunPendingTasks(); +} + +TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameNoObserver) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + source_->RemoveObserver(obs_.get()); + source_->DidFinishFrame(obs_.get()); + EXPECT_FALSE(task_runner_->RunPendingTasks()); +} + +TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameMultipleCallsIdempotent) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(obs_.get()); + source_->DidFinishFrame(obs_.get()); + source_->DidFinishFrame(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100, + 1100 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(obs_.get()); + source_->DidFinishFrame(obs_.get()); + source_->DidFinishFrame(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 1200, + 1200 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); +} + +TEST_F(BackToBackBeginFrameSourceTest, DelayInPostedTaskProducesCorrectFrame) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + source_->AddObserver(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000, + 1000 + kDeadline, kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(obs_.get()); + now_src_->Advance(base::TimeDelta::FromMicroseconds(50)); + // Ticks at the time the last frame finished, so ignores the last change to + // "now". + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100, + 1100 + kDeadline, kInterval); + + EXPECT_TRUE(task_runner_->HasPendingTasks()); + task_runner_->RunPendingTasks(); +} + +TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversSynchronized) { + NiceMock<MockBeginFrameObserver> obs1, obs2; + + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); + source_->AddObserver(&obs1); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); + source_->AddObserver(&obs2); + + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(&obs1); + source_->DidFinishFrame(&obs2); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(&obs1); + source_->DidFinishFrame(&obs2); + EXPECT_TRUE(task_runner_->HasPendingTasks()); + source_->RemoveObserver(&obs1); + source_->RemoveObserver(&obs2); + task_runner_->RunPendingTasks(); +} + +TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversInterleaved) { + NiceMock<MockBeginFrameObserver> obs1, obs2; + + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); + source_->AddObserver(&obs1); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); + source_->AddObserver(&obs2); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(&obs1); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 1200, 1200 + kDeadline, + kInterval); + task_runner_->RunPendingTasks(); + + source_->DidFinishFrame(&obs1); + source_->RemoveObserver(&obs1); + // Removing all finished observers should disable the time source. + EXPECT_FALSE(delay_based_time_source_->Active()); + // Finishing the frame for |obs1| posts a begin frame task, which will be + // aborted since |obs1| is removed. Clear that from the task runner. + task_runner_->RunPendingTasks(); + + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(&obs2); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 1300, 1300 + kDeadline, + kInterval); + task_runner_->RunPendingTasks(); + + source_->DidFinishFrame(&obs2); + source_->RemoveObserver(&obs2); +} + +TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversAtOnce) { + NiceMock<MockBeginFrameObserver> obs1, obs2; + + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); + source_->AddObserver(&obs1); + source_->AddObserver(&obs2); + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline, + kInterval); + task_runner_->RunPendingTasks(); + + // |obs1| finishes first. + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(&obs1); + + // |obs2| finishes also, before getting to the newly posted begin frame. + now_src_->Advance(base::TimeDelta::FromMicroseconds(100)); + source_->DidFinishFrame(&obs2); + + // Because the begin frame source already ticked when |obs1| finished, + // we see it as the frame time for both observers. + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline, + kInterval); + task_runner_->RunPendingTasks(); + + source_->DidFinishFrame(&obs1); + source_->RemoveObserver(&obs1); + source_->DidFinishFrame(&obs2); + source_->RemoveObserver(&obs2); +} + +// DelayBasedBeginFrameSource testing +// ------------------------------------------ +class DelayBasedBeginFrameSourceTest : public ::testing::Test { + public: + std::unique_ptr<base::SimpleTestTickClock> now_src_; + scoped_refptr<OrderedSimpleTaskRunner> task_runner_; + std::unique_ptr<DelayBasedBeginFrameSource> source_; + std::unique_ptr<MockBeginFrameObserver> obs_; + + void SetUp() override { + now_src_.reset(new base::SimpleTestTickClock()); + now_src_->Advance(base::TimeDelta::FromMicroseconds(1000)); + task_runner_ = + make_scoped_refptr(new OrderedSimpleTaskRunner(now_src_.get(), false)); + std::unique_ptr<DelayBasedTimeSource> time_source( + new FakeDelayBasedTimeSource(now_src_.get(), task_runner_.get())); + time_source->SetTimebaseAndInterval( + base::TimeTicks(), base::TimeDelta::FromMicroseconds(10000)); + source_.reset(new DelayBasedBeginFrameSource(std::move(time_source))); + obs_.reset(new MockBeginFrameObserver); + } + + void TearDown() override { obs_.reset(); } +}; + +TEST_F(DelayBasedBeginFrameSourceTest, + AddObserverCallsOnBeginFrameWithMissedTick) { + now_src_->Advance(base::TimeDelta::FromMicroseconds(9010)); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 10000, 20000, + 10000); + source_->AddObserver(obs_.get()); // Should cause the last tick to be sent + // No tasks should need to be run for this to occur. +} + +TEST_F(DelayBasedBeginFrameSourceTest, AddObserverCallsCausesOnBeginFrame) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000, + 10000); + source_->AddObserver(obs_.get()); + EXPECT_EQ(TicksFromMicroseconds(10000), task_runner_->NextTaskTime()); + + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000); + now_src_->Advance(base::TimeDelta::FromMicroseconds(9010)); + task_runner_->RunPendingTasks(); +} + +TEST_F(DelayBasedBeginFrameSourceTest, BasicOperation) { + task_runner_->SetAutoAdvanceNowToPendingTasks(true); + + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000, + 10000); + source_->AddObserver(obs_.get()); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000); + task_runner_->RunUntilTime(TicksFromMicroseconds(30001)); + + source_->RemoveObserver(obs_.get()); + // No new frames.... + task_runner_->RunUntilTime(TicksFromMicroseconds(60000)); +} + +TEST_F(DelayBasedBeginFrameSourceTest, VSyncChanges) { + task_runner_->SetAutoAdvanceNowToPendingTasks(true); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000, + 10000); + source_->AddObserver(obs_.get()); + + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000); + task_runner_->RunUntilTime(TicksFromMicroseconds(30001)); + + // Update the vsync information + source_->OnUpdateVSyncParameters(TicksFromMicroseconds(27500), + base::TimeDelta::FromMicroseconds(10001)); + + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 47502, 10001); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 47502, 57503, 10001); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 57503, 67504, 10001); + task_runner_->RunUntilTime(TicksFromMicroseconds(60000)); +} + +TEST_F(DelayBasedBeginFrameSourceTest, AuthoritativeVSyncChanges) { + task_runner_->SetAutoAdvanceNowToPendingTasks(true); + source_->OnUpdateVSyncParameters(TicksFromMicroseconds(500), + base::TimeDelta::FromMicroseconds(10000)); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 500, 10500, + 10000); + source_->AddObserver(obs_.get()); + + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10500, 20500, 10000); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20500, 30500, 10000); + task_runner_->RunUntilTime(TicksFromMicroseconds(20501)); + + // This will keep the same timebase, so 500, 9999 + source_->SetAuthoritativeVSyncInterval( + base::TimeDelta::FromMicroseconds(9999)); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30500, 40496, 9999); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40496, 50495, 9999); + task_runner_->RunUntilTime(TicksFromMicroseconds(40497)); + + // Change the vsync params, but the new interval will be ignored. + source_->OnUpdateVSyncParameters(TicksFromMicroseconds(400), + base::TimeDelta::FromMicroseconds(1)); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 50495, 60394, 9999); + EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 60394, 70393, 9999); + task_runner_->RunUntilTime(TicksFromMicroseconds(60395)); +} + +TEST_F(DelayBasedBeginFrameSourceTest, MultipleObservers) { + NiceMock<MockBeginFrameObserver> obs1, obs2; + + // now_src_ starts off at 1000. + task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(9010)); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); + EXPECT_BEGIN_FRAME_USED_MISSED(obs1, source_->source_id(), 1, 10000, 20000, + 10000); + source_->AddObserver(&obs1); // Should cause the last tick to be sent + // No tasks should need to be run for this to occur. + + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 20000, 30000, 10000); + task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000)); + + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); + // Sequence number unchanged for missed frame with time of last normal frame. + EXPECT_BEGIN_FRAME_USED_MISSED(obs2, source_->source_id(), 2, 20000, 30000, + 10000); + source_->AddObserver(&obs2); // Should cause the last tick to be sent + // No tasks should need to be run for this to occur. + + EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 30000, 40000, 10000); + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 3, 30000, 40000, 10000); + task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000)); + + source_->RemoveObserver(&obs1); + + EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 40000, 50000, 10000); + task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000)); + + source_->RemoveObserver(&obs2); + task_runner_->RunUntilTime(TicksFromMicroseconds(50000)); + EXPECT_FALSE(task_runner_->HasPendingTasks()); +} + +TEST_F(DelayBasedBeginFrameSourceTest, DoubleTick) { + NiceMock<MockBeginFrameObserver> obs; + + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); + EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000, 10000); + source_->AddObserver(&obs); + + source_->OnUpdateVSyncParameters(TicksFromMicroseconds(5000), + base::TimeDelta::FromMicroseconds(10000)); + now_src_->Advance(base::TimeDelta::FromMicroseconds(4000)); + + // No begin frame received. + task_runner_->RunPendingTasks(); + + // Begin frame received. + source_->OnUpdateVSyncParameters(TicksFromMicroseconds(10000), + base::TimeDelta::FromMicroseconds(10000)); + now_src_->Advance(base::TimeDelta::FromMicroseconds(5000)); + EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000); + task_runner_->RunPendingTasks(); +} + +TEST_F(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) { + NiceMock<MockBeginFrameObserver> obs; + + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); + EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000, 10000); + source_->AddObserver(&obs); + source_->RemoveObserver(&obs); + + source_->OnUpdateVSyncParameters(TicksFromMicroseconds(5000), + base::TimeDelta::FromMicroseconds(10000)); + now_src_->Advance(base::TimeDelta::FromMicroseconds(4000)); + + // No missed frame received. + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); + // This does not cause a missed BeginFrame because of double ticking + // prevention. It does not produce a new sequence number. + source_->AddObserver(&obs); + source_->RemoveObserver(&obs); + + // Missed frame received. + source_->OnUpdateVSyncParameters(TicksFromMicroseconds(10000), + base::TimeDelta::FromMicroseconds(10000)); + now_src_->Advance(base::TimeDelta::FromMicroseconds(5000)); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); + // Sequence number is incremented again, because sufficient time has passed. + EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 2, 10000, 20000, + 10000); + source_->AddObserver(&obs); + source_->RemoveObserver(&obs); +} + +// ExternalBeginFrameSource testing +// -------------------------------------------- +class MockExternalBeginFrameSourceClient + : public ExternalBeginFrameSourceClient { + public: + MOCK_METHOD1(OnNeedsBeginFrames, void(bool)); +}; + +class ExternalBeginFrameSourceTest : public ::testing::Test { + public: + std::unique_ptr<MockExternalBeginFrameSourceClient> client_; + std::unique_ptr<ExternalBeginFrameSource> source_; + std::unique_ptr<MockBeginFrameObserver> obs_; + + void SetUp() override { + client_.reset(new MockExternalBeginFrameSourceClient); + source_.reset(new ExternalBeginFrameSource(client_.get())); + obs_.reset(new MockBeginFrameObserver); + } + + void TearDown() override { + client_.reset(); + obs_.reset(); + } +}; + +// https://crbug.com/690127: Duplicate BeginFrame caused DCHECK crash. +TEST_F(ExternalBeginFrameSourceTest, OnBeginFrameChecksBeginFrameContinuity) { + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1); + source_->AddObserver(obs_.get()); + + BeginFrameArgs args = CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, 0, 2, TicksFromMicroseconds(10000)); + EXPECT_BEGIN_FRAME_ARGS_USED(*obs_, args); + source_->OnBeginFrame(args); + + // Providing same args again to OnBeginFrame() should not notify observer. + source_->OnBeginFrame(args); + + // Providing same args through a different ExternalBeginFrameSource also + // does not notify observer. + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1); + ExternalBeginFrameSource source2(client_.get()); + source2.AddObserver(obs_.get()); + source2.OnBeginFrame(args); +} + +// https://crbug.com/730218: Avoid DCHECK crash in +// ExternalBeginFrameSource::GetMissedBeginFrameArgs. +TEST_F(ExternalBeginFrameSourceTest, GetMissedBeginFrameArgs) { + BeginFrameArgs args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, + 2, 10000, 10100, 100); + source_->OnBeginFrame(args); + + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 2, 10000, 10100, 100); + source_->AddObserver(obs_.get()); + source_->RemoveObserver(obs_.get()); + + // Out of order frame_time. This might not be valid but still shouldn't + // cause a DCHECK in ExternalBeginFrameSource code. + args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, 9999, 10100, + 101); + source_->OnBeginFrame(args); + + EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1); + EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); + EXPECT_CALL(*obs_, OnBeginFrame(_)).Times(0); + source_->AddObserver(obs_.get()); +} + +} // namespace +} // namespace viz diff --git a/chromium/components/viz/common/frame_sinks/delay_based_time_source.cc b/chromium/components/viz/common/frame_sinks/delay_based_time_source.cc new file mode 100644 index 00000000000..1a6cd56f4a2 --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/delay_based_time_source.cc @@ -0,0 +1,180 @@ +// 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. + +#include "components/viz/common/frame_sinks/delay_based_time_source.h" + +#include <algorithm> +#include <cmath> +#include <string> + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_argument.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" + +namespace viz { + +// The following methods correspond to the DelayBasedTimeSource that uses +// the base::TimeTicks::Now as the timebase. +DelayBasedTimeSource::DelayBasedTimeSource( + base::SingleThreadTaskRunner* task_runner) + : client_(nullptr), + active_(false), + timebase_(base::TimeTicks()), + interval_(BeginFrameArgs::DefaultInterval()), + last_tick_time_(base::TimeTicks() - interval_), + next_tick_time_(base::TimeTicks()), + task_runner_(task_runner), + weak_factory_(this) {} + +DelayBasedTimeSource::~DelayBasedTimeSource() = default; + +void DelayBasedTimeSource::SetActive(bool active) { + TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); + + if (active == active_) + return; + + active_ = active; + + if (active_) { + PostNextTickTask(Now()); + } else { + last_tick_time_ = base::TimeTicks(); + next_tick_time_ = base::TimeTicks(); + tick_closure_.Cancel(); + } +} + +base::TimeDelta DelayBasedTimeSource::Interval() const { + return interval_; +} + +bool DelayBasedTimeSource::Active() const { + return active_; +} + +base::TimeTicks DelayBasedTimeSource::LastTickTime() const { + return last_tick_time_; +} + +base::TimeTicks DelayBasedTimeSource::NextTickTime() const { + return next_tick_time_; +} + +void DelayBasedTimeSource::OnTimerTick() { + DCHECK(active_); + + last_tick_time_ = next_tick_time_; + + PostNextTickTask(Now()); + + // Fire the tick. + if (client_) + client_->OnTimerTick(); +} + +void DelayBasedTimeSource::SetClient(DelayBasedTimeSourceClient* client) { + client_ = client; +} + +void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, + base::TimeDelta interval) { + interval_ = interval; + timebase_ = timebase; +} + +base::TimeTicks DelayBasedTimeSource::Now() const { + return base::TimeTicks::Now(); +} + +// This code tries to achieve an average tick rate as close to interval_ as +// possible. To do this, it has to deal with a few basic issues: +// 1. PostDelayedTask can delay only at a millisecond granularity. So, 16.666 +// has to posted as 16 or 17. +// 2. A delayed task may come back a bit late (a few ms), or really late +// (frames later) +// +// The basic idea with this scheduler here is to keep track of where we *want* +// to run in tick_target_. We update this with the exact interval. +// +// Then, when we post our task, we take the floor of (tick_target_ and Now()). +// If we started at now=0, and 60FPs (all times in milliseconds): +// now=0 target=16.667 PostDelayedTask(16) +// +// When our callback runs, we figure out how far off we were from that goal. +// Because of the flooring operation, and assuming our timer runs exactly when +// it should, this yields: +// now=16 target=16.667 +// +// Since we can't post a 0.667 ms task to get to now=16, we just treat this as a +// tick. Then, we update target to be 33.333. We now post another task based on +// the difference between our target and now: +// now=16 tick_target=16.667 new_target=33.333 --> +// PostDelayedTask(floor(33.333 - 16)) --> PostDelayedTask(17) +// +// Over time, with no late tasks, this leads to us posting tasks like this: +// now=0 tick_target=0 new_target=16.667 --> +// tick(), PostDelayedTask(16) +// now=16 tick_target=16.667 new_target=33.333 --> +// tick(), PostDelayedTask(17) +// now=33 tick_target=33.333 new_target=50.000 --> +// tick(), PostDelayedTask(17) +// now=50 tick_target=50.000 new_target=66.667 --> +// tick(), PostDelayedTask(16) +// +// We treat delays in tasks differently depending on the amount of delay we +// encounter. Suppose we posted a task with a target=16.667: +// Case 1: late but not unrecoverably-so +// now=18 tick_target=16.667 +// +// Case 2: so late we obviously missed the tick +// now=25.0 tick_target=16.667 +// +// We treat the first case as a tick anyway, and assume the delay was unusual. +// Thus, we compute the new_target based on the old timebase: +// now=18 tick_target=16.667 new_target=33.333 --> +// tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) +// This brings us back to 18+15 = 33, which was where we would have been if the +// task hadn't been late. +// +// For the really late delay, we we move to the next logical tick. The timebase +// is not reset. +// now=37 tick_target=16.667 new_target=50.000 --> +// tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) +void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { + if (interval_.is_zero()) { + next_tick_time_ = now; + } else { + next_tick_time_ = now.SnappedToNextTick(timebase_, interval_); + if (next_tick_time_ == now) + next_tick_time_ += interval_; + DCHECK_GT(next_tick_time_, now); + } + tick_closure_.Reset(base::Bind(&DelayBasedTimeSource::OnTimerTick, + weak_factory_.GetWeakPtr())); + task_runner_->PostDelayedTask(FROM_HERE, tick_closure_.callback(), + next_tick_time_ - now); +} + +std::string DelayBasedTimeSource::TypeString() const { + return "DelayBasedTimeSource"; +} + +void DelayBasedTimeSource::AsValueInto( + base::trace_event::TracedValue* state) const { + state->SetString("type", TypeString()); + state->SetDouble("last_tick_time_us", + LastTickTime().since_origin().InMicroseconds()); + state->SetDouble("next_tick_time_us", + NextTickTime().since_origin().InMicroseconds()); + state->SetDouble("interval_us", interval_.InMicroseconds()); + state->SetDouble("timebase_us", timebase_.since_origin().InMicroseconds()); + state->SetBoolean("active", active_); +} + +} // namespace viz diff --git a/chromium/components/viz/common/frame_sinks/delay_based_time_source.h b/chromium/components/viz/common/frame_sinks/delay_based_time_source.h new file mode 100644 index 00000000000..9a9d0a3d069 --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/delay_based_time_source.h @@ -0,0 +1,91 @@ +// 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_FRAME_SINKS_DELAY_BASED_TIME_SOURCE_H_ +#define COMPONENTS_VIZ_COMMON_FRAME_SINKS_DELAY_BASED_TIME_SOURCE_H_ + +#include <memory> +#include <string> + +#include "base/cancelable_callback.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" +#include "base/values.h" +#include "components/viz/common/viz_common_export.h" + +namespace base { +namespace trace_event { +class TracedValue; +} +class SingleThreadTaskRunner; +} // namespace base + +namespace viz { +class VIZ_COMMON_EXPORT DelayBasedTimeSourceClient { + public: + virtual void OnTimerTick() = 0; + + protected: + virtual ~DelayBasedTimeSourceClient() {} +}; + +// This timer implements a time source that achieves the specified interval +// in face of millisecond-precision delayed callbacks and random queueing +// delays. DelayBasedTimeSource uses base::TimeTicks::Now as its timebase. +class VIZ_COMMON_EXPORT DelayBasedTimeSource { + public: + explicit DelayBasedTimeSource(base::SingleThreadTaskRunner* task_runner); + virtual ~DelayBasedTimeSource(); + + void SetClient(DelayBasedTimeSourceClient* client); + + void SetTimebaseAndInterval(base::TimeTicks timebase, + base::TimeDelta interval); + + base::TimeDelta Interval() const; + + void SetActive(bool active); + bool Active() const; + + // Get the last and next tick times. NextTickTime() returns null when + // inactive. + base::TimeTicks LastTickTime() const; + base::TimeTicks NextTickTime() const; + + virtual void AsValueInto(base::trace_event::TracedValue* dict) const; + + protected: + // Virtual for testing. + virtual base::TimeTicks Now() const; + virtual std::string TypeString() const; + + private: + void PostNextTickTask(base::TimeTicks now); + void ResetTickTask(base::TimeTicks now); + + void OnTimerTick(); + + DelayBasedTimeSourceClient* client_; + + bool active_; + + base::TimeTicks timebase_; + base::TimeDelta interval_; + + base::TimeTicks last_tick_time_; + base::TimeTicks next_tick_time_; + + base::CancelableClosure tick_closure_; + + base::SingleThreadTaskRunner* task_runner_; + + base::WeakPtrFactory<DelayBasedTimeSource> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(DelayBasedTimeSource); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_FRAME_SINKS_DELAY_BASED_TIME_SOURCE_H_ diff --git a/chromium/components/viz/common/frame_sinks/delay_based_time_source_unittest.cc b/chromium/components/viz/common/frame_sinks/delay_based_time_source_unittest.cc new file mode 100644 index 00000000000..be4437dcce4 --- /dev/null +++ b/chromium/components/viz/common/frame_sinks/delay_based_time_source_unittest.cc @@ -0,0 +1,351 @@ +// 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. + +#include "components/viz/common/frame_sinks/delay_based_time_source.h" + +#include <stdint.h> + +#include "base/test/test_simple_task_runner.h" +#include "components/viz/test/fake_delay_based_time_source.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace viz { +namespace { + +base::TimeDelta Interval() { + return base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond / + 60); +} + +class DelayBasedTimeSourceTest : public ::testing::Test { + protected: + void SetUp() override { + now_src_ = base::MakeUnique<base::SimpleTestTickClock>(); + task_runner_ = make_scoped_refptr(new base::TestSimpleTaskRunner); + delay_based_time_source_ = base::MakeUnique<FakeDelayBasedTimeSource>( + now_src_.get(), task_runner_.get()); + delay_based_time_source_->SetClient(&client_); + } + + void TearDown() override { + delay_based_time_source_.reset(); + task_runner_ = nullptr; + now_src_.reset(); + } + + void SetNow(base::TimeTicks ticks) { now_src_->SetNowTicks(ticks); } + + base::TestSimpleTaskRunner* task_runner() { return task_runner_.get(); } + + FakeDelayBasedTimeSource* timer() { return delay_based_time_source_.get(); } + + FakeDelayBasedTimeSourceClient* client() { return &client_; } + + std::unique_ptr<base::SimpleTestTickClock> now_src_; + FakeDelayBasedTimeSourceClient client_; + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + std::unique_ptr<FakeDelayBasedTimeSource> delay_based_time_source_; +}; + +TEST_F(DelayBasedTimeSourceTest, TaskPostedAndTickCalled) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + EXPECT_TRUE(timer()->Active()); + EXPECT_TRUE(task_runner()->HasPendingTask()); + + SetNow(timer()->Now() + base::TimeDelta::FromMilliseconds(16)); + task_runner()->RunPendingTasks(); + EXPECT_TRUE(timer()->Active()); + EXPECT_TRUE(client()->TickCalled()); +} + +TEST_F(DelayBasedTimeSourceTest, TickNotCalledWithTaskPosted) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + EXPECT_TRUE(task_runner()->HasPendingTask()); + timer()->SetActive(false); + task_runner()->RunPendingTasks(); + EXPECT_FALSE(client()->TickCalled()); +} + +TEST_F(DelayBasedTimeSourceTest, StartTwiceEnqueuesOneTask) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + EXPECT_TRUE(task_runner()->HasPendingTask()); + task_runner()->ClearPendingTasks(); + timer()->SetActive(true); + EXPECT_FALSE(task_runner()->HasPendingTask()); +} + +TEST_F(DelayBasedTimeSourceTest, StartWhenRunningDoesntTick) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + EXPECT_TRUE(task_runner()->HasPendingTask()); + task_runner()->RunPendingTasks(); + task_runner()->ClearPendingTasks(); + timer()->SetActive(true); + EXPECT_FALSE(task_runner()->HasPendingTask()); +} + +// At 60Hz, when the tick returns at exactly the requested next time, make sure +// a 16ms next delay is posted. +TEST_F(DelayBasedTimeSourceTest, NextDelaySaneWhenExactlyOnRequestedTime) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + // Run the first tick. + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + SetNow(timer()->Now() + Interval()); + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); +} + +// At 60Hz, when the tick returns at slightly after the requested next time, +// make sure a 16ms next delay is posted. +TEST_F(DelayBasedTimeSourceTest, NextDelaySaneWhenSlightlyAfterRequestedTime) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + // Run the first tick. + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + SetNow(timer()->Now() + Interval() + base::TimeDelta::FromMicroseconds(1)); + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); +} + +// At 60Hz, when the tick returns at exactly 2*interval after the requested next +// time, make sure we don't tick unnecessarily. +TEST_F(DelayBasedTimeSourceTest, + NextDelaySaneWhenExactlyTwiceAfterRequestedTime) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + // Run the first tick. + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + SetNow(timer()->Now() + 2 * Interval()); + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); +} + +// At 60Hz, when the tick returns at 2*interval and a bit after the requested +// next time, make sure a 16ms next delay is posted. +TEST_F(DelayBasedTimeSourceTest, + NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + // Run the first tick. + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + SetNow(timer()->Now() + 2 * Interval() + + base::TimeDelta::FromMicroseconds(1)); + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); +} + +// At 60Hz, when the tick returns halfway to the next frame time, make sure +// a correct next delay value is posted. +TEST_F(DelayBasedTimeSourceTest, NextDelaySaneWhenHalfAfterRequestedTime) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + // Run the first tick. + task_runner()->RunPendingTasks(); + + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + SetNow(timer()->Now() + Interval() + base::TimeDelta::FromMilliseconds(8)); + task_runner()->RunPendingTasks(); + + EXPECT_EQ(8, task_runner()->NextPendingTaskDelay().InMilliseconds()); +} + +TEST_F(DelayBasedTimeSourceTest, JitteryRuntimeWithFutureTimebases) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + + // Run the first tick. + task_runner()->RunPendingTasks(); + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + base::TimeTicks future_timebase = timer()->Now() + Interval() * 10; + + // 1ms jitter + base::TimeDelta jitter1 = base::TimeDelta::FromMilliseconds(1); + + // Tick with +1ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() + jitter1); + task_runner()->RunPendingTasks(); + EXPECT_EQ(15, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() - jitter1); + task_runner()->RunPendingTasks(); + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with -1ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() - jitter1); + task_runner()->RunPendingTasks(); + EXPECT_EQ(1, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() + jitter1); + task_runner()->RunPendingTasks(); + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // 8 ms jitter + base::TimeDelta jitter8 = base::TimeDelta::FromMilliseconds(8); + + // Tick with +8ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() + jitter8); + task_runner()->RunPendingTasks(); + EXPECT_EQ(8, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() - jitter8); + task_runner()->RunPendingTasks(); + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with -8ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() - jitter8); + task_runner()->RunPendingTasks(); + EXPECT_EQ(8, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() + jitter8); + task_runner()->RunPendingTasks(); + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // 15 ms jitter + base::TimeDelta jitter15 = base::TimeDelta::FromMilliseconds(15); + + // Tick with +15ms jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() + jitter15); + task_runner()->RunPendingTasks(); + EXPECT_EQ(1, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() - jitter15); + task_runner()->RunPendingTasks(); + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with -15ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() - jitter15); + task_runner()->RunPendingTasks(); + EXPECT_EQ(15, task_runner()->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer()->SetTimebaseAndInterval(future_timebase, Interval()); + SetNow(timer()->Now() + Interval() + jitter15); + task_runner()->RunPendingTasks(); + EXPECT_EQ(16, task_runner()->NextPendingTaskDelay().InMilliseconds()); +} + +TEST_F(DelayBasedTimeSourceTest, AchievesTargetRateWithNoNoise) { + int num_iterations = 10; + + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); + + double total_frame_time = 0.0; + for (int i = 0; i < num_iterations; ++i) { + int64_t delay_ms = task_runner()->NextPendingTaskDelay().InMilliseconds(); + + // accumulate the "delay" + total_frame_time += delay_ms / 1000.0; + + // Run the callback exactly when asked + SetNow(timer()->Now() + base::TimeDelta::FromMilliseconds(delay_ms)); + task_runner()->RunPendingTasks(); + } + double average_interval = + total_frame_time / static_cast<double>(num_iterations); + EXPECT_NEAR(1.0 / 60.0, average_interval, 0.1); +} + +TEST_F(DelayBasedTimeSourceTest, TestDeactivateWhilePending) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + timer()->SetActive(true); // Should post a task. + timer()->SetActive(false); + // Should run the posted task without crashing. + EXPECT_TRUE(task_runner()->HasPendingTask()); + task_runner()->RunPendingTasks(); +} + +TEST_F(DelayBasedTimeSourceTest, + TestDeactivateAndReactivateBeforeNextTickTime) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + + // Should run the activate task, and pick up a new timebase. + timer()->SetActive(true); + task_runner()->RunPendingTasks(); + + // Stop the timer() + timer()->SetActive(false); + + // Task will be pending anyway, run it + task_runner()->RunPendingTasks(); + + // Start the timer() again, but before the next tick time the timer() + // previously planned on using. That same tick time should still be targeted. + SetNow(timer()->Now() + base::TimeDelta::FromMilliseconds(4)); + timer()->SetActive(true); + EXPECT_EQ(12, task_runner()->NextPendingTaskDelay().InMilliseconds()); +} + +TEST_F(DelayBasedTimeSourceTest, TestDeactivateAndReactivateAfterNextTickTime) { + timer()->SetTimebaseAndInterval(base::TimeTicks(), Interval()); + + // Should run the activate task, and pick up a new timebase. + timer()->SetActive(true); + task_runner()->RunPendingTasks(); + + // Stop the timer(). + timer()->SetActive(false); + + // Task will be pending anyway, run it. + task_runner()->RunPendingTasks(); + + // Start the timer() again, but before the next tick time the timer() + // previously planned on using. That same tick time should still be targeted. + SetNow(timer()->Now() + base::TimeDelta::FromMilliseconds(20)); + timer()->SetActive(true); + EXPECT_EQ(13, task_runner()->NextPendingTaskDelay().InMilliseconds()); +} + +} // namespace +} // namespace viz diff --git a/chromium/components/viz/common/gl_helper_benchmark.cc b/chromium/components/viz/common/gl_helper_benchmark.cc index 59c3a73e537..7d9a76b811c 100644 --- a/chromium/components/viz/common/gl_helper_benchmark.cc +++ b/chromium/components/viz/common/gl_helper_benchmark.cc @@ -120,7 +120,7 @@ class GLHelperBenchmark : public testing::Test { gpu::gles2::GLES2Interface* gl_; std::unique_ptr<GLHelper> helper_; std::unique_ptr<GLHelperScaling> helper_scaling_; - std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; + base::circular_deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; }; TEST_F(GLHelperBenchmark, ScaleBenchmark) { diff --git a/chromium/components/viz/common/gl_helper_scaling.cc b/chromium/components/viz/common/gl_helper_scaling.cc index e394202df35..4fa88db8b92 100644 --- a/chromium/components/viz/common/gl_helper_scaling.cc +++ b/chromium/components/viz/common/gl_helper_scaling.cc @@ -6,11 +6,11 @@ #include <stddef.h> -#include <deque> #include <string> #include <vector> #include "base/bind.h" +#include "base/containers/circular_deque.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" @@ -271,12 +271,12 @@ void GLHelperScaling::ConvertScalerOpsToScalerStages( const gfx::Size& dst_size, bool vertically_flip_texture, bool swizzle, - std::deque<GLHelperScaling::ScaleOp>* x_ops, - std::deque<GLHelperScaling::ScaleOp>* y_ops, + base::circular_deque<GLHelperScaling::ScaleOp>* x_ops, + base::circular_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; + base::circular_deque<ScaleOp>* current_queue = NULL; if (!y_ops->empty()) { current_queue = y_ops; @@ -397,7 +397,7 @@ void GLHelperScaling::ComputeScalerStages( return; } - std::deque<GLHelperScaling::ScaleOp> x_ops, y_ops; + base::circular_deque<GLHelperScaling::ScaleOp> x_ops, y_ops; GLHelperScaling::ScaleOp::AddOps(src_subrect.width(), dst_size.width(), true, quality == GLHelper::SCALER_QUALITY_GOOD, &x_ops); diff --git a/chromium/components/viz/common/gl_helper_scaling.h b/chromium/components/viz/common/gl_helper_scaling.h index a37ea919ee5..1d21916942b 100644 --- a/chromium/components/viz/common/gl_helper_scaling.h +++ b/chromium/components/viz/common/gl_helper_scaling.h @@ -5,10 +5,10 @@ #ifndef COMPONENTS_VIZ_COMMON_GL_HELPER_SCALING_H_ #define COMPONENTS_VIZ_COMMON_GL_HELPER_SCALING_H_ -#include <deque> #include <map> #include <vector> +#include "base/containers/circular_deque.h" #include "base/macros.h" #include "components/viz/common/gl_helper.h" #include "components/viz/common/viz_common_export.h" @@ -97,7 +97,7 @@ class VIZ_COMMON_EXPORT GLHelperScaling { int dst, bool scale_x, bool allow3, - std::deque<ScaleOp>* ops) { + base::circular_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 @@ -177,8 +177,8 @@ class VIZ_COMMON_EXPORT GLHelperScaling { const gfx::Size& dst_size, bool vertically_flip_texture, bool swizzle, - std::deque<GLHelperScaling::ScaleOp>* x_ops, - std::deque<GLHelperScaling::ScaleOp>* y_ops, + base::circular_deque<GLHelperScaling::ScaleOp>* x_ops, + base::circular_deque<GLHelperScaling::ScaleOp>* y_ops, std::vector<ScalerStage>* scaler_stages); scoped_refptr<ShaderProgram> GetShaderProgram(ShaderType type, bool swizzle); diff --git a/chromium/components/viz/common/gl_helper_unittest.cc b/chromium/components/viz/common/gl_helper_unittest.cc index 34192b4837c..49a42f581b8 100644 --- a/chromium/components/viz/common/gl_helper_unittest.cc +++ b/chromium/components/viz/common/gl_helper_unittest.cc @@ -960,7 +960,7 @@ class GLHelperTest : public testing::Test { 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"; + LOG(ERROR) << "Bitmap color comparison failure"; return false; } } @@ -1038,7 +1038,7 @@ class GLHelperTest : public testing::Test { 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"; + LOG(ERROR) << "Bitmap comparison failure Pattern-1"; return false; } const int rect_w = 10, rect_h = 4, src_grid_pitch = 10, src_grid_width = 4; @@ -1051,7 +1051,7 @@ class GLHelperTest : public testing::Test { ReadBackTexture(src_texture, src_size, pixels, color_type, async); result = IsEqual(input_pixels, output_pixels); if (!result) { - LOG(ERROR) << "Bitmap comparision failure Pattern-2"; + LOG(ERROR) << "Bitmap comparison failure Pattern-2"; return false; } // Test Pattern-3, Fill with CheckerBoard Pattern. @@ -1062,7 +1062,7 @@ class GLHelperTest : public testing::Test { ReadBackTexture(src_texture, src_size, pixels, color_type, async); result = IsEqual(input_pixels, output_pixels); if (!result) { - LOG(ERROR) << "Bitmap comparision failure Pattern-3"; + LOG(ERROR) << "Bitmap comparison failure Pattern-3"; return false; } gl_->DeleteTextures(1, &src_texture); @@ -1073,7 +1073,7 @@ class GLHelperTest : public testing::Test { } void TestAddOps(int src, int dst, bool scale_x, bool allow3) { - std::deque<GLHelperScaling::ScaleOp> ops; + base::circular_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. @@ -1249,7 +1249,7 @@ class GLHelperTest : public testing::Test { gpu::gles2::GLES2Interface* gl_; std::unique_ptr<GLHelper> helper_; std::unique_ptr<GLHelperScaling> helper_scaling_; - std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; + base::circular_deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_; }; class GLHelperPixelTest : public GLHelperTest { diff --git a/chromium/components/viz/common/gpu/DEPS b/chromium/components/viz/common/gpu/DEPS index 945aba3fc2b..d038e11da6e 100644 --- a/chromium/components/viz/common/gpu/DEPS +++ b/chromium/components/viz/common/gpu/DEPS @@ -5,7 +5,7 @@ include_rules = [ "+gpu/GLES2/gl2extchromium.h", "+gpu/ipc", "+gpu/skia_bindings", - "+ui/gfx", + "+gpu/vulkan", "+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/in_process_context_provider.h b/chromium/components/viz/common/gpu/in_process_context_provider.h index 493191fc389..f1eea6c030f 100644 --- a/chromium/components/viz/common/gpu/in_process_context_provider.h +++ b/chromium/components/viz/common/gpu/in_process_context_provider.h @@ -33,8 +33,7 @@ class GrContextForGLES2Interface; namespace viz { -class VIZ_COMMON_EXPORT InProcessContextProvider - : public NON_EXPORTED_BASE(ContextProvider) { +class VIZ_COMMON_EXPORT InProcessContextProvider : public ContextProvider { public: InProcessContextProvider( scoped_refptr<gpu::InProcessCommandBuffer::Service> service, diff --git a/chromium/components/viz/common/gpu/vulkan_context_provider.h b/chromium/components/viz/common/gpu/vulkan_context_provider.h new file mode 100644 index 00000000000..0dc77b003fa --- /dev/null +++ b/chromium/components/viz/common/gpu/vulkan_context_provider.h @@ -0,0 +1,30 @@ +// 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_VULKAN_CONTEXT_PROVIDER_H_ +#define COMPONENTS_VIZ_COMMON_GPU_VULKAN_CONTEXT_PROVIDER_H_ + +#include "base/memory/ref_counted.h" +#include "components/viz/common/viz_common_export.h" + +namespace gpu { +class VulkanDeviceQueue; +} + +namespace viz { + +// The VulkanContextProvider groups sharing of vulkan objects synchronously. +class VIZ_COMMON_EXPORT VulkanContextProvider + : public base::RefCountedThreadSafe<VulkanContextProvider> { + public: + virtual gpu::VulkanDeviceQueue* GetDeviceQueue() = 0; + + protected: + friend class base::RefCountedThreadSafe<VulkanContextProvider>; + virtual ~VulkanContextProvider() {} +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_GPU_VULKAN_CONTEXT_PROVIDER_H_ diff --git a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc new file mode 100644 index 00000000000..d1714290d3d --- /dev/null +++ b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc @@ -0,0 +1,73 @@ +// 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/vulkan_in_process_context_provider.h" +#include "gpu/vulkan/features.h" + +#if BUILDFLAG(ENABLE_VULKAN) +#include "gpu/vulkan/vulkan_device_queue.h" +#include "gpu/vulkan/vulkan_implementation.h" +#endif // BUILDFLAG(ENABLE_VULKAN) + +namespace viz { + +scoped_refptr<VulkanInProcessContextProvider> +VulkanInProcessContextProvider::Create() { +#if BUILDFLAG(ENABLE_VULKAN) + if (!gpu::VulkanSupported()) + return nullptr; + + scoped_refptr<VulkanInProcessContextProvider> context_provider( + new VulkanInProcessContextProvider); + if (!context_provider->Initialize()) + return nullptr; + return context_provider; +#else + return nullptr; +#endif +} + +bool VulkanInProcessContextProvider::Initialize() { +#if BUILDFLAG(ENABLE_VULKAN) + DCHECK(!device_queue_); + std::unique_ptr<gpu::VulkanDeviceQueue> device_queue( + new gpu::VulkanDeviceQueue); + if (!device_queue->Initialize( + gpu::VulkanDeviceQueue::GRAPHICS_QUEUE_FLAG | + gpu::VulkanDeviceQueue::PRESENTATION_SUPPORT_QUEUE_FLAG)) { + device_queue->Destroy(); + return false; + } + + device_queue_ = std::move(device_queue); + return true; +#else + return false; +#endif +} + +void VulkanInProcessContextProvider::Destroy() { +#if BUILDFLAG(ENABLE_VULKAN) + if (device_queue_) { + device_queue_->Destroy(); + device_queue_.reset(); + } +#endif +} + +gpu::VulkanDeviceQueue* VulkanInProcessContextProvider::GetDeviceQueue() { +#if BUILDFLAG(ENABLE_VULKAN) + return device_queue_.get(); +#else + return nullptr; +#endif +} + +VulkanInProcessContextProvider::VulkanInProcessContextProvider() {} + +VulkanInProcessContextProvider::~VulkanInProcessContextProvider() { + Destroy(); +} + +} // namespace viz diff --git a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h new file mode 100644 index 00000000000..9a94fccaf99 --- /dev/null +++ b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h @@ -0,0 +1,45 @@ +// 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_VULKAN_IN_PROCESS_CONTEXT_PROVIDER_H_ +#define COMPONENTS_VIZ_COMMON_GPU_VULKAN_IN_PROCESS_CONTEXT_PROVIDER_H_ + +#include <memory> + +#include "components/viz/common/gpu/vulkan_context_provider.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/vulkan/features.h" + +namespace gpu { +class VulkanDeviceQueue; +} + +namespace viz { + +class VIZ_COMMON_EXPORT VulkanInProcessContextProvider + : public VulkanContextProvider { + public: + static scoped_refptr<VulkanInProcessContextProvider> Create(); + + bool Initialize(); + void Destroy(); + + // VulkanContextProvider implementation + gpu::VulkanDeviceQueue* GetDeviceQueue() override; + + protected: + VulkanInProcessContextProvider(); + ~VulkanInProcessContextProvider() override; + + private: +#if BUILDFLAG(ENABLE_VULKAN) + std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_; +#endif + + DISALLOW_COPY_AND_ASSIGN(VulkanInProcessContextProvider); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_GPU_VULKAN_IN_PROCESS_CONTEXT_PROVIDER_H_ diff --git a/chromium/components/viz/common/hit_test/DEPS b/chromium/components/viz/common/hit_test/DEPS deleted file mode 100644 index b49b8636b9b..00000000000 --- a/chromium/components/viz/common/hit_test/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -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 index b90bfb19a6e..2f93daae037 100644 --- a/chromium/components/viz/common/hit_test/aggregated_hit_test_region.h +++ b/chromium/components/viz/common/hit_test/aggregated_hit_test_region.h @@ -15,7 +15,7 @@ namespace viz { // A AggregatedHitTestRegion element with child_count of kEndOfList indicates // the last element and end of the list. -constexpr int kEndOfList = -1; +constexpr int32_t kEndOfList = -1; // An array of AggregatedHitTestRegion elements is used to define the // aggregated hit-test data for the Display. @@ -24,6 +24,17 @@ constexpr int kEndOfList = -1; // write the hit_test data, and the viz host can read without // process hops. struct AggregatedHitTestRegion { + AggregatedHitTestRegion(FrameSinkId frame_sink_id, + uint32_t flags, + gfx::Rect rect, + gfx::Transform transform, + int32_t child_count) + : frame_sink_id(frame_sink_id), + flags(flags), + rect(rect), + transform(transform), + child_count(child_count) {} + // The FrameSinkId corresponding to this region. Events that match // are routed to this surface. FrameSinkId frame_sink_id; @@ -41,7 +52,7 @@ struct AggregatedHitTestRegion { // 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; + int32_t child_count; }; } // namespace viz diff --git a/chromium/components/viz/common/quads/DEPS b/chromium/components/viz/common/quads/DEPS index 766ac6bb3c0..abf379f6838 100644 --- a/chromium/components/viz/common/quads/DEPS +++ b/chromium/components/viz/common/quads/DEPS @@ -1,5 +1,9 @@ include_rules = [ + # TODO(staraz): cc/base was added because SharedQuadState includes + # cc::MathUtil. Remove it once cc/base/math_util* are moved to viz. + "+cc/base", + "+gpu/command_buffer/common", "+mojo/public/cpp/bindings", - "+ui/gfx", + "+third_party/skia", ] diff --git a/chromium/components/viz/common/quads/copy_output_request.cc b/chromium/components/viz/common/quads/copy_output_request.cc new file mode 100644 index 00000000000..de505d68c32 --- /dev/null +++ b/chromium/components/viz/common/quads/copy_output_request.cc @@ -0,0 +1,68 @@ +// 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/copy_output_request.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/trace_event/trace_event.h" +#include "components/viz/common/quads/copy_output_result.h" +#include "components/viz/common/quads/texture_mailbox.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace viz { + +CopyOutputRequest::CopyOutputRequest() : force_bitmap_result_(false) {} + +CopyOutputRequest::CopyOutputRequest(bool force_bitmap_result, + CopyOutputRequestCallback result_callback) + : force_bitmap_result_(force_bitmap_result), + result_callback_(std::move(result_callback)) { + DCHECK(!result_callback_.is_null()); + TRACE_EVENT_ASYNC_BEGIN0("viz", "CopyOutputRequest", this); +} + +CopyOutputRequest::~CopyOutputRequest() { + if (!result_callback_.is_null()) + SendResult(CopyOutputResult::CreateEmptyResult()); +} + +void CopyOutputRequest::SendResult(std::unique_ptr<CopyOutputResult> result) { + TRACE_EVENT_ASYNC_END1("viz", "CopyOutputRequest", this, "success", + !result->IsEmpty()); + if (result_task_runner_) { + result_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(std::move(result_callback_), std::move(result))); + result_task_runner_ = nullptr; + } else { + std::move(result_callback_).Run(std::move(result)); + } +} + +void CopyOutputRequest::SendEmptyResult() { + SendResult(CopyOutputResult::CreateEmptyResult()); +} + +void CopyOutputRequest::SendBitmapResult(std::unique_ptr<SkBitmap> bitmap) { + SendResult(CopyOutputResult::CreateBitmapResult(std::move(bitmap))); +} + +void CopyOutputRequest::SendTextureResult( + const gfx::Size& size, + const TextureMailbox& texture_mailbox, + std::unique_ptr<SingleReleaseCallback> release_callback) { + DCHECK(texture_mailbox.IsTexture()); + SendResult(CopyOutputResult::CreateTextureResult( + size, texture_mailbox, std::move(release_callback))); +} + +void CopyOutputRequest::SetTextureMailbox( + const TextureMailbox& texture_mailbox) { + DCHECK(!force_bitmap_result_); + DCHECK(texture_mailbox.IsTexture()); + texture_mailbox_ = texture_mailbox; +} + +} // namespace viz diff --git a/chromium/components/viz/common/quads/copy_output_request.h b/chromium/components/viz/common/quads/copy_output_request.h new file mode 100644 index 00000000000..ccb42adfdb2 --- /dev/null +++ b/chromium/components/viz/common/quads/copy_output_request.h @@ -0,0 +1,114 @@ +// 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_COPY_OUTPUT_REQUEST_H_ +#define COMPONENTS_VIZ_COMMON_QUADS_COPY_OUTPUT_REQUEST_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/memory/ptr_util.h" +#include "base/optional.h" +#include "base/task_runner.h" +#include "base/unguessable_token.h" +#include "components/viz/common/quads/single_release_callback.h" +#include "components/viz/common/quads/texture_mailbox.h" +#include "components/viz/common/viz_common_export.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "ui/gfx/geometry/rect.h" + +class SkBitmap; + +namespace viz { + +namespace mojom { +class CopyOutputRequestDataView; +} + +class CopyOutputResult; + +class VIZ_COMMON_EXPORT CopyOutputRequest { + public: + using CopyOutputRequestCallback = + base::OnceCallback<void(std::unique_ptr<CopyOutputResult> result)>; + + static std::unique_ptr<CopyOutputRequest> CreateEmptyRequest() { + return base::WrapUnique(new CopyOutputRequest); + } + static std::unique_ptr<CopyOutputRequest> CreateRequest( + CopyOutputRequestCallback result_callback) { + return base::WrapUnique( + new CopyOutputRequest(false, std::move(result_callback))); + } + static std::unique_ptr<CopyOutputRequest> CreateBitmapRequest( + CopyOutputRequestCallback result_callback) { + return base::WrapUnique( + new CopyOutputRequest(true, std::move(result_callback))); + } + + ~CopyOutputRequest(); + + bool IsEmpty() const { return result_callback_.is_null(); } + + // Requests that the result callback be run as a task posted to the given + // |task_runner|. If this is not set, the result callback could be run from + // any context. + void set_result_task_runner(scoped_refptr<base::TaskRunner> task_runner) { + result_task_runner_ = std::move(task_runner); + } + bool has_result_task_runner() const { return !!result_task_runner_; } + + // Optionally specify the source of this copy request. If set when this copy + // request is submitted to a layer, a prior uncommitted copy request from the + // same source will be aborted. + void set_source(const base::UnguessableToken& source) { source_ = source; } + bool has_source() const { return source_.has_value(); } + const base::UnguessableToken& source() const { return *source_; } + + bool force_bitmap_result() const { return force_bitmap_result_; } + + // By default copy requests copy the entire layer's subtree output. If an + // area is given, then the intersection of this rect (in layer space) with + // the layer's subtree output will be returned. + void set_area(const gfx::Rect& area) { area_ = area; } + bool has_area() const { return area_.has_value(); } + const gfx::Rect& area() const { return *area_; } + + // By default copy requests create a new TextureMailbox to return contents + // in. This allows a client to provide a TextureMailbox, and the compositor + // will place the result inside the TextureMailbox. + void SetTextureMailbox(const TextureMailbox& texture_mailbox); + bool has_texture_mailbox() const { return texture_mailbox_.has_value(); } + const TextureMailbox& texture_mailbox() const { return *texture_mailbox_; } + + void SendEmptyResult(); + void SendBitmapResult(std::unique_ptr<SkBitmap> bitmap); + void SendTextureResult( + const gfx::Size& size, + const TextureMailbox& texture_mailbox, + std::unique_ptr<SingleReleaseCallback> release_callback); + + void SendResult(std::unique_ptr<CopyOutputResult> result); + + private: + friend struct mojo::StructTraits<mojom::CopyOutputRequestDataView, + std::unique_ptr<CopyOutputRequest>>; + + CopyOutputRequest(); + CopyOutputRequest(bool force_bitmap_result, + CopyOutputRequestCallback result_callback); + + scoped_refptr<base::TaskRunner> result_task_runner_; + base::Optional<base::UnguessableToken> source_; + bool force_bitmap_result_; + base::Optional<gfx::Rect> area_; + base::Optional<TextureMailbox> texture_mailbox_; + CopyOutputRequestCallback result_callback_; + + DISALLOW_COPY_AND_ASSIGN(CopyOutputRequest); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_QUADS_COPY_OUTPUT_REQUEST_H_ diff --git a/chromium/components/viz/common/quads/copy_output_result.cc b/chromium/components/viz/common/quads/copy_output_result.cc new file mode 100644 index 00000000000..62f723c8223 --- /dev/null +++ b/chromium/components/viz/common/quads/copy_output_result.cc @@ -0,0 +1,47 @@ +// 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/copy_output_result.h" + +#include "base/logging.h" +#include "components/viz/common/quads/texture_mailbox.h" + +namespace viz { + +CopyOutputResult::CopyOutputResult() {} + +CopyOutputResult::CopyOutputResult(std::unique_ptr<SkBitmap> bitmap) + : size_(bitmap->width(), bitmap->height()), bitmap_(std::move(bitmap)) { + DCHECK(bitmap_); +} + +CopyOutputResult::CopyOutputResult( + const gfx::Size& size, + const TextureMailbox& texture_mailbox, + std::unique_ptr<SingleReleaseCallback> release_callback) + : size_(size), + texture_mailbox_(texture_mailbox), + release_callback_(std::move(release_callback)) { + DCHECK(texture_mailbox_.IsTexture()); +} + +CopyOutputResult::~CopyOutputResult() { + if (release_callback_) + release_callback_->Run(gpu::SyncToken(), false); +} + +std::unique_ptr<SkBitmap> CopyOutputResult::TakeBitmap() { + return std::move(bitmap_); +} + +void CopyOutputResult::TakeTexture( + TextureMailbox* texture_mailbox, + std::unique_ptr<SingleReleaseCallback>* release_callback) { + *texture_mailbox = texture_mailbox_; + *release_callback = std::move(release_callback_); + + texture_mailbox_ = TextureMailbox(); +} + +} // namespace viz diff --git a/chromium/components/viz/common/quads/copy_output_result.h b/chromium/components/viz/common/quads/copy_output_result.h new file mode 100644 index 00000000000..901ea5c9628 --- /dev/null +++ b/chromium/components/viz/common/quads/copy_output_result.h @@ -0,0 +1,79 @@ +// 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_COPY_OUTPUT_RESULT_H_ +#define COMPONENTS_VIZ_COMMON_QUADS_COPY_OUTPUT_RESULT_H_ + +#include <memory> + +#include "base/memory/ptr_util.h" +#include "components/viz/common/quads/single_release_callback.h" +#include "components/viz/common/quads/texture_mailbox.h" +#include "components/viz/common/viz_common_export.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/geometry/size.h" + +class SkBitmap; + +namespace cc { +namespace mojom { +class CopyOutputResultDataView; +} +} // namespace cc + +namespace viz { + +class TextureMailbox; + +class VIZ_COMMON_EXPORT CopyOutputResult { + public: + static std::unique_ptr<CopyOutputResult> CreateEmptyResult() { + return base::WrapUnique(new CopyOutputResult); + } + static std::unique_ptr<CopyOutputResult> CreateBitmapResult( + std::unique_ptr<SkBitmap> bitmap) { + return base::WrapUnique(new CopyOutputResult(std::move(bitmap))); + } + static std::unique_ptr<CopyOutputResult> CreateTextureResult( + const gfx::Size& size, + const TextureMailbox& texture_mailbox, + std::unique_ptr<SingleReleaseCallback> release_callback) { + return base::WrapUnique(new CopyOutputResult(size, texture_mailbox, + std::move(release_callback))); + } + + ~CopyOutputResult(); + + bool IsEmpty() const { return !HasBitmap() && !HasTexture(); } + bool HasBitmap() const { return !!bitmap_ && !bitmap_->isNull(); } + bool HasTexture() const { return texture_mailbox_.IsValid(); } + + gfx::Size size() const { return size_; } + std::unique_ptr<SkBitmap> TakeBitmap(); + void TakeTexture(TextureMailbox* texture_mailbox, + std::unique_ptr<SingleReleaseCallback>* release_callback); + + private: + friend struct mojo::StructTraits<cc::mojom::CopyOutputResultDataView, + std::unique_ptr<CopyOutputResult>>; + + CopyOutputResult(); + explicit CopyOutputResult(std::unique_ptr<SkBitmap> bitmap); + explicit CopyOutputResult( + const gfx::Size& size, + const TextureMailbox& texture_mailbox, + std::unique_ptr<SingleReleaseCallback> release_callback); + + gfx::Size size_; + std::unique_ptr<SkBitmap> bitmap_; + TextureMailbox texture_mailbox_; + std::unique_ptr<SingleReleaseCallback> release_callback_; + + DISALLOW_COPY_AND_ASSIGN(CopyOutputResult); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_QUADS_COPY_OUTPUT_RESULT_H_ diff --git a/chromium/components/viz/common/quads/release_callback.h b/chromium/components/viz/common/quads/release_callback.h new file mode 100644 index 00000000000..8166416c16c --- /dev/null +++ b/chromium/components/viz/common/quads/release_callback.h @@ -0,0 +1,21 @@ +// 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_RELEASE_CALLBACK_H_ +#define COMPONENTS_VIZ_COMMON_QUADS_RELEASE_CALLBACK_H_ + +#include "base/callback.h" + +namespace gpu { +struct SyncToken; +} // namespace gpu + +namespace viz { + +typedef base::Callback<void(const gpu::SyncToken& sync_token, bool is_lost)> + ReleaseCallback; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_QUADS_RELEASE_CALLBACK_H_ diff --git a/chromium/components/viz/common/quads/resource_format.h b/chromium/components/viz/common/quads/resource_format.h deleted file mode 100644 index 4ae7b0675fb..00000000000 --- a/chromium/components/viz/common/quads/resource_format.h +++ /dev/null @@ -1,35 +0,0 @@ -// 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_quad_state.cc b/chromium/components/viz/common/quads/shared_quad_state.cc new file mode 100644 index 00000000000..71eed58d78a --- /dev/null +++ b/chromium/components/viz/common/quads/shared_quad_state.cc @@ -0,0 +1,65 @@ +// Copyright 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/quads/shared_quad_state.h" + +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_argument.h" +#include "base/values.h" +#include "cc/base/math_util.h" +#include "components/viz/common/traced_value.h" +#include "third_party/skia/include/core/SkBlendMode.h" + +namespace viz { + +SharedQuadState::SharedQuadState() + : is_clipped(false), + opacity(0.f), + blend_mode(SkBlendMode::kSrcOver), + sorting_context_id(0) {} + +SharedQuadState::SharedQuadState(const SharedQuadState& other) = default; + +SharedQuadState::~SharedQuadState() { + TRACE_EVENT_OBJECT_DELETED_WITH_ID( + TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), "viz::SharedQuadState", + this); +} + +void SharedQuadState::SetAll(const gfx::Transform& quad_to_target_transform, + const gfx::Rect& quad_layer_rect, + const gfx::Rect& visible_quad_layer_rect, + const gfx::Rect& clip_rect, + bool is_clipped, + float opacity, + SkBlendMode blend_mode, + int sorting_context_id) { + this->quad_to_target_transform = quad_to_target_transform; + this->quad_layer_rect = quad_layer_rect; + this->visible_quad_layer_rect = visible_quad_layer_rect; + this->clip_rect = clip_rect; + this->is_clipped = is_clipped; + this->opacity = opacity; + this->blend_mode = blend_mode; + this->sorting_context_id = sorting_context_id; +} + +void SharedQuadState::AsValueInto(base::trace_event::TracedValue* value) const { + cc::MathUtil::AddToTracedValue("transform", quad_to_target_transform, value); + cc::MathUtil::AddToTracedValue("layer_content_rect", quad_layer_rect, value); + cc::MathUtil::AddToTracedValue("layer_visible_content_rect", + visible_quad_layer_rect, value); + + value->SetBoolean("is_clipped", is_clipped); + + cc::MathUtil::AddToTracedValue("clip_rect", clip_rect, value); + + value->SetDouble("opacity", opacity); + value->SetString("blend_mode", SkBlendMode_Name(blend_mode)); + TracedValue::MakeDictIntoImplicitSnapshotWithCategory( + TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), value, + "viz::SharedQuadState", this); +} + +} // namespace viz diff --git a/chromium/components/viz/common/quads/shared_quad_state.h b/chromium/components/viz/common/quads/shared_quad_state.h new file mode 100644 index 00000000000..d1b0a974745 --- /dev/null +++ b/chromium/components/viz/common/quads/shared_quad_state.h @@ -0,0 +1,61 @@ +// Copyright 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_QUADS_SHARED_QUAD_STATE_H_ +#define COMPONENTS_VIZ_COMMON_QUADS_SHARED_QUAD_STATE_H_ + +#include <memory> + +#include "components/viz/common/viz_common_export.h" +#include "third_party/skia/include/core/SkBlendMode.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/transform.h" + +namespace base { +namespace trace_event { +class TracedValue; +} +} // namespace base + +namespace viz { + +// SharedQuadState holds a set of properties that are common across multiple +// DrawQuads. It's purely an optimization - the properties behave in exactly the +// same way as if they were replicated on each DrawQuad. A given SharedQuadState +// can only be shared by DrawQuads that are adjacent in their RenderPass' +// QuadList. +class VIZ_COMMON_EXPORT SharedQuadState { + public: + SharedQuadState(); + SharedQuadState(const SharedQuadState& other); + ~SharedQuadState(); + + void SetAll(const gfx::Transform& quad_to_target_transform, + const gfx::Rect& layer_rect, + const gfx::Rect& visible_layer_rect, + const gfx::Rect& clip_rect, + bool is_clipped, + float opacity, + SkBlendMode blend_mode, + int sorting_context_id); + void AsValueInto(base::trace_event::TracedValue* dict) const; + + // Transforms quad rects into the target content space. + gfx::Transform quad_to_target_transform; + // The rect of the quads' originating layer in the space of the quad rects. + gfx::Rect quad_layer_rect; + // The size of the visible area in the quads' originating layer, in the space + // of the quad rects. + gfx::Rect visible_quad_layer_rect; + // This rect lives in the target content space. + gfx::Rect clip_rect; + bool is_clipped; + float opacity; + SkBlendMode blend_mode; + int sorting_context_id; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_QUADS_SHARED_QUAD_STATE_H_ diff --git a/chromium/components/viz/common/quads/single_release_callback.cc b/chromium/components/viz/common/quads/single_release_callback.cc new file mode 100644 index 00000000000..bd3dca9eb12 --- /dev/null +++ b/chromium/components/viz/common/quads/single_release_callback.cc @@ -0,0 +1,29 @@ +// 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/single_release_callback.h" + +#include "base/callback_helpers.h" +#include "base/logging.h" + +namespace viz { + +SingleReleaseCallback::SingleReleaseCallback(const ReleaseCallback& callback) + : callback_(callback) { + DCHECK(!callback_.is_null()) + << "Use a NULL SingleReleaseCallback for an empty callback."; +} + +SingleReleaseCallback::~SingleReleaseCallback() { + DCHECK(callback_.is_null()) << "SingleReleaseCallback was never run."; +} + +void SingleReleaseCallback::Run(const gpu::SyncToken& sync_token, + bool is_lost) { + DCHECK(!callback_.is_null()) + << "SingleReleaseCallback was run more than once."; + base::ResetAndReturn(&callback_).Run(sync_token, is_lost); +} + +} // namespace viz diff --git a/chromium/components/viz/common/quads/single_release_callback.h b/chromium/components/viz/common/quads/single_release_callback.h new file mode 100644 index 00000000000..29420f57487 --- /dev/null +++ b/chromium/components/viz/common/quads/single_release_callback.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_SINGLE_RELEASE_CALLBACK_H_ +#define COMPONENTS_VIZ_COMMON_QUADS_SINGLE_RELEASE_CALLBACK_H_ + +#include <memory> + +#include "base/memory/ptr_util.h" +#include "components/viz/common/quads/release_callback.h" +#include "components/viz/common/viz_common_export.h" + +namespace viz { + +class VIZ_COMMON_EXPORT SingleReleaseCallback { + public: + static std::unique_ptr<SingleReleaseCallback> Create( + const ReleaseCallback& cb) { + return base::WrapUnique(new SingleReleaseCallback(cb)); + } + + ~SingleReleaseCallback(); + + void Run(const gpu::SyncToken& sync_token, bool is_lost); + + private: + explicit SingleReleaseCallback(const ReleaseCallback& callback); + + ReleaseCallback callback_; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_QUADS_SINGLE_RELEASE_CALLBACK_H_ diff --git a/chromium/components/viz/common/resources/DEPS b/chromium/components/viz/common/resources/DEPS index ce3dad47826..fe456ebd62e 100644 --- a/chromium/components/viz/common/resources/DEPS +++ b/chromium/components/viz/common/resources/DEPS @@ -1,6 +1,6 @@ include_rules = [ + "+gpu/command_buffer/common", "+gpu/GLES2", "+third_party/khronos/GLES2", "+third_party/skia", - "+ui/gfx", ] 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 index 83877f4e1fb..4d3e66b9058 100644 --- a/chromium/components/viz/common/resources/buffer_to_texture_target_map.cc +++ b/chromium/components/viz/common/resources/buffer_to_texture_target_map.cc @@ -7,7 +7,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" -#include "gpu/GLES2/gl2extchromium.h" +#include "third_party/khronos/GLES2/gl2.h" namespace viz { diff --git a/chromium/components/viz/common/resources/platform_color.h b/chromium/components/viz/common/resources/platform_color.h index 505b6d43269..9c0fa1e66e3 100644 --- a/chromium/components/viz/common/resources/platform_color.h +++ b/chromium/components/viz/common/resources/platform_color.h @@ -7,7 +7,7 @@ #include "base/logging.h" #include "base/macros.h" -#include "components/viz/common/quads/resource_format.h" +#include "components/viz/common/resources/resource_format.h" #include "third_party/skia/include/core/SkTypes.h" namespace viz { diff --git a/chromium/components/viz/common/resources/resource_format.h b/chromium/components/viz/common/resources/resource_format.h new file mode 100644 index 00000000000..1e90781fb63 --- /dev/null +++ b/chromium/components/viz/common/resources/resource_format.h @@ -0,0 +1,28 @@ +// 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_RESOURCE_FORMAT_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_H_ + +namespace viz { + +// If these values are modified, then it is likely that resource_format_utils.cc +// has to be updated as well. +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_RESOURCES_RESOURCE_FORMAT_H_ diff --git a/chromium/components/viz/common/resources/resource_format_utils.cc b/chromium/components/viz/common/resources/resource_format_utils.cc index f15ddc72682..4cee8649088 100644 --- a/chromium/components/viz/common/resources/resource_format_utils.cc +++ b/chromium/components/viz/common/resources/resource_format_utils.cc @@ -4,9 +4,11 @@ #include "components/viz/common/resources/resource_format_utils.h" +#include "base/logging.h" +#include "base/macros.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" -#include "ui/gfx/gpu_memory_buffer.h" +#include "ui/gfx/buffer_types.h" namespace viz { @@ -57,7 +59,7 @@ int BitsPerPixel(ResourceFormat format) { return 0; } -GLenum GLDataType(ResourceFormat format) { +unsigned int GLDataType(ResourceFormat format) { DCHECK_LE(format, RESOURCE_FORMAT_MAX); static const GLenum format_gl_data_type[] = { GL_UNSIGNED_BYTE, // RGBA_8888 @@ -77,7 +79,7 @@ GLenum GLDataType(ResourceFormat format) { return format_gl_data_type[format]; } -GLenum GLDataFormat(ResourceFormat format) { +unsigned int GLDataFormat(ResourceFormat format) { DCHECK_LE(format, RESOURCE_FORMAT_MAX); static const GLenum format_gl_data_format[] = { GL_RGBA, // RGBA_8888 @@ -96,13 +98,13 @@ GLenum GLDataFormat(ResourceFormat format) { return format_gl_data_format[format]; } -GLenum GLInternalFormat(ResourceFormat format) { +unsigned int 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) { +unsigned int 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 @@ -151,6 +153,23 @@ gfx::BufferFormat BufferFormat(ResourceFormat format) { return gfx::BufferFormat::RGBA_8888; } +GrPixelConfig ToGrPixelConfig(ResourceFormat format) { + switch (format) { + case RGBA_8888: + return kRGBA_8888_GrPixelConfig; + case BGRA_8888: + return kBGRA_8888_GrPixelConfig; + case RGBA_4444: + return kRGBA_4444_GrPixelConfig; + case RGBA_F16: + return kRGBA_half_GrPixelConfig; + default: + break; + } + DCHECK(false) << "Unsupported resource format."; + return kSkia8888_GrPixelConfig; +} + bool IsResourceFormatCompressed(ResourceFormat format) { return format == ETC1; } diff --git a/chromium/components/viz/common/resources/resource_format_utils.h b/chromium/components/viz/common/resources/resource_format_utils.h index 935ccc8796e..262d5d67a93 100644 --- a/chromium/components/viz/common/resources/resource_format_utils.h +++ b/chromium/components/viz/common/resources/resource_format_utils.h @@ -5,22 +5,35 @@ #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 "components/viz/common/resources/resource_format.h" +#include "components/viz/common/viz_resource_format_export.h" #include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/gpu/GrTypes.h" +#include "ui/gfx/buffer_types.h" namespace viz { -VIZ_COMMON_EXPORT SkColorType +VIZ_RESOURCE_FORMAT_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); +VIZ_RESOURCE_FORMAT_EXPORT int BitsPerPixel(ResourceFormat format); + +// The following functions use unsigned int instead of GLenum, since 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. See http://crbug.com/512833 for more information. +VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLDataType(ResourceFormat format); +VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLDataFormat(ResourceFormat format); +VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLInternalFormat(ResourceFormat format); +VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLCopyTextureInternalFormat( + ResourceFormat format); + +VIZ_RESOURCE_FORMAT_EXPORT gfx::BufferFormat BufferFormat( + ResourceFormat format); +VIZ_RESOURCE_FORMAT_EXPORT bool IsResourceFormatCompressed( + ResourceFormat format); +VIZ_RESOURCE_FORMAT_EXPORT bool DoesResourceFormatSupportAlpha( + ResourceFormat format); +VIZ_RESOURCE_FORMAT_EXPORT GrPixelConfig ToGrPixelConfig(ResourceFormat format); } // namespace viz diff --git a/chromium/components/viz/common/resources/resource_id.h b/chromium/components/viz/common/resources/resource_id.h new file mode 100644 index 00000000000..b7a3a48c1b1 --- /dev/null +++ b/chromium/components/viz/common/resources/resource_id.h @@ -0,0 +1,19 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_ID_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_ID_H_ + +#include <stdint.h> + +#include "base/containers/flat_set.h" + +namespace viz { + +using ResourceId = uint32_t; +using ResourceIdSet = base::flat_set<ResourceId>; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_ID_H_ diff --git a/chromium/components/viz/common/resources/returned_resource.h b/chromium/components/viz/common/resources/returned_resource.h new file mode 100644 index 00000000000..6e60a51e72a --- /dev/null +++ b/chromium/components/viz/common/resources/returned_resource.h @@ -0,0 +1,56 @@ +// 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_RETURNED_RESOURCE_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_RETURNED_RESOURCE_H_ + +#include <vector> + +#include "components/viz/common/resources/resource_id.h" +#include "components/viz/common/resources/returned_resource.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/command_buffer/common/sync_token.h" + +namespace viz { + +// A ReturnedResource is a struct passed along to a child compositor from a +// parent compositor that corresponds to a TransferableResource that was +// first passed to the parent compositor. +struct VIZ_COMMON_EXPORT ReturnedResource { + ReturnedResource() : id(0), count(0), lost(false) {} + + bool operator==(const ReturnedResource& other) const { + return id == other.id && sync_token == other.sync_token && + count == other.count && lost == other.lost; + } + + bool operator!=(const ReturnedResource& other) const { + return !(*this == other); + } + + // |id| is an identifier generated by the child compositor that uniquely + // identifies a resource. This is the same ID space as TransferableResource. + ResourceId id; + + // A |sync_token| is an identifier for a point in the parent compositor's + // command buffer. The child compositor then issues a WaitSyncPointCHROMIUM + // command with this |sync_token| as a parameter into its own command buffer. + // This ensures that uses of the resource submitted by the parent compositor + // are executed before commands submitted by the child. + gpu::SyncToken sync_token; + + // |count| is a reference count for this resource. A resource may be used + // by mulitple compositor frames submitted to the parent compositor. |count| + // is the number of references being returned back to the child compositor. + int count; + + // If the resource is lost, then the returner cannot give a sync point for it, + // and so it has taken ownership of the resource. The receiver cannot do + // anything with the resource except delete it. + bool lost; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RETURNED_RESOURCE_H_ diff --git a/chromium/components/viz/common/resources/transferable_resource.cc b/chromium/components/viz/common/resources/transferable_resource.cc new file mode 100644 index 00000000000..7d44148cf52 --- /dev/null +++ b/chromium/components/viz/common/resources/transferable_resource.cc @@ -0,0 +1,48 @@ +// Copyright 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/resources/transferable_resource.h" +#include "components/viz/common/resources/returned_resource.h" + +namespace viz { + +TransferableResource::TransferableResource() + : id(0), + format(RGBA_8888), + buffer_format(gfx::BufferFormat::RGBA_8888), + filter(0), + read_lock_fences_enabled(false), + is_software(false), + shared_bitmap_sequence_number(0), +#if defined(OS_ANDROID) + is_backed_by_surface_texture(false), + wants_promotion_hint(false), +#endif + is_overlay_candidate(false) { +} + +TransferableResource::TransferableResource(const TransferableResource& other) = + default; + +TransferableResource::~TransferableResource() {} + +ReturnedResource TransferableResource::ToReturnedResource() const { + ReturnedResource returned; + returned.id = id; + returned.sync_token = mailbox_holder.sync_token; + returned.count = 1; + return returned; +} + +// static +std::vector<ReturnedResource> TransferableResource::ReturnResources( + const std::vector<TransferableResource>& input) { + std::vector<ReturnedResource> out; + out.reserve(input.size()); + for (const auto& r : input) + out.push_back(r.ToReturnedResource()); + return out; +} + +} // namespace viz diff --git a/chromium/components/viz/common/resources/transferable_resource.h b/chromium/components/viz/common/resources/transferable_resource.h new file mode 100644 index 00000000000..c85f6917a0d --- /dev/null +++ b/chromium/components/viz/common/resources/transferable_resource.h @@ -0,0 +1,54 @@ +// Copyright 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_RESOURCES_TRANSFERABLE_RESOURCE_H_ +#define COMPONENTS_VIZ_COMMON_RESOURCES_TRANSFERABLE_RESOURCE_H_ + +#include <stdint.h> + +#include <vector> + +#include "build/build_config.h" +#include "components/viz/common/resources/resource_format.h" +#include "components/viz/common/resources/resource_id.h" +#include "components/viz/common/viz_common_export.h" +#include "gpu/command_buffer/common/mailbox_holder.h" +#include "ui/gfx/buffer_types.h" +#include "ui/gfx/color_space.h" +#include "ui/gfx/geometry/size.h" + +namespace viz { + +struct ReturnedResource; + +struct VIZ_COMMON_EXPORT TransferableResource { + TransferableResource(); + TransferableResource(const TransferableResource& other); + ~TransferableResource(); + + ReturnedResource ToReturnedResource() const; + static std::vector<ReturnedResource> ReturnResources( + const std::vector<TransferableResource>& input); + + ResourceId id; + // Refer to ResourceProvider::Resource for the meaning of the following data. + ResourceFormat format; + gfx::BufferFormat buffer_format; + uint32_t filter; + gfx::Size size; + gpu::MailboxHolder mailbox_holder; + bool read_lock_fences_enabled; + bool is_software; + uint32_t shared_bitmap_sequence_number; +#if defined(OS_ANDROID) + bool is_backed_by_surface_texture; + bool wants_promotion_hint; +#endif + bool is_overlay_candidate; + gfx::ColorSpace color_space; +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_RESOURCES_TRANSFERABLE_RESOURCE_H_ diff --git a/chromium/components/viz/common/surfaces/DEPS b/chromium/components/viz/common/surfaces/DEPS index 826db9ece7e..16764a92a85 100644 --- a/chromium/components/viz/common/surfaces/DEPS +++ b/chromium/components/viz/common/surfaces/DEPS @@ -1,4 +1,3 @@ include_rules = [ "+mojo/public/cpp/bindings", - "+ui/gfx/geometry", ] diff --git a/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h index 93fa9534bf2..c3cbde918b3 100644 --- a/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h +++ b/chromium/components/viz/common/surfaces/sequence_surface_reference_factory.h @@ -13,7 +13,7 @@ namespace viz { // A surface reference factory that uses SurfaceSequence. class VIZ_COMMON_EXPORT SequenceSurfaceReferenceFactory - : public NON_EXPORTED_BASE(SurfaceReferenceFactory) { + : public SurfaceReferenceFactory { public: SequenceSurfaceReferenceFactory() = default; diff --git a/chromium/components/viz/common/surfaces/stub_surface_reference_factory.cc b/chromium/components/viz/common/surfaces/stub_surface_reference_factory.cc new file mode 100644 index 00000000000..e5d2ce50a92 --- /dev/null +++ b/chromium/components/viz/common/surfaces/stub_surface_reference_factory.cc @@ -0,0 +1,17 @@ +// 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/stub_surface_reference_factory.h" + +#include "base/callback.h" + +namespace viz { + +base::Closure StubSurfaceReferenceFactory::CreateReference( + SurfaceReferenceOwner* owner, + const SurfaceId& surface_id) const { + return base::Closure(); +} + +} // namespace viz diff --git a/chromium/components/viz/common/surfaces/stub_surface_reference_factory.h b/chromium/components/viz/common/surfaces/stub_surface_reference_factory.h new file mode 100644 index 00000000000..156809c777c --- /dev/null +++ b/chromium/components/viz/common/surfaces/stub_surface_reference_factory.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_STUB_SURFACE_REFERENCE_FACTORY_H_ +#define COMPONENTS_VIZ_COMMON_SURFACES_STUB_SURFACE_REFERENCE_FACTORY_H_ + +#include "base/compiler_specific.h" +#include "components/viz/common/surfaces/surface_reference_factory.h" +#include "components/viz/common/viz_common_export.h" + +namespace viz { + +// A stub implementation that creates a closure which does nothing. +// TODO(kylechar): Delete this class and all usage of +// SurfaceReferenceFactory when surface references are enabled by default. +class VIZ_COMMON_EXPORT StubSurfaceReferenceFactory + : public SurfaceReferenceFactory { + public: + StubSurfaceReferenceFactory() = default; + + // SurfaceReferenceFactory: + base::Closure CreateReference(SurfaceReferenceOwner* owner, + const SurfaceId& surface_id) const override; + + protected: + ~StubSurfaceReferenceFactory() override = default; + + DISALLOW_COPY_AND_ASSIGN(StubSurfaceReferenceFactory); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_SURFACES_STUB_SURFACE_REFERENCE_FACTORY_H_ diff --git a/chromium/components/viz/common/surfaces/surface_id.h b/chromium/components/viz/common/surfaces/surface_id.h index b008abaf288..dda288c13f2 100644 --- a/chromium/components/viz/common/surfaces/surface_id.h +++ b/chromium/components/viz/common/surfaces/surface_id.h @@ -17,13 +17,11 @@ #include "components/viz/common/viz_common_export.h" #include "mojo/public/cpp/bindings/struct_traits.h" -namespace cc { +namespace viz { + namespace mojom { class SurfaceIdDataView; } -} // namespace cc - -namespace viz { class VIZ_COMMON_EXPORT SurfaceId { public: @@ -70,7 +68,7 @@ class VIZ_COMMON_EXPORT SurfaceId { } private: - friend struct mojo::StructTraits<cc::mojom::SurfaceIdDataView, SurfaceId>; + friend struct mojo::StructTraits<mojom::SurfaceIdDataView, SurfaceId>; FrameSinkId frame_sink_id_; LocalSurfaceId local_surface_id_; diff --git a/chromium/components/viz/common/surfaces/surface_info.h b/chromium/components/viz/common/surfaces/surface_info.h index 1e21fda57d8..b40092bcb4c 100644 --- a/chromium/components/viz/common/surfaces/surface_info.h +++ b/chromium/components/viz/common/surfaces/surface_info.h @@ -13,13 +13,11 @@ template <class T> struct ParamTraits; } // namespace IPC -namespace cc { +namespace viz { + namespace mojom { class SurfaceInfoDataView; -} -} // namespace cc - -namespace viz { +} // namespace mojom // This class contains information about the surface that is being embedded. class SurfaceInfo { @@ -50,7 +48,7 @@ class SurfaceInfo { const gfx::Size& size_in_pixels() const { return size_in_pixels_; } private: - friend struct mojo::StructTraits<cc::mojom::SurfaceInfoDataView, SurfaceInfo>; + friend struct mojo::StructTraits<mojom::SurfaceInfoDataView, SurfaceInfo>; friend struct IPC::ParamTraits<SurfaceInfo>; SurfaceId id_; diff --git a/chromium/components/viz/common/switches.cc b/chromium/components/viz/common/switches.cc new file mode 100644 index 00000000000..ee13e20ad7f --- /dev/null +++ b/chromium/components/viz/common/switches.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/switches.h" + +namespace switches { + +// Disable surface lifetime management using surface references. This enables +// adding surface sequences and disables adding temporary references. +const char kDisableSurfaceReferences[] = "disable-surface-references"; + +// Enables multi-client Surface synchronization. In practice, this indicates +// that LayerTreeHost expects to be given a valid viz::LocalSurfaceId provided +// by the parent compositor. +const char kEnableSurfaceSynchronization[] = "enable-surface-synchronization"; + +} // namespace switches diff --git a/chromium/components/viz/common/switches.h b/chromium/components/viz/common/switches.h new file mode 100644 index 00000000000..69bd3077588 --- /dev/null +++ b/chromium/components/viz/common/switches.h @@ -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. + +#ifndef COMPONENTS_VIZ_COMMON_SWITCHES_H_ +#define COMPONENTS_VIZ_COMMON_SWITCHES_H_ + +#include "components/viz/common/viz_common_export.h" + +namespace switches { + +// Keep list in alphabetical order. +VIZ_COMMON_EXPORT extern const char kDisableSurfaceReferences[]; +VIZ_COMMON_EXPORT extern const char kEnableSurfaceSynchronization[]; + +} // namespace switches + +#endif // COMPONENTS_VIZ_COMMON_SWITCHES_H_ diff --git a/chromium/components/viz/common/traced_value.cc b/chromium/components/viz/common/traced_value.cc new file mode 100644 index 00000000000..b3a23c2f16d --- /dev/null +++ b/chromium/components/viz/common/traced_value.cc @@ -0,0 +1,54 @@ +// 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. + +#include "components/viz/common/traced_value.h" + +#include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event_argument.h" + +namespace viz { + +void TracedValue::AppendIDRef(const void* id, + base::trace_event::TracedValue* state) { + state->BeginDictionary(); + state->SetString("id_ref", base::StringPrintf("%p", id)); + state->EndDictionary(); +} + +void TracedValue::SetIDRef(const void* id, + base::trace_event::TracedValue* state, + const char* name) { + state->BeginDictionary(name); + state->SetString("id_ref", base::StringPrintf("%p", id)); + state->EndDictionary(); +} + +void TracedValue::MakeDictIntoImplicitSnapshot( + base::trace_event::TracedValue* dict, + const char* object_name, + const void* id) { + dict->SetString("id", base::StringPrintf("%s/%p", object_name, id)); +} + +void TracedValue::MakeDictIntoImplicitSnapshotWithCategory( + const char* category, + base::trace_event::TracedValue* dict, + const char* object_name, + const void* id) { + dict->SetString("cat", category); + MakeDictIntoImplicitSnapshot(dict, object_name, id); +} + +void TracedValue::MakeDictIntoImplicitSnapshotWithCategory( + const char* category, + base::trace_event::TracedValue* dict, + const char* object_base_type_name, + const char* object_name, + const void* id) { + dict->SetString("cat", category); + dict->SetString("base_type", object_base_type_name); + MakeDictIntoImplicitSnapshot(dict, object_name, id); +} + +} // namespace viz diff --git a/chromium/components/viz/common/traced_value.h b/chromium/components/viz/common/traced_value.h new file mode 100644 index 00000000000..2f2d144aa5d --- /dev/null +++ b/chromium/components/viz/common/traced_value.h @@ -0,0 +1,43 @@ +// 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_TRACED_VALUE_H_ +#define COMPONENTS_VIZ_COMMON_TRACED_VALUE_H_ + +#include "components/viz/common/viz_common_export.h" + +namespace base { +namespace trace_event { +class TracedValue; +} +} // namespace base + +namespace viz { + +class VIZ_COMMON_EXPORT TracedValue { + public: + static void AppendIDRef(const void* id, + base::trace_event::TracedValue* array); + static void SetIDRef(const void* id, + base::trace_event::TracedValue* dict, + const char* name); + static void MakeDictIntoImplicitSnapshot(base::trace_event::TracedValue* dict, + const char* object_name, + const void* id); + static void MakeDictIntoImplicitSnapshotWithCategory( + const char* category, + base::trace_event::TracedValue* dict, + const char* object_name, + const void* id); + static void MakeDictIntoImplicitSnapshotWithCategory( + const char* category, + base::trace_event::TracedValue* dict, + const char* object_base_type_name, + const char* object_name, + const void* id); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_COMMON_TRACED_VALUE_H_ diff --git a/chromium/components/viz/common/viz_resource_format_export.h b/chromium/components/viz/common/viz_resource_format_export.h new file mode 100644 index 00000000000..549ac8c357f --- /dev/null +++ b/chromium/components/viz/common/viz_resource_format_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_RESOURCE_FORMAT_EXPORT_H_ +#define COMPONENTS_VIZ_COMMON_VIZ_RESOURCE_FORMAT_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(VIZ_RESOURCE_FORMAT_IMPLEMENTATION) +#define VIZ_RESOURCE_FORMAT_EXPORT __declspec(dllexport) +#else +#define VIZ_RESOURCE_FORMAT_EXPORT __declspec(dllimport) +#endif // defined(VIZ_RESOURCE_FORMAT_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(VIZ_RESOURCE_FORMAT_IMPLEMENTATION) +#define VIZ_RESOURCE_FORMAT_EXPORT __attribute__((visibility("default"))) +#else +#define VIZ_RESOURCE_FORMAT_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define VIZ_RESOURCE_FORMAT_EXPORT +#endif + +#endif // COMPONENTS_VIZ_COMMON_VIZ_RESOURCE_FORMAT_EXPORT_H_ |