Fixing offset alignments when using DynamicUploader

When using a dynamic uploader we didn't align the offset
that the allocated memory might have already had.
That fixes WriteTexture, WriteBuffer, ClearTexture and
on D3D12 ClearBuffer.

Bug: dawn:512
Change-Id: I64c7511ad6b0d3d6a28a494e1324a10ad4d38091
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/27020
Commit-Queue: Tomek Ponitka <tommek@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
This commit is contained in:
Tomek Ponitka 2020-08-20 11:25:49 +00:00 committed by Commit Bot service account
parent eff9ef0f22
commit 7f265d1d40
26 changed files with 300 additions and 122 deletions

View File

@ -64,4 +64,8 @@ static constexpr uint32_t kMaxTexture2DMipLevels = 14u;
static_assert(1 << (kMaxTexture2DMipLevels - 1) == kMaxTextureSize, static_assert(1 << (kMaxTexture2DMipLevels - 1) == kMaxTextureSize,
"kMaxTexture2DMipLevels and kMaxTextureSize size mismatch"); "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_ #endif // COMMON_CONSTANTS_H_

View File

@ -28,7 +28,8 @@ namespace dawn_native {
mDevice->GetPendingCommandSerial()); mDevice->GetPendingCommandSerial());
} }
ResultOrError<UploadHandle> DynamicUploader::Allocate(uint64_t allocationSize, Serial serial) { ResultOrError<UploadHandle> DynamicUploader::AllocateInternal(uint64_t allocationSize,
Serial serial) {
// Disable further sub-allocation should the request be too large. // Disable further sub-allocation should the request be too large.
if (allocationSize > kRingBufferSize) { if (allocationSize > kRingBufferSize) {
std::unique_ptr<StagingBufferBase> stagingBuffer; std::unique_ptr<StagingBufferBase> stagingBuffer;
@ -108,4 +109,21 @@ namespace dawn_native {
} }
mReleasedStagingBuffers.ClearUpTo(lastCompletedSerial); mReleasedStagingBuffers.ClearUpTo(lastCompletedSerial);
} }
// TODO(dawn:512): Optimize this function so that it doesn't allocate additional memory
// when it's not necessary.
ResultOrError<UploadHandle> 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<uint8_t*>(uploadHandle.mappedBuffer) + additionalOffset;
uploadHandle.startOffset += additionalOffset;
return uploadHandle;
}
} // namespace dawn_native } // namespace dawn_native

View File

@ -40,7 +40,9 @@ namespace dawn_native {
// implemented. // implemented.
void ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer); void ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer);
ResultOrError<UploadHandle> Allocate(uint64_t allocationSize, Serial serial); ResultOrError<UploadHandle> Allocate(uint64_t allocationSize,
Serial serial,
uint64_t offsetAlignment);
void Deallocate(Serial lastCompletedSerial); void Deallocate(Serial lastCompletedSerial);
private: private:
@ -51,6 +53,8 @@ namespace dawn_native {
RingBufferAllocator mAllocator; RingBufferAllocator mAllocator;
}; };
ResultOrError<UploadHandle> AllocateInternal(uint64_t allocationSize, Serial serial);
std::vector<std::unique_ptr<RingBuffer>> mRingBuffers; std::vector<std::unique_ptr<RingBuffer>> mRingBuffers;
SerialQueue<std::unique_ptr<StagingBufferBase>> mReleasedStagingBuffers; SerialQueue<std::unique_ptr<StagingBufferBase>> mReleasedStagingBuffers;
DeviceBase* mDevice; DeviceBase* mDevice;

View File

@ -14,6 +14,7 @@
#include "dawn_native/Queue.h" #include "dawn_native/Queue.h"
#include "common/Constants.h"
#include "dawn_native/Buffer.h" #include "dawn_native/Buffer.h"
#include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandBuffer.h"
#include "dawn_native/CommandValidation.h" #include "dawn_native/CommandValidation.h"
@ -110,7 +111,8 @@ namespace dawn_native {
UploadHandle uploadHandle; UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate(
size, device->GetPendingCommandSerial())); size, device->GetPendingCommandSerial(),
kCopyBufferToBufferOffsetAlignment));
ASSERT(uploadHandle.mappedBuffer != nullptr); ASSERT(uploadHandle.mappedBuffer != nullptr);
memcpy(uploadHandle.mappedBuffer, data, size); memcpy(uploadHandle.mappedBuffer, data, size);

View File

@ -425,7 +425,8 @@ namespace dawn_native { namespace d3d12 {
DynamicUploader* uploader = device->GetDynamicUploader(); DynamicUploader* uploader = device->GetDynamicUploader();
UploadHandle uploadHandle; UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle, DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(GetSize(), device->GetPendingCommandSerial())); uploader->Allocate(GetSize(), device->GetPendingCommandSerial(),
kCopyBufferToBufferOffsetAlignment));
memset(uploadHandle.mappedBuffer, clearValue, GetSize()); memset(uploadHandle.mappedBuffer, clearValue, GetSize());

View File

@ -44,7 +44,8 @@ namespace dawn_native { namespace d3d12 {
UploadHandle uploadHandle; UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate(
newDataSizeBytes, device->GetPendingCommandSerial())); newDataSizeBytes, device->GetPendingCommandSerial(),
textureFormat.blockByteSize));
ASSERT(uploadHandle.mappedBuffer != nullptr); ASSERT(uploadHandle.mappedBuffer != nullptr);
uint8_t* dstPointer = static_cast<uint8_t*>(uploadHandle.mappedBuffer); uint8_t* dstPointer = static_cast<uint8_t*>(uploadHandle.mappedBuffer);

View File

@ -951,7 +951,8 @@ namespace dawn_native { namespace d3d12 {
DynamicUploader* uploader = device->GetDynamicUploader(); DynamicUploader* uploader = device->GetDynamicUploader();
UploadHandle uploadHandle; UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle, DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); uploader->Allocate(bufferSize, device->GetPendingCommandSerial(),
GetFormat().blockByteSize));
memset(uploadHandle.mappedBuffer, clearColor, bufferSize); memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
for (uint32_t level = range.baseMipLevel; for (uint32_t level = range.baseMipLevel;

View File

@ -14,6 +14,7 @@
#include "dawn_native/metal/DeviceMTL.h" #include "dawn_native/metal/DeviceMTL.h"
#include "common/Platform.h"
#include "dawn_native/BackendConnection.h" #include "dawn_native/BackendConnection.h"
#include "dawn_native/BindGroupLayout.h" #include "dawn_native/BindGroupLayout.h"
#include "dawn_native/Commands.h" #include "dawn_native/Commands.h"
@ -75,8 +76,10 @@ namespace dawn_native { namespace metal {
{ {
bool haveStoreAndMSAAResolve = false; bool haveStoreAndMSAAResolve = false;
#if defined(DAWN_PLATFORM_MACOS) #if defined(DAWN_PLATFORM_MACOS)
haveStoreAndMSAAResolve = if (@available(macOS 10.12, *)) {
[mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2]; haveStoreAndMSAAResolve =
[mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2];
}
#elif defined(DAWN_PLATFORM_IOS) #elif defined(DAWN_PLATFORM_IOS)
haveStoreAndMSAAResolve = haveStoreAndMSAAResolve =
[mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]; [mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2];

View File

@ -41,7 +41,8 @@ namespace dawn_native { namespace metal {
UploadHandle uploadHandle; UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate(
newDataSizeBytes, device->GetPendingCommandSerial())); newDataSizeBytes, device->GetPendingCommandSerial(),
blockInfo.blockByteSize));
ASSERT(uploadHandle.mappedBuffer != nullptr); ASSERT(uploadHandle.mappedBuffer != nullptr);
uint8_t* dstPointer = static_cast<uint8_t*>(uploadHandle.mappedBuffer); uint8_t* dstPointer = static_cast<uint8_t*>(uploadHandle.mappedBuffer);

View File

@ -510,7 +510,8 @@ namespace dawn_native { namespace metal {
DynamicUploader* uploader = device->GetDynamicUploader(); DynamicUploader* uploader = device->GetDynamicUploader();
UploadHandle uploadHandle; UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle, DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); uploader->Allocate(bufferSize, device->GetPendingCommandSerial(),
GetFormat().blockByteSize));
memset(uploadHandle.mappedBuffer, clearColor, bufferSize); memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
id<MTLBlitCommandEncoder> encoder = commandContext->EnsureBlit(); id<MTLBlitCommandEncoder> encoder = commandContext->EnsureBlit();

View File

@ -47,11 +47,17 @@ namespace dawn_native { namespace vulkan {
ToBackend(device) ToBackend(device)
->GetDeviceInfo() ->GetDeviceInfo()
.properties.limits.optimalBufferCopyOffsetAlignment; .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; UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate(
newDataSizeBytes + optimalOffsetAlignment - 1, newDataSizeBytes, device->GetPendingCommandSerial(),
device->GetPendingCommandSerial())); offsetAlignment));
ASSERT(uploadHandle.mappedBuffer != nullptr); ASSERT(uploadHandle.mappedBuffer != nullptr);
uint8_t* dstPointer = static_cast<uint8_t*>(uploadHandle.mappedBuffer); uint8_t* dstPointer = static_cast<uint8_t*>(uploadHandle.mappedBuffer);
@ -64,11 +70,6 @@ namespace dawn_native { namespace vulkan {
dataRowsPerImageInBlock = writeSizePixel.height / blockInfo.blockHeight; dataRowsPerImageInBlock = writeSizePixel.height / blockInfo.blockHeight;
} }
uint64_t additionalOffset =
Align(uploadHandle.startOffset, optimalOffsetAlignment) - uploadHandle.startOffset;
uploadHandle.startOffset += additionalOffset;
dstPointer += additionalOffset;
ASSERT(dataRowsPerImageInBlock >= alignedRowsPerImageInBlock); ASSERT(dataRowsPerImageInBlock >= alignedRowsPerImageInBlock);
uint64_t imageAdditionalStride = uint64_t imageAdditionalStride =
dataLayout.bytesPerRow * (dataRowsPerImageInBlock - alignedRowsPerImageInBlock); dataLayout.bytesPerRow * (dataRowsPerImageInBlock - alignedRowsPerImageInBlock);

View File

@ -951,7 +951,8 @@ namespace dawn_native { namespace vulkan {
DynamicUploader* uploader = device->GetDynamicUploader(); DynamicUploader* uploader = device->GetDynamicUploader();
UploadHandle uploadHandle; UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle, DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); uploader->Allocate(bufferSize, device->GetPendingCommandSerial(),
GetFormat().blockByteSize));
memset(uploadHandle.mappedBuffer, clearColor, bufferSize); memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
// compute the buffer image copy to set the clear region of entire texture // compute the buffer image copy to set the clear region of entire texture

View File

@ -15,6 +15,7 @@
#include "tests/DawnTest.h" #include "tests/DawnTest.h"
#include "utils/ComboRenderPipelineDescriptor.h" #include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/TestUtils.h"
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"
#define EXPECT_LAZY_CLEAR(N, statement) \ #define EXPECT_LAZY_CLEAR(N, statement) \

View File

@ -18,6 +18,7 @@
#include "common/Constants.h" #include "common/Constants.h"
#include "common/Math.h" #include "common/Math.h"
#include "utils/ComboRenderPipelineDescriptor.h" #include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/TestUtils.h"
#include "utils/TextureFormatUtils.h" #include "utils/TextureFormatUtils.h"
#include "utils/WGPUHelpers.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, &copyExtent);
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
// TODO(jiawei.shao@intel.com): support BC formats on OpenGL backend // TODO(jiawei.shao@intel.com): support BC formats on OpenGL backend
DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest,
D3D12Backend(), D3D12Backend(),

View File

@ -17,6 +17,7 @@
#include <array> #include <array>
#include "common/Constants.h" #include "common/Constants.h"
#include "common/Math.h" #include "common/Math.h"
#include "utils/TestUtils.h"
#include "utils/TextureFormatUtils.h" #include "utils/TextureFormatUtils.h"
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"

View File

@ -20,6 +20,7 @@
#include "tests/DawnTest.h" #include "tests/DawnTest.h"
#include "common/Math.h" #include "common/Math.h"
#include "utils/TestUtils.h"
#include "utils/TextureFormatUtils.h" #include "utils/TextureFormatUtils.h"
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"
@ -170,6 +171,22 @@ TEST_P(QueueWriteBufferTests, SuperLargeWriteBuffer) {
EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); 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, DAWN_INSTANTIATE_TEST(QueueWriteBufferTests,
D3D12Backend(), D3D12Backend(),
MetalBackend(), 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()); DAWN_INSTANTIATE_TEST(QueueWriteTextureTests, D3D12Backend(), MetalBackend(), VulkanBackend());

View File

@ -16,6 +16,7 @@
#include "common/Math.h" #include "common/Math.h"
#include "utils/ComboRenderPipelineDescriptor.h" #include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/TestUtils.h"
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"
#define EXPECT_LAZY_CLEAR(N, statement) \ #define EXPECT_LAZY_CLEAR(N, statement) \

View File

@ -16,6 +16,7 @@
#include "common/Constants.h" #include "common/Constants.h"
#include "common/Math.h" #include "common/Math.h"
#include "tests/unittests/validation/ValidationTest.h" #include "tests/unittests/validation/ValidationTest.h"
#include "utils/TestUtils.h"
#include "utils/TextureFormatUtils.h" #include "utils/TextureFormatUtils.h"
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"

View File

@ -15,6 +15,7 @@
#include "tests/unittests/validation/ValidationTest.h" #include "tests/unittests/validation/ValidationTest.h"
#include "common/Math.h" #include "common/Math.h"
#include "utils/TestUtils.h"
#include "utils/TextureFormatUtils.h" #include "utils/TextureFormatUtils.h"
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"

View File

@ -75,6 +75,8 @@ static_library("dawn_utils") {
"SystemUtils.h", "SystemUtils.h",
"TerribleCommandBuffer.cpp", "TerribleCommandBuffer.cpp",
"TerribleCommandBuffer.h", "TerribleCommandBuffer.h",
"TestUtils.cpp",
"TestUtils.h",
"TextureFormatUtils.cpp", "TextureFormatUtils.cpp",
"TextureFormatUtils.h", "TextureFormatUtils.h",
"Timer.h", "Timer.h",

View File

@ -27,6 +27,8 @@ target_sources(dawn_utils PRIVATE
"SystemUtils.h" "SystemUtils.h"
"TerribleCommandBuffer.cpp" "TerribleCommandBuffer.cpp"
"TerribleCommandBuffer.h" "TerribleCommandBuffer.h"
"TestUtils.cpp"
"TestUtils.h"
"TextureFormatUtils.cpp" "TextureFormatUtils.cpp"
"TextureFormatUtils.h" "TextureFormatUtils.h"
"Timer.h" "Timer.h"

107
src/utils/TestUtils.cpp Normal file
View File

@ -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 <vector>
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<uint8_t> 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,
&copyExtent);
}
} // namespace utils

57
src/utils/TestUtils.h Normal file
View File

@ -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 <dawn/webgpu_cpp.h>
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_

View File

@ -78,6 +78,15 @@ namespace utils {
wgpu::TextureFormat::BC7RGBAUnormSrgb, wgpu::TextureFormat::BC7RGBAUnormSrgb,
}; };
static constexpr std::array<wgpu::TextureFormat, 14> 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); const char* GetColorTextureComponentTypePrefix(wgpu::TextureFormat textureFormat);
bool TextureFormatSupportsStorageTexture(wgpu::TextureFormat format); bool TextureFormatSupportsStorageTexture(wgpu::TextureFormat format);

View File

@ -14,11 +14,8 @@
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"
#include "common/Assert.h"
#include "common/Constants.h" #include "common/Constants.h"
#include "common/Log.h" #include "common/Log.h"
#include "common/Math.h"
#include "utils/TextureFormatUtils.h"
#include <shaderc/shaderc.hpp> #include <shaderc/shaderc.hpp>
@ -393,76 +390,4 @@ namespace utils {
return device.CreateBindGroup(&descriptor); 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<wgpu::TextureFormat, 14> 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 } // namespace utils

View File

@ -137,35 +137,6 @@ namespace utils {
const wgpu::BindGroupLayout& layout, const wgpu::BindGroupLayout& layout,
std::initializer_list<BindingInitializationHelper> entriesInitializer); std::initializer_list<BindingInitializationHelper> 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<wgpu::TextureFormat, 14> kBCFormats;
uint64_t RequiredBytesInCopy(uint64_t bytesPerRow,
uint64_t rowsPerImage,
wgpu::Extent3D copyExtent,
wgpu::TextureFormat textureFormat);
} // namespace utils } // namespace utils
#endif // UTILS_DAWNHELPERS_H_ #endif // UTILS_DAWNHELPERS_H_