/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gtest/gtest.h" #include "mozilla/widget/DMABufSurface.h" #include "mozilla/gfx/FileHandleWrapper.h" #include "mozilla/ipc/FileDescriptor.h" #include "mozilla/layers/LayersSurfaces.h" #include "mozilla/NotNull.h" #include "nsTArray.h" #include #include using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::layers; // These tests verify that DMABufSurface descriptors and surfaces round-trip // stably through serialization and import. static RefPtr MakeFd() { int fd = open("/dev/null", O_RDONLY | O_CLOEXEC); if (fd == -1) return nullptr; return new FileHandleWrapper(UniqueFileHandle(fd)); } // Matches what DMABufSurfaceRGBA::Serialize() produces for a single-plane // 128×128 RGBA surface. static SurfaceDescriptor MakeRGBADescriptor(RefPtr fd) { AutoTArray>, 4> fds; fds.AppendElement(WrapNotNull(fd)); AutoTArray modifiers = {0}; AutoTArray width = {128}, height = {128}, strides = {512}, offsets = {0}; AutoTArray format; // intentionally empty; Serialize() leaves this unset for RGBA AutoTArray>, 1> fence; AutoTArray refCount; return SurfaceDescriptor(SurfaceDescriptorDMABuf( DMABufSurface::SURFACE_RGBA, 0, modifiers, 0, fds, width, height, width, height, format, strides, offsets, gfx::YUVColorSpace::BT601, gfx::ColorRange::LIMITED, gfx::ColorSpace2::UNKNOWN, gfx::TransferFunction::Default, 0, fence, 1, 0, refCount, nullptr, false)); } // Matches what DMABufSurfaceYUV::Serialize() produces for a two-plane 128×128 // (NV12-style, 4:2:0) YUV surface. static SurfaceDescriptor MakeYUVDescriptor(RefPtr fd0, RefPtr fd1) { AutoTArray>, 4> fds; fds.AppendElement(WrapNotNull(fd0)); fds.AppendElement(WrapNotNull(fd1)); AutoTArray modifiers = {0, 0}; // Y plane full size, UV plane half size (4:2:0 chroma). AutoTArray width = {128, 64}, height = {128, 64}, widthAligned = {128, 64}, heightAligned = {128, 64}, format = {0, 0}, strides = {128, 128}, offsets = {0, 0}; AutoTArray>, 1> fence; AutoTArray refCount; return SurfaceDescriptor(SurfaceDescriptorDMABuf( DMABufSurface::SURFACE_YUV, VA_FOURCC_NV12, modifiers, 0, fds, width, height, widthAligned, heightAligned, format, strides, offsets, gfx::YUVColorSpace::BT601, gfx::ColorRange::LIMITED, gfx::ColorSpace2::UNKNOWN, gfx::TransferFunction::Default, 0, fence, 1, 0, refCount, nullptr, false)); } // Run 3 serialize → import cycles for a single-plane RGBA surface. static void RGBARoundtrip(RefPtr surface) { SurfaceDescriptor desc; for (int i = 0; i < 3; i++) { ASSERT_TRUE(surface->Serialize(desc)); const auto& d = desc.get_SurfaceDescriptorDMABuf(); EXPECT_EQ(d.bufferType(), uint32_t{DMABufSurface::SURFACE_RGBA}); EXPECT_EQ(d.fds().Length(), 1u); EXPECT_EQ(d.width()[0], 128u); EXPECT_EQ(d.height()[0], 128u); EXPECT_EQ(d.strides()[0], 512u); EXPECT_EQ(d.modifier()[0], uint64_t{0}); surface = DMABufSurface::CreateDMABufSurface(desc); ASSERT_NE(surface, nullptr); EXPECT_EQ(surface->GetWidth(), 128); EXPECT_EQ(surface->GetHeight(), 128); } } // Run 3 serialize → import cycles for a two-plane YUV surface. static void YUVRoundtrip(RefPtr surface) { SurfaceDescriptor desc; for (int i = 0; i < 3; i++) { ASSERT_TRUE(surface->Serialize(desc)); const auto& d = desc.get_SurfaceDescriptorDMABuf(); EXPECT_EQ(d.bufferType(), uint32_t{DMABufSurface::SURFACE_YUV}); EXPECT_EQ(d.fds().Length(), 2u); EXPECT_EQ(d.width()[0], 128u); EXPECT_EQ(d.height()[0], 128u); EXPECT_EQ(d.width()[1], 64u); EXPECT_EQ(d.height()[1], 64u); EXPECT_EQ(d.format().Length(), 2u); EXPECT_EQ(d.modifier().Length(), 2u); surface = DMABufSurface::CreateDMABufSurface(desc); ASSERT_NE(surface, nullptr); EXPECT_EQ(surface->GetWidth(0), 128); EXPECT_EQ(surface->GetHeight(0), 128); EXPECT_EQ(surface->GetWidth(1), 64); EXPECT_EQ(surface->GetHeight(1), 64); } } // Test both starting points for RGBA: from a descriptor (compositor side, // verify initial import) and from a surface (GPU process side, no descriptor // needed thanks to WGPUDMABufInfo). TEST(DMABufSurface, RGBARoundtrip) { { RefPtr fd = MakeFd(); ASSERT_NE(fd, nullptr); RefPtr surface = DMABufSurface::CreateDMABufSurface(MakeRGBADescriptor(fd)); ASSERT_NE(surface, nullptr); EXPECT_EQ(surface->GetWidth(), 128); EXPECT_EQ(surface->GetHeight(), 128); RGBARoundtrip(surface); } { RefPtr fd = MakeFd(); ASSERT_NE(fd, nullptr); webgpu::ffi::WGPUDMABufInfo info{true, 0, 1, {}, {512}}; RefPtr surface = DMABufSurfaceRGBA::CreateDMABufSurface(std::move(fd), info, 128, 128); ASSERT_NE(surface, nullptr); EXPECT_EQ(surface->GetWidth(), 128); EXPECT_EQ(surface->GetHeight(), 128); RGBARoundtrip(surface); } } // YUV surfaces require VA-API hardware to create from scratch, so the surface // is always bootstrapped via descriptor import. Verify that import then run // 3 serialize/import cycles from the surface side. TEST(DMABufSurface, YUVRoundtrip) { RefPtr fd0 = MakeFd(), fd1 = MakeFd(); ASSERT_NE(fd0, nullptr); ASSERT_NE(fd1, nullptr); RefPtr surface = DMABufSurface::CreateDMABufSurface(MakeYUVDescriptor(fd0, fd1)); ASSERT_NE(surface, nullptr); EXPECT_EQ(surface->GetWidth(0), 128); EXPECT_EQ(surface->GetHeight(0), 128); EXPECT_EQ(surface->GetWidth(1), 64); EXPECT_EQ(surface->GetHeight(1), 64); YUVRoundtrip(surface); }