| // Copyright 2021 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. | |
| #include <string> | |
| #include <vector> | |
| #include "dawn/tests/DawnTest.h" | |
| #include "dawn/utils/ComboRenderPipelineDescriptor.h" | |
| #include "dawn/utils/WGPUHelpers.h" | |
| namespace dawn { | |
| namespace { | |
| wgpu::Texture Create2DTexture(wgpu::Device device, | |
| uint32_t width, | |
| uint32_t height, | |
| wgpu::TextureFormat format, | |
| wgpu::TextureUsage usage) { | |
| wgpu::TextureDescriptor descriptor; | |
| descriptor.dimension = wgpu::TextureDimension::e2D; | |
| descriptor.size.width = width; | |
| descriptor.size.height = height; | |
| descriptor.size.depthOrArrayLayers = 1; | |
| descriptor.sampleCount = 1; | |
| descriptor.format = format; | |
| descriptor.mipLevelCount = 1; | |
| descriptor.usage = usage; | |
| return device.CreateTexture(&descriptor); | |
| } | |
| struct YUVTestData { | |
| float y; | |
| float u; | |
| float v; | |
| std::array<float, 4> rgbaFloats; | |
| utils::RGBA8 rgba; | |
| }; | |
| static const YUVTestData kBlack = { | |
| 0.0, 0.5, 0.5, {0.0, 0.0, 0.0, 1.0}, utils::RGBA8::kBlack, | |
| }; | |
| static const YUVTestData kRed = { | |
| 0.2126, 0.4172, 1.0, {1.0, 0.0, 0.0, 1.0}, utils::RGBA8::kRed, | |
| }; | |
| static const YUVTestData kGreen = { | |
| 0.7152, 0.1402, 0.0175, {0.0, 1.0, 0.0, 1.0}, utils::RGBA8::kGreen, | |
| }; | |
| static const YUVTestData kBlue = { | |
| 0.0722, 1.0, 0.4937, {0.0, 0.0, 1.0, 1.0}, utils::RGBA8::kBlue, | |
| }; | |
| static const YUVTestData kColor1 = {0.6382, | |
| 0.3232, | |
| 0.6644, | |
| {246 / 255.0, 169 / 255.0, 90 / 255.0, 1}, | |
| {246, 169, 90, 255}}; | |
| template <typename Parent> | |
| class ExternalTextureTestsBase : public Parent { | |
| protected: | |
| void SetUp() override { | |
| Parent::SetUp(); | |
| vsModule = utils::CreateShaderModule(this->device, R"( | |
| @vertex fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4f { | |
| var positions = array( | |
| vec4f(-1.0, 1.0, 0.0, 1.0), | |
| vec4f(-1.0, -1.0, 0.0, 1.0), | |
| vec4f(1.0, 1.0, 0.0, 1.0), | |
| vec4f(1.0, -1.0, 0.0, 1.0), | |
| vec4f(-1.0, -1.0, 0.0, 1.0), | |
| vec4f(1.0, 1.0, 0.0, 1.0) | |
| ); | |
| return positions[VertexIndex]; | |
| })"); | |
| fsSampleExternalTextureModule = utils::CreateShaderModule(this->device, R"( | |
| @group(0) @binding(0) var s : sampler; | |
| @group(0) @binding(1) var t : texture_external; | |
| @fragment fn main(@builtin(position) FragCoord : vec4f) | |
| -> @location(0) vec4f { | |
| return textureSampleBaseClampToEdge(t, s, FragCoord.xy / vec2f(4.0, 4.0)); | |
| })"); | |
| } | |
| wgpu::ExternalTextureDescriptor InitExternalTextureDescriptor(wgpu::Texture plane0, | |
| wgpu::Texture plane1 = nullptr) { | |
| wgpu::ExternalTextureDescriptor desc; | |
| desc.plane0 = plane0.CreateView(); | |
| desc.plane1 = plane1 != nullptr ? plane1.CreateView() : nullptr; | |
| const auto& conversion = plane1 == nullptr ? noopRGBConversion : bt709Conversion; | |
| desc.yuvToRgbConversionMatrix = conversion.yuvToRgbConversionMatrix.data(); | |
| desc.gamutConversionMatrix = conversion.gamutConversionMatrix.data(); | |
| desc.srcTransferFunctionParameters = conversion.srcTransferFunctionParameters.data(); | |
| desc.dstTransferFunctionParameters = conversion.dstTransferFunctionParameters.data(); | |
| desc.cropOrigin = {0, 0}; | |
| desc.cropSize = {plane0.GetWidth(), plane0.GetHeight()}; | |
| desc.apparentSize = {plane0.GetWidth(), plane0.GetHeight()}; | |
| return desc; | |
| } | |
| // Helper function to render a quad of data in a texture with a different color for each | |
| // corner as well as an optional color for the outside of the quad. (the quad can be scaled | |
| // to make it fill only parts of the texture). | |
| struct QuadData { | |
| std::array<float, 4> upperLeft; | |
| std::array<float, 4> upperRight; | |
| std::array<float, 4> lowerLeft; | |
| std::array<float, 4> lowerRight; | |
| std::array<float, 4> outsideData = {}; | |
| float scale = 1.0; | |
| std::array<float, 3> padding = {}; | |
| }; | |
| void RenderQuad(const wgpu::Texture& texture, const QuadData& quad) { | |
| // Make the pipeline drawing a quad in the texture, taking the colors as a uniform buffer. | |
| wgpu::ShaderModule module = utils::CreateShaderModule(this->device, R"( | |
| struct VsOut { | |
| @builtin(position) pos : vec4f, | |
| @interpolate(perspective) @location(0) ndc : vec4f, | |
| } | |
| @vertex fn vs(@builtin(vertex_index) VertexIndex : u32) -> VsOut { | |
| var pos = array( | |
| vec4f(-3, -1, 0, 1), | |
| vec4f( 3, -1, 0, 1), | |
| vec4f( 0, 2, 0, 1), | |
| ); | |
| return VsOut(pos[VertexIndex], pos[VertexIndex]); | |
| } | |
| struct QuadData { | |
| upperLeft : vec4f, | |
| upperRight : vec4f, | |
| lowerLeft : vec4f, | |
| lowerRight : vec4f, | |
| outside : vec4f, | |
| scale : f32, | |
| } | |
| @group(0) @binding(0) var<uniform> quad : QuadData; | |
| @fragment fn fs(@interpolate(perspective) @location(0) ndc : vec4f) | |
| -> @location(0) vec4f { | |
| if abs(ndc.x) > quad.scale || abs(ndc.y) > quad.scale { | |
| return quad.outside; | |
| } | |
| if ndc.x <= 0 && ndc.y >= 0 { | |
| return quad.upperLeft; | |
| } else if ndc.x >= 0 && ndc.y >= 0 { | |
| return quad.upperRight; | |
| } else if ndc.x <= 0 && ndc.y <= 0 { | |
| return quad.lowerLeft; | |
| } else { | |
| return quad.lowerRight; | |
| } | |
| })"); | |
| utils::ComboRenderPipelineDescriptor pDesc; | |
| pDesc.vertex.module = module; | |
| pDesc.cFragment.module = module; | |
| pDesc.cTargets[0].format = texture.GetFormat(); | |
| wgpu::RenderPipeline quadPipeline = this->device.CreateRenderPipeline(&pDesc); | |
| // Make the storage buffer and the bind group containing it. | |
| wgpu::Buffer quadData = utils::CreateBufferFromData(this->device, &quad, sizeof(quad), | |
| wgpu::BufferUsage::Uniform); | |
| wgpu::BindGroup bg = | |
| utils::MakeBindGroup(this->device, quadPipeline.GetBindGroupLayout(0), {{0, quadData}}); | |
| // Do the render. | |
| wgpu::CommandEncoder encoder = this->device.CreateCommandEncoder(); | |
| utils::ComboRenderPassDescriptor renderPass({texture.CreateView()}); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| pass.SetPipeline(quadPipeline); | |
| pass.SetBindGroup(0, bg); | |
| pass.Draw(3); | |
| pass.End(); | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| this->queue.Submit(1, &commands); | |
| } | |
| static constexpr uint32_t kWidth = 4; | |
| static constexpr uint32_t kHeight = 4; | |
| static constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; | |
| static constexpr wgpu::TextureUsage kSampledUsage = wgpu::TextureUsage::TextureBinding; | |
| wgpu::ShaderModule vsModule; | |
| wgpu::ShaderModule fsSampleExternalTextureModule; | |
| utils::ColorSpaceConversionInfo noopRGBConversion = utils::GetNoopRGBColorSpaceConversionInfo(); | |
| utils::ColorSpaceConversionInfo bt709Conversion = | |
| utils::GetYUVBT709ToRGBSRGBColorSpaceConversionInfo(); | |
| }; | |
| class ExternalTextureTests : public ExternalTextureTestsBase<DawnTest> { | |
| protected: | |
| std::vector<wgpu::FeatureName> GetRequiredFeatures() override { | |
| std::vector<wgpu::FeatureName> requiredFeatures = {}; | |
| if (SupportsFeatures({wgpu::FeatureName::Unorm16TextureFormats})) { | |
| mIsUnorm16TextureFormatsSupported = true; | |
| requiredFeatures.push_back(wgpu::FeatureName::Unorm16TextureFormats); | |
| } | |
| return requiredFeatures; | |
| } | |
| bool IsUnorm16TextureFormatsSupported() { return mIsUnorm16TextureFormatsSupported; } | |
| bool mIsUnorm16TextureFormatsSupported = false; | |
| }; | |
| TEST_P(ExternalTextureTests, CreateExternalTextureSuccess) { | |
| wgpu::Texture texture = Create2DTexture(device, kWidth, kHeight, kFormat, kSampledUsage); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(texture); | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| ASSERT_NE(externalTexture.Get(), nullptr); | |
| } | |
| TEST_P(ExternalTextureTests, SampleExternalTexture) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::Texture sampledTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| // Initialize texture with green to ensure it is sampled from later. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass({sampledTexture.CreateView()}, nullptr); | |
| renderPass.cColorAttachments[0].clearValue = {0.0f, 1.0f, 0.0f, 1.0f}; | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| pass.End(); | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| } | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(sampledTexture); | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into the | |
| // upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(3); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kGreen, renderTexture, 0, 0); | |
| } | |
| // https://crbug.com/1515439 | |
| TEST_P(ExternalTextureTests, SampleExternalTextureDifferingGroup) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::Texture sampledTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| // Initialize texture with green to ensure it is sampled from later. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass({sampledTexture.CreateView()}, nullptr); | |
| renderPass.cColorAttachments[0].clearValue = {0.0f, 1.0f, 0.0f, 1.0f}; | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| pass.End(); | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| } | |
| wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"( | |
| @group(0) @binding(0) var s : sampler; | |
| @group(1) @binding(0) var t : texture_external; | |
| @fragment fn main(@builtin(position) FragCoord : vec4f) | |
| -> @location(0) vec4f { | |
| return textureSampleBaseClampToEdge(t, s, FragCoord.xy / vec2f(4.0, 4.0)); | |
| })"); | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(sampledTexture); | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup samplerBindGroup = | |
| utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, sampler}}); | |
| wgpu::BindGroup texBindGroup = | |
| utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(1), {{0, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into the | |
| // upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, samplerBindGroup); | |
| pass.SetBindGroup(1, texBindGroup); | |
| pass.Draw(3); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kGreen, renderTexture, 0, 0); | |
| } | |
| TEST_P(ExternalTextureTests, SampleMultiplanarExternalTexture) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::Texture sampledTexturePlane0 = | |
| Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::R8Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture sampledTexturePlane1 = | |
| Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::RG8Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| // Create a texture view for the external texture | |
| wgpu::TextureView externalViewPlane0 = sampledTexturePlane0.CreateView(); | |
| wgpu::TextureView externalViewPlane1 = sampledTexturePlane1.CreateView(); | |
| for (const auto& expectation : {kBlack, kRed, kGreen, kBlue, kColor1}) { | |
| // Initialize the texture planes with YUV data | |
| { | |
| utils::ComboRenderPassDescriptor renderPass({externalViewPlane0, externalViewPlane1}, | |
| nullptr); | |
| renderPass.cColorAttachments[0].clearValue = {expectation.y, 0.0f, 0.0f, 0.0f}; | |
| renderPass.cColorAttachments[1].clearValue = {expectation.u, expectation.v, 0.0f, 0.0f}; | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| pass.End(); | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| } | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| // descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = | |
| InitExternalTextureDescriptor(sampledTexturePlane0, sampledTexturePlane1); | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(3); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(expectation.rgba, renderTexture, 0, 0); | |
| } | |
| } | |
| TEST_P(ExternalTextureTests, SampleMultiplanarExternalTextureNorm16) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsUnorm16TextureFormatsSupported()); | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::Texture sampledTexturePlane0 = | |
| Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::R16Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture sampledTexturePlane1 = | |
| Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::RG16Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| // Create a texture view for the external texture | |
| wgpu::TextureView externalViewPlane0 = sampledTexturePlane0.CreateView(); | |
| wgpu::TextureView externalViewPlane1 = sampledTexturePlane1.CreateView(); | |
| for (const auto& expectation : {kBlack, kRed, kGreen, kBlue, kColor1}) { | |
| // Initialize the texture planes with YUV data | |
| { | |
| utils::ComboRenderPassDescriptor renderPass({externalViewPlane0, externalViewPlane1}, | |
| nullptr); | |
| renderPass.cColorAttachments[0].clearValue = {expectation.y, 0.0f, 0.0f, 0.0f}; | |
| renderPass.cColorAttachments[1].clearValue = {expectation.u, expectation.v, 0.0f, 0.0f}; | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| pass.End(); | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| } | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = | |
| InitExternalTextureDescriptor(sampledTexturePlane0, sampledTexturePlane1); | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(3); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(expectation.rgba, renderTexture, 0, 0); | |
| } | |
| } | |
| // Test draws a green square in the upper left quadrant, a black square in the upper right, a red | |
| // square in the lower left and a blue square in the lower right. The image is then sampled as an | |
| // external texture and rotated 0, 90, 180, and 270 degrees with and without the y-axis flipped. | |
| TEST_P(ExternalTextureTests, RotateAndOrFlipSampleSinglePlane) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| // TODO(41487285): Fails on OpenGL ANGLE D3D11 Intel (but not other configs). Suppress since we | |
| // don't want to ship that configuration. | |
| DAWN_SUPPRESS_TEST_IF(IsANGLED3D11() && IsIntel()); | |
| wgpu::Texture sourceTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| RenderQuad(sourceTexture, | |
| {kGreen.rgbaFloats, kBlack.rgbaFloats, kRed.rgbaFloats, kBlue.rgbaFloats}); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| // Control case to verify mirrored and rotation defaults | |
| { | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(sourceTexture); | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kGreen, renderTexture, 0, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kBlack, renderTexture, 3, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kRed, renderTexture, 0, 3); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kBlue, renderTexture, 3, 3); | |
| } | |
| struct RotationExpectation { | |
| wgpu::ExternalTextureRotation rotation; | |
| bool mirrored; | |
| utils::RGBA8 upperLeftColor; | |
| utils::RGBA8 upperRightColor; | |
| utils::RGBA8 lowerLeftColor; | |
| utils::RGBA8 lowerRightColor; | |
| }; | |
| std::array<RotationExpectation, 8> expectations = { | |
| {{wgpu::ExternalTextureRotation::Rotate0Degrees, false, utils::RGBA8::kGreen, | |
| utils::RGBA8::kBlack, utils::RGBA8::kRed, utils::RGBA8::kBlue}, | |
| {wgpu::ExternalTextureRotation::Rotate90Degrees, false, utils::RGBA8::kRed, | |
| utils::RGBA8::kGreen, utils::RGBA8::kBlue, utils::RGBA8::kBlack}, | |
| {wgpu::ExternalTextureRotation::Rotate180Degrees, false, utils::RGBA8::kBlue, | |
| utils::RGBA8::kRed, utils::RGBA8::kBlack, utils::RGBA8::kGreen}, | |
| {wgpu::ExternalTextureRotation::Rotate270Degrees, false, utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlue, utils::RGBA8::kGreen, utils::RGBA8::kRed}, | |
| {wgpu::ExternalTextureRotation::Rotate0Degrees, true, utils::RGBA8::kBlack, | |
| utils::RGBA8::kGreen, utils::RGBA8::kBlue, utils::RGBA8::kRed}, | |
| {wgpu::ExternalTextureRotation::Rotate90Degrees, true, utils::RGBA8::kGreen, | |
| utils::RGBA8::kRed, utils::RGBA8::kBlack, utils::RGBA8::kBlue}, | |
| {wgpu::ExternalTextureRotation::Rotate180Degrees, true, utils::RGBA8::kRed, | |
| utils::RGBA8::kBlue, utils::RGBA8::kGreen, utils::RGBA8::kBlack}, | |
| {wgpu::ExternalTextureRotation::Rotate270Degrees, true, utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlack, utils::RGBA8::kRed, utils::RGBA8::kGreen}}}; | |
| for (const RotationExpectation& exp : expectations) { | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(sourceTexture); | |
| externalDesc.rotation = exp.rotation; | |
| externalDesc.mirrored = exp.mirrored; | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperLeftColor, renderTexture, 0, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperLeftColor, renderTexture, 1, 1); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperRightColor, renderTexture, 3, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperRightColor, renderTexture, 2, 1); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerLeftColor, renderTexture, 0, 3); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerLeftColor, renderTexture, 1, 2); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerRightColor, renderTexture, 3, 3); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerRightColor, renderTexture, 2, 2); | |
| } | |
| } | |
| // Test for a bug found during review where the cropSize was not correctly rotated during the | |
| // initialization of the ExternalTexture, which could lead to incorrect textureLoad operations when | |
| // rotating 90 and 270 degrees. | |
| TEST_P(ExternalTextureTests, RotateAndOrFlipTextureLoadSinglePlaneNotSquare) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::Texture sourceTexture = | |
| Create2DTexture(device, 2, 16, kFormat, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| RenderQuad(sourceTexture, | |
| {kGreen.rgbaFloats, kBlack.rgbaFloats, kRed.rgbaFloats, kBlue.rgbaFloats}); | |
| wgpu::Texture renderTexture = Create2DTexture( | |
| device, 2, 2, kFormat, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture dimensionTexture = | |
| Create2DTexture(device, 2, 2, wgpu::TextureFormat::RG32Uint, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| struct RotationExpectation { | |
| wgpu::ExternalTextureRotation rotation; | |
| bool mirrored; | |
| utils::RGBA8 upperLeftColor; | |
| utils::RGBA8 upperRightColor; | |
| utils::RGBA8 lowerLeftColor; | |
| utils::RGBA8 lowerRightColor; | |
| }; | |
| std::array<RotationExpectation, 8> expectations = { | |
| {{wgpu::ExternalTextureRotation::Rotate0Degrees, false, utils::RGBA8::kGreen, | |
| utils::RGBA8::kBlack, utils::RGBA8::kRed, utils::RGBA8::kBlue}, | |
| {wgpu::ExternalTextureRotation::Rotate90Degrees, false, utils::RGBA8::kRed, | |
| utils::RGBA8::kGreen, utils::RGBA8::kBlue, utils::RGBA8::kBlack}, | |
| {wgpu::ExternalTextureRotation::Rotate180Degrees, false, utils::RGBA8::kBlue, | |
| utils::RGBA8::kRed, utils::RGBA8::kBlack, utils::RGBA8::kGreen}, | |
| {wgpu::ExternalTextureRotation::Rotate270Degrees, false, utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlue, utils::RGBA8::kGreen, utils::RGBA8::kRed}, | |
| {wgpu::ExternalTextureRotation::Rotate0Degrees, true, utils::RGBA8::kBlack, | |
| utils::RGBA8::kGreen, utils::RGBA8::kBlue, utils::RGBA8::kRed}, | |
| {wgpu::ExternalTextureRotation::Rotate90Degrees, true, utils::RGBA8::kGreen, | |
| utils::RGBA8::kRed, utils::RGBA8::kBlack, utils::RGBA8::kBlue}, | |
| {wgpu::ExternalTextureRotation::Rotate180Degrees, true, utils::RGBA8::kRed, | |
| utils::RGBA8::kBlue, utils::RGBA8::kGreen, utils::RGBA8::kBlack}, | |
| {wgpu::ExternalTextureRotation::Rotate270Degrees, true, utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlack, utils::RGBA8::kRed, utils::RGBA8::kGreen}}}; | |
| wgpu::ShaderModule loadModule = utils::CreateShaderModule(device, R"( | |
| @group(0) @binding(0) var t : texture_external; | |
| struct FragOut { | |
| @location(0) color: vec4f, | |
| @location(1) dimension: vec2u, | |
| }; | |
| @fragment fn main(@builtin(position) FragCoord : vec4f) | |
| -> FragOut { | |
| let dimension = textureDimensions(t); | |
| let coords = textureDimensions(t) / 2 + vec2u(FragCoord.xy) - vec2(1, 1); | |
| return FragOut(textureLoad(t, coords), dimension); | |
| })"); | |
| wgpu::BufferDescriptor dimensionBufferDesc; | |
| dimensionBufferDesc.size = 256; | |
| dimensionBufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; | |
| wgpu::Buffer dimensionBuffer = device.CreateBuffer(&dimensionBufferDesc); | |
| for (const RotationExpectation& exp : expectations) { | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = loadModule; | |
| descriptor.cFragment.targetCount = 2; | |
| descriptor.cTargets[0].format = kFormat; | |
| descriptor.cTargets[1].format = wgpu::TextureFormat::RG32Uint; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture and make the bindgroup. | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(sourceTexture); | |
| externalDesc.rotation = exp.rotation; | |
| externalDesc.mirrored = exp.mirrored; | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| wgpu::BindGroup bindGroup = | |
| utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| utils::ComboRenderPassDescriptor renderPass( | |
| {renderTexture.CreateView(), dimensionTexture.CreateView()}); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| { | |
| wgpu::ImageCopyTexture imageCopyTexture = | |
| utils::CreateImageCopyTexture(dimensionTexture, 0, {0, 0, 0}); | |
| wgpu::ImageCopyBuffer imageCopyBuffer = | |
| utils::CreateImageCopyBuffer(dimensionBuffer, 0, 256, 1); | |
| wgpu::Extent3D size = {1, 1, 1}; | |
| encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &size); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperLeftColor, renderTexture, 0, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperRightColor, renderTexture, 1, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerLeftColor, renderTexture, 0, 1); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerRightColor, renderTexture, 1, 1); | |
| if (exp.rotation == wgpu::ExternalTextureRotation::Rotate90Degrees || | |
| exp.rotation == wgpu::ExternalTextureRotation::Rotate270Degrees) { | |
| EXPECT_BUFFER_U32_EQ(sourceTexture.GetHeight(), dimensionBuffer, 0); | |
| EXPECT_BUFFER_U32_EQ(sourceTexture.GetWidth(), dimensionBuffer, 4); | |
| } else { | |
| EXPECT_BUFFER_U32_EQ(sourceTexture.GetWidth(), dimensionBuffer, 0); | |
| EXPECT_BUFFER_U32_EQ(sourceTexture.GetHeight(), dimensionBuffer, 4); | |
| } | |
| } | |
| } | |
| // Test draws a green square in the upper left quadrant, a black square in the upper right, a red | |
| // square in the lower left and a blue square in the lower right. The image is then sampled as an | |
| // external texture and rotated 0, 90, 180, and 270 degrees with and without the y-axis flipped. | |
| TEST_P(ExternalTextureTests, RotateAndOrFlipSampleMultiplanar) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| // TODO(41487285): Fails on OpenGL ANGLE D3D11 Intel (but not other configs). Suppress since we | |
| // don't want to ship that configuration. | |
| DAWN_SUPPRESS_TEST_IF(IsANGLED3D11() && IsIntel()); | |
| wgpu::Texture sourceTexturePlane0 = | |
| Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::R8Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture sourceTexturePlane1 = | |
| Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::RG8Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| RenderQuad(sourceTexturePlane0, {{kGreen.y}, {kBlack.y}, {kRed.y}, {kBlue.y}}); | |
| RenderQuad(sourceTexturePlane1, | |
| {{kGreen.u, kGreen.v}, {kBlack.u, kBlack.v}, {kRed.u, kRed.v}, {kBlue.u, kBlue.v}}); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| // Control case to verify mirrored and rotation defaults | |
| { | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Create an ExternalTextureDescriptor from the texture view | |
| wgpu::ExternalTextureDescriptor externalDesc = | |
| InitExternalTextureDescriptor(sourceTexturePlane0, sourceTexturePlane1); | |
| // Import the external texture | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kGreen, renderTexture, 0, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kBlack, renderTexture, 3, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kRed, renderTexture, 0, 3); | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kBlue, renderTexture, 3, 3); | |
| } | |
| struct RotationExpectation { | |
| wgpu::ExternalTextureRotation rotation; | |
| bool mirrored; | |
| utils::RGBA8 upperLeftColor; | |
| utils::RGBA8 upperRightColor; | |
| utils::RGBA8 lowerLeftColor; | |
| utils::RGBA8 lowerRightColor; | |
| }; | |
| std::array<RotationExpectation, 8> expectations = { | |
| {{wgpu::ExternalTextureRotation::Rotate0Degrees, false, utils::RGBA8::kGreen, | |
| utils::RGBA8::kBlack, utils::RGBA8::kRed, utils::RGBA8::kBlue}, | |
| {wgpu::ExternalTextureRotation::Rotate90Degrees, false, utils::RGBA8::kRed, | |
| utils::RGBA8::kGreen, utils::RGBA8::kBlue, utils::RGBA8::kBlack}, | |
| {wgpu::ExternalTextureRotation::Rotate180Degrees, false, utils::RGBA8::kBlue, | |
| utils::RGBA8::kRed, utils::RGBA8::kBlack, utils::RGBA8::kGreen}, | |
| {wgpu::ExternalTextureRotation::Rotate270Degrees, false, utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlue, utils::RGBA8::kGreen, utils::RGBA8::kRed}, | |
| {wgpu::ExternalTextureRotation::Rotate0Degrees, true, utils::RGBA8::kBlack, | |
| utils::RGBA8::kGreen, utils::RGBA8::kBlue, utils::RGBA8::kRed}, | |
| {wgpu::ExternalTextureRotation::Rotate90Degrees, true, utils::RGBA8::kGreen, | |
| utils::RGBA8::kRed, utils::RGBA8::kBlack, utils::RGBA8::kBlue}, | |
| {wgpu::ExternalTextureRotation::Rotate180Degrees, true, utils::RGBA8::kRed, | |
| utils::RGBA8::kBlue, utils::RGBA8::kGreen, utils::RGBA8::kBlack}, | |
| {wgpu::ExternalTextureRotation::Rotate270Degrees, true, utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlack, utils::RGBA8::kRed, utils::RGBA8::kGreen}}}; | |
| for (const RotationExpectation& exp : expectations) { | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = | |
| InitExternalTextureDescriptor(sourceTexturePlane0, sourceTexturePlane1); | |
| externalDesc.rotation = exp.rotation; | |
| externalDesc.mirrored = exp.mirrored; | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperLeftColor, renderTexture, 0, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperLeftColor, renderTexture, 1, 1); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperRightColor, renderTexture, 3, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperRightColor, renderTexture, 2, 1); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerLeftColor, renderTexture, 0, 3); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerLeftColor, renderTexture, 1, 2); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerRightColor, renderTexture, 3, 3); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerRightColor, renderTexture, 2, 2); | |
| } | |
| } | |
| // This test draws a 2x2 multi-colored square surrounded by a 1px black border. We test the external | |
| // texture crop functionality by cropping to specific ranges inside the texture. | |
| TEST_P(ExternalTextureTests, CropSinglePlane) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::Texture sourceTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| RenderQuad(sourceTexture, {kGreen.rgbaFloats, kColor1.rgbaFloats, kRed.rgbaFloats, | |
| kBlue.rgbaFloats, kBlack.rgbaFloats, 0.5}); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| struct CropExpectation { | |
| wgpu::Origin2D cropOrigin; | |
| wgpu::Extent2D cropSize; | |
| wgpu::ExternalTextureRotation rotation; | |
| utils::RGBA8 upperLeftColor; | |
| utils::RGBA8 upperRightColor; | |
| utils::RGBA8 lowerLeftColor; | |
| utils::RGBA8 lowerRightColor; | |
| }; | |
| std::array<CropExpectation, 9> expectations = {{ | |
| {{0, 0}, | |
| {kWidth, kHeight}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlack}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 4, kHeight / 4}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kGreen}, | |
| {{kWidth / 2, kHeight / 4}, | |
| {kWidth / 4, kHeight / 4}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| kColor1.rgba, | |
| kColor1.rgba, | |
| kColor1.rgba, | |
| kColor1.rgba}, | |
| {{kWidth / 4, kHeight / 2}, | |
| {kWidth / 4, kHeight / 4}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kRed}, | |
| {{kWidth / 2, kHeight / 2}, | |
| {kWidth / 4, kHeight / 4}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlue}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 2, kHeight / 2}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kGreen, | |
| kColor1.rgba, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kBlue}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 2, kHeight / 2}, | |
| wgpu::ExternalTextureRotation::Rotate90Degrees, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kBlue, | |
| kColor1.rgba}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 2, kHeight / 2}, | |
| wgpu::ExternalTextureRotation::Rotate180Degrees, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kRed, | |
| kColor1.rgba, | |
| utils::RGBA8::kGreen}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 2, kHeight / 2}, | |
| wgpu::ExternalTextureRotation::Rotate270Degrees, | |
| kColor1.rgba, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kRed}, | |
| }}; | |
| for (const CropExpectation& exp : expectations) { | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(sourceTexture); | |
| externalDesc.rotation = exp.rotation; | |
| externalDesc.cropOrigin = exp.cropOrigin; | |
| externalDesc.cropSize = exp.cropSize; | |
| externalDesc.apparentSize = exp.cropSize; | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperLeftColor, renderTexture, 0, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperRightColor, renderTexture, 3, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerLeftColor, renderTexture, 0, 3); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerRightColor, renderTexture, 3, 3); | |
| } | |
| } | |
| // Test that the apparentSize takes effect by using it to scale a texture and "blitting" it. | |
| TEST_P(ExternalTextureTests, ApparentSizeEffect) { | |
| // Create the test pipeline | |
| wgpu::ShaderModule blitAndOutputSize = utils::CreateShaderModule(this->device, R"( | |
| @group(0) @binding(0) var t : texture_external; | |
| struct FragOut { | |
| @location(0) color: vec4f, | |
| @location(1) dimension: vec2u, | |
| }; | |
| @fragment fn main(@builtin(position) FragCoord : vec4f) | |
| -> FragOut { | |
| let dimensions = textureDimensions(t); | |
| return FragOut(textureLoad(t, vec2u(FragCoord.xy)), dimensions); | |
| })"); | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = blitAndOutputSize; | |
| descriptor.cFragment.targetCount = 2; | |
| descriptor.cTargets[0].format = kFormat; | |
| descriptor.cTargets[1].format = wgpu::TextureFormat::RG32Uint; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // The source texture will contain a quad and have a larger apparent size. | |
| wgpu::Texture sourceTexture = | |
| Create2DTexture(device, 2, 2, kFormat, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| RenderQuad(sourceTexture, | |
| {kGreen.rgbaFloats, kColor1.rgbaFloats, kRed.rgbaFloats, kBlue.rgbaFloats}); | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(sourceTexture); | |
| externalDesc.apparentSize = {8, 16}; | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // The texture that will receive the blit operation, uses apparentSize | |
| wgpu::Texture renderTexture = Create2DTexture( | |
| device, externalDesc.apparentSize.width, externalDesc.apparentSize.height, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture dimensionTexture = | |
| Create2DTexture(device, externalDesc.apparentSize.width, externalDesc.apparentSize.height, | |
| wgpu::TextureFormat::RG32Uint, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| // The buffer that will receive the result of `textureDimensions` | |
| wgpu::BufferDescriptor bufDesc; | |
| bufDesc.size = 256; | |
| bufDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; | |
| wgpu::Buffer dimensionBuffer = device.CreateBuffer(&bufDesc); | |
| wgpu::BindGroup bindGroup = | |
| utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, externalTexture}}); | |
| // Do the blit | |
| utils::ComboRenderPassDescriptor renderPass( | |
| {renderTexture.CreateView(), dimensionTexture.CreateView()}); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| { | |
| wgpu::ImageCopyTexture imageCopyTexture = | |
| utils::CreateImageCopyTexture(dimensionTexture, 0, {0, 0, 0}); | |
| wgpu::ImageCopyBuffer imageCopyBuffer = | |
| utils::CreateImageCopyBuffer(dimensionBuffer, 0, 256, 1); | |
| wgpu::Extent3D size = {1, 1, 1}; | |
| encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &size); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| // Check that the apparentSize is correct and that the center of the quad has moved due to the | |
| // scaling with apparentSize. | |
| EXPECT_BUFFER_U32_EQ(externalDesc.apparentSize.width, dimensionBuffer, 0); | |
| EXPECT_BUFFER_U32_EQ(externalDesc.apparentSize.height, dimensionBuffer, 4); | |
| EXPECT_PIXEL_RGBA8_EQ(kGreen.rgba, renderTexture, 3, 7); | |
| EXPECT_PIXEL_RGBA8_EQ(kColor1.rgba, renderTexture, 4, 7); | |
| EXPECT_PIXEL_RGBA8_EQ(kRed.rgba, renderTexture, 3, 8); | |
| EXPECT_PIXEL_RGBA8_EQ(kBlue.rgba, renderTexture, 4, 8); | |
| } | |
| // This test draws a 2x2 multi-colored square surrounded by a 1px black border. We test the external | |
| // texture crop functionality by cropping to specific ranges inside the texture. | |
| TEST_P(ExternalTextureTests, CropMultiplanar) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::Texture sourceTexturePlane0 = | |
| Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::R8Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture sourceTexturePlane1 = | |
| Create2DTexture(device, kWidth, kHeight, wgpu::TextureFormat::RG8Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| RenderQuad(sourceTexturePlane0, | |
| {{kGreen.y}, {kColor1.y}, {kRed.y}, {kBlue.y}, {kBlack.y}, 0.5}); | |
| RenderQuad(sourceTexturePlane1, {{kGreen.u, kGreen.v}, | |
| {kColor1.u, kColor1.v}, | |
| {kRed.u, kRed.v}, | |
| {kBlue.u, kBlue.v}, | |
| {kBlack.u, kBlack.v}, | |
| 0.5}); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| struct CropExpectation { | |
| wgpu::Origin2D cropOrigin; | |
| wgpu::Extent2D cropSize; | |
| wgpu::ExternalTextureRotation rotation; | |
| utils::RGBA8 upperLeftColor; | |
| utils::RGBA8 upperRightColor; | |
| utils::RGBA8 lowerLeftColor; | |
| utils::RGBA8 lowerRightColor; | |
| }; | |
| std::array<CropExpectation, 9> expectations = { | |
| {{{0, 0}, | |
| {kWidth, kHeight}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlack, | |
| utils::RGBA8::kBlack}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 4, kHeight / 4}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kGreen}, | |
| {{kWidth / 2, kHeight / 4}, | |
| {kWidth / 4, kHeight / 4}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| kColor1.rgba, | |
| kColor1.rgba, | |
| kColor1.rgba, | |
| kColor1.rgba}, | |
| {{kWidth / 4, kHeight / 2}, | |
| {kWidth / 4, kHeight / 4}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kRed}, | |
| {{kWidth / 2, kHeight / 2}, | |
| {kWidth / 4, kHeight / 4}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kBlue}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 2, kHeight / 2}, | |
| wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| utils::RGBA8::kGreen, | |
| kColor1.rgba, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kBlue}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 2, kHeight / 2}, | |
| wgpu::ExternalTextureRotation::Rotate90Degrees, | |
| utils::RGBA8::kRed, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kBlue, | |
| kColor1.rgba}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 2, kHeight / 2}, | |
| wgpu::ExternalTextureRotation::Rotate180Degrees, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kRed, | |
| kColor1.rgba, | |
| utils::RGBA8::kGreen}, | |
| {{kWidth / 4, kHeight / 4}, | |
| {kWidth / 2, kHeight / 2}, | |
| wgpu::ExternalTextureRotation::Rotate270Degrees, | |
| kColor1.rgba, | |
| utils::RGBA8::kBlue, | |
| utils::RGBA8::kGreen, | |
| utils::RGBA8::kRed}}}; | |
| for (const CropExpectation& exp : expectations) { | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = | |
| InitExternalTextureDescriptor(sourceTexturePlane0, sourceTexturePlane1); | |
| externalDesc.rotation = exp.rotation; | |
| externalDesc.cropOrigin = exp.cropOrigin; | |
| externalDesc.cropSize = exp.cropSize; | |
| externalDesc.apparentSize = exp.cropSize; | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperLeftColor, renderTexture, 0, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.upperRightColor, renderTexture, 3, 0); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerLeftColor, renderTexture, 0, 3); | |
| EXPECT_PIXEL_RGBA8_EQ(exp.lowerRightColor, renderTexture, 3, 3); | |
| } | |
| } | |
| // Test that sampling an external texture with non-one alpha preserves the alpha channel. | |
| TEST_P(ExternalTextureTests, SampleExternalTextureAlpha) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::Texture sampledTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| wgpu::Texture renderTexture = | |
| Create2DTexture(device, kWidth, kHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| utils::RGBA8 kColor = {255, 255, 255, 128}; | |
| // Initialize texture with green to ensure it is sampled from later. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass({sampledTexture.CreateView()}, nullptr); | |
| renderPass.cColorAttachments[0].clearValue = {kColor.r / 255.0f, kColor.g / 255.0f, | |
| kColor.b / 255.0f, kColor.a / 255.0f}; | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| pass.End(); | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| } | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = vsModule; | |
| descriptor.cFragment.module = fsSampleExternalTextureModule; | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = InitExternalTextureDescriptor(sampledTexture); | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into the | |
| // upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(3); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| EXPECT_PIXEL_RGBA8_EQ(kColor, renderTexture, 0, 0); | |
| } | |
| // Test for crbug.com/dawn/2472 | |
| TEST_P(ExternalTextureTests, RemappingBugDawn2472) { | |
| auto wgslModule = utils::CreateShaderModule(device, R"( | |
| @vertex | |
| fn vertexMain() -> @builtin(position) vec4f { | |
| return vec4f(1); | |
| } | |
| @group(0) @binding(0) var myTexture: texture_external; | |
| @fragment | |
| fn fragmentMain() -> @location(0) vec4f { | |
| let result = textureLoad(myTexture, vec2u(1, 1)); | |
| return vec4f(1); | |
| })"); | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = wgslModule; | |
| descriptor.cFragment.module = wgslModule; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| ASSERT_NE(pipeline.Get(), nullptr); | |
| } | |
| // Regression test for issue 346174896. | |
| TEST_P(ExternalTextureTests, Regression346174896) { | |
| auto wgslModule = utils::CreateShaderModule(device, R"( | |
| @vertex fn vertexMain() -> @builtin(position) vec4f { | |
| return vec4f(1); | |
| } | |
| @group(0) @binding(1) var<uniform> dimension : vec2u; | |
| @group(0) @binding(0) var t : texture_external; | |
| @fragment fn main(@builtin(position) FragCoord : vec4f) -> @location(0) vec4f { | |
| _ = dimension; | |
| return textureLoad(t, vec2u(0, 0)); | |
| })"); | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = wgslModule; | |
| descriptor.cFragment.module = wgslModule; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| ASSERT_NE(pipeline.Get(), nullptr); | |
| } | |
| TEST_P(ExternalTextureTests, MultipleBindings) { | |
| auto wgslModule = utils::CreateShaderModule(device, R"( | |
| @vertex | |
| fn vertexMain() -> @builtin(position) vec4f { | |
| return vec4f(1); | |
| } | |
| @group(0) @binding(0) var<uniform> u : f32; | |
| @group(0) @binding(1) var s : sampler; | |
| @group(0) @binding(2) var et : texture_external; | |
| @fragment | |
| fn main() -> @location(0) vec4f { | |
| return textureSampleBaseClampToEdge(et, s, vec2f(u)); | |
| })"); | |
| // Pipeline Creation | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = wgslModule; | |
| descriptor.cFragment.module = wgslModule; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| ASSERT_NE(pipeline.Get(), nullptr); | |
| } | |
| DAWN_INSTANTIATE_TEST(ExternalTextureTests, | |
| D3D11Backend(), | |
| D3D12Backend(), | |
| D3D12Backend({}, {"d3d12_use_root_signature_version_1_1"}), | |
| MetalBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| VulkanBackend()); | |
| enum class AccessMode { Sample, Load }; | |
| enum class OOBAccess { BelowBottomLeft, OverTopRight }; | |
| enum class Flip { None, Mirrored }; | |
| using Rotation = wgpu::ExternalTextureRotation; | |
| std::ostream& operator<<(std::ostream& o, Flip flip) { | |
| switch (flip) { | |
| case Flip::None: | |
| o << "no flip "; | |
| break; | |
| case Flip::Mirrored: | |
| o << "mirrored "; | |
| break; | |
| default: | |
| DAWN_UNREACHABLE(); | |
| break; | |
| } | |
| return o; | |
| } | |
| std::ostream& operator<<(std::ostream& o, AccessMode mode) { | |
| switch (mode) { | |
| case AccessMode::Sample: | |
| o << "sample "; | |
| break; | |
| case AccessMode::Load: | |
| o << "load "; | |
| break; | |
| default: | |
| DAWN_UNREACHABLE(); | |
| break; | |
| } | |
| return o; | |
| } | |
| std::ostream& operator<<(std::ostream& o, OOBAccess rect) { | |
| switch (rect) { | |
| case OOBAccess::BelowBottomLeft: | |
| o << "below bottom left "; | |
| break; | |
| case OOBAccess::OverTopRight: | |
| o << "over top right"; | |
| break; | |
| default: | |
| DAWN_UNREACHABLE(); | |
| break; | |
| } | |
| return o; | |
| } | |
| DAWN_TEST_PARAM_STRUCT(OOBTestParams, Rotation, Flip, AccessMode, OOBAccess); | |
| class ExternalTextureOOBTests : public ExternalTextureTestsBase<DawnTestWithParams<OOBTestParams>> { | |
| protected: | |
| void SetUp() override { | |
| ExternalTextureTestsBase<DawnTestWithParams<OOBTestParams>>::SetUp(); | |
| sourceTexturePlane0 = Create2DTexture( | |
| device, kPlaneWidth, kPlaneHeight, wgpu::TextureFormat::R8Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| sourceTexturePlane1 = Create2DTexture( | |
| device, kPlaneWidth, kPlaneHeight, wgpu::TextureFormat::RG8Unorm, | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment); | |
| RenderQuad(sourceTexturePlane0, | |
| {{kGreen.y}, {kColor1.y}, {kRed.y}, {kBlue.y}, {kBlack.y}, 0.5}); | |
| RenderQuad(sourceTexturePlane1, {{kGreen.u, kGreen.v}, | |
| {kColor1.u, kColor1.v}, | |
| {kRed.u, kRed.v}, | |
| {kBlue.u, kBlue.v}, | |
| {kBlack.u, kBlack.v}, | |
| 0.5}); | |
| renderTexture = | |
| Create2DTexture(device, kPlaneWidth, kPlaneHeight, kFormat, | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); | |
| oobTestShaderModule = utils::CreateShaderModule(device, R"( | |
| @vertex fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4f { | |
| var positions = array( | |
| vec4f(-1.0, 1.0, 0.0, 1.0), | |
| vec4f(-1.0, -1.0, 0.0, 1.0), | |
| vec4f(1.0, 1.0, 0.0, 1.0), | |
| vec4f(1.0, -1.0, 0.0, 1.0), | |
| vec4f(-1.0, -1.0, 0.0, 1.0), | |
| vec4f(1.0, 1.0, 0.0, 1.0) | |
| ); | |
| return positions[VertexIndex]; | |
| } | |
| @group(0) @binding(0) var s : sampler; | |
| @group(0) @binding(1) var t : texture_external; | |
| @fragment fn sampleOverTopRight() -> @location(0) vec4f { | |
| return textureSampleBaseClampToEdge(t, s, vec2f(1.1, 1.1)); | |
| } | |
| @fragment fn sampleBelowBottomLeft() -> @location(0) vec4f { | |
| return textureSampleBaseClampToEdge(t, s, vec2f(-0.1, -0.1)); | |
| } | |
| @fragment fn loadOverTopRight() -> @location(0) vec4f { | |
| _ = textureSampleBaseClampToEdge(t, s, vec2f(0.0, 0.0)); | |
| return textureLoad(t, vec2<i32>(5, 5)); | |
| } | |
| @fragment fn loadBelowBottomLeft() -> @location(0) vec4f { | |
| _ = textureSampleBaseClampToEdge(t, s, vec2f(0.0, 0.0)); | |
| return textureLoad(t, vec2<i32>(-1, -1)); | |
| } | |
| )"); | |
| } | |
| std::string GetEntryPoint(AccessMode mode, OOBAccess access) { | |
| switch (mode) { | |
| case AccessMode::Sample: | |
| switch (access) { | |
| case OOBAccess::BelowBottomLeft: | |
| return "sampleBelowBottomLeft"; | |
| case OOBAccess::OverTopRight: | |
| return "sampleOverTopRight"; | |
| default: | |
| DAWN_UNREACHABLE(); | |
| } | |
| break; | |
| case AccessMode::Load: | |
| switch (access) { | |
| case OOBAccess::BelowBottomLeft: | |
| return "loadBelowBottomLeft"; | |
| case OOBAccess::OverTopRight: | |
| return "loadOverTopRight"; | |
| default: | |
| DAWN_UNREACHABLE(); | |
| } | |
| break; | |
| default: | |
| DAWN_UNREACHABLE(); | |
| } | |
| } | |
| void DoTest() { | |
| wgpu::ExternalTextureRotation rotation = GetParam().mRotation; | |
| OOBAccess oobAccess = GetParam().mOOBAccess; | |
| AccessMode accessMode = GetParam().mAccessMode; | |
| Flip flip = GetParam().mFlip; | |
| utils::ComboRenderPipelineDescriptor descriptor; | |
| descriptor.vertex.module = oobTestShaderModule; | |
| descriptor.vertex.entryPoint = "main"; | |
| descriptor.cFragment.module = oobTestShaderModule; | |
| std::string fragmentEntryPoint = GetEntryPoint(accessMode, oobAccess); | |
| descriptor.cFragment.entryPoint = fragmentEntryPoint.c_str(); | |
| descriptor.cTargets[0].format = kFormat; | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); | |
| // Import the external texture | |
| wgpu::ExternalTextureDescriptor externalDesc = | |
| InitExternalTextureDescriptor(sourceTexturePlane0, sourceTexturePlane1); | |
| externalDesc.rotation = rotation; | |
| externalDesc.mirrored = flip == Flip::Mirrored; | |
| wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc); | |
| // Create a sampler and bind group | |
| wgpu::Sampler sampler = device.CreateSampler(); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, sampler}, {1, externalTexture}}); | |
| // Run the shader, which should sample from the external texture and draw a triangle into | |
| // the upper left corner of the render texture. | |
| wgpu::TextureView renderView = renderTexture.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); | |
| { | |
| pass.SetPipeline(pipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| // Border color is black. | |
| EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kBlack, renderTexture, 0, 0); | |
| } | |
| static constexpr uint32_t kPlaneWidth = 5; | |
| static constexpr uint32_t kPlaneHeight = 5; | |
| wgpu::ShaderModule oobTestShaderModule; | |
| wgpu::Texture sourceTexturePlane0; | |
| wgpu::Texture sourceTexturePlane1; | |
| wgpu::Texture renderTexture; | |
| }; | |
| // Test for OOB access | |
| TEST_P(ExternalTextureOOBTests, ExternalTextureOOB) { | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| DoTest(); | |
| } | |
| DAWN_INSTANTIATE_TEST_P( | |
| ExternalTextureOOBTests, | |
| {D3D11Backend(), D3D12Backend(), D3D12Backend({}, {"d3d12_use_root_signature_version_1_1"}), | |
| MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()}, | |
| std::vector<wgpu::ExternalTextureRotation>({wgpu::ExternalTextureRotation::Rotate0Degrees, | |
| wgpu::ExternalTextureRotation::Rotate90Degrees, | |
| wgpu::ExternalTextureRotation::Rotate180Degrees, | |
| wgpu::ExternalTextureRotation::Rotate270Degrees}), | |
| std::vector<Flip>({Flip::None, Flip::Mirrored}), | |
| std::vector<AccessMode>({AccessMode::Sample, AccessMode::Load}), | |
| std::vector<OOBAccess>({OOBAccess::BelowBottomLeft, OOBAccess::OverTopRight})); | |
| } // anonymous namespace | |
| } // namespace dawn |