| // Copyright 2017 The Dawn Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "dawn_native/CommandBuffer.h" | |
| #include "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()) { | |
| TrackInDevice(); | |
| } | |
| CommandBufferBase::CommandBufferBase(DeviceBase* device) | |
| : ApiObjectBase(device, kLabelNotImplemented) { | |
| TrackInDevice(); | |
| } | |
| CommandBufferBase::CommandBufferBase(DeviceBase* device, ObjectBase::ErrorTag tag) | |
| : ApiObjectBase(device, tag) { | |
| } | |
| // static | |
| CommandBufferBase* CommandBufferBase::MakeError(DeviceBase* device) { | |
| return new CommandBufferBase(device, ObjectBase::kError); | |
| } | |
| ObjectType CommandBufferBase::GetType() const { | |
| return ObjectType::CommandBuffer; | |
| } | |
| MaybeError CommandBufferBase::ValidateCanUseInSubmitNow() const { | |
| 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) { | |
| Extent3D extent = texture->GetMipLevelPhysicalSize(mipLevel); | |
| 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; | |
| } | |
| } | |
| SubresourceRange GetSubresourcesAffectedByCopy(const TextureCopy& copy, | |
| const Extent3D& copySize) { | |
| switch (copy.texture->GetDimension()) { | |
| case wgpu::TextureDimension::e1D: | |
| ASSERT(copy.origin.z == 0 && copySize.depthOrArrayLayers == 1); | |
| 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}}; | |
| } | |
| } | |
| void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass) { | |
| for (ColorAttachmentIndex i : | |
| IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { | |
| auto& attachmentInfo = renderPass->colorAttachments[i]; | |
| TextureViewBase* view = attachmentInfo.view.Get(); | |
| bool hasResolveTarget = attachmentInfo.resolveTarget != nullptr; | |
| ASSERT(view->GetLayerCount() == 1); | |
| 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(); | |
| ASSERT(resolveView->GetLayerCount() == 1); | |
| 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; | |
| } | |
| } | |
| if (renderPass->attachmentState->HasDepthStencilAttachment()) { | |
| auto& attachmentInfo = renderPass->depthStencilAttachment; | |
| TextureViewBase* view = attachmentInfo.view.Get(); | |
| ASSERT(view->GetLayerCount() == 1); | |
| 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); | |
| } | |
| } | |
| bool IsFullBufferOverwrittenInTextureToBufferCopy(const CopyTextureToBufferCmd* copy) { | |
| 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 |