| // Copyright 2021 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 "common/Assert.h" | |
| #include "common/Constants.h" | |
| #include "common/Math.h" | |
| #include "tests/unittests/validation/ValidationTest.h" | |
| #include "utils/TestUtils.h" | |
| #include "utils/TextureUtils.h" | |
| #include "utils/WGPUHelpers.h" | |
| class CopyTextureForBrowserTest : public ValidationTest { | |
| protected: | |
| wgpu::Texture Create2DTexture(uint32_t width, | |
| uint32_t height, | |
| uint32_t mipLevelCount, | |
| uint32_t arrayLayerCount, | |
| wgpu::TextureFormat format, | |
| wgpu::TextureUsage usage, | |
| uint32_t sampleCount = 1) { | |
| wgpu::TextureDescriptor descriptor; | |
| descriptor.dimension = wgpu::TextureDimension::e2D; | |
| descriptor.size.width = width; | |
| descriptor.size.height = height; | |
| descriptor.size.depthOrArrayLayers = arrayLayerCount; | |
| descriptor.sampleCount = sampleCount; | |
| descriptor.format = format; | |
| descriptor.mipLevelCount = mipLevelCount; | |
| descriptor.usage = usage; | |
| wgpu::Texture tex = device.CreateTexture(&descriptor); | |
| return tex; | |
| } | |
| void TestCopyTextureForBrowser(utils::Expectation expectation, | |
| wgpu::Texture srcTexture, | |
| uint32_t srcLevel, | |
| wgpu::Origin3D srcOrigin, | |
| wgpu::Texture dstTexture, | |
| uint32_t dstLevel, | |
| wgpu::Origin3D dstOrigin, | |
| wgpu::Extent3D extent3D, | |
| wgpu::TextureAspect aspect = wgpu::TextureAspect::All, | |
| wgpu::CopyTextureForBrowserOptions options = {}) { | |
| wgpu::ImageCopyTexture srcImageCopyTexture = | |
| utils::CreateImageCopyTexture(srcTexture, srcLevel, srcOrigin, aspect); | |
| wgpu::ImageCopyTexture dstImageCopyTexture = | |
| utils::CreateImageCopyTexture(dstTexture, dstLevel, dstOrigin, aspect); | |
| if (expectation == utils::Expectation::Success) { | |
| device.GetQueue().CopyTextureForBrowser(&srcImageCopyTexture, &dstImageCopyTexture, | |
| &extent3D, &options); | |
| } else { | |
| ASSERT_DEVICE_ERROR(device.GetQueue().CopyTextureForBrowser( | |
| &srcImageCopyTexture, &dstImageCopyTexture, &extent3D, &options)); | |
| } | |
| } | |
| }; | |
| // Tests should be Success | |
| TEST_F(CopyTextureForBrowserTest, Success) { | |
| wgpu::Texture source = | |
| Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); | |
| wgpu::Texture destination = | |
| Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); | |
| // Different copies, including some that touch the OOB condition | |
| { | |
| // Copy a region along top left boundary | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}); | |
| // Copy entire texture | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {16, 16, 1}); | |
| // Copy a region along bottom right boundary | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {8, 8, 0}, destination, 0, | |
| {8, 8, 0}, {8, 8, 1}); | |
| // Copy region into mip | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 2, | |
| {0, 0, 0}, {4, 4, 1}); | |
| // Copy mip into region | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 2, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}); | |
| // Copy between slices | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 1}, {16, 16, 1}); | |
| } | |
| // Empty copies are valid | |
| { | |
| // An empty copy | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {0, 0, 1}); | |
| // An empty copy with depth = 0 | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {0, 0, 0}); | |
| // An empty copy touching the side of the source texture | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {16, 16, 0}, {0, 0, 1}); | |
| // An empty copy touching the side of the destination texture | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {16, 16, 0}, {0, 0, 1}); | |
| } | |
| } | |
| // Test source or destination texture has wrong usages | |
| TEST_F(CopyTextureForBrowserTest, IncorrectUsage) { | |
| wgpu::Texture validSource = | |
| Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); | |
| wgpu::Texture validDestination = | |
| Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture noSampledUsageSource = | |
| Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); | |
| wgpu::Texture noRenderAttachmentUsageDestination = | |
| Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); | |
| wgpu::Texture noCopySrcUsageSource = Create2DTexture( | |
| 16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::TextureBinding); | |
| wgpu::Texture noCopyDstUsageSource = Create2DTexture( | |
| 16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::RenderAttachment); | |
| // Incorrect source usage causes failure : lack |Sampled| usage | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, noSampledUsageSource, 0, {0, 0, 0}, | |
| validDestination, 0, {0, 0, 0}, {16, 16, 1}); | |
| // Incorrect destination usage causes failure: lack |RenderAttachement| usage. | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, validSource, 0, {0, 0, 0}, | |
| noRenderAttachmentUsageDestination, 0, {0, 0, 0}, {16, 16, 1}); | |
| // Incorrect source usage causes failure : lack |CopySrc| usage | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, noCopySrcUsageSource, 0, {0, 0, 0}, | |
| validDestination, 0, {0, 0, 0}, {16, 16, 1}); | |
| // Incorrect destination usage causes failure: lack |CopyDst| usage. | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, validSource, 0, {0, 0, 0}, | |
| noCopyDstUsageSource, 0, {0, 0, 0}, {16, 16, 1}); | |
| } | |
| // Test non-zero value origin in source and OOB copy rects. | |
| TEST_F(CopyTextureForBrowserTest, OutOfBounds) { | |
| wgpu::Texture source = | |
| Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); | |
| wgpu::Texture destination = | |
| Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); | |
| // OOB on source | |
| { | |
| // x + width overflows | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {1, 0, 0}, destination, 0, | |
| {0, 0, 0}, {16, 16, 1}); | |
| // y + height overflows | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 1, 0}, destination, 0, | |
| {0, 0, 0}, {16, 16, 1}); | |
| // non-zero mip overflows | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 1, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {9, 9, 1}); | |
| // copy to multiple slices | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 2}, {16, 16, 2}); | |
| // copy origin z value is non-zero. | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 1}, destination, 0, | |
| {0, 0, 2}, {16, 16, 1}); | |
| } | |
| // OOB on destination | |
| { | |
| // x + width overflows | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {1, 0, 0}, {16, 16, 1}); | |
| // y + height overflows | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 1, 0}, {16, 16, 1}); | |
| // non-zero mip overflows | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 1, | |
| {0, 0, 0}, {9, 9, 1}); | |
| // arrayLayer + depth OOB | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 4}, {16, 16, 1}); | |
| // empty copy on non-existent mip fails | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 6, | |
| {0, 0, 0}, {0, 0, 1}); | |
| // empty copy on non-existent slice fails | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 4}, {0, 0, 1}); | |
| } | |
| } | |
| // Test destination texture has format that not supported by CopyTextureForBrowser(). | |
| TEST_F(CopyTextureForBrowserTest, InvalidDstFormat) { | |
| wgpu::Texture source = | |
| Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); | |
| wgpu::Texture destination = | |
| Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RG8Uint, | |
| wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); | |
| // Not supported dst texture format. | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {0, 0, 1}); | |
| } | |
| // Test source or destination texture are multisampled. | |
| TEST_F(CopyTextureForBrowserTest, InvalidSampleCount) { | |
| wgpu::Texture sourceMultiSampled1x = | |
| Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding, 1); | |
| wgpu::Texture destinationMultiSampled1x = | |
| Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, 1); | |
| wgpu::Texture sourceMultiSampled4x = | |
| Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding, 4); | |
| wgpu::Texture destinationMultiSampled4x = | |
| Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, 4); | |
| // An empty copy with dst texture sample count > 1 failure. | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, sourceMultiSampled1x, 0, {0, 0, 0}, | |
| destinationMultiSampled4x, 0, {0, 0, 0}, {0, 0, 1}); | |
| // A empty copy with source texture sample count > 1 failure | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, sourceMultiSampled4x, 0, {0, 0, 0}, | |
| destinationMultiSampled1x, 0, {0, 0, 0}, {0, 0, 1}); | |
| } | |
| // Test color space conversion related attributes in CopyTextureForBrowserOptions. | |
| TEST_F(CopyTextureForBrowserTest, ColorSpaceConversion_ColorSpace) { | |
| wgpu::Texture source = | |
| Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); | |
| wgpu::Texture destination = | |
| Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::CopyTextureForBrowserOptions options = {}; | |
| options.needsColorSpaceConversion = true; | |
| // Valid cases | |
| { | |
| wgpu::CopyTextureForBrowserOptions validOptions = options; | |
| std::array<float, 7> srcTransferFunctionParameters = {}; | |
| std::array<float, 7> dstTransferFunctionParameters = {}; | |
| std::array<float, 9> conversionMatrix = {}; | |
| validOptions.srcTransferFunctionParameters = srcTransferFunctionParameters.data(); | |
| validOptions.dstTransferFunctionParameters = dstTransferFunctionParameters.data(); | |
| validOptions.conversionMatrix = conversionMatrix.data(); | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, validOptions); | |
| // if no color space conversion, no need to validate related attributes | |
| wgpu::CopyTextureForBrowserOptions noColorSpaceConversion = options; | |
| noColorSpaceConversion.needsColorSpaceConversion = false; | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, | |
| noColorSpaceConversion); | |
| } | |
| // Invalid cases: srcTransferFunctionParameters, dstTransferFunctionParameters or | |
| // conversionMatrix is nullptr or not set | |
| { | |
| // not set srcTransferFunctionParameters | |
| wgpu::CopyTextureForBrowserOptions invalidOptions = options; | |
| std::array<float, 7> dstTransferFunctionParameters = {}; | |
| std::array<float, 9> conversionMatrix = {}; | |
| invalidOptions.dstTransferFunctionParameters = dstTransferFunctionParameters.data(); | |
| invalidOptions.conversionMatrix = conversionMatrix.data(); | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, invalidOptions); | |
| // set to nullptr | |
| invalidOptions.srcTransferFunctionParameters = nullptr; | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, invalidOptions); | |
| } | |
| { | |
| // not set dstTransferFunctionParameters | |
| wgpu::CopyTextureForBrowserOptions invalidOptions = options; | |
| std::array<float, 7> srcTransferFunctionParameters = {}; | |
| std::array<float, 9> conversionMatrix = {}; | |
| invalidOptions.srcTransferFunctionParameters = srcTransferFunctionParameters.data(); | |
| invalidOptions.conversionMatrix = conversionMatrix.data(); | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, invalidOptions); | |
| // set to nullptr | |
| invalidOptions.dstTransferFunctionParameters = nullptr; | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, invalidOptions); | |
| } | |
| { | |
| // not set conversionMatrix | |
| wgpu::CopyTextureForBrowserOptions invalidOptions = options; | |
| std::array<float, 7> srcTransferFunctionParameters = {}; | |
| std::array<float, 7> dstTransferFunctionParameters = {}; | |
| invalidOptions.srcTransferFunctionParameters = srcTransferFunctionParameters.data(); | |
| invalidOptions.dstTransferFunctionParameters = dstTransferFunctionParameters.data(); | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, invalidOptions); | |
| // set to nullptr | |
| invalidOptions.conversionMatrix = nullptr; | |
| TestCopyTextureForBrowser(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, invalidOptions); | |
| } | |
| } | |
| // Test option.srcAlphaMode/dstAlphaMode | |
| TEST_F(CopyTextureForBrowserTest, ColorSpaceConversion_TextureAlphaState) { | |
| wgpu::Texture source = | |
| Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding); | |
| wgpu::Texture destination = | |
| Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, | |
| wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::CopyTextureForBrowserOptions options = {}; | |
| // Valid src texture alpha state and valid dst texture alpha state | |
| { | |
| options.srcAlphaMode = wgpu::AlphaMode::Premultiplied; | |
| options.dstAlphaMode = wgpu::AlphaMode::Premultiplied; | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, options); | |
| options.srcAlphaMode = wgpu::AlphaMode::Premultiplied; | |
| options.dstAlphaMode = wgpu::AlphaMode::Unpremultiplied; | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, options); | |
| options.srcAlphaMode = wgpu::AlphaMode::Unpremultiplied; | |
| options.dstAlphaMode = wgpu::AlphaMode::Premultiplied; | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, options); | |
| options.srcAlphaMode = wgpu::AlphaMode::Unpremultiplied; | |
| options.dstAlphaMode = wgpu::AlphaMode::Unpremultiplied; | |
| TestCopyTextureForBrowser(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, | |
| {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All, options); | |
| } | |
| } |