summaryrefslogtreecommitdiff
path: root/chromium/components/viz
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/viz')
-rw-r--r--chromium/components/viz/README.md13
-rw-r--r--chromium/components/viz/client/client_resource_provider.cc158
-rw-r--r--chromium/components/viz/client/client_resource_provider.h3
-rw-r--r--chromium/components/viz/client/client_resource_provider_unittest.cc251
-rw-r--r--chromium/components/viz/client/frame_eviction_manager.cc69
-rw-r--r--chromium/components/viz/client/frame_eviction_manager.h9
-rw-r--r--chromium/components/viz/client/hit_test_data_provider_draw_quad.cc13
-rw-r--r--chromium/components/viz/client/hit_test_data_provider_draw_quad.h7
-rw-r--r--chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc50
-rw-r--r--chromium/components/viz/common/BUILD.gn14
-rw-r--r--chromium/components/viz/common/DEPS4
-rw-r--r--chromium/components/viz/common/display/renderer_settings.h6
-rw-r--r--chromium/components/viz/common/features.cc17
-rw-r--r--chromium/components/viz/common/features.h2
-rw-r--r--chromium/components/viz/common/frame_sinks/begin_frame_source.cc10
-rw-r--r--chromium/components/viz/common/frame_sinks/begin_frame_source.h5
-rw-r--r--chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc27
-rw-r--r--chromium/components/viz/common/gl_helper_benchmark.cc1
-rw-r--r--chromium/components/viz/common/gl_helper_unittest.cc1
-rw-r--r--chromium/components/viz/common/gl_scaler.cc809
-rw-r--r--chromium/components/viz/common/gl_scaler.h391
-rw-r--r--chromium/components/viz/common/gl_scaler_shader_pixeltest.cc687
-rw-r--r--chromium/components/viz/common/gl_scaler_test_util.cc362
-rw-r--r--chromium/components/viz/common/gl_scaler_test_util.h154
-rw-r--r--chromium/components/viz/common/gl_scaler_unittest.cc206
-rw-r--r--chromium/components/viz/common/gpu/context_cache_controller.cc12
-rw-r--r--chromium/components/viz/common/gpu/context_provider.h3
-rw-r--r--chromium/components/viz/common/gpu/raster_context_provider.h3
-rw-r--r--chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc1
-rw-r--r--chromium/components/viz/common/hit_test/hit_test_region_list.h19
-rw-r--r--chromium/components/viz/common/quads/compositor_frame_metadata.h10
-rw-r--r--chromium/components/viz/common/quads/draw_quad_unittest.cc20
-rw-r--r--chromium/components/viz/common/quads/frame_deadline.cc43
-rw-r--r--chromium/components/viz/common/quads/frame_deadline.h24
-rw-r--r--chromium/components/viz/common/quads/render_pass_draw_quad.cc11
-rw-r--r--chromium/components/viz/common/quads/render_pass_draw_quad.h8
-rw-r--r--chromium/components/viz/common/quads/render_pass_unittest.cc2
-rw-r--r--chromium/components/viz/common/resources/resource_format_utils.cc7
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id.cc4
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id.h4
-rw-r--r--chromium/components/viz/common/surfaces/surface_id.cc8
-rw-r--r--chromium/components/viz/common/surfaces/surface_id.h8
-rw-r--r--chromium/components/viz/common/yuv_readback_unittest.cc1
-rw-r--r--chromium/components/viz/host/BUILD.gn1
-rw-r--r--chromium/components/viz/host/DEPS2
-rw-r--r--chromium/components/viz/host/client_frame_sink_video_capturer.cc4
-rw-r--r--chromium/components/viz/host/client_frame_sink_video_capturer.h6
-rw-r--r--chromium/components/viz/host/gpu_client.cc38
-rw-r--r--chromium/components/viz/host/gpu_client.h12
-rw-r--r--chromium/components/viz/host/gpu_client_delegate.h41
-rw-r--r--chromium/components/viz/host/gpu_host_impl.cc197
-rw-r--r--chromium/components/viz/host/gpu_host_impl.h94
-rw-r--r--chromium/components/viz/host/hit_test/hit_test_query.cc138
-rw-r--r--chromium/components/viz/host/hit_test/hit_test_query.h16
-rw-r--r--chromium/components/viz/host/hit_test/hit_test_query_unittest.cc65
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager.cc83
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager.h27
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager_unittest.cc226
-rw-r--r--chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc4
-rw-r--r--chromium/components/viz/service/BUILD.gn68
-rw-r--r--chromium/components/viz/service/display/direct_renderer.cc60
-rw-r--r--chromium/components/viz/service/display/direct_renderer.h4
-rw-r--r--chromium/components/viz/service/display/display.cc60
-rw-r--r--chromium/components/viz/service/display/display.h4
-rw-r--r--chromium/components/viz/service/display/display_resource_provider.cc93
-rw-r--r--chromium/components/viz/service/display/display_resource_provider.h29
-rw-r--r--chromium/components/viz/service/display/display_resource_provider_unittest.cc88
-rw-r--r--chromium/components/viz/service/display/display_scheduler.cc6
-rw-r--r--chromium/components/viz/service/display/display_scheduler_unittest.cc18
-rw-r--r--chromium/components/viz/service/display/display_unittest.cc15
-rw-r--r--chromium/components/viz/service/display/gl_renderer.cc92
-rw-r--r--chromium/components/viz/service/display/gl_renderer.h36
-rw-r--r--chromium/components/viz/service/display/overlay_candidate.cc11
-rw-r--r--chromium/components/viz/service/display/overlay_candidate.h9
-rw-r--r--chromium/components/viz/service/display/overlay_processor.cc8
-rw-r--r--chromium/components/viz/service/display/overlay_strategy_underlay.cc21
-rw-r--r--chromium/components/viz/service/display/overlay_unittest.cc68
-rw-r--r--chromium/components/viz/service/display/renderer_pixeltest.cc100
-rw-r--r--chromium/components/viz/service/display/skia_output_surface.h23
-rw-r--r--chromium/components/viz/service/display/skia_renderer.cc430
-rw-r--r--chromium/components/viz/service/display/skia_renderer.h50
-rw-r--r--chromium/components/viz/service/display/surface_aggregator.cc34
-rw-r--r--chromium/components/viz/service/display/surface_aggregator.h4
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_unittest.cc431
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface.cc6
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc79
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h39
-rw-r--r--chromium/components/viz/service/display_embedder/gpu_display_provider.cc25
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc56
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl.h3
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc58
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h3
-rw-r--r--chromium/components/viz/service/display_embedder/software_output_device_x11.cc23
-rw-r--r--chromium/components/viz/service/display_embedder/viz_process_context_provider.cc146
-rw-r--r--chromium/components/viz/service/display_embedder/viz_process_context_provider.h41
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc59
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h9
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc75
-rw-r--r--chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc92
-rw-r--r--chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h22
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc18
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h10
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc13
-rw-r--r--chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc22
-rw-r--r--chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h1
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_references_unittest.cc116
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_resource_holder.cc6
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc382
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc22
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h8
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc12
-rw-r--r--chromium/components/viz/service/gl/gpu_service_impl.cc58
-rw-r--r--chromium/components/viz/service/gl/gpu_service_impl.h3
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_aggregator.cc83
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_aggregator.h14
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc156
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_manager.cc10
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_manager.h3
-rw-r--r--chromium/components/viz/service/java/src/org/chromium/components/viz/service/frame_sinks/ExternalBeginFrameSourceAndroid.java54
-rw-r--r--chromium/components/viz/service/java/src/org/chromium/components/viz/service/gl/ThrowUncaughtException.java22
-rw-r--r--chromium/components/viz/service/main/BUILD.gn6
-rw-r--r--chromium/components/viz/service/main/viz_main_impl.cc6
-rw-r--r--chromium/components/viz/service/main/viz_main_impl.h9
-rw-r--r--chromium/components/viz/service/surfaces/surface.cc119
-rw-r--r--chromium/components/viz/service/surfaces/surface.h32
-rw-r--r--chromium/components/viz/service/surfaces/surface_dependency_deadline.cc4
-rw-r--r--chromium/components/viz/service/surfaces/surface_dependency_tracker.cc61
-rw-r--r--chromium/components/viz/service/surfaces/surface_dependency_tracker.h13
-rw-r--r--chromium/components/viz/service/surfaces/surface_manager.cc50
-rw-r--r--chromium/components/viz/service/surfaces/surface_manager.h41
-rw-r--r--chromium/components/viz/service/surfaces/surface_observer.h3
-rw-r--r--chromium/components/viz/service/surfaces/surface_reference.cc5
-rw-r--r--chromium/components/viz/service/surfaces/surface_reference.h4
-rw-r--r--chromium/components/viz/test/BUILD.gn6
-rw-r--r--chromium/components/viz/viz.gni7
135 files changed, 6462 insertions, 1826 deletions
diff --git a/chromium/components/viz/README.md b/chromium/components/viz/README.md
index 74ed1c37c57..b39fbbc213e 100644
--- a/chromium/components/viz/README.md
+++ b/chromium/components/viz/README.md
@@ -130,6 +130,13 @@ Code here supports presentation of the backing store drawn by the display
compositor (typically thought of as SwapBuffers), as well as the use of
overlays.
+The source code is split into two build targets,
+``components/viz/service:service`` and
+``components/viz/service:gpu_service_dependencies``. The latter is
+code that requires being run in the gpu process, thus could not work
+if the viz service is located elsewhere. This is forward-looking code
+as the viz service is moving into the gpu process always.
+
| Can depend on: |
|:--------------------------------------|
| viz/common/* |
@@ -157,6 +164,12 @@ be composited.
deallocating) gpu memory buffers, setting up a channel for the command buffer,
etc.
+Similar to ``service/display_embedder`` this is split into two targets in
+the build system. Code that must run in the gpu process is compiled in
+the ``components/viz/service:gpu_service_dependencies`` build target,
+and the rest is compiled in ``components/viz/service:service``. As all
+code moves to the gpu process, these two build targets will merge.
+
| Can depend on: |
|:----------------------|
| viz/common/* |
diff --git a/chromium/components/viz/client/client_resource_provider.cc b/chromium/components/viz/client/client_resource_provider.cc
index c7b2477ea18..5bf168b4a45 100644
--- a/chromium/components/viz/client/client_resource_provider.cc
+++ b/chromium/components/viz/client/client_resource_provider.cc
@@ -133,34 +133,131 @@ void ClientResourceProvider::PrepareSendToParent(
}
void ClientResourceProvider::ReceiveReturnsFromParent(
- const std::vector<ReturnedResource>& resources) {
+ std::vector<ReturnedResource> resources) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- for (const ReturnedResource& returned : resources) {
- ResourceId local_id = returned.id;
+ // |imported_resources_| is a set sorted by id, so if we sort the incoming
+ // |resources| list by id also, we can walk through both structures in order,
+ // replacing things to be removed from imported_resources_ with things that
+ // we want to keep, making the removal of all items O(N+MlogM) instead of
+ // O(N*M). This algorithm is modelled after std::remove_if() but with a
+ // parallel walk through |resources| instead of an O(logM) lookup into
+ // |resources| for each step.
+ std::sort(resources.begin(), resources.end(),
+ [](const ReturnedResource& a, const ReturnedResource& b) {
+ return a.id < b.id;
+ });
+
+ auto returned_it = resources.begin();
+ auto returned_end = resources.end();
+ auto imported_keep_end_it = imported_resources_.begin();
+ auto imported_compare_it = imported_resources_.begin();
+ auto imported_end = imported_resources_.end();
+
+ std::vector<base::OnceClosure> release_callbacks;
+ release_callbacks.reserve(resources.size());
+
+ while (imported_compare_it != imported_end) {
+ if (returned_it == returned_end) {
+ // Nothing else to remove from |imported_resources_|.
+ if (imported_keep_end_it == imported_compare_it) {
+ // We didn't remove anything, so we're done.
+ break;
+ }
+ // If something was removed, we need to shift everything into the empty
+ // space that was made.
+ *imported_keep_end_it = std::move(*imported_compare_it);
+ ++imported_keep_end_it;
+ ++imported_compare_it;
+ continue;
+ }
+
+ const ResourceId returned_resource_id = returned_it->id;
+ const ResourceId imported_resource_id = imported_compare_it->first;
+
+ if (returned_resource_id != imported_resource_id) {
+ // They're both sorted, and everything being returned should already
+ // be in the imported list. So we should be able to walk through the
+ // resources list in order, and find each id in both sets when present.
+ // That means if it's not matching, we should be above it in the sorted
+ // order of the |imported_resources_|, allowing us to get to it by
+ // continuing to traverse |imported_resources_|.
+ DCHECK_GT(returned_resource_id, imported_resource_id);
+ // This means we want to keep the resource at |imported_compare_it|. So go
+ // to the next. If we removed anything previously and made empty space,
+ // fill it as we move.
+ if (imported_keep_end_it != imported_compare_it)
+ *imported_keep_end_it = std::move(*imported_compare_it);
+ ++imported_keep_end_it;
+ ++imported_compare_it;
+ continue;
+ }
- auto import_it = imported_resources_.find(local_id);
- DCHECK(import_it != imported_resources_.end());
- ImportedResource& imported = import_it->second;
+ const ReturnedResource& returned = *returned_it;
+ ImportedResource& imported = imported_compare_it->second;
DCHECK_GE(imported.exported_count, returned.count);
imported.exported_count -= returned.count;
imported.returned_lost |= returned.lost;
- if (imported.exported_count)
+ if (imported.exported_count) {
+ // Can't remove the imported yet so go to the next, looking for the next
+ // returned resource.
+ ++returned_it;
+ // The same ResourceId may appear multiple times (in a row) in the
+ // returned set. In that case, we do not want to increment the iterators
+ // in |imported_resources_| yet. So we don't increment them here, and let
+ // the next iteration of the loop do so.
continue;
+ }
+ // Save the sync token only when the exported count is going to 0. Or IOW
+ // drop all by the last returned sync token.
if (returned.sync_token.HasData()) {
DCHECK(!imported.resource.is_software);
imported.returned_sync_token = returned.sync_token;
}
- if (imported.marked_for_deletion) {
- imported.release_callback->Run(imported.returned_sync_token,
- imported.returned_lost);
- imported_resources_.erase(import_it);
+ if (!imported.marked_for_deletion) {
+ // Not going to remove the imported yet so go to the next, looking for the
+ // next returned resource.
+ ++returned_it;
+ // If we removed anything previously and made empty space, fill it as we
+ // move.
+ if (imported_keep_end_it != imported_compare_it)
+ *imported_keep_end_it = std::move(*imported_compare_it);
+ ++imported_keep_end_it;
+ ++imported_compare_it;
+ continue;
}
+
+ // Save the ReleaseCallback to run after iterating through internal data
+ // structures, in case it calls back into this class.
+ auto run_callback = [](std::unique_ptr<SingleReleaseCallback> cb,
+ const gpu::SyncToken& sync_token, bool lost) {
+ cb->Run(sync_token, lost);
+ // |cb| is destroyed when leaving scope.
+ };
+ release_callbacks.push_back(
+ base::BindOnce(run_callback, base::Passed(&imported.release_callback),
+ imported.returned_sync_token, imported.returned_lost));
+ // We don't want to keep this resource, so we leave |imported_keep_end_it|
+ // pointing to it (since it points past the end of what we're keeping). We
+ // do move |imported_compare_it| in order to move on to the next resource.
+ ++imported_compare_it;
+ // The imported iterator is pointing at the next element already, so no need
+ // to increment it, and we can move on to looking for the next returned
+ // resource.
+ ++returned_it;
}
+
+ // Drop anything that was moved after the |imported_end| iterator in a single
+ // O(N) operation.
+ if (imported_keep_end_it != imported_compare_it)
+ imported_resources_.erase(imported_keep_end_it, imported_resources_.end());
+
+ for (auto& cb : release_callbacks)
+ std::move(cb).Run();
}
ResourceId ClientResourceProvider::ImportResource(
@@ -188,21 +285,30 @@ void ClientResourceProvider::RemoveImportedResource(ResourceId id) {
}
void ClientResourceProvider::ReleaseAllExportedResources(bool lose) {
- std::vector<ResourceId> to_remove;
- for (auto& pair : imported_resources_) {
- ImportedResource& imported = pair.second;
- if (!imported.exported_count)
- continue;
- imported.exported_count = 0;
- imported.returned_lost |= lose;
- if (imported.marked_for_deletion) {
- imported.release_callback->Run(imported.returned_sync_token,
- imported.returned_lost);
- to_remove.push_back(pair.first);
- }
- }
- for (ResourceId id : to_remove)
- imported_resources_.erase(id);
+ auto release_and_remove =
+ [lose](std::pair<ResourceId, ImportedResource>& pair) {
+ ImportedResource& imported = pair.second;
+ if (!imported.exported_count) {
+ // Not exported, not up for consideration to be returned here.
+ return false;
+ }
+ imported.exported_count = 0;
+ imported.returned_lost |= lose;
+ if (!imported.marked_for_deletion) {
+ // Was exported, but not removed by the client, so don't return it
+ // yet.
+ return false;
+ }
+
+ imported.release_callback->Run(imported.returned_sync_token,
+ imported.returned_lost);
+ // Was exported and removed by the client, so return it now.
+ return true;
+ };
+
+ // This will run |release_and_remove| on each element of |imported_resources_|
+ // and drop any resources from the set that it requests.
+ base::EraseIf(imported_resources_, release_and_remove);
}
void ClientResourceProvider::ShutdownAndReleaseAllResources() {
diff --git a/chromium/components/viz/client/client_resource_provider.h b/chromium/components/viz/client/client_resource_provider.h
index 4087b5754ec..4706087be98 100644
--- a/chromium/components/viz/client/client_resource_provider.h
+++ b/chromium/components/viz/client/client_resource_provider.h
@@ -13,6 +13,7 @@
#include "components/viz/common/resources/release_callback.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/resource_settings.h"
+#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/single_release_callback.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "third_party/khronos/GLES2/gl2.h"
@@ -64,7 +65,7 @@ class VIZ_CLIENT_EXPORT ClientResourceProvider {
// NOTE: if the sync_token is set on any TransferableResource, this will
// wait on it.
void ReceiveReturnsFromParent(
- const std::vector<ReturnedResource>& transferable_resources);
+ std::vector<ReturnedResource> transferable_resources);
// Receives a resource from an external client that can be used in compositor
// frames, via the returned ResourceId.
diff --git a/chromium/components/viz/client/client_resource_provider_unittest.cc b/chromium/components/viz/client/client_resource_provider_unittest.cc
index da03c3cf750..cdb71a9b030 100644
--- a/chromium/components/viz/client/client_resource_provider_unittest.cc
+++ b/chromium/components/viz/client/client_resource_provider_unittest.cc
@@ -16,6 +16,7 @@
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::Each;
namespace viz {
namespace {
@@ -86,6 +87,8 @@ INSTANTIATE_TEST_CASE_P(ClientResourceProviderTests,
class MockReleaseCallback {
public:
MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost));
+ MOCK_METHOD3(ReleasedWithId,
+ void(ResourceId id, const gpu::SyncToken& token, bool lost));
};
TEST_P(ClientResourceProviderTest, TransferableResourceReleased) {
@@ -238,6 +241,65 @@ TEST_P(ClientResourceProviderTest,
DestroyProvider();
}
+TEST_P(ClientResourceProviderTest, TransferableResourceSendToParentManyUnsent) {
+ MockReleaseCallback release;
+
+ // 5 resources to have 2 before and 2 after the one being sent and returned,
+ // to verify the data structures are treated correctly when removing from the
+ // middle. 1 after is not enough as that one will replace the resource being
+ // removed and misses some edge cases. We want another resource after that in
+ // the structure to verify it is also not treated incorrectly.
+ struct Data {
+ TransferableResource tran;
+ ResourceId id;
+ } data[5];
+ for (int i = 0; i < 5; ++i) {
+ data[i].tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ data[i].id = provider().ImportResource(
+ data[i].tran,
+ SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+ }
+ std::sort(std::begin(data), std::end(data),
+ [](const Data& a, const Data& b) { return a.id < b.id; });
+
+ // Export the resource.
+ std::vector<ResourceId> to_send = {data[2].id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+ ASSERT_EQ(exported.size(), 1u);
+
+ // Exported resource matches except for the id which was mapped
+ // to the local ResourceProvider, and the sync token should be
+ // verified if it's a gpu resource.
+ gpu::SyncToken verified_sync_token = data[2].tran.mailbox_holder.sync_token;
+ if (!data[2].tran.is_software)
+ verified_sync_token.SetVerifyFlush();
+
+ // Exported resources are not released when removed, until the export returns.
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+ provider().RemoveImportedResource(data[2].id);
+
+ // Return the resource, with a sync token if using gpu.
+ std::vector<ReturnedResource> returned;
+ returned.push_back({});
+ returned.back().id = exported[0].id;
+ if (use_gpu())
+ returned.back().sync_token = SyncTokenFromUInt(31);
+ returned.back().count = 1;
+ returned.back().lost = false;
+
+ // The sync token is given to the ReleaseCallback.
+ EXPECT_CALL(release, Released(returned[0].sync_token, false));
+ provider().ReceiveReturnsFromParent(returned);
+
+ EXPECT_CALL(release, Released(_, false)).Times(4);
+ provider().RemoveImportedResource(data[0].id);
+ provider().RemoveImportedResource(data[1].id);
+ provider().RemoveImportedResource(data[3].id);
+ provider().RemoveImportedResource(data[4].id);
+}
+
TEST_P(ClientResourceProviderTest, TransferableResourceRemovedAfterReturn) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
@@ -554,7 +616,7 @@ TEST_P(ClientResourceProviderTest, ReleaseExportedResourcesThenRemove) {
provider().PrepareSendToParent({resource}, &list, context_provider());
EXPECT_EQ(1u, list.size());
- // Drop any exported resources. Yhey are now considered lost for gpu
+ // Drop any exported resources. They are now considered lost for gpu
// compositing, since gpu resources are modified (in their metadata) while
// being used by the parent.
provider().ReleaseAllExportedResources(use_gpu());
@@ -567,5 +629,192 @@ TEST_P(ClientResourceProviderTest, ReleaseExportedResourcesThenRemove) {
EXPECT_CALL(release, Released(_, _)).Times(0);
}
+TEST_P(ClientResourceProviderTest, ReleaseMultipleResources) {
+ MockReleaseCallback release;
+
+ // Make 5 resources, put them in a non-sorted order.
+ ResourceId resources[5];
+ for (int i = 0; i < 5; ++i) {
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 1 + i);
+ resources[i] = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(
+ base::BindOnce(&MockReleaseCallback::ReleasedWithId,
+ base::Unretained(&release), i)));
+ }
+
+ // Transfer some resources to the parent, but not in the sorted order.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resources[2], resources[0], resources[4]},
+ &list, context_provider());
+ EXPECT_EQ(3u, list.size());
+
+ // Receive them back. Since these are not in the same order they were
+ // inserted originally, we verify the ClientResourceProvider can handle
+ // resources being returned (in a group) that are not at the front/back
+ // of its internal storage. IOW This shouldn't crash or corrupt state.
+ std::vector<ReturnedResource> returned_to_child;
+ returned_to_child.push_back(list[0].ToReturnedResource());
+ returned_to_child.push_back(list[1].ToReturnedResource());
+ returned_to_child.push_back(list[2].ToReturnedResource());
+ provider().ReceiveReturnsFromParent(returned_to_child);
+
+ // Remove them from the ClientResourceProvider, they should be returned as
+ // they're no longer exported.
+ EXPECT_CALL(release, ReleasedWithId(0, _, false));
+ provider().RemoveImportedResource(resources[0]);
+ EXPECT_CALL(release, ReleasedWithId(2, _, false));
+ provider().RemoveImportedResource(resources[2]);
+ EXPECT_CALL(release, ReleasedWithId(4, _, false));
+ provider().RemoveImportedResource(resources[4]);
+
+ // These were never exported.
+ EXPECT_CALL(release, ReleasedWithId(1, _, false));
+ EXPECT_CALL(release, ReleasedWithId(3, _, false));
+ provider().RemoveImportedResource(resources[1]);
+ provider().RemoveImportedResource(resources[3]);
+
+ EXPECT_CALL(release, Released(_, false)).Times(0);
+}
+
+TEST_P(ClientResourceProviderTest, ReleaseMultipleResourcesBeforeReturn) {
+ MockReleaseCallback release;
+
+ // Make 5 resources, put them in a non-sorted order.
+ ResourceId resources[5];
+ for (int i = 0; i < 5; ++i) {
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 1 + i);
+ resources[i] = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(
+ base::BindOnce(&MockReleaseCallback::ReleasedWithId,
+ base::Unretained(&release), i)));
+ }
+
+ // Transfer some resources to the parent, but not in the sorted order.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resources[2], resources[0], resources[4]},
+ &list, context_provider());
+ EXPECT_EQ(3u, list.size());
+
+ // Remove the exported resources from the ClientResourceProvider, they should
+ // not yet be returned, since they are exported.
+ provider().RemoveImportedResource(resources[0]);
+ provider().RemoveImportedResource(resources[2]);
+ provider().RemoveImportedResource(resources[4]);
+
+ // Receive them back now. Since these are not in the same order they were
+ // inserted originally, we verify the ClientResourceProvider can handle
+ // resources being returned (in a group) that are not at the front/back
+ // of its internal storage. IOW This shouldn't crash or corrupt state.
+ std::vector<ReturnedResource> returned_to_child;
+ returned_to_child.push_back(list[0].ToReturnedResource());
+ returned_to_child.push_back(list[1].ToReturnedResource());
+ returned_to_child.push_back(list[2].ToReturnedResource());
+ // The resources are returned immediately since they were previously removed.
+ EXPECT_CALL(release, ReleasedWithId(0, _, false));
+ EXPECT_CALL(release, ReleasedWithId(2, _, false));
+ EXPECT_CALL(release, ReleasedWithId(4, _, false));
+ provider().ReceiveReturnsFromParent(returned_to_child);
+
+ // These were never exported.
+ EXPECT_CALL(release, ReleasedWithId(1, _, false));
+ EXPECT_CALL(release, ReleasedWithId(3, _, false));
+ provider().RemoveImportedResource(resources[1]);
+ provider().RemoveImportedResource(resources[3]);
+
+ EXPECT_CALL(release, Released(_, false)).Times(0);
+}
+
+TEST_P(ClientResourceProviderTest, ReturnDuplicateResourceBeforeRemove) {
+ MockReleaseCallback release;
+
+ // Make 5 resources, put them in a non-sorted order.
+ ResourceId resources[5];
+ for (int i = 0; i < 5; ++i) {
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 1 + i);
+ resources[i] = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(
+ base::BindOnce(&MockReleaseCallback::ReleasedWithId,
+ base::Unretained(&release), i)));
+ }
+
+ // Transfer a resource to the parent, do it twice.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resources[2]}, &list, context_provider());
+ list.clear();
+ provider().PrepareSendToParent({resources[2]}, &list, context_provider());
+
+ // Receive the resource back. It's possible that the parent may return
+ // the same ResourceId multiple times in the same message, which we test
+ // for here.
+ std::vector<ReturnedResource> returned_to_child;
+ returned_to_child.push_back(list[0].ToReturnedResource());
+ returned_to_child.push_back(list[0].ToReturnedResource());
+ provider().ReceiveReturnsFromParent(returned_to_child);
+
+ // Remove it from the ClientResourceProvider, it should be returned as
+ // it's no longer exported.
+ EXPECT_CALL(release, ReleasedWithId(2, _, false));
+ provider().RemoveImportedResource(resources[2]);
+
+ // These were never exported.
+ EXPECT_CALL(release, ReleasedWithId(0, _, false));
+ EXPECT_CALL(release, ReleasedWithId(1, _, false));
+ EXPECT_CALL(release, ReleasedWithId(3, _, false));
+ EXPECT_CALL(release, ReleasedWithId(4, _, false));
+ provider().RemoveImportedResource(resources[0]);
+ provider().RemoveImportedResource(resources[1]);
+ provider().RemoveImportedResource(resources[3]);
+ provider().RemoveImportedResource(resources[4]);
+
+ EXPECT_CALL(release, Released(_, false)).Times(0);
+}
+
+TEST_P(ClientResourceProviderTest, ReturnDuplicateResourceAfterRemove) {
+ MockReleaseCallback release;
+
+ // Make 5 resources, put them in a non-sorted order.
+ ResourceId resources[5];
+ for (int i = 0; i < 5; ++i) {
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 1 + i);
+ resources[i] = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(
+ base::BindOnce(&MockReleaseCallback::ReleasedWithId,
+ base::Unretained(&release), i)));
+ }
+
+ // Transfer a resource to the parent, do it twice.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resources[2]}, &list, context_provider());
+ list.clear();
+ provider().PrepareSendToParent({resources[2]}, &list, context_provider());
+
+ // Remove it from the ClientResourceProvider, it should not be returned yet
+ // as it's still exported.
+ provider().RemoveImportedResource(resources[2]);
+
+ // Receive the resource back. It's possible that the parent may return
+ // the same ResourceId multiple times in the same message, which we test
+ // for here.
+ std::vector<ReturnedResource> returned_to_child;
+ returned_to_child.push_back(list[0].ToReturnedResource());
+ returned_to_child.push_back(list[0].ToReturnedResource());
+ // Once no longer exported, since it was removed earlier, it will be returned
+ // immediately.
+ EXPECT_CALL(release, ReleasedWithId(2, _, false));
+ provider().ReceiveReturnsFromParent(returned_to_child);
+
+ // These were never exported.
+ EXPECT_CALL(release, ReleasedWithId(0, _, false));
+ EXPECT_CALL(release, ReleasedWithId(1, _, false));
+ EXPECT_CALL(release, ReleasedWithId(3, _, false));
+ EXPECT_CALL(release, ReleasedWithId(4, _, false));
+ provider().RemoveImportedResource(resources[0]);
+ provider().RemoveImportedResource(resources[1]);
+ provider().RemoveImportedResource(resources[3]);
+ provider().RemoveImportedResource(resources[4]);
+
+ EXPECT_CALL(release, Released(_, false)).Times(0);
+}
+
} // namespace
} // namespace viz
diff --git a/chromium/components/viz/client/frame_eviction_manager.cc b/chromium/components/viz/client/frame_eviction_manager.cc
index 6a9412eccf9..c1c706eb646 100644
--- a/chromium/components/viz/client/frame_eviction_manager.cc
+++ b/chromium/components/viz/client/frame_eviction_manager.cc
@@ -8,11 +8,10 @@
#include "base/bind.h"
#include "base/logging.h"
-#include "base/memory/memory_coordinator_client_registry.h"
-#include "base/memory/memory_coordinator_proxy.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/memory_pressure_monitor.h"
#include "base/memory/shared_memory.h"
+#include "base/stl_util.h"
#include "base/sys_info.h"
#include "build/build_config.h"
@@ -28,6 +27,8 @@ FrameEvictionManager* FrameEvictionManager::GetInstance() {
return base::Singleton<FrameEvictionManager>::get();
}
+FrameEvictionManager::~FrameEvictionManager() {}
+
void FrameEvictionManager::AddFrame(FrameEvictionManagerClient* frame,
bool locked) {
RemoveFrame(frame);
@@ -39,17 +40,14 @@ void FrameEvictionManager::AddFrame(FrameEvictionManagerClient* frame,
}
void FrameEvictionManager::RemoveFrame(FrameEvictionManagerClient* frame) {
- std::map<FrameEvictionManagerClient*, size_t>::iterator locked_iter =
- locked_frames_.find(frame);
+ auto locked_iter = locked_frames_.find(frame);
if (locked_iter != locked_frames_.end())
locked_frames_.erase(locked_iter);
unlocked_frames_.remove(frame);
}
void FrameEvictionManager::LockFrame(FrameEvictionManagerClient* frame) {
- std::list<FrameEvictionManagerClient*>::iterator unlocked_iter =
- std::find(unlocked_frames_.begin(), unlocked_frames_.end(), frame);
- if (unlocked_iter != unlocked_frames_.end()) {
+ if (base::ContainsValue(unlocked_frames_, frame)) {
DCHECK(locked_frames_.find(frame) == locked_frames_.end());
unlocked_frames_.remove(frame);
locked_frames_[frame] = 1;
@@ -74,39 +72,23 @@ void FrameEvictionManager::UnlockFrame(FrameEvictionManagerClient* frame) {
size_t FrameEvictionManager::GetMaxNumberOfSavedFrames() const {
int percentage = 100;
- auto* memory_coordinator_proxy = base::MemoryCoordinatorProxy::GetInstance();
- if (memory_coordinator_proxy) {
- switch (memory_coordinator_proxy->GetCurrentMemoryState()) {
- case base::MemoryState::NORMAL:
- percentage = 100;
- break;
- case base::MemoryState::THROTTLED:
- percentage = kCriticalPressurePercentage;
- break;
- case base::MemoryState::SUSPENDED:
- case base::MemoryState::UNKNOWN:
- NOTREACHED();
- break;
- }
- } else {
- base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get();
-
- if (!monitor)
- return max_number_of_saved_frames_;
-
- // Until we have a global OnMemoryPressureChanged event we need to query the
- // value from our specific pressure monitor.
- switch (monitor->GetCurrentPressureLevel()) {
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
- percentage = 100;
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
- percentage = kModeratePressurePercentage;
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
- percentage = kCriticalPressurePercentage;
- break;
- }
+ base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get();
+
+ if (!monitor)
+ return max_number_of_saved_frames_;
+
+ // Until we have a global OnMemoryPressureChanged event we need to query the
+ // value from our specific pressure monitor.
+ switch (monitor->GetCurrentPressureLevel()) {
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+ percentage = 100;
+ break;
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+ percentage = kModeratePressurePercentage;
+ break;
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+ percentage = kCriticalPressurePercentage;
+ break;
}
size_t frames = (max_number_of_saved_frames_ * percentage) / 100;
return std::max(static_cast<size_t>(1), frames);
@@ -116,7 +98,6 @@ FrameEvictionManager::FrameEvictionManager()
: memory_pressure_listener_(new base::MemoryPressureListener(
base::Bind(&FrameEvictionManager::OnMemoryPressure,
base::Unretained(this)))) {
- base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this);
max_number_of_saved_frames_ =
#if defined(OS_ANDROID)
// If the amount of memory on the device is >= 3.5 GB, save up to 5
@@ -127,8 +108,6 @@ FrameEvictionManager::FrameEvictionManager()
#endif
}
-FrameEvictionManager::~FrameEvictionManager() {}
-
void FrameEvictionManager::CullUnlockedFrames(size_t saved_frame_limit) {
while (!unlocked_frames_.empty() &&
unlocked_frames_.size() + locked_frames_.size() > saved_frame_limit) {
@@ -154,10 +133,6 @@ void FrameEvictionManager::OnMemoryPressure(
}
}
-void FrameEvictionManager::OnPurgeMemory() {
- PurgeMemory(kCriticalPressurePercentage);
-}
-
void FrameEvictionManager::PurgeMemory(int percentage) {
int saved_frame_limit = max_number_of_saved_frames_;
if (saved_frame_limit <= 1)
diff --git a/chromium/components/viz/client/frame_eviction_manager.h b/chromium/components/viz/client/frame_eviction_manager.h
index 86486913040..3307db34a48 100644
--- a/chromium/components/viz/client/frame_eviction_manager.h
+++ b/chromium/components/viz/client/frame_eviction_manager.h
@@ -11,7 +11,6 @@
#include <map>
#include "base/macros.h"
-#include "base/memory/memory_coordinator_client.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/singleton.h"
#include "components/viz/client/viz_client_export.h"
@@ -30,8 +29,7 @@ class FrameEvictionManagerClient {
// between a small set of tabs faster. The limit is a soft limit, because
// clients can lock their frame to prevent it from being discarded, e.g. if the
// tab is visible, or while capturing a screenshot.
-class VIZ_CLIENT_EXPORT FrameEvictionManager
- : public base::MemoryCoordinatorClient {
+class VIZ_CLIENT_EXPORT FrameEvictionManager {
public:
static FrameEvictionManager* GetInstance();
@@ -57,10 +55,7 @@ class VIZ_CLIENT_EXPORT FrameEvictionManager
private:
FrameEvictionManager();
- ~FrameEvictionManager() override;
-
- // base::MemoryCoordinatorClient implementation:
- void OnPurgeMemory() override;
+ ~FrameEvictionManager();
void CullUnlockedFrames(size_t saved_frame_limit);
diff --git a/chromium/components/viz/client/hit_test_data_provider_draw_quad.cc b/chromium/components/viz/client/hit_test_data_provider_draw_quad.cc
index 822e04dac0d..8cb65f15d93 100644
--- a/chromium/components/viz/client/hit_test_data_provider_draw_quad.cc
+++ b/chromium/components/viz/client/hit_test_data_provider_draw_quad.cc
@@ -10,8 +10,10 @@
namespace viz {
HitTestDataProviderDrawQuad::HitTestDataProviderDrawQuad(
- bool should_ask_for_child_region)
- : should_ask_for_child_region_(should_ask_for_child_region) {}
+ bool should_ask_for_child_region,
+ bool root_accepts_events)
+ : should_ask_for_child_region_(should_ask_for_child_region),
+ root_accepts_events_(root_accepts_events) {}
HitTestDataProviderDrawQuad::~HitTestDataProviderDrawQuad() = default;
@@ -19,9 +21,10 @@ HitTestDataProviderDrawQuad::~HitTestDataProviderDrawQuad() = default;
base::Optional<HitTestRegionList> HitTestDataProviderDrawQuad::GetHitTestData(
const CompositorFrame& compositor_frame) const {
base::Optional<HitTestRegionList> hit_test_region_list(base::in_place);
- hit_test_region_list->flags = HitTestRegionFlags::kHitTestMouse |
- HitTestRegionFlags::kHitTestTouch |
- HitTestRegionFlags::kHitTestMine;
+ hit_test_region_list->flags =
+ (root_accepts_events_ ? HitTestRegionFlags::kHitTestMine
+ : HitTestRegionFlags::kHitTestIgnore) |
+ HitTestRegionFlags::kHitTestMouse | HitTestRegionFlags::kHitTestTouch;
hit_test_region_list->bounds.set_size(compositor_frame.size_in_pixels());
for (const auto& render_pass : compositor_frame.render_pass_list) {
diff --git a/chromium/components/viz/client/hit_test_data_provider_draw_quad.h b/chromium/components/viz/client/hit_test_data_provider_draw_quad.h
index 66ecdd776b6..8dee35c1132 100644
--- a/chromium/components/viz/client/hit_test_data_provider_draw_quad.h
+++ b/chromium/components/viz/client/hit_test_data_provider_draw_quad.h
@@ -14,7 +14,8 @@ namespace viz {
class VIZ_CLIENT_EXPORT HitTestDataProviderDrawQuad
: public HitTestDataProvider {
public:
- explicit HitTestDataProviderDrawQuad(bool should_ask_for_child_region);
+ HitTestDataProviderDrawQuad(bool should_ask_for_child_region,
+ bool root_accepts_events);
~HitTestDataProviderDrawQuad() override;
base::Optional<HitTestRegionList> GetHitTestData(
@@ -23,6 +24,10 @@ class VIZ_CLIENT_EXPORT HitTestDataProviderDrawQuad
private:
const bool should_ask_for_child_region_;
+ // Only used by HitTestRegionList to indicate if it should accept events or
+ // not.
+ const bool root_accepts_events_;
+
DISALLOW_COPY_AND_ASSIGN(HitTestDataProviderDrawQuad);
};
diff --git a/chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc b/chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
index 0b6ba4715ba..9cbc521c897 100644
--- a/chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
+++ b/chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
@@ -62,7 +62,8 @@ std::unique_ptr<RenderPass> CreateRenderPassWithChildSurface(
TEST(HitTestDataProviderDrawQuad, HitTestDataRenderer) {
std::unique_ptr<HitTestDataProvider> hit_test_data_provider =
std::make_unique<HitTestDataProviderDrawQuad>(
- true /* should_ask_for_child_region */);
+ true /* should_ask_for_child_region */,
+ true /* root_accepts_events */);
constexpr gfx::Rect kFrameRect(0, 0, 1024, 768);
@@ -125,7 +126,8 @@ TEST(HitTestDataProviderDrawQuad, HitTestDataRenderer) {
TEST(HitTestDataProviderDrawQuad, HitTestDataSkipQuads) {
std::unique_ptr<HitTestDataProvider> hit_test_data_provider =
std::make_unique<HitTestDataProviderDrawQuad>(
- true /* should_ask_for_child_region */);
+ true /* should_ask_for_child_region */,
+ true /* root_accepts_events */);
constexpr gfx::Rect kFrameRect(0, 0, 1024, 768);
gfx::Rect child_rect(200, 100);
@@ -188,7 +190,8 @@ TEST(HitTestDataProviderDrawQuad, HitTestDataSkipQuads) {
TEST(HitTestDataProviderDrawQuad, HitTestDataBrowser) {
std::unique_ptr<HitTestDataProvider> hit_test_data_provider =
std::make_unique<HitTestDataProviderDrawQuad>(
- /*should_ask_for_child_region=*/false);
+ false /* should_ask_for_child_region */,
+ true /* root_accepts_events */);
constexpr gfx::Rect frame_rect(1024, 768);
SurfaceId child_surface_id = CreateChildSurfaceId(2);
@@ -227,4 +230,45 @@ TEST(HitTestDataProviderDrawQuad, HitTestDataBrowser) {
hit_test_region_list->regions[0].transform);
}
+// Test to ensure that we should set kHitTestIgnore flag for transparent
+// windows.
+TEST(HitTestDataProviderDrawQuad, HitTestDataTransparent) {
+ std::unique_ptr<HitTestDataProvider> hit_test_data_provider =
+ std::make_unique<HitTestDataProviderDrawQuad>(
+ true /* should_ask_for_child_region */,
+ false /* root_accepts_events */);
+
+ constexpr gfx::Rect frame_rect(1024, 768);
+ SurfaceId child_surface_id = CreateChildSurfaceId(2);
+ constexpr gfx::Rect child_rect(200, 100);
+ gfx::Transform child_to_parent_transform;
+ child_to_parent_transform.Translate(-200, -100);
+ auto pass = CreateRenderPassWithChildSurface(child_surface_id, frame_rect,
+ child_rect, gfx::Transform(),
+ child_to_parent_transform);
+ CompositorFrame compositor_frame =
+ CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build();
+ base::Optional<HitTestRegionList> hit_test_region_list =
+ hit_test_data_provider->GetHitTestData(compositor_frame);
+
+ EXPECT_EQ(HitTestRegionFlags::kHitTestMouse |
+ HitTestRegionFlags::kHitTestTouch |
+ HitTestRegionFlags::kHitTestIgnore,
+ hit_test_region_list->flags);
+ EXPECT_EQ(frame_rect, hit_test_region_list->bounds);
+ EXPECT_EQ(1u, hit_test_region_list->regions.size());
+ EXPECT_EQ(child_surface_id.frame_sink_id(),
+ hit_test_region_list->regions[0].frame_sink_id);
+ EXPECT_EQ(HitTestRegionFlags::kHitTestMouse |
+ HitTestRegionFlags::kHitTestTouch |
+ HitTestRegionFlags::kHitTestChildSurface |
+ HitTestRegionFlags::kHitTestAsk,
+ hit_test_region_list->regions[0].flags);
+ EXPECT_EQ(child_rect, hit_test_region_list->regions[0].rect);
+ gfx::Transform parent_to_child_transform;
+ EXPECT_TRUE(child_to_parent_transform.GetInverse(&parent_to_child_transform));
+ EXPECT_EQ(parent_to_child_transform,
+ hit_test_region_list->regions[0].transform);
+}
+
} // namespace viz
diff --git a/chromium/components/viz/common/BUILD.gn b/chromium/components/viz/common/BUILD.gn
index f03d0c6bc9a..06122b22ac4 100644
--- a/chromium/components/viz/common/BUILD.gn
+++ b/chromium/components/viz/common/BUILD.gn
@@ -64,6 +64,8 @@ viz_component("common") {
"gl_helper.h",
"gl_helper_scaling.cc",
"gl_helper_scaling.h",
+ "gl_scaler.cc",
+ "gl_scaler.h",
"gpu/context_cache_controller.cc",
"gpu/context_cache_controller.h",
"gpu/context_lost_observer.h",
@@ -88,6 +90,7 @@ viz_component("common") {
"quads/debug_border_draw_quad.h",
"quads/draw_quad.cc",
"quads/draw_quad.h",
+ "quads/frame_deadline.cc",
"quads/frame_deadline.h",
"quads/largest_draw_quad.cc",
"quads/largest_draw_quad.h",
@@ -184,7 +187,7 @@ viz_component("common") {
"gpu/vulkan_in_process_context_provider.h",
]
configs = [ "//third_party/vulkan:vulkan_config" ]
- deps += [ "//gpu/vulkan/init" ]
+ deps += [ "//gpu/vulkan" ]
}
if (is_win) {
@@ -206,6 +209,8 @@ viz_component("common") {
}
viz_source_set("unit_tests") {
+ # Not ready for jumbo compilation. Too much repeated test code.
+ never_build_jumbo = true
testonly = true
sources = [
"frame_sinks/begin_frame_args_unittest.cc",
@@ -213,6 +218,10 @@ viz_source_set("unit_tests") {
"frame_sinks/copy_output_util_unittest.cc",
"frame_sinks/delay_based_time_source_unittest.cc",
"gl_helper_unittest.cc",
+ "gl_scaler_shader_pixeltest.cc",
+ "gl_scaler_test_util.cc",
+ "gl_scaler_test_util.h",
+ "gl_scaler_unittest.cc",
"gpu/context_cache_controller_unittest.cc",
"quads/draw_quad_unittest.cc",
"quads/render_pass_unittest.cc",
@@ -238,6 +247,9 @@ viz_source_set("unit_tests") {
"//media",
"//testing/gmock",
"//testing/gtest",
+ "//ui/gfx",
+ "//ui/gfx:color_space",
+ "//ui/gfx/geometry",
]
}
diff --git a/chromium/components/viz/common/DEPS b/chromium/components/viz/common/DEPS
index 2026c4e9b22..639f7cb71d5 100644
--- a/chromium/components/viz/common/DEPS
+++ b/chromium/components/viz/common/DEPS
@@ -14,7 +14,7 @@ specific_include_rules = {
"+third_party/skia",
],
# DEPS for GLHelper and friends which are in the root common/ directory.
- "(yuv_readback|gl_helper).*\.(cc|h)": [
+ "(yuv_readback|gl_helper|gl_scaler).*\.(cc|h)": [
"+gpu/GLES2",
"+gpu/command_buffer/client",
"+gpu/command_buffer/common",
@@ -22,7 +22,7 @@ specific_include_rules = {
"+gpu/ipc/common",
"+third_party/skia",
],
- ".*_unittest\.cc": [
+ ".*(_unittest|_pixeltest)\.cc": [
"+cc/test",
"+gpu/ipc/gl_in_process_context.h",
"+media/base",
diff --git a/chromium/components/viz/common/display/renderer_settings.h b/chromium/components/viz/common/display/renderer_settings.h
index 1327e8607c5..9d16d67c69e 100644
--- a/chromium/components/viz/common/display/renderer_settings.h
+++ b/chromium/components/viz/common/display/renderer_settings.h
@@ -7,6 +7,7 @@
#include <stddef.h>
+#include "build/build_config.h"
#include "components/viz/common/viz_common_export.h"
#include "ui/gfx/geometry/size.h"
@@ -40,6 +41,11 @@ class VIZ_COMMON_EXPORT RendererSettings {
// The required minimum size for DrawQuad to apply Draw Occlusion on.
gfx::Size kMinimumDrawOcclusionSize = gfx::Size(60, 60);
+
+#if defined(OS_ANDROID)
+ // The screen size at renderer creation time.
+ gfx::Size initial_screen_size = gfx::Size(0, 0);
+#endif
};
} // namespace viz
diff --git a/chromium/components/viz/common/features.cc b/chromium/components/viz/common/features.cc
index 745c26bb950..f9c28763a00 100644
--- a/chromium/components/viz/common/features.cc
+++ b/chromium/components/viz/common/features.cc
@@ -23,10 +23,6 @@ const base::Feature kEnableSurfaceSynchronization{
"SurfaceSynchronization", base::FEATURE_DISABLED_BY_DEFAULT};
#endif
-// Enables DumpWithoutCrashing of surface invariants violations.
-const base::Feature kEnableInvariantsViolationLogging{
- "InvariantsViolationLogging", base::FEATURE_DISABLED_BY_DEFAULT};
-
// Enables running the display compositor as part of the viz service in the GPU
// process. This is also referred to as out-of-process display compositor
// (OOP-D).
@@ -55,11 +51,6 @@ bool IsSurfaceSynchronizationEnabled() {
base::FeatureList::IsEnabled(kVizDisplayCompositor);
}
-bool IsSurfaceInvariantsViolationLoggingEnabled() {
- return IsSurfaceSynchronizationEnabled() &&
- base::FeatureList::IsEnabled(kEnableInvariantsViolationLogging);
-}
-
bool IsVizHitTestingDrawQuadEnabled() {
return base::FeatureList::IsEnabled(kEnableVizHitTestDrawQuad) ||
base::FeatureList::IsEnabled(kVizDisplayCompositor);
@@ -71,12 +62,12 @@ bool IsVizHitTestingEnabled() {
}
bool IsVizHitTestingSurfaceLayerEnabled() {
- // TODO(riajiang): Check feature flag as well. https://crbug.com/804888
// TODO(riajiang): Check kVizDisplayCompositor feature when it works with
// that config.
- return base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseVizHitTestSurfaceLayer) ||
- base::FeatureList::IsEnabled(kEnableVizHitTestSurfaceLayer);
+ return (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseVizHitTestSurfaceLayer) ||
+ base::FeatureList::IsEnabled(kEnableVizHitTestSurfaceLayer)) &&
+ !IsVizHitTestingDrawQuadEnabled();
}
bool IsDrawOcclusionEnabled() {
diff --git a/chromium/components/viz/common/features.h b/chromium/components/viz/common/features.h
index f5f21df0018..1a257ec42b1 100644
--- a/chromium/components/viz/common/features.h
+++ b/chromium/components/viz/common/features.h
@@ -13,7 +13,6 @@ namespace features {
VIZ_COMMON_EXPORT extern const base::Feature kEnableDrawOcclusion;
VIZ_COMMON_EXPORT extern const base::Feature kEnableSurfaceSynchronization;
-VIZ_COMMON_EXPORT extern const base::Feature kEnableInvariantsViolationLogging;
VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTestDrawQuad;
VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTestSurfaceLayer;
VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaDeferredDisplayList;
@@ -22,7 +21,6 @@ VIZ_COMMON_EXPORT extern const base::Feature kVizDisplayCompositor;
VIZ_COMMON_EXPORT bool IsDrawOcclusionEnabled();
VIZ_COMMON_EXPORT bool IsSurfaceSynchronizationEnabled();
-VIZ_COMMON_EXPORT bool IsSurfaceInvariantsViolationLoggingEnabled();
VIZ_COMMON_EXPORT bool IsVizHitTestingDrawQuadEnabled();
VIZ_COMMON_EXPORT bool IsVizHitTestingEnabled();
VIZ_COMMON_EXPORT bool IsVizHitTestingSurfaceLayerEnabled();
diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source.cc b/chromium/components/viz/common/frame_sinks/begin_frame_source.cc
index 857aca0c98d..ff9b0b78779 100644
--- a/chromium/components/viz/common/frame_sinks/begin_frame_source.cc
+++ b/chromium/components/viz/common/frame_sinks/begin_frame_source.cc
@@ -187,9 +187,7 @@ 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()) {
+ if (interval.is_zero()) {
// TODO(brianderson): We should not be receiving 0 intervals.
interval = BeginFrameArgs::DefaultInterval();
}
@@ -198,12 +196,6 @@ void DelayBasedBeginFrameSource::OnUpdateVSyncParameters(
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_++;
diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source.h b/chromium/components/viz/common/frame_sinks/begin_frame_source.h
index 677ed18a87f..8b8a7756ea9 100644
--- a/chromium/components/viz/common/frame_sinks/begin_frame_source.h
+++ b/chromium/components/viz/common/frame_sinks/begin_frame_source.h
@@ -175,8 +175,6 @@ class VIZ_COMMON_EXPORT SyntheticBeginFrameSource : public BeginFrameSource {
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
@@ -198,7 +196,6 @@ class VIZ_COMMON_EXPORT BackToBackBeginFrameSource
// SyntheticBeginFrameSource implementation.
void OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) override {}
- void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override {}
// DelayBasedTimeSourceClient implementation.
void OnTimerTick() override;
@@ -232,7 +229,6 @@ class VIZ_COMMON_EXPORT DelayBasedBeginFrameSource
// SyntheticBeginFrameSource implementation.
void OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) override;
- void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override;
// DelayBasedTimeSourceClient implementation.
void OnTimerTick() override;
@@ -243,7 +239,6 @@ class VIZ_COMMON_EXPORT DelayBasedBeginFrameSource
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_;
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
index f9170155b4e..6d5633a5791 100644
--- a/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc
+++ b/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc
@@ -431,33 +431,6 @@ TEST_F(DelayBasedBeginFrameSourceTest, VSyncChanges) {
task_runner_->FastForwardTo(TicksFromMicroseconds(60000));
}
-TEST_F(DelayBasedBeginFrameSourceTest, AuthoritativeVSyncChanges) {
- 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_->FastForwardTo(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_->FastForwardTo(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_->FastForwardTo(TicksFromMicroseconds(60395));
-}
-
TEST_F(DelayBasedBeginFrameSourceTest, MultipleObservers) {
NiceMock<MockBeginFrameObserver> obs1, obs2;
diff --git a/chromium/components/viz/common/gl_helper_benchmark.cc b/chromium/components/viz/common/gl_helper_benchmark.cc
index b54d6ab7797..ec352dbad17 100644
--- a/chromium/components/viz/common/gl_helper_benchmark.cc
+++ b/chromium/components/viz/common/gl_helper_benchmark.cc
@@ -76,7 +76,6 @@ class GLHelperBenchmark : public testing::Test {
attributes, gpu::SharedMemoryLimits(),
nullptr, /* gpu_memory_buffer_manager */
nullptr, /* image_factory */
- nullptr /* gpu_channel_manager_delegate */,
base::ThreadTaskRunnerHandle::Get());
DCHECK_EQ(result, gpu::ContextResult::kSuccess);
gl_ = context_->GetImplementation();
diff --git a/chromium/components/viz/common/gl_helper_unittest.cc b/chromium/components/viz/common/gl_helper_unittest.cc
index 5395980abed..13f80a296ee 100644
--- a/chromium/components/viz/common/gl_helper_unittest.cc
+++ b/chromium/components/viz/common/gl_helper_unittest.cc
@@ -72,7 +72,6 @@ class GLHelperTest : public testing::Test {
attributes, gpu::SharedMemoryLimits(),
nullptr, /* gpu_memory_buffer_manager */
nullptr, /* image_factory */
- nullptr /* gpu_channel_manager_delegate */,
base::ThreadTaskRunnerHandle::Get());
DCHECK_EQ(result, gpu::ContextResult::kSuccess);
gl_ = context_->GetImplementation();
diff --git a/chromium/components/viz/common/gl_scaler.cc b/chromium/components/viz/common/gl_scaler.cc
new file mode 100644
index 00000000000..4c4ae200889
--- /dev/null
+++ b/chromium/components/viz/common/gl_scaler.cc
@@ -0,0 +1,809 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/gl_scaler.h"
+
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "gpu/GLES2/gl2chromium.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "ui/gfx/color_transform.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace viz {
+
+GLScaler::GLScaler(scoped_refptr<ContextProvider> context_provider)
+ : context_provider_(std::move(context_provider)) {
+ if (context_provider_) {
+ DCHECK(context_provider_->ContextGL());
+ context_provider_->AddObserver(this);
+ }
+}
+
+GLScaler::~GLScaler() {
+ OnContextLost(); // Ensures destruction in dependency order.
+}
+
+bool GLScaler::SupportsPreciseColorManagement() const {
+ if (!context_provider_) {
+ return false;
+ }
+ const gpu::Capabilities& caps = context_provider_->ContextCapabilities();
+ return caps.texture_half_float_linear && caps.color_buffer_half_float_rgba;
+}
+
+int GLScaler::GetMaxDrawBuffersSupported() const {
+ if (!context_provider_) {
+ return 0;
+ }
+
+ if (max_draw_buffers_ < 0) {
+ // Query the GL context for the multiple draw buffers extension and, if
+ // present, the actual platform-supported maximum.
+ GLES2Interface* const gl = context_provider_->ContextGL();
+ DCHECK(gl);
+ if (const auto* extensions = gl->GetString(GL_EXTENSIONS)) {
+ const std::string extensions_string =
+ " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
+ if (extensions_string.find(" GL_EXT_draw_buffers ") !=
+ std::string::npos) {
+ gl->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_);
+ }
+ }
+
+ if (max_draw_buffers_ < 1) {
+ max_draw_buffers_ = 1;
+ }
+ }
+
+ return max_draw_buffers_;
+}
+
+bool GLScaler::Configure(const Parameters& new_params) {
+ shader_programs_.clear();
+
+ if (!context_provider_) {
+ return false;
+ }
+ GLES2Interface* const gl = context_provider_->ContextGL();
+ DCHECK(gl);
+
+ params_ = new_params;
+
+ // Ensure the client has provided valid scaling vectors.
+ if (params_.scale_from.x() == 0 || params_.scale_from.y() == 0 ||
+ params_.scale_to.x() == 0 || params_.scale_to.y() == 0) {
+ // The caller computed invalid scale_from and/or scale_to values.
+ DVLOG(1) << __func__ << ": Invalid scaling vectors: scale_from="
+ << params_.scale_from.ToString()
+ << ", scale_to=" << params_.scale_to.ToString();
+ return false;
+ }
+
+ // Resolve the color spaces according to the rules described in the header
+ // file.
+ if (!params_.source_color_space.IsValid()) {
+ params_.source_color_space = gfx::ColorSpace::CreateSRGB();
+ }
+ if (!params_.output_color_space.IsValid()) {
+ params_.output_color_space = params_.source_color_space;
+ }
+
+ // Check that 16-bit half floats are supported if precise color management is
+ // being requested.
+ if (params_.enable_precise_color_management) {
+ if (!SupportsPreciseColorManagement()) {
+ DVLOG(1) << __func__
+ << ": GL context does not support the half-floats "
+ "required for precise color management.";
+ return false;
+ }
+ }
+
+ // Check that MRT support is available if certain export formats were
+ // specified in the Parameters.
+ if (params_.export_format == Parameters::ExportFormat::NV61 ||
+ params_.export_format ==
+ Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE) {
+ if (GetMaxDrawBuffersSupported() < 2) {
+ DVLOG(1) << __func__ << ": GL context does not support 2+ draw buffers.";
+ return false;
+ }
+ }
+
+ // Color space transformation is meaningless when using the deinterleaver.
+ if (params_.export_format ==
+ Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE &&
+ params_.source_color_space != params_.output_color_space) {
+ NOTIMPLEMENTED();
+ return false;
+ }
+
+ // Check that one of the two implemented output swizzles has been specified.
+ for (GLenum s : params_.swizzle) {
+ if (s != GL_RGBA && s != GL_BGRA_EXT) {
+ NOTIMPLEMENTED();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool GLScaler::ScaleToMultipleOutputs(GLuint src_texture,
+ const gfx::Size& src_texture_size,
+ const gfx::Vector2dF& src_offset,
+ GLuint dest_texture_0,
+ GLuint dest_texture_1,
+ const gfx::Rect& output_rect) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool GLScaler::ComputeRegionOfInfluence(const gfx::Size& src_texture_size,
+ const gfx::Vector2dF& src_offset,
+ const gfx::Rect& output_rect,
+ gfx::Rect* sampling_rect,
+ gfx::Vector2dF* offset) const {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+// static
+bool GLScaler::ParametersHasSameScaleRatio(const GLScaler::Parameters& params,
+ const gfx::Vector2d& from,
+ const gfx::Vector2d& to) {
+ // Returns true iff a_num/a_denom == b_num/b_denom.
+ const auto AreRatiosEqual = [](int32_t a_num, int32_t a_denom, int32_t b_num,
+ int32_t b_denom) -> bool {
+ // The math (for each dimension):
+ // If: a_num/a_denom == b_num/b_denom
+ // Then: a_num*b_denom == b_num*a_denom
+ //
+ // ...and cast to int64_t to guarantee no overflow from the multiplications.
+ return (static_cast<int64_t>(a_num) * b_denom) ==
+ (static_cast<int64_t>(b_num) * a_denom);
+ };
+
+ return AreRatiosEqual(params.scale_from.x(), params.scale_to.x(), from.x(),
+ to.x()) &&
+ AreRatiosEqual(params.scale_from.y(), params.scale_to.y(), from.y(),
+ to.y());
+}
+
+void GLScaler::OnContextLost() {
+ // The destruction order here is important due to data dependencies.
+ shader_programs_.clear();
+ if (context_provider_) {
+ context_provider_->RemoveObserver(this);
+ context_provider_ = nullptr;
+ }
+}
+
+GLScaler::ShaderProgram* GLScaler::GetShaderProgram(
+ Shader shader,
+ GLint texture_format,
+ const gfx::ColorTransform* color_transform,
+ const GLenum swizzle[2]) {
+ const ShaderCacheKey key{
+ shader,
+ texture_format,
+ color_transform ? color_transform->GetSrcColorSpace() : gfx::ColorSpace(),
+ color_transform ? color_transform->GetDstColorSpace() : gfx::ColorSpace(),
+ swizzle[0],
+ swizzle[1]};
+ auto it = shader_programs_.find(key);
+ if (it == shader_programs_.end()) {
+ GLES2Interface* const gl = context_provider_->ContextGL();
+ DCHECK(gl);
+ it = shader_programs_
+ .emplace(std::piecewise_construct, std::forward_as_tuple(key),
+ std::forward_as_tuple(gl, shader, texture_format,
+ color_transform, swizzle))
+ .first;
+ }
+ return &it->second;
+}
+
+GLScaler::Parameters::Parameters() = default;
+GLScaler::Parameters::~Parameters() = default;
+
+// static
+const GLfloat GLScaler::ShaderProgram::kVertexAttributes[16] = {
+ -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0
+ 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1
+ -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2
+ 1.0f, 1.0f, 1.0f, 1.0f, // vertex 3
+};
+
+GLScaler::ShaderProgram::ShaderProgram(
+ gpu::gles2::GLES2Interface* gl,
+ GLScaler::Shader shader,
+ GLint texture_format,
+ const gfx::ColorTransform* color_transform,
+ const GLenum swizzle[2])
+ : gl_(gl),
+ shader_(shader),
+ texture_format_(texture_format),
+ program_(gl_->CreateProgram()) {
+ DCHECK(program_);
+
+ std::basic_ostringstream<GLchar> vertex_header;
+ std::basic_ostringstream<GLchar> fragment_directives;
+ std::basic_ostringstream<GLchar> fragment_header;
+ std::basic_ostringstream<GLchar> shared_variables;
+ std::basic_ostringstream<GLchar> vertex_main;
+ std::basic_ostringstream<GLchar> fragment_main;
+
+ vertex_header
+ << ("precision highp float;\n"
+ "attribute vec2 a_position;\n"
+ "attribute vec2 a_texcoord;\n"
+ "uniform vec4 src_rect;\n");
+
+ fragment_header << "precision mediump float;\n";
+ if (texture_format_ == GL_RGBA16F_EXT) {
+ fragment_header << "precision mediump sampler2D;\n";
+ } else if (texture_format_ == GL_RGBA) {
+ fragment_header << "precision lowp sampler2D;\n";
+ } else {
+ NOTIMPLEMENTED();
+ }
+ fragment_header << "uniform sampler2D s_texture;\n";
+
+ if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) {
+ const std::string& source = color_transform->GetShaderSource();
+ // Assumption: gfx::ColorTransform::GetShaderSource() should provide a
+ // function named DoColorConversion() that takes a vec3 argument and returns
+ // a vec3.
+ DCHECK_NE(source.find("DoColorConversion"), std::string::npos);
+ fragment_header << source;
+ }
+
+ vertex_main
+ << (" gl_Position = vec4(a_position, 0.0, 1.0);\n"
+ " vec2 texcoord = src_rect.xy + a_texcoord * src_rect.zw;\n");
+
+ switch (shader_) {
+ case Shader::BILINEAR:
+ shared_variables << "varying highp vec2 v_texcoord;\n";
+ vertex_main << " v_texcoord = texcoord;\n";
+ fragment_main << " vec4 sample = texture2D(s_texture, v_texcoord);\n";
+ if (color_transform) {
+ fragment_main << " sample.rgb = DoColorConversion(sample.rgb);\n";
+ }
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " sample.rb = sample.br;\n";
+ }
+ fragment_main << " gl_FragColor = sample;\n";
+ break;
+
+ case Shader::BILINEAR2:
+ // This is equivialent to two passes of the BILINEAR shader above. It can
+ // be used to scale an image down 1.0x-2.0x in either dimension, or
+ // exactly 4x.
+ shared_variables << "varying highp vec4 v_texcoords;\n";
+ vertex_header << "uniform vec2 scaling_vector;\n";
+ vertex_main
+ << (" vec2 step = scaling_vector / 4.0;\n"
+ " v_texcoords.xy = texcoord + step;\n"
+ " v_texcoords.zw = texcoord - step;\n");
+ fragment_main
+ << (" vec4 blended = (texture2D(s_texture, v_texcoords.xy) +\n"
+ " texture2D(s_texture, v_texcoords.zw)) /\n"
+ " 2.0;\n");
+ if (color_transform) {
+ fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
+ }
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " blended.rb = blended.br;\n";
+ }
+ fragment_main << " gl_FragColor = blended;\n";
+ break;
+
+ case Shader::BILINEAR3:
+ // This is kind of like doing 1.5 passes of the BILINEAR shader. It can be
+ // used to scale an image down 1.5x-3.0x, or exactly 6x.
+ shared_variables
+ << ("varying highp vec4 v_texcoords0;\n"
+ "varying highp vec2 v_texcoords1;\n");
+ vertex_header << "uniform vec2 scaling_vector;\n";
+ vertex_main
+ << (" vec2 step = scaling_vector / 3.0;\n"
+ " v_texcoords0.xy = texcoord + step;\n"
+ " v_texcoords0.zw = texcoord;\n"
+ " v_texcoords1 = texcoord - step;\n");
+ fragment_main
+ << (" vec4 blended = (texture2D(s_texture, v_texcoords0.xy) +\n"
+ " texture2D(s_texture, v_texcoords0.zw) +\n"
+ " texture2D(s_texture, v_texcoords1)) / 3.0;\n");
+ if (color_transform) {
+ fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
+ }
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " blended.rb = blended.br;\n";
+ }
+ fragment_main << " gl_FragColor = blended;\n";
+ break;
+
+ case Shader::BILINEAR4:
+ // This is equivialent to three passes of the BILINEAR shader above. It
+ // can be used to scale an image down 2.0x-4.0x or exactly 8x.
+ shared_variables << "varying highp vec4 v_texcoords[2];\n";
+ vertex_header << "uniform vec2 scaling_vector;\n";
+ vertex_main
+ << (" vec2 step = scaling_vector / 8.0;\n"
+ " v_texcoords[0].xy = texcoord - step * 3.0;\n"
+ " v_texcoords[0].zw = texcoord - step;\n"
+ " v_texcoords[1].xy = texcoord + step;\n"
+ " v_texcoords[1].zw = texcoord + step * 3.0;\n");
+ fragment_main
+ << (" vec4 blended = (\n"
+ " texture2D(s_texture, v_texcoords[0].xy) +\n"
+ " texture2D(s_texture, v_texcoords[0].zw) +\n"
+ " texture2D(s_texture, v_texcoords[1].xy) +\n"
+ " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
+ if (color_transform) {
+ fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
+ }
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " blended.rb = blended.br;\n";
+ }
+ fragment_main << " gl_FragColor = blended;\n";
+ break;
+
+ case Shader::BILINEAR2X2:
+ // This is equivialent to four passes of the BILINEAR shader above, two in
+ // each dimension. It can be used to scale an image down 1.0x-2.0x in both
+ // X and Y directions. Or, it could be used to scale an image down by
+ // exactly 4x in both dimensions.
+ shared_variables << "varying highp vec4 v_texcoords[2];\n";
+ vertex_header << "uniform vec2 scaling_vector;\n";
+ vertex_main
+ << (" vec2 step = scaling_vector / 4.0;\n"
+ " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
+ " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
+ " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
+ " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
+ fragment_main
+ << (" vec4 blended = (\n"
+ " texture2D(s_texture, v_texcoords[0].xy) +\n"
+ " texture2D(s_texture, v_texcoords[0].zw) +\n"
+ " texture2D(s_texture, v_texcoords[1].xy) +\n"
+ " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
+ if (color_transform) {
+ fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
+ }
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " blended.rb = blended.br;\n";
+ }
+ fragment_main << " gl_FragColor = blended;\n";
+ break;
+
+ case Shader::BICUBIC_UPSCALE:
+ // When scaling up, 4 texture reads are necessary. However, some
+ // instructions can be saved because the parameter passed to the bicubic
+ // function will be in a known range. Also, when sampling the bicubic
+ // function like this, the sum is always exactly one, so normalization can
+ // be skipped as well.
+ shared_variables << "varying highp vec2 v_texcoord;\n";
+ vertex_main << " v_texcoord = texcoord;\n";
+ fragment_header
+ << ("uniform highp vec2 src_pixelsize;\n"
+ "uniform highp vec2 scaling_vector;\n"
+ "const float a = -0.5;\n"
+ // This function is equivialent to calling the bicubic
+ // function with x-1, x, 1-x and 2-x (assuming
+ // 0 <= x < 1)
+ "vec4 filt4(float x) {\n"
+ " return vec4(x * x * x, x * x, x, 1) *\n"
+ " mat4( a, -2.0 * a, a, 0.0,\n"
+ " a + 2.0, -a - 3.0, 0.0, 1.0,\n"
+ " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n"
+ " -a, a, 0.0, 0.0);\n"
+ "}\n"
+ "mat4 pixels_x(highp vec2 pos, highp vec2 step) {\n"
+ " return mat4(texture2D(s_texture, pos - step),\n"
+ " texture2D(s_texture, pos),\n"
+ " texture2D(s_texture, pos + step),\n"
+ " texture2D(s_texture, pos + step * 2.0));\n"
+ "}\n");
+ fragment_main
+ << (" highp vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
+ " scaling_vector / 2.0;\n"
+ " highp float frac = fract(dot(pixel_pos, scaling_vector));\n"
+ " highp vec2 base = \n"
+ " (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
+ " highp vec2 step = scaling_vector / src_pixelsize;\n"
+ " vec4 blended = pixels_x(base, step) * filt4(frac);\n");
+ if (color_transform) {
+ fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
+ }
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " blended.rb = blended.br;\n";
+ }
+ fragment_main << " gl_FragColor = blended;\n";
+ break;
+
+ case Shader::BICUBIC_HALF_1D:
+ // This scales down an image by exactly half in one dimension. The
+ // bilinear lookup reduces the number of texture reads from 8 to 4.
+ shared_variables << "varying highp vec4 v_texcoords[2];\n";
+ vertex_header
+ << ("uniform vec2 scaling_vector;\n"
+ "const float CenterDist = 99.0 / 140.0;\n"
+ "const float LobeDist = 11.0 / 4.0;\n");
+ vertex_main
+ << (" vec2 step = scaling_vector / 2.0;\n"
+ " v_texcoords[0].xy = texcoord - LobeDist * step;\n"
+ " v_texcoords[0].zw = texcoord - CenterDist * step;\n"
+ " v_texcoords[1].xy = texcoord + CenterDist * step;\n"
+ " v_texcoords[1].zw = texcoord + LobeDist * step;\n");
+ fragment_header
+ << ("const float CenterWeight = 35.0 / 64.0;\n"
+ "const float LobeWeight = -3.0 / 64.0;\n");
+ fragment_main
+ << (" vec4 blended = \n"
+ // Lobe pixels
+ " (texture2D(s_texture, v_texcoords[0].xy) +\n"
+ " texture2D(s_texture, v_texcoords[1].zw)) *\n"
+ " LobeWeight +\n"
+ // Center pixels
+ " (texture2D(s_texture, v_texcoords[0].zw) +\n"
+ " texture2D(s_texture, v_texcoords[1].xy)) *\n"
+ " CenterWeight;\n");
+ if (color_transform) {
+ fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
+ }
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " blended.rb = blended.br;\n";
+ }
+ fragment_main << " gl_FragColor = blended;\n";
+ break;
+
+ case Shader::PLANAR_CHANNEL_0:
+ case Shader::PLANAR_CHANNEL_1:
+ case Shader::PLANAR_CHANNEL_2:
+ case Shader::PLANAR_CHANNEL_3: {
+ // Select one color channel, and pack 4 pixels into one output quad. See
+ // header file for diagram.
+ shared_variables << "varying highp vec4 v_texcoords[2];\n";
+ vertex_header << "uniform vec2 scaling_vector;\n";
+ vertex_main
+ << (" vec2 step = scaling_vector / 4.0;\n"
+ " v_texcoords[0].xy = texcoord - step * 1.5;\n"
+ " v_texcoords[0].zw = texcoord - step * 0.5;\n"
+ " v_texcoords[1].xy = texcoord + step * 0.5;\n"
+ " v_texcoords[1].zw = texcoord + step * 1.5;\n");
+ std::basic_string<GLchar> convert_open;
+ std::basic_string<GLchar> convert_close;
+ if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) {
+ convert_open = "DoColorConversion(";
+ convert_close = ".rgb)";
+ }
+ const char selector = "rgba"[static_cast<int>(shader_) -
+ static_cast<int>(Shader::PLANAR_CHANNEL_0)];
+ fragment_main << " vec4 packed_quad = vec4(\n"
+ << " " << convert_open
+ << "texture2D(s_texture, v_texcoords[0].xy)"
+ << convert_close << '.' << selector << ",\n"
+ << " " << convert_open
+ << "texture2D(s_texture, v_texcoords[0].zw)"
+ << convert_close << '.' << selector << ",\n"
+ << " " << convert_open
+ << "texture2D(s_texture, v_texcoords[1].xy)"
+ << convert_close << '.' << selector << ",\n"
+ << " " << convert_open
+ << "texture2D(s_texture, v_texcoords[1].zw)"
+ << convert_close << '.' << selector << ");\n";
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " packed_quad.rb = packed_quad.br;\n";
+ }
+ fragment_main << " gl_FragColor = packed_quad;\n";
+ break;
+ }
+
+ case Shader::I422_NV61_MRT:
+ // I422 sampling, delivered via two output textures (NV61 format). See
+ // header file for diagram.
+ shared_variables << "varying highp vec4 v_texcoords[2];\n";
+ vertex_header << "uniform vec2 scaling_vector;\n";
+ vertex_main
+ << (" vec2 step = scaling_vector / 4.0;\n"
+ " v_texcoords[0].xy = texcoord - step * 1.5;\n"
+ " v_texcoords[0].zw = texcoord - step * 0.5;\n"
+ " v_texcoords[1].xy = texcoord + step * 0.5;\n"
+ " v_texcoords[1].zw = texcoord + step * 1.5;\n");
+ fragment_directives << "#extension GL_EXT_draw_buffers : enable\n";
+ fragment_main
+ << (" vec3 pixel0 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
+ " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
+ " vec3 pixel01 = (pixel0 + pixel1) / 2.0;\n"
+ " vec3 pixel2 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
+ " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
+ " vec3 pixel23 = (pixel2 + pixel3) / 2.0;\n");
+ if (color_transform) {
+ fragment_main
+ << (" pixel0 = DoColorConversion(pixel0);\n"
+ " pixel1 = DoColorConversion(pixel1);\n"
+ " pixel01 = DoColorConversion(pixel01);\n"
+ " pixel2 = DoColorConversion(pixel2);\n"
+ " pixel3 = DoColorConversion(pixel3);\n"
+ " pixel23 = DoColorConversion(pixel23);\n");
+ }
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main
+ << (" gl_FragData[0] = \n"
+ " vec4(pixel2.r, pixel1.r, pixel0.r, pixel3.r);\n");
+ } else {
+ fragment_main
+ << (" gl_FragData[0] = \n"
+ " vec4(pixel0.r, pixel1.r, pixel2.r, pixel3.r);\n");
+ }
+ if (swizzle[1] == GL_BGRA_EXT) {
+ fragment_main
+ << (" gl_FragData[1] = \n"
+ " vec4(pixel23.g, pixel01.b, pixel01.g, pixel23.b);\n");
+ } else {
+ fragment_main
+ << (" gl_FragData[1] = \n"
+ " vec4(pixel01.g, pixel01.b, pixel23.g, pixel23.b);\n");
+ }
+ break;
+
+ case Shader::DEINTERLEAVE_PAIRWISE_MRT:
+ // Sample two pixels and unswizzle them. There's no need to do vertical
+ // scaling with math, since the bilinear interpolation in the sampler
+ // takes care of that.
+ shared_variables << "varying highp vec4 v_texcoords;\n";
+ vertex_header << "uniform vec2 scaling_vector;\n";
+ vertex_main
+ << (" vec2 step = scaling_vector / 2.0;\n"
+ " v_texcoords.xy = texcoord - step * 0.5;\n"
+ " v_texcoords.zw = texcoord + step * 0.5;\n");
+ fragment_directives << "#extension GL_EXT_draw_buffers : enable\n";
+ DCHECK(!color_transform);
+ fragment_main
+ << (" vec4 lo_uvuv = texture2D(s_texture, v_texcoords.xy);\n"
+ " vec4 hi_uvuv = texture2D(s_texture, v_texcoords.zw);\n"
+ " vec4 uuuu = vec4(lo_uvuv.rb, hi_uvuv.rb);\n"
+ " vec4 vvvv = vec4(lo_uvuv.ga, hi_uvuv.ga);\n");
+ if (swizzle[0] == GL_BGRA_EXT) {
+ fragment_main << " uuuu.rb = uuuu.br;\n";
+ }
+ fragment_main << " gl_FragData[0] = uuuu;\n";
+ if (swizzle[1] == GL_BGRA_EXT) {
+ fragment_main << " vvvv.rb = vvvv.br;\n";
+ }
+ fragment_main << " gl_FragData[1] = vvvv;\n";
+ break;
+ }
+
+ // Helper function to compile the shader source and log the GLSL compiler's
+ // results.
+ const auto CompileShaderFromSource =
+ [](GLES2Interface* gl, const std::basic_string<GLchar>& source,
+ GLenum type) -> GLuint {
+ VLOG(2) << __func__ << ": Compiling shader " << type
+ << " with source:" << std::endl
+ << source;
+ const GLuint shader = gl->CreateShader(type);
+ const GLchar* source_data = source.data();
+ const GLint length = base::checked_cast<GLint>(source.size());
+ gl->ShaderSource(shader, 1, &source_data, &length);
+ gl->CompileShader(shader);
+ GLint compile_status = GL_FALSE;
+ gl->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
+
+ // Fetch logs and forward them to the system logger. If compilation failed,
+ // clean-up and return 0 for error.
+ if (compile_status != GL_TRUE || VLOG_IS_ON(2)) {
+ GLint log_length = 0;
+ gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+ std::string log;
+ if (log_length > 0) {
+ std::unique_ptr<GLchar[]> tmp(new GLchar[log_length]);
+ GLsizei returned_log_length = 0;
+ gl->GetShaderInfoLog(shader, log_length, &returned_log_length,
+ tmp.get());
+ log.assign(tmp.get(), returned_log_length);
+ }
+ if (log.empty()) {
+ log = "<<NO LOG>>";
+ }
+ if (compile_status != GL_TRUE) {
+ LOG(ERROR) << __func__ << ": Compilation of shader " << type
+ << " failed:" << std::endl
+ << log;
+ gl->DeleteShader(shader);
+ return 0;
+ }
+ VLOG(2) << __func__ << ": Compilation of shader " << type
+ << " succeeded:" << std::endl
+ << log;
+ }
+ return shader;
+ };
+
+ // Compile the vertex shader and attach it to the program.
+ const std::string shared_variables_str = shared_variables.str();
+ const GLuint vertex_shader =
+ CompileShaderFromSource(gl_,
+ vertex_header.str() + shared_variables_str +
+ "void main() {\n" + vertex_main.str() + "}\n",
+ GL_VERTEX_SHADER);
+ if (vertex_shader == 0) {
+ return;
+ }
+ gl_->AttachShader(program_, vertex_shader);
+ gl_->DeleteShader(vertex_shader);
+
+ // Compile the fragment shader and attach to |program_|.
+ const GLuint fragment_shader = CompileShaderFromSource(
+ gl_,
+ fragment_directives.str() + fragment_header.str() + shared_variables_str +
+ "void main() {\n" + fragment_main.str() + "}\n",
+ GL_FRAGMENT_SHADER);
+ if (fragment_shader == 0) {
+ return;
+ }
+ gl_->AttachShader(program_, fragment_shader);
+ gl_->DeleteShader(fragment_shader);
+
+ // Link the program.
+ gl_->LinkProgram(program_);
+ GLint link_status = GL_FALSE;
+ gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
+ if (link_status != GL_TRUE) {
+ LOG(ERROR) << "Failed to link shader program.";
+ return;
+ }
+
+#define DCHECK_RESOLVED_LOCATION(member) \
+ DCHECK(member != -1 || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) \
+ << "Failed to get " #member " in program, or GPU process crashed."
+
+ // Resolve the locations of the global variables.
+ position_location_ = gl_->GetAttribLocation(program_, "a_position");
+ DCHECK_RESOLVED_LOCATION(position_location_);
+ texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord");
+ DCHECK_RESOLVED_LOCATION(texcoord_location_);
+ texture_location_ = gl_->GetUniformLocation(program_, "s_texture");
+ DCHECK_RESOLVED_LOCATION(texture_location_);
+ src_rect_location_ = gl_->GetUniformLocation(program_, "src_rect");
+ DCHECK_RESOLVED_LOCATION(src_rect_location_);
+ switch (shader_) {
+ case Shader::BILINEAR:
+ break;
+
+ case Shader::BILINEAR2:
+ case Shader::BILINEAR3:
+ case Shader::BILINEAR4:
+ case Shader::BILINEAR2X2:
+ case Shader::BICUBIC_HALF_1D:
+ case Shader::PLANAR_CHANNEL_0:
+ case Shader::PLANAR_CHANNEL_1:
+ case Shader::PLANAR_CHANNEL_2:
+ case Shader::PLANAR_CHANNEL_3:
+ case Shader::I422_NV61_MRT:
+ case Shader::DEINTERLEAVE_PAIRWISE_MRT:
+ scaling_vector_location_ =
+ gl_->GetUniformLocation(program_, "scaling_vector");
+ DCHECK_RESOLVED_LOCATION(scaling_vector_location_);
+ break;
+
+ case Shader::BICUBIC_UPSCALE:
+ src_pixelsize_location_ =
+ gl_->GetUniformLocation(program_, "src_pixelsize");
+ DCHECK_RESOLVED_LOCATION(src_pixelsize_location_);
+ scaling_vector_location_ =
+ gl_->GetUniformLocation(program_, "scaling_vector");
+ DCHECK_RESOLVED_LOCATION(scaling_vector_location_);
+ break;
+ }
+
+#undef DCHECK_RESOLVED_LOCATION
+}
+
+GLScaler::ShaderProgram::~ShaderProgram() {
+ gl_->DeleteProgram(program_);
+}
+
+void GLScaler::ShaderProgram::UseProgram(const gfx::Size& src_texture_size,
+ const gfx::RectF& src_rect,
+ const gfx::Size& dst_size,
+ GLScaler::Axis primary_axis,
+ bool flip_y) {
+ gl_->UseProgram(program_);
+
+ // OpenGL defines the last parameter to VertexAttribPointer as type
+ // "const GLvoid*" even though it is actually an offset into the buffer
+ // object's data store and not a pointer to the client's address space.
+ const void* offsets[2] = {nullptr,
+ reinterpret_cast<const void*>(2 * sizeof(GLfloat))};
+
+ gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE,
+ 4 * sizeof(GLfloat), offsets[0]);
+ gl_->EnableVertexAttribArray(position_location_);
+
+ gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE,
+ 4 * sizeof(GLfloat), offsets[1]);
+ gl_->EnableVertexAttribArray(texcoord_location_);
+
+ // Always sample from the first texture unit.
+ gl_->Uniform1i(texture_location_, 0);
+
+ // Convert |src_rect| from pixel coordinates to texture coordinates. The
+ // source texture coordinates are in the range [0.0,1.0] for each dimension,
+ // but the sampling rect may slightly "spill" outside that range (e.g., for
+ // scaler overscan).
+ GLfloat src_rect_texcoord[4] = {
+ src_rect.x() / src_texture_size.width(),
+ src_rect.y() / src_texture_size.height(),
+ src_rect.width() / src_texture_size.width(),
+ src_rect.height() / src_texture_size.height(),
+ };
+ if (flip_y) {
+ src_rect_texcoord[1] += src_rect_texcoord[3];
+ src_rect_texcoord[3] *= -1.0f;
+ }
+ gl_->Uniform4fv(src_rect_location_, 1, src_rect_texcoord);
+
+ // Set shader-specific uniform inputs. The |scaling_vector| is the ratio of
+ // the number of source pixels sampled per dest pixels output. It is used by
+ // the shader programs to locate distinct texels from the source texture, and
+ // sample them at the appropriate offset to produce each output texel.
+ switch (shader_) {
+ case Shader::BILINEAR:
+ break;
+
+ case Shader::BILINEAR2:
+ case Shader::BILINEAR3:
+ case Shader::BILINEAR4:
+ case Shader::BICUBIC_HALF_1D:
+ case Shader::PLANAR_CHANNEL_0:
+ case Shader::PLANAR_CHANNEL_1:
+ case Shader::PLANAR_CHANNEL_2:
+ case Shader::PLANAR_CHANNEL_3:
+ case Shader::I422_NV61_MRT:
+ case Shader::DEINTERLEAVE_PAIRWISE_MRT:
+ switch (primary_axis) {
+ case HORIZONTAL:
+ gl_->Uniform2f(scaling_vector_location_,
+ src_rect_texcoord[2] / dst_size.width(), 0.0);
+ break;
+ case VERTICAL:
+ gl_->Uniform2f(scaling_vector_location_, 0.0,
+ src_rect_texcoord[3] / dst_size.height());
+ break;
+ }
+ break;
+
+ case Shader::BILINEAR2X2:
+ gl_->Uniform2f(scaling_vector_location_,
+ src_rect_texcoord[2] / dst_size.width(),
+ src_rect_texcoord[3] / dst_size.height());
+ break;
+
+ case Shader::BICUBIC_UPSCALE:
+ gl_->Uniform2f(src_pixelsize_location_, src_texture_size.width(),
+ src_texture_size.height());
+ // For this shader program, the |scaling_vector| has an alternate meaning:
+ // It is only being used to select whether bicubic sampling is stepped in
+ // the X or the Y direction.
+ gl_->Uniform2f(scaling_vector_location_,
+ primary_axis == HORIZONTAL ? 1.0 : 0.0,
+ primary_axis == VERTICAL ? 1.0 : 0.0);
+ break;
+ }
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gl_scaler.h b/chromium/components/viz/common/gl_scaler.h
new file mode 100644
index 00000000000..67f3ad514b1
--- /dev/null
+++ b/chromium/components/viz/common/gl_scaler.h
@@ -0,0 +1,391 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_GL_SCALER_H_
+#define COMPONENTS_VIZ_COMMON_GL_SCALER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <tuple>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "components/viz/common/gpu/context_lost_observer.h"
+#include "components/viz/common/viz_common_export.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/vector2d.h"
+
+namespace gfx {
+class ColorTransform;
+class Vector2dF;
+class Rect;
+class RectF;
+class Size;
+} // namespace gfx
+
+namespace viz {
+
+class ContextProvider;
+
+// A high-performance texture scaler for use with an OpenGL ES 2.0 context. It
+// can be configured to operate at different quality levels, manages/converts
+// color spaces, and optionally re-arranges/formats data in output textures for
+// use with more-efficient texture readback pipelines.
+class VIZ_COMMON_EXPORT GLScaler : public ContextLostObserver {
+ public:
+ struct VIZ_COMMON_EXPORT Parameters {
+ // Relative scale from/to factors. Both of these must be non-zero.
+ gfx::Vector2d scale_from = gfx::Vector2d(1, 1);
+ gfx::Vector2d scale_to = gfx::Vector2d(1, 1);
+
+ // The color space of the source texture and the desired color space of the
+ // output texture. If |source_color_space| is not set (or invalid), sRGB is
+ // assumed. If |output_color_space| is not set (or invalid), the source
+ // color space is assumed.
+ gfx::ColorSpace source_color_space;
+ gfx::ColorSpace output_color_space;
+
+ // Enable color management heuristics, using higher precision texture and
+ // gamma-aware scaling?
+ //
+ // When disabled, the gamma of the source color space and other concerns are
+ // ignored and 8-bit precision is used.
+ //
+ // When enabled, scaling occurs in a linear color space with 16-bit floats.
+ // This produces excellent results for virtually all color spaces while
+ // typically requiring twice the memory and execution resources. The caller
+ // must ensure the GL context supports the use of GL_RGBA16F format
+ // textures.
+ //
+ // Relevant reading: http://www.ericbrasseur.org/gamma.html
+ bool enable_precise_color_management = false;
+
+ // Selects the trade-off between quality and speed.
+ enum class Quality : int8_t {
+ // Bilinear single pass. Fastest possible. Do not use this unless the GL
+ // implementation is so slow that the other quality options won't work.
+ FAST,
+
+ // Bilinear upscale + N * 50% bilinear downscales. This is still fast
+ // enough for general-purpose use, and image quality is nearly as good as
+ // BEST when downscaling.
+ GOOD,
+
+ // Bicubic upscale + N * 50% bicubic downscales. Produces very good
+ // quality scaled images, but it's 2-8x slower than the "GOOD" quality.
+ BEST,
+ } quality = Quality::GOOD;
+
+ // Is the source texture Y-flipped (i.e., the origin is the lower-left
+ // corner and not the upper-left corner)? Most GL textures are Y-flipped.
+ // This information is required so that the scaler can correctly compute the
+ // sampling region.
+ bool is_flipped_source = true;
+
+ // Should the output be vertically flipped? Usually, this is used when the
+ // source is not Y-flipped, but the destination texture needs to be. Or, it
+ // can be used to draw the final output upside-down to avoid having to copy
+ // the rows in reverse order after a glReadPixels().
+ bool flip_output = false;
+
+ // Optionally rearrange the image data for export. Generally, this is used
+ // to make later readback steps more efficient (e.g., using glReadPixels()
+ // will produce the raw bytes in their correct locations).
+ //
+ // Output textures are assumed to be using one of the 4-channel RGBA
+ // formats. While it may be more "proper" to use a single-component texture
+ // format for the planar-oriented image data, not all GL implementations
+ // support the use of those formats. However, all must support at least
+ // GL_RGBA. Therefore, each RGBA pixel is treated as a generic "vec4" (a
+ // quad of values).
+ //
+ // When using this feature, it is usually necessary to adjust the
+ // |output_rect| passed to Scale() or ScaleToMultipleOutputs(). See notes
+ // below.
+ enum class ExportFormat : int8_t {
+ // Do not rearrange the image data:
+ //
+ // (interleaved quads) (interleaved quads)
+ // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
+ // RGBA RGBA RGBA RGBA --> RGBA RGBA RGBA RGBA
+ // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
+ INTERLEAVED_QUADS,
+
+ // Select one color channel, packing each of 4 pixels' values into the 4
+ // elements of one output quad.
+ //
+ // For example, for CHANNEL_0:
+ //
+ // (interleaved quads) (channel 0)
+ // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA RRRR RRRR
+ // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA --> RRRR RRRR
+ // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA RRRR RRRR
+ //
+ // Note: Because of this packing, the horizontal coordinates of the
+ // |output_rect| used with Scale() should be divided by 4.
+ CHANNEL_0,
+ CHANNEL_1,
+ CHANNEL_2,
+ CHANNEL_3,
+
+ // I422 sampling, delivered via two output textures (NV61 format): The
+ // first texture is produced the same as CHANNEL_0, while the second
+ // texture contains CHANNEL_1 and CHANNEL_2 at half-width interleaved and
+ // full-height. For example, if this is combined with RGB→YUV color space
+ // conversion:
+ //
+ // (interleaved quads)
+ // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
+ // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
+ // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
+ // |
+ // | (luma plane) (chroma, interleaved)
+ // | YYYY YYYY UVUV UVUV
+ // +---> { YYYY YYYY + UVUV UVUV }
+ // YYYY YYYY UVUV UVUV
+ //
+ // Note: Because of this packing, the horizontal coordinates of the
+ // |output_rect| used with ScaleToMultipleOutputs() should be divided by
+ // 4.
+ // Note 2: This requires a GL context that supports multiple render
+ // targets with at least two draw buffers.
+ NV61,
+
+ // Deinterleave into two output textures.
+ //
+ // UVUV UVUV UUUU VVVV
+ // UVUV UVUV --> { UUUU + VVVV }
+ // UVUV UVUV UUUU VVVV
+ //
+ // Note: Because of this packing, the horizontal coordinates of the
+ // |output_rect| used with ScaleToMultipleOutputs() should be divided by
+ // 2.
+ // Note 2: This requires a GL context that supports multiple render
+ // targets with at least two draw buffers.
+ DEINTERLEAVE_PAIRWISE,
+ } export_format = ExportFormat::INTERLEAVED_QUADS;
+
+ // Optionally swizzle the ordering of the values in each output quad. If the
+ // output of the scaler is not going to be read back (e.g., used with
+ // glReadPixels()), simply leave these unchanged. Otherwise, changing this
+ // allows a read-back pipeline to use the native format of the platform to
+ // avoid having to perform extra "BGRA⇄RGBA swizzle" memcpy's. Usually, this
+ // should match the format to be used with glReadPixels(), and that should
+ // match the GL_IMPLEMENTATION_COLOR_READ_FORMAT.
+ GLenum swizzle[2] = {
+ GL_RGBA, // For |dest_texture_0|.
+ GL_RGBA, // For |dest_texture_1|.
+ };
+
+ Parameters();
+ ~Parameters();
+ };
+
+ explicit GLScaler(scoped_refptr<ContextProvider> context_provider);
+
+ ~GLScaler() final;
+
+ // Returns true if the GL context provides the necessary support for enabling
+ // precise color management (see Parameters::enable_precise_color_management).
+ bool SupportsPreciseColorManagement() const;
+
+ // Returns the maximum number of simultaneous drawing buffers supported by the
+ // GL context. Certain Parameters can only be used when this is more than 1.
+ int GetMaxDrawBuffersSupported() const;
+
+ // [Re]Configure the scaler with the given |new_params|. Returns true on
+ // success, or false on failure.
+ bool Configure(const Parameters& new_params) WARN_UNUSED_RESULT;
+
+ // Returns the currently-configured and resolved Parameters. Note that these
+ // Parameters might not be exactly the same as those that were passed to
+ // Configure() because some properties (e.g., color spaces) are auto-resolved.
+ // Results are undefined if Configure() has never been called successfully.
+ const Parameters& params() const { return params_; }
+
+ // Scales a portion of |src_texture| and draws the result into |dest_texture|
+ // at offset (0, 0). Returns true to indicate success, or false if this
+ // GLScaler is not valid.
+ //
+ // |src_texture_size| is the full, allocated size of the |src_texture|. This
+ // is required for computing texture coordinate transforms (and only because
+ // the OpenGL ES 2.0 API lacks the ability to query this info).
+ //
+ // |src_offset| is the offset in the source texture corresponding to point
+ // (0,0) in the source/output coordinate spaces. This prevents the need for
+ // extra texture copies just to re-position the source coordinate system.
+ //
+ // |output_rect| selects the region to draw (in the scaled, not the source,
+ // coordinate space). This is used to save work in cases where only a portion
+ // needs to be re-scaled. The implementation will back-compute, internally, to
+ // determine the region of the |src_texture| to sample.
+ //
+ // WARNING: The output will always be placed at (0, 0) in the |dest_texture|,
+ // and not at |output_rect.origin()|.
+ //
+ // Note that the |src_texture| will have the min/mag filter set to GL_LINEAR
+ // and wrap_s/t set to CLAMP_TO_EDGE in this call.
+ bool Scale(GLuint src_texture,
+ const gfx::Size& src_texture_size,
+ const gfx::Vector2dF& src_offset,
+ GLuint dest_texture,
+ const gfx::Rect& output_rect) WARN_UNUSED_RESULT {
+ return ScaleToMultipleOutputs(src_texture, src_texture_size, src_offset,
+ dest_texture, 0, output_rect);
+ }
+
+ // Same as above, but for use cases where there are two output textures drawn
+ // (see Parameters::ExportFormat).
+ bool ScaleToMultipleOutputs(GLuint src_texture,
+ const gfx::Size& src_texture_size,
+ const gfx::Vector2dF& src_offset,
+ GLuint dest_texture_0,
+ GLuint dest_texture_1,
+ const gfx::Rect& output_rect) WARN_UNUSED_RESULT;
+
+ // Given the |src_texture_size|, |src_offset| and |output_rect| arguments that
+ // would be passed to Scale(), compute the region of pixels in the source
+ // texture that would be sampled to produce a scaled result. The result is
+ // stored in |sampling_rect|, along with the |offset| to the (0,0) point
+ // relative to |sampling_rect|'s origin. Returns true to indicate success, or
+ // false if this GLScaler is not valid.
+ //
+ // This is used by clients that need to know the minimal portion of a source
+ // buffer that must be copied without affecting Scale()'s results. This
+ // method also accounts for vertical flipping.
+ bool ComputeRegionOfInfluence(const gfx::Size& src_texture_size,
+ const gfx::Vector2dF& src_offset,
+ const gfx::Rect& output_rect,
+ gfx::Rect* sampling_rect,
+ gfx::Vector2dF* offset) const
+ WARN_UNUSED_RESULT;
+
+ // Returns true if from:to represent the same scale ratio as that specified in
+ // |params|.
+ static bool ParametersHasSameScaleRatio(const Parameters& params,
+ const gfx::Vector2d& from,
+ const gfx::Vector2d& to);
+
+ private:
+ friend class GLScalerShaderPixelTest;
+
+ using GLES2Interface = gpu::gles2::GLES2Interface;
+
+ enum Axis { HORIZONTAL, VERTICAL };
+
+ // The shaders used by each stage in the scaling pipeline.
+ enum class Shader : int8_t {
+ BILINEAR,
+ BILINEAR2,
+ BILINEAR3,
+ BILINEAR4,
+ BILINEAR2X2,
+ BICUBIC_UPSCALE,
+ BICUBIC_HALF_1D,
+ PLANAR_CHANNEL_0,
+ PLANAR_CHANNEL_1,
+ PLANAR_CHANNEL_2,
+ PLANAR_CHANNEL_3,
+ I422_NV61_MRT,
+ DEINTERLEAVE_PAIRWISE_MRT,
+ };
+
+ // A cached, re-usable shader program that performs one step in the scaling
+ // pipeline.
+ class VIZ_COMMON_EXPORT ShaderProgram {
+ public:
+ ShaderProgram(GLES2Interface* gl,
+ Shader shader,
+ GLint texture_format,
+ const gfx::ColorTransform* color_transform,
+ const GLenum swizzle[2]);
+ ~ShaderProgram();
+
+ Shader shader() const { return shader_; }
+ GLint texture_format() const { return texture_format_; }
+
+ // UseProgram must be called with GL_ARRAY_BUFFER bound to a vertex
+ // attribute buffer. |src_texture_size| is the size of the entire source
+ // texture, regardless of which region is to be sampled. |src_rect| is the
+ // source region, not including overscan pixels past the edges.
+ // |primary_axis| configures certain programs which scale in only one
+ // particular direction. |flip_y| causes the |src_rect| to be scanned
+ // upside-down, to produce a vertically-flipped result.
+ void UseProgram(const gfx::Size& src_texture_size,
+ const gfx::RectF& src_rect,
+ const gfx::Size& dst_size,
+ Axis primary_axis,
+ bool flip_y);
+
+ // GL_ARRAY_BUFFER data that must be bound when drawing with a
+ // ShaderProgram. These are the vertex attributes that will sweep the entire
+ // source area when executing the program. They represent triangle strip
+ // coordinates: The first two columns are (x,y) values interpolated to
+ // produce the vertex coordinates in object space, while the latter two
+ // columns are (s,t) values interpolated to produce the texture coordinates
+ // that correspond to the vertex coordinates.
+ static const GLfloat kVertexAttributes[16];
+
+ private:
+ GLES2Interface* const gl_;
+ const Shader shader_;
+ const GLint texture_format_;
+
+ // A program for copying a source texture into a destination texture.
+ const GLuint program_;
+
+ // The location of the position in the program.
+ GLint position_location_ = -1;
+ // The location of the texture coordinate in the program.
+ GLint texcoord_location_ = -1;
+ // The location of the source texture in the program.
+ GLint texture_location_ = -1;
+ // The location of the texture coordinate of the source rectangle in the
+ // program.
+ GLint src_rect_location_ = -1;
+ // Location of size of source image in pixels.
+ GLint src_pixelsize_location_ = -1;
+ // Location of vector for scaling ratio between source and dest textures.
+ GLint scaling_vector_location_ = -1;
+
+ DISALLOW_COPY_AND_ASSIGN(ShaderProgram);
+ };
+
+ // ContextLostObserver implementation.
+ void OnContextLost() final;
+
+ // Returns a cached ShaderProgram, creating one on-demand if necessary.
+ ShaderProgram* GetShaderProgram(Shader shader,
+ GLint texture_format,
+ const gfx::ColorTransform* color_transform,
+ const GLenum swizzle[2]);
+
+ // The provider of the GL context. This is non-null while the GL context is
+ // valid and GLScaler is observing for context loss.
+ scoped_refptr<ContextProvider> context_provider_;
+
+ // Set by Configure() to the resolved set of Parameters.
+ Parameters params_;
+
+ // The maximum number of simultaneous draw buffers, lazy initialized by
+ // GetMaxDrawBuffersSupported(). -1 means "not yet known."
+ mutable int max_draw_buffers_ = -1;
+
+ // Cache of ShaderPrograms. The cache key consists of fields that correspond
+ // to the arguments of GetShaderProgram(): the shader, the texture format, the
+ // source and output color spaces (color transform), and the two swizzles.
+ using ShaderCacheKey = std::
+ tuple<Shader, GLint, gfx::ColorSpace, gfx::ColorSpace, GLenum, GLenum>;
+ std::map<ShaderCacheKey, ShaderProgram> shader_programs_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLScaler);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GL_SCALER_H_
diff --git a/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc b/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc
new file mode 100644
index 00000000000..cb84b1c8251
--- /dev/null
+++ b/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc
@@ -0,0 +1,687 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/gl_scaler.h"
+
+#include <sstream>
+#include <tuple>
+#include <vector>
+
+#include "build/build_config.h"
+#include "cc/test/pixel_test.h"
+#include "cc/test/pixel_test_utils.h"
+#include "components/viz/common/gl_scaler_test_util.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "gpu/GLES2/gl2chromium.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/gfx/color_transform.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#endif
+
+namespace viz {
+
+namespace {
+
+// Base size of test images to be operated upon. Both dimensions must be
+// divisible by 2, 3, or 4.
+constexpr gfx::Size kBaseSize = gfx::Size(16 * 4 * 3, 9 * 4 * 3);
+
+} // namespace
+
+class GLScalerShaderPixelTest
+ : public cc::PixelTest,
+ public testing::WithParamInterface<std::tuple<bool, bool>>,
+ public GLScalerTestUtil {
+ public:
+ using Axis = GLScaler::Axis;
+ using Shader = GLScaler::Shader;
+ using ShaderProgram = GLScaler::ShaderProgram;
+
+ GLScalerShaderPixelTest()
+ : scoped_trace_(
+ __FILE__,
+ __LINE__,
+ (testing::Message()
+ << "is_converting_rgb_to_yuv=" << is_converting_rgb_to_yuv()
+ << ", is_swizzling_output=" << is_swizzling_output())) {}
+
+ bool is_converting_rgb_to_yuv() const { return std::get<0>(GetParam()); }
+ bool is_swizzling_output() const { return std::get<1>(GetParam()); }
+
+ bool AreMultipleRenderingTargetsSupported() const {
+ return scaler_->GetMaxDrawBuffersSupported() > 1;
+ }
+
+ // Returns a cached ShaderProgram, maybe configured to convert RGB→YUV and/or
+ // swizzle the 1st and 3rd bytes in the output (depending on GetParams()).
+ ShaderProgram* GetShaderProgram(Shader shader) {
+ std::unique_ptr<gfx::ColorTransform> transform;
+ if (is_converting_rgb_to_yuv()) {
+ transform = gfx::ColorTransform::NewColorTransform(
+ DefaultRGBColorSpace(), DefaultYUVColorSpace(),
+ gfx::ColorTransform::Intent::INTENT_ABSOLUTE);
+ }
+ const GLenum swizzle[2] = {
+ is_swizzling_output() ? GL_BGRA_EXT : GL_RGBA,
+ is_swizzling_output() ? GL_BGRA_EXT : GL_RGBA,
+ };
+ return scaler_->GetShaderProgram(shader, GL_RGBA, transform.get(), swizzle);
+ }
+
+ GLuint CreateTexture(const gfx::Size& size) {
+ return texture_helper_->CreateTexture(size);
+ }
+
+ GLuint UploadTexture(const SkBitmap& bitmap) {
+ return texture_helper_->UploadTexture(bitmap);
+ }
+
+ SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) {
+ return texture_helper_->DownloadTexture(texture, size);
+ }
+
+ GLuint RenderToNewTexture(GLuint src_texture, const gfx::Size& size) {
+ return RenderToNewTextures(src_texture, size, false).first;
+ }
+
+ // Using the current shader program, creates new texture(s) of the given
+ // |size| and draws using |src_texture| as input. If |dual_outputs| is true,
+ // two new textures are created and drawn-to simultaneously; otherwise, only
+ // one is created and drawn-to. The caller does not take ownership of the new
+ // texture(s).
+ std::pair<GLuint, GLuint> RenderToNewTextures(GLuint src_texture,
+ const gfx::Size& size,
+ bool dual_outputs) {
+ auto dst_textures = std::make_pair<GLuint, GLuint>(
+ CreateTexture(size), dual_outputs ? CreateTexture(size) : 0u);
+ GLuint framebuffer = 0;
+ gl_->GenFramebuffers(1, &framebuffer);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, dst_textures.first, 0);
+ if (dual_outputs) {
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1,
+ GL_TEXTURE_2D, dst_textures.second, 0);
+ }
+
+ gl_->BindTexture(GL_TEXTURE_2D, src_texture);
+
+ gl_->Viewport(0, 0, size.width(), size.height());
+ const GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1};
+ gl_->DrawBuffersEXT(dual_outputs ? 2 : 1, buffers);
+ // Assumption: The |vertex_attributes_buffer_| created in SetUp() is
+ // currently bound to GL_ARRAY_BUFFER.
+ gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ gl_->DeleteFramebuffers(1, &framebuffer);
+
+ return dst_textures;
+ }
+
+ // Returns a texture that converts the input |texture| back to unswizzled RGB,
+ // if necessary, depending on GetParam(). The caller does not take ownership
+ // of the returned texture, which could be the same texture as the input
+ // argument in some cases.
+ GLuint ConvertBackToUnswizzledRGB(GLuint texture, const gfx::Size& size) {
+ GLuint result = texture;
+ if (is_swizzling_output()) {
+ const GLenum swizzle[2] = {GL_BGRA_EXT, GL_BGRA_EXT};
+ scaler_->GetShaderProgram(Shader::BILINEAR, GL_RGBA, nullptr, swizzle)
+ ->UseProgram(size, gfx::RectF(gfx::Rect(size)), size,
+ Axis::HORIZONTAL, false);
+ result = RenderToNewTexture(result, size);
+ }
+ if (is_converting_rgb_to_yuv()) {
+ const auto transform = gfx::ColorTransform::NewColorTransform(
+ DefaultYUVColorSpace(), DefaultRGBColorSpace(),
+ gfx::ColorTransform::Intent::INTENT_ABSOLUTE);
+ const GLenum swizzle[2] = {GL_RGBA, GL_RGBA};
+ scaler_
+ ->GetShaderProgram(Shader::BILINEAR, GL_RGBA, transform.get(),
+ swizzle)
+ ->UseProgram(size, gfx::RectF(gfx::Rect(size)), size,
+ Axis::HORIZONTAL, false);
+ result = RenderToNewTexture(result, size);
+ }
+ return result;
+ }
+
+ // A test case executed by RunSMPTEScalingTestCases().
+ struct SMPTEScalingTestCase {
+ gfx::Rect src_rect; // Selects a subrect of the source.
+ int scale_from; // Scale ratio denominator.
+ int scale_to; // Scale ratio numerator.
+ int fuzzy_bar_border; // Ignored pixels between color bars, when comparing.
+ };
+
+ // Draws with the given shader for each of the provided |test_cases| and adds
+ // gtest failure(s) if the output does not look like a part of the SMPTE test
+ // image.
+ void RunSMPTEScalingTestCases(
+ Shader shader,
+ const std::vector<SMPTEScalingTestCase>& test_cases) {
+ const SkBitmap source = CreateSMPTETestImage(kBaseSize);
+ const GLuint src_texture = UploadTexture(source);
+
+ for (const auto& tc : test_cases) {
+ for (int is_horizontal = 0; is_horizontal <= 1; ++is_horizontal) {
+ gfx::Size dst_size = tc.src_rect.size();
+ Axis axis;
+ if (is_horizontal) {
+ CHECK_EQ((dst_size.width() * tc.scale_to) % tc.scale_from, 0);
+ dst_size.set_width(dst_size.width() * tc.scale_to / tc.scale_from);
+ axis = Axis::HORIZONTAL;
+ } else {
+ CHECK_EQ((dst_size.height() * tc.scale_to) % tc.scale_from, 0);
+ dst_size.set_height(dst_size.height() * tc.scale_to / tc.scale_from);
+ axis = Axis::VERTICAL;
+ }
+
+ SCOPED_TRACE(testing::Message()
+ << "src_rect=" << tc.src_rect.ToString()
+ << ", scale from→to=" << tc.scale_from << "→"
+ << tc.scale_to << ", dst_size=" << dst_size.ToString());
+
+ GetShaderProgram(shader)->UseProgram(kBaseSize, gfx::RectF(tc.src_rect),
+ dst_size, axis, false);
+ const SkBitmap actual = DownloadTexture(
+ ConvertBackToUnswizzledRGB(
+ RenderToNewTexture(src_texture, dst_size), dst_size),
+ dst_size);
+ int max_color_diff = GetMaxAllowedColorDifference();
+ if (!LooksLikeSMPTETestImage(actual, kBaseSize, tc.src_rect,
+ tc.fuzzy_bar_border, &max_color_diff)) {
+ ADD_FAILURE() << "Scaled image does not look like the correct scaled "
+ "subrect of the SMPTE test image (max diff measured="
+ << max_color_diff
+ << "):\nActual: " << cc::GetPNGDataUrl(actual);
+ }
+ }
+ }
+ }
+
+ // Adds test failures if an |actual| image does not match the |expected|
+ // image. When not doing color space conversion, the images must match
+ // exactly; otherwise, some minor differences are allowed.
+ void ExpectAreTheSameImage(const SkBitmap& expected,
+ const SkBitmap& actual) const {
+ const int max_color_diff = GetMaxAllowedColorDifference();
+ if (!cc::FuzzyPixelComparator(false, 100.0f, 0.0f, max_color_diff,
+ max_color_diff, 0)
+ .Compare(expected, actual)) {
+ ADD_FAILURE() << "Images are not similar enough (max_color_diff="
+ << max_color_diff
+ << "):\nExpected: " << cc::GetPNGDataUrl(expected)
+ << "\nActual: " << cc::GetPNGDataUrl(actual);
+ }
+ }
+
+ // Draws with the given shader to downscale a "striped pattern" image by
+ // |downscale_factor| in one dimension only, and adds gtest failure(s) if the
+ // resulting image is not of the |expected_solid_color|. |cycle| specifies the
+ // colors of the stripes, which should average to |expected_solid_color|.
+ //
+ // If the shader program is correct, it should be sampling the texture halfway
+ // between each pair of stripes and then averaging the result. This means that
+ // every N pixels in the source will be averaged to one pixel in the output,
+ // creating a solid color fill as output. If the shader is sampling the
+ // texture at the wrong points, the result will be tinted and/or contain
+ // striping.
+ void RunMultiplePassBilinearTest(Shader shader,
+ int downscale_factor,
+ const std::vector<SkColor>& cycle) {
+ // Compute the expected solid fill color from the colors in |cycle|.
+ uint32_t sum_red = 0;
+ uint32_t sum_green = 0;
+ uint32_t sum_blue = 0;
+ uint32_t sum_alpha = 0;
+ for (SkColor c : cycle) {
+ sum_red += SkColorGetR(c);
+ sum_green += SkColorGetG(c);
+ sum_blue += SkColorGetB(c);
+ sum_alpha += SkColorGetA(c);
+ }
+ const float count = cycle.size();
+ // Note: Taking the rounded average for each color channel.
+ const SkColor expected_solid_color =
+ SkColorSetARGB(sum_alpha / count + 0.5f, sum_red / count + 0.5f,
+ sum_green / count + 0.5f, sum_blue / count + 0.5f);
+
+ // Run the test for the vertical direction, and again for the horizontal
+ // direction.
+ const gfx::Rect src_rect =
+ gfx::Rect(0, 0, 10 * downscale_factor, 10 * downscale_factor);
+ for (int is_horizontal = 0; is_horizontal <= 1; ++is_horizontal) {
+ gfx::Size dst_size = src_rect.size();
+ Axis axis;
+ CyclicalPattern pattern;
+ if (is_horizontal) {
+ dst_size.set_width(dst_size.width() / downscale_factor);
+ axis = Axis::HORIZONTAL;
+ pattern = VERTICAL_STRIPES;
+ } else {
+ dst_size.set_height(dst_size.height() / downscale_factor);
+ axis = Axis::VERTICAL;
+ pattern = HORIZONTAL_STRIPES;
+ }
+
+ // Create the expected output image consisting of a solid fill color.
+ SkBitmap expected = AllocateRGBABitmap(dst_size);
+ expected.eraseColor(expected_solid_color);
+
+ // Run the test for each of N possible rotations of the |cycle| of
+ // stripes.
+ for (size_t rotation = 0; rotation < cycle.size(); ++rotation) {
+ SCOPED_TRACE(testing::Message() << "is_horizontal=" << !!is_horizontal
+ << ", rotation=" << rotation
+ << ", expected_solid_color=" << std::hex
+ << expected_solid_color);
+
+ const SkBitmap source =
+ CreateCyclicalTestImage(src_rect.size(), pattern, cycle, rotation);
+ const GLuint src_texture = UploadTexture(source);
+
+ // Execute the program, and convert the shader program's drawn result
+ // back to an unswizzled RGB form, and compare that with the expected
+ // image.
+ GetShaderProgram(shader)->UseProgram(
+ src_rect.size(), gfx::RectF(src_rect), dst_size, axis, false);
+ const SkBitmap actual = DownloadTexture(
+ ConvertBackToUnswizzledRGB(
+ RenderToNewTexture(src_texture, dst_size), dst_size),
+ dst_size);
+ ExpectAreTheSameImage(expected, actual);
+ }
+ }
+ }
+
+ protected:
+ void SetUp() final {
+ cc::PixelTest::SetUpGLWithoutRenderer(false);
+
+ scaler_ = std::make_unique<GLScaler>(context_provider());
+ gl_ = context_provider()->ContextGL();
+ CHECK(gl_);
+
+ // Set up vertex attributes buffer and its data.
+ gl_->GenBuffers(1, &vertex_attributes_buffer_);
+ gl_->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_);
+ gl_->BufferData(GL_ARRAY_BUFFER, sizeof(ShaderProgram::kVertexAttributes),
+ ShaderProgram::kVertexAttributes, GL_STATIC_DRAW);
+
+ texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(gl_);
+ }
+
+ void TearDown() final {
+ texture_helper_.reset();
+
+ if (vertex_attributes_buffer_) {
+ gl_->DeleteBuffers(1, &vertex_attributes_buffer_);
+ vertex_attributes_buffer_ = 0;
+ }
+
+ gl_ = nullptr;
+ scaler_.reset();
+
+ cc::PixelTest::TearDown();
+ }
+
+ // Returns the maximum allowed absolute difference between any two color
+ // values in the expected vs actual image comparisons, given the current test
+ // parameters and known platform-specific inaccuracy.
+ int GetMaxAllowedColorDifference() const {
+#if defined(OS_ANDROID)
+ // Android seems to have texture sampling and/or readback accuracy issues
+ // with these programs that are not at all seen on any of the desktop
+ // platforms. Also, versions before Marshmallow seem to have a much larger
+ // accuracy issue with a few of the programs. Thus, use higher thresholds,
+ // assuming that the programs are correct if they can pass a much lower
+ // threshold on other platforms.
+ if (base::android::BuildInfo::GetInstance()->sdk_int() <
+ base::android::SDK_VERSION_MARSHMALLOW) {
+ return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 24 : 12;
+ }
+ return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 4 : 2;
+#else
+ return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 2 : 0;
+#endif
+ }
+
+ testing::ScopedTrace scoped_trace_;
+ std::unique_ptr<GLScaler> scaler_;
+ gpu::gles2::GLES2Interface* gl_ = nullptr;
+ GLuint vertex_attributes_buffer_ = 0;
+ std::unique_ptr<GLScalerTestTextureHelper> texture_helper_;
+};
+
+// As the BILINEAR shader is used by some of the test helpers, this test is
+// necessary to ensure the correctness of the tools used by all the other tests.
+TEST_P(GLScalerShaderPixelTest, ValidateTestHelpers) {
+ // Create/validate a SMPTE color bar test image.
+ const SkBitmap original = CreateSMPTETestImage(kBaseSize);
+ int max_color_diff = GetMaxAllowedColorDifference();
+ ASSERT_TRUE(LooksLikeSMPTETestImage(original, kBaseSize, gfx::Rect(kBaseSize),
+ 0, &max_color_diff))
+ << "max diff measured=" << max_color_diff;
+
+ // Create and upload a test image that has had RGB→YUV conversion performed
+ // and/or had its color channels swizzled, depending on the testing params.
+ SkBitmap image = CreateSMPTETestImage(kBaseSize);
+ if (is_converting_rgb_to_yuv()) {
+ ConvertBitmapToYUV(&image);
+ }
+ if (is_swizzling_output()) {
+ SwizzleBitmap(&image);
+ }
+ const GLuint uploaded_texture = UploadTexture(image);
+
+ // Use the convert-back helper, which uses the BILINEAR shader to convert the
+ // |uploaded_texture| back to an unswizzled RGB form. Then, download the
+ // result and check whether it matches the original.
+ const gfx::Size size(image.width(), image.height());
+ const GLuint converted_back_texture =
+ ConvertBackToUnswizzledRGB(uploaded_texture, size);
+ const SkBitmap actual = DownloadTexture(converted_back_texture, size);
+ ExpectAreTheSameImage(original, actual);
+}
+
+// Tests the default, one-pass bilinear shader which can upscale or downscale by
+// up to 2X.
+TEST_P(GLScalerShaderPixelTest, Bilinear) {
+ constexpr gfx::Rect whole = gfx::Rect(kBaseSize);
+ constexpr gfx::Rect quadrant =
+ gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2,
+ kBaseSize.width() / 2, kBaseSize.height() / 2);
+ const std::vector<SMPTEScalingTestCase> kTestCases = {
+ // No scaling.
+ {whole, 1, 1, 0},
+ // Downscale by half.
+ {whole, 2, 1, 1},
+ // Upscale by 1.5.
+ {whole, 2, 3, 1},
+ // No scaling; lower-right quadrant only.
+ {quadrant, 1, 1, 0},
+ // Downscale by half; lower-right quadrant only.
+ {quadrant, 2, 1, 1},
+ // Upscale by 1.5; lower-right quadrant only.
+ {quadrant, 2, 3, 1},
+ };
+
+ RunSMPTEScalingTestCases(Shader::BILINEAR, kTestCases);
+}
+
+// Test the 2-tap bilinear shader, which downscales by 4X in one dimension.
+TEST_P(GLScalerShaderPixelTest, TwoTapBilinear) {
+ RunMultiplePassBilinearTest(Shader::BILINEAR2, 4,
+ {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0xff, 0x00, 0x00, 0xff)});
+}
+
+// Test the 3-tap bilinear shader, which downscales by 6X in one dimension.
+TEST_P(GLScalerShaderPixelTest, ThreeTapBilinear) {
+ RunMultiplePassBilinearTest(Shader::BILINEAR3, 6,
+ {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
+ SkColorSetARGB(0xbf, 0x00, 0x80, 0xff),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0xbf, 0xff, 0x80, 0x00),
+ SkColorSetARGB(0xff, 0x00, 0x00, 0xff)});
+}
+
+// Test the 4-tap bilinear shader, which downscales by 8X in one dimension.
+TEST_P(GLScalerShaderPixelTest, FourTapBilinear) {
+ RunMultiplePassBilinearTest(Shader::BILINEAR4, 8,
+ {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0xff, 0x00, 0x00, 0xff),
+ SkColorSetARGB(0xff, 0xff, 0xff, 0x00),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x80),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x80),
+ SkColorSetARGB(0xff, 0xff, 0x00, 0xff)});
+}
+
+// Test the 2-by-2-tap bilinear shader, which downscales by 4X in both
+// dimensions at the same time.
+TEST_P(GLScalerShaderPixelTest, TwoByTwoTapBilinear) {
+ RunMultiplePassBilinearTest(Shader::BILINEAR2X2, 4,
+ {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0xff, 0x00, 0x00, 0xff)});
+}
+
+// Tests the bicubic upscaler for a variety of scaling factors between 1X and
+// 2X, and over the entire source texture versus just its lower-right quadrant.
+TEST_P(GLScalerShaderPixelTest, BicubicUpscale) {
+ constexpr gfx::Rect whole = gfx::Rect(kBaseSize);
+ constexpr gfx::Rect quadrant =
+ gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2,
+ kBaseSize.width() / 2, kBaseSize.height() / 2);
+ const std::vector<SMPTEScalingTestCase> kTestCases = {
+ // No scaling.
+ {whole, 1, 1, 0},
+ // Upscale by 4/3.
+ {whole, 3, 4, 3},
+ // Upscale by 3/2.
+ {whole, 2, 3, 3},
+ // Upscale by 2X.
+ {whole, 1, 2, 3},
+ // No scaling; lower-right quadrant only.
+ {quadrant, 1, 1, 0},
+ // Upscale by 4/3.
+ {quadrant, 3, 4, 3},
+ // Upscale by 3/2.
+ {quadrant, 2, 3, 3},
+ // Upscale by 2X.
+ {quadrant, 1, 2, 3},
+ };
+
+ RunSMPTEScalingTestCases(Shader::BICUBIC_UPSCALE, kTestCases);
+}
+
+// Tests the bicubic half-downscaler, both over an entire source texture and
+// over just its lower-right quadrant.
+TEST_P(GLScalerShaderPixelTest, BicubicDownscaleByHalf) {
+ constexpr gfx::Rect whole = gfx::Rect(kBaseSize);
+ constexpr gfx::Rect quadrant =
+ gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2,
+ kBaseSize.width() / 2, kBaseSize.height() / 2);
+ const std::vector<SMPTEScalingTestCase> kTestCases = {
+ // Downscale by half.
+ {whole, 2, 1, 2},
+ // Downscale by half; lower-right quadrant only.
+ {quadrant, 2, 1, 2},
+ };
+
+ RunSMPTEScalingTestCases(Shader::BICUBIC_HALF_1D, kTestCases);
+}
+
+// Tests the shaders that read a normal 4-channel interleaved texture and
+// produce a planar texture consisting of just one color channel, packed into
+// RGBA quads.
+TEST_P(GLScalerShaderPixelTest, Export_Planar) {
+ const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
+ SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0xff, 0x00, 0x00, 0xff)};
+ SkBitmap source = CreateCyclicalTestImage(kBaseSize, STAGGERED, kCycle, 0);
+ const GLuint src_texture = UploadTexture(source);
+
+ // For each channel, create an expected bitmap and compare it to the result
+ // from drawing with the shader program.
+ if (is_converting_rgb_to_yuv()) {
+ ConvertBitmapToYUV(&source);
+ }
+ for (int channel = 0; channel <= 3; ++channel) {
+ SkBitmap expected = CreatePackedPlanarBitmap(source, channel);
+ if (is_swizzling_output()) {
+ SwizzleBitmap(&expected);
+ }
+
+ const Shader shader = static_cast<Shader>(
+ static_cast<int>(Shader::PLANAR_CHANNEL_0) + channel);
+ const gfx::Size dst_size(kBaseSize.width() / 4, kBaseSize.height());
+ GetShaderProgram(shader)->UseProgram(kBaseSize,
+ gfx::RectF(gfx::Rect(kBaseSize)),
+ dst_size, Axis::HORIZONTAL, false);
+ const SkBitmap actual =
+ DownloadTexture(RenderToNewTexture(src_texture, dst_size), dst_size);
+ ExpectAreTheSameImage(expected, actual);
+ }
+}
+
+// Tests that the I422/NV61 formatter shader program produces a planar texture
+// and an interleaved half-width texture from a normal 4-channel interleaved
+// texture. See gl_shader.h for more specifics.
+TEST_P(GLScalerShaderPixelTest, Export_I422_NV61) {
+ if (!AreMultipleRenderingTargetsSupported()) {
+ LOG(WARNING) << "Skipping test due to lack of MRT support on this machine.";
+ return;
+ }
+
+ // Use a vertical stripes source image/texture to test that the shader is
+ // sampling the texture at the correct points and performing
+ // downscale-blending in the second texture.
+ const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
+ SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
+ SkColorSetARGB(0xff, 0x00, 0x00, 0xff)};
+ SkBitmap source =
+ CreateCyclicalTestImage(kBaseSize, VERTICAL_STRIPES, kCycle, 0);
+ const GLuint src_texture = UploadTexture(source);
+
+ // Create the expected output images: The first (A) is simply the first color
+ // channel of the source packed into a planar format. The second (BC) consists
+ // of the second and third color channels downscaled by half and interleaved.
+ // The following can be considered a reference implementation for what the
+ // shader program is supposed to do.
+ if (is_converting_rgb_to_yuv()) {
+ ConvertBitmapToYUV(&source);
+ }
+ SkBitmap expected_a = CreatePackedPlanarBitmap(source, 0);
+ if (is_swizzling_output()) {
+ SwizzleBitmap(&expected_a);
+ }
+ const gfx::Size dst_size(expected_a.width(), expected_a.height());
+ SkBitmap expected_bc = AllocateRGBABitmap(dst_size);
+ for (int y = 0; y < dst_size.height(); ++y) {
+ const uint32_t* const src = source.getAddr32(0, y);
+ uint32_t* const dst_bc = expected_bc.getAddr32(0, y);
+ for (int x = 0; x < dst_size.width(); ++x) {
+ // (src[0..3]) (dst_bc)
+ // RGBA RGBA rgba rgba --> GBgb (e.g, two G's blended into one G)
+ const uint32_t g01 = ((((src[x * 4 + 0] >> kGreenShift) & 0xff) +
+ ((src[x * 4 + 1] >> kGreenShift) & 0xff)) /
+ 2.f +
+ 0.5f);
+ const uint32_t b01 = ((((src[x * 4 + 0] >> kBlueShift) & 0xff) +
+ ((src[x * 4 + 1] >> kBlueShift) & 0xff)) /
+ 2.f +
+ 0.5f);
+ const uint32_t g23 = ((((src[x * 4 + 2] >> kGreenShift) & 0xff) +
+ ((src[x * 4 + 3] >> kGreenShift) & 0xff)) /
+ 2.f +
+ 0.5f);
+ const uint32_t b23 = ((((src[x * 4 + 2] >> kBlueShift) & 0xff) +
+ ((src[x * 4 + 3] >> kBlueShift) & 0xff)) /
+ 2.f +
+ 0.5f);
+ dst_bc[x] = ((g01 << kRedShift) | (b01 << kGreenShift) |
+ (g23 << kBlueShift) | (b23 << kAlphaShift));
+ }
+ }
+ if (is_swizzling_output()) {
+ SwizzleBitmap(&expected_bc);
+ }
+
+ // Execute the program, and compare the shader program's drawn result with the
+ // expected images.
+ GetShaderProgram(Shader::I422_NV61_MRT)
+ ->UseProgram(kBaseSize, gfx::RectF(gfx::Rect(kBaseSize)), dst_size,
+ Axis::HORIZONTAL, false);
+ const auto textures = RenderToNewTextures(src_texture, dst_size, true);
+ const SkBitmap actual_a = DownloadTexture(textures.first, dst_size);
+ ExpectAreTheSameImage(expected_a, actual_a);
+ const SkBitmap actual_bc = DownloadTexture(textures.second, dst_size);
+ ExpectAreTheSameImage(expected_bc, actual_bc);
+}
+
+// Tests the pairwise-deinterleave shader program that produces two planar
+// textures from a single interleaved one.
+TEST_P(GLScalerShaderPixelTest, Export_PairwiseDeinterleave) {
+ if (!AreMultipleRenderingTargetsSupported()) {
+ LOG(WARNING) << "Skipping test due to lack of MRT support on this machine.";
+ return;
+ }
+
+ // This shader does not provide color space conversion. It is just a
+ // demultiplexer/repackager.
+ if (is_converting_rgb_to_yuv()) {
+ return;
+ }
+
+ // Create a source image/texture with a pattern suitable for ensuring the
+ // shader is sampling the texture at the correct points.
+ const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
+ SkColorSetARGB(0xc0, 0x00, 0xc0, 0x00),
+ SkColorSetARGB(0x80, 0x00, 0x00, 0x80),
+ SkColorSetARGB(0xff, 0xff, 0xff, 0xff)};
+ const SkBitmap source =
+ CreateCyclicalTestImage(kBaseSize, STAGGERED, kCycle, 0);
+ const GLuint src_texture = UploadTexture(source);
+
+ // Create the expected pair of planar images.
+ const gfx::Size dst_size(kBaseSize.width() / 2, kBaseSize.height());
+ SkBitmap expected_a = AllocateRGBABitmap(dst_size);
+ SkBitmap expected_b = AllocateRGBABitmap(dst_size);
+ for (int y = 0; y < dst_size.height(); ++y) {
+ const uint32_t* const src = source.getAddr32(0, y);
+ uint32_t* const dst_a = expected_a.getAddr32(0, y);
+ uint32_t* const dst_b = expected_b.getAddr32(0, y);
+ for (int x = 0; x < dst_size.width(); ++x) {
+ // (src) (dst_a) (dst_b)
+ // ABAB abab --> { AAaa + BBbb }
+ dst_a[x] = ((((src[x * 2 + 0] >> kRedShift) & 0xff) << kRedShift) |
+ (((src[x * 2 + 0] >> kBlueShift) & 0xff) << kGreenShift) |
+ (((src[x * 2 + 1] >> kRedShift) & 0xff) << kBlueShift) |
+ (((src[x * 2 + 1] >> kBlueShift) & 0xff) << kAlphaShift));
+ dst_b[x] = ((((src[x * 2 + 0] >> kGreenShift) & 0xff) << kRedShift) |
+ (((src[x * 2 + 0] >> kAlphaShift) & 0xff) << kGreenShift) |
+ (((src[x * 2 + 1] >> kGreenShift) & 0xff) << kBlueShift) |
+ (((src[x * 2 + 1] >> kAlphaShift) & 0xff) << kAlphaShift));
+ }
+ }
+ if (is_swizzling_output()) {
+ SwizzleBitmap(&expected_a);
+ SwizzleBitmap(&expected_b);
+ }
+
+ // Execute the program, and compare the shader program's drawn result with the
+ // expected images.
+ GetShaderProgram(Shader::DEINTERLEAVE_PAIRWISE_MRT)
+ ->UseProgram(kBaseSize, gfx::RectF(gfx::Rect(kBaseSize)), dst_size,
+ Axis::HORIZONTAL, false);
+ const auto textures = RenderToNewTextures(src_texture, dst_size, true);
+ const SkBitmap actual_a = DownloadTexture(textures.first, dst_size);
+ ExpectAreTheSameImage(expected_a, actual_a);
+ const SkBitmap actual_b = DownloadTexture(textures.second, dst_size);
+ ExpectAreTheSameImage(expected_b, actual_b);
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ GLScalerShaderPixelTest,
+ testing::Combine(testing::Bool(), testing::Bool()));
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gl_scaler_test_util.cc b/chromium/components/viz/common/gl_scaler_test_util.cc
new file mode 100644
index 00000000000..a706e82d650
--- /dev/null
+++ b/chromium/components/viz/common/gl_scaler_test_util.cc
@@ -0,0 +1,362 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/gl_scaler_test_util.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace viz {
+
+using ColorBar = GLScalerTestUtil::ColorBar;
+
+// static
+SkBitmap GLScalerTestUtil::AllocateRGBABitmap(const gfx::Size& size) {
+ SkBitmap bitmap;
+ bitmap.allocPixels(SkImageInfo::Make(size.width(), size.height(),
+ kRGBA_8888_SkColorType,
+ kUnpremul_SkAlphaType));
+ return bitmap;
+}
+
+// static
+uint8_t GLScalerTestUtil::ToClamped255(float value) {
+ value = std::fma(value, 255.0f, 0.5f /* rounding */);
+ return base::saturated_cast<uint8_t>(value);
+}
+
+// static
+std::vector<ColorBar> GLScalerTestUtil::GetScaledSMPTEColorBars(
+ const gfx::Size& size) {
+ std::vector<ColorBar> rects;
+ const SkScalar scale_x =
+ static_cast<SkScalar>(size.width()) / kSMPTEFullSize.width();
+ const SkScalar scale_y =
+ static_cast<SkScalar>(size.height()) / kSMPTEFullSize.height();
+ for (const auto& cr : kSMPTEColorBars) {
+ const SkRect rect =
+ SkRect{cr.rect.fLeft * scale_x, cr.rect.fTop * scale_y,
+ cr.rect.fRight * scale_x, cr.rect.fBottom * scale_y};
+ rects.push_back(ColorBar{rect.round(), cr.color});
+ }
+ return rects;
+}
+
+// static
+SkBitmap GLScalerTestUtil::CreateSMPTETestImage(const gfx::Size& size) {
+ SkBitmap result = AllocateRGBABitmap(size);
+
+ // Set all pixels to a color that should not exist in the result. Later, a
+ // sanity-check will ensure all pixels have been overwritten.
+ constexpr SkColor kDeadColor = SkColorSetARGB(0xde, 0xad, 0xbe, 0xef);
+ result.eraseColor(kDeadColor);
+
+ // Set the pixels corresponding to each color bar.
+ for (const auto& cr : GetScaledSMPTEColorBars(size)) {
+ result.erase(cr.color, cr.rect);
+ }
+
+ // Validate that every pixel in the result bitmap has been touched by one of
+ // the color bars.
+ for (int y = 0; y < result.height(); ++y) {
+ for (int x = 0; x < result.width(); ++x) {
+ if (result.getColor(x, y) == kDeadColor) {
+ NOTREACHED() << "TEST BUG: Error creating SMPTE test image. Bad size ("
+ << size.ToString() << ")?";
+ return result;
+ }
+ }
+ }
+
+ return result;
+}
+
+// static
+bool GLScalerTestUtil::LooksLikeSMPTETestImage(const SkBitmap& image,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_rect,
+ int fuzzy_pixels,
+ int* max_color_diff) {
+ if (image.width() <= 0 || image.height() <= 0) {
+ return false;
+ }
+
+ const SkScalar offset_x = static_cast<SkScalar>(src_rect.x());
+ const SkScalar offset_y = static_cast<SkScalar>(src_rect.y());
+ const SkScalar scale_x =
+ static_cast<SkScalar>(image.width()) / src_rect.width();
+ const SkScalar scale_y =
+ static_cast<SkScalar>(image.height()) / src_rect.height();
+ int measured_max_diff = 0;
+ for (const auto& cr : GetScaledSMPTEColorBars(src_size)) {
+ const SkIRect offset_rect = cr.rect.makeOffset(-offset_x, -offset_y);
+ const SkIRect rect =
+ SkRect{offset_rect.fLeft * scale_x, offset_rect.fTop * scale_y,
+ offset_rect.fRight * scale_x, offset_rect.fBottom * scale_y}
+ .round()
+ .makeInset(fuzzy_pixels, fuzzy_pixels);
+ for (int y = std::max(0, rect.fTop),
+ y_end = std::min(image.height(), rect.fBottom);
+ y < y_end; ++y) {
+ for (int x = std::max(0, rect.fLeft),
+ x_end = std::min(image.width(), rect.fRight);
+ x < x_end; ++x) {
+ const SkColor actual = image.getColor(x, y);
+ measured_max_diff =
+ std::max({measured_max_diff,
+ std::abs(static_cast<int>(SkColorGetR(cr.color)) -
+ static_cast<int>(SkColorGetR(actual))),
+ std::abs(static_cast<int>(SkColorGetG(cr.color)) -
+ static_cast<int>(SkColorGetG(actual))),
+ std::abs(static_cast<int>(SkColorGetB(cr.color)) -
+ static_cast<int>(SkColorGetB(actual))),
+ std::abs(static_cast<int>(SkColorGetA(cr.color)) -
+ static_cast<int>(SkColorGetA(actual)))});
+ }
+ }
+ }
+
+ if (max_color_diff) {
+ const int threshold = *max_color_diff;
+ *max_color_diff = measured_max_diff;
+ return measured_max_diff <= threshold;
+ }
+ return measured_max_diff == 0;
+}
+
+// static
+SkBitmap GLScalerTestUtil::CreateCyclicalTestImage(
+ const gfx::Size& size,
+ CyclicalPattern pattern,
+ const std::vector<SkColor>& cycle,
+ size_t rotation) {
+ CHECK(!cycle.empty());
+
+ // Map SkColors to RGBA data. Also, applies the cycle |rotation| to simplify
+ // the rest of the code below.
+ std::vector<uint32_t> cycle_as_rgba(cycle.size());
+ for (size_t i = 0; i < cycle.size(); ++i) {
+ const SkColor color = cycle[(i + rotation) % cycle.size()];
+ cycle_as_rgba[i] = ((SkColorGetR(color) << kRedShift) |
+ (SkColorGetG(color) << kGreenShift) |
+ (SkColorGetB(color) << kBlueShift) |
+ (SkColorGetA(color) << kAlphaShift));
+ }
+
+ SkBitmap result = AllocateRGBABitmap(size);
+ switch (pattern) {
+ case HORIZONTAL_STRIPES:
+ for (int y = 0; y < size.height(); ++y) {
+ uint32_t* const pixels = result.getAddr32(0, y);
+ const uint32_t stripe_rgba = cycle_as_rgba[y % cycle_as_rgba.size()];
+ for (int x = 0; x < size.width(); ++x) {
+ pixels[x] = stripe_rgba;
+ }
+ }
+ break;
+
+ case VERTICAL_STRIPES:
+ for (int y = 0; y < size.height(); ++y) {
+ uint32_t* const pixels = result.getAddr32(0, y);
+ for (int x = 0; x < size.width(); ++x) {
+ pixels[x] = cycle_as_rgba[x % cycle_as_rgba.size()];
+ }
+ }
+ break;
+
+ case STAGGERED:
+ for (int y = 0; y < size.height(); ++y) {
+ uint32_t* const pixels = result.getAddr32(0, y);
+ for (int x = 0; x < size.width(); ++x) {
+ pixels[x] = cycle_as_rgba[(x + y) % cycle_as_rgba.size()];
+ }
+ }
+ break;
+ }
+
+ return result;
+}
+
+// static
+gfx::ColorSpace GLScalerTestUtil::DefaultRGBColorSpace() {
+ return gfx::ColorSpace::CreateSRGB();
+}
+
+// static
+gfx::ColorSpace GLScalerTestUtil::DefaultYUVColorSpace() {
+ return gfx::ColorSpace::CreateREC709();
+}
+
+// static
+void GLScalerTestUtil::ConvertBitmapToYUV(SkBitmap* image) {
+ const auto transform = gfx::ColorTransform::NewColorTransform(
+ DefaultRGBColorSpace(), DefaultYUVColorSpace(),
+ gfx::ColorTransform::Intent::INTENT_ABSOLUTE);
+
+ // Loop, transforming one row of pixels at a time.
+ std::vector<gfx::ColorTransform::TriStim> stims(image->width());
+ for (int y = 0; y < image->height(); ++y) {
+ uint32_t* const pixels = image->getAddr32(0, y);
+ for (int x = 0; x < image->width(); ++x) {
+ stims[x].set_x(((pixels[x] >> kRedShift) & 0xff) / 255.0f);
+ stims[x].set_y(((pixels[x] >> kGreenShift) & 0xff) / 255.0f);
+ stims[x].set_z(((pixels[x] >> kBlueShift) & 0xff) / 255.0f);
+ }
+ transform->Transform(stims.data(), stims.size());
+ for (int x = 0; x < image->width(); ++x) {
+ pixels[x] = ((ToClamped255(stims[x].x()) << kRedShift) |
+ (ToClamped255(stims[x].y()) << kGreenShift) |
+ (ToClamped255(stims[x].z()) << kBlueShift) |
+ (((pixels[x] >> kAlphaShift) & 0xff) << kAlphaShift));
+ }
+ }
+}
+
+// static
+void GLScalerTestUtil::SwizzleBitmap(SkBitmap* image) {
+ for (int y = 0; y < image->height(); ++y) {
+ uint32_t* const pixels = image->getAddr32(0, y);
+ for (int x = 0; x < image->width(); ++x) {
+ pixels[x] = ((((pixels[x] >> kBlueShift) & 0xff) << kRedShift) |
+ (((pixels[x] >> kGreenShift) & 0xff) << kGreenShift) |
+ (((pixels[x] >> kRedShift) & 0xff) << kBlueShift) |
+ (((pixels[x] >> kAlphaShift) & 0xff) << kAlphaShift));
+ }
+ }
+}
+
+// static
+SkBitmap GLScalerTestUtil::CreatePackedPlanarBitmap(const SkBitmap& source,
+ int channel) {
+ CHECK_EQ(source.width() % 4, 0);
+ SkBitmap result =
+ AllocateRGBABitmap(gfx::Size(source.width() / 4, source.height()));
+
+ constexpr int kShiftForChannel[4] = {kRedShift, kGreenShift, kBlueShift,
+ kAlphaShift};
+ const int shift = kShiftForChannel[channel];
+ for (int y = 0; y < result.height(); ++y) {
+ const uint32_t* const src = source.getAddr32(0, y);
+ uint32_t* const dst = result.getAddr32(0, y);
+ for (int x = 0; x < result.width(); ++x) {
+ // (src[0..3]) (dst)
+ // RGBA RGBA RGBA RGBA --> RRRR (if channel is 0)
+ dst[x] = ((((src[x * 4 + 0] >> shift) & 0xff) << kRedShift) |
+ (((src[x * 4 + 1] >> shift) & 0xff) << kGreenShift) |
+ (((src[x * 4 + 2] >> shift) & 0xff) << kBlueShift) |
+ (((src[x * 4 + 3] >> shift) & 0xff) << kAlphaShift));
+ }
+ }
+ return result;
+}
+
+// The area and color of the bars in a 1920x1080 HD SMPTE color bars test image
+// (https://commons.wikimedia.org/wiki/File:SMPTE_Color_Bars_16x9.svg). The gray
+// linear gradient bar is defined as half solid 0-level black and half solid
+// full-intensity white).
+const ColorBar GLScalerTestUtil::kSMPTEColorBars[30] = {
+ {{0, 0, 240, 630}, SkColorSetRGB(0x66, 0x66, 0x66)},
+ {{240, 0, 445, 630}, SkColorSetRGB(0xbf, 0xbf, 0xbf)},
+ {{445, 0, 651, 630}, SkColorSetRGB(0xbf, 0xbf, 0x00)},
+ {{651, 0, 857, 630}, SkColorSetRGB(0x00, 0xbf, 0xbf)},
+ {{857, 0, 1063, 630}, SkColorSetRGB(0x00, 0xbf, 0x00)},
+ {{1063, 0, 1269, 630}, SkColorSetRGB(0xbf, 0x00, 0xbf)},
+ {{1269, 0, 1475, 630}, SkColorSetRGB(0xbf, 0x00, 0x00)},
+ {{1475, 0, 1680, 630}, SkColorSetRGB(0x00, 0x00, 0xbf)},
+ {{1680, 0, 1920, 630}, SkColorSetRGB(0x66, 0x66, 0x66)},
+ {{0, 630, 240, 720}, SkColorSetRGB(0x00, 0xff, 0xff)},
+ {{240, 630, 445, 720}, SkColorSetRGB(0x00, 0x21, 0x4c)},
+ {{445, 630, 1680, 720}, SkColorSetRGB(0xbf, 0xbf, 0xbf)},
+ {{1680, 630, 1920, 720}, SkColorSetRGB(0x00, 0x00, 0xff)},
+ {{0, 720, 240, 810}, SkColorSetRGB(0xff, 0xff, 0x00)},
+ {{240, 720, 445, 810}, SkColorSetRGB(0x32, 0x00, 0x6a)},
+ {{445, 720, 1063, 810}, SkColorSetRGB(0x00, 0x00, 0x00)},
+ {{1063, 720, 1680, 810}, SkColorSetRGB(0xff, 0xff, 0xff)},
+ {{1680, 720, 1920, 810}, SkColorSetRGB(0xff, 0x00, 0x00)},
+ {{0, 810, 240, 1080}, SkColorSetRGB(0x26, 0x26, 0x26)},
+ {{240, 810, 549, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
+ {{549, 810, 960, 1080}, SkColorSetRGB(0xff, 0xff, 0xff)},
+ {{960, 810, 1131, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
+ {{1131, 810, 1200, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
+ {{1200, 810, 1268, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
+ {{1268, 810, 1337, 1080}, SkColorSetRGB(0x05, 0x05, 0x05)},
+ {{1337, 810, 1405, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
+ {{1405, 810, 1474, 1080}, SkColorSetRGB(0x0a, 0x0a, 0x0a)},
+ {{1474, 810, 1680, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
+ {{1680, 810, 1920, 1080}, SkColorSetRGB(0x26, 0x26, 0x26)},
+};
+
+constexpr gfx::Size GLScalerTestUtil::kSMPTEFullSize;
+
+GLScalerTestTextureHelper::GLScalerTestTextureHelper(
+ gpu::gles2::GLES2Interface* gl)
+ : gl_(gl) {
+ CHECK(gl_);
+}
+
+GLScalerTestTextureHelper::~GLScalerTestTextureHelper() {
+ gl_->DeleteTextures(textures_to_delete_.size(), textures_to_delete_.data());
+ textures_to_delete_.clear();
+}
+
+GLuint GLScalerTestTextureHelper::CreateTexture(const gfx::Size& size) {
+ GLuint texture = 0;
+ gl_->GenTextures(1, &texture);
+ gl_->BindTexture(GL_TEXTURE_2D, texture);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ gl_->BindTexture(GL_TEXTURE_2D, 0);
+
+ if (texture) {
+ textures_to_delete_.push_back(texture);
+ }
+
+ return texture;
+}
+
+GLuint GLScalerTestTextureHelper::UploadTexture(const SkBitmap& bitmap) {
+ CHECK_EQ(bitmap.colorType(), kRGBA_8888_SkColorType);
+
+ GLuint texture = 0;
+ gl_->GenTextures(1, &texture);
+ gl_->BindTexture(GL_TEXTURE_2D, texture);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap.width(), bitmap.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, bitmap.getAddr32(0, 0));
+ gl_->BindTexture(GL_TEXTURE_2D, 0);
+
+ if (texture) {
+ textures_to_delete_.push_back(texture);
+ }
+
+ return texture;
+}
+
+SkBitmap GLScalerTestTextureHelper::DownloadTexture(GLuint texture,
+ const gfx::Size& size) {
+ GLuint framebuffer = 0;
+ gl_->GenFramebuffers(1, &framebuffer);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ texture, 0);
+ SkBitmap result = GLScalerTestUtil::AllocateRGBABitmap(size);
+ gl_->ReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+ result.getAddr32(0, 0));
+ gl_->DeleteFramebuffers(1, &framebuffer);
+ return result;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gl_scaler_test_util.h b/chromium/components/viz/common/gl_scaler_test_util.h
new file mode 100644
index 00000000000..3c4e47e0da6
--- /dev/null
+++ b/chromium/components/viz/common/gl_scaler_test_util.h
@@ -0,0 +1,154 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_GL_SCALER_TEST_UTIL_H_
+#define COMPONENTS_VIZ_COMMON_GL_SCALER_TEST_UTIL_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/color_transform.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace viz {
+
+// A collection of utility functions used in the GLScaler-related pixel tests.
+class GLScalerTestUtil {
+ public:
+ struct ColorBar {
+ SkIRect rect;
+ SkColor color;
+ };
+
+ // The patterns that can be created by CreateCyclicalTestImage().
+ enum CyclicalPattern {
+ HORIZONTAL_STRIPES,
+ VERTICAL_STRIPES,
+ STAGGERED,
+ };
+
+ // Returns an SkBitmap with pixels allocated, having a RGBA format with
+ // unpremultiplied alpha.
+ static SkBitmap AllocateRGBABitmap(const gfx::Size& size);
+
+ // Returns a |value| in the range [0.0,1.0] as an unsigned integer in the
+ // range [0,255].
+ static uint8_t ToClamped255(float value);
+
+ // Return the SMPTE color bars that make up a test image, scaled to an image
+ // of the given |size|.
+ static std::vector<ColorBar> GetScaledSMPTEColorBars(const gfx::Size& size);
+
+ // Create a SMPTE color bar test image, scaled to the given |size|.
+ static SkBitmap CreateSMPTETestImage(const gfx::Size& size);
+
+ // Returns true if the given |image| looks similar-enough to a scaled version
+ // of a SMPTE color bar test image that was originally of |src_size| and
+ // cropped to |src_rect|. |fuzzy_pixels| is used to ignore a border of pixels
+ // surrounding each color bar, since the scaling algorithms may blend
+ // in-between colors where the bars touch. |max_color_diff| controls what
+ // "similar-enough" means: 0 for "exact," or otherwise some positive value
+ // specifying the maximum color value difference; and is updated with the
+ // the actual maximum.
+ static bool LooksLikeSMPTETestImage(const SkBitmap& image,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_rect,
+ int fuzzy_pixels,
+ int* max_color_diff);
+
+ // Returns an image of the given |size| with the colors in |cycle| used to
+ // generate a striped or staggered |pattern|. |rotation| specifies which index
+ // in the |cycle| to start with.
+ static SkBitmap CreateCyclicalTestImage(const gfx::Size& size,
+ CyclicalPattern pattern,
+ const std::vector<SkColor>& cycle,
+ size_t rotation);
+
+ // Returns the RGB/YUV color spaces used by default when color space
+ // conversion is requested.
+ static gfx::ColorSpace DefaultRGBColorSpace();
+ static gfx::ColorSpace DefaultYUVColorSpace();
+
+ // Performs an in-place transform of the given |image| from
+ // DefaultRGBColorSpace() to DefaultYUVColorSpace(). The color channels (plus
+ // one alpha) remain interleaved (i.e., no pixel blending or format transform
+ // is being done).
+ static void ConvertBitmapToYUV(SkBitmap* image);
+
+ // Performs an in-place swizzling of the red and blue color channels in the
+ // given |image|.
+ static void SwizzleBitmap(SkBitmap* image);
+
+ // Returns a bitmap consisting of one color channel from every 4 pixels in a
+ // |source| bitmap packed into a single quad. Thus, the resulting bitmap will
+ // have 1/4 the width of the source bitmap. This is used to create the
+ // expected output of the single-channel export shaders, and thus can be
+ // considered a reference implementation of that.
+ static SkBitmap CreatePackedPlanarBitmap(const SkBitmap& source, int channel);
+
+ // The area and color of the bars in a 1920x1080 HD SMPTE color bars test
+ // image (https://commons.wikimedia.org/wiki/File:SMPTE_Color_Bars_16x9.svg).
+ // The gray linear gradient bar is defined as half solid 0-level black and
+ // half solid full-intensity white).
+ static const ColorBar kSMPTEColorBars[30];
+ static constexpr gfx::Size kSMPTEFullSize = gfx::Size(1920, 1080);
+
+#ifdef SK_CPU_BENDIAN
+ // Bit shift offsets (within a uint32_t RGBA quad) to access each color
+ // channel's byte.
+ static constexpr int kRedShift = 24;
+ static constexpr int kGreenShift = 16;
+ static constexpr int kBlueShift = 8;
+ static constexpr int kAlphaShift = 0;
+#else
+ static constexpr int kRedShift = 0;
+ static constexpr int kGreenShift = 8;
+ static constexpr int kBlueShift = 16;
+ static constexpr int kAlphaShift = 24;
+#endif
+};
+
+// A helper for tests to create textures, and download/upload RGBA textures
+// to/from SkBitmaps. All textures created by this helper will be deleted when
+// its destructor is invoked.
+class GLScalerTestTextureHelper {
+ public:
+ // |gl| context must outlive this instance.
+ explicit GLScalerTestTextureHelper(gpu::gles2::GLES2Interface* gl);
+
+ ~GLScalerTestTextureHelper();
+
+ // Creates a fully-defined RGBA texture of the given |size|, returning its GL
+ // name.
+ GLuint CreateTexture(const gfx::Size& size);
+
+ // Uploads the given RGBA |bitmap| to the GPU into a new texture, returning
+ // its GL name.
+ GLuint UploadTexture(const SkBitmap& bitmap);
+
+ // Reads-back the |texture| which is of the given |size|, returning the result
+ // as a RGBA SkBitmap.
+ SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size);
+
+ private:
+ gpu::gles2::GLES2Interface* const gl_;
+ std::vector<GLuint> textures_to_delete_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLScalerTestTextureHelper);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GL_SCALER_TEST_UTIL_H_
diff --git a/chromium/components/viz/common/gl_scaler_unittest.cc b/chromium/components/viz/common/gl_scaler_unittest.cc
new file mode 100644
index 00000000000..ed702154c98
--- /dev/null
+++ b/chromium/components/viz/common/gl_scaler_unittest.cc
@@ -0,0 +1,206 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/gl_scaler.h"
+
+#include "components/viz/common/gpu/context_provider.h"
+#include "gpu/GLES2/gl2chromium.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::ByRef;
+using ::testing::Eq;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
+using ::testing::Sequence;
+
+namespace viz {
+namespace {
+
+class MockContextProvider : public ContextProvider {
+ public:
+ MockContextProvider() {
+ ON_CALL(*this, ContextGL())
+ .WillByDefault(
+ Return(reinterpret_cast<gpu::gles2::GLES2Interface*>(0xdeadbeef)));
+ ON_CALL(*this, ContextCapabilities()).WillByDefault(ReturnRef(caps_));
+ }
+
+ MOCK_METHOD1(AddObserver, void(ContextLostObserver* obs));
+ MOCK_METHOD1(RemoveObserver, void(ContextLostObserver* obs));
+ MOCK_CONST_METHOD0(ContextCapabilities, const gpu::Capabilities&());
+ MOCK_METHOD0(ContextGL, gpu::gles2::GLES2Interface*());
+
+ // Stubbed-out, because the tests just stack-allocate this object.
+ void AddRef() const final {}
+ void Release() const final {}
+
+ private:
+ gpu::Capabilities caps_;
+
+ // Other ContextProvider methods; but stubbed-out because they are never
+ // called.
+ gpu::ContextResult BindToCurrentThread() final {
+ NOTREACHED();
+ return gpu::ContextResult::kSuccess;
+ }
+ base::Lock* GetLock() final {
+ NOTREACHED();
+ return nullptr;
+ }
+ ContextCacheController* CacheController() final {
+ NOTREACHED();
+ return nullptr;
+ }
+ gpu::ContextSupport* ContextSupport() final {
+ NOTREACHED();
+ return nullptr;
+ }
+ class GrContext* GrContext() final {
+ NOTREACHED();
+ return nullptr;
+ }
+ gpu::SharedImageInterface* SharedImageInterface() final {
+ NOTREACHED();
+ return nullptr;
+ }
+ const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const final {
+ NOTREACHED();
+ return *reinterpret_cast<gpu::GpuFeatureInfo*>(0xdeadbeef);
+ }
+};
+
+class GLScalerTest : public testing::Test {};
+
+TEST_F(GLScalerTest, AddAndRemovesSelfAsContextLossObserver) {
+ NiceMock<MockContextProvider> provider;
+ ContextLostObserver* registered_observer = nullptr;
+ Sequence s;
+ EXPECT_CALL(provider, AddObserver(NotNull()))
+ .InSequence(s)
+ .WillOnce(SaveArg<0>(&registered_observer));
+ EXPECT_CALL(provider, RemoveObserver(Eq(ByRef(registered_observer))))
+ .InSequence(s);
+ GLScaler scaler(base::WrapRefCounted(&provider));
+}
+
+TEST_F(GLScalerTest, CleansUpWhenContextIsLost) {
+ NiceMock<MockContextProvider> provider;
+ ContextLostObserver* registered_observer = nullptr;
+ Sequence s;
+ EXPECT_CALL(provider, AddObserver(NotNull()))
+ .InSequence(s)
+ .WillOnce(SaveArg<0>(&registered_observer));
+ EXPECT_CALL(provider, RemoveObserver(Eq(ByRef(registered_observer))))
+ .InSequence(s);
+ GLScaler scaler(base::WrapRefCounted(&provider));
+ static_cast<ContextLostObserver&>(scaler).OnContextLost();
+ // Verify RemoveObserver() was called before |scaler| goes out-of-scope.
+ Mock::VerifyAndClearExpectations(&provider);
+}
+
+TEST_F(GLScalerTest, Configure_RequiresValidScalingVectors) {
+ NiceMock<MockContextProvider> provider;
+ GLScaler scaler(base::WrapRefCounted(&provider));
+
+ GLScaler::Parameters params;
+ EXPECT_TRUE(scaler.Configure(params));
+
+ for (int i = 0; i < 4; ++i) {
+ params.scale_from = gfx::Vector2d(i == 0 ? 0 : 1, i == 1 ? 0 : 1);
+ params.scale_to = gfx::Vector2d(i == 2 ? 0 : 1, i == 3 ? 0 : 1);
+ EXPECT_FALSE(scaler.Configure(params));
+ }
+}
+
+TEST_F(GLScalerTest, Configure_ResolvesUnspecifiedColorSpaces) {
+ NiceMock<MockContextProvider> provider;
+ GLScaler scaler(base::WrapRefCounted(&provider));
+
+ // Neither source nor output space specified: Both should resolve to sRGB.
+ GLScaler::Parameters params;
+ EXPECT_TRUE(scaler.Configure(params));
+ const auto srgb = gfx::ColorSpace::CreateSRGB();
+ EXPECT_EQ(srgb, scaler.params().source_color_space);
+ EXPECT_EQ(srgb, scaler.params().output_color_space);
+
+ // Source space set to XYZD50 with no output space specified: Both should
+ // resolve to XYZD50.
+ const auto xyzd50 = gfx::ColorSpace::CreateXYZD50();
+ params.source_color_space = xyzd50;
+ EXPECT_TRUE(scaler.Configure(params));
+ EXPECT_EQ(xyzd50, scaler.params().source_color_space);
+ EXPECT_EQ(xyzd50, scaler.params().output_color_space);
+
+ // Source space set to XYZD50 with output space set to P3D65: Nothing should
+ // change.
+ const auto p3d65 = gfx::ColorSpace::CreateDisplayP3D65();
+ params.output_color_space = p3d65;
+ EXPECT_TRUE(scaler.Configure(params));
+ EXPECT_EQ(xyzd50, scaler.params().source_color_space);
+ EXPECT_EQ(p3d65, scaler.params().output_color_space);
+}
+
+TEST_F(GLScalerTest, Configure_RequiresValidSwizzles) {
+ NiceMock<MockContextProvider> provider;
+ GLScaler scaler(base::WrapRefCounted(&provider));
+ GLScaler::Parameters params;
+
+ // Test that all valid combinations work.
+ for (int i = 0; i < 4; ++i) {
+ params.swizzle[0] = (i % 2 == 0) ? GL_RGBA : GL_BGRA_EXT;
+ params.swizzle[1] = (i / 2 == 0) ? GL_RGBA : GL_BGRA_EXT;
+ EXPECT_TRUE(scaler.Configure(params)) << "i=" << i;
+ }
+
+ // Test that invalid combinations don't work.
+ for (int i = 1; i < 4; ++i) {
+ params.swizzle[0] = (i % 2 == 0) ? GL_RGBA : GL_RGB;
+ params.swizzle[1] = (i / 2 == 0) ? GL_RGBA : GL_RGB;
+ EXPECT_FALSE(scaler.Configure(params)) << "i=" << i;
+ }
+}
+
+TEST_F(GLScalerTest, DetectsEquivalentScaleRatios) {
+ GLScaler::Parameters params;
+ EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(1, 1),
+ gfx::Vector2d(1, 1)));
+ EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(
+ params, gfx::Vector2d(15, 15), gfx::Vector2d(15, 15)));
+
+ params.scale_from = gfx::Vector2d(2, 1);
+ EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(2, 1),
+ gfx::Vector2d(1, 1)));
+ EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(
+ params, gfx::Vector2d(30, 15), gfx::Vector2d(15, 15)));
+
+ params.scale_from = gfx::Vector2d(1, 2);
+ EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(1, 2),
+ gfx::Vector2d(1, 1)));
+ EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(
+ params, gfx::Vector2d(15, 30), gfx::Vector2d(15, 15)));
+
+ params.scale_from = gfx::Vector2d(2, 1);
+ EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio(
+ params, gfx::Vector2d(1, 2), gfx::Vector2d(1, 1)));
+ EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio(
+ params, gfx::Vector2d(15, 30), gfx::Vector2d(15, 15)));
+
+ params.scale_from = gfx::Vector2d(1, 2);
+ EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio(
+ params, gfx::Vector2d(2, 1), gfx::Vector2d(1, 1)));
+ EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio(
+ params, gfx::Vector2d(30, 15), gfx::Vector2d(15, 15)));
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/common/gpu/context_cache_controller.cc b/chromium/components/viz/common/gpu/context_cache_controller.cc
index d49abb72890..6b27d149e10 100644
--- a/chromium/components/viz/common/gpu/context_cache_controller.cc
+++ b/chromium/components/viz/common/gpu/context_cache_controller.cc
@@ -4,6 +4,8 @@
#include "components/viz/common/gpu/context_cache_controller.h"
+#include <chrono>
+
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
@@ -14,6 +16,7 @@
namespace viz {
namespace {
static const int kIdleCleanupDelaySeconds = 1;
+static const int kOldResourceCleanupDelaySeconds = 15;
} // namespace
ContextCacheController::ScopedToken::ScopedToken() = default;
@@ -83,6 +86,7 @@ void ContextCacheController::ClientBecameNotVisible(
if (gr_context_)
gr_context_->freeGpuResources();
context_support_->SetAggressivelyFreeResources(true);
+ context_support_->FlushPendingWork();
}
}
@@ -109,6 +113,13 @@ void ContextCacheController::ClientBecameNotBusy(
DCHECK_GT(num_clients_busy_, 0u);
--num_clients_busy_;
+ // Here we ask GrContext to free any resources that haven't been used in
+ // a long while even if it is under budget.
+ if (gr_context_) {
+ gr_context_->performDeferredCleanup(
+ std::chrono::seconds(kOldResourceCleanupDelaySeconds));
+ }
+
// If we have become idle and we are visible, queue a task to drop resources
// after a delay. If are not visible, we have already dropped resources.
if (num_clients_busy_ == 0 && num_clients_visible_ > 0 && task_runner_) {
@@ -164,6 +175,7 @@ void ContextCacheController::OnIdle(uint32_t idle_generation) {
// Toggle SetAggressivelyFreeResources to drop command buffer data.
context_support_->SetAggressivelyFreeResources(true);
+ context_support_->FlushPendingWork();
context_support_->SetAggressivelyFreeResources(false);
callback_pending_ = false;
diff --git a/chromium/components/viz/common/gpu/context_provider.h b/chromium/components/viz/common/gpu/context_provider.h
index c00e9ff75b2..70c1ed5a9af 100644
--- a/chromium/components/viz/common/gpu/context_provider.h
+++ b/chromium/components/viz/common/gpu/context_provider.h
@@ -26,6 +26,7 @@ class Lock;
namespace gpu {
class ContextSupport;
struct GpuFeatureInfo;
+class SharedImageInterface;
namespace gles2 {
class GLES2Interface;
@@ -92,6 +93,8 @@ class VIZ_COMMON_EXPORT ContextProvider {
// nullptr if a GrContext fails to initialize on this context.
virtual class GrContext* GrContext() = 0;
+ virtual gpu::SharedImageInterface* SharedImageInterface() = 0;
+
// Returns the capabilities of the currently bound 3d context. The context
// provider must have been successfully bound to a thread before calling this.
virtual const gpu::Capabilities& ContextCapabilities() const = 0;
diff --git a/chromium/components/viz/common/gpu/raster_context_provider.h b/chromium/components/viz/common/gpu/raster_context_provider.h
index d4c7953cfe7..7aeacdd3ef9 100644
--- a/chromium/components/viz/common/gpu/raster_context_provider.h
+++ b/chromium/components/viz/common/gpu/raster_context_provider.h
@@ -26,6 +26,7 @@ class Lock;
namespace gpu {
class ContextSupport;
struct GpuFeatureInfo;
+class SharedImageInterface;
namespace gles2 {
class GLES2Interface;
@@ -99,6 +100,8 @@ class VIZ_COMMON_EXPORT RasterContextProvider {
// nullptr if a GrContext fails to initialize on this context.
virtual class GrContext* GrContext() = 0;
+ virtual gpu::SharedImageInterface* SharedImageInterface() = 0;
+
// Returns the capabilities of the currently bound 3d context. The context
// provider must have been successfully bound to a thread before calling this.
virtual const gpu::Capabilities& ContextCapabilities() const = 0;
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
index 992c7cd5186..7dee33e3d68 100644
--- a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc
+++ b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc
@@ -4,7 +4,6 @@
#include "components/viz/common/gpu/vulkan_in_process_context_provider.h"
#include "gpu/vulkan/buildflags.h"
-#include "gpu/vulkan/init/vulkan_factory.h"
#include "gpu/vulkan/vulkan_device_queue.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_implementation.h"
diff --git a/chromium/components/viz/common/hit_test/hit_test_region_list.h b/chromium/components/viz/common/hit_test/hit_test_region_list.h
index 64d33806fde..9c0d73cbb84 100644
--- a/chromium/components/viz/common/hit_test/hit_test_region_list.h
+++ b/chromium/components/viz/common/hit_test/hit_test_region_list.h
@@ -14,23 +14,28 @@
namespace viz {
-struct HitTestRegionFlags {
+// New flags must be added to GetFlagNames in hit_test_query.cc in order to be
+// displayed in hit-test debug logging.
+enum HitTestRegionFlags : uint32_t {
// Region maps to this surface (me).
- enum : uint32_t { kHitTestMine = 0x01 };
+ kHitTestMine = 0x01,
// Region ignored for hit testing (transparent backgrounds & hover:none).
- enum : uint32_t { kHitTestIgnore = 0x02 };
+ kHitTestIgnore = 0x02,
// Region maps to child surface (OOPIF).
- enum : uint32_t { kHitTestChildSurface = 0x04 };
+ kHitTestChildSurface = 0x04,
// Irregular boundary - send HitTestRequest to resolve.
- enum : uint32_t { kHitTestAsk = 0x08 };
+ kHitTestAsk = 0x08,
// TODO(varkha): Add other kHitTest* flags as necessary for other event
// sources such as mouse-wheel, stylus or perhaps even mouse-move.
// Hit-testing for mouse events.
- enum : uint32_t { kHitTestMouse = 0x10 };
+ kHitTestMouse = 0x10,
// Hit-testing for touch events.
- enum : uint32_t { kHitTestTouch = 0x20 };
+ kHitTestTouch = 0x20,
+
+ // Client hasn't submitted its own hit-test data yet.
+ kHitTestNotActive = 0x40,
};
struct HitTestRegion {
diff --git a/chromium/components/viz/common/quads/compositor_frame_metadata.h b/chromium/components/viz/common/quads/compositor_frame_metadata.h
index 1baee74d991..91be80ea6a6 100644
--- a/chromium/components/viz/common/quads/compositor_frame_metadata.h
+++ b/chromium/components/viz/common/quads/compositor_frame_metadata.h
@@ -132,16 +132,16 @@ class VIZ_COMMON_EXPORT CompositorFrameMetadata {
// determine if scrolling/scaling in a particular direction is possible.
float min_page_scale_factor = 0.f;
+ // Used to position the location top bar and page content, whose precise
+ // position is computed by the renderer compositor.
+ float top_controls_height = 0.f;
+ float top_controls_shown_ratio = 0.f;
+
#if defined(OS_ANDROID)
float max_page_scale_factor = 0.f;
gfx::SizeF root_layer_size;
bool root_overflow_y_hidden = false;
- // Used to position the Android location top bar and page content, whose
- // precise position is computed by the renderer compositor.
- float top_controls_height = 0.f;
- float top_controls_shown_ratio = 0.f;
-
// Used to position Android bottom bar, whose position is computed by the
// renderer compositor.
float bottom_controls_height = 0.f;
diff --git a/chromium/components/viz/common/quads/draw_quad_unittest.cc b/chromium/components/viz/common/quads/draw_quad_unittest.cc
index 82b9c82e1b5..cae3f216694 100644
--- a/chromium/components/viz/common/quads/draw_quad_unittest.cc
+++ b/chromium/components/viz/common/quads/draw_quad_unittest.cc
@@ -151,19 +151,19 @@ void CompareDrawQuad(DrawQuad* quad, DrawQuad* copy) {
{ QUAD_DATA quad_new->SetNew(shared_state, quad_rect, __VA_ARGS__); } \
SETUP_AND_COPY_QUAD_NEW(Type, quad_new);
-#define CREATE_QUAD_ALL_RP(Type, a, b, c, d, e, f, g, h, copy_a) \
+#define CREATE_QUAD_ALL_RP(Type, a, b, c, d, e, f, g, h, i, copy_a) \
Type* quad_all = render_pass->CreateAndAppendDrawQuad<Type>(); \
{ \
QUAD_DATA quad_all->SetAll(shared_state, quad_rect, quad_visible_rect, \
- needs_blending, a, b, c, d, e, f, g, h); \
+ needs_blending, a, b, c, d, e, f, g, h, i); \
} \
SETUP_AND_COPY_QUAD_ALL_RP(Type, quad_all, copy_a);
-#define CREATE_QUAD_NEW_RP(Type, a, b, c, d, e, f, g, h, i, copy_a) \
+#define CREATE_QUAD_NEW_RP(Type, a, b, c, d, e, f, g, h, i, j, copy_a) \
Type* quad_new = render_pass->CreateAndAppendDrawQuad<Type>(); \
{ \
QUAD_DATA quad_new->SetNew(shared_state, quad_rect, a, b, c, d, e, f, g, \
- h, i); \
+ h, i, j); \
} \
SETUP_AND_COPY_QUAD_NEW_RP(Type, quad_new, copy_a);
@@ -195,6 +195,7 @@ TEST(DrawQuadTest, CopyRenderPassDrawQuad) {
gfx::PointF filters_origin;
gfx::RectF tex_coord_rect(1, 1, 255, 254);
bool force_anti_aliasing_off = false;
+ float backdrop_filter_quality = 1.0f;
RenderPassId copied_render_pass_id = 235;
CREATE_SHARED_STATE();
@@ -202,7 +203,8 @@ TEST(DrawQuadTest, CopyRenderPassDrawQuad) {
CREATE_QUAD_NEW_RP(RenderPassDrawQuad, visible_rect, render_pass_id,
mask_resource_id, mask_uv_rect, mask_texture_size,
filters_scale, filters_origin, tex_coord_rect,
- force_anti_aliasing_off, copied_render_pass_id);
+ force_anti_aliasing_off, backdrop_filter_quality,
+ copied_render_pass_id);
EXPECT_EQ(DrawQuad::RENDER_PASS, copy_quad->material);
EXPECT_EQ(visible_rect, copy_quad->visible_rect);
EXPECT_EQ(copied_render_pass_id, copy_quad->render_pass_id);
@@ -217,7 +219,7 @@ TEST(DrawQuadTest, CopyRenderPassDrawQuad) {
CREATE_QUAD_ALL_RP(RenderPassDrawQuad, render_pass_id, mask_resource_id,
mask_uv_rect, mask_texture_size, filters_scale,
filters_origin, tex_coord_rect, force_anti_aliasing_off,
- copied_render_pass_id);
+ backdrop_filter_quality, copied_render_pass_id);
EXPECT_EQ(DrawQuad::RENDER_PASS, copy_quad->material);
EXPECT_EQ(copied_render_pass_id, copy_quad->render_pass_id);
EXPECT_EQ(mask_resource_id, copy_quad->mask_resource_id());
@@ -521,6 +523,7 @@ TEST_F(DrawQuadIteratorTest, RenderPassDrawQuad) {
gfx::PointF filters_origin(0.f, 0.f);
gfx::RectF tex_coord_rect(1.f, 1.f, 33.f, 19.f);
bool force_anti_aliasing_off = false;
+ float backdrop_filter_quality = 1.0f;
int copied_render_pass_id = 235;
@@ -528,7 +531,8 @@ TEST_F(DrawQuadIteratorTest, RenderPassDrawQuad) {
CREATE_QUAD_NEW_RP(RenderPassDrawQuad, visible_rect, render_pass_id,
mask_resource_id, mask_uv_rect, mask_texture_size,
filters_scale, filters_origin, tex_coord_rect,
- force_anti_aliasing_off, copied_render_pass_id);
+ force_anti_aliasing_off, backdrop_filter_quality,
+ copied_render_pass_id);
EXPECT_EQ(mask_resource_id, quad_new->mask_resource_id());
EXPECT_EQ(1, IterateAndCount(quad_new));
EXPECT_EQ(mask_resource_id + 1, quad_new->mask_resource_id());
@@ -538,7 +542,7 @@ TEST_F(DrawQuadIteratorTest, RenderPassDrawQuad) {
quad_new->SetNew(shared_state, quad_rect, visible_rect, render_pass_id,
new_mask_resource_id, mask_uv_rect, mask_texture_size,
filters_scale, filters_origin, tex_coord_rect,
- force_anti_aliasing_off);
+ force_anti_aliasing_off, backdrop_filter_quality);
EXPECT_EQ(0, IterateAndCount(quad_new));
EXPECT_EQ(0u, quad_new->mask_resource_id());
}
diff --git a/chromium/components/viz/common/quads/frame_deadline.cc b/chromium/components/viz/common/quads/frame_deadline.cc
new file mode 100644
index 00000000000..f5f5619b86a
--- /dev/null
+++ b/chromium/components/viz/common/quads/frame_deadline.cc
@@ -0,0 +1,43 @@
+// Copyright 2018 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/frame_deadline.h"
+
+#include <cinttypes>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+
+namespace viz {
+
+base::TimeTicks FrameDeadline::ToWallTime(
+ base::Optional<uint32_t> default_deadline_in_frames) const {
+ uint32_t deadline_in_frames = deadline_in_frames_;
+ if (use_default_lower_bound_deadline_) {
+ deadline_in_frames =
+ std::max(deadline_in_frames, default_deadline_in_frames.value_or(
+ std::numeric_limits<uint32_t>::max()));
+ }
+ return frame_start_time_ + deadline_in_frames * frame_interval_;
+}
+
+std::string FrameDeadline::ToString() const {
+ const base::TimeDelta start_time_delta =
+ frame_start_time_ - base::TimeTicks();
+ return base::StringPrintf(
+ "FrameDeadline(start time: %" PRId64
+ " ms, deadline in frames: %s, frame interval: %" PRId64 " ms)",
+ start_time_delta.InMilliseconds(),
+ use_default_lower_bound_deadline_
+ ? "unresolved"
+ : base::UintToString(deadline_in_frames_).c_str(),
+ frame_interval_.InMilliseconds());
+}
+
+std::ostream& operator<<(std::ostream& out,
+ const FrameDeadline& frame_deadline) {
+ return out << frame_deadline.ToString();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/quads/frame_deadline.h b/chromium/components/viz/common/quads/frame_deadline.h
index faa992f7565..eef30ed741a 100644
--- a/chromium/components/viz/common/quads/frame_deadline.h
+++ b/chromium/components/viz/common/quads/frame_deadline.h
@@ -8,9 +8,20 @@
#include "components/viz/common/viz_common_export.h"
#include "base/time/time.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
namespace viz {
+// FrameDeadline is a class that represents a CompositorFrame's deadline for
+// activation. The deadline consists of three components: start time, deadline
+// in frames, and frame interval. All three components are stored individually
+// in this class in order to allow resolution of this deadline that incorporates
+// a system default deadline in the Viz service. In particular, the computation
+// to translate FrameDeadline into wall time is:
+// if use system default lower bound deadline:
+// start time + max(deadline in frames, default deadline) * frame interval
+// else:
+// start time + deadline in frames * frame interval
class VIZ_COMMON_EXPORT FrameDeadline {
public:
FrameDeadline() = default;
@@ -27,6 +38,12 @@ class VIZ_COMMON_EXPORT FrameDeadline {
FrameDeadline& operator=(const FrameDeadline& other) = default;
+ // Converts this FrameDeadline object into a wall time given a system default
+ // deadline in frames.
+ base::TimeTicks ToWallTime(
+ base::Optional<uint32_t> default_deadline_in_frames =
+ base::nullopt) const;
+
bool operator==(const FrameDeadline& other) const {
return other.frame_start_time_ == frame_start_time_ &&
other.deadline_in_frames_ == deadline_in_frames_ &&
@@ -49,13 +66,18 @@ class VIZ_COMMON_EXPORT FrameDeadline {
return use_default_lower_bound_deadline_;
}
+ std::string ToString() const;
+
private:
base::TimeTicks frame_start_time_;
uint32_t deadline_in_frames_ = 0u;
- base::TimeDelta frame_interval_;
+ base::TimeDelta frame_interval_ = BeginFrameArgs::DefaultInterval();
bool use_default_lower_bound_deadline_ = true;
};
+VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
+ const FrameDeadline& frame_deadline);
+
} // namespace viz
#endif // COMPONENTS_VIZ_COMMON_QUADS_FRAME_DEADLINE_H_
diff --git a/chromium/components/viz/common/quads/render_pass_draw_quad.cc b/chromium/components/viz/common/quads/render_pass_draw_quad.cc
index 1f4a71b5705..931d1caded1 100644
--- a/chromium/components/viz/common/quads/render_pass_draw_quad.cc
+++ b/chromium/components/viz/common/quads/render_pass_draw_quad.cc
@@ -29,13 +29,15 @@ void RenderPassDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
const gfx::Vector2dF& filters_scale,
const gfx::PointF& filters_origin,
const gfx::RectF& tex_coord_rect,
- bool force_anti_aliasing_off) {
+ bool force_anti_aliasing_off,
+ float backdrop_filter_quality) {
DCHECK(render_pass_id);
bool needs_blending = true;
SetAll(shared_quad_state, rect, visible_rect, needs_blending, render_pass_id,
mask_resource_id, mask_uv_rect, mask_texture_size, filters_scale,
- filters_origin, tex_coord_rect, force_anti_aliasing_off);
+ filters_origin, tex_coord_rect, force_anti_aliasing_off,
+ backdrop_filter_quality);
}
void RenderPassDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
@@ -49,7 +51,8 @@ void RenderPassDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
const gfx::Vector2dF& filters_scale,
const gfx::PointF& filters_origin,
const gfx::RectF& tex_coord_rect,
- bool force_anti_aliasing_off) {
+ bool force_anti_aliasing_off,
+ float backdrop_filter_quality) {
DCHECK(render_pass_id);
DrawQuad::SetAll(shared_quad_state, DrawQuad::RENDER_PASS, rect, visible_rect,
@@ -63,6 +66,7 @@ void RenderPassDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
this->filters_origin = filters_origin;
this->tex_coord_rect = tex_coord_rect;
this->force_anti_aliasing_off = force_anti_aliasing_off;
+ this->backdrop_filter_quality = backdrop_filter_quality;
}
const RenderPassDrawQuad* RenderPassDrawQuad::MaterialCast(
@@ -80,6 +84,7 @@ void RenderPassDrawQuad::ExtendValue(
cc::MathUtil::AddToTracedValue("mask_uv_rect", mask_uv_rect, value);
cc::MathUtil::AddToTracedValue("tex_coord_rect", tex_coord_rect, value);
value->SetBoolean("force_anti_aliasing_off", force_anti_aliasing_off);
+ value->SetDouble("backdrop_filter_quality", backdrop_filter_quality);
}
} // namespace viz
diff --git a/chromium/components/viz/common/quads/render_pass_draw_quad.h b/chromium/components/viz/common/quads/render_pass_draw_quad.h
index c4e3f6deebe..fe641f91a22 100644
--- a/chromium/components/viz/common/quads/render_pass_draw_quad.h
+++ b/chromium/components/viz/common/quads/render_pass_draw_quad.h
@@ -37,7 +37,8 @@ class VIZ_COMMON_EXPORT RenderPassDrawQuad : public DrawQuad {
const gfx::Vector2dF& filters_scale,
const gfx::PointF& filters_origin,
const gfx::RectF& tex_coord_rect,
- bool force_anti_aliasing_off);
+ bool force_anti_aliasing_off,
+ float backdrop_filter_quality);
void SetAll(const SharedQuadState* shared_quad_state,
const gfx::Rect& rect,
@@ -50,7 +51,8 @@ class VIZ_COMMON_EXPORT RenderPassDrawQuad : public DrawQuad {
const gfx::Vector2dF& filters_scale,
const gfx::PointF& filters_origin,
const gfx::RectF& tex_coord_rect,
- bool force_anti_aliasing_off);
+ bool force_anti_aliasing_off,
+ float backdrop_filter_quality);
RenderPassId render_pass_id;
gfx::RectF mask_uv_rect;
@@ -70,6 +72,8 @@ class VIZ_COMMON_EXPORT RenderPassDrawQuad : public DrawQuad {
bool force_anti_aliasing_off;
+ float backdrop_filter_quality;
+
ResourceId mask_resource_id() const {
return resources.ids[kMaskResourceIdIndex];
}
diff --git a/chromium/components/viz/common/quads/render_pass_unittest.cc b/chromium/components/viz/common/quads/render_pass_unittest.cc
index 0f919937d81..965a64156ae 100644
--- a/chromium/components/viz/common/quads/render_pass_unittest.cc
+++ b/chromium/components/viz/common/quads/render_pass_unittest.cc
@@ -215,7 +215,7 @@ TEST(RenderPassTest, CopyAllShouldBeIdentical) {
pass_quad->SetNew(pass->shared_quad_state_list.back(), contrib_output_rect,
contrib_output_rect, contrib_id, 0, gfx::RectF(),
gfx::Size(), gfx::Vector2dF(), gfx::PointF(), gfx::RectF(),
- false);
+ false, 1.0f);
pass_list.push_back(std::move(pass));
pass_list.push_back(std::move(contrib));
diff --git a/chromium/components/viz/common/resources/resource_format_utils.cc b/chromium/components/viz/common/resources/resource_format_utils.cc
index b8b89bcc167..57c8212d649 100644
--- a/chromium/components/viz/common/resources/resource_format_utils.cc
+++ b/chromium/components/viz/common/resources/resource_format_utils.cc
@@ -211,8 +211,6 @@ gfx::BufferFormat BufferFormat(ResourceFormat format) {
return gfx::BufferFormat::RGBA_4444;
case RGBA_8888:
return gfx::BufferFormat::RGBA_8888;
- case ETC1:
- return gfx::BufferFormat::ETC1;
case RGBA_F16:
return gfx::BufferFormat::RGBA_F16;
case BGR_565:
@@ -233,6 +231,7 @@ gfx::BufferFormat BufferFormat(ResourceFormat format) {
return gfx::BufferFormat::YUV_420_BIPLANAR;
case UYVY_422:
return gfx::BufferFormat::UYVY_422;
+ case ETC1:
case ALPHA_8:
case LUMINANCE_8:
case RGB_565:
@@ -296,11 +295,11 @@ bool IsGpuMemoryBufferFormatSupported(ResourceFormat format) {
case R16_EXT:
case RGBA_4444:
case RGBA_8888:
- case ETC1:
case RGBA_F16:
return true;
// These formats have no BufferFormat equivalent or are only used
// for external textures, or have no GL equivalent formats.
+ case ETC1:
case ALPHA_8:
case LUMINANCE_8:
case RGB_565:
@@ -361,8 +360,6 @@ ResourceFormat GetResourceFormat(gfx::BufferFormat format) {
return RGBA_4444;
case gfx::BufferFormat::RGBA_8888:
return RGBA_8888;
- case gfx::BufferFormat::ETC1:
- return ETC1;
case gfx::BufferFormat::RGBA_F16:
return RGBA_F16;
case gfx::BufferFormat::BGR_565:
diff --git a/chromium/components/viz/common/surfaces/local_surface_id.cc b/chromium/components/viz/common/surfaces/local_surface_id.cc
index 897e83c8336..d5fc63330f7 100644
--- a/chromium/components/viz/common/surfaces/local_surface_id.cc
+++ b/chromium/components/viz/common/surfaces/local_surface_id.cc
@@ -38,4 +38,8 @@ bool LocalSurfaceId::IsSameOrNewerThan(const LocalSurfaceId& other) const {
return IsNewerThan(other) || *this == other;
}
+LocalSurfaceId LocalSurfaceId::ToSmallestId() const {
+ return LocalSurfaceId(1, 1, embed_token_);
+}
+
} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/local_surface_id.h b/chromium/components/viz/common/surfaces/local_surface_id.h
index 88cccf2fb6d..7a832c0e210 100644
--- a/chromium/components/viz/common/surfaces/local_surface_id.h
+++ b/chromium/components/viz/common/surfaces/local_surface_id.h
@@ -144,6 +144,10 @@ class VIZ_COMMON_EXPORT LocalSurfaceId {
// it.
bool IsSameOrNewerThan(const LocalSurfaceId& other) const;
+ // Returns the smallest valid LocalSurfaceId with the same embed token as this
+ // LocalSurfaceID.
+ LocalSurfaceId ToSmallestId() const;
+
private:
friend struct mojo::StructTraits<mojom::LocalSurfaceIdDataView,
LocalSurfaceId>;
diff --git a/chromium/components/viz/common/surfaces/surface_id.cc b/chromium/components/viz/common/surfaces/surface_id.cc
index 45b8850c350..02dc2a48498 100644
--- a/chromium/components/viz/common/surfaces/surface_id.cc
+++ b/chromium/components/viz/common/surfaces/surface_id.cc
@@ -32,6 +32,10 @@ uint32_t SurfaceId::ManhattanDistanceTo(const SurfaceId& other) const {
other.local_surface_id().child_sequence_number()));
}
+SurfaceId SurfaceId::ToSmallestId() const {
+ return SurfaceId(frame_sink_id_, local_surface_id_.ToSmallestId());
+}
+
std::ostream& operator<<(std::ostream& out, const SurfaceId& surface_id) {
return out << surface_id.ToString();
}
@@ -42,4 +46,8 @@ bool SurfaceId::IsNewerThan(const SurfaceId& other) const {
return local_surface_id_.IsNewerThan(other.local_surface_id());
}
+bool SurfaceId::IsSameOrNewerThan(const SurfaceId& other) const {
+ return (*this == other) || IsNewerThan(other);
+}
+
} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/surface_id.h b/chromium/components/viz/common/surfaces/surface_id.h
index a1ddf1753eb..f0dd7c4bf5b 100644
--- a/chromium/components/viz/common/surfaces/surface_id.h
+++ b/chromium/components/viz/common/surfaces/surface_id.h
@@ -62,10 +62,18 @@ class VIZ_COMMON_EXPORT SurfaceId {
// Returns whether this SurfaceId was generated after |other|.
bool IsNewerThan(const SurfaceId& other) const;
+ // Returns whether this SurfaceId is the same as or was generated after
+ // |other|.
+ bool IsSameOrNewerThan(const SurfaceId& other) const;
+
// Compare this SurfaceId with |other| and returns the difference between the
// parent sequence numbers plus the difference between child sequence numbers.
uint32_t ManhattanDistanceTo(const SurfaceId& other) const;
+ // Returns the smallest valid SurfaceId with the same FrameSinkId and embed
+ // token as this SurfaceId.
+ SurfaceId ToSmallestId() const;
+
bool operator==(const SurfaceId& other) const {
return frame_sink_id_ == other.frame_sink_id_ &&
local_surface_id_ == other.local_surface_id_;
diff --git a/chromium/components/viz/common/yuv_readback_unittest.cc b/chromium/components/viz/common/yuv_readback_unittest.cc
index 9bc57fcad1b..cb392fb8105 100644
--- a/chromium/components/viz/common/yuv_readback_unittest.cc
+++ b/chromium/components/viz/common/yuv_readback_unittest.cc
@@ -52,7 +52,6 @@ class YUVReadbackTest : public testing::Test {
attributes, gpu::SharedMemoryLimits(),
nullptr, /* gpu_memory_buffer_manager */
nullptr, /* image_factory */
- nullptr /* gpu_channel_manager_delegate */,
base::ThreadTaskRunnerHandle::Get());
DCHECK_EQ(result, gpu::ContextResult::kSuccess);
gl_ = context_->GetImplementation();
diff --git a/chromium/components/viz/host/BUILD.gn b/chromium/components/viz/host/BUILD.gn
index bed0815f82e..5b4427a1a04 100644
--- a/chromium/components/viz/host/BUILD.gn
+++ b/chromium/components/viz/host/BUILD.gn
@@ -80,6 +80,7 @@ viz_source_set("unit_tests") {
"//base",
"//base/test:test_support",
"//components/viz/test:test_support",
+ "//gpu",
"//gpu/ipc/host",
"//mojo/public/cpp/bindings",
"//services/viz/privileged/interfaces",
diff --git a/chromium/components/viz/host/DEPS b/chromium/components/viz/host/DEPS
index 036d1f1f77a..d8f55bbb226 100644
--- a/chromium/components/viz/host/DEPS
+++ b/chromium/components/viz/host/DEPS
@@ -11,7 +11,6 @@ include_rules = [
"+gpu/ipc/client",
"+gpu/ipc/common",
"+gpu/ipc/host",
- "+ipc",
"+mojo/public/cpp",
"+services/viz/privileged/interfaces",
"+services/viz/public/interfaces",
@@ -20,6 +19,7 @@ include_rules = [
"+skia",
"+third_party/skia",
"+ui/accelerated_widget_mac",
+ "+ui/ozone/public",
]
specific_include_rules = {
diff --git a/chromium/components/viz/host/client_frame_sink_video_capturer.cc b/chromium/components/viz/host/client_frame_sink_video_capturer.cc
index 95a2c4036a9..387f8c6bbb7 100644
--- a/chromium/components/viz/host/client_frame_sink_video_capturer.cc
+++ b/chromium/components/viz/host/client_frame_sink_video_capturer.cc
@@ -29,7 +29,7 @@ ClientFrameSinkVideoCapturer::~ClientFrameSinkVideoCapturer() {
}
void ClientFrameSinkVideoCapturer::SetFormat(media::VideoPixelFormat format,
- media::ColorSpace color_space) {
+ gfx::ColorSpace color_space) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
format_.emplace(format, color_space);
@@ -133,7 +133,7 @@ ClientFrameSinkVideoCapturer::CreateOverlay(int32_t stacking_index) {
ClientFrameSinkVideoCapturer::Format::Format(
media::VideoPixelFormat pixel_format,
- media::ColorSpace color_space)
+ gfx::ColorSpace color_space)
: pixel_format(pixel_format), color_space(color_space) {}
ClientFrameSinkVideoCapturer::ResolutionConstraints::ResolutionConstraints(
diff --git a/chromium/components/viz/host/client_frame_sink_video_capturer.h b/chromium/components/viz/host/client_frame_sink_video_capturer.h
index 786dc3ef9a6..c5717d7a4bb 100644
--- a/chromium/components/viz/host/client_frame_sink_video_capturer.h
+++ b/chromium/components/viz/host/client_frame_sink_video_capturer.h
@@ -71,7 +71,7 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer
~ClientFrameSinkVideoCapturer() override;
// See FrameSinkVideoCapturer for documentation.
- void SetFormat(media::VideoPixelFormat format, media::ColorSpace color_space);
+ void SetFormat(media::VideoPixelFormat format, gfx::ColorSpace color_space);
void SetMinCapturePeriod(base::TimeDelta min_capture_period);
void SetMinSizeChangePeriod(base::TimeDelta min_period);
void SetResolutionConstraints(const gfx::Size& min_size,
@@ -97,10 +97,10 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer
private:
struct Format {
- Format(media::VideoPixelFormat pixel_format, media::ColorSpace color_space);
+ Format(media::VideoPixelFormat pixel_format, gfx::ColorSpace color_space);
media::VideoPixelFormat pixel_format;
- media::ColorSpace color_space;
+ gfx::ColorSpace color_space;
};
struct ResolutionConstraints {
diff --git a/chromium/components/viz/host/gpu_client.cc b/chromium/components/viz/host/gpu_client.cc
index faed754380d..2dd5619fcb8 100644
--- a/chromium/components/viz/host/gpu_client.cc
+++ b/chromium/components/viz/host/gpu_client.cc
@@ -5,6 +5,7 @@
#include "components/viz/host/gpu_client.h"
#include "base/numerics/checked_math.h"
+#include "components/viz/host/gpu_host_impl.h"
#include "components/viz/host/host_gpu_memory_buffer_manager.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "gpu/ipc/common/gpu_memory_buffer_impl.h"
@@ -75,18 +76,22 @@ void GpuClient::SetConnectionErrorHandler(
connection_error_handler_ = std::move(connection_error_handler);
}
+base::WeakPtr<GpuClient> GpuClient::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
void GpuClient::OnEstablishGpuChannel(
mojo::ScopedMessagePipeHandle channel_handle,
const gpu::GPUInfo& gpu_info,
const gpu::GpuFeatureInfo& gpu_feature_info,
- GpuClientDelegate::EstablishGpuChannelStatus status) {
+ GpuHostImpl::EstablishChannelStatus status) {
DCHECK_EQ(channel_handle.is_valid(),
- status == GpuClientDelegate::EstablishGpuChannelStatus::kSuccess);
+ status == GpuHostImpl::EstablishChannelStatus::kSuccess);
gpu_channel_requested_ = false;
EstablishGpuChannelCallback callback = std::move(callback_);
DCHECK(!callback_);
- if (status == GpuClientDelegate::EstablishGpuChannelStatus::kGpuHostInvalid) {
+ if (status == GpuHostImpl::EstablishChannelStatus::kGpuHostInvalid) {
// GPU process may have crashed or been killed. Try again.
EstablishGpuChannel(std::move(callback));
return;
@@ -97,7 +102,7 @@ void GpuClient::OnEstablishGpuChannel(
gpu_feature_info);
return;
}
- if (status == GpuClientDelegate::EstablishGpuChannelStatus::kSuccess) {
+ if (status == GpuHostImpl::EstablishChannelStatus::kSuccess) {
// This is the case we pre-establish a channel before a request arrives.
// Cache the channel for a future request.
channel_handle_ = std::move(channel_handle);
@@ -136,26 +141,39 @@ void GpuClient::EstablishGpuChannel(EstablishGpuChannelCallback callback) {
}
return;
}
+
+ GpuHostImpl* gpu_host = delegate_->EnsureGpuHost();
+ if (!gpu_host) {
+ if (callback) {
+ std::move(callback).Run(client_id_, mojo::ScopedMessagePipeHandle(),
+ gpu::GPUInfo(), gpu::GpuFeatureInfo());
+ }
+ return;
+ }
+
callback_ = std::move(callback);
if (gpu_channel_requested_)
return;
gpu_channel_requested_ = true;
- delegate_->EstablishGpuChannel(
- client_id_, client_tracing_id_,
+ const bool is_gpu_host = false;
+ gpu_host->EstablishGpuChannel(
+ client_id_, client_tracing_id_, is_gpu_host,
base::BindOnce(&GpuClient::OnEstablishGpuChannel,
weak_factory_.GetWeakPtr()));
}
void GpuClient::CreateJpegDecodeAccelerator(
media::mojom::JpegDecodeAcceleratorRequest jda_request) {
- if (auto* gpu_service = delegate_->EnsureGpuService())
- gpu_service->CreateJpegDecodeAccelerator(std::move(jda_request));
+ if (auto* gpu_host = delegate_->EnsureGpuHost()) {
+ gpu_host->gpu_service()->CreateJpegDecodeAccelerator(
+ std::move(jda_request));
+ }
}
void GpuClient::CreateVideoEncodeAcceleratorProvider(
media::mojom::VideoEncodeAcceleratorProviderRequest vea_provider_request) {
- if (auto* gpu_service = delegate_->EnsureGpuService()) {
- gpu_service->CreateVideoEncodeAcceleratorProvider(
+ if (auto* gpu_host = delegate_->EnsureGpuHost()) {
+ gpu_host->gpu_service()->CreateVideoEncodeAcceleratorProvider(
std::move(vea_provider_request));
}
}
diff --git a/chromium/components/viz/host/gpu_client.h b/chromium/components/viz/host/gpu_client.h
index 79dd89ea3b7..fdfcb9fd2f5 100644
--- a/chromium/components/viz/host/gpu_client.h
+++ b/chromium/components/viz/host/gpu_client.h
@@ -8,6 +8,7 @@
#include "base/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "components/viz/host/gpu_client_delegate.h"
+#include "components/viz/host/gpu_host_impl.h"
#include "components/viz/host/viz_host_export.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/ws/public/mojom/gpu.mojom.h"
@@ -36,6 +37,8 @@ class VIZ_HOST_EXPORT GpuClient : public ws::mojom::GpuMemoryBufferFactory,
void SetConnectionErrorHandler(
ConnectionErrorHandlerClosure connection_error_handler);
+ base::WeakPtr<GpuClient> GetWeakPtr();
+
// ws::mojom::GpuMemoryBufferFactory overrides:
void CreateGpuMemoryBuffer(
gfx::GpuMemoryBufferId id,
@@ -65,11 +68,10 @@ class VIZ_HOST_EXPORT GpuClient : public ws::mojom::GpuMemoryBufferFactory,
kConnectionLost
};
void OnError(ErrorReason reason);
- void OnEstablishGpuChannel(
- mojo::ScopedMessagePipeHandle channel_handle,
- const gpu::GPUInfo& gpu_info,
- const gpu::GpuFeatureInfo& gpu_feature_info,
- GpuClientDelegate::EstablishGpuChannelStatus status);
+ void OnEstablishGpuChannel(mojo::ScopedMessagePipeHandle channel_handle,
+ const gpu::GPUInfo& gpu_info,
+ const gpu::GpuFeatureInfo& gpu_feature_info,
+ GpuHostImpl::EstablishChannelStatus status);
void OnCreateGpuMemoryBuffer(CreateGpuMemoryBufferCallback callback,
gfx::GpuMemoryBufferHandle handle);
void ClearCallback();
diff --git a/chromium/components/viz/host/gpu_client_delegate.h b/chromium/components/viz/host/gpu_client_delegate.h
index 51a283a3f5a..19f08b2a1e1 100644
--- a/chromium/components/viz/host/gpu_client_delegate.h
+++ b/chromium/components/viz/host/gpu_client_delegate.h
@@ -8,50 +8,21 @@
#include "base/callback_forward.h"
#include "mojo/public/cpp/system/message_pipe.h"
-namespace gpu {
-struct GPUInfo;
-struct GpuFeatureInfo;
-} // namespace gpu
-
namespace viz {
-namespace mojom {
-class GpuService;
-}
+class GpuHostImpl;
class HostGpuMemoryBufferManager;
-// Delegate interface that GpuClientImpl uses to get the current GpuService
-// instance and establish GPU channel for a client. These functions are
-// guaranteed to be called on the thread corresponding to GpuClientImpl's task
-// runner.
+// Delegate interface that GpuClient uses to get the current GpuHost and/or
+// GpuMemoryBufferManager instances. These functions are guaranteed to be called
+// on the thread corresponding to GpuClient's task runner.
class GpuClientDelegate {
public:
- enum class EstablishGpuChannelStatus {
- kGpuAccessDenied, // GPU access was not allowed.
- kGpuHostInvalid, // Request failed because the gpu host became invalid
- // while processing the request (e.g. the gpu process may
- // have been killed). The caller should normally make
- // another request to establish a new channel.
- kSuccess,
- };
-
- using EstablishGpuChannelCallback =
- base::OnceCallback<void(mojo::ScopedMessagePipeHandle channel_handle,
- const gpu::GPUInfo&,
- const gpu::GpuFeatureInfo&,
- EstablishGpuChannelStatus status)>;
-
virtual ~GpuClientDelegate() {}
- // Returns the current instance of GPU service. If GPU service is not running,
+ // Returns the current instance of GpuHostImpl. If GPU service is not running,
// tries to launch it. If the launch is unsuccessful, returns nullptr.
- virtual mojom::GpuService* EnsureGpuService() = 0;
-
- // Sends a request to establish a GPU channel for a client to the GPU service.
- // If GPU service is not running, tries to launch it and send the request.
- virtual void EstablishGpuChannel(int client_id,
- uint64_t client_tracing_id,
- EstablishGpuChannelCallback callback) = 0;
+ virtual GpuHostImpl* EnsureGpuHost() = 0;
// Returns the current HostGpuMemoryBufferManager instance.
virtual HostGpuMemoryBufferManager* GetGpuMemoryBufferManager() = 0;
diff --git a/chromium/components/viz/host/gpu_host_impl.cc b/chromium/components/viz/host/gpu_host_impl.cc
index d16453fdca8..9e034f9afdf 100644
--- a/chromium/components/viz/host/gpu_host_impl.cc
+++ b/chromium/components/viz/host/gpu_host_impl.cc
@@ -12,6 +12,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/viz/common/features.h"
@@ -21,8 +22,10 @@
#include "gpu/config/gpu_info.h"
#include "gpu/ipc/common/gpu_client_ids.h"
#include "gpu/ipc/host/shader_disk_cache.h"
-#include "ipc/ipc_channel.h"
+#include "ui/base/ui_base_features.h"
#include "ui/gfx/font_render_params.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_platform.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
@@ -40,6 +43,7 @@ namespace {
class FontRenderParams {
public:
void Set(const gfx::FontRenderParams& params);
+ void Reset();
const base::Optional<gfx::FontRenderParams>& Get();
private:
@@ -59,6 +63,11 @@ void FontRenderParams::Set(const gfx::FontRenderParams& params) {
params_ = params;
}
+void FontRenderParams::Reset() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ params_ = base::nullopt;
+}
+
const base::Optional<gfx::FontRenderParams>& FontRenderParams::Get() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return params_;
@@ -75,8 +84,69 @@ FontRenderParams& GetFontRenderParams() {
return *instance;
}
+#if defined(USE_OZONE)
+// Helper to register Mus/conventional thread bouncers for ozone startup.
+void OzoneRegisterStartupCallbackHelper(
+ scoped_refptr<base::SingleThreadTaskRunner> host_thread_task_runner,
+ base::OnceCallback<void(ui::OzonePlatform*)> callback) {
+ // The callback registered in ozone can be called in any thread. So use an
+ // intermediary callback that bounces to the GpuHost thread if needed, before
+ // running the callback.
+ auto bounce_callback = base::BindOnce(
+ [](base::SingleThreadTaskRunner* host_thread_task_runner,
+ base::OnceCallback<void(ui::OzonePlatform*)> callback,
+ ui::OzonePlatform* platform) {
+ if (host_thread_task_runner->BelongsToCurrentThread()) {
+ std::move(callback).Run(platform);
+ } else {
+ host_thread_task_runner->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), platform));
+ }
+ },
+ base::RetainedRef(host_thread_task_runner), std::move(callback));
+ ui::OzonePlatform::RegisterStartupCallback(std::move(bounce_callback));
+}
+#endif // defined(USE_OZONE)
+
} // namespace
+VizMainWrapper::VizMainWrapper(mojom::VizMainPtr viz_main_ptr)
+ : viz_main_ptr_(std::move(viz_main_ptr)) {}
+
+VizMainWrapper::VizMainWrapper(
+ mojom::VizMainAssociatedPtr viz_main_associated_ptr)
+ : viz_main_associated_ptr_(std::move(viz_main_associated_ptr)) {}
+
+VizMainWrapper::~VizMainWrapper() = default;
+
+void VizMainWrapper::CreateGpuService(
+ mojom::GpuServiceRequest request,
+ mojom::GpuHostPtr gpu_host,
+ discardable_memory::mojom::DiscardableSharedMemoryManagerPtr
+ discardable_memory_manager,
+ mojo::ScopedSharedBufferHandle activity_flags,
+ gfx::FontRenderParams::SubpixelRendering subpixel_rendering) {
+ if (viz_main_ptr_) {
+ viz_main_ptr_->CreateGpuService(std::move(request), std::move(gpu_host),
+ std::move(discardable_memory_manager),
+ std::move(activity_flags),
+ subpixel_rendering);
+ } else {
+ viz_main_associated_ptr_->CreateGpuService(
+ std::move(request), std::move(gpu_host),
+ std::move(discardable_memory_manager), std::move(activity_flags),
+ subpixel_rendering);
+ }
+}
+
+void VizMainWrapper::CreateFrameSinkManager(
+ mojom::FrameSinkManagerParamsPtr params) {
+ if (viz_main_ptr_)
+ viz_main_ptr_->CreateFrameSinkManager(std::move(params));
+ else
+ viz_main_associated_ptr_->CreateFrameSinkManager(std::move(params));
+}
+
GpuHostImpl::InitParams::InitParams() = default;
GpuHostImpl::InitParams::InitParams(InitParams&&) = default;
@@ -84,17 +154,15 @@ GpuHostImpl::InitParams::InitParams(InitParams&&) = default;
GpuHostImpl::InitParams::~InitParams() = default;
GpuHostImpl::GpuHostImpl(Delegate* delegate,
- IPC::Channel* channel,
+ std::unique_ptr<VizMainWrapper> viz_main_ptr,
InitParams params)
: delegate_(delegate),
- channel_(channel),
+ viz_main_ptr_(std::move(viz_main_ptr)),
params_(std::move(params)),
+ host_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
gpu_host_binding_(this),
weak_ptr_factory_(this) {
DCHECK(delegate_);
- DCHECK(channel_);
- channel_->GetAssociatedInterfaceSupport()->GetRemoteAssociatedInterface(
- &viz_main_ptr_);
mojom::GpuHostPtr host_proxy;
gpu_host_binding_.Bind(mojo::MakeRequest(&host_proxy));
@@ -108,9 +176,15 @@ GpuHostImpl::GpuHostImpl(Delegate* delegate,
mojo::MakeRequest(&gpu_service_ptr_), std::move(host_proxy),
std::move(discardable_manager_ptr), activity_flags_.CloneHandle(),
GetFontRenderParams().Get()->subpixel_rendering);
+
+#if defined(USE_OZONE)
+ InitOzone();
+#endif // defined(USE_OZONE)
}
-GpuHostImpl::~GpuHostImpl() = default;
+GpuHostImpl::~GpuHostImpl() {
+ SendOutstandingReplies();
+}
// static
void GpuHostImpl::InitFontRenderParams(const gfx::FontRenderParams& params) {
@@ -118,6 +192,12 @@ void GpuHostImpl::InitFontRenderParams(const gfx::FontRenderParams& params) {
GetFontRenderParams().Set(params);
}
+// static
+void GpuHostImpl::ResetFontRenderParams() {
+ DCHECK(GetFontRenderParams().Get());
+ GetFontRenderParams().Reset();
+}
+
void GpuHostImpl::OnProcessLaunched(base::ProcessId pid) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(base::kNullProcessId, pid_);
@@ -145,12 +225,16 @@ void GpuHostImpl::OnProcessCrashed() {
}
}
+void GpuHostImpl::AddConnectionErrorHandler(base::OnceClosure handler) {
+ connection_error_handlers_.push_back(std::move(handler));
+}
+
void GpuHostImpl::BlockLiveOffscreenContexts() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto iter = urls_with_live_offscreen_contexts_.begin();
iter != urls_with_live_offscreen_contexts_.end(); ++iter) {
- delegate_->BlockDomainFrom3DAPIs(*iter, Delegate::DomainGuilt::kUnknown);
+ delegate_->BlockDomainFrom3DAPIs(*iter, gpu::DomainGuilt::kUnknown);
}
}
@@ -224,6 +308,10 @@ void GpuHostImpl::EstablishGpuChannel(int client_id,
void GpuHostImpl::SendOutstandingReplies() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ for (auto& handler : connection_error_handlers_)
+ std::move(handler).Run();
+ connection_error_handlers_.clear();
+
// Send empty channel handles for all EstablishChannel requests.
while (!channel_requests_.empty()) {
auto callback = std::move(channel_requests_.front());
@@ -234,12 +322,89 @@ void GpuHostImpl::SendOutstandingReplies() {
}
}
+void GpuHostImpl::BindInterface(const std::string& interface_name,
+ mojo::ScopedMessagePipeHandle interface_pipe) {
+ delegate_->BindInterface(interface_name, std::move(interface_pipe));
+}
+
mojom::GpuService* GpuHostImpl::gpu_service() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(gpu_service_ptr_.is_bound());
return gpu_service_ptr_.get();
}
+#if defined(USE_OZONE)
+void GpuHostImpl::InitOzone() {
+ // Ozone needs to send the primary DRM device to GPU service as early as
+ // possible to ensure the latter always has a valid device.
+ // https://crbug.com/608839
+ // If the OzonePlatform is not created yet, defer the callback until
+ // OzonePlatform instance is created.
+ //
+ // The Ozone/Wayland requires mojo communication to be established to be
+ // functional with a separate gpu process. Thus, using the PlatformProperties,
+ // check if there is such a requirement.
+ if (features::IsOzoneDrmMojo() || ui::OzonePlatform::EnsureInstance()
+ ->GetPlatformProperties()
+ .requires_mojo) {
+ // TODO(rjkroege): Remove the legacy IPC code paths when no longer
+ // necessary. https://crbug.com/806092
+ auto interface_binder = base::BindRepeating(&GpuHostImpl::BindInterface,
+ weak_ptr_factory_.GetWeakPtr());
+ auto terminate_callback = base::BindOnce(&GpuHostImpl::TerminateGpuProcess,
+ weak_ptr_factory_.GetWeakPtr());
+
+ auto startup_callback = base::BindOnce(
+ [](const base::RepeatingCallback<void(const std::string&,
+ mojo::ScopedMessagePipeHandle)>&
+ interface_binder,
+ base::OnceCallback<void(const std::string&)> terminate_callback,
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> host_thread_task_runner,
+ ui::OzonePlatform* platform) {
+ DCHECK(host_thread_task_runner->BelongsToCurrentThread());
+ platform->GetGpuPlatformSupportHost()->OnGpuServiceLaunched(
+ main_thread_task_runner, host_thread_task_runner,
+ interface_binder, std::move(terminate_callback));
+ },
+ interface_binder, std::move(terminate_callback),
+ params_.main_thread_task_runner, host_thread_task_runner_);
+ OzoneRegisterStartupCallbackHelper(host_thread_task_runner_,
+ std::move(startup_callback));
+ } else {
+ auto send_callback = base::BindRepeating(
+ [](base::WeakPtr<GpuHostImpl> host, IPC::Message* message) {
+ if (host)
+ host->delegate_->SendGpuProcessMessage(message);
+ else
+ delete message;
+ },
+ weak_ptr_factory_.GetWeakPtr());
+ // Create the callback that should run on the current thread.
+ auto startup_callback = base::BindOnce(
+ [](int host_id,
+ const base::RepeatingCallback<void(IPC::Message*)>& send_callback,
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> host_thread_task_runner,
+ ui::OzonePlatform* platform) {
+ DCHECK(host_thread_task_runner->BelongsToCurrentThread());
+ platform->GetGpuPlatformSupportHost()->OnGpuProcessLaunched(
+ host_id, main_thread_task_runner, host_thread_task_runner,
+ send_callback);
+ },
+ params_.restart_id, send_callback, params_.main_thread_task_runner,
+ host_thread_task_runner_);
+ OzoneRegisterStartupCallbackHelper(host_thread_task_runner_,
+ std::move(startup_callback));
+ }
+}
+
+void GpuHostImpl::TerminateGpuProcess(const std::string& message) {
+ delegate_->TerminateGpuProcess(message);
+}
+
+#endif // defined(USE_OZONE)
+
std::string GpuHostImpl::GetShaderPrefixKey() {
if (shader_prefix_key_.empty()) {
const gpu::GPUInfo& info = delegate_->GetGPUInfo();
@@ -252,9 +417,6 @@ std::string GpuHostImpl::GetShaderPrefixKey() {
#if defined(OS_ANDROID)
std::string build_fp =
base::android::BuildInfo::GetInstance()->android_build_fp();
- // TODO(ericrk): Remove this after it's up for a few days.
- // https://crbug.com/699122.
- CHECK(!build_fp.empty());
shader_prefix_key_ += "-" + build_fp;
#endif
}
@@ -326,7 +488,6 @@ void GpuHostImpl::DidInitialize(
const base::Optional<gpu::GpuFeatureInfo>&
gpu_feature_info_for_hardware_gpu) {
UMA_HISTOGRAM_BOOLEAN("GPU.GPUProcessInitialized", true);
- initialized_ = true;
// Set GPU driver bug workaround flags that are checked on the browser side.
wake_up_gpu_before_drawing_ =
@@ -335,7 +496,7 @@ void GpuHostImpl::DidInitialize(
gpu_feature_info.IsWorkaroundEnabled(
gpu::DONT_DISABLE_WEBGL_WHEN_COMPOSITOR_CONTEXT_LOST);
- delegate_->UpdateGpuInfo(gpu_info, gpu_feature_info,
+ delegate_->DidInitialize(gpu_info, gpu_feature_info,
gpu_info_for_hardware_gpu,
gpu_feature_info_for_hardware_gpu);
}
@@ -386,10 +547,10 @@ void GpuHostImpl::DidLoseContext(bool offscreen,
return;
}
- Delegate::DomainGuilt guilt = Delegate::DomainGuilt::kUnknown;
+ gpu::DomainGuilt guilt = gpu::DomainGuilt::kUnknown;
switch (reason) {
case gpu::error::kGuilty:
- guilt = Delegate::DomainGuilt::kKnown;
+ guilt = gpu::DomainGuilt::kKnown;
break;
// Treat most other error codes as though they had unknown provenance.
// In practice this doesn't affect the user experience. A lost context
@@ -413,17 +574,15 @@ void GpuHostImpl::DisableGpuCompositing() {
delegate_->DisableGpuCompositing();
}
+#if defined(OS_WIN)
void GpuHostImpl::SetChildSurface(gpu::SurfaceHandle parent,
gpu::SurfaceHandle child) {
-#if defined(OS_WIN)
if (pid_ != base::kNullProcessId) {
gfx::RenderingWindowManager::GetInstance()->RegisterChild(
parent, child, /*expected_child_process_id=*/pid_);
}
-#else
- NOTREACHED();
-#endif
}
+#endif // defined(OS_WIN)
void GpuHostImpl::StoreShaderToDisk(int32_t client_id,
const std::string& key,
diff --git a/chromium/components/viz/host/gpu_host_impl.h b/chromium/components/viz/host/gpu_host_impl.h
index 6d9c648a720..54f6e5db9a9 100644
--- a/chromium/components/viz/host/gpu_host_impl.h
+++ b/chromium/components/viz/host/gpu_host_impl.h
@@ -9,7 +9,9 @@
#include <queue>
#include <set>
#include <string>
+#include <vector>
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
@@ -20,6 +22,7 @@
#include "components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom.h"
#include "components/viz/host/viz_host_export.h"
#include "gpu/command_buffer/common/activity_flags.h"
+#include "gpu/config/gpu_domain_guilt.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom.h"
@@ -37,28 +40,39 @@ class ShaderCacheFactory;
class ShaderDiskCache;
} // namespace gpu
-namespace IPC {
-class Channel;
-}
-
namespace viz {
+// Contains either an interface or an associated interface pointer to a
+// mojom::VizMain implementation and routes the requests appropriately.
+class VIZ_HOST_EXPORT VizMainWrapper {
+ public:
+ explicit VizMainWrapper(mojom::VizMainPtr viz_main_ptr);
+ explicit VizMainWrapper(mojom::VizMainAssociatedPtr viz_main_associated_ptr);
+ ~VizMainWrapper();
+
+ void CreateGpuService(
+ mojom::GpuServiceRequest request,
+ mojom::GpuHostPtr gpu_host,
+ discardable_memory::mojom::DiscardableSharedMemoryManagerPtr
+ discardable_memory_manager,
+ mojo::ScopedSharedBufferHandle activity_flags,
+ gfx::FontRenderParams::SubpixelRendering subpixel_rendering);
+ void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params);
+
+ private:
+ mojom::VizMainPtr viz_main_ptr_;
+ mojom::VizMainAssociatedPtr viz_main_associated_ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(VizMainWrapper);
+};
+
class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost {
public:
class VIZ_HOST_EXPORT Delegate {
public:
- // Indicates the guilt level of a domain which caused a GPU reset.
- // If a domain is 100% known to be guilty of resetting the GPU, then
- // it will generally not cause other domains' use of 3D APIs to be
- // blocked, unless system stability would be compromised.
- enum class DomainGuilt {
- kKnown,
- kUnknown,
- };
-
virtual gpu::GPUInfo GetGPUInfo() const = 0;
virtual gpu::GpuFeatureInfo GetGpuFeatureInfo() const = 0;
- virtual void UpdateGpuInfo(
+ virtual void DidInitialize(
const gpu::GPUInfo& gpu_info,
const gpu::GpuFeatureInfo& gpu_feature_info,
const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
@@ -66,7 +80,8 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost {
gpu_feature_info_for_hardware_gpu) = 0;
virtual void DidFailInitialize() = 0;
virtual void DidCreateContextSuccessfully() = 0;
- virtual void BlockDomainFrom3DAPIs(const GURL& url, DomainGuilt guilt) = 0;
+ virtual void BlockDomainFrom3DAPIs(const GURL& url,
+ gpu::DomainGuilt guilt) = 0;
virtual void DisableGpuCompositing() = 0;
virtual bool GpuAccessAllowed() const = 0;
virtual gpu::ShaderCacheFactory* GetShaderCacheFactory() = 0;
@@ -76,6 +91,16 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost {
virtual void BindDiscardableMemoryRequest(
discardable_memory::mojom::DiscardableSharedMemoryManagerRequest
request) = 0;
+ virtual void BindInterface(
+ const std::string& interface_name,
+ mojo::ScopedMessagePipeHandle interface_pipe) = 0;
+#if defined(USE_OZONE)
+ virtual void TerminateGpuProcess(const std::string& message) = 0;
+
+ // TODO(https://crbug.com/806092): Remove this when legacy IPC-based Ozone
+ // is removed.
+ virtual void SendGpuProcessMessage(IPC::Message* message) = 0;
+#endif
protected:
virtual ~Delegate() {}
@@ -93,14 +118,17 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost {
bool in_process = false;
// Whether caching GPU shader on disk is disabled or not.
- bool disable_gpu_shader_disk_cache;
+ bool disable_gpu_shader_disk_cache = false;
// A string representing the product name and version; used to build a
// prefix for shader keys.
std::string product;
// Number of frames to CompositorFrame activation deadline.
- base::Optional<uint32_t> deadline_to_synchronize_surfaces = base::nullopt;
+ base::Optional<uint32_t> deadline_to_synchronize_surfaces;
+
+ // Task runner corresponding to the main thread.
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner;
};
enum class EstablishChannelStatus {
@@ -117,14 +145,20 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost {
const gpu::GpuFeatureInfo&,
EstablishChannelStatus)>;
- GpuHostImpl(Delegate* delegate, IPC::Channel* channel, InitParams params);
+ GpuHostImpl(Delegate* delegate,
+ std::unique_ptr<VizMainWrapper> viz_main_ptr,
+ InitParams params);
~GpuHostImpl() override;
static void InitFontRenderParams(const gfx::FontRenderParams& params);
+ static void ResetFontRenderParams();
void OnProcessLaunched(base::ProcessId pid);
void OnProcessCrashed();
+ // Adds a connection error handler for the GpuService.
+ void AddConnectionErrorHandler(base::OnceClosure handler);
+
void BlockLiveOffscreenContexts();
// Connects to FrameSinkManager running in the Viz service.
@@ -141,15 +175,23 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost {
void SendOutstandingReplies();
- mojom::GpuService* gpu_service();
+ void BindInterface(const std::string& interface_name,
+ mojo::ScopedMessagePipeHandle interface_pipe);
- bool initialized() const { return initialized_; }
+ mojom::GpuService* gpu_service();
bool wake_up_gpu_before_drawing() const {
return wake_up_gpu_before_drawing_;
}
private:
+ friend class GpuHostImplTestApi;
+
+#if defined(USE_OZONE)
+ void InitOzone();
+ void TerminateGpuProcess(const std::string& message);
+#endif // defined(USE_OZONE)
+
std::string GetShaderPrefixKey();
void LoadedShader(int32_t client_id,
@@ -177,8 +219,10 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost {
gpu::error::ContextLostReason reason,
const GURL& active_url) override;
void DisableGpuCompositing() override;
+#if defined(OS_WIN)
void SetChildSurface(gpu::SurfaceHandle parent,
gpu::SurfaceHandle child) override;
+#endif
void StoreShaderToDisk(int32_t client_id,
const std::string& key,
const std::string& shader) override;
@@ -187,18 +231,20 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost {
const std::string& message) override;
Delegate* const delegate_;
- IPC::Channel* const channel_;
+ std::unique_ptr<VizMainWrapper> viz_main_ptr_;
const InitParams params_;
- mojom::VizMainAssociatedPtr viz_main_ptr_;
+ // Task runner corresponding to the thread |this| is created on.
+ scoped_refptr<base::SingleThreadTaskRunner> host_thread_task_runner_;
+
mojom::GpuServicePtr gpu_service_ptr_;
mojo::Binding<mojom::GpuHost> gpu_host_binding_;
gpu::GpuProcessHostActivityFlags activity_flags_;
base::ProcessId pid_ = base::kNullProcessId;
- // Whether the GPU service has started successfully or not.
- bool initialized_ = false;
+ // List of connection error handlers for the GpuService.
+ std::vector<base::OnceClosure> connection_error_handlers_;
// The following are a list of driver bug workarounds that will only be
// set to true in DidInitialize(), where GPU service has started and GPU
diff --git a/chromium/components/viz/host/hit_test/hit_test_query.cc b/chromium/components/viz/host/hit_test/hit_test_query.cc
index 2d5a6170178..df683ddbebc 100644
--- a/chromium/components/viz/host/hit_test/hit_test_query.cc
+++ b/chromium/components/viz/host/hit_test/hit_test_query.cc
@@ -4,6 +4,7 @@
#include "components/viz/host/hit_test/hit_test_query.h"
+#include "base/containers/stack.h"
#include "base/metrics/histogram_macros.h"
#include "base/timer/elapsed_timer.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
@@ -24,9 +25,51 @@ bool RegionMatchEventSource(EventSource event_source, uint32_t flags) {
HitTestRegionFlags::kHitTestTouch)) != 0u;
}
-bool CheckChildCount(int32_t child_count, uint32_t child_count_max) {
+bool CheckChildCount(int32_t child_count, size_t child_count_max) {
return (child_count >= 0) &&
- (static_cast<uint32_t>(child_count) < child_count_max);
+ (static_cast<size_t>(child_count) < child_count_max);
+}
+
+const std::string GetFlagNames(uint32_t flag) {
+ std::string names = "";
+ uint32_t mask = 1;
+
+ while (flag) {
+ std::string name = "";
+ switch (flag & mask) {
+ case kHitTestMine:
+ name = "Mine";
+ break;
+ case kHitTestIgnore:
+ name = "Ignore";
+ break;
+ case kHitTestChildSurface:
+ name = "ChildSurface";
+ break;
+ case kHitTestAsk:
+ name = "Ask";
+ break;
+ case kHitTestMouse:
+ name = "Mouse";
+ break;
+ case kHitTestTouch:
+ name = "Touch";
+ break;
+ case kHitTestNotActive:
+ name = "NotActive";
+ break;
+ case 0:
+ break;
+ }
+ if (!name.empty()) {
+ names += names.empty() ? name : ", " + name;
+ }
+
+ flag &= ~mask;
+ mask <<= 1;
+ }
+
+ return names;
}
} // namespace
@@ -40,18 +83,16 @@ void HitTestQuery::OnAggregatedHitTestRegionListUpdated(
const std::vector<AggregatedHitTestRegion>& hit_test_data) {
hit_test_data_.clear();
hit_test_data_ = hit_test_data;
- hit_test_data_size_ = hit_test_data.size();
}
Target HitTestQuery::FindTargetForLocation(
EventSource event_source,
const gfx::PointF& location_in_root) const {
- base::ElapsedTimer target_timer;
+ if (hit_test_data_.empty())
+ return Target();
+ base::ElapsedTimer target_timer;
Target target;
- if (!hit_test_data_size_)
- return target;
-
FindTargetInRegionForLocation(event_source, location_in_root, 0, &target);
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES("Event.VizHitTest.TargetTimeUs",
target_timer.Elapsed(),
@@ -67,7 +108,7 @@ bool HitTestQuery::TransformLocationForTarget(
gfx::PointF* transformed_location) const {
base::ElapsedTimer transform_timer;
- if (!hit_test_data_size_)
+ if (hit_test_data_.empty())
return false;
if (target_ancestors.size() == 0u ||
@@ -90,16 +131,19 @@ bool HitTestQuery::TransformLocationForTarget(
bool HitTestQuery::GetTransformToTarget(const FrameSinkId& target,
gfx::Transform* transform) const {
- if (!hit_test_data_size_)
+ if (hit_test_data_.empty())
return false;
return GetTransformToTargetRecursively(target, 0, transform);
}
-bool HitTestQuery::ContainsFrameSinkId(const FrameSinkId& frame_sink_id) const {
+bool HitTestQuery::ContainsActiveFrameSinkId(
+ const FrameSinkId& frame_sink_id) const {
for (auto& it : hit_test_data_) {
- if (it.frame_sink_id == frame_sink_id)
+ if (it.frame_sink_id == frame_sink_id &&
+ !(it.flags & HitTestRegionFlags::kHitTestNotActive)) {
return true;
+ }
}
return false;
}
@@ -107,7 +151,7 @@ bool HitTestQuery::ContainsFrameSinkId(const FrameSinkId& frame_sink_id) const {
bool HitTestQuery::FindTargetInRegionForLocation(
EventSource event_source,
const gfx::PointF& location_in_parent,
- uint32_t region_index,
+ size_t region_index,
Target* target) const {
gfx::PointF location_transformed(location_in_parent);
@@ -129,11 +173,12 @@ bool HitTestQuery::FindTargetInRegionForLocation(
}
const int32_t region_child_count = hit_test_data_[region_index].child_count;
- if (!CheckChildCount(region_child_count, hit_test_data_size_ - region_index))
+ if (!CheckChildCount(region_child_count,
+ hit_test_data_.size() - region_index))
return false;
- uint32_t child_region = region_index + 1;
- uint32_t child_region_end = child_region + region_child_count;
+ size_t child_region = region_index + 1;
+ size_t child_region_end = child_region + region_child_count;
gfx::PointF location_in_target =
location_transformed -
hit_test_data_[region_index].rect.OffsetFromOrigin();
@@ -168,7 +213,7 @@ bool HitTestQuery::TransformLocationForTargetRecursively(
EventSource event_source,
const std::vector<FrameSinkId>& target_ancestors,
size_t target_ancestor,
- uint32_t region_index,
+ size_t region_index,
gfx::PointF* location_in_target) const {
const uint32_t flags = hit_test_data_[region_index].flags;
if ((flags & HitTestRegionFlags::kHitTestChildSurface) == 0u &&
@@ -183,11 +228,13 @@ bool HitTestQuery::TransformLocationForTargetRecursively(
return true;
const int32_t region_child_count = hit_test_data_[region_index].child_count;
- if (!CheckChildCount(region_child_count, hit_test_data_size_ - region_index))
+ if (!CheckChildCount(region_child_count,
+ hit_test_data_.size() - region_index)) {
return false;
+ }
- uint32_t child_region = region_index + 1;
- uint32_t child_region_end = child_region + region_child_count;
+ size_t child_region = region_index + 1;
+ size_t child_region_end = child_region + region_child_count;
while (child_region < child_region_end) {
if (hit_test_data_[child_region].frame_sink_id ==
target_ancestors[target_ancestor - 1]) {
@@ -209,7 +256,7 @@ bool HitTestQuery::TransformLocationForTargetRecursively(
bool HitTestQuery::GetTransformToTargetRecursively(
const FrameSinkId& target,
- uint32_t region_index,
+ size_t region_index,
gfx::Transform* transform) const {
// TODO(riajiang): Cache the matrix product such that the transform can be
// found immediately.
@@ -221,11 +268,13 @@ bool HitTestQuery::GetTransformToTargetRecursively(
}
const int32_t region_child_count = hit_test_data_[region_index].child_count;
- if (!CheckChildCount(region_child_count, hit_test_data_size_ - region_index))
+ if (!CheckChildCount(region_child_count,
+ hit_test_data_.size() - region_index)) {
return false;
+ }
- uint32_t child_region = region_index + 1;
- uint32_t child_region_end = child_region + region_child_count;
+ size_t child_region = region_index + 1;
+ size_t child_region_end = child_region + region_child_count;
while (child_region < child_region_end) {
gfx::Transform transform_to_child;
if (GetTransformToTargetRecursively(target, child_region,
@@ -253,4 +302,47 @@ void HitTestQuery::ReceivedBadMessageFromGpuProcess() const {
bad_message_gpu_callback_.Run();
}
+std::string HitTestQuery::PrintHitTestData() const {
+ std::ostringstream oss;
+ base::stack<uint32_t> parents;
+ std::string tabs = "";
+
+ for (uint32_t i = 0; i < hit_test_data_.size(); ++i) {
+ const AggregatedHitTestRegion& htr = hit_test_data_[i];
+
+ oss << tabs << "Index: " << i << '\n';
+ oss << tabs << "Children: " << htr.child_count << '\n';
+ oss << tabs << "Flags: " << GetFlagNames(htr.flags) << '\n';
+ oss << tabs << "Frame Sink Id: " << htr.frame_sink_id.ToString() << '\n';
+ oss << tabs << "Rect: " << htr.rect.ToString() << '\n';
+ oss << tabs << "Transform:" << '\n';
+
+ // gfx::Transform::ToString spans multiple lines, so we use an additional
+ // stringstream.
+ {
+ std::string s;
+ std::stringstream transform_ss;
+
+ transform_ss << htr.transform().ToString() << '\n';
+
+ while (getline(transform_ss, s)) {
+ oss << tabs << s << '\n';
+ }
+ }
+
+ tabs += "\t\t";
+ parents.push(i);
+
+ while (!parents.empty() &&
+ parents.top() + hit_test_data_[parents.top()].child_count <= i) {
+ tabs.pop_back();
+ tabs.pop_back();
+
+ parents.pop();
+ }
+ }
+
+ return oss.str();
+}
+
} // namespace viz
diff --git a/chromium/components/viz/host/hit_test/hit_test_query.h b/chromium/components/viz/host/hit_test/hit_test_query.h
index 3289f422910..cef04e93e51 100644
--- a/chromium/components/viz/host/hit_test/hit_test_query.h
+++ b/chromium/components/viz/host/hit_test/hit_test_query.h
@@ -96,8 +96,13 @@ class VIZ_HOST_EXPORT HitTestQuery {
bool GetTransformToTarget(const FrameSinkId& target,
gfx::Transform* transform) const;
- // Returns whether hit test data for |frame_sink_id| is available.
- bool ContainsFrameSinkId(const FrameSinkId& frame_sink_id) const;
+ // Returns whether client has submitted hit test data for |frame_sink_id|.
+ // Note that this returns false even if the embedder has submitted hit-test
+ // data for |frame_sink_id|.
+ bool ContainsActiveFrameSinkId(const FrameSinkId& frame_sink_id) const;
+
+ // Returns hit-test data, using indentation to visualize the tree structure.
+ std::string PrintHitTestData() const;
private:
friend class content::HitTestRegionObserver;
@@ -106,7 +111,7 @@ class VIZ_HOST_EXPORT HitTestQuery {
// |location_in_parent| is in the coordinate space of |region_index|'s parent.
bool FindTargetInRegionForLocation(EventSource event_source,
const gfx::PointF& location_in_parent,
- uint32_t region_index,
+ size_t region_index,
Target* target) const;
// Transform |location_in_target| to be in |region_index|'s coordinate space.
@@ -116,17 +121,16 @@ class VIZ_HOST_EXPORT HitTestQuery {
EventSource event_source,
const std::vector<FrameSinkId>& target_ancestors,
size_t target_ancestor,
- uint32_t region_index,
+ size_t region_index,
gfx::PointF* location_in_target) const;
bool GetTransformToTargetRecursively(const FrameSinkId& target,
- uint32_t region_index,
+ size_t region_index,
gfx::Transform* transform) const;
void ReceivedBadMessageFromGpuProcess() const;
std::vector<AggregatedHitTestRegion> hit_test_data_;
- uint32_t hit_test_data_size_ = 0;
// Log bad message and shut down Viz process when it is compromised.
base::RepeatingClosure bad_message_gpu_callback_;
diff --git a/chromium/components/viz/host/hit_test/hit_test_query_unittest.cc b/chromium/components/viz/host/hit_test/hit_test_query_unittest.cc
index 3b56f36e25b..aa7172c07c8 100644
--- a/chromium/components/viz/host/hit_test/hit_test_query_unittest.cc
+++ b/chromium/components/viz/host/hit_test/hit_test_query_unittest.cc
@@ -1161,5 +1161,70 @@ TEST_F(HitTestQueryTest, GetTransformToTarget) {
EXPECT_EQ(transform_to_d, expected_transform_to_d);
}
+// Region c1 is transparent and on top of e, c2 is a child of e, d1 is a
+// child of c1. Any events outside of d1 should go to either c2 or e instead
+// of being taken by the transparent c1 region.
+//
+// +e/c1----------+
+// | | Point maps to
+// | +c2-+ | ----- -------
+// | | 1 | | 1 c2
+// | +d1--------| 2 d1
+// | | 2 |
+// | | |
+// +--------------+
+//
+TEST_F(HitTestQueryTest, TransparentOverlayRegions) {
+ FrameSinkId e_id = FrameSinkId(1, 1);
+ FrameSinkId c1_id = FrameSinkId(2, 2);
+ FrameSinkId c2_id = FrameSinkId(3, 3);
+ FrameSinkId d1_id = FrameSinkId(4, 4);
+ gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
+ gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 600, 600);
+ gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 200, 100);
+ gfx::Rect d1_bounds_in_c1 = gfx::Rect(0, 0, 400, 300);
+ gfx::Transform transform_e_to_e, transform_e_to_c1, transform_e_to_c2,
+ transform_c1_to_d1;
+ transform_e_to_c2.Translate(-200, -100);
+ transform_c1_to_d1.Translate(-200, -200);
+ active_data_.push_back(AggregatedHitTestRegion(
+ e_id,
+ HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
+ e_bounds_in_e, transform_e_to_e, 3)); // e
+ active_data_.push_back(
+ AggregatedHitTestRegion(c1_id,
+ HitTestRegionFlags::kHitTestChildSurface |
+ HitTestRegionFlags::kHitTestIgnore |
+ HitTestRegionFlags::kHitTestMouse,
+ c1_bounds_in_e, transform_e_to_c1, 1)); // c1
+ active_data_.push_back(AggregatedHitTestRegion(
+ d1_id,
+ HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
+ d1_bounds_in_c1, transform_c1_to_d1, 0)); // d1
+ active_data_.push_back(AggregatedHitTestRegion(
+ c2_id,
+ HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
+ c2_bounds_in_e, transform_e_to_c2, 0)); // c2
+ SendHitTestData();
+
+ // All points are in e's coordinate system when we reach this case.
+ gfx::PointF point1(202, 102);
+ gfx::PointF point2(202, 202);
+
+ Target target1 =
+ hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
+ EXPECT_EQ(target1.frame_sink_id, c2_id);
+ EXPECT_EQ(target1.location_in_target, gfx::PointF(2, 2));
+ EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
+ HitTestRegionFlags::kHitTestMouse);
+
+ Target target2 =
+ hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
+ EXPECT_EQ(target2.frame_sink_id, d1_id);
+ EXPECT_EQ(target2.location_in_target, gfx::PointF(2, 2));
+ EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
+ HitTestRegionFlags::kHitTestMouse);
+}
+
} // namespace test
} // namespace viz
diff --git a/chromium/components/viz/host/host_frame_sink_manager.cc b/chromium/components/viz/host/host_frame_sink_manager.cc
index 53ccb9f26f8..450f16ca38e 100644
--- a/chromium/components/viz/host/host_frame_sink_manager.cc
+++ b/chromium/components/viz/host/host_frame_sink_manager.cc
@@ -72,6 +72,12 @@ void HostFrameSinkManager::RegisterFrameSinkId(const FrameSinkId& frame_sink_id,
frame_sink_manager_->RegisterFrameSinkId(frame_sink_id);
}
+bool HostFrameSinkManager::IsFrameSinkIdRegistered(
+ const FrameSinkId& frame_sink_id) const {
+ auto iter = frame_sink_data_map_.find(frame_sink_id);
+ return iter != frame_sink_data_map_.end() && iter->second.client != nullptr;
+}
+
void HostFrameSinkManager::InvalidateFrameSinkId(
const FrameSinkId& frame_sink_id) {
DCHECK(frame_sink_id.is_valid());
@@ -231,19 +237,29 @@ void HostFrameSinkManager::UnregisterFrameSinkHierarchy(
frame_sink_data_map_.erase(parent_frame_sink_id);
}
-void HostFrameSinkManager::DropTemporaryReference(const SurfaceId& surface_id) {
- frame_sink_manager_->DropTemporaryReference(surface_id);
+bool HostFrameSinkManager::IsFrameSinkHierarchyRegistered(
+ const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id) const {
+ auto iter = frame_sink_data_map_.find(parent_frame_sink_id);
+ return iter != frame_sink_data_map_.end() &&
+ base::ContainsValue(iter->second.children, child_frame_sink_id);
}
-void HostFrameSinkManager::WillAssignTemporaryReferencesExternally() {
- assign_temporary_references_ = false;
-}
+base::Optional<FrameSinkId> HostFrameSinkManager::FindRootFrameSinkId(
+ const FrameSinkId& start) const {
+ auto iter = frame_sink_data_map_.find(start);
+ if (iter == frame_sink_data_map_.end())
+ return base::nullopt;
-void HostFrameSinkManager::AssignTemporaryReference(
- const SurfaceId& surface_id,
- const FrameSinkId& frame_sink_id) {
- DCHECK(!assign_temporary_references_);
- frame_sink_manager_->AssignTemporaryReference(surface_id, frame_sink_id);
+ if (iter->second.is_root)
+ return start;
+
+ for (const FrameSinkId& parent_id : iter->second.parents) {
+ base::Optional<FrameSinkId> root = FindRootFrameSinkId(parent_id);
+ if (root)
+ return root;
+ }
+ return base::nullopt;
}
void HostFrameSinkManager::AddVideoDetectorObserver(
@@ -311,48 +327,6 @@ HostFrameSinkManager::CreateCompositorFrameSinkSupport(
return support;
}
-void HostFrameSinkManager::PerformAssignTemporaryReference(
- const SurfaceId& surface_id) {
- auto iter = frame_sink_data_map_.find(surface_id.frame_sink_id());
- if (iter == frame_sink_data_map_.end()) {
- // We don't have any hierarchy information for what will embed the new
- // surface, drop the temporary reference.
- frame_sink_manager_->DropTemporaryReference(surface_id);
- return;
- }
-
- const FrameSinkData& data = iter->second;
-
- // Display roots don't have temporary references to assign.
- if (data.is_root)
- return;
-
- // If the frame sink has already been invalidated then we just drop the
- // temporary reference.
- if (!data.IsFrameSinkRegistered()) {
- frame_sink_manager_->DropTemporaryReference(surface_id);
- return;
- }
-
- // Find the oldest non-invalidated parent.
- for (const FrameSinkId& parent_id : data.parents) {
- const FrameSinkData& parent_data = frame_sink_data_map_[parent_id];
- if (parent_data.IsFrameSinkRegistered()) {
- frame_sink_manager_->AssignTemporaryReference(surface_id, parent_id);
- return;
- }
- }
- // TODO(kylechar): We might need to handle the case where there are multiple
- // embedders better, so that the owner doesn't remove a surface reference
- // until the other embedder has added a surface reference. Maybe just letting
- // the client know what is the owner is sufficient, so the client can handle
- // this.
-
- // We don't have any hierarchy information for what will embed the new
- // surface, drop the temporary reference.
- frame_sink_manager_->DropTemporaryReference(surface_id);
-}
-
void HostFrameSinkManager::OnConnectionLost() {
connection_was_lost_ = true;
@@ -397,11 +371,6 @@ void HostFrameSinkManager::RegisterAfterConnectionLoss() {
}
}
-void HostFrameSinkManager::OnSurfaceCreated(const SurfaceId& surface_id) {
- if (assign_temporary_references_)
- PerformAssignTemporaryReference(surface_id);
-}
-
void HostFrameSinkManager::OnFirstSurfaceActivation(
const SurfaceInfo& surface_info) {
diff --git a/chromium/components/viz/host/host_frame_sink_manager.h b/chromium/components/viz/host/host_frame_sink_manager.h
index b8c07938a74..bd67536e8fb 100644
--- a/chromium/components/viz/host/host_frame_sink_manager.h
+++ b/chromium/components/viz/host/host_frame_sink_manager.h
@@ -77,6 +77,10 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
void RegisterFrameSinkId(const FrameSinkId& frame_sink_id,
HostFrameSinkClient* client);
+ // Returns true if RegisterFrameSinkId() was called with |frame_sink_id| and
+ // InvalidateFrameSinkId() has not been called.
+ bool IsFrameSinkIdRegistered(const FrameSinkId& frame_sink_id) const;
+
// Invalidates |frame_sink_id| which cleans up any dangling temporary
// references assigned to it. If there is a CompositorFrameSink for
// |frame_sink_id| then it will be destroyed and the message pipe to the
@@ -132,12 +136,15 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
void UnregisterFrameSinkHierarchy(const FrameSinkId& parent_frame_sink_id,
const FrameSinkId& child_frame_sink_id);
- void DropTemporaryReference(const SurfaceId& surface_id);
+ // Returns true if RegisterFrameSinkHierarchy() was called with the supplied
+ // arguments.
+ bool IsFrameSinkHierarchyRegistered(
+ const FrameSinkId& parent_frame_sink_id,
+ const FrameSinkId& child_frame_sink_id) const;
- // These two functions should only be used by WindowServer.
- void WillAssignTemporaryReferencesExternally();
- void AssignTemporaryReference(const SurfaceId& surface_id,
- const FrameSinkId& owner);
+ // Returns the first ancestor of |start| (including |start|) that is a root.
+ base::Optional<FrameSinkId> FindRootFrameSinkId(
+ const FrameSinkId& start) const;
// Asks viz to send updates regarding video activity to |observer|.
void AddVideoDetectorObserver(mojom::VideoDetectorObserverPtr observer);
@@ -221,10 +228,6 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
DISALLOW_COPY_AND_ASSIGN(FrameSinkData);
};
- // Assigns the temporary reference to the frame sink that is expected to
- // embed |surface_id|, otherwise drops the temporary reference.
- void PerformAssignTemporaryReference(const SurfaceId& surface_id);
-
// Handles connection loss to |frame_sink_manager_ptr_|. This should only
// happen when the GPU process crashes.
void OnConnectionLost();
@@ -233,7 +236,6 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
void RegisterAfterConnectionLoss();
// mojom::FrameSinkManagerClient:
- void OnSurfaceCreated(const SurfaceId& surface_id) override;
void OnFirstSurfaceActivation(const SurfaceInfo& surface_info) override;
void OnAggregatedHitTestRegionListUpdated(
const FrameSinkId& frame_sink_id,
@@ -262,11 +264,6 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
// If |frame_sink_manager_ptr_| connection was lost.
bool connection_was_lost_ = false;
- // If a FrameSinkId owner should be assigned when a new surface is created.
- // This will be set false if using surface sequences or if temporary
- // references are being assigned externally.
- bool assign_temporary_references_ = true;
-
base::RepeatingClosure connection_lost_callback_;
base::RepeatingClosure bad_message_received_from_gpu_callback_;
diff --git a/chromium/components/viz/host/host_frame_sink_manager_unittest.cc b/chromium/components/viz/host/host_frame_sink_manager_unittest.cc
index 0f91e10691b..27b8e7a282c 100644
--- a/chromium/components/viz/host/host_frame_sink_manager_unittest.cc
+++ b/chromium/components/viz/host/host_frame_sink_manager_unittest.cc
@@ -107,9 +107,6 @@ class MockFrameSinkManagerImpl : public FrameSinkManagerImpl {
void(const FrameSinkId& parent, const FrameSinkId& child));
MOCK_METHOD2(UnregisterFrameSinkHierarchy,
void(const FrameSinkId& parent, const FrameSinkId& child));
- MOCK_METHOD2(AssignTemporaryReference,
- void(const SurfaceId& surface_id, const FrameSinkId& owner));
- MOCK_METHOD1(DropTemporaryReference, void(const SurfaceId& surface_id));
private:
DISALLOW_COPY_AND_ASSIGN(MockFrameSinkManagerImpl);
@@ -345,83 +342,6 @@ TEST_F(HostFrameSinkManagerLocalTest, CreateCompositorFrameSinkSupport) {
EXPECT_FALSE(FrameSinkDataExists(kFrameSinkParent1));
}
-TEST_F(HostFrameSinkManagerLocalTest, AssignTemporaryReference) {
- FakeHostFrameSinkClient host_client;
- host().RegisterFrameSinkId(kFrameSinkParent1, &host_client);
-
- const SurfaceId surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
- host().RegisterFrameSinkId(surface_id.frame_sink_id(), &host_client);
- auto support = CreateCompositorFrameSinkSupport(surface_id.frame_sink_id(),
- false /* is_root */);
-
- host().RegisterFrameSinkHierarchy(kFrameSinkParent1,
- surface_id.frame_sink_id());
-
- // When HostFrameSinkManager gets OnSurfaceCreated() it should assign
- // the temporary reference to the registered parent |kFrameSinkParent1|.
- EXPECT_CALL(impl(), AssignTemporaryReference(surface_id, kFrameSinkParent1));
- GetFrameSinkManagerClient()->OnSurfaceCreated(surface_id);
-}
-
-// Verify that we drop temporary reference to a surface that doesn't have any
-// registered parent.
-TEST_F(HostFrameSinkManagerLocalTest, DropTemporaryReference) {
- FakeHostFrameSinkClient host_client;
-
- const SurfaceId surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
- host().RegisterFrameSinkId(surface_id.frame_sink_id(), &host_client);
- auto support = CreateCompositorFrameSinkSupport(surface_id.frame_sink_id(),
- false /* is_root */);
-
- // When HostFrameSinkManager gets OnSurfaceCreated() it should find no
- // registered parent and drop the temporary reference.
- EXPECT_CALL(impl(), DropTemporaryReference(surface_id));
- GetFrameSinkManagerClient()->OnSurfaceCreated(surface_id);
-}
-
-// Verify that we drop the temporary reference to a new surface if the frame
-// sink that corresponds to the new surface has been invalidated.
-TEST_F(HostFrameSinkManagerLocalTest, DropTemporaryReferenceForStaleClient) {
- FakeHostFrameSinkClient host_client;
-
- host().RegisterFrameSinkId(kFrameSinkChild1, &host_client);
- auto support_child =
- CreateCompositorFrameSinkSupport(kFrameSinkChild1, false /* is_root */);
- EXPECT_TRUE(FrameSinkDataExists(kFrameSinkChild1));
-
- host().RegisterFrameSinkId(kFrameSinkParent1, &host_client);
- auto support_parent =
- CreateCompositorFrameSinkSupport(kFrameSinkParent1, true /* is_root */);
- EXPECT_TRUE(FrameSinkDataExists(kFrameSinkParent1));
-
- // Register should call through to FrameSinkManagerImpl.
- EXPECT_CALL(impl(),
- RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1));
- host().RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
-
- // Verify that temporary reference is assigned correctly before invalidation.
- const SurfaceId client_surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
- EXPECT_CALL(impl(), DropTemporaryReference(client_surface_id)).Times(0);
- EXPECT_CALL(impl(),
- AssignTemporaryReference(client_surface_id, kFrameSinkParent1))
- .Times(1);
- GetFrameSinkManagerClient()->OnSurfaceCreated(client_surface_id);
- testing::Mock::VerifyAndClearExpectations(&impl());
-
- // Invaidating the child should cause the temporary reference to the next
- // SurfaceId to be dropped.
- support_child.reset();
- host().InvalidateFrameSinkId(kFrameSinkChild1);
-
- const SurfaceId client_surface_id2 = MakeSurfaceId(kFrameSinkChild1, 2);
- EXPECT_CALL(impl(), DropTemporaryReference(client_surface_id2)).Times(1);
- EXPECT_CALL(impl(), AssignTemporaryReference(client_surface_id2, _)).Times(0);
- GetFrameSinkManagerClient()->OnSurfaceCreated(client_surface_id2);
-
- support_parent.reset();
- host().InvalidateFrameSinkId(kFrameSinkParent1);
-}
-
// Verify that multiple parents in the frame sink hierarchy works.
TEST_F(HostFrameSinkManagerLocalTest, HierarchyMultipleParents) {
FakeHostFrameSinkClient host_client;
@@ -453,13 +373,6 @@ TEST_F(HostFrameSinkManagerLocalTest, HierarchyMultipleParents) {
host().RegisterFrameSinkHierarchy(id_parent2, id_child);
testing::Mock::VerifyAndClearExpectations(&impl());
- // The oldest registered parent in the hierarchy is assigned the temporary
- // reference.
- const SurfaceId surface_id = MakeSurfaceId(id_child, 1);
- EXPECT_CALL(impl(), AssignTemporaryReference(surface_id, id_parent1));
- GetFrameSinkManagerClient()->OnSurfaceCreated(surface_id);
- testing::Mock::VerifyAndClearExpectations(&impl());
-
// Unregistering hierarchy with multiple parents should also work.
EXPECT_CALL(impl(), UnregisterFrameSinkHierarchy(id_parent2, id_child));
host().UnregisterFrameSinkHierarchy(id_parent2, id_child);
@@ -468,65 +381,6 @@ TEST_F(HostFrameSinkManagerLocalTest, HierarchyMultipleParents) {
host().UnregisterFrameSinkHierarchy(id_parent1, id_child);
}
-// Verify that we drop the temporary reference to a new surface if the only
-// frame sink registered as an embedder has been invalidated.
-TEST_F(HostFrameSinkManagerLocalTest,
- DropTemporaryReferenceForInvalidatedParent) {
- FakeHostFrameSinkClient host_client;
-
- host().RegisterFrameSinkId(kFrameSinkChild1, &host_client);
- auto support_child =
- CreateCompositorFrameSinkSupport(kFrameSinkChild1, false /* is_root */);
- EXPECT_TRUE(FrameSinkDataExists(kFrameSinkChild1));
-
- host().RegisterFrameSinkId(kFrameSinkParent1, &host_client);
- auto support_parent =
- CreateCompositorFrameSinkSupport(kFrameSinkParent1, true /* is_root */);
- EXPECT_TRUE(FrameSinkDataExists(kFrameSinkParent1));
-
- // Register should call through to FrameSinkManagerImpl.
- EXPECT_CALL(impl(),
- RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1));
- host().RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
-
- // Verify that temporary reference is assigned correctly before invalidation.
- const SurfaceId client_surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
- EXPECT_CALL(impl(), DropTemporaryReference(client_surface_id)).Times(0);
- EXPECT_CALL(impl(),
- AssignTemporaryReference(client_surface_id, kFrameSinkParent1))
- .Times(1);
- GetFrameSinkManagerClient()->OnSurfaceCreated(client_surface_id);
- testing::Mock::VerifyAndClearExpectations(&impl());
-
- // Invaidating the parent should cause the next SurfaceId to be dropped
- // because there is no registered frame sink as the parent.
- support_parent.reset();
- host().InvalidateFrameSinkId(kFrameSinkParent1);
-
- const SurfaceId client_surface_id2 = MakeSurfaceId(kFrameSinkChild1, 2);
- EXPECT_CALL(impl(), DropTemporaryReference(client_surface_id2)).Times(1);
- EXPECT_CALL(impl(), AssignTemporaryReference(client_surface_id2, _)).Times(0);
- GetFrameSinkManagerClient()->OnSurfaceCreated(client_surface_id2);
-
- support_child.reset();
- host().InvalidateFrameSinkId(kFrameSinkChild1);
-}
-
-TEST_F(HostFrameSinkManagerLocalTest, DisplayRootTemporaryReference) {
- FakeHostFrameSinkClient host_client;
-
- const SurfaceId surface_id = MakeSurfaceId(kFrameSinkParent1, 1);
- host().RegisterFrameSinkId(surface_id.frame_sink_id(), &host_client);
- auto support = CreateCompositorFrameSinkSupport(surface_id.frame_sink_id(),
- true /* is_root */);
-
- // When HostFrameSinkManager gets OnSurfaceCreated() it should do
- // nothing since |kFrameSinkParent1| is a display root.
- EXPECT_CALL(impl(), DropTemporaryReference(surface_id)).Times(0);
- EXPECT_CALL(impl(), AssignTemporaryReference(surface_id, _)).Times(0);
- GetFrameSinkManagerClient()->OnSurfaceCreated(surface_id);
-}
-
// Test the creation and desctruction of HitTestAggregator and HitTestQuery.
TEST_F(HostFrameSinkManagerLocalTest, HitTestAggregatorQuery) {
FakeHostFrameSinkClient client;
@@ -547,6 +401,37 @@ TEST_F(HostFrameSinkManagerLocalTest, HitTestAggregatorQuery) {
EXPECT_FALSE(DisplayHitTestQueryExists(kFrameSinkChild1));
}
+TEST_F(HostFrameSinkManagerRemoteTest, FindRootFrameSinkId) {
+ FakeHostFrameSinkClient host_client;
+
+ EXPECT_FALSE(host().FindRootFrameSinkId(kFrameSinkParent1));
+ EXPECT_FALSE(host().FindRootFrameSinkId(kFrameSinkChild1));
+
+ // Register two FrameSinkIds, hierarchy between them and create a
+ // CompositorFrameSink for one.
+ host().RegisterFrameSinkId(kFrameSinkParent1, &host_client);
+ host().RegisterFrameSinkId(kFrameSinkChild1, &host_client);
+ host().RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
+
+ EXPECT_FALSE(host().FindRootFrameSinkId(kFrameSinkParent1));
+ EXPECT_FALSE(host().FindRootFrameSinkId(kFrameSinkChild1));
+
+ RootCompositorFrameSinkData root_data;
+ host().CreateRootCompositorFrameSink(
+ root_data.BuildParams(kFrameSinkParent1));
+
+ MockCompositorFrameSinkClient compositor_frame_sink_client;
+ mojom::CompositorFrameSinkPtr compositor_frame_sink;
+ host().CreateCompositorFrameSink(
+ kFrameSinkChild1, MakeRequest(&compositor_frame_sink),
+ compositor_frame_sink_client.BindInterfacePtr());
+
+ EXPECT_EQ(base::Optional<FrameSinkId>(kFrameSinkParent1),
+ host().FindRootFrameSinkId(kFrameSinkParent1));
+ EXPECT_EQ(base::Optional<FrameSinkId>(kFrameSinkParent1),
+ host().FindRootFrameSinkId(kFrameSinkChild1));
+}
+
// Verify that HostFrameSinkManager can handle restarting after a GPU crash.
TEST_F(HostFrameSinkManagerRemoteTest, RestartOnGpuCrash) {
FakeHostFrameSinkClient host_client;
@@ -644,55 +529,6 @@ TEST_F(HostFrameSinkManagerRemoteTest, DeletedHitTestQuery) {
kFrameSinkParent1, {});
}
-// Verify that HostFrameSinkManager assigns temporary references when connected
-// to a remote mojom::FrameSinkManager.
-TEST_F(HostFrameSinkManagerRemoteTest, AssignTemporaryReference) {
- FakeHostFrameSinkClient host_client;
- host().RegisterFrameSinkId(kFrameSinkParent1, &host_client);
-
- const SurfaceId surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
- host().RegisterFrameSinkId(surface_id.frame_sink_id(), &host_client);
- MockCompositorFrameSinkClient compositor_frame_sink_client;
- mojom::CompositorFrameSinkPtr compositor_frame_sink;
- host().CreateCompositorFrameSink(
- kFrameSinkChild1, MakeRequest(&compositor_frame_sink),
- compositor_frame_sink_client.BindInterfacePtr());
-
- host().RegisterFrameSinkHierarchy(kFrameSinkParent1,
- surface_id.frame_sink_id());
-
- // When HostFrameSinkManager gets OnSuraceCreated() it should assign
- // the temporary reference to the registered parent |kFrameSinkParent1|.
- GetFrameSinkManagerClient()->OnSurfaceCreated(surface_id);
-
- base::RunLoop run_loop;
- EXPECT_CALL(impl(), AssignTemporaryReference(surface_id, kFrameSinkParent1))
- .WillOnce(InvokeClosure(run_loop.QuitClosure()));
- run_loop.Run();
-}
-
-TEST_F(HostFrameSinkManagerRemoteTest, DropTemporaryReference) {
- FakeHostFrameSinkClient host_client;
-
- const SurfaceId surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
- host().RegisterFrameSinkId(surface_id.frame_sink_id(), &host_client);
- MockCompositorFrameSinkClient compositor_frame_sink_client;
- mojom::CompositorFrameSinkPtr compositor_frame_sink;
- host().CreateCompositorFrameSink(
- kFrameSinkChild1, MakeRequest(&compositor_frame_sink),
- compositor_frame_sink_client.BindInterfacePtr());
-
- // When HostFrameSinkManager gets OnSuraceCreated() it should find that
- // kFrameSinkChild1 isn't embedded by anything and drop the temporary
- // reference.
- GetFrameSinkManagerClient()->OnSurfaceCreated(surface_id);
-
- base::RunLoop run_loop;
- EXPECT_CALL(impl(), DropTemporaryReference(surface_id))
- .WillOnce(InvokeClosure(run_loop.QuitClosure()));
- run_loop.Run();
-}
-
// Verify that on lost context a RootCompositorFrameSink can be recreated.
TEST_F(HostFrameSinkManagerRemoteTest, ContextLossRecreateRoot) {
FakeHostFrameSinkClient host_client;
diff --git a/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc b/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
index 7a633898431..e534bb060cd 100644
--- a/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
+++ b/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
@@ -111,11 +111,13 @@ class TestGpuService : public mojom::GpuService {
void GetVideoMemoryUsageStats(
GetVideoMemoryUsageStatsCallback callback) override {}
+#if defined(OS_WIN)
void RequestCompleteGpuInfo(
RequestCompleteGpuInfoCallback callback) override {}
void GetGpuSupportedRuntimeVersion(
GetGpuSupportedRuntimeVersionCallback callback) override {}
+#endif
void RequestHDRStatus(RequestHDRStatusCallback callback) override {}
@@ -292,7 +294,6 @@ TEST_F(HostGpuMemoryBufferManagerTest, RequestsFromUntrustedClientsValidated) {
const auto buffer_id = static_cast<gfx::GpuMemoryBufferId>(1);
const int client_id = 2;
// SCANOUT cannot be used if native gpu memory buffer is not supported.
- // When ATC is used, both width and height should be multiples of 4.
struct {
gfx::BufferUsage usage;
gfx::BufferFormat format;
@@ -300,7 +301,6 @@ TEST_F(HostGpuMemoryBufferManagerTest, RequestsFromUntrustedClientsValidated) {
bool expect_null_handle;
} configs[] = {
{gfx::BufferUsage::SCANOUT, gfx::BufferFormat::YVU_420, {10, 20}, true},
- {gfx::BufferUsage::GPU_READ, gfx::BufferFormat::ATC, {10, 20}, true},
{gfx::BufferUsage::GPU_READ, gfx::BufferFormat::YVU_420, {64, 64}, false},
};
for (const auto& config : configs) {
diff --git a/chromium/components/viz/service/BUILD.gn b/chromium/components/viz/service/BUILD.gn
index d5fd8af7c49..3fa0f48db33 100644
--- a/chromium/components/viz/service/BUILD.gn
+++ b/chromium/components/viz/service/BUILD.gn
@@ -109,10 +109,6 @@ viz_component("service") {
"display_embedder/in_process_gpu_memory_buffer_manager.h",
"display_embedder/server_shared_bitmap_manager.cc",
"display_embedder/server_shared_bitmap_manager.h",
- "display_embedder/skia_output_surface_impl.cc",
- "display_embedder/skia_output_surface_impl.h",
- "display_embedder/skia_output_surface_impl_on_gpu.cc",
- "display_embedder/skia_output_surface_impl_on_gpu.h",
"display_embedder/software_output_surface.cc",
"display_embedder/software_output_surface.h",
"display_embedder/viz_process_context_provider.cc",
@@ -148,8 +144,6 @@ viz_component("service") {
"frame_sinks/video_capture/video_capture_overlay.h",
"frame_sinks/video_detector.cc",
"frame_sinks/video_detector.h",
- "gl/gpu_service_impl.cc",
- "gl/gpu_service_impl.h",
"hit_test/hit_test_aggregator.cc",
"hit_test/hit_test_aggregator.h",
"hit_test/hit_test_aggregator_delegate.h",
@@ -180,33 +174,33 @@ viz_component("service") {
defines = [ "VIZ_SERVICE_IMPLEMENTATION" ]
- configs = [ "//build/config/compiler:no_size_t_to_int_warning" ]
+ allow_circular_includes_from = [ ":gpu_service_dependencies" ]
deps = [
+ "//cc/base",
"//cc/paint",
"//components/crash/core/common:crash_key",
"//components/viz/common",
+ "//gpu/command_buffer/client:gles2_cmd_helper",
"//gpu/command_buffer/client:gles2_implementation",
"//gpu/command_buffer/client:raster",
- "//gpu/command_buffer/service:gles2",
- "//gpu/ipc:gl_in_process_context",
# Note that dependency on //gpu/ipc/client is for GpuMemoryBufferImpl. This
# dependency should not be in public_deps.
"//gpu/ipc/client",
+ "//gpu/ipc/common:surface_handle_type",
"//gpu/ipc/service",
"//gpu/skia_bindings:skia_bindings",
"//gpu/vulkan:buildflags",
"//media",
"//media/capture:capture_lib",
- "//media/gpu/ipc/service",
"//media/mojo/services",
"//services/viz/privileged/interfaces",
- "//skia",
"//ui/display/types",
]
public_deps = [
+ ":gpu_service_dependencies",
"//base",
"//cc",
"//cc/debug",
@@ -223,7 +217,7 @@ viz_component("service") {
"display_embedder/software_output_device_x11.cc",
"display_embedder/software_output_device_x11.h",
]
- configs += [ "//build/config/linux:x11" ]
+ configs = [ "//build/config/linux:x11" ]
deps += [ "//ui/gfx/x" ]
}
@@ -288,6 +282,8 @@ viz_component("service") {
sources += [
"display_embedder/gl_output_surface_android.cc",
"display_embedder/gl_output_surface_android.h",
+ "display_embedder/gl_output_surface_buffer_queue_android.cc",
+ "display_embedder/gl_output_surface_buffer_queue_android.h",
]
}
@@ -296,7 +292,53 @@ viz_component("service") {
}
}
+# The gpu_service_dependencies source set contains source files that
+# use the service side GL library (ui/gl), while the rest of
+# viz/service use the client side GL library. This split is needed
+# because the two GL libraries are incompatible and can't compile
+# together in jumbo builds.
+#
+# Long term all service code is moving to be in the gpu process and
+# then this build target will no longer be needed.
+viz_source_set("gpu_service_dependencies") {
+ sources = [
+ "display_embedder/skia_output_surface_impl.cc",
+ "display_embedder/skia_output_surface_impl.h",
+ "display_embedder/skia_output_surface_impl_on_gpu.cc",
+ "display_embedder/skia_output_surface_impl_on_gpu.h",
+ "gl/gpu_service_impl.cc",
+ "gl/gpu_service_impl.h",
+ ]
+
+ public_deps = [
+ "//gpu/command_buffer/service:gles2",
+ "//gpu/ipc:gl_in_process_context",
+ "//gpu/ipc/service",
+ "//gpu/vulkan:buildflags",
+ "//media/gpu/ipc/service",
+ "//media/mojo/services",
+ "//services/viz/privileged/interfaces/gl",
+ "//skia",
+ ]
+
+ defines = [ "VIZ_SERVICE_IMPLEMENTATION" ]
+
+ if (is_chromeos) {
+ deps = [
+ "//components/arc/video_accelerator",
+ "//gpu/command_buffer/service:gles2",
+ "//media/mojo/services",
+ ]
+ }
+
+ if (enable_vulkan) {
+ public_deps += [ "//gpu/vulkan" ]
+ }
+}
+
viz_source_set("unit_tests") {
+ # Not ready for jumbo compilation. Too much repeated test code.
+ never_build_jumbo = true
testonly = true
sources = [
"display/bsp_tree_unittest.cc",
@@ -394,6 +436,8 @@ viz_source_set("unit_tests") {
}
viz_source_set("perf_tests") {
+ # Not ready for jumbo compilation. Too much repeated test code.
+ never_build_jumbo = true
testonly = true
sources = [
"display/bsp_tree_perftest.cc",
diff --git a/chromium/components/viz/service/display/direct_renderer.cc b/chromium/components/viz/service/display/direct_renderer.cc
index e9ca0df5ffd..8a69964e548 100644
--- a/chromium/components/viz/service/display/direct_renderer.cc
+++ b/chromium/components/viz/service/display/direct_renderer.cc
@@ -15,6 +15,7 @@
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
#include "cc/base/math_util.h"
#include "cc/paint/filter_operations.h"
#include "components/viz/common/display/renderer_settings.h"
@@ -163,6 +164,63 @@ gfx::Rect DirectRenderer::MoveFromDrawToWindowSpace(
return window_rect;
}
+// static
+const TileDrawQuad* DirectRenderer::CanPassBeDrawnDirectly(
+ const RenderPass* pass,
+ bool is_using_vulkan,
+ DisplayResourceProvider* const resource_provider) {
+#if defined(OS_MACOSX)
+ // On Macs, this path can sometimes lead to all black output.
+ // TODO(enne): investigate this and remove this hack.
+ return nullptr;
+#endif
+
+ // Can only collapse a single tile quad.
+ if (pass->quad_list.size() != 1)
+ return nullptr;
+ // If we need copy requests, then render pass has to exist.
+ if (!pass->copy_requests.empty())
+ return nullptr;
+
+ const DrawQuad* quad = *pass->quad_list.BackToFrontBegin();
+ // Hack: this could be supported by concatenating transforms, but
+ // in practice if there is one quad, it is at the origin of the render pass
+ // and has the same size as the pass.
+ if (!quad->shared_quad_state->quad_to_target_transform.IsIdentity() ||
+ quad->rect != pass->output_rect)
+ return nullptr;
+ // The quad is expected to be the entire layer so that AA edges are correct.
+ if (quad->shared_quad_state->quad_layer_rect != quad->rect)
+ return nullptr;
+ if (quad->material != DrawQuad::TILED_CONTENT)
+ return nullptr;
+
+ // TODO(chrishtr): support could be added for opacity, but care needs
+ // to be taken to make sure it is correct w.r.t. non-commutative filters etc.
+ if (quad->shared_quad_state->opacity != 1.0f)
+ return nullptr;
+
+ const TileDrawQuad* tile_quad = TileDrawQuad::MaterialCast(quad);
+ // Hack: this could be supported by passing in a subrectangle to draw
+ // render pass, although in practice if there is only one quad there
+ // will be no border texels on the input.
+ if (tile_quad->tex_coord_rect != gfx::RectF(tile_quad->rect))
+ return nullptr;
+ // Tile quad features not supported in render pass shaders.
+ if (tile_quad->swizzle_contents || tile_quad->nearest_neighbor)
+ return nullptr;
+ if (!is_using_vulkan) {
+ // BUG=skia:3868, Skia currently doesn't support texture rectangle inputs.
+ // See also the DCHECKs about GL_TEXTURE_2D in DrawRenderPassQuad.
+ GLenum target =
+ resource_provider->GetResourceTextureTarget(tile_quad->resource_id());
+ if (target != GL_TEXTURE_2D)
+ return nullptr;
+ }
+
+ return tile_quad;
+}
+
const TileDrawQuad* DirectRenderer::CanPassBeDrawnDirectly(
const RenderPass* pass) {
return nullptr;
@@ -203,7 +261,7 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order,
const gfx::Size& device_viewport_size) {
DCHECK(visible_);
TRACE_EVENT0("viz,benchmark", "DirectRenderer::DrawFrame");
- UMA_HISTOGRAM_COUNTS(
+ UMA_HISTOGRAM_COUNTS_1M(
"Renderer4.renderPassCount",
base::saturated_cast<int>(render_passes_in_draw_order->size()));
diff --git a/chromium/components/viz/service/display/direct_renderer.h b/chromium/components/viz/service/display/direct_renderer.h
index 5cf6c6840aa..5780b2f3535 100644
--- a/chromium/components/viz/service/display/direct_renderer.h
+++ b/chromium/components/viz/service/display/direct_renderer.h
@@ -185,6 +185,10 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
// If a pass contains a single tile draw quad and can be drawn without
// a render pass (e.g. applying a filter directly to the tile quad)
// return that quad, otherwise return null.
+ static const TileDrawQuad* CanPassBeDrawnDirectly(
+ const RenderPass* pass,
+ bool is_using_vulkan,
+ DisplayResourceProvider* const resource_provider);
virtual const TileDrawQuad* CanPassBeDrawnDirectly(const RenderPass* pass);
virtual void FinishDrawingQuadList() {}
virtual bool FlippedFramebuffer() const = 0;
diff --git a/chromium/components/viz/service/display/display.cc b/chromium/components/viz/service/display/display.cc
index d6dfe6e9f92..9414a5d154e 100644
--- a/chromium/components/viz/service/display/display.cc
+++ b/chromium/components/viz/service/display/display.cc
@@ -39,6 +39,18 @@
namespace viz {
+namespace {
+
+// Assign each Display instance a starting value for the the display-trace id,
+// so that multiple Displays all don't start at 0, because that makes it
+// difficult to associate the trace-events with the particular displays.
+int64_t GetStartingTraceId() {
+ static int64_t client = 0;
+ return ((++client & 0xffffffff) << 32);
+}
+
+} // namespace
+
Display::Display(
SharedBitmapManager* bitmap_manager,
const RendererSettings& settings,
@@ -53,7 +65,9 @@ Display::Display(
skia_output_surface_(skia_output_surface),
output_surface_(std::move(output_surface)),
scheduler_(std::move(scheduler)),
- current_task_runner_(std::move(current_task_runner)) {
+ current_task_runner_(std::move(current_task_runner)),
+ swapped_trace_id_(GetStartingTraceId()),
+ last_acked_trace_id_(swapped_trace_id_) {
DCHECK(output_surface_);
DCHECK(frame_sink_id_.is_valid());
if (scheduler_)
@@ -216,12 +230,19 @@ void Display::InitializeRenderer() {
mode, output_surface_->context_provider(), bitmap_manager_);
if (settings_.use_skia_renderer && mode == DisplayResourceProvider::kGpu) {
- // Check the compositing mode, because SkiaRenderer only works with GPU
- // compositing.
- DCHECK(output_surface_);
- renderer_ = std::make_unique<SkiaRenderer>(
- &settings_, output_surface_.get(), resource_provider_.get(),
- skia_output_surface_);
+ // Default to use DDL if skia_output_surface is not null.
+ if (skia_output_surface_) {
+ renderer_ = std::make_unique<SkiaRenderer>(
+ &settings_, output_surface_.get(), resource_provider_.get(),
+ skia_output_surface_, SkiaRenderer::DrawMode::DDL);
+ } else {
+ // GPU compositing with GL.
+ DCHECK(output_surface_);
+ DCHECK(output_surface_->context_provider());
+ renderer_ = std::make_unique<SkiaRenderer>(
+ &settings_, output_surface_.get(), resource_provider_.get(),
+ nullptr /* skia_output_surface */, SkiaRenderer::DrawMode::GL);
+ }
} else if (output_surface_->context_provider()) {
renderer_ = std::make_unique<GLRenderer>(&settings_, output_surface_.get(),
resource_provider_.get(),
@@ -230,7 +251,7 @@ void Display::InitializeRenderer() {
} else if (output_surface_->vulkan_context_provider()) {
renderer_ = std::make_unique<SkiaRenderer>(
&settings_, output_surface_.get(), resource_provider_.get(),
- nullptr /* skia_output_surface */);
+ nullptr /* skia_output_surface */, SkiaRenderer::DrawMode::VULKAN);
#endif
} else {
auto renderer = std::make_unique<SoftwareRenderer>(
@@ -280,6 +301,18 @@ bool Display::DrawAndSwap() {
return false;
}
+ // During aggregation, SurfaceAggregator marks all resources used for a draw
+ // in the resource provider. This has the side effect of deleting unused
+ // resources and their textures, generating sync tokens, and returning the
+ // resources to the client. This involves GL work which is issued before
+ // drawing commands, and gets prioritized by GPU scheduler because sync token
+ // dependencies aren't issued until the draw.
+ //
+ // Batch and defer returning resources in resource provider. This defers the
+ // GL commands for deleting resources to after the draw, and prevents context
+ // switching because the scheduler knows sync token dependencies at that time.
+ DisplayResourceProvider::ScopedBatchReturnResources returner(
+ resource_provider_.get());
base::ElapsedTimer aggregate_timer;
CompositorFrame frame = aggregator_->Aggregate(
current_surface_id_,
@@ -399,14 +432,13 @@ bool Display::DrawAndSwap() {
callbacks.emplace_back(std::move(callback));
}
}
- bool need_presentation_feedback = !callbacks.empty();
- if (need_presentation_feedback)
- pending_presented_callbacks_.emplace_back(std::move(callbacks));
+ pending_presented_callbacks_.emplace_back(std::move(callbacks));
ui::LatencyInfo::TraceIntermediateFlowEvents(frame.metadata.latency_info,
"Display::DrawAndSwap");
cc::benchmark_instrumentation::IssueDisplayRenderingStatsEvent();
+ const bool need_presentation_feedback = true;
renderer_->SwapBuffers(std::move(frame.metadata.latency_info),
need_presentation_feedback);
if (scheduler_)
@@ -435,9 +467,9 @@ bool Display::DrawAndSwap() {
}
}
- ++last_acked_trace_id_;
- TRACE_EVENT_ASYNC_END0("viz,benchmark", "Graphics.Pipeline.DrawAndSwap",
- last_acked_trace_id_);
+ TRACE_EVENT_ASYNC_END1("viz,benchmark", "Graphics.Pipeline.DrawAndSwap",
+ swapped_trace_id_, "status", "canceled");
+ --swapped_trace_id_;
if (scheduler_) {
scheduler_->DidSwapBuffers();
scheduler_->DidReceiveSwapBuffersAck();
diff --git a/chromium/components/viz/service/display/display.h b/chromium/components/viz/service/display/display.h
index fd23ced5c53..6b3c4ef7eb7 100644
--- a/chromium/components/viz/service/display/display.h
+++ b/chromium/components/viz/service/display/display.h
@@ -171,8 +171,8 @@ class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient,
base::circular_deque<std::vector<Surface::PresentedCallback>>
pending_presented_callbacks_;
- int32_t swapped_trace_id_ = 0;
- int32_t last_acked_trace_id_ = 0;
+ int64_t swapped_trace_id_ = 0;
+ int64_t last_acked_trace_id_ = 0;
DISALLOW_COPY_AND_ASSIGN(Display);
};
diff --git a/chromium/components/viz/service/display/display_resource_provider.cc b/chromium/components/viz/service/display/display_resource_provider.cc
index 129624f40f5..bf811bb09a2 100644
--- a/chromium/components/viz/service/display/display_resource_provider.cc
+++ b/chromium/components/viz/service/display/display_resource_provider.cc
@@ -168,12 +168,13 @@ bool DisplayResourceProvider::OnMemoryDump(
#if defined(OS_ANDROID)
void DisplayResourceProvider::SendPromotionHints(
- const OverlayCandidateList::PromotionHintInfoMap& promotion_hints) {
+ const OverlayCandidateList::PromotionHintInfoMap& promotion_hints,
+ const ResourceIdSet& requestor_set) {
GLES2Interface* gl = ContextGL();
if (!gl)
return;
- for (const auto& id : wants_promotion_hints_set_) {
+ for (const auto& id : requestor_set) {
auto it = resources_.find(id);
if (it == resources_.end())
continue;
@@ -210,15 +211,28 @@ bool DisplayResourceProvider::IsBackedBySurfaceTexture(ResourceId id) {
return resource->transferable.is_backed_by_surface_texture;
}
-bool DisplayResourceProvider::WantsPromotionHintForTesting(ResourceId id) {
- return wants_promotion_hints_set_.count(id) > 0;
-}
-
size_t DisplayResourceProvider::CountPromotionHintRequestsForTesting() {
return wants_promotion_hints_set_.size();
}
#endif
+bool DisplayResourceProvider::DoesResourceWantPromotionHint(
+ ResourceId id) const {
+#if defined(OS_ANDROID)
+ return wants_promotion_hints_set_.count(id) > 0;
+#else
+ return false;
+#endif
+}
+
+bool DisplayResourceProvider::DoAnyResourcesWantPromotionHints() const {
+#if defined(OS_ANDROID)
+ return wants_promotion_hints_set_.size() > 0;
+#else
+ return false;
+#endif
+}
+
bool DisplayResourceProvider::IsOverlayCandidate(ResourceId id) {
ChildResource* resource = TryGetResource(id);
// TODO(ericrk): We should never fail TryGetResource, but we appear to
@@ -289,8 +303,7 @@ void DisplayResourceProvider::ReceiveFromChild(
CHECK(child_it != children_.end());
Child& child_info = child_it->second;
DCHECK(!child_info.marked_for_deletion);
- for (std::vector<TransferableResource>::const_iterator it = resources.begin();
- it != resources.end(); ++it) {
+ for (auto it = resources.begin(); it != resources.end(); ++it) {
auto resource_in_map_it = child_info.child_to_parent_map.find(it->id);
if (resource_in_map_it != child_info.child_to_parent_map.end()) {
ChildResource* resource = GetResource(resource_in_map_it->second);
@@ -349,7 +362,7 @@ void DisplayResourceProvider::DeclareUsedResourcesFromChild(
const std::unordered_map<ResourceId, ResourceId>&
DisplayResourceProvider::GetChildToParentMap(int child) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- ChildMap::const_iterator it = children_.find(child);
+ auto it = children_.find(child);
DCHECK(it != children_.end());
DCHECK(!it->second.marked_for_deletion);
return it->second.child_to_parent_map;
@@ -555,14 +568,8 @@ void DisplayResourceProvider::TryReleaseResource(ResourceMap::iterator it) {
ChildResource* resource = &it->second;
if (resource->marked_for_deletion && !resource->lock_for_read_count &&
!resource->locked_for_external_use) {
- if (batch_return_resources_) {
- batched_returning_resources_[resource->child_id].push_back(id);
- } else {
- auto child_it = children_.find(resource->child_id);
- std::vector<ResourceId> unused;
- unused.push_back(id);
- DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
- }
+ auto child_it = children_.find(resource->child_id);
+ DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, {id});
}
}
@@ -617,9 +624,23 @@ void DisplayResourceProvider::DeleteAndReturnUnusedResourcesToChild(
DCHECK(child_it != children_.end());
Child* child_info = &child_it->second;
+ // No work is done in this case.
if (unused.empty() && !child_info->marked_for_deletion)
return;
+ // Store unused resources while batching is enabled.
+ if (batch_return_resources_lock_count_ > 0) {
+ int child_id = child_it->first;
+ // Ensure that we have an entry in |batched_returning_resources_| for child
+ // even if |unused| is empty, in case child is marked for deletion.
+ // Note: emplace() does not overwrite an entry if already present.
+ batched_returning_resources_.emplace(child_id, std::vector<ResourceId>());
+ auto& child_resources = batched_returning_resources_[child_id];
+ child_resources.reserve(child_resources.size() + unused.size());
+ child_resources.insert(child_resources.end(), unused.begin(), unused.end());
+ return;
+ }
+
std::vector<ReturnedResource> to_return;
to_return.reserve(unused.size());
std::vector<ReturnedResource*> need_synchronization_resources;
@@ -631,6 +652,9 @@ void DisplayResourceProvider::DeleteAndReturnUnusedResourcesToChild(
for (ResourceId local_id : unused) {
auto it = resources_.find(local_id);
CHECK(it != resources_.end());
+
+ resource_sk_image_.erase(local_id);
+
ChildResource& resource = it->second;
ResourceId child_id = resource.transferable.id;
@@ -750,15 +774,32 @@ void DisplayResourceProvider::DestroyChildInternal(ChildMap::iterator it,
}
void DisplayResourceProvider::SetBatchReturnResources(bool batch) {
- DCHECK_NE(batch_return_resources_, batch);
- batch_return_resources_ = batch;
- if (!batch) {
- for (const auto& resources : batched_returning_resources_) {
- auto child_it = children_.find(resources.first);
- DCHECK(child_it != children_.end());
- DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, resources.second);
+ if (batch) {
+ DCHECK_GE(batch_return_resources_lock_count_, 0);
+ batch_return_resources_lock_count_++;
+ } else {
+ DCHECK_GT(batch_return_resources_lock_count_, 0);
+ batch_return_resources_lock_count_--;
+ if (batch_return_resources_lock_count_ == 0) {
+ for (auto& child_resources_kv : batched_returning_resources_) {
+ auto child_it = children_.find(child_resources_kv.first);
+
+ // Remove duplicates from child's unused resources. Duplicates are
+ // possible when batching is enabled because resources are saved in
+ // |batched_returning_resources_| for removal, and not removed from the
+ // child's |child_to_parent_map|, so the same set of resources can be
+ // saved again using DeclareUsedResourcesForChild() or DestroyChild().
+ auto& unused_resources = child_resources_kv.second;
+ std::sort(unused_resources.begin(), unused_resources.end());
+ auto last =
+ std::unique(unused_resources.begin(), unused_resources.end());
+ unused_resources.erase(last, unused_resources.end());
+
+ DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL,
+ unused_resources);
+ }
+ batched_returning_resources_.clear();
}
- batched_returning_resources_.clear();
}
}
@@ -831,6 +872,7 @@ DisplayResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage(
ResourceFormatToClosestSkColorType(!resource_provider->IsSoftware(),
resource->transferable.format),
kPremul_SkAlphaType, nullptr);
+ resource_provider_->resource_sk_image_[resource_id] = sk_image_;
return;
}
@@ -850,6 +892,7 @@ DisplayResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage(
resource_provider->PopulateSkBitmapWithResource(&sk_bitmap, resource);
sk_bitmap.setImmutable();
sk_image_ = SkImage::MakeFromBitmap(sk_bitmap);
+ resource_provider_->resource_sk_image_[resource_id] = sk_image_;
}
DisplayResourceProvider::ScopedReadLockSkImage::~ScopedReadLockSkImage() {
diff --git a/chromium/components/viz/service/display/display_resource_provider.h b/chromium/components/viz/service/display/display_resource_provider.h
index 1e22a80758b..3e790d7df9f 100644
--- a/chromium/components/viz/service/display/display_resource_provider.h
+++ b/chromium/components/viz/service/display/display_resource_provider.h
@@ -78,23 +78,32 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
#if defined(OS_ANDROID)
// Send an overlay promotion hint to all resources that requested it via
- // |wants_promotion_hints_set_|. |promotable_hints| contains all the
- // resources that should be told that they're promotable. Others will be told
- // that they're not promotable right now.
+ // |requestor_set|. |promotable_hints| contains all the resources that should
+ // be told that they're promotable. Others will be told that they're not.
+ //
+ // We don't use |wants_promotion_hints_set_| in place of |requestor_set|,
+ // since we might have resources that aren't used for drawing. Sending a hint
+ // for a resource that wasn't even considered for overlay would be misleading
+ // to the requestor; the resource might be overlayable except that nobody
+ // tried to do it.
void SendPromotionHints(
- const OverlayCandidateList::PromotionHintInfoMap& promotion_hints);
+ const OverlayCandidateList::PromotionHintInfoMap& promotion_hints,
+ const ResourceIdSet& requestor_set);
// Indicates if this resource is backed by an Android SurfaceTexture, and thus
// can't really be promoted to an overlay.
bool IsBackedBySurfaceTexture(ResourceId id);
- // Indicates if this resource wants to receive promotion hints.
- bool WantsPromotionHintForTesting(ResourceId id);
-
// Return the number of resources that request promotion hints.
size_t CountPromotionHintRequestsForTesting();
#endif
+ // Indicates if this resource wants to receive promotion hints.
+ bool DoesResourceWantPromotionHint(ResourceId id) const;
+
+ // Return true if and only if any resource wants a promotion hint.
+ bool DoAnyResourcesWantPromotionHints() const;
+
bool IsResourceSoftwareBacked(ResourceId id);
GLenum GetResourceTextureTarget(ResourceId id);
// Return the format of the underlying buffer that can be used for scanout.
@@ -461,13 +470,11 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
ResourceMap resources_;
ChildMap children_;
base::flat_map<ResourceId, sk_sp<SkImage>> resource_sk_image_;
- // Maps from a child id to the set of resources to be returned to it.
- base::small_map<std::map<int, std::vector<ResourceId>>>
- batched_returning_resources_;
+ base::flat_map<int, std::vector<ResourceId>> batched_returning_resources_;
scoped_refptr<ResourceFence> current_read_lock_fence_;
// Keep track of whether deleted resources should be batched up or returned
// immediately.
- bool batch_return_resources_ = false;
+ int batch_return_resources_lock_count_ = 0;
// Set to true when the ContextProvider becomes lost, to inform that resources
// modified by this class are now in an indeterminate state.
bool lost_context_provider_ = false;
diff --git a/chromium/components/viz/service/display/display_resource_provider_unittest.cc b/chromium/components/viz/service/display/display_resource_provider_unittest.cc
index bfbc68edf26..27133f36d55 100644
--- a/chromium/components/viz/service/display/display_resource_provider_unittest.cc
+++ b/chromium/components/viz/service/display/display_resource_provider_unittest.cc
@@ -819,58 +819,89 @@ TEST_P(DisplayResourceProviderTest, ScopedBatchReturnResourcesPreventsReturn) {
GetReturnCallback(&returned_to_child), true);
// Transfer some resources to the parent.
- std::vector<ResourceId> resource_ids_to_transfer;
- ResourceId ids[2];
- for (size_t i = 0; i < base::size(ids); i++) {
+ constexpr size_t kTotalResources = 5;
+ constexpr size_t kLockedResources = 3;
+ constexpr size_t kUsedResources = 4;
+ ResourceId ids[kTotalResources];
+ for (size_t i = 0; i < kTotalResources; i++) {
TransferableResource tran = CreateResource(RGBA_8888);
ids[i] = child_resource_provider_->ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
- resource_ids_to_transfer.push_back(ids[i]);
}
+ std::vector<ResourceId> resource_ids_to_transfer(ids, ids + kTotalResources);
std::vector<TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list,
child_context_provider_.get());
- ASSERT_EQ(2u, list.size());
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(ids[0]));
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(ids[1]));
+ ASSERT_EQ(kTotalResources, list.size());
+ for (const auto& id : ids)
+ EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
resource_provider_->ReceiveFromChild(child_id, list);
// In DisplayResourceProvider's namespace, use the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
resource_provider_->GetChildToParentMap(child_id);
-
std::vector<std::unique_ptr<DisplayResourceProvider::ScopedReadLockGL>>
read_locks;
- for (auto& resource_id : list) {
- unsigned int mapped_resource_id = resource_map[resource_id.id];
+ for (size_t i = 0; i < kLockedResources; i++) {
+ unsigned int mapped_resource_id = resource_map[ids[i]];
resource_provider_->WaitSyncToken(mapped_resource_id);
read_locks.push_back(
std::make_unique<DisplayResourceProvider::ScopedReadLockGL>(
resource_provider_.get(), mapped_resource_id));
}
- resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- std::unique_ptr<DisplayResourceProvider::ScopedBatchReturnResources>
- returner =
- std::make_unique<DisplayResourceProvider::ScopedBatchReturnResources>(
- resource_provider_.get());
- EXPECT_EQ(0u, returned_to_child.size());
+ // Mark all locked resources, and one unlocked resource as used for first
+ // batch.
+ {
+ DisplayResourceProvider::ScopedBatchReturnResources returner(
+ resource_provider_.get());
+ resource_provider_->DeclareUsedResourcesFromChild(
+ child_id, ResourceIdSet(ids, ids + kUsedResources));
+ EXPECT_EQ(0u, returned_to_child.size());
+ }
+ EXPECT_EQ(1u, returned_to_child.size());
+ child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+ returned_to_child.clear();
- read_locks.clear();
- EXPECT_EQ(0u, returned_to_child.size());
+ // Return all locked resources.
+ {
+ DisplayResourceProvider::ScopedBatchReturnResources returner(
+ resource_provider_.get());
+ resource_provider_->DeclareUsedResourcesFromChild(
+ child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
+ // Can be called multiple times while batching is enabled. This happens in
+ // practice when the same surface is visited using different paths during
+ // surface aggregation.
+ resource_provider_->DeclareUsedResourcesFromChild(
+ child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
+ read_locks.clear();
+ EXPECT_EQ(0u, returned_to_child.size());
+ }
+ EXPECT_EQ(kLockedResources, returned_to_child.size());
+ // Returned resources that were locked share the same sync token.
+ for (const auto& resource : returned_to_child)
+ EXPECT_EQ(resource.sync_token, returned_to_child[0].sync_token);
- returner.reset();
- EXPECT_EQ(2u, returned_to_child.size());
- // All resources in a batch should share a sync token.
- EXPECT_EQ(returned_to_child[0].sync_token, returned_to_child[1].sync_token);
+ child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+ returned_to_child.clear();
+ // Returns from destroying the child is also batched.
+ {
+ DisplayResourceProvider::ScopedBatchReturnResources returner(
+ resource_provider_.get());
+ resource_provider_->DestroyChild(child_id);
+ EXPECT_EQ(0u, returned_to_child.size());
+ }
+ EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
- EXPECT_CALL(release, Released(_, _)).Times(2);
- child_resource_provider_->RemoveImportedResource(ids[0]);
- child_resource_provider_->RemoveImportedResource(ids[1]);
+ returned_to_child.clear();
+
+ EXPECT_CALL(release, Released(_, _)).Times(kTotalResources);
+ for (const auto& id : ids)
+ child_resource_provider_->RemoveImportedResource(id);
}
TEST_P(DisplayResourceProviderTest, LostMailboxInParent) {
@@ -1341,12 +1372,14 @@ TEST_P(DisplayResourceProviderTest, OverlayPromotionHint) {
// up in the request set before we wait, then the attempt to notify them wil;
// DCHECK when we try to lock them for reading in SendPromotionHints.
EXPECT_EQ(0u, resource_provider_->CountPromotionHintRequestsForTesting());
+ EXPECT_FALSE(resource_provider_->DoAnyResourcesWantPromotionHints());
{
resource_provider_->WaitSyncToken(mapped_id1);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
mapped_id1);
}
EXPECT_EQ(1u, resource_provider_->CountPromotionHintRequestsForTesting());
+ EXPECT_TRUE(resource_provider_->DoAnyResourcesWantPromotionHints());
EXPECT_EQ(list[0].mailbox_holder.sync_token, gl_->last_waited_sync_token());
ResourceIdSet resource_ids_to_receive;
@@ -1363,11 +1396,11 @@ TEST_P(DisplayResourceProviderTest, OverlayPromotionHint) {
// Make sure that the request for a promotion hint was noticed.
EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id1));
EXPECT_TRUE(resource_provider_->IsBackedBySurfaceTexture(mapped_id1));
- EXPECT_TRUE(resource_provider_->WantsPromotionHintForTesting(mapped_id1));
+ EXPECT_TRUE(resource_provider_->DoesResourceWantPromotionHint(mapped_id1));
EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id2));
EXPECT_FALSE(resource_provider_->IsBackedBySurfaceTexture(mapped_id2));
- EXPECT_FALSE(resource_provider_->WantsPromotionHintForTesting(mapped_id2));
+ EXPECT_FALSE(resource_provider_->DoesResourceWantPromotionHint(mapped_id2));
// ResourceProvider maintains a set of promotion hint requests that should be
// cleared when resources are deleted.
@@ -1376,6 +1409,7 @@ TEST_P(DisplayResourceProviderTest, OverlayPromotionHint) {
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
EXPECT_EQ(0u, resource_provider_->CountPromotionHintRequestsForTesting());
+ EXPECT_FALSE(resource_provider_->DoAnyResourcesWantPromotionHints());
resource_provider_->DestroyChild(child_id);
diff --git a/chromium/components/viz/service/display/display_scheduler.cc b/chromium/components/viz/service/display/display_scheduler.cc
index 8c3816d14f3..f155aa6ef81 100644
--- a/chromium/components/viz/service/display/display_scheduler.cc
+++ b/chromium/components/viz/service/display/display_scheduler.cc
@@ -329,11 +329,7 @@ bool DisplayScheduler::OnSurfaceDamaged(const SurfaceId& surface_id,
bool damaged = client_->SurfaceDamaged(surface_id, ack);
ProcessSurfaceDamage(surface_id, ack, damaged);
- // If we are not visible, return false. We may never draw in this
- // state, and other displays may have embedded this surface without
- // damage. Those displays shouldn't have their frame production
- // blocked by waiting for this ack.
- return damaged && visible_;
+ return damaged;
}
void DisplayScheduler::OnSurfaceDiscarded(const SurfaceId& surface_id) {
diff --git a/chromium/components/viz/service/display/display_scheduler_unittest.cc b/chromium/components/viz/service/display/display_scheduler_unittest.cc
index a1ec362100d..2430f76c73a 100644
--- a/chromium/components/viz/service/display/display_scheduler_unittest.cc
+++ b/chromium/components/viz/service/display/display_scheduler_unittest.cc
@@ -48,8 +48,7 @@ class FakeDisplaySchedulerClient : public DisplaySchedulerClient {
bool SurfaceDamaged(const SurfaceId& surface_id,
const BeginFrameAck& ack) override {
- undrawn_surfaces_.insert(surface_id);
- return true;
+ return false;
}
void SurfaceDiscarded(const SurfaceId& surface_id) override {}
@@ -62,13 +61,16 @@ class FakeDisplaySchedulerClient : public DisplaySchedulerClient {
void SetNextDrawAndSwapFails() { next_draw_and_swap_fails_ = true; }
+ void SurfaceDamaged(const SurfaceId& surface_id) {
+ undrawn_surfaces_.insert(surface_id);
+ }
+
const BeginFrameAck& last_begin_frame_ack() { return last_begin_frame_ack_; }
protected:
int draw_and_swap_count_;
bool next_draw_and_swap_fails_;
std::set<SurfaceId> undrawn_surfaces_;
- std::set<SurfaceId> non_damaging_surfaces_;
BeginFrameAck last_begin_frame_ack_;
};
@@ -112,8 +114,6 @@ class TestDisplayScheduler : public DisplayScheduler {
bool has_pending_surfaces() { return has_pending_surfaces_; }
- bool is_visible() const { return visible_; }
-
protected:
int scheduler_begin_frame_deadline_count_;
};
@@ -152,11 +152,9 @@ class DisplaySchedulerTest : public testing::Test {
}
void SurfaceDamaged(const SurfaceId& surface_id) {
- // While our fake client always returns true for damage, OnSurfaceDamaged
- // should only return true if we are visible.
- EXPECT_EQ(
- scheduler_.is_visible(),
- scheduler_.OnSurfaceDamaged(surface_id, AckForCurrentBeginFrame()));
+ client_.SurfaceDamaged(surface_id);
+ scheduler_.ProcessSurfaceDamage(surface_id, AckForCurrentBeginFrame(),
+ true);
}
protected:
diff --git a/chromium/components/viz/service/display/display_unittest.cc b/chromium/components/viz/service/display/display_unittest.cc
index d4e8ccf2b49..40bf2c85b52 100644
--- a/chromium/components/viz/service/display/display_unittest.cc
+++ b/chromium/components/viz/service/display/display_unittest.cc
@@ -2522,7 +2522,8 @@ TEST_F(DisplayTest, CompositorFrameWithCoveredRenderPass) {
quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
quad1->SetNew(shared_quad_state2, rect1, rect1, render_pass_id,
mask_resource_id, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+ 1.0f);
EXPECT_EQ(1u, frame.render_pass_list.front()->quad_list.size());
EXPECT_EQ(1u, frame.render_pass_list.at(1)->quad_list.size());
display_->RemoveOverdrawQuads(&frame);
@@ -2767,10 +2768,10 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) {
SkBlendMode::kSrcOver, 0);
R1->SetNew(shared_quad_state, rect1, rect1, render_pass_id,
mask_resource_id, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
R2->SetNew(shared_quad_state, rect2, rect2, render_pass_id,
mask_resource_id, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
D1->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
D2->SetNew(shared_quad_state4, rect4, rect4, SK_ColorBLACK, false);
EXPECT_EQ(4u, frame.render_pass_list.front()->quad_list.size());
@@ -2815,10 +2816,10 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) {
SkBlendMode::kSrcOver, 0);
R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id,
mask_resource_id, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
R2->SetNew(shared_quad_state, rect1, rect1, render_pass_id,
mask_resource_id, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
D1->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
D2->SetNew(shared_quad_state4, rect6, rect6, SK_ColorBLACK, false);
EXPECT_EQ(4u, frame.render_pass_list.front()->quad_list.size());
@@ -2862,10 +2863,10 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) {
SkBlendMode::kSrcOver, 0);
R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id,
mask_resource_id, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
R2->SetNew(shared_quad_state, rect1, rect1, render_pass_id,
mask_resource_id, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
D1->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
D2->SetNew(shared_quad_state4, rect7, rect7, SK_ColorBLACK, false);
EXPECT_EQ(4u, frame.render_pass_list.front()->quad_list.size());
diff --git a/chromium/components/viz/service/display/gl_renderer.cc b/chromium/components/viz/service/display/gl_renderer.cc
index a9ba5ca388b..207c08f0806 100644
--- a/chromium/components/viz/service/display/gl_renderer.cc
+++ b/chromium/components/viz/service/display/gl_renderer.cc
@@ -259,6 +259,9 @@ struct GLRenderer::DrawRenderPassDrawQuadParams {
// Filtered background texture.
sk_sp<SkImage> background_image;
GLuint background_image_id = 0;
+ // A multiplier for the temporary surface we create to apply the backdrop
+ // filter.
+ float backdrop_filter_quality = 1.0;
// Whether the original background texture is needed for the mask.
bool mask_for_background = false;
};
@@ -792,9 +795,15 @@ uint32_t GLRenderer::GetBackdropTexture(const gfx::Rect& window_rect) {
gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl_->BindTexture(GL_TEXTURE_2D, texture_id);
- gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, GetFramebufferCopyTextureFormat(),
- window_rect.x(), window_rect.y(), window_rect.width(),
+ unsigned internalformat = GetFramebufferCopyTextureFormat();
+ // CopyTexImage2D requires inernalformat channels to be a subset of
+ // the channels of the source texture internalformat.
+ DCHECK(internalformat == GL_RGB || internalformat == GL_RGBA ||
+ internalformat == GL_BGRA_EXT);
+ if (internalformat == GL_BGRA_EXT)
+ internalformat = GL_RGBA;
+ gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, internalformat, window_rect.x(),
+ window_rect.y(), window_rect.width(),
window_rect.height(), 0);
gl_->BindTexture(GL_TEXTURE_2D, 0);
return texture_id;
@@ -805,7 +814,8 @@ sk_sp<SkImage> GLRenderer::ApplyBackgroundFilters(
const cc::FilterOperations& background_filters,
uint32_t background_texture,
const gfx::Rect& rect,
- const gfx::Rect& unclipped_rect) {
+ const gfx::Rect& unclipped_rect,
+ const float backdrop_filter_quality) {
DCHECK(ShouldApplyBackgroundFilters(quad, &background_filters));
auto use_gr_context = ScopedUseGrContext::Create(this);
@@ -834,9 +844,12 @@ sk_sp<SkImage> GLRenderer::ApplyBackgroundFilters(
return nullptr;
}
+ gfx::Rect quality_adjusted_rect =
+ ScaleToEnclosingRect(rect, backdrop_filter_quality);
+
// Create surface to draw into.
- SkImageInfo dst_info =
- SkImageInfo::MakeN32Premul(rect.width(), rect.height());
+ SkImageInfo dst_info = SkImageInfo::MakeN32Premul(
+ quality_adjusted_rect.width(), quality_adjusted_rect.height());
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
use_gr_context->context(), SkBudgeted::kYes, dst_info);
if (!surface) {
@@ -850,12 +863,17 @@ sk_sp<SkImage> GLRenderer::ApplyBackgroundFilters(
// to disable subnormal floats for performance and security reasons.
cc::ScopedSubnormalFloatDisabler disabler;
SkMatrix local_matrix;
- local_matrix.setScale(quad->filters_scale.x(), quad->filters_scale.y());
+ local_matrix.setScale(quad->filters_scale.x() * backdrop_filter_quality,
+ quad->filters_scale.y() * backdrop_filter_quality);
SkPaint paint;
paint.setImageFilter(filter->makeWithLocalMatrix(local_matrix));
- surface->getCanvas()->translate(-rect.x(), -rect.y());
- surface->getCanvas()->drawImage(src_image, rect.x(), rect.y(), &paint);
+ surface->getCanvas()->translate(-quality_adjusted_rect.x(),
+ -quality_adjusted_rect.y());
+ surface->getCanvas()->drawImageRect(
+ src_image, SkRect::MakeWH(rect.width(), rect.height()),
+ RectToSkRect(quality_adjusted_rect), &paint);
+
// Flush the drawing before source texture read lock goes out of scope.
// Skia API does not guarantee that when the SkImage goes out of scope,
// its externally referenced resources would force the rendering to be
@@ -870,54 +888,8 @@ sk_sp<SkImage> GLRenderer::ApplyBackgroundFilters(
}
const TileDrawQuad* GLRenderer::CanPassBeDrawnDirectly(const RenderPass* pass) {
-#if defined(OS_MACOSX)
- // On Macs, this path can sometimes lead to all black output.
- // TODO(enne): investigate this and remove this hack.
- return nullptr;
-#endif
-
- // Can only collapse a single tile quad.
- if (pass->quad_list.size() != 1)
- return nullptr;
- // If we need copy requests, then render pass has to exist.
- if (!pass->copy_requests.empty())
- return nullptr;
-
- const DrawQuad* quad = *pass->quad_list.BackToFrontBegin();
- // Hack: this could be supported by concatenating transforms, but
- // in practice if there is one quad, it is at the origin of the render pass
- // and has the same size as the pass.
- if (!quad->shared_quad_state->quad_to_target_transform.IsIdentity() ||
- quad->rect != pass->output_rect)
- return nullptr;
- // The quad is expected to be the entire layer so that AA edges are correct.
- if (quad->shared_quad_state->quad_layer_rect != quad->rect)
- return nullptr;
- if (quad->material != DrawQuad::TILED_CONTENT)
- return nullptr;
-
- // TODO(chrishtr): support could be added for opacity, but care needs
- // to be taken to make sure it is correct w.r.t. non-commutative filters etc.
- if (quad->shared_quad_state->opacity != 1.0f)
- return nullptr;
-
- const TileDrawQuad* tile_quad = TileDrawQuad::MaterialCast(quad);
- // Hack: this could be supported by passing in a subrectangle to draw
- // render pass, although in practice if there is only one quad there
- // will be no border texels on the input.
- if (tile_quad->tex_coord_rect != gfx::RectF(tile_quad->rect))
- return nullptr;
- // Tile quad features not supported in render pass shaders.
- if (tile_quad->swizzle_contents || tile_quad->nearest_neighbor)
- return nullptr;
- // BUG=skia:3868, Skia currently doesn't support texture rectangle inputs.
- // See also the DCHECKs about GL_TEXTURE_2D in DrawRenderPassQuad.
- GLenum target =
- resource_provider_->GetResourceTextureTarget(tile_quad->resource_id());
- if (target != GL_TEXTURE_2D)
- return nullptr;
-
- return tile_quad;
+ return DirectRenderer::CanPassBeDrawnDirectly(pass, false,
+ resource_provider_);
}
void GLRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad,
@@ -929,6 +901,7 @@ void GLRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad,
params.window_matrix = current_frame()->window_matrix;
params.projection_matrix = current_frame()->projection_matrix;
params.tex_coord_rect = quad->tex_coord_rect;
+ params.backdrop_filter_quality = quad->backdrop_filter_quality;
if (bypass != render_pass_bypass_quads_.end()) {
TileDrawQuad* tile_quad = &bypass->second;
// The projection matrix used by GLRenderer has a flip. As tile texture
@@ -1081,7 +1054,8 @@ void GLRenderer::UpdateRPDQShadersForBlending(
// pixels' coordinate space.
params->background_image = ApplyBackgroundFilters(
quad, *params->background_filters, params->background_texture,
- params->background_rect, unclipped_rect);
+ params->background_rect, unclipped_rect,
+ params->backdrop_filter_quality);
if (params->background_image) {
params->background_image_id =
GetGLTextureIDFromSkImage(params->background_image.get());
@@ -1403,6 +1377,8 @@ void GLRenderer::UpdateRPDQUniforms(DrawRenderPassDrawQuadParams* params) {
if (params->background_image_id) {
gl_->ActiveTexture(GL_TEXTURE0 + last_texture_unit);
gl_->BindTexture(GL_TEXTURE_2D, params->background_image_id);
+ if (params->backdrop_filter_quality != 1.0f)
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl_->ActiveTexture(GL_TEXTURE0);
}
// If |mask_for_background| then we have both |background_image_id| and
diff --git a/chromium/components/viz/service/display/gl_renderer.h b/chromium/components/viz/service/display/gl_renderer.h
index ac1516231d6..e6e3a638854 100644
--- a/chromium/components/viz/service/display/gl_renderer.h
+++ b/chromium/components/viz/service/display/gl_renderer.h
@@ -76,23 +76,7 @@ class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer {
protected:
void DidChangeVisibility() override;
- const gfx::QuadF& SharedGeometryQuad() const { return shared_geometry_quad_; }
- const StaticGeometryBinding* SharedGeometry() const {
- return shared_geometry_.get();
- }
-
- // Returns the format to use for storage if copying from the current
- // framebuffer. If the root renderpass is current, it uses the best matching
- // format from the OutputSurface, otherwise it uses the best matching format
- // from the texture being drawn to as the backbuffer.
- GLenum GetFramebufferCopyTextureFormat();
- void ReleaseRenderPassTextures();
- enum BoundGeometry { NO_BINDING, SHARED_BINDING, CLIPPED_BINDING };
- void PrepareGeometry(BoundGeometry geometry_to_bind);
- void SetStencilEnabled(bool enabled);
bool stencil_enabled() const { return stencil_shadow_; }
- void SetBlendEnabled(bool enabled);
- bool blend_enabled() const { return blend_shadow_; }
bool CanPartialSwap() override;
void UpdateRenderPassTextures(
@@ -169,6 +153,18 @@ class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer {
struct DrawRenderPassDrawQuadParams;
+ // Returns the format to use for storage if copying from the current
+ // framebuffer. If the root renderpass is current, it uses the best matching
+ // format from the OutputSurface, otherwise it uses the best matching format
+ // from the texture being drawn to as the backbuffer.
+ GLenum GetFramebufferCopyTextureFormat();
+ void ReleaseRenderPassTextures();
+ enum BoundGeometry { NO_BINDING, SHARED_BINDING, CLIPPED_BINDING };
+ void PrepareGeometry(BoundGeometry geometry_to_bind);
+ void SetStencilEnabled(bool enabled);
+ void SetBlendEnabled(bool enabled);
+ bool blend_enabled() const { return blend_shadow_; }
+
// If any of the following functions returns false, then it means that drawing
// is not possible.
bool InitializeRPDQParameters(DrawRenderPassDrawQuadParams* params);
@@ -215,7 +211,8 @@ class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer {
const cc::FilterOperations& background_filters,
uint32_t background_texture,
const gfx::Rect& rect,
- const gfx::Rect& unclipped_rect);
+ const gfx::Rect& unclipped_rect,
+ const float backdrop_filter_quality);
const TileDrawQuad* CanPassBeDrawnDirectly(const RenderPass* pass) override;
@@ -259,6 +256,11 @@ class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer {
gfx::QuadF* local_quad,
const gfx::Rect& tile_rect);
+ const gfx::QuadF& SharedGeometryQuad() const { return shared_geometry_quad_; }
+ const StaticGeometryBinding* SharedGeometry() const {
+ return shared_geometry_.get();
+ }
+
// If |dst_color_space| is invalid, then no color conversion (apart from
// YUV to RGB conversion) is performed. This explicit argument is available
// so that video color conversion can be enabled separately from general color
diff --git a/chromium/components/viz/service/display/overlay_candidate.cc b/chromium/components/viz/service/display/overlay_candidate.cc
index 8db823bbe6d..1c0d425f1e2 100644
--- a/chromium/components/viz/service/display/overlay_candidate.cc
+++ b/chromium/components/viz/service/display/overlay_candidate.cc
@@ -420,4 +420,15 @@ void OverlayCandidateList::AddPromotionHint(const OverlayCandidate& candidate) {
promotion_hint_info_map_[candidate.resource_id] = candidate.display_rect;
}
+void OverlayCandidateList::AddToPromotionHintRequestorSetIfNeeded(
+ const DisplayResourceProvider* resource_provider,
+ const DrawQuad* quad) {
+ if (quad->material != DrawQuad::STREAM_VIDEO_CONTENT)
+ return;
+ ResourceId id = StreamVideoDrawQuad::MaterialCast(quad)->resource_id();
+ if (!resource_provider->DoesResourceWantPromotionHint(id))
+ return;
+ promotion_hint_requestor_set_.insert(id);
+}
+
} // namespace viz
diff --git a/chromium/components/viz/service/display/overlay_candidate.h b/chromium/components/viz/service/display/overlay_candidate.h
index 57ae63a72ae..483b90a3b3e 100644
--- a/chromium/components/viz/service/display/overlay_candidate.h
+++ b/chromium/components/viz/service/display/overlay_candidate.h
@@ -145,8 +145,17 @@ class VIZ_SERVICE_EXPORT OverlayCandidateList
// overlay, if one backs them with a SurfaceView.
PromotionHintInfoMap promotion_hint_info_map_;
+ // Set of resources that have requested a promotion hint that also have quads
+ // that use them.
+ ResourceIdSet promotion_hint_requestor_set_;
+
// Helper to insert |candidate| into |promotion_hint_info_|.
void AddPromotionHint(const OverlayCandidate& candidate);
+
+ // Add |quad| to |promotion_hint_requestors_| if it is requesting a hint.
+ void AddToPromotionHintRequestorSetIfNeeded(
+ const DisplayResourceProvider* resource_provider,
+ const DrawQuad* quad);
};
} // namespace viz
diff --git a/chromium/components/viz/service/display/overlay_processor.cc b/chromium/components/viz/service/display/overlay_processor.cc
index 80766089118..e30aa3143eb 100644
--- a/chromium/components/viz/service/display/overlay_processor.cc
+++ b/chromium/components/viz/service/display/overlay_processor.cc
@@ -4,6 +4,8 @@
#include "components/viz/service/display/overlay_processor.h"
+#include <vector>
+
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
@@ -29,7 +31,8 @@ class SendPromotionHintsBeforeReturning {
: resource_provider_(resource_provider), candidates_(candidates) {}
~SendPromotionHintsBeforeReturning() {
resource_provider_->SendPromotionHints(
- candidates_->promotion_hint_info_map_);
+ candidates_->promotion_hint_info_map_,
+ candidates_->promotion_hint_requestor_set_);
}
private:
@@ -178,6 +181,9 @@ void OverlayProcessor::ProcessForOverlays(
break;
}
+ if (!successful_strategy && !previous_frame_underlay_rect.IsEmpty())
+ damage_rect->Union(previous_frame_underlay_rect);
+
UMA_HISTOGRAM_ENUMERATION("Viz.DisplayCompositor.OverlayStrategy",
successful_strategy
? successful_strategy->GetUMAEnum()
diff --git a/chromium/components/viz/service/display/overlay_strategy_underlay.cc b/chromium/components/viz/service/display/overlay_strategy_underlay.cc
index 70682346e1d..34b990f29da 100644
--- a/chromium/components/viz/service/display/overlay_strategy_underlay.cc
+++ b/chromium/components/viz/service/display/overlay_strategy_underlay.cc
@@ -7,6 +7,7 @@
#include "build/build_config.h"
#include "components/viz/common/quads/draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/service/display/display_resource_provider.h"
#include "components/viz/service/display/overlay_candidate_validator.h"
namespace viz {
@@ -28,8 +29,19 @@ bool OverlayStrategyUnderlay::Attempt(
OverlayCandidateList* candidate_list,
std::vector<gfx::Rect>* content_bounds) {
QuadList& quad_list = render_pass->quad_list;
+ const bool compute_hints =
+ resource_provider->DoAnyResourcesWantPromotionHints();
+
for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
OverlayCandidate candidate;
+
+ // If we're computing the list of all resources that requested promotion
+ // hints, then add this candidate to the set if needed.
+ if (compute_hints) {
+ candidate_list->AddToPromotionHintRequestorSetIfNeeded(resource_provider,
+ *it);
+ }
+
if (!OverlayCandidate::FromDrawQuad(resource_provider, output_color_matrix,
*it, &candidate) ||
(opaque_mode_ == OpaqueMode::RequireOpaqueCandidates &&
@@ -68,9 +80,16 @@ bool OverlayStrategyUnderlay::Attempt(
// |candidate| would have to fall back to a texture.
candidate_list->promotion_hint_info_map_.clear();
candidate_list->AddPromotionHint(candidate);
+ if (compute_hints) {
+ // Finish the quad list to find any other resources.
+ for (; it != quad_list.end(); ++it) {
+ candidate_list->AddToPromotionHintRequestorSetIfNeeded(
+ resource_provider, *it);
+ }
+ }
return true;
} else {
- // If |candidate| should get a promotion hint, then rememeber that now.
+ // If |candidate| should get a promotion hint, then remember that now.
candidate_list->promotion_hint_info_map_.insert(
new_candidate_list.promotion_hint_info_map_.begin(),
new_candidate_list.promotion_hint_info_map_.end());
diff --git a/chromium/components/viz/service/display/overlay_unittest.cc b/chromium/components/viz/service/display/overlay_unittest.cc
index 6e6c5ffc4f7..9c4ac77eb19 100644
--- a/chromium/components/viz/service/display/overlay_unittest.cc
+++ b/chromium/components/viz/service/display/overlay_unittest.cc
@@ -1341,7 +1341,7 @@ TEST_F(UnderlayTest, DisallowFilteredQuadOnTop) {
pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
quad->SetNew(pass->shared_quad_state_list.back(), kOverlayRect, kOverlayRect,
render_pass_id, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
CreateFullscreenCandidateQuad(
resource_provider_.get(), child_resource_provider_.get(),
@@ -1963,6 +1963,49 @@ TEST_F(UnderlayTest, UpdateDamageWhenChangingUnderlays) {
EXPECT_EQ(kOverlayRect, damage_rect_);
}
+TEST_F(UnderlayTest, UpdateDamageRectWhenNoPromotion) {
+ // In the first pass there is an overlay promotion and the expected damage
+ // size should be unchanged.
+ // In the second pass there is no overlay promotion, but the damage should be
+ // the union of the damage_rect with CreateRenderPass's output_rect which is
+ // {0, 0, 256, 256}.
+ bool has_fullscreen_candidate[] = {true, false};
+ gfx::Rect damages[] = {gfx::Rect(0, 0, 32, 32), gfx::Rect(0, 0, 312, 16)};
+ gfx::Rect expected_damages[] = {gfx::Rect(0, 0, 32, 32),
+ gfx::Rect(0, 0, 312, 256)};
+ size_t expected_candidate_size[] = {1, 0};
+
+ for (int i = 0; i < 2; ++i) {
+ std::unique_ptr<RenderPass> pass = CreateRenderPass();
+
+ if (has_fullscreen_candidate[i]) {
+ CreateFullscreenCandidateQuad(
+ resource_provider_.get(), child_resource_provider_.get(),
+ child_provider_.get(), pass->shared_quad_state_list.back(),
+ pass.get());
+ }
+
+ gfx::Rect damage_rect{damages[i]};
+
+ // Add something behind it.
+ CreateFullscreenOpaqueQuad(resource_provider_.get(),
+ pass->shared_quad_state_list.back(), pass.get());
+
+ OverlayCandidateList candidate_list;
+ OverlayProcessor::FilterOperationsMap render_pass_filters;
+ OverlayProcessor::FilterOperationsMap render_pass_background_filters;
+ RenderPassList pass_list;
+ pass_list.push_back(std::move(pass));
+ overlay_processor_->ProcessForOverlays(
+ resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+ render_pass_filters, render_pass_background_filters, &candidate_list,
+ nullptr, nullptr, &damage_rect, &content_bounds_);
+
+ EXPECT_EQ(expected_damages[i], damage_rect);
+ ASSERT_EQ(expected_candidate_size[i], candidate_list.size());
+ }
+}
+
TEST_F(UnderlayCastTest, NoOverlayContentBounds) {
std::unique_ptr<RenderPass> pass = CreateRenderPass();
@@ -2550,7 +2593,7 @@ TEST_F(DCLayerOverlayTest, MultiplePassDamageRect) {
child_pass1_rpdq->SetNew(child_pass1_sqs, unit_rect, unit_rect,
child_pass1_id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(0, 0, 1, 1), false);
+ gfx::RectF(0, 0, 1, 1), false, 1.0f);
SharedQuadState* child_pass2_sqs =
root_pass->shared_quad_state_list.ElementAt(1);
@@ -2563,7 +2606,7 @@ TEST_F(DCLayerOverlayTest, MultiplePassDamageRect) {
child_pass2_rpdq->SetNew(child_pass2_sqs, unit_rect, unit_rect,
child_pass2_id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(0, 0, 1, 1), false);
+ gfx::RectF(0, 0, 1, 1), false, 1.0f);
root_pass->damage_rect = gfx::Rect();
@@ -3341,7 +3384,7 @@ class CALayerOverlayRPDQTest : public CALayerOverlayTest {
TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadNoFilters) {
quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
ProcessForOverlays();
EXPECT_EQ(1U, ca_layer_list_.size());
@@ -3362,7 +3405,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadAllValidFilters) {
render_pass_filters_[render_pass_id_] = &filters_;
quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
ProcessForOverlays();
EXPECT_EQ(1U, ca_layer_list_.size());
@@ -3373,7 +3416,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadOpacityFilterScale) {
render_pass_filters_[render_pass_id_] = &filters_;
quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
ProcessForOverlays();
EXPECT_EQ(1U, ca_layer_list_.size());
}
@@ -3383,7 +3426,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBlurFilterScale) {
render_pass_filters_[render_pass_id_] = &filters_;
quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
ProcessForOverlays();
EXPECT_EQ(1U, ca_layer_list_.size());
}
@@ -3394,7 +3437,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadDropShadowFilterScale) {
render_pass_filters_[render_pass_id_] = &filters_;
quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
ProcessForOverlays();
EXPECT_EQ(1U, ca_layer_list_.size());
}
@@ -3404,7 +3447,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBackgroundFilter) {
render_pass_background_filters_[render_pass_id_] = &background_filters_;
quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
ProcessForOverlays();
EXPECT_EQ(0U, ca_layer_list_.size());
}
@@ -3412,7 +3455,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBackgroundFilter) {
TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadMask) {
quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
ProcessForOverlays();
EXPECT_EQ(1U, ca_layer_list_.size());
}
@@ -3422,7 +3465,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadUnsupportedFilter) {
render_pass_filters_[render_pass_id_] = &filters_;
quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
ProcessForOverlays();
EXPECT_EQ(0U, ca_layer_list_.size());
}
@@ -3435,7 +3478,8 @@ TEST_F(CALayerOverlayRPDQTest, TooManyRenderPassDrawQuads) {
auto* quad = pass_->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
quad->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false);
+ gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+ 1.0f);
}
ProcessForOverlays();
diff --git a/chromium/components/viz/service/display/renderer_pixeltest.cc b/chromium/components/viz/service/display/renderer_pixeltest.cc
index d61603aea99..6c62958d798 100644
--- a/chromium/components/viz/service/display/renderer_pixeltest.cc
+++ b/chromium/components/viz/service/display/renderer_pixeltest.cc
@@ -31,7 +31,6 @@
#include "media/renderers/video_resource_updater.h"
#include "media/video/half_float_maker.h"
#include "third_party/skia/include/core/SkColorPriv.h"
-#include "third_party/skia/include/core/SkColorSpaceXform.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSurface.h"
@@ -180,7 +179,8 @@ void CreateTestRenderPassDrawQuad(const SharedQuadState* shared_state,
gfx::Vector2dF(), // filters scale
gfx::PointF(), // filter origin
gfx::RectF(rect), // tex_coord_rect
- false); // force_anti_aliasing_off
+ false, // force_anti_aliasing_off
+ 1.0f); // backdrop_filter_quality
}
void CreateTestTwoColoredTextureDrawQuad(
@@ -547,7 +547,7 @@ scoped_refptr<media::VideoFrame> CreateHighbitVideoFrame(
void CreateTestYUVVideoDrawQuad_Striped(
const SharedQuadState* shared_state,
media::VideoPixelFormat format,
- media::ColorSpace color_space,
+ gfx::ColorSpace color_space,
bool is_transparent,
bool highbit,
const gfx::RectF& tex_coord_rect,
@@ -589,8 +589,7 @@ void CreateTestYUVVideoDrawQuad_Striped(
if (highbit)
video_frame = CreateHighbitVideoFrame(video_frame.get());
- video_frame->metadata()->SetInteger(media::VideoFrameMetadata::COLOR_SPACE,
- color_space);
+ video_frame->set_color_space(color_space);
CreateTestYUVVideoDrawQuad_FromVideoFrame(
shared_state, video_frame, alpha_value, tex_coord_rect, render_pass,
@@ -605,7 +604,7 @@ void CreateTestYUVVideoDrawQuad_Striped(
void CreateTestYUVVideoDrawQuad_TwoColor(
const SharedQuadState* shared_state,
media::VideoPixelFormat format,
- media::ColorSpace color_space,
+ gfx::ColorSpace color_space,
bool is_transparent,
const gfx::RectF& tex_coord_rect,
const gfx::Size& background_size,
@@ -627,8 +626,7 @@ void CreateTestYUVVideoDrawQuad_TwoColor(
scoped_refptr<media::VideoFrame> video_frame =
media::VideoFrame::CreateFrame(format, background_size, foreground_rect,
foreground_rect.size(), base::TimeDelta());
- video_frame->metadata()->SetInteger(media::VideoFrameMetadata::COLOR_SPACE,
- color_space);
+ video_frame->set_color_space(color_space);
int planes[] = {media::VideoFrame::kYPlane, media::VideoFrame::kUPlane,
media::VideoFrame::kVPlane};
@@ -671,7 +669,7 @@ void CreateTestYUVVideoDrawQuad_TwoColor(
void CreateTestYUVVideoDrawQuad_Solid(
const SharedQuadState* shared_state,
media::VideoPixelFormat format,
- media::ColorSpace color_space,
+ const gfx::ColorSpace& color_space,
bool is_transparent,
const gfx::RectF& tex_coord_rect,
uint8_t y,
@@ -686,8 +684,7 @@ void CreateTestYUVVideoDrawQuad_Solid(
ContextProvider* child_context_provider) {
scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
format, rect.size(), rect, rect.size(), base::TimeDelta());
- video_frame->metadata()->SetInteger(media::VideoFrameMetadata::COLOR_SPACE,
- color_space);
+ video_frame->set_color_space(color_space);
// YUV values of a solid, constant, color. Useful for testing that color
// space/color range are being handled properly.
@@ -710,8 +707,7 @@ void CreateTestYUVVideoDrawQuad_Solid(
void CreateTestYUVVideoDrawQuad_NV12(
const SharedQuadState* shared_state,
- media::ColorSpace video_frame_color_space,
- const gfx::ColorSpace& video_color_space,
+ const gfx::ColorSpace& color_space,
const gfx::RectF& tex_coord_rect,
uint8_t y,
uint8_t u,
@@ -723,11 +719,6 @@ void CreateTestYUVVideoDrawQuad_NV12(
DisplayResourceProvider* resource_provider,
ClientResourceProvider* child_resource_provider,
scoped_refptr<ContextProvider> child_context_provider) {
- gfx::ColorSpace gfx_color_space = gfx::ColorSpace::CreateREC601();
- if (video_frame_color_space == media::COLOR_SPACE_JPEG) {
- gfx_color_space = gfx::ColorSpace::CreateJpeg();
- }
-
bool needs_blending = true;
const gfx::Size ya_tex_size = rect.size();
const gfx::Size uv_tex_size = media::VideoFrame::PlaneSize(
@@ -736,15 +727,15 @@ void CreateTestYUVVideoDrawQuad_NV12(
std::vector<uint8_t> y_pixels(ya_tex_size.GetArea(), y);
ResourceId resource_y = CreateGpuResource(
child_context_provider, child_resource_provider, ya_tex_size,
- video_resource_updater->YuvResourceFormat(8), gfx_color_space,
+ video_resource_updater->YuvResourceFormat(8), color_space,
y_pixels.data());
// U goes in the R component and V goes in the G component.
uint32_t rgba_pixel = (u << 24) | (v << 16);
std::vector<uint32_t> uv_pixels(uv_tex_size.GetArea(), rgba_pixel);
- ResourceId resource_u = CreateGpuResource(
- child_context_provider, child_resource_provider, uv_tex_size, RGBA_8888,
- gfx_color_space, uv_pixels.data());
+ ResourceId resource_u =
+ CreateGpuResource(child_context_provider, child_resource_provider,
+ uv_tex_size, RGBA_8888, color_space, uv_pixels.data());
ResourceId resource_v = resource_u;
ResourceId resource_a = 0;
@@ -771,8 +762,7 @@ void CreateTestYUVVideoDrawQuad_NV12(
yuv_quad->SetNew(shared_state, rect, visible_rect, needs_blending,
ya_tex_coord_rect, uv_tex_coord_rect, ya_tex_size,
uv_tex_size, mapped_resource_y, mapped_resource_u,
- mapped_resource_v, resource_a, video_color_space, 0.0f, 1.0f,
- 8);
+ mapped_resource_v, resource_a, color_space, 0.0f, 1.0f, 8);
}
void CreateTestY16TextureDrawQuad_TwoColor(
@@ -1441,17 +1431,17 @@ TYPED_TEST(IntersectingQuadGLPixelTest, YUVVideoQuads) {
CreateTestYUVVideoDrawQuad_TwoColor(
this->front_quad_state_, media::PIXEL_FORMAT_I420,
- media::COLOR_SPACE_JPEG, false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
+ gfx::ColorSpace::CreateJpeg(), false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
this->quad_rect_.size(), this->quad_rect_, 0, 128, 128, inner_rect, 29,
255, 107, this->render_pass_.get(), this->video_resource_updater_.get(),
this->resource_provider_.get(), this->child_resource_provider_.get(),
this->child_context_provider_.get());
CreateTestYUVVideoDrawQuad_TwoColor(
- this->back_quad_state_, media::PIXEL_FORMAT_I420, media::COLOR_SPACE_JPEG,
- false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), this->quad_rect_.size(),
- this->quad_rect_, 149, 43, 21, inner_rect, 0, 128, 128,
- this->render_pass_.get(), this->video_resource_updater2_.get(),
+ this->back_quad_state_, media::PIXEL_FORMAT_I420,
+ gfx::ColorSpace::CreateJpeg(), false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
+ this->quad_rect_.size(), this->quad_rect_, 149, 43, 21, inner_rect, 0,
+ 128, 128, this->render_pass_.get(), this->video_resource_updater2_.get(),
this->resource_provider_.get(), this->child_resource_provider_.get(),
this->child_context_provider_.get());
@@ -1555,7 +1545,7 @@ TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithBackground) {
class VideoGLRendererPixelTest : public cc::GLRendererPixelTest {
protected:
void CreateEdgeBleedPass(media::VideoPixelFormat format,
- media::ColorSpace color_space,
+ const gfx::ColorSpace& color_space,
RenderPassList* pass_list) {
gfx::Rect rect(200, 200);
@@ -1631,7 +1621,7 @@ TEST_P(VideoGLRendererPixelHiLoTest, SimpleYUVRect) {
CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
CreateTestYUVVideoDrawQuad_Striped(
- shared_state, media::PIXEL_FORMAT_I420, media::COLOR_SPACE_SD_REC601,
+ shared_state, media::PIXEL_FORMAT_I420, gfx::ColorSpace::CreateREC601(),
false, IsHighbit(), gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), pass.get(),
video_resource_updater_.get(), rect, rect, resource_provider_.get(),
child_resource_provider_.get(), child_context_provider_.get());
@@ -1659,7 +1649,7 @@ TEST_P(VideoGLRendererPixelHiLoTest, ClippedYUVRect) {
CreateTestSharedQuadState(gfx::Transform(), viewport, pass.get());
CreateTestYUVVideoDrawQuad_Striped(
- shared_state, media::PIXEL_FORMAT_I420, media::COLOR_SPACE_SD_REC601,
+ shared_state, media::PIXEL_FORMAT_I420, gfx::ColorSpace::CreateREC601(),
false, IsHighbit(), gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), pass.get(),
video_resource_updater_.get(), draw_rect, viewport,
resource_provider_.get(), child_resource_provider_.get(),
@@ -1686,7 +1676,7 @@ TEST_F(VideoGLRendererPixelHiLoTest, OffsetYUVRect) {
// Intentionally sets frame format to I420 for testing coverage.
CreateTestYUVVideoDrawQuad_Striped(
- shared_state, media::PIXEL_FORMAT_I420, media::COLOR_SPACE_SD_REC601,
+ shared_state, media::PIXEL_FORMAT_I420, gfx::ColorSpace::CreateREC601(),
false, false, gfx::RectF(0.125f, 0.25f, 0.75f, 0.5f), pass.get(),
video_resource_updater_.get(), rect, rect, resource_provider_.get(),
child_resource_provider_.get(), child_context_provider_.get());
@@ -1713,7 +1703,7 @@ TEST_F(VideoGLRendererPixelTest, SimpleYUVRectBlack) {
// In MPEG color range YUV values of (15,128,128) should produce black.
CreateTestYUVVideoDrawQuad_Solid(
- shared_state, media::PIXEL_FORMAT_I420, media::COLOR_SPACE_SD_REC601,
+ shared_state, media::PIXEL_FORMAT_I420, gfx::ColorSpace::CreateREC601(),
false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 15, 128, 128, pass.get(),
video_resource_updater_.get(), rect, rect, resource_provider_.get(),
child_resource_provider_.get(), child_context_provider_.get());
@@ -1742,8 +1732,8 @@ TEST_F(VideoGLRendererPixelTest, SimpleYUVJRect) {
// YUV of (149,43,21) should be green (0,255,0) in RGB.
CreateTestYUVVideoDrawQuad_Solid(
- shared_state, media::PIXEL_FORMAT_I420, media::COLOR_SPACE_JPEG, false,
- gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 149, 43, 21, pass.get(),
+ shared_state, media::PIXEL_FORMAT_I420, gfx::ColorSpace::CreateJpeg(),
+ false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 149, 43, 21, pass.get(),
video_resource_updater_.get(), rect, rect, resource_provider_.get(),
child_resource_provider_.get(), child_context_provider_.get());
@@ -1766,7 +1756,7 @@ TEST_F(VideoGLRendererPixelTest, SimpleNV12JRect) {
// YUV of (149,43,21) should be green (0,255,0) in RGB.
CreateTestYUVVideoDrawQuad_NV12(
- shared_state, media::COLOR_SPACE_JPEG, gfx::ColorSpace::CreateJpeg(),
+ shared_state, gfx::ColorSpace::CreateJpeg(),
gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 149, 43, 21, pass.get(),
video_resource_updater_.get(), rect, rect, resource_provider_.get(),
child_resource_provider_.get(), child_context_provider_);
@@ -1783,7 +1773,7 @@ TEST_F(VideoGLRendererPixelTest, SimpleNV12JRect) {
// tex coord rect is only a partial subrectangle of the coded contents.
TEST_F(VideoGLRendererPixelTest, YUVEdgeBleed) {
RenderPassList pass_list;
- CreateEdgeBleedPass(media::PIXEL_FORMAT_I420, media::COLOR_SPACE_JPEG,
+ CreateEdgeBleedPass(media::PIXEL_FORMAT_I420, gfx::ColorSpace::CreateJpeg(),
&pass_list);
EXPECT_TRUE(this->RunPixelTest(&pass_list,
base::FilePath(FILE_PATH_LITERAL("green.png")),
@@ -1792,8 +1782,8 @@ TEST_F(VideoGLRendererPixelTest, YUVEdgeBleed) {
TEST_F(VideoGLRendererPixelTest, YUVAEdgeBleed) {
RenderPassList pass_list;
- CreateEdgeBleedPass(media::PIXEL_FORMAT_I420A, media::COLOR_SPACE_SD_REC601,
- &pass_list);
+ CreateEdgeBleedPass(media::PIXEL_FORMAT_I420A,
+ gfx::ColorSpace::CreateREC601(), &pass_list);
// Set the output color space to match the input primaries and transfer.
pass_list.back()->color_space =
gfx::ColorSpace(gfx::ColorSpace::PrimaryID::SMPTE170M,
@@ -1814,8 +1804,8 @@ TEST_F(VideoGLRendererPixelTest, SimpleYUVJRectGrey) {
// Dark grey in JPEG color range (in MPEG, this is black).
CreateTestYUVVideoDrawQuad_Solid(
- shared_state, media::PIXEL_FORMAT_I420, media::COLOR_SPACE_JPEG, false,
- gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 15, 128, 128, pass.get(),
+ shared_state, media::PIXEL_FORMAT_I420, gfx::ColorSpace::CreateJpeg(),
+ false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 15, 128, 128, pass.get(),
video_resource_updater_.get(), rect, rect, resource_provider_.get(),
child_resource_provider_.get(), child_context_provider_.get());
@@ -1840,7 +1830,7 @@ TEST_F(VideoGLRendererPixelHiLoTest, SimpleYUVARect) {
CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
CreateTestYUVVideoDrawQuad_Striped(
- shared_state, media::PIXEL_FORMAT_I420A, media::COLOR_SPACE_SD_REC601,
+ shared_state, media::PIXEL_FORMAT_I420A, gfx::ColorSpace::CreateREC601(),
false, false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), pass.get(),
video_resource_updater_.get(), rect, rect, resource_provider_.get(),
child_resource_provider_.get(), child_context_provider_.get());
@@ -1869,7 +1859,7 @@ TEST_F(VideoGLRendererPixelTest, FullyTransparentYUVARect) {
CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
CreateTestYUVVideoDrawQuad_Striped(
- shared_state, media::PIXEL_FORMAT_I420A, media::COLOR_SPACE_SD_REC601,
+ shared_state, media::PIXEL_FORMAT_I420A, gfx::ColorSpace::CreateREC601(),
true, false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), pass.get(),
video_resource_updater_.get(), rect, rect, resource_provider_.get(),
child_resource_provider_.get(), child_context_provider_.get());
@@ -1975,7 +1965,7 @@ TYPED_TEST(RendererPixelTest, FastPassColorFilterAlpha) {
render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect,
child_pass_id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(pass_rect), false);
+ gfx::RectF(pass_rect), false, 1.0f);
RenderPassList pass_list;
pass_list.push_back(std::move(child_pass));
@@ -2035,7 +2025,7 @@ TYPED_TEST(RendererPixelTest, FastPassSaturateFilter) {
render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect,
child_pass_id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(pass_rect), false);
+ gfx::RectF(pass_rect), false, 1.0f);
RenderPassList pass_list;
pass_list.push_back(std::move(child_pass));
@@ -2096,7 +2086,7 @@ TYPED_TEST(RendererPixelTest, FastPassFilterChain) {
render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect,
child_pass_id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(pass_rect), false);
+ gfx::RectF(pass_rect), false, 1.0f);
RenderPassList pass_list;
pass_list.push_back(std::move(child_pass));
@@ -2178,7 +2168,7 @@ TYPED_TEST(RendererPixelTest, FastPassColorFilterAlphaTranslation) {
render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect,
child_pass_id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(pass_rect), false);
+ gfx::RectF(pass_rect), false, 1.0f);
RenderPassList pass_list;
@@ -2371,7 +2361,8 @@ TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad) {
gfx::Vector2dF(), // filters scale
gfx::PointF(), // filter origin
gfx::RectF(sub_rect), // tex_coord_rect
- false); // force_anti_aliasing_off
+ false, // force_anti_aliasing_off
+ 1.0f); // backdrop_filter_quality
// White background behind the masked render pass.
auto* white = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
white->SetNew(root_pass_shared_state, viewport_rect, viewport_rect,
@@ -2467,7 +2458,8 @@ TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad2) {
gfx::Vector2dF(), // filters scale
gfx::PointF(), // filter origin
gfx::RectF(sub_rect), // tex_coord_rect
- false); // force_anti_aliasing_off
+ false, // force_anti_aliasing_off
+ 1.0f); // backdrop_filter_quality
// White background behind the masked render pass.
auto* white = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
white->SetNew(root_pass_shared_state, viewport_rect, viewport_rect,
@@ -2527,7 +2519,8 @@ class RendererPixelTestWithBackgroundFilter
gfx::Vector2dF(1.0f, 1.0f), // filters_scale
gfx::PointF(), // filters_origin
gfx::RectF(), // tex_coord_rect
- false); // force_anti_aliasing_off
+ false, // force_anti_aliasing_off
+ 1.0f); // backdrop_filter_quality
}
const int kColumnWidth = device_viewport_rect.width() / 3;
@@ -2868,6 +2861,7 @@ TEST_F(GLRendererPixelTest, RenderPassDrawQuadForceAntiAliasingOff) {
bool needs_blending = false;
bool force_anti_aliasing_off = true;
+ float backdrop_filter_quality = 1.0f;
gfx::Transform hole_pass_to_target_transform;
hole_pass_to_target_transform.Translate(50, 50);
hole_pass_to_target_transform.Scale(0.5f + 1.0f / (rect.width() * 2.0f),
@@ -2879,7 +2873,7 @@ TEST_F(GLRendererPixelTest, RenderPassDrawQuadForceAntiAliasingOff) {
pass_quad->SetAll(pass_shared_state, rect, rect, needs_blending,
child_pass_id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(), gfx::RectF(rect),
- force_anti_aliasing_off);
+ force_anti_aliasing_off, backdrop_filter_quality);
gfx::Transform green_quad_to_target_transform;
SharedQuadState* green_shared_state = CreateTestSharedQuadState(
@@ -3047,7 +3041,7 @@ TEST_F(GLRendererPixelTest, TrilinearFiltering) {
child_pass_quad->SetNew(child_pass_shared_state, child_pass_rect,
child_pass_rect, child_pass_id, 0, gfx::RectF(),
gfx::Size(), gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(child_pass_rect), false);
+ gfx::RectF(child_pass_rect), false, 1.0f);
RenderPassList pass_list;
pass_list.push_back(std::move(child_pass));
diff --git a/chromium/components/viz/service/display/skia_output_surface.h b/chromium/components/viz/service/display/skia_output_surface.h
index bdc29aa3109..9857096997a 100644
--- a/chromium/components/viz/service/display/skia_output_surface.h
+++ b/chromium/components/viz/service/display/skia_output_surface.h
@@ -34,15 +34,6 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface {
// called.
virtual SkCanvas* BeginPaintCurrentFrame() = 0;
- // Finish painting the current frame. It should be paired with
- // BeginPaintCurrentFrame. This method will schedule a GPU task to play the
- // DDL back on GPU thread on the SkSurface for the framebuffer. This method
- // returns a sync token which can be waited on in a command buffer to ensure
- // the paint operation is completed. This token is released when the GPU ops
- // from painting the current frame have been seen and processed by the GPU
- // main.
- virtual gpu::SyncToken FinishPaintCurrentFrame() = 0;
-
// Make a promise SkImage from the given |metadata|. The SkiaRenderer can use
// the image with SkCanvas returned by |GetSkCanvasForCurrentFrame|, but Skia
// will not read the content of the resource until the sync token in the
@@ -75,13 +66,13 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface {
ResourceFormat format,
bool mipmap) = 0;
- // Finish painting a render pass. It should be paired with
- // BeginPaintRenderPass. This method will schedule a GPU task to play the DDL
- // back on GPU thread on a cached SkSurface. This method returns a sync token
- // which can be waited on in a command buffer to ensure the paint operation is
- // completed. This token is released when the GPU ops from painting the render
- // pass have been seen and processed by the GPU main.
- virtual gpu::SyncToken FinishPaintRenderPass() = 0;
+ // Finish painting the current frame or current render pass, depends on which
+ // BeginPaint function is called last. This method will schedule a GPU task to
+ // play the DDL back on GPU thread on a cached SkSurface. This method returns
+ // a sync token which can be waited on in a command buffer to ensure the paint
+ // operation is completed. This token is released when the GPU ops from
+ // painting the render pass have been seen and processed by the GPU main.
+ virtual gpu::SyncToken SubmitPaint() = 0;
// Make a promise SkImage from a render pass id. The render pass has been
// painted with BeginPaintRenderPass and FinishPaintRenderPass. The format
diff --git a/chromium/components/viz/service/display/skia_renderer.cc b/chromium/components/viz/service/display/skia_renderer.cc
index 0544d031b60..58fb3ca97d5 100644
--- a/chromium/components/viz/service/display/skia_renderer.cc
+++ b/chromium/components/viz/service/display/skia_renderer.cc
@@ -29,7 +29,6 @@
#include "components/viz/service/display/resource_metadata.h"
#include "components/viz/service/display/skia_output_surface.h"
#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/vulkan/buildflags.h"
#include "skia/ext/opacity_filter_canvas.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -179,16 +178,34 @@ class SkiaRenderer::ScopedYUVSkImageBuilder {
SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
OutputSurface* output_surface,
DisplayResourceProvider* resource_provider,
- SkiaOutputSurface* skia_output_surface)
+ SkiaOutputSurface* skia_output_surface,
+ DrawMode mode)
: DirectRenderer(settings, output_surface, resource_provider),
+ draw_mode_(mode),
skia_output_surface_(skia_output_surface),
lock_set_for_external_use_(resource_provider) {
- if (auto* context_provider = output_surface_->context_provider()) {
- const auto& context_caps = context_provider->ContextCapabilities();
- use_swap_with_bounds_ = context_caps.swap_buffers_with_bounds;
- if (context_caps.sync_query) {
- sync_queries_ =
- base::Optional<SyncQueryCollection>(context_provider->ContextGL());
+ switch (draw_mode_) {
+ case DrawMode::GL: {
+ DCHECK(output_surface_);
+ context_provider_ = output_surface_->context_provider();
+ const auto& context_caps = context_provider_->ContextCapabilities();
+ use_swap_with_bounds_ = context_caps.swap_buffers_with_bounds;
+ if (context_caps.sync_query) {
+ sync_queries_ =
+ base::Optional<SyncQueryCollection>(context_provider_->ContextGL());
+ }
+ break;
+ }
+ case DrawMode::VULKAN: {
+ DCHECK(output_surface_);
+#if BUILDFLAG(ENABLE_VULKAN)
+ vulkan_context_provider_ = output_surface_->vulkan_context_provider();
+#endif
+ break;
+ }
+ case DrawMode::DDL: {
+ DCHECK(skia_output_surface_);
+ break;
}
}
}
@@ -196,20 +213,21 @@ SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
SkiaRenderer::~SkiaRenderer() = default;
bool SkiaRenderer::CanPartialSwap() {
- if (IsUsingVulkan())
+ if (draw_mode_ != DrawMode::GL)
return false;
+
+ DCHECK(context_provider_);
if (use_swap_with_bounds_)
return false;
- auto* context_provider = output_surface_->context_provider();
- return context_provider
- ? context_provider->ContextCapabilities().post_sub_buffer
- : false;
+
+ return context_provider_->ContextCapabilities().post_sub_buffer;
}
void SkiaRenderer::BeginDrawingFrame() {
TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingFrame");
- if (IsUsingVulkan() || is_using_ddl())
+ if (draw_mode_ != DrawMode::GL)
return;
+
// Copied from GLRenderer.
scoped_refptr<ResourceFence> read_lock_fence;
if (sync_queries_) {
@@ -217,7 +235,7 @@ void SkiaRenderer::BeginDrawingFrame() {
} else {
read_lock_fence =
base::MakeRefCounted<DisplayResourceProvider::SynchronousFence>(
- output_surface_->context_provider()->ContextGL());
+ context_provider_->ContextGL());
}
resource_provider_->SetReadLockFence(read_lock_fence.get());
@@ -276,11 +294,30 @@ void SkiaRenderer::SwapBuffers(std::vector<ui::LatencyInfo> latency_info,
output_frame.sub_buffer_rect = swap_buffer_rect_;
}
- if (is_using_ddl()) {
- skia_output_surface_->SkiaSwapBuffers(std::move(output_frame));
- } else {
- // TODO(penghuang): remove it when SkiaRenderer and SkDDL are always used.
- output_surface_->SwapBuffers(std::move(output_frame));
+ switch (draw_mode_) {
+ case DrawMode::DDL: {
+ skia_output_surface_->SkiaSwapBuffers(std::move(output_frame));
+ break;
+ }
+ case DrawMode::VULKAN: {
+#if BUILDFLAG(ENABLE_VULKAN)
+ // TODO(penghuang): remove it when SkiaRenderer and SkDDL are always used.
+ auto backend = root_surface_->getBackendRenderTarget(
+ SkSurface::kFlushRead_BackendHandleAccess);
+ GrVkImageInfo vk_image_info;
+ if (!backend.getVkImageInfo(&vk_image_info))
+ NOTREACHED() << "Failed to get the image info.";
+ auto* vulkan_surface = output_surface_->GetVulkanSurface();
+ auto* swap_chain = vulkan_surface->GetSwapChain();
+ swap_chain->SetCurrentImageLayout(vk_image_info.fImageLayout);
+ output_surface_->SwapBuffers(std::move(output_frame));
+#endif
+ break;
+ }
+ case DrawMode::GL: {
+ output_surface_->SwapBuffers(std::move(output_frame));
+ break;
+ }
}
swap_buffer_rect_ = gfx::Rect();
@@ -310,21 +347,22 @@ void SkiaRenderer::BindFramebufferToOutputSurface() {
// TODO(weiliangc): Set up correct can_use_lcd_text for SkSurfaceProps flags.
// How to setup is in ResourceProvider. (http://crbug.com/644851)
- if (is_using_ddl()) {
- root_canvas_ = skia_output_surface_->BeginPaintCurrentFrame();
- is_drawing_render_pass_ = false;
- DCHECK(root_canvas_);
- } else {
- auto* gr_context = GetGrContext();
- if (IsUsingVulkan()) {
+ switch (draw_mode_) {
+ case DrawMode::DDL: {
+ root_canvas_ = skia_output_surface_->BeginPaintCurrentFrame();
+ DCHECK(root_canvas_);
+ break;
+ }
+ case DrawMode::VULKAN: {
#if BUILDFLAG(ENABLE_VULKAN)
auto* vulkan_surface = output_surface_->GetVulkanSurface();
auto* swap_chain = vulkan_surface->GetSwapChain();
- VkImage image = swap_chain->GetCurrentImage(swap_chain->current_image());
+ VkImage image = swap_chain->GetCurrentImage();
+ VkImageLayout image_layout = swap_chain->GetCurrentImageLayout();
GrVkImageInfo vk_image_info;
vk_image_info.fImage = image;
vk_image_info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0};
- vk_image_info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ vk_image_info.fImageLayout = image_layout;
vk_image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
vk_image_info.fFormat = VK_FORMAT_B8G8R8A8_UNORM;
vk_image_info.fLevelCount = 1;
@@ -332,34 +370,43 @@ void SkiaRenderer::BindFramebufferToOutputSurface() {
current_frame()->device_viewport_size.width(),
current_frame()->device_viewport_size.height(), 0, 0, vk_image_info);
root_surface_ = SkSurface::MakeFromBackendRenderTarget(
- gr_context, render_target, kTopLeft_GrSurfaceOrigin,
+ GetGrContext(), render_target, kTopLeft_GrSurfaceOrigin,
kBGRA_8888_SkColorType, nullptr, &surface_props);
DCHECK(root_surface_);
root_canvas_ = root_surface_->getCanvas();
#else
NOTREACHED();
#endif
- } else if (!root_canvas_ || root_canvas_->getGrContext() != gr_context ||
- gfx::SkISizeToSize(root_canvas_->getBaseLayerSize()) !=
- current_frame()->device_viewport_size) {
- // Either no SkSurface setup yet, or new GrContext, need to create new
- // surface.
- GrGLFramebufferInfo framebuffer_info;
- framebuffer_info.fFBOID = 0;
- framebuffer_info.fFormat = GL_RGB8_OES;
- GrBackendRenderTarget render_target(
- current_frame()->device_viewport_size.width(),
- current_frame()->device_viewport_size.height(), 0, 8,
- framebuffer_info);
-
- root_surface_ = SkSurface::MakeFromBackendRenderTarget(
- gr_context, render_target, kBottomLeft_GrSurfaceOrigin,
- kRGB_888x_SkColorType, nullptr, &surface_props);
- DCHECK(root_surface_);
- root_canvas_ = root_surface_->getCanvas();
+ break;
+ }
+ case DrawMode::GL: {
+ auto* gr_context = GetGrContext();
+ if (!root_canvas_ || root_canvas_->getGrContext() != gr_context ||
+ gfx::SkISizeToSize(root_canvas_->getBaseLayerSize()) !=
+ current_frame()->device_viewport_size) {
+ // Either no SkSurface setup yet, or new GrContext, need to create new
+ // surface.
+ GrGLFramebufferInfo framebuffer_info;
+ framebuffer_info.fFBOID = 0;
+ framebuffer_info.fFormat = GL_RGB8_OES;
+ GrBackendRenderTarget render_target(
+ current_frame()->device_viewport_size.width(),
+ current_frame()->device_viewport_size.height(), 0, 8,
+ framebuffer_info);
+
+ root_surface_ = SkSurface::MakeFromBackendRenderTarget(
+ gr_context, render_target, kBottomLeft_GrSurfaceOrigin,
+ kRGB_888x_SkColorType, nullptr, &surface_props);
+ DCHECK(root_surface_);
+ root_canvas_ = root_surface_->getCanvas();
+ }
+ break;
}
}
+ current_canvas_ = root_canvas_;
+ current_surface_ = root_surface_.get();
+
if (settings_->show_overdraw_feedback) {
const auto& size = current_frame()->device_viewport_size;
overdraw_surface_ = root_canvas_->makeSurface(
@@ -371,9 +418,6 @@ void SkiaRenderer::BindFramebufferToOutputSurface() {
nway_canvas_->addCanvas(root_canvas_);
current_canvas_ = nway_canvas_.get();
current_surface_ = overdraw_surface_.get();
- } else {
- current_canvas_ = root_canvas_;
- current_surface_ = root_surface_.get();
}
}
@@ -383,16 +427,20 @@ void SkiaRenderer::BindFramebufferToTexture(const RenderPassId render_pass_id) {
// This function is called after AllocateRenderPassResourceIfNeeded, so there
// should be backing ready.
RenderPassBacking& backing = iter->second;
- if (is_using_ddl()) {
- non_root_surface_ = nullptr;
- current_canvas_ = skia_output_surface_->BeginPaintRenderPass(
- render_pass_id, backing.size, backing.format, backing.mipmap);
- } else {
- non_root_surface_ = backing.render_pass_surface;
- current_surface_ = non_root_surface_.get();
- current_canvas_ = non_root_surface_->getCanvas();
+ switch (draw_mode_) {
+ case DrawMode::DDL: {
+ non_root_surface_ = nullptr;
+ current_canvas_ = skia_output_surface_->BeginPaintRenderPass(
+ render_pass_id, backing.size, backing.format, backing.mipmap);
+ break;
+ }
+ case DrawMode::GL: // Fallthrough
+ case DrawMode::VULKAN: {
+ non_root_surface_ = backing.render_pass_surface;
+ current_surface_ = non_root_surface_.get();
+ current_canvas_ = non_root_surface_->getCanvas();
+ }
}
- is_drawing_render_pass_ = true;
}
void SkiaRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
@@ -405,11 +453,10 @@ void SkiaRenderer::ClearCanvas(SkColor color) {
return;
if (is_scissor_enabled_) {
- // The same paint used by SkCanvas::clear, but applied to the scissor rect.
- SkPaint clear_paint;
- clear_paint.setColor(color);
- clear_paint.setBlendMode(SkBlendMode::kSrc);
- current_canvas_->drawRect(gfx::RectToSkRect(scissor_rect_), clear_paint);
+ // Limit the clear with the scissor rect.
+ SkAutoCanvasRestore autoRestore(current_canvas_, true /* do_save */);
+ current_canvas_->clipRect(gfx::RectToSkRect(scissor_rect_));
+ current_canvas_->clear(color);
} else {
current_canvas_->clear(color);
}
@@ -450,17 +497,15 @@ void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
if (!current_canvas_)
return;
base::Optional<SkAutoCanvasRestore> auto_canvas_restore;
- if (draw_region)
+ if (draw_region || is_scissor_enabled_) {
auto_canvas_restore.emplace(current_canvas_, true /* do_save */);
-
+ if (is_scissor_enabled_)
+ current_canvas_->clipRect(gfx::RectToSkRect(scissor_rect_));
+ }
TRACE_EVENT0("viz", "SkiaRenderer::DoDrawQuad");
- gfx::Transform quad_rect_matrix;
- QuadRectTransform(&quad_rect_matrix,
- quad->shared_quad_state->quad_to_target_transform,
- gfx::RectF(quad->rect));
gfx::Transform contents_device_transform =
current_frame()->window_matrix * current_frame()->projection_matrix *
- quad_rect_matrix;
+ quad->shared_quad_state->quad_to_target_transform;
contents_device_transform.FlattenTo2d();
SkMatrix sk_device_matrix;
gfx::TransformToFlattenedSkMatrix(contents_device_transform,
@@ -481,26 +526,14 @@ void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
current_paint_.setFilterQuality(kLow_SkFilterQuality);
}
- if (quad->ShouldDrawWithBlending() ||
- quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver) {
- current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
- current_paint_.setBlendMode(
- static_cast<SkBlendMode>(quad->shared_quad_state->blend_mode));
- } else {
- current_paint_.setBlendMode(SkBlendMode::kSrc);
- }
+ current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
+ current_paint_.setBlendMode(
+ static_cast<SkBlendMode>(quad->shared_quad_state->blend_mode));
if (draw_region) {
- gfx::QuadF local_draw_region(*draw_region);
SkPath draw_region_clip_path;
- local_draw_region -=
- gfx::Vector2dF(quad->visible_rect.x(), quad->visible_rect.y());
- local_draw_region.Scale(1.0f / quad->visible_rect.width(),
- 1.0f / quad->visible_rect.height());
- local_draw_region -= gfx::Vector2dF(0.5f, 0.5f);
-
SkPoint clip_points[4];
- QuadFToSkPoints(local_draw_region, clip_points);
+ QuadFToSkPoints(*draw_region, clip_points);
draw_region_clip_path.addPoly(clip_points, 4, true);
current_canvas_->clipPath(draw_region_clip_path);
@@ -531,12 +564,7 @@ void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
NOTREACHED();
break;
case DrawQuad::YUV_VIDEO_CONTENT:
- if (is_using_ddl()) {
- DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad));
- } else {
- DrawUnsupportedQuad(quad);
- NOTIMPLEMENTED();
- }
+ DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad));
break;
case DrawQuad::INVALID:
case DrawQuad::STREAM_VIDEO_CONTENT:
@@ -551,7 +579,7 @@ void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
// We need to apply the matrix manually to have pixel-sized stroke width.
SkPoint vertices[4];
- gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
+ gfx::RectToSkRect(quad->rect).toQuad(vertices);
SkPoint transformed_vertices[4];
current_canvas_->getTotalMatrix().mapPoints(transformed_vertices, vertices,
4);
@@ -569,7 +597,7 @@ void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
SkMatrix content_matrix;
content_matrix.setRectToRect(gfx::RectFToSkRect(quad->tex_coord_rect),
- gfx::RectFToSkRect(QuadVertexRect()),
+ gfx::RectToSkRect(quad->rect),
SkMatrix::kFill_ScaleToFit);
current_canvas_->concat(content_matrix);
@@ -612,12 +640,10 @@ void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
}
void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad) {
- gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
- QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
current_paint_.setColor(quad->color);
current_paint_.setAlpha(quad->shared_quad_state->opacity *
SkColorGetA(quad->color));
- current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect),
+ current_canvas_->drawRect(gfx::RectToSkRect(quad->visible_rect),
current_paint_);
}
@@ -632,9 +658,7 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad) {
gfx::RectF visible_uv_rect = cc::MathUtil::ScaleRectProportional(
uv_rect, gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
SkRect sk_uv_rect = gfx::RectFToSkRect(visible_uv_rect);
- gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
- QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
- SkRect quad_rect = gfx::RectFToSkRect(visible_quad_vertex_rect);
+ SkRect quad_rect = gfx::RectToSkRect(quad->visible_rect);
if (quad->y_flipped)
current_canvas_->scale(1, -1);
@@ -669,18 +693,20 @@ void SkiaRenderer::DrawTileQuad(const TileDrawQuad* quad) {
gfx::RectF visible_tex_coord_rect = cc::MathUtil::ScaleRectProportional(
quad->tex_coord_rect, gfx::RectF(quad->rect),
gfx::RectF(quad->visible_rect));
- gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
- QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
SkRect uv_rect = gfx::RectFToSkRect(visible_tex_coord_rect);
current_paint_.setFilterQuality(
quad->nearest_neighbor ? kNone_SkFilterQuality : kLow_SkFilterQuality);
- current_canvas_->drawImageRect(image, uv_rect,
- gfx::RectFToSkRect(visible_quad_vertex_rect),
- &current_paint_);
+ current_canvas_->drawImageRect(
+ image, uv_rect, gfx::RectToSkRect(quad->visible_rect), &current_paint_);
}
void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad) {
+ if (draw_mode_ != DrawMode::DDL) {
+ NOTIMPLEMENTED();
+ return;
+ }
+
DCHECK(resource_provider_);
ScopedYUVSkImageBuilder builder(this, quad);
const SkImage* image = builder.sk_image();
@@ -689,15 +715,12 @@ void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad) {
gfx::RectF visible_tex_coord_rect = cc::MathUtil::ScaleRectProportional(
quad->ya_tex_coord_rect, gfx::RectF(quad->rect),
gfx::RectF(quad->visible_rect));
- gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
- QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
SkRect uv_rect = gfx::RectFToSkRect(visible_tex_coord_rect);
// TODO(penghuang): figure out how to set correct filter quality.
current_paint_.setFilterQuality(kLow_SkFilterQuality);
- current_canvas_->drawImageRect(image, uv_rect,
- gfx::RectFToSkRect(visible_quad_vertex_rect),
- &current_paint_);
+ current_canvas_->drawImageRect(
+ image, uv_rect, gfx::RectToSkRect(quad->visible_rect), &current_paint_);
}
bool SkiaRenderer::CalculateRPDQParams(sk_sp<SkImage> content,
@@ -765,22 +788,46 @@ bool SkiaRenderer::CalculateRPDQParams(sk_sp<SkImage> content,
return true;
}
+const TileDrawQuad* SkiaRenderer::CanPassBeDrawnDirectly(
+ const RenderPass* pass) {
+ return DirectRenderer::CanPassBeDrawnDirectly(pass, is_using_vulkan(),
+ resource_provider_);
+}
+
void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
- auto iter = render_pass_backings_.find(quad->render_pass_id);
- DCHECK(render_pass_backings_.end() != iter);
- // This function is called after AllocateRenderPassResourceIfNeeded, so there
- // should be backing ready.
- RenderPassBacking& backing = iter->second;
+ auto bypass = render_pass_bypass_quads_.find(quad->render_pass_id);
+ // When Render Pass has a single quad inside we would draw that directly.
+ if (bypass != render_pass_bypass_quads_.end()) {
+ TileDrawQuad* tile_quad = &bypass->second;
+ ScopedSkImageBuilder builder(this, tile_quad->resource_id());
+ sk_sp<SkImage> content_image = sk_ref_sp(builder.sk_image());
+ DrawRenderPassQuadInternal(quad, content_image);
+ } else {
+ auto iter = render_pass_backings_.find(quad->render_pass_id);
+ DCHECK(render_pass_backings_.end() != iter);
+ // This function is called after AllocateRenderPassResourceIfNeeded, so
+ // there should be backing ready.
+ RenderPassBacking& backing = iter->second;
+
+ sk_sp<SkImage> content_image;
+ switch (draw_mode_) {
+ case DrawMode::DDL: {
+ content_image = skia_output_surface_->MakePromiseSkImageFromRenderPass(
+ quad->render_pass_id, backing.size, backing.format, backing.mipmap);
+ break;
+ }
+ case DrawMode::GL: // Fallthrough
+ case DrawMode::VULKAN: {
+ content_image = backing.render_pass_surface->makeImageSnapshot();
+ }
+ }
- // TODO(weiliangc): GL Renderer has optimization that when Render Pass has a
- // single quad inside we would draw that directly. We could add similar
- // optimization here by using the quad's SkImage.
- sk_sp<SkImage> content_image =
- is_using_ddl() ? skia_output_surface_->MakePromiseSkImageFromRenderPass(
- quad->render_pass_id, backing.size, backing.format,
- backing.mipmap)
- : backing.render_pass_surface->makeImageSnapshot();
+ DrawRenderPassQuadInternal(quad, content_image);
+ }
+}
+void SkiaRenderer::DrawRenderPassQuadInternal(const RenderPassDrawQuad* quad,
+ sk_sp<SkImage> content_image) {
DrawRenderPassDrawQuadParams params;
params.filters = FiltersForPass(quad->render_pass_id);
bool can_draw = CalculateRPDQParams(content_image, quad, &params);
@@ -788,19 +835,15 @@ void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
if (!can_draw)
return;
- const auto dest_rect = gfx::RectFToSkRect(QuadVertexRect());
SkRect content_rect;
SkRect dest_visible_rect;
if (params.filter_image) {
content_rect = RectFToSkRect(params.tex_coord_rect);
- dest_visible_rect = gfx::RectFToSkRect(cc::MathUtil::ScaleRectProportional(
- QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(params.dst_rect)));
+ dest_visible_rect = gfx::RectFToSkRect(params.dst_rect);
content_image = params.filter_image;
} else {
content_rect = RectFToSkRect(quad->tex_coord_rect);
- dest_visible_rect = gfx::RectFToSkRect(cc::MathUtil::ScaleRectProportional(
- QuadVertexRect(), gfx::RectF(quad->rect),
- gfx::RectF(quad->visible_rect)));
+ dest_visible_rect = gfx::RectToSkRect(quad->visible_rect);
}
// Prepare mask.
@@ -814,7 +857,7 @@ void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
SkRect mask_rect = gfx::RectFToSkRect(
gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
quad->mask_texture_size.height()));
- mask_to_dest_matrix.setRectToRect(mask_rect, dest_rect,
+ mask_to_dest_matrix.setRectToRect(mask_rect, gfx::RectToSkRect(quad->rect),
SkMatrix::kFill_ScaleToFit);
mask_filter =
SkShaderMaskFilter::Make(mask_image->makeShader(&mask_to_dest_matrix));
@@ -838,7 +881,8 @@ void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
// Convert the content_image to a shader, and use drawRect() with the
// shader.
SkMatrix content_to_dest_matrix;
- content_to_dest_matrix.setRectToRect(content_rect, dest_rect,
+ content_to_dest_matrix.setRectToRect(content_rect,
+ gfx::RectToSkRect(quad->rect),
SkMatrix::kFill_ScaleToFit);
auto shader = content_image->makeShader(&content_to_dest_matrix);
current_paint_.setShader(std::move(shader));
@@ -855,8 +899,8 @@ void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
: nullptr;
DCHECK(background_image_filter);
SkMatrix content_to_dest_matrix;
- content_to_dest_matrix.setRectToRect(content_rect, dest_rect,
- SkMatrix::kFill_ScaleToFit);
+ content_to_dest_matrix.setRectToRect(
+ content_rect, gfx::RectToSkRect(quad->rect), SkMatrix::kFill_ScaleToFit);
SkMatrix local_matrix;
local_matrix.setTranslate(quad->filters_origin.x(), quad->filters_origin.y());
local_matrix.postScale(quad->filters_scale.x(), quad->filters_scale.y());
@@ -865,7 +909,7 @@ void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
background_image_filter->makeWithLocalMatrix(local_matrix);
SkAutoCanvasRestore auto_canvas_restore(current_canvas_, true /* do_save */);
- current_canvas_->clipRect(dest_rect);
+ current_canvas_->clipRect(gfx::RectToSkRect(quad->rect));
SkPaint paint;
paint.setMaskFilter(mask_filter);
@@ -892,8 +936,7 @@ void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad) {
current_paint_.setColor(SK_ColorMAGENTA);
#endif
current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
- current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
- current_paint_);
+ current_canvas_->drawRect(gfx::RectToSkRect(quad->rect), current_paint_);
}
void SkiaRenderer::CopyDrawnRenderPass(
@@ -920,22 +963,36 @@ void SkiaRenderer::CopyDrawnRenderPass(
return;
}
- if (is_using_ddl()) {
- auto render_pass_id =
- is_drawing_render_pass_ ? current_frame()->current_render_pass->id : 0;
- skia_output_surface_->CopyOutput(render_pass_id, window_copy_rect,
- std::move(request));
- return;
+ switch (draw_mode_) {
+ case DrawMode::DDL: {
+ if (settings_->show_overdraw_feedback) {
+ // TODO(crbug.com/889122): Overdraw currently requires calling flush on
+ // canvas on SkiaRenderer's thread.
+ return;
+ }
+ // Root framebuffer uses id 0 in SkiaOutputSurface.
+ RenderPassId render_pass_id = 0;
+ // If we are in child render pass and we don't have overdraw, copy the
+ // current render pass.
+ if (root_canvas_ != current_canvas_)
+ render_pass_id = current_frame()->current_render_pass->id;
+ skia_output_surface_->CopyOutput(render_pass_id, window_copy_rect,
+ std::move(request));
+ break;
+ }
+ case DrawMode::GL: // Fallthrough
+ case DrawMode::VULKAN: {
+ sk_sp<SkImage> copy_image =
+ current_surface_->makeImageSnapshot()->makeSubset(
+ RectToSkIRect(window_copy_rect));
+
+ // Send copy request by copying into a bitmap.
+ SkBitmap bitmap;
+ copy_image->asLegacyBitmap(&bitmap);
+ request->SendResult(
+ std::make_unique<CopyOutputSkBitmapResult>(copy_rect, bitmap));
+ }
}
-
- sk_sp<SkImage> copy_image = current_surface_->makeImageSnapshot()->makeSubset(
- RectToSkIRect(window_copy_rect));
-
- // Send copy request by copying into a bitmap.
- SkBitmap bitmap;
- copy_image->asLegacyBitmap(&bitmap);
- request->SendResult(
- std::make_unique<CopyOutputSkBitmapResult>(copy_rect, bitmap));
}
void SkiaRenderer::SetEnableDCLayers(bool enable) {
@@ -951,16 +1008,19 @@ void SkiaRenderer::DidChangeVisibility() {
}
void SkiaRenderer::FinishDrawingQuadList() {
- if (is_using_ddl()) {
- gpu::SyncToken sync_token =
- is_drawing_render_pass_
- ? skia_output_surface_->FinishPaintRenderPass()
- : skia_output_surface_->FinishPaintCurrentFrame();
- promise_images_.clear();
- yuv_promise_images_.clear();
- lock_set_for_external_use_.UnlockResources(sync_token);
- } else {
- current_canvas_->flush();
+ switch (draw_mode_) {
+ case DrawMode::DDL: {
+ gpu::SyncToken sync_token = skia_output_surface_->SubmitPaint();
+ promise_images_.clear();
+ yuv_promise_images_.clear();
+ lock_set_for_external_use_.UnlockResources(sync_token);
+ break;
+ }
+ case DrawMode::GL: // Fallthrough
+ case DrawMode::VULKAN: {
+ current_canvas_->flush();
+ break;
+ }
}
}
@@ -982,21 +1042,20 @@ bool SkiaRenderer::ShouldApplyBackgroundFilters(
return true;
}
-bool SkiaRenderer::IsUsingVulkan() const {
-#if BUILDFLAG(ENABLE_VULKAN)
- if (output_surface_->vulkan_context_provider())
- return output_surface_->vulkan_context_provider()->GetGrContext();
-#endif
- return false;
-}
-
GrContext* SkiaRenderer::GetGrContext() {
- DCHECK(!is_using_ddl());
+ switch (draw_mode_) {
+ case DrawMode::DDL:
+ return nullptr;
+ case DrawMode::VULKAN:
#if BUILDFLAG(ENABLE_VULKAN)
- if (output_surface_->vulkan_context_provider())
- return output_surface_->vulkan_context_provider()->GetGrContext();
+ return vulkan_context_provider_->GetGrContext();
+#else
+ NOTREACHED();
+ return nullptr;
#endif
- return output_surface_->context_provider()->GrContext();
+ case DrawMode::GL:
+ return context_provider_->GrContext();
+ }
}
void SkiaRenderer::UpdateRenderPassTextures(
@@ -1042,20 +1101,21 @@ void SkiaRenderer::AllocateRenderPassResourceIfNeeded(
// TODO(penghuang): check supported format correctly.
gpu::Capabilities caps;
caps.texture_format_bgra8888 = true;
- GrContext* gr_context = nullptr;
- if (!is_using_ddl()) {
- if (IsUsingVulkan()) {
+ GrContext* gr_context = GetGrContext();
+ switch (draw_mode_) {
+ case DrawMode::DDL:
+ break;
+ case DrawMode::VULKAN: {
// TODO(penghuang): check supported format correctly.
caps.texture_format_bgra8888 = true;
- } else {
- ContextProvider* context_provider = output_surface_->context_provider();
- if (context_provider) {
- caps.texture_format_bgra8888 =
- context_provider->ContextCapabilities().texture_format_bgra8888;
- }
+ break;
+ }
+ case DrawMode::GL: {
+ caps.texture_format_bgra8888 =
+ context_provider_->ContextCapabilities().texture_format_bgra8888;
}
- gr_context = GetGrContext();
}
+
render_pass_backings_.insert(std::pair<RenderPassId, RenderPassBacking>(
render_pass_id,
RenderPassBacking(gr_context, caps, requirements.size,
diff --git a/chromium/components/viz/service/display/skia_renderer.h b/chromium/components/viz/service/display/skia_renderer.h
index 4f38c9987e7..b96d21f8441 100644
--- a/chromium/components/viz/service/display/skia_renderer.h
+++ b/chromium/components/viz/service/display/skia_renderer.h
@@ -12,6 +12,7 @@
#include "components/viz/service/display/direct_renderer.h"
#include "components/viz/service/display/sync_query_collection.h"
#include "components/viz/service/viz_service_export.h"
+#include "gpu/vulkan/buildflags.h"
#include "ui/latency/latency_info.h"
class SkNWayCanvas;
@@ -27,15 +28,20 @@ class SkiaOutputSurface;
class SolidColorDrawQuad;
class TextureDrawQuad;
class TileDrawQuad;
+class VulkanContextProvider;
class YUVVideoDrawQuad;
class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
public:
+ // Different draw modes that are supported by SkiaRenderer right now.
+ enum DrawMode { GL, DDL, VULKAN };
+
// TODO(penghuang): Remove skia_output_surface when DDL is used everywhere.
SkiaRenderer(const RendererSettings* settings,
OutputSurface* output_surface,
DisplayResourceProvider* resource_provider,
- SkiaOutputSurface* skia_output_surface);
+ SkiaOutputSurface* skia_output_surface,
+ DrawMode mode);
~SkiaRenderer() override;
void SwapBuffers(std::vector<ui::LatencyInfo> latency_info,
@@ -86,6 +92,9 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad);
void DrawPictureQuad(const PictureDrawQuad* quad);
void DrawRenderPassQuad(const RenderPassDrawQuad* quad);
+ void DrawRenderPassQuadInternal(const RenderPassDrawQuad* quad,
+ sk_sp<SkImage> content_image);
+
void DrawSolidColorQuad(const SolidColorDrawQuad* quad);
void DrawTextureQuad(const TextureDrawQuad* quad);
void DrawTileQuad(const TileDrawQuad* quad);
@@ -98,8 +107,7 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
const RenderPassDrawQuad* quad,
const cc::FilterOperations* background_filters) const;
bool IsUsingVulkan() const;
- GrContext* GetGrContext();
- bool is_using_ddl() const { return !!skia_output_surface_; }
+ const TileDrawQuad* CanPassBeDrawnDirectly(const RenderPass* pass) override;
// A map from RenderPass id to the texture used to draw the RenderPass from.
struct RenderPassBacking {
@@ -119,29 +127,46 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
};
base::flat_map<RenderPassId, RenderPassBacking> render_pass_backings_;
- SkiaOutputSurface* const skia_output_surface_ = nullptr;
- bool disable_picture_quad_image_filtering_ = false;
- bool is_scissor_enabled_ = false;
+ const DrawMode draw_mode_;
- gfx::Rect scissor_rect_;
+ // Get corresponding GrContext in DrawMode::GL or DrawMode::VULKAN. Returns
+ // nullptr when there is no GrContext.
+ GrContext* GetGrContext();
+ bool is_using_ddl() const { return draw_mode_ == DrawMode::DDL; }
+ bool is_using_vulkan() const { return draw_mode_ == DrawMode::VULKAN; }
- bool is_drawing_render_pass_ = false;
+ // Interface used for drawing. Common among different draw modes.
sk_sp<SkSurface> root_surface_;
sk_sp<SkSurface> non_root_surface_;
- sk_sp<SkSurface> overdraw_surface_;
- std::unique_ptr<SkCanvas> overdraw_canvas_;
- std::unique_ptr<SkNWayCanvas> nway_canvas_;
SkCanvas* root_canvas_ = nullptr;
SkCanvas* current_canvas_ = nullptr;
SkSurface* current_surface_ = nullptr;
+
+ bool disable_picture_quad_image_filtering_ = false;
+ bool is_scissor_enabled_ = false;
+ gfx::Rect scissor_rect_;
SkPaint current_paint_;
+ // Specific for overdraw.
+ sk_sp<SkSurface> overdraw_surface_;
+ std::unique_ptr<SkCanvas> overdraw_canvas_;
+ std::unique_ptr<SkNWayCanvas> nway_canvas_;
+
+ // Specific for GL.
+ ContextProvider* context_provider_ = nullptr;
base::Optional<SyncQueryCollection> sync_queries_;
bool use_swap_with_bounds_ = false;
-
gfx::Rect swap_buffer_rect_;
std::vector<gfx::Rect> swap_content_bounds_;
+// Specific for Vulkan.
+#if BUILDFLAG(ENABLE_VULKAN)
+ VulkanContextProvider* vulkan_context_provider_ = nullptr;
+#endif
+
+ // Specific for SkDDL.
+ SkiaOutputSurface* const skia_output_surface_ = nullptr;
+
// Lock set for resources that are used for the current frame. All resources
// in this set will be unlocked with a sync token when the frame is done in
// the compositor thread. And the sync token will be released when the DDL
@@ -154,7 +179,6 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
// |lock_set_for_external_use_| are unlocked on the compositor thread.
// It is only used with DDL.
base::flat_map<ResourceId, sk_sp<SkImage>> promise_images_;
-
using YUVIds = std::tuple<ResourceId, ResourceId, ResourceId, ResourceId>;
base::flat_map<YUVIds, sk_sp<SkImage>> yuv_promise_images_;
diff --git a/chromium/components/viz/service/display/surface_aggregator.cc b/chromium/components/viz/service/display/surface_aggregator.cc
index 6280d82054a..35e7f452982 100644
--- a/chromium/components/viz/service/display/surface_aggregator.cc
+++ b/chromium/components/viz/service/display/surface_aggregator.cc
@@ -449,7 +449,8 @@ void SurfaceAggregator::EmitSurfaceContent(
quad->SetNew(shared_quad_state, scaled_rect, scaled_visible_rect,
remapped_pass_id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(), gfx::RectF(scaled_rect),
- /*force_anti_aliasing_off=*/false);
+ /*force_anti_aliasing_off=*/false,
+ /* backdrop_filter_quality*/ 1.0f);
}
// Need to re-query since referenced_surfaces_ iterators are not stable.
@@ -558,7 +559,8 @@ void SurfaceAggregator::AddColorConversionPass() {
quad->SetNew(shared_quad_state, output_rect, output_rect,
root_render_pass->id, 0, gfx::RectF(), gfx::Size(),
gfx::Vector2dF(), gfx::PointF(), gfx::RectF(output_rect),
- /*force_anti_aliasing_off=*/false);
+ /*force_anti_aliasing_off=*/false,
+ /*backdrop_filter_quality*/ 1.0f);
dest_pass_list_->push_back(std::move(color_conversion_pass));
}
@@ -887,8 +889,9 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface,
render_pass->filters.HasFilterThatMovesPixels();
if (has_pixel_moving_filter)
moved_pixel_passes_.insert(remapped_pass_id);
- bool in_moved_pixel_pass = has_pixel_moving_filter ||
- !!moved_pixel_passes_.count(remapped_pass_id);
+ bool in_moved_pixel_pass =
+ has_pixel_moving_filter ||
+ base::ContainsKey(moved_pixel_passes_, remapped_pass_id);
for (auto* quad : render_pass->quad_list) {
if (quad->material == DrawQuad::SURFACE_CONTENT) {
const auto* surface_quad = SurfaceDrawQuad::MaterialCast(quad);
@@ -945,16 +948,6 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface,
// referenced_surfaces_.
referenced_surfaces_.insert(surface->surface_id());
for (const auto& surface_info : child_surfaces) {
- if (will_draw) {
- const SurfaceRange& surface_range = surface_info.surface_range;
- damage_ranges_[surface_range.end().frame_sink_id()].push_back(
- surface_range);
- if (surface_range.HasDifferentFrameSinkIds()) {
- damage_ranges_[surface_range.start()->frame_sink_id()].push_back(
- surface_range);
- }
- }
-
// TODO(fsamuel): Consider caching this value somewhere so that
// HandleSurfaceQuad doesn't need to call it again.
Surface* child_surface =
@@ -1020,6 +1013,15 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface,
if (will_draw)
surface->OnWillBeDrawn();
+ for (const SurfaceRange& surface_range : frame.metadata.referenced_surfaces) {
+ damage_ranges_[surface_range.end().frame_sink_id()].push_back(
+ surface_range);
+ if (surface_range.HasDifferentFrameSinkIds()) {
+ damage_ranges_[surface_range.start()->frame_sink_id()].push_back(
+ surface_range);
+ }
+ }
+
for (const SurfaceId& surface_id : surface->active_referenced_surfaces()) {
if (!contained_surfaces_.count(surface_id)) {
result->undrawn_surfaces.insert(surface_id);
@@ -1109,7 +1111,7 @@ void SurfaceAggregator::PropagateCopyRequestPasses() {
CompositorFrame SurfaceAggregator::Aggregate(
const SurfaceId& surface_id,
base::TimeTicks expected_display_time,
- int32_t display_trace_id) {
+ int64_t display_trace_id) {
DCHECK(!expected_display_time.is_null());
uma_stats_.Reset();
@@ -1126,7 +1128,7 @@ CompositorFrame SurfaceAggregator::Aggregate(
if (!surface->HasActiveFrame())
return {};
- base::AutoReset<int32_t> reset_display_trace_id(&display_trace_id_,
+ base::AutoReset<int64_t> reset_display_trace_id(&display_trace_id_,
display_trace_id);
const CompositorFrame& root_surface_frame = surface->GetActiveFrame();
TRACE_EVENT_WITH_FLOW2(
diff --git a/chromium/components/viz/service/display/surface_aggregator.h b/chromium/components/viz/service/display/surface_aggregator.h
index 7647ddbae2d..325b7e1ac05 100644
--- a/chromium/components/viz/service/display/surface_aggregator.h
+++ b/chromium/components/viz/service/display/surface_aggregator.h
@@ -40,7 +40,7 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
CompositorFrame Aggregate(const SurfaceId& surface_id,
base::TimeTicks expected_display_time,
- int32_t display_trace_id = -1);
+ int64_t display_trace_id = -1);
void ReleaseResources(const SurfaceId& surface_id);
const SurfaceIndexMap& previous_contained_surfaces() const {
return previous_contained_surfaces_;
@@ -281,7 +281,7 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
// the display if they're damaged.
base::flat_map<FrameSinkId, std::vector<SurfaceRange>> damage_ranges_;
- int32_t display_trace_id_ = -1;
+ int64_t display_trace_id_ = -1;
base::WeakPtrFactory<SurfaceAggregator> weak_factory_;
diff --git a/chromium/components/viz/service/display/surface_aggregator_unittest.cc b/chromium/components/viz/service/display/surface_aggregator_unittest.cc
index 84ec6099baa..483dd5f0140 100644
--- a/chromium/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/chromium/components/viz/service/display/surface_aggregator_unittest.cc
@@ -213,12 +213,17 @@ class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource {
gfx::Rect damage_rect;
};
- static void AddQuadInPass(RenderPass* pass, Quad desc) {
+ // |referenced_surfaces| refers to the SurfaceRanges of all the
+ // SurfaceDrawQuads added to the provided |pass|.
+ static void AddQuadInPass(const Quad& desc,
+ RenderPass* pass,
+ std::vector<SurfaceRange>* referenced_surfaces) {
switch (desc.material) {
case DrawQuad::SOLID_COLOR:
cc::AddQuad(pass, gfx::Rect(0, 0, 5, 5), desc.color);
break;
case DrawQuad::SURFACE_CONTENT:
+ referenced_surfaces->emplace_back(desc.surface_range);
AddSurfaceQuad(pass, desc.primary_surface_rect, desc.opacity,
desc.to_target_transform, desc.surface_range,
desc.default_background_color,
@@ -233,6 +238,7 @@ class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource {
}
static void AddPasses(RenderPassList* pass_list,
+ std::vector<SurfaceRange>* referenced_surfaces,
Pass* passes,
size_t pass_count) {
gfx::Transform root_transform;
@@ -241,9 +247,8 @@ class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource {
RenderPass* test_pass = AddRenderPassWithDamage(
pass_list, pass.id, gfx::Rect(pass.size), pass.damage_rect,
root_transform, cc::FilterOperations());
- for (size_t j = 0; j < pass.quad_count; ++j) {
- AddQuadInPass(test_pass, pass.quads[j]);
- }
+ for (size_t j = 0; j < pass.quad_count; ++j)
+ AddQuadInPass(pass.quads[j], test_pass, referenced_surfaces);
}
}
@@ -334,7 +339,7 @@ class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource {
auto* quad = pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
quad->SetNew(shared_state, output_rect, output_rect, render_pass_id, 0,
gfx::RectF(), gfx::Size(), gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(), false);
+ gfx::RectF(), false, 1.0f);
}
protected:
@@ -397,8 +402,7 @@ class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest {
void AggregateAndVerify(Pass* expected_passes,
size_t expected_pass_count,
- SurfaceId* surface_ids,
- size_t expected_surface_count) {
+ const std::vector<SurfaceId>& expected_surface_ids) {
CompositorFrame aggregated_frame = aggregator_.Aggregate(
SurfaceId(support_->frame_sink_id(), root_local_surface_id_),
GetNextDisplayTimeAndIncrement());
@@ -409,31 +413,32 @@ class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest {
// Ensure no duplicate pass ids output.
std::set<RenderPassId> used_passes;
- for (const auto& pass : aggregated_frame.render_pass_list) {
+ for (const auto& pass : aggregated_frame.render_pass_list)
EXPECT_TRUE(used_passes.insert(pass->id).second);
- }
- EXPECT_EQ(expected_surface_count,
+ EXPECT_EQ(expected_surface_ids.size(),
aggregator_.previous_contained_surfaces().size());
- for (size_t i = 0; i < expected_surface_count; i++) {
- EXPECT_TRUE(
- aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
- aggregator_.previous_contained_surfaces().end());
- auto it = aggregator_.previous_contained_frame_sinks().find(
- surface_ids[i].frame_sink_id());
- EXPECT_TRUE(it != aggregator_.previous_contained_frame_sinks().end());
- EXPECT_EQ(it->second, surface_ids[i].local_surface_id());
+ for (const SurfaceId& surface_id : expected_surface_ids) {
+ EXPECT_THAT(aggregator_.previous_contained_surfaces(),
+ testing::Contains(testing::Key(surface_id)));
+ EXPECT_THAT(
+ aggregator_.previous_contained_frame_sinks(),
+ testing::Contains(testing::Pair(surface_id.frame_sink_id(),
+ surface_id.local_surface_id())));
}
}
void SubmitPassListAsFrame(CompositorFrameSinkSupport* support,
const LocalSurfaceId& local_surface_id,
RenderPassList* pass_list,
+ std::vector<SurfaceRange> referenced_surfaces,
float device_scale_factor) {
- CompositorFrame frame = CompositorFrameBuilder()
- .SetRenderPassList(std::move(*pass_list))
- .SetDeviceScaleFactor(device_scale_factor)
- .Build();
+ CompositorFrame frame =
+ CompositorFrameBuilder()
+ .SetRenderPassList(std::move(*pass_list))
+ .SetDeviceScaleFactor(device_scale_factor)
+ .SetReferencedSurfaces(std::move(referenced_surfaces))
+ .Build();
pass_list->clear();
support->SubmitCompositorFrame(local_surface_id, std::move(frame));
@@ -445,9 +450,28 @@ class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest {
const LocalSurfaceId& local_surface_id,
float device_scale_factor) {
RenderPassList pass_list;
- AddPasses(&pass_list, passes, pass_count);
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&pass_list, &referenced_surfaces, passes, pass_count);
SubmitPassListAsFrame(support, local_surface_id, &pass_list,
- device_scale_factor);
+ std::move(referenced_surfaces), device_scale_factor);
+ }
+
+ CompositorFrame MakeCompositorFrameFromSurfaceRanges(
+ const std::vector<SurfaceRange>& ranges) {
+ std::vector<Quad> quads;
+ for (const SurfaceRange& range : ranges) {
+ quads.push_back(Quad::SurfaceQuad(range, SK_ColorWHITE, gfx::Rect(5, 5),
+ 1.f, gfx::Transform(), false));
+ }
+ Pass passes[] = {Pass(&quads[0], quads.size(), SurfaceSize())};
+ RenderPassList pass_list;
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&pass_list, &referenced_surfaces, passes, base::size(passes));
+ return CompositorFrameBuilder()
+ .SetRenderPassList(std::move(pass_list))
+ .SetDeviceScaleFactor(1.f)
+ .SetReferencedSurfaces(ranges)
+ .Build();
}
void QueuePassAsFrame(std::unique_ptr<RenderPass> pass,
@@ -487,7 +511,6 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) {
root_local_surface_id_, device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SurfaceId ids[] = {root_surface_id};
// Check that the AggregatedDamageCallback is called with the right arguments.
EXPECT_CALL(
@@ -495,7 +518,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) {
OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
gfx::Rect(SurfaceSize()), next_display_time()));
- AggregateAndVerify(passes, base::size(passes), ids, base::size(ids));
+ AggregateAndVerify(passes, base::size(passes), {root_surface_id});
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
}
@@ -614,9 +637,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSimpleFrame) {
root_local_surface_id_, device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(passes, base::size(passes), ids, base::size(ids));
+ AggregateAndVerify(passes, base::size(passes), {root_surface_id});
}
// Ensure that the render pass ID map properly keeps and deletes entries.
@@ -715,9 +737,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleSurfaceReference) {
Pass expected_passes[] = {
Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SurfaceId ids[] = {root_surface_id, embedded_surface_id};
- AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes),
+ {root_surface_id, embedded_surface_id});
}
// This test verifies that in the absence of a primary Surface,
@@ -727,17 +748,20 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
auto primary_child_support = std::make_unique<CompositorFrameSinkSupport>(
nullptr, &manager_, kArbitraryFrameSinkId1, kChildIsRoot,
kNeedsSyncPoints);
- LocalSurfaceId primary_child_local_surface_id = allocator_.GenerateId();
- SurfaceId primary_child_surface_id(primary_child_support->frame_sink_id(),
- primary_child_local_surface_id);
auto fallback_child_support = std::make_unique<CompositorFrameSinkSupport>(
nullptr, &manager_, kArbitraryFrameSinkId2, kChildIsRoot,
kNeedsSyncPoints);
- LocalSurfaceId fallback_child_local_surface_id = allocator_.GenerateId();
+
+ LocalSurfaceId fallback_child_local_surface_id =
+ child_allocator_.GenerateId();
SurfaceId fallback_child_surface_id(fallback_child_support->frame_sink_id(),
fallback_child_local_surface_id);
+ LocalSurfaceId primary_child_local_surface_id = child_allocator_.GenerateId();
+ SurfaceId primary_child_surface_id(primary_child_support->frame_sink_id(),
+ primary_child_local_surface_id);
+
constexpr gfx::Size fallback_size(10, 10);
Quad fallback_child_quads[] = {
Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(fallback_size))};
@@ -775,19 +799,19 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
// There is no CompositorFrame submitted to |primary_child_surface_id| and
// so |fallback_child_surface_id| will be embedded and we should see a red
- // SolidColorDrawQuad.
+ // SolidColorDrawQuad. These quads are in physical pixels.
Quad expected_quads1[] = {
- // right gutter
+ // Right gutter.
Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 0, 7, 15)),
- // bottom guttter
+ // Bottom guttter.
Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(0, 5, 5, 10)),
+ // Contents of the fallback surface.
Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(5, 5)),
};
Pass expected_passes1[] = {
Pass(expected_quads1, base::size(expected_quads1), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SurfaceId ids[] = {root_surface_id, fallback_child_surface_id};
EXPECT_CALL(aggregated_damage_callback,
OnAggregatedDamage(fallback_child_local_surface_id, fallback_size,
@@ -805,25 +829,31 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
.Times(1);
// The primary_surface will not be listed in previously contained surfaces.
- AggregateAndVerify(expected_passes1, base::size(expected_passes1), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes1, base::size(expected_passes1),
+ {root_surface_id, fallback_child_surface_id});
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
// Submit the fallback again to create some damage then aggregate again.
- fallback_child_local_surface_id = allocator_.GenerateId();
+ fallback_child_local_surface_id = child_allocator_.GenerateId();
+
SubmitCompositorFrame(fallback_child_support.get(), fallback_child_passes,
base::size(fallback_child_passes),
- fallback_child_local_surface_id, device_scale_factor_1);
+ fallback_child_local_surface_id, device_scale_factor_2);
- // The damage should be equal to whole size of the primary SurfaceDrawQuad.
EXPECT_CALL(aggregated_damage_callback,
- OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
- surface_quad_rect, next_display_time()))
+ OnAggregatedDamage(fallback_child_local_surface_id, _, _, _));
+ // The damage should be equal to whole size of the primary SurfaceDrawQuad.
+ EXPECT_CALL(
+ aggregated_damage_callback,
+ OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+ surface_quad_rect, testing::A<base::TimeTicks>()))
.Times(1);
- AggregateAndVerify(expected_passes1, base::size(expected_passes1), ids,
- base::size(ids));
+ AggregateAndVerify(
+ expected_passes1, base::size(expected_passes1),
+ {root_surface_id, SurfaceId(fallback_child_support->frame_sink_id(),
+ fallback_child_local_surface_id)});
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
@@ -848,8 +878,6 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
Pass expected_passes2[] = {
Pass(expected_quads2, base::size(expected_quads2), SurfaceSize())};
- SurfaceId ids2[] = {root_surface_id, primary_child_surface_id};
-
EXPECT_CALL(
aggregated_damage_callback,
OnAggregatedDamage(primary_child_local_surface_id, primary_surface_size,
@@ -867,8 +895,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
gfx::Rect(primary_surface_size), next_display_time()))
.Times(1);
- AggregateAndVerify(expected_passes2, base::size(expected_passes2), ids2,
- base::size(ids2));
+ AggregateAndVerify(expected_passes2, base::size(expected_passes2),
+ {root_surface_id, primary_child_surface_id});
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
}
@@ -1145,15 +1173,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
Pass(expected_quads1, base::size(expected_quads1), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SurfaceId ids[] = {root_surface_id, primary_child_surface_id};
EXPECT_CALL(aggregated_damage_callback,
OnAggregatedDamage(root_local_surface_id_, root_size,
gfx::Rect(root_size), next_display_time()));
// The fallback will not be contained within the aggregated frame.
- AggregateAndVerify(expected_passes1, base::size(expected_passes1), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes1, base::size(expected_passes1),
+ {root_surface_id, primary_child_surface_id});
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
@@ -1168,8 +1195,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
gfx::Rect(primary_size), next_display_time()));
// Generate a new aggregated frame.
- AggregateAndVerify(expected_passes1, base::size(expected_passes1), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes1, base::size(expected_passes1),
+ {root_surface_id, primary_child_surface_id});
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
}
@@ -1269,7 +1296,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) {
Pass(root_quads2, base::size(root_quads2), 2, SurfaceSize())};
{
CompositorFrame frame = MakeEmptyCompositorFrame();
- AddPasses(&frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&frame.render_pass_list, &frame.metadata.referenced_surfaces,
+ root_passes, base::size(root_passes));
frame.render_pass_list[0]->copy_requests.push_back(std::move(copy_request));
frame.render_pass_list[1]->copy_requests.push_back(
std::move(copy_request2));
@@ -1359,8 +1387,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) {
{
CompositorFrame frame = MakeEmptyCompositorFrame();
- AddPasses(&frame.render_pass_list, parent_passes,
- base::size(parent_passes));
+ AddPasses(&frame.render_pass_list, &frame.metadata.referenced_surfaces,
+ parent_passes, base::size(parent_passes));
frame.metadata.referenced_surfaces.emplace_back(embedded_surface_id);
@@ -1375,7 +1403,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) {
{
CompositorFrame frame = MakeEmptyCompositorFrame();
- AddPasses(&frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&frame.render_pass_list, &frame.metadata.referenced_surfaces,
+ root_passes, base::size(root_passes));
frame.metadata.referenced_surfaces.emplace_back(parent_surface_id);
// Reference to Surface ID of a Surface that doesn't exist should be
@@ -1584,9 +1613,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, InvalidSurfaceReference) {
Pass expected_passes[] = {
Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes),
+ {root_surface_id});
}
// Tests a reference to a valid surface with no submitted frame. A
@@ -1614,9 +1642,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ValidSurfaceReferenceWithNoFrame) {
Pass expected_passes[] = {
Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes),
+ {root_surface_id});
}
// Tests a reference to a valid primary surface and a fallback surface
@@ -1641,9 +1668,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ValidFallbackWithNoFrame) {
Pass expected_passes[] = {
Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes),
+ {root_surface_id});
}
// Tests a surface quad referencing itself, generating a trivial cycle.
@@ -1664,9 +1690,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleCyclicalReference) {
Quad::SolidColorQuad(SK_ColorYELLOW, gfx::Rect(5, 5))};
Pass expected_passes[] = {
Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
- SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes),
+ {root_surface_id});
}
// Tests a more complex cycle with one intermediate surface.
@@ -1714,9 +1739,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, TwoSurfaceCyclicalReference) {
Quad::SolidColorQuad(SK_ColorCYAN, gfx::Rect(5, 5))};
Pass expected_passes[] = {
Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
- SurfaceId ids[] = {root_surface_id, child_surface_id};
- AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
- base::size(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes),
+ {root_surface_id, child_surface_id});
}
// Tests that we map render pass IDs from different surfaces into a unified
@@ -1989,7 +2013,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) {
child_pass_id[1], SurfaceSize())};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
@@ -2021,7 +2046,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) {
};
CompositorFrame middle_frame = MakeEmptyCompositorFrame();
- AddPasses(&middle_frame.render_pass_list, middle_passes,
+ AddPasses(&middle_frame.render_pass_list,
+ &middle_frame.metadata.referenced_surfaces, middle_passes,
base::size(middle_passes));
auto* middle_root_pass = middle_frame.render_pass_list[0].get();
@@ -2047,7 +2073,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) {
Pass(root_quads, base::size(root_quads), SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
+ base::size(root_passes));
root_frame.render_pass_list[0]
->shared_quad_state_list.front()
@@ -2152,7 +2180,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
Pass(child_quads, base::size(child_quads), 1, SurfaceSize())};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
@@ -2175,8 +2204,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
// Parent surface is only used to test if the transform is applied correctly
// to the child surface's damage.
CompositorFrame parent_surface_frame = MakeEmptyCompositorFrame();
- AddPasses(&parent_surface_frame.render_pass_list, parent_surface_passes,
- base::size(parent_surface_passes));
+ AddPasses(&parent_surface_frame.render_pass_list,
+ &parent_surface_frame.metadata.referenced_surfaces,
+ parent_surface_passes, base::size(parent_surface_passes));
LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
SurfaceId parent_surface_id(parent_support->frame_sink_id(),
@@ -2196,7 +2226,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
+ base::size(root_passes));
root_frame.render_pass_list[0]
->shared_quad_state_list.front()
@@ -2224,7 +2256,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
@@ -2253,7 +2286,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
{
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes,
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
base::size(root_passes));
root_frame.render_pass_list[0]
@@ -2267,7 +2301,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
{
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes,
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
base::size(root_passes));
root_frame.render_pass_list[0]
@@ -2339,7 +2374,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithSquashToFit) {
Pass(child_quads, base::size(child_quads), 1, gfx::Size(100, 100))};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
@@ -2362,8 +2398,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithSquashToFit) {
// Parent surface is only used to test if the transform is applied correctly
// to the child surface's damage.
CompositorFrame parent_surface_frame = MakeEmptyCompositorFrame();
- AddPasses(&parent_surface_frame.render_pass_list, parent_surface_passes,
- base::size(parent_surface_passes));
+ AddPasses(&parent_surface_frame.render_pass_list,
+ &parent_surface_frame.metadata.referenced_surfaces,
+ parent_surface_passes, base::size(parent_surface_passes));
LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
SurfaceId parent_surface_id(parent_support->frame_sink_id(),
@@ -2401,7 +2438,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithSquashToFit) {
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
@@ -2445,7 +2483,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithStretchToFit) {
Pass(child_quads, base::size(child_quads), 1, gfx::Size(100, 100))};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
@@ -2468,8 +2507,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithStretchToFit) {
// Parent surface is only used to test if the transform is applied correctly
// to the child surface's damage.
CompositorFrame parent_surface_frame = MakeEmptyCompositorFrame();
- AddPasses(&parent_surface_frame.render_pass_list, parent_surface_passes,
- base::size(parent_surface_passes));
+ AddPasses(&parent_surface_frame.render_pass_list,
+ &parent_surface_frame.metadata.referenced_surfaces,
+ parent_surface_passes, base::size(parent_surface_passes));
LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
SurfaceId parent_surface_id(parent_support->frame_sink_id(),
@@ -2508,7 +2548,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithStretchToFit) {
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
@@ -2545,7 +2586,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) {
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
+ base::size(root_passes));
root_frame.render_pass_list[0]->damage_rect = gfx::Rect(5, 5, 100, 100);
@@ -2578,7 +2621,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) {
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes,
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
base::size(root_passes));
root_frame.render_pass_list[0]->damage_rect = gfx::Rect(1, 2, 3, 4);
@@ -2630,13 +2674,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SurfaceDamageSameFrameSinkId) {
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
base::size(embedded_passes), id2, device_scale_factor);
- Quad quads[] = {Quad::SurfaceQuad(
- SurfaceRange(fallback_surface_id, primary_surface_id), SK_ColorWHITE,
- gfx::Rect(5, 5), 1.f, gfx::Transform(), false)};
- Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
- SubmitCompositorFrame(support_.get(), passes, base::size(passes),
- root_local_surface_id_, device_scale_factor);
+ CompositorFrame frame = MakeCompositorFrameFromSurfaceRanges(
+ {SurfaceRange(fallback_surface_id, primary_surface_id)});
+ support_->SubmitCompositorFrame(root_local_surface_id_, std::move(frame));
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
CompositorFrame aggregated_frame =
@@ -2688,13 +2729,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SurfaceDamageDifferentFrameSinkId) {
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
base::size(embedded_passes), id2, device_scale_factor);
- Quad quads[] = {Quad::SurfaceQuad(
- SurfaceRange(fallback_surface_id, primary_surface_id), SK_ColorWHITE,
- gfx::Rect(5, 5), 1.f, gfx::Transform(), false)};
- Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
- SubmitCompositorFrame(support_.get(), passes, base::size(passes),
- root_local_surface_id_, device_scale_factor);
+ CompositorFrame frame = MakeCompositorFrameFromSurfaceRanges(
+ {SurfaceRange(fallback_surface_id, primary_surface_id)});
+ support_->SubmitCompositorFrame(root_local_surface_id_, std::move(frame));
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
CompositorFrame aggregated_frame =
@@ -2735,14 +2773,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SurfaceDamagePrimarySurfaceOnly) {
LocalSurfaceId id2 = allocator_.GenerateId();
LocalSurfaceId id3 = allocator_.GenerateId();
SurfaceId primary_surface_id(kArbitraryFrameSinkId1, id2);
- Quad quads[] = {Quad::SurfaceQuad(
- SurfaceRange(base::nullopt, primary_surface_id), SK_ColorWHITE,
- gfx::Rect(5, 5), 1.f, gfx::Transform(), false)};
- Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
- constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, base::size(passes),
- root_local_surface_id_, device_scale_factor);
+ CompositorFrame frame = MakeCompositorFrameFromSurfaceRanges(
+ {SurfaceRange(base::nullopt, primary_surface_id)});
+ support_->SubmitCompositorFrame(root_local_surface_id_, std::move(frame));
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
CompositorFrame aggregated_frame =
@@ -2785,12 +2819,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
base::size(embedded_passes), id2, device_scale_factor);
- Quad quads[] = {Quad::SurfaceQuad(SurfaceRange(surface_id), SK_ColorWHITE,
- gfx::Rect(5, 5), 1.f, gfx::Transform(),
- false)};
- Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
- SubmitCompositorFrame(support_.get(), passes, base::size(passes),
- root_local_surface_id_, device_scale_factor);
+ CompositorFrame frame =
+ MakeCompositorFrameFromSurfaceRanges({SurfaceRange(surface_id)});
+ support_->SubmitCompositorFrame(root_local_surface_id_, std::move(frame));
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
CompositorFrame aggregated_frame =
@@ -2843,7 +2874,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
child_pass_id, SurfaceSize())};
RenderPassList child_pass_list;
- AddPasses(&child_pass_list, child_passes, base::size(child_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&child_pass_list, &referenced_surfaces, child_passes,
+ base::size(child_passes));
child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
gfx::Rect(1, 1, 2, 2);
@@ -2864,7 +2897,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
gfx::Rect(0, 0, 2, 2);
SubmitPassListAsFrame(child_support_.get(), child_local_surface_id,
- &child_pass_list, device_scale_factor);
+ &child_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
@@ -2876,7 +2910,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
Pass(root_quads, base::size(root_quads), SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, base::size(root_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, &referenced_surfaces, root_passes,
+ base::size(root_passes));
auto* root_pass = root_pass_list[0].get();
root_pass->shared_quad_state_list.front()
@@ -2884,7 +2920,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
root_pass->damage_rect = gfx::Rect(0, 0, 1, 1);
SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
- &root_pass_list, device_scale_factor);
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
@@ -2911,14 +2948,17 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
Pass(root_quads, base::size(root_quads), SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, base::size(root_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, &referenced_surfaces, root_passes,
+ base::size(root_passes));
auto* root_pass = root_pass_list[0].get();
root_pass->shared_quad_state_list.front()
->quad_to_target_transform.Translate(10, 10);
root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
- &root_pass_list, device_scale_factor);
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
@@ -2951,7 +2991,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
child_pass_ids[1], SurfaceSize())};
RenderPassList child_pass_list;
- AddPasses(&child_pass_list, child_passes, base::size(child_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&child_pass_list, &referenced_surfaces, child_passes,
+ base::size(child_passes));
child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
gfx::Rect(1, 1, 2, 2);
@@ -2968,7 +3010,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
CopyOutputRequest::CreateStubForTesting());
child_root_pass->damage_rect = gfx::Rect();
SubmitPassListAsFrame(child_support_.get(), child_local_surface_id,
- &child_pass_list, device_scale_factor);
+ &child_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
@@ -3021,7 +3064,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
root_pass_ids[2], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, base::size(root_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, &referenced_surfaces, root_passes,
+ base::size(root_passes));
auto* filter_pass = root_pass_list[1].get();
filter_pass->shared_quad_state_list.front()
@@ -3030,7 +3075,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
filter_pass->filters.Append(cc::FilterOperation::CreateBlurFilter(2));
root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
- &root_pass_list, device_scale_factor);
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
@@ -3070,7 +3116,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
root_pass_ids[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, base::size(root_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, &referenced_surfaces, root_passes,
+ base::size(root_passes));
auto* pass = root_pass_list[0].get();
auto* root_pass = root_pass_list[1].get();
@@ -3080,7 +3128,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
cc::FilterOperation::CreateOpacityFilter(0.5f));
root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
- &root_pass_list, device_scale_factor);
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
@@ -3121,7 +3170,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
root_pass_ids[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, base::size(root_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, &referenced_surfaces, root_passes,
+ base::size(root_passes));
auto* pass_with_filter = root_pass_list[0].get();
auto* root_pass = root_pass_list[1].get();
@@ -3133,7 +3184,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
// |root_pass|.
root_pass->damage_rect = gfx::Rect(3, 3, 3, 3);
SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
- &root_pass_list, device_scale_factor);
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
@@ -3176,7 +3228,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
root_pass_ids[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, base::size(root_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, &referenced_surfaces, root_passes,
+ base::size(root_passes));
auto* pass_with_filter = root_pass_list[0].get();
auto* root_pass = root_pass_list[1].get();
@@ -3187,7 +3241,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
// Damage rect does not intersect with render pass.
root_pass->damage_rect = gfx::Rect(6, 6, 3, 3);
SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
- &root_pass_list, device_scale_factor);
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
@@ -3600,8 +3655,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
child_surface_quads, base::size(child_surface_quads), 1, SurfaceSize())};
CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
- base::size(child_surface_passes));
+ AddPasses(&child_surface_frame.render_pass_list,
+ &child_surface_frame.metadata.referenced_surfaces,
+ child_surface_passes, base::size(child_surface_passes));
LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
SurfaceId child_surface_id(child_support_->frame_sink_id(),
@@ -3616,7 +3672,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
1, SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
+ base::size(root_passes));
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
support_->SubmitCompositorFrame(root_local_surface_id_,
@@ -3638,8 +3696,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
// Change child_frame with damage should set the flag.
{
CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
- base::size(child_surface_passes));
+ AddPasses(&child_surface_frame.render_pass_list,
+ &child_surface_frame.metadata.referenced_surfaces,
+ child_surface_passes, base::size(child_surface_passes));
child_support_->SubmitCompositorFrame(child_local_surface_id,
std::move(child_surface_frame));
@@ -3653,8 +3712,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
// Change child_frame without damage should not set the flag.
{
CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
- base::size(child_surface_passes));
+ AddPasses(&child_surface_frame.render_pass_list,
+ &child_surface_frame.metadata.referenced_surfaces,
+ child_surface_passes, base::size(child_surface_passes));
child_surface_frame.render_pass_list[0]->damage_rect = gfx::Rect();
child_support_->SubmitCompositorFrame(child_local_surface_id,
std::move(child_surface_frame));
@@ -3680,8 +3740,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
child_surface_quads, base::size(child_surface_quads), 1, SurfaceSize())};
CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
- base::size(child_surface_passes));
+ AddPasses(&child_surface_frame.render_pass_list,
+ &child_surface_frame.metadata.referenced_surfaces,
+ child_surface_passes, base::size(child_surface_passes));
LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
SurfaceId child_surface_id(child_support_->frame_sink_id(),
@@ -3696,7 +3757,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
1, SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
+ base::size(root_passes));
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
support_->SubmitCompositorFrame(root_local_surface_id_,
@@ -3724,8 +3787,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
grand_child_local_surface_id);
{
CompositorFrame grand_child_frame = MakeEmptyCompositorFrame();
- AddPasses(&grand_child_frame.render_pass_list, grand_child_passes,
- base::size(grand_child_passes));
+ AddPasses(&grand_child_frame.render_pass_list,
+ &grand_child_frame.metadata.referenced_surfaces,
+ grand_child_passes, base::size(grand_child_passes));
grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
std::move(grand_child_frame));
@@ -3738,8 +3802,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
base::size(new_child_surface_quads),
1, SurfaceSize())};
child_surface_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_surface_frame.render_pass_list, new_child_surface_passes,
- base::size(new_child_surface_passes));
+ AddPasses(&child_surface_frame.render_pass_list,
+ &child_surface_frame.metadata.referenced_surfaces,
+ new_child_surface_passes, base::size(new_child_surface_passes));
child_support_->SubmitCompositorFrame(child_local_surface_id,
std::move(child_surface_frame));
@@ -3761,8 +3826,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
// Change grand_child_frame with damage should set the flag.
{
CompositorFrame grand_child_frame = MakeEmptyCompositorFrame();
- AddPasses(&grand_child_frame.render_pass_list, grand_child_passes,
- base::size(grand_child_passes));
+ AddPasses(&grand_child_frame.render_pass_list,
+ &grand_child_frame.metadata.referenced_surfaces,
+ grand_child_passes, base::size(grand_child_passes));
grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
std::move(grand_child_frame));
@@ -3776,8 +3842,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
// Change grand_child_frame without damage should not set the flag.
{
CompositorFrame grand_child_frame = MakeEmptyCompositorFrame();
- AddPasses(&grand_child_frame.render_pass_list, grand_child_passes,
- base::size(grand_child_passes));
+ AddPasses(&grand_child_frame.render_pass_list,
+ &grand_child_frame.metadata.referenced_surfaces,
+ grand_child_passes, base::size(grand_child_passes));
grand_child_frame.render_pass_list[0]->damage_rect = gfx::Rect();
grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
std::move(grand_child_frame));
@@ -3798,7 +3865,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageFromRenderPassQuads) {
Pass(child_quads, base::size(child_quads), 1, SurfaceSize())};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
@@ -3819,7 +3887,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageFromRenderPassQuads) {
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
+ base::size(root_passes));
support_->SubmitCompositorFrame(root_local_surface_id_,
std::move(root_frame));
@@ -3847,7 +3917,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageFromRenderPassQuads) {
// Changing child_frame should damage both render_pass.
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
child_support_->SubmitCompositorFrame(child_local_surface_id,
std::move(child_frame));
@@ -3876,7 +3947,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, DamageRectOfCachedRenderPass) {
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
+ base::size(root_passes));
support_->SubmitCompositorFrame(root_local_surface_id_,
std::move(root_frame));
@@ -3899,7 +3972,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, DamageRectOfCachedRenderPass) {
// For offscreen render pass, only the visible area is damaged.
{
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes,
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
base::size(root_passes));
auto* nonroot_pass = root_frame.render_pass_list[0].get();
@@ -3926,7 +4000,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, DamageRectOfCachedRenderPass) {
// For offscreen cached render pass, should have full damage.
{
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes,
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
base::size(root_passes));
auto* nonroot_pass = root_frame.render_pass_list[0].get();
@@ -3967,7 +4042,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
pass_id[1], SurfaceSize())};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
@@ -3984,7 +4060,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
1, SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
+ AddPasses(&root_frame.render_pass_list,
+ &root_frame.metadata.referenced_surfaces, root_passes,
+ base::size(root_passes));
support_->SubmitCompositorFrame(root_local_surface_id_,
std::move(root_frame));
@@ -4007,7 +4085,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
// For offscreen render pass, only the visible area is damaged.
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
@@ -4034,7 +4113,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
// For offscreen cached render pass, should have full damage.
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
- AddPasses(&child_frame.render_pass_list, child_passes,
+ AddPasses(&child_frame.render_pass_list,
+ &child_frame.metadata.referenced_surfaces, child_passes,
base::size(child_passes));
auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
@@ -4082,7 +4162,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
pass_id[1], SurfaceSize())};
RenderPassList child_pass_list;
- AddPasses(&child_pass_list, child_passes, base::size(child_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&child_pass_list, &referenced_surfaces, child_passes,
+ base::size(child_passes));
child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
gfx::Rect(1, 1, 3, 3);
@@ -4096,7 +4178,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
gfx::Rect(0, 0, 2, 2);
SubmitPassListAsFrame(child_support_.get(), child_local_surface_id,
- &child_pass_list, device_scale_factor);
+ &child_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
@@ -4112,7 +4195,9 @@ TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
pass_id[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, base::size(root_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, &referenced_surfaces, root_passes,
+ base::size(root_passes));
auto* root_pass = root_pass_list[1].get();
root_pass->shared_quad_state_list.front()
@@ -4120,7 +4205,8 @@ TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
root_pass->damage_rect = gfx::Rect(0, 0, 1, 1);
SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
- &root_pass_list, device_scale_factor);
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
@@ -4152,14 +4238,17 @@ TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
pass_id[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, base::size(root_passes));
+ std::vector<SurfaceRange> referenced_surfaces;
+ AddPasses(&root_pass_list, &referenced_surfaces, root_passes,
+ base::size(root_passes));
auto* root_pass = root_pass_list[1].get();
root_pass->shared_quad_state_list.front()
->quad_to_target_transform.Translate(10, 10);
root_pass->damage_rect = gfx::Rect(10, 10, 2, 2);
SubmitPassListAsFrame(support_.get(), root_local_surface_id_,
- &root_pass_list, device_scale_factor);
+ &root_pass_list, std::move(referenced_surfaces),
+ device_scale_factor);
}
{
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface.cc b/chromium/components/viz/service/display_embedder/gl_output_surface.cc
index 9ef3fbc3db9..5585ac34308 100644
--- a/chromium/components/viz/service/display_embedder/gl_output_surface.cc
+++ b/chromium/components/viz/service/display_embedder/gl_output_surface.cc
@@ -117,10 +117,8 @@ void GLOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
}
uint32_t GLOutputSurface::GetFramebufferCopyTextureFormat() {
- // TODO(danakj): What attributes are used for the default framebuffer here?
- // Can it have alpha? VizProcessContextProvider doesn't take any
- // attributes.
- return GL_RGB;
+ auto* gl = static_cast<VizProcessContextProvider*>(context_provider());
+ return gl->GetCopyTextureInternalFormat();
}
OverlayCandidateValidator* GLOutputSurface::GetOverlayCandidateValidator()
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
new file mode 100644
index 00000000000..9e99733e523
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
@@ -0,0 +1,79 @@
+// Copyright 2018 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/service/display_embedder/gl_output_surface_buffer_queue_android.h"
+
+#include "components/viz/service/display/overlay_strategy_single_on_top.h"
+#include "components/viz/service/display/overlay_strategy_underlay.h"
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+
+namespace viz {
+namespace {
+class OverlayCandidateValidatorImpl : public OverlayCandidateValidator {
+ public:
+ OverlayCandidateValidatorImpl() = default;
+ ~OverlayCandidateValidatorImpl() override = default;
+
+ void GetStrategies(OverlayProcessor::StrategyList* strategies) override {
+ strategies->push_back(std::make_unique<OverlayStrategyUnderlay>(
+ this, OverlayStrategyUnderlay::OpaqueMode::AllowTransparentCandidates));
+ }
+ bool AllowCALayerOverlays() override { return false; }
+ bool AllowDCLayerOverlays() override { return false; }
+ void CheckOverlaySupport(OverlayCandidateList* surfaces) override {
+ DCHECK(!surfaces->empty());
+
+ // Only update the last candidate that was added to the list. All previous
+ // overlays should have already been handled.
+ auto& candidate = surfaces->back();
+ candidate.display_rect =
+ gfx::RectF(gfx::ToEnclosingRect(candidate.display_rect));
+ candidate.overlay_handled = true;
+
+#if DCHECK_IS_ON()
+ for (auto& candidate : *surfaces)
+ DCHECK(candidate.overlay_handled);
+#endif
+ }
+};
+
+} // namespace
+
+GLOutputSurfaceBufferQueueAndroid::GLOutputSurfaceBufferQueueAndroid(
+ scoped_refptr<VizProcessContextProvider> context_provider,
+ gpu::SurfaceHandle surface_handle,
+ SyntheticBeginFrameSource* synthetic_begin_frame_source,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gfx::BufferFormat buffer_format)
+ : GLOutputSurfaceBufferQueue(context_provider,
+ surface_handle,
+ synthetic_begin_frame_source,
+ gpu_memory_buffer_manager,
+ GL_TEXTURE_2D,
+ GL_RGBA,
+ buffer_format),
+ overlay_candidate_validator_(
+ std::make_unique<OverlayCandidateValidatorImpl>()) {}
+
+GLOutputSurfaceBufferQueueAndroid::~GLOutputSurfaceBufferQueueAndroid() =
+ default;
+
+void GLOutputSurfaceBufferQueueAndroid::HandlePartialSwap(
+ const gfx::Rect& sub_buffer_rect,
+ uint32_t flags,
+ gpu::ContextSupport::SwapCompletedCallback swap_callback,
+ gpu::ContextSupport::PresentationCallback presentation_callback) {
+ DCHECK(sub_buffer_rect.IsEmpty());
+ context_provider_->ContextSupport()->CommitOverlayPlanes(
+ flags, std::move(swap_callback), std::move(presentation_callback));
+}
+
+OverlayCandidateValidator*
+GLOutputSurfaceBufferQueueAndroid::GetOverlayCandidateValidator() const {
+ return overlay_candidate_validator_.get();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h
new file mode 100644
index 00000000000..822a78d77ef
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h
@@ -0,0 +1,39 @@
+// Copyright 2018 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_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_ANDROID_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_ANDROID_H_
+
+#include "components/viz/service/display_embedder/gl_output_surface_buffer_queue.h"
+#include "ui/gfx/buffer_types.h"
+
+namespace viz {
+
+class GLOutputSurfaceBufferQueueAndroid : public GLOutputSurfaceBufferQueue {
+ public:
+ GLOutputSurfaceBufferQueueAndroid(
+ scoped_refptr<VizProcessContextProvider> context_provider,
+ gpu::SurfaceHandle surface_handle,
+ SyntheticBeginFrameSource* synthetic_begin_frame_source,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gfx::BufferFormat buffer_format);
+ ~GLOutputSurfaceBufferQueueAndroid() override;
+
+ // GLOutputSurfaceBufferQueue implementation:
+ void HandlePartialSwap(
+ const gfx::Rect& sub_buffer_rect,
+ uint32_t flags,
+ gpu::ContextSupport::SwapCompletedCallback swap_callback,
+ gpu::ContextSupport::PresentationCallback presentation_callback) override;
+ OverlayCandidateValidator* GetOverlayCandidateValidator() const override;
+
+ private:
+ std::unique_ptr<OverlayCandidateValidator> overlay_candidate_validator_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLOutputSurfaceBufferQueueAndroid);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_ANDROID_H_
diff --git a/chromium/components/viz/service/display_embedder/gpu_display_provider.cc b/chromium/components/viz/service/display_embedder/gpu_display_provider.cc
index ce37bdf9975..e5b0cda003d 100644
--- a/chromium/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/chromium/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -36,6 +36,7 @@
#if defined(OS_ANDROID)
#include "components/viz/service/display_embedder/gl_output_surface_android.h"
+#include "components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h"
#endif
#if defined(OS_MACOSX)
@@ -138,13 +139,24 @@ std::unique_ptr<Display> GpuDisplayProvider::CreateDisplay(
// Retry creating and binding |context_provider| on transient failures.
gpu::ContextResult context_result = gpu::ContextResult::kTransientFailure;
while (context_result != gpu::ContextResult::kSuccess) {
+#if defined(OS_ANDROID)
+ gpu::SharedMemoryLimits memory_limits =
+ gpu::SharedMemoryLimits::ForDisplayCompositor(
+ renderer_settings.initial_screen_size);
+#else
+ gpu::SharedMemoryLimits memory_limits =
+ gpu::SharedMemoryLimits::ForDisplayCompositor();
+#endif
context_provider = base::MakeRefCounted<VizProcessContextProvider>(
task_executor_, surface_handle, gpu_memory_buffer_manager_.get(),
- image_factory_, gpu_channel_manager_delegate_,
- gpu::SharedMemoryLimits(), renderer_settings.requires_alpha_channel);
+ image_factory_, gpu_channel_manager_delegate_, memory_limits,
+ renderer_settings.requires_alpha_channel);
context_result = context_provider->BindToCurrentThread();
- if (context_result == gpu::ContextResult::kFatalFailure) {
+ if (IsFatalOrSurfaceFailure(context_result)) {
+#if defined(OS_ANDROID)
+ display_client->OnFatalOrSurfaceContextCreationFailure(context_result);
+#endif
gpu_service_impl_->DisableGpuCompositing();
return nullptr;
}
@@ -161,6 +173,13 @@ std::unique_ptr<Display> GpuDisplayProvider::CreateDisplay(
std::move(context_provider), surface_handle,
synthetic_begin_frame_source, gpu_memory_buffer_manager_.get(),
renderer_settings.allow_overlays);
+#elif defined(OS_ANDROID)
+ // TODO(khushalsagar): Use RGB_565 if specified by context provider.
+ auto buffer_format = gfx::BufferFormat::RGBA_8888;
+ output_surface = std::make_unique<GLOutputSurfaceBufferQueueAndroid>(
+ std::move(context_provider), surface_handle,
+ synthetic_begin_frame_source, gpu_memory_buffer_manager_.get(),
+ buffer_format);
#else
NOTREACHED();
#endif
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 66ce09d1a3c..576a5d169d4 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -287,30 +287,6 @@ SkCanvas* SkiaOutputSurfaceImpl::BeginPaintCurrentFrame() {
return recorder_->getCanvas();
}
-gpu::SyncToken SkiaOutputSurfaceImpl::FinishPaintCurrentFrame() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(recorder_);
-
- gpu::SyncToken sync_token(gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE,
- impl_on_gpu_->command_buffer_id(),
- ++sync_fence_release_);
- sync_token.SetVerifyFlush();
-
- auto ddl = recorder_->detach();
- DCHECK(ddl);
- recorder_.reset();
- auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
- // impl_on_gpu_ is released on the GPU thread by a posted task from
- // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
- auto callback =
- base::BindOnce(&SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame,
- base::Unretained(impl_on_gpu_.get()), std::move(ddl),
- std::move(yuv_resource_metadatas_), sync_fence_release_);
- gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
- sequence_id, std::move(callback), std::move(resource_sync_tokens_)));
- return sync_token;
-}
-
sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImage(
ResourceMetadata metadata) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -436,27 +412,39 @@ SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass(
return offscreen_surface_recorder_->getCanvas();
}
-gpu::SyncToken SkiaOutputSurfaceImpl::FinishPaintRenderPass() {
+gpu::SyncToken SkiaOutputSurfaceImpl::SubmitPaint() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(current_render_pass_id_);
- DCHECK(offscreen_surface_recorder_);
+ // If current_render_pass_id_ is not 0, we are painting a render pass.
+ // Otherwise we are painting a frame.
+ bool painting_render_pass = current_render_pass_id_ != 0;
+ auto& current_recorder =
+ painting_render_pass ? offscreen_surface_recorder_ : recorder_;
+ DCHECK(current_recorder);
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE,
impl_on_gpu_->command_buffer_id(),
++sync_fence_release_);
sync_token.SetVerifyFlush();
- auto ddl = offscreen_surface_recorder_->detach();
- offscreen_surface_recorder_.reset();
+ auto ddl = current_recorder->detach();
DCHECK(ddl);
-
+ current_recorder.reset();
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
- auto callback = base::BindOnce(
- &SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass,
- base::Unretained(impl_on_gpu_.get()), current_render_pass_id_,
- std::move(ddl), std::move(yuv_resource_metadatas_), sync_fence_release_);
+ base::OnceCallback<void()> callback;
+ if (painting_render_pass) {
+ callback =
+ base::BindOnce(&SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass,
+ base::Unretained(impl_on_gpu_.get()),
+ current_render_pass_id_, std::move(ddl),
+ std::move(yuv_resource_metadatas_), sync_fence_release_);
+ } else {
+ callback =
+ base::BindOnce(&SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame,
+ base::Unretained(impl_on_gpu_.get()), std::move(ddl),
+ std::move(yuv_resource_metadatas_), sync_fence_release_);
+ }
gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
sequence_id, std::move(callback), std::move(resource_sync_tokens_)));
current_render_pass_id_ = 0;
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h
index 9fdcf5e1eac..d1c13a780f0 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -74,7 +74,6 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
// SkiaOutputSurface implementation:
SkCanvas* BeginPaintCurrentFrame() override;
- gpu::SyncToken FinishPaintCurrentFrame() override;
sk_sp<SkImage> MakePromiseSkImage(ResourceMetadata metadata) override;
sk_sp<SkImage> MakePromiseSkImageFromYUV(
std::vector<ResourceMetadata> metadatas,
@@ -84,7 +83,7 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
const gfx::Size& surface_size,
ResourceFormat format,
bool mipmap) override;
- gpu::SyncToken FinishPaintRenderPass() override;
+ gpu::SyncToken SubmitPaint() override;
sk_sp<SkImage> MakePromiseSkImageFromRenderPass(const RenderPassId& id,
const gfx::Size& size,
ResourceFormat format,
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index f08a906b64e..ede7755366d 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -161,7 +161,14 @@ void SkiaOutputSurfaceImplOnGpu::Reshape(
}
vulkan_surface_ = std::move(vulkan_surface);
}
+ auto old_size = vulkan_surface_->size();
vulkan_surface_->SetSize(size);
+ if (vulkan_surface_->size() != old_size) {
+ // Size has been changed, we need to clear all surfaces which will be
+ // recreated later.
+ sk_surfaces_.clear();
+ sk_surfaces_.resize(vulkan_surface_->GetSwapChain()->num_images());
+ }
CreateSkSurfaceForVulkan();
#else
NOTREACHED();
@@ -209,6 +216,14 @@ void SkiaOutputSurfaceImplOnGpu::SwapBuffers(OutputSurfaceFrame frame) {
} else {
#if BUILDFLAG(ENABLE_VULKAN)
OnSwapBuffers();
+ auto backend = sk_surface_->getBackendRenderTarget(
+ SkSurface::kFlushRead_BackendHandleAccess);
+ GrVkImageInfo vk_image_info;
+ if (!backend.getVkImageInfo(&vk_image_info))
+ NOTREACHED() << "Failed to get the image info.";
+ vulkan_surface_->GetSwapChain()->SetCurrentImageLayout(
+ vk_image_info.fImageLayout);
+
gpu::SwapBuffersCompleteParams params;
params.swap_response.swap_start = base::TimeTicks::Now();
params.swap_response.result = vulkan_surface_->SwapBuffers();
@@ -523,23 +538,34 @@ void SkiaOutputSurfaceImplOnGpu::OnSwapBuffers() {
void SkiaOutputSurfaceImplOnGpu::CreateSkSurfaceForVulkan() {
#if BUILDFLAG(ENABLE_VULKAN)
- SkSurfaceProps surface_props =
- SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
auto* swap_chain = vulkan_surface_->GetSwapChain();
- VkImage vk_image = swap_chain->GetCurrentImage(swap_chain->current_image());
- GrVkImageInfo vk_image_info;
- vk_image_info.fImage = vk_image;
- vk_image_info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0};
- vk_image_info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- vk_image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
- vk_image_info.fFormat = VK_FORMAT_B8G8R8A8_UNORM;
- vk_image_info.fLevelCount = 1;
- GrBackendRenderTarget render_target(vulkan_surface_->size().width(),
- vulkan_surface_->size().height(), 0, 0,
- vk_image_info);
- sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
- gr_context_, render_target, kTopLeft_GrSurfaceOrigin,
- kBGRA_8888_SkColorType, nullptr, &surface_props);
+ auto index = swap_chain->current_image();
+ auto& sk_surface = sk_surfaces_[index];
+ if (!sk_surface) {
+ SkSurfaceProps surface_props =
+ SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
+ VkImage vk_image = swap_chain->GetCurrentImage();
+ VkImageLayout vk_image_layout = swap_chain->GetCurrentImageLayout();
+ GrVkImageInfo vk_image_info;
+ vk_image_info.fImage = vk_image;
+ vk_image_info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0};
+ vk_image_info.fImageLayout = vk_image_layout;
+ vk_image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
+ vk_image_info.fFormat = VK_FORMAT_B8G8R8A8_UNORM;
+ vk_image_info.fLevelCount = 1;
+ GrBackendRenderTarget render_target(vulkan_surface_->size().width(),
+ vulkan_surface_->size().height(), 0, 0,
+ vk_image_info);
+ sk_surface = SkSurface::MakeFromBackendRenderTarget(
+ gr_context_, render_target, kTopLeft_GrSurfaceOrigin,
+ kBGRA_8888_SkColorType, nullptr, &surface_props);
+ } else {
+ auto backend = sk_surface->getBackendRenderTarget(
+ SkSurface::kFlushRead_BackendHandleAccess);
+ backend.setVkImageLayout(swap_chain->GetCurrentImageLayout());
+ }
+
+ sk_surface_ = sk_surface;
#endif
}
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index 1d976f3f3c7..507ba3f0568 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -172,6 +172,9 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate {
#if BUILDFLAG(ENABLE_VULKAN)
std::unique_ptr<gpu::VulkanSurface> vulkan_surface_;
+
+ // surfaces for swap chain images.
+ std::vector<sk_sp<SkSurface>> sk_surfaces_;
#endif
// Offscreen surfaces for render passes. It can only be accessed on GPU
diff --git a/chromium/components/viz/service/display_embedder/software_output_device_x11.cc b/chromium/components/viz/service/display_embedder/software_output_device_x11.cc
index c80efdb5884..07b26f7e614 100644
--- a/chromium/components/viz/service/display_embedder/software_output_device_x11.cc
+++ b/chromium/components/viz/service/display_embedder/software_output_device_x11.cc
@@ -100,17 +100,22 @@ void SoftwareOutputDeviceX11::EndPaint() {
XRenderFreePicture(display_, picture);
XRenderFreePicture(display_, dest_picture);
XFreePixmap(display_, pixmap);
- return;
+ } else {
+ // TODO(jbauman): Switch to XShmPutImage since it's async.
+ SkPixmap pixmap;
+ surface_->peekPixels(&pixmap);
+ gfx::PutARGBImage(display_, attributes_.visual, attributes_.depth, widget_,
+ gc_, static_cast<const uint8_t*>(pixmap.addr()),
+ viewport_pixel_size_.width(),
+ viewport_pixel_size_.height(), rect.x(), rect.y(),
+ rect.x(), rect.y(), rect.width(), rect.height());
}
- // TODO(jbauman): Switch to XShmPutImage since it's async.
- SkPixmap pixmap;
- surface_->peekPixels(&pixmap);
- gfx::PutARGBImage(display_, attributes_.visual, attributes_.depth, widget_,
- gc_, static_cast<const uint8_t*>(pixmap.addr()),
- viewport_pixel_size_.width(), viewport_pixel_size_.height(),
- rect.x(), rect.y(), rect.x(), rect.y(), rect.width(),
- rect.height());
+ // Ensure the new window content appears immediately. On a TYPE_UI thread we
+ // can rely on the message loop to flush for us so XFlush() isn't necessary.
+ // However, this code can run on a different thread and would have to wait for
+ // the TYPE_UI thread to no longer be idle before a flush happens.
+ XFlush(display_);
}
} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc b/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc
index c3aa280c037..dfcd97117ed 100644
--- a/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc
+++ b/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc
@@ -9,28 +9,26 @@
#include <utility>
#include "base/lazy_instance.h"
-#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/sys_info.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/memory_dump_manager.h"
#include "build/build_config.h"
#include "components/viz/common/gpu/context_lost_observer.h"
#include "components/viz/common/gpu/context_lost_reason.h"
#include "components/viz/common/resources/platform_color.h"
#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "gpu/command_buffer/client/raster_implementation_gles.h"
-#include "gpu/command_buffer/common/context_creation_attribs.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
#include "gpu/command_buffer/common/skia_utils.h"
-#include "gpu/command_buffer/service/mailbox_manager.h"
-#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/ipc/common/surface_handle.h"
-#include "gpu/ipc/gl_in_process_context.h"
-#include "gpu/ipc/gpu_in_process_thread_service.h"
-#include "gpu/ipc/in_process_command_buffer.h"
+#include "gpu/skia_bindings/gles2_implementation_with_grcontext_support.h"
#include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
@@ -96,35 +94,30 @@ VizProcessContextProvider::VizProcessContextProvider(
gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
const gpu::SharedMemoryLimits& limits,
bool requires_alpha_channel)
- : attributes_(CreateAttributes(requires_alpha_channel)),
- context_(std::make_unique<gpu::GLInProcessContext>()),
- context_result_(
- context_->Initialize(std::move(task_executor),
- nullptr,
- (surface_handle == gpu::kNullSurfaceHandle),
- surface_handle,
- attributes_,
- limits,
- gpu_memory_buffer_manager,
- image_factory,
- gpu_channel_manager_delegate,
- base::ThreadTaskRunnerHandle::Get())) {
+ : attributes_(CreateAttributes(requires_alpha_channel)) {
+ InitializeContext(std::move(task_executor), surface_handle,
+ gpu_memory_buffer_manager, image_factory,
+ gpu_channel_manager_delegate, limits);
+
if (context_result_ == gpu::ContextResult::kSuccess) {
- auto* gles2_implementation = context_->GetImplementation();
- cache_controller_ = std::make_unique<ContextCacheController>(
- gles2_implementation, base::ThreadTaskRunnerHandle::Get());
- // |context_| is owned here so bind an unretained pointer or there will be a
- // circular reference preventing destruction.
- gles2_implementation->SetLostContextCallback(base::BindOnce(
+ // |gles2_implementation_| is owned here so bind an unretained pointer or
+ // there will be a circular reference preventing destruction.
+ gles2_implementation_->SetLostContextCallback(base::BindOnce(
&VizProcessContextProvider::OnContextLost, base::Unretained(this)));
+
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ this, "VizProcessContextProvider", base::ThreadTaskRunnerHandle::Get());
} else {
- // Context initialization failed. Record UMA and cleanup.
UmaRecordContextLost(CONTEXT_INIT_FAILED);
- context_.reset();
}
}
-VizProcessContextProvider::~VizProcessContextProvider() = default;
+VizProcessContextProvider::~VizProcessContextProvider() {
+ if (context_result_ == gpu::ContextResult::kSuccess) {
+ base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+ this);
+ }
+}
void VizProcessContextProvider::AddRef() const {
base::RefCountedThreadSafe<VizProcessContextProvider>::AddRef();
@@ -139,11 +132,11 @@ gpu::ContextResult VizProcessContextProvider::BindToCurrentThread() {
}
gpu::gles2::GLES2Interface* VizProcessContextProvider::ContextGL() {
- return context_->GetImplementation();
+ return gles2_implementation_.get();
}
gpu::ContextSupport* VizProcessContextProvider::ContextSupport() {
- return context_->GetImplementation();
+ return gles2_implementation_.get();
}
class GrContext* VizProcessContextProvider::GrContext() {
@@ -161,22 +154,27 @@ class GrContext* VizProcessContextProvider::GrContext() {
return gr_context_->get();
}
+gpu::SharedImageInterface* VizProcessContextProvider::SharedImageInterface() {
+ return command_buffer_->GetSharedImageInterface();
+}
+
ContextCacheController* VizProcessContextProvider::CacheController() {
return cache_controller_.get();
}
base::Lock* VizProcessContextProvider::GetLock() {
- return &context_lock_;
+ // Locking isn't supported on display compositor contexts.
+ return nullptr;
}
const gpu::Capabilities& VizProcessContextProvider::ContextCapabilities()
const {
- return context_->GetCapabilities();
+ return command_buffer_->GetCapabilities();
}
const gpu::GpuFeatureInfo& VizProcessContextProvider::GetGpuFeatureInfo()
const {
- return context_->GetGpuFeatureInfo();
+ return command_buffer_->GetGpuFeatureInfo();
}
void VizProcessContextProvider::AddObserver(ContextLostObserver* obs) {
@@ -190,7 +188,66 @@ void VizProcessContextProvider::RemoveObserver(ContextLostObserver* obs) {
void VizProcessContextProvider::SetUpdateVSyncParametersCallback(
const gpu::InProcessCommandBuffer::UpdateVSyncParametersCallback&
callback) {
- context_->SetUpdateVSyncParametersCallback(callback);
+ command_buffer_->SetUpdateVSyncParametersCallback(callback);
+}
+
+bool VizProcessContextProvider::UseRGB565PixelFormat() const {
+ return attributes_.alpha_size == 0 && attributes_.red_size == 5 &&
+ attributes_.green_size == 6 && attributes_.blue_size == 5;
+}
+
+uint32_t VizProcessContextProvider::GetCopyTextureInternalFormat() {
+ return attributes_.alpha_size > 0 ? GL_RGBA : GL_RGB;
+}
+
+void VizProcessContextProvider::InitializeContext(
+ scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
+ gpu::SurfaceHandle surface_handle,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gpu::ImageFactory* image_factory,
+ gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
+ const gpu::SharedMemoryLimits& mem_limits) {
+ const bool is_offscreen = surface_handle == gpu::kNullSurfaceHandle;
+
+ command_buffer_ =
+ std::make_unique<gpu::InProcessCommandBuffer>(std::move(task_executor));
+ context_result_ = command_buffer_->Initialize(
+ /*surface=*/nullptr, is_offscreen, surface_handle, attributes_,
+ /*share_command_buffer=*/nullptr, gpu_memory_buffer_manager,
+ image_factory, gpu_channel_manager_delegate,
+ base::ThreadTaskRunnerHandle::Get(), nullptr, nullptr);
+ if (context_result_ != gpu::ContextResult::kSuccess) {
+ DLOG(ERROR) << "Failed to initialize InProcessCommmandBuffer";
+ return;
+ }
+
+ // Create the GLES2 helper, which writes the command buffer protocol.
+ gles2_helper_ =
+ std::make_unique<gpu::gles2::GLES2CmdHelper>(command_buffer_.get());
+ context_result_ = gles2_helper_->Initialize(mem_limits.command_buffer_size);
+ if (context_result_ != gpu::ContextResult::kSuccess) {
+ DLOG(ERROR) << "Failed to initialize GLES2CmdHelper";
+ return;
+ }
+
+ transfer_buffer_ = std::make_unique<gpu::TransferBuffer>(gles2_helper_.get());
+
+ // Create the object exposing the OpenGL API.
+ gles2_implementation_ =
+ std::make_unique<skia_bindings::GLES2ImplementationWithGrContextSupport>(
+ gles2_helper_.get(), /*share_group=*/nullptr, transfer_buffer_.get(),
+ attributes_.bind_generates_resource,
+ attributes_.lose_context_when_out_of_memory,
+ /*support_client_side_arrays=*/false, command_buffer_.get());
+
+ context_result_ = gles2_implementation_->Initialize(mem_limits);
+ if (context_result_ != gpu::ContextResult::kSuccess) {
+ DLOG(ERROR) << "Failed to initialize GLES2Implementation";
+ return;
+ }
+
+ cache_controller_ = std::make_unique<ContextCacheController>(
+ gles2_implementation_.get(), base::ThreadTaskRunnerHandle::Get());
}
void VizProcessContextProvider::OnContextLost() {
@@ -199,10 +256,25 @@ void VizProcessContextProvider::OnContextLost() {
if (gr_context_)
gr_context_->OnLostContext();
- gpu::CommandBuffer::State state =
- context_->GetCommandBuffer()->GetLastState();
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState();
UmaRecordContextLost(
GetContextLostReason(state.error, state.context_lost_reason));
}
+bool VizProcessContextProvider::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* pmd) {
+ DCHECK_EQ(context_result_, gpu::ContextResult::kSuccess);
+
+ gles2_implementation_->OnMemoryDump(args, pmd);
+ gles2_helper_->OnMemoryDump(args, pmd);
+
+ if (gr_context_) {
+ gpu::raster::DumpGrMemoryStatistics(
+ gr_context_->get(), pmd,
+ gles2_implementation_->ShareGroupTracingGUID());
+ }
+ return true;
+}
+
} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/viz_process_context_provider.h b/chromium/components/viz/service/display_embedder/viz_process_context_provider.h
index 8780e7bd60e..11c9f0d4b5d 100644
--- a/chromium/components/viz/service/display_embedder/viz_process_context_provider.h
+++ b/chromium/components/viz/service/display_embedder/viz_process_context_provider.h
@@ -9,9 +9,8 @@
#include <memory>
-#include "base/compiler_specific.h"
#include "base/observer_list.h"
-#include "base/synchronization/lock.h"
+#include "base/trace_event/memory_dump_provider.h"
#include "components/viz/common/gpu/context_cache_controller.h"
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/service/viz_service_export.h"
@@ -22,10 +21,14 @@
class GrContext;
namespace gpu {
-class GLInProcessContext;
+namespace gles2 {
+class GLES2CmdHelper;
+class GLES2Implementation;
+} // namespace gles2
class GpuChannelManagerDelegate;
class GpuMemoryBufferManager;
class ImageFactory;
+class TransferBuffer;
struct SharedMemoryLimits;
} // namespace gpu
@@ -40,7 +43,8 @@ class ContextLostObserver;
// for the display compositor.
class VIZ_SERVICE_EXPORT VizProcessContextProvider
: public base::RefCountedThreadSafe<VizProcessContextProvider>,
- public ContextProvider {
+ public ContextProvider,
+ public base::trace_event::MemoryDumpProvider {
public:
VizProcessContextProvider(
scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
@@ -58,6 +62,7 @@ class VIZ_SERVICE_EXPORT VizProcessContextProvider
gpu::gles2::GLES2Interface* ContextGL() override;
gpu::ContextSupport* ContextSupport() override;
class GrContext* GrContext() override;
+ gpu::SharedImageInterface* SharedImageInterface() override;
ContextCacheController* CacheController() override;
base::Lock* GetLock() override;
const gpu::Capabilities& ContextCapabilities() const override;
@@ -68,21 +73,39 @@ class VIZ_SERVICE_EXPORT VizProcessContextProvider
void SetUpdateVSyncParametersCallback(
const gpu::InProcessCommandBuffer::UpdateVSyncParametersCallback&
callback);
+ bool UseRGB565PixelFormat() const;
- protected:
+ // Provides the GL internal format that should be used when calling
+ // glCopyTexImage2D() on the default framebuffer.
+ uint32_t GetCopyTextureInternalFormat();
+
+ private:
friend class base::RefCountedThreadSafe<VizProcessContextProvider>;
~VizProcessContextProvider() override;
- private:
+ void InitializeContext(
+ scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
+ gpu::SurfaceHandle surface_handle,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ gpu::ImageFactory* image_factory,
+ gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
+ const gpu::SharedMemoryLimits& mem_limits);
void OnContextLost();
+ // base::trace_event::MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* pmd) override;
+
const gpu::ContextCreationAttribs attributes_;
- base::Lock context_lock_;
- std::unique_ptr<gpu::GLInProcessContext> context_;
+ std::unique_ptr<gpu::InProcessCommandBuffer> command_buffer_;
+ std::unique_ptr<gpu::gles2::GLES2CmdHelper> gles2_helper_;
+ std::unique_ptr<gpu::TransferBuffer> transfer_buffer_;
+ std::unique_ptr<gpu::gles2::GLES2Implementation> gles2_implementation_;
+ std::unique_ptr<ContextCacheController> cache_controller_;
gpu::ContextResult context_result_;
+
std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
- std::unique_ptr<ContextCacheController> cache_controller_;
base::ObserverList<ContextLostObserver>::Unchecked observers_;
};
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index 1d6edc81d4d..62d1f140d0e 100644
--- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -98,20 +98,20 @@ void CompositorFrameSinkSupport::SetBeginFrameSource(
void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) {
DCHECK(surface);
DCHECK(surface->HasActiveFrame());
- if (last_activated_surface_id_ != surface->surface_id()) {
+
+ const LocalSurfaceId& local_surface_id =
+ surface->surface_id().local_surface_id();
+ const LocalSurfaceId& last_activated_local_surface_id =
+ last_activated_surface_id_.local_surface_id();
+
+ if (!last_activated_surface_id_.is_valid() ||
+ local_surface_id > last_activated_local_surface_id) {
if (last_activated_surface_id_.is_valid()) {
- const LocalSurfaceId& local_surface_id =
- surface->surface_id().local_surface_id();
- const LocalSurfaceId& last_activated_local_surface_id =
- last_activated_surface_id_.local_surface_id();
CHECK_GE(local_surface_id.parent_sequence_number(),
last_activated_local_surface_id.parent_sequence_number());
CHECK_GE(local_surface_id.child_sequence_number(),
last_activated_local_surface_id.child_sequence_number());
- CHECK(local_surface_id.parent_sequence_number() >
- last_activated_local_surface_id.parent_sequence_number() ||
- local_surface_id.child_sequence_number() >
- last_activated_local_surface_id.child_sequence_number());
+
Surface* prev_surface =
surface_manager_->GetSurfaceForId(last_activated_surface_id_);
DCHECK(prev_surface);
@@ -119,6 +119,12 @@ void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) {
surface_manager_->DestroySurface(prev_surface->surface_id());
}
last_activated_surface_id_ = surface->surface_id();
+ } else if (surface->surface_id() < last_activated_surface_id_) {
+ // We can get into a situation where a child-initiated synchronization is
+ // deferred until after a parent-initiated synchronization happens resulting
+ // in activations happening out of order. In that case, we simply discard
+ // the stale surface.
+ surface_manager_->DestroySurface(surface->surface_id());
}
DCHECK(surface->HasActiveFrame());
@@ -126,7 +132,7 @@ void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) {
// Check if this is a display root surface and the SurfaceId is changing.
if (is_root_ && (!referenced_local_surface_id_ ||
*referenced_local_surface_id_ !=
- surface->surface_id().local_surface_id())) {
+ last_activated_surface_id_.local_surface_id())) {
UpdateDisplayRootReference(surface);
}
}
@@ -392,6 +398,14 @@ SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrameInternal(
// to determine the freshness of a surface at aggregation time.
const LocalSurfaceId& last_created_local_surface_id =
last_created_surface_id_.local_surface_id();
+ bool last_surface_has_dependent_frame =
+ prev_surface && prev_surface->HasDependentFrame();
+
+ bool child_initiated_synchronization_event =
+ last_created_local_surface_id.is_valid() &&
+ local_surface_id.child_sequence_number() >
+ last_created_local_surface_id.child_sequence_number();
+
// Neither sequence numbers of the LocalSurfaceId can decrease and at least
// one must increase.
bool monotonically_increasing_id =
@@ -401,8 +415,7 @@ SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrameInternal(
last_created_local_surface_id.child_sequence_number()) &&
(local_surface_id.parent_sequence_number() >
last_created_local_surface_id.parent_sequence_number() ||
- local_surface_id.child_sequence_number() >
- last_created_local_surface_id.child_sequence_number());
+ child_initiated_synchronization_event);
if (!surface_info.is_valid() || !monotonically_increasing_id) {
TRACE_EVENT_INSTANT0("viz", "Surface Invariants Violation",
@@ -410,12 +423,26 @@ SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrameInternal(
return SubmitResult::SURFACE_INVARIANTS_VIOLATION;
}
- current_surface = CreateSurface(surface_info);
+ // If the last Surface doesn't have a dependent frame, and this frame
+ // corresponds to a child-initiated synchronization event then defer this
+ // Surface until a dependent frame arrives. This throttles child submission
+ // of CompositorFrames to the parent's embedding rate.
+ const bool block_activation_on_parent =
+ child_initiated_synchronization_event &&
+ !last_surface_has_dependent_frame;
+
+ current_surface = CreateSurface(surface_info, block_activation_on_parent);
last_created_surface_id_ = SurfaceId(frame_sink_id_, local_surface_id);
surface_manager_->SurfaceDamageExpected(current_surface->surface_id(),
last_begin_frame_args_);
}
+ const int64_t trace_id = ~frame.metadata.begin_frame_ack.trace_id;
+ TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow"),
+ "Event.Pipeline", TRACE_ID_GLOBAL(trace_id),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+ "step", "ReceiveHitTestData");
+
// QueueFrame can fail in unit tests, so SubmitHitTestRegionList has to be
// called before that.
frame_sink_manager()->SubmitHitTestRegionList(
@@ -564,10 +591,12 @@ void CompositorFrameSinkSupport::UpdateNeedsBeginFramesInternal() {
}
Surface* CompositorFrameSinkSupport::CreateSurface(
- const SurfaceInfo& surface_info) {
+ const SurfaceInfo& surface_info,
+ bool block_activation_on_parent) {
return surface_manager_->CreateSurface(
weak_factory_.GetWeakPtr(), surface_info,
- frame_sink_manager_->GetPrimaryBeginFrameSource(), needs_sync_tokens_);
+ frame_sink_manager_->GetPrimaryBeginFrameSource(), needs_sync_tokens_,
+ block_activation_on_parent);
}
SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index 7a774708ae2..ebf210aaa49 100644
--- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -28,6 +28,10 @@
namespace viz {
+namespace test {
+class FrameSinkManagerTest;
+}
+
class FrameSinkManagerImpl;
class LatestLocalSurfaceIdLookupDelegate;
class Surface;
@@ -167,7 +171,7 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
static const char* GetSubmitResultAsString(SubmitResult result);
private:
- friend class FrameSinkManagerTest;
+ friend class test::FrameSinkManagerTest;
SubmitResult MaybeSubmitCompositorFrameInternal(
const LocalSurfaceId& local_surface_id,
@@ -197,7 +201,8 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
bool WantsAnimateOnlyBeginFrames() const override;
void UpdateNeedsBeginFramesInternal();
- Surface* CreateSurface(const SurfaceInfo& surface_info);
+ Surface* CreateSurface(const SurfaceInfo& surface_info,
+ bool block_activation_on_parent);
// For the sync API calls, if we are blocking a client callback, runs it once
// BeginFrame and FrameAck are done.
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index a0d62a044f8..bc703a096c4 100644
--- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -33,6 +33,7 @@ using testing::_;
using testing::Eq;
namespace viz {
+namespace test {
namespace {
constexpr bool kIsRoot = false;
@@ -61,13 +62,14 @@ gpu::SyncToken GenTestSyncToken(int id) {
return token;
}
+} // namespace
+
class MockFrameSinkManagerClient : public mojom::FrameSinkManagerClient {
public:
MockFrameSinkManagerClient() = default;
~MockFrameSinkManagerClient() override = default;
// mojom::FrameSinkManagerClient:
- MOCK_METHOD1(OnSurfaceCreated, void(const SurfaceId&));
MOCK_METHOD1(OnFirstSurfaceActivation, void(const SurfaceInfo&));
MOCK_METHOD2(OnFrameTokenChanged, void(const FrameSinkId&, uint32_t));
void OnAggregatedHitTestRegionListUpdated(
@@ -93,12 +95,6 @@ class CompositorFrameSinkSupportTest : public testing::Test {
&fake_support_client_, &manager_, kArbitraryFrameSinkId, kIsRoot,
kNeedsSyncPoints);
support_->SetBeginFrameSource(&begin_frame_source_);
-
- // By default drop temporary references.
- ON_CALL(frame_sink_manager_client_, OnSurfaceCreated(_))
- .WillByDefault(Invoke([this](const SurfaceId& surface_id) {
- manager_.DropTemporaryReference(surface_id);
- }));
}
~CompositorFrameSinkSupportTest() override {
manager_.InvalidateFrameSinkId(kArbitraryFrameSinkId);
@@ -199,6 +195,13 @@ class CompositorFrameSinkSupportTest : public testing::Test {
support_->RefResources(surface->GetActiveFrame().resource_list);
}
+ void ExpireAllTemporaryReferences() {
+ // First call marks temporary references as old.
+ manager_.surface_manager()->ExpireOldTemporaryReferences();
+ // Second call removes the temporary references marked as old.
+ manager_.surface_manager()->ExpireOldTemporaryReferences();
+ }
+
protected:
ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl manager_;
@@ -538,6 +541,7 @@ TEST_F(CompositorFrameSinkSupportTest, AddDuringEviction) {
}))
.WillRepeatedly(testing::Return());
support->EvictLastActivatedSurface();
+ ExpireAllTemporaryReferences();
manager_.InvalidateFrameSinkId(kAnotherArbitraryFrameSinkId);
}
@@ -554,26 +558,64 @@ TEST_F(CompositorFrameSinkSupportTest, MonotonicallyIncreasingLocalSurfaceIds) {
LocalSurfaceId local_surface_id4(5, 3, kArbitraryToken);
LocalSurfaceId local_surface_id5(8, 1, kArbitraryToken);
LocalSurfaceId local_surface_id6(9, 3, kArbitraryToken);
+
+ // LocalSurfaceId1(6, 1)
auto result = support->MaybeSubmitCompositorFrame(
local_surface_id1, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
EXPECT_EQ(SubmitResult::ACCEPTED, result);
+
+ // LocalSurfaceId(6, 2): Child-initiated synchronization.
result = support->MaybeSubmitCompositorFrame(
local_surface_id2, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
EXPECT_EQ(SubmitResult::ACCEPTED, result);
+
+ // Since the Surface corresponding to |local_surface_id1| was not a dependency
+ // anywhere then the Surface corresponding to |local_surface_id2| will not
+ // activate until it becomes a dependency.
+ Surface* last_created_surface = support->GetLastCreatedSurfaceForTesting();
+ EXPECT_EQ(local_surface_id2,
+ last_created_surface->surface_id().local_surface_id());
+ EXPECT_FALSE(last_created_surface->HasActiveFrame());
+
+ SurfaceId surface_id2(kAnotherArbitraryFrameSinkId, local_surface_id2);
+ auto frame =
+ CompositorFrameBuilder()
+ .AddDefaultRenderPass()
+ .SetActivationDependencies({surface_id2})
+ .SetReferencedSurfaces({SurfaceRange(base::nullopt, surface_id2)})
+ .Build();
+ result = support_->MaybeSubmitCompositorFrame(
+ local_surface_id_, std::move(frame), base::nullopt, 0,
+ mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
+ EXPECT_EQ(SubmitResult::ACCEPTED, result);
+
+ // Submitting a CompositorFrame to the parent FrameSink with a dependency on
+ // |local_surface_id2| causes that Surface's CompositorFrame to activate.
+ EXPECT_TRUE(last_created_surface->HasActiveFrame());
+
+ // LocalSurfaceId(7, 2): Parent-initiated synchronization.
result = support->MaybeSubmitCompositorFrame(
local_surface_id3, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
EXPECT_EQ(SubmitResult::ACCEPTED, result);
+
+ // LocalSurfaceId(5, 3): Surface Invariants Violation. Not monotonically
+ // increasing.
result = support->MaybeSubmitCompositorFrame(
local_surface_id4, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result);
+
+ // LocalSurfaceId(8, 1): Surface Invariants Violation. Not monotonically
+ // increasing.
result = support->MaybeSubmitCompositorFrame(
local_surface_id5, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result);
+
+ // LocalSurfaceId(9, 3): Parent AND child-initiated synchronization.
result = support->MaybeSubmitCompositorFrame(
local_surface_id6, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
@@ -644,6 +686,7 @@ TEST_F(CompositorFrameSinkSupportTest, EvictLastActivatedSurface) {
EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources))
.Times(1);
support->EvictLastActivatedSurface();
+ ExpireAllTemporaryReferences();
manager_.surface_manager()->GarbageCollectSurfaces();
EXPECT_FALSE(GetSurfaceForId(id));
manager_.InvalidateFrameSinkId(kAnotherArbitraryFrameSinkId);
@@ -699,14 +742,7 @@ TEST_F(CompositorFrameSinkSupportTest, EvictSurfaceWithTemporaryReference) {
const LocalSurfaceId local_surface_id(5, kArbitraryToken);
const SurfaceId surface_id(support_->frame_sink_id(), local_surface_id);
- EXPECT_CALL(frame_sink_manager_client_, OnSurfaceCreated(surface_id))
- .WillOnce(
- Invoke([this, &parent_frame_sink_id](const SurfaceId& surface_id) {
- manager_.AssignTemporaryReference(surface_id, parent_frame_sink_id);
- }));
-
- // When CompositorFrame is submitted, a temporary reference will be created
- // and |parent_frame_sink_id| will be assigned as the owner.
+ // When CompositorFrame is submitted, a temporary reference will be created.
support_->SubmitCompositorFrame(local_surface_id,
MakeDefaultCompositorFrame());
@@ -715,8 +751,9 @@ TEST_F(CompositorFrameSinkSupportTest, EvictSurfaceWithTemporaryReference) {
support_->EvictLastActivatedSurface();
EXPECT_TRUE(GetSurfaceForId(surface_id));
- // Verify the temporary reference is removed when the parent is invalidated.
- manager_.InvalidateFrameSinkId(parent_frame_sink_id);
+ // Verify the temporary reference is removed when expired.
+ ExpireAllTemporaryReferences();
+ manager_.surface_manager()->GarbageCollectSurfaces();
EXPECT_FALSE(GetSurfaceForId(surface_id));
}
@@ -774,6 +811,7 @@ TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) {
EXPECT_FALSE(called3);
support_->EvictLastActivatedSurface();
+ ExpireAllTemporaryReferences();
local_surface_id_ = LocalSurfaceId();
manager_.surface_manager()->GarbageCollectSurfaces();
EXPECT_TRUE(called1);
@@ -1073,11 +1111,10 @@ TEST_F(CompositorFrameSinkSupportTest,
.Build();
testing::InSequence sequence;
- EXPECT_CALL(frame_sink_manager_client_, OnSurfaceCreated(_));
EXPECT_CALL(frame_sink_manager_client_, OnFirstSurfaceActivation(_));
EXPECT_CALL(frame_sink_manager_client_, OnFrameTokenChanged(_, frame_token));
support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
}
-} // namespace
+} // namespace test
} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
index a555d9a9530..4d5f2e200c8 100644
--- a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
+++ b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
@@ -8,7 +8,9 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
+#include "cc/base/histograms.h"
#include "cc/trees/layer_tree_frame_sink_client.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "components/viz/common/quads/compositor_frame.h"
@@ -23,6 +25,30 @@
namespace viz {
+DirectLayerTreeFrameSink::PipelineReporting::PipelineReporting(
+ const BeginFrameArgs args,
+ base::TimeTicks now)
+ : trace_id_(args.trace_id), frame_time_(now) {}
+
+DirectLayerTreeFrameSink::PipelineReporting::~PipelineReporting() = default;
+
+void DirectLayerTreeFrameSink::PipelineReporting::Report() {
+ TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline",
+ TRACE_ID_GLOBAL(trace_id_),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+ "step", "SubmitCompositorFrame");
+
+ // Note that client_name is constant during the lifetime of the process and
+ // it's either "Browser" or "Renderer".
+ UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+ base::StringPrintf(
+ "GraphicsPipeline.%s.SubmitCompositorFrameAfterBeginFrame",
+ cc::GetClientNameForMetrics()),
+ base::TimeTicks::Now() - frame_time_,
+ base::TimeDelta::FromMicroseconds(1),
+ base::TimeDelta::FromMilliseconds(200), 50);
+}
+
DirectLayerTreeFrameSink::DirectLayerTreeFrameSink(
const FrameSinkId& frame_sink_id,
CompositorFrameSinkSupportManager* support_manager,
@@ -107,15 +133,6 @@ static HitTestRegionList CreateHitTestData(const CompositorFrame& frame) {
const SurfaceDrawQuad* surface_quad =
SurfaceDrawQuad::MaterialCast(quad);
- // Skip the quad if the FrameSinkId between fallback and primary is not
- // the same, because we don't know which FrameSinkId would be used to
- // draw this quad.
- if (surface_quad->surface_range.start() &&
- surface_quad->surface_range.start()->frame_sink_id() !=
- surface_quad->surface_range.end().frame_sink_id()) {
- continue;
- }
-
// Skip the quad if the transform is not invertible (i.e. it will not
// be able to receive events).
gfx::Transform target_to_quad_transform;
@@ -145,26 +162,48 @@ void DirectLayerTreeFrameSink::SubmitCompositorFrame(CompositorFrame frame) {
DCHECK_LE(BeginFrameArgs::kStartingFrameNumber,
frame.metadata.begin_frame_ack.sequence_number);
+ // It's possible to request an immediate composite from cc which will bypass
+ // BeginFrame. In that case, we cannot collect full graphics pipeline data.
+ auto it = pipeline_reporting_frame_times_.find(
+ frame.metadata.begin_frame_ack.trace_id);
+ if (it != pipeline_reporting_frame_times_.end()) {
+ it->second.Report();
+ pipeline_reporting_frame_times_.erase(it);
+ }
+
+ const LocalSurfaceId& local_surface_id =
+ parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId();
+
if (frame.size_in_pixels() != last_swap_frame_size_ ||
frame.device_scale_factor() != device_scale_factor_) {
parent_local_surface_id_allocator_.GenerateId();
last_swap_frame_size_ = frame.size_in_pixels();
device_scale_factor_ = frame.device_scale_factor();
- display_->SetLocalSurfaceId(
- parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId(),
- device_scale_factor_);
+ display_->SetLocalSurfaceId(local_surface_id, device_scale_factor_);
}
+ const int64_t trace_id = ~frame.metadata.begin_frame_ack.trace_id;
+ TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow"),
+ "Event.Pipeline", TRACE_ID_GLOBAL(trace_id),
+ TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "SubmitHitTestData");
+
HitTestRegionList hit_test_region_list = CreateHitTestData(frame);
- support_->SubmitCompositorFrame(
- parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId(),
- std::move(frame), std::move(hit_test_region_list));
+ support_->SubmitCompositorFrame(local_surface_id, std::move(frame),
+ std::move(hit_test_region_list));
}
void DirectLayerTreeFrameSink::DidNotProduceFrame(const BeginFrameAck& ack) {
DCHECK(!ack.has_damage);
DCHECK_LE(BeginFrameArgs::kStartingFrameNumber, ack.sequence_number);
- support_->DidNotProduceFrame(ack);
+
+ // TODO(yiyix): Remove duplicated calls of DidNotProduceFrame from the same
+ // BeginFrames. https://crbug.com/881949
+ auto it = pipeline_reporting_frame_times_.find(ack.trace_id);
+ if (it != pipeline_reporting_frame_times_.end()) {
+ support_->DidNotProduceFrame(ack);
+ pipeline_reporting_frame_times_.erase(it);
+ }
}
void DirectLayerTreeFrameSink::DidAllocateSharedBitmap(
@@ -245,6 +284,27 @@ void DirectLayerTreeFrameSink::DidPresentCompositorFrame(
}
void DirectLayerTreeFrameSink::OnBeginFrame(const BeginFrameArgs& args) {
+ DCHECK_LE(pipeline_reporting_frame_times_.size(), 25u);
+ // Note that client_name is constant during the lifetime of the process and
+ // it's either "Browser" or "Renderer".
+ const char* client_name = cc::GetClientNameForMetrics();
+ if (client_name && args.trace_id != -1) {
+ base::TimeTicks current_time = base::TimeTicks::Now();
+ PipelineReporting report(args, current_time);
+ pipeline_reporting_frame_times_.emplace(args.trace_id, report);
+ // Missed BeginFrames use the frame time of the last received BeginFrame
+ // which is bogus from a reporting perspective if nothing has been updating
+ // on screen for a while.
+ if (args.type != BeginFrameArgs::MISSED) {
+ base::TimeDelta frame_difference = current_time - args.frame_time;
+ UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+ base::StringPrintf("GraphicsPipeline.%s.ReceivedBeginFrame",
+ client_name),
+ frame_difference, base::TimeDelta::FromMicroseconds(1),
+ base::TimeDelta::FromMilliseconds(100), 50);
+ }
+ }
+
begin_frame_source_->OnBeginFrame(args);
}
diff --git a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
index 5a4c38a09b7..e76bb6c409d 100644
--- a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
+++ b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
@@ -30,6 +30,26 @@ class VIZ_SERVICE_EXPORT DirectLayerTreeFrameSink
public ExternalBeginFrameSourceClient,
public DisplayClient {
public:
+ // This class is used to handle the graphics pipeline related metrics
+ // reporting.
+ class PipelineReporting {
+ public:
+ PipelineReporting(BeginFrameArgs args, base::TimeTicks now);
+ ~PipelineReporting();
+
+ void Report();
+
+ int64_t trace_id() const { return trace_id_; }
+
+ private:
+ // The trace id of a BeginFrame which is used to track its progress on the
+ // client side.
+ int64_t trace_id_;
+
+ // The time stamp for the begin frame to arrive on client side.
+ base::TimeTicks frame_time_;
+ };
+
// The underlying Display, FrameSinkManagerImpl, and LocalSurfaceIdAllocator
// must outlive this class.
DirectLayerTreeFrameSink(
@@ -103,6 +123,8 @@ class VIZ_SERVICE_EXPORT DirectLayerTreeFrameSink
float device_scale_factor_ = 1.f;
bool is_lost_ = false;
std::unique_ptr<ExternalBeginFrameSource> begin_frame_source_;
+ // Use this map to record the time when client received the BeginFrameArgs.
+ base::flat_map<int64_t, PipelineReporting> pipeline_reporting_frame_times_;
base::WeakPtrFactory<DirectLayerTreeFrameSink> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DirectLayerTreeFrameSink);
diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index bb6789baf76..5af675a9561 100644
--- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -241,15 +241,6 @@ void FrameSinkManagerImpl::UnregisterFrameSinkHierarchy(
RecursivelyAttachBeginFrameSource(source_iter.second, source_iter.first);
}
-void FrameSinkManagerImpl::AssignTemporaryReference(const SurfaceId& surface_id,
- const FrameSinkId& owner) {
- surface_manager_.AssignTemporaryReference(surface_id, owner);
-}
-
-void FrameSinkManagerImpl::DropTemporaryReference(const SurfaceId& surface_id) {
- surface_manager_.DropTemporaryReference(surface_id);
-}
-
void FrameSinkManagerImpl::AddVideoDetectorObserver(
mojom::VideoDetectorObserverPtr observer) {
if (!video_detector_) {
@@ -289,15 +280,6 @@ void FrameSinkManagerImpl::RequestCopyOfOutput(
}
void FrameSinkManagerImpl::OnSurfaceCreated(const SurfaceId& surface_id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
- if (client_) {
- client_->OnSurfaceCreated(surface_id);
- } else {
- // There is no client to assign an owner for the temporary reference, so we
- // can drop the temporary reference safely.
- surface_manager_.DropTemporaryReference(surface_id);
- }
}
void FrameSinkManagerImpl::OnFirstSurfaceActivation(
diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index a08691e519c..03d01a023af 100644
--- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -38,6 +38,11 @@
#include "services/viz/public/interfaces/compositing/video_detector_observer.mojom.h"
namespace viz {
+
+namespace test {
+class FrameSinkManagerTest;
+} // namespace test
+
class CapturableFrameSink;
class CompositorFrameSinkSupport;
class DisplayProvider;
@@ -95,9 +100,6 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
void UnregisterFrameSinkHierarchy(
const FrameSinkId& parent_frame_sink_id,
const FrameSinkId& child_frame_sink_id) override;
- void AssignTemporaryReference(const SurfaceId& surface_id,
- const FrameSinkId& owner) override;
- void DropTemporaryReference(const SurfaceId& surface_id) override;
void AddVideoDetectorObserver(
mojom::VideoDetectorObserverPtr observer) override;
void CreateVideoCapturer(
@@ -189,7 +191,7 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
const FrameSinkId& frame_sink_id) const;
private:
- friend class FrameSinkManagerTest;
+ friend class test::FrameSinkManagerTest;
// Metadata for a CompositorFrameSink.
struct FrameSinkData {
diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
index a18c25db64c..28d3bccc8db 100644
--- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
@@ -23,6 +23,8 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace viz {
+namespace test {
+
namespace {
constexpr FrameSinkId kFrameSinkIdRoot(1, 1);
@@ -72,6 +74,12 @@ class FrameSinkManagerTest : public testing::Test {
return support->begin_frame_source_;
}
+ void ExpireAllTemporaryReferencesAndGarbageCollect() {
+ manager_.surface_manager()->ExpireOldTemporaryReferences();
+ manager_.surface_manager()->ExpireOldTemporaryReferences();
+ manager_.surface_manager()->GarbageCollectSurfaces();
+ }
+
// Checks if a [Root]CompositorFrameSinkImpl exists for |frame_sink_id|.
bool CompositorFrameSinkExists(const FrameSinkId& frame_sink_id) {
return base::ContainsKey(manager_.sink_map_, frame_sink_id);
@@ -427,13 +435,13 @@ TEST_F(FrameSinkManagerTest, EvictSurfaces) {
// |surface_id1| and |surface_id2| should remain alive after garbage
// collection because they're not marked for destruction.
- manager_.surface_manager()->GarbageCollectSurfaces();
+ ExpireAllTemporaryReferencesAndGarbageCollect();
EXPECT_TRUE(manager_.surface_manager()->GetSurfaceForId(surface_id1));
EXPECT_TRUE(manager_.surface_manager()->GetSurfaceForId(surface_id2));
// Call EvictSurfaces. Now the garbage collector can destroy the surfaces.
manager_.EvictSurfaces({surface_id1, surface_id2});
- manager_.surface_manager()->GarbageCollectSurfaces();
+ ExpireAllTemporaryReferencesAndGarbageCollect();
EXPECT_FALSE(manager_.surface_manager()->GetSurfaceForId(surface_id1));
EXPECT_FALSE(manager_.surface_manager()->GetSurfaceForId(surface_id2));
}
@@ -642,4 +650,5 @@ INSTANTIATE_TEST_CASE_P(
::testing::ValuesIn(kUnregisterOrderList),
::testing::ValuesIn(kBFSOrderList)));
+} // namespace test
} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index dedcdc38013..384d279c805 100644
--- a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -54,10 +54,18 @@ RootCompositorFrameSinkImpl::Create(
external_begin_frame_source =
std::make_unique<ExternalBeginFrameSourceAndroid>(restart_id);
#else
- synthetic_begin_frame_source = std::make_unique<DelayBasedBeginFrameSource>(
- std::make_unique<DelayBasedTimeSource>(
- base::ThreadTaskRunnerHandle::Get().get()),
- restart_id);
+ if (params->disable_frame_rate_limit) {
+ synthetic_begin_frame_source =
+ std::make_unique<BackToBackBeginFrameSource>(
+ std::make_unique<DelayBasedTimeSource>(
+ base::ThreadTaskRunnerHandle::Get().get()));
+ } else {
+ synthetic_begin_frame_source =
+ std::make_unique<DelayBasedBeginFrameSource>(
+ std::make_unique<DelayBasedTimeSource>(
+ base::ThreadTaskRunnerHandle::Get().get()),
+ restart_id);
+ }
#endif
}
@@ -127,12 +135,6 @@ void RootCompositorFrameSinkImpl::SetOutputIsSecure(bool secure) {
display_->SetOutputIsSecure(secure);
}
-void RootCompositorFrameSinkImpl::SetAuthoritativeVSyncInterval(
- base::TimeDelta interval) {
- if (synthetic_begin_frame_source_)
- synthetic_begin_frame_source_->SetAuthoritativeVSyncInterval(interval);
-}
-
void RootCompositorFrameSinkImpl::SetDisplayVSyncParameters(
base::TimeTicks timebase,
base::TimeDelta interval) {
diff --git a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index 790885987eb..8c717154ff5 100644
--- a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
+++ b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -47,7 +47,6 @@ class RootCompositorFrameSinkImpl : public mojom::CompositorFrameSink,
void SetDisplayColorSpace(const gfx::ColorSpace& blending_color_space,
const gfx::ColorSpace& device_color_space) override;
void SetOutputIsSecure(bool secure) override;
- void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override;
void SetDisplayVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) override;
void ForceImmediateDrawAndSwapIfPossible() override;
diff --git a/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc b/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc
index 54007fa569d..f82ea76454f 100644
--- a/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc
@@ -16,7 +16,6 @@
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/test/compositor_frame_helpers.h"
-#include "components/viz/test/test_frame_sink_manager_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -125,9 +124,6 @@ class SurfaceReferencesTest : public testing::Test {
void SetUp() override {
// Start each test with a fresh SurfaceManager instance.
manager_ = std::make_unique<FrameSinkManagerImpl>(&shared_bitmap_manager_);
- frame_sink_manager_client_ =
- std::make_unique<TestFrameSinkManagerClient>(manager_.get());
- manager_->SetLocalClient(frame_sink_manager_client_.get());
}
void TearDown() override {
supports_.clear();
@@ -140,7 +136,6 @@ class SurfaceReferencesTest : public testing::Test {
ServerSharedBitmapManager shared_bitmap_manager_;
std::unique_ptr<FrameSinkManagerImpl> manager_;
- std::unique_ptr<TestFrameSinkManagerClient> frame_sink_manager_client_;
std::unordered_map<FrameSinkId,
std::unique_ptr<CompositorFrameSinkSupport>,
FrameSinkIdHash>
@@ -157,8 +152,6 @@ TEST_F(SurfaceReferencesTest, AddReference) {
}
TEST_F(SurfaceReferencesTest, AddRemoveReference) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
SurfaceId id1 = CreateSurface(kFrameSink1, 1);
AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
@@ -179,9 +172,6 @@ TEST_F(SurfaceReferencesTest, AddRemoveReference) {
}
TEST_F(SurfaceReferencesTest, NewSurfaceFromFrameSink) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink2, kFrameSink3);
-
SurfaceId id1 = CreateSurface(kFrameSink1, 1);
SurfaceId id2 = CreateSurface(kFrameSink2, 1);
SurfaceId id3 = CreateSurface(kFrameSink3, 1);
@@ -243,8 +233,6 @@ TEST_F(SurfaceReferencesTest, ReferenceCycleGetsDeleted) {
}
TEST_F(SurfaceReferencesTest, SurfacesAreDeletedDuringGarbageCollection) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
SurfaceId id1 = CreateSurface(kFrameSink1, 1);
SurfaceId id2 = CreateSurface(kFrameSink2, 1);
@@ -275,9 +263,6 @@ TEST_F(SurfaceReferencesTest, SurfacesAreDeletedDuringGarbageCollection) {
}
TEST_F(SurfaceReferencesTest, GarbageCollectionWorksRecursively) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink2, kFrameSink3);
-
SurfaceId id1 = CreateSurface(kFrameSink1, 1);
SurfaceId id2 = CreateSurface(kFrameSink2, 1);
SurfaceId id3 = CreateSurface(kFrameSink3, 1);
@@ -312,9 +297,6 @@ TEST_F(SurfaceReferencesTest, GarbageCollectionWorksRecursively) {
// Verify that surfaces marked as live are not garbage collected and any
// dependencies are also not garbage collected.
TEST_F(SurfaceReferencesTest, LiveSurfaceStillReachable) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink2, kFrameSink3);
-
SurfaceId id1 = CreateSurface(kFrameSink1, 1);
SurfaceId id2 = CreateSurface(kFrameSink2, 1);
SurfaceId id3 = CreateSurface(kFrameSink3, 1);
@@ -347,8 +329,6 @@ TEST_F(SurfaceReferencesTest, LiveSurfaceStillReachable) {
}
TEST_F(SurfaceReferencesTest, TryAddReferenceSameReferenceTwice) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
SurfaceId id1 = CreateSurface(kFrameSink1, 1);
SurfaceId id2 = CreateSurface(kFrameSink2, 1);
@@ -364,8 +344,6 @@ TEST_F(SurfaceReferencesTest, TryAddReferenceSameReferenceTwice) {
}
TEST_F(SurfaceReferencesTest, AddingSelfReferenceFails) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
SurfaceId id1 = CreateSurface(kFrameSink2, 1);
// A temporary reference must exist to |id1|.
@@ -395,8 +373,6 @@ TEST_F(SurfaceReferencesTest, RemovingNonexistantReferenceFails) {
}
TEST_F(SurfaceReferencesTest, AddSurfaceThenReference) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
// Create a new surface.
const SurfaceId surface_id = CreateSurface(kFrameSink2, 1);
@@ -410,14 +386,11 @@ TEST_F(SurfaceReferencesTest, AddSurfaceThenReference) {
// The temporary reference to |surface_id| should be gone.
// The only temporary reference should be to |parent_id|.
// There must be a real reference from |parent_id| to |child_id|.
- EXPECT_THAT(GetAllTempReferences(), IsEmpty());
EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id));
+ EXPECT_THAT(GetAllTempReferences(), ElementsAre(parent_id));
}
TEST_F(SurfaceReferencesTest, AddSurfaceThenRootReference) {
- // The test will deal with temporary references explicitly.
- frame_sink_manager_client_->DisableAssignTemporaryReferences();
-
// Create a new surface.
const SurfaceId surface_id = CreateSurface(kFrameSink1, 1);
@@ -435,9 +408,6 @@ TEST_F(SurfaceReferencesTest, AddSurfaceThenRootReference) {
}
TEST_F(SurfaceReferencesTest, AddTwoSurfacesThenOneReference) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink3);
-
// Create two surfaces with different FrameSinkIds.
const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
const SurfaceId surface_id2 = CreateSurface(kFrameSink3, 1);
@@ -454,13 +424,12 @@ TEST_F(SurfaceReferencesTest, AddTwoSurfacesThenOneReference) {
// to it must be gone.
// There should still be a temporary reference left to |surface_id2|.
// A temporary reference to |parent_id| must be created.
- EXPECT_THAT(GetAllTempReferences(), UnorderedElementsAre(surface_id2));
+ EXPECT_THAT(GetAllTempReferences(),
+ UnorderedElementsAre(parent_id, surface_id2));
EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
}
TEST_F(SurfaceReferencesTest, AddSurfacesSkipReference) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
// Add two surfaces that have the same FrameSinkId. This would happen
// when a client submits two CompositorFrames before parent submits a new
// CompositorFrame.
@@ -480,13 +449,11 @@ TEST_F(SurfaceReferencesTest, AddSurfacesSkipReference) {
// The real reference should be added for |surface_id2| and the temporary
// references to both |surface_id1| and |surface_id2| should be gone.
// There should be a temporary reference to |parent_id|.
- EXPECT_THAT(GetAllTempReferences(), IsEmpty());
+ EXPECT_THAT(GetAllTempReferences(), ElementsAre(parent_id));
EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id2));
}
TEST_F(SurfaceReferencesTest, RemoveFirstTempReferenceOnly) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
// Add two surfaces that have the same FrameSinkId. This would happen
// when a client submits two CFs before parent submits a new CF.
const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
@@ -505,13 +472,12 @@ TEST_F(SurfaceReferencesTest, RemoveFirstTempReferenceOnly) {
// The real reference should be added for |surface_id1| and its temporary
// reference should be removed. The temporary reference for |surface_id2|
// should remain. A temporary reference must be added for |parent_id|.
- EXPECT_THAT(GetAllTempReferences(), UnorderedElementsAre(surface_id2));
+ EXPECT_THAT(GetAllTempReferences(),
+ UnorderedElementsAre(parent_id, surface_id2));
EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
}
TEST_F(SurfaceReferencesTest, SurfaceWithTemporaryReferenceIsNotDeleted) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
@@ -536,25 +502,8 @@ TEST_F(SurfaceReferencesTest, SurfaceWithTemporaryReferenceIsNotDeleted) {
EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
}
-// Checks that when a temporary reference is assigned an owner, if the owner is
-// invalidated then the temporary reference is also removed.
-TEST_F(SurfaceReferencesTest, InvalidateTempReferenceOwnerRemovesReference) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
- // Surface |id1| should have a temporary reference on creation.
- const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
- ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
-
- // When |kFrameSink1| is invalidated the temporary reference will be removed.
- GetSurfaceManager().InvalidateFrameSinkId(kFrameSink1);
- ASSERT_THAT(GetAllTempReferences(), IsEmpty());
-}
-
-// Checks that adding a surface reference clears the temporary reference and
-// ownership. Invalidating the old owner shouldn't do anything.
+// Checks that adding a surface reference clears the temporary reference.
TEST_F(SurfaceReferencesTest, InvalidateHasNoEffectOnSurfaceReferences) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);
@@ -565,62 +514,11 @@ TEST_F(SurfaceReferencesTest, InvalidateHasNoEffectOnSurfaceReferences) {
AddSurfaceReference(parent_id, id1);
ASSERT_THAT(GetAllTempReferences(), IsEmpty());
ASSERT_THAT(GetReferencesFor(id1), UnorderedElementsAre(parent_id));
-
- // When |kFrameSink1| is invalidated it shouldn't change the surface
- // references.
- DestroyCompositorFrameSinkSupport(kFrameSink1);
- ASSERT_THAT(GetReferencesFor(id1), UnorderedElementsAre(parent_id));
-}
-
-TEST_F(SurfaceReferencesTest, CheckDropTemporaryReferenceWorks) {
- // The test will deal with temporary references explicitly.
- frame_sink_manager_client_->DisableAssignTemporaryReferences();
-
- const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
- ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
-
- // An example of why this could happen is the window server doesn't know the
- // owner, maybe it has crashed and been cleanup already, and asks to drop the
- // temporary reference.
- GetSurfaceManager().DropTemporaryReference(id1);
- ASSERT_THAT(GetAllTempReferences(), IsEmpty());
-}
-
-// Checks that we handle ownership and temporary references correctly when there
-// are multiple temporary references. This tests something like the parent
-// client crashing, so it's
-TEST_F(SurfaceReferencesTest, TempReferencesWithClientCrash) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
- const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
- AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);
-
- const SurfaceId id1a = CreateSurface(kFrameSink2, 1);
-
- // Don't assign owner for temporary reference to |id1b|.
- frame_sink_manager_client_->DisableAssignTemporaryReferences();
- const SurfaceId id1b = CreateSurface(kFrameSink2, 2);
-
- ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1a, id1b));
-
- // If the parent client crashes then the FrameSink connection will be closed
- // and the FrameSinkId invalidated. The temporary reference |kFrameSink1|
- // owns to |id2a| will be removed.
- DestroyCompositorFrameSinkSupport(kFrameSink1);
- ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1b));
-
- // If the parent has crashed then the window server will have already removed
- // it from the ServerWindow hierarchy and won't have an owner for |id2b|. The
- // window server will ask to drop the reference instead.
- GetSurfaceManager().DropTemporaryReference(id1b);
- ASSERT_THAT(GetAllTempReferences(), IsEmpty());
}
// Check that old temporary references are deleted, but only for surfaces marked
// as destroyed.
TEST_F(SurfaceReferencesTest, MarkOldTemporaryReferences) {
- frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
-
constexpr base::TimeDelta kFastForwardTime = base::TimeDelta::FromSeconds(30);
// There are no temporary references so the timer should be stopped.
diff --git a/chromium/components/viz/service/frame_sinks/surface_resource_holder.cc b/chromium/components/viz/service/frame_sinks/surface_resource_holder.cc
index 6a742f4cadf..9cb78884597 100644
--- a/chromium/components/viz/service/frame_sinks/surface_resource_holder.cc
+++ b/chromium/components/viz/service/frame_sinks/surface_resource_holder.cc
@@ -34,8 +34,7 @@ void SurfaceResourceHolder::ReceiveFromChild(
void SurfaceResourceHolder::RefResources(
const std::vector<TransferableResource>& resources) {
for (const auto& resource : resources) {
- ResourceIdInfoMap::iterator count_it =
- resource_id_info_map_.find(resource.id);
+ auto count_it = resource_id_info_map_.find(resource.id);
DCHECK(count_it != resource_id_info_map_.end());
count_it->second.refs_holding_resource_alive++;
}
@@ -46,8 +45,7 @@ void SurfaceResourceHolder::UnrefResources(
std::vector<ReturnedResource> resources_available_to_return;
for (const auto& resource : resources) {
- ResourceIdInfoMap::iterator count_it =
- resource_id_info_map_.find(resource.id);
+ auto count_it = resource_id_info_map_.find(resource.id);
if (count_it == resource_id_info_map_.end())
continue;
ResourceRefs& ref = count_it->second;
diff --git a/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
index 8174fbf0b56..17140b3248b 100644
--- a/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -13,7 +13,6 @@
#include "components/viz/test/fake_external_begin_frame_source.h"
#include "components/viz/test/fake_surface_observer.h"
#include "components/viz/test/mock_compositor_frame_sink_client.h"
-#include "components/viz/test/test_frame_sink_manager_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -66,33 +65,10 @@ CompositorFrame MakeCompositorFrame(
} // namespace
-class FakeExternalBeginFrameSourceClient
- : public FakeExternalBeginFrameSource::Client {
- public:
- FakeExternalBeginFrameSourceClient() = default;
- ~FakeExternalBeginFrameSourceClient() = default;
-
- bool has_observers() const { return observer_count_ > 0; }
-
- // FakeExternalBeginFrameSource::Client implementation:
- void OnAddObserver(BeginFrameObserver* obs) override { ++observer_count_; }
-
- void OnRemoveObserver(BeginFrameObserver* obs) override {
- DCHECK_GT(observer_count_, 0);
- --observer_count_;
- }
-
- private:
- int observer_count_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(FakeExternalBeginFrameSourceClient);
-};
-
class SurfaceSynchronizationTest : public testing::Test {
public:
SurfaceSynchronizationTest()
: frame_sink_manager_(&shared_bitmap_manager_),
- frame_sink_manager_client_(&frame_sink_manager_),
surface_observer_(false) {}
~SurfaceSynchronizationTest() override {}
@@ -137,6 +113,12 @@ class SurfaceSynchronizationTest : public testing::Test {
supports_.erase(it);
}
+ void ExpireAllTemporaryReferencesAndGarbageCollect() {
+ frame_sink_manager_.surface_manager()->ExpireOldTemporaryReferences();
+ frame_sink_manager_.surface_manager()->ExpireOldTemporaryReferences();
+ frame_sink_manager_.surface_manager()->GarbageCollectSurfaces();
+ }
+
// Returns all the references where |surface_id| is the parent.
const base::flat_set<SurfaceId>& GetReferencesFrom(
const SurfaceId& surface_id) {
@@ -182,6 +164,11 @@ class SurfaceSynchronizationTest : public testing::Test {
return FrameDeadline(Now(), 4u, BeginFrameArgs::DefaultInterval(), false);
}
+ FrameDeadline MakeDeadline(uint32_t deadline_in_frames) {
+ return FrameDeadline(Now(), deadline_in_frames,
+ BeginFrameArgs::DefaultInterval(), false);
+ }
+
void SendNextBeginFrame() {
// Creep the time forward so that any BeginFrameArgs is not equal to the
// last one otherwise we violate the BeginFrameSource contract.
@@ -200,16 +187,6 @@ class SurfaceSynchronizationTest : public testing::Test {
begin_frame_source_->TestOnBeginFrame(args);
}
- void SetFrameSinkHierarchy(const FrameSinkId& parent_frame_sink_id,
- const FrameSinkId& child_frame_sink_id) {
- frame_sink_manager_client_.SetFrameSinkHierarchy(parent_frame_sink_id,
- child_frame_sink_id);
- }
-
- void DisableAssignTemporaryReferences() {
- frame_sink_manager_client_.DisableAssignTemporaryReferences();
- }
-
FakeSurfaceObserver& surface_observer() { return surface_observer_; }
// testing::Test:
@@ -218,12 +195,10 @@ class SurfaceSynchronizationTest : public testing::Test {
begin_frame_source_ =
std::make_unique<FakeExternalBeginFrameSource>(0.f, false);
- begin_frame_source_->SetClient(&begin_frame_source_client_);
now_src_ = std::make_unique<base::SimpleTestTickClock>();
frame_sink_manager_.surface_manager()->SetTickClockForTesting(
now_src_.get());
frame_sink_manager_.surface_manager()->AddObserver(&surface_observer_);
- frame_sink_manager_.SetLocalClient(&frame_sink_manager_client_);
supports_[kDisplayFrameSink] = std::make_unique<CompositorFrameSinkSupport>(
&support_client_, &frame_sink_manager_, kDisplayFrameSink, kIsRoot,
kNeedsSyncPoints);
@@ -252,8 +227,6 @@ class SurfaceSynchronizationTest : public testing::Test {
frame_sink_manager_.surface_manager()->RemoveObserver(&surface_observer_);
frame_sink_manager_.UnregisterBeginFrameSource(begin_frame_source_.get());
- frame_sink_manager_client_.Reset();
-
begin_frame_source_->SetClient(nullptr);
begin_frame_source_.reset();
@@ -278,9 +251,7 @@ class SurfaceSynchronizationTest : public testing::Test {
std::unique_ptr<base::SimpleTestTickClock> now_src_;
ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl frame_sink_manager_;
- TestFrameSinkManagerClient frame_sink_manager_client_;
FakeSurfaceObserver surface_observer_;
- FakeExternalBeginFrameSourceClient begin_frame_source_client_;
std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_;
std::unordered_map<FrameSinkId,
std::unique_ptr<CompositorFrameSinkSupport>,
@@ -664,9 +635,6 @@ TEST_F(SurfaceSynchronizationTest, NewFrameOverridesOldDependencies) {
// references. A new surface from a child will continue to exist as a temporary
// reference until the parent's frame activates.
TEST_F(SurfaceSynchronizationTest, OnlyActiveFramesAffectSurfaceReferences) {
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1);
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink2);
-
const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1);
@@ -2041,9 +2009,6 @@ TEST_F(SurfaceSynchronizationTest, FrameIndexWithPendingFrames) {
// This test verifies that a new surface with a pending CompositorFrame gets
// a temporary reference immediately, as opposed to when the surface activates.
TEST_F(SurfaceSynchronizationTest, PendingSurfaceKeptAlive) {
- SetFrameSinkHierarchy(kDisplayFrameSink, kParentFrameSink);
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1);
-
const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
@@ -2087,8 +2052,6 @@ TEST_F(SurfaceSynchronizationTest, ActiveFrameIndex) {
// synchronization to present the freshest surfaces available at aggregation
// time.
TEST_F(SurfaceSynchronizationTest, LatestInFlightSurface) {
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1);
-
const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
@@ -2137,9 +2100,6 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurface) {
EXPECT_EQ(GetSurfaceForId(child_id1),
GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2)));
- // Don't assign owner for temporary reference to |child_id2|.
- DisableAssignTemporaryReferences();
-
// Submit a child CompositorFrame to a new SurfaceId and verify that
// GetLatestInFlightSurface returns the right surface.
child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
@@ -2166,9 +2126,19 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurface) {
// Verify that there is a temporary reference for child_id3.
EXPECT_TRUE(HasTemporaryReference(child_id3));
+ // The surface corresponding to |child_id3| will not be activated until
+ // a parent embeds it because it's a child initiated synchronization.
+ EXPECT_EQ(GetSurfaceForId(child_id2),
+ GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
+
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
+ std::vector<TransferableResource>()));
+
EXPECT_EQ(GetSurfaceForId(child_id3),
GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
- EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1));
+ EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id3));
// If the primary surface is active, we return it.
EXPECT_EQ(GetSurfaceForId(child_id3),
@@ -2215,7 +2185,6 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithBogusFallback) {
// nullptr if fallback is not specified.
// TODO(akaba): this would change after https://crbug.com/861769
TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithoutFallback) {
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1);
const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
@@ -2267,8 +2236,6 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithoutFallback) {
// fallback is garbage collected, but instead returns the latest surface older
// than primary if that exists.
TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithGarbageFallback) {
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1);
-
const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
@@ -2350,8 +2317,6 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithGarbageFallback) {
// FrameSinkId or the latest in the fallback's FrameSinkId if no surface exists
// in the primary's.
TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceDifferentFrameSinkIds) {
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1);
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink2);
const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
@@ -2393,9 +2358,6 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceReturnPrimary) {
const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 3);
- // Don't automatically assign temporary references.
- DisableAssignTemporaryReferences();
-
child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
MakeDefaultCompositorFrame());
@@ -2424,7 +2386,6 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceReturnPrimary) {
// references to compute the latest surface.
TEST_F(SurfaceSynchronizationTest,
LatestInFlightSurfaceUsesPersistentReferences) {
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1);
const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
@@ -2486,9 +2447,6 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceSkipDifferentNonce) {
const SurfaceId child_id5 =
SurfaceId(kChildFrameSink1, LocalSurfaceId(5, nonce3));
- // Don't automatically assign temporary references.
- DisableAssignTemporaryReferences();
-
child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
MakeDefaultCompositorFrame());
@@ -2696,7 +2654,7 @@ TEST_F(SurfaceSynchronizationTest, SetPreviousFrameSurfaceDoesntCrash) {
// Perform a garbage collection. |parent_id| should no longer exist.
EXPECT_NE(nullptr, GetSurfaceForId(parent_id));
- frame_sink_manager().surface_manager()->GarbageCollectSurfaces();
+ ExpireAllTemporaryReferencesAndGarbageCollect();
EXPECT_EQ(nullptr, GetSurfaceForId(parent_id));
// This should not crash as the previous surface was cleared in
@@ -2709,7 +2667,6 @@ TEST_F(SurfaceSynchronizationTest, SetPreviousFrameSurfaceDoesntCrash) {
// whenever a child surface activates inside this range. This should also update
// the SurfaceReferences tree.
TEST_F(SurfaceSynchronizationTest, SurfaceReferencesChangeOnChildActivation) {
- SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1);
const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
@@ -2780,5 +2737,298 @@ TEST_F(SurfaceSynchronizationTest,
EXPECT_EQ(parent_id, parent_support().last_activated_surface_id());
}
+// If a parent CompositorFrame embeds a child Surface newer than the throttled
+// child then the throttled child surface is immediately unthrottled. This
+// unblocks the child to make progress to catch up with the parent.
+TEST_F(SurfaceSynchronizationTest,
+ ParentEmbeddingFutureChildUnblocksCurrentChildSurface) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
+ const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 1, 3);
+
+ // |child_id1| Surface should immediately activate.
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ Surface* child_surface1 = GetSurfaceForId(child_id1);
+ ASSERT_NE(nullptr, child_surface1);
+ EXPECT_FALSE(child_surface1->HasPendingFrame());
+ EXPECT_TRUE(child_surface1->HasActiveFrame());
+
+ // |child_id2| Surface should not activate because |child_id1| was never
+ // added as a dependency by a parent.
+ child_support1().SubmitCompositorFrame(
+ child_id2.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+ std::vector<TransferableResource>(),
+ MakeDeadline(1u)));
+ Surface* child_surface2 = GetSurfaceForId(child_id2);
+ ASSERT_NE(nullptr, child_surface2);
+ EXPECT_TRUE(child_surface2->HasPendingFrame());
+ EXPECT_FALSE(child_surface2->HasActiveFrame());
+ EXPECT_TRUE(child_surface2->has_deadline());
+
+ FrameDeadline deadline = MakeDefaultDeadline();
+ base::TimeTicks deadline_wall_time = deadline.ToWallTime();
+ EXPECT_EQ(deadline_wall_time, child_surface2->deadline_for_testing());
+
+ // The parent finally embeds a child surface that hasn't arrived which
+ // activates |child_id2|'s Surface in order for the child to make forward
+ // progress.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
+ std::vector<TransferableResource>(),
+ MakeDefaultDeadline()));
+
+ EXPECT_FALSE(child_surface2->HasPendingFrame());
+ EXPECT_TRUE(child_surface2->HasActiveFrame());
+}
+
+// A child surface can be blocked on its own activation dependencies and on a
+// parent embedding it as an activation dependency. Even if a child's activation
+// dependencies arrive, it may not activate until it is embedded.
+TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
+ const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ // |child_id1| Surface should immediately activate.
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ Surface* child_surface1 = GetSurfaceForId(child_id1);
+ ASSERT_NE(nullptr, child_surface1);
+ EXPECT_FALSE(child_surface1->HasPendingFrame());
+ EXPECT_TRUE(child_surface1->HasActiveFrame());
+
+ // |child_id2| Surface should not activate because |child_id1| was never
+ // added as a dependency by a parent AND it depends on |child_id3| which has
+ // not yet arrived.
+ child_support1().SubmitCompositorFrame(
+ child_id2.local_surface_id(),
+ MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
+ std::vector<TransferableResource>(),
+ MakeDefaultDeadline()));
+ Surface* child_surface2 = GetSurfaceForId(child_id2);
+ ASSERT_NE(nullptr, child_surface2);
+ EXPECT_TRUE(child_surface2->HasPendingFrame());
+ EXPECT_FALSE(child_surface2->HasActiveFrame());
+ EXPECT_THAT(child_surface2->activation_dependencies(),
+ UnorderedElementsAre(child_id3));
+
+ // |child_id2|'s dependency has arrived but |child_id2| Surface has not
+ // activated because it is still throttled by its parent. It will not
+ // activate until its parent arrives.
+ child_support2().SubmitCompositorFrame(child_id3.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty());
+ EXPECT_TRUE(child_surface2->HasPendingFrame());
+ EXPECT_FALSE(child_surface2->HasActiveFrame());
+
+ // The parent finally embeds a |child_id2| which activates |child_id2|'s
+ // Surface.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)},
+ std::vector<TransferableResource>(),
+ MakeDefaultDeadline()));
+
+ EXPECT_FALSE(child_surface2->HasPendingFrame());
+ EXPECT_TRUE(child_surface2->HasActiveFrame());
+}
+
+// Similar to the previous test, a child surface can be blocked on its own
+// activation dependencies and on a parent embedding it as an activation
+// dependency. In this case, the parent CompositorFrame arrives first, and then
+// the activation dependency.
+TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent2) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
+ const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1);
+
+ // |child_id1| Surface should immediately activate.
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ Surface* child_surface1 = GetSurfaceForId(child_id1);
+ ASSERT_NE(nullptr, child_surface1);
+ EXPECT_FALSE(child_surface1->HasPendingFrame());
+ EXPECT_TRUE(child_surface1->HasActiveFrame());
+
+ // |child_id2| Surface should not activate because |child_id1| was never
+ // added as a dependency by a parent AND it depends on |child_id3| which has
+ // not yet arrived.
+ child_support1().SubmitCompositorFrame(
+ child_id2.local_surface_id(),
+ MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
+ std::vector<TransferableResource>(),
+ MakeDefaultDeadline()));
+ Surface* child_surface2 = GetSurfaceForId(child_id2);
+ ASSERT_NE(nullptr, child_surface2);
+ EXPECT_TRUE(child_surface2->HasPendingFrame());
+ EXPECT_FALSE(child_surface2->HasActiveFrame());
+ EXPECT_THAT(child_surface2->activation_dependencies(),
+ UnorderedElementsAre(child_id3));
+ EXPECT_FALSE(child_surface2->HasDependentFrame());
+
+ // The parent embeds |child_id2| but |child_id2|'s Surface cannot activate
+ // until its dependencies arrive.
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(),
+ MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)},
+ std::vector<TransferableResource>(),
+ MakeDefaultDeadline()));
+ EXPECT_TRUE(child_surface2->HasPendingFrame());
+ EXPECT_FALSE(child_surface2->HasActiveFrame());
+ EXPECT_THAT(child_surface2->activation_dependencies(),
+ UnorderedElementsAre(child_id3));
+ EXPECT_TRUE(child_surface2->HasDependentFrame());
+
+ // |child_id2|'s dependency has arrived and |child_id2|'s Surface finally
+ // activates.
+ child_support2().SubmitCompositorFrame(child_id3.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty());
+ EXPECT_FALSE(child_surface2->HasPendingFrame());
+ EXPECT_TRUE(child_surface2->HasActiveFrame());
+}
+
+// This test verifies that if a child-initiated synchronization is blocked
+// on a parent, then the frame will still activate after a deadline passes.
+TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentActivatesAfterDeadline) {
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
+
+ // |child_id1| Surface should immediately activate.
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ Surface* child_surface1 = GetSurfaceForId(child_id1);
+ ASSERT_NE(nullptr, child_surface1);
+ EXPECT_FALSE(child_surface1->HasPendingFrame());
+ EXPECT_TRUE(child_surface1->HasActiveFrame());
+ EXPECT_FALSE(child_surface1->HasDependentFrame());
+ EXPECT_FALSE(child_surface1->has_deadline());
+
+ // |child_id2| Surface should not activate because |child_id1| was never
+ // added as a dependency by a parent.
+ child_support1().SubmitCompositorFrame(
+ child_id2.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+ std::vector<TransferableResource>(),
+ MakeDefaultDeadline()));
+ Surface* child_surface2 = GetSurfaceForId(child_id2);
+ ASSERT_NE(nullptr, child_surface2);
+ EXPECT_TRUE(child_surface2->HasPendingFrame());
+ EXPECT_FALSE(child_surface2->HasActiveFrame());
+ EXPECT_FALSE(child_surface2->HasDependentFrame());
+ EXPECT_TRUE(child_surface2->has_deadline());
+
+ for (int i = 0; i < 3; ++i) {
+ SendNextBeginFrame();
+ // There is still a looming deadline! Eeek!
+ EXPECT_TRUE(child_surface2->HasPendingFrame());
+ EXPECT_FALSE(child_surface2->HasActiveFrame());
+ EXPECT_FALSE(child_surface2->HasDependentFrame());
+ EXPECT_TRUE(child_surface2->has_deadline());
+ }
+
+ SendNextBeginFrame();
+
+ EXPECT_FALSE(child_surface2->HasPendingFrame());
+ EXPECT_TRUE(child_surface2->HasActiveFrame());
+ // We pretend this is true so that subsequent CompositorFrames to the same
+ // surface do not block on the parent again.
+ EXPECT_TRUE(child_surface2->HasDependentFrame());
+ EXPECT_FALSE(child_surface2->has_deadline());
+}
+
+// This test verifies that if a child-initiated synchronization is initiated
+// with a deadline in the past, then the frame will immediately activate and
+// be marked as having a dependent frame so that it does not block again in
+// the future.
+TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentDeadlineInPast) {
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
+
+ // |child_id1| Surface should immediately activate.
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ Surface* child_surface1 = GetSurfaceForId(child_id1);
+ ASSERT_NE(nullptr, child_surface1);
+ EXPECT_FALSE(child_surface1->HasPendingFrame());
+ EXPECT_TRUE(child_surface1->HasActiveFrame());
+ EXPECT_FALSE(child_surface1->HasDependentFrame());
+ EXPECT_FALSE(child_surface1->has_deadline());
+
+ // Pick a deadline in the near future.
+ FrameDeadline deadline = MakeDeadline(1u);
+
+ // Advance time beyond the |deadline| above.
+ SendLateBeginFrame();
+
+ // |child_id2| Surface should activate because it was submitted with a
+ // deadline in the past.
+ child_support1().SubmitCompositorFrame(
+ child_id2.local_surface_id(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+ std::vector<TransferableResource>(), deadline));
+ Surface* child_surface2 = GetSurfaceForId(child_id2);
+ ASSERT_NE(nullptr, child_surface2);
+ EXPECT_FALSE(child_surface2->HasPendingFrame());
+ EXPECT_TRUE(child_surface2->HasActiveFrame());
+ EXPECT_FALSE(child_surface2->has_deadline());
+
+ EXPECT_TRUE(child_surface2->HasDependentFrame());
+}
+
+// A child may submit CompositorFrame corresponding to a child-initiated
+// synchronization event followed by a CompositorFrame corresponding to a
+// parent-initiated synchronization event.
+TEST_F(SurfaceSynchronizationTest,
+ ParentInitiatedAfterChildInitiatedSynchronization) {
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
+ // Child-initiated synchronization event:
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
+ // Parent-initiated synchronizaton event:
+ const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 2);
+
+ // |child_id1| Surface should immediately activate.
+ child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ Surface* child_surface1 = GetSurfaceForId(child_id1);
+ ASSERT_NE(nullptr, child_surface1);
+ EXPECT_FALSE(child_surface1->HasPendingFrame());
+ EXPECT_TRUE(child_surface1->HasActiveFrame());
+
+ // |child_id2| Surface should not activate because |child_id1| was never
+ // added as a dependency by a parent.
+ child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ Surface* child_surface2 = GetSurfaceForId(child_id2);
+ ASSERT_NE(nullptr, child_surface2);
+ EXPECT_TRUE(child_surface2->HasPendingFrame());
+ EXPECT_FALSE(child_surface2->HasActiveFrame());
+ EXPECT_TRUE(child_surface2->has_deadline());
+
+ // |child_id3| Surface should activate immediately because it corresponds to a
+ // parent-initiated synchronization event. |child_surface3| activating
+ // triggers all predecessors to activate as well if they're blocked on a
+ // parent.
+ child_support1().SubmitCompositorFrame(child_id3.local_surface_id(),
+ MakeDefaultCompositorFrame());
+ Surface* child_surface3 = GetSurfaceForId(child_id3);
+ ASSERT_NE(nullptr, child_surface3);
+ EXPECT_FALSE(child_surface3->HasPendingFrame());
+ EXPECT_TRUE(child_surface3->HasActiveFrame());
+ EXPECT_FALSE(IsMarkedForDestruction(child_id3));
+
+ // |child_surface2| should have activated now (and should be a candidate for
+ // garbage collection).
+ EXPECT_FALSE(child_surface2->HasPendingFrame());
+ EXPECT_TRUE(child_surface2->HasActiveFrame());
+ EXPECT_TRUE(IsMarkedForDestruction(child_id2));
+}
+
} // namespace test
} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index 2fa99723717..082034fbb19 100644
--- a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -47,7 +47,7 @@ constexpr media::VideoPixelFormat
FrameSinkVideoCapturerImpl::kDefaultPixelFormat;
// static
-constexpr media::ColorSpace FrameSinkVideoCapturerImpl::kDefaultColorSpace;
+constexpr gfx::ColorSpace FrameSinkVideoCapturerImpl::kDefaultColorSpace;
FrameSinkVideoCapturerImpl::FrameSinkVideoCapturerImpl(
FrameSinkVideoCapturerManager* frame_sink_manager,
@@ -109,7 +109,7 @@ void FrameSinkVideoCapturerImpl::OnTargetWillGoAway() {
}
void FrameSinkVideoCapturerImpl::SetFormat(media::VideoPixelFormat format,
- media::ColorSpace color_space) {
+ const gfx::ColorSpace& color_space) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool format_changed = false;
@@ -122,16 +122,17 @@ void FrameSinkVideoCapturerImpl::SetFormat(media::VideoPixelFormat format,
pixel_format_ = format;
}
- if (color_space == media::COLOR_SPACE_UNSPECIFIED) {
- color_space = kDefaultColorSpace;
+ gfx::ColorSpace color_space_copy = color_space;
+ if (!color_space_copy.IsValid()) {
+ color_space_copy = kDefaultColorSpace;
}
// TODO(crbug/758057): Plumb output color space through to the
// CopyOutputRequests.
- if (color_space != media::COLOR_SPACE_HD_REC709) {
+ if (color_space_copy != gfx::ColorSpace::CreateREC709()) {
LOG(DFATAL) << "Unsupported color space: Only BT.709 is supported.";
} else {
format_changed |= (color_space_ != color_space);
- color_space_ = color_space;
+ color_space_ = color_space_copy;
}
if (format_changed) {
@@ -478,14 +479,14 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
base::saturated_cast<int>(utilization * 100.0f + 0.5f));
}
+ // See TODO in SetFormat(). For now, always assume Rec. 709.
+ frame->set_color_space(gfx::ColorSpace::CreateREC709());
+
// At this point, the capture is going to proceed. Populate the VideoFrame's
// metadata, and notify the oracle.
VideoFrameMetadata* const metadata = frame->metadata();
metadata->SetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME,
clock_->NowTicks());
- // See TODO in SetFormat(). For now, always assume Rec. 709.
- metadata->SetInteger(VideoFrameMetadata::COLOR_SPACE,
- media::COLOR_SPACE_HD_REC709);
metadata->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION,
oracle_.estimated_frame_duration());
metadata->SetDouble(VideoFrameMetadata::FRAME_RATE,
@@ -499,12 +500,10 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
frame_metadata.root_scroll_offset.x());
metadata->SetDouble(VideoFrameMetadata::ROOT_SCROLL_OFFSET_Y,
frame_metadata.root_scroll_offset.y());
-#if defined(OS_ANDROID)
metadata->SetDouble(VideoFrameMetadata::TOP_CONTROLS_HEIGHT,
frame_metadata.top_controls_height);
metadata->SetDouble(VideoFrameMetadata::TOP_CONTROLS_SHOWN_RATIO,
frame_metadata.top_controls_shown_ratio);
-#endif // defined(OS_ANDROID)
oracle_.RecordCapture(utilization);
const int64_t frame_number = next_capture_frame_number_++;
@@ -715,6 +714,7 @@ void FrameSinkVideoCapturerImpl::MaybeDeliverFrame(
info->pixel_format = frame->format();
info->coded_size = frame->coded_size();
info->visible_rect = frame->visible_rect();
+ info->color_space = frame->ColorSpace();
const gfx::Rect update_rect = frame->visible_rect();
// Create an InFlightFrameDelivery for this frame, owned by its mojo binding.
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
index ebbf0e3be6c..69f25dab2aa 100644
--- a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
+++ b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
@@ -97,7 +97,7 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final
// mojom::FrameSinkVideoCapturer implementation:
void SetFormat(media::VideoPixelFormat format,
- media::ColorSpace color_space) final;
+ const gfx::ColorSpace& color_space) final;
void SetMinCapturePeriod(base::TimeDelta min_capture_period) final;
void SetMinSizeChangePeriod(base::TimeDelta min_period) final;
void SetResolutionConstraints(const gfx::Size& min_size,
@@ -114,8 +114,8 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final
// Default configuration.
static constexpr media::VideoPixelFormat kDefaultPixelFormat =
media::PIXEL_FORMAT_I420;
- static constexpr media::ColorSpace kDefaultColorSpace =
- media::COLOR_SPACE_HD_REC709;
+ static constexpr gfx::ColorSpace kDefaultColorSpace =
+ gfx::ColorSpace::CreateREC709();
// The maximum number of frames in-flight in the capture pipeline, reflecting
// the storage capacity dedicated for this purpose. Example numbers, for a
@@ -231,7 +231,7 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final
// Current image format.
media::VideoPixelFormat pixel_format_ = kDefaultPixelFormat;
- media::ColorSpace color_space_ = kDefaultColorSpace;
+ gfx::ColorSpace color_space_ = kDefaultColorSpace;
// Models current content change/draw behavior and proposes when to capture
// frames, and at what size and frame rate.
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
index d11645261b9..64ab61ed712 100644
--- a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -160,6 +160,8 @@ class MockConsumer : public mojom::FrameSinkVideoConsumer {
mapping.size(), info->timestamp);
ASSERT_TRUE(frame);
frame->metadata()->MergeInternalValuesFrom(info->metadata);
+ if (info->color_space.has_value())
+ frame->set_color_space(info->color_space.value());
frame->AddDestructionObserver(base::BindOnce(
[](base::ReadOnlySharedMemoryMapping mapping) {}, std::move(mapping)));
OnFrameCapturedMock(frame, update_rect, callbacks.get());
@@ -345,9 +347,10 @@ class FrameSinkVideoCapturerTest : public testing::Test {
capturer_.pixel_format_);
ASSERT_EQ(FrameSinkVideoCapturerImpl::kDefaultColorSpace,
capturer_.color_space_);
- capturer_.SetFormat(media::PIXEL_FORMAT_I420, media::COLOR_SPACE_HD_REC709);
+ capturer_.SetFormat(media::PIXEL_FORMAT_I420,
+ gfx::ColorSpace::CreateREC709());
ASSERT_EQ(media::PIXEL_FORMAT_I420, capturer_.pixel_format_);
- ASSERT_EQ(media::COLOR_SPACE_HD_REC709, capturer_.color_space_);
+ ASSERT_EQ(gfx::ColorSpace::CreateREC709(), capturer_.color_space_);
// Set min capture period as small as possible so that the
// media::VideoCapturerOracle used by the capturer will want to capture
@@ -560,10 +563,7 @@ TEST_F(FrameSinkVideoCapturerTest, CapturesCompositedFrames) {
EXPECT_TRUE(metadata->GetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME,
&capture_end_time));
EXPECT_EQ(expected_capture_end_time, capture_end_time);
- int color_space = media::COLOR_SPACE_UNSPECIFIED;
- EXPECT_TRUE(
- metadata->GetInteger(VideoFrameMetadata::COLOR_SPACE, &color_space));
- EXPECT_EQ(media::COLOR_SPACE_HD_REC709, color_space);
+ EXPECT_EQ(gfx::ColorSpace::CreateREC709(), frame->ColorSpace());
EXPECT_TRUE(metadata->HasKey(VideoFrameMetadata::FRAME_DURATION));
// FRAME_DURATION is an estimate computed by the VideoCaptureOracle, so it
// its exact value is not being checked here.
diff --git a/chromium/components/viz/service/gl/gpu_service_impl.cc b/chromium/components/viz/service/gl/gpu_service_impl.cc
index 67d04badb31..7a7d21a758c 100644
--- a/chromium/components/viz/service/gl/gpu_service_impl.cc
+++ b/chromium/components/viz/service/gl/gpu_service_impl.cc
@@ -12,7 +12,6 @@
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/memory/shared_memory.h"
-#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -159,6 +158,8 @@ GpuServiceImpl::GpuServiceImpl(
bindings_(std::make_unique<mojo::BindingSet<mojom::GpuService>>()),
weak_ptr_factory_(this) {
DCHECK(!io_runner_->BelongsToCurrentThread());
+ DCHECK(exit_callback_);
+
#if defined(OS_CHROMEOS)
protected_buffer_manager_ = new arc::ProtectedBufferManager();
#endif // defined(OS_CHROMEOS)
@@ -518,10 +519,9 @@ void GpuServiceImpl::GetVideoMemoryUsageStats(
std::move(callback).Run(video_memory_usage_stats);
}
-// Currently, this function only supports the Windows platform.
+#if defined(OS_WIN)
void GpuServiceImpl::GetGpuSupportedRuntimeVersion(
GetGpuSupportedRuntimeVersionCallback callback) {
-#if defined(OS_WIN)
if (io_runner_->BelongsToCurrentThread()) {
auto wrap_callback = WrapCallback(io_runner_, std::move(callback));
main_runner_->PostTask(
@@ -537,14 +537,13 @@ void GpuServiceImpl::GetGpuSupportedRuntimeVersion(
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
DCHECK(command_line->HasSwitch("disable-gpu-sandbox") || in_host_process());
- gpu::RecordGpuSupportedRuntimeVersionHistograms(&gpu_info_);
- std::move(callback).Run(gpu_info_);
+ gpu::RecordGpuSupportedRuntimeVersionHistograms(
+ &gpu_info_.dx12_vulkan_version_info);
+ std::move(callback).Run(gpu_info_.dx12_vulkan_version_info);
if (!in_host_process()) {
- // The unsandboxed GPU process fulfilled its duty. Rest
- // in peace.
- base::RunLoop().QuitCurrentWhenIdleDeprecated();
+ // The unsandboxed GPU process fulfilled its duty. Bye bye.
+ ExitProcess();
}
-#endif
}
void GpuServiceImpl::RequestCompleteGpuInfo(
@@ -563,17 +562,15 @@ void GpuServiceImpl::RequestCompleteGpuInfo(
base::BindOnce(
[](GpuServiceImpl* gpu_service,
RequestCompleteGpuInfoCallback callback) {
- std::move(callback).Run(gpu_service->gpu_info_);
-#if defined(OS_WIN)
+ std::move(callback).Run(gpu_service->gpu_info_.dx_diagnostics);
if (!gpu_service->in_host_process()) {
- // The unsandboxed GPU process fulfilled its duty. Rest
- // in peace.
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
+ // The unsandboxed GPU process fulfilled its duty. Bye bye.
+ gpu_service->ExitProcess();
}
-#endif
},
this, std::move(callback))));
}
+#endif
void GpuServiceImpl::RequestHDRStatus(RequestHDRStatusCallback callback) {
DCHECK(io_runner_->BelongsToCurrentThread());
@@ -593,26 +590,7 @@ void GpuServiceImpl::RequestHDRStatusOnMainThread(
base::BindOnce(std::move(callback), hdr_enabled));
}
-#if defined(OS_MACOSX)
-void GpuServiceImpl::UpdateGpuInfoPlatform(
- base::OnceClosure on_gpu_info_updated) {
- DCHECK(main_runner_->BelongsToCurrentThread());
- // gpu::CollectContextGraphicsInfo() is already called during gpu process
- // initialization (see GpuInit::InitializeAndStartSandbox()) on non-mac
- // platforms, and during in-browser gpu thread initialization on all platforms
- // (See InProcessGpuThread::Init()).
- if (in_host_process())
- return;
-
- bool success = gpu::CollectContextGraphicsInfo(&gpu_info_, gpu_preferences_);
- if (!success) {
- LOG(ERROR) << "gpu::CollectGraphicsInfo failed.";
- // TODO(piman): can we signal overall failure?
- }
- gpu::SetKeysForCrashLogging(gpu_info_);
- std::move(on_gpu_info_updated).Run();
-}
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
void GpuServiceImpl::UpdateGpuInfoPlatform(
base::OnceClosure on_gpu_info_updated) {
DCHECK(main_runner_->BelongsToCurrentThread());
@@ -684,11 +662,8 @@ void GpuServiceImpl::StoreShaderToDisk(int client_id,
}
void GpuServiceImpl::ExitProcess() {
- if (is_exiting_)
- return;
-
- is_exiting_ = true;
- std::move(exit_callback_).Run();
+ if (exit_callback_)
+ std::move(exit_callback_).Run();
}
#if defined(OS_WIN)
@@ -735,8 +710,7 @@ void GpuServiceImpl::EstablishGpuChannel(int32_t client_id,
client_id, client_tracing_id, is_gpu_host, cache_shaders_on_disk);
mojo::MessagePipe pipe;
- gpu_channel->Init(std::make_unique<gpu::SyncChannelFilteredSender>(
- pipe.handle0.release(), gpu_channel, io_runner_, shutdown_event_));
+ gpu_channel->Init(pipe.handle0.release(), shutdown_event_);
media_gpu_channel_manager_->AddChannel(client_id);
diff --git a/chromium/components/viz/service/gl/gpu_service_impl.h b/chromium/components/viz/service/gl/gpu_service_impl.h
index b99f0109bfb..9fc34bf3e1a 100644
--- a/chromium/components/viz/service/gl/gpu_service_impl.h
+++ b/chromium/components/viz/service/gl/gpu_service_impl.h
@@ -216,9 +216,11 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
const gpu::SyncToken& sync_token) override;
void GetVideoMemoryUsageStats(
GetVideoMemoryUsageStatsCallback callback) override;
+#if defined(OS_WIN)
void RequestCompleteGpuInfo(RequestCompleteGpuInfoCallback callback) override;
void GetGpuSupportedRuntimeVersion(
GetGpuSupportedRuntimeVersionCallback callback) override;
+#endif
void RequestHDRStatus(RequestHDRStatusCallback callback) override;
void LoadedShader(int32_t client_id,
const std::string& key,
@@ -301,7 +303,6 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
// Callback that safely exits GPU process.
base::OnceClosure exit_callback_;
- bool is_exiting_ = false;
base::Time start_time_;
diff --git a/chromium/components/viz/service/hit_test/hit_test_aggregator.cc b/chromium/components/viz/service/hit_test/hit_test_aggregator.cc
index 5b9f8201001..2f0d1f022d4 100644
--- a/chromium/components/viz/service/hit_test/hit_test_aggregator.cc
+++ b/chromium/components/viz/service/hit_test/hit_test_aggregator.cc
@@ -6,8 +6,10 @@
#include "base/metrics/histogram_macros.h"
#include "base/timer/elapsed_timer.h"
+#include "base/trace_event/trace_event.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "components/viz/service/hit_test/hit_test_aggregator_delegate.h"
+#include "components/viz/service/surfaces/latest_local_surface_id_lookup_delegate.h"
#include "third_party/skia/include/core/SkMatrix44.h"
namespace viz {
@@ -55,6 +57,23 @@ void HitTestAggregator::SendHitTestData() {
hit_test_data_);
}
+base::Optional<int64_t> HitTestAggregator::GetTraceIdIfUpdated(
+ const SurfaceId& surface_id) {
+ bool enabled;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(
+ TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow"), &enabled);
+ if (!enabled)
+ return base::nullopt;
+
+ int32_t active_frame_index =
+ hit_test_manager_->GetActiveFrameIndex(surface_id);
+ int32_t& frame_index = last_active_frame_index_[surface_id];
+ if (frame_index == active_frame_index)
+ return base::nullopt;
+ frame_index = active_frame_index;
+ return ~hit_test_manager_->GetTraceId(surface_id);
+}
+
void HitTestAggregator::AppendRoot(const SurfaceId& surface_id) {
const HitTestRegionList* hit_test_region_list =
hit_test_manager_->GetActiveHitTestRegionList(
@@ -62,6 +81,13 @@ void HitTestAggregator::AppendRoot(const SurfaceId& surface_id) {
if (!hit_test_region_list)
return;
+ base::Optional<int64_t> trace_id = GetTraceIdIfUpdated(surface_id);
+ TRACE_EVENT_WITH_FLOW1(
+ TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow"), "Event.Pipeline",
+ TRACE_ID_GLOBAL(trace_id.value_or(-1)),
+ trace_id ? TRACE_EVENT_FLAG_FLOW_IN : TRACE_EVENT_FLAG_NONE, "step",
+ "AggregateHitTestData(Root)");
+
referenced_child_regions_.insert(surface_id.frame_sink_id());
size_t region_index = 1;
@@ -98,30 +124,51 @@ size_t HitTestAggregator::AppendRegion(size_t region_index,
if (referenced_child_regions_.count(region.frame_sink_id))
return parent_index;
+ referenced_child_regions_.insert(region.frame_sink_id);
+
const HitTestRegionList* hit_test_region_list =
hit_test_manager_->GetActiveHitTestRegionList(
local_surface_id_lookup_delegate_, region.frame_sink_id);
if (!hit_test_region_list) {
// Hit-test data not found with this FrameSinkId. This means that it
// failed to find a surface corresponding to this FrameSinkId at surface
- // aggregation time.
- return parent_index;
- }
-
- referenced_child_regions_.insert(region.frame_sink_id);
-
- // Rather than add a node in the tree for this hit_test_region_list
- // element we can simplify the tree by merging the flags and transform
- // into the kHitTestChildSurface element.
- if (!hit_test_region_list->transform.IsIdentity())
- transform.PreconcatTransform(hit_test_region_list->transform);
-
- flags |= hit_test_region_list->flags;
-
- for (const auto& child_region : hit_test_region_list->regions) {
- region_index = AppendRegion(region_index, child_region);
- if (region_index >= hit_test_data_capacity_ - 1)
- break;
+ // aggregation time. This might be because the embedded client hasn't
+ // submitted its own hit-test data yet, we are going to do async
+ // targeting for this embedded client.
+ flags |= (HitTestRegionFlags::kHitTestAsk |
+ HitTestRegionFlags::kHitTestNotActive);
+ } else {
+ // Rather than add a node in the tree for this hit_test_region_list
+ // element we can simplify the tree by merging the flags and transform
+ // into the kHitTestChildSurface element.
+ if (!hit_test_region_list->transform.IsIdentity())
+ transform.PreconcatTransform(hit_test_region_list->transform);
+
+ flags |= hit_test_region_list->flags;
+
+ bool enabled;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(
+ TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow"), &enabled);
+ if (enabled) {
+ // Preconditions are already verified in GetActiveHitTestRegionList.
+ LocalSurfaceId local_surface_id =
+ local_surface_id_lookup_delegate_->GetSurfaceAtAggregation(
+ region.frame_sink_id);
+ SurfaceId surface_id(region.frame_sink_id, local_surface_id);
+
+ base::Optional<int64_t> trace_id = GetTraceIdIfUpdated(surface_id);
+ TRACE_EVENT_WITH_FLOW1(
+ TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow"), "Event.Pipeline",
+ TRACE_ID_GLOBAL(trace_id.value_or(-1)),
+ trace_id ? TRACE_EVENT_FLAG_FLOW_IN : TRACE_EVENT_FLAG_NONE, "step",
+ "AggregateHitTestData");
+ }
+
+ for (const auto& child_region : hit_test_region_list->regions) {
+ region_index = AppendRegion(region_index, child_region);
+ if (region_index >= hit_test_data_capacity_ - 1)
+ break;
+ }
}
}
DCHECK_GE(region_index - parent_index - 1, 0u);
diff --git a/chromium/components/viz/service/hit_test/hit_test_aggregator.h b/chromium/components/viz/service/hit_test/hit_test_aggregator.h
index b4999a0ddcf..6b4ee2dd386 100644
--- a/chromium/components/viz/service/hit_test/hit_test_aggregator.h
+++ b/chromium/components/viz/service/hit_test/hit_test_aggregator.h
@@ -12,6 +12,10 @@
#include "components/viz/service/viz_service_export.h"
namespace viz {
+
+namespace test {
+class TestHitTestAggregator;
+} // namespace test
class HitTestAggregatorDelegate;
struct HitTestRegion;
@@ -38,7 +42,7 @@ class VIZ_SERVICE_EXPORT HitTestAggregator {
void Aggregate(const SurfaceId& display_surface_id);
private:
- friend class TestHitTestAggregator;
+ friend class test::TestHitTestAggregator;
void SendHitTestData();
@@ -58,6 +62,12 @@ class VIZ_SERVICE_EXPORT HitTestAggregator {
const gfx::Transform& transform,
int32_t child_count);
+ // Returns the |trace_id| of the |begin_frame_ack| in the active frame for
+ // the given surface_id if it is different than when it was last queried.
+ // This is used in order to ensure that the flow between receiving hit-test
+ // data and aggregating is included only once per submission.
+ base::Optional<int64_t> GetTraceIdIfUpdated(const SurfaceId& surface_id);
+
const HitTestManager* const hit_test_manager_;
HitTestAggregatorDelegate* const delegate_;
@@ -82,6 +92,8 @@ class VIZ_SERVICE_EXPORT HitTestAggregator {
// to detect cycles.
base::flat_set<FrameSinkId> referenced_child_regions_;
+ base::flat_map<SurfaceId, int32_t> last_active_frame_index_;
+
// Handles the case when this object is deleted after
// the PostTaskAggregation call is scheduled but before invocation.
base::WeakPtrFactory<HitTestAggregator> weak_ptr_factory_;
diff --git a/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc b/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
index 7e8a966240b..e932f9fa4e7 100644
--- a/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
+++ b/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
@@ -21,6 +21,7 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace viz {
+namespace test {
namespace {
@@ -33,6 +34,8 @@ SurfaceId MakeSurfaceId(uint32_t frame_sink_id_client_id) {
LocalSurfaceId(1, base::UnguessableToken::Deserialize(0, 1u)));
}
+} // namespace
+
// TODO(riajiang): TestHostFrameSinkManager should be based on
// mojom::FrameSinkManagerClient instead.
class TestHostFrameSinkManager : public HostFrameSinkManager {
@@ -84,8 +87,6 @@ class TestFrameSinkManagerImpl : public FrameSinkManagerImpl {
DISALLOW_COPY_AND_ASSIGN(TestFrameSinkManagerImpl);
};
-} // namespace
-
class TestHitTestAggregator final : public HitTestAggregator {
public:
TestHitTestAggregator(
@@ -133,6 +134,12 @@ class HitTestAggregatorTest : public testing::Test {
host_frame_sink_manager_.reset();
}
+ void ExpireAllTemporaryReferencesAndGarbageCollect() {
+ frame_sink_manager_->surface_manager()->ExpireOldTemporaryReferences();
+ frame_sink_manager_->surface_manager()->ExpireOldTemporaryReferences();
+ frame_sink_manager_->surface_manager()->GarbageCollectSurfaces();
+ }
+
// Creates a hit test data element with 8 children recursively to
// the specified depth. SurfaceIds are generated in sequential order and
// the method returns the next unused id.
@@ -866,8 +873,7 @@ TEST_F(HitTestAggregatorTest, MissingChildFrame) {
aggregator->Aggregate(e_surface_id);
- // Child c didn't submit any CompositorFrame. Events should go to parent.
- EXPECT_EQ(aggregator->GetRegionCount(), 2);
+ EXPECT_EQ(aggregator->GetRegionCount(), 3);
EXPECT_EQ(host_buffer_frame_sink_id(), kDisplayFrameSink);
@@ -875,9 +881,19 @@ TEST_F(HitTestAggregatorTest, MissingChildFrame) {
EXPECT_EQ(region.flags, HitTestRegionFlags::kHitTestMine);
EXPECT_EQ(region.frame_sink_id, e_surface_id.frame_sink_id());
EXPECT_EQ(region.rect, gfx::Rect(0, 0, 1024, 768));
- EXPECT_EQ(region.child_count, 1);
+ EXPECT_EQ(region.child_count, 2);
+ // |c_hit_test_region_list| was not submitted on time, so we should do
+ // async targeting with |e_hit_test_region_c|.
region = host_regions()[1];
+ EXPECT_EQ(region.flags, HitTestRegionFlags::kHitTestChildSurface |
+ HitTestRegionFlags::kHitTestAsk |
+ HitTestRegionFlags::kHitTestNotActive);
+ EXPECT_EQ(region.frame_sink_id, c_surface_id.frame_sink_id());
+ EXPECT_EQ(region.rect, gfx::Rect(100, 100, 200, 500));
+ EXPECT_EQ(region.child_count, 0);
+
+ region = host_regions()[2];
EXPECT_EQ(region.flags, HitTestRegionFlags::kHitTestMine);
EXPECT_EQ(region.frame_sink_id, e_surface_id.frame_sink_id());
EXPECT_EQ(region.rect, gfx::Rect(200, 200, 300, 200));
@@ -984,18 +1000,144 @@ TEST_F(HitTestAggregatorTest, DiscardedSurfaces) {
// Discard Surface and ensure active count goes down.
support2->EvictLastActivatedSurface();
- surface_manager()->GarbageCollectSurfaces();
+ ExpireAllTemporaryReferencesAndGarbageCollect();
EXPECT_TRUE(hit_test_manager()->GetActiveHitTestRegionList(
local_surface_id_lookup_delegate(), e_surface_id.frame_sink_id()));
EXPECT_FALSE(hit_test_manager()->GetActiveHitTestRegionList(
local_surface_id_lookup_delegate(), c_surface_id.frame_sink_id()));
support()->EvictLastActivatedSurface();
- surface_manager()->GarbageCollectSurfaces();
+ ExpireAllTemporaryReferencesAndGarbageCollect();
EXPECT_FALSE(hit_test_manager()->GetActiveHitTestRegionList(
local_surface_id_lookup_delegate(), e_surface_id.frame_sink_id()));
EXPECT_FALSE(hit_test_manager()->GetActiveHitTestRegionList(
local_surface_id_lookup_delegate(), c_surface_id.frame_sink_id()));
}
+// Region c1 is transparent and on top of e, c2 is a child of e, d1 is a
+// child of c1.
+//
+// +e/c1----------+
+// | | Point maps to
+// | +c2-+ | ----- -------
+// | | 1 | | 1 c2
+// | +d1--------|
+// | | |
+// | | |
+// +--------------+
+//
+
+TEST_F(HitTestAggregatorTest, TransparentOverlayRegions) {
+ TestHitTestAggregator* aggregator = hit_test_aggregator();
+ EXPECT_EQ(aggregator->GetRegionCount(), 0);
+
+ SurfaceId e_surface_id = MakeSurfaceId(kDisplayClientId);
+ SurfaceId c1_surface_id = MakeSurfaceId(kDisplayClientId + 1);
+ SurfaceId c2_surface_id = MakeSurfaceId(kDisplayClientId + 2);
+ SurfaceId d1_surface_id = MakeSurfaceId(kDisplayClientId + 3);
+
+ HitTestRegionList e_hit_test_region_list;
+ e_hit_test_region_list.flags = HitTestRegionFlags::kHitTestMine;
+ e_hit_test_region_list.bounds.SetRect(0, 0, 1024, 768);
+
+ HitTestRegion e_hit_test_region_c1;
+ e_hit_test_region_c1.flags = HitTestRegionFlags::kHitTestChildSurface;
+ e_hit_test_region_c1.frame_sink_id = c1_surface_id.frame_sink_id();
+ e_hit_test_region_c1.rect.SetRect(0, 0, 1024, 768);
+
+ HitTestRegion e_hit_test_region_c2;
+ e_hit_test_region_c2.flags = HitTestRegionFlags::kHitTestChildSurface;
+ e_hit_test_region_c2.frame_sink_id = c2_surface_id.frame_sink_id();
+ e_hit_test_region_c2.rect.SetRect(0, 0, 200, 100);
+ e_hit_test_region_c2.transform.Translate(200, 100);
+
+ e_hit_test_region_list.regions.push_back(std::move(e_hit_test_region_c1));
+ e_hit_test_region_list.regions.push_back(std::move(e_hit_test_region_c2));
+
+ HitTestRegionList c1_hit_test_region_list;
+ c1_hit_test_region_list.flags = HitTestRegionFlags::kHitTestIgnore;
+ c1_hit_test_region_list.bounds.SetRect(0, 0, 1024, 768);
+
+ HitTestRegion c1_hit_test_region_d1;
+ c1_hit_test_region_d1.flags = HitTestRegionFlags::kHitTestChildSurface;
+ c1_hit_test_region_d1.frame_sink_id = d1_surface_id.frame_sink_id();
+ c1_hit_test_region_d1.rect.SetRect(0, 100, 800, 600);
+ c1_hit_test_region_d1.transform.Translate(200, 100);
+
+ c1_hit_test_region_list.regions.push_back(std::move(c1_hit_test_region_d1));
+
+ HitTestRegionList d1_hit_test_region_list;
+ d1_hit_test_region_list.flags = HitTestRegionFlags::kHitTestMine;
+ d1_hit_test_region_list.bounds.SetRect(0, 100, 800, 600);
+
+ HitTestRegionList c2_hit_test_region_list;
+ c2_hit_test_region_list.flags = HitTestRegionFlags::kHitTestMine;
+ c2_hit_test_region_list.bounds.SetRect(0, 0, 200, 100);
+
+ // Submit in unexpected order.
+
+ auto support2 = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, frame_sink_manager(), c1_surface_id.frame_sink_id(),
+ false /* is_root */, false /* needs_sync_points */);
+ support2->SubmitCompositorFrame(c1_surface_id.local_surface_id(),
+ MakeDefaultCompositorFrame(),
+ std::move(c1_hit_test_region_list));
+ local_surface_id_lookup_delegate()->SetSurfaceIdMap(c1_surface_id);
+ auto support3 = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, frame_sink_manager(), c2_surface_id.frame_sink_id(),
+ false /* is_root */, false /* needs_sync_points */);
+ support3->SubmitCompositorFrame(c2_surface_id.local_surface_id(),
+ MakeDefaultCompositorFrame(),
+ std::move(c2_hit_test_region_list));
+ local_surface_id_lookup_delegate()->SetSurfaceIdMap(c2_surface_id);
+ auto support4 = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, frame_sink_manager(), d1_surface_id.frame_sink_id(),
+ false /* is_root */, false /* needs_sync_points */);
+ support4->SubmitCompositorFrame(d1_surface_id.local_surface_id(),
+ MakeDefaultCompositorFrame(),
+ std::move(d1_hit_test_region_list));
+ local_surface_id_lookup_delegate()->SetSurfaceIdMap(d1_surface_id);
+ support()->SubmitCompositorFrame(e_surface_id.local_surface_id(),
+ MakeDefaultCompositorFrame(),
+ std::move(e_hit_test_region_list));
+ local_surface_id_lookup_delegate()->SetSurfaceIdMap(e_surface_id);
+
+ aggregator->Aggregate(e_surface_id);
+
+ EXPECT_EQ(aggregator->GetRegionCount(), 4);
+
+ EXPECT_EQ(host_buffer_frame_sink_id(), kDisplayFrameSink);
+
+ AggregatedHitTestRegion region = host_regions()[0];
+ EXPECT_EQ(region.flags, HitTestRegionFlags::kHitTestMine);
+ EXPECT_EQ(region.frame_sink_id, e_surface_id.frame_sink_id());
+ EXPECT_EQ(region.rect, gfx::Rect(0, 0, 1024, 768));
+ EXPECT_EQ(region.child_count, 3);
+
+ region = host_regions()[1];
+ EXPECT_EQ(HitTestRegionFlags::kHitTestChildSurface |
+ HitTestRegionFlags::kHitTestIgnore,
+ region.flags);
+ EXPECT_EQ(region.frame_sink_id, c1_surface_id.frame_sink_id());
+ EXPECT_EQ(region.rect, gfx::Rect(0, 0, 1024, 768));
+ EXPECT_EQ(region.child_count, 1);
+
+ region = host_regions()[2];
+ EXPECT_EQ(HitTestRegionFlags::kHitTestChildSurface |
+ HitTestRegionFlags::kHitTestMine,
+ region.flags);
+ EXPECT_EQ(region.frame_sink_id, d1_surface_id.frame_sink_id());
+ EXPECT_EQ(region.rect, gfx::Rect(0, 100, 800, 600));
+ EXPECT_EQ(region.child_count, 0);
+
+ region = host_regions()[3];
+ EXPECT_EQ(HitTestRegionFlags::kHitTestChildSurface |
+ HitTestRegionFlags::kHitTestMine,
+ region.flags);
+ EXPECT_EQ(region.frame_sink_id, c2_surface_id.frame_sink_id());
+ EXPECT_EQ(region.rect, gfx::Rect(0, 0, 200, 100));
+ EXPECT_EQ(region.child_count, 0);
+}
+
+} // namespace test
} // namespace viz
diff --git a/chromium/components/viz/service/hit_test/hit_test_manager.cc b/chromium/components/viz/service/hit_test/hit_test_manager.cc
index 4f3e7e1ae9d..17afc70a588 100644
--- a/chromium/components/viz/service/hit_test/hit_test_manager.cc
+++ b/chromium/components/viz/service/hit_test/hit_test_manager.cc
@@ -94,6 +94,16 @@ const HitTestRegionList* HitTestManager::GetActiveHitTestRegionList(
return &search2->second;
}
+int32_t HitTestManager::GetActiveFrameIndex(const SurfaceId& id) const {
+ Surface* surface = surface_manager_->GetSurfaceForId(id);
+ return surface->GetActiveFrameIndex();
+}
+
+int64_t HitTestManager::GetTraceId(const SurfaceId& id) const {
+ Surface* surface = surface_manager_->GetSurfaceForId(id);
+ return surface->GetActiveFrame().metadata.begin_frame_ack.trace_id;
+}
+
bool HitTestManager::ValidateHitTestRegionList(
const SurfaceId& surface_id,
HitTestRegionList* hit_test_region_list) {
diff --git a/chromium/components/viz/service/hit_test/hit_test_manager.h b/chromium/components/viz/service/hit_test/hit_test_manager.h
index ae4ae6064ad..21281e5cafc 100644
--- a/chromium/components/viz/service/hit_test/hit_test_manager.h
+++ b/chromium/components/viz/service/hit_test/hit_test_manager.h
@@ -51,6 +51,9 @@ class VIZ_SERVICE_EXPORT HitTestManager : public SurfaceObserver {
LatestLocalSurfaceIdLookupDelegate* delegate,
const FrameSinkId& frame_sink_id) const;
+ int32_t GetActiveFrameIndex(const SurfaceId& id) const;
+ int64_t GetTraceId(const SurfaceId& id) const;
+
private:
bool ValidateHitTestRegionList(const SurfaceId& surface_id,
HitTestRegionList* hit_test_region_list);
diff --git a/chromium/components/viz/service/java/src/org/chromium/components/viz/service/frame_sinks/ExternalBeginFrameSourceAndroid.java b/chromium/components/viz/service/java/src/org/chromium/components/viz/service/frame_sinks/ExternalBeginFrameSourceAndroid.java
new file mode 100644
index 00000000000..c53e1d6b4c5
--- /dev/null
+++ b/chromium/components/viz/service/java/src/org/chromium/components/viz/service/frame_sinks/ExternalBeginFrameSourceAndroid.java
@@ -0,0 +1,54 @@
+// Copyright 2018 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.
+
+package org.chromium.components.viz.service.frame_sinks;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.ui.VSyncMonitor;
+
+/**
+ * Provides a VSyncMonitor backed BeginFrameSource.
+ */
+@JNINamespace("viz")
+@MainDex
+public class ExternalBeginFrameSourceAndroid {
+ private final long mNativeExternalBeginFrameSourceAndroid;
+ private boolean mVSyncNotificationsEnabled;
+ private final VSyncMonitor mVSyncMonitor;
+ private final VSyncMonitor.Listener mVSyncListener = new VSyncMonitor.Listener() {
+ @Override
+ public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) {
+ if (!mVSyncNotificationsEnabled) {
+ return;
+ }
+ nativeOnVSync(mNativeExternalBeginFrameSourceAndroid, vsyncTimeMicros,
+ mVSyncMonitor.getVSyncPeriodInMicroseconds());
+ mVSyncMonitor.requestUpdate();
+ }
+ };
+
+ @CalledByNative
+ private ExternalBeginFrameSourceAndroid(long nativeExternalBeginFrameSourceAndroid) {
+ mNativeExternalBeginFrameSourceAndroid = nativeExternalBeginFrameSourceAndroid;
+ mVSyncMonitor = new VSyncMonitor(ContextUtils.getApplicationContext(), mVSyncListener);
+ }
+
+ @CalledByNative
+ private void setEnabled(boolean enabled) {
+ if (mVSyncNotificationsEnabled == enabled) {
+ return;
+ }
+
+ mVSyncNotificationsEnabled = enabled;
+ if (mVSyncNotificationsEnabled) {
+ mVSyncMonitor.requestUpdate();
+ }
+ }
+
+ private native void nativeOnVSync(long nativeExternalBeginFrameSourceAndroid,
+ long vsyncTimeMicros, long vsyncPeriodMicros);
+};
diff --git a/chromium/components/viz/service/java/src/org/chromium/components/viz/service/gl/ThrowUncaughtException.java b/chromium/components/viz/service/java/src/org/chromium/components/viz/service/gl/ThrowUncaughtException.java
new file mode 100644
index 00000000000..57dcf3cd295
--- /dev/null
+++ b/chromium/components/viz/service/java/src/org/chromium/components/viz/service/gl/ThrowUncaughtException.java
@@ -0,0 +1,22 @@
+// 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.
+
+package org.chromium.components.viz.service.gl;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.MainDex;
+
+@MainDex
+abstract class ThrowUncaughtException {
+ @CalledByNative
+ private static void post() {
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ throw new RuntimeException("Intentional exception not caught by JNI");
+ }
+ });
+ }
+}
diff --git a/chromium/components/viz/service/main/BUILD.gn b/chromium/components/viz/service/main/BUILD.gn
index c3e716ab9aa..9f5ade6bb1c 100644
--- a/chromium/components/viz/service/main/BUILD.gn
+++ b/chromium/components/viz/service/main/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/config/ui.gni")
+import("//media/gpu/args.gni")
import("//services/catalog/public/tools/catalog.gni")
import("//services/service_manager/public/service_manifest.gni")
import("//testing/test.gni")
@@ -24,6 +25,7 @@ source_set("main") {
"//gpu/ipc/common",
"//gpu/ipc/service",
"//ipc",
+ "//media/gpu:buildflags",
"//mojo/public/cpp/system",
"//services/metrics/public/cpp:metrics_cpp",
"//services/metrics/public/mojom",
@@ -40,4 +42,8 @@ source_set("main") {
if (is_chromeos) {
deps += [ "//media/gpu" ]
}
+
+ if (use_vaapi) {
+ deps += [ "//media/gpu/vaapi" ]
+ }
}
diff --git a/chromium/components/viz/service/main/viz_main_impl.cc b/chromium/components/viz/service/main/viz_main_impl.cc
index 2196abef4ba..6f5f55de758 100644
--- a/chromium/components/viz/service/main/viz_main_impl.cc
+++ b/chromium/components/viz/service/main/viz_main_impl.cc
@@ -282,10 +282,10 @@ void VizMainImpl::ExitProcess() {
// OOP-D requires destroying RootCompositorFrameSinkImpls on the compositor
// thread while the GPU thread is still running to avoid deadlock. Quit GPU
// thread TaskRunner after cleanup on compositor thread is finished.
- viz_compositor_thread_runner_->CleanupForShutdown(
- base::BindOnce([]() { base::RunLoop::QuitCurrentDeprecated(); }));
+ viz_compositor_thread_runner_->CleanupForShutdown(base::BindOnce(
+ &Delegate::QuitMainMessageLoop, base::Unretained(delegate_)));
} else {
- base::RunLoop::QuitCurrentDeprecated();
+ delegate_->QuitMainMessageLoop();
}
}
diff --git a/chromium/components/viz/service/main/viz_main_impl.h b/chromium/components/viz/service/main/viz_main_impl.h
index c83792f769f..876ba38240f 100644
--- a/chromium/components/viz/service/main/viz_main_impl.h
+++ b/chromium/components/viz/service/main/viz_main_impl.h
@@ -57,6 +57,7 @@ class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
virtual void OnGpuServiceConnection(GpuServiceImpl* gpu_service) = 0;
virtual void PostCompositorThreadCreated(
base::SingleThreadTaskRunner* task_runner) = 0;
+ virtual void QuitMainMessageLoop() = 0;
};
struct ExternalDependencies {
@@ -77,8 +78,6 @@ class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
DISALLOW_COPY_AND_ASSIGN(ExternalDependencies);
};
- // TODO(kylechar): Provide a quit closure for the appropriate RunLoop instance
- // to stop the thread and remove base::RunLoop::QuitCurrentDeprecated() usage.
VizMainImpl(Delegate* delegate,
ExternalDependencies dependencies,
std::unique_ptr<gpu::GpuInit> gpu_init = nullptr);
@@ -110,15 +109,15 @@ class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
return discardable_shared_memory_manager_.get();
}
+ // Cleanly exits the process.
+ void ExitProcess();
+
private:
// Initializes GPU's UkmRecorder if GPU is running in it's own process.
void CreateUkmRecorderIfNeeded(service_manager::Connector* connector);
void CreateFrameSinkManagerInternal(mojom::FrameSinkManagerParamsPtr params);
- // Cleanly exits the process.
- void ExitProcess();
-
// gpu::GpuSandboxHelper:
void PreSandboxStartup() override;
bool EnsureSandboxInitialized(gpu::GpuWatchdogThread* watchdog_thread,
diff --git a/chromium/components/viz/service/surfaces/surface.cc b/chromium/components/viz/service/surfaces/surface.cc
index 69b41d91228..ded72a25ad9 100644
--- a/chromium/components/viz/service/surfaces/surface.cc
+++ b/chromium/components/viz/service/surfaces/surface.cc
@@ -26,11 +26,13 @@ namespace viz {
Surface::Surface(const SurfaceInfo& surface_info,
SurfaceManager* surface_manager,
base::WeakPtr<SurfaceClient> surface_client,
- bool needs_sync_tokens)
+ bool needs_sync_tokens,
+ bool block_activation_on_parent)
: surface_info_(surface_info),
surface_manager_(surface_manager),
surface_client_(std::move(surface_client)),
- needs_sync_tokens_(needs_sync_tokens) {
+ needs_sync_tokens_(needs_sync_tokens),
+ block_activation_on_parent_(block_activation_on_parent) {
TRACE_EVENT_ASYNC_BEGIN1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
"Surface", this, "surface_info",
surface_info.ToString());
@@ -50,8 +52,8 @@ Surface::~Surface() {
for (const FrameSinkId& sink_id : observed_sinks_)
surface_manager_->RemoveActivationObserver(sink_id, surface_info_.id());
- if (deadline_)
- deadline_->Cancel();
+ DCHECK(deadline_);
+ deadline_->Cancel();
TRACE_EVENT_ASYNC_END1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
"Surface", this, "surface_info",
@@ -77,8 +79,8 @@ void Surface::Reset(base::WeakPtr<SurfaceClient> client) {
void Surface::InheritActivationDeadlineFrom(Surface* surface) {
TRACE_EVENT1("viz", "Surface::InheritActivationDeadlineFrom", "FrameSinkId",
surface_id().frame_sink_id().ToString());
- if (!deadline_ || !surface->deadline_)
- return;
+ DCHECK(deadline_);
+ DCHECK(surface->deadline_);
deadline_->InheritFrom(*surface->deadline_);
}
@@ -186,6 +188,20 @@ void Surface::OnChildActivated(const SurfaceId& activated_id) {
}
}
+void Surface::OnSurfaceDependencyAdded() {
+ if (seen_first_surface_dependency_)
+ return;
+
+ seen_first_surface_dependency_ = true;
+ if (!activation_dependencies_.empty() || !pending_frame_data_)
+ return;
+
+ DCHECK(frame_sink_id_dependencies_.empty());
+
+ // All blockers have been cleared. The surface can be activated now.
+ ActivatePendingFrame(base::nullopt);
+}
+
void Surface::Close() {
closed_ = true;
}
@@ -216,14 +232,22 @@ bool Surface::QueueFrame(
std::move(pending_frame_data_);
pending_frame_data_.reset();
- FrameDeadline deadline = UpdateActivationDependencies(frame);
+ UpdateActivationDependencies(frame);
// Receive and track the resources referenced from the CompositorFrame
// regardless of whether it's pending or active.
surface_client_->ReceiveFromChild(frame.resource_list);
- if (activation_dependencies_.empty() ||
- (deadline_ && !deadline.deadline_in_frames())) {
+ if (!seen_first_surface_dependency_) {
+ seen_first_surface_dependency_ =
+ surface_manager_->dependency_tracker()->HasSurfaceBlockedOn(
+ surface_id());
+ }
+
+ bool block_activation =
+ block_activation_on_parent_ && !seen_first_surface_dependency_;
+
+ if (!block_activation && activation_dependencies_.empty()) {
// If there are no blockers, then immediately activate the frame.
ActivateFrame(
FrameData(std::move(frame), frame_index, std::move(presented_callback)),
@@ -233,8 +257,13 @@ bool Surface::QueueFrame(
FrameData(std::move(frame), frame_index, std::move(presented_callback));
RejectCompositorFramesToFallbackSurfaces();
- // If the deadline is in the past, then we will activate immediately.
- if (!deadline_ || deadline_->Set(deadline)) {
+ // Ask SurfaceDependencyTracker to inform |this| when it is embedded.
+ if (block_activation)
+ surface_manager_->dependency_tracker()->TrackEmbedding(this);
+
+ // If the deadline is in the past, then the CompositorFrame will activate
+ // immediately.
+ if (deadline_->Set(ResolveFrameDeadline(frame))) {
// Ask the SurfaceDependencyTracker to inform |this| when its dependencies
// are resolved.
surface_manager_->dependency_tracker()->RequestSurfaceResolution(this);
@@ -298,7 +327,12 @@ void Surface::NotifySurfaceIdAvailable(const SurfaceId& surface_id) {
dependency.local_surface_id() <= surface_id.local_surface_id();
});
- if (!activation_dependencies_.empty())
+ // We cannot activate this CompositorFrame if there are still missing
+ // activation dependencies or this surface is blocked on its parent arriving
+ // and the parent has not arrived yet.
+ bool block_activation =
+ block_activation_on_parent_ && !seen_first_surface_dependency_;
+ if (block_activation || !activation_dependencies_.empty())
return;
DCHECK(frame_sink_id_dependencies_.empty());
@@ -307,6 +341,17 @@ void Surface::NotifySurfaceIdAvailable(const SurfaceId& surface_id) {
ActivatePendingFrame(base::nullopt);
}
+bool Surface::IsBlockedOn(const SurfaceId& surface_id) const {
+ for (const SurfaceId& dependency : activation_dependencies_) {
+ if (dependency.frame_sink_id() != surface_id.frame_sink_id())
+ continue;
+
+ if (dependency.local_surface_id() <= surface_id.local_surface_id())
+ return true;
+ }
+ return false;
+}
+
void Surface::ActivatePendingFrameForDeadline(
base::Optional<base::TimeDelta> duration) {
if (!pending_frame_data_)
@@ -339,8 +384,8 @@ void Surface::ActivatePendingFrame(base::Optional<base::TimeDelta> duration) {
FrameData frame_data = std::move(*pending_frame_data_);
pending_frame_data_.reset();
- DCHECK(!duration || !deadline_ || !deadline_->has_deadline());
- if (!duration && deadline_)
+ DCHECK(!duration || !deadline_->has_deadline());
+ if (!duration)
duration = deadline_->Cancel();
ActivateFrame(std::move(frame_data), duration);
@@ -456,35 +501,47 @@ void Surface::ActivateFrame(FrameData frame_data,
surface_client_->OnFrameTokenChanged(metadata.frame_token);
}
-FrameDeadline Surface::UpdateActivationDependencies(
+FrameDeadline Surface::ResolveFrameDeadline(
const CompositorFrame& current_frame) {
const base::Optional<uint32_t>& default_deadline =
surface_manager_->activation_deadline_in_frames();
- FrameDeadline deadline = current_frame.metadata.deadline;
-
+ const FrameDeadline& deadline = current_frame.metadata.deadline;
uint32_t deadline_in_frames = deadline.deadline_in_frames();
- if (default_deadline && deadline.use_default_lower_bound_deadline())
- deadline_in_frames = std::max(deadline_in_frames, *default_deadline);
- deadline = FrameDeadline(deadline.frame_start_time(), deadline_in_frames,
- deadline.frame_interval(),
- false /* use_default_lower_bound_deadline */);
+ bool block_activation =
+ block_activation_on_parent_ && !seen_first_surface_dependency_;
+
+ // If no default deadline is available then all deadlines are treated as
+ // effectively infinite deadlines.
+ if (!default_deadline || deadline.use_default_lower_bound_deadline() ||
+ block_activation) {
+ deadline_in_frames = std::max(
+ deadline_in_frames,
+ default_deadline.value_or(std::numeric_limits<uint32_t>::max()));
+ }
- bool track_dependencies = !default_deadline || deadline_in_frames > 0;
+ return FrameDeadline(deadline.frame_start_time(), deadline_in_frames,
+ deadline.frame_interval(),
+ false /* use_default_lower_bound_deadline */);
+}
+void Surface::UpdateActivationDependencies(
+ const CompositorFrame& current_frame) {
base::flat_map<FrameSinkId, SequenceNumbers> new_frame_sink_id_dependencies;
base::flat_set<SurfaceId> new_activation_dependencies;
for (const SurfaceId& surface_id :
current_frame.metadata.activation_dependencies) {
+ // Inform the Surface |dependency| that it's been added as a dependency in
+ // another Surface's CompositorFrame.
+ surface_manager_->SurfaceDependencyAdded(surface_id);
+
Surface* dependency = surface_manager_->GetSurfaceForId(surface_id);
+
// If a activation dependency does not have a corresponding active frame in
// the display compositor, then it blocks this frame.
if (!dependency || !dependency->HasActiveFrame()) {
new_activation_dependencies.insert(surface_id);
- if (!track_dependencies)
- continue;
-
TRACE_EVENT_WITH_FLOW2(
TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
"LocalSurfaceId.Embed.Flow",
@@ -516,16 +573,8 @@ FrameDeadline Surface::UpdateActivationDependencies(
// map.
ComputeChangeInDependencies(new_frame_sink_id_dependencies);
- if (track_dependencies) {
- activation_dependencies_ = std::move(new_activation_dependencies);
- } else {
- // If the deadline is zero, then all dependencies are late.
- activation_dependencies_.clear();
- late_activation_dependencies_ = std::move(new_activation_dependencies);
- }
-
+ activation_dependencies_ = std::move(new_activation_dependencies);
frame_sink_id_dependencies_ = std::move(new_frame_sink_id_dependencies);
- return deadline;
}
void Surface::ComputeChangeInDependencies(
diff --git a/chromium/components/viz/service/surfaces/surface.h b/chromium/components/viz/service/surfaces/surface.h
index ca220381d6b..e8c1afe2622 100644
--- a/chromium/components/viz/service/surfaces/surface.h
+++ b/chromium/components/viz/service/surfaces/surface.h
@@ -81,7 +81,8 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
Surface(const SurfaceInfo& surface_info,
SurfaceManager* surface_manager,
base::WeakPtr<SurfaceClient> surface_client,
- bool needs_sync_tokens);
+ bool needs_sync_tokens,
+ bool block_activation_on_parent);
~Surface();
void SetDependencyDeadline(
@@ -104,6 +105,10 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
bool has_deadline() const { return deadline_ && deadline_->has_deadline(); }
+ base::Optional<base::TimeTicks> deadline_for_testing() const {
+ return deadline_->deadline_for_testing();
+ }
+
// Inherits the same deadline as the one specified by |surface|. A deadline
// may be set further out in order to avoid doing unnecessary work while a
// parent surface is blocked on dependencies. A deadline may be shortened
@@ -121,6 +126,10 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
bool needs_sync_tokens() const { return needs_sync_tokens_; }
+ bool block_activation_on_parent() const {
+ return block_activation_on_parent_;
+ }
+
// Returns false if |frame| is invalid.
// |frame_rejected_callback| will be called once if the frame will not be
// displayed.
@@ -136,6 +145,10 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
// frame.
void NotifySurfaceIdAvailable(const SurfaceId& surface_id);
+ // Returns whether the Surface is blocked on the provided |surface_id| or a
+ // predecessor.
+ bool IsBlockedOn(const SurfaceId& surface_id) const;
+
// Called if a deadline has been hit and this surface is not yet active but
// it's marked as respecting deadlines.
void ActivatePendingFrameForDeadline(
@@ -200,6 +213,10 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
return HasActiveFrame() && !active_frame_data_->frame_processed;
}
+ // Returns true if at any point, another Surface's CompositorFrame has
+ // depended on this Surface.
+ bool HasDependentFrame() const { return seen_first_surface_dependency_; }
+
// SurfaceDeadlineClient implementation:
void OnDeadline(base::TimeDelta duration) override;
@@ -210,6 +227,9 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
// referenced SurfaceRange.
void OnChildActivated(const SurfaceId& surface_id);
+ // Called when this surface is embedded by another Surface's CompositorFrame.
+ void OnSurfaceDependencyAdded();
+
private:
struct SequenceNumbers {
uint32_t parent_sequence_number = 0u;
@@ -262,11 +282,15 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
void ActivateFrame(FrameData frame_data,
base::Optional<base::TimeDelta> duration);
+ // Resolve the activation deadline specified by |current_frame| into a wall
+ // time to be used by SurfaceDependencyDeadline.
+ FrameDeadline ResolveFrameDeadline(const CompositorFrame& current_frame);
+
// Updates the set of unresolved activation dependenices of the
// |current_frame|. If the deadline requested by the frame is 0 then no
// dependencies will be added even if they're not yet available.
- FrameDeadline UpdateActivationDependencies(
- const CompositorFrame& current_frame);
+ void UpdateActivationDependencies(const CompositorFrame& current_frame);
+
void ComputeChangeInDependencies(
const base::flat_map<FrameSinkId, SequenceNumbers>& new_dependencies);
@@ -292,7 +316,9 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
bool closed_ = false;
bool seen_first_frame_activation_ = false;
bool seen_first_surface_embedding_ = false;
+ bool seen_first_surface_dependency_ = false;
const bool needs_sync_tokens_;
+ const bool block_activation_on_parent_;
base::flat_set<SurfaceId> activation_dependencies_;
base::flat_set<SurfaceId> late_activation_dependencies_;
diff --git a/chromium/components/viz/service/surfaces/surface_dependency_deadline.cc b/chromium/components/viz/service/surfaces/surface_dependency_deadline.cc
index 4e86b838144..65d8abe1313 100644
--- a/chromium/components/viz/service/surfaces/surface_dependency_deadline.cc
+++ b/chromium/components/viz/service/surfaces/surface_dependency_deadline.cc
@@ -29,11 +29,9 @@ SurfaceDependencyDeadline::~SurfaceDependencyDeadline() {
}
bool SurfaceDependencyDeadline::Set(const FrameDeadline& frame_deadline) {
- DCHECK_GT(frame_deadline.deadline_in_frames(), 0u);
CancelInternal(false);
start_time_ = frame_deadline.frame_start_time();
- deadline_ = start_time_ + frame_deadline.deadline_in_frames() *
- frame_deadline.frame_interval();
+ deadline_ = frame_deadline.ToWallTime();
begin_frame_source_->AddObserver(this);
return has_deadline();
}
diff --git a/chromium/components/viz/service/surfaces/surface_dependency_tracker.cc b/chromium/components/viz/service/surfaces/surface_dependency_tracker.cc
index 12a6310abf5..b1435079d60 100644
--- a/chromium/components/viz/service/surfaces/surface_dependency_tracker.cc
+++ b/chromium/components/viz/service/surfaces/surface_dependency_tracker.cc
@@ -16,6 +16,18 @@ SurfaceDependencyTracker::SurfaceDependencyTracker(
SurfaceDependencyTracker::~SurfaceDependencyTracker() = default;
+void SurfaceDependencyTracker::TrackEmbedding(Surface* surface) {
+ // If |surface| is blocking on the arrival of a parent and the parent frame
+ // has not yet arrived then track this |surface|'s SurfaceId by FrameSinkId so
+ // that if a parent refers to it or a more recent surface, then
+ // SurfaceDependencyTracker reports back that a dependency has been added.
+ if (surface->block_activation_on_parent() && !surface->HasDependentFrame()) {
+ surfaces_blocked_on_parent_by_frame_sink_id_[surface->surface_id()
+ .frame_sink_id()]
+ .insert(surface->surface_id());
+ }
+}
+
void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) {
DCHECK(surface->HasPendingFrame());
@@ -37,12 +49,59 @@ void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) {
UpdateSurfaceDeadline(surface);
}
+bool SurfaceDependencyTracker::HasSurfaceBlockedOn(
+ const SurfaceId& surface_id) const {
+ auto it = blocked_surfaces_from_dependency_.find(surface_id.frame_sink_id());
+ if (it == blocked_surfaces_from_dependency_.end())
+ return false;
+
+ for (const SurfaceId& blocked_surface_by_id : it->second) {
+ Surface* blocked_surface =
+ surface_manager_->GetSurfaceForId(blocked_surface_by_id);
+ if (blocked_surface && blocked_surface->IsBlockedOn(surface_id))
+ return true;
+ }
+ return false;
+}
+
void SurfaceDependencyTracker::OnSurfaceActivated(Surface* surface) {
if (!surface->late_activation_dependencies().empty())
surfaces_with_missing_dependencies_.insert(surface->surface_id());
else
surfaces_with_missing_dependencies_.erase(surface->surface_id());
NotifySurfaceIdAvailable(surface->surface_id());
+ // We treat an activation (by deadline) as being the equivalent of a parent
+ // embedding the surface.
+ OnSurfaceDependencyAdded(surface->surface_id());
+}
+
+void SurfaceDependencyTracker::OnSurfaceDependencyAdded(
+ const SurfaceId& surface_id) {
+ auto it = surfaces_blocked_on_parent_by_frame_sink_id_.find(
+ surface_id.frame_sink_id());
+ if (it == surfaces_blocked_on_parent_by_frame_sink_id_.end())
+ return;
+
+ std::vector<SurfaceId> dependencies_to_notify;
+
+ base::flat_set<SurfaceId>& blocked_surfaces = it->second;
+ for (auto iter = blocked_surfaces.begin(); iter != blocked_surfaces.end();) {
+ if (iter->local_surface_id() <= surface_id.local_surface_id()) {
+ dependencies_to_notify.push_back(*iter);
+ iter = blocked_surfaces.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ if (blocked_surfaces.empty())
+ surfaces_blocked_on_parent_by_frame_sink_id_.erase(it);
+
+ for (const SurfaceId& dependency : dependencies_to_notify) {
+ Surface* surface = surface_manager_->GetSurfaceForId(dependency);
+ if (surface)
+ surface->OnSurfaceDependencyAdded();
+ }
}
void SurfaceDependencyTracker::OnSurfaceDependenciesChanged(
@@ -78,6 +137,7 @@ void SurfaceDependencyTracker::OnSurfaceDiscarded(Surface* surface) {
// Pretend that the discarded surface's SurfaceId is now available to
// unblock dependencies because we now know the surface will never activate.
NotifySurfaceIdAvailable(surface->surface_id());
+ OnSurfaceDependencyAdded(surface->surface_id());
}
void SurfaceDependencyTracker::OnFrameSinkInvalidated(
@@ -85,6 +145,7 @@ void SurfaceDependencyTracker::OnFrameSinkInvalidated(
// We now know the frame sink will never generated any more frames,
// thus unblock all dependencies to any future surfaces.
NotifySurfaceIdAvailable(SurfaceId::MaxSequenceId(frame_sink_id));
+ OnSurfaceDependencyAdded(SurfaceId::MaxSequenceId(frame_sink_id));
}
void SurfaceDependencyTracker::ActivateLateSurfaceSubtree(Surface* surface) {
diff --git a/chromium/components/viz/service/surfaces/surface_dependency_tracker.h b/chromium/components/viz/service/surfaces/surface_dependency_tracker.h
index 064487b4332..81542ac333f 100644
--- a/chromium/components/viz/service/surfaces/surface_dependency_tracker.h
+++ b/chromium/components/viz/service/surfaces/surface_dependency_tracker.h
@@ -30,11 +30,19 @@ class VIZ_SERVICE_EXPORT SurfaceDependencyTracker {
explicit SurfaceDependencyTracker(SurfaceManager* surface_manager);
~SurfaceDependencyTracker();
+ // Called when |surface| wishes to track when it is embedded.
+ void TrackEmbedding(Surface* surface);
+
// Called when |surface| has a pending CompositorFrame and it wishes to be
// informed when that surface's dependencies are resolved.
void RequestSurfaceResolution(Surface* surface);
+ // Returns whether the dependency tracker has a surface blocked on the
+ // provided |surface_id|.
+ bool HasSurfaceBlockedOn(const SurfaceId& surface_id) const;
+
void OnSurfaceActivated(Surface* surface);
+ void OnSurfaceDependencyAdded(const SurfaceId& surface_id);
void OnSurfaceDependenciesChanged(
Surface* surface,
const base::flat_set<FrameSinkId>& added_dependencies,
@@ -68,6 +76,11 @@ class VIZ_SERVICE_EXPORT SurfaceDependencyTracker {
std::unordered_map<FrameSinkId, base::flat_set<SurfaceId>, FrameSinkIdHash>
blocked_surfaces_from_dependency_;
+ // A map from a FrameSinkid to a set of surfaces with that FrameSinkId that
+ // are blocked on a parent arriving to embed them.
+ std::unordered_map<FrameSinkId, base::flat_set<SurfaceId>, FrameSinkIdHash>
+ surfaces_blocked_on_parent_by_frame_sink_id_;
+
// The set of SurfaceIds corresponding to Surfaces that have active
// CompositorFrames with missing dependencies.
base::flat_set<SurfaceId> surfaces_with_missing_dependencies_;
diff --git a/chromium/components/viz/service/surfaces/surface_manager.cc b/chromium/components/viz/service/surfaces/surface_manager.cc
index f65f31b1698..72a2a71d055 100644
--- a/chromium/components/viz/service/surfaces/surface_manager.cc
+++ b/chromium/components/viz/service/surfaces/surface_manager.cc
@@ -40,10 +40,6 @@ const char kUmaRemovedTemporaryReference[] =
} // namespace
-SurfaceManager::TemporaryReferenceData::TemporaryReferenceData() = default;
-
-SurfaceManager::TemporaryReferenceData::~TemporaryReferenceData() = default;
-
SurfaceManager::SurfaceManager(
base::Optional<uint32_t> activation_deadline_in_frames)
: activation_deadline_in_frames_(activation_deadline_in_frames),
@@ -106,7 +102,8 @@ Surface* SurfaceManager::CreateSurface(
base::WeakPtr<SurfaceClient> surface_client,
const SurfaceInfo& surface_info,
BeginFrameSource* begin_frame_source,
- bool needs_sync_tokens) {
+ bool needs_sync_tokens,
+ bool block_activation_on_parent) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(surface_info.is_valid());
DCHECK(surface_client);
@@ -116,13 +113,10 @@ Surface* SurfaceManager::CreateSurface(
auto it = surface_map_.find(surface_info.id());
if (it == surface_map_.end()) {
std::unique_ptr<Surface> surface = std::make_unique<Surface>(
- surface_info, this, surface_client, needs_sync_tokens);
- // If no default deadline is specified then don't track deadlines.
- if (activation_deadline_in_frames_) {
- surface->SetDependencyDeadline(
- std::make_unique<SurfaceDependencyDeadline>(
- surface.get(), begin_frame_source, tick_clock_));
- }
+ surface_info, this, surface_client, needs_sync_tokens,
+ block_activation_on_parent);
+ surface->SetDependencyDeadline(std::make_unique<SurfaceDependencyDeadline>(
+ surface.get(), begin_frame_source, tick_clock_));
surface_map_[surface_info.id()] = std::move(surface);
// We can get into a situation where multiple CompositorFrames arrive for a
// FrameSink before the client can add any references for the frame. When
@@ -161,17 +155,6 @@ void SurfaceManager::DestroySurface(const SurfaceId& surface_id) {
}
void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
- // Remove any temporary references owned by |frame_sink_id|.
- std::vector<SurfaceId> temp_refs_to_clear;
- for (auto& map_entry : temporary_references_) {
- base::Optional<FrameSinkId>& owner = map_entry.second.owner;
- if (owner.has_value() && owner.value() == frame_sink_id)
- temp_refs_to_clear.push_back(map_entry.first);
- }
-
- for (auto& surface_id : temp_refs_to_clear)
- RemoveTemporaryReference(surface_id, RemovedReason::INVALIDATED);
-
dependency_tracker_.OnFrameSinkInvalidated(frame_sink_id);
GarbageCollectSurfaces();
@@ -204,16 +187,6 @@ void SurfaceManager::RemoveSurfaceReferences(
RemoveSurfaceReferenceImpl(reference);
}
-void SurfaceManager::AssignTemporaryReference(const SurfaceId& surface_id,
- const FrameSinkId& owner) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
- if (!HasTemporaryReference(surface_id))
- return;
-
- temporary_references_[surface_id].owner = owner;
-}
-
void SurfaceManager::DropTemporaryReference(const SurfaceId& surface_id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -432,10 +405,9 @@ bool SurfaceManager::HasPersistentReference(const SurfaceId& surface_id) const {
void SurfaceManager::AddTemporaryReference(const SurfaceId& surface_id) {
DCHECK(!HasTemporaryReference(surface_id));
- // Add an entry to |temporary_references_| with no owner for the temporary
- // reference. Also add a range tracking entry so we know the order that
- // surfaces were created for the FrameSinkId.
- temporary_references_[surface_id].owner = base::Optional<FrameSinkId>();
+ // Add an entry to |temporary_references_|. Also add a range tracking entry so
+ // we know the order that surfaces were created for the FrameSinkId.
+ temporary_references_.emplace(surface_id, TemporaryReferenceData());
temporary_reference_ranges_[surface_id.frame_sink_id()].push_back(
surface_id.local_surface_id());
@@ -610,6 +582,10 @@ void SurfaceManager::SurfaceActivated(
dependency_tracker_.OnSurfaceActivated(surface);
}
+void SurfaceManager::SurfaceDependencyAdded(const SurfaceId& surface_id) {
+ dependency_tracker_.OnSurfaceDependencyAdded(surface_id);
+}
+
void SurfaceManager::SurfaceDependenciesChanged(
Surface* surface,
const base::flat_set<FrameSinkId>& added_dependencies,
diff --git a/chromium/components/viz/service/surfaces/surface_manager.h b/chromium/components/viz/service/surfaces/surface_manager.h
index c976f6bc9c2..d7bc9530166 100644
--- a/chromium/components/viz/service/surfaces/surface_manager.h
+++ b/chromium/components/viz/service/surfaces/surface_manager.h
@@ -39,9 +39,13 @@ class TickClock;
namespace viz {
namespace test {
+class CompositorFrameSinkSupportTest;
+class FrameSinkManagerTest;
+class HitTestAggregatorTest;
class SurfaceReferencesTest;
class SurfaceSynchronizationTest;
} // namespace test
+
class Surface;
struct BeginFrameAck;
struct BeginFrameArgs;
@@ -85,11 +89,13 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
Surface* CreateSurface(base::WeakPtr<SurfaceClient> surface_client,
const SurfaceInfo& surface_info,
BeginFrameSource* begin_frame_source,
- bool needs_sync_tokens);
+ bool needs_sync_tokens,
+ bool block_activation_on_parent);
// Destroy the Surface once a set of sequence numbers has been satisfied.
void DestroySurface(const SurfaceId& surface_id);
+ // Returns a Surface corresponding to the provided |surface_id|.
Surface* GetSurfaceForId(const SurfaceId& surface_id);
void AddObserver(SurfaceObserver* obs) { observer_list_.AddObserver(obs); }
@@ -123,6 +129,10 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
void SurfaceActivated(Surface* surface,
base::Optional<base::TimeDelta> duration);
+ // Called when this |surface_id| is referenced as an activation dependency
+ // from a parent CompositorFrame.
+ void SurfaceDependencyAdded(const SurfaceId& surface_id);
+
// Called when the dependencies of a pending CompositorFrame within |surface|
// has changed.
void SurfaceDependenciesChanged(
@@ -168,13 +178,6 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
// collection to delete unreachable surfaces.
void RemoveSurfaceReferences(const std::vector<SurfaceReference>& references);
- // Assigns |owner| as the owner of the temporary reference to
- // |surface_id|. If |owner| is invalidated the temporary reference
- // will be removed. If a surface reference has already been added from the
- // parent to |surface_id| then this will do nothing.
- void AssignTemporaryReference(const SurfaceId& surface_id,
- const FrameSinkId& owner);
-
// Drops the temporary reference for |surface_id|. If a surface reference has
// already been added from the parent to |surface_id| then this will do
// nothing.
@@ -203,29 +206,27 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
void SurfaceWillBeDrawn(Surface* surface);
private:
+ friend class test::CompositorFrameSinkSupportTest;
+ friend class test::FrameSinkManagerTest;
+ friend class test::HitTestAggregatorTest;
friend class test::SurfaceSynchronizationTest;
friend class test::SurfaceReferencesTest;
+ friend class test::SurfaceSynchronizationTest;
using SurfaceIdSet = std::unordered_set<SurfaceId, SurfaceIdHash>;
// The reason for removing a temporary reference.
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
enum class RemovedReason {
- EMBEDDED, // The surface was embedded.
- DROPPED, // The surface won't be embedded so it was dropped.
- SKIPPED, // A newer surface was embedded and the surface was skipped.
- INVALIDATED, // The expected embedder was invalidated.
- EXPIRED, // The surface was never embedded and expired.
+ EMBEDDED = 0, // The surface was embedded.
+ DROPPED = 1, // The surface won't be embedded so it was dropped.
+ SKIPPED = 2, // A newer surface was embedded and the surface was skipped.
+ EXPIRED = 4, // The surface was never embedded and expired.
COUNT
};
struct TemporaryReferenceData {
- TemporaryReferenceData();
- ~TemporaryReferenceData();
-
- // The FrameSinkId that is expected to embed this SurfaceId. This will
- // initially be empty and set later by AssignTemporaryReference().
- base::Optional<FrameSinkId> owner;
-
// Used to track old surface references, will be marked as true on first
// timer tick and will be true on second timer tick.
bool marked_as_old = false;
diff --git a/chromium/components/viz/service/surfaces/surface_observer.h b/chromium/components/viz/service/surfaces/surface_observer.h
index a9558a197b3..3a7e0d03fa1 100644
--- a/chromium/components/viz/service/surfaces/surface_observer.h
+++ b/chromium/components/viz/service/surfaces/surface_observer.h
@@ -40,8 +40,7 @@ class SurfaceObserver {
// in response to a BeginFrame, or a CopyOutputRequest is issued.
//
// |ack.sequence_number| is only valid if called in response to a BeginFrame.
- // Should return true if this causes a Display to be damaged resulting in a
- // draw.
+ // Should return true if this causes a Display to be damaged.
virtual bool OnSurfaceDamaged(const SurfaceId& surface_id,
const BeginFrameAck& ack) = 0;
diff --git a/chromium/components/viz/service/surfaces/surface_reference.cc b/chromium/components/viz/service/surfaces/surface_reference.cc
index 5f61d8a3816..721d9d396c1 100644
--- a/chromium/components/viz/service/surfaces/surface_reference.cc
+++ b/chromium/components/viz/service/surfaces/surface_reference.cc
@@ -24,4 +24,9 @@ std::string SurfaceReference::ToString() const {
child_id_.ToString().c_str());
}
+std::ostream& operator<<(std::ostream& out,
+ const SurfaceReference& surface_reference) {
+ return out << surface_reference.ToString();
+}
+
} // namespace viz
diff --git a/chromium/components/viz/service/surfaces/surface_reference.h b/chromium/components/viz/service/surfaces/surface_reference.h
index 1b4c0cfa1b6..6085a9bd955 100644
--- a/chromium/components/viz/service/surfaces/surface_reference.h
+++ b/chromium/components/viz/service/surfaces/surface_reference.h
@@ -54,6 +54,10 @@ struct SurfaceReferenceHash {
size_t operator()(const SurfaceReference& ref) const { return ref.hash(); }
};
+VIZ_SERVICE_EXPORT std::ostream& operator<<(
+ std::ostream& out,
+ const SurfaceReference& surface_reference);
+
} // namespace viz
#endif // COMPONENTS_VIZ_SERVICE_SURFACES_SURFACE_REFERENCE_H_
diff --git a/chromium/components/viz/test/BUILD.gn b/chromium/components/viz/test/BUILD.gn
index e19a20c4997..d83a217b990 100644
--- a/chromium/components/viz/test/BUILD.gn
+++ b/chromium/components/viz/test/BUILD.gn
@@ -25,6 +25,8 @@ viz_static_library("test_support") {
"fake_output_surface.h",
"fake_surface_observer.cc",
"fake_surface_observer.h",
+ "gpu_host_impl_test_api.cc",
+ "gpu_host_impl_test_api.h",
"host_frame_sink_manager_test_api.cc",
"host_frame_sink_manager_test_api.h",
"mock_compositor_frame_sink_client.cc",
@@ -46,8 +48,6 @@ viz_static_library("test_support") {
"test_display_provider.h",
"test_frame_sink_manager.cc",
"test_frame_sink_manager.h",
- "test_frame_sink_manager_client.cc",
- "test_frame_sink_manager_client.h",
"test_gles2_interface.cc",
"test_gles2_interface.h",
"test_gpu_memory_buffer_manager.cc",
@@ -70,7 +70,7 @@ viz_static_library("test_support") {
"//gpu:raster",
"//gpu:test_support",
"//gpu/skia_bindings",
- "//services/viz/privileged/interfaces/compositing",
+ "//services/viz/privileged/interfaces",
"//testing/gmock",
"//testing/gtest",
"//ui/gfx/geometry",
diff --git a/chromium/components/viz/viz.gni b/chromium/components/viz/viz.gni
index 5df8b1aba69..89ee3c3f1f8 100644
--- a/chromium/components/viz/viz.gni
+++ b/chromium/components/viz/viz.gni
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/config/jumbo.gni")
import("//testing/test.gni")
viz_remove_configs = []
@@ -13,7 +14,7 @@ if (!is_debug && (is_win || is_android)) {
}
template("viz_source_set") {
- source_set(target_name) {
+ jumbo_source_set(target_name) {
forward_variables_from(invoker, "*", [ "configs" ])
if (defined(invoker.configs)) {
configs += invoker.configs
@@ -24,7 +25,7 @@ template("viz_source_set") {
}
template("viz_component") {
- component(target_name) {
+ jumbo_component(target_name) {
forward_variables_from(invoker, "*", [ "configs" ])
if (defined(invoker.configs)) {
configs += invoker.configs
@@ -35,7 +36,7 @@ template("viz_component") {
}
template("viz_static_library") {
- static_library(target_name) {
+ jumbo_static_library(target_name) {
forward_variables_from(invoker, "*", [ "configs" ])
if (defined(invoker.configs)) {
configs += invoker.configs