D3D11: Cache last created SRV and RTV in the Texture class.
It's a common scenario that users create a texture's view transiently
once per frame and never cache it.
e.g:
void renderStep() {
// Create a view and attach it to a render pass.
wgpu::RenderPassDescriptor renderPassDesc = ...;
renderPassDesc.colorAttachments[0].view = texture.CreateView();
// Draw the render pass.
}
In this case, D3D11 has to repeatedly re-create the texture's D3D native
view every frame. This is not a cheap operation. Unlike metal backend,
where the view creation could be skipped entirely in some simple
scenarios:
See: https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/src/dawn/native/metal/TextureMTL.mm;drc=5ca64904963dc4676216ddfd226779522c271540;l=104
This CL adds a small optimization for this scenario by:
- Caching last created D3D views within the wgpu::Texture object.
- Checking the cache for an existing D3D view before creating a new one
when a wgpu::TextureView is initialized.
Bug: chromium:377716220
Bug: 409035452
Change-Id: Id99c38f8c91dc441f9a6ba92ca888dea3e8d37dc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/234254
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
diff --git a/src/dawn/native/d3d11/TextureD3D11.cpp b/src/dawn/native/d3d11/TextureD3D11.cpp
index 7fbe37c..ea1f482 100644
--- a/src/dawn/native/d3d11/TextureD3D11.cpp
+++ b/src/dawn/native/d3d11/TextureD3D11.cpp
@@ -146,6 +146,34 @@
} // namespace
+// TODO(409035452): Consider using the real LRU cache for caching the views.
+template <typename K, typename T>
+ComPtr<T> Texture::ViewCache<K, T>::Get(const K& key) const {
+ for (auto ite = mViews.begin(); ite != mViews.end(); ++ite) {
+ if (ite->key == key) {
+ return ite->view;
+ }
+ }
+
+ return nullptr;
+}
+
+template <typename K, typename T>
+void Texture::ViewCache<K, T>::Set(const K& key, ComPtr<T> view) {
+ if (mViews.size() == kMaxCacheSize) {
+ // Remove oldest entry.
+ mViews.erase(mViews.begin());
+ }
+
+ DAWN_ASSERT(mViews.size() < kMaxCacheSize);
+ mViews.emplace_back(key, std::move(view));
+}
+
+template <typename K, typename T>
+void Texture::ViewCache<K, T>::Clear() {
+ mViews.clear();
+}
+
// static
ResultOrError<Ref<Texture>> Texture::Create(Device* device,
const UnpackedPtr<TextureDescriptor>& descriptor) {
@@ -320,27 +348,30 @@
mD3d11Resource = nullptr;
mKeyedMutex = nullptr;
mTextureForStencilSampling = nullptr;
+ mCachedRTVs.Clear();
+ mCachedSRVs.Clear();
}
ID3D11Resource* Texture::GetD3D11Resource() const {
return mD3d11Resource.Get();
}
-ResultOrError<ComPtr<ID3D11RenderTargetView>> Texture::CreateD3D11RenderTargetView(
- wgpu::TextureFormat format,
- uint32_t mipLevel,
- uint32_t baseSlice,
- uint32_t sliceCount,
- uint32_t planeSlice) const {
+ResultOrError<ComPtr<ID3D11RenderTargetView1>> Texture::GetOrCreateD3D11RenderTargetView(
+ const RTVKey& key) {
+ auto cachedRTV = mCachedRTVs.Get(key);
+ if (cachedRTV) {
+ return std::move(cachedRTV);
+ }
+
D3D11_RENDER_TARGET_VIEW_DESC1 rtvDesc;
- rtvDesc.Format = d3d::DXGITextureFormat(GetDevice(), format);
+ rtvDesc.Format = d3d::DXGITextureFormat(GetDevice(), key.viewFormat);
if (IsMultisampledTexture()) {
DAWN_ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
DAWN_ASSERT(GetNumMipLevels() == 1);
- DAWN_ASSERT(mipLevel == 0);
- DAWN_ASSERT(baseSlice == 0);
- DAWN_ASSERT(sliceCount == 1);
- DAWN_ASSERT(planeSlice == 0);
+ DAWN_ASSERT(key.mipLevel == 0);
+ DAWN_ASSERT(key.baseSlice == 0);
+ DAWN_ASSERT(key.sliceCount == 1);
+ DAWN_ASSERT(key.planeSlice == 0);
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
} else {
switch (GetDimension()) {
@@ -354,20 +385,20 @@
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/ns-d3d11-d3d11_tex2d_array
// _rtv
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
- rtvDesc.Texture2DArray.MipSlice = mipLevel;
- rtvDesc.Texture2DArray.FirstArraySlice = baseSlice;
- rtvDesc.Texture2DArray.ArraySize = sliceCount;
- rtvDesc.Texture2DArray.PlaneSlice = planeSlice;
+ rtvDesc.Texture2DArray.MipSlice = key.mipLevel;
+ rtvDesc.Texture2DArray.FirstArraySlice = key.baseSlice;
+ rtvDesc.Texture2DArray.ArraySize = key.sliceCount;
+ rtvDesc.Texture2DArray.PlaneSlice = key.planeSlice;
break;
case wgpu::TextureDimension::e3D:
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
- rtvDesc.Texture3D.MipSlice = mipLevel;
- rtvDesc.Texture3D.FirstWSlice = baseSlice;
- rtvDesc.Texture3D.WSize = sliceCount;
+ rtvDesc.Texture3D.MipSlice = key.mipLevel;
+ rtvDesc.Texture3D.FirstWSlice = key.baseSlice;
+ rtvDesc.Texture3D.WSize = key.sliceCount;
break;
case wgpu::TextureDimension::e1D:
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
- rtvDesc.Texture1D.MipSlice = mipLevel;
+ rtvDesc.Texture1D.MipSlice = key.mipLevel;
break;
}
}
@@ -378,7 +409,9 @@
->CreateRenderTargetView1(GetD3D11Resource(), &rtvDesc, &rtv),
"CreateRenderTargetView"));
- return {std::move(rtv)};
+ mCachedRTVs.Set(key, rtv);
+
+ return std::move(rtv);
}
ResultOrError<ComPtr<ID3D11DepthStencilView>> Texture::CreateD3D11DepthStencilView(
@@ -519,12 +552,12 @@
// RTV, we can use the 'layer' as baseSlice and the 'depthOrArrayLayers' as
// sliceCount to create RTV without checking the dimension.
DAWN_TRY_ASSIGN(d3d11RTV,
- CreateD3D11RenderTargetView(
- format, clearRange.baseMipLevel, clearRange.baseArrayLayer,
- GetMipLevelSingleSubresourceVirtualSize(
- clearRange.baseMipLevel, clearRange.aspects)
- .depthOrArrayLayers,
- GetAspectIndex(aspect)));
+ GetOrCreateD3D11RenderTargetView(
+ {format, clearRange.baseMipLevel, clearRange.baseArrayLayer,
+ GetMipLevelSingleSubresourceVirtualSize(
+ clearRange.baseMipLevel, clearRange.aspects)
+ .depthOrArrayLayers,
+ GetAspectIndex(aspect)}));
commandContext->ClearRenderTargetView(d3d11RTV.Get(), d3d11ClearValue.color);
}
}
@@ -1148,6 +1181,85 @@
return srv;
}
+ResultOrError<ComPtr<ID3D11ShaderResourceView1>> Texture::GetOrCreateSRV(const SRVKey& key) {
+ auto cacheSRV = mCachedSRVs.Get(key);
+ if (cacheSRV) {
+ return std::move(cacheSRV);
+ }
+
+ D3D11_SHADER_RESOURCE_VIEW_DESC1 srvDesc;
+ srvDesc.Format = d3d::D3DShaderResourceViewFormat(
+ GetDevice(), GetFormat(), GetDevice()->GetValidInternalFormat(key.viewFormat), key.aspects);
+
+ // Currently we always use D3D11_TEX2D_ARRAY_SRV because we cannot specify base array
+ // layer and layer count in D3D11_TEX2D_SRV. For 2D texture views, we treat them as
+ // 1-layer 2D array textures. Multisampled textures may only be one array layer, so we
+ // use D3D11_SRV_DIMENSION_TEXTURE2DMS.
+ // https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/ns-d3d11-d3d11_tex2d_srv
+ // https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/ns-d3d11-d3d11_tex2d_array_srv
+ if (IsMultisampledTexture()) {
+ switch (key.viewDimension) {
+ case wgpu::TextureViewDimension::e2DArray:
+ DAWN_ASSERT(GetArrayLayers() == 1);
+ [[fallthrough]];
+ case wgpu::TextureViewDimension::e2D:
+ DAWN_ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
+ break;
+
+ default:
+ DAWN_UNREACHABLE();
+ }
+ } else {
+ switch (key.viewDimension) {
+ case wgpu::TextureViewDimension::e1D:
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
+ srvDesc.Texture1D.MipLevels = key.levelCount;
+ srvDesc.Texture1D.MostDetailedMip = key.mipLevel;
+ break;
+
+ case wgpu::TextureViewDimension::e2D:
+ case wgpu::TextureViewDimension::e2DArray:
+ DAWN_ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
+ srvDesc.Texture2DArray.ArraySize = key.sliceCount;
+ srvDesc.Texture2DArray.FirstArraySlice = key.baseSlice;
+ srvDesc.Texture2DArray.MipLevels = key.levelCount;
+ srvDesc.Texture2DArray.MostDetailedMip = key.mipLevel;
+ srvDesc.Texture2DArray.PlaneSlice = GetAspectIndex(key.aspects);
+ break;
+ case wgpu::TextureViewDimension::Cube:
+ case wgpu::TextureViewDimension::CubeArray:
+ DAWN_ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
+ DAWN_ASSERT(key.sliceCount % 6 == 0);
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY;
+ srvDesc.TextureCubeArray.First2DArrayFace = key.baseSlice;
+ srvDesc.TextureCubeArray.NumCubes = key.sliceCount / 6;
+ srvDesc.TextureCubeArray.MipLevels = key.levelCount;
+ srvDesc.TextureCubeArray.MostDetailedMip = key.mipLevel;
+ break;
+ case wgpu::TextureViewDimension::e3D:
+ DAWN_ASSERT(GetDimension() == wgpu::TextureDimension::e3D);
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+ srvDesc.Texture3D.MostDetailedMip = key.mipLevel;
+ srvDesc.Texture3D.MipLevels = key.levelCount;
+ break;
+
+ case wgpu::TextureViewDimension::Undefined:
+ DAWN_UNREACHABLE();
+ }
+ }
+
+ ComPtr<ID3D11ShaderResourceView1> srv;
+ DAWN_TRY(CheckHRESULT(ToBackend(GetDevice())
+ ->GetD3D11Device3()
+ ->CreateShaderResourceView1(GetD3D11Resource(), &srvDesc, &srv),
+ "CreateShaderResourceView1"));
+
+ mCachedSRVs.Set(key, srv);
+ return std::move(srv);
+}
+
// static
Ref<TextureView> TextureView::Create(TextureBase* texture,
const UnpackedPtr<TextureViewDescriptor>& descriptor) {
@@ -1169,75 +1281,11 @@
return mD3d11SharedResourceView.Get();
}
- Device* device = ToBackend(GetDevice());
- D3D11_SHADER_RESOURCE_VIEW_DESC1 srvDesc;
- srvDesc.Format = d3d::D3DShaderResourceViewFormat(GetDevice(), GetTexture()->GetFormat(),
- GetFormat(), GetAspects());
-
- // Currently we always use D3D11_TEX2D_ARRAY_SRV because we cannot specify base array
- // layer and layer count in D3D11_TEX2D_SRV. For 2D texture views, we treat them as
- // 1-layer 2D array textures. Multisampled textures may only be one array layer, so we
- // use D3D11_SRV_DIMENSION_TEXTURE2DMS.
- // https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/ns-d3d11-d3d11_tex2d_srv
- // https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/ns-d3d11-d3d11_tex2d_array_srv
- if (GetTexture()->IsMultisampledTexture()) {
- switch (GetDimension()) {
- case wgpu::TextureViewDimension::e2DArray:
- DAWN_ASSERT(GetTexture()->GetArrayLayers() == 1);
- [[fallthrough]];
- case wgpu::TextureViewDimension::e2D:
- DAWN_ASSERT(GetTexture()->GetDimension() == wgpu::TextureDimension::e2D);
- srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
- break;
-
- default:
- DAWN_UNREACHABLE();
- }
- } else {
- switch (GetDimension()) {
- case wgpu::TextureViewDimension::e1D:
- srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
- srvDesc.Texture1D.MipLevels = GetLevelCount();
- srvDesc.Texture1D.MostDetailedMip = GetBaseMipLevel();
- break;
-
- case wgpu::TextureViewDimension::e2D:
- case wgpu::TextureViewDimension::e2DArray:
- DAWN_ASSERT(GetTexture()->GetDimension() == wgpu::TextureDimension::e2D);
- srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
- srvDesc.Texture2DArray.ArraySize = GetLayerCount();
- srvDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer();
- srvDesc.Texture2DArray.MipLevels = GetLevelCount();
- srvDesc.Texture2DArray.MostDetailedMip = GetBaseMipLevel();
- srvDesc.Texture2DArray.PlaneSlice = GetAspectIndex(GetAspects());
- break;
- case wgpu::TextureViewDimension::Cube:
- case wgpu::TextureViewDimension::CubeArray:
- DAWN_ASSERT(GetTexture()->GetDimension() == wgpu::TextureDimension::e2D);
- DAWN_ASSERT(GetLayerCount() % 6 == 0);
- srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY;
- srvDesc.TextureCubeArray.First2DArrayFace = GetBaseArrayLayer();
- srvDesc.TextureCubeArray.NumCubes = GetLayerCount() / 6;
- srvDesc.TextureCubeArray.MipLevels = GetLevelCount();
- srvDesc.TextureCubeArray.MostDetailedMip = GetBaseMipLevel();
- break;
- case wgpu::TextureViewDimension::e3D:
- DAWN_ASSERT(GetTexture()->GetDimension() == wgpu::TextureDimension::e3D);
- srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
- srvDesc.Texture3D.MostDetailedMip = GetBaseMipLevel();
- srvDesc.Texture3D.MipLevels = GetLevelCount();
- break;
-
- case wgpu::TextureViewDimension::Undefined:
- DAWN_UNREACHABLE();
- }
- }
-
- ComPtr<ID3D11ShaderResourceView1> srv;
- DAWN_TRY(CheckHRESULT(device->GetD3D11Device3()->CreateShaderResourceView1(
- ToBackend(GetTexture())->GetD3D11Resource(), &srvDesc, &srv),
- "CreateShaderResourceView1"));
- mD3d11SharedResourceView = std::move(srv);
+ DAWN_TRY_ASSIGN(
+ mD3d11SharedResourceView,
+ ToBackend(GetTexture())
+ ->GetOrCreateSRV({GetDimension(), GetFormat().format, GetAspects(), GetBaseMipLevel(),
+ GetLevelCount(), GetBaseArrayLayer(), GetLayerCount()}));
return mD3d11SharedResourceView.Get();
}
@@ -1257,11 +1305,12 @@
// 2d RTVs, which value is set to 0. For 3d RTVs, the baseArrayLayer must be 0. So here we can
// simply use baseArrayLayer + depthSlice to specify the slice in RTVs without checking the
// view's dimension.
- DAWN_TRY_ASSIGN(mD3d11RenderTargetViews[depthSlice],
- ToBackend(GetTexture())
- ->CreateD3D11RenderTargetView(
- GetFormat().format, GetBaseMipLevel(), GetBaseArrayLayer() + depthSlice,
- GetLayerCount(), GetAspectIndex(GetAspects())));
+ DAWN_TRY_ASSIGN(
+ mD3d11RenderTargetViews[depthSlice],
+ ToBackend(GetTexture())
+ ->GetOrCreateD3D11RenderTargetView({GetFormat().format, GetBaseMipLevel(),
+ GetBaseArrayLayer() + depthSlice, GetLayerCount(),
+ GetAspectIndex(GetAspects())}));
return mD3d11RenderTargetViews[depthSlice].Get();
}
diff --git a/src/dawn/native/d3d11/TextureD3D11.h b/src/dawn/native/d3d11/TextureD3D11.h
index 3f9b21f..770c676 100644
--- a/src/dawn/native/d3d11/TextureD3D11.h
+++ b/src/dawn/native/d3d11/TextureD3D11.h
@@ -28,8 +28,10 @@
#ifndef SRC_DAWN_NATIVE_D3D11_TEXTURED3D11_H_
#define SRC_DAWN_NATIVE_D3D11_TEXTURED3D11_H_
+#include <utility>
#include <vector>
+#include "absl/container/inlined_vector.h"
#include "dawn/native/DawnNative.h"
#include "dawn/native/Error.h"
#include "dawn/native/IntegerTypes.h"
@@ -65,12 +67,22 @@
const UnpackedPtr<TextureDescriptor>& descriptor);
ID3D11Resource* GetD3D11Resource() const;
- ResultOrError<ComPtr<ID3D11RenderTargetView>> CreateD3D11RenderTargetView(
- wgpu::TextureFormat format,
- uint32_t mipLevel,
- uint32_t baseSlice,
- uint32_t sliceCount,
- uint32_t planeSlice) const;
+ struct RTVKey {
+ bool operator==(const RTVKey& rhs) const {
+ return viewFormat == rhs.viewFormat && mipLevel == rhs.mipLevel &&
+ baseSlice == rhs.baseSlice && sliceCount == rhs.sliceCount &&
+ planeSlice == rhs.planeSlice;
+ }
+
+ wgpu::TextureFormat viewFormat;
+ uint32_t mipLevel;
+ uint32_t baseSlice;
+ uint32_t sliceCount;
+ uint32_t planeSlice;
+ };
+
+ ResultOrError<ComPtr<ID3D11RenderTargetView1>> GetOrCreateD3D11RenderTargetView(
+ const RTVKey& key);
ResultOrError<ComPtr<ID3D11DepthStencilView>> CreateD3D11DepthStencilView(
const SubresourceRange& singleLevelRange,
bool depthReadOnly,
@@ -99,7 +111,6 @@
static MaybeError Copy(const ScopedCommandRecordingContext* commandContext,
CopyTextureToTextureCmd* copy);
-
// As D3D11 SRV doesn't support 'Shader4ComponentMapping' for depth-stencil textures, we can't
// sample the stencil component directly. As a workaround we create an internal R8Uint texture,
// holding the copy of its stencil data, and use the internal texture's SRV instead.
@@ -107,6 +118,24 @@
const ScopedCommandRecordingContext* commandContext,
const TextureView* view);
+ struct SRVKey {
+ bool operator==(const SRVKey& rhs) const {
+ return viewDimension == rhs.viewDimension && viewFormat == rhs.viewFormat &&
+ aspects == rhs.aspects && mipLevel == rhs.mipLevel &&
+ levelCount == rhs.levelCount && baseSlice == rhs.baseSlice &&
+ sliceCount == rhs.sliceCount;
+ }
+
+ wgpu::TextureViewDimension viewDimension;
+ wgpu::TextureFormat viewFormat;
+ Aspect aspects;
+ uint32_t mipLevel;
+ uint32_t levelCount;
+ uint32_t baseSlice;
+ uint32_t sliceCount;
+ };
+ ResultOrError<ComPtr<ID3D11ShaderResourceView1>> GetOrCreateSRV(const SRVKey& key);
+
private:
using Base = TextureBase;
@@ -187,6 +216,28 @@
// The internal 'R8Uint' texture for sampling stencil from depth-stencil textures.
Ref<Texture> mTextureForStencilSampling;
+
+ // Simple view cache that store up to 3 recently created views.
+ template <typename K, typename T>
+ class ViewCache {
+ public:
+ ComPtr<T> Get(const K& key) const;
+ void Set(const K& key, ComPtr<T> view);
+ void Clear();
+
+ private:
+ static constexpr uint32_t kMaxCacheSize = 3;
+
+ struct Entry {
+ Entry(const K& keyIn, ComPtr<T> viewIn) : key(keyIn), view(std::move(viewIn)) {}
+
+ K key;
+ ComPtr<T> view;
+ };
+ absl::InlinedVector<Entry, kMaxCacheSize> mViews;
+ };
+ ViewCache<RTVKey, ID3D11RenderTargetView1> mCachedRTVs;
+ ViewCache<SRVKey, ID3D11ShaderResourceView1> mCachedSRVs;
};
class TextureView final : public TextureViewBase {