Commit 58b07299 authored by Emircan Uysaler's avatar Emircan Uysaler Committed by Chromium LUCI CQ

[vulkan] Adjust sync cpu memory limit based on pressure signals

This CL sets the soft limit at 0 when we are in CRITICAL memory
pressure. This enforces sync and cleanup in every submit call to
reduce memory usage spikes.

This CL only affects platforms that have soft limit. That happens
if --vulkan-sync-cpu-memory-limit-mb flag is set, which is only
enabled on fuchsia.

Bug: 1166839

Change-Id: I9d38cba2479a8c912d6b1e1624e9f893be8188de
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2629573
Commit-Queue: Emircan Uysaler <emircan@chromium.org>
Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Reviewed-by: default avatarVasiliy Telezhnikov <vasilyt@chromium.org>
Reviewed-by: default avatarDavid Reveman <reveman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845162}
parent 6df77202
...@@ -209,8 +209,9 @@ void AwVulkanContextProvider::EnqueueSecondaryCBPostSubmitTask( ...@@ -209,8 +209,9 @@ void AwVulkanContextProvider::EnqueueSecondaryCBPostSubmitTask(
post_submit_tasks_.push_back(std::move(closure)); post_submit_tasks_.push_back(std::move(closure));
} }
uint32_t AwVulkanContextProvider::GetSyncCpuMemoryLimit() const { base::Optional<uint32_t> AwVulkanContextProvider::GetSyncCpuMemoryLimit()
return 0; const {
return base::Optional<uint32_t>();
} }
bool AwVulkanContextProvider::Initialize(AwDrawFn_InitVkParams* params) { bool AwVulkanContextProvider::Initialize(AwDrawFn_InitVkParams* params) {
......
...@@ -56,7 +56,7 @@ class AwVulkanContextProvider final : public viz::VulkanContextProvider { ...@@ -56,7 +56,7 @@ class AwVulkanContextProvider final : public viz::VulkanContextProvider {
void EnqueueSecondaryCBSemaphores( void EnqueueSecondaryCBSemaphores(
std::vector<VkSemaphore> semaphores) override; std::vector<VkSemaphore> semaphores) override;
void EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure) override; void EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure) override;
uint32_t GetSyncCpuMemoryLimit() const override; base::Optional<uint32_t> GetSyncCpuMemoryLimit() const override;
VkDevice device() { return globals_->device_queue->GetVulkanDevice(); } VkDevice device() { return globals_->device_queue->GetVulkanDevice(); }
VkQueue queue() { return globals_->device_queue->GetVulkanQueue(); } VkQueue queue() { return globals_->device_queue->GetVulkanQueue(); }
......
...@@ -385,6 +385,9 @@ viz_source_set("unit_tests") { ...@@ -385,6 +385,9 @@ viz_source_set("unit_tests") {
] ]
} }
if (enable_vulkan) {
sources += [ "gpu/vulkan_in_process_context_provider_unittest.cc" ]
}
deps = [ deps = [
":common", ":common",
"//base/test:test_support", "//base/test:test_support",
......
...@@ -48,12 +48,12 @@ class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanContextProvider ...@@ -48,12 +48,12 @@ class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanContextProvider
// semphores are submitted. // semphores are submitted.
virtual void EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure) = 0; virtual void EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure) = 0;
// Returns the memory limit where GPU work should be synchronized with the CPU // Returns a valid limit in MB if there is a memory limit where GPU work
// in order to free previously released memory immediately. In other words, // should be synchronized with the CPU in order to free previously released
// the CPU will wait for GPU work to complete before proceeding when the // memory immediately. In other words, the CPU will wait for GPU work to
// current amount of allocated memory exceeds this limit. Zero return value // complete before proceeding when the current amount of allocated memory
// indicate that there's no limit. // exceeds this limit.
virtual uint32_t GetSyncCpuMemoryLimit() const = 0; virtual base::Optional<uint32_t> GetSyncCpuMemoryLimit() const = 0;
protected: protected:
friend class base::RefCountedThreadSafe<VulkanContextProvider>; friend class base::RefCountedThreadSafe<VulkanContextProvider>;
......
...@@ -17,6 +17,13 @@ ...@@ -17,6 +17,13 @@
#include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/vk/GrVkExtensions.h" #include "third_party/skia/include/gpu/vk/GrVkExtensions.h"
namespace {
// Setting this limit to 0 practically forces sync at every submit.
constexpr uint32_t kSyncCpuMemoryLimitAtMemoryPressureCritical = 0;
} // namespace
namespace viz { namespace viz {
// static // static
...@@ -25,10 +32,12 @@ VulkanInProcessContextProvider::Create( ...@@ -25,10 +32,12 @@ VulkanInProcessContextProvider::Create(
gpu::VulkanImplementation* vulkan_implementation, gpu::VulkanImplementation* vulkan_implementation,
uint32_t heap_memory_limit, uint32_t heap_memory_limit,
uint32_t sync_cpu_memory_limit, uint32_t sync_cpu_memory_limit,
const gpu::GPUInfo* gpu_info) { const gpu::GPUInfo* gpu_info,
base::TimeDelta cooldown_duration_at_memory_pressure_critical) {
scoped_refptr<VulkanInProcessContextProvider> context_provider( scoped_refptr<VulkanInProcessContextProvider> context_provider(
new VulkanInProcessContextProvider( new VulkanInProcessContextProvider(
vulkan_implementation, heap_memory_limit, sync_cpu_memory_limit)); vulkan_implementation, heap_memory_limit, sync_cpu_memory_limit,
cooldown_duration_at_memory_pressure_critical));
if (!context_provider->Initialize(gpu_info)) if (!context_provider->Initialize(gpu_info))
return nullptr; return nullptr;
return context_provider; return context_provider;
...@@ -37,10 +46,18 @@ VulkanInProcessContextProvider::Create( ...@@ -37,10 +46,18 @@ VulkanInProcessContextProvider::Create(
VulkanInProcessContextProvider::VulkanInProcessContextProvider( VulkanInProcessContextProvider::VulkanInProcessContextProvider(
gpu::VulkanImplementation* vulkan_implementation, gpu::VulkanImplementation* vulkan_implementation,
uint32_t heap_memory_limit, uint32_t heap_memory_limit,
uint32_t sync_cpu_memory_limit) uint32_t sync_cpu_memory_limit,
base::TimeDelta cooldown_duration_at_memory_pressure_critical)
: vulkan_implementation_(vulkan_implementation), : vulkan_implementation_(vulkan_implementation),
heap_memory_limit_(heap_memory_limit), heap_memory_limit_(heap_memory_limit),
sync_cpu_memory_limit_(sync_cpu_memory_limit) {} sync_cpu_memory_limit_(sync_cpu_memory_limit),
cooldown_duration_at_memory_pressure_critical_(
cooldown_duration_at_memory_pressure_critical) {
memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
FROM_HERE,
base::BindRepeating(&VulkanInProcessContextProvider::OnMemoryPressure,
base::Unretained(this)));
}
VulkanInProcessContextProvider::~VulkanInProcessContextProvider() { VulkanInProcessContextProvider::~VulkanInProcessContextProvider() {
Destroy(); Destroy();
...@@ -172,8 +189,24 @@ void VulkanInProcessContextProvider::EnqueueSecondaryCBPostSubmitTask( ...@@ -172,8 +189,24 @@ void VulkanInProcessContextProvider::EnqueueSecondaryCBPostSubmitTask(
NOTREACHED(); NOTREACHED();
} }
uint32_t VulkanInProcessContextProvider::GetSyncCpuMemoryLimit() const { base::Optional<uint32_t> VulkanInProcessContextProvider::GetSyncCpuMemoryLimit()
return sync_cpu_memory_limit_; const {
// Return false to indicate that there's no limit.
if (!sync_cpu_memory_limit_)
return base::Optional<uint32_t>();
return base::TimeTicks::Now() < critical_memory_pressure_expiration_time_
? base::Optional<uint32_t>(
kSyncCpuMemoryLimitAtMemoryPressureCritical)
: base::Optional<uint32_t>(sync_cpu_memory_limit_);
}
void VulkanInProcessContextProvider::OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel level) {
if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL)
return;
critical_memory_pressure_expiration_time_ =
base::TimeTicks::Now() + cooldown_duration_at_memory_pressure_critical_;
} }
} // namespace viz } // namespace viz
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "base/memory/memory_pressure_listener.h"
#include "base/time/time.h"
#include "components/viz/common/gpu/vulkan_context_provider.h" #include "components/viz/common/gpu/vulkan_context_provider.h"
#include "components/viz/common/viz_vulkan_context_provider_export.h" #include "components/viz/common/viz_vulkan_context_provider_export.h"
#include "gpu/vulkan/buildflags.h" #include "gpu/vulkan/buildflags.h"
...@@ -28,11 +30,17 @@ namespace viz { ...@@ -28,11 +30,17 @@ namespace viz {
class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanInProcessContextProvider class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanInProcessContextProvider
: public VulkanContextProvider { : public VulkanContextProvider {
public: public:
// if |sync_cpu_memory_limit| is set and greater than zero,
// |cooldown_duration_at_memory_pressure_critical| is the duration of applying
// zero sync cpu memory limit after CRITICAL memory pressure signal is
// received. 15s is default to sync with memory monitor cycles.
static scoped_refptr<VulkanInProcessContextProvider> Create( static scoped_refptr<VulkanInProcessContextProvider> Create(
gpu::VulkanImplementation* vulkan_implementation, gpu::VulkanImplementation* vulkan_implementation,
uint32_t heap_memory_limit = 0, uint32_t heap_memory_limit = 0,
uint32_t sync_cpu_memory_limit = 0, uint32_t sync_cpu_memory_limit = 0,
const gpu::GPUInfo* gpu_info = nullptr); const gpu::GPUInfo* gpu_info = nullptr,
base::TimeDelta cooldown_duration_at_memory_pressure_critical =
base::TimeDelta::FromSeconds(15));
void Destroy(); void Destroy();
...@@ -45,25 +53,36 @@ class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanInProcessContextProvider ...@@ -45,25 +53,36 @@ class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanInProcessContextProvider
void EnqueueSecondaryCBSemaphores( void EnqueueSecondaryCBSemaphores(
std::vector<VkSemaphore> semaphores) override; std::vector<VkSemaphore> semaphores) override;
void EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure) override; void EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure) override;
uint32_t GetSyncCpuMemoryLimit() const override; base::Optional<uint32_t> GetSyncCpuMemoryLimit() const override;
private: private:
explicit VulkanInProcessContextProvider( friend class VulkanInProcessContextProviderTest;
VulkanInProcessContextProvider(
gpu::VulkanImplementation* vulkan_implementation, gpu::VulkanImplementation* vulkan_implementation,
uint32_t heap_memory_limit, uint32_t heap_memory_limit,
uint32_t sync_cpu_memory_limit); uint32_t sync_cpu_memory_limit,
base::TimeDelta cooldown_duration_at_memory_pressure_critical);
~VulkanInProcessContextProvider() override; ~VulkanInProcessContextProvider() override;
bool Initialize(const gpu::GPUInfo* gpu_info); bool Initialize(const gpu::GPUInfo* gpu_info);
// Memory pressure handler, called by |memory_pressure_listener_|.
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel level);
#if BUILDFLAG(ENABLE_VULKAN) #if BUILDFLAG(ENABLE_VULKAN)
sk_sp<GrDirectContext> gr_context_; sk_sp<GrDirectContext> gr_context_;
gpu::VulkanImplementation* vulkan_implementation_; gpu::VulkanImplementation* vulkan_implementation_;
std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_; std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_;
const uint32_t heap_memory_limit_; const uint32_t heap_memory_limit_;
const uint32_t sync_cpu_memory_limit_; const uint32_t sync_cpu_memory_limit_;
const base::TimeDelta cooldown_duration_at_memory_pressure_critical_;
base::TimeTicks critical_memory_pressure_expiration_time_;
#endif #endif
std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
DISALLOW_COPY_AND_ASSIGN(VulkanInProcessContextProvider); DISALLOW_COPY_AND_ASSIGN(VulkanInProcessContextProvider);
}; };
......
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/common/gpu/vulkan_in_process_context_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace viz {
class VulkanInProcessContextProviderTest : public testing::Test {
public:
void CreateVulkanInProcessContextProvider(
uint32_t sync_cpu_memory_limit,
const base::TimeDelta& cooldown_duration_at_memory_pressure_critical) {
context_provider_ = new VulkanInProcessContextProvider(
nullptr, 0, sync_cpu_memory_limit,
cooldown_duration_at_memory_pressure_critical);
}
void TearDown() override { context_provider_.reset(); }
void SendCriticalMemoryPressureSignal() {
context_provider_->OnMemoryPressure(
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
}
protected:
scoped_refptr<VulkanInProcessContextProvider> context_provider_;
};
TEST_F(VulkanInProcessContextProviderTest,
NotifyMemoryPressureChangesSyncCpuMemoryLimit) {
const uint32_t kTestSyncCpuMemoryLimit = 1234;
CreateVulkanInProcessContextProvider(kTestSyncCpuMemoryLimit,
base::TimeDelta::Max());
auto limit = context_provider_->GetSyncCpuMemoryLimit();
EXPECT_TRUE(limit.has_value());
EXPECT_EQ(kTestSyncCpuMemoryLimit, limit.value());
SendCriticalMemoryPressureSignal();
limit = context_provider_->GetSyncCpuMemoryLimit();
EXPECT_TRUE(limit.has_value());
EXPECT_EQ(0u, limit.value());
}
TEST_F(VulkanInProcessContextProviderTest,
ZeroSyncCpuMemoryLimitDoesNotChange) {
CreateVulkanInProcessContextProvider(0, base::TimeDelta::Max());
auto limit = context_provider_->GetSyncCpuMemoryLimit();
EXPECT_FALSE(limit.has_value());
SendCriticalMemoryPressureSignal();
limit = context_provider_->GetSyncCpuMemoryLimit();
EXPECT_FALSE(limit.has_value());
}
TEST_F(VulkanInProcessContextProviderTest, SyncCpuMemoryResetsAfterCooldown) {
const uint32_t kTestSyncCpuMemoryLimit = 1234;
CreateVulkanInProcessContextProvider(kTestSyncCpuMemoryLimit,
base::TimeDelta::Min());
auto limit = context_provider_->GetSyncCpuMemoryLimit();
EXPECT_TRUE(limit.has_value());
EXPECT_EQ(kTestSyncCpuMemoryLimit, limit.value());
SendCriticalMemoryPressureSignal();
limit = context_provider_->GetSyncCpuMemoryLimit();
EXPECT_TRUE(limit.has_value());
EXPECT_EQ(kTestSyncCpuMemoryLimit, limit.value());
}
} // namespace viz
...@@ -297,11 +297,12 @@ bool ShouldVulkanSyncCpuForSkiaSubmit( ...@@ -297,11 +297,12 @@ bool ShouldVulkanSyncCpuForSkiaSubmit(
viz::VulkanContextProvider* context_provider) { viz::VulkanContextProvider* context_provider) {
#if BUILDFLAG(ENABLE_VULKAN) #if BUILDFLAG(ENABLE_VULKAN)
if (context_provider) { if (context_provider) {
uint32_t sync_cpu_memory_limit = context_provider->GetSyncCpuMemoryLimit(); const base::Optional<uint32_t>& sync_cpu_memory_limit =
if (sync_cpu_memory_limit) { context_provider->GetSyncCpuMemoryLimit();
if (sync_cpu_memory_limit.has_value()) {
uint64_t total_allocated_bytes = gpu::vma::GetTotalAllocatedMemory( uint64_t total_allocated_bytes = gpu::vma::GetTotalAllocatedMemory(
context_provider->GetDeviceQueue()->vma_allocator()); context_provider->GetDeviceQueue()->vma_allocator());
if (total_allocated_bytes > sync_cpu_memory_limit) { if (total_allocated_bytes > sync_cpu_memory_limit.value()) {
return true; return true;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment