| // Copyright 2017 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "dawn/native/CommandBuffer.h" |
| |
| #include "dawn/common/BitSetIterator.h" |
| #include "dawn/native/Buffer.h" |
| #include "dawn/native/CommandEncoder.h" |
| #include "dawn/native/CommandValidation.h" |
| #include "dawn/native/Commands.h" |
| #include "dawn/native/Format.h" |
| #include "dawn/native/ObjectType_autogen.h" |
| #include "dawn/native/Texture.h" |
| |
| namespace dawn::native { |
| |
| CommandBufferBase::CommandBufferBase(CommandEncoder* encoder, |
| const CommandBufferDescriptor* descriptor) |
| : ApiObjectBase(encoder->GetDevice(), descriptor->label), |
| mCommands(encoder->AcquireCommands()), |
| mResourceUsages(encoder->AcquireResourceUsages()), |
| mEncoderLabel(encoder->GetLabel()) { |
| GetObjectTrackingList()->Track(this); |
| } |
| |
| CommandBufferBase::CommandBufferBase(DeviceBase* device, |
| ObjectBase::ErrorTag tag, |
| const char* label) |
| : ApiObjectBase(device, tag, label) {} |
| |
| // static |
| CommandBufferBase* CommandBufferBase::MakeError(DeviceBase* device, const char* label) { |
| return new CommandBufferBase(device, ObjectBase::kError, label); |
| } |
| |
| ObjectType CommandBufferBase::GetType() const { |
| return ObjectType::CommandBuffer; |
| } |
| |
| void CommandBufferBase::FormatLabel(absl::FormatSink* s) const { |
| s->Append(ObjectTypeAsString(GetType())); |
| |
| const std::string& label = GetLabel(); |
| if (!label.empty()) { |
| s->Append(absl::StrFormat(" \"%s\"", label)); |
| } |
| |
| if (!mEncoderLabel.empty()) { |
| s->Append(absl::StrFormat(" from %s \"%s\"", ObjectTypeAsString(ObjectType::CommandEncoder), |
| mEncoderLabel)); |
| } |
| } |
| |
| const std::string& CommandBufferBase::GetEncoderLabel() const { |
| return mEncoderLabel; |
| } |
| |
| void CommandBufferBase::SetEncoderLabel(std::string encoderLabel) { |
| mEncoderLabel = encoderLabel; |
| } |
| |
| MaybeError CommandBufferBase::ValidateCanUseInSubmitNow() const { |
| DAWN_ASSERT(!IsError()); |
| |
| DAWN_INVALID_IF(!IsAlive(), "%s cannot be submitted more than once.", this); |
| return {}; |
| } |
| |
| void CommandBufferBase::DestroyImpl() { |
| FreeCommands(&mCommands); |
| mResourceUsages = {}; |
| } |
| |
| const CommandBufferResourceUsage& CommandBufferBase::GetResourceUsages() const { |
| return mResourceUsages; |
| } |
| |
| CommandIterator* CommandBufferBase::GetCommandIteratorForTesting() { |
| return &mCommands; |
| } |
| |
| bool IsCompleteSubresourceCopiedTo(const TextureBase* texture, |
| const Extent3D& copySize, |
| const uint32_t mipLevel, |
| Aspect aspect) { |
| DAWN_ASSERT(HasOneBit(aspect) || aspect == (Aspect::Depth | Aspect::Stencil)); |
| |
| Extent3D extent = texture->GetMipLevelSingleSubresourcePhysicalSize(mipLevel, aspect); |
| |
| switch (texture->GetDimension()) { |
| case wgpu::TextureDimension::e1D: |
| return extent.width == copySize.width; |
| case wgpu::TextureDimension::e2D: |
| return extent.width == copySize.width && extent.height == copySize.height; |
| case wgpu::TextureDimension::e3D: |
| return extent.width == copySize.width && extent.height == copySize.height && |
| extent.depthOrArrayLayers == copySize.depthOrArrayLayers; |
| } |
| |
| DAWN_UNREACHABLE(); |
| } |
| |
| bool IsCompleteSubresourceCopiedTo(const TextureBase* texture, |
| const Extent3D& copySize, |
| const uint32_t mipLevel, |
| wgpu::TextureAspect textureAspect) { |
| auto aspect = SelectFormatAspects(texture->GetFormat(), textureAspect); |
| return IsCompleteSubresourceCopiedTo(texture, copySize, mipLevel, aspect); |
| } |
| |
| SubresourceRange GetSubresourcesAffectedByCopy(const TextureCopy& copy, const Extent3D& copySize) { |
| switch (copy.texture->GetDimension()) { |
| case wgpu::TextureDimension::e1D: |
| DAWN_ASSERT(copy.origin.z == 0 && copySize.depthOrArrayLayers == 1); |
| DAWN_ASSERT(copy.mipLevel == 0); |
| return {copy.aspect, {0, 1}, {0, 1}}; |
| case wgpu::TextureDimension::e2D: |
| return {copy.aspect, {copy.origin.z, copySize.depthOrArrayLayers}, {copy.mipLevel, 1}}; |
| case wgpu::TextureDimension::e3D: |
| return {copy.aspect, {0, 1}, {copy.mipLevel, 1}}; |
| } |
| |
| DAWN_UNREACHABLE(); |
| } |
| |
| void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass) { |
| for (auto i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { |
| auto& attachmentInfo = renderPass->colorAttachments[i]; |
| TextureViewBase* view = attachmentInfo.view.Get(); |
| bool hasResolveTarget = attachmentInfo.resolveTarget != nullptr; |
| |
| DAWN_ASSERT(view->GetLayerCount() == 1); |
| DAWN_ASSERT(view->GetLevelCount() == 1); |
| SubresourceRange range = view->GetSubresourceRange(); |
| |
| // If the loadOp is Load, but the subresource is not initialized, use Clear instead. |
| if (attachmentInfo.loadOp == wgpu::LoadOp::Load && |
| !view->GetTexture()->IsSubresourceContentInitialized(range)) { |
| attachmentInfo.loadOp = wgpu::LoadOp::Clear; |
| attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f}; |
| } |
| |
| if (hasResolveTarget) { |
| // We need to set the resolve target to initialized so that it does not get |
| // cleared later in the pipeline. The texture will be resolved from the |
| // source color attachment, which will be correctly initialized. |
| TextureViewBase* resolveView = attachmentInfo.resolveTarget.Get(); |
| DAWN_ASSERT(resolveView->GetLayerCount() == 1); |
| DAWN_ASSERT(resolveView->GetLevelCount() == 1); |
| resolveView->GetTexture()->SetIsSubresourceContentInitialized( |
| true, resolveView->GetSubresourceRange()); |
| } |
| |
| switch (attachmentInfo.storeOp) { |
| case wgpu::StoreOp::Store: |
| view->GetTexture()->SetIsSubresourceContentInitialized(true, range); |
| break; |
| |
| case wgpu::StoreOp::Discard: |
| view->GetTexture()->SetIsSubresourceContentInitialized(false, range); |
| break; |
| |
| case wgpu::StoreOp::Undefined: |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| } |
| |
| if (renderPass->attachmentState->HasDepthStencilAttachment()) { |
| auto& attachmentInfo = renderPass->depthStencilAttachment; |
| TextureViewBase* view = attachmentInfo.view.Get(); |
| DAWN_ASSERT(view->GetLayerCount() == 1); |
| DAWN_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(depthRange) && |
| attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) { |
| attachmentInfo.clearDepth = 0.0f; |
| attachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; |
| } |
| |
| if (!view->GetTexture()->IsSubresourceContentInitialized(stencilRange) && |
| attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) { |
| attachmentInfo.clearStencil = 0u; |
| attachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; |
| } |
| |
| view->GetTexture()->SetIsSubresourceContentInitialized( |
| attachmentInfo.depthStoreOp == wgpu::StoreOp::Store, depthRange); |
| |
| view->GetTexture()->SetIsSubresourceContentInitialized( |
| attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store, stencilRange); |
| } |
| |
| if (renderPass->attachmentState->HasPixelLocalStorage()) { |
| for (auto& attachmentInfo : renderPass->storageAttachments) { |
| TextureViewBase* view = attachmentInfo.storage.Get(); |
| |
| if (view == nullptr) { |
| continue; |
| } |
| |
| DAWN_ASSERT(view->GetLayerCount() == 1); |
| DAWN_ASSERT(view->GetLevelCount() == 1); |
| const SubresourceRange& range = view->GetSubresourceRange(); |
| |
| // If the loadOp is Load, but the subresource is not initialized, use Clear instead. |
| if (attachmentInfo.loadOp == wgpu::LoadOp::Load && |
| !view->GetTexture()->IsSubresourceContentInitialized(range)) { |
| attachmentInfo.loadOp = wgpu::LoadOp::Clear; |
| attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f}; |
| } |
| |
| switch (attachmentInfo.storeOp) { |
| case wgpu::StoreOp::Store: |
| view->GetTexture()->SetIsSubresourceContentInitialized(true, range); |
| break; |
| |
| case wgpu::StoreOp::Discard: |
| view->GetTexture()->SetIsSubresourceContentInitialized(false, range); |
| break; |
| |
| case wgpu::StoreOp::Undefined: |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| } |
| } |
| } |
| |
| bool IsFullBufferOverwrittenInTextureToBufferCopy(const CopyTextureToBufferCmd* copy) { |
| DAWN_ASSERT(copy != nullptr); |
| |
| if (copy->destination.offset > 0) { |
| // The copy doesn't touch the start of the buffer. |
| return false; |
| } |
| |
| const TextureBase* texture = copy->source.texture.Get(); |
| const TexelBlockInfo& blockInfo = texture->GetFormat().GetAspectInfo(copy->source.aspect).block; |
| const uint64_t widthInBlocks = copy->copySize.width / blockInfo.width; |
| const uint64_t heightInBlocks = copy->copySize.height / blockInfo.height; |
| const bool multiSlice = copy->copySize.depthOrArrayLayers > 1; |
| const bool multiRow = multiSlice || heightInBlocks > 1; |
| |
| if (multiSlice && copy->destination.rowsPerImage > heightInBlocks) { |
| // There are gaps between slices that aren't overwritten |
| return false; |
| } |
| |
| const uint64_t copyTextureDataSizePerRow = widthInBlocks * blockInfo.byteSize; |
| if (multiRow && copy->destination.bytesPerRow > copyTextureDataSizePerRow) { |
| // There are gaps between rows that aren't overwritten |
| return false; |
| } |
| |
| // After the above checks, we're sure the copy has no gaps. |
| // Now, compute the total number of bytes written. |
| const uint64_t writtenBytes = |
| ComputeRequiredBytesInCopy(blockInfo, copy->copySize, copy->destination.bytesPerRow, |
| copy->destination.rowsPerImage) |
| .AcquireSuccess(); |
| if (!copy->destination.buffer->IsFullBufferRange(copy->destination.offset, writtenBytes)) { |
| // The written bytes don't cover the whole buffer. |
| return false; |
| } |
| |
| return true; |
| } |
| |
| std::array<float, 4> ConvertToFloatColor(dawn::native::Color color) { |
| const std::array<float, 4> outputValue = { |
| static_cast<float>(color.r), static_cast<float>(color.g), static_cast<float>(color.b), |
| static_cast<float>(color.a)}; |
| return outputValue; |
| } |
| std::array<int32_t, 4> ConvertToSignedIntegerColor(dawn::native::Color color) { |
| const std::array<int32_t, 4> outputValue = { |
| static_cast<int32_t>(color.r), static_cast<int32_t>(color.g), static_cast<int32_t>(color.b), |
| static_cast<int32_t>(color.a)}; |
| return outputValue; |
| } |
| |
| std::array<uint32_t, 4> ConvertToUnsignedIntegerColor(dawn::native::Color color) { |
| const std::array<uint32_t, 4> outputValue = { |
| static_cast<uint32_t>(color.r), static_cast<uint32_t>(color.g), |
| static_cast<uint32_t>(color.b), static_cast<uint32_t>(color.a)}; |
| return outputValue; |
| } |
| |
| } // namespace dawn::native |