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:
Corentin Wallez 2021-01-21 11:07:44 +00:00 committed by Commit Bot service account
parent 738aa17493
commit ef869c2d01
15 changed files with 284 additions and 287 deletions

View File

@ -234,6 +234,7 @@ source_set("dawn_native_sources") {
"ObjectBase.h",
"ObjectContentHasher.cpp",
"ObjectContentHasher.h",
"PassResourceUsage.cpp",
"PassResourceUsage.h",
"PassResourceUsageTracker.cpp",
"PassResourceUsageTracker.h",

View File

@ -107,6 +107,7 @@ target_sources(dawn_native PRIVATE
"IntegerTypes.h"
"ObjectBase.cpp"
"ObjectBase.h"
"PassResourceUsage.cpp"
"PassResourceUsage.h"
"PassResourceUsageTracker.cpp"
"PassResourceUsageTracker.h"

View File

@ -308,6 +308,7 @@ namespace dawn_native {
// Performs the per-pass usage validation checks
// 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.
// TODO(yunchao.he@intel.com): add read/write usage tracking for compute
MaybeError ValidatePassResourceUsage(const PassResourceUsage& pass) {
// Buffers can only be used as single-write or multiple read.
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");
}
// 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.
// 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.
@ -347,16 +346,20 @@ namespace dawn_native {
if (pass.passType != PassType::Render || readOnly || singleUse) {
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.
for (wgpu::TextureUsage subresourceUsage : textureUsage.subresourceUsages) {
bool readOnly = IsSubset(subresourceUsage, kReadOnlyTextureUsages);
bool singleUse = wgpu::HasZeroOrOneBits(subresourceUsage);
if (!readOnly && !singleUse) {
return DAWN_VALIDATION_ERROR(
// Check that every single subresource is used as either a single-write usage or a
// combination of readonly usages.
MaybeError error = {};
textureUsage.subresourceUsages.Iterate(
[&](const SubresourceRange&, const wgpu::TextureUsage& usage) {
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");
}
}
});
DAWN_TRY(std::move(error));
}
return {};
}

View File

@ -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

View File

@ -15,6 +15,7 @@
#ifndef DAWNNATIVE_PASSRESOURCEUSAGE_H
#define DAWNNATIVE_PASSRESOURCEUSAGE_H
#include "dawn_native/SubresourceStorage.h"
#include "dawn_native/dawn_platform.h"
#include <set>
@ -29,26 +30,18 @@ namespace dawn_native {
enum class PassType { Render, Compute };
// 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
// subresources' usages. This is designed deliberately to track texture usage in a fast path
// at frontend.
//
// - sameUsagesAcrossSubresources is used for optimization at backend. If the texture view
// 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.
// - subresourceUsages is used to track every subresource's usage within a texture.
struct PassTextureUsage {
// Constructor used to size subresourceUsages correctly.
PassTextureUsage(const TextureBase* texture);
wgpu::TextureUsage usage = wgpu::TextureUsage::None;
bool sameUsagesAcrossSubresources = true;
std::vector<wgpu::TextureUsage> subresourceUsages;
SubresourceStorage<wgpu::TextureUsage> subresourceUsages;
};
// Which resources are used by pass and how they are used. The command buffer validation

View File

@ -34,51 +34,31 @@ namespace dawn_native {
TextureBase* texture = view->GetTexture();
const SubresourceRange& range = view->GetSubresourceRange();
// std::map's operator[] will create the key and return a PassTextureUsage with usage = 0
// and an empty vector for subresourceUsages.
// TODO (yunchao.he@intel.com): optimize this
PassTextureUsage& textureUsage = mTextureUsages[texture];
// Get or create a new PassTextureUsage for that texture (initially filled with
// wgpu::TextureUsage::None)
auto it = mTextureUsages.emplace(texture, texture);
PassTextureUsage& textureUsage = it.first->second;
// Set parameters for the whole texture
textureUsage.usage |= usage;
textureUsage.sameUsagesAcrossSubresources &=
(range.levelCount == texture->GetNumMipLevels() && //
range.layerCount == texture->GetArrayLayers() && //
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;
}
}
}
textureUsage.subresourceUsages.Update(
range, [usage](const SubresourceRange&, wgpu::TextureUsage* storedUsage) {
*storedUsage |= usage;
});
}
void PassResourceUsageTracker::AddTextureUsage(TextureBase* texture,
const PassTextureUsage& textureUsage) {
PassTextureUsage& passTextureUsage = mTextureUsages[texture];
passTextureUsage.usage |= textureUsage.usage;
passTextureUsage.sameUsagesAcrossSubresources &= textureUsage.sameUsagesAcrossSubresources;
// Get or create a new PassTextureUsage for that texture (initially filled with
// wgpu::TextureUsage::None)
auto it = mTextureUsages.emplace(texture, texture);
PassTextureUsage* passTextureUsage = &it.first->second;
uint32_t subresourceCount = texture->GetSubresourceCount();
ASSERT(textureUsage.subresourceUsages.size() == subresourceCount);
if (!passTextureUsage.subresourceUsages.size()) {
passTextureUsage.subresourceUsages = textureUsage.subresourceUsages;
return;
}
for (uint32_t i = 0; i < subresourceCount; ++i) {
passTextureUsage.subresourceUsages[i] |= textureUsage.subresourceUsages[i];
}
passTextureUsage->usage |= textureUsage.usage;
passTextureUsage->subresourceUsages.Merge(
textureUsage.subresourceUsages,
[](const SubresourceRange&, wgpu::TextureUsage* storedUsage,
const wgpu::TextureUsage& addedUsage) { *storedUsage |= addedUsage; });
}
// Returns the per-pass usage for use by backends for APIs with explicit barriers.

View File

@ -46,8 +46,8 @@ namespace dawn_native {
ASSERT(HasOneBit(aspect));
switch (aspect) {
case Aspect::Color:
return 0;
case Aspect::Depth:
case Aspect::CombinedDepthStencil:
return 0;
case Aspect::Stencil:
return 1;
@ -60,7 +60,8 @@ namespace dawn_native {
// 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
// a valid enum value.
if (aspects == Aspect::Color || aspects == Aspect::Depth) {
if (aspects == Aspect::Color || aspects == Aspect::Depth ||
aspects == Aspect::CombinedDepthStencil) {
return 1;
} else {
ASSERT(aspects == (Aspect::Depth | Aspect::Stencil));

View File

@ -28,11 +28,15 @@ namespace dawn_native {
Color = 0x1,
Depth = 0x2,
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 <>
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

View File

@ -472,7 +472,7 @@ namespace dawn_native {
T layer0Data = Data(aspectIndex, 0);
for (uint32_t layer = 1; layer < mArrayLayerCount; layer++) {
if (Data(aspectIndex, layer) != layer0Data) {
if (!(Data(aspectIndex, layer) == layer0Data)) {
return;
}
}
@ -502,7 +502,7 @@ namespace dawn_native {
const T& level0Data = Data(aspectIndex, layer, 0);
for (uint32_t level = 1; level < mMipLevelCount; level++) {
if (Data(aspectIndex, layer, level) != level0Data) {
if (!(Data(aspectIndex, layer, level) == level0Data)) {
return;
}
}

View File

@ -513,7 +513,9 @@ namespace dawn_native { namespace d3d12 {
Texture::Texture(Device* device, const TextureDescriptor* descriptor, TextureState state)
: TextureBase(device, descriptor, state),
mSubresourceStateAndDecay(
GetSubresourceCount(),
GetFormat().aspects,
GetArrayLayers(),
GetNumMipLevels(),
{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,
uint32_t index,
void Texture::TransitionSubresourceRange(std::vector<D3D12_RESOURCE_BARRIER>* barriers,
const SubresourceRange& range,
StateAndDecay* state,
D3D12_RESOURCE_STATES newState,
ExecutionSerial pendingCommandSerial,
bool allSubresources) {
StateAndDecay* state = &mSubresourceStateAndDecay[index];
ExecutionSerial pendingCommandSerial) const {
// Reuse the subresource(s) directly and avoid transition when it isn't needed, and
// return false.
// 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.StateBefore = lastState;
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);
} 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;
}
@ -719,36 +739,11 @@ namespace dawn_native { namespace d3d12 {
// This transitions assume it is a 2D texture
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
// If the usages transitions can cover all subresources, and old usages of all subresources
// are the same, then we can use one barrier to do state transition for all subresources.
// Note that if the texture has only one mip level and one array slice, it will fall into
// this category.
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;
mSubresourceStateAndDecay.Update(
range, [&](const SubresourceRange& updateRange, StateAndDecay* state) {
TransitionSubresourceRange(barriers, updateRange, state, newState,
pendingCommandSerial);
});
}
void Texture::TrackUsageAndGetResourceBarrierForPass(
@ -765,47 +760,21 @@ namespace dawn_native { namespace d3d12 {
const ExecutionSerial pendingCommandSerial =
ToBackend(GetDevice())->GetPendingCommandSerial();
uint32_t subresourceCount = GetSubresourceCount();
ASSERT(textureUsages.subresourceUsages.size() == subresourceCount);
// This transitions assume it is a 2D texture
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
// If new usages of all subresources are the same and old usages of all subresources are
// the same too, we can use one barrier to do state transition for all subresources.
// Note that if the texture has only one mip level and one array slice, it will fall into
// this category.
if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) {
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];
}
mSubresourceStateAndDecay.Merge(
textureUsages.subresourceUsages, [&](const SubresourceRange& mergeRange,
StateAndDecay* state, wgpu::TextureUsage usage) {
// Skip if this subresource is not used during the current pass
if (usage == wgpu::TextureUsage::None) {
return;
}
for (Aspect aspect : IterateEnumMask(GetFormat().aspects)) {
for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) {
for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) {
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_RESOURCE_STATES newState = D3D12TextureUsage(usage, GetFormat());
TransitionSubresourceRange(barriers, mergeRange, state, newState,
pendingCommandSerial);
});
}
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)
: TextureViewBase(texture, descriptor) {
mSrvDesc.Format = D3D12TextureFormat(descriptor->format);

View File

@ -96,26 +96,26 @@ namespace dawn_native { namespace d3d12 {
const SubresourceRange& range,
TextureBase::ClearValue clearValue);
void TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
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;
// Barriers implementation details.
struct StateAndDecay {
D3D12_RESOURCE_STATES lastState;
ExecutionSerial lastDecaySerial;
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;
bool mSwapChainTexture = false;

View File

@ -306,6 +306,7 @@ namespace dawn_native { namespace opengl {
switch (aspect) {
case Aspect::None:
case Aspect::Color:
case Aspect::CombinedDepthStencil:
UNREACHABLE();
case Aspect::Depth:
gl.TexParameteri(target, GL_DEPTH_STENCIL_TEXTURE_MODE,
@ -714,6 +715,7 @@ namespace dawn_native { namespace opengl {
glType = GL_UNSIGNED_BYTE;
break;
case Aspect::CombinedDepthStencil:
case Aspect::None:
UNREACHABLE();
}

View File

@ -218,7 +218,7 @@ namespace dawn_native { namespace vulkan {
barrier.oldLayout = VulkanImageLayout(lastUsage, format);
barrier.newLayout = VulkanImageLayout(usage, format);
barrier.image = image;
barrier.subresourceRange.aspectMask = VulkanAspectMask(format.aspects);
barrier.subresourceRange.aspectMask = VulkanAspectMask(range.aspects);
barrier.subresourceRange.baseMipLevel = range.baseMipLevel;
barrier.subresourceRange.levelCount = range.levelCount;
barrier.subresourceRange.baseArrayLayer = range.baseArrayLayer;
@ -489,6 +489,16 @@ namespace dawn_native { namespace vulkan {
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) {
Device* device = ToBackend(GetDevice());
@ -619,12 +629,12 @@ namespace dawn_native { namespace vulkan {
}
ASSERT(mSignalSemaphore != VK_NULL_HANDLE);
ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
// Release the texture
mExternalState = ExternalState::Released;
wgpu::TextureUsage usage = mSubresourceLastUsages[0];
ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
wgpu::TextureUsage usage = mSubresourceLastUsages.Get(Aspect::Color, 0, 0);
VkImageMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
@ -812,9 +822,15 @@ namespace dawn_native { namespace vulkan {
return false;
}
void Texture::TransitionFullUsage(CommandRecordingContext* recordingContext,
wgpu::TextureUsage usage) {
TransitionUsageNow(recordingContext, usage, GetAllSubresources());
bool Texture::ShouldCombineDepthStencilBarriers() const {
return GetFormat().aspects == (Aspect::Depth | Aspect::Stencil);
}
Aspect Texture::ComputeAspectsForSubresourceStorage() const {
if (ShouldCombineDepthStencilBarriers()) {
return Aspect::CombinedDepthStencil;
}
return GetFormat().aspects;
}
void Texture::TransitionUsageForPass(CommandRecordingContext* recordingContext,
@ -822,73 +838,63 @@ namespace dawn_native { namespace vulkan {
std::vector<VkImageMemoryBarrier>* imageBarriers,
VkPipelineStageFlags* srcStages,
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();
const Format& format = GetFormat();
wgpu::TextureUsage allUsages = 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
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
// If new usages of all subresources are the same and old usages of all subresources are
// the same too, we can use one barrier to do state transition for all subresources.
// Note that if the texture has only one mip level and one array slice, it will fall into
// this category.
if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) {
if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], textureUsages.usage)) {
mSubresourceLastUsages.Merge(
subresourceUsages, [&](const SubresourceRange& range, wgpu::TextureUsage* lastUsage,
const wgpu::TextureUsage& newUsage) {
if (newUsage == wgpu::TextureUsage::None ||
CanReuseWithoutBarrier(*lastUsage, newUsage)) {
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(
BuildMemoryBarrier(format, mHandle, lastUsage, usage,
SubresourceRange::SingleMipAndLayer(
mipLevel, arrayLayer, GetFormat().aspects)));
}
}
}
BuildMemoryBarrier(format, mHandle, *lastUsage, newUsage, range));
allLastUsages |= *lastUsage;
allUsages |= newUsage;
*lastUsage = newUsage;
});
if (mExternalState != ExternalState::InternalOnly) {
TweakTransitionForExternalUsage(recordingContext, imageBarriers,
@ -897,7 +903,6 @@ namespace dawn_native { namespace vulkan {
*srcStages |= VulkanPipelineStage(allLastUsages, format);
*dstStages |= VulkanPipelineStage(allUsages, format);
mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources;
}
void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext,
@ -923,74 +928,55 @@ namespace dawn_native { namespace vulkan {
}
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,
const SubresourceRange& range,
std::vector<VkImageMemoryBarrier>* imageBarriers,
VkPipelineStageFlags* srcStages,
VkPipelineStageFlags* dstStages) {
ASSERT(imageBarriers != nullptr);
const Format& format = GetFormat();
wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
// This transitions assume it is a 2D texture
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
// If the usages transitions can cover all subresources, and old usages of all subresources
// are the same, then we can use one barrier to do state transition for all subresources.
// Note that if the texture has only one mip level and one array slice, it will fall into
// this category.
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)) {
wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
mSubresourceLastUsages.Update(range, [&](const SubresourceRange& range,
wgpu::TextureUsage* lastUsage) {
if (CanReuseWithoutBarrier(*lastUsage, usage)) {
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)) {
continue;
}
imageBarriers->push_back(BuildMemoryBarrier(format, mHandle, *lastUsage, usage, range));
allLastUsages |= lastUsage;
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)));
}
}
}
allLastUsages |= *lastUsage;
*lastUsage = usage;
});
*srcStages |= VulkanPipelineStage(allLastUsages, format);
*dstStages |= VulkanPipelineStage(usage, format);
mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered;
}
MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,
@ -1082,7 +1068,8 @@ namespace dawn_native { namespace vulkan {
imageRange.aspectMask = VulkanAspectMask(aspects);
imageRange.baseArrayLayer = layer;
if (aspects & (Aspect::Depth | Aspect::Stencil)) {
if (aspects &
(Aspect::Depth | Aspect::Stencil | Aspect::CombinedDepthStencil)) {
VkClearDepthStencilValue clearDepthStencilValue[1];
clearDepthStencilValue[0].depth = fClearColor;
clearDepthStencilValue[0].stencil = uClearColor;
@ -1144,8 +1131,7 @@ namespace dawn_native { namespace vulkan {
}
VkImageLayout Texture::GetCurrentLayoutForSwapChain() const {
ASSERT(mSubresourceLastUsages.size() == 1);
return VulkanImageLayout(mSubresourceLastUsages[0], GetFormat());
return VulkanImageLayout(mSubresourceLastUsages.Get(Aspect::Color, 0, 0), GetFormat());
}
// static

View File

@ -65,12 +65,11 @@ namespace dawn_native { namespace vulkan {
// Transitions the texture to be used as `usage`, recording any necessary barrier in
// `commands`.
// TODO(cwallez@chromium.org): coalesce barriers and do them early when possible.
void TransitionFullUsage(CommandRecordingContext* recordingContext,
wgpu::TextureUsage usage);
void TransitionUsageNow(CommandRecordingContext* recordingContext,
wgpu::TextureUsage usage,
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,
const SubresourceRange& range,
std::vector<VkImageMemoryBarrier>* imageBarriers,
@ -101,7 +100,7 @@ namespace dawn_native { namespace vulkan {
private:
~Texture() override;
using TextureBase::TextureBase;
Texture(Device* device, const TextureDescriptor* descriptor, TextureState state);
MaybeError InitializeAsInternalTexture(VkImageUsageFlags extraUsages);
MaybeError InitializeFromExternal(const ExternalImageDescriptorVk* descriptor,
@ -113,11 +112,32 @@ namespace dawn_native { namespace vulkan {
const SubresourceRange& range,
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,
std::vector<VkImageMemoryBarrier>* barriers,
size_t transitionBarrierStart);
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;
ResourceMemoryAllocation mMemoryAllocation;
VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
@ -137,12 +157,10 @@ namespace dawn_native { namespace vulkan {
VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
std::vector<VkSemaphore> mWaitRequirements;
bool mSameLastUsagesAcrossSubresources = true;
// A usage of none will make sure the texture is transitioned before its first use as
// required by the Vulkan spec.
std::vector<wgpu::TextureUsage> mSubresourceLastUsages =
std::vector<wgpu::TextureUsage>(GetSubresourceCount(), wgpu::TextureUsage::None);
// 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
// storage.
SubresourceStorage<wgpu::TextureUsage> mSubresourceLastUsages;
};
class TextureView final : public TextureViewBase {

View File

@ -60,6 +60,11 @@ namespace dawn_native { namespace vulkan {
case Aspect::Stencil:
flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
break;
case Aspect::CombinedDepthStencil:
flags |= VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
break;
case Aspect::None:
UNREACHABLE();
}