Adds pipeline cache and implementation for Vulkan backend.
- Adds testing for Vulkan pipeline caching. - Removed redundant VK_NULL_HANDLE and use explicit {} initialization for 0 handles when necessary. - Adds some const qualifiers where applicable and useful. - Removes overloaded GetCacheKey (const/non-const) versions and exposed the cache key member directly for modifiers in derived classes. Bug: dawn:549 Change-Id: I5e8ab9716eebc916b813c9d032f8dc1f3f5261bc Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/86581 Commit-Queue: Loko Kung <lokokung@google.com> Reviewed-by: Austin Eng <enga@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
03ddfbb81f
commit
d181a3cf55
|
@ -117,8 +117,6 @@ class alignas(detail::kNativeVkHandleAlignment) VkHandle {
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
static constexpr std::nullptr_t VK_NULL_HANDLE = nullptr;
|
|
||||||
|
|
||||||
template <typename Tag, typename HandleType>
|
template <typename Tag, typename HandleType>
|
||||||
HandleType* AsVkArray(detail::VkHandle<Tag, HandleType>* handle) {
|
HandleType* AsVkArray(detail::VkHandle<Tag, HandleType>* handle) {
|
||||||
return reinterpret_cast<HandleType*>(handle);
|
return reinterpret_cast<HandleType*>(handle);
|
||||||
|
@ -179,12 +177,6 @@ HandleType* AsVkArray(detail::VkHandle<Tag, HandleType>* handle) {
|
||||||
|
|
||||||
// Redefine VK_NULL_HANDLE for better type safety where possible.
|
// Redefine VK_NULL_HANDLE for better type safety where possible.
|
||||||
#undef VK_NULL_HANDLE
|
#undef VK_NULL_HANDLE
|
||||||
#if defined(DAWN_PLATFORM_64_BIT)
|
|
||||||
static constexpr std::nullptr_t VK_NULL_HANDLE = nullptr;
|
static constexpr std::nullptr_t VK_NULL_HANDLE = nullptr;
|
||||||
#elif defined(DAWN_PLATFORM_32_BIT)
|
|
||||||
static constexpr uint64_t VK_NULL_HANDLE = 0;
|
|
||||||
#else
|
|
||||||
#error "Unsupported platform"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // SRC_DAWN_COMMON_VULKAN_PLATFORM_H_
|
#endif // SRC_DAWN_COMMON_VULKAN_PLATFORM_H_
|
||||||
|
|
|
@ -260,6 +260,8 @@ source_set("sources") {
|
||||||
"PerStage.h",
|
"PerStage.h",
|
||||||
"Pipeline.cpp",
|
"Pipeline.cpp",
|
||||||
"Pipeline.h",
|
"Pipeline.h",
|
||||||
|
"PipelineCache.cpp",
|
||||||
|
"PipelineCache.h",
|
||||||
"PipelineLayout.cpp",
|
"PipelineLayout.cpp",
|
||||||
"PipelineLayout.h",
|
"PipelineLayout.h",
|
||||||
"PooledResourceMemoryAllocator.cpp",
|
"PooledResourceMemoryAllocator.cpp",
|
||||||
|
@ -586,6 +588,8 @@ source_set("sources") {
|
||||||
"vulkan/Forward.h",
|
"vulkan/Forward.h",
|
||||||
"vulkan/NativeSwapChainImplVk.cpp",
|
"vulkan/NativeSwapChainImplVk.cpp",
|
||||||
"vulkan/NativeSwapChainImplVk.h",
|
"vulkan/NativeSwapChainImplVk.h",
|
||||||
|
"vulkan/PipelineCacheVk.cpp",
|
||||||
|
"vulkan/PipelineCacheVk.h",
|
||||||
"vulkan/PipelineLayoutVk.cpp",
|
"vulkan/PipelineLayoutVk.cpp",
|
||||||
"vulkan/PipelineLayoutVk.h",
|
"vulkan/PipelineLayoutVk.h",
|
||||||
"vulkan/QuerySetVk.cpp",
|
"vulkan/QuerySetVk.cpp",
|
||||||
|
|
|
@ -126,6 +126,8 @@ target_sources(dawn_native PRIVATE
|
||||||
"PerStage.h"
|
"PerStage.h"
|
||||||
"Pipeline.cpp"
|
"Pipeline.cpp"
|
||||||
"Pipeline.h"
|
"Pipeline.h"
|
||||||
|
"PipelineCache.cpp"
|
||||||
|
"PipelineCache.h"
|
||||||
"PipelineLayout.cpp"
|
"PipelineLayout.cpp"
|
||||||
"PipelineLayout.h"
|
"PipelineLayout.h"
|
||||||
"PooledResourceMemoryAllocator.cpp"
|
"PooledResourceMemoryAllocator.cpp"
|
||||||
|
@ -477,6 +479,8 @@ if (DAWN_ENABLE_VULKAN)
|
||||||
"vulkan/Forward.h"
|
"vulkan/Forward.h"
|
||||||
"vulkan/NativeSwapChainImplVk.cpp"
|
"vulkan/NativeSwapChainImplVk.cpp"
|
||||||
"vulkan/NativeSwapChainImplVk.h"
|
"vulkan/NativeSwapChainImplVk.h"
|
||||||
|
"vulkan/PipelineCacheVk.cpp"
|
||||||
|
"vulkan/PipelineCacheVk.h"
|
||||||
"vulkan/PipelineLayoutVk.cpp"
|
"vulkan/PipelineLayoutVk.cpp"
|
||||||
"vulkan/PipelineLayoutVk.h"
|
"vulkan/PipelineLayoutVk.h"
|
||||||
"vulkan/QuerySetVk.cpp"
|
"vulkan/QuerySetVk.cpp"
|
||||||
|
|
|
@ -46,8 +46,4 @@ const CacheKey& CachedObject::GetCacheKey() const {
|
||||||
return mCacheKey;
|
return mCacheKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheKey* CachedObject::GetCacheKey() {
|
|
||||||
return &mCacheKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dawn::native
|
} // namespace dawn::native
|
||||||
|
|
|
@ -43,8 +43,8 @@ class CachedObject {
|
||||||
const CacheKey& GetCacheKey() const;
|
const CacheKey& GetCacheKey() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Protected accessor for derived classes to access and modify the key.
|
// Cache key member is protected so that derived classes can modify it.
|
||||||
CacheKey* GetCacheKey();
|
CacheKey mCacheKey;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class DeviceBase;
|
friend class DeviceBase;
|
||||||
|
@ -57,7 +57,6 @@ class CachedObject {
|
||||||
|
|
||||||
size_t mContentHash = 0;
|
size_t mContentHash = 0;
|
||||||
bool mIsContentHashInitialized = false;
|
bool mIsContentHashInitialized = false;
|
||||||
CacheKey mCacheKey;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dawn::native
|
} // namespace dawn::native
|
||||||
|
|
|
@ -50,7 +50,7 @@ ComputePipelineBase::ComputePipelineBase(DeviceBase* device,
|
||||||
TrackInDevice();
|
TrackInDevice();
|
||||||
|
|
||||||
// Initialize the cache key to include the cache type and device information.
|
// Initialize the cache key to include the cache type and device information.
|
||||||
GetCacheKey()->Record(CacheKey::Type::ComputePipeline, device->GetCacheKey());
|
mCacheKey.Record(CacheKey::Type::ComputePipeline, device->GetCacheKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputePipelineBase::ComputePipelineBase(DeviceBase* device) : PipelineBase(device) {
|
ComputePipelineBase::ComputePipelineBase(DeviceBase* device) : PipelineBase(device) {
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "dawn/native/Instance.h"
|
#include "dawn/native/Instance.h"
|
||||||
#include "dawn/native/InternalPipelineStore.h"
|
#include "dawn/native/InternalPipelineStore.h"
|
||||||
#include "dawn/native/ObjectType_autogen.h"
|
#include "dawn/native/ObjectType_autogen.h"
|
||||||
|
#include "dawn/native/PipelineCache.h"
|
||||||
#include "dawn/native/QuerySet.h"
|
#include "dawn/native/QuerySet.h"
|
||||||
#include "dawn/native/Queue.h"
|
#include "dawn/native/Queue.h"
|
||||||
#include "dawn/native/RenderBundleEncoder.h"
|
#include "dawn/native/RenderBundleEncoder.h"
|
||||||
|
@ -968,6 +969,10 @@ void DeviceBase::UncacheAttachmentState(AttachmentState* obj) {
|
||||||
ASSERT(removedCount == 1);
|
ASSERT(removedCount == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref<PipelineCacheBase> DeviceBase::GetOrCreatePipelineCache(const CacheKey& key) {
|
||||||
|
return GetOrCreatePipelineCacheImpl(key);
|
||||||
|
}
|
||||||
|
|
||||||
// Object creation API methods
|
// Object creation API methods
|
||||||
|
|
||||||
BindGroupBase* DeviceBase::APICreateBindGroup(const BindGroupDescriptor* descriptor) {
|
BindGroupBase* DeviceBase::APICreateBindGroup(const BindGroupDescriptor* descriptor) {
|
||||||
|
@ -1377,6 +1382,11 @@ ResultOrError<Ref<CommandEncoder>> DeviceBase::CreateCommandEncoder(
|
||||||
return CommandEncoder::Create(this, descriptor);
|
return CommandEncoder::Create(this, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overwritten on the backends to return pipeline caches if supported.
|
||||||
|
Ref<PipelineCacheBase> DeviceBase::GetOrCreatePipelineCacheImpl(const CacheKey& key) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
MaybeError DeviceBase::CreateComputePipelineAsync(const ComputePipelineDescriptor* descriptor,
|
MaybeError DeviceBase::CreateComputePipelineAsync(const ComputePipelineDescriptor* descriptor,
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
|
|
|
@ -195,6 +195,8 @@ class DeviceBase : public RefCounted {
|
||||||
Ref<AttachmentState> GetOrCreateAttachmentState(const RenderPassDescriptor* descriptor);
|
Ref<AttachmentState> GetOrCreateAttachmentState(const RenderPassDescriptor* descriptor);
|
||||||
void UncacheAttachmentState(AttachmentState* obj);
|
void UncacheAttachmentState(AttachmentState* obj);
|
||||||
|
|
||||||
|
Ref<PipelineCacheBase> GetOrCreatePipelineCache(const CacheKey& key);
|
||||||
|
|
||||||
// Object creation methods that be used in a reentrant manner.
|
// Object creation methods that be used in a reentrant manner.
|
||||||
ResultOrError<Ref<BindGroupBase>> CreateBindGroup(const BindGroupDescriptor* descriptor);
|
ResultOrError<Ref<BindGroupBase>> CreateBindGroup(const BindGroupDescriptor* descriptor);
|
||||||
ResultOrError<Ref<BindGroupLayoutBase>> CreateBindGroupLayout(
|
ResultOrError<Ref<BindGroupLayoutBase>> CreateBindGroupLayout(
|
||||||
|
@ -441,6 +443,7 @@ class DeviceBase : public RefCounted {
|
||||||
Ref<ComputePipelineBase> AddOrGetCachedComputePipeline(
|
Ref<ComputePipelineBase> AddOrGetCachedComputePipeline(
|
||||||
Ref<ComputePipelineBase> computePipeline);
|
Ref<ComputePipelineBase> computePipeline);
|
||||||
Ref<RenderPipelineBase> AddOrGetCachedRenderPipeline(Ref<RenderPipelineBase> renderPipeline);
|
Ref<RenderPipelineBase> AddOrGetCachedRenderPipeline(Ref<RenderPipelineBase> renderPipeline);
|
||||||
|
virtual Ref<PipelineCacheBase> GetOrCreatePipelineCacheImpl(const CacheKey& key);
|
||||||
virtual void InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
|
virtual void InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata);
|
void* userdata);
|
||||||
|
|
|
@ -35,6 +35,7 @@ class ComputePassEncoder;
|
||||||
class ExternalTextureBase;
|
class ExternalTextureBase;
|
||||||
class InstanceBase;
|
class InstanceBase;
|
||||||
class PipelineBase;
|
class PipelineBase;
|
||||||
|
class PipelineCacheBase;
|
||||||
class PipelineLayoutBase;
|
class PipelineLayoutBase;
|
||||||
class QuerySetBase;
|
class QuerySetBase;
|
||||||
class QueueBase;
|
class QueueBase;
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "dawn/native/PipelineCache.h"
|
||||||
|
|
||||||
|
namespace dawn::native {
|
||||||
|
|
||||||
|
PipelineCacheBase::PipelineCacheBase(BlobCache* cache, const CacheKey& key)
|
||||||
|
: mCache(cache), mKey(key) {}
|
||||||
|
|
||||||
|
CachedBlob PipelineCacheBase::Initialize() {
|
||||||
|
ASSERT(!mInitialized);
|
||||||
|
CachedBlob blob = mCache->Load(mKey);
|
||||||
|
mCacheHit = !blob.Empty();
|
||||||
|
mInitialized = true;
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PipelineCacheBase::CacheHit() const {
|
||||||
|
ASSERT(mInitialized);
|
||||||
|
return mCacheHit;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeError PipelineCacheBase::Flush() {
|
||||||
|
// Try to write the data out to the persistent cache.
|
||||||
|
CachedBlob blob;
|
||||||
|
DAWN_TRY_ASSIGN(blob, SerializeToBlobImpl());
|
||||||
|
if (blob.Size() > 0) {
|
||||||
|
// Using a simple heuristic to decide whether to write out the blob right now. May need
|
||||||
|
// smarter tracking when we are dealing with monolithic caches.
|
||||||
|
mCache->Store(mKey, blob);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeError PipelineCacheBase::FlushIfNeeded() {
|
||||||
|
ASSERT(mInitialized);
|
||||||
|
if (!CacheHit()) {
|
||||||
|
return Flush();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dawn::native
|
|
@ -0,0 +1,63 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef SRC_DAWN_NATIVE_PIPELINECACHE_H_
|
||||||
|
#define SRC_DAWN_NATIVE_PIPELINECACHE_H_
|
||||||
|
|
||||||
|
#include "dawn/common/RefCounted.h"
|
||||||
|
#include "dawn/native/BlobCache.h"
|
||||||
|
#include "dawn/native/CacheKey.h"
|
||||||
|
#include "dawn/native/Error.h"
|
||||||
|
|
||||||
|
namespace dawn::native {
|
||||||
|
|
||||||
|
// Abstraction layer for backend dependent pipeline caching.
|
||||||
|
class PipelineCacheBase : public RefCounted {
|
||||||
|
public:
|
||||||
|
// Returns whether or not we got a cache hit when initializing.
|
||||||
|
bool CacheHit() const;
|
||||||
|
|
||||||
|
// Serializes and writes the current contents of the backend cache object into the backing
|
||||||
|
// blob cache, potentially overwriting what is already there. Useful when we are working
|
||||||
|
// with more monolithic-like caches where we expect overwriting sometimes.
|
||||||
|
MaybeError Flush();
|
||||||
|
|
||||||
|
// Serializes and writes the current contents of the backend cache object into the backing
|
||||||
|
// blob cache iff the initial read from the backend cache did not result in a hit.
|
||||||
|
MaybeError FlushIfNeeded();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PipelineCacheBase(BlobCache* cache, const CacheKey& key);
|
||||||
|
|
||||||
|
// Initializes and returns the cached blob given the cache and keys. Used by backend
|
||||||
|
// implementations to get the cache and set the cache hit state. Should only be called once.
|
||||||
|
CachedBlob Initialize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Backend implementation of serialization of the cache into a blob. Note that an empty
|
||||||
|
// blob may be returned.
|
||||||
|
virtual ResultOrError<CachedBlob> SerializeToBlobImpl() = 0;
|
||||||
|
|
||||||
|
// The blob cache is owned by the Adapter and pipeline caches are owned/created by devices
|
||||||
|
// or adapters. Since the device owns a reference to the Instance which owns the Adapter,
|
||||||
|
// the blob cache is guaranteed to be valid throughout the lifetime of the object.
|
||||||
|
BlobCache* mCache;
|
||||||
|
CacheKey mKey;
|
||||||
|
bool mInitialized = false;
|
||||||
|
bool mCacheHit = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dawn::native
|
||||||
|
|
||||||
|
#endif // SRC_DAWN_NATIVE_PIPELINECACHE_H_
|
|
@ -613,7 +613,7 @@ RenderPipelineBase::RenderPipelineBase(DeviceBase* device,
|
||||||
TrackInDevice();
|
TrackInDevice();
|
||||||
|
|
||||||
// Initialize the cache key to include the cache type and device information.
|
// Initialize the cache key to include the cache type and device information.
|
||||||
GetCacheKey()->Record(CacheKey::Type::RenderPipeline, device->GetCacheKey());
|
mCacheKey.Record(CacheKey::Type::RenderPipeline, device->GetCacheKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderPipelineBase::RenderPipelineBase(DeviceBase* device) : PipelineBase(device) {
|
RenderPipelineBase::RenderPipelineBase(DeviceBase* device) : PipelineBase(device) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ enum class InterpolationSampling {
|
||||||
Sample,
|
Sample,
|
||||||
};
|
};
|
||||||
|
|
||||||
using PipelineLayoutEntryPointPair = std::pair<PipelineLayoutBase*, std::string>;
|
using PipelineLayoutEntryPointPair = std::pair<const PipelineLayoutBase*, std::string>;
|
||||||
struct PipelineLayoutEntryPointPairHashFunc {
|
struct PipelineLayoutEntryPointPairHashFunc {
|
||||||
size_t operator()(const PipelineLayoutEntryPointPair& pair) const;
|
size_t operator()(const PipelineLayoutEntryPointPair& pair) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -58,6 +58,11 @@ struct ToBackendTraits<DeviceBase, BackendTraits> {
|
||||||
using BackendType = typename BackendTraits::DeviceType;
|
using BackendType = typename BackendTraits::DeviceType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename BackendTraits>
|
||||||
|
struct ToBackendTraits<PipelineCacheBase, BackendTraits> {
|
||||||
|
using BackendType = typename BackendTraits::PipelineCacheType;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename BackendTraits>
|
template <typename BackendTraits>
|
||||||
struct ToBackendTraits<PipelineLayoutBase, BackendTraits> {
|
struct ToBackendTraits<PipelineLayoutBase, BackendTraits> {
|
||||||
using BackendType = typename BackendTraits::PipelineLayoutType;
|
using BackendType = typename BackendTraits::PipelineLayoutType;
|
||||||
|
|
|
@ -117,7 +117,7 @@ MaybeError BindGroupLayout::Initialize() {
|
||||||
createInfo.pBindings = bindings.data();
|
createInfo.pBindings = bindings.data();
|
||||||
|
|
||||||
// Record cache key information now since the createInfo is not stored.
|
// Record cache key information now since the createInfo is not stored.
|
||||||
GetCacheKey()->Record(createInfo);
|
mCacheKey.Record(createInfo);
|
||||||
|
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorSetLayout(device->GetVkDevice(), &createInfo,
|
DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorSetLayout(device->GetVkDevice(), &createInfo,
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "dawn/native/CreatePipelineAsyncTask.h"
|
#include "dawn/native/CreatePipelineAsyncTask.h"
|
||||||
#include "dawn/native/vulkan/DeviceVk.h"
|
#include "dawn/native/vulkan/DeviceVk.h"
|
||||||
#include "dawn/native/vulkan/FencedDeleter.h"
|
#include "dawn/native/vulkan/FencedDeleter.h"
|
||||||
|
#include "dawn/native/vulkan/PipelineCacheVk.h"
|
||||||
#include "dawn/native/vulkan/PipelineLayoutVk.h"
|
#include "dawn/native/vulkan/PipelineLayoutVk.h"
|
||||||
#include "dawn/native/vulkan/ShaderModuleVk.h"
|
#include "dawn/native/vulkan/ShaderModuleVk.h"
|
||||||
#include "dawn/native/vulkan/UtilsVulkan.h"
|
#include "dawn/native/vulkan/UtilsVulkan.h"
|
||||||
|
@ -36,12 +37,18 @@ Ref<ComputePipeline> ComputePipeline::CreateUninitialized(
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError ComputePipeline::Initialize() {
|
MaybeError ComputePipeline::Initialize() {
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
const PipelineLayout* layout = ToBackend(GetLayout());
|
||||||
|
|
||||||
|
// Vulkan devices need cache UUID field to be serialized into pipeline cache keys.
|
||||||
|
mCacheKey.Record(device->GetDeviceInfo().properties.pipelineCacheUUID);
|
||||||
|
|
||||||
VkComputePipelineCreateInfo createInfo;
|
VkComputePipelineCreateInfo createInfo;
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||||
createInfo.pNext = nullptr;
|
createInfo.pNext = nullptr;
|
||||||
createInfo.flags = 0;
|
createInfo.flags = 0;
|
||||||
createInfo.layout = ToBackend(GetLayout())->GetHandle();
|
createInfo.layout = layout->GetHandle();
|
||||||
createInfo.basePipelineHandle = ::VK_NULL_HANDLE;
|
createInfo.basePipelineHandle = VkPipeline{};
|
||||||
createInfo.basePipelineIndex = -1;
|
createInfo.basePipelineIndex = -1;
|
||||||
|
|
||||||
createInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
createInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
@ -51,7 +58,6 @@ MaybeError ComputePipeline::Initialize() {
|
||||||
// Generate a new VkShaderModule with BindingRemapper tint transform for each pipeline
|
// Generate a new VkShaderModule with BindingRemapper tint transform for each pipeline
|
||||||
const ProgrammableStage& computeStage = GetStage(SingleShaderStage::Compute);
|
const ProgrammableStage& computeStage = GetStage(SingleShaderStage::Compute);
|
||||||
ShaderModule* module = ToBackend(computeStage.module.Get());
|
ShaderModule* module = ToBackend(computeStage.module.Get());
|
||||||
PipelineLayout* layout = ToBackend(GetLayout());
|
|
||||||
const ShaderModule::Spirv* spirv;
|
const ShaderModule::Spirv* spirv;
|
||||||
DAWN_TRY_ASSIGN((std::tie(createInfo.stage.module, spirv)),
|
DAWN_TRY_ASSIGN((std::tie(createInfo.stage.module, spirv)),
|
||||||
module->GetHandleAndSpirv(computeStage.entryPoint.c_str(), layout));
|
module->GetHandleAndSpirv(computeStage.entryPoint.c_str(), layout));
|
||||||
|
@ -64,8 +70,6 @@ MaybeError ComputePipeline::Initialize() {
|
||||||
createInfo.stage.pSpecializationInfo = GetVkSpecializationInfo(
|
createInfo.stage.pSpecializationInfo = GetVkSpecializationInfo(
|
||||||
computeStage, &specializationInfo, &specializationDataEntries, &specializationMapEntries);
|
computeStage, &specializationInfo, &specializationDataEntries, &specializationMapEntries);
|
||||||
|
|
||||||
Device* device = ToBackend(GetDevice());
|
|
||||||
|
|
||||||
PNextChainBuilder stageExtChain(&createInfo.stage);
|
PNextChainBuilder stageExtChain(&createInfo.stage);
|
||||||
|
|
||||||
VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroupSizeInfo = {};
|
VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroupSizeInfo = {};
|
||||||
|
@ -79,14 +83,16 @@ MaybeError ComputePipeline::Initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record cache key information now since the createInfo is not stored.
|
// Record cache key information now since the createInfo is not stored.
|
||||||
GetCacheKey()
|
mCacheKey.Record(createInfo, layout).RecordIterable(*spirv);
|
||||||
->Record(createInfo, static_cast<const ComputePipeline*>(this)->GetLayout())
|
|
||||||
.RecordIterable(*spirv);
|
|
||||||
|
|
||||||
|
// Try to see if we have anything in the blob cache.
|
||||||
|
Ref<PipelineCache> cache = ToBackend(GetDevice()->GetOrCreatePipelineCache(GetCacheKey()));
|
||||||
DAWN_TRY(
|
DAWN_TRY(
|
||||||
CheckVkSuccess(device->fn.CreateComputePipelines(device->GetVkDevice(), ::VK_NULL_HANDLE, 1,
|
CheckVkSuccess(device->fn.CreateComputePipelines(device->GetVkDevice(), cache->GetHandle(),
|
||||||
&createInfo, nullptr, &*mHandle),
|
1, &createInfo, nullptr, &*mHandle),
|
||||||
"CreateComputePipeline"));
|
"CreateComputePipeline"));
|
||||||
|
// TODO(dawn:549): Flush is currently in the same thread, but perhaps deferrable.
|
||||||
|
DAWN_TRY(cache->FlushIfNeeded());
|
||||||
|
|
||||||
SetLabelImpl();
|
SetLabelImpl();
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "dawn/native/vulkan/CommandBufferVk.h"
|
#include "dawn/native/vulkan/CommandBufferVk.h"
|
||||||
#include "dawn/native/vulkan/ComputePipelineVk.h"
|
#include "dawn/native/vulkan/ComputePipelineVk.h"
|
||||||
#include "dawn/native/vulkan/FencedDeleter.h"
|
#include "dawn/native/vulkan/FencedDeleter.h"
|
||||||
|
#include "dawn/native/vulkan/PipelineCacheVk.h"
|
||||||
#include "dawn/native/vulkan/PipelineLayoutVk.h"
|
#include "dawn/native/vulkan/PipelineLayoutVk.h"
|
||||||
#include "dawn/native/vulkan/QuerySetVk.h"
|
#include "dawn/native/vulkan/QuerySetVk.h"
|
||||||
#include "dawn/native/vulkan/QueueVk.h"
|
#include "dawn/native/vulkan/QueueVk.h"
|
||||||
|
@ -167,6 +168,9 @@ ResultOrError<Ref<TextureViewBase>> Device::CreateTextureViewImpl(
|
||||||
const TextureViewDescriptor* descriptor) {
|
const TextureViewDescriptor* descriptor) {
|
||||||
return TextureView::Create(texture, descriptor);
|
return TextureView::Create(texture, descriptor);
|
||||||
}
|
}
|
||||||
|
Ref<PipelineCacheBase> Device::GetOrCreatePipelineCacheImpl(const CacheKey& key) {
|
||||||
|
return PipelineCache::Create(this, key);
|
||||||
|
}
|
||||||
void Device::InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
|
void Device::InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
|
|
|
@ -35,8 +35,6 @@
|
||||||
|
|
||||||
namespace dawn::native::vulkan {
|
namespace dawn::native::vulkan {
|
||||||
|
|
||||||
class Adapter;
|
|
||||||
class BindGroupLayout;
|
|
||||||
class BufferUploader;
|
class BufferUploader;
|
||||||
class FencedDeleter;
|
class FencedDeleter;
|
||||||
class RenderPassCache;
|
class RenderPassCache;
|
||||||
|
@ -138,6 +136,7 @@ class Device final : public DeviceBase {
|
||||||
const ComputePipelineDescriptor* descriptor) override;
|
const ComputePipelineDescriptor* descriptor) override;
|
||||||
Ref<RenderPipelineBase> CreateUninitializedRenderPipelineImpl(
|
Ref<RenderPipelineBase> CreateUninitializedRenderPipelineImpl(
|
||||||
const RenderPipelineDescriptor* descriptor) override;
|
const RenderPipelineDescriptor* descriptor) override;
|
||||||
|
Ref<PipelineCacheBase> GetOrCreatePipelineCacheImpl(const CacheKey& key) override;
|
||||||
void InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
|
void InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata) override;
|
void* userdata) override;
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Buffer;
|
||||||
class CommandBuffer;
|
class CommandBuffer;
|
||||||
class ComputePipeline;
|
class ComputePipeline;
|
||||||
class Device;
|
class Device;
|
||||||
|
class PipelineCache;
|
||||||
class PipelineLayout;
|
class PipelineLayout;
|
||||||
class QuerySet;
|
class QuerySet;
|
||||||
class Queue;
|
class Queue;
|
||||||
|
@ -46,6 +47,7 @@ struct VulkanBackendTraits {
|
||||||
using CommandBufferType = CommandBuffer;
|
using CommandBufferType = CommandBuffer;
|
||||||
using ComputePipelineType = ComputePipeline;
|
using ComputePipelineType = ComputePipeline;
|
||||||
using DeviceType = Device;
|
using DeviceType = Device;
|
||||||
|
using PipelineCacheType = PipelineCache;
|
||||||
using PipelineLayoutType = PipelineLayout;
|
using PipelineLayoutType = PipelineLayout;
|
||||||
using QuerySetType = QuerySet;
|
using QuerySetType = QuerySet;
|
||||||
using QueueType = Queue;
|
using QueueType = Queue;
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "dawn/native/vulkan/PipelineCacheVk.h"
|
||||||
|
|
||||||
|
#include "dawn/common/Assert.h"
|
||||||
|
#include "dawn/native/Device.h"
|
||||||
|
#include "dawn/native/Error.h"
|
||||||
|
#include "dawn/native/vulkan/DeviceVk.h"
|
||||||
|
#include "dawn/native/vulkan/FencedDeleter.h"
|
||||||
|
#include "dawn/native/vulkan/VulkanError.h"
|
||||||
|
|
||||||
|
namespace dawn::native::vulkan {
|
||||||
|
|
||||||
|
// static
|
||||||
|
Ref<PipelineCache> PipelineCache::Create(DeviceBase* device, const CacheKey& key) {
|
||||||
|
Ref<PipelineCache> cache = AcquireRef(new PipelineCache(device, key));
|
||||||
|
cache->Initialize();
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipelineCache::PipelineCache(DeviceBase* device, const CacheKey& key)
|
||||||
|
: PipelineCacheBase(device->GetBlobCache(), key), mDevice(device) {}
|
||||||
|
|
||||||
|
PipelineCache::~PipelineCache() {
|
||||||
|
if (mHandle == VK_NULL_HANDLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
device->fn.DestroyPipelineCache(device->GetVkDevice(), mHandle, nullptr);
|
||||||
|
mHandle = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceBase* PipelineCache::GetDevice() const {
|
||||||
|
return mDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPipelineCache PipelineCache::GetHandle() const {
|
||||||
|
return mHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultOrError<CachedBlob> PipelineCache::SerializeToBlobImpl() {
|
||||||
|
CachedBlob emptyBlob;
|
||||||
|
if (mHandle == VK_NULL_HANDLE) {
|
||||||
|
return emptyBlob;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bufferSize;
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
DAWN_TRY(CheckVkSuccess(
|
||||||
|
device->fn.GetPipelineCacheData(device->GetVkDevice(), mHandle, &bufferSize, nullptr),
|
||||||
|
"GetPipelineCacheData"));
|
||||||
|
|
||||||
|
CachedBlob blob(bufferSize);
|
||||||
|
DAWN_TRY(CheckVkSuccess(
|
||||||
|
device->fn.GetPipelineCacheData(device->GetVkDevice(), mHandle, &bufferSize, blob.Data()),
|
||||||
|
"GetPipelineCacheData"));
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineCache::Initialize() {
|
||||||
|
CachedBlob blob = PipelineCacheBase::Initialize();
|
||||||
|
|
||||||
|
VkPipelineCacheCreateInfo createInfo;
|
||||||
|
createInfo.flags = 0;
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||||
|
createInfo.pNext = nullptr;
|
||||||
|
createInfo.initialDataSize = blob.Size();
|
||||||
|
createInfo.pInitialData = blob.Data();
|
||||||
|
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
mHandle = VK_NULL_HANDLE;
|
||||||
|
GetDevice()->ConsumedError(CheckVkSuccess(
|
||||||
|
device->fn.CreatePipelineCache(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
|
||||||
|
"CreatePipelineCache"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dawn::native::vulkan
|
|
@ -0,0 +1,49 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef SRC_DAWN_NATIVE_VULKAN_PIPELINECACHEVK_H_
|
||||||
|
#define SRC_DAWN_NATIVE_VULKAN_PIPELINECACHEVK_H_
|
||||||
|
|
||||||
|
#include "dawn/native/ObjectBase.h"
|
||||||
|
#include "dawn/native/PipelineCache.h"
|
||||||
|
|
||||||
|
#include "dawn/common/vulkan_platform.h"
|
||||||
|
|
||||||
|
namespace dawn::native {
|
||||||
|
class DeviceBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace dawn::native::vulkan {
|
||||||
|
|
||||||
|
class PipelineCache final : public PipelineCacheBase {
|
||||||
|
public:
|
||||||
|
static Ref<PipelineCache> Create(DeviceBase* device, const CacheKey& key);
|
||||||
|
|
||||||
|
DeviceBase* GetDevice() const;
|
||||||
|
VkPipelineCache GetHandle() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit PipelineCache(DeviceBase* device, const CacheKey& key);
|
||||||
|
~PipelineCache() override;
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
ResultOrError<CachedBlob> SerializeToBlobImpl() override;
|
||||||
|
|
||||||
|
DeviceBase* mDevice;
|
||||||
|
VkPipelineCache mHandle = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dawn::native::vulkan
|
||||||
|
|
||||||
|
#endif // SRC_DAWN_NATIVE_VULKAN_PIPELINECACHEVK_H_
|
|
@ -56,7 +56,7 @@ MaybeError PipelineLayout::Initialize() {
|
||||||
createInfo.pPushConstantRanges = nullptr;
|
createInfo.pPushConstantRanges = nullptr;
|
||||||
|
|
||||||
// Record cache key information now since the createInfo is not stored.
|
// Record cache key information now since the createInfo is not stored.
|
||||||
GetCacheKey()->RecordIterable(cachedObjects.data(), numSetLayouts).Record(createInfo);
|
mCacheKey.RecordIterable(cachedObjects.data(), numSetLayouts).Record(createInfo);
|
||||||
|
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
DAWN_TRY(CheckVkSuccess(
|
DAWN_TRY(CheckVkSuccess(
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "dawn/native/CreatePipelineAsyncTask.h"
|
#include "dawn/native/CreatePipelineAsyncTask.h"
|
||||||
#include "dawn/native/vulkan/DeviceVk.h"
|
#include "dawn/native/vulkan/DeviceVk.h"
|
||||||
#include "dawn/native/vulkan/FencedDeleter.h"
|
#include "dawn/native/vulkan/FencedDeleter.h"
|
||||||
|
#include "dawn/native/vulkan/PipelineCacheVk.h"
|
||||||
#include "dawn/native/vulkan/PipelineLayoutVk.h"
|
#include "dawn/native/vulkan/PipelineLayoutVk.h"
|
||||||
#include "dawn/native/vulkan/RenderPassCache.h"
|
#include "dawn/native/vulkan/RenderPassCache.h"
|
||||||
#include "dawn/native/vulkan/ShaderModuleVk.h"
|
#include "dawn/native/vulkan/ShaderModuleVk.h"
|
||||||
|
@ -331,7 +332,10 @@ Ref<RenderPipeline> RenderPipeline::CreateUninitialized(
|
||||||
|
|
||||||
MaybeError RenderPipeline::Initialize() {
|
MaybeError RenderPipeline::Initialize() {
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
PipelineLayout* layout = ToBackend(GetLayout());
|
const PipelineLayout* layout = ToBackend(GetLayout());
|
||||||
|
|
||||||
|
// Vulkan devices need cache UUID field to be serialized into pipeline cache keys.
|
||||||
|
mCacheKey.Record(device->GetDeviceInfo().properties.pipelineCacheUUID);
|
||||||
|
|
||||||
// There are at most 2 shader stages in render pipeline, i.e. vertex and fragment
|
// There are at most 2 shader stages in render pipeline, i.e. vertex and fragment
|
||||||
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
|
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
|
||||||
|
@ -381,7 +385,7 @@ MaybeError RenderPipeline::Initialize() {
|
||||||
stageCount++;
|
stageCount++;
|
||||||
|
|
||||||
// Record cache key for each shader since it will become inaccessible later on.
|
// Record cache key for each shader since it will become inaccessible later on.
|
||||||
GetCacheKey()->Record(stage).RecordIterable(*spirv);
|
mCacheKey.Record(stage).RecordIterable(*spirv);
|
||||||
}
|
}
|
||||||
|
|
||||||
PipelineVertexInputStateCreateInfoTemporaryAllocations tempAllocations;
|
PipelineVertexInputStateCreateInfoTemporaryAllocations tempAllocations;
|
||||||
|
@ -528,7 +532,7 @@ MaybeError RenderPipeline::Initialize() {
|
||||||
|
|
||||||
query.SetSampleCount(GetSampleCount());
|
query.SetSampleCount(GetSampleCount());
|
||||||
|
|
||||||
GetCacheKey()->Record(query);
|
mCacheKey.Record(query);
|
||||||
DAWN_TRY_ASSIGN(renderPass, device->GetRenderPassCache()->GetRenderPass(query));
|
DAWN_TRY_ASSIGN(renderPass, device->GetRenderPassCache()->GetRenderPass(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,13 +561,16 @@ MaybeError RenderPipeline::Initialize() {
|
||||||
createInfo.basePipelineIndex = -1;
|
createInfo.basePipelineIndex = -1;
|
||||||
|
|
||||||
// Record cache key information now since createInfo is not stored.
|
// Record cache key information now since createInfo is not stored.
|
||||||
GetCacheKey()->Record(createInfo,
|
mCacheKey.Record(createInfo, layout->GetCacheKey());
|
||||||
static_cast<const RenderPipeline*>(this)->GetLayout()->GetCacheKey());
|
|
||||||
|
|
||||||
|
// Try to see if we have anything in the blob cache.
|
||||||
|
Ref<PipelineCache> cache = ToBackend(GetDevice()->GetOrCreatePipelineCache(GetCacheKey()));
|
||||||
DAWN_TRY(
|
DAWN_TRY(
|
||||||
CheckVkSuccess(device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VkPipelineCache{},
|
CheckVkSuccess(device->fn.CreateGraphicsPipelines(device->GetVkDevice(), cache->GetHandle(),
|
||||||
1, &createInfo, nullptr, &*mHandle),
|
1, &createInfo, nullptr, &*mHandle),
|
||||||
"CreateGraphicsPipeline"));
|
"CreateGraphicsPipelines"));
|
||||||
|
// TODO(dawn:549): Flush is currently in the same thread, but perhaps deferrable.
|
||||||
|
DAWN_TRY(cache->FlushIfNeeded());
|
||||||
|
|
||||||
SetLabelImpl();
|
SetLabelImpl();
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ ShaderModule::~ShaderModule() = default;
|
||||||
|
|
||||||
ResultOrError<ShaderModule::ModuleAndSpirv> ShaderModule::GetHandleAndSpirv(
|
ResultOrError<ShaderModule::ModuleAndSpirv> ShaderModule::GetHandleAndSpirv(
|
||||||
const char* entryPointName,
|
const char* entryPointName,
|
||||||
PipelineLayout* layout) {
|
const PipelineLayout* layout) {
|
||||||
TRACE_EVENT0(GetDevice()->GetPlatform(), General, "ShaderModuleVk::GetHandleAndSpirv");
|
TRACE_EVENT0(GetDevice()->GetPlatform(), General, "ShaderModuleVk::GetHandleAndSpirv");
|
||||||
|
|
||||||
// If the shader was destroyed, we should never call this function.
|
// If the shader was destroyed, we should never call this function.
|
||||||
|
@ -170,7 +170,7 @@ ResultOrError<ShaderModule::ModuleAndSpirv> ShaderModule::GetHandleAndSpirv(
|
||||||
// TODO(dawn:1082): Replace this block with ShaderModuleBase::AddExternalTextureTransform.
|
// TODO(dawn:1082): Replace this block with ShaderModuleBase::AddExternalTextureTransform.
|
||||||
tint::transform::MultiplanarExternalTexture::BindingsMap newBindingsMap;
|
tint::transform::MultiplanarExternalTexture::BindingsMap newBindingsMap;
|
||||||
for (BindGroupIndex i : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
|
for (BindGroupIndex i : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
|
||||||
BindGroupLayoutBase* bgl = layout->GetBindGroupLayout(i);
|
const BindGroupLayoutBase* bgl = layout->GetBindGroupLayout(i);
|
||||||
|
|
||||||
ExternalTextureBindingExpansionMap expansions =
|
ExternalTextureBindingExpansionMap expansions =
|
||||||
bgl->GetExternalTextureBindingExpansionMap();
|
bgl->GetExternalTextureBindingExpansionMap();
|
||||||
|
|
|
@ -43,7 +43,7 @@ class ShaderModule final : public ShaderModuleBase {
|
||||||
ShaderModuleParseResult* parseResult);
|
ShaderModuleParseResult* parseResult);
|
||||||
|
|
||||||
ResultOrError<ModuleAndSpirv> GetHandleAndSpirv(const char* entryPointName,
|
ResultOrError<ModuleAndSpirv> GetHandleAndSpirv(const char* entryPointName,
|
||||||
PipelineLayout* layout);
|
const PipelineLayout* layout);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor);
|
ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor);
|
||||||
|
|
|
@ -446,6 +446,7 @@ source_set("end2end_tests_sources") {
|
||||||
"end2end/NonzeroTextureCreationTests.cpp",
|
"end2end/NonzeroTextureCreationTests.cpp",
|
||||||
"end2end/ObjectCachingTests.cpp",
|
"end2end/ObjectCachingTests.cpp",
|
||||||
"end2end/OpArrayLengthTests.cpp",
|
"end2end/OpArrayLengthTests.cpp",
|
||||||
|
"end2end/PipelineCachingTests.cpp",
|
||||||
"end2end/PipelineLayoutTests.cpp",
|
"end2end/PipelineLayoutTests.cpp",
|
||||||
"end2end/PrimitiveStateTests.cpp",
|
"end2end/PrimitiveStateTests.cpp",
|
||||||
"end2end/PrimitiveTopologyTests.cpp",
|
"end2end/PrimitiveTopologyTests.cpp",
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "dawn/tests/DawnTest.h"
|
||||||
|
#include "dawn/tests/end2end/mocks/CachingInterfaceMock.h"
|
||||||
|
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
|
||||||
|
#include "dawn/utils/WGPUHelpers.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::NiceMock;
|
||||||
|
|
||||||
|
// TODO(dawn:549) Add some sort of pipeline descriptor repository to test more caching.
|
||||||
|
|
||||||
|
static constexpr std::string_view kComputeShader = R"(
|
||||||
|
@stage(compute) @workgroup_size(1) fn main() {}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr std::string_view kVertexShader = R"(
|
||||||
|
@stage(vertex) fn main() -> @builtin(position) vec4<f32> {
|
||||||
|
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr std::string_view kFragmentShader = R"(
|
||||||
|
@stage(fragment) fn main() {}
|
||||||
|
)";
|
||||||
|
|
||||||
|
class PipelineCachingTests : public DawnTest {
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<dawn::platform::Platform> CreateTestPlatform() override {
|
||||||
|
return std::make_unique<DawnCachingMockPlatform>(&mMockCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
NiceMock<CachingInterfaceMock> mMockCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SinglePipelineCachingTests : public PipelineCachingTests {};
|
||||||
|
|
||||||
|
// Tests that pipeline creation works fine even if the cache is disabled.
|
||||||
|
// Note: This tests needs to use more than 1 device since the frontend cache on each device
|
||||||
|
// will prevent going out to the blob cache.
|
||||||
|
TEST_P(SinglePipelineCachingTests, ComputePipelineNoCache) {
|
||||||
|
mMockCache.Disable();
|
||||||
|
|
||||||
|
// First time should create and since cache is disabled, it should not write out to the
|
||||||
|
// cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 0u);
|
||||||
|
|
||||||
|
// Second time should create fine with no cache hits since cache is disabled.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation on the same device uses frontend cache when possible.
|
||||||
|
TEST_P(SinglePipelineCachingTests, ComputePipelineFrontedCache) {
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
|
||||||
|
// First creation should create a cache entry.
|
||||||
|
wgpu::ComputePipeline pipeline;
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, pipeline = device.CreateComputePipeline(&desc));
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Second creation on the same device should just return from frontend cache and should not
|
||||||
|
// call out to the blob cache.
|
||||||
|
EXPECT_CALL(mMockCache, LoadData).Times(0);
|
||||||
|
wgpu::ComputePipeline samePipeline;
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, samePipeline = device.CreateComputePipeline(&desc));
|
||||||
|
EXPECT_EQ(pipeline.Get() == samePipeline.Get(), !UsesWire());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation hits the cache when it is enabled.
|
||||||
|
// Note: This test needs to use more than 1 device since the frontend cache on each device
|
||||||
|
// will prevent going out to the blob cache.
|
||||||
|
TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCache) {
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Second time should create using the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 1u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation does not hits the cache when it is enabled but we use different
|
||||||
|
// isolation keys.
|
||||||
|
TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCacheIsolationKey) {
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice("isolation key 1");
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Second time should also create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice("isolation key 2");
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation works fine even if the cache is disabled.
|
||||||
|
// Note: This tests needs to use more than 1 device since the frontend cache on each device
|
||||||
|
// will prevent going out to the blob cache.
|
||||||
|
TEST_P(SinglePipelineCachingTests, RenderPipelineNoCache) {
|
||||||
|
mMockCache.Disable();
|
||||||
|
|
||||||
|
// First time should create and since cache is disabled, it should not write out to the
|
||||||
|
// cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 0u);
|
||||||
|
|
||||||
|
// Second time should create fine with no cache hits since cache is disabled.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation on the same device uses frontend cache when possible.
|
||||||
|
TEST_P(SinglePipelineCachingTests, RenderPipelineFrontedCache) {
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
|
||||||
|
// First creation should create a cache entry.
|
||||||
|
wgpu::RenderPipeline pipeline;
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, pipeline = device.CreateRenderPipeline(&desc));
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Second creation on the same device should just return from frontend cache and should not
|
||||||
|
// call out to the blob cache.
|
||||||
|
EXPECT_CALL(mMockCache, LoadData).Times(0);
|
||||||
|
wgpu::RenderPipeline samePipeline;
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, samePipeline = device.CreateRenderPipeline(&desc));
|
||||||
|
EXPECT_EQ(pipeline.Get() == samePipeline.Get(), !UsesWire());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation hits the cache when it is enabled.
|
||||||
|
// Note: This test needs to use more than 1 device since the frontend cache on each device
|
||||||
|
// will prevent going out to the blob cache.
|
||||||
|
TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCache) {
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Second time should create using the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 1u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation does not hits the cache when it is enabled but we use different
|
||||||
|
// isolation keys.
|
||||||
|
TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheIsolationKey) {
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice("isolation key 1");
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Second time should also create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice("isolation key 2");
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_INSTANTIATE_TEST(SinglePipelineCachingTests, VulkanBackend());
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -21,8 +21,9 @@
|
||||||
#include "dawn/native/vulkan/DeviceVk.h"
|
#include "dawn/native/vulkan/DeviceVk.h"
|
||||||
#include "dawn/native/vulkan/VulkanError.h"
|
#include "dawn/native/vulkan/VulkanError.h"
|
||||||
|
|
||||||
namespace {
|
namespace dawn::native::vulkan {
|
||||||
|
|
||||||
|
namespace {
|
||||||
class VulkanErrorInjectorTests : public DawnTest {
|
class VulkanErrorInjectorTests : public DawnTest {
|
||||||
public:
|
public:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
|
@ -35,7 +36,6 @@ class VulkanErrorInjectorTests : public DawnTest {
|
||||||
protected:
|
protected:
|
||||||
dawn::native::vulkan::Device* mDeviceVk;
|
dawn::native::vulkan::Device* mDeviceVk;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
TEST_P(VulkanErrorInjectorTests, InjectErrorOnCreateBuffer) {
|
TEST_P(VulkanErrorInjectorTests, InjectErrorOnCreateBuffer) {
|
||||||
|
@ -49,15 +49,15 @@ TEST_P(VulkanErrorInjectorTests, InjectErrorOnCreateBuffer) {
|
||||||
{
|
{
|
||||||
VkBuffer buffer = VK_NULL_HANDLE;
|
VkBuffer buffer = VK_NULL_HANDLE;
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
mDeviceVk->fn.CreateBuffer(mDeviceVk->GetVkDevice(), &createInfo, nullptr, &buffer),
|
mDeviceVk->fn.CreateBuffer(mDeviceVk->GetVkDevice(), &createInfo, nullptr, &*buffer),
|
||||||
VK_SUCCESS);
|
VK_SUCCESS);
|
||||||
mDeviceVk->fn.DestroyBuffer(mDeviceVk->GetVkDevice(), buffer, nullptr);
|
mDeviceVk->fn.DestroyBuffer(mDeviceVk->GetVkDevice(), buffer, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CreateTestBuffer = [&]() -> bool {
|
auto CreateTestBuffer = [&]() -> bool {
|
||||||
VkBuffer buffer = VK_NULL_HANDLE;
|
VkBuffer buffer = VK_NULL_HANDLE;
|
||||||
dawn::native::MaybeError err = CheckVkSuccess(
|
MaybeError err = CheckVkSuccess(
|
||||||
mDeviceVk->fn.CreateBuffer(mDeviceVk->GetVkDevice(), &createInfo, nullptr, &buffer),
|
mDeviceVk->fn.CreateBuffer(mDeviceVk->GetVkDevice(), &createInfo, nullptr, &*buffer),
|
||||||
"vkCreateBuffer");
|
"vkCreateBuffer");
|
||||||
if (err.IsError()) {
|
if (err.IsError()) {
|
||||||
// The handle should never be written to, even for mock failures.
|
// The handle should never be written to, even for mock failures.
|
||||||
|
@ -78,47 +78,49 @@ TEST_P(VulkanErrorInjectorTests, InjectErrorOnCreateBuffer) {
|
||||||
EXPECT_TRUE(CreateTestBuffer());
|
EXPECT_TRUE(CreateTestBuffer());
|
||||||
|
|
||||||
// The error injector call count should be empty
|
// The error injector call count should be empty
|
||||||
EXPECT_EQ(dawn::native::AcquireErrorInjectorCallCount(), 0u);
|
EXPECT_EQ(AcquireErrorInjectorCallCount(), 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test error injection works.
|
// Test error injection works.
|
||||||
dawn::native::EnableErrorInjector();
|
EnableErrorInjector();
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(CreateTestBuffer());
|
EXPECT_TRUE(CreateTestBuffer());
|
||||||
EXPECT_TRUE(CreateTestBuffer());
|
EXPECT_TRUE(CreateTestBuffer());
|
||||||
|
|
||||||
// The error injector call count should be two.
|
// The error injector call count should be two.
|
||||||
EXPECT_EQ(dawn::native::AcquireErrorInjectorCallCount(), 2u);
|
EXPECT_EQ(AcquireErrorInjectorCallCount(), 2u);
|
||||||
|
|
||||||
// Inject an error at index 0. The first should fail, the second succeed.
|
// Inject an error at index 0. The first should fail, the second succeed.
|
||||||
{
|
{
|
||||||
dawn::native::InjectErrorAt(0u);
|
InjectErrorAt(0u);
|
||||||
EXPECT_FALSE(CreateTestBuffer());
|
EXPECT_FALSE(CreateTestBuffer());
|
||||||
EXPECT_TRUE(CreateTestBuffer());
|
EXPECT_TRUE(CreateTestBuffer());
|
||||||
|
|
||||||
dawn::native::ClearErrorInjector();
|
ClearErrorInjector();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject an error at index 1. The second should fail, the first succeed.
|
// Inject an error at index 1. The second should fail, the first succeed.
|
||||||
{
|
{
|
||||||
dawn::native::InjectErrorAt(1u);
|
InjectErrorAt(1u);
|
||||||
EXPECT_TRUE(CreateTestBuffer());
|
EXPECT_TRUE(CreateTestBuffer());
|
||||||
EXPECT_FALSE(CreateTestBuffer());
|
EXPECT_FALSE(CreateTestBuffer());
|
||||||
|
|
||||||
dawn::native::ClearErrorInjector();
|
ClearErrorInjector();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject an error and then clear the injector. Calls should be successful.
|
// Inject an error and then clear the injector. Calls should be successful.
|
||||||
{
|
{
|
||||||
dawn::native::InjectErrorAt(0u);
|
InjectErrorAt(0u);
|
||||||
dawn::native::DisableErrorInjector();
|
DisableErrorInjector();
|
||||||
|
|
||||||
EXPECT_TRUE(CreateTestBuffer());
|
EXPECT_TRUE(CreateTestBuffer());
|
||||||
EXPECT_TRUE(CreateTestBuffer());
|
EXPECT_TRUE(CreateTestBuffer());
|
||||||
|
|
||||||
dawn::native::ClearErrorInjector();
|
ClearErrorInjector();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DAWN_INSTANTIATE_TEST(VulkanErrorInjectorTests, VulkanBackend());
|
DAWN_INSTANTIATE_TEST(VulkanErrorInjectorTests, VulkanBackend());
|
||||||
|
|
||||||
|
} // namespace dawn::native::vulkan
|
||||||
|
|
Loading…
Reference in New Issue