| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/platform/graphics/video_frame_image_util.h" |
| |
| #include "base/test/task_environment.h" |
| #include "build/build_config.h" |
| #include "components/viz/common/gpu/raster_context_provider.h" |
| #include "components/viz/test/test_context_provider.h" |
| #include "gpu/command_buffer/common/capabilities.h" |
| #include "media/base/video_frame.h" |
| #include "media/renderers/shared_image_video_frame_test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" |
| #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" |
| #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" |
| #include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h" |
| #include "third_party/blink/renderer/platform/testing/video_frame_utils.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| constexpr auto kTestSize = gfx::Size(64, 64); |
| |
| class ScopedFakeGpuContext { |
| public: |
| explicit ScopedFakeGpuContext(bool disable_imagebitmap) { |
| SharedGpuContext::ResetForTesting(); |
| test_context_provider_ = viz::TestContextProvider::Create(); |
| |
| if (disable_imagebitmap) { |
| // Disable CanvasResourceProvider using GPU. |
| auto& feature_info = test_context_provider_->GetWritableGpuFeatureInfo(); |
| feature_info.enabled_gpu_driver_bug_workarounds.push_back( |
| DISABLE_IMAGEBITMAP_FROM_VIDEO_USING_GPU); |
| } |
| |
| InitializeSharedGpuContext(test_context_provider_.get()); |
| } |
| |
| scoped_refptr<viz::ContextProvider> context_provider() const { |
| return test_context_provider_; |
| } |
| |
| viz::RasterContextProvider* raster_context_provider() const { |
| return test_context_provider_.get(); |
| } |
| |
| ~ScopedFakeGpuContext() { |
| task_environment_.RunUntilIdle(); |
| SharedGpuContext::ResetForTesting(); |
| } |
| |
| private: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| scoped_refptr<viz::TestContextProvider> test_context_provider_; |
| }; |
| |
| } // namespace |
| |
| TEST(VideoFrameImageUtilTest, WillCreateAcceleratedImagesFromVideoFrame) { |
| // I420A isn't a supported zero copy format. |
| { |
| auto alpha_frame = media::VideoFrame::CreateTransparentFrame(kTestSize); |
| EXPECT_FALSE(WillCreateAcceleratedImagesFromVideoFrame(alpha_frame.get())); |
| } |
| |
| // Software RGB frames aren't supported. |
| { |
| auto cpu_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OWNED_MEMORY, |
| media::PIXEL_FORMAT_XRGB); |
| EXPECT_FALSE(WillCreateAcceleratedImagesFromVideoFrame(cpu_frame.get())); |
| } |
| |
| // GpuMemoryBuffer frames aren't supported. |
| { |
| auto cpu_frame = CreateTestFrame( |
| kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER, media::PIXEL_FORMAT_XRGB); |
| EXPECT_FALSE(WillCreateAcceleratedImagesFromVideoFrame(cpu_frame.get())); |
| } |
| |
| // Single mailbox shared images should be supported except on Android. |
| { |
| auto shared_image_frame = CreateTestFrame( |
| kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OPAQUE, media::PIXEL_FORMAT_XRGB); |
| EXPECT_EQ(shared_image_frame->NumTextures(), 1u); |
| EXPECT_TRUE(shared_image_frame->mailbox_holder(0).mailbox.IsSharedImage()); |
| #if defined(OS_ANDROID) |
| EXPECT_FALSE( |
| WillCreateAcceleratedImagesFromVideoFrame(shared_image_frame.get())); |
| #else |
| EXPECT_TRUE( |
| WillCreateAcceleratedImagesFromVideoFrame(shared_image_frame.get())); |
| #endif |
| } |
| } |
| |
| // Android doesn't support zero copy images. |
| #if !defined(OS_ANDROID) |
| TEST(VideoFrameImageUtilTest, CreateImageFromVideoFrameZeroCopy) { |
| ScopedFakeGpuContext fake_context(/*disable_imagebitmap=*/false); |
| auto shared_image_frame = CreateTestFrame( |
| kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OPAQUE, media::PIXEL_FORMAT_XRGB); |
| EXPECT_EQ(shared_image_frame->NumTextures(), 1u); |
| EXPECT_TRUE(shared_image_frame->mailbox_holder(0).mailbox.IsSharedImage()); |
| |
| auto image = CreateImageFromVideoFrame(shared_image_frame); |
| ASSERT_TRUE(image->IsTextureBacked()); |
| EXPECT_EQ(memcmp(image->GetMailboxHolder().mailbox.name, |
| shared_image_frame->mailbox_holder(0).mailbox.name, |
| sizeof(gpu::Mailbox::Name)), |
| 0); |
| } |
| #endif |
| |
| TEST(VideoFrameImageUtilTest, CreateImageFromVideoFrameSoftwareFrame) { |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| auto cpu_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OWNED_MEMORY, |
| media::PIXEL_FORMAT_XRGB); |
| auto image = CreateImageFromVideoFrame(cpu_frame); |
| ASSERT_FALSE(image->IsTextureBacked()); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST(VideoFrameImageUtilTest, CreateImageFromVideoFrameGpuMemoryBufferFrame) { |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| auto cpu_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER, |
| media::PIXEL_FORMAT_NV12); |
| auto image = CreateImageFromVideoFrame(cpu_frame); |
| ASSERT_FALSE(image->IsTextureBacked()); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST(VideoFrameImageUtilTest, CreateImageFromVideoFrameTextureFrame) { |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| auto cpu_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OPAQUE, |
| media::PIXEL_FORMAT_NV12); |
| auto image = CreateImageFromVideoFrame(cpu_frame); |
| |
| // An unaccelerated image can't be created from a texture based VideoFrame |
| // without a viz::RasterContextProvider. |
| ASSERT_FALSE(image); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST(VideoFrameImageUtilTest, |
| CreateAcceleratedImageFromVideoFrameBasicSoftwareFrame) { |
| ScopedFakeGpuContext fake_context(/*disable_imagebitmap=*/false); |
| auto cpu_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OWNED_MEMORY, |
| media::PIXEL_FORMAT_XRGB); |
| auto image = CreateImageFromVideoFrame(cpu_frame); |
| ASSERT_TRUE(image->IsTextureBacked()); |
| } |
| |
| TEST(VideoFrameImageUtilTest, CreateAcceleratedImageFromGpuMemoryBufferFrame) { |
| ScopedFakeGpuContext fake_context(/*disable_imagebitmap=*/false); |
| auto gmb_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER, |
| media::PIXEL_FORMAT_NV12); |
| auto image = CreateImageFromVideoFrame(gmb_frame); |
| ASSERT_TRUE(image->IsTextureBacked()); |
| } |
| |
| TEST(VideoFrameImageUtilTest, CreateAcceleratedImageFromTextureFrame) { |
| ScopedFakeGpuContext fake_context(/*disable_imagebitmap=*/false); |
| |
| auto texture_frame = media::CreateSharedImageRGBAFrame( |
| fake_context.context_provider(), kTestSize, gfx::Rect(kTestSize), |
| base::DoNothing::Once()); |
| auto image = CreateImageFromVideoFrame(texture_frame, |
| /*allow_zero_copy_images=*/false); |
| ASSERT_TRUE(image->IsTextureBacked()); |
| } |
| |
| TEST(VideoFrameImageUtilTest, FlushedAcceleratedImage) { |
| ScopedFakeGpuContext fake_context(/*disable_imagebitmap=*/false); |
| |
| auto texture_frame = media::CreateSharedImageRGBAFrame( |
| fake_context.context_provider(), kTestSize, gfx::Rect(kTestSize), |
| base::DoNothing::Once()); |
| |
| auto* raster_context_provider = fake_context.raster_context_provider(); |
| ASSERT_TRUE(raster_context_provider); |
| |
| auto provider = CreateResourceProviderForVideoFrame(IntSize(kTestSize), |
| raster_context_provider); |
| ASSERT_TRUE(provider); |
| EXPECT_TRUE(provider->IsAccelerated()); |
| |
| auto image = CreateImageFromVideoFrame(texture_frame, |
| /*allow_zero_copy_images=*/false, |
| provider.get()); |
| EXPECT_TRUE(image->IsTextureBacked()); |
| |
| image = CreateImageFromVideoFrame(texture_frame, |
| /*allow_zero_copy_images=*/false, |
| provider.get()); |
| EXPECT_TRUE(image->IsTextureBacked()); |
| |
| ASSERT_FALSE(provider->needs_flush()); |
| ASSERT_FALSE(provider->HasRecordedDrawOps()); |
| } |
| |
| TEST(VideoFrameImageUtilTest, SoftwareCreateResourceProviderForVideoFrame) { |
| // Creating a provider with a null viz::RasterContextProvider should result in |
| // a non-accelerated provider being created. |
| auto provider = |
| CreateResourceProviderForVideoFrame(IntSize(kTestSize), nullptr); |
| ASSERT_TRUE(provider); |
| EXPECT_FALSE(provider->IsAccelerated()); |
| } |
| |
| TEST(VideoFrameImageUtilTest, AcceleratedCreateResourceProviderForVideoFrame) { |
| ScopedFakeGpuContext fake_context(/*disable_imagebitmap=*/false); |
| ASSERT_TRUE(SharedGpuContext::IsGpuCompositingEnabled()); |
| |
| auto* raster_context_provider = fake_context.raster_context_provider(); |
| ASSERT_TRUE(raster_context_provider); |
| |
| // Creating a provider with a null viz::RasterContextProvider should result in |
| // a non-accelerated provider being created. |
| { |
| auto provider = |
| CreateResourceProviderForVideoFrame(IntSize(kTestSize), nullptr); |
| ASSERT_TRUE(provider); |
| EXPECT_FALSE(provider->IsAccelerated()); |
| } |
| |
| // Creating a provider with a real raster context provider should result in |
| // an accelerated provider being created. |
| { |
| auto provider = CreateResourceProviderForVideoFrame( |
| IntSize(kTestSize), raster_context_provider); |
| ASSERT_TRUE(provider); |
| EXPECT_TRUE(provider->IsAccelerated()); |
| } |
| } |
| |
| TEST(VideoFrameImageUtilTest, WorkaroundCreateResourceProviderForVideoFrame) { |
| ScopedFakeGpuContext fake_context(/*disable_imagebitmap=*/true); |
| ASSERT_TRUE(SharedGpuContext::IsGpuCompositingEnabled()); |
| |
| auto* raster_context_provider = fake_context.raster_context_provider(); |
| ASSERT_TRUE(raster_context_provider); |
| |
| // Creating a provider with a real raster context provider should result in |
| // an unaccelerated provider being created due to the workaround. |
| { |
| auto provider = CreateResourceProviderForVideoFrame( |
| IntSize(kTestSize), raster_context_provider); |
| ASSERT_TRUE(provider); |
| EXPECT_FALSE(provider->IsAccelerated()); |
| } |
| } |
| |
| TEST(VideoFrameImageUtilTest, DestRectWithoutCanvasResourceProvider) { |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| auto cpu_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OWNED_MEMORY, |
| media::PIXEL_FORMAT_XRGB); |
| |
| // A CanvasResourceProvider must be provided with a custom destination rect. |
| auto image = CreateImageFromVideoFrame(cpu_frame, true, nullptr, nullptr, |
| gfx::Rect(0, 0, 10, 10)); |
| ASSERT_FALSE(image); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST(VideoFrameImageUtilTest, CanvasResourceProviderTooSmallForDestRect) { |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| auto cpu_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OWNED_MEMORY, |
| media::PIXEL_FORMAT_XRGB); |
| |
| auto provider = |
| CreateResourceProviderForVideoFrame(IntSize(gfx::Size(16, 16)), nullptr); |
| ASSERT_TRUE(provider); |
| EXPECT_FALSE(provider->IsAccelerated()); |
| |
| auto image = CreateImageFromVideoFrame(cpu_frame, true, provider.get(), |
| nullptr, gfx::Rect(kTestSize)); |
| ASSERT_FALSE(image); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST(VideoFrameImageUtilTest, CanvasResourceProviderDestRect) { |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| auto cpu_frame = CreateTestFrame(kTestSize, gfx::Rect(kTestSize), kTestSize, |
| media::VideoFrame::STORAGE_OWNED_MEMORY, |
| media::PIXEL_FORMAT_XRGB); |
| |
| auto provider = CreateResourceProviderForVideoFrame( |
| IntSize(gfx::Size(128, 128)), nullptr); |
| ASSERT_TRUE(provider); |
| EXPECT_FALSE(provider->IsAccelerated()); |
| |
| auto image = CreateImageFromVideoFrame(cpu_frame, true, provider.get(), |
| nullptr, gfx::Rect(16, 16, 64, 64)); |
| ASSERT_TRUE(image); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| } // namespace blink |