diff --git a/src/dawn/tests/end2end/VideoViewsTests.cpp b/src/dawn/tests/end2end/VideoViewsTests.cpp index bbc04a2dbe..eee92bdd3b 100644 --- a/src/dawn/tests/end2end/VideoViewsTests.cpp +++ b/src/dawn/tests/end2end/VideoViewsTests.cpp @@ -71,45 +71,58 @@ std::vector VideoViewsTests::GetTestTextureData(wgpu::TextureFormat for constexpr uint8_t Yu = kYellowYUVColor[kYUVChromaPlaneIndex].r; constexpr uint8_t Yv = kYellowYUVColor[kYUVChromaPlaneIndex].g; + constexpr uint8_t Wy = kWhiteYUVColor[kYUVLumaPlaneIndex].r; + constexpr uint8_t Wu = kWhiteYUVColor[kYUVChromaPlaneIndex].r; + constexpr uint8_t Wv = kWhiteYUVColor[kYUVChromaPlaneIndex].g; + + constexpr uint8_t Ry = kRedYUVColor[kYUVLumaPlaneIndex].r; + constexpr uint8_t Ru = kRedYUVColor[kYUVChromaPlaneIndex].r; + constexpr uint8_t Rv = kRedYUVColor[kYUVChromaPlaneIndex].g; + + constexpr uint8_t By = kBlueYUVColor[kYUVLumaPlaneIndex].r; + constexpr uint8_t Bu = kBlueYUVColor[kYUVChromaPlaneIndex].r; + constexpr uint8_t Bv = kBlueYUVColor[kYUVChromaPlaneIndex].g; + switch (format) { // The first 16 bytes is the luma plane (Y), followed by the chroma plane (UV) which // is half the number of bytes (subsampled by 2) but same bytes per line as luma // plane. case wgpu::TextureFormat::R8BG8Biplanar420Unorm: if (isCheckerboard) { - constexpr uint8_t Wy = kWhiteYUVColor[kYUVLumaPlaneIndex].r; - constexpr uint8_t Wu = kWhiteYUVColor[kYUVChromaPlaneIndex].r; - constexpr uint8_t Wv = kWhiteYUVColor[kYUVChromaPlaneIndex].g; - - constexpr uint8_t Ry = kRedYUVColor[kYUVLumaPlaneIndex].r; - constexpr uint8_t Ru = kRedYUVColor[kYUVChromaPlaneIndex].r; - constexpr uint8_t Rv = kRedYUVColor[kYUVChromaPlaneIndex].g; - - constexpr uint8_t By = kBlueYUVColor[kYUVLumaPlaneIndex].r; - constexpr uint8_t Bu = kBlueYUVColor[kYUVChromaPlaneIndex].r; - constexpr uint8_t Bv = kBlueYUVColor[kYUVChromaPlaneIndex].g; - - // clang-format off - return { - Wy, Wy, Ry, Ry, // plane 0, start + 0 - Wy, Wy, Ry, Ry, - Yy, Yy, By, By, - Yy, Yy, By, By, - Wu, Wv, Ru, Rv, // plane 1, start + 16 - Yu, Yv, Bu, Bv, - }; - // clang-format on + return { + Wy, Wy, Ry, Ry, // plane 0, start + 0 + Wy, Wy, Ry, Ry, // + Yy, Yy, By, By, // + Yy, Yy, By, By, // + Wu, Wv, Ru, Rv, // plane 1, start + 16 + Yu, Yv, Bu, Bv, // + }; } else { - // clang-format off - return { - Yy, Yy, Yy, Yy, // plane 0, start + 0 - Yy, Yy, Yy, Yy, - Yy, Yy, Yy, Yy, - Yy, Yy, Yy, Yy, - Yu, Yv, Yu, Yv, // plane 1, start + 16 - Yu, Yv, Yu, Yv, - }; - // clang-format on + return { + Yy, Yy, Yy, Yy, // plane 0, start + 0 + Yy, Yy, Yy, Yy, // + Yy, Yy, Yy, Yy, // + Yy, Yy, Yy, Yy, // + Yu, Yv, Yu, Yv, // plane 1, start + 16 + Yu, Yv, Yu, Yv, // + }; + } + case wgpu::TextureFormat::RGBA8Unorm: + // Combines both NV12 planes by directly mapping back to RGB: R=Y, G=U, B=V. + if (isCheckerboard) { + return { + Yy, Yu, Yv, Yy, Yu, Yv, By, Bu, Bv, By, Bu, Bv, // + Yy, Yu, Yv, Yy, Yu, Yv, By, Bu, Bv, By, Bu, Bv, // + Wy, Wu, Wv, Wy, Wu, Wv, Ry, Ru, Rv, Ry, Ru, Rv, // + Wy, Wu, Wv, Wy, Wu, Wv, Ry, Ru, Rv, Ry, Ru, Rv, // + }; + } else { + return { + Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, // + Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, // + Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, // + Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, Yy, Yu, Yv, // + }; } default: UNREACHABLE(); @@ -323,8 +336,8 @@ TEST_P(VideoViewsTests, NV12SampleUVtoRG) { mBackend->DestroyVideoTextureForTest(std::move(platformTexture)); } -// Renders a NV12 "checkerboard" texture into a RGB quad then checks the color at specific -// points to ensure the image has not been flipped. +// Renders a NV12 "checkerboard" texture into a RGB quad, then checks the the entire +// contents to ensure the image has not been flipped. TEST_P(VideoViewsTests, NV12SampleYUVtoRGB) { std::unique_ptr platformTexture = mBackend->CreateVideoTextureForTest(wgpu::TextureFormat::R8BG8Biplanar420Unorm, @@ -385,27 +398,90 @@ TEST_P(VideoViewsTests, NV12SampleYUVtoRGB) { wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - // Test four corners of the checkerboard image (YUV color space). - utils::RGBA8 yellowYUV(kYellowYUVColor[kYUVLumaPlaneIndex].r, - kYellowYUVColor[kYUVChromaPlaneIndex].r, - kYellowYUVColor[kYUVChromaPlaneIndex].g, 0xFF); - EXPECT_PIXEL_RGBA8_EQ(yellowYUV, renderPass.color, 0, 0); // top left + std::vector expectedData = GetTestTextureData(wgpu::TextureFormat::RGBA8Unorm, true); + std::vector expectedRGBA; + for (uint8_t i = 0; i < expectedData.size(); i += 3) { + expectedRGBA.push_back({expectedData[i], expectedData[i + 1], expectedData[i + 2], 0xFF}); + } - utils::RGBA8 redYUV(kRedYUVColor[kYUVLumaPlaneIndex].r, kRedYUVColor[kYUVChromaPlaneIndex].r, - kRedYUVColor[kYUVChromaPlaneIndex].g, 0xFF); - EXPECT_PIXEL_RGBA8_EQ(redYUV, renderPass.color, kYUVImageDataWidthInTexels - 1, - kYUVImageDataHeightInTexels - 1); // bottom right + EXPECT_TEXTURE_EQ(expectedRGBA.data(), renderPass.color, {0, 0}, + {kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels}); + mBackend->DestroyVideoTextureForTest(std::move(platformTexture)); +} - utils::RGBA8 blueYUV(kBlueYUVColor[kYUVLumaPlaneIndex].r, kBlueYUVColor[kYUVChromaPlaneIndex].r, - kBlueYUVColor[kYUVChromaPlaneIndex].g, 0xFF); - EXPECT_PIXEL_RGBA8_EQ(blueYUV, renderPass.color, kYUVImageDataWidthInTexels - 1, - 0); // top right +// Renders a NV12 "checkerboard" texture into a RGB quad with two samplers, then checks the the +// entire contents to ensure the image has not been flipped. +TEST_P(VideoViewsTests, NV12SampleYUVtoRGBMultipleSamplers) { + std::unique_ptr platformTexture = + mBackend->CreateVideoTextureForTest(wgpu::TextureFormat::R8BG8Biplanar420Unorm, + wgpu::TextureUsage::TextureBinding, + /*isCheckerboard*/ true, + /*initialized*/ true); + ASSERT_NE(platformTexture.get(), nullptr); + if (!platformTexture->CanWrapAsWGPUTexture()) { + mBackend->DestroyVideoTextureForTest(std::move(platformTexture)); + GTEST_SKIP() << "Skipped because not supported."; + } - utils::RGBA8 whiteYUV(kWhiteYUVColor[kYUVLumaPlaneIndex].r, - kWhiteYUVColor[kYUVChromaPlaneIndex].r, - kWhiteYUVColor[kYUVChromaPlaneIndex].g, 0xFF); - EXPECT_PIXEL_RGBA8_EQ(whiteYUV, renderPass.color, 0, - kYUVImageDataHeightInTexels - 1); // bottom left + wgpu::TextureViewDescriptor lumaViewDesc; + lumaViewDesc.format = wgpu::TextureFormat::R8Unorm; + lumaViewDesc.aspect = wgpu::TextureAspect::Plane0Only; + wgpu::TextureView lumaTextureView = platformTexture->wgpuTexture.CreateView(&lumaViewDesc); + + wgpu::TextureViewDescriptor chromaViewDesc; + chromaViewDesc.format = wgpu::TextureFormat::RG8Unorm; + chromaViewDesc.aspect = wgpu::TextureAspect::Plane1Only; + wgpu::TextureView chromaTextureView = platformTexture->wgpuTexture.CreateView(&chromaViewDesc); + + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor; + renderPipelineDescriptor.vertex.module = GetTestVertexShaderModule(); + + renderPipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"( + @group(0) @binding(0) var sampler0 : sampler; + @group(0) @binding(1) var sampler1 : sampler; + @group(0) @binding(2) var lumaTexture : texture_2d; + @group(0) @binding(3) var chromaTexture : texture_2d; + + @fragment + fn main(@location(0) texCoord : vec2f) -> @location(0) vec4f { + let y : f32 = textureSample(lumaTexture, sampler0, texCoord).r; + let u : f32 = textureSample(chromaTexture, sampler1, texCoord).r; + let v : f32 = textureSample(chromaTexture, sampler1, texCoord).g; + return vec4f(y, u, v, 1.0); + })"); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass( + device, kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels); + renderPipelineDescriptor.cTargets[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); + + wgpu::Sampler sampler0 = device.CreateSampler(); + wgpu::Sampler sampler1 = device.CreateSampler(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(renderPipeline); + pass.SetBindGroup( + 0, utils::MakeBindGroup( + device, renderPipeline.GetBindGroupLayout(0), + {{0, sampler0}, {1, sampler1}, {2, lumaTextureView}, {3, chromaTextureView}})); + pass.Draw(6); + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = GetTestTextureData(wgpu::TextureFormat::RGBA8Unorm, true); + std::vector expectedRGBA; + for (uint8_t i = 0; i < expectedData.size(); i += 3) { + expectedRGBA.push_back({expectedData[i], expectedData[i + 1], expectedData[i + 2], 0xFF}); + } + + EXPECT_TEXTURE_EQ(expectedRGBA.data(), renderPass.color, {0, 0}, + {kYUVImageDataWidthInTexels, kYUVImageDataHeightInTexels}); mBackend->DestroyVideoTextureForTest(std::move(platformTexture)); } diff --git a/src/dawn/tests/end2end/VideoViewsTests_win.cpp b/src/dawn/tests/end2end/VideoViewsTests_win.cpp index 45991dd5ab..d8f4c25473 100644 --- a/src/dawn/tests/end2end/VideoViewsTests_win.cpp +++ b/src/dawn/tests/end2end/VideoViewsTests_win.cpp @@ -74,6 +74,13 @@ class VideoViewsTestBackendWin : public VideoViewsTestBackend { ASSERT_GE(featureOptions5.SharedResourceTier, D3D11_SHARED_RESOURCE_TIER_2); + // Not all D3D11 devices support NV12 textures. + UINT formatSupport; + hr = d3d11Device->CheckFormatSupport(DXGI_FORMAT_NV12, &formatSupport); + ASSERT_EQ(hr, S_OK); + + ASSERT_TRUE(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D); + mD3d11Device = std::move(d3d11Device); }