[go: nahoru, domu]

blob: 4147bbebc3e5f701c7dbbc5bab9c23be9fe40eee [file] [log] [blame]
// Copyright 2023 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/ipc/service/built_in_shader_cache_loader.h"
#include "base/apple/foundation_util.h"
#include "base/files/file.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "gpu/ipc/service/built_in_shader_cache_writer.h"
namespace gpu {
namespace {
// File the shaders are put in.
const char kShaderCacheFileName[] = "gpu_shader_cache.bin";
// A single instance is created. It is destroyed once the values are taken.
static BuiltInShaderCacheLoader* g_loader = nullptr;
// Responsible for reading the shader file.
class FileReader {
public:
bool Init(const base::FilePath& path) {
file_.Initialize(AdjustPath(path),
base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file_.IsValid()) {
LOG(WARNING) << "Failed opening metal shader cache";
return false;
}
return ReadHeader();
}
bool ReadHeader() {
uint32_t header;
if (!ReadBytes(sizeof(header), reinterpret_cast<char*>(&header))) {
return false;
}
return header == BuiltInShaderCacheWriter::kSignature;
}
bool ReadKeyOrValue(std::vector<uint8_t>& data) {
// uint32_t corresponds to how the data is written. See writer for details.
uint32_t data_size;
if (!ReadBytes(sizeof(data_size), reinterpret_cast<char*>(&data_size))) {
return false;
}
if (data_size == 0) {
// Invalid data size.
return false;
}
data.resize(data_size);
return ReadBytes(data_size, reinterpret_cast<char*>(&data.front()));
}
private:
static base::FilePath AdjustPath(const base::FilePath& path) {
return path.empty() ? base::apple::PathForFrameworkBundleResource(
kShaderCacheFileName)
: path;
}
bool ReadBytes(uint32_t size, char* data) {
char* data_ptr = data;
char* data_end = data + size;
while (!AtEndOrErrored()) {
if (!RemainingBytesInBuffer() && !FillBuffer()) {
return false;
}
const uint32_t bytes_to_copy = std::min(
static_cast<uint32_t>(data_end - data_ptr), RemainingBytesInBuffer());
CopyFromBufferAndAdvance(bytes_to_copy, data_ptr);
data_ptr += bytes_to_copy;
if (data_ptr == data_end) {
return true;
}
}
return false;
}
bool AtEndOrErrored() const { return at_end_or_errored_; }
void CopyFromBufferAndAdvance(uint32_t num_bytes, char* data) {
CHECK_LE(num_bytes, RemainingBytesInBuffer());
memcpy(data, read_buffer_ + current_pos_, num_bytes);
current_pos_ += num_bytes;
}
uint32_t RemainingBytesInBuffer() const {
CHECK_GE(bytes_available_, current_pos_);
return bytes_available_ - current_pos_;
}
bool FillBuffer() {
current_pos_ = 0;
int read = file_.ReadAtCurrentPos(read_buffer_, kReadBufferSize);
if (read <= 0) {
bytes_available_ = 0;
at_end_or_errored_ = true;
return false;
}
bytes_available_ = static_cast<uint32_t>(read);
return true;
}
static constexpr size_t kReadBufferSize = 4096;
base::File file_;
char read_buffer_[kReadBufferSize];
uint32_t current_pos_ = 0;
uint32_t bytes_available_ = 0;
bool at_end_or_errored_ = false;
};
} // namespace
BuiltInShaderCacheLoader::CacheEntry::CacheEntry() = default;
BuiltInShaderCacheLoader::CacheEntry::CacheEntry(CacheEntry&& other) = default;
BuiltInShaderCacheLoader::CacheEntry::~CacheEntry() = default;
// static
void BuiltInShaderCacheLoader::StartLoading() {
CHECK(!g_loader);
// Destroyed when finished loading.
g_loader = new BuiltInShaderCacheLoader;
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
base::BindOnce(&BuiltInShaderCacheLoader::Load,
base::Unretained(g_loader), base::FilePath()));
}
// static
std::unique_ptr<std::vector<BuiltInShaderCacheLoader::CacheEntry>>
BuiltInShaderCacheLoader::TakeEntries() {
// This is always called, but StartLoading() is only called if loading is
// needed.
if (!g_loader) {
return std::make_unique<std::vector<CacheEntry>>();
}
auto entries = g_loader->TakeEntriesImpl();
delete g_loader;
g_loader = nullptr;
return entries;
}
std::unique_ptr<std::vector<BuiltInShaderCacheLoader::CacheEntry>>
BuiltInShaderCacheLoader::TakeEntriesImpl() {
const base::TimeTicks start_time = base::TimeTicks::Now();
loaded_signaler_.Wait();
const base::TimeTicks end_time = base::TimeTicks::Now();
base::UmaHistogramCustomMicrosecondsTimes(
"Gpu.MetalShaderCache.WaitTime", end_time - start_time,
base::Microseconds(1), base::Milliseconds(100), 100);
std::unique_ptr<std::vector<CacheEntry>> entries =
std::make_unique<std::vector<CacheEntry>>(std::move(entries_));
return entries;
}
BuiltInShaderCacheLoader::BuiltInShaderCacheLoader() = default;
BuiltInShaderCacheLoader::~BuiltInShaderCacheLoader() = default;
void BuiltInShaderCacheLoader::Load(const base::FilePath& path) {
const base::TimeTicks start_time = base::TimeTicks::Now();
LoadImpl(path);
const base::TimeTicks end_time = base::TimeTicks::Now();
base::UmaHistogramCustomMicrosecondsTimes(
"Gpu.MetalShaderCache.LoadTime", end_time - start_time,
base::Microseconds(10), base::Milliseconds(100), 100);
base::UmaHistogramCounts100("Gpu.MetalShaderCache.NumEntriesInCache",
entries_.size());
loaded_signaler_.Signal();
}
void BuiltInShaderCacheLoader::LoadImpl(const base::FilePath& path) {
FileReader reader;
if (!reader.Init(path)) {
return;
}
for (;;) {
CacheEntry entry;
if (!reader.ReadKeyOrValue(entry.key) ||
!reader.ReadKeyOrValue(entry.value)) {
return;
}
entries_.push_back(std::move(entry));
}
}
} // namespace gpu