blob: f9485edcbd19f9d2f432860c1be557245c598620 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/gpu/peak_gpu_memory_tracker_impl.h"
#include "base/clang_profiling_buildflags.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/viz/test/gpu_host_impl_test_api.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/test_utils.h"
#include "gpu/ipc/common/gpu_peak_memory.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
namespace content {
namespace {
const uint64_t kPeakMemoryMB = 42u;
const uint64_t kPeakMemory = kPeakMemoryMB * 1048576u;
// Test implementation of viz::mojom::GpuService which only implements the peak
// memory monitoring aspects.
class TestGpuService : public viz::mojom::GpuService {
public:
explicit TestGpuService(base::RepeatingClosure quit_closure)
: quit_closure_(quit_closure) {}
TestGpuService(const TestGpuService*) = delete;
~TestGpuService() override = default;
TestGpuService& operator=(const TestGpuService&) = delete;
// mojom::GpuService:
void StartPeakMemoryMonitor(uint32_t sequence_num) override {
quit_closure_.Run();
}
void GetPeakMemoryUsage(uint32_t sequence_num,
GetPeakMemoryUsageCallback callback) override {
base::flat_map<gpu::GpuPeakMemoryAllocationSource, uint64_t>
allocation_per_source;
allocation_per_source[gpu::GpuPeakMemoryAllocationSource::UNKNOWN] =
kPeakMemory;
std::move(callback).Run(kPeakMemory, allocation_per_source);
}
private:
// mojom::GpuService:
void EstablishGpuChannel(int32_t client_id,
uint64_t client_tracing_id,
bool is_gpu_host,
EstablishGpuChannelCallback callback) override {}
void SetChannelClientPid(int32_t client_id,
base::ProcessId client_pid) override {}
void SetChannelDiskCacheHandle(
int32_t client_id,
const gpu::GpuDiskCacheHandle& handle) override {}
void OnDiskCacheHandleDestoyed(
const gpu::GpuDiskCacheHandle& handle) override {}
void CloseChannel(int32_t client_id) override {}
#if BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
void CreateArcVideoDecodeAccelerator(
mojo::PendingReceiver<arc::mojom::VideoDecodeAccelerator> vda_receiver)
override {}
void CreateArcVideoDecoder(
mojo::PendingReceiver<arc::mojom::VideoDecoder> vd_receiver) override {}
void CreateArcVideoEncodeAccelerator(
mojo::PendingReceiver<arc::mojom::VideoEncodeAccelerator> vea_receiver)
override {}
void CreateArcVideoProtectedBufferAllocator(
mojo::PendingReceiver<arc::mojom::VideoProtectedBufferAllocator>
pba_receiver) override {}
void CreateArcProtectedBufferManager(
mojo::PendingReceiver<arc::mojom::ProtectedBufferManager> pbm_receiver)
override {}
#endif // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
void CreateJpegDecodeAccelerator(
mojo::PendingReceiver<chromeos_camera::mojom::MjpegDecodeAccelerator>
jda_receiver) override {}
void CreateJpegEncodeAccelerator(
mojo::PendingReceiver<chromeos_camera::mojom::JpegEncodeAccelerator>
jea_receiver) override {}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_WIN)
void RegisterDCOMPSurfaceHandle(
mojo::PlatformHandle surface_handle,
RegisterDCOMPSurfaceHandleCallback callback) override {}
void UnregisterDCOMPSurfaceHandle(
const base::UnguessableToken& token) override {}
#endif
void CreateVideoEncodeAcceleratorProvider(
mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
receiver) override {}
void CreateGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
int client_id,
gpu::SurfaceHandle surface_handle,
CreateGpuMemoryBufferCallback callback) override {}
void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
int client_id) override {}
void CopyGpuMemoryBuffer(::gfx::GpuMemoryBufferHandle buffer_handle,
::base::UnsafeSharedMemoryRegion shared_memory,
CopyGpuMemoryBufferCallback callback) override {}
void GetVideoMemoryUsageStats(
GetVideoMemoryUsageStatsCallback callback) override {}
#if BUILDFLAG(IS_WIN)
void RequestDXGIInfo(RequestDXGIInfoCallback callback) override {}
#endif
void LoadedBlob(const gpu::GpuDiskCacheHandle& handle,
const std::string& key,
const std::string& data) override {}
void WakeUpGpu() override {}
void GpuSwitched(gl::GpuPreference active_gpu_heuristic) override {}
void DisplayAdded() override {}
void DisplayRemoved() override {}
void DisplayMetricsChanged() override {}
void DestroyAllChannels() override {}
void OnBackgroundCleanup() override {}
void OnBackgrounded() override {}
void OnForegrounded() override {}
#if !BUILDFLAG(IS_ANDROID)
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel level) override {}
#endif
#if BUILDFLAG(IS_APPLE)
void BeginCATransaction() override {}
void CommitCATransaction(CommitCATransactionCallback callback) override {}
#endif
#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
void WriteClangProfilingProfile(
WriteClangProfilingProfileCallback callback) override {}
#endif
void GetDawnInfo(GetDawnInfoCallback callback) override {}
void Crash() override {}
void Hang() override {}
void ThrowJavaException() override {}
base::RepeatingClosure quit_closure_;
};
} // namespace
class PeakGpuMemoryTrackerImplTest : public ContentBrowserTest {
public:
PeakGpuMemoryTrackerImplTest() = default;
~PeakGpuMemoryTrackerImplTest() override = default;
// Waits until all messages to the mojo::Remote<viz::mojom::GpuService> have
// been processed.
void FlushRemoteForTesting() {
gpu_host_impl_test_api_->FlushRemoteForTesting();
}
void SetTestingCallback(PeakGpuMemoryTracker* tracker,
base::OnceClosure callback) {
static_cast<PeakGpuMemoryTrackerImpl*>(tracker)
->post_gpu_service_callback_for_testing_ = std::move(callback);
}
// Provides access to the TestGpuService on the Main Thread for test
// verifications. All mojo calls should be performed on the IO Thread.
TestGpuService* gpu_service() const { return test_gpu_service_.get(); }
// Setup requires that we have the Browser threads still initialized.
// ContentBrowserTest:
void PreRunTestOnMainThread() override {
run_loop_for_start_ = std::make_unique<base::RunLoop>();
ContentBrowserTest::PreRunTestOnMainThread();
// Initializes the TestGpuService, and installs it as the active service.
gpu_host_impl_test_api_ = std::make_unique<viz::GpuHostImplTestApi>(
GpuProcessHost::Get()->gpu_host());
test_gpu_service_ =
std::make_unique<TestGpuService>(run_loop_for_start_->QuitClosure());
mojo::Remote<viz::mojom::GpuService> gpu_service_remote;
gpu_service_receiver_ =
std::make_unique<mojo::Receiver<viz::mojom::GpuService>>(
test_gpu_service_.get(),
gpu_service_remote.BindNewPipeAndPassReceiver());
gpu_host_impl_test_api_->SetGpuService(std::move(gpu_service_remote));
}
void PostRunTestOnMainThread() override {
gpu_service_receiver_.reset();
ContentBrowserTest::PostRunTestOnMainThread();
}
void WaitForStartPeakMemoryMonitor() { run_loop_for_start_->Run(); }
private:
std::unique_ptr<base::RunLoop> run_loop_for_start_;
std::unique_ptr<TestGpuService> test_gpu_service_;
std::unique_ptr<viz::GpuHostImplTestApi> gpu_host_impl_test_api_;
std::unique_ptr<mojo::Receiver<viz::mojom::GpuService>> gpu_service_receiver_;
};
// Verifies that when a PeakGpuMemoryTracker is destroyed, that the browser's
// callback properly updates the histograms.
IN_PROC_BROWSER_TEST_F(PeakGpuMemoryTrackerImplTest, PeakGpuMemoryCallback) {
base::HistogramTester histogram;
base::RunLoop run_loop;
std::unique_ptr<PeakGpuMemoryTracker> tracker =
PeakGpuMemoryTracker::Create(PeakGpuMemoryTracker::Usage::PAGE_LOAD);
SetTestingCallback(tracker.get(), run_loop.QuitClosure());
FlushRemoteForTesting();
// No report in response to creation.
histogram.ExpectTotalCount("Memory.GPU.PeakMemoryUsage2.PageLoad", 0);
histogram.ExpectTotalCount(
"Memory.GPU.PeakMemoryAllocationSource.PageLoad.Unknown", 0);
// However the serive should have started monitoring.
WaitForStartPeakMemoryMonitor();
// Deleting the tracker should start a request for peak Gpu memory usage, with
// the callback being a posted task.
tracker.reset();
FlushRemoteForTesting();
// Wait for callback to be ran on the IO thread, which will call the
// QuitClosure.
run_loop.Run();
histogram.ExpectUniqueSample("Memory.GPU.PeakMemoryUsage2.PageLoad",
kPeakMemoryMB, 1);
histogram.ExpectUniqueSample(
"Memory.GPU.PeakMemoryAllocationSource2.PageLoad.Unknown", kPeakMemoryMB,
1);
}
} // namespace content