|  | // 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 <vector> | 
|  |  | 
|  | #include "dawn/common/Constants.h" | 
|  | #include "dawn/common/Math.h" | 
|  | #include "dawn/tests/DawnTest.h" | 
|  | #include "dawn/utils/ComboRenderPipelineDescriptor.h" | 
|  | #include "dawn/utils/TestUtils.h" | 
|  | #include "dawn/utils/TextureUtils.h" | 
|  | #include "dawn/utils/WGPUHelpers.h" | 
|  |  | 
|  | namespace dawn { | 
|  | namespace { | 
|  |  | 
|  | static constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::RGBA8Unorm; | 
|  |  | 
|  | // Set default texture size to single line texture for color conversion tests. | 
|  | static constexpr uint64_t kDefaultTextureWidth = 10; | 
|  | static constexpr uint64_t kDefaultTextureHeight = 1; | 
|  |  | 
|  | enum class ColorSpace : uint32_t { | 
|  | SRGB = 0x00, | 
|  | DisplayP3 = 0x01, | 
|  | }; | 
|  |  | 
|  | using SrcFormat = wgpu::TextureFormat; | 
|  | using DstFormat = wgpu::TextureFormat; | 
|  | using SrcOrigin = wgpu::Origin3D; | 
|  | using DstOrigin = wgpu::Origin3D; | 
|  | using CopySize = wgpu::Extent3D; | 
|  | using FlipY = bool; | 
|  | using SrcColorSpace = ColorSpace; | 
|  | using DstColorSpace = ColorSpace; | 
|  | using SrcAlphaMode = wgpu::AlphaMode; | 
|  | using DstAlphaMode = wgpu::AlphaMode; | 
|  |  | 
|  | std::ostream& operator<<(std::ostream& o, wgpu::Origin3D origin) { | 
|  | o << origin.x << ", " << origin.y << ", " << origin.z; | 
|  | return o; | 
|  | } | 
|  |  | 
|  | std::ostream& operator<<(std::ostream& o, wgpu::Extent3D copySize) { | 
|  | o << copySize.width << ", " << copySize.height << ", " << copySize.depthOrArrayLayers; | 
|  | return o; | 
|  | } | 
|  |  | 
|  | std::ostream& operator<<(std::ostream& o, ColorSpace space) { | 
|  | o << static_cast<uint32_t>(space); | 
|  | return o; | 
|  | } | 
|  |  | 
|  | DAWN_TEST_PARAM_STRUCT(AlphaTestParams, SrcAlphaMode, DstAlphaMode); | 
|  | DAWN_TEST_PARAM_STRUCT(FormatTestParams, SrcFormat, DstFormat); | 
|  | DAWN_TEST_PARAM_STRUCT(SubRectTestParams, SrcOrigin, DstOrigin, CopySize, FlipY); | 
|  | DAWN_TEST_PARAM_STRUCT(ColorSpaceTestParams, | 
|  | DstFormat, | 
|  | SrcColorSpace, | 
|  | DstColorSpace, | 
|  | SrcAlphaMode, | 
|  | DstAlphaMode); | 
|  |  | 
|  | // Color Space table | 
|  | struct ColorSpaceInfo { | 
|  | ColorSpace index; | 
|  | std::array<float, 9> toXYZD50;             // 3x3 row major transform matrix | 
|  | std::array<float, 9> fromXYZD50;           // inverse transform matrix of toXYZD50, precomputed | 
|  | std::array<float, 7> gammaDecodingParams;  // Follow { A, B, G, E, epsilon, C, F } order | 
|  | std::array<float, 7> gammaEncodingParams;  // inverse op of decoding, precomputed | 
|  | bool isNonLinear; | 
|  | bool isExtended;  // For extended color space. | 
|  | }; | 
|  | static constexpr size_t kSupportedColorSpaceCount = 2; | 
|  | static constexpr std::array<ColorSpaceInfo, kSupportedColorSpaceCount> ColorSpaceTable = {{ | 
|  | // sRGB, | 
|  | // Got primary attributes from https://drafts.csswg.org/css-color/#predefined-sRGB | 
|  | // Use matrices from | 
|  | // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html#WSMatrices | 
|  | // Get gamma-linear conversion params from https://en.wikipedia.org/wiki/SRGB with some | 
|  | // mathematics. | 
|  | { | 
|  | // | 
|  | ColorSpace::SRGB, | 
|  | {{ | 
|  | // | 
|  | 0.4360747, 0.3850649, 0.1430804,  // | 
|  | 0.2225045, 0.7168786, 0.0606169,  // | 
|  | 0.0139322, 0.0971045, 0.7141733   // | 
|  | }}, | 
|  |  | 
|  | {{ | 
|  | // | 
|  | 3.1338561, -1.6168667, -0.4906146,  // | 
|  | -0.9787684, 1.9161415, 0.0334540,   // | 
|  | 0.0719453, -0.2289914, 1.4052427    // | 
|  | }}, | 
|  |  | 
|  | // {G, A, B, C, D, E, F, } | 
|  | {{2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 4.045e-02, 0.0, 0.0}}, | 
|  |  | 
|  | {{1.0 / 2.4, 1.13711 /*pow(1.055, 2.4)*/, 0.0, 12.92f, 3.1308e-03, -0.055, 0.0}}, | 
|  |  | 
|  | true, | 
|  | true  // | 
|  | }, | 
|  |  | 
|  | // Display P3, got primary attributes from | 
|  | // https://www.w3.org/TR/css-color-4/#valdef-color-display-p3 | 
|  | // Use equations found in | 
|  | // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html, | 
|  | // Use Bradford method to do D65 to D50 transform. | 
|  | // Get matrices with help of http://www.russellcottrell.com/photo/matrixCalculator.htm | 
|  | // Gamma-linear conversion params is the same as Srgb. | 
|  | { | 
|  | // | 
|  | ColorSpace::DisplayP3, | 
|  | {{ | 
|  | // | 
|  | 0.5151114, 0.2919612, 0.1571274,  // | 
|  | 0.2411865, 0.6922440, 0.0665695,  // | 
|  | -0.0010491, 0.0418832, 0.7842659  // | 
|  | }}, | 
|  |  | 
|  | {{ | 
|  | // | 
|  | 2.4039872, -0.9898498, -0.3976181,  // | 
|  | -0.8422138, 1.7988188, 0.0160511,   // | 
|  | 0.0481937, -0.0973889, 1.2736887    // | 
|  | }}, | 
|  |  | 
|  | // {G, A, B, C, D, E, F, } | 
|  | {{2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 4.045e-02, 0.0, 0.0}}, | 
|  |  | 
|  | {{1.0 / 2.4, 1.13711 /*pow(1.055, 2.4)*/, 0.0, 12.92f, 3.1308e-03, -0.055, 0.0}}, | 
|  |  | 
|  | true, | 
|  | false  // | 
|  | } | 
|  | // | 
|  | }}; | 
|  |  | 
|  | template <typename Parent> | 
|  | class CopyTextureForBrowserTests : public Parent { | 
|  | protected: | 
|  | struct TextureSpec { | 
|  | wgpu::Origin3D copyOrigin = {}; | 
|  | wgpu::Extent3D textureSize = {kDefaultTextureWidth, kDefaultTextureHeight}; | 
|  | uint32_t level = 0; | 
|  | wgpu::TextureFormat format = kTextureFormat; | 
|  | }; | 
|  |  | 
|  | enum class TextureCopyRole { | 
|  | SOURCE, | 
|  | DEST, | 
|  | }; | 
|  |  | 
|  | // Source texture contains red pixels and dst texture contains green pixels at start. | 
|  | static std::vector<utils::RGBA8> GetTextureData( | 
|  | const utils::TextureDataCopyLayout& layout, | 
|  | TextureCopyRole textureRole, | 
|  | wgpu::AlphaMode srcAlphaMode = wgpu::AlphaMode::Premultiplied, | 
|  | wgpu::AlphaMode dstAlphaMode = wgpu::AlphaMode::Unpremultiplied) { | 
|  | std::array<uint8_t, 4> alpha = {0, 102, 153, 255};  // 0.0, 0.4, 0.6, 1.0 | 
|  | std::vector<utils::RGBA8> textureData(layout.texelBlockCount); | 
|  | for (uint32_t layer = 0; layer < layout.mipSize.depthOrArrayLayers; ++layer) { | 
|  | const uint32_t sliceOffset = layout.texelBlocksPerImage * layer; | 
|  | for (uint32_t y = 0; y < layout.mipSize.height; ++y) { | 
|  | const uint32_t rowOffset = layout.texelBlocksPerRow * y; | 
|  | for (uint32_t x = 0; x < layout.mipSize.width; ++x) { | 
|  | // Source textures will have variable pixel data to cover cases like | 
|  | // flipY. | 
|  | if (textureRole == TextureCopyRole::SOURCE) { | 
|  | if (srcAlphaMode == wgpu::AlphaMode::Unpremultiplied && | 
|  | dstAlphaMode == wgpu::AlphaMode::Premultiplied) { | 
|  | // We expect each channel in dst | 
|  | // texture will equal to the alpha channel value. | 
|  | textureData[sliceOffset + rowOffset + x] = utils::RGBA8( | 
|  | static_cast<uint8_t>(255), static_cast<uint8_t>(255), | 
|  | static_cast<uint8_t>(255), static_cast<uint8_t>(alpha[x % 4])); | 
|  | } else if (srcAlphaMode == wgpu::AlphaMode::Premultiplied && | 
|  | dstAlphaMode == wgpu::AlphaMode::Unpremultiplied) { | 
|  | // We expect each channel in dst | 
|  | // texture will equal to 1.0. | 
|  | textureData[sliceOffset + rowOffset + x] = | 
|  | utils::RGBA8(static_cast<uint8_t>(alpha[x % 4]), | 
|  | static_cast<uint8_t>(alpha[x % 4]), | 
|  | static_cast<uint8_t>(alpha[x % 4]), | 
|  | static_cast<uint8_t>(alpha[x % 4])); | 
|  | } else { | 
|  | textureData[sliceOffset + rowOffset + x] = utils::RGBA8( | 
|  | static_cast<uint8_t>((x + layer * x) % 256), | 
|  | static_cast<uint8_t>((y + layer * y) % 256), | 
|  | static_cast<uint8_t>(x % 256), static_cast<uint8_t>(x % 256)); | 
|  | } | 
|  | } else {  // Dst textures will have be init as `green` to ensure subrect | 
|  | // copy not cross bound. | 
|  | textureData[sliceOffset + rowOffset + x] = | 
|  | utils::RGBA8(static_cast<uint8_t>(0), static_cast<uint8_t>(255), | 
|  | static_cast<uint8_t>(0), static_cast<uint8_t>(255)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return textureData; | 
|  | } | 
|  |  | 
|  | void SetUp() override { | 
|  | Parent::SetUp(); | 
|  | pipeline = MakeTestPipeline(); | 
|  |  | 
|  | uint32_t uniformBufferData[] = { | 
|  | 0,  // copy have flipY option | 
|  | 4,  // channelCount | 
|  | 0, | 
|  | 0,  // uvec2, subrect copy src origin | 
|  | 0, | 
|  | 0,  // uvec2, subrect copy dst origin | 
|  | 0, | 
|  | 0,  // uvec2, subrect copy size | 
|  | 0,  // srcAlphaMode, wgpu::AlphaMode::Premultiplied | 
|  | 0   // dstAlphaMode, wgpu::AlphaMode::Premultiplied | 
|  | }; | 
|  |  | 
|  | wgpu::BufferDescriptor uniformBufferDesc = {}; | 
|  | uniformBufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform; | 
|  | uniformBufferDesc.size = sizeof(uniformBufferData); | 
|  | uniformBuffer = this->device.CreateBuffer(&uniformBufferDesc); | 
|  | } | 
|  |  | 
|  | // Do the bit-by-bit comparison between the source and destination texture with GPU (compute | 
|  | // shader) instead of CPU after executing CopyTextureForBrowser() to avoid the errors caused by | 
|  | // comparing a value generated on CPU to the one generated on GPU. | 
|  | wgpu::ComputePipeline MakeTestPipeline() { | 
|  | wgpu::ShaderModule csModule = utils::CreateShaderModule(this->device, R"( | 
|  | struct Uniforms { | 
|  | dstTextureFlipY : u32, | 
|  | channelCount    : u32, | 
|  | srcCopyOrigin   : vec2u, | 
|  | dstCopyOrigin   : vec2u, | 
|  | copySize        : vec2u, | 
|  | srcAlphaMode    : u32, | 
|  | dstAlphaMode    : u32, | 
|  | } | 
|  | struct OutputBuf { | 
|  | result : array<u32> | 
|  | } | 
|  | @group(0) @binding(0) var src : texture_2d<f32>; | 
|  | @group(0) @binding(1) var dst : texture_2d<f32>; | 
|  | @group(0) @binding(2) var<storage, read_write> output : OutputBuf; | 
|  | @group(0) @binding(3) var<uniform> uniforms : Uniforms; | 
|  | fn aboutEqual(value : f32, expect : f32) -> bool { | 
|  | // The value diff should be smaller than the hard coded tolerance. | 
|  | return abs(value - expect) < 0.01; | 
|  | } | 
|  | @compute @workgroup_size(1, 1, 1) | 
|  | fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3u) { | 
|  | let srcSize = textureDimensions(src); | 
|  | let dstSize = textureDimensions(dst); | 
|  | let dstTexCoord = vec2u(GlobalInvocationID.xy); | 
|  | let nonCoveredColor = vec4f(0.0, 1.0, 0.0, 1.0); // should be green | 
|  |  | 
|  | var success : bool = true; | 
|  | if (dstTexCoord.x < uniforms.dstCopyOrigin.x || | 
|  | dstTexCoord.y < uniforms.dstCopyOrigin.y || | 
|  | dstTexCoord.x >= uniforms.dstCopyOrigin.x + uniforms.copySize.x || | 
|  | dstTexCoord.y >= uniforms.dstCopyOrigin.y + uniforms.copySize.y) { | 
|  | success = success && | 
|  | all(textureLoad(dst, vec2i(dstTexCoord), 0) == nonCoveredColor); | 
|  | } else { | 
|  | // Calculate source texture coord. | 
|  | var srcTexCoordInRect = dstTexCoord - uniforms.dstCopyOrigin; | 
|  |  | 
|  | // Note that |flipY| equals flip src texture in copy sub rect. | 
|  | if (uniforms.dstTextureFlipY == 1u) { | 
|  | srcTexCoordInRect.y = uniforms.copySize.y - srcTexCoordInRect.y - 1; | 
|  | } | 
|  |  | 
|  | var srcTexCoord = srcTexCoordInRect + uniforms.srcCopyOrigin; | 
|  |  | 
|  | var srcColor = textureLoad(src, vec2i(srcTexCoord), 0); | 
|  | var dstColor = textureLoad(dst, vec2i(dstTexCoord), 0); | 
|  |  | 
|  | // Expect the dst texture channels should be all equal to alpha value | 
|  | // after premultiply. | 
|  | let premultiplied = 0u; | 
|  | let unpremultiplied = 1u; | 
|  | let opaque = 2u; | 
|  | if (uniforms.srcAlphaMode == opaque) { | 
|  | srcColor.a = 1.0; | 
|  | } | 
|  |  | 
|  | if (uniforms.srcAlphaMode == unpremultiplied && uniforms.dstAlphaMode == premultiplied) { | 
|  | srcColor = vec4f(srcColor.rgb * srcColor.a, srcColor.a); | 
|  | } | 
|  |  | 
|  | if (uniforms.srcAlphaMode == premultiplied && uniforms.dstAlphaMode == unpremultiplied) { | 
|  | if (srcColor.a != 0.0) { | 
|  | srcColor = vec4f(srcColor.rgb / srcColor.a, srcColor.a); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Not use loop and variable index format to workaround | 
|  | // crbug.com/tint/638. | 
|  | switch(uniforms.channelCount) { | 
|  | case 1u: { | 
|  | success = success && aboutEqual(dstColor.r, srcColor.r); | 
|  | break; | 
|  | } | 
|  | case 2u: { | 
|  | success = success && | 
|  | aboutEqual(dstColor.r, srcColor.r) && | 
|  | aboutEqual(dstColor.g, srcColor.g); | 
|  | break; | 
|  | } | 
|  | case 4u: { | 
|  | success = success && | 
|  | aboutEqual(dstColor.r, srcColor.r) && | 
|  | aboutEqual(dstColor.g, srcColor.g) && | 
|  | aboutEqual(dstColor.b, srcColor.b) && | 
|  | aboutEqual(dstColor.a, srcColor.a); | 
|  | break; | 
|  | } | 
|  | default: { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | let outputIndex = GlobalInvocationID.y * u32(dstSize.x) + GlobalInvocationID.x; | 
|  | if (success) { | 
|  | output.result[outputIndex] = 1u; | 
|  | } else { | 
|  | output.result[outputIndex] = 0u; | 
|  | } | 
|  | } | 
|  | )"); | 
|  |  | 
|  | wgpu::ComputePipelineDescriptor csDesc; | 
|  | csDesc.compute.module = csModule; | 
|  | csDesc.compute.entryPoint = "main"; | 
|  |  | 
|  | return this->device.CreateComputePipeline(&csDesc); | 
|  | } | 
|  | static uint32_t GetTextureFormatComponentCount(wgpu::TextureFormat format) { | 
|  | switch (format) { | 
|  | case wgpu::TextureFormat::RGBA8Unorm: | 
|  | case wgpu::TextureFormat::RGBA8UnormSrgb: | 
|  | case wgpu::TextureFormat::BGRA8Unorm: | 
|  | case wgpu::TextureFormat::BGRA8UnormSrgb: | 
|  | case wgpu::TextureFormat::RGB10A2Unorm: | 
|  | case wgpu::TextureFormat::RGBA16Float: | 
|  | case wgpu::TextureFormat::RGBA32Float: | 
|  | return 4; | 
|  | case wgpu::TextureFormat::RG8Unorm: | 
|  | case wgpu::TextureFormat::RG16Float: | 
|  | case wgpu::TextureFormat::RG32Float: | 
|  | return 2; | 
|  | case wgpu::TextureFormat::R8Unorm: | 
|  | case wgpu::TextureFormat::R16Float: | 
|  | case wgpu::TextureFormat::R32Float: | 
|  | return 1; | 
|  | default: | 
|  | DAWN_UNREACHABLE(); | 
|  | } | 
|  | } | 
|  |  | 
|  | wgpu::Texture CreateTexture(const TextureSpec& spec, wgpu::TextureUsage usage) { | 
|  | // Create and initialize src texture. | 
|  | wgpu::TextureDescriptor descriptor; | 
|  | descriptor.size = spec.textureSize; | 
|  | descriptor.format = spec.format; | 
|  | descriptor.mipLevelCount = spec.level + 1; | 
|  | descriptor.usage = usage; | 
|  | wgpu::Texture texture = this->device.CreateTexture(&descriptor); | 
|  | return texture; | 
|  | } | 
|  |  | 
|  | wgpu::Texture CreateAndInitTexture(const TextureSpec& spec, | 
|  | wgpu::TextureUsage usage, | 
|  | utils::TextureDataCopyLayout copyLayout, | 
|  | void const* init, | 
|  | uint32_t initBytes) { | 
|  | wgpu::Texture texture = CreateTexture(spec, usage); | 
|  |  | 
|  | wgpu::ImageCopyTexture imageTextureInit = | 
|  | utils::CreateImageCopyTexture(texture, spec.level, {0, 0}); | 
|  |  | 
|  | wgpu::TextureDataLayout textureDataLayout; | 
|  | textureDataLayout.offset = 0; | 
|  | textureDataLayout.bytesPerRow = copyLayout.bytesPerRow; | 
|  | textureDataLayout.rowsPerImage = copyLayout.rowsPerImage; | 
|  |  | 
|  | this->device.GetQueue().WriteTexture(&imageTextureInit, init, initBytes, &textureDataLayout, | 
|  | ©Layout.mipSize); | 
|  | return texture; | 
|  | } | 
|  |  | 
|  | void RunCopyExternalImageToTexture(const TextureSpec& srcSpec, | 
|  | wgpu::Texture srcTexture, | 
|  | const TextureSpec& dstSpec, | 
|  | wgpu::Texture dstTexture, | 
|  | const wgpu::Extent3D& copySize, | 
|  | const wgpu::CopyTextureForBrowserOptions options) { | 
|  | wgpu::ImageCopyTexture srcImageCopyTexture = | 
|  | utils::CreateImageCopyTexture(srcTexture, srcSpec.level, srcSpec.copyOrigin); | 
|  | wgpu::ImageCopyTexture dstImageCopyTexture = | 
|  | utils::CreateImageCopyTexture(dstTexture, dstSpec.level, dstSpec.copyOrigin); | 
|  | this->device.GetQueue().CopyTextureForBrowser(&srcImageCopyTexture, &dstImageCopyTexture, | 
|  | ©Size, &options); | 
|  | } | 
|  |  | 
|  | void CheckResultInBuiltInComputePipeline(const TextureSpec& srcSpec, | 
|  | wgpu::Texture srcTexture, | 
|  | const TextureSpec& dstSpec, | 
|  | wgpu::Texture dstTexture, | 
|  | const wgpu::Extent3D& copySize, | 
|  | const wgpu::CopyTextureForBrowserOptions options) { | 
|  | // Update uniform buffer based on test config | 
|  | uint32_t uniformBufferData[] = { | 
|  | options.flipY,                                   // copy have flipY option | 
|  | GetTextureFormatComponentCount(dstSpec.format),  // channelCount | 
|  | srcSpec.copyOrigin.x, | 
|  | srcSpec.copyOrigin.y,  // src texture copy origin | 
|  | dstSpec.copyOrigin.x, | 
|  | dstSpec.copyOrigin.y,  // dst texture copy origin | 
|  | copySize.width, | 
|  | copySize.height,  // copy size | 
|  | static_cast<uint32_t>(options.srcAlphaMode), | 
|  | static_cast<uint32_t>(options.dstAlphaMode)}; | 
|  |  | 
|  | this->device.GetQueue().WriteBuffer(uniformBuffer, 0, uniformBufferData, | 
|  | sizeof(uniformBufferData)); | 
|  |  | 
|  | // Create output buffer to store result | 
|  | wgpu::BufferDescriptor outputDesc; | 
|  | outputDesc.size = dstSpec.textureSize.width * dstSpec.textureSize.height * sizeof(uint32_t); | 
|  | outputDesc.usage = | 
|  | wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | 
|  | wgpu::Buffer outputBuffer = this->device.CreateBuffer(&outputDesc); | 
|  |  | 
|  | // Create texture views for test. | 
|  | wgpu::TextureViewDescriptor srcTextureViewDesc = {}; | 
|  | srcTextureViewDesc.baseMipLevel = srcSpec.level; | 
|  | wgpu::TextureView srcTextureView = srcTexture.CreateView(&srcTextureViewDesc); | 
|  |  | 
|  | wgpu::TextureViewDescriptor dstTextureViewDesc = {}; | 
|  | dstTextureViewDesc.baseMipLevel = dstSpec.level; | 
|  | wgpu::TextureView dstTextureView = dstTexture.CreateView(&dstTextureViewDesc); | 
|  |  | 
|  | // Create bind group based on the config. | 
|  | wgpu::BindGroup bindGroup = utils::MakeBindGroup( | 
|  | this->device, pipeline.GetBindGroupLayout(0), | 
|  | {{0, srcTextureView}, {1, dstTextureView}, {2, outputBuffer}, {3, uniformBuffer}}); | 
|  |  | 
|  | // Start a pipeline to check pixel value in bit form. | 
|  | wgpu::CommandEncoder testEncoder = this->device.CreateCommandEncoder(); | 
|  |  | 
|  | wgpu::CommandBuffer testCommands; | 
|  | { | 
|  | wgpu::CommandEncoder encoder = this->device.CreateCommandEncoder(); | 
|  | wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); | 
|  | pass.SetPipeline(pipeline); | 
|  | pass.SetBindGroup(0, bindGroup); | 
|  | pass.DispatchWorkgroups(dstSpec.textureSize.width, | 
|  | dstSpec.textureSize.height);  // Verify dst texture content | 
|  | pass.End(); | 
|  |  | 
|  | testCommands = encoder.Finish(); | 
|  | } | 
|  | this->device.GetQueue().Submit(1, &testCommands); | 
|  |  | 
|  | std::vector<uint32_t> expectResult(dstSpec.textureSize.width * dstSpec.textureSize.height, | 
|  | 1); | 
|  | EXPECT_BUFFER_U32_RANGE_EQ(expectResult.data(), outputBuffer, 0, | 
|  | dstSpec.textureSize.width * dstSpec.textureSize.height); | 
|  | } | 
|  |  | 
|  | void DoTest(const TextureSpec& srcSpec, | 
|  | const TextureSpec& dstSpec, | 
|  | const wgpu::Extent3D& copySize = {kDefaultTextureWidth, kDefaultTextureHeight}, | 
|  | const wgpu::CopyTextureForBrowserOptions options = {}) { | 
|  | // Create and initialize src texture. | 
|  | const utils::TextureDataCopyLayout srcCopyLayout = | 
|  | utils::GetTextureDataCopyLayoutForTextureAtLevel( | 
|  | kTextureFormat, | 
|  | {srcSpec.textureSize.width, srcSpec.textureSize.height, | 
|  | copySize.depthOrArrayLayers}, | 
|  | srcSpec.level); | 
|  |  | 
|  | std::vector<utils::RGBA8> srcTextureArrayCopyData = GetTextureData( | 
|  | srcCopyLayout, TextureCopyRole::SOURCE, options.srcAlphaMode, options.dstAlphaMode); | 
|  |  | 
|  | wgpu::TextureUsage srcUsage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | | 
|  | wgpu::TextureUsage::TextureBinding; | 
|  | wgpu::Texture srcTexture = | 
|  | CreateAndInitTexture(srcSpec, srcUsage, srcCopyLayout, srcTextureArrayCopyData.data(), | 
|  | srcTextureArrayCopyData.size() * sizeof(utils::RGBA8)); | 
|  |  | 
|  | bool testSubRectCopy = srcSpec.copyOrigin.x > 0 || srcSpec.copyOrigin.y > 0 || | 
|  | dstSpec.copyOrigin.x > 0 || dstSpec.copyOrigin.y > 0 || | 
|  | srcSpec.textureSize.width > copySize.width || | 
|  | srcSpec.textureSize.height > copySize.height || | 
|  | dstSpec.textureSize.width > copySize.width || | 
|  | dstSpec.textureSize.height > copySize.height; | 
|  |  | 
|  | // Create and init dst texture. | 
|  | wgpu::Texture dstTexture; | 
|  | wgpu::TextureUsage dstUsage = | 
|  | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding | | 
|  | wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; | 
|  |  | 
|  | if (testSubRectCopy) { | 
|  | // For subrect copy tests, dst texture use kTextureFormat always. | 
|  | const utils::TextureDataCopyLayout dstCopyLayout = | 
|  | utils::GetTextureDataCopyLayoutForTextureAtLevel( | 
|  | kTextureFormat, | 
|  | {dstSpec.textureSize.width, dstSpec.textureSize.height, | 
|  | copySize.depthOrArrayLayers}, | 
|  | dstSpec.level); | 
|  |  | 
|  | const std::vector<utils::RGBA8> dstTextureArrayCopyData = | 
|  | GetTextureData(dstCopyLayout, TextureCopyRole::DEST); | 
|  | dstTexture = CreateAndInitTexture( | 
|  | dstSpec, dstUsage, dstCopyLayout, dstTextureArrayCopyData.data(), | 
|  | dstTextureArrayCopyData.size() * sizeof(utils::RGBA8)); | 
|  | } else { | 
|  | dstTexture = CreateTexture(dstSpec, dstUsage); | 
|  | } | 
|  |  | 
|  | // Perform the texture to texture copy | 
|  | RunCopyExternalImageToTexture(srcSpec, srcTexture, dstSpec, dstTexture, copySize, options); | 
|  |  | 
|  | // Check Result | 
|  | CheckResultInBuiltInComputePipeline(srcSpec, srcTexture, dstSpec, dstTexture, copySize, | 
|  | options); | 
|  | } | 
|  |  | 
|  | wgpu::Buffer uniformBuffer; | 
|  | wgpu::ComputePipeline pipeline; | 
|  | }; | 
|  |  | 
|  | class CopyTextureForBrowser_Basic : public CopyTextureForBrowserTests<DawnTest> { | 
|  | protected: | 
|  | void DoBasicCopyTest(const wgpu::Extent3D& copySize, | 
|  | const wgpu::CopyTextureForBrowserOptions options = {}) { | 
|  | TextureSpec textureSpec; | 
|  | textureSpec.textureSize = copySize; | 
|  |  | 
|  | DoTest(textureSpec, textureSpec, copySize, options); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class CopyTextureForBrowser_Formats | 
|  | : public CopyTextureForBrowserTests<DawnTestWithParams<FormatTestParams>> { | 
|  | protected: | 
|  | bool IsDstFormatSrgbFormats() { | 
|  | return GetParam().mDstFormat == wgpu::TextureFormat::RGBA8UnormSrgb || | 
|  | GetParam().mDstFormat == wgpu::TextureFormat::BGRA8UnormSrgb; | 
|  | } | 
|  |  | 
|  | wgpu::TextureFormat GetNonSrgbFormat(wgpu::TextureFormat format) { | 
|  | switch (format) { | 
|  | case wgpu::TextureFormat::RGBA8UnormSrgb: | 
|  | return wgpu::TextureFormat::RGBA8Unorm; | 
|  | case wgpu::TextureFormat::BGRA8UnormSrgb: | 
|  | return wgpu::TextureFormat::BGRA8Unorm; | 
|  | default: | 
|  | return format; | 
|  | } | 
|  | } | 
|  |  | 
|  | wgpu::Texture CreateAndInitSourceTextureForColorFormatConversion( | 
|  | const TextureSpec& srcSpec, | 
|  | wgpu::TextureUsage srcUsage, | 
|  | utils::TextureDataCopyLayout srcCopyLayout) { | 
|  | // Create and init source texture. | 
|  | // This fixed source texture data is for color conversion tests. | 
|  | // The source data can fill a texture in default width and height. | 
|  | std::vector<utils::RGBA8> srcRGBA8UnormTextureArrayCopyData{ | 
|  | // Take RGBA8Unorm as example: | 
|  | // R channel has different values | 
|  | utils::RGBA8(0, 255, 255, 255),    // r = 0.0 | 
|  | utils::RGBA8(102, 255, 255, 255),  // r = 0.4 | 
|  | utils::RGBA8(153, 255, 255, 255),  // r = 0.6 | 
|  |  | 
|  | // G channel has different values | 
|  | utils::RGBA8(255, 0, 255, 255),    // g = 0.0 | 
|  | utils::RGBA8(255, 102, 255, 255),  // g = 0.4 | 
|  | utils::RGBA8(255, 153, 255, 255),  // g = 0.6 | 
|  |  | 
|  | // B channel has different values | 
|  | utils::RGBA8(255, 255, 0, 255),    // b = 0.0 | 
|  | utils::RGBA8(255, 255, 102, 255),  // b = 0.4 | 
|  | utils::RGBA8(255, 255, 153, 255),  // b = 0.6 | 
|  |  | 
|  | // A channel set to 0 | 
|  | utils::RGBA8(255, 255, 255, 0)  // a = 0 | 
|  | }; | 
|  |  | 
|  | std::vector<uint16_t> srcRGBA16FloatTextureArrayCopyData{ | 
|  | // R channel has different values | 
|  | // r = 0.0 | 
|  | Float32ToFloat16(0.0), Float32ToFloat16(1.0), Float32ToFloat16(1.0), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // r = 0.4 | 
|  | Float32ToFloat16(0.4), Float32ToFloat16(1.0), Float32ToFloat16(1.0), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // r = 0.6 | 
|  | Float32ToFloat16(0.6), Float32ToFloat16(1.0), Float32ToFloat16(1.0), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // G channel has different values | 
|  | // g = 0.0 | 
|  | Float32ToFloat16(1.0), Float32ToFloat16(0.0), Float32ToFloat16(1.0), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // g = 0.4 | 
|  | Float32ToFloat16(1.0), Float32ToFloat16(0.4), Float32ToFloat16(1.0), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // g = 0.6 | 
|  | Float32ToFloat16(1.0), Float32ToFloat16(0.6), Float32ToFloat16(1.0), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // B channel has different values | 
|  | // b = 0.0 | 
|  | Float32ToFloat16(1.0), Float32ToFloat16(1.0), Float32ToFloat16(0.0), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // b = 0.4 | 
|  | Float32ToFloat16(1.0), Float32ToFloat16(1.0), Float32ToFloat16(0.4), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // b = 0.6 | 
|  | Float32ToFloat16(1.0), Float32ToFloat16(1.0), Float32ToFloat16(0.6), | 
|  | Float32ToFloat16(1.0), | 
|  |  | 
|  | // A channel set to 0 | 
|  | // a = 0 | 
|  | Float32ToFloat16(1.0), Float32ToFloat16(1.0), Float32ToFloat16(1.0), | 
|  | Float32ToFloat16(0.0)}; | 
|  |  | 
|  | switch (srcSpec.format) { | 
|  | case wgpu::TextureFormat::RGBA8Unorm: | 
|  | case wgpu::TextureFormat::BGRA8Unorm: | 
|  | return CreateAndInitTexture( | 
|  | srcSpec, srcUsage, srcCopyLayout, srcRGBA8UnormTextureArrayCopyData.data(), | 
|  | srcRGBA8UnormTextureArrayCopyData.size() * sizeof(utils::RGBA8)); | 
|  | case wgpu::TextureFormat::RGBA16Float: | 
|  | return CreateAndInitTexture( | 
|  | srcSpec, srcUsage, srcCopyLayout, srcRGBA16FloatTextureArrayCopyData.data(), | 
|  | srcRGBA16FloatTextureArrayCopyData.size() * sizeof(uint16_t)); | 
|  | default: | 
|  | DAWN_UNREACHABLE(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DoColorConversionTest() { | 
|  | TextureSpec srcTextureSpec; | 
|  | srcTextureSpec.format = GetParam().mSrcFormat; | 
|  |  | 
|  | TextureSpec dstTextureSpec; | 
|  | dstTextureSpec.format = GetParam().mDstFormat; | 
|  |  | 
|  | wgpu::Extent3D copySize = {kDefaultTextureWidth, kDefaultTextureHeight}; | 
|  | wgpu::CopyTextureForBrowserOptions options = {}; | 
|  |  | 
|  | const utils::TextureDataCopyLayout srcCopyLayout = | 
|  | utils::GetTextureDataCopyLayoutForTextureAtLevel( | 
|  | srcTextureSpec.format, | 
|  | {srcTextureSpec.textureSize.width, srcTextureSpec.textureSize.height, | 
|  | copySize.depthOrArrayLayers}, | 
|  | srcTextureSpec.level); | 
|  |  | 
|  | wgpu::TextureUsage srcUsage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | | 
|  | wgpu::TextureUsage::TextureBinding; | 
|  | wgpu::Texture srcTexture = CreateAndInitSourceTextureForColorFormatConversion( | 
|  | srcTextureSpec, srcUsage, srcCopyLayout); | 
|  |  | 
|  | // Create dst texture. | 
|  | wgpu::Texture dstTexture = CreateTexture( | 
|  | dstTextureSpec, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding | | 
|  | wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc); | 
|  |  | 
|  | // Perform the texture to texture copy | 
|  | RunCopyExternalImageToTexture(srcTextureSpec, srcTexture, dstTextureSpec, dstTexture, | 
|  | copySize, options); | 
|  |  | 
|  | wgpu::Texture result; | 
|  | TextureSpec resultSpec = dstTextureSpec; | 
|  |  | 
|  | // To construct the expected value for the case that dst texture is srgb format, | 
|  | // we need to ensure it is byte level equal to the comparable non-srgb format texture. | 
|  | // We schedule an copy from srgb texture to non-srgb texture which keeps the bytes | 
|  | // same and bypass the sampler to do gamma correction when comparing the expected values | 
|  | // in compute shader. | 
|  | if (IsDstFormatSrgbFormats()) { | 
|  | resultSpec.format = GetNonSrgbFormat(dstTextureSpec.format); | 
|  | wgpu::Texture intermediateTexture = CreateTexture( | 
|  | resultSpec, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding | | 
|  | wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc); | 
|  |  | 
|  | wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | 
|  |  | 
|  | // Perform the texture to texture copy | 
|  | wgpu::ImageCopyTexture dstImageCopyTexture = | 
|  | utils::CreateImageCopyTexture(dstTexture, 0, {0, 0, 0}); | 
|  | wgpu::ImageCopyTexture intermediateImageCopyTexture = | 
|  | utils::CreateImageCopyTexture(intermediateTexture, 0, {0, 0, 0}); | 
|  |  | 
|  | encoder.CopyTextureToTexture(&dstImageCopyTexture, &intermediateImageCopyTexture, | 
|  | &(dstTextureSpec.textureSize)); | 
|  | wgpu::CommandBuffer commands = encoder.Finish(); | 
|  | queue.Submit(1, &commands); | 
|  |  | 
|  | result = intermediateTexture; | 
|  | } else { | 
|  | result = dstTexture; | 
|  | } | 
|  |  | 
|  | // Check Result | 
|  | CheckResultInBuiltInComputePipeline(srcTextureSpec, srcTexture, resultSpec, result, | 
|  | copySize, options); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class CopyTextureForBrowser_SubRects | 
|  | : public CopyTextureForBrowserTests<DawnTestWithParams<SubRectTestParams>> { | 
|  | protected: | 
|  | void DoCopySubRectTest() { | 
|  | TextureSpec srcTextureSpec; | 
|  | srcTextureSpec.copyOrigin = GetParam().mSrcOrigin; | 
|  | srcTextureSpec.textureSize = {6, 7}; | 
|  |  | 
|  | TextureSpec dstTextureSpec; | 
|  | dstTextureSpec.copyOrigin = GetParam().mDstOrigin; | 
|  | dstTextureSpec.textureSize = {8, 5}; | 
|  | wgpu::CopyTextureForBrowserOptions options = {}; | 
|  | options.flipY = GetParam().mFlipY; | 
|  |  | 
|  | wgpu::Extent3D copySize = GetParam().mCopySize; | 
|  |  | 
|  | DoTest(srcTextureSpec, dstTextureSpec, copySize, options); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class CopyTextureForBrowser_AlphaMode | 
|  | : public CopyTextureForBrowserTests<DawnTestWithParams<AlphaTestParams>> { | 
|  | protected: | 
|  | void DoAlphaModeTest() { | 
|  | constexpr uint32_t kWidth = 10; | 
|  | constexpr uint32_t kHeight = 10; | 
|  |  | 
|  | TextureSpec textureSpec; | 
|  | textureSpec.textureSize = {kWidth, kHeight}; | 
|  |  | 
|  | wgpu::CopyTextureForBrowserOptions options = {}; | 
|  | options.srcAlphaMode = GetParam().mSrcAlphaMode; | 
|  | options.dstAlphaMode = GetParam().mDstAlphaMode; | 
|  |  | 
|  | DoTest(textureSpec, textureSpec, {kWidth, kHeight}, options); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class CopyTextureForBrowser_ColorSpace | 
|  | : public CopyTextureForBrowserTests<DawnTestWithParams<ColorSpaceTestParams>> { | 
|  | protected: | 
|  | const ColorSpaceInfo& GetColorSpaceInfo(ColorSpace colorSpace) { | 
|  | uint32_t index = static_cast<uint32_t>(colorSpace); | 
|  | DAWN_ASSERT(index < ColorSpaceTable.size()); | 
|  | DAWN_ASSERT(ColorSpaceTable[index].index == colorSpace); | 
|  | return ColorSpaceTable[index]; | 
|  | } | 
|  |  | 
|  | std::array<float, 9> GetConversionMatrix(ColorSpace src, ColorSpace dst) { | 
|  | const ColorSpaceInfo& srcColorSpace = GetColorSpaceInfo(src); | 
|  | const ColorSpaceInfo& dstColorSpace = GetColorSpaceInfo(dst); | 
|  |  | 
|  | const std::array<float, 9> toXYZD50 = srcColorSpace.toXYZD50; | 
|  | const std::array<float, 9> fromXYZD50 = dstColorSpace.fromXYZD50; | 
|  |  | 
|  | // Fuse the transform matrix. The color space transformation equation is: | 
|  | // Pixels = fromXYZD50 * toXYZD50 * Pixels. | 
|  | // Calculate fromXYZD50 * toXYZD50 to simplify | 
|  | // Add a padding in each row for Mat3x3 in wgsl uniform(mat3x3, Align(16), Size(48)). | 
|  | std::array<float, 9> fuseMatrix = {}; | 
|  |  | 
|  | // Mat3x3 * Mat3x3 | 
|  | for (uint32_t row = 0; row < 3; ++row) { | 
|  | for (uint32_t col = 0; col < 3; ++col) { | 
|  | // Transpose the matrix from row major to column major for wgsl. | 
|  | fuseMatrix[col * 3 + row] = fromXYZD50[row * 3 + 0] * toXYZD50[col] + | 
|  | fromXYZD50[row * 3 + 1] * toXYZD50[3 + col] + | 
|  | fromXYZD50[row * 3 + 2] * toXYZD50[3 * 2 + col]; | 
|  | } | 
|  | } | 
|  |  | 
|  | return fuseMatrix; | 
|  | } | 
|  |  | 
|  | // TODO(crbug.com/dawn/1140): Generate source data automatically. | 
|  | std::vector<utils::RGBA8> GetSourceData(wgpu::AlphaMode srcTextureAlphaMode) { | 
|  | if (srcTextureAlphaMode == wgpu::AlphaMode::Premultiplied) { | 
|  | return std::vector<utils::RGBA8>{ | 
|  | utils::RGBA8(0, 102, 102, 102),  // a = 0.4 | 
|  | utils::RGBA8(102, 0, 0, 102),    // a = 0.4 | 
|  | utils::RGBA8(153, 0, 0, 153),    // a = 0.6 | 
|  | utils::RGBA8(255, 0, 0, 255),    // a = 1.0 | 
|  |  | 
|  | utils::RGBA8(153, 0, 153, 153),  // a = 0.6 | 
|  | utils::RGBA8(0, 102, 0, 102),    // a = 0.4 | 
|  | utils::RGBA8(0, 153, 0, 153),    // a = 0.6 | 
|  | utils::RGBA8(0, 255, 0, 255),    // a = 1.0 | 
|  |  | 
|  | utils::RGBA8(255, 255, 0, 255),  // a = 1.0 | 
|  | utils::RGBA8(0, 0, 102, 102),    // a = 0.4 | 
|  | utils::RGBA8(0, 0, 153, 153),    // a = 0.6 | 
|  | utils::RGBA8(0, 0, 255, 255),    // a = 1.0 | 
|  | }; | 
|  | } | 
|  |  | 
|  | return std::vector<utils::RGBA8>{ | 
|  | // Take RGBA8Unorm as example: | 
|  | // R channel has different values | 
|  | utils::RGBA8(0, 255, 255, 255),  // r = 0.0 | 
|  | utils::RGBA8(102, 0, 0, 255),    // r = 0.4 | 
|  | utils::RGBA8(153, 0, 0, 255),    // r = 0.6 | 
|  | utils::RGBA8(255, 0, 0, 255),    // r = 1.0 | 
|  |  | 
|  | // G channel has different values | 
|  | utils::RGBA8(255, 0, 255, 255),  // g = 0.0 | 
|  | utils::RGBA8(0, 102, 0, 255),    // g = 0.4 | 
|  | utils::RGBA8(0, 153, 0, 255),    // g = 0.6 | 
|  | utils::RGBA8(0, 255, 0, 255),    // g = 1.0 | 
|  |  | 
|  | // B channel has different values | 
|  | utils::RGBA8(255, 255, 0, 255),  // b = 0.0 | 
|  | utils::RGBA8(0, 0, 102, 255),    // b = 0.4 | 
|  | utils::RGBA8(0, 0, 153, 255),    // b = 0.6 | 
|  | utils::RGBA8(0, 0, 255, 255),    // b = 1.0 | 
|  | }; | 
|  | } | 
|  |  | 
|  | // TODO(crbug.com/dawn/1140): Current expected values are from ColorSync utils | 
|  | // tool on Mac. Should implement CPU or compute shader algorithm to do color | 
|  | // conversion and use the result as expected data. | 
|  | std::vector<float> GetExpectedData(ColorSpace srcColorSpace, | 
|  | ColorSpace dstColorSpace, | 
|  | wgpu::AlphaMode srcTextureAlphaMode, | 
|  | wgpu::AlphaMode dstTextureAlphaMode) { | 
|  | if (srcTextureAlphaMode == wgpu::AlphaMode::Premultiplied) { | 
|  | return GetExpectedDataForPremultipliedSource(srcColorSpace, dstColorSpace, | 
|  | dstTextureAlphaMode); | 
|  | } | 
|  |  | 
|  | return GetExpectedDataForSeperateSource(srcColorSpace, dstColorSpace); | 
|  | } | 
|  |  | 
|  | std::vector<float> GeneratePremultipliedResult(std::vector<float> result) { | 
|  | // Four channels per pixel | 
|  | for (uint32_t i = 0; i < result.size(); i += 4) { | 
|  | result[i] *= result[i + 3]; | 
|  | result[i + 1] *= result[i + 3]; | 
|  | result[i + 2] *= result[i + 3]; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | std::vector<float> GetExpectedDataForPremultipliedSource(ColorSpace srcColorSpace, | 
|  | ColorSpace dstColorSpace, | 
|  | wgpu::AlphaMode dstTextureAlphaMode) { | 
|  | if (srcColorSpace == dstColorSpace) { | 
|  | std::vector<float> expected = { | 
|  | 0.0, 1.0, 1.0, 0.4,  // | 
|  | 1.0, 0.0, 0.0, 0.4,  // | 
|  | 1.0, 0.0, 0.0, 0.6,  // | 
|  | 1.0, 0.0, 0.0, 1.0,  // | 
|  |  | 
|  | 1.0, 0.0, 1.0, 0.6,  // | 
|  | 0.0, 1.0, 0.0, 0.4,  // | 
|  | 0.0, 1.0, 0.0, 0.6,  // | 
|  | 0.0, 1.0, 0.0, 1.0,  // | 
|  |  | 
|  | 1.0, 1.0, 0.0, 1.0,  // | 
|  | 0.0, 0.0, 1.0, 0.4,  // | 
|  | 0.0, 0.0, 1.0, 0.6,  // | 
|  | 0.0, 0.0, 1.0, 1.0,  // | 
|  | }; | 
|  |  | 
|  | return dstTextureAlphaMode == wgpu::AlphaMode::Premultiplied | 
|  | ? GeneratePremultipliedResult(expected) | 
|  | : expected; | 
|  | } | 
|  |  | 
|  | switch (srcColorSpace) { | 
|  | case ColorSpace::DisplayP3: { | 
|  | switch (dstColorSpace) { | 
|  | case ColorSpace::SRGB: { | 
|  | std::vector<float> expected = { | 
|  | -0.5118, 1.0183,  1.0085,  0.4,  // | 
|  | 1.093,   -0.2267, -0.1501, 0.4,  // | 
|  | 1.093,   -0.2267, -0.1501, 0.6,  // | 
|  | 1.093,   -0.2267, -0.1501, 1.0,  // | 
|  |  | 
|  | 1.093,   -0.2266, 1.0337,  0.6,  // | 
|  | -0.5118, 1.0183,  -0.3107, 0.4,  // | 
|  | -0.5118, 1.0183,  -0.3107, 0.6,  // | 
|  | -0.5118, 1.0183,  -0.3107, 1.0,  // | 
|  |  | 
|  | 0.9999,  1.0001,  -0.3462, 1.0,  // | 
|  | 0.0002,  0.0004,  1.0419,  0.4,  // | 
|  | 0.0002,  0.0004,  1.0419,  0.6,  // | 
|  | 0.0002,  0.0004,  1.0419,  1.0,  // | 
|  | }; | 
|  |  | 
|  | return dstTextureAlphaMode == wgpu::AlphaMode::Premultiplied | 
|  | ? GeneratePremultipliedResult(expected) | 
|  | : expected; | 
|  | } | 
|  | default: | 
|  | DAWN_UNREACHABLE(); | 
|  | } | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  | DAWN_UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | std::vector<float> GetExpectedDataForSeperateSource(ColorSpace srcColorSpace, | 
|  | ColorSpace dstColorSpace) { | 
|  | if (srcColorSpace == dstColorSpace) { | 
|  | return std::vector<float>{ | 
|  | 0.0, 1.0, 1.0, 1.0,  // | 
|  | 0.4, 0.0, 0.0, 1.0,  // | 
|  | 0.6, 0.0, 0.0, 1.0,  // | 
|  | 1.0, 0.0, 0.0, 1.0,  // | 
|  |  | 
|  | 1.0, 0.0, 1.0, 1.0,  // | 
|  | 0.0, 0.4, 0.0, 1.0,  // | 
|  | 0.0, 0.6, 0.0, 1.0,  // | 
|  | 0.0, 1.0, 0.0, 1.0,  // | 
|  |  | 
|  | 1.0, 1.0, 0.0, 1.0,  // | 
|  | 0.0, 0.0, 0.4, 1.0,  // | 
|  | 0.0, 0.0, 0.6, 1.0,  // | 
|  | 0.0, 0.0, 1.0, 1.0,  // | 
|  | }; | 
|  | } | 
|  |  | 
|  | switch (srcColorSpace) { | 
|  | case ColorSpace::DisplayP3: { | 
|  | switch (dstColorSpace) { | 
|  | case ColorSpace::SRGB: { | 
|  | return std::vector<float>{ | 
|  | -0.5118, 1.0183,  1.0085,  1.0,  // | 
|  | 0.4401,  -0.0665, -0.0337, 1.0,  // | 
|  | 0.6578,  -0.1199, -0.0723, 1.0,  // | 
|  | 1.093,   -0.2267, -0.1501, 1.0,  // | 
|  |  | 
|  | 1.093,   -0.2266, 1.0337,  1.0,  // | 
|  | -0.1894, 0.4079,  -0.1027, 1.0,  // | 
|  | -0.2969, 0.6114,  -0.1720, 1.0,  // | 
|  | -0.5118, 1.0183,  -0.3107, 1.0,  // | 
|  |  | 
|  | 0.9999,  1.0001,  -0.3462, 1.0,  // | 
|  | 0.0000,  0.0001,  0.4181,  1.0,  // | 
|  | 0.0001,  0.0001,  0.6260,  1.0,  // | 
|  | 0.0002,  0.0004,  1.0419,  1.0,  // | 
|  | }; | 
|  | } | 
|  | default: | 
|  | DAWN_UNREACHABLE(); | 
|  | } | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  | DAWN_UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void DoColorSpaceConversionTest() { | 
|  | constexpr uint32_t kWidth = 12; | 
|  | constexpr uint32_t kHeight = 1; | 
|  |  | 
|  | TextureSpec srcTextureSpec; | 
|  | srcTextureSpec.textureSize = {kWidth, kHeight}; | 
|  |  | 
|  | TextureSpec dstTextureSpec; | 
|  | dstTextureSpec.textureSize = {kWidth, kHeight}; | 
|  | dstTextureSpec.format = GetParam().mDstFormat; | 
|  |  | 
|  | ColorSpace srcColorSpace = GetParam().mSrcColorSpace; | 
|  | ColorSpace dstColorSpace = GetParam().mDstColorSpace; | 
|  |  | 
|  | ColorSpaceInfo srcColorSpaceInfo = GetColorSpaceInfo(srcColorSpace); | 
|  | ColorSpaceInfo dstColorSpaceInfo = GetColorSpaceInfo(dstColorSpace); | 
|  |  | 
|  | std::array<float, 9> matrix = GetConversionMatrix(srcColorSpace, dstColorSpace); | 
|  |  | 
|  | wgpu::CopyTextureForBrowserOptions options = {}; | 
|  | options.needsColorSpaceConversion = srcColorSpace != dstColorSpace; | 
|  | options.srcAlphaMode = GetParam().mSrcAlphaMode; | 
|  | options.srcTransferFunctionParameters = srcColorSpaceInfo.gammaDecodingParams.data(); | 
|  | options.conversionMatrix = matrix.data(); | 
|  | options.dstTransferFunctionParameters = dstColorSpaceInfo.gammaEncodingParams.data(); | 
|  | options.dstAlphaMode = GetParam().mDstAlphaMode; | 
|  |  | 
|  | std::vector<utils::RGBA8> sourceTextureData = GetSourceData(options.srcAlphaMode); | 
|  | const wgpu::Extent3D& copySize = {kWidth, kHeight}; | 
|  |  | 
|  | const utils::TextureDataCopyLayout srcCopyLayout = | 
|  | utils::GetTextureDataCopyLayoutForTextureAtLevel( | 
|  | kTextureFormat, | 
|  | {srcTextureSpec.textureSize.width, srcTextureSpec.textureSize.height}, | 
|  | srcTextureSpec.level); | 
|  |  | 
|  | wgpu::TextureUsage srcUsage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | | 
|  | wgpu::TextureUsage::TextureBinding; | 
|  | wgpu::Texture srcTexture = this->CreateAndInitTexture( | 
|  | srcTextureSpec, srcUsage, srcCopyLayout, sourceTextureData.data(), | 
|  | sourceTextureData.size() * sizeof(utils::RGBA8)); | 
|  |  | 
|  | // Create dst texture. | 
|  | wgpu::Texture dstTexture = this->CreateTexture( | 
|  | dstTextureSpec, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding | | 
|  | wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc); | 
|  |  | 
|  | // Perform the texture to texture copy | 
|  | this->RunCopyExternalImageToTexture(srcTextureSpec, srcTexture, dstTextureSpec, dstTexture, | 
|  | copySize, options); | 
|  |  | 
|  | std::vector<float> expectedData = GetExpectedData( | 
|  | srcColorSpace, dstColorSpace, options.srcAlphaMode, options.dstAlphaMode); | 
|  |  | 
|  | // The value provided by Apple's ColorSync Utility. | 
|  | float tolerance = 0.001; | 
|  | if (dstTextureSpec.format == wgpu::TextureFormat::RGBA16Float) { | 
|  | EXPECT_TEXTURE_FLOAT16_EQ(expectedData.data(), dstTexture, {0, 0}, {kWidth, kHeight}, | 
|  | dstTextureSpec.format, tolerance); | 
|  | } else { | 
|  | EXPECT_TEXTURE_EQ(expectedData.data(), dstTexture, {0, 0}, {kWidth, kHeight}, | 
|  | dstTextureSpec.format, tolerance); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Verify CopyTextureForBrowserTests works with internal pipeline. | 
|  | // The case do copy without any transform. | 
|  | TEST_P(CopyTextureForBrowser_Basic, PassthroughCopy) { | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | DoBasicCopyTest({10, 1}); | 
|  | } | 
|  |  | 
|  | TEST_P(CopyTextureForBrowser_Basic, VerifyCopyOnXDirection) { | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | DoBasicCopyTest({1000, 1}); | 
|  | } | 
|  |  | 
|  | TEST_P(CopyTextureForBrowser_Basic, VerifyCopyOnYDirection) { | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | DoBasicCopyTest({1, 1000}); | 
|  | } | 
|  |  | 
|  | TEST_P(CopyTextureForBrowser_Basic, VerifyCopyFromLargeTexture) { | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | DoBasicCopyTest({899, 999}); | 
|  | } | 
|  |  | 
|  | TEST_P(CopyTextureForBrowser_Basic, VerifyFlipY) { | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | wgpu::CopyTextureForBrowserOptions options = {}; | 
|  | options.flipY = true; | 
|  |  | 
|  | DoBasicCopyTest({901, 1001}, options); | 
|  | } | 
|  |  | 
|  | TEST_P(CopyTextureForBrowser_Basic, VerifyFlipYInSlimTexture) { | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | wgpu::CopyTextureForBrowserOptions options = {}; | 
|  | options.flipY = true; | 
|  |  | 
|  | DoBasicCopyTest({1, 1001}, options); | 
|  | } | 
|  |  | 
|  | DAWN_INSTANTIATE_TEST(CopyTextureForBrowser_Basic, | 
|  | D3D11Backend(), | 
|  | D3D12Backend(), | 
|  | MetalBackend(), | 
|  | OpenGLBackend(), | 
|  | OpenGLESBackend(), | 
|  | VulkanBackend()); | 
|  |  | 
|  | // Verify |CopyTextureForBrowser| doing color conversion correctly when | 
|  | // the source texture is RGBA8Unorm format. | 
|  | TEST_P(CopyTextureForBrowser_Formats, ColorConversion) { | 
|  | // Skip OpenGLES backend because it fails on using RGBA8Unorm as | 
|  | // source texture format. | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | // Skip OpenGL backend on linux because it fails on using *-srgb format as | 
|  | // dst texture format | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux() && IsDstFormatSrgbFormats()); | 
|  |  | 
|  | DoColorConversionTest(); | 
|  | } | 
|  |  | 
|  | DAWN_INSTANTIATE_TEST_P( | 
|  | CopyTextureForBrowser_Formats, | 
|  | {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()}, | 
|  | std::vector<wgpu::TextureFormat>({wgpu::TextureFormat::RGBA8Unorm, | 
|  | wgpu::TextureFormat::BGRA8Unorm, | 
|  | wgpu::TextureFormat::RGBA16Float}), | 
|  | std::vector<wgpu::TextureFormat>( | 
|  | {wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R16Float, wgpu::TextureFormat::R32Float, | 
|  | wgpu::TextureFormat::RG8Unorm, wgpu::TextureFormat::RG16Float, | 
|  | wgpu::TextureFormat::RG32Float, wgpu::TextureFormat::RGBA8Unorm, | 
|  | wgpu::TextureFormat::RGBA8UnormSrgb, wgpu::TextureFormat::BGRA8Unorm, | 
|  | wgpu::TextureFormat::BGRA8UnormSrgb, wgpu::TextureFormat::RGB10A2Unorm, | 
|  | wgpu::TextureFormat::RGBA16Float, wgpu::TextureFormat::RGBA32Float})); | 
|  |  | 
|  | // Verify |CopyTextureForBrowser| doing subrect copy. | 
|  | // Source texture is a full red texture and dst texture is a full | 
|  | // green texture originally. After the subrect copy, affected part | 
|  | // in dst texture should be red and other part should remain green. | 
|  | TEST_P(CopyTextureForBrowser_SubRects, CopySubRect) { | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | // Tests skip due to crbug.com/dawn/592. | 
|  | DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsBackendValidationEnabled()); | 
|  |  | 
|  | // Tests skip due to crbug.com/dawn/1104. | 
|  | DAWN_SUPPRESS_TEST_IF(IsWARP()); | 
|  |  | 
|  | DoCopySubRectTest(); | 
|  | } | 
|  |  | 
|  | DAWN_INSTANTIATE_TEST_P(CopyTextureForBrowser_SubRects, | 
|  | {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), | 
|  | OpenGLESBackend(), VulkanBackend()}, | 
|  | std::vector<wgpu::Origin3D>({{1, 1}, {1, 2}, {2, 1}}), | 
|  | std::vector<wgpu::Origin3D>({{1, 1}, {1, 2}, {2, 1}}), | 
|  | std::vector<wgpu::Extent3D>({{1, 1}, {2, 1}, {1, 2}, {2, 2}}), | 
|  | std::vector<bool>({true, false})); | 
|  |  | 
|  | // Verify |CopyTextureForBrowser| doing alpha changes. | 
|  | // Test srcAlphaMode and dstAlphaMode: Premultiplied, Unpremultiplied. | 
|  | TEST_P(CopyTextureForBrowser_AlphaMode, alphaMode) { | 
|  | // Skip OpenGLES backend because it fails on using RGBA8Unorm as | 
|  | // source texture format. | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | // Tests skip due to crbug.com/dawn/1104. | 
|  | DAWN_SUPPRESS_TEST_IF(IsWARP()); | 
|  |  | 
|  | DoAlphaModeTest(); | 
|  | } | 
|  |  | 
|  | DAWN_INSTANTIATE_TEST_P( | 
|  | CopyTextureForBrowser_AlphaMode, | 
|  | {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(), | 
|  | VulkanBackend()}, | 
|  | std::vector<wgpu::AlphaMode>({wgpu::AlphaMode::Premultiplied, wgpu::AlphaMode::Unpremultiplied, | 
|  | wgpu::AlphaMode::Opaque}), | 
|  | std::vector<wgpu::AlphaMode>({wgpu::AlphaMode::Premultiplied, wgpu::AlphaMode::Unpremultiplied, | 
|  | wgpu::AlphaMode::Opaque})); | 
|  |  | 
|  | // Verify |CopyTextureForBrowser| doing color space conversion. | 
|  | TEST_P(CopyTextureForBrowser_ColorSpace, colorSpaceConversion) { | 
|  | // TODO(crbug.com/dawn/1232): Program link error on OpenGLES backend | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGLES()); | 
|  | DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux()); | 
|  |  | 
|  | // Tests skip due to crbug.com/dawn/1104. | 
|  | DAWN_SUPPRESS_TEST_IF(IsWARP()); | 
|  |  | 
|  | DoColorSpaceConversionTest(); | 
|  | } | 
|  |  | 
|  | DAWN_INSTANTIATE_TEST_P(CopyTextureForBrowser_ColorSpace, | 
|  | {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), | 
|  | OpenGLESBackend(), VulkanBackend()}, | 
|  | std::vector<wgpu::TextureFormat>({wgpu::TextureFormat::RGBA16Float, | 
|  | wgpu::TextureFormat::RGBA32Float}), | 
|  | std::vector<ColorSpace>({ColorSpace::SRGB, ColorSpace::DisplayP3}), | 
|  | std::vector<ColorSpace>({ColorSpace::SRGB}), | 
|  | std::vector<wgpu::AlphaMode>({wgpu::AlphaMode::Premultiplied, | 
|  | wgpu::AlphaMode::Unpremultiplied}), | 
|  | std::vector<wgpu::AlphaMode>({wgpu::AlphaMode::Premultiplied, | 
|  | wgpu::AlphaMode::Unpremultiplied})); | 
|  |  | 
|  | }  // anonymous namespace | 
|  | }  // namespace dawn |