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:
parent
792873ecb6
commit
c083d65a0c
|
@ -955,6 +955,10 @@ class MultiGeneratorFromDawnJSON(Generator):
|
||||||
FileRender('dawn/native/ObjectType.cpp',
|
FileRender('dawn/native/ObjectType.cpp',
|
||||||
'src/' + native_dir + '/ObjectType_autogen.cpp',
|
'src/' + native_dir + '/ObjectType_autogen.cpp',
|
||||||
frontend_params))
|
frontend_params))
|
||||||
|
renders.append(
|
||||||
|
FileRender('dawn/native/CacheKey.cpp',
|
||||||
|
'src/' + native_dir + '/CacheKey_autogen.cpp',
|
||||||
|
frontend_params))
|
||||||
|
|
||||||
if 'wire' in targets:
|
if 'wire' in targets:
|
||||||
params_dawn_wire = parse_json(loaded_json,
|
params_dawn_wire = parse_json(loaded_json,
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
//* Copyright 2022 The Dawn Authors
|
||||||
|
//*
|
||||||
|
//* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
//* you may not use this file except in compliance with the License.
|
||||||
|
//* You may obtain a copy of the License at
|
||||||
|
//*
|
||||||
|
//* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//*
|
||||||
|
//* Unless required by applicable law or agreed to in writing, software
|
||||||
|
//* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//* See the License for the specific language governing permissions and
|
||||||
|
//* limitations under the License.
|
||||||
|
|
||||||
|
{% set impl_dir = metadata.impl_dir + "/" if metadata.impl_dir else "" %}
|
||||||
|
{% set namespace_name = Name(metadata.native_namespace) %}
|
||||||
|
{% set native_namespace = namespace_name.namespace_case() %}
|
||||||
|
{% set native_dir = impl_dir + namespace_name.Dirs() %}
|
||||||
|
{% set prefix = metadata.proc_table_prefix.lower() %}
|
||||||
|
#include "{{native_dir}}/CacheKey.h"
|
||||||
|
#include "{{native_dir}}/{{prefix}}_platform.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace {{native_namespace}} {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cache key serializers for wgpu structures used in caching.
|
||||||
|
//
|
||||||
|
{% macro render_serializer(member) %}
|
||||||
|
{%- set name = member.name.camelCase() -%}
|
||||||
|
{% if member.length == None %}
|
||||||
|
key->Record(t.{{name}});
|
||||||
|
{% elif member.length == "strlen" %}
|
||||||
|
key->RecordIterable(t.{{name}}, strlen(t.{{name}}));
|
||||||
|
{% else %}
|
||||||
|
key->RecordIterable(t.{{name}}, t.{{member.length.name.camelCase()}});
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{# Helper macro to render serializers. Should be used in a call block to provide additional custom
|
||||||
|
handling when necessary. The optional `omit` field can be used to omit fields that are either
|
||||||
|
handled in the custom code, or unnecessary in the serialized output.
|
||||||
|
Example:
|
||||||
|
{% call render_cache_key_serializer("struct name", omits=["omit field"]) %}
|
||||||
|
// Custom C++ code to handle special types/members that are hard to generate code for
|
||||||
|
{% endcall %}
|
||||||
|
#}
|
||||||
|
{% macro render_cache_key_serializer(json_type, omits=[]) %}
|
||||||
|
{%- set cpp_type = types[json_type].name.CamelCase() -%}
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<{{cpp_type}}>::Serialize(CacheKey* key, const {{cpp_type}}& t) {
|
||||||
|
{{ caller() }}
|
||||||
|
{% for member in types[json_type].members %}
|
||||||
|
{%- if not member.name.get() in omits %}
|
||||||
|
{{render_serializer(member)}}
|
||||||
|
{%- endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% call render_cache_key_serializer("adapter properties") %}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
{% call render_cache_key_serializer("dawn cache device descriptor") %}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
} // namespace {{native_namespace}}
|
|
@ -119,6 +119,7 @@ dawn_json_generator("utils_gen") {
|
||||||
"src/dawn/native/webgpu_absl_format_autogen.cpp",
|
"src/dawn/native/webgpu_absl_format_autogen.cpp",
|
||||||
"src/dawn/native/ObjectType_autogen.h",
|
"src/dawn/native/ObjectType_autogen.h",
|
||||||
"src/dawn/native/ObjectType_autogen.cpp",
|
"src/dawn/native/ObjectType_autogen.cpp",
|
||||||
|
"src/dawn/native/CacheKey_autogen.cpp",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#ifndef SRC_DAWN_NATIVE_CACHEKEY_H_
|
#ifndef SRC_DAWN_NATIVE_CACHEKEY_H_
|
||||||
#define SRC_DAWN_NATIVE_CACHEKEY_H_
|
#define SRC_DAWN_NATIVE_CACHEKEY_H_
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <string>
|
#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.
|
// Specialized overload for enums.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class CacheKeySerializer<T, std::enable_if_t<std::is_enum_v<T>>> {
|
class CacheKeySerializer<T, std::enable_if_t<std::is_enum_v<T>>> {
|
||||||
|
|
|
@ -177,6 +177,9 @@ namespace dawn::native {
|
||||||
: mInstance(adapter->GetInstance()), mAdapter(adapter), mNextPipelineCompatibilityToken(1) {
|
: mInstance(adapter->GetInstance()), mAdapter(adapter), mNextPipelineCompatibilityToken(1) {
|
||||||
ASSERT(descriptor != nullptr);
|
ASSERT(descriptor != nullptr);
|
||||||
|
|
||||||
|
AdapterProperties adapterProperties;
|
||||||
|
adapter->APIGetProperties(&adapterProperties);
|
||||||
|
|
||||||
const DawnTogglesDeviceDescriptor* togglesDesc = nullptr;
|
const DawnTogglesDeviceDescriptor* togglesDesc = nullptr;
|
||||||
FindInChain(descriptor->nextInChain, &togglesDesc);
|
FindInChain(descriptor->nextInChain, &togglesDesc);
|
||||||
if (togglesDesc != nullptr) {
|
if (togglesDesc != nullptr) {
|
||||||
|
@ -184,10 +187,11 @@ namespace dawn::native {
|
||||||
}
|
}
|
||||||
ApplyFeatures(descriptor);
|
ApplyFeatures(descriptor);
|
||||||
|
|
||||||
|
DawnCacheDeviceDescriptor defaultCacheDesc = {};
|
||||||
const DawnCacheDeviceDescriptor* cacheDesc = nullptr;
|
const DawnCacheDeviceDescriptor* cacheDesc = nullptr;
|
||||||
FindInChain(descriptor->nextInChain, &cacheDesc);
|
FindInChain(descriptor->nextInChain, &cacheDesc);
|
||||||
if (cacheDesc != nullptr) {
|
if (cacheDesc == nullptr) {
|
||||||
mCacheIsolationKey = cacheDesc->isolationKey;
|
cacheDesc = &defaultCacheDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptor->requiredLimits != nullptr) {
|
if (descriptor->requiredLimits != nullptr) {
|
||||||
|
@ -202,6 +206,12 @@ namespace dawn::native {
|
||||||
if (descriptor->label != nullptr && strlen(descriptor->label) != 0) {
|
if (descriptor->label != nullptr && strlen(descriptor->label) != 0) {
|
||||||
mLabel = descriptor->label;
|
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) {
|
DeviceBase::DeviceBase() : mState(State::Alive) {
|
||||||
|
@ -1789,8 +1799,8 @@ namespace dawn::native {
|
||||||
return PipelineCompatibilityToken(mNextPipelineCompatibilityToken++);
|
return PipelineCompatibilityToken(mNextPipelineCompatibilityToken++);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& DeviceBase::GetCacheIsolationKey() const {
|
const CacheKey& DeviceBase::GetCacheKey() const {
|
||||||
return mCacheIsolationKey;
|
return mDeviceCacheKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& DeviceBase::GetLabel() const {
|
const std::string& DeviceBase::GetLabel() const {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#ifndef SRC_DAWN_NATIVE_DEVICE_H_
|
#ifndef SRC_DAWN_NATIVE_DEVICE_H_
|
||||||
#define SRC_DAWN_NATIVE_DEVICE_H_
|
#define SRC_DAWN_NATIVE_DEVICE_H_
|
||||||
|
|
||||||
|
#include "dawn/native/CacheKey.h"
|
||||||
#include "dawn/native/Commands.h"
|
#include "dawn/native/Commands.h"
|
||||||
#include "dawn/native/ComputePipeline.h"
|
#include "dawn/native/ComputePipeline.h"
|
||||||
#include "dawn/native/Error.h"
|
#include "dawn/native/Error.h"
|
||||||
|
@ -370,7 +371,7 @@ namespace dawn::native {
|
||||||
|
|
||||||
PipelineCompatibilityToken GetNextPipelineCompatibilityToken();
|
PipelineCompatibilityToken GetNextPipelineCompatibilityToken();
|
||||||
|
|
||||||
const std::string& GetCacheIsolationKey() const;
|
const CacheKey& GetCacheKey() const;
|
||||||
const std::string& GetLabel() const;
|
const std::string& GetLabel() const;
|
||||||
void APISetLabel(const char* label);
|
void APISetLabel(const char* label);
|
||||||
void APIDestroy();
|
void APIDestroy();
|
||||||
|
@ -547,7 +548,7 @@ namespace dawn::native {
|
||||||
std::unique_ptr<CallbackTaskManager> mCallbackTaskManager;
|
std::unique_ptr<CallbackTaskManager> mCallbackTaskManager;
|
||||||
std::unique_ptr<dawn::platform::WorkerTaskPool> mWorkerTaskPool;
|
std::unique_ptr<dawn::platform::WorkerTaskPool> mWorkerTaskPool;
|
||||||
std::string mLabel;
|
std::string mLabel;
|
||||||
std::string mCacheIsolationKey = "";
|
CacheKey mDeviceCacheKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dawn::native
|
} // namespace dawn::native
|
||||||
|
|
|
@ -86,41 +86,55 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeviceCreationTest, CreateDeviceWithCacheSuccess) {
|
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::DeviceDescriptor desc = {};
|
||||||
wgpu::Device device = adapter.CreateDevice(&desc);
|
wgpu::Device device1 = adapter.CreateDevice(&desc);
|
||||||
EXPECT_NE(device, nullptr);
|
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 = {};
|
wgpu::DawnCacheDeviceDescriptor cacheDesc = {};
|
||||||
desc.nextInChain = &cacheDesc;
|
desc.nextInChain = &cacheDesc;
|
||||||
|
wgpu::Device device2 = adapter.CreateDevice(&desc);
|
||||||
|
|
||||||
wgpu::Device device = adapter.CreateDevice(&desc);
|
EXPECT_EQ(dawn::native::FromAPI(device1.Get())->GetCacheKey(),
|
||||||
EXPECT_NE(device, nullptr);
|
dawn::native::FromAPI(device2.Get())->GetCacheKey());
|
||||||
|
|
||||||
EXPECT_THAT(dawn::native::FromAPI(device.Get())->GetCacheIsolationKey(),
|
|
||||||
testing::StrEq(""));
|
|
||||||
}
|
}
|
||||||
// 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::DeviceDescriptor desc = {};
|
||||||
|
wgpu::Device device1 = adapter.CreateDevice(&desc);
|
||||||
|
EXPECT_NE(device1, nullptr);
|
||||||
|
|
||||||
wgpu::DawnCacheDeviceDescriptor cacheDesc = {};
|
wgpu::DawnCacheDeviceDescriptor cacheDesc = {};
|
||||||
desc.nextInChain = &cacheDesc;
|
desc.nextInChain = &cacheDesc;
|
||||||
|
|
||||||
const char* isolationKey = "isolation key";
|
const char* isolationKey = "isolation key";
|
||||||
cacheDesc.isolationKey = isolationKey;
|
cacheDesc.isolationKey = isolationKey;
|
||||||
|
wgpu::Device device2 = adapter.CreateDevice(&desc);
|
||||||
|
EXPECT_NE(device2, nullptr);
|
||||||
|
|
||||||
wgpu::Device device = adapter.CreateDevice(&desc);
|
EXPECT_NE(dawn::native::FromAPI(device1.Get())->GetCacheKey(),
|
||||||
EXPECT_NE(device, nullptr);
|
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(),
|
cacheDesc.isolationKey = isolationKey1;
|
||||||
testing::StrEq(isolationKey));
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue