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(
post_submit_tasks_.push_back(std::move(closure));
}
uint32_t AwVulkanContextProvider::GetSyncCpuMemoryLimit() const {
return 0;
base::Optional<uint32_t> AwVulkanContextProvider::GetSyncCpuMemoryLimit()
const {
return base::Optional<uint32_t>();
}
bool AwVulkanContextProvider::Initialize(AwDrawFn_InitVkParams* params) {
......
......@@ -56,7 +56,7 @@ class AwVulkanContextProvider final : public viz::VulkanContextProvider {
void EnqueueSecondaryCBSemaphores(
std::vector<VkSemaphore> semaphores) 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(); }
VkQueue queue() { return globals_->device_queue->GetVulkanQueue(); }
......
......@@ -385,6 +385,9 @@ viz_source_set("unit_tests") {
]
}
if (enable_vulkan) {
sources += [ "gpu/vulkan_in_process_context_provider_unittest.cc" ]
}
deps = [
":common",
"//base/test:test_support",
......
......@@ -48,12 +48,12 @@ class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanContextProvider
// semphores are submitted.
virtual void EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure) = 0;
// Returns the memory limit where GPU work should be synchronized with the CPU
// in order to free previously released memory immediately. In other words,
// the CPU will wait for GPU work to complete before proceeding when the
// current amount of allocated memory exceeds this limit. Zero return value
// indicate that there's no limit.
virtual uint32_t GetSyncCpuMemoryLimit() const = 0;
// Returns a valid limit in MB if there is a memory limit where GPU work
// should be synchronized with the CPU in order to free previously released
// memory immediately. In other words, the CPU will wait for GPU work to
// complete before proceeding when the current amount of allocated memory
// exceeds this limit.
virtual base::Optional<uint32_t> GetSyncCpuMemoryLimit() const = 0;
protected:
friend class base::RefCountedThreadSafe<VulkanContextProvider>;
......
......@@ -17,6 +17,13 @@
#include "third_party/skia/include/gpu/GrDirectContext.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 {
// static
......@@ -25,10 +32,12 @@ VulkanInProcessContextProvider::Create(
gpu::VulkanImplementation* vulkan_implementation,
uint32_t heap_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(
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))
return nullptr;
return context_provider;
......@@ -37,10 +46,18 @@ VulkanInProcessContextProvider::Create(
VulkanInProcessContextProvider::VulkanInProcessContextProvider(
gpu::VulkanImplementation* vulkan_implementation,
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),
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() {
Destroy();
......@@ -172,8 +189,24 @@ void VulkanInProcessContextProvider::EnqueueSecondaryCBPostSubmitTask(
NOTREACHED();
}
uint32_t VulkanInProcessContextProvider::GetSyncCpuMemoryLimit() const {
return sync_cpu_memory_limit_;
base::Optional<uint32_t> VulkanInProcessContextProvider::GetSyncCpuMemoryLimit()
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
......@@ -8,6 +8,8 @@
#include <memory>
#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/viz_vulkan_context_provider_export.h"
#include "gpu/vulkan/buildflags.h"
......@@ -28,11 +30,17 @@ namespace viz {
class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanInProcessContextProvider
: public VulkanContextProvider {
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(
gpu::VulkanImplementation* vulkan_implementation,
uint32_t heap_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();
......@@ -45,25 +53,36 @@ class VIZ_VULKAN_CONTEXT_PROVIDER_EXPORT VulkanInProcessContextProvider
void EnqueueSecondaryCBSemaphores(
std::vector<VkSemaphore> semaphores) override;
void EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure) override;
uint32_t GetSyncCpuMemoryLimit() const override;
base::Optional<uint32_t> GetSyncCpuMemoryLimit() const override;
private:
explicit VulkanInProcessContextProvider(
friend class VulkanInProcessContextProviderTest;
VulkanInProcessContextProvider(
gpu::VulkanImplementation* vulkan_implementation,
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;
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)
sk_sp<GrDirectContext> gr_context_;
gpu::VulkanImplementation* vulkan_implementation_;
std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_;
const uint32_t heap_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
std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
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(
viz::VulkanContextProvider* context_provider) {
#if BUILDFLAG(ENABLE_VULKAN)
if (context_provider) {
uint32_t sync_cpu_memory_limit = context_provider->GetSyncCpuMemoryLimit();
if (sync_cpu_memory_limit) {
const base::Optional<uint32_t>& sync_cpu_memory_limit =
context_provider->GetSyncCpuMemoryLimit();
if (sync_cpu_memory_limit.has_value()) {
uint64_t total_allocated_bytes = gpu::vma::GetTotalAllocatedMemory(
context_provider->GetDeviceQueue()->vma_allocator());
if (total_allocated_bytes > sync_cpu_memory_limit) {
if (total_allocated_bytes > sync_cpu_memory_limit.value()) {
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