diff options
Diffstat (limited to 'chromium/components/viz')
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>(®istered_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>(®istered_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), - ¤t_paint_); + current_canvas_->drawImageRect( + image, uv_rect, gfx::RectToSkRect(quad->visible_rect), ¤t_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), - ¤t_paint_); + current_canvas_->drawImageRect( + image, uv_rect, gfx::RectToSkRect(quad->visible_rect), ¤t_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, ¶ms); @@ -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 |