| // Copyright 2020 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. | |
| #ifdef UNSAFE_BUFFERS_BUILD | |
| // TODO(crbug.com/40285824): Remove this and convert code to safer constructs. | |
| #pragma allow_unsafe_buffers | |
| #endif | |
| #include <vector> | |
| #include "src/dawn/common/Math.h" | |
| #include "src/dawn/tests/DawnTest.h" | |
| #include "src/dawn/utils/TestUtils.h" | |
| #include "src/dawn/utils/TextureUtils.h" | |
| #include "src/dawn/utils/WGPUHelpers.h" | |
| namespace dawn { | |
| namespace { | |
| class QueueTests : public DawnTest {}; | |
| // Test that GetQueue always returns the same object. | |
| TEST_P(QueueTests, GetQueueSameObject) { | |
| wgpu::Queue q1 = device.GetQueue(); | |
| wgpu::Queue q2 = device.GetQueue(); | |
| EXPECT_EQ(q1.Get(), q2.Get()); | |
| } | |
| DAWN_INSTANTIATE_TEST(QueueTests, | |
| D3D11Backend(), | |
| D3D11Backend({"d3d11_delay_flush_to_gpu"}), | |
| D3D12Backend(), | |
| MetalBackend(), | |
| NullBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| VulkanBackend(), | |
| WebGPUBackend()); | |
| class QueueWriteBufferTests : public DawnTest {}; | |
| // Test the simplest WriteBuffer setting one u32 at offset 0. | |
| TEST_P(QueueWriteBufferTests, SmallDataAtZero) { | |
| 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); | |
| } | |
| // Test an empty WriteBuffer | |
| TEST_P(QueueWriteBufferTests, ZeroSized) { | |
| wgpu::BufferDescriptor descriptor; | |
| descriptor.size = 4; | |
| descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| wgpu::Buffer buffer = device.CreateBuffer(&descriptor); | |
| uint32_t initialValue = 0x42; | |
| queue.WriteBuffer(buffer, 0, &initialValue, sizeof(initialValue)); | |
| queue.WriteBuffer(buffer, 0, nullptr, 0); | |
| // The content of the buffer isn't changed | |
| EXPECT_BUFFER_U32_EQ(initialValue, buffer, 0); | |
| } | |
| // Call WriteBuffer at offset 0 via a u32 twice. Test that data is updated accoordingly. | |
| TEST_P(QueueWriteBufferTests, SetTwice) { | |
| 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); | |
| value = 0x05060708; | |
| queue.WriteBuffer(buffer, 0, &value, sizeof(value)); | |
| EXPECT_BUFFER_U32_EQ(value, buffer, 0); | |
| } | |
| // Test that WriteBuffer offset works. | |
| TEST_P(QueueWriteBufferTests, SmallDataAtOffset) { | |
| wgpu::BufferDescriptor descriptor; | |
| descriptor.size = 4000; | |
| descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| wgpu::Buffer buffer = device.CreateBuffer(&descriptor); | |
| constexpr uint64_t kOffset = 2000; | |
| uint32_t value = 0x01020304; | |
| queue.WriteBuffer(buffer, kOffset, &value, sizeof(value)); | |
| EXPECT_BUFFER_U32_EQ(value, buffer, kOffset); | |
| } | |
| // Stress test for many calls to WriteBuffer | |
| TEST_P(QueueWriteBufferTests, ManyWriteBuffer) { | |
| // Note: Increasing the size of the buffer will likely cause timeout issues. | |
| // In D3D12, timeout detection occurs when the GPU scheduler tries but cannot preempt the task | |
| // executing these commands in-flight. If this takes longer than ~2s, a device reset occurs and | |
| // fails the test. Since GPUs may or may not complete by then, this test must be disabled OR | |
| // modified to be well-below the timeout limit. | |
| // TODO(crbug.com/dawn/228): Re-enable once the issue with Metal on 10.14.6 is fixed. | |
| DAWN_SUPPRESS_TEST_IF(IsMacOS() && IsIntel() && IsMetal()); | |
| // The Vulkan Validation Layers' memory barrier validation keeps track of every range written | |
| // to independently which causes validation of each WriteBuffer to take increasing time, and | |
| // this test to take forever. Skip it when VVLs are enabled. | |
| DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsBackendValidationEnabled()); | |
| constexpr uint64_t kSize = 4000ULL * 1000; | |
| constexpr uint32_t kElements = 250 * 250; | |
| wgpu::BufferDescriptor descriptor; | |
| descriptor.size = kSize; | |
| descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| wgpu::Buffer buffer = device.CreateBuffer(&descriptor); | |
| std::vector<uint32_t> expectedData; | |
| for (uint32_t i = 0; i < kElements; ++i) { | |
| queue.WriteBuffer(buffer, i * sizeof(uint32_t), &i, sizeof(i)); | |
| expectedData.push_back(i); | |
| } | |
| EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); | |
| } | |
| // Test using WriteBuffer for lots of data | |
| TEST_P(QueueWriteBufferTests, LargeWriteBuffer) { | |
| constexpr uint64_t kSize = 4000ULL * 1000; | |
| constexpr uint32_t kElements = 1000 * 1000; | |
| wgpu::BufferDescriptor descriptor; | |
| descriptor.size = kSize; | |
| descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| wgpu::Buffer buffer = device.CreateBuffer(&descriptor); | |
| std::vector<uint32_t> expectedData; | |
| for (uint32_t i = 0; i < kElements; ++i) { | |
| expectedData.push_back(i); | |
| } | |
| queue.WriteBuffer(buffer, 0, expectedData.data(), kElements * sizeof(uint32_t)); | |
| EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); | |
| } | |
| // Test using WriteBuffer for super large data block | |
| TEST_P(QueueWriteBufferTests, SuperLargeWriteBuffer) { | |
| constexpr uint64_t kSize = 12000ULL * 1000; | |
| constexpr uint64_t kElements = 3000ULL * 1000; | |
| wgpu::BufferDescriptor descriptor; | |
| descriptor.size = kSize; | |
| descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| wgpu::Buffer buffer = device.CreateBuffer(&descriptor); | |
| std::vector<uint32_t> expectedData; | |
| for (uint32_t i = 0; i < kElements; ++i) { | |
| expectedData.push_back(i); | |
| } | |
| queue.WriteBuffer(buffer, 0, expectedData.data(), kElements * sizeof(uint32_t)); | |
| EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); | |
| } | |
| // Test using the max buffer size. Regression test for dawn:1985. We don't bother validating the | |
| // results for this case since that would take a lot longer, just that there are no errors. | |
| TEST_P(QueueWriteBufferTests, MaxBufferSizeWriteBuffer) { | |
| uint32_t maxBufferSize = GetSupportedLimits().maxBufferSize; | |
| wgpu::BufferDescriptor descriptor; | |
| descriptor.size = maxBufferSize; | |
| descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| wgpu::Buffer buffer = device.CreateBuffer(&descriptor); | |
| std::vector<uint8_t> data(maxBufferSize); | |
| queue.WriteBuffer(buffer, 0, data.data(), maxBufferSize); | |
| } | |
| // 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); | |
| } | |
| // Test using various offset and size alignments to write a uniform buffer. | |
| TEST_P(QueueWriteBufferTests, WriteUniformBufferWithVariousOffsetAndSizeAlignments) { | |
| wgpu::BufferDescriptor descriptor; | |
| descriptor.size = 128; | |
| descriptor.usage = | |
| wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform; | |
| wgpu::Buffer buffer = device.CreateBuffer(&descriptor); | |
| constexpr size_t kElementCount = 16; | |
| uint32_t data[kElementCount] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; | |
| constexpr size_t kElementBytes = sizeof(data[0]); | |
| queue.WriteBuffer(buffer, 0, data, sizeof(data)); | |
| EXPECT_BUFFER_U32_RANGE_EQ(data, buffer, 0, kElementCount); | |
| // Alignments: offset -- 4, size -- 4 | |
| size_t offset = 1; | |
| data[offset] = 100; | |
| size_t size = kElementBytes; | |
| queue.WriteBuffer(buffer, offset * kElementBytes, &data[offset], size); | |
| EXPECT_BUFFER_U32_RANGE_EQ(data, buffer, 0, kElementCount); | |
| // Alignments: offset -- 16, size -- 16 | |
| offset = 4; | |
| data[offset] = 101; | |
| data[offset + 1] = 102; | |
| data[offset + 2] = 103; | |
| data[offset + 3] = 104; | |
| size = 4 * kElementBytes; | |
| queue.WriteBuffer(buffer, offset * kElementBytes, &data[offset], size); | |
| EXPECT_BUFFER_U32_RANGE_EQ(data, buffer, 0, kElementCount); | |
| // Alignments: offset -- 4, size -- 16 | |
| offset = 10; | |
| data[offset] = 105; | |
| data[offset + 1] = 106; | |
| data[offset + 2] = 107; | |
| data[offset + 3] = 108; | |
| queue.WriteBuffer(buffer, offset * kElementBytes, &data[offset], size); | |
| EXPECT_BUFFER_U32_RANGE_EQ(data, buffer, 0, kElementCount); | |
| // Alignments: offset -- 16, size -- 4 | |
| offset = 12; | |
| data[offset] = 109; | |
| size = kElementBytes; | |
| queue.WriteBuffer(buffer, offset * kElementBytes, &data[offset], size); | |
| EXPECT_BUFFER_U32_RANGE_EQ(data, buffer, 0, kElementCount); | |
| } | |
| DAWN_INSTANTIATE_TEST(QueueWriteBufferTests, | |
| D3D11Backend(), | |
| D3D11Backend({"d3d11_delay_flush_to_gpu"}), | |
| D3D12Backend(), | |
| MetalBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| VulkanBackend(), | |
| WebGPUBackend()); | |
| // For MinimumDataSpec bytesPerRow and rowsPerImage, compute a default from the copy extent. | |
| constexpr uint32_t kStrideComputeDefault = 0xFFFF'FFFEul; | |
| namespace { | |
| using TextureFormat = wgpu::TextureFormat; | |
| DAWN_TEST_PARAM_STRUCT(WriteTextureFormatParams, TextureFormat); | |
| struct TextureSpec { | |
| wgpu::Origin3D copyOrigin; | |
| wgpu::Extent3D textureSize; | |
| uint32_t level; | |
| }; | |
| struct DataSpec { | |
| uint64_t size; | |
| uint64_t offset; | |
| uint32_t bytesPerRow; | |
| uint32_t rowsPerImage; | |
| }; | |
| void PackTextureData(const uint8_t* srcData, | |
| uint32_t width, | |
| uint32_t height, | |
| uint32_t srcBytesPerRow, | |
| uint8_t* dstData, | |
| uint32_t dstBytesPerRow, | |
| uint32_t bytesPerTexel) { | |
| for (uint64_t y = 0; y < height; ++y) { | |
| for (uint64_t x = 0; x < width; ++x) { | |
| uint64_t src = x * bytesPerTexel + y * srcBytesPerRow; | |
| uint64_t dst = x * bytesPerTexel + y * dstBytesPerRow; | |
| for (uint64_t i = 0; i < bytesPerTexel; i++) { | |
| dstData[dst + i] = srcData[src + i]; | |
| } | |
| } | |
| } | |
| } | |
| void FillData(uint8_t* data, size_t count) { | |
| for (size_t i = 0; i < count; ++i) { | |
| data[i] = static_cast<uint8_t>(i % 253); | |
| } | |
| } | |
| } // namespace | |
| class QueueWriteTextureTests : public DawnTestWithParams<WriteTextureFormatParams> { | |
| protected: | |
| static DataSpec MinimumDataSpec(wgpu::Extent3D writeSize, | |
| uint32_t overrideBytesPerRow = kStrideComputeDefault, | |
| uint32_t overrideRowsPerImage = kStrideComputeDefault) { | |
| uint32_t bytesPerRow = | |
| writeSize.width * utils::GetTexelBlockSizeInBytes(GetParam().mTextureFormat); | |
| if (overrideBytesPerRow != kStrideComputeDefault) { | |
| bytesPerRow = overrideBytesPerRow; | |
| } | |
| uint32_t rowsPerImage = writeSize.height; | |
| if (overrideRowsPerImage != kStrideComputeDefault) { | |
| rowsPerImage = overrideRowsPerImage; | |
| } | |
| uint32_t totalDataSize = utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, writeSize, | |
| GetParam().mTextureFormat); | |
| return {totalDataSize, 0, bytesPerRow, rowsPerImage}; | |
| } | |
| void DoTest(const TextureSpec& textureSpec, | |
| const DataSpec& dataSpec, | |
| const wgpu::Extent3D& copySize, | |
| const wgpu::TextureViewDimension bindingViewDimension = | |
| wgpu::TextureViewDimension::Undefined) { | |
| // Create data of size `size` and populate it | |
| std::vector<uint8_t> data(dataSpec.size); | |
| FillData(data.data(), data.size()); | |
| // Create a texture that is `width` x `height` with (`level` + 1) mip levels. | |
| wgpu::TextureDescriptor descriptor = {}; | |
| wgpu::TextureBindingViewDimension textureBindingViewDimensionDesc; | |
| if (IsCompatibilityMode() && | |
| bindingViewDimension != wgpu::TextureViewDimension::Undefined) { | |
| textureBindingViewDimensionDesc.textureBindingViewDimension = bindingViewDimension; | |
| descriptor.nextInChain = &textureBindingViewDimensionDesc; | |
| } | |
| descriptor.dimension = wgpu::TextureDimension::e2D; | |
| descriptor.size = textureSpec.textureSize; | |
| descriptor.format = GetParam().mTextureFormat; | |
| descriptor.mipLevelCount = textureSpec.level + 1; | |
| descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; | |
| wgpu::Texture texture = device.CreateTexture(&descriptor); | |
| wgpu::TexelCopyBufferLayout texelCopyBufferLayout = utils::CreateTexelCopyBufferLayout( | |
| dataSpec.offset, dataSpec.bytesPerRow, dataSpec.rowsPerImage); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo = | |
| utils::CreateTexelCopyTextureInfo(texture, textureSpec.level, textureSpec.copyOrigin); | |
| queue.WriteTexture(&texelCopyTextureInfo, data.data(), dataSpec.size, | |
| &texelCopyBufferLayout, ©Size); | |
| const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(GetParam().mTextureFormat); | |
| wgpu::Extent3D mipSize = {textureSpec.textureSize.width >> textureSpec.level, | |
| textureSpec.textureSize.height >> textureSpec.level, | |
| textureSpec.textureSize.depthOrArrayLayers}; | |
| uint32_t bytesPerRow = dataSpec.bytesPerRow; | |
| if (bytesPerRow == wgpu::kCopyStrideUndefined) { | |
| bytesPerRow = mipSize.width * bytesPerTexel; | |
| } | |
| uint32_t alignedBytesPerRow = Align(bytesPerRow, bytesPerTexel); | |
| uint32_t appliedRowsPerImage = | |
| dataSpec.rowsPerImage > 0 ? dataSpec.rowsPerImage : mipSize.height; | |
| uint32_t bytesPerImage = bytesPerRow * appliedRowsPerImage; | |
| const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depthOrArrayLayers; | |
| uint64_t dataOffset = dataSpec.offset; | |
| const uint32_t byteSizeLastLayer = | |
| alignedBytesPerRow * (mipSize.height - 1) + mipSize.width * bytesPerTexel; | |
| for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) { | |
| // Pack the data in the specified copy region to have the same | |
| // format as the expected texture data. | |
| std::vector<uint8_t> expected(byteSizeLastLayer, 0); | |
| PackTextureData(data.data() + dataOffset, copySize.width, copySize.height, | |
| dataSpec.bytesPerRow, expected.data(), copySize.width * bytesPerTexel, | |
| bytesPerTexel); | |
| EXPECT_TEXTURE_EQ(expected.data(), texture, | |
| {textureSpec.copyOrigin.x, textureSpec.copyOrigin.y, slice}, | |
| {copySize.width, copySize.height, 1}, descriptor.format, | |
| static_cast<uint8_t>(0), textureSpec.level) | |
| << "Write to texture failed copying " << dataSpec.size << "-byte data with offset " | |
| << dataSpec.offset << " and bytes per row " << dataSpec.bytesPerRow << " to [(" | |
| << textureSpec.copyOrigin.x << ", " << textureSpec.copyOrigin.y << "), (" | |
| << textureSpec.copyOrigin.x + copySize.width << ", " | |
| << textureSpec.copyOrigin.y + copySize.height << ")) region of " | |
| << textureSpec.textureSize.width << " x " << textureSpec.textureSize.height | |
| << " texture at mip level " << textureSpec.level << " layer " << slice << "\n"; | |
| dataOffset += bytesPerImage; | |
| } | |
| } | |
| }; | |
| // Test writing the whole texture for varying texture sizes. | |
| TEST_P(QueueWriteTextureTests, VaryingTextureSize) { | |
| for (unsigned int w : {127, 128}) { | |
| for (unsigned int h : {63, 64}) { | |
| for (unsigned int d : {1, 3, 4}) { | |
| TextureSpec textureSpec; | |
| textureSpec.textureSize = {w, h, d}; | |
| textureSpec.copyOrigin = {0, 0, 0}; | |
| textureSpec.level = 0; | |
| DoTest(textureSpec, MinimumDataSpec({w, h, d}), {w, h, d}); | |
| } | |
| } | |
| } | |
| } | |
| // Test uploading a large amount of data with writeTexture. | |
| TEST_P(QueueWriteTextureTests, LargeWriteTexture) { | |
| // TODO(crbug.com/473870505): [Capture] support depth/stencil and multi-planar textures. | |
| DAWN_SUPPRESS_TEST_IF(IsCaptureReplayCheckingEnabled() && | |
| utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)); | |
| TextureSpec textureSpec; | |
| textureSpec.textureSize = {2048, 2048, 2}; | |
| textureSpec.copyOrigin = {0, 0, 0}; | |
| textureSpec.level = 0; | |
| DoTest(textureSpec, MinimumDataSpec(textureSpec.textureSize), textureSpec.textureSize); | |
| } | |
| // Test writing a pixel with an offset. | |
| TEST_P(QueueWriteTextureTests, VaryingTextureOffset) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| DAWN_TEST_UNSUPPORTED_IF(utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)); | |
| constexpr uint32_t kWidth = 259; | |
| constexpr uint32_t kHeight = 127; | |
| DataSpec pixelData = MinimumDataSpec({1, 1, 1}); | |
| constexpr wgpu::Extent3D kCopySize = {1, 1, 1}; | |
| constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1}; | |
| TextureSpec defaultTextureSpec; | |
| defaultTextureSpec.textureSize = kTextureSize; | |
| defaultTextureSpec.level = 0; | |
| for (unsigned int w : {0u, kWidth / 7, kWidth / 3, kWidth - 1}) { | |
| for (unsigned int h : {0u, kHeight / 7, kHeight / 3, kHeight - 1}) { | |
| TextureSpec textureSpec = defaultTextureSpec; | |
| textureSpec.copyOrigin = {w, h, 0}; | |
| DoTest(textureSpec, pixelData, kCopySize); | |
| } | |
| } | |
| } | |
| // Test writing a pixel with an offset to a texture array | |
| TEST_P(QueueWriteTextureTests, VaryingTextureArrayOffset) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| DAWN_TEST_UNSUPPORTED_IF(utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)); | |
| // TODO(crbug.com/dawn/2095): Failing on ANGLE + SwiftShader, needs investigation. | |
| DAWN_SUPPRESS_TEST_IF(IsANGLESwiftShader()); | |
| constexpr uint32_t kWidth = 259; | |
| constexpr uint32_t kHeight = 127; | |
| constexpr uint32_t kDepth = 62; | |
| DataSpec pixelData = MinimumDataSpec({1, 1, 1}); | |
| constexpr wgpu::Extent3D kCopySize = {1, 1, 1}; | |
| constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, kDepth}; | |
| TextureSpec defaultTextureSpec; | |
| defaultTextureSpec.textureSize = kTextureSize; | |
| defaultTextureSpec.level = 0; | |
| for (unsigned int w : {0u, kWidth / 7, kWidth / 3, kWidth - 1}) { | |
| for (unsigned int h : {0u, kHeight / 7, kHeight / 3, kHeight - 1}) { | |
| for (unsigned int d : {0u, kDepth / 7, kDepth / 3, kDepth - 1}) { | |
| TextureSpec textureSpec = defaultTextureSpec; | |
| textureSpec.copyOrigin = {w, h, d}; | |
| DoTest(textureSpec, pixelData, kCopySize); | |
| } | |
| } | |
| } | |
| } | |
| // Test writing with varying write sizes. | |
| TEST_P(QueueWriteTextureTests, VaryingWriteSize) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| DAWN_TEST_UNSUPPORTED_IF(utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)); | |
| constexpr uint32_t kWidth = 257; | |
| constexpr uint32_t kHeight = 127; | |
| for (unsigned int w : {13, 63, 128, 256}) { | |
| for (unsigned int h : {16, 19, 32, 63}) { | |
| TextureSpec textureSpec; | |
| textureSpec.copyOrigin = {0, 0, 0}; | |
| textureSpec.level = 0; | |
| textureSpec.textureSize = {kWidth, kHeight, 1}; | |
| DoTest(textureSpec, MinimumDataSpec({w, h, 1}), {w, h, 1}); | |
| } | |
| } | |
| } | |
| // Test writing with varying write sizes to texture arrays. | |
| TEST_P(QueueWriteTextureTests, VaryingArrayWriteSize) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| DAWN_TEST_UNSUPPORTED_IF(utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)); | |
| constexpr uint32_t kWidth = 257; | |
| constexpr uint32_t kHeight = 127; | |
| constexpr uint32_t kDepth = 65; | |
| for (unsigned int w : {13, 63, 128, 256}) { | |
| for (unsigned int h : {16, 19, 32, 63}) { | |
| for (unsigned int d : {3, 6}) { | |
| TextureSpec textureSpec; | |
| textureSpec.copyOrigin = {0, 0, 0}; | |
| textureSpec.level = 0; | |
| textureSpec.textureSize = {kWidth, kHeight, kDepth}; | |
| DoTest(textureSpec, MinimumDataSpec({w, h, d}), {w, h, d}); | |
| } | |
| } | |
| } | |
| } | |
| // Test writing to varying mips | |
| TEST_P(QueueWriteTextureTests, TextureWriteToMip) { | |
| constexpr uint32_t kWidth = 259; | |
| constexpr uint32_t kHeight = 127; | |
| TextureSpec defaultTextureSpec; | |
| defaultTextureSpec.copyOrigin = {0, 0, 0}; | |
| defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; | |
| for (unsigned int i = 1; i < 4; ++i) { | |
| TextureSpec textureSpec = defaultTextureSpec; | |
| textureSpec.level = i; | |
| DoTest(textureSpec, MinimumDataSpec({kWidth >> i, kHeight >> i, 1}), | |
| {kWidth >> i, kHeight >> i, 1}); | |
| } | |
| } | |
| // Test writing with different multiples of texel block size as data offset | |
| TEST_P(QueueWriteTextureTests, VaryingDataOffset) { | |
| constexpr uint32_t kWidth = 259; | |
| constexpr uint32_t kHeight = 127; | |
| TextureSpec textureSpec; | |
| textureSpec.copyOrigin = {0, 0, 0}; | |
| textureSpec.textureSize = {kWidth, kHeight, 1}; | |
| textureSpec.level = 0; | |
| for (uint64_t offset : {1, 2, 4, 17, 64, 128, 300}) { | |
| DataSpec dataSpec = MinimumDataSpec({kWidth, kHeight, 1}); | |
| dataSpec.size += offset; | |
| dataSpec.offset += offset; | |
| DoTest(textureSpec, dataSpec, {kWidth, kHeight, 1}); | |
| } | |
| } | |
| // Test writing with rowsPerImage greater than needed. | |
| TEST_P(QueueWriteTextureTests, VaryingRowsPerImage) { | |
| constexpr uint32_t kWidth = 65; | |
| constexpr uint32_t kHeight = 31; | |
| constexpr uint32_t kDepth = 17; | |
| TextureSpec textureSpec; | |
| textureSpec.textureSize = {kWidth, kHeight, kDepth}; | |
| textureSpec.level = 0; | |
| auto TestBody = [&](wgpu::Origin3D copyOrigin, wgpu::Extent3D copySize) { | |
| textureSpec.copyOrigin = copyOrigin; | |
| for (unsigned int r : {1, 2, 3, 64, 200}) { | |
| DataSpec dataSpec = | |
| MinimumDataSpec(copySize, kStrideComputeDefault, copySize.height + r); | |
| DoTest(textureSpec, dataSpec, copySize); | |
| } | |
| }; | |
| TestBody({0, 0, 0}, textureSpec.textureSize); | |
| if (utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| return; | |
| } | |
| TestBody({1, 1, 1}, {kWidth - 1, kHeight - 1, kDepth - 1}); | |
| } | |
| // Test with bytesPerRow greater than needed | |
| TEST_P(QueueWriteTextureTests, VaryingBytesPerRow) { | |
| constexpr uint32_t kWidth = 257; | |
| constexpr uint32_t kHeight = 129; | |
| TextureSpec textureSpec; | |
| textureSpec.textureSize = {kWidth, kHeight, 1}; | |
| textureSpec.level = 0; | |
| auto TestBody = [&](wgpu::Origin3D copyOrigin, wgpu::Extent3D copyExtent) { | |
| textureSpec.copyOrigin = copyOrigin; | |
| for (unsigned int b : {1, 2, 3, 4}) { | |
| uint32_t bytesPerRow = | |
| copyExtent.width * utils::GetTexelBlockSizeInBytes(GetParam().mTextureFormat) + b; | |
| DoTest(textureSpec, MinimumDataSpec(copyExtent, bytesPerRow), copyExtent); | |
| } | |
| }; | |
| TestBody({0, 0, 0}, textureSpec.textureSize); | |
| if (utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| return; | |
| } | |
| TestBody({1, 2, 0}, {17, 19, 1}); | |
| } | |
| // Test with bytesPerRow greater than needed for cube textures. | |
| // Made for testing compat behavior. | |
| TEST_P(QueueWriteTextureTests, VaryingBytesPerRowCube) { | |
| // TODO(crbug.com/40238674): Fails on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsImgTec()); | |
| auto format = GetParam().mTextureFormat; | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 6 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsARM()); | |
| // TODO(crbug.com/444741058): Fails on Intel-based brya devices running Android Desktop. | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsIntel() && IsAndroid()); | |
| // TODO(crbug.com/dawn/42241333): diagnose stencil8 failure on Angle Swiftshader | |
| DAWN_SUPPRESS_TEST_IF(format == wgpu::TextureFormat::Stencil8 && IsANGLESwiftShader()); | |
| // TODO(383765096): D3D11 doesn't allow calling Gather() on R8_UINT | |
| DAWN_SUPPRESS_TEST_IF(format == wgpu::TextureFormat::Stencil8 && IsD3D11() && | |
| IsCompatibilityMode()); | |
| constexpr uint32_t kWidth = 257; | |
| constexpr uint32_t kHeight = 257; | |
| TextureSpec textureSpec; | |
| textureSpec.textureSize = {kWidth, kHeight, 6}; | |
| textureSpec.level = 0; | |
| auto TestBody = [&](wgpu::Origin3D copyOrigin, wgpu::Extent3D copyExtent) { | |
| textureSpec.copyOrigin = copyOrigin; | |
| for (unsigned int b : {1, 2, 3, 4}) { | |
| uint32_t bytesPerRow = copyExtent.width * utils::GetTexelBlockSizeInBytes(format) + b; | |
| DoTest(textureSpec, MinimumDataSpec(copyExtent, bytesPerRow), copyExtent, | |
| wgpu::TextureViewDimension::Cube); | |
| } | |
| }; | |
| TestBody({0, 0, 0}, textureSpec.textureSize); | |
| if (utils::IsDepthOrStencilFormat(format)) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| return; | |
| } | |
| TestBody({1, 2, 0}, {17, 17, 1}); | |
| } | |
| // Test that writing with bytesPerRow = 0 and bytesPerRow < bytesInACompleteRow works | |
| // when we're copying one row only | |
| TEST_P(QueueWriteTextureTests, BytesPerRowWithOneRowCopy) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| DAWN_TEST_UNSUPPORTED_IF(utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)); | |
| constexpr uint32_t kWidth = 259; | |
| constexpr uint32_t kHeight = 127; | |
| TextureSpec textureSpec; | |
| textureSpec.copyOrigin = {0, 0, 0}; | |
| textureSpec.textureSize = {kWidth, kHeight, 1}; | |
| textureSpec.level = 0; | |
| { | |
| constexpr wgpu::Extent3D copyExtent = {5, 1, 1}; | |
| DataSpec dataSpec = MinimumDataSpec(copyExtent); | |
| // bytesPerRow undefined | |
| dataSpec.bytesPerRow = wgpu::kCopyStrideUndefined; | |
| DoTest(textureSpec, dataSpec, copyExtent); | |
| } | |
| } | |
| // Test with bytesPerRow greater than needed in a write to a texture array. | |
| TEST_P(QueueWriteTextureTests, VaryingArrayBytesPerRow) { | |
| // TODO(crbug.com/dawn/2095): Failing on ANGLE + SwiftShader, needs investigation. | |
| DAWN_SUPPRESS_TEST_IF(IsANGLESwiftShader()); | |
| // TODO(383779503): reading stencil texture is too slow on D3D11. | |
| DAWN_SUPPRESS_TEST_IF(IsD3D11() && GetParam().mTextureFormat == wgpu::TextureFormat::Stencil8); | |
| constexpr uint32_t kWidth = 257; | |
| constexpr uint32_t kHeight = 129; | |
| constexpr uint32_t kLayers = 65; | |
| TextureSpec textureSpec; | |
| textureSpec.textureSize = {kWidth, kHeight, kLayers}; | |
| textureSpec.level = 0; | |
| auto TestBody = [&](wgpu::Origin3D copyOrigin, wgpu::Extent3D copyExtent) { | |
| textureSpec.copyOrigin = copyOrigin; | |
| // Test with bytesPerRow divisible by blockWidth | |
| for (unsigned int b : {1, 2, 3, 65, 300}) { | |
| uint32_t bytesPerRow = | |
| (copyExtent.width + b) * utils::GetTexelBlockSizeInBytes(GetParam().mTextureFormat); | |
| uint32_t rowsPerImage = copyExtent.height; | |
| DoTest(textureSpec, MinimumDataSpec(copyExtent, bytesPerRow, rowsPerImage), copyExtent); | |
| } | |
| // Test with bytesPerRow not divisible by blockWidth | |
| for (unsigned int b : {1, 2, 3, 19, 301}) { | |
| uint32_t bytesPerRow = | |
| copyExtent.width * utils::GetTexelBlockSizeInBytes(GetParam().mTextureFormat) + b; | |
| uint32_t rowsPerImage = copyExtent.height; | |
| DoTest(textureSpec, MinimumDataSpec(copyExtent, bytesPerRow, rowsPerImage), copyExtent); | |
| } | |
| }; | |
| TestBody({0, 0, 0}, textureSpec.textureSize); | |
| if (utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| return; | |
| } | |
| TestBody({1, 2, 3}, {17, 19, 21}); | |
| } | |
| // Test valid special cases of bytesPerRow and rowsPerImage (0 or undefined). | |
| TEST_P(QueueWriteTextureTests, StrideSpecialCases) { | |
| // The entire subresource must be copied when the format is a depth/stencil format. | |
| DAWN_TEST_UNSUPPORTED_IF(utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)); | |
| TextureSpec textureSpec; | |
| textureSpec.copyOrigin = {0, 0, 0}; | |
| textureSpec.textureSize = {4, 4, 4}; | |
| textureSpec.level = 0; | |
| // bytesPerRow 0 | |
| for (const wgpu::Extent3D copyExtent : | |
| {wgpu::Extent3D{0, 2, 2}, {0, 0, 2}, {0, 2, 0}, {0, 0, 0}}) { | |
| DoTest(textureSpec, MinimumDataSpec(copyExtent, 0, 2), copyExtent); | |
| } | |
| // bytesPerRow undefined | |
| for (const wgpu::Extent3D copyExtent : | |
| {wgpu::Extent3D{2, 1, 1}, {2, 0, 1}, {2, 1, 0}, {2, 0, 0}}) { | |
| DoTest(textureSpec, MinimumDataSpec(copyExtent, wgpu::kCopyStrideUndefined, 2), copyExtent); | |
| } | |
| // rowsPerImage 0 | |
| for (const wgpu::Extent3D copyExtent : | |
| {wgpu::Extent3D{2, 0, 2}, {2, 0, 0}, {0, 0, 2}, {0, 0, 0}}) { | |
| DoTest(textureSpec, MinimumDataSpec(copyExtent, 256, 0), copyExtent); | |
| } | |
| // rowsPerImage undefined | |
| for (const wgpu::Extent3D copyExtent : {wgpu::Extent3D{2, 2, 1}, {2, 2, 0}}) { | |
| DoTest(textureSpec, MinimumDataSpec(copyExtent, 256, wgpu::kCopyStrideUndefined), | |
| copyExtent); | |
| } | |
| } | |
| // 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_P(QueueWriteTextureTests, | |
| {D3D11Backend(), D3D11Backend({"d3d11_delay_flush_to_gpu"}), D3D12Backend(), | |
| D3D12Backend({"d3d12_use_temp_buffer_in_depth_stencil_texture_and_buffer_" | |
| "copy_with_non_zero_buffer_offset"}), | |
| MetalBackend(), | |
| MetalBackend({"use_blit_for_buffer_to_depth_texture_copy", | |
| "use_blit_for_buffer_to_stencil_texture_copy"}), | |
| OpenGLBackend(), OpenGLESBackend(), | |
| OpenGLESBackend({"use_blit_for_stencil_texture_write"}), VulkanBackend(), | |
| WebGPUBackend()}, | |
| { | |
| wgpu::TextureFormat::R8Unorm, | |
| wgpu::TextureFormat::RG8Unorm, | |
| wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureFormat::Stencil8, | |
| }); | |
| class QueueWriteTextureSimpleTests : public DawnTest { | |
| protected: | |
| void DoSimpleWriteTextureTest(uint32_t width, uint32_t height) { | |
| constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; | |
| constexpr uint32_t kPixelSize = 4; | |
| std::vector<uint32_t> data(static_cast<size_t>(width) * height); | |
| for (size_t i = 0; i < data.size(); i++) { | |
| data[i] = 0xFFFFFFFF; | |
| } | |
| wgpu::TextureDescriptor descriptor = {}; | |
| descriptor.size = {width, height, 1}; | |
| descriptor.format = kFormat; | |
| descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; | |
| wgpu::Texture texture = device.CreateTexture(&descriptor); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo = | |
| utils::CreateTexelCopyTextureInfo(texture, 0, {0, 0, 0}); | |
| wgpu::TexelCopyBufferLayout texelCopyBufferLayout = | |
| utils::CreateTexelCopyBufferLayout(0, width * kPixelSize); | |
| wgpu::Extent3D copyExtent = {width, height, 1}; | |
| device.GetQueue().WriteTexture(&texelCopyTextureInfo, data.data(), | |
| static_cast<size_t>(width) * height * kPixelSize, | |
| &texelCopyBufferLayout, ©Extent); | |
| EXPECT_TEXTURE_EQ(data.data(), texture, {0, 0}, {width, height}); | |
| } | |
| }; | |
| // This tests for a bug that occurred within the D3D12 CopyTextureSplitter, which incorrectly copied | |
| // data when the internal offset was larger than 256, but less than 512 and the copy size was 64 | |
| // width or less with a height of 1. | |
| TEST_P(QueueWriteTextureSimpleTests, WriteTo64x1TextureFromUnalignedDynamicUploader) { | |
| // First, WriteTexture with 96 pixels, or 384 bytes to create an offset in the dynamic uploader. | |
| DoSimpleWriteTextureTest(96, 1); | |
| // Now test writing to a 64x1 texture. Because a 64x1 texture's row pitch is equal to its slice | |
| // pitch, the texture copy offset could be calculated incorrectly inside the internal D3D12 | |
| // TextureCopySplitter. | |
| DoSimpleWriteTextureTest(64, 1); | |
| } | |
| // This tests for a bug in the allocation of internal staging buffer, which incorrectly copied depth | |
| // stencil data to the internal offset that is not a multiple of 4. | |
| TEST_P(QueueWriteTextureSimpleTests, WriteStencilAspectWithSourceOffsetUnalignedTo4) { | |
| // TODO(crbug.com/dawn/2095): Failing on ANGLE + SwiftShader, needs investigation. | |
| DAWN_SUPPRESS_TEST_IF(IsANGLESwiftShader()); | |
| // TODO(crbug.com/473870505): [Capture] support depth/stencil and multi-planar textures. | |
| DAWN_SUPPRESS_TEST_IF(IsCaptureReplayCheckingEnabled()); | |
| wgpu::TextureDescriptor textureDescriptor; | |
| textureDescriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; | |
| textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; | |
| textureDescriptor.size = {1, 1, 1}; | |
| wgpu::Texture dstTexture1 = device.CreateTexture(&textureDescriptor); | |
| wgpu::Texture dstTexture2 = device.CreateTexture(&textureDescriptor); | |
| wgpu::BufferDescriptor bufferDescriptor; | |
| bufferDescriptor.size = 8u; | |
| bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| wgpu::Buffer outputBuffer = device.CreateBuffer(&bufferDescriptor); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| constexpr wgpu::Extent3D kWriteSize = {1, 1, 1}; | |
| constexpr uint8_t kData[] = {1, 2}; | |
| constexpr uint32_t kBytesPerRowForWriteTexture = 1u; | |
| std::vector<uint8_t> expectedData(8, 0); | |
| // In the first call of queue.writeTexture(), Dawn will allocate a new staging buffer in its | |
| // internal ring buffer and write the user data into it at the offset 0. | |
| { | |
| constexpr uint32_t kDataOffset1 = 0u; | |
| wgpu::TexelCopyBufferLayout texelCopyBufferLayout = | |
| utils::CreateTexelCopyBufferLayout(kDataOffset1, kBytesPerRowForWriteTexture); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo = utils::CreateTexelCopyTextureInfo( | |
| dstTexture1, 0, {0, 0, 0}, wgpu::TextureAspect::StencilOnly); | |
| queue.WriteTexture(&texelCopyTextureInfo, kData, sizeof(kData), &texelCopyBufferLayout, | |
| &kWriteSize); | |
| constexpr uint32_t kOutputBufferOffset1 = 0u; | |
| wgpu::TexelCopyBufferInfo texelCopyBufferInfo = utils::CreateTexelCopyBufferInfo( | |
| outputBuffer, kOutputBufferOffset1, kTextureBytesPerRowAlignment); | |
| encoder.CopyTextureToBuffer(&texelCopyTextureInfo, &texelCopyBufferInfo, &kWriteSize); | |
| expectedData[kOutputBufferOffset1] = kData[kDataOffset1]; | |
| } | |
| // In the second call of queue.writeTexture(), Dawn will still use the same staging buffer | |
| // allocated in the first call, whose first 2 bytes have been used in the first call of | |
| // queue.writeTexture(). Dawn should write the user data at the offset 4 bytes since the | |
| // destination texture aspect is stencil. | |
| { | |
| constexpr uint32_t kDataOffset2 = 1u; | |
| wgpu::TexelCopyBufferLayout texelCopyBufferLayout = | |
| utils::CreateTexelCopyBufferLayout(kDataOffset2, kBytesPerRowForWriteTexture); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo = utils::CreateTexelCopyTextureInfo( | |
| dstTexture2, 0, {0, 0, 0}, wgpu::TextureAspect::StencilOnly); | |
| queue.WriteTexture(&texelCopyTextureInfo, kData, sizeof(kData), &texelCopyBufferLayout, | |
| &kWriteSize); | |
| constexpr uint32_t kOutputBufferOffset2 = 4u; | |
| wgpu::TexelCopyBufferInfo texelCopyBufferInfo = utils::CreateTexelCopyBufferInfo( | |
| outputBuffer, kOutputBufferOffset2, kTextureBytesPerRowAlignment); | |
| encoder.CopyTextureToBuffer(&texelCopyTextureInfo, &texelCopyBufferInfo, &kWriteSize); | |
| expectedData[kOutputBufferOffset2] = kData[kDataOffset2]; | |
| } | |
| wgpu::CommandBuffer commandBuffer = encoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| EXPECT_BUFFER_U8_RANGE_EQ(expectedData.data(), outputBuffer, 0, 8); | |
| } | |
| // Tests calling queue.writeTexture() to a depth texture after calling queue.writeTexture() on | |
| // another texture always works. On some D3D12 backends the buffer offset of buffer-to-texture | |
| // copies must be a multiple of 512 when the destination texture is a depth stencil texture. | |
| TEST_P(QueueWriteTextureSimpleTests, WriteDepthAspectAfterOtherQueueWriteTextureCalls) { | |
| // TODO(crbug.com/473870505): [Capture] support depth/stencil and multi-planar textures. | |
| DAWN_SUPPRESS_TEST_IF(IsCaptureReplayCheckingEnabled()); | |
| wgpu::TextureDescriptor textureDescriptor; | |
| textureDescriptor.format = wgpu::TextureFormat::Depth16Unorm; | |
| textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; | |
| textureDescriptor.size = {1, 1, 1}; | |
| wgpu::Texture depthTexture1 = device.CreateTexture(&textureDescriptor); | |
| wgpu::Texture depthTexture2 = device.CreateTexture(&textureDescriptor); | |
| constexpr uint16_t kExpectedData1 = (204 << 8) | 205; | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo1 = | |
| utils::CreateTexelCopyTextureInfo(depthTexture1); | |
| // (Off-topic) spot-test for defaulting of .aspect. | |
| texelCopyTextureInfo1.aspect = wgpu::TextureAspect::Undefined; | |
| wgpu::TexelCopyBufferLayout texelCopyBufferLayout = | |
| utils::CreateTexelCopyBufferLayout(0, sizeof(kExpectedData1)); | |
| queue.WriteTexture(&texelCopyTextureInfo1, &kExpectedData1, sizeof(kExpectedData1), | |
| &texelCopyBufferLayout, &textureDescriptor.size); | |
| constexpr uint16_t kExpectedData2 = (206 << 8) | 207; | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo2 = | |
| utils::CreateTexelCopyTextureInfo(depthTexture2); | |
| queue.WriteTexture(&texelCopyTextureInfo2, &kExpectedData2, sizeof(kExpectedData2), | |
| &texelCopyBufferLayout, &textureDescriptor.size); | |
| EXPECT_TEXTURE_EQ(&kExpectedData1, depthTexture1, {0, 0}, {1, 1}, 0, | |
| wgpu::TextureAspect::DepthOnly); | |
| EXPECT_TEXTURE_EQ(&kExpectedData2, depthTexture2, {0, 0}, {1, 1}, 0, | |
| wgpu::TextureAspect::DepthOnly); | |
| } | |
| // Tests calling queue.writeTexture() to the stencil aspect after calling queue.writeTexture() on | |
| // another texture always works. On some D3D12 backends the buffer offset of buffer-to-texture | |
| // copies must be a multiple of 512 when the destination texture is a depth stencil texture. | |
| TEST_P(QueueWriteTextureSimpleTests, WriteStencilAspectAfterOtherQueueWriteTextureCalls) { | |
| // TODO(crbug.com/dawn/2095): Failing on ANGLE + SwiftShader, needs investigation. | |
| DAWN_SUPPRESS_TEST_IF(IsANGLESwiftShader()); | |
| // TODO(crbug.com/473870505): [Capture] support depth/stencil and multi-planar textures. | |
| DAWN_SUPPRESS_TEST_IF(IsCaptureReplayCheckingEnabled()); | |
| wgpu::TextureDescriptor textureDescriptor; | |
| textureDescriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; | |
| textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; | |
| textureDescriptor.size = {1, 1, 1}; | |
| wgpu::Texture depthStencilTexture1 = device.CreateTexture(&textureDescriptor); | |
| wgpu::Texture depthStencilTexture2 = device.CreateTexture(&textureDescriptor); | |
| constexpr uint8_t kExpectedData1 = 204u; | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo1 = utils::CreateTexelCopyTextureInfo( | |
| depthStencilTexture1, 0, {0, 0, 0}, wgpu::TextureAspect::StencilOnly); | |
| wgpu::TexelCopyBufferLayout texelCopyBufferLayout = | |
| utils::CreateTexelCopyBufferLayout(0, sizeof(kExpectedData1)); | |
| queue.WriteTexture(&texelCopyTextureInfo1, &kExpectedData1, sizeof(kExpectedData1), | |
| &texelCopyBufferLayout, &textureDescriptor.size); | |
| constexpr uint8_t kExpectedData2 = 205; | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo2 = utils::CreateTexelCopyTextureInfo( | |
| depthStencilTexture2, 0, {0, 0, 0}, wgpu::TextureAspect::StencilOnly); | |
| queue.WriteTexture(&texelCopyTextureInfo2, &kExpectedData2, sizeof(kExpectedData2), | |
| &texelCopyBufferLayout, &textureDescriptor.size); | |
| EXPECT_TEXTURE_EQ(&kExpectedData1, depthStencilTexture1, {0, 0}, {1, 1}, 0, | |
| wgpu::TextureAspect::StencilOnly); | |
| EXPECT_TEXTURE_EQ(&kExpectedData2, depthStencilTexture2, {0, 0}, {1, 1}, 0, | |
| wgpu::TextureAspect::StencilOnly); | |
| } | |
| DAWN_INSTANTIATE_TEST(QueueWriteTextureSimpleTests, | |
| D3D11Backend(), | |
| D3D11Backend({"d3d11_use_unmonitored_fence"}), | |
| D3D11Backend({"d3d11_disable_fence"}), | |
| D3D11Backend({"d3d11_delay_flush_to_gpu"}), | |
| D3D12Backend(), | |
| MetalBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| VulkanBackend(), | |
| WebGPUBackend()); | |
| } // anonymous namespace | |
| } // namespace dawn |