blob: 645a1eb5b1e732157a985b8550a40b5176ccaf64 [file]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/service/dawn_caching_interface.h"
#include <cstring>
#include <string_view>
#include <tuple>
#include <variant>
#include "base/compiler_specific.h"
#include "base/containers/heap_array.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "base/trace_event/trace_event.h"
#include "gpu/config/gpu_preferences.h"
#include "net/base/io_buffer.h"
namespace wgpu::detail {
namespace {
using LoadCacheDataConverter =
CArgConverter<WGPUDawnLoadCacheDataCallbackInfo,
std::tuple<std::string_view, base::span<uint8_t>>>;
using StoreCacheDataConverter =
CArgConverter<WGPUDawnStoreCacheDataCallbackInfo,
std::tuple<std::string_view, base::span<const uint8_t>>>;
} // namespace
// static
LoadCacheDataConverter::Result LoadCacheDataConverter::Convert(
size_t key_size,
const uint8_t* key,
size_t value_size,
uint8_t* value) {
return std::make_tuple(
std::string_view(reinterpret_cast<const char*>(key), key_size),
// SAFETY: `key` and `value` are provided by Dawn with valid sizes.
UNSAFE_BUFFERS(base::span<uint8_t>(value, value_size)));
}
// static
StoreCacheDataConverter::Result StoreCacheDataConverter::Convert(
size_t key_size,
const uint8_t* key,
size_t value_size,
const uint8_t* value) {
return std::make_tuple(
std::string_view(reinterpret_cast<const char*>(key), key_size),
// SAFETY: `key` and `value` are provided by Dawn with valid sizes.
UNSAFE_BUFFERS(base::span<const uint8_t>(value, value_size)));
}
} // namespace wgpu::detail
namespace gpu::webgpu {
DawnInMemoryCache::DawnInMemoryCache(scoped_refptr<MemoryCache> backend,
CacheBlobCallback callback)
: memory_cache_backend_(std::move(backend)),
cache_blob_callback_(std::move(callback)) {}
DawnInMemoryCache::~DawnInMemoryCache() = default;
size_t DawnInMemoryCache::FindKey(std::string_view key) {
if (memory_cache() == nullptr) {
return 0u;
}
auto entry = memory_cache()->Find(key);
if (!entry) {
return 0u;
}
return entry->DataSize();
}
size_t DawnInMemoryCache::LoadData(std::string_view key,
base::span<uint8_t> dst) {
if (memory_cache() == nullptr) {
return 0u;
}
auto entry = memory_cache()->Find(key);
if (!entry) {
return 0u;
}
auto src = entry->Data();
if (src.size() <= dst.size()) {
dst.copy_prefix_from(src);
return entry->DataSize();
}
return 0u;
}
void DawnInMemoryCache::StoreData(std::string_view key,
base::span<const uint8_t> src) {
if (memory_cache() == nullptr || src.empty()) {
return;
}
memory_cache()->Store(key, src);
// Send the cache entry to be stored on the host-side if applicable.
if (cache_blob_callback_) {
std::string key_str_copy(key);
std::string src_str(reinterpret_cast<const char*>(src.data()), src.size());
cache_blob_callback_.Run(key_str_copy, src_str);
}
}
DawnCachingInterfaceFactory::DawnCachingInterfaceFactory(BackendFactory factory)
: backend_factory_(factory) {
if (base::SingleThreadTaskRunner::HasCurrentDefault()) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "DawnCache", base::SingleThreadTaskRunner::GetCurrentDefault());
}
}
DawnCachingInterfaceFactory::DawnCachingInterfaceFactory()
: DawnCachingInterfaceFactory(base::BindRepeating(
&DawnCachingInterfaceFactory::CreateDefaultInMemoryBackend)) {}
DawnCachingInterfaceFactory::~DawnCachingInterfaceFactory() {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
}
std::unique_ptr<DawnInMemoryCache> DawnCachingInterfaceFactory::CreateInstance(
const gpu::GpuDiskCacheHandle& handle,
DawnInMemoryCache::CacheBlobCallback callback) {
return base::WrapUnique(new DawnInMemoryCache(GetOrCreateMemoryCache(handle),
std::move(callback)));
}
std::unique_ptr<DawnInMemoryCache>
DawnCachingInterfaceFactory::CreateInstance() {
return base::WrapUnique(new DawnInMemoryCache(backend_factory_.Run()));
}
scoped_refptr<MemoryCache> DawnCachingInterfaceFactory::GetOrCreateMemoryCache(
const gpu::GpuDiskCacheHandle& handle) {
DCHECK(gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnWebGPU ||
gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnGraphite);
if (const auto it = backends_.find(handle); it != backends_.end()) {
return it->second;
}
scoped_refptr<MemoryCache> backend = backend_factory_.Run();
if (backend != nullptr) {
backend->OnUpdateMemoryLimit(current_memory_limit_);
backends_[handle] = backend;
}
return backend;
}
void DawnCachingInterfaceFactory::ReleaseHandle(
const gpu::GpuDiskCacheHandle& handle) {
DCHECK(gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnWebGPU ||
gpu::GetHandleType(handle) == gpu::GpuDiskCacheType::kDawnGraphite);
backends_.erase(handle);
}
void DawnCachingInterfaceFactory::OnUpdateMemoryLimit(
base::MemoryLimit memory_limit) {
current_memory_limit_ = memory_limit;
for (auto& [key, backend] : backends_) {
backend->OnUpdateMemoryLimit(memory_limit);
}
}
void DawnCachingInterfaceFactory::OnReleaseMemory(
base::MemoryLimit memory_limit) {
current_memory_limit_ = memory_limit;
for (auto& [key, backend] : backends_) {
CHECK(std::holds_alternative<GpuDiskCacheDawnGraphiteHandle>(key) ||
std::holds_alternative<GpuDiskCacheDawnWebGPUHandle>(key));
backend->OnReleaseMemory(memory_limit);
}
}
bool DawnCachingInterfaceFactory::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
const bool is_background =
args.level_of_detail ==
base::trace_event::MemoryDumpLevelOfDetail::kBackground;
for (auto& [key, backend] : backends_) {
if (std::holds_alternative<GpuDiskCacheDawnGraphiteHandle>(key)) {
// There should only be a single graphite cache.
backend->OnMemoryDump("gpu/shader_cache/graphite_cache", pmd);
} else if (!is_background &&
std::holds_alternative<GpuDiskCacheDawnWebGPUHandle>(key)) {
// Note that in memory only webgpu caches aren't stored in `backends_` so
// they won't produce memory dumps.
std::string dump_name = base::StringPrintf(
"gpu/shader_cache/webgpu_cache_0x%X", GetHandleValue(key));
backend->OnMemoryDump(dump_name, pmd);
}
}
return true;
}
scoped_refptr<MemoryCache>
DawnCachingInterfaceFactory::CreateDefaultInMemoryBackend() {
return base::MakeRefCounted<MemoryCache>(GetDefaultGpuDiskCacheSize(),
"DawnCachingInterface::CacheHit");
}
} // namespace gpu::webgpu