diff --git a/src/dawn_native/CommandBuffer.cpp b/src/dawn_native/CommandBuffer.cpp index 8329e95d9a..57ff4ecce4 100644 --- a/src/dawn_native/CommandBuffer.cpp +++ b/src/dawn_native/CommandBuffer.cpp @@ -56,7 +56,7 @@ namespace dawn_native { const Extent3D& copySize) { switch (copy.texture->GetDimension()) { case wgpu::TextureDimension::e2D: - return {copy.mipLevel, 1, copy.origin.z, copySize.depth}; + return {copy.mipLevel, 1, copy.origin.z, copySize.depth, copy.aspect}; default: UNREACHABLE(); return {}; @@ -113,36 +113,31 @@ namespace dawn_native { ASSERT(view->GetLevelCount() == 1); SubresourceRange range = view->GetSubresourceRange(); + SubresourceRange depthRange = range; + depthRange.aspects = range.aspects & Aspect::Depth; + + SubresourceRange stencilRange = range; + stencilRange.aspects = range.aspects & Aspect::Stencil; + // If the depth stencil texture has not been initialized, we want to use loadop // clear to init the contents to 0's - if (!view->GetTexture()->IsSubresourceContentInitialized(range)) { - if (view->GetTexture()->GetFormat().HasDepth() && - attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) { - attachmentInfo.clearDepth = 0.0f; - attachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; - } - if (view->GetTexture()->GetFormat().HasStencil() && - attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) { - attachmentInfo.clearStencil = 0u; - attachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; - } + if (!view->GetTexture()->IsSubresourceContentInitialized(depthRange) && + attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) { + attachmentInfo.clearDepth = 0.0f; + attachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; } - // If these have different store ops, make them both Store because we can't track - // initialized state separately yet. TODO(crbug.com/dawn/145) - if (attachmentInfo.depthStoreOp != attachmentInfo.stencilStoreOp) { - attachmentInfo.depthStoreOp = wgpu::StoreOp::Store; - attachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + if (!view->GetTexture()->IsSubresourceContentInitialized(stencilRange) && + attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) { + attachmentInfo.clearStencil = 0u; + attachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; } - if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Store && - attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store) { - view->GetTexture()->SetIsSubresourceContentInitialized(true, range); - } else { - ASSERT(attachmentInfo.depthStoreOp == wgpu::StoreOp::Clear && - attachmentInfo.stencilStoreOp == wgpu::StoreOp::Clear); - view->GetTexture()->SetIsSubresourceContentInitialized(false, range); - } + view->GetTexture()->SetIsSubresourceContentInitialized( + attachmentInfo.depthStoreOp == wgpu::StoreOp::Store, depthRange); + + view->GetTexture()->SetIsSubresourceContentInitialized( + attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store, stencilRange); } } diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp index b1a63488eb..62e730ab08 100644 --- a/src/dawn_native/CommandEncoder.cpp +++ b/src/dawn_native/CommandEncoder.cpp @@ -384,12 +384,6 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("Depth clear value cannot be NaN"); } - // This validates that the depth storeOp and stencil storeOps are the same - if (depthStencilAttachment->depthStoreOp != depthStencilAttachment->stencilStoreOp) { - return DAWN_VALIDATION_ERROR( - "The depth storeOp and stencil storeOp are not the same"); - } - // *sampleCount == 0 must only happen when there is no color attachment. In that case we // do not need to validate the sample count of the depth stencil attachment. const uint32_t depthStencilSampleCount = attachment->GetTexture()->GetSampleCount(); @@ -732,7 +726,8 @@ namespace dawn_native { copy->destination.texture = destination->texture; copy->destination.origin = destination->origin; copy->destination.mipLevel = destination->mipLevel; - copy->destination.aspect = destination->aspect; + copy->destination.aspect = + ConvertAspect(destination->texture->GetFormat(), destination->aspect); copy->copySize = *copySize; return {}; @@ -795,7 +790,7 @@ namespace dawn_native { copy->source.texture = source->texture; copy->source.origin = source->origin; copy->source.mipLevel = source->mipLevel; - copy->source.aspect = source->aspect; + copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect); copy->destination.buffer = destination->buffer; copy->destination.offset = destination->layout.offset; copy->destination.bytesPerRow = bytesPerRow; @@ -844,11 +839,12 @@ namespace dawn_native { copy->source.texture = source->texture; copy->source.origin = source->origin; copy->source.mipLevel = source->mipLevel; - copy->source.aspect = source->aspect; + copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect); copy->destination.texture = destination->texture; copy->destination.origin = destination->origin; copy->destination.mipLevel = destination->mipLevel; - copy->destination.aspect = destination->aspect; + copy->destination.aspect = + ConvertAspect(destination->texture->GetFormat(), destination->aspect); copy->copySize = *copySize; return {}; diff --git a/src/dawn_native/Commands.h b/src/dawn_native/Commands.h index 32ffcc3bf3..094867839f 100644 --- a/src/dawn_native/Commands.h +++ b/src/dawn_native/Commands.h @@ -105,7 +105,7 @@ namespace dawn_native { Ref texture; uint32_t mipLevel; Origin3D origin; // Texels / array layer - wgpu::TextureAspect aspect; + Aspect aspect; }; struct CopyBufferToBufferCmd { diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp index 3fe408c101..b7bb94b32d 100644 --- a/src/dawn_native/DawnNative.cpp +++ b/src/dawn_native/DawnNative.cpp @@ -179,10 +179,13 @@ namespace dawn_native { uint32_t baseMipLevel, uint32_t levelCount, uint32_t baseArrayLayer, - uint32_t layerCount) { + uint32_t layerCount, + WGPUTextureAspect aspect) { dawn_native::TextureBase* textureBase = reinterpret_cast(texture); - SubresourceRange range = {baseMipLevel, levelCount, baseArrayLayer, layerCount}; + SubresourceRange range = { + baseMipLevel, levelCount, baseArrayLayer, layerCount, + ConvertAspect(textureBase->GetFormat(), static_cast(aspect))}; return textureBase->IsSubresourceContentInitialized(range); } diff --git a/src/dawn_native/EnumClassBitmasks.h b/src/dawn_native/EnumClassBitmasks.h index 2072b6007b..3227cd2db0 100644 --- a/src/dawn_native/EnumClassBitmasks.h +++ b/src/dawn_native/EnumClassBitmasks.h @@ -36,6 +36,13 @@ namespace dawn_native { using wgpu::operator|=; using wgpu::operator^=; + using wgpu::HasZeroOrOneBits; + + template + constexpr bool HasOneBit(T value) { + return HasZeroOrOneBits(value) && value != T(0); + } + } // namespace dawn_native #endif // DAWNNATIVE_ENUMCLASSBITMASK_H_ diff --git a/src/dawn_native/Format.cpp b/src/dawn_native/Format.cpp index 92a8a606d5..3b65c3f860 100644 --- a/src/dawn_native/Format.cpp +++ b/src/dawn_native/Format.cpp @@ -120,6 +120,36 @@ namespace dawn_native { } } + TexelBlockInfo Format::GetTexelBlockInfo(Aspect aspect) const { + ASSERT(HasOneBit(aspect)); + ASSERT(aspects & aspect); + switch (aspect) { + case Aspect::Color: + ASSERT(aspects == aspect); + return *this; + case Aspect::Depth: + switch (format) { + case wgpu::TextureFormat::Depth32Float: + return *this; + default: + UNREACHABLE(); + break; + } + case Aspect::Stencil: + switch (format) { + case wgpu::TextureFormat::Depth24PlusStencil8: + return {1, 1, 1}; + default: + UNREACHABLE(); + break; + } + break; + default: + UNREACHABLE(); + break; + } + } + size_t Format::GetIndex() const { return ComputeFormatIndex(format); } diff --git a/src/dawn_native/Format.h b/src/dawn_native/Format.h index f57370ea80..8113a94448 100644 --- a/src/dawn_native/Format.h +++ b/src/dawn_native/Format.h @@ -67,6 +67,7 @@ namespace dawn_native { bool HasComponentType(Type componentType) const; TexelBlockInfo GetTexelBlockInfo(wgpu::TextureAspect aspect) const; + TexelBlockInfo GetTexelBlockInfo(Aspect aspect) const; // The index of the format in the list of all known formats: a unique number for each format // in [0, kKnownFormatCount) diff --git a/src/dawn_native/PassResourceUsageTracker.cpp b/src/dawn_native/PassResourceUsageTracker.cpp index bd176dfcb2..3f5a9159dd 100644 --- a/src/dawn_native/PassResourceUsageTracker.cpp +++ b/src/dawn_native/PassResourceUsageTracker.cpp @@ -15,6 +15,8 @@ #include "dawn_native/PassResourceUsageTracker.h" #include "dawn_native/Buffer.h" +#include "dawn_native/EnumMaskIterator.h" +#include "dawn_native/Format.h" #include "dawn_native/Texture.h" namespace dawn_native { @@ -30,10 +32,7 @@ namespace dawn_native { void PassResourceUsageTracker::TextureViewUsedAs(TextureViewBase* view, wgpu::TextureUsage usage) { TextureBase* texture = view->GetTexture(); - uint32_t baseMipLevel = view->GetBaseMipLevel(); - uint32_t levelCount = view->GetLevelCount(); - uint32_t baseArrayLayer = view->GetBaseArrayLayer(); - uint32_t layerCount = view->GetLayerCount(); + 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. @@ -42,20 +41,25 @@ namespace dawn_native { // Set parameters for the whole texture textureUsage.usage |= usage; - uint32_t subresourceCount = texture->GetSubresourceCount(); - textureUsage.sameUsagesAcrossSubresources &= levelCount * layerCount == subresourceCount; + 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(subresourceCount, wgpu::TextureUsage::None); + textureUsage.subresourceUsages = std::vector( + texture->GetSubresourceCount(), wgpu::TextureUsage::None); } - for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount; - ++arrayLayer) { - for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; - ++mipLevel) { - uint32_t subresourceIndex = texture->GetSubresourceIndex(mipLevel, arrayLayer); - textureUsage.subresourceUsages[subresourceIndex] |= usage; + 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; + } } } } diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp index df46321826..550846b186 100644 --- a/src/dawn_native/Texture.cpp +++ b/src/dawn_native/Texture.cpp @@ -20,6 +20,7 @@ #include "common/Constants.h" #include "common/Math.h" #include "dawn_native/Device.h" +#include "dawn_native/EnumMaskIterator.h" #include "dawn_native/PassResourceUsage.h" #include "dawn_native/ValidationUtils_autogen.h" @@ -200,6 +201,11 @@ namespace dawn_native { return {}; } + uint8_t GetPlaneIndex(Aspect aspect) { + ASSERT(HasOneBit(aspect)); + return static_cast(Log2(static_cast(aspect))); + } + } // anonymous namespace MaybeError ValidateTextureDescriptor(const DeviceBase* device, @@ -357,10 +363,33 @@ namespace dawn_native { } } + Aspect ConvertSingleAspect(const Format& format, wgpu::TextureAspect aspect) { + Aspect aspectMask = ConvertAspect(format, aspect); + ASSERT(HasOneBit(aspectMask)); + return aspectMask; + } + + Aspect ConvertAspect(const Format& format, wgpu::TextureAspect aspect) { + switch (aspect) { + case wgpu::TextureAspect::All: + return format.aspects; + case wgpu::TextureAspect::DepthOnly: + ASSERT(format.aspects & Aspect::Depth); + return Aspect::Depth; + case wgpu::TextureAspect::StencilOnly: + ASSERT(format.aspects & Aspect::Stencil); + return Aspect::Stencil; + default: + UNREACHABLE(); + break; + } + } + // static - SubresourceRange SubresourceRange::SingleSubresource(uint32_t baseMipLevel, - uint32_t baseArrayLayer) { - return {baseMipLevel, 1, baseArrayLayer, 1}; + SubresourceRange SubresourceRange::SingleMipAndLayer(uint32_t baseMipLevel, + uint32_t baseArrayLayer, + Aspect aspects) { + return {baseMipLevel, 1, baseArrayLayer, 1, aspects}; } // TextureBase @@ -376,7 +405,13 @@ namespace dawn_native { mSampleCount(descriptor->sampleCount), mUsage(descriptor->usage), mState(state) { - uint32_t subresourceCount = GetSubresourceCount(); + uint8_t planeIndex = 0; + for (Aspect aspect : IterateEnumMask(mFormat.aspects)) { + mPlaneIndices[GetPlaneIndex(aspect)] = planeIndex++; + } + + uint32_t subresourceCount = + mMipLevelCount * mSize.depth * static_cast(planeIndex); mIsSubresourceContentInitializedAtIndex = std::vector(subresourceCount, false); // Add readonly storage usage if the texture has a storage usage. The validation rules in @@ -438,7 +473,7 @@ namespace dawn_native { } SubresourceRange TextureBase::GetAllSubresources() const { ASSERT(!IsError()); - return {0, mMipLevelCount, 0, GetArrayLayers()}; + return {0, mMipLevelCount, 0, GetArrayLayers(), mFormat.aspects}; } uint32_t TextureBase::GetSampleCount() const { ASSERT(!IsError()); @@ -446,7 +481,7 @@ namespace dawn_native { } uint32_t TextureBase::GetSubresourceCount() const { ASSERT(!IsError()); - return mMipLevelCount * mSize.depth; + return static_cast(mIsSubresourceContentInitializedAtIndex.size()); } wgpu::TextureUsage TextureBase::GetUsage() const { ASSERT(!IsError()); @@ -458,25 +493,32 @@ namespace dawn_native { return mState; } - uint32_t TextureBase::GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const { + uint32_t TextureBase::GetSubresourceIndex(uint32_t mipLevel, + uint32_t arraySlice, + Aspect aspect) const { ASSERT(arraySlice <= kMaxTexture2DArrayLayers); ASSERT(mipLevel <= kMaxTexture2DMipLevels); + ASSERT(HasOneBit(aspect)); static_assert(kMaxTexture2DMipLevels <= std::numeric_limits::max() / kMaxTexture2DArrayLayers, "texture size overflows uint32_t"); - return GetNumMipLevels() * arraySlice + mipLevel; + return mipLevel + + GetNumMipLevels() * + (arraySlice + GetArrayLayers() * mPlaneIndices[GetPlaneIndex(aspect)]); } bool TextureBase::IsSubresourceContentInitialized(const SubresourceRange& range) const { ASSERT(!IsError()); - 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 = GetSubresourceIndex(mipLevel, arrayLayer); - ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); - if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) { - return false; + 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 = GetSubresourceIndex(mipLevel, arrayLayer, aspect); + ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); + if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) { + return false; + } } } } @@ -486,13 +528,15 @@ namespace dawn_native { void TextureBase::SetIsSubresourceContentInitialized(bool isInitialized, const SubresourceRange& range) { ASSERT(!IsError()); - 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 = GetSubresourceIndex(mipLevel, arrayLayer); - ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); - mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized; + 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 = GetSubresourceIndex(mipLevel, arrayLayer, aspect); + ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); + mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized; + } } } } @@ -588,7 +632,9 @@ namespace dawn_native { mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)), mDimension(descriptor->dimension), mRange({descriptor->baseMipLevel, descriptor->mipLevelCount, descriptor->baseArrayLayer, - descriptor->arrayLayerCount}) { + descriptor->arrayLayerCount, ConvertAspect(mFormat, mAspect)}) { + // TODO(crbug.com/dawn/439): Current validation only allows texture views with aspect "all". + ASSERT(mAspect == wgpu::TextureAspect::All); } TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag) diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h index 6c027e1747..fbb2deac1a 100644 --- a/src/dawn_native/Texture.h +++ b/src/dawn_native/Texture.h @@ -15,6 +15,7 @@ #ifndef DAWNNATIVE_TEXTURE_H_ #define DAWNNATIVE_TEXTURE_H_ +#include "common/ityp_array.h" #include "common/ityp_bitset.h" #include "dawn_native/EnumClassBitmasks.h" #include "dawn_native/Error.h" @@ -27,7 +28,11 @@ namespace dawn_native { + // Note: Subresource indices are computed by iterating the aspects in increasing order. + // D3D12 uses these directly, so the order much match D3D12's indices. + // - Depth/Stencil textures have Depth as Plane 0, and Stencil as Plane 1. enum class Aspect : uint8_t { + None = 0x0, Color = 0x1, Depth = 0x2, Stencil = 0x4, @@ -73,13 +78,19 @@ namespace dawn_native { wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Storage | wgpu::TextureUsage::OutputAttachment; + Aspect ConvertSingleAspect(const Format& format, wgpu::TextureAspect aspect); + Aspect ConvertAspect(const Format& format, wgpu::TextureAspect aspect); + struct SubresourceRange { uint32_t baseMipLevel; uint32_t levelCount; uint32_t baseArrayLayer; uint32_t layerCount; + Aspect aspects; - static SubresourceRange SingleSubresource(uint32_t baseMipLevel, uint32_t baseArrayLayer); + static SubresourceRange SingleMipAndLayer(uint32_t baseMipLevel, + uint32_t baseArrayLayer, + Aspect aspects); }; class TextureBase : public ObjectBase { @@ -103,7 +114,7 @@ namespace dawn_native { uint32_t GetSubresourceCount() const; wgpu::TextureUsage GetUsage() const; TextureState GetTextureState() const; - uint32_t GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const; + uint32_t GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice, Aspect aspect) const; bool IsSubresourceContentInitialized(const SubresourceRange& range) const; void SetIsSubresourceContentInitialized(bool isInitialized, const SubresourceRange& range); @@ -145,6 +156,7 @@ namespace dawn_native { // TODO(natlee@microsoft.com): Use a more optimized data structure to save space std::vector mIsSubresourceContentInitializedAtIndex; + std::array::value> mPlaneIndices; }; class TextureViewBase : public ObjectBase { diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index a1baf89104..c58f150928 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -19,6 +19,7 @@ #include "dawn_native/CommandEncoder.h" #include "dawn_native/CommandValidation.h" #include "dawn_native/Commands.h" +#include "dawn_native/EnumMaskIterator.h" #include "dawn_native/RenderBundle.h" #include "dawn_native/d3d12/BindGroupD3D12.h" #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" @@ -101,9 +102,11 @@ namespace dawn_native { namespace d3d12 { uint64_t bufferBytesPerRow, Texture* texture, uint32_t textureMiplevel, - uint32_t textureSlice) { + uint32_t textureSlice, + Aspect aspect) { const D3D12_TEXTURE_COPY_LOCATION textureLocation = - ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureSlice); + ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureSlice, + aspect); const uint64_t offset = baseCopySplit.offset + baseOffset; @@ -132,9 +135,11 @@ namespace dawn_native { namespace d3d12 { uint64_t bufferBytesPerRow, Texture* texture, uint32_t textureMiplevel, - uint32_t textureSlice) { + uint32_t textureSlice, + Aspect aspect) { const D3D12_TEXTURE_COPY_LOCATION textureLocation = - ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureSlice); + ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureSlice, + aspect); const uint64_t offset = baseCopySplit.offset + baseOffset; @@ -551,7 +556,8 @@ namespace dawn_native { namespace d3d12 { ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource(); ID3D12Resource* resolveTextureHandle = resolveTexture->GetD3D12Resource(); const uint32_t resolveTextureSubresourceIndex = resolveTexture->GetSubresourceIndex( - resolveTarget->GetBaseMipLevel(), resolveTarget->GetBaseArrayLayer()); + resolveTarget->GetBaseMipLevel(), resolveTarget->GetBaseArrayLayer(), + Aspect::Color); constexpr uint32_t kColorTextureSubresourceIndex = 0; commandContext->GetCommandList()->ResolveSubresource( resolveTextureHandle, resolveTextureSubresourceIndex, colorTextureHandle, @@ -693,9 +699,9 @@ namespace dawn_native { namespace d3d12 { DAWN_TRY(buffer->EnsureDataInitialized(commandContext)); ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); - SubresourceRange subresources = {copy->destination.mipLevel, 1, - copy->destination.origin.z, - copy->copySize.depth}; + SubresourceRange subresources = + GetSubresourcesAffectedByCopy(copy->destination, copy->copySize); + if (IsCompleteSubresourceCopiedTo(texture, copy->copySize, copy->destination.mipLevel)) { texture->SetIsSubresourceContentInitialized(true, subresources); @@ -737,7 +743,7 @@ namespace dawn_native { namespace d3d12 { RecordCopyBufferToTextureFromTextureCopySplit( commandList, copySplitPerLayerBase, buffer, bufferOffsetForNextSlice, copy->source.bytesPerRow, texture, copy->destination.mipLevel, - copyTextureLayer); + copyTextureLayer, subresources.aspects); bufferOffsetsForNextSlice[splitIndex] += bytesPerSlice * copySplits.copies2D.size(); @@ -754,8 +760,9 @@ namespace dawn_native { namespace d3d12 { DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy)); ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); - SubresourceRange subresources = {copy->source.mipLevel, 1, - copy->source.origin.z, copy->copySize.depth}; + SubresourceRange subresources = + GetSubresourcesAffectedByCopy(copy->source, copy->copySize); + texture->EnsureSubresourceContentInitialized(commandContext, subresources); texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc, @@ -793,7 +800,7 @@ namespace dawn_native { namespace d3d12 { RecordCopyTextureToBufferFromTextureCopySplit( commandList, copySplitPerLayerBase, buffer, bufferOffsetForNextSlice, copy->destination.bytesPerRow, texture, copy->source.mipLevel, - copyTextureLayer); + copyTextureLayer, subresources.aspects); bufferOffsetsForNextSlice[splitIndex] += bytesPerSlice * copySplits.copies2D.size(); @@ -808,10 +815,11 @@ namespace dawn_native { namespace d3d12 { Texture* source = ToBackend(copy->source.texture.Get()); Texture* destination = ToBackend(copy->destination.texture.Get()); - SubresourceRange srcRange = {copy->source.mipLevel, 1, copy->source.origin.z, - copy->copySize.depth}; - SubresourceRange dstRange = {copy->destination.mipLevel, 1, - copy->destination.origin.z, copy->copySize.depth}; + + SubresourceRange srcRange = + GetSubresourcesAffectedByCopy(copy->source, copy->copySize); + SubresourceRange dstRange = + GetSubresourcesAffectedByCopy(copy->destination, copy->copySize); source->EnsureSubresourceContentInitialized(commandContext, srcRange); if (IsCompleteSubresourceCopiedTo(destination, copy->copySize, @@ -835,6 +843,7 @@ namespace dawn_native { namespace d3d12 { destination->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst, dstRange); + ASSERT(srcRange.aspects == dstRange.aspects); if (CanUseCopyResource(source, destination, copy->copySize)) { commandList->CopyResource(destination->GetD3D12Resource(), source->GetD3D12Resource()); @@ -844,24 +853,28 @@ namespace dawn_native { namespace d3d12 { destination->GetDimension() == wgpu::TextureDimension::e2D); const dawn_native::Extent3D copyExtentOneSlice = { copy->copySize.width, copy->copySize.height, 1u}; - for (uint32_t slice = 0; slice < copy->copySize.depth; ++slice) { - D3D12_TEXTURE_COPY_LOCATION srcLocation = - ComputeTextureCopyLocationForTexture(source, copy->source.mipLevel, - copy->source.origin.z + slice); - D3D12_TEXTURE_COPY_LOCATION dstLocation = - ComputeTextureCopyLocationForTexture( - destination, copy->destination.mipLevel, - copy->destination.origin.z + slice); + for (Aspect aspect : IterateEnumMask(srcRange.aspects)) { + for (uint32_t slice = 0; slice < copy->copySize.depth; ++slice) { + D3D12_TEXTURE_COPY_LOCATION srcLocation = + ComputeTextureCopyLocationForTexture( + source, copy->source.mipLevel, + copy->source.origin.z + slice, aspect); - Origin3D sourceOriginInSubresource = copy->source.origin; - sourceOriginInSubresource.z = 0; - D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize( - sourceOriginInSubresource, copyExtentOneSlice); + D3D12_TEXTURE_COPY_LOCATION dstLocation = + ComputeTextureCopyLocationForTexture( + destination, copy->destination.mipLevel, + copy->destination.origin.z + slice, aspect); - commandList->CopyTextureRegion(&dstLocation, copy->destination.origin.x, - copy->destination.origin.y, 0, - &srcLocation, &sourceRegion); + Origin3D sourceOriginInSubresource = copy->source.origin; + sourceOriginInSubresource.z = 0; + D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize( + sourceOriginInSubresource, copyExtentOneSlice); + + commandList->CopyTextureRegion( + &dstLocation, copy->destination.origin.x, + copy->destination.origin.y, 0, &srcLocation, &sourceRegion); + } } } break; diff --git a/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp b/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp index 66558cb7ef..587f48a9ae 100644 --- a/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp +++ b/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp @@ -86,12 +86,14 @@ namespace dawn_native { namespace d3d12 { D3D12EndingAccessResolveSubresourceParameters(TextureView* resolveDestination) { D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS subresourceParameters; Texture* resolveDestinationTexture = ToBackend(resolveDestination->GetTexture()); + ASSERT(resolveDestinationTexture->GetFormat().aspects == Aspect::Color); subresourceParameters.DstX = 0; subresourceParameters.DstY = 0; subresourceParameters.SrcSubresource = 0; subresourceParameters.DstSubresource = resolveDestinationTexture->GetSubresourceIndex( - resolveDestination->GetBaseMipLevel(), resolveDestination->GetBaseArrayLayer()); + resolveDestination->GetBaseMipLevel(), resolveDestination->GetBaseArrayLayer(), + Aspect::Color); // Resolving a specified sub-rect is only valid on hardware that supports sample // positions. This means even {0, 0, width, height} would be invalid if unsupported. To // avoid this, we assume sub-rect resolves never work by setting them to all zeros or diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index b85f72a715..62fe6e9b2b 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -17,6 +17,7 @@ #include "common/Constants.h" #include "common/Math.h" #include "dawn_native/DynamicUploader.h" +#include "dawn_native/EnumMaskIterator.h" #include "dawn_native/Error.h" #include "dawn_native/d3d12/BufferD3D12.h" #include "dawn_native/d3d12/CommandRecordingContext.h" @@ -574,7 +575,15 @@ namespace dawn_native { namespace d3d12 { } std::vector barriers; - barriers.reserve(range.levelCount * range.layerCount); + + // TODO(enga): Consider adding a Count helper. + uint32_t aspectCount = 0; + for (Aspect aspect : IterateEnumMask(range.aspects)) { + aspectCount++; + DAWN_UNUSED(aspect); + } + + barriers.reserve(range.levelCount * range.layerCount * aspectCount); TransitionUsageAndGetResourceBarrier(commandContext, &barriers, newState, range); if (barriers.size()) { @@ -652,19 +661,6 @@ namespace dawn_native { namespace d3d12 { barrier.Transition.Subresource = allSubresources ? D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES : index; barriers->push_back(barrier); - // TODO(yunchao.he@intel.com): support subresource for depth/stencil. Depth stencil - // texture has different plane slices. While the current implementation only has differernt - // mip slices and array slices for subresources. - // This is a hack because Dawn doesn't handle subresource of multiplanar resources - // correctly. We force the transition to be the same for all planes to match what the - // frontend validation checks for. This hack might be incorrect for stencil-only texture - // because we always set transition barrier for depth plane. - if (!allSubresources && newState == D3D12_RESOURCE_STATE_DEPTH_WRITE && - GetFormat().HasStencil()) { - D3D12_RESOURCE_BARRIER barrierStencil = barrier; - barrierStencil.Transition.Subresource += GetArrayLayers() * GetNumMipLevels(); - barriers->push_back(barrierStencil); - } state->isValidToDecay = false; } @@ -686,7 +682,6 @@ namespace dawn_native { namespace d3d12 { HandleTransitionSpecialCases(commandContext); const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial(); - uint32_t subresourceCount = GetSubresourceCount(); // This transitions assume it is a 2D texture ASSERT(GetDimension() == wgpu::TextureDimension::e2D); @@ -695,25 +690,29 @@ namespace dawn_native { namespace d3d12 { // 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 * range.layerCount == subresourceCount; + 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 < subresourceCount; ++i) { + for (uint32_t i = 1; i < GetSubresourceCount(); ++i) { mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0]; } return; } - 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); + 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); + TransitionSingleOrAllSubresources(barriers, index, newState, + pendingCommandSerial, false); + } } } mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered; @@ -748,20 +747,22 @@ namespace dawn_native { namespace d3d12 { return; } - for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) { - for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) { - uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer); + 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; + // 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); } - - D3D12_RESOURCE_STATES newState = - D3D12TextureUsage(textureUsages.subresourceUsages[index], GetFormat()); - - TransitionSingleOrAllSubresources(barriers, index, newState, pendingCommandSerial, - false); } } mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources; @@ -838,16 +839,34 @@ namespace dawn_native { namespace d3d12 { if (GetFormat().HasDepthOrStencil()) { TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE, range); - D3D12_CLEAR_FLAGS clearFlags = {}; - for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; ++level) { for (uint32_t layer = range.baseArrayLayer; layer < range.baseArrayLayer + range.layerCount; ++layer) { - if (clearValue == TextureBase::ClearValue::Zero && - IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, layer))) { - // Skip lazy clears if already initialized. + // Iterate the aspects individually to determine which clear flags to use. + D3D12_CLEAR_FLAGS clearFlags = {}; + for (Aspect aspect : IterateEnumMask(range.aspects)) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleMipAndLayer(level, layer, aspect))) { + // Skip lazy clears if already initialized. + continue; + } + + switch (aspect) { + case Aspect::Depth: + clearFlags |= D3D12_CLEAR_FLAG_DEPTH; + break; + case Aspect::Stencil: + clearFlags |= D3D12_CLEAR_FLAG_STENCIL; + break; + default: + UNREACHABLE(); + break; + } + } + + if (clearFlags == 0) { continue; } @@ -860,13 +879,6 @@ namespace dawn_native { namespace d3d12 { device->GetD3D12Device()->CreateDepthStencilView(GetD3D12Resource(), &dsvDesc, baseDescriptor); - if (GetFormat().HasDepth()) { - clearFlags |= D3D12_CLEAR_FLAG_DEPTH; - } - if (GetFormat().HasStencil()) { - clearFlags |= D3D12_CLEAR_FLAG_STENCIL; - } - commandList->ClearDepthStencilView(baseDescriptor, clearFlags, fClearColor, clearColor, 0, nullptr); } @@ -878,13 +890,14 @@ namespace dawn_native { namespace d3d12 { const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor, fClearColor}; + ASSERT(range.aspects == Aspect::Color); for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; ++level) { for (uint32_t layer = range.baseArrayLayer; layer < range.baseArrayLayer + range.layerCount; ++layer) { if (clearValue == TextureBase::ClearValue::Zero && IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, layer))) { + SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) { // Skip lazy clears if already initialized. continue; } @@ -920,6 +933,7 @@ namespace dawn_native { namespace d3d12 { TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST, range); + ASSERT(range.aspects == Aspect::Color); for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; ++level) { // compute d3d12 texture copy locations for texture and buffer @@ -934,13 +948,13 @@ namespace dawn_native { namespace d3d12 { layer < range.baseArrayLayer + range.layerCount; ++layer) { if (clearValue == TextureBase::ClearValue::Zero && IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, layer))) { + SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) { // Skip lazy clears if already initialized. continue; } D3D12_TEXTURE_COPY_LOCATION textureLocation = - ComputeTextureCopyLocationForTexture(this, level, layer); + ComputeTextureCopyLocationForTexture(this, level, layer, Aspect::Color); for (uint32_t i = 0; i < copySplit.count; ++i) { Texture2DCopySplit::CopyInfo& info = copySplit.copies[i]; diff --git a/src/dawn_native/d3d12/UtilsD3D12.cpp b/src/dawn_native/d3d12/UtilsD3D12.cpp index 33a1de703c..97de20403a 100644 --- a/src/dawn_native/d3d12/UtilsD3D12.cpp +++ b/src/dawn_native/d3d12/UtilsD3D12.cpp @@ -64,11 +64,12 @@ namespace dawn_native { namespace d3d12 { D3D12_TEXTURE_COPY_LOCATION ComputeTextureCopyLocationForTexture(const Texture* texture, uint32_t level, - uint32_t slice) { + uint32_t slice, + Aspect aspect) { D3D12_TEXTURE_COPY_LOCATION copyLocation; copyLocation.pResource = texture->GetD3D12Resource(); copyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - copyLocation.SubresourceIndex = texture->GetSubresourceIndex(level, slice); + copyLocation.SubresourceIndex = texture->GetSubresourceIndex(level, slice, aspect); return copyLocation; } diff --git a/src/dawn_native/d3d12/UtilsD3D12.h b/src/dawn_native/d3d12/UtilsD3D12.h index 90842d7187..97bf74c3ad 100644 --- a/src/dawn_native/d3d12/UtilsD3D12.h +++ b/src/dawn_native/d3d12/UtilsD3D12.h @@ -29,7 +29,8 @@ namespace dawn_native { namespace d3d12 { D3D12_TEXTURE_COPY_LOCATION ComputeTextureCopyLocationForTexture(const Texture* texture, uint32_t level, - uint32_t slice); + uint32_t slice, + Aspect aspect); D3D12_TEXTURE_COPY_LOCATION ComputeBufferLocationForCopyTextureRegion( const Texture* texture, diff --git a/src/dawn_native/metal/QueueMTL.mm b/src/dawn_native/metal/QueueMTL.mm index a4c9caaf59..237750cbbe 100644 --- a/src/dawn_native/metal/QueueMTL.mm +++ b/src/dawn_native/metal/QueueMTL.mm @@ -115,7 +115,7 @@ namespace dawn_native { namespace metal { textureCopy.texture = destination->texture; textureCopy.mipLevel = destination->mipLevel; textureCopy.origin = destination->origin; - textureCopy.aspect = destination->aspect; + textureCopy.aspect = ConvertAspect(destination->texture->GetFormat(), destination->aspect); return ToBackend(GetDevice()) ->CopyFromStagingToTexture(uploadHandle.stagingBuffer, passDataLayout, &textureCopy, diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm index 454a14b273..da42d94b45 100644 --- a/src/dawn_native/metal/TextureMTL.mm +++ b/src/dawn_native/metal/TextureMTL.mm @@ -18,8 +18,10 @@ #include "common/Math.h" #include "common/Platform.h" #include "dawn_native/DynamicUploader.h" +#include "dawn_native/EnumMaskIterator.h" #include "dawn_native/metal/DeviceMTL.h" #include "dawn_native/metal/StagingBufferMTL.h" +#include "dawn_native/metal/UtilsMetal.h" #include @@ -353,7 +355,7 @@ namespace dawn_native { namespace metal { plane:plane]; [mtlDesc release]; - SetIsSubresourceContentInitialized(descriptor->isCleared, {0, 1, 0, 1}); + SetIsSubresourceContentInitialized(descriptor->isCleared, GetAllSubresources()); } Texture::~Texture() { @@ -393,8 +395,8 @@ namespace dawn_native { namespace metal { for (uint32_t arrayLayer = range.baseArrayLayer; arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) { if (clearValue == TextureBase::ClearValue::Zero && - IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, arrayLayer))) { + IsSubresourceContentInitialized(SubresourceRange::SingleMipAndLayer( + level, arrayLayer, range.aspects))) { // Skip lazy clears if already initialized. continue; } @@ -402,18 +404,34 @@ namespace dawn_native { namespace metal { MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - if (GetFormat().HasDepth()) { - descriptor.depthAttachment.texture = GetMTLTexture(); - descriptor.depthAttachment.loadAction = MTLLoadActionClear; - descriptor.depthAttachment.storeAction = MTLStoreActionStore; - descriptor.depthAttachment.clearDepth = dClearColor; - } - if (GetFormat().HasStencil()) { - descriptor.stencilAttachment.texture = GetMTLTexture(); - descriptor.stencilAttachment.loadAction = MTLLoadActionClear; - descriptor.stencilAttachment.storeAction = MTLStoreActionStore; - descriptor.stencilAttachment.clearStencil = - static_cast(clearColor); + // At least one aspect needs clearing. Iterate the aspects individually to + // determine which to clear. + for (Aspect aspect : IterateEnumMask(range.aspects)) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized(SubresourceRange::SingleMipAndLayer( + level, arrayLayer, aspect))) { + // Skip lazy clears if already initialized. + continue; + } + + switch (aspect) { + case Aspect::Depth: + descriptor.depthAttachment.texture = GetMTLTexture(); + descriptor.depthAttachment.loadAction = MTLLoadActionClear; + descriptor.depthAttachment.storeAction = MTLStoreActionStore; + descriptor.depthAttachment.clearDepth = dClearColor; + break; + case Aspect::Stencil: + descriptor.stencilAttachment.texture = GetMTLTexture(); + descriptor.stencilAttachment.loadAction = MTLLoadActionClear; + descriptor.stencilAttachment.storeAction = MTLStoreActionStore; + descriptor.stencilAttachment.clearStencil = + static_cast(clearColor); + break; + default: + UNREACHABLE(); + break; + } } commandContext->BeginRender(descriptor); @@ -433,8 +451,8 @@ namespace dawn_native { namespace metal { for (uint32_t arrayLayer = range.baseArrayLayer; arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) { if (clearValue == TextureBase::ClearValue::Zero && - IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, arrayLayer))) { + IsSubresourceContentInitialized(SubresourceRange::SingleMipAndLayer( + level, arrayLayer, Aspect::Color))) { // Skip lazy clears if already initialized. continue; } @@ -499,34 +517,21 @@ namespace dawn_native { namespace metal { id uploadBuffer = ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle(); // Encode a buffer to texture copy to clear each subresource. - for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; - ++level) { - Extent3D virtualSize = GetMipLevelVirtualSize(level); + for (Aspect aspect : IterateEnumMask(range.aspects)) { + for (uint32_t level = range.baseMipLevel; + level < range.baseMipLevel + range.levelCount; ++level) { + Extent3D virtualSize = GetMipLevelVirtualSize(level); - for (uint32_t arrayLayer = range.baseArrayLayer; - arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) { - if (clearValue == TextureBase::ClearValue::Zero && - IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, arrayLayer))) { - // Skip lazy clears if already initialized. - continue; - } + for (uint32_t arrayLayer = range.baseArrayLayer; + arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleMipAndLayer(level, arrayLayer, aspect))) { + // Skip lazy clears if already initialized. + continue; + } - // If the texture’s pixel format is a combined depth/stencil format, then - // options must be set to either blit the depth attachment portion or blit the - // stencil attachment portion. - std::array blitOptions = { - MTLBlitOptionNone, MTLBlitOptionDepthFromDepthStencil, - MTLBlitOptionStencilFromDepthStencil}; - - auto blitOptionStart = blitOptions.begin(); - auto blitOptionEnd = blitOptionStart + 1; - if (GetFormat().format == wgpu::TextureFormat::Depth24PlusStencil8) { - blitOptionStart = blitOptions.begin() + 1; - blitOptionEnd = blitOptionStart + 2; - } - - for (auto it = blitOptionStart; it != blitOptionEnd; ++it) { + MTLBlitOption blitOption = ComputeMTLBlitOption(GetFormat(), aspect); [encoder copyFromBuffer:uploadBuffer sourceOffset:uploadHandle.startOffset sourceBytesPerRow:largestMipBytesPerRow @@ -537,7 +542,7 @@ namespace dawn_native { namespace metal { destinationSlice:arrayLayer destinationLevel:level destinationOrigin:MTLOriginMake(0, 0, 0) - options:(*it)]; + options:blitOption]; } } } diff --git a/src/dawn_native/metal/UtilsMetal.h b/src/dawn_native/metal/UtilsMetal.h index f7d514d1ba..d7c0a70e52 100644 --- a/src/dawn_native/metal/UtilsMetal.h +++ b/src/dawn_native/metal/UtilsMetal.h @@ -53,7 +53,7 @@ namespace dawn_native { namespace metal { const TextureCopy& dst, const Extent3D& size); - MTLBlitOption ComputeMTLBlitOption(const Format& format, wgpu::TextureAspect aspect); + MTLBlitOption ComputeMTLBlitOption(const Format& format, Aspect aspect); }} // namespace dawn_native::metal diff --git a/src/dawn_native/metal/UtilsMetal.mm b/src/dawn_native/metal/UtilsMetal.mm index 5f50a0fb7f..3b8f64c0d8 100644 --- a/src/dawn_native/metal/UtilsMetal.mm +++ b/src/dawn_native/metal/UtilsMetal.mm @@ -164,15 +164,18 @@ namespace dawn_native { namespace metal { } } - MTLBlitOption ComputeMTLBlitOption(const Format& format, wgpu::TextureAspect aspect) { + MTLBlitOption ComputeMTLBlitOption(const Format& format, Aspect aspect) { + ASSERT(HasOneBit(aspect)); + ASSERT(format.aspects & aspect); + constexpr Aspect kDepthStencil = Aspect::Depth | Aspect::Stencil; if ((format.aspects & kDepthStencil) == kDepthStencil) { // We only provide a blit option if the format has both depth and stencil. // It is invalid to provide a blit option otherwise. switch (aspect) { - case wgpu::TextureAspect::DepthOnly: + case Aspect::Depth: return MTLBlitOptionDepthFromDepthStencil; - case wgpu::TextureAspect::StencilOnly: + case Aspect::Stencil: return MTLBlitOptionStencilFromDepthStencil; default: UNREACHABLE(); diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp index c15b943eb8..695e9396ff 100644 --- a/src/dawn_native/opengl/CommandBufferGL.cpp +++ b/src/dawn_native/opengl/CommandBufferGL.cpp @@ -525,8 +525,8 @@ namespace dawn_native { namespace opengl { buffer->EnsureDataInitialized(); ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); - SubresourceRange subresources = {dst.mipLevel, 1, dst.origin.z, - copy->copySize.depth}; + SubresourceRange subresources = + GetSubresourcesAffectedByCopy(dst, copy->copySize); if (IsCompleteSubresourceCopiedTo(texture, copySize, dst.mipLevel)) { texture->SetIsSubresourceContentInitialized(true, subresources); } else { @@ -618,8 +618,8 @@ namespace dawn_native { namespace opengl { buffer->EnsureDataInitializedAsDestination(copy); ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); - SubresourceRange subresources = {src.mipLevel, 1, src.origin.z, - copy->copySize.depth}; + SubresourceRange subresources = + GetSubresourcesAffectedByCopy(src, copy->copySize); texture->EnsureSubresourceContentInitialized(subresources); // The only way to move data from a texture to a buffer in GL is via // glReadPixels with a pack buffer. Create a temporary FBO for the copy. @@ -699,10 +699,9 @@ namespace dawn_native { namespace opengl { Extent3D copySize = ComputeTextureCopyExtent(dst, copy->copySize); Texture* srcTexture = ToBackend(src.texture.Get()); Texture* dstTexture = ToBackend(dst.texture.Get()); - SubresourceRange srcRange = {src.mipLevel, 1, src.origin.z, - copy->copySize.depth}; - SubresourceRange dstRange = {dst.mipLevel, 1, dst.origin.z, - copy->copySize.depth}; + + SubresourceRange srcRange = GetSubresourcesAffectedByCopy(src, copy->copySize); + SubresourceRange dstRange = GetSubresourcesAffectedByCopy(dst, copy->copySize); srcTexture->EnsureSubresourceContentInitialized(srcRange); if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) { diff --git a/src/dawn_native/opengl/TextureGL.cpp b/src/dawn_native/opengl/TextureGL.cpp index 0c4ea27bd7..7cfb05f6a4 100644 --- a/src/dawn_native/opengl/TextureGL.cpp +++ b/src/dawn_native/opengl/TextureGL.cpp @@ -17,6 +17,7 @@ #include "common/Assert.h" #include "common/Constants.h" #include "common/Math.h" +#include "dawn_native/EnumMaskIterator.h" #include "dawn_native/opengl/BufferGL.h" #include "dawn_native/opengl/DeviceGL.h" #include "dawn_native/opengl/UtilsGL.h" @@ -199,25 +200,25 @@ namespace dawn_native { namespace opengl { float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f; if (GetFormat().isRenderable) { - if (GetFormat().HasDepthOrStencil()) { - bool doDepthClear = GetFormat().HasDepth(); - bool doStencilClear = GetFormat().HasStencil(); + if ((range.aspects & (Aspect::Depth | Aspect::Stencil)) != 0) { GLfloat depth = fClearColor; GLint stencil = clearColor; - if (doDepthClear) { + if (range.aspects & Aspect::Depth) { gl.DepthMask(GL_TRUE); } - if (doStencilClear) { + if (range.aspects & Aspect::Stencil) { gl.StencilMask(GetStencilMaskFromStencilFormat(GetFormat().format)); } - auto DoClear = [&]() { - if (doDepthClear && doStencilClear) { + auto DoClear = [&](Aspect aspects) { + if (aspects == (Aspect::Depth | Aspect::Stencil)) { gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil); - } else if (doDepthClear) { + } else if (aspects == Aspect::Depth) { gl.ClearBufferfv(GL_DEPTH, 0, &depth); - } else if (doStencilClear) { + } else if (aspects == Aspect::Stencil) { gl.ClearBufferiv(GL_STENCIL, 0, &stencil); + } else { + UNREACHABLE(); } }; @@ -230,23 +231,42 @@ namespace dawn_native { namespace opengl { switch (GetDimension()) { case wgpu::TextureDimension::e2D: if (GetArrayLayers() == 1) { - if (clearValue == TextureBase::ClearValue::Zero && - IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, 0))) { - // Skip lazy clears if already initialized. + Aspect aspectsToClear = Aspect::None; + for (Aspect aspect : IterateEnumMask(range.aspects)) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleMipAndLayer(level, 0, + aspect))) { + // Skip lazy clears if already initialized. + continue; + } + aspectsToClear |= aspect; + } + + if (aspectsToClear == Aspect::None) { continue; } + gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GetGLTarget(), GetHandle(), static_cast(level)); - DoClear(); + DoClear(aspectsToClear); } else { for (uint32_t layer = range.baseArrayLayer; layer < range.baseArrayLayer + range.layerCount; ++layer) { - if (clearValue == TextureBase::ClearValue::Zero && - IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, layer))) { - // Skip lazy clears if already initialized. + Aspect aspectsToClear = Aspect::None; + for (Aspect aspect : IterateEnumMask(range.aspects)) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleMipAndLayer(level, layer, + aspect))) { + // Skip lazy clears if already initialized. + continue; + } + aspectsToClear |= aspect; + } + + if (aspectsToClear == Aspect::None) { continue; } @@ -254,7 +274,7 @@ namespace dawn_native { namespace opengl { GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GetHandle(), static_cast(level), static_cast(layer)); - DoClear(); + DoClear(aspectsToClear); } } break; @@ -266,6 +286,8 @@ namespace dawn_native { namespace opengl { gl.DeleteFramebuffers(1, &framebuffer); } else { + ASSERT(range.aspects == Aspect::Color); + static constexpr uint32_t MAX_TEXEL_SIZE = 16; ASSERT(GetFormat().blockByteSize <= MAX_TEXEL_SIZE); std::array clearColorData; @@ -280,7 +302,7 @@ namespace dawn_native { namespace opengl { layer < range.baseArrayLayer + range.layerCount; ++layer) { if (clearValue == TextureBase::ClearValue::Zero && IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, layer))) { + SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) { // Skip lazy clears if already initialized. continue; } @@ -292,6 +314,8 @@ namespace dawn_native { namespace opengl { } } } else { + ASSERT(range.aspects == Aspect::Color); + // TODO(natlee@microsoft.com): test compressed textures are cleared // create temp buffer with clear color to copy to the texture image ASSERT(kTextureBytesPerRowAlignment % GetFormat().blockByteSize == 0); @@ -334,7 +358,7 @@ namespace dawn_native { namespace opengl { if (GetArrayLayers() == 1) { if (clearValue == TextureBase::ClearValue::Zero && IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, 0))) { + SubresourceRange::SingleMipAndLayer(level, 0, Aspect::Color))) { // Skip lazy clears if already initialized. continue; } @@ -346,7 +370,8 @@ namespace dawn_native { namespace opengl { layer < range.baseArrayLayer + range.layerCount; ++layer) { if (clearValue == TextureBase::ClearValue::Zero && IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, layer))) { + SubresourceRange::SingleMipAndLayer(level, layer, + Aspect::Color))) { // Skip lazy clears if already initialized. continue; } diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp index 3b7d016e4d..27f92b716e 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -68,7 +68,7 @@ namespace dawn_native { namespace vulkan { // TODO(jiawei.shao@intel.com): support 1D and 3D textures ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D && dstTexture->GetDimension() == wgpu::TextureDimension::e2D); - region.srcSubresource.aspectMask = srcTexture->GetVkAspectMask(srcCopy.aspect); + region.srcSubresource.aspectMask = VulkanAspectMask(srcCopy.aspect); region.srcSubresource.mipLevel = srcCopy.mipLevel; region.srcSubresource.baseArrayLayer = srcCopy.origin.z; region.srcSubresource.layerCount = copySize.depth; @@ -77,7 +77,7 @@ namespace dawn_native { namespace vulkan { region.srcOffset.y = srcCopy.origin.y; region.srcOffset.z = 0; - region.dstSubresource.aspectMask = dstTexture->GetVkAspectMask(dstCopy.aspect); + region.dstSubresource.aspectMask = VulkanAspectMask(dstCopy.aspect); region.dstSubresource.mipLevel = dstCopy.mipLevel; region.dstSubresource.baseArrayLayer = dstCopy.origin.z; region.dstSubresource.layerCount = copySize.depth; @@ -472,8 +472,9 @@ namespace dawn_native { namespace vulkan { VkImageSubresourceLayers subresource = region.imageSubresource; ASSERT(dst.texture->GetDimension() == wgpu::TextureDimension::e2D); - SubresourceRange range = {subresource.mipLevel, 1, subresource.baseArrayLayer, - subresource.layerCount}; + SubresourceRange range = + GetSubresourcesAffectedByCopy(copy->destination, copy->copySize); + if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, subresource.mipLevel)) { // Since texture has been overwritten, it has been "initialized" @@ -507,12 +508,11 @@ namespace dawn_native { namespace vulkan { VkBufferImageCopy region = ComputeBufferImageCopyRegion(dst, src, copy->copySize); - VkImageSubresourceLayers subresource = region.imageSubresource; ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D); - const SubresourceRange range = {subresource.mipLevel, 1, - subresource.baseArrayLayer, - subresource.layerCount}; + SubresourceRange range = + GetSubresourcesAffectedByCopy(copy->source, copy->copySize); + ToBackend(src.texture) ->EnsureSubresourceContentInitialized(recordingContext, range); diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp index 398e30218c..f6afcfdb49 100644 --- a/src/dawn_native/vulkan/DeviceVk.cpp +++ b/src/dawn_native/vulkan/DeviceVk.cpp @@ -628,8 +628,8 @@ namespace dawn_native { namespace vulkan { VkImageSubresourceLayers subresource = region.imageSubresource; ASSERT(dst->texture->GetDimension() == wgpu::TextureDimension::e2D); - SubresourceRange range = {subresource.mipLevel, 1, subresource.baseArrayLayer, - subresource.layerCount}; + SubresourceRange range = GetSubresourcesAffectedByCopy(*dst, copySize); + if (IsCompleteSubresourceCopiedTo(dst->texture.Get(), copySize, subresource.mipLevel)) { // Since texture has been overwritten, it has been "initialized" dst->texture->SetIsSubresourceContentInitialized(true, range); diff --git a/src/dawn_native/vulkan/QueueVk.cpp b/src/dawn_native/vulkan/QueueVk.cpp index c7c4ad7c6d..227ccb4593 100644 --- a/src/dawn_native/vulkan/QueueVk.cpp +++ b/src/dawn_native/vulkan/QueueVk.cpp @@ -142,7 +142,7 @@ namespace dawn_native { namespace vulkan { textureCopy.texture = destination->texture; textureCopy.mipLevel = destination->mipLevel; textureCopy.origin = destination->origin; - textureCopy.aspect = destination->aspect; + textureCopy.aspect = ConvertAspect(destination->texture->GetFormat(), destination->aspect); return ToBackend(GetDevice()) ->CopyFromStagingToTexture(uploadHandle.stagingBuffer, passDataLayout, &textureCopy, diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp index 6d1291bfb8..8c7bd3f684 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -193,28 +193,6 @@ namespace dawn_native { namespace vulkan { return flags; } - // Computes which Vulkan texture aspects are relevant for the given Dawn format - VkImageAspectFlags VulkanAspectMask(const Aspect& aspects) { - VkImageAspectFlags flags = 0; - for (Aspect aspect : IterateEnumMask(aspects)) { - switch (aspect) { - case Aspect::Color: - flags |= VK_IMAGE_ASPECT_COLOR_BIT; - break; - case Aspect::Depth: - flags |= VK_IMAGE_ASPECT_DEPTH_BIT; - break; - case Aspect::Stencil: - flags |= VK_IMAGE_ASPECT_STENCIL_BIT; - break; - default: - UNREACHABLE(); - break; - } - } - return flags; - } - VkImageMemoryBarrier BuildMemoryBarrier(const Format& format, const VkImage& image, wgpu::TextureUsage lastUsage, @@ -597,7 +575,7 @@ namespace dawn_native { namespace vulkan { // Don't clear imported texture if already cleared if (descriptor->isCleared) { - SetIsSubresourceContentInitialized(true, {0, 1, 0, 1}); + SetIsSubresourceContentInitialized(true, GetAllSubresources()); } // Success, acquire all the external objects. @@ -700,7 +678,7 @@ namespace dawn_native { namespace vulkan { if (barriers->size() == transitionBarrierStart) { barriers->push_back(BuildMemoryBarrier( GetFormat(), mHandle, wgpu::TextureUsage::None, wgpu::TextureUsage::None, - SubresourceRange::SingleSubresource(0, 0))); + SubresourceRange::SingleMipAndLayer(0, 0, GetFormat().aspects))); } // Transfer texture from external queue to graphics queue @@ -714,7 +692,7 @@ namespace dawn_native { namespace vulkan { if (barriers->size() == transitionBarrierStart) { barriers->push_back(BuildMemoryBarrier( GetFormat(), mHandle, wgpu::TextureUsage::None, wgpu::TextureUsage::None, - SubresourceRange::SingleSubresource(0, 0))); + SubresourceRange::SingleMipAndLayer(0, 0, GetFormat().aspects))); } // Transfer texture from graphics queue to external queue @@ -781,24 +759,40 @@ namespace dawn_native { namespace vulkan { } else { for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) { for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) { - uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer); + 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 (textureUsages.subresourceUsages[index] == wgpu::TextureUsage::None) { + if (usage == wgpu::TextureUsage::None) { continue; } - if (CanReuseWithoutBarrier(mSubresourceLastUsages[index], - textureUsages.subresourceUsages[index])) { + if (CanReuseWithoutBarrier(lastUsage, usage)) { continue; } - imageBarriers->push_back(BuildMemoryBarrier( - format, mHandle, mSubresourceLastUsages[index], - textureUsages.subresourceUsages[index], - SubresourceRange::SingleSubresource(mipLevel, arrayLayer))); - allLastUsages |= mSubresourceLastUsages[index]; - allUsages |= textureUsages.subresourceUsages[index]; - mSubresourceLastUsages[index] = textureUsages.subresourceUsages[index]; + + 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))); } } } @@ -820,7 +814,6 @@ namespace dawn_native { namespace vulkan { const Format& format = GetFormat(); wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None; - uint32_t subresourceCount = GetSubresourceCount(); // This transitions assume it is a 2D texture ASSERT(GetDimension() == wgpu::TextureDimension::e2D); @@ -829,7 +822,9 @@ namespace dawn_native { namespace vulkan { // 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 * range.layerCount == subresourceCount; + 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)) { @@ -838,7 +833,7 @@ namespace dawn_native { namespace vulkan { barriers.push_back( BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[0], usage, range)); allLastUsages = mSubresourceLastUsages[0]; - for (uint32_t i = 0; i < subresourceCount; ++i) { + for (uint32_t i = 0; i < GetSubresourceCount(); ++i) { mSubresourceLastUsages[i] = usage; } } else { @@ -846,17 +841,29 @@ namespace dawn_native { namespace vulkan { layer < range.baseArrayLayer + range.layerCount; ++layer) { for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; ++level) { - uint32_t index = GetSubresourceIndex(level, layer); + // 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(mSubresourceLastUsages[index], usage)) { + if (CanReuseWithoutBarrier(lastUsage, usage)) { continue; } - barriers.push_back( - BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[index], usage, - SubresourceRange::SingleSubresource(level, layer))); - allLastUsages |= mSubresourceLastUsages[index]; - mSubresourceLastUsages[index] = usage; + allLastUsages |= lastUsage; + + for (Aspect aspect : IterateEnumMask(format.aspects)) { + uint32_t index = GetSubresourceIndex(level, layer, aspect); + mSubresourceLastUsages[index] = usage; + } + + barriers.push_back(BuildMemoryBarrier( + format, mHandle, lastUsage, usage, + SubresourceRange::SingleMipAndLayer(level, layer, format.aspects))); } } } @@ -885,7 +892,6 @@ namespace dawn_native { namespace vulkan { TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range); if (GetFormat().isRenderable) { VkImageSubresourceRange imageRange = {}; - imageRange.aspectMask = GetVkAspectMask(wgpu::TextureAspect::All); imageRange.levelCount = 1; imageRange.layerCount = 1; @@ -894,16 +900,25 @@ namespace dawn_native { namespace vulkan { imageRange.baseMipLevel = level; for (uint32_t layer = range.baseArrayLayer; layer < range.baseArrayLayer + range.layerCount; ++layer) { - if (clearValue == TextureBase::ClearValue::Zero && - IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, layer))) { - // Skip lazy clears if already initialized. + Aspect aspects = Aspect::None; + for (Aspect aspect : IterateEnumMask(range.aspects)) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleMipAndLayer(level, layer, aspect))) { + // Skip lazy clears if already initialized. + continue; + } + aspects |= aspect; + } + + if (aspects == Aspect::None) { continue; } + imageRange.aspectMask = VulkanAspectMask(aspects); imageRange.baseArrayLayer = layer; - if (GetFormat().HasDepthOrStencil()) { + if (aspects & (Aspect::Depth | Aspect::Stencil)) { VkClearDepthStencilValue clearDepthStencilValue[1]; clearDepthStencilValue[0].depth = fClearColor; clearDepthStencilValue[0].stencil = clearColor; @@ -912,6 +927,7 @@ namespace dawn_native { namespace vulkan { VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clearDepthStencilValue, 1, &imageRange); } else { + ASSERT(aspects == Aspect::Color); VkClearColorValue clearColorValue = { {fClearColor, fClearColor, fClearColor, fClearColor}}; device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(), @@ -943,6 +959,7 @@ namespace dawn_native { namespace vulkan { bufferCopy.offset = uploadHandle.startOffset; bufferCopy.bytesPerRow = bytesPerRow; + ASSERT(range.aspects == Aspect::Color); for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; ++level) { Extent3D copySize = GetMipLevelVirtualSize(level); @@ -951,7 +968,7 @@ namespace dawn_native { namespace vulkan { layer < range.baseArrayLayer + range.layerCount; ++layer) { if (clearValue == TextureBase::ClearValue::Zero && IsSubresourceContentInitialized( - SubresourceRange::SingleSubresource(level, layer))) { + SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) { // Skip lazy clears if already initialized. continue; } @@ -961,7 +978,7 @@ namespace dawn_native { namespace vulkan { textureCopy.texture = this; textureCopy.origin = {0, 0, layer}; textureCopy.mipLevel = level; - textureCopy.aspect = wgpu::TextureAspect::All; + textureCopy.aspect = GetFormat().aspects; VkBufferImageCopy region = ComputeBufferImageCopyRegion(bufferCopy, textureCopy, copySize); @@ -1028,11 +1045,13 @@ namespace dawn_native { namespace vulkan { createInfo.format = VulkanImageFormat(device, descriptor->format); createInfo.components = VkComponentMapping{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}; - createInfo.subresourceRange.aspectMask = VulkanAspectMask(GetFormat().aspects); - createInfo.subresourceRange.baseMipLevel = descriptor->baseMipLevel; - createInfo.subresourceRange.levelCount = descriptor->mipLevelCount; - createInfo.subresourceRange.baseArrayLayer = descriptor->baseArrayLayer; - createInfo.subresourceRange.layerCount = descriptor->arrayLayerCount; + + const SubresourceRange& subresources = GetSubresourceRange(); + createInfo.subresourceRange.baseMipLevel = subresources.baseMipLevel; + createInfo.subresourceRange.levelCount = subresources.levelCount; + createInfo.subresourceRange.baseArrayLayer = subresources.baseArrayLayer; + createInfo.subresourceRange.layerCount = subresources.layerCount; + createInfo.subresourceRange.aspectMask = VulkanAspectMask(subresources.aspects); return CheckVkSuccess( device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), diff --git a/src/dawn_native/vulkan/UtilsVulkan.cpp b/src/dawn_native/vulkan/UtilsVulkan.cpp index b7116da568..1b6b2d66d3 100644 --- a/src/dawn_native/vulkan/UtilsVulkan.cpp +++ b/src/dawn_native/vulkan/UtilsVulkan.cpp @@ -15,6 +15,7 @@ #include "dawn_native/vulkan/UtilsVulkan.h" #include "common/Assert.h" +#include "dawn_native/EnumMaskIterator.h" #include "dawn_native/Format.h" #include "dawn_native/vulkan/Forward.h" #include "dawn_native/vulkan/TextureVk.h" @@ -44,6 +45,28 @@ namespace dawn_native { namespace vulkan { } } + // Convert Dawn texture aspects to Vulkan texture aspect flags + VkImageAspectFlags VulkanAspectMask(const Aspect& aspects) { + VkImageAspectFlags flags = 0; + for (Aspect aspect : IterateEnumMask(aspects)) { + switch (aspect) { + case Aspect::Color: + flags |= VK_IMAGE_ASPECT_COLOR_BIT; + break; + case Aspect::Depth: + flags |= VK_IMAGE_ASPECT_DEPTH_BIT; + break; + case Aspect::Stencil: + flags |= VK_IMAGE_ASPECT_STENCIL_BIT; + break; + default: + UNREACHABLE(); + break; + } + } + return flags; + } + // Vulkan SPEC requires the source/destination region specified by each element of // pRegions must be a region that is contained within srcImage/dstImage. Here the size of // the image refers to the virtual size, while Dawn validates texture copy extent with the @@ -91,7 +114,7 @@ namespace dawn_native { namespace vulkan { dataLayout.bytesPerRow / blockInfo.blockByteSize * blockInfo.blockWidth; region.bufferImageHeight = dataLayout.rowsPerImage; - region.imageSubresource.aspectMask = texture->GetVkAspectMask(textureCopy.aspect); + region.imageSubresource.aspectMask = VulkanAspectMask(textureCopy.aspect); region.imageSubresource.mipLevel = textureCopy.mipLevel; switch (textureCopy.texture->GetDimension()) { diff --git a/src/dawn_native/vulkan/UtilsVulkan.h b/src/dawn_native/vulkan/UtilsVulkan.h index 36ed8fc6ff..e57e3f4c76 100644 --- a/src/dawn_native/vulkan/UtilsVulkan.h +++ b/src/dawn_native/vulkan/UtilsVulkan.h @@ -88,6 +88,8 @@ namespace dawn_native { namespace vulkan { VkCompareOp ToVulkanCompareOp(wgpu::CompareFunction op); + VkImageAspectFlags VulkanAspectMask(const Aspect& aspects); + Extent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy, const Extent3D& copySize); VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy, @@ -99,4 +101,4 @@ namespace dawn_native { namespace vulkan { }} // namespace dawn_native::vulkan -#endif // DAWNNATIVE_VULKAN_UTILSVULKAN_H_ \ No newline at end of file +#endif // DAWNNATIVE_VULKAN_UTILSVULKAN_H_ diff --git a/src/include/dawn_native/DawnNative.h b/src/include/dawn_native/DawnNative.h index d8bf6b4f06..a57baeb119 100644 --- a/src/include/dawn_native/DawnNative.h +++ b/src/include/dawn_native/DawnNative.h @@ -182,11 +182,13 @@ namespace dawn_native { DAWN_NATIVE_EXPORT size_t GetDeprecationWarningCountForTesting(WGPUDevice device); // Query if texture has been initialized - DAWN_NATIVE_EXPORT bool IsTextureSubresourceInitialized(WGPUTexture texture, - uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount); + DAWN_NATIVE_EXPORT bool IsTextureSubresourceInitialized( + WGPUTexture texture, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount, + WGPUTextureAspect aspect = WGPUTextureAspect_All); // Backdoor to get the order of the ProcMap for testing DAWN_NATIVE_EXPORT std::vector GetProcMapNamesForTesting(); diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp index 41bb49b877..b4646f52cf 100644 --- a/src/tests/end2end/TextureZeroInitTests.cpp +++ b/src/tests/end2end/TextureZeroInitTests.cpp @@ -62,9 +62,9 @@ class TextureZeroInitTest : public DawnTest { descriptor.dimension = wgpu::TextureViewDimension::e2D; return descriptor; } - wgpu::RenderPipeline CreatePipelineForTest() { + wgpu::RenderPipeline CreatePipelineForTest(float depth = 0.f) { utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); - pipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest(); + pipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest(depth); const char* fs = R"(#version 450 layout(location = 0) out vec4 fragColor; @@ -80,8 +80,8 @@ class TextureZeroInitTest : public DawnTest { return device.CreateRenderPipeline(&pipelineDescriptor); } - wgpu::ShaderModule CreateBasicVertexShaderForTest() { - return utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(#version 450 + wgpu::ShaderModule CreateBasicVertexShaderForTest(float depth = 0.f) { + std::string source = R"(#version 450 const vec2 pos[6] = vec2[6](vec2(-1.0f, -1.0f), vec2(-1.0f, 1.0f), vec2( 1.0f, -1.0f), @@ -91,8 +91,10 @@ class TextureZeroInitTest : public DawnTest { ); void main() { - gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); - })"); + gl_Position = vec4(pos[gl_VertexIndex], )" + + std::to_string(depth) + R"(, 1.0); + })"; + return utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, source.c_str()); } wgpu::ShaderModule CreateSampledTextureFragmentShaderForTest() { return utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, @@ -574,6 +576,239 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencil) { EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); } +// Test that clear state is tracked independently for depth/stencil textures. +TEST_P(TextureZeroInitTest, IndependentDepthStencilLoadAfterDiscard) { + // TODO(enga): Figure out why this fails on Metal Intel. + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + + wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, + kDepthStencilFormat); + wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); + + // Uninitialize only depth + { + // Clear the stencil to 2 and discard the depth + { + utils::ComboRenderPassDescriptor renderPassDescriptor({}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; + renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 2; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + } + + // "all" subresources are not initialized; Depth is not initialized + EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); + EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); + + // Now load both depth and stencil. Depth should be cleared and stencil should stay the same + // at 2. + { + wgpu::TextureDescriptor colorDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::OutputAttachment, + kColorFormat); + wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor); + + utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.SetPipeline(CreatePipelineForTest()); + pass.SetStencilReference(2); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + // No lazy clear because depth will be cleared with a loadOp + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + // Expect the texture to be red because the depth and stencil tests passed. Depth was 0 + // and stencil was 2. + std::vector expected(kSize * kSize, {255, 0, 0, 255}); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), colorTexture, 0, 0, kSize, kSize, 0, 0); + } + + // Everything is initialized now + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); + + // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms + if (IsMetal() || IsVulkan()) { + // Check by copy that the stencil data is 2. + EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_EQ(uint8_t(2), depthStencilTexture, 0, 0, 0, 0, + wgpu::TextureAspect::StencilOnly)); + } + } + + // Uninitialize only stencil + { + // Clear the depth to 0.7 and discard the stencil. + { + utils::ComboRenderPassDescriptor renderPassDescriptor({}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.7; + renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + } + + // "all" subresources are not initialized; Stencil is not initialized + EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); + EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); + + // Now load both depth and stencil. Stencil should be cleared and depth should stay the same + // at 0.7. + { + wgpu::TextureDescriptor colorDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::OutputAttachment, + kColorFormat); + wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor); + + utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.SetPipeline(CreatePipelineForTest(0.7)); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + // No lazy clear because stencil will clear using a loadOp. + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + // Expect the texture to be red because both the depth a stencil tests passed. + // Depth was 0.7 and stencil was 0 + std::vector expected(kSize * kSize, {255, 0, 0, 255}); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), colorTexture, 0, 0, kSize, kSize, 0, 0); + } + + // Everything is initialized now + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); + + // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms + if (IsMetal() || IsVulkan()) { + // Check by copy that the stencil data is 0. + EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_EQ(uint8_t(0), depthStencilTexture, 0, 0, 0, 0, + wgpu::TextureAspect::StencilOnly)); + } + } +} + +// Test that clear state is tracked independently for depth/stencil textures. +// Lazy clear of the stencil aspect via copy should not touch depth. +TEST_P(TextureZeroInitTest, IndependentDepthStencilCopyAfterDiscard) { + // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms + DAWN_SKIP_TEST_IF(!(IsMetal() || IsVulkan())); + + // TODO(enga): Figure out why this fails on Metal Intel. + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + + wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, + kDepthStencilFormat); + wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); + + // Clear the depth to 0.3 and discard the stencil. + { + utils::ComboRenderPassDescriptor renderPassDescriptor({}, depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.3; + renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + } + + // "all" subresources are not initialized; Stencil is not initialized + EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, + 0, 1, WGPUTextureAspect_All)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, + 1, WGPUTextureAspect_DepthOnly)); + EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( + depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); + + // Check by copy that the stencil data is lazily cleared to 0. + EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_EQ(uint8_t(0), depthStencilTexture, 0, 0, 0, 0, + wgpu::TextureAspect::StencilOnly)); + + // Everything is initialized now + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, + 1, WGPUTextureAspect_All)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, + 1, WGPUTextureAspect_DepthOnly)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, + 1, WGPUTextureAspect_StencilOnly)); + + // Now load both depth and stencil. Stencil should be cleared and depth should stay the same + // at 0.3. + { + wgpu::TextureDescriptor colorDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::OutputAttachment, + kColorFormat); + wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor); + + utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.SetPipeline(CreatePipelineForTest(0.3)); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + // No lazy clear because stencil will clear using a loadOp. + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + // Expect the texture to be red because both the depth a stencil tests passed. + // Depth was 0.3 and stencil was 0 + std::vector expected(kSize * kSize, {255, 0, 0, 255}); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), colorTexture, 0, 0, kSize, kSize, 0, 0); + } +} + // This tests the color attachments clear to 0s TEST_P(TextureZeroInitTest, ColorAttachmentsClear) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( diff --git a/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp b/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp index b1658eaffb..277c12e32f 100644 --- a/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp +++ b/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp @@ -198,7 +198,7 @@ namespace { } } - // Depth and stencil storeOps must match + // Depth and stencil storeOps can be different TEST_F(RenderPassDescriptorValidationTest, DepthStencilStoreOpMismatch) { constexpr uint32_t kArrayLayers = 1; constexpr uint32_t kLevelCount = 1; @@ -223,15 +223,7 @@ namespace { wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); - // StoreOps mismatch causing the render pass to error - { - utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); - renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; - renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; - AssertBeginRenderPassError(&renderPass); - } - - // StoreOps match so render pass is a success + // Base case: StoreOps match so render pass is a success { utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; @@ -239,13 +231,21 @@ namespace { AssertBeginRenderPassSuccess(&renderPass); } - // StoreOps match so render pass is a success + // Base case: StoreOps match so render pass is a success { utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; AssertBeginRenderPassSuccess(&renderPass); } + + // StoreOps mismatch still is a success + { + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; + AssertBeginRenderPassSuccess(&renderPass); + } } // Currently only texture views with arrayLayerCount == 1 are allowed to be color and depth