Use SubresourceStorage to track per-subresource state.
Using this container is a small performance regression in the simple cases where all subresources are the same, or when the texture has few subrsources. However it give better performance in the hard subresource tracking cases of textures with many subresources. Using SubresourceStorage also makes it easier to work with compressed storage since the compression is mostly transparent. It reduces code duplication and prevent bugs from appearing when a developer would forget to handle compression. This fixes a state tracking issue in ValidatePassResourceUsage where the function didn't correctly handle the case where the PassResourceUsage was compressed. Also removes the unused vulkan::Texture::TransitionFullUsage. Also makes SubresourceStorage<T> only require operator== on T and not operator !=. Also fixes the texture format's aspect being used to create pipeline barriers instead of the range's aspects. Bug: dawn:441 Change-Id: I234b8191f39a09b541c1c63a60cccd6cee970550 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/37706 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org> Auto-Submit: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
738aa17493
commit
ef869c2d01
|
@ -234,6 +234,7 @@ source_set("dawn_native_sources") {
|
||||||
"ObjectBase.h",
|
"ObjectBase.h",
|
||||||
"ObjectContentHasher.cpp",
|
"ObjectContentHasher.cpp",
|
||||||
"ObjectContentHasher.h",
|
"ObjectContentHasher.h",
|
||||||
|
"PassResourceUsage.cpp",
|
||||||
"PassResourceUsage.h",
|
"PassResourceUsage.h",
|
||||||
"PassResourceUsageTracker.cpp",
|
"PassResourceUsageTracker.cpp",
|
||||||
"PassResourceUsageTracker.h",
|
"PassResourceUsageTracker.h",
|
||||||
|
|
|
@ -107,6 +107,7 @@ target_sources(dawn_native PRIVATE
|
||||||
"IntegerTypes.h"
|
"IntegerTypes.h"
|
||||||
"ObjectBase.cpp"
|
"ObjectBase.cpp"
|
||||||
"ObjectBase.h"
|
"ObjectBase.h"
|
||||||
|
"PassResourceUsage.cpp"
|
||||||
"PassResourceUsage.h"
|
"PassResourceUsage.h"
|
||||||
"PassResourceUsageTracker.cpp"
|
"PassResourceUsageTracker.cpp"
|
||||||
"PassResourceUsageTracker.h"
|
"PassResourceUsageTracker.h"
|
||||||
|
|
|
@ -308,6 +308,7 @@ namespace dawn_native {
|
||||||
// Performs the per-pass usage validation checks
|
// Performs the per-pass usage validation checks
|
||||||
// This will eventually need to differentiate between render and compute passes.
|
// This will eventually need to differentiate between render and compute passes.
|
||||||
// It will be valid to use a buffer both as uniform and storage in the same compute pass.
|
// It will be valid to use a buffer both as uniform and storage in the same compute pass.
|
||||||
|
// TODO(yunchao.he@intel.com): add read/write usage tracking for compute
|
||||||
MaybeError ValidatePassResourceUsage(const PassResourceUsage& pass) {
|
MaybeError ValidatePassResourceUsage(const PassResourceUsage& pass) {
|
||||||
// Buffers can only be used as single-write or multiple read.
|
// Buffers can only be used as single-write or multiple read.
|
||||||
for (size_t i = 0; i < pass.buffers.size(); ++i) {
|
for (size_t i = 0; i < pass.buffers.size(); ++i) {
|
||||||
|
@ -337,8 +338,6 @@ namespace dawn_native {
|
||||||
return DAWN_VALIDATION_ERROR("Texture missing usage for the pass");
|
return DAWN_VALIDATION_ERROR("Texture missing usage for the pass");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO (yunchao.he@intel.com): add read/write usage tracking for compute
|
|
||||||
|
|
||||||
// The usage variable for the whole texture is a fast path for texture usage tracking.
|
// The usage variable for the whole texture is a fast path for texture usage tracking.
|
||||||
// Because in most cases a texture (with or without subresources) is used as
|
// Because in most cases a texture (with or without subresources) is used as
|
||||||
// single-write or multiple read, then we can skip iterating the subresources' usages.
|
// single-write or multiple read, then we can skip iterating the subresources' usages.
|
||||||
|
@ -347,16 +346,20 @@ namespace dawn_native {
|
||||||
if (pass.passType != PassType::Render || readOnly || singleUse) {
|
if (pass.passType != PassType::Render || readOnly || singleUse) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Inspect the subresources if the usage of the whole texture violates usage validation.
|
|
||||||
// Every single subresource can only be used as single-write or multiple read.
|
// Check that every single subresource is used as either a single-write usage or a
|
||||||
for (wgpu::TextureUsage subresourceUsage : textureUsage.subresourceUsages) {
|
// combination of readonly usages.
|
||||||
bool readOnly = IsSubset(subresourceUsage, kReadOnlyTextureUsages);
|
MaybeError error = {};
|
||||||
bool singleUse = wgpu::HasZeroOrOneBits(subresourceUsage);
|
textureUsage.subresourceUsages.Iterate(
|
||||||
if (!readOnly && !singleUse) {
|
[&](const SubresourceRange&, const wgpu::TextureUsage& usage) {
|
||||||
return DAWN_VALIDATION_ERROR(
|
bool readOnly = IsSubset(usage, kReadOnlyTextureUsages);
|
||||||
|
bool singleUse = wgpu::HasZeroOrOneBits(usage);
|
||||||
|
if (!readOnly && !singleUse && !error.IsError()) {
|
||||||
|
error = DAWN_VALIDATION_ERROR(
|
||||||
"Texture used as writable usage and another usage in render pass");
|
"Texture used as writable usage and another usage in render pass");
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
DAWN_TRY(std::move(error));
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2021 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/PassResourceUsage.h"
|
||||||
|
|
||||||
|
#include "dawn_native/Format.h"
|
||||||
|
#include "dawn_native/Texture.h"
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
|
||||||
|
PassTextureUsage::PassTextureUsage(const TextureBase* texture)
|
||||||
|
: subresourceUsages(texture->GetFormat().aspects,
|
||||||
|
texture->GetArrayLayers(),
|
||||||
|
texture->GetNumMipLevels(),
|
||||||
|
wgpu::TextureUsage::None) {
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dawn_native
|
|
@ -15,6 +15,7 @@
|
||||||
#ifndef DAWNNATIVE_PASSRESOURCEUSAGE_H
|
#ifndef DAWNNATIVE_PASSRESOURCEUSAGE_H
|
||||||
#define DAWNNATIVE_PASSRESOURCEUSAGE_H
|
#define DAWNNATIVE_PASSRESOURCEUSAGE_H
|
||||||
|
|
||||||
|
#include "dawn_native/SubresourceStorage.h"
|
||||||
#include "dawn_native/dawn_platform.h"
|
#include "dawn_native/dawn_platform.h"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -29,26 +30,18 @@ namespace dawn_native {
|
||||||
enum class PassType { Render, Compute };
|
enum class PassType { Render, Compute };
|
||||||
|
|
||||||
// Describe the usage of the whole texture and its subresources.
|
// Describe the usage of the whole texture and its subresources.
|
||||||
// - subresourceUsages vector is used to track every subresource's usage within a texture.
|
|
||||||
//
|
//
|
||||||
// - usage variable is used the track the whole texture even though it can be deduced from
|
// - usage variable is used the track the whole texture even though it can be deduced from
|
||||||
// subresources' usages. This is designed deliberately to track texture usage in a fast path
|
// subresources' usages. This is designed deliberately to track texture usage in a fast path
|
||||||
// at frontend.
|
// at frontend.
|
||||||
//
|
//
|
||||||
// - sameUsagesAcrossSubresources is used for optimization at backend. If the texture view
|
// - subresourceUsages is used to track every subresource's usage within a texture.
|
||||||
// we are using covers all subresources, then the texture's usages of all subresources are
|
|
||||||
// the same. Otherwise the texture's usages of all subresources are thought as different,
|
|
||||||
// although we can deliberately design some particular cases in which we have a few texture
|
|
||||||
// views and all of them have the same usages and they cover all subresources of the texture
|
|
||||||
// altogether.
|
|
||||||
|
|
||||||
// TODO(yunchao.he@intel.com): if sameUsagesAcrossSubresources is true, we don't need
|
|
||||||
// the vector to record every single subresource's Usages. The texture usage is enough. And we
|
|
||||||
// can decompress texture usage to a vector if necessary.
|
|
||||||
struct PassTextureUsage {
|
struct PassTextureUsage {
|
||||||
|
// Constructor used to size subresourceUsages correctly.
|
||||||
|
PassTextureUsage(const TextureBase* texture);
|
||||||
|
|
||||||
wgpu::TextureUsage usage = wgpu::TextureUsage::None;
|
wgpu::TextureUsage usage = wgpu::TextureUsage::None;
|
||||||
bool sameUsagesAcrossSubresources = true;
|
SubresourceStorage<wgpu::TextureUsage> subresourceUsages;
|
||||||
std::vector<wgpu::TextureUsage> subresourceUsages;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Which resources are used by pass and how they are used. The command buffer validation
|
// Which resources are used by pass and how they are used. The command buffer validation
|
||||||
|
|
|
@ -34,51 +34,31 @@ namespace dawn_native {
|
||||||
TextureBase* texture = view->GetTexture();
|
TextureBase* texture = view->GetTexture();
|
||||||
const SubresourceRange& range = view->GetSubresourceRange();
|
const SubresourceRange& range = view->GetSubresourceRange();
|
||||||
|
|
||||||
// std::map's operator[] will create the key and return a PassTextureUsage with usage = 0
|
// Get or create a new PassTextureUsage for that texture (initially filled with
|
||||||
// and an empty vector for subresourceUsages.
|
// wgpu::TextureUsage::None)
|
||||||
// TODO (yunchao.he@intel.com): optimize this
|
auto it = mTextureUsages.emplace(texture, texture);
|
||||||
PassTextureUsage& textureUsage = mTextureUsages[texture];
|
PassTextureUsage& textureUsage = it.first->second;
|
||||||
|
|
||||||
// Set parameters for the whole texture
|
|
||||||
textureUsage.usage |= usage;
|
textureUsage.usage |= usage;
|
||||||
textureUsage.sameUsagesAcrossSubresources &=
|
textureUsage.subresourceUsages.Update(
|
||||||
(range.levelCount == texture->GetNumMipLevels() && //
|
range, [usage](const SubresourceRange&, wgpu::TextureUsage* storedUsage) {
|
||||||
range.layerCount == texture->GetArrayLayers() && //
|
*storedUsage |= usage;
|
||||||
range.aspects == texture->GetFormat().aspects);
|
});
|
||||||
|
|
||||||
// Set usages for subresources
|
|
||||||
if (!textureUsage.subresourceUsages.size()) {
|
|
||||||
textureUsage.subresourceUsages = std::vector<wgpu::TextureUsage>(
|
|
||||||
texture->GetSubresourceCount(), wgpu::TextureUsage::None);
|
|
||||||
}
|
|
||||||
for (Aspect aspect : IterateEnumMask(range.aspects)) {
|
|
||||||
for (uint32_t arrayLayer = range.baseArrayLayer;
|
|
||||||
arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) {
|
|
||||||
for (uint32_t mipLevel = range.baseMipLevel;
|
|
||||||
mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) {
|
|
||||||
uint32_t subresourceIndex =
|
|
||||||
texture->GetSubresourceIndex(mipLevel, arrayLayer, aspect);
|
|
||||||
textureUsage.subresourceUsages[subresourceIndex] |= usage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PassResourceUsageTracker::AddTextureUsage(TextureBase* texture,
|
void PassResourceUsageTracker::AddTextureUsage(TextureBase* texture,
|
||||||
const PassTextureUsage& textureUsage) {
|
const PassTextureUsage& textureUsage) {
|
||||||
PassTextureUsage& passTextureUsage = mTextureUsages[texture];
|
// Get or create a new PassTextureUsage for that texture (initially filled with
|
||||||
passTextureUsage.usage |= textureUsage.usage;
|
// wgpu::TextureUsage::None)
|
||||||
passTextureUsage.sameUsagesAcrossSubresources &= textureUsage.sameUsagesAcrossSubresources;
|
auto it = mTextureUsages.emplace(texture, texture);
|
||||||
|
PassTextureUsage* passTextureUsage = &it.first->second;
|
||||||
|
|
||||||
uint32_t subresourceCount = texture->GetSubresourceCount();
|
passTextureUsage->usage |= textureUsage.usage;
|
||||||
ASSERT(textureUsage.subresourceUsages.size() == subresourceCount);
|
|
||||||
if (!passTextureUsage.subresourceUsages.size()) {
|
passTextureUsage->subresourceUsages.Merge(
|
||||||
passTextureUsage.subresourceUsages = textureUsage.subresourceUsages;
|
textureUsage.subresourceUsages,
|
||||||
return;
|
[](const SubresourceRange&, wgpu::TextureUsage* storedUsage,
|
||||||
}
|
const wgpu::TextureUsage& addedUsage) { *storedUsage |= addedUsage; });
|
||||||
for (uint32_t i = 0; i < subresourceCount; ++i) {
|
|
||||||
passTextureUsage.subresourceUsages[i] |= textureUsage.subresourceUsages[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the per-pass usage for use by backends for APIs with explicit barriers.
|
// Returns the per-pass usage for use by backends for APIs with explicit barriers.
|
||||||
|
|
|
@ -46,8 +46,8 @@ namespace dawn_native {
|
||||||
ASSERT(HasOneBit(aspect));
|
ASSERT(HasOneBit(aspect));
|
||||||
switch (aspect) {
|
switch (aspect) {
|
||||||
case Aspect::Color:
|
case Aspect::Color:
|
||||||
return 0;
|
|
||||||
case Aspect::Depth:
|
case Aspect::Depth:
|
||||||
|
case Aspect::CombinedDepthStencil:
|
||||||
return 0;
|
return 0;
|
||||||
case Aspect::Stencil:
|
case Aspect::Stencil:
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -60,7 +60,8 @@ namespace dawn_native {
|
||||||
// TODO(cwallez@chromium.org): This should use popcount once Dawn has such a function.
|
// TODO(cwallez@chromium.org): This should use popcount once Dawn has such a function.
|
||||||
// Note that we can't do a switch because compilers complain that Depth | Stencil is not
|
// Note that we can't do a switch because compilers complain that Depth | Stencil is not
|
||||||
// a valid enum value.
|
// a valid enum value.
|
||||||
if (aspects == Aspect::Color || aspects == Aspect::Depth) {
|
if (aspects == Aspect::Color || aspects == Aspect::Depth ||
|
||||||
|
aspects == Aspect::CombinedDepthStencil) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
ASSERT(aspects == (Aspect::Depth | Aspect::Stencil));
|
ASSERT(aspects == (Aspect::Depth | Aspect::Stencil));
|
||||||
|
|
|
@ -28,11 +28,15 @@ namespace dawn_native {
|
||||||
Color = 0x1,
|
Color = 0x1,
|
||||||
Depth = 0x2,
|
Depth = 0x2,
|
||||||
Stencil = 0x4,
|
Stencil = 0x4,
|
||||||
|
|
||||||
|
// An aspect for that represents the combination of both the depth and stencil aspects. It
|
||||||
|
// can be ignored outside of the Vulkan backend.
|
||||||
|
CombinedDepthStencil = 0x8,
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct EnumBitmaskSize<Aspect> {
|
struct EnumBitmaskSize<Aspect> {
|
||||||
static constexpr unsigned value = 3;
|
static constexpr unsigned value = 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert the TextureAspect to an Aspect mask for the format. ASSERTs if the aspect
|
// Convert the TextureAspect to an Aspect mask for the format. ASSERTs if the aspect
|
||||||
|
|
|
@ -472,7 +472,7 @@ namespace dawn_native {
|
||||||
|
|
||||||
T layer0Data = Data(aspectIndex, 0);
|
T layer0Data = Data(aspectIndex, 0);
|
||||||
for (uint32_t layer = 1; layer < mArrayLayerCount; layer++) {
|
for (uint32_t layer = 1; layer < mArrayLayerCount; layer++) {
|
||||||
if (Data(aspectIndex, layer) != layer0Data) {
|
if (!(Data(aspectIndex, layer) == layer0Data)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ namespace dawn_native {
|
||||||
const T& level0Data = Data(aspectIndex, layer, 0);
|
const T& level0Data = Data(aspectIndex, layer, 0);
|
||||||
|
|
||||||
for (uint32_t level = 1; level < mMipLevelCount; level++) {
|
for (uint32_t level = 1; level < mMipLevelCount; level++) {
|
||||||
if (Data(aspectIndex, layer, level) != level0Data) {
|
if (!(Data(aspectIndex, layer, level) == level0Data)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -513,7 +513,9 @@ namespace dawn_native { namespace d3d12 {
|
||||||
Texture::Texture(Device* device, const TextureDescriptor* descriptor, TextureState state)
|
Texture::Texture(Device* device, const TextureDescriptor* descriptor, TextureState state)
|
||||||
: TextureBase(device, descriptor, state),
|
: TextureBase(device, descriptor, state),
|
||||||
mSubresourceStateAndDecay(
|
mSubresourceStateAndDecay(
|
||||||
GetSubresourceCount(),
|
GetFormat().aspects,
|
||||||
|
GetArrayLayers(),
|
||||||
|
GetNumMipLevels(),
|
||||||
{D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON, kMaxExecutionSerial, false}) {
|
{D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON, kMaxExecutionSerial, false}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,12 +617,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::TransitionSingleOrAllSubresources(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
void Texture::TransitionSubresourceRange(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
||||||
uint32_t index,
|
const SubresourceRange& range,
|
||||||
|
StateAndDecay* state,
|
||||||
D3D12_RESOURCE_STATES newState,
|
D3D12_RESOURCE_STATES newState,
|
||||||
ExecutionSerial pendingCommandSerial,
|
ExecutionSerial pendingCommandSerial) const {
|
||||||
bool allSubresources) {
|
|
||||||
StateAndDecay* state = &mSubresourceStateAndDecay[index];
|
|
||||||
// Reuse the subresource(s) directly and avoid transition when it isn't needed, and
|
// Reuse the subresource(s) directly and avoid transition when it isn't needed, and
|
||||||
// return false.
|
// return false.
|
||||||
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
|
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
|
||||||
|
@ -682,9 +683,28 @@ namespace dawn_native { namespace d3d12 {
|
||||||
barrier.Transition.pResource = GetD3D12Resource();
|
barrier.Transition.pResource = GetD3D12Resource();
|
||||||
barrier.Transition.StateBefore = lastState;
|
barrier.Transition.StateBefore = lastState;
|
||||||
barrier.Transition.StateAfter = newState;
|
barrier.Transition.StateAfter = newState;
|
||||||
barrier.Transition.Subresource =
|
|
||||||
allSubresources ? D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES : index;
|
bool isFullRange = range.baseArrayLayer == 0 && range.baseMipLevel == 0 &&
|
||||||
|
range.layerCount == GetArrayLayers() &&
|
||||||
|
range.levelCount == GetNumMipLevels() &&
|
||||||
|
range.aspects == GetFormat().aspects;
|
||||||
|
|
||||||
|
// Use a single transition for all subresources if possible.
|
||||||
|
if (isFullRange) {
|
||||||
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
barriers->push_back(barrier);
|
barriers->push_back(barrier);
|
||||||
|
} else {
|
||||||
|
for (Aspect aspect : IterateEnumMask(range.aspects)) {
|
||||||
|
for (uint32_t arrayLayer = 0; arrayLayer < range.layerCount; ++arrayLayer) {
|
||||||
|
for (uint32_t mipLevel = 0; mipLevel < range.levelCount; ++mipLevel) {
|
||||||
|
barrier.Transition.Subresource =
|
||||||
|
GetSubresourceIndex(range.baseMipLevel + mipLevel,
|
||||||
|
range.baseArrayLayer + arrayLayer, aspect);
|
||||||
|
barriers->push_back(barrier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state->isValidToDecay = false;
|
state->isValidToDecay = false;
|
||||||
}
|
}
|
||||||
|
@ -719,36 +739,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
// This transitions assume it is a 2D texture
|
// This transitions assume it is a 2D texture
|
||||||
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
|
||||||
// If the usages transitions can cover all subresources, and old usages of all subresources
|
mSubresourceStateAndDecay.Update(
|
||||||
// are the same, then we can use one barrier to do state transition for all subresources.
|
range, [&](const SubresourceRange& updateRange, StateAndDecay* state) {
|
||||||
// Note that if the texture has only one mip level and one array slice, it will fall into
|
TransitionSubresourceRange(barriers, updateRange, state, newState,
|
||||||
// this category.
|
pendingCommandSerial);
|
||||||
bool areAllSubresourcesCovered = (range.levelCount == GetNumMipLevels() && //
|
});
|
||||||
range.layerCount == GetArrayLayers() && //
|
|
||||||
range.aspects == GetFormat().aspects);
|
|
||||||
if (mSameLastUsagesAcrossSubresources && areAllSubresourcesCovered) {
|
|
||||||
TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true);
|
|
||||||
|
|
||||||
// TODO(yunchao.he@intel.com): compress and decompress if all subresources have the
|
|
||||||
// same states. We may need to retain mSubresourceStateAndDecay[0] only.
|
|
||||||
for (uint32_t i = 1; i < GetSubresourceCount(); ++i) {
|
|
||||||
mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (Aspect aspect : IterateEnumMask(range.aspects)) {
|
|
||||||
for (uint32_t arrayLayer = 0; arrayLayer < range.layerCount; ++arrayLayer) {
|
|
||||||
for (uint32_t mipLevel = 0; mipLevel < range.levelCount; ++mipLevel) {
|
|
||||||
uint32_t index = GetSubresourceIndex(range.baseMipLevel + mipLevel,
|
|
||||||
range.baseArrayLayer + arrayLayer, aspect);
|
|
||||||
|
|
||||||
TransitionSingleOrAllSubresources(barriers, index, newState,
|
|
||||||
pendingCommandSerial, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::TrackUsageAndGetResourceBarrierForPass(
|
void Texture::TrackUsageAndGetResourceBarrierForPass(
|
||||||
|
@ -765,47 +760,21 @@ namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
const ExecutionSerial pendingCommandSerial =
|
const ExecutionSerial pendingCommandSerial =
|
||||||
ToBackend(GetDevice())->GetPendingCommandSerial();
|
ToBackend(GetDevice())->GetPendingCommandSerial();
|
||||||
uint32_t subresourceCount = GetSubresourceCount();
|
|
||||||
ASSERT(textureUsages.subresourceUsages.size() == subresourceCount);
|
|
||||||
// This transitions assume it is a 2D texture
|
// This transitions assume it is a 2D texture
|
||||||
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
|
||||||
// If new usages of all subresources are the same and old usages of all subresources are
|
mSubresourceStateAndDecay.Merge(
|
||||||
// the same too, we can use one barrier to do state transition for all subresources.
|
textureUsages.subresourceUsages, [&](const SubresourceRange& mergeRange,
|
||||||
// Note that if the texture has only one mip level and one array slice, it will fall into
|
StateAndDecay* state, wgpu::TextureUsage usage) {
|
||||||
// this category.
|
// Skip if this subresource is not used during the current pass
|
||||||
if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) {
|
if (usage == wgpu::TextureUsage::None) {
|
||||||
D3D12_RESOURCE_STATES newState = D3D12TextureUsage(textureUsages.usage, GetFormat());
|
|
||||||
TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true);
|
|
||||||
|
|
||||||
// TODO(yunchao.he@intel.com): compress and decompress if all subresources have the
|
|
||||||
// same states. We may need to retain mSubresourceStateAndDecay[0] only.
|
|
||||||
for (uint32_t i = 1; i < subresourceCount; ++i) {
|
|
||||||
mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Aspect aspect : IterateEnumMask(GetFormat().aspects)) {
|
D3D12_RESOURCE_STATES newState = D3D12TextureUsage(usage, GetFormat());
|
||||||
for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) {
|
TransitionSubresourceRange(barriers, mergeRange, state, newState,
|
||||||
for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) {
|
pendingCommandSerial);
|
||||||
uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer, aspect);
|
});
|
||||||
|
|
||||||
// Skip if this subresource is not used during the current pass
|
|
||||||
if (textureUsages.subresourceUsages[index] == wgpu::TextureUsage::None) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12_RESOURCE_STATES newState =
|
|
||||||
D3D12TextureUsage(textureUsages.subresourceUsages[index], GetFormat());
|
|
||||||
|
|
||||||
TransitionSingleOrAllSubresources(barriers, index, newState,
|
|
||||||
pendingCommandSerial, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel,
|
D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel,
|
||||||
|
@ -1025,6 +994,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Texture::StateAndDecay::operator==(const Texture::StateAndDecay& other) const {
|
||||||
|
return lastState == other.lastState && lastDecaySerial == other.lastDecaySerial &&
|
||||||
|
isValidToDecay == other.isValidToDecay;
|
||||||
|
}
|
||||||
|
|
||||||
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
|
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
|
||||||
: TextureViewBase(texture, descriptor) {
|
: TextureViewBase(texture, descriptor) {
|
||||||
mSrvDesc.Format = D3D12TextureFormat(descriptor->format);
|
mSrvDesc.Format = D3D12TextureFormat(descriptor->format);
|
||||||
|
|
|
@ -96,26 +96,26 @@ namespace dawn_native { namespace d3d12 {
|
||||||
const SubresourceRange& range,
|
const SubresourceRange& range,
|
||||||
TextureBase::ClearValue clearValue);
|
TextureBase::ClearValue clearValue);
|
||||||
|
|
||||||
void TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
|
// Barriers implementation details.
|
||||||
std::vector<D3D12_RESOURCE_BARRIER>* barrier,
|
|
||||||
D3D12_RESOURCE_STATES newState,
|
|
||||||
const SubresourceRange& range);
|
|
||||||
|
|
||||||
void TransitionSingleOrAllSubresources(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
|
||||||
uint32_t index,
|
|
||||||
D3D12_RESOURCE_STATES subresourceNewState,
|
|
||||||
ExecutionSerial pendingCommandSerial,
|
|
||||||
bool allSubresources);
|
|
||||||
void HandleTransitionSpecialCases(CommandRecordingContext* commandContext);
|
|
||||||
|
|
||||||
bool mSameLastUsagesAcrossSubresources = true;
|
|
||||||
|
|
||||||
struct StateAndDecay {
|
struct StateAndDecay {
|
||||||
D3D12_RESOURCE_STATES lastState;
|
D3D12_RESOURCE_STATES lastState;
|
||||||
ExecutionSerial lastDecaySerial;
|
ExecutionSerial lastDecaySerial;
|
||||||
bool isValidToDecay;
|
bool isValidToDecay;
|
||||||
|
|
||||||
|
bool operator==(const StateAndDecay& other) const;
|
||||||
};
|
};
|
||||||
std::vector<StateAndDecay> mSubresourceStateAndDecay;
|
void TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
|
||||||
|
std::vector<D3D12_RESOURCE_BARRIER>* barrier,
|
||||||
|
D3D12_RESOURCE_STATES newState,
|
||||||
|
const SubresourceRange& range);
|
||||||
|
void TransitionSubresourceRange(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
|
||||||
|
const SubresourceRange& range,
|
||||||
|
StateAndDecay* state,
|
||||||
|
D3D12_RESOURCE_STATES subresourceNewState,
|
||||||
|
ExecutionSerial pendingCommandSerial) const;
|
||||||
|
void HandleTransitionSpecialCases(CommandRecordingContext* commandContext);
|
||||||
|
|
||||||
|
SubresourceStorage<StateAndDecay> mSubresourceStateAndDecay;
|
||||||
|
|
||||||
ResourceHeapAllocation mResourceAllocation;
|
ResourceHeapAllocation mResourceAllocation;
|
||||||
bool mSwapChainTexture = false;
|
bool mSwapChainTexture = false;
|
||||||
|
|
|
@ -306,6 +306,7 @@ namespace dawn_native { namespace opengl {
|
||||||
switch (aspect) {
|
switch (aspect) {
|
||||||
case Aspect::None:
|
case Aspect::None:
|
||||||
case Aspect::Color:
|
case Aspect::Color:
|
||||||
|
case Aspect::CombinedDepthStencil:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
case Aspect::Depth:
|
case Aspect::Depth:
|
||||||
gl.TexParameteri(target, GL_DEPTH_STENCIL_TEXTURE_MODE,
|
gl.TexParameteri(target, GL_DEPTH_STENCIL_TEXTURE_MODE,
|
||||||
|
@ -714,6 +715,7 @@ namespace dawn_native { namespace opengl {
|
||||||
glType = GL_UNSIGNED_BYTE;
|
glType = GL_UNSIGNED_BYTE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Aspect::CombinedDepthStencil:
|
||||||
case Aspect::None:
|
case Aspect::None:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,7 +218,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
barrier.oldLayout = VulkanImageLayout(lastUsage, format);
|
barrier.oldLayout = VulkanImageLayout(lastUsage, format);
|
||||||
barrier.newLayout = VulkanImageLayout(usage, format);
|
barrier.newLayout = VulkanImageLayout(usage, format);
|
||||||
barrier.image = image;
|
barrier.image = image;
|
||||||
barrier.subresourceRange.aspectMask = VulkanAspectMask(format.aspects);
|
barrier.subresourceRange.aspectMask = VulkanAspectMask(range.aspects);
|
||||||
barrier.subresourceRange.baseMipLevel = range.baseMipLevel;
|
barrier.subresourceRange.baseMipLevel = range.baseMipLevel;
|
||||||
barrier.subresourceRange.levelCount = range.levelCount;
|
barrier.subresourceRange.levelCount = range.levelCount;
|
||||||
barrier.subresourceRange.baseArrayLayer = range.baseArrayLayer;
|
barrier.subresourceRange.baseArrayLayer = range.baseArrayLayer;
|
||||||
|
@ -489,6 +489,16 @@ namespace dawn_native { namespace vulkan {
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Texture::Texture(Device* device, const TextureDescriptor* descriptor, TextureState state)
|
||||||
|
: TextureBase(device, descriptor, state),
|
||||||
|
// A usage of none will make sure the texture is transitioned before its first use as
|
||||||
|
// required by the Vulkan spec.
|
||||||
|
mSubresourceLastUsages(ComputeAspectsForSubresourceStorage(),
|
||||||
|
GetArrayLayers(),
|
||||||
|
GetNumMipLevels(),
|
||||||
|
wgpu::TextureUsage::None) {
|
||||||
|
}
|
||||||
|
|
||||||
MaybeError Texture::InitializeAsInternalTexture(VkImageUsageFlags extraUsages) {
|
MaybeError Texture::InitializeAsInternalTexture(VkImageUsageFlags extraUsages) {
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
|
@ -619,12 +629,12 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(mSignalSemaphore != VK_NULL_HANDLE);
|
ASSERT(mSignalSemaphore != VK_NULL_HANDLE);
|
||||||
ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
|
|
||||||
|
|
||||||
// Release the texture
|
// Release the texture
|
||||||
mExternalState = ExternalState::Released;
|
mExternalState = ExternalState::Released;
|
||||||
|
|
||||||
wgpu::TextureUsage usage = mSubresourceLastUsages[0];
|
ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
|
||||||
|
wgpu::TextureUsage usage = mSubresourceLastUsages.Get(Aspect::Color, 0, 0);
|
||||||
|
|
||||||
VkImageMemoryBarrier barrier;
|
VkImageMemoryBarrier barrier;
|
||||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
@ -812,9 +822,15 @@ namespace dawn_native { namespace vulkan {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::TransitionFullUsage(CommandRecordingContext* recordingContext,
|
bool Texture::ShouldCombineDepthStencilBarriers() const {
|
||||||
wgpu::TextureUsage usage) {
|
return GetFormat().aspects == (Aspect::Depth | Aspect::Stencil);
|
||||||
TransitionUsageNow(recordingContext, usage, GetAllSubresources());
|
}
|
||||||
|
|
||||||
|
Aspect Texture::ComputeAspectsForSubresourceStorage() const {
|
||||||
|
if (ShouldCombineDepthStencilBarriers()) {
|
||||||
|
return Aspect::CombinedDepthStencil;
|
||||||
|
}
|
||||||
|
return GetFormat().aspects;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::TransitionUsageForPass(CommandRecordingContext* recordingContext,
|
void Texture::TransitionUsageForPass(CommandRecordingContext* recordingContext,
|
||||||
|
@ -822,73 +838,63 @@ namespace dawn_native { namespace vulkan {
|
||||||
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
||||||
VkPipelineStageFlags* srcStages,
|
VkPipelineStageFlags* srcStages,
|
||||||
VkPipelineStageFlags* dstStages) {
|
VkPipelineStageFlags* dstStages) {
|
||||||
|
// Base Vulkan doesn't support transitioning depth and stencil separately. We work around
|
||||||
|
// this limitation by combining the usages in the two planes of `textureUsages` into a
|
||||||
|
// single plane in a new SubresourceStorage<TextureUsage>. The barriers will be produced
|
||||||
|
// for DEPTH | STENCIL since the SubresourceRange uses Aspect::CombinedDepthStencil.
|
||||||
|
if (ShouldCombineDepthStencilBarriers()) {
|
||||||
|
SubresourceStorage<wgpu::TextureUsage> combinedUsages(
|
||||||
|
Aspect::CombinedDepthStencil, GetArrayLayers(), GetNumMipLevels());
|
||||||
|
textureUsages.subresourceUsages.Iterate([&](const SubresourceRange& range,
|
||||||
|
wgpu::TextureUsage usage) {
|
||||||
|
SubresourceRange updateRange = range;
|
||||||
|
updateRange.aspects = Aspect::CombinedDepthStencil;
|
||||||
|
|
||||||
|
combinedUsages.Update(
|
||||||
|
updateRange, [&](const SubresourceRange&, wgpu::TextureUsage* combinedUsage) {
|
||||||
|
*combinedUsage |= usage;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
TransitionUsageForPassImpl(recordingContext, combinedUsages, imageBarriers, srcStages,
|
||||||
|
dstStages);
|
||||||
|
} else {
|
||||||
|
TransitionUsageForPassImpl(recordingContext, textureUsages.subresourceUsages,
|
||||||
|
imageBarriers, srcStages, dstStages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::TransitionUsageForPassImpl(
|
||||||
|
CommandRecordingContext* recordingContext,
|
||||||
|
const SubresourceStorage<wgpu::TextureUsage>& subresourceUsages,
|
||||||
|
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
||||||
|
VkPipelineStageFlags* srcStages,
|
||||||
|
VkPipelineStageFlags* dstStages) {
|
||||||
size_t transitionBarrierStart = imageBarriers->size();
|
size_t transitionBarrierStart = imageBarriers->size();
|
||||||
const Format& format = GetFormat();
|
const Format& format = GetFormat();
|
||||||
|
|
||||||
wgpu::TextureUsage allUsages = wgpu::TextureUsage::None;
|
wgpu::TextureUsage allUsages = wgpu::TextureUsage::None;
|
||||||
wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
|
wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
|
||||||
|
|
||||||
uint32_t subresourceCount = GetSubresourceCount();
|
|
||||||
ASSERT(textureUsages.subresourceUsages.size() == subresourceCount);
|
|
||||||
// This transitions assume it is a 2D texture
|
// This transitions assume it is a 2D texture
|
||||||
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
|
||||||
// If new usages of all subresources are the same and old usages of all subresources are
|
mSubresourceLastUsages.Merge(
|
||||||
// the same too, we can use one barrier to do state transition for all subresources.
|
subresourceUsages, [&](const SubresourceRange& range, wgpu::TextureUsage* lastUsage,
|
||||||
// Note that if the texture has only one mip level and one array slice, it will fall into
|
const wgpu::TextureUsage& newUsage) {
|
||||||
// this category.
|
if (newUsage == wgpu::TextureUsage::None ||
|
||||||
if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) {
|
CanReuseWithoutBarrier(*lastUsage, newUsage)) {
|
||||||
if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], textureUsages.usage)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
imageBarriers->push_back(BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[0],
|
|
||||||
textureUsages.usage, GetAllSubresources()));
|
|
||||||
allLastUsages = mSubresourceLastUsages[0];
|
|
||||||
allUsages = textureUsages.usage;
|
|
||||||
for (uint32_t i = 0; i < subresourceCount; ++i) {
|
|
||||||
mSubresourceLastUsages[i] = textureUsages.usage;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) {
|
|
||||||
for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) {
|
|
||||||
wgpu::TextureUsage lastUsage = wgpu::TextureUsage::None;
|
|
||||||
wgpu::TextureUsage usage = wgpu::TextureUsage::None;
|
|
||||||
|
|
||||||
// Accumulate usage for all format aspects because we cannot transition
|
|
||||||
// separately.
|
|
||||||
// TODO(enga): Use VK_KHR_separate_depth_stencil_layouts.
|
|
||||||
for (Aspect aspect : IterateEnumMask(GetFormat().aspects)) {
|
|
||||||
uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer, aspect);
|
|
||||||
|
|
||||||
usage |= textureUsages.subresourceUsages[index];
|
|
||||||
lastUsage |= mSubresourceLastUsages[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid encoding barriers when it isn't needed.
|
|
||||||
if (usage == wgpu::TextureUsage::None) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CanReuseWithoutBarrier(lastUsage, usage)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
allLastUsages |= lastUsage;
|
|
||||||
allUsages |= usage;
|
|
||||||
|
|
||||||
for (Aspect aspect : IterateEnumMask(GetFormat().aspects)) {
|
|
||||||
uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer, aspect);
|
|
||||||
mSubresourceLastUsages[index] = usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
imageBarriers->push_back(
|
imageBarriers->push_back(
|
||||||
BuildMemoryBarrier(format, mHandle, lastUsage, usage,
|
BuildMemoryBarrier(format, mHandle, *lastUsage, newUsage, range));
|
||||||
SubresourceRange::SingleMipAndLayer(
|
|
||||||
mipLevel, arrayLayer, GetFormat().aspects)));
|
allLastUsages |= *lastUsage;
|
||||||
}
|
allUsages |= newUsage;
|
||||||
}
|
|
||||||
}
|
*lastUsage = newUsage;
|
||||||
|
});
|
||||||
|
|
||||||
if (mExternalState != ExternalState::InternalOnly) {
|
if (mExternalState != ExternalState::InternalOnly) {
|
||||||
TweakTransitionForExternalUsage(recordingContext, imageBarriers,
|
TweakTransitionForExternalUsage(recordingContext, imageBarriers,
|
||||||
|
@ -897,7 +903,6 @@ namespace dawn_native { namespace vulkan {
|
||||||
|
|
||||||
*srcStages |= VulkanPipelineStage(allLastUsages, format);
|
*srcStages |= VulkanPipelineStage(allLastUsages, format);
|
||||||
*dstStages |= VulkanPipelineStage(allUsages, format);
|
*dstStages |= VulkanPipelineStage(allUsages, format);
|
||||||
mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext,
|
void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext,
|
||||||
|
@ -923,74 +928,55 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::TransitionUsageAndGetResourceBarrier(
|
void Texture::TransitionUsageAndGetResourceBarrier(
|
||||||
|
wgpu::TextureUsage usage,
|
||||||
|
const SubresourceRange& range,
|
||||||
|
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
||||||
|
VkPipelineStageFlags* srcStages,
|
||||||
|
VkPipelineStageFlags* dstStages) {
|
||||||
|
// Base Vulkan doesn't support transitioning depth and stencil separately. We work around
|
||||||
|
// this limitation by modifying the range to be on CombinedDepthStencil. The barriers will
|
||||||
|
// be produced for DEPTH | STENCIL since the SubresourceRange uses
|
||||||
|
// Aspect::CombinedDepthStencil.
|
||||||
|
if (ShouldCombineDepthStencilBarriers()) {
|
||||||
|
SubresourceRange updatedRange = range;
|
||||||
|
updatedRange.aspects = Aspect::CombinedDepthStencil;
|
||||||
|
|
||||||
|
std::vector<VkImageMemoryBarrier> newBarriers;
|
||||||
|
TransitionUsageAndGetResourceBarrierImpl(usage, updatedRange, imageBarriers, srcStages,
|
||||||
|
dstStages);
|
||||||
|
} else {
|
||||||
|
TransitionUsageAndGetResourceBarrierImpl(usage, range, imageBarriers, srcStages,
|
||||||
|
dstStages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::TransitionUsageAndGetResourceBarrierImpl(
|
||||||
wgpu::TextureUsage usage,
|
wgpu::TextureUsage usage,
|
||||||
const SubresourceRange& range,
|
const SubresourceRange& range,
|
||||||
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
||||||
VkPipelineStageFlags* srcStages,
|
VkPipelineStageFlags* srcStages,
|
||||||
VkPipelineStageFlags* dstStages) {
|
VkPipelineStageFlags* dstStages) {
|
||||||
ASSERT(imageBarriers != nullptr);
|
ASSERT(imageBarriers != nullptr);
|
||||||
|
|
||||||
const Format& format = GetFormat();
|
const Format& format = GetFormat();
|
||||||
|
|
||||||
wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
|
|
||||||
|
|
||||||
// This transitions assume it is a 2D texture
|
// This transitions assume it is a 2D texture
|
||||||
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
|
||||||
// If the usages transitions can cover all subresources, and old usages of all subresources
|
wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
|
||||||
// are the same, then we can use one barrier to do state transition for all subresources.
|
mSubresourceLastUsages.Update(range, [&](const SubresourceRange& range,
|
||||||
// Note that if the texture has only one mip level and one array slice, it will fall into
|
wgpu::TextureUsage* lastUsage) {
|
||||||
// this category.
|
if (CanReuseWithoutBarrier(*lastUsage, usage)) {
|
||||||
bool areAllSubresourcesCovered = (range.levelCount == GetNumMipLevels() && //
|
|
||||||
range.layerCount == GetArrayLayers() && //
|
|
||||||
range.aspects == format.aspects);
|
|
||||||
if (mSameLastUsagesAcrossSubresources && areAllSubresourcesCovered) {
|
|
||||||
ASSERT(range.baseMipLevel == 0 && range.baseArrayLayer == 0);
|
|
||||||
if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], usage)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
imageBarriers->push_back(
|
|
||||||
BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[0], usage, range));
|
|
||||||
allLastUsages = mSubresourceLastUsages[0];
|
|
||||||
for (uint32_t i = 0; i < GetSubresourceCount(); ++i) {
|
|
||||||
mSubresourceLastUsages[i] = usage;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (uint32_t layer = range.baseArrayLayer;
|
|
||||||
layer < range.baseArrayLayer + range.layerCount; ++layer) {
|
|
||||||
for (uint32_t level = range.baseMipLevel;
|
|
||||||
level < range.baseMipLevel + range.levelCount; ++level) {
|
|
||||||
// Accumulate usage for all format aspects because we cannot transition
|
|
||||||
// separately.
|
|
||||||
// TODO(enga): Use VK_KHR_separate_depth_stencil_layouts.
|
|
||||||
wgpu::TextureUsage lastUsage = wgpu::TextureUsage::None;
|
|
||||||
for (Aspect aspect : IterateEnumMask(format.aspects)) {
|
|
||||||
uint32_t index = GetSubresourceIndex(level, layer, aspect);
|
|
||||||
lastUsage |= mSubresourceLastUsages[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CanReuseWithoutBarrier(lastUsage, usage)) {
|
imageBarriers->push_back(BuildMemoryBarrier(format, mHandle, *lastUsage, usage, range));
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
allLastUsages |= lastUsage;
|
allLastUsages |= *lastUsage;
|
||||||
|
*lastUsage = usage;
|
||||||
for (Aspect aspect : IterateEnumMask(format.aspects)) {
|
});
|
||||||
uint32_t index = GetSubresourceIndex(level, layer, aspect);
|
|
||||||
mSubresourceLastUsages[index] = usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
imageBarriers->push_back(BuildMemoryBarrier(
|
|
||||||
format, mHandle, lastUsage, usage,
|
|
||||||
SubresourceRange::SingleMipAndLayer(level, layer, format.aspects)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*srcStages |= VulkanPipelineStage(allLastUsages, format);
|
*srcStages |= VulkanPipelineStage(allLastUsages, format);
|
||||||
*dstStages |= VulkanPipelineStage(usage, format);
|
*dstStages |= VulkanPipelineStage(usage, format);
|
||||||
|
|
||||||
mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,
|
MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,
|
||||||
|
@ -1082,7 +1068,8 @@ namespace dawn_native { namespace vulkan {
|
||||||
imageRange.aspectMask = VulkanAspectMask(aspects);
|
imageRange.aspectMask = VulkanAspectMask(aspects);
|
||||||
imageRange.baseArrayLayer = layer;
|
imageRange.baseArrayLayer = layer;
|
||||||
|
|
||||||
if (aspects & (Aspect::Depth | Aspect::Stencil)) {
|
if (aspects &
|
||||||
|
(Aspect::Depth | Aspect::Stencil | Aspect::CombinedDepthStencil)) {
|
||||||
VkClearDepthStencilValue clearDepthStencilValue[1];
|
VkClearDepthStencilValue clearDepthStencilValue[1];
|
||||||
clearDepthStencilValue[0].depth = fClearColor;
|
clearDepthStencilValue[0].depth = fClearColor;
|
||||||
clearDepthStencilValue[0].stencil = uClearColor;
|
clearDepthStencilValue[0].stencil = uClearColor;
|
||||||
|
@ -1144,8 +1131,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageLayout Texture::GetCurrentLayoutForSwapChain() const {
|
VkImageLayout Texture::GetCurrentLayoutForSwapChain() const {
|
||||||
ASSERT(mSubresourceLastUsages.size() == 1);
|
return VulkanImageLayout(mSubresourceLastUsages.Get(Aspect::Color, 0, 0), GetFormat());
|
||||||
return VulkanImageLayout(mSubresourceLastUsages[0], GetFormat());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
|
@ -65,12 +65,11 @@ namespace dawn_native { namespace vulkan {
|
||||||
// Transitions the texture to be used as `usage`, recording any necessary barrier in
|
// Transitions the texture to be used as `usage`, recording any necessary barrier in
|
||||||
// `commands`.
|
// `commands`.
|
||||||
// TODO(cwallez@chromium.org): coalesce barriers and do them early when possible.
|
// TODO(cwallez@chromium.org): coalesce barriers and do them early when possible.
|
||||||
void TransitionFullUsage(CommandRecordingContext* recordingContext,
|
|
||||||
wgpu::TextureUsage usage);
|
|
||||||
|
|
||||||
void TransitionUsageNow(CommandRecordingContext* recordingContext,
|
void TransitionUsageNow(CommandRecordingContext* recordingContext,
|
||||||
wgpu::TextureUsage usage,
|
wgpu::TextureUsage usage,
|
||||||
const SubresourceRange& range);
|
const SubresourceRange& range);
|
||||||
|
// TODO(cwallez@chromium.org): This function should be an implementation detail of
|
||||||
|
// vulkan::Texture but it is currently used by the barrier tracking for compute passes.
|
||||||
void TransitionUsageAndGetResourceBarrier(wgpu::TextureUsage usage,
|
void TransitionUsageAndGetResourceBarrier(wgpu::TextureUsage usage,
|
||||||
const SubresourceRange& range,
|
const SubresourceRange& range,
|
||||||
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
||||||
|
@ -101,7 +100,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~Texture() override;
|
~Texture() override;
|
||||||
using TextureBase::TextureBase;
|
Texture(Device* device, const TextureDescriptor* descriptor, TextureState state);
|
||||||
|
|
||||||
MaybeError InitializeAsInternalTexture(VkImageUsageFlags extraUsages);
|
MaybeError InitializeAsInternalTexture(VkImageUsageFlags extraUsages);
|
||||||
MaybeError InitializeFromExternal(const ExternalImageDescriptorVk* descriptor,
|
MaybeError InitializeFromExternal(const ExternalImageDescriptorVk* descriptor,
|
||||||
|
@ -113,11 +112,32 @@ namespace dawn_native { namespace vulkan {
|
||||||
const SubresourceRange& range,
|
const SubresourceRange& range,
|
||||||
TextureBase::ClearValue);
|
TextureBase::ClearValue);
|
||||||
|
|
||||||
|
// Implementation details of the barrier computations for the texture.
|
||||||
|
void TransitionUsageForPassImpl(
|
||||||
|
CommandRecordingContext* recordingContext,
|
||||||
|
const SubresourceStorage<wgpu::TextureUsage>& subresourceUsages,
|
||||||
|
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
||||||
|
VkPipelineStageFlags* srcStages,
|
||||||
|
VkPipelineStageFlags* dstStages);
|
||||||
|
void TransitionUsageAndGetResourceBarrierImpl(
|
||||||
|
wgpu::TextureUsage usage,
|
||||||
|
const SubresourceRange& range,
|
||||||
|
std::vector<VkImageMemoryBarrier>* imageBarriers,
|
||||||
|
VkPipelineStageFlags* srcStages,
|
||||||
|
VkPipelineStageFlags* dstStages);
|
||||||
void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,
|
void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,
|
||||||
std::vector<VkImageMemoryBarrier>* barriers,
|
std::vector<VkImageMemoryBarrier>* barriers,
|
||||||
size_t transitionBarrierStart);
|
size_t transitionBarrierStart);
|
||||||
bool CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage, wgpu::TextureUsage usage);
|
bool CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage, wgpu::TextureUsage usage);
|
||||||
|
|
||||||
|
// In base Vulkan, Depth and stencil can only be transitioned together. This function
|
||||||
|
// indicates whether we should combine depth and stencil barriers to accommodate this
|
||||||
|
// limitation.
|
||||||
|
bool ShouldCombineDepthStencilBarriers() const;
|
||||||
|
// Compute the Aspects of the SubresourceStoage for this texture depending on whether we're
|
||||||
|
// doing the workaround for combined depth and stencil barriers.
|
||||||
|
Aspect ComputeAspectsForSubresourceStorage() const;
|
||||||
|
|
||||||
VkImage mHandle = VK_NULL_HANDLE;
|
VkImage mHandle = VK_NULL_HANDLE;
|
||||||
ResourceMemoryAllocation mMemoryAllocation;
|
ResourceMemoryAllocation mMemoryAllocation;
|
||||||
VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
|
VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
|
||||||
|
@ -137,12 +157,10 @@ namespace dawn_native { namespace vulkan {
|
||||||
VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
|
VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
|
||||||
std::vector<VkSemaphore> mWaitRequirements;
|
std::vector<VkSemaphore> mWaitRequirements;
|
||||||
|
|
||||||
bool mSameLastUsagesAcrossSubresources = true;
|
// Note that in early Vulkan versions it is not possible to transition depth and stencil
|
||||||
|
// separately so textures with Depth|Stencil aspects will have a single Depth aspect in the
|
||||||
// A usage of none will make sure the texture is transitioned before its first use as
|
// storage.
|
||||||
// required by the Vulkan spec.
|
SubresourceStorage<wgpu::TextureUsage> mSubresourceLastUsages;
|
||||||
std::vector<wgpu::TextureUsage> mSubresourceLastUsages =
|
|
||||||
std::vector<wgpu::TextureUsage>(GetSubresourceCount(), wgpu::TextureUsage::None);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextureView final : public TextureViewBase {
|
class TextureView final : public TextureViewBase {
|
||||||
|
|
|
@ -60,6 +60,11 @@ namespace dawn_native { namespace vulkan {
|
||||||
case Aspect::Stencil:
|
case Aspect::Stencil:
|
||||||
flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Aspect::CombinedDepthStencil:
|
||||||
|
flags |= VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||||
|
break;
|
||||||
|
|
||||||
case Aspect::None:
|
case Aspect::None:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue