diff --git a/src/common/Constants.h b/src/common/Constants.h index 7576d665c7..e281646bff 100644 --- a/src/common/Constants.h +++ b/src/common/Constants.h @@ -64,4 +64,8 @@ static constexpr uint32_t kMaxTexture2DMipLevels = 14u; static_assert(1 << (kMaxTexture2DMipLevels - 1) == kMaxTextureSize, "kMaxTexture2DMipLevels and kMaxTextureSize size mismatch"); +// Offset alignment for CopyB2B. Strictly speaking this alignment is required only +// on macOS, but we decide to do it on all platforms. +static constexpr uint64_t kCopyBufferToBufferOffsetAlignment = 4u; + #endif // COMMON_CONSTANTS_H_ diff --git a/src/dawn_native/DynamicUploader.cpp b/src/dawn_native/DynamicUploader.cpp index a732f495ce..6fb8eef685 100644 --- a/src/dawn_native/DynamicUploader.cpp +++ b/src/dawn_native/DynamicUploader.cpp @@ -28,7 +28,8 @@ namespace dawn_native { mDevice->GetPendingCommandSerial()); } - ResultOrError DynamicUploader::Allocate(uint64_t allocationSize, Serial serial) { + ResultOrError DynamicUploader::AllocateInternal(uint64_t allocationSize, + Serial serial) { // Disable further sub-allocation should the request be too large. if (allocationSize > kRingBufferSize) { std::unique_ptr stagingBuffer; @@ -108,4 +109,21 @@ namespace dawn_native { } mReleasedStagingBuffers.ClearUpTo(lastCompletedSerial); } + + // TODO(dawn:512): Optimize this function so that it doesn't allocate additional memory + // when it's not necessary. + ResultOrError DynamicUploader::Allocate(uint64_t allocationSize, + Serial serial, + uint64_t offsetAlignment) { + ASSERT(offsetAlignment > 0); + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, + AllocateInternal(allocationSize + offsetAlignment - 1, serial)); + uint64_t additionalOffset = + Align(uploadHandle.startOffset, offsetAlignment) - uploadHandle.startOffset; + uploadHandle.mappedBuffer = + static_cast(uploadHandle.mappedBuffer) + additionalOffset; + uploadHandle.startOffset += additionalOffset; + return uploadHandle; + } } // namespace dawn_native diff --git a/src/dawn_native/DynamicUploader.h b/src/dawn_native/DynamicUploader.h index 8210b035b2..a652bd8853 100644 --- a/src/dawn_native/DynamicUploader.h +++ b/src/dawn_native/DynamicUploader.h @@ -40,7 +40,9 @@ namespace dawn_native { // implemented. void ReleaseStagingBuffer(std::unique_ptr stagingBuffer); - ResultOrError Allocate(uint64_t allocationSize, Serial serial); + ResultOrError Allocate(uint64_t allocationSize, + Serial serial, + uint64_t offsetAlignment); void Deallocate(Serial lastCompletedSerial); private: @@ -51,6 +53,8 @@ namespace dawn_native { RingBufferAllocator mAllocator; }; + ResultOrError AllocateInternal(uint64_t allocationSize, Serial serial); + std::vector> mRingBuffers; SerialQueue> mReleasedStagingBuffers; DeviceBase* mDevice; diff --git a/src/dawn_native/Queue.cpp b/src/dawn_native/Queue.cpp index bf7a0fde50..eb0b053e7d 100644 --- a/src/dawn_native/Queue.cpp +++ b/src/dawn_native/Queue.cpp @@ -14,6 +14,7 @@ #include "dawn_native/Queue.h" +#include "common/Constants.h" #include "dawn_native/Buffer.h" #include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandValidation.h" @@ -110,7 +111,8 @@ namespace dawn_native { UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( - size, device->GetPendingCommandSerial())); + size, device->GetPendingCommandSerial(), + kCopyBufferToBufferOffsetAlignment)); ASSERT(uploadHandle.mappedBuffer != nullptr); memcpy(uploadHandle.mappedBuffer, data, size); diff --git a/src/dawn_native/d3d12/BufferD3D12.cpp b/src/dawn_native/d3d12/BufferD3D12.cpp index 712c896952..6699110999 100644 --- a/src/dawn_native/d3d12/BufferD3D12.cpp +++ b/src/dawn_native/d3d12/BufferD3D12.cpp @@ -425,7 +425,8 @@ namespace dawn_native { namespace d3d12 { DynamicUploader* uploader = device->GetDynamicUploader(); UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, - uploader->Allocate(GetSize(), device->GetPendingCommandSerial())); + uploader->Allocate(GetSize(), device->GetPendingCommandSerial(), + kCopyBufferToBufferOffsetAlignment)); memset(uploadHandle.mappedBuffer, clearValue, GetSize()); diff --git a/src/dawn_native/d3d12/QueueD3D12.cpp b/src/dawn_native/d3d12/QueueD3D12.cpp index 9969dbc138..b17919eec4 100644 --- a/src/dawn_native/d3d12/QueueD3D12.cpp +++ b/src/dawn_native/d3d12/QueueD3D12.cpp @@ -44,7 +44,8 @@ namespace dawn_native { namespace d3d12 { UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( - newDataSizeBytes, device->GetPendingCommandSerial())); + newDataSizeBytes, device->GetPendingCommandSerial(), + textureFormat.blockByteSize)); ASSERT(uploadHandle.mappedBuffer != nullptr); uint8_t* dstPointer = static_cast(uploadHandle.mappedBuffer); diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index a7dd331501..3d87186e4a 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -951,7 +951,8 @@ namespace dawn_native { namespace d3d12 { DynamicUploader* uploader = device->GetDynamicUploader(); UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, - uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); + uploader->Allocate(bufferSize, device->GetPendingCommandSerial(), + GetFormat().blockByteSize)); memset(uploadHandle.mappedBuffer, clearColor, bufferSize); for (uint32_t level = range.baseMipLevel; diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm index 0867639e0f..4004d788b3 100644 --- a/src/dawn_native/metal/DeviceMTL.mm +++ b/src/dawn_native/metal/DeviceMTL.mm @@ -14,6 +14,7 @@ #include "dawn_native/metal/DeviceMTL.h" +#include "common/Platform.h" #include "dawn_native/BackendConnection.h" #include "dawn_native/BindGroupLayout.h" #include "dawn_native/Commands.h" @@ -75,8 +76,10 @@ namespace dawn_native { namespace metal { { bool haveStoreAndMSAAResolve = false; #if defined(DAWN_PLATFORM_MACOS) - haveStoreAndMSAAResolve = - [mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2]; + if (@available(macOS 10.12, *)) { + haveStoreAndMSAAResolve = + [mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2]; + } #elif defined(DAWN_PLATFORM_IOS) haveStoreAndMSAAResolve = [mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]; diff --git a/src/dawn_native/metal/QueueMTL.mm b/src/dawn_native/metal/QueueMTL.mm index 07b1f79ea9..5202ccfef1 100644 --- a/src/dawn_native/metal/QueueMTL.mm +++ b/src/dawn_native/metal/QueueMTL.mm @@ -41,7 +41,8 @@ namespace dawn_native { namespace metal { UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( - newDataSizeBytes, device->GetPendingCommandSerial())); + newDataSizeBytes, device->GetPendingCommandSerial(), + blockInfo.blockByteSize)); ASSERT(uploadHandle.mappedBuffer != nullptr); uint8_t* dstPointer = static_cast(uploadHandle.mappedBuffer); diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm index fc5906b48d..449f338951 100644 --- a/src/dawn_native/metal/TextureMTL.mm +++ b/src/dawn_native/metal/TextureMTL.mm @@ -510,7 +510,8 @@ namespace dawn_native { namespace metal { DynamicUploader* uploader = device->GetDynamicUploader(); UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, - uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); + uploader->Allocate(bufferSize, device->GetPendingCommandSerial(), + GetFormat().blockByteSize)); memset(uploadHandle.mappedBuffer, clearColor, bufferSize); id encoder = commandContext->EnsureBlit(); diff --git a/src/dawn_native/vulkan/QueueVk.cpp b/src/dawn_native/vulkan/QueueVk.cpp index 8d70fd534c..fc11c10bb1 100644 --- a/src/dawn_native/vulkan/QueueVk.cpp +++ b/src/dawn_native/vulkan/QueueVk.cpp @@ -47,11 +47,17 @@ namespace dawn_native { namespace vulkan { ToBackend(device) ->GetDeviceInfo() .properties.limits.optimalBufferCopyOffsetAlignment; + ASSERT(IsPowerOfTwo(optimalOffsetAlignment)); + ASSERT(IsPowerOfTwo(blockInfo.blockByteSize)); + // We need the offset to be aligned to both optimalOffsetAlignment and blockByteSize, + // since both of them are powers of two, we only need to align to the max value. + uint64_t offsetAlignment = + std::max(optimalOffsetAlignment, uint64_t(blockInfo.blockByteSize)); UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( - newDataSizeBytes + optimalOffsetAlignment - 1, - device->GetPendingCommandSerial())); + newDataSizeBytes, device->GetPendingCommandSerial(), + offsetAlignment)); ASSERT(uploadHandle.mappedBuffer != nullptr); uint8_t* dstPointer = static_cast(uploadHandle.mappedBuffer); @@ -64,11 +70,6 @@ namespace dawn_native { namespace vulkan { dataRowsPerImageInBlock = writeSizePixel.height / blockInfo.blockHeight; } - uint64_t additionalOffset = - Align(uploadHandle.startOffset, optimalOffsetAlignment) - uploadHandle.startOffset; - uploadHandle.startOffset += additionalOffset; - dstPointer += additionalOffset; - ASSERT(dataRowsPerImageInBlock >= alignedRowsPerImageInBlock); uint64_t imageAdditionalStride = dataLayout.bytesPerRow * (dataRowsPerImageInBlock - alignedRowsPerImageInBlock); diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp index 6553406f96..e61facd09f 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -951,7 +951,8 @@ namespace dawn_native { namespace vulkan { DynamicUploader* uploader = device->GetDynamicUploader(); UploadHandle uploadHandle; DAWN_TRY_ASSIGN(uploadHandle, - uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); + uploader->Allocate(bufferSize, device->GetPendingCommandSerial(), + GetFormat().blockByteSize)); memset(uploadHandle.mappedBuffer, clearColor, bufferSize); // compute the buffer image copy to set the clear region of entire texture diff --git a/src/tests/end2end/BufferZeroInitTests.cpp b/src/tests/end2end/BufferZeroInitTests.cpp index e348c95350..6a65f177a3 100644 --- a/src/tests/end2end/BufferZeroInitTests.cpp +++ b/src/tests/end2end/BufferZeroInitTests.cpp @@ -15,6 +15,7 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/TestUtils.h" #include "utils/WGPUHelpers.h" #define EXPECT_LAZY_CLEAR(N, statement) \ diff --git a/src/tests/end2end/CompressedTextureFormatTests.cpp b/src/tests/end2end/CompressedTextureFormatTests.cpp index 2fe87ede1c..20bd34d61f 100644 --- a/src/tests/end2end/CompressedTextureFormatTests.cpp +++ b/src/tests/end2end/CompressedTextureFormatTests.cpp @@ -18,6 +18,7 @@ #include "common/Constants.h" #include "common/Math.h" #include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/TestUtils.h" #include "utils/TextureFormatUtils.h" #include "utils/WGPUHelpers.h" @@ -1066,6 +1067,35 @@ TEST_P(CompressedTextureBCFormatTest, CopyMultiple2DArrayLayers) { } } +// Testing a special code path: clearing a non-renderable texture when DynamicUploader +// is unaligned doesn't throw validation errors. +TEST_P(CompressedTextureBCFormatTest, UnalignedDynamicUploader) { + // CopyT2B for compressed texture formats is unimplemented on OpenGL. + DAWN_SKIP_TEST_IF(IsOpenGL()); + + utils::UnalignDynamicUploader(device); + + wgpu::TextureDescriptor textureDescriptor = {}; + textureDescriptor.size = {4, 4, 1}; + textureDescriptor.format = wgpu::TextureFormat::BC1RGBAUnorm; + textureDescriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&textureDescriptor); + + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = 8; + bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(buffer, 0, 256, 0); + wgpu::Extent3D copyExtent = {4, 4, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Extent); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); +} + // TODO(jiawei.shao@intel.com): support BC formats on OpenGL backend DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, D3D12Backend(), diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp index 434e248453..311d98a37b 100644 --- a/src/tests/end2end/CopyTests.cpp +++ b/src/tests/end2end/CopyTests.cpp @@ -17,6 +17,7 @@ #include #include "common/Constants.h" #include "common/Math.h" +#include "utils/TestUtils.h" #include "utils/TextureFormatUtils.h" #include "utils/WGPUHelpers.h" diff --git a/src/tests/end2end/QueueTests.cpp b/src/tests/end2end/QueueTests.cpp index 21876b6ced..783db0466c 100644 --- a/src/tests/end2end/QueueTests.cpp +++ b/src/tests/end2end/QueueTests.cpp @@ -20,6 +20,7 @@ #include "tests/DawnTest.h" #include "common/Math.h" +#include "utils/TestUtils.h" #include "utils/TextureFormatUtils.h" #include "utils/WGPUHelpers.h" @@ -170,6 +171,22 @@ TEST_P(QueueWriteBufferTests, SuperLargeWriteBuffer) { EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); } +// Test a special code path: writing when dynamic uploader already contatins some unaligned +// data, it might be necessary to use a ring buffer with properly aligned offset. +TEST_P(QueueWriteBufferTests, UnalignedDynamicUploader) { + utils::UnalignDynamicUploader(device); + + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + uint32_t value = 0x01020304; + queue.WriteBuffer(buffer, 0, &value, sizeof(value)); + + EXPECT_BUFFER_U32_EQ(value, buffer, 0); +} + DAWN_INSTANTIATE_TEST(QueueWriteBufferTests, D3D12Backend(), MetalBackend(), @@ -522,4 +539,19 @@ TEST_P(QueueWriteTextureTests, VaryingArrayBytesPerRow) { } } +// Testing a special code path: writing when dynamic uploader already contatins some unaligned +// data, it might be necessary to use a ring buffer with properly aligned offset. +TEST_P(QueueWriteTextureTests, UnalignedDynamicUploader) { + utils::UnalignDynamicUploader(device); + + constexpr wgpu::Extent3D size = {10, 10, 1}; + + TextureSpec textureSpec; + textureSpec.textureSize = size; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumDataSpec(size), size); +} + DAWN_INSTANTIATE_TEST(QueueWriteTextureTests, D3D12Backend(), MetalBackend(), VulkanBackend()); diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp index e6e6a7517a..3b0414bca3 100644 --- a/src/tests/end2end/TextureZeroInitTests.cpp +++ b/src/tests/end2end/TextureZeroInitTests.cpp @@ -16,6 +16,7 @@ #include "common/Math.h" #include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/TestUtils.h" #include "utils/WGPUHelpers.h" #define EXPECT_LAZY_CLEAR(N, statement) \ diff --git a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp index 52c242d846..c6beb1fbce 100644 --- a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp +++ b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp @@ -16,6 +16,7 @@ #include "common/Constants.h" #include "common/Math.h" #include "tests/unittests/validation/ValidationTest.h" +#include "utils/TestUtils.h" #include "utils/TextureFormatUtils.h" #include "utils/WGPUHelpers.h" diff --git a/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp b/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp index ec65bf65f5..cba31747d8 100644 --- a/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp +++ b/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp @@ -15,6 +15,7 @@ #include "tests/unittests/validation/ValidationTest.h" #include "common/Math.h" +#include "utils/TestUtils.h" #include "utils/TextureFormatUtils.h" #include "utils/WGPUHelpers.h" diff --git a/src/utils/BUILD.gn b/src/utils/BUILD.gn index eff41835b1..38fd58797b 100644 --- a/src/utils/BUILD.gn +++ b/src/utils/BUILD.gn @@ -75,6 +75,8 @@ static_library("dawn_utils") { "SystemUtils.h", "TerribleCommandBuffer.cpp", "TerribleCommandBuffer.h", + "TestUtils.cpp", + "TestUtils.h", "TextureFormatUtils.cpp", "TextureFormatUtils.h", "Timer.h", diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 080c2d5603..e215f51637 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -27,6 +27,8 @@ target_sources(dawn_utils PRIVATE "SystemUtils.h" "TerribleCommandBuffer.cpp" "TerribleCommandBuffer.h" + "TestUtils.cpp" + "TestUtils.h" "TextureFormatUtils.cpp" "TextureFormatUtils.h" "Timer.h" diff --git a/src/utils/TestUtils.cpp b/src/utils/TestUtils.cpp new file mode 100644 index 0000000000..cdbe3b3fc8 --- /dev/null +++ b/src/utils/TestUtils.cpp @@ -0,0 +1,107 @@ +// Copyright 2020 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 "utils/TestUtils.h" + +#include "common/Assert.h" +#include "common/Constants.h" +#include "common/Math.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" + +#include + +namespace utils { + + uint32_t GetMinimumBytesPerRow(wgpu::TextureFormat format, uint32_t width) { + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); + return Align(bytesPerTexel * width, kTextureBytesPerRowAlignment); + } + + uint32_t GetBytesInBufferTextureCopy(wgpu::TextureFormat format, + uint32_t width, + uint32_t bytesPerRow, + uint32_t rowsPerImage, + uint32_t copyArrayLayerCount) { + ASSERT(rowsPerImage > 0); + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); + const uint32_t bytesAtLastImage = bytesPerRow * (rowsPerImage - 1) + bytesPerTexel * width; + return bytesPerRow * rowsPerImage * (copyArrayLayerCount - 1) + bytesAtLastImage; + } + + // TODO(jiawei.shao@intel.com): support compressed texture formats + TextureDataCopyLayout GetTextureDataCopyLayoutForTexture2DAtLevel( + wgpu::TextureFormat format, + wgpu::Extent3D textureSizeAtLevel0, + uint32_t mipmapLevel, + uint32_t rowsPerImage) { + TextureDataCopyLayout layout; + + layout.mipSize = {textureSizeAtLevel0.width >> mipmapLevel, + textureSizeAtLevel0.height >> mipmapLevel, textureSizeAtLevel0.depth}; + + layout.bytesPerRow = GetMinimumBytesPerRow(format, layout.mipSize.width); + + uint32_t appliedRowsPerImage = rowsPerImage > 0 ? rowsPerImage : layout.mipSize.height; + layout.bytesPerImage = layout.bytesPerRow * appliedRowsPerImage; + + layout.byteLength = + GetBytesInBufferTextureCopy(format, layout.mipSize.width, layout.bytesPerRow, + appliedRowsPerImage, textureSizeAtLevel0.depth); + + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); + layout.texelBlocksPerRow = layout.bytesPerRow / bytesPerTexel; + layout.texelBlocksPerImage = layout.bytesPerImage / bytesPerTexel; + layout.texelBlockCount = layout.byteLength / bytesPerTexel; + + return layout; + } + + uint64_t RequiredBytesInCopy(uint64_t bytesPerRow, + uint64_t rowsPerImage, + wgpu::Extent3D copyExtent, + wgpu::TextureFormat textureFormat) { + if (copyExtent.width == 0 || copyExtent.height == 0 || copyExtent.depth == 0) { + return 0; + } else { + uint32_t blockSize = utils::GetTexelBlockSizeInBytes(textureFormat); + uint32_t blockWidth = utils::GetTextureFormatBlockWidth(textureFormat); + uint32_t blockHeight = utils::GetTextureFormatBlockHeight(textureFormat); + + uint64_t texelBlockRowsPerImage = rowsPerImage / blockHeight; + uint64_t bytesPerImage = bytesPerRow * texelBlockRowsPerImage; + uint64_t bytesInLastSlice = bytesPerRow * (copyExtent.height / blockHeight - 1) + + (copyExtent.width / blockWidth * blockSize); + return bytesPerImage * (copyExtent.depth - 1) + bytesInLastSlice; + } + } + + void UnalignDynamicUploader(wgpu::Device device) { + std::vector data = {1}; + + wgpu::TextureDescriptor descriptor = {}; + descriptor.size = {1, 1, 1}; + descriptor.format = wgpu::TextureFormat::R8Unorm; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::TextureDataLayout textureDataLayout = utils::CreateTextureDataLayout(0, 0, 0); + wgpu::Extent3D copyExtent = {1, 1, 1}; + + // WriteTexture with exactly 1 byte of data. + device.GetDefaultQueue().WriteTexture(&textureCopyView, data.data(), 1, &textureDataLayout, + ©Extent); + } +} // namespace utils \ No newline at end of file diff --git a/src/utils/TestUtils.h b/src/utils/TestUtils.h new file mode 100644 index 0000000000..d1ba25fa41 --- /dev/null +++ b/src/utils/TestUtils.h @@ -0,0 +1,57 @@ +// Copyright 2020 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. + +#ifndef UTILS_TESTHELPERS_H_ +#define UTILS_TESTHELPERS_H_ + +#include + +namespace utils { + + struct TextureDataCopyLayout { + uint64_t byteLength; + uint64_t texelBlockCount; + uint32_t bytesPerRow; + uint32_t texelBlocksPerRow; + uint32_t bytesPerImage; + uint32_t texelBlocksPerImage; + wgpu::Extent3D mipSize; + }; + + uint32_t GetMinimumBytesPerRow(wgpu::TextureFormat format, uint32_t width); + uint32_t GetBytesInBufferTextureCopy(wgpu::TextureFormat format, + uint32_t width, + uint32_t bytesPerRow, + uint32_t rowsPerImage, + uint32_t copyArrayLayerCount); + TextureDataCopyLayout GetTextureDataCopyLayoutForTexture2DAtLevel( + wgpu::TextureFormat format, + wgpu::Extent3D textureSizeAtLevel0, + uint32_t mipmapLevel, + uint32_t rowsPerImage); + + uint64_t RequiredBytesInCopy(uint64_t bytesPerRow, + uint64_t rowsPerImage, + wgpu::Extent3D copyExtent, + wgpu::TextureFormat textureFormat); + + // A helper function used for testing DynamicUploader offset alignment. + // A call of this function will do a Queue::WriteTexture with 1 byte of data, + // so that assuming that WriteTexture uses DynamicUploader, the first RingBuffer + // in it will contain 1 byte of data. + void UnalignDynamicUploader(wgpu::Device device); + +} // namespace utils + +#endif // UTILS_TESTHELPERS_H_ diff --git a/src/utils/TextureFormatUtils.h b/src/utils/TextureFormatUtils.h index b78f902309..7a7fa429bd 100644 --- a/src/utils/TextureFormatUtils.h +++ b/src/utils/TextureFormatUtils.h @@ -78,6 +78,15 @@ namespace utils { wgpu::TextureFormat::BC7RGBAUnormSrgb, }; + static constexpr std::array kBCFormats = { + wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb, + wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb, + wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb, + wgpu::TextureFormat::BC4RUnorm, wgpu::TextureFormat::BC4RSnorm, + wgpu::TextureFormat::BC5RGUnorm, wgpu::TextureFormat::BC5RGSnorm, + wgpu::TextureFormat::BC6HRGBUfloat, wgpu::TextureFormat::BC6HRGBSfloat, + wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb}; + const char* GetColorTextureComponentTypePrefix(wgpu::TextureFormat textureFormat); bool TextureFormatSupportsStorageTexture(wgpu::TextureFormat format); diff --git a/src/utils/WGPUHelpers.cpp b/src/utils/WGPUHelpers.cpp index 94f34c8259..4e2f6e8711 100644 --- a/src/utils/WGPUHelpers.cpp +++ b/src/utils/WGPUHelpers.cpp @@ -14,11 +14,8 @@ #include "utils/WGPUHelpers.h" -#include "common/Assert.h" #include "common/Constants.h" #include "common/Log.h" -#include "common/Math.h" -#include "utils/TextureFormatUtils.h" #include @@ -393,76 +390,4 @@ namespace utils { return device.CreateBindGroup(&descriptor); } - uint32_t GetMinimumBytesPerRow(wgpu::TextureFormat format, uint32_t width) { - const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); - return Align(bytesPerTexel * width, kTextureBytesPerRowAlignment); - } - - uint32_t GetBytesInBufferTextureCopy(wgpu::TextureFormat format, - uint32_t width, - uint32_t bytesPerRow, - uint32_t rowsPerImage, - uint32_t copyArrayLayerCount) { - ASSERT(rowsPerImage > 0); - const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); - const uint32_t bytesAtLastImage = bytesPerRow * (rowsPerImage - 1) + bytesPerTexel * width; - return bytesPerRow * rowsPerImage * (copyArrayLayerCount - 1) + bytesAtLastImage; - } - - // TODO(jiawei.shao@intel.com): support compressed texture formats - TextureDataCopyLayout GetTextureDataCopyLayoutForTexture2DAtLevel( - wgpu::TextureFormat format, - wgpu::Extent3D textureSizeAtLevel0, - uint32_t mipmapLevel, - uint32_t rowsPerImage) { - TextureDataCopyLayout layout; - - layout.mipSize = {textureSizeAtLevel0.width >> mipmapLevel, - textureSizeAtLevel0.height >> mipmapLevel, textureSizeAtLevel0.depth}; - - layout.bytesPerRow = GetMinimumBytesPerRow(format, layout.mipSize.width); - - uint32_t appliedRowsPerImage = rowsPerImage > 0 ? rowsPerImage : layout.mipSize.height; - layout.bytesPerImage = layout.bytesPerRow * appliedRowsPerImage; - - layout.byteLength = - GetBytesInBufferTextureCopy(format, layout.mipSize.width, layout.bytesPerRow, - appliedRowsPerImage, textureSizeAtLevel0.depth); - - const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); - layout.texelBlocksPerRow = layout.bytesPerRow / bytesPerTexel; - layout.texelBlocksPerImage = layout.bytesPerImage / bytesPerTexel; - layout.texelBlockCount = layout.byteLength / bytesPerTexel; - - return layout; - } - - const std::array kBCFormats = { - wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb, - wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb, - wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb, - wgpu::TextureFormat::BC4RUnorm, wgpu::TextureFormat::BC4RSnorm, - wgpu::TextureFormat::BC5RGUnorm, wgpu::TextureFormat::BC5RGSnorm, - wgpu::TextureFormat::BC6HRGBUfloat, wgpu::TextureFormat::BC6HRGBSfloat, - wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb}; - - uint64_t RequiredBytesInCopy(uint64_t bytesPerRow, - uint64_t rowsPerImage, - wgpu::Extent3D copyExtent, - wgpu::TextureFormat textureFormat) { - if (copyExtent.width == 0 || copyExtent.height == 0 || copyExtent.depth == 0) { - return 0; - } else { - uint32_t blockSize = utils::GetTexelBlockSizeInBytes(textureFormat); - uint32_t blockWidth = utils::GetTextureFormatBlockWidth(textureFormat); - uint32_t blockHeight = utils::GetTextureFormatBlockHeight(textureFormat); - - uint64_t texelBlockRowsPerImage = rowsPerImage / blockHeight; - uint64_t bytesPerImage = bytesPerRow * texelBlockRowsPerImage; - uint64_t bytesInLastSlice = bytesPerRow * (copyExtent.height / blockHeight - 1) + - (copyExtent.width / blockWidth * blockSize); - return bytesPerImage * (copyExtent.depth - 1) + bytesInLastSlice; - } - } - } // namespace utils diff --git a/src/utils/WGPUHelpers.h b/src/utils/WGPUHelpers.h index d0a66fdb89..b77b6a438b 100644 --- a/src/utils/WGPUHelpers.h +++ b/src/utils/WGPUHelpers.h @@ -137,35 +137,6 @@ namespace utils { const wgpu::BindGroupLayout& layout, std::initializer_list entriesInitializer); - struct TextureDataCopyLayout { - uint64_t byteLength; - uint64_t texelBlockCount; - uint32_t bytesPerRow; - uint32_t texelBlocksPerRow; - uint32_t bytesPerImage; - uint32_t texelBlocksPerImage; - wgpu::Extent3D mipSize; - }; - - uint32_t GetMinimumBytesPerRow(wgpu::TextureFormat format, uint32_t width); - uint32_t GetBytesInBufferTextureCopy(wgpu::TextureFormat format, - uint32_t width, - uint32_t bytesPerRow, - uint32_t rowsPerImage, - uint32_t copyArrayLayerCount); - TextureDataCopyLayout GetTextureDataCopyLayoutForTexture2DAtLevel( - wgpu::TextureFormat format, - wgpu::Extent3D textureSizeAtLevel0, - uint32_t mipmapLevel, - uint32_t rowsPerImage); - - extern const std::array kBCFormats; - - uint64_t RequiredBytesInCopy(uint64_t bytesPerRow, - uint64_t rowsPerImage, - wgpu::Extent3D copyExtent, - wgpu::TextureFormat textureFormat); - } // namespace utils #endif // UTILS_DAWNHELPERS_H_