Adds device-side cache key generation.

- Note that the device-side cache key will be prepended to object cache keys to prevent incompatible adapter/device cache clashes.
- Adds a new template file to auto=generate these cache serializers based on arguments.

Bug: dawn:549
Change-Id: I24b9d11eb38c579acfcc173a5dced9e1b649cf2c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/86081
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
This commit is contained in:
Loko Kung
2022-04-12 23:50:56 +00:00
committed by Dawn LUCI CQ
parent 792873ecb6
commit c083d65a0c
7 changed files with 160 additions and 26 deletions

View File

@@ -119,6 +119,7 @@ dawn_json_generator("utils_gen") {
"src/dawn/native/webgpu_absl_format_autogen.cpp",
"src/dawn/native/ObjectType_autogen.h",
"src/dawn/native/ObjectType_autogen.cpp",
"src/dawn/native/CacheKey_autogen.cpp",
]
}

View File

@@ -15,6 +15,7 @@
#ifndef SRC_DAWN_NATIVE_CACHEKEY_H_
#define SRC_DAWN_NATIVE_CACHEKEY_H_
#include <bitset>
#include <iostream>
#include <limits>
#include <string>
@@ -85,6 +86,41 @@ namespace dawn::native {
}
};
// Specialized overload for bitsets that are smaller than 64.
template <size_t N>
class CacheKeySerializer<std::bitset<N>, std::enable_if_t<(N <= 64)>> {
public:
static void Serialize(CacheKey* key, const std::bitset<N>& t) {
key->Record(t.to_ullong());
}
};
// Specialized overload for bitsets since using the built-in to_ullong have a size limit.
template <size_t N>
class CacheKeySerializer<std::bitset<N>, std::enable_if_t<(N > 64)>> {
public:
static void Serialize(CacheKey* key, const std::bitset<N>& t) {
// Serializes the bitset into series of uint8_t, along with recording the size.
static_assert(N > 0);
key->Record(static_cast<size_t>(N));
uint8_t value = 0;
for (size_t i = 0; i < N; i++) {
value <<= 1;
// Explicitly convert to numeric since MSVC doesn't like mixing of bools.
value |= t[i] ? 1 : 0;
if (i % 8 == 7) {
// Whenever we fill an 8 bit value, record it and zero it out.
key->Record(value);
value = 0;
}
}
// Serialize the last value if we are not a multiple of 8.
if (N % 8 != 0) {
key->Record(value);
}
}
};
// Specialized overload for enums.
template <typename T>
class CacheKeySerializer<T, std::enable_if_t<std::is_enum_v<T>>> {

View File

@@ -177,6 +177,9 @@ namespace dawn::native {
: mInstance(adapter->GetInstance()), mAdapter(adapter), mNextPipelineCompatibilityToken(1) {
ASSERT(descriptor != nullptr);
AdapterProperties adapterProperties;
adapter->APIGetProperties(&adapterProperties);
const DawnTogglesDeviceDescriptor* togglesDesc = nullptr;
FindInChain(descriptor->nextInChain, &togglesDesc);
if (togglesDesc != nullptr) {
@@ -184,10 +187,11 @@ namespace dawn::native {
}
ApplyFeatures(descriptor);
DawnCacheDeviceDescriptor defaultCacheDesc = {};
const DawnCacheDeviceDescriptor* cacheDesc = nullptr;
FindInChain(descriptor->nextInChain, &cacheDesc);
if (cacheDesc != nullptr) {
mCacheIsolationKey = cacheDesc->isolationKey;
if (cacheDesc == nullptr) {
cacheDesc = &defaultCacheDesc;
}
if (descriptor->requiredLimits != nullptr) {
@@ -202,6 +206,12 @@ namespace dawn::native {
if (descriptor->label != nullptr && strlen(descriptor->label) != 0) {
mLabel = descriptor->label;
}
// Record the cache key from the properties. Note that currently, if a new extension
// descriptor is added (and probably handled here), the cache key recording needs to be
// updated.
mDeviceCacheKey.Record(adapterProperties, mEnabledFeatures.featuresBitSet,
mEnabledToggles.toggleBitset, cacheDesc);
}
DeviceBase::DeviceBase() : mState(State::Alive) {
@@ -1789,8 +1799,8 @@ namespace dawn::native {
return PipelineCompatibilityToken(mNextPipelineCompatibilityToken++);
}
const std::string& DeviceBase::GetCacheIsolationKey() const {
return mCacheIsolationKey;
const CacheKey& DeviceBase::GetCacheKey() const {
return mDeviceCacheKey;
}
const std::string& DeviceBase::GetLabel() const {

View File

@@ -15,6 +15,7 @@
#ifndef SRC_DAWN_NATIVE_DEVICE_H_
#define SRC_DAWN_NATIVE_DEVICE_H_
#include "dawn/native/CacheKey.h"
#include "dawn/native/Commands.h"
#include "dawn/native/ComputePipeline.h"
#include "dawn/native/Error.h"
@@ -370,7 +371,7 @@ namespace dawn::native {
PipelineCompatibilityToken GetNextPipelineCompatibilityToken();
const std::string& GetCacheIsolationKey() const;
const CacheKey& GetCacheKey() const;
const std::string& GetLabel() const;
void APISetLabel(const char* label);
void APIDestroy();
@@ -547,7 +548,7 @@ namespace dawn::native {
std::unique_ptr<CallbackTaskManager> mCallbackTaskManager;
std::unique_ptr<dawn::platform::WorkerTaskPool> mWorkerTaskPool;
std::string mLabel;
std::string mCacheIsolationKey = "";
CacheKey mDeviceCacheKey;
};
} // namespace dawn::native

View File

@@ -86,41 +86,55 @@ namespace {
}
TEST_F(DeviceCreationTest, CreateDeviceWithCacheSuccess) {
// Default device descriptor should have an empty cache isolation key.
// Default device descriptor should have the same cache key as a device descriptor with a
// default cache descriptor.
{
wgpu::DeviceDescriptor desc = {};
wgpu::Device device = adapter.CreateDevice(&desc);
EXPECT_NE(device, nullptr);
wgpu::Device device1 = adapter.CreateDevice(&desc);
EXPECT_NE(device1, nullptr);
EXPECT_THAT(dawn::native::FromAPI(device.Get())->GetCacheIsolationKey(),
testing::StrEq(""));
}
// Device descriptor with empty cache descriptor should have an empty cache isolation key.
{
wgpu::DeviceDescriptor desc = {};
wgpu::DawnCacheDeviceDescriptor cacheDesc = {};
desc.nextInChain = &cacheDesc;
wgpu::Device device2 = adapter.CreateDevice(&desc);
wgpu::Device device = adapter.CreateDevice(&desc);
EXPECT_NE(device, nullptr);
EXPECT_THAT(dawn::native::FromAPI(device.Get())->GetCacheIsolationKey(),
testing::StrEq(""));
EXPECT_EQ(dawn::native::FromAPI(device1.Get())->GetCacheKey(),
dawn::native::FromAPI(device2.Get())->GetCacheKey());
}
// Specified cache isolation key should be retained.
// Default device descriptor should not have the same cache key as a device descriptor with
// a non-default cache descriptor.
{
wgpu::DeviceDescriptor desc = {};
wgpu::Device device1 = adapter.CreateDevice(&desc);
EXPECT_NE(device1, nullptr);
wgpu::DawnCacheDeviceDescriptor cacheDesc = {};
desc.nextInChain = &cacheDesc;
const char* isolationKey = "isolation key";
cacheDesc.isolationKey = isolationKey;
wgpu::Device device2 = adapter.CreateDevice(&desc);
EXPECT_NE(device2, nullptr);
wgpu::Device device = adapter.CreateDevice(&desc);
EXPECT_NE(device, nullptr);
EXPECT_NE(dawn::native::FromAPI(device1.Get())->GetCacheKey(),
dawn::native::FromAPI(device2.Get())->GetCacheKey());
}
// Two non-default cache descriptors should not have the same cache key.
{
wgpu::DawnCacheDeviceDescriptor cacheDesc = {};
const char* isolationKey1 = "isolation key 1";
const char* isolationKey2 = "isolation key 2";
wgpu::DeviceDescriptor desc = {};
desc.nextInChain = &cacheDesc;
EXPECT_THAT(dawn::native::FromAPI(device.Get())->GetCacheIsolationKey(),
testing::StrEq(isolationKey));
cacheDesc.isolationKey = isolationKey1;
wgpu::Device device1 = adapter.CreateDevice(&desc);
EXPECT_NE(device1, nullptr);
cacheDesc.isolationKey = isolationKey2;
wgpu::Device device2 = adapter.CreateDevice(&desc);
EXPECT_NE(device2, nullptr);
EXPECT_NE(dawn::native::FromAPI(device1.Get())->GetCacheKey(),
dawn::native::FromAPI(device2.Get())->GetCacheKey());
}
}