Pipeline cache D3D12 backend impl
Add D3D12 pipeline caching impl: store cachedPSO blob in cached blob. Record root signature ID3DBlob in cache key together with D3D_SHADER_BYTECODE, D3D12_GRAPHICS_PIPELINE_STATE_DESC or D3D12_COMPUTE_PIPELINE_STATE_DESC. Shader caching is not added. Add some pipeline caching negative tests. Bug: dawn:549 Change-Id: Id1cb560b49f1cf495860e2e0bcf92d8d988c5379 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91180 Auto-Submit: Shrek Shao <shrekshao@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Loko Kung <lokokung@google.com> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
c0af5c5c9c
commit
4313dba514
|
@ -7,3 +7,5 @@
|
||||||
*.sh eol=lf
|
*.sh eol=lf
|
||||||
*.spvasm eol=lf
|
*.spvasm eol=lf
|
||||||
*.wgsl eol=lf
|
*.wgsl eol=lf
|
||||||
|
*.h eol=lf
|
||||||
|
*.cpp eol=lf
|
|
@ -378,6 +378,7 @@ source_set("sources") {
|
||||||
"d3d12/BufferD3D12.h",
|
"d3d12/BufferD3D12.h",
|
||||||
"d3d12/CPUDescriptorHeapAllocationD3D12.cpp",
|
"d3d12/CPUDescriptorHeapAllocationD3D12.cpp",
|
||||||
"d3d12/CPUDescriptorHeapAllocationD3D12.h",
|
"d3d12/CPUDescriptorHeapAllocationD3D12.h",
|
||||||
|
"d3d12/CacheKeyD3D12.cpp",
|
||||||
"d3d12/CommandAllocatorManager.cpp",
|
"d3d12/CommandAllocatorManager.cpp",
|
||||||
"d3d12/CommandAllocatorManager.h",
|
"d3d12/CommandAllocatorManager.h",
|
||||||
"d3d12/CommandBufferD3D12.cpp",
|
"d3d12/CommandBufferD3D12.cpp",
|
||||||
|
@ -406,6 +407,7 @@ source_set("sources") {
|
||||||
"d3d12/NativeSwapChainImplD3D12.h",
|
"d3d12/NativeSwapChainImplD3D12.h",
|
||||||
"d3d12/PageableD3D12.cpp",
|
"d3d12/PageableD3D12.cpp",
|
||||||
"d3d12/PageableD3D12.h",
|
"d3d12/PageableD3D12.h",
|
||||||
|
"d3d12/PipelineCacheD3D12.cpp",
|
||||||
"d3d12/PipelineLayoutD3D12.cpp",
|
"d3d12/PipelineLayoutD3D12.cpp",
|
||||||
"d3d12/PipelineLayoutD3D12.h",
|
"d3d12/PipelineLayoutD3D12.h",
|
||||||
"d3d12/PlatformFunctions.cpp",
|
"d3d12/PlatformFunctions.cpp",
|
||||||
|
|
|
@ -21,39 +21,47 @@
|
||||||
|
|
||||||
namespace dawn::native {
|
namespace dawn::native {
|
||||||
|
|
||||||
CachedBlob::CachedBlob(size_t size) {
|
// static
|
||||||
if (size != 0) {
|
CachedBlob CachedBlob::Create(size_t size) {
|
||||||
Reset(size);
|
if (size > 0) {
|
||||||
|
uint8_t* data = new uint8_t[size];
|
||||||
|
return CachedBlob(data, size, [=]() { delete[] data; });
|
||||||
|
} else {
|
||||||
|
return CachedBlob();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CachedBlob::CachedBlob() : mData(nullptr), mSize(0), mDeleter({}) {}
|
||||||
|
|
||||||
|
CachedBlob::CachedBlob(uint8_t* data, size_t size, std::function<void()> deleter)
|
||||||
|
: mData(data), mSize(size), mDeleter(deleter) {}
|
||||||
|
|
||||||
CachedBlob::CachedBlob(CachedBlob&&) = default;
|
CachedBlob::CachedBlob(CachedBlob&&) = default;
|
||||||
|
|
||||||
CachedBlob::~CachedBlob() = default;
|
|
||||||
|
|
||||||
CachedBlob& CachedBlob::operator=(CachedBlob&&) = default;
|
CachedBlob& CachedBlob::operator=(CachedBlob&&) = default;
|
||||||
|
|
||||||
|
CachedBlob::~CachedBlob() {
|
||||||
|
if (mDeleter) {
|
||||||
|
mDeleter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CachedBlob::Empty() const {
|
bool CachedBlob::Empty() const {
|
||||||
return mSize == 0;
|
return mSize == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t* CachedBlob::Data() const {
|
const uint8_t* CachedBlob::Data() const {
|
||||||
return mData.get();
|
return mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* CachedBlob::Data() {
|
uint8_t* CachedBlob::Data() {
|
||||||
return mData.get();
|
return mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CachedBlob::Size() const {
|
size_t CachedBlob::Size() const {
|
||||||
return mSize;
|
return mSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CachedBlob::Reset(size_t size) {
|
|
||||||
mSize = size;
|
|
||||||
mData = std::make_unique<uint8_t[]>(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlobCache::BlobCache(dawn::platform::CachingInterface* cachingInterface)
|
BlobCache::BlobCache(dawn::platform::CachingInterface* cachingInterface)
|
||||||
: mCache(cachingInterface) {}
|
: mCache(cachingInterface) {}
|
||||||
|
|
||||||
|
@ -72,19 +80,20 @@ void BlobCache::Store(const CacheKey& key, const CachedBlob& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedBlob BlobCache::LoadInternal(const CacheKey& key) {
|
CachedBlob BlobCache::LoadInternal(const CacheKey& key) {
|
||||||
CachedBlob result;
|
|
||||||
if (mCache == nullptr) {
|
if (mCache == nullptr) {
|
||||||
return result;
|
return CachedBlob();
|
||||||
}
|
}
|
||||||
const size_t expectedSize = mCache->LoadData(key.data(), key.size(), nullptr, 0);
|
const size_t expectedSize = mCache->LoadData(key.data(), key.size(), nullptr, 0);
|
||||||
if (expectedSize > 0) {
|
if (expectedSize > 0) {
|
||||||
result.Reset(expectedSize);
|
// Need to put this inside to trigger copy elision.
|
||||||
|
CachedBlob result = CachedBlob::Create(expectedSize);
|
||||||
const size_t actualSize =
|
const size_t actualSize =
|
||||||
mCache->LoadData(key.data(), key.size(), result.Data(), expectedSize);
|
mCache->LoadData(key.data(), key.size(), result.Data(), expectedSize);
|
||||||
ASSERT(expectedSize == actualSize);
|
ASSERT(expectedSize == actualSize);
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
return CachedBlob();
|
||||||
|
}
|
||||||
|
|
||||||
void BlobCache::StoreInternal(const CacheKey& key, size_t valueSize, const void* value) {
|
void BlobCache::StoreInternal(const CacheKey& key, size_t valueSize, const void* value) {
|
||||||
ASSERT(value != nullptr);
|
ASSERT(value != nullptr);
|
||||||
|
|
|
@ -15,9 +15,16 @@
|
||||||
#ifndef SRC_DAWN_NATIVE_BLOBCACHE_H_
|
#ifndef SRC_DAWN_NATIVE_BLOBCACHE_H_
|
||||||
#define SRC_DAWN_NATIVE_BLOBCACHE_H_
|
#define SRC_DAWN_NATIVE_BLOBCACHE_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "dawn/common/Platform.h"
|
||||||
|
|
||||||
|
#if defined(DAWN_PLATFORM_WINDOWS)
|
||||||
|
#include "dawn/native/d3d12/d3d12_platform.h"
|
||||||
|
#endif // DAWN_PLATFORM_WINDOWS
|
||||||
|
|
||||||
namespace dawn::platform {
|
namespace dawn::platform {
|
||||||
class CachingInterface;
|
class CachingInterface;
|
||||||
}
|
}
|
||||||
|
@ -30,21 +37,34 @@ class InstanceBase;
|
||||||
|
|
||||||
class CachedBlob {
|
class CachedBlob {
|
||||||
public:
|
public:
|
||||||
explicit CachedBlob(size_t size = 0);
|
static CachedBlob Create(size_t size);
|
||||||
CachedBlob(CachedBlob&&);
|
|
||||||
~CachedBlob();
|
|
||||||
|
|
||||||
|
#if defined(DAWN_PLATFORM_WINDOWS)
|
||||||
|
static CachedBlob Create(Microsoft::WRL::ComPtr<ID3DBlob> blob);
|
||||||
|
#endif // DAWN_PLATFORM_WINDOWS
|
||||||
|
|
||||||
|
CachedBlob(const CachedBlob&) = delete;
|
||||||
|
CachedBlob& operator=(const CachedBlob&) = delete;
|
||||||
|
|
||||||
|
CachedBlob(CachedBlob&&);
|
||||||
CachedBlob& operator=(CachedBlob&&);
|
CachedBlob& operator=(CachedBlob&&);
|
||||||
|
|
||||||
|
~CachedBlob();
|
||||||
|
|
||||||
bool Empty() const;
|
bool Empty() const;
|
||||||
const uint8_t* Data() const;
|
const uint8_t* Data() const;
|
||||||
uint8_t* Data();
|
uint8_t* Data();
|
||||||
size_t Size() const;
|
size_t Size() const;
|
||||||
void Reset(size_t size);
|
void Reset(size_t size);
|
||||||
|
|
||||||
|
CachedBlob();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<uint8_t[]> mData = nullptr;
|
explicit CachedBlob(uint8_t* data, size_t size, std::function<void()> deleter);
|
||||||
size_t mSize = 0;
|
|
||||||
|
uint8_t* mData;
|
||||||
|
size_t mSize;
|
||||||
|
std::function<void()> mDeleter;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This class should always be thread-safe because it may be called asynchronously. Its purpose
|
// This class should always be thread-safe because it may be called asynchronously. Its purpose
|
||||||
|
|
|
@ -247,6 +247,7 @@ if (DAWN_ENABLE_D3D12)
|
||||||
"d3d12/CPUDescriptorHeapAllocationD3D12.h"
|
"d3d12/CPUDescriptorHeapAllocationD3D12.h"
|
||||||
"d3d12/CommandAllocatorManager.cpp"
|
"d3d12/CommandAllocatorManager.cpp"
|
||||||
"d3d12/CommandAllocatorManager.h"
|
"d3d12/CommandAllocatorManager.h"
|
||||||
|
"d3d12/CacheKeyD3D12.cpp"
|
||||||
"d3d12/CommandBufferD3D12.cpp"
|
"d3d12/CommandBufferD3D12.cpp"
|
||||||
"d3d12/CommandBufferD3D12.h"
|
"d3d12/CommandBufferD3D12.h"
|
||||||
"d3d12/CommandRecordingContext.cpp"
|
"d3d12/CommandRecordingContext.cpp"
|
||||||
|
@ -273,6 +274,7 @@ if (DAWN_ENABLE_D3D12)
|
||||||
"d3d12/NativeSwapChainImplD3D12.h"
|
"d3d12/NativeSwapChainImplD3D12.h"
|
||||||
"d3d12/PageableD3D12.cpp"
|
"d3d12/PageableD3D12.cpp"
|
||||||
"d3d12/PageableD3D12.h"
|
"d3d12/PageableD3D12.h"
|
||||||
|
"d3d12/PipelineCacheD3D12.cpp"
|
||||||
"d3d12/PipelineLayoutD3D12.cpp"
|
"d3d12/PipelineLayoutD3D12.cpp"
|
||||||
"d3d12/PipelineLayoutD3D12.h"
|
"d3d12/PipelineLayoutD3D12.h"
|
||||||
"d3d12/PlatformFunctions.cpp"
|
"d3d12/PlatformFunctions.cpp"
|
||||||
|
|
|
@ -628,6 +628,23 @@ BlobCache* DeviceBase::GetBlobCache() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CachedBlob DeviceBase::LoadCachedBlob(const CacheKey& key) {
|
||||||
|
BlobCache* blobCache = GetBlobCache();
|
||||||
|
if (!blobCache) {
|
||||||
|
return CachedBlob();
|
||||||
|
}
|
||||||
|
return blobCache->Load(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceBase::StoreCachedBlob(const CacheKey& key, const CachedBlob& blob) {
|
||||||
|
if (!blob.Empty()) {
|
||||||
|
BlobCache* blobCache = GetBlobCache();
|
||||||
|
if (blobCache) {
|
||||||
|
blobCache->Store(key, blob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MaybeError DeviceBase::ValidateObject(const ApiObjectBase* object) const {
|
MaybeError DeviceBase::ValidateObject(const ApiObjectBase* object) const {
|
||||||
ASSERT(object != nullptr);
|
ASSERT(object != nullptr);
|
||||||
DAWN_INVALID_IF(object->GetDevice() != this,
|
DAWN_INVALID_IF(object->GetDevice() != this,
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dawn/native/BlobCache.h"
|
||||||
#include "dawn/native/CacheKey.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"
|
||||||
|
@ -47,7 +48,6 @@ namespace dawn::native {
|
||||||
class AsyncTaskManager;
|
class AsyncTaskManager;
|
||||||
class AttachmentState;
|
class AttachmentState;
|
||||||
class AttachmentStateBlueprint;
|
class AttachmentStateBlueprint;
|
||||||
class BlobCache;
|
|
||||||
class CallbackTaskManager;
|
class CallbackTaskManager;
|
||||||
class DynamicUploader;
|
class DynamicUploader;
|
||||||
class ErrorScopeStack;
|
class ErrorScopeStack;
|
||||||
|
@ -284,6 +284,8 @@ class DeviceBase : public RefCountedWithExternalCount {
|
||||||
MaybeError ValidateIsAlive() const;
|
MaybeError ValidateIsAlive() const;
|
||||||
|
|
||||||
BlobCache* GetBlobCache();
|
BlobCache* GetBlobCache();
|
||||||
|
CachedBlob LoadCachedBlob(const CacheKey& key);
|
||||||
|
void StoreCachedBlob(const CacheKey& key, const CachedBlob& blob);
|
||||||
|
|
||||||
virtual ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) = 0;
|
virtual ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) = 0;
|
||||||
virtual MaybeError CopyFromStagingToBuffer(StagingBufferBase* source,
|
virtual MaybeError CopyFromStagingToBuffer(StagingBufferBase* source,
|
||||||
|
|
|
@ -38,7 +38,7 @@ MaybeError PipelineCacheBase::Flush() {
|
||||||
}
|
}
|
||||||
// Try to write the data out to the persistent cache.
|
// Try to write the data out to the persistent cache.
|
||||||
CachedBlob blob;
|
CachedBlob blob;
|
||||||
DAWN_TRY_ASSIGN(blob, SerializeToBlobImpl());
|
DAWN_TRY(SerializeToBlobImpl(&blob));
|
||||||
if (blob.Size() > 0) {
|
if (blob.Size() > 0) {
|
||||||
// Using a simple heuristic to decide whether to write out the blob right now. May need
|
// 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.
|
// smarter tracking when we are dealing with monolithic caches.
|
||||||
|
|
|
@ -45,9 +45,10 @@ class PipelineCacheBase : public RefCounted {
|
||||||
CachedBlob Initialize();
|
CachedBlob Initialize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Backend implementation of serialization of the cache into a blob. Note that an empty
|
// Backend implementation of serialization of the cache into a blob.
|
||||||
// blob may be returned.
|
// Note: given that no local cached blob should be destructed and copy elision has strict
|
||||||
virtual ResultOrError<CachedBlob> SerializeToBlobImpl() = 0;
|
// requirement cached blob is passed in as a pointer to be assigned.
|
||||||
|
virtual MaybeError SerializeToBlobImpl(CachedBlob* blob) = 0;
|
||||||
|
|
||||||
// The blob cache is owned by the Adapter and pipeline caches are owned/created by devices
|
// 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,
|
// or adapters. Since the device owns a reference to the Instance which owns the Adapter,
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
// 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/common/Assert.h"
|
||||||
|
#include "dawn/common/Constants.h"
|
||||||
|
#include "dawn/native/CacheKey.h"
|
||||||
|
#include "dawn/native/d3d12/d3d12_platform.h"
|
||||||
|
|
||||||
|
namespace dawn::native {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_COMPUTE_PIPELINE_STATE_DESC>::Serialize(
|
||||||
|
CacheKey* key,
|
||||||
|
const D3D12_COMPUTE_PIPELINE_STATE_DESC& t) {
|
||||||
|
// Don't record pRootSignature as we already record the signature blob in pipline layout.
|
||||||
|
key->Record(t.CS).Record(t.NodeMask).Record(t.Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_RENDER_TARGET_BLEND_DESC>::Serialize(
|
||||||
|
CacheKey* key,
|
||||||
|
const D3D12_RENDER_TARGET_BLEND_DESC& t) {
|
||||||
|
key->Record(t.BlendEnable, t.LogicOpEnable, t.SrcBlend, t.DestBlend, t.BlendOp, t.SrcBlendAlpha,
|
||||||
|
t.DestBlendAlpha, t.BlendOpAlpha, t.LogicOp, t.RenderTargetWriteMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_BLEND_DESC>::Serialize(CacheKey* key, const D3D12_BLEND_DESC& t) {
|
||||||
|
key->Record(t.AlphaToCoverageEnable, t.IndependentBlendEnable).Record(t.RenderTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_DEPTH_STENCILOP_DESC>::Serialize(
|
||||||
|
CacheKey* key,
|
||||||
|
const D3D12_DEPTH_STENCILOP_DESC& t) {
|
||||||
|
key->Record(t.StencilFailOp, t.StencilDepthFailOp, t.StencilPassOp, t.StencilFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_DEPTH_STENCIL_DESC>::Serialize(CacheKey* key,
|
||||||
|
const D3D12_DEPTH_STENCIL_DESC& t) {
|
||||||
|
key->Record(t.DepthEnable, t.DepthWriteMask, t.DepthFunc, t.StencilEnable, t.StencilReadMask,
|
||||||
|
t.StencilWriteMask, t.FrontFace, t.BackFace);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_RASTERIZER_DESC>::Serialize(CacheKey* key,
|
||||||
|
const D3D12_RASTERIZER_DESC& t) {
|
||||||
|
key->Record(t.FillMode, t.CullMode, t.FrontCounterClockwise, t.DepthBias, t.DepthBiasClamp,
|
||||||
|
t.SlopeScaledDepthBias, t.DepthClipEnable, t.MultisampleEnable,
|
||||||
|
t.AntialiasedLineEnable, t.ForcedSampleCount, t.ConservativeRaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_INPUT_ELEMENT_DESC>::Serialize(CacheKey* key,
|
||||||
|
const D3D12_INPUT_ELEMENT_DESC& t) {
|
||||||
|
key->Record(t.SemanticName, t.SemanticIndex, t.Format, t.InputSlot, t.AlignedByteOffset,
|
||||||
|
t.InputSlotClass, t.InstanceDataStepRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_INPUT_LAYOUT_DESC>::Serialize(CacheKey* key,
|
||||||
|
const D3D12_INPUT_LAYOUT_DESC& t) {
|
||||||
|
key->RecordIterable(t.pInputElementDescs, t.NumElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_SO_DECLARATION_ENTRY>::Serialize(
|
||||||
|
CacheKey* key,
|
||||||
|
const D3D12_SO_DECLARATION_ENTRY& t) {
|
||||||
|
key->Record(t.Stream, t.SemanticName, t.SemanticIndex, t.StartComponent, t.ComponentCount,
|
||||||
|
t.OutputSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_STREAM_OUTPUT_DESC>::Serialize(CacheKey* key,
|
||||||
|
const D3D12_STREAM_OUTPUT_DESC& t) {
|
||||||
|
key->RecordIterable(t.pSODeclaration, t.NumEntries)
|
||||||
|
.RecordIterable(t.pBufferStrides, t.NumStrides)
|
||||||
|
.Record(t.RasterizedStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<DXGI_SAMPLE_DESC>::Serialize(CacheKey* key, const DXGI_SAMPLE_DESC& t) {
|
||||||
|
key->Record(t.Count, t.Quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_SHADER_BYTECODE>::Serialize(CacheKey* key,
|
||||||
|
const D3D12_SHADER_BYTECODE& t) {
|
||||||
|
key->RecordIterable(reinterpret_cast<const uint8_t*>(t.pShaderBytecode), t.BytecodeLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<D3D12_GRAPHICS_PIPELINE_STATE_DESC>::Serialize(
|
||||||
|
CacheKey* key,
|
||||||
|
const D3D12_GRAPHICS_PIPELINE_STATE_DESC& t) {
|
||||||
|
// Don't record pRootSignature as we already record the signature blob in pipline layout.
|
||||||
|
// Don't record CachedPSO as it is in the cached blob.
|
||||||
|
key->Record(t.VS)
|
||||||
|
.Record(t.PS)
|
||||||
|
.Record(t.DS)
|
||||||
|
.Record(t.HS)
|
||||||
|
.Record(t.GS)
|
||||||
|
.Record(t.StreamOutput)
|
||||||
|
.Record(t.BlendState)
|
||||||
|
.Record(t.SampleMask)
|
||||||
|
.Record(t.RasterizerState)
|
||||||
|
.Record(t.DepthStencilState)
|
||||||
|
.Record(t.InputLayout)
|
||||||
|
.Record(t.IBStripCutValue)
|
||||||
|
.Record(t.PrimitiveTopologyType)
|
||||||
|
.RecordIterable(t.RTVFormats, t.NumRenderTargets)
|
||||||
|
.Record(t.DSVFormat)
|
||||||
|
.Record(t.SampleDesc)
|
||||||
|
.Record(t.NodeMask)
|
||||||
|
.Record(t.Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void CacheKeySerializer<ID3DBlob>::Serialize(CacheKey* key, const ID3DBlob& t) {
|
||||||
|
// Workaround: GetBufferPointer and GetbufferSize are not marked as const
|
||||||
|
ID3DBlob* pBlob = const_cast<ID3DBlob*>(&t);
|
||||||
|
key->RecordIterable(reinterpret_cast<uint8_t*>(pBlob->GetBufferPointer()),
|
||||||
|
pBlob->GetBufferSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dawn::native
|
|
@ -55,15 +55,36 @@ MaybeError ComputePipeline::Initialize() {
|
||||||
D3D12_COMPUTE_PIPELINE_STATE_DESC d3dDesc = {};
|
D3D12_COMPUTE_PIPELINE_STATE_DESC d3dDesc = {};
|
||||||
d3dDesc.pRootSignature = ToBackend(GetLayout())->GetRootSignature();
|
d3dDesc.pRootSignature = ToBackend(GetLayout())->GetRootSignature();
|
||||||
|
|
||||||
|
// TODO(dawn:549): Compile shader everytime before we implement compiled shader cache
|
||||||
CompiledShader compiledShader;
|
CompiledShader compiledShader;
|
||||||
DAWN_TRY_ASSIGN(compiledShader, module->Compile(computeStage, SingleShaderStage::Compute,
|
DAWN_TRY_ASSIGN(compiledShader, module->Compile(computeStage, SingleShaderStage::Compute,
|
||||||
ToBackend(GetLayout()), compileFlags));
|
ToBackend(GetLayout()), compileFlags));
|
||||||
d3dDesc.CS = compiledShader.GetD3D12ShaderBytecode();
|
d3dDesc.CS = compiledShader.GetD3D12ShaderBytecode();
|
||||||
|
|
||||||
|
mCacheKey.Record(d3dDesc, ToBackend(GetLayout())->GetRootSignatureBlob());
|
||||||
|
|
||||||
|
// Try to see if we have anything in the blob cache.
|
||||||
|
CachedBlob blob = device->LoadCachedBlob(GetCacheKey());
|
||||||
|
const bool cacheHit = !blob.Empty();
|
||||||
|
if (cacheHit) {
|
||||||
|
// Cache hits, attach cached blob to descriptor.
|
||||||
|
d3dDesc.CachedPSO.pCachedBlob = blob.Data();
|
||||||
|
d3dDesc.CachedPSO.CachedBlobSizeInBytes = blob.Size();
|
||||||
|
}
|
||||||
|
|
||||||
auto* d3d12Device = device->GetD3D12Device();
|
auto* d3d12Device = device->GetD3D12Device();
|
||||||
DAWN_TRY(CheckHRESULT(
|
DAWN_TRY(CheckHRESULT(
|
||||||
d3d12Device->CreateComputePipelineState(&d3dDesc, IID_PPV_ARGS(&mPipelineState)),
|
d3d12Device->CreateComputePipelineState(&d3dDesc, IID_PPV_ARGS(&mPipelineState)),
|
||||||
"D3D12 creating pipeline state"));
|
"D3D12 creating pipeline state"));
|
||||||
|
|
||||||
|
if (!cacheHit) {
|
||||||
|
// Cache misses, need to get pipeline cached blob and store.
|
||||||
|
ComPtr<ID3DBlob> d3dBlob;
|
||||||
|
DAWN_TRY(CheckHRESULT(GetPipelineState()->GetCachedBlob(&d3dBlob),
|
||||||
|
"D3D12 compute pipeline state get cached blob"));
|
||||||
|
device->StoreCachedBlob(GetCacheKey(), CachedBlob::Create(std::move(d3dBlob)));
|
||||||
|
}
|
||||||
|
|
||||||
SetLabelImpl();
|
SetLabelImpl();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -441,6 +441,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) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
void Device::InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
|
void Device::InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
|
|
|
@ -188,6 +188,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;
|
||||||
|
|
|
@ -27,6 +27,7 @@ class CommandBuffer;
|
||||||
class ComputePipeline;
|
class ComputePipeline;
|
||||||
class Device;
|
class Device;
|
||||||
class Heap;
|
class Heap;
|
||||||
|
class PipelineCache;
|
||||||
class PipelineLayout;
|
class PipelineLayout;
|
||||||
class QuerySet;
|
class QuerySet;
|
||||||
class Queue;
|
class Queue;
|
||||||
|
@ -46,6 +47,7 @@ struct D3D12BackendTraits {
|
||||||
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,33 @@
|
||||||
|
// 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/BlobCache.h"
|
||||||
|
#include "dawn/native/d3d12/d3d12_platform.h"
|
||||||
|
|
||||||
|
namespace dawn::native {
|
||||||
|
|
||||||
|
// static
|
||||||
|
CachedBlob CachedBlob::Create(ComPtr<ID3DBlob> blob) {
|
||||||
|
// Detach so the deleter callback can "own" the reference
|
||||||
|
ID3DBlob* ptr = blob.Detach();
|
||||||
|
return CachedBlob(reinterpret_cast<uint8_t*>(ptr->GetBufferPointer()), ptr->GetBufferSize(),
|
||||||
|
[=]() {
|
||||||
|
// Reattach and drop to delete it.
|
||||||
|
ComPtr<ID3DBlob> b;
|
||||||
|
b.Attach(ptr);
|
||||||
|
b = nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dawn::native
|
|
@ -252,10 +252,9 @@ MaybeError PipelineLayout::Initialize() {
|
||||||
rootSignatureDescriptor.pStaticSamplers = nullptr;
|
rootSignatureDescriptor.pStaticSamplers = nullptr;
|
||||||
rootSignatureDescriptor.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
rootSignatureDescriptor.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
||||||
|
|
||||||
ComPtr<ID3DBlob> signature;
|
|
||||||
ComPtr<ID3DBlob> error;
|
ComPtr<ID3DBlob> error;
|
||||||
HRESULT hr = device->GetFunctions()->d3d12SerializeRootSignature(
|
HRESULT hr = device->GetFunctions()->d3d12SerializeRootSignature(
|
||||||
&rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
|
&rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &mRootSignatureBlob, &error);
|
||||||
if (DAWN_UNLIKELY(FAILED(hr))) {
|
if (DAWN_UNLIKELY(FAILED(hr))) {
|
||||||
std::ostringstream messageStream;
|
std::ostringstream messageStream;
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -269,9 +268,10 @@ MaybeError PipelineLayout::Initialize() {
|
||||||
DAWN_TRY(CheckHRESULT(hr, messageStream.str().c_str()));
|
DAWN_TRY(CheckHRESULT(hr, messageStream.str().c_str()));
|
||||||
}
|
}
|
||||||
DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateRootSignature(
|
DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateRootSignature(
|
||||||
0, signature->GetBufferPointer(), signature->GetBufferSize(),
|
0, mRootSignatureBlob->GetBufferPointer(),
|
||||||
IID_PPV_ARGS(&mRootSignature)),
|
mRootSignatureBlob->GetBufferSize(), IID_PPV_ARGS(&mRootSignature)),
|
||||||
"D3D12 create root signature"));
|
"D3D12 create root signature"));
|
||||||
|
mCacheKey.Record(mRootSignatureBlob.Get());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,6 +310,10 @@ ID3D12RootSignature* PipelineLayout::GetRootSignature() const {
|
||||||
return mRootSignature.Get();
|
return mRootSignature.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ID3DBlob* PipelineLayout::GetRootSignatureBlob() const {
|
||||||
|
return mRootSignatureBlob.Get();
|
||||||
|
}
|
||||||
|
|
||||||
const PipelineLayout::DynamicStorageBufferLengthInfo&
|
const PipelineLayout::DynamicStorageBufferLengthInfo&
|
||||||
PipelineLayout::GetDynamicStorageBufferLengthInfo() const {
|
PipelineLayout::GetDynamicStorageBufferLengthInfo() const {
|
||||||
return mDynamicStorageBufferLengthInfo;
|
return mDynamicStorageBufferLengthInfo;
|
||||||
|
|
|
@ -52,6 +52,8 @@ class PipelineLayout final : public PipelineLayoutBase {
|
||||||
|
|
||||||
ID3D12RootSignature* GetRootSignature() const;
|
ID3D12RootSignature* GetRootSignature() const;
|
||||||
|
|
||||||
|
ID3DBlob* GetRootSignatureBlob() const;
|
||||||
|
|
||||||
ID3D12CommandSignature* GetDispatchIndirectCommandSignatureWithNumWorkgroups();
|
ID3D12CommandSignature* GetDispatchIndirectCommandSignatureWithNumWorkgroups();
|
||||||
|
|
||||||
ID3D12CommandSignature* GetDrawIndirectCommandSignatureWithInstanceVertexOffsets();
|
ID3D12CommandSignature* GetDrawIndirectCommandSignatureWithInstanceVertexOffsets();
|
||||||
|
@ -98,6 +100,8 @@ class PipelineLayout final : public PipelineLayoutBase {
|
||||||
uint32_t mNumWorkgroupsParameterIndex;
|
uint32_t mNumWorkgroupsParameterIndex;
|
||||||
uint32_t mDynamicStorageBufferLengthsParameterIndex;
|
uint32_t mDynamicStorageBufferLengthsParameterIndex;
|
||||||
ComPtr<ID3D12RootSignature> mRootSignature;
|
ComPtr<ID3D12RootSignature> mRootSignature;
|
||||||
|
// Store the root signature blob to put in pipeline cachekey
|
||||||
|
ComPtr<ID3DBlob> mRootSignatureBlob;
|
||||||
ComPtr<ID3D12CommandSignature> mDispatchIndirectCommandSignatureWithNumWorkgroups;
|
ComPtr<ID3D12CommandSignature> mDispatchIndirectCommandSignatureWithNumWorkgroups;
|
||||||
ComPtr<ID3D12CommandSignature> mDrawIndirectCommandSignatureWithInstanceVertexOffsets;
|
ComPtr<ID3D12CommandSignature> mDrawIndirectCommandSignatureWithInstanceVertexOffsets;
|
||||||
ComPtr<ID3D12CommandSignature> mDrawIndexedIndirectCommandSignatureWithInstanceVertexOffsets;
|
ComPtr<ID3D12CommandSignature> mDrawIndexedIndirectCommandSignatureWithInstanceVertexOffsets;
|
||||||
|
|
|
@ -429,10 +429,29 @@ MaybeError RenderPipeline::Initialize() {
|
||||||
|
|
||||||
mD3d12PrimitiveTopology = D3D12PrimitiveTopology(GetPrimitiveTopology());
|
mD3d12PrimitiveTopology = D3D12PrimitiveTopology(GetPrimitiveTopology());
|
||||||
|
|
||||||
|
mCacheKey.Record(descriptorD3D12, *layout->GetRootSignatureBlob());
|
||||||
|
|
||||||
|
// Try to see if we have anything in the blob cache.
|
||||||
|
CachedBlob blob = device->LoadCachedBlob(GetCacheKey());
|
||||||
|
const bool cacheHit = !blob.Empty();
|
||||||
|
if (cacheHit) {
|
||||||
|
// Cache hits, attach cached blob to descriptor.
|
||||||
|
descriptorD3D12.CachedPSO.pCachedBlob = blob.Data();
|
||||||
|
descriptorD3D12.CachedPSO.CachedBlobSizeInBytes = blob.Size();
|
||||||
|
}
|
||||||
|
|
||||||
DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateGraphicsPipelineState(
|
DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateGraphicsPipelineState(
|
||||||
&descriptorD3D12, IID_PPV_ARGS(&mPipelineState)),
|
&descriptorD3D12, IID_PPV_ARGS(&mPipelineState)),
|
||||||
"D3D12 create graphics pipeline state"));
|
"D3D12 create graphics pipeline state"));
|
||||||
|
|
||||||
|
if (!cacheHit) {
|
||||||
|
// Cache misses, need to get pipeline cached blob and store.
|
||||||
|
ComPtr<ID3DBlob> d3dBlob;
|
||||||
|
DAWN_TRY(CheckHRESULT(GetPipelineState()->GetCachedBlob(&d3dBlob),
|
||||||
|
"D3D12 render pipeline state get cached blob"));
|
||||||
|
device->StoreCachedBlob(GetCacheKey(), CachedBlob::Create(std::move(d3dBlob)));
|
||||||
|
}
|
||||||
|
|
||||||
SetLabelImpl();
|
SetLabelImpl();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -53,6 +53,8 @@ uint64_t Buffer::QueryMaxBufferLength(id<MTLDevice> mtlDevice) {
|
||||||
if (@available(macOS 10.11, *)) {
|
if (@available(macOS 10.11, *)) {
|
||||||
return 256 * 1024 * 1024;
|
return 256 * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
// 256Mb for other platform if any. (Need to have a return for all branches).
|
||||||
|
return 256 * 1024 * 1024;
|
||||||
#else
|
#else
|
||||||
// macOS / tvOS: 256Mb limit in versions without [MTLDevice maxBufferLength]
|
// macOS / tvOS: 256Mb limit in versions without [MTLDevice maxBufferLength]
|
||||||
return 256 * 1024 * 1024;
|
return 256 * 1024 * 1024;
|
||||||
|
|
|
@ -50,10 +50,10 @@ VkPipelineCache PipelineCache::GetHandle() const {
|
||||||
return mHandle;
|
return mHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultOrError<CachedBlob> PipelineCache::SerializeToBlobImpl() {
|
MaybeError PipelineCache::SerializeToBlobImpl(CachedBlob* blob) {
|
||||||
CachedBlob emptyBlob;
|
|
||||||
if (mHandle == VK_NULL_HANDLE) {
|
if (mHandle == VK_NULL_HANDLE) {
|
||||||
return emptyBlob;
|
// Pipeline cache isn't created successfully
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t bufferSize;
|
size_t bufferSize;
|
||||||
|
@ -61,12 +61,13 @@ ResultOrError<CachedBlob> PipelineCache::SerializeToBlobImpl() {
|
||||||
DAWN_TRY(CheckVkSuccess(
|
DAWN_TRY(CheckVkSuccess(
|
||||||
device->fn.GetPipelineCacheData(device->GetVkDevice(), mHandle, &bufferSize, nullptr),
|
device->fn.GetPipelineCacheData(device->GetVkDevice(), mHandle, &bufferSize, nullptr),
|
||||||
"GetPipelineCacheData"));
|
"GetPipelineCacheData"));
|
||||||
|
if (bufferSize > 0) {
|
||||||
CachedBlob blob(bufferSize);
|
*blob = CachedBlob::Create(bufferSize);
|
||||||
DAWN_TRY(CheckVkSuccess(
|
DAWN_TRY(CheckVkSuccess(device->fn.GetPipelineCacheData(device->GetVkDevice(), mHandle,
|
||||||
device->fn.GetPipelineCacheData(device->GetVkDevice(), mHandle, &bufferSize, blob.Data()),
|
&bufferSize, blob->Data()),
|
||||||
"GetPipelineCacheData"));
|
"GetPipelineCacheData"));
|
||||||
return blob;
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineCache::Initialize() {
|
void PipelineCache::Initialize() {
|
||||||
|
|
|
@ -38,7 +38,7 @@ class PipelineCache final : public PipelineCacheBase {
|
||||||
~PipelineCache() override;
|
~PipelineCache() override;
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
ResultOrError<CachedBlob> SerializeToBlobImpl() override;
|
MaybeError SerializeToBlobImpl(CachedBlob* blob) override;
|
||||||
|
|
||||||
DeviceBase* mDevice;
|
DeviceBase* mDevice;
|
||||||
VkPipelineCache mHandle = VK_NULL_HANDLE;
|
VkPipelineCache mHandle = VK_NULL_HANDLE;
|
||||||
|
|
|
@ -26,18 +26,49 @@ using ::testing::NiceMock;
|
||||||
|
|
||||||
// TODO(dawn:549) Add some sort of pipeline descriptor repository to test more caching.
|
// TODO(dawn:549) Add some sort of pipeline descriptor repository to test more caching.
|
||||||
|
|
||||||
static constexpr std::string_view kComputeShader = R"(
|
static constexpr std::string_view kComputeShaderDefault = R"(
|
||||||
@stage(compute) @workgroup_size(1) fn main() {}
|
@stage(compute) @workgroup_size(1) fn main() {}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
static constexpr std::string_view kVertexShader = R"(
|
static constexpr std::string_view kComputeShaderMultipleEntryPoints = R"(
|
||||||
|
@stage(compute) @workgroup_size(16) fn main() {}
|
||||||
|
@stage(compute) @workgroup_size(64) fn main2() {}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr std::string_view kVertexShaderDefault = R"(
|
||||||
@stage(vertex) fn main() -> @builtin(position) vec4<f32> {
|
@stage(vertex) fn main() -> @builtin(position) vec4<f32> {
|
||||||
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
static constexpr std::string_view kFragmentShader = R"(
|
static constexpr std::string_view kVertexShaderMultipleEntryPoints = R"(
|
||||||
@stage(fragment) fn main() {}
|
@stage(vertex) fn main() -> @builtin(position) vec4<f32> {
|
||||||
|
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@stage(vertex) fn main2() -> @builtin(position) vec4<f32> {
|
||||||
|
return vec4<f32>(0.5, 0.5, 0.5, 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr std::string_view kFragmentShaderDefault = R"(
|
||||||
|
@stage(fragment) fn main() -> @location(0) vec4<f32> {
|
||||||
|
return vec4<f32>(0.1, 0.2, 0.3, 0.4);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr std::string_view kFragmentShaderMultipleOutput = R"(
|
||||||
|
struct FragmentOut {
|
||||||
|
@location(0) fragColor0 : vec4<f32>,
|
||||||
|
@location(1) fragColor1 : vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@stage(fragment) fn main() -> FragmentOut {
|
||||||
|
var output : FragmentOut;
|
||||||
|
output.fragColor0 = vec4<f32>(0.1, 0.2, 0.3, 0.4);
|
||||||
|
output.fragColor1 = vec4<f32>(0.5, 0.6, 0.7, 0.8);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
class PipelineCachingTests : public DawnTest {
|
class PipelineCachingTests : public DawnTest {
|
||||||
|
@ -62,7 +93,7 @@ TEST_P(SinglePipelineCachingTests, ComputePipelineNoCache) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice();
|
wgpu::Device device = CreateDevice();
|
||||||
wgpu::ComputePipelineDescriptor desc;
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
desc.compute.entryPoint = "main";
|
desc.compute.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -72,7 +103,7 @@ TEST_P(SinglePipelineCachingTests, ComputePipelineNoCache) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice();
|
wgpu::Device device = CreateDevice();
|
||||||
wgpu::ComputePipelineDescriptor desc;
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
desc.compute.entryPoint = "main";
|
desc.compute.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -82,7 +113,7 @@ TEST_P(SinglePipelineCachingTests, ComputePipelineNoCache) {
|
||||||
// Tests that pipeline creation on the same device uses frontend cache when possible.
|
// Tests that pipeline creation on the same device uses frontend cache when possible.
|
||||||
TEST_P(SinglePipelineCachingTests, ComputePipelineFrontedCache) {
|
TEST_P(SinglePipelineCachingTests, ComputePipelineFrontedCache) {
|
||||||
wgpu::ComputePipelineDescriptor desc;
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
desc.compute.entryPoint = "main";
|
desc.compute.entryPoint = "main";
|
||||||
|
|
||||||
// First creation should create a cache entry.
|
// First creation should create a cache entry.
|
||||||
|
@ -106,7 +137,7 @@ TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCache) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice();
|
wgpu::Device device = CreateDevice();
|
||||||
wgpu::ComputePipelineDescriptor desc;
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
desc.compute.entryPoint = "main";
|
desc.compute.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -116,13 +147,74 @@ TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCache) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice();
|
wgpu::Device device = CreateDevice();
|
||||||
wgpu::ComputePipelineDescriptor desc;
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
desc.compute.entryPoint = "main";
|
desc.compute.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 1u, device.CreateComputePipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 1u, device.CreateComputePipeline(&desc));
|
||||||
}
|
}
|
||||||
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation hits the cache when using the same pipeline but with explicit
|
||||||
|
// layout.
|
||||||
|
TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCacheExplictLayout) {
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Cache should hit: use the same pipeline but with explicit pipeline layout.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
desc.layout = utils::MakeBasicPipelineLayout(device, {});
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 1u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation wouldn't hit the cache if the pipelines are not exactly the same.
|
||||||
|
TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCacheShaderNegativeCases) {
|
||||||
|
size_t numCacheEntries = 0u;
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
|
||||||
|
// Cache should not hit: different shader module.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module =
|
||||||
|
utils::CreateShaderModule(device, kComputeShaderMultipleEntryPoints.data());
|
||||||
|
desc.compute.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
|
||||||
|
// Cache should not hit: same shader module but different shader entry point.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
|
desc.compute.module =
|
||||||
|
utils::CreateShaderModule(device, kComputeShaderMultipleEntryPoints.data());
|
||||||
|
desc.compute.entryPoint = "main2";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that pipeline creation does not hits the cache when it is enabled but we use different
|
// Tests that pipeline creation does not hits the cache when it is enabled but we use different
|
||||||
// isolation keys.
|
// isolation keys.
|
||||||
TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCacheIsolationKey) {
|
TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCacheIsolationKey) {
|
||||||
|
@ -130,7 +222,7 @@ TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCacheIsolationKey) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice("isolation key 1");
|
wgpu::Device device = CreateDevice("isolation key 1");
|
||||||
wgpu::ComputePipelineDescriptor desc;
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
desc.compute.entryPoint = "main";
|
desc.compute.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -140,7 +232,7 @@ TEST_P(SinglePipelineCachingTests, ComputePipelineBlobCacheIsolationKey) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice("isolation key 2");
|
wgpu::Device device = CreateDevice("isolation key 2");
|
||||||
wgpu::ComputePipelineDescriptor desc;
|
wgpu::ComputePipelineDescriptor desc;
|
||||||
desc.compute.module = utils::CreateShaderModule(device, kComputeShader.data());
|
desc.compute.module = utils::CreateShaderModule(device, kComputeShaderDefault.data());
|
||||||
desc.compute.entryPoint = "main";
|
desc.compute.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateComputePipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -158,10 +250,9 @@ TEST_P(SinglePipelineCachingTests, RenderPipelineNoCache) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice();
|
wgpu::Device device = CreateDevice();
|
||||||
utils::ComboRenderPipelineDescriptor desc;
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
|
||||||
desc.vertex.entryPoint = "main";
|
desc.vertex.entryPoint = "main";
|
||||||
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
desc.cFragment.entryPoint = "main";
|
desc.cFragment.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -171,10 +262,9 @@ TEST_P(SinglePipelineCachingTests, RenderPipelineNoCache) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice();
|
wgpu::Device device = CreateDevice();
|
||||||
utils::ComboRenderPipelineDescriptor desc;
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
|
||||||
desc.vertex.entryPoint = "main";
|
desc.vertex.entryPoint = "main";
|
||||||
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
desc.cFragment.entryPoint = "main";
|
desc.cFragment.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -184,10 +274,9 @@ TEST_P(SinglePipelineCachingTests, RenderPipelineNoCache) {
|
||||||
// Tests that pipeline creation on the same device uses frontend cache when possible.
|
// Tests that pipeline creation on the same device uses frontend cache when possible.
|
||||||
TEST_P(SinglePipelineCachingTests, RenderPipelineFrontedCache) {
|
TEST_P(SinglePipelineCachingTests, RenderPipelineFrontedCache) {
|
||||||
utils::ComboRenderPipelineDescriptor desc;
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
|
||||||
desc.vertex.entryPoint = "main";
|
desc.vertex.entryPoint = "main";
|
||||||
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
desc.cFragment.entryPoint = "main";
|
desc.cFragment.entryPoint = "main";
|
||||||
|
|
||||||
// First creation should create a cache entry.
|
// First creation should create a cache entry.
|
||||||
|
@ -211,10 +300,9 @@ TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCache) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice();
|
wgpu::Device device = CreateDevice();
|
||||||
utils::ComboRenderPipelineDescriptor desc;
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
|
||||||
desc.vertex.entryPoint = "main";
|
desc.vertex.entryPoint = "main";
|
||||||
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
desc.cFragment.entryPoint = "main";
|
desc.cFragment.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -224,16 +312,172 @@ TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCache) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice();
|
wgpu::Device device = CreateDevice();
|
||||||
utils::ComboRenderPipelineDescriptor desc;
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
|
||||||
desc.vertex.entryPoint = "main";
|
desc.vertex.entryPoint = "main";
|
||||||
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
desc.cFragment.entryPoint = "main";
|
desc.cFragment.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 1u, device.CreateRenderPipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 1u, device.CreateRenderPipeline(&desc));
|
||||||
}
|
}
|
||||||
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation hits the cache when using the same pipeline but with explicit
|
||||||
|
// layout.
|
||||||
|
TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheExplictLayout) {
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Cache should hit: use the same pipeline but with explicit pipeline layout.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
desc.layout = utils::MakeBasicPipelineLayout(device, {});
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 1u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation wouldn't hit the cache if the pipelines have different state set in
|
||||||
|
// the descriptor.
|
||||||
|
TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheDescriptorNegativeCases) {
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 1u);
|
||||||
|
|
||||||
|
// Cache should not hit: different pipeline descriptor state.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation wouldn't hit the cache if the pipelines are not exactly the same in
|
||||||
|
// terms of shader.
|
||||||
|
TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheShaderNegativeCases) {
|
||||||
|
size_t numCacheEntries = 0u;
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
|
||||||
|
// Cache should not hit: different shader module.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.vertex.module =
|
||||||
|
utils::CreateShaderModule(device, kVertexShaderMultipleEntryPoints.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
|
||||||
|
// Cache should not hit: same shader module but different shader entry point.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.vertex.module =
|
||||||
|
utils::CreateShaderModule(device, kVertexShaderMultipleEntryPoints.data());
|
||||||
|
desc.vertex.entryPoint = "main2";
|
||||||
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that pipeline creation wouldn't hit the cache if the pipelines are not exactly the same
|
||||||
|
// (fragment color targets differences).
|
||||||
|
TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheNegativeCasesFragmentColorTargets) {
|
||||||
|
size_t numCacheEntries = 0u;
|
||||||
|
// First time should create and write out to the cache.
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cFragment.targetCount = 2;
|
||||||
|
desc.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
desc.cTargets[1].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.cTargets[1].format = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module =
|
||||||
|
utils::CreateShaderModule(device, kFragmentShaderMultipleOutput.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
|
||||||
|
// Cache should not hit: different fragment color target state (sparse).
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cFragment.targetCount = 2;
|
||||||
|
desc.cTargets[0].format = wgpu::TextureFormat::Undefined;
|
||||||
|
desc.cTargets[1].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.cTargets[1].format = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module =
|
||||||
|
utils::CreateShaderModule(device, kFragmentShaderMultipleOutput.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
|
||||||
|
// Cache should not hit: different fragment color target state (trailing empty).
|
||||||
|
{
|
||||||
|
wgpu::Device device = CreateDevice();
|
||||||
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
|
desc.cFragment.targetCount = 2;
|
||||||
|
desc.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
desc.cTargets[1].writeMask = wgpu::ColorWriteMask::None;
|
||||||
|
desc.cTargets[1].format = wgpu::TextureFormat::Undefined;
|
||||||
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
|
desc.vertex.entryPoint = "main";
|
||||||
|
desc.cFragment.module =
|
||||||
|
utils::CreateShaderModule(device, kFragmentShaderMultipleOutput.data());
|
||||||
|
desc.cFragment.entryPoint = "main";
|
||||||
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(mMockCache.GetNumEntries(), ++numCacheEntries);
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that pipeline creation does not hits the cache when it is enabled but we use different
|
// Tests that pipeline creation does not hits the cache when it is enabled but we use different
|
||||||
// isolation keys.
|
// isolation keys.
|
||||||
TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheIsolationKey) {
|
TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheIsolationKey) {
|
||||||
|
@ -241,10 +485,9 @@ TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheIsolationKey) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice("isolation key 1");
|
wgpu::Device device = CreateDevice("isolation key 1");
|
||||||
utils::ComboRenderPipelineDescriptor desc;
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
|
||||||
desc.vertex.entryPoint = "main";
|
desc.vertex.entryPoint = "main";
|
||||||
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
desc.cFragment.entryPoint = "main";
|
desc.cFragment.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
}
|
}
|
||||||
|
@ -254,16 +497,17 @@ TEST_P(SinglePipelineCachingTests, RenderPipelineBlobCacheIsolationKey) {
|
||||||
{
|
{
|
||||||
wgpu::Device device = CreateDevice("isolation key 2");
|
wgpu::Device device = CreateDevice("isolation key 2");
|
||||||
utils::ComboRenderPipelineDescriptor desc;
|
utils::ComboRenderPipelineDescriptor desc;
|
||||||
desc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
desc.vertex.module = utils::CreateShaderModule(device, kVertexShaderDefault.data());
|
||||||
desc.vertex.module = utils::CreateShaderModule(device, kVertexShader.data());
|
|
||||||
desc.vertex.entryPoint = "main";
|
desc.vertex.entryPoint = "main";
|
||||||
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShader.data());
|
desc.cFragment.module = utils::CreateShaderModule(device, kFragmentShaderDefault.data());
|
||||||
desc.cFragment.entryPoint = "main";
|
desc.cFragment.entryPoint = "main";
|
||||||
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
EXPECT_CACHE_HIT(mMockCache, 0u, device.CreateRenderPipeline(&desc));
|
||||||
}
|
}
|
||||||
EXPECT_EQ(mMockCache.GetNumEntries(), 2u);
|
EXPECT_EQ(mMockCache.GetNumEntries(), 2u);
|
||||||
}
|
}
|
||||||
|
|
||||||
DAWN_INSTANTIATE_TEST(SinglePipelineCachingTests, VulkanBackend({"enable_blob_cache"}));
|
DAWN_INSTANTIATE_TEST(SinglePipelineCachingTests,
|
||||||
|
VulkanBackend({"enable_blob_cache"}),
|
||||||
|
D3D12Backend({"enable_blob_cache"}));
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
Loading…
Reference in New Issue