1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h"
6 
7 #include "base/memory/scoped_refptr.h"
8 #include "base/test/null_task_runner.h"
9 #include "base/test/task_environment.h"
10 #include "build/build_config.h"
11 #include "components/viz/common/resources/transferable_resource.h"
12 #include "components/viz/test/test_context_provider.h"
13 #include "gpu/command_buffer/client/webgpu_interface_stub.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
17 #include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
18 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
19 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
20 #include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h"
21 
22 using testing::_;
23 using testing::Return;
24 
25 namespace blink {
26 
27 namespace {
GenTestSyncToken(GLbyte id)28 gpu::SyncToken GenTestSyncToken(GLbyte id) {
29   gpu::SyncToken token;
30   token.Set(gpu::CommandBufferNamespace::GPU_IO,
31             gpu::CommandBufferId::FromUnsafeValue(64), id);
32   return token;
33 }
34 
CreateBitmap()35 scoped_refptr<StaticBitmapImage> CreateBitmap() {
36   auto mailbox = gpu::Mailbox::GenerateForSharedImage();
37   auto release_callback = viz::SingleReleaseCallback::Create(
38       base::BindOnce([](const gpu::SyncToken&, bool) {}));
39   return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
40       mailbox, GenTestSyncToken(100), 0, SkImageInfo::MakeN32Premul(100, 100),
41       GL_TEXTURE_2D, true, SharedGpuContext::ContextProviderWrapper(),
42       base::PlatformThread::CurrentRef(),
43       base::MakeRefCounted<base::NullTaskRunner>(),
44       std::move(release_callback));
45 }
46 
GPUUploadingPathSupported()47 bool GPUUploadingPathSupported() {
48 // In current state, only passthrough command buffer can work on this path.
49 // and Windows is the platform that is using passthrough command buffer by
50 // default.
51 // TODO(shaobo.yan@intel.com): Enable test on more platforms when they're ready.
52 #if defined(OS_WIN)
53   return true;
54 #else
55   return false;
56 #endif  // defined(OS_WIN)
57 }
58 
59 class MockWebGPUInterface : public gpu::webgpu::WebGPUInterfaceStub {
60  public:
61   MOCK_METHOD(gpu::webgpu::ReservedTexture,
62               ReserveTexture,
63               (uint64_t device_client_id));
64   MOCK_METHOD(void,
65               AssociateMailbox,
66               (GLuint64 device_client_id,
67                GLuint device_generation,
68                GLuint id,
69                GLuint generation,
70                GLuint usage,
71                const GLbyte* mailbox));
72   MOCK_METHOD(void,
73               DissociateMailbox,
74               (GLuint64 device_client_id,
75                GLuint texture_id,
76                GLuint texture_generation));
77 };
78 
79 // The six reference pixels are: red, green, blue, white, black.
80 static const uint8_t rgba8[] = {
81     0xFF, 0x00, 0x00, 0xFF,  // Red
82     0x00, 0xFF, 0x00, 0xFF,  // Green
83     0x00, 0x00, 0xFF, 0xFF,  // Blue
84     0x00, 0x00, 0x00, 0xFF,  // White
85     0xFF, 0xFF, 0xFF, 0xFF,  // Opaque Black
86     0xFF, 0xFF, 0xFF, 0x00,  // Transparent Black
87 };
88 
89 static const uint8_t bgra8[] = {
90     0x00, 0x00, 0xFF, 0xFF,  // Red
91     0x00, 0xFF, 0x00, 0xFF,  // Green
92     0xFF, 0x00, 0x00, 0xFF,  // Blue
93     0x00, 0x00, 0x00, 0xFF,  // White
94     0xFF, 0xFF, 0xFF, 0xFF,  // Opaque Black
95     0xFF, 0xFF, 0xFF, 0x00,  // Transparent Black
96 };
97 
98 static const uint8_t rgb10a2[] = {
99     0xFF, 0x03, 0x00, 0xC0,  // Red
100     0x00, 0xFC, 0x0F, 0xC0,  // Green
101     0x00, 0x00, 0xF0, 0xFF,  // Blue
102     0x00, 0x00, 0x00, 0xC0,  // White
103     0xFF, 0xFF, 0xFF, 0xFF,  // Opaque Black
104     0xFF, 0xFF, 0xFF, 0x3F,  // Transparent Black
105 };
106 
107 static const uint16_t f16[] = {
108     0x3C00, 0x0000, 0x0000, 0x3C00,  // Red
109     0x0000, 0x3C00, 0x0000, 0x3C00,  // Green
110     0x0000, 0x0000, 0x3C00, 0x3C00,  // Blue
111     0x0000, 0x0000, 0x0000, 0x3C00,  // White
112     0x3C00, 0x3C00, 0x3C00, 0x3C00,  // Opaque Black
113     0x3C00, 0x3C00, 0x3C00, 0x0000,  // Transparent Black
114 };
115 
116 static const float f32[] = {
117     1.0f, 0.0f, 0.0f, 1.0f,  // Red
118     0.0f, 1.0f, 0.0f, 1.0f,  // Green
119     0.0f, 0.0f, 1.0f, 1.0f,  // Blue
120     0.0f, 0.0f, 0.0f, 1.0f,  // White
121     1.0f, 1.0f, 1.0f, 1.0f,  // Opaque Black
122     1.0f, 1.0f, 1.0f, 0.0f,  // Transparent Black
123 };
124 
125 static const uint8_t rg8[] = {
126     0xFF, 0x00,  // Red
127     0x00, 0xFF,  // Green
128     0x00, 0x00,  // No Blue
129     0x00, 0x00,  // White
130     0xFF, 0xFF,  // Opaque Black
131     0xFF, 0xFF,  // Transparent Black
132 };
133 
134 static const uint16_t rg16f[] = {
135     0x3C00, 0x0000,  // Red
136     0x0000, 0x3C00,  // Green
137     0x0000, 0x0000,  // No Blue
138     0x0000, 0x0000,  // White
139     0x3C00, 0x3C00,  // Opaque Black
140     0x3C00, 0x3C00,  // Transparent Black
141 };
142 
GetDstContent(WGPUTextureFormat format)143 base::span<const uint8_t> GetDstContent(WGPUTextureFormat format) {
144   switch (format) {
145     case WGPUTextureFormat_RG8Unorm:
146       return base::span<const uint8_t>(rg8, sizeof(rg8));
147     case WGPUTextureFormat_RGBA8Unorm:
148     // We need to ensure no color space conversion happens
149     // during imageBitmap uploading.
150     case WGPUTextureFormat_RGBA8UnormSrgb:
151       return base::span<const uint8_t>(rgba8, sizeof(rgba8));
152     case WGPUTextureFormat_BGRA8Unorm:
153     case WGPUTextureFormat_BGRA8UnormSrgb:
154       return base::span<const uint8_t>(bgra8, sizeof(bgra8));
155     case WGPUTextureFormat_RGB10A2Unorm:
156       return base::span<const uint8_t>(rgb10a2, sizeof(rgb10a2));
157     case WGPUTextureFormat_RG16Float:
158       return base::span<const uint8_t>(reinterpret_cast<const uint8_t*>(rg16f),
159                                        sizeof(rg16f));
160     case WGPUTextureFormat_RGBA16Float:
161       return base::span<const uint8_t>(reinterpret_cast<const uint8_t*>(f16),
162                                        sizeof(f16));
163     case WGPUTextureFormat_RGBA32Float:
164       return base::span<const uint8_t>(reinterpret_cast<const uint8_t*>(f32),
165                                        sizeof(f32));
166     default:
167       NOTREACHED();
168       return {};
169   }
170 }
171 
GetSrcPixelContent(SkColorType format)172 base::span<const uint8_t> GetSrcPixelContent(SkColorType format) {
173   switch (format) {
174     case SkColorType::kRGBA_8888_SkColorType:
175       return base::span<const uint8_t>(rgba8, sizeof(rgba8));
176     case SkColorType::kBGRA_8888_SkColorType:
177       return base::span<const uint8_t>(bgra8, sizeof(bgra8));
178     case SkColorType::kRGBA_F16_SkColorType:
179       return base::span<const uint8_t>(reinterpret_cast<const uint8_t*>(f16),
180                                        sizeof(f16));
181     default:
182       NOTREACHED();
183       return {};
184   }
185 }
186 
187 }  // anonymous namespace
188 
189 class WebGPUImageBitmapHandlerTest : public testing::Test {
190  protected:
SetUp()191   void SetUp() override {}
192 
VerifyCopyBytesForCanvasColorParams(uint64_t width,uint64_t height,SkImageInfo info,IntRect copy_rect,WGPUTextureFormat color_type)193   void VerifyCopyBytesForCanvasColorParams(uint64_t width,
194                                            uint64_t height,
195                                            SkImageInfo info,
196                                            IntRect copy_rect,
197                                            WGPUTextureFormat color_type) {
198     const uint64_t content_length = width * height * info.bytesPerPixel();
199     std::vector<uint8_t> contents(content_length, 0);
200     // Initialize contents.
201     for (size_t i = 0; i < content_length; ++i) {
202       contents[i] = i % std::numeric_limits<uint8_t>::max();
203     }
204 
205     VerifyCopyBytes(width, height, info, copy_rect, color_type,
206                     base::span<uint8_t>(contents.data(), content_length),
207                     base::span<uint8_t>(contents.data(), content_length));
208   }
209 
VerifyCopyBytes(uint64_t width,uint64_t height,SkImageInfo info,IntRect copy_rect,WGPUTextureFormat color_type,base::span<const uint8_t> contents,base::span<const uint8_t> expected_value)210   void VerifyCopyBytes(uint64_t width,
211                        uint64_t height,
212                        SkImageInfo info,
213                        IntRect copy_rect,
214                        WGPUTextureFormat color_type,
215                        base::span<const uint8_t> contents,
216                        base::span<const uint8_t> expected_value) {
217     uint64_t bytes_per_pixel = DawnTextureFormatBytesPerPixel(color_type);
218     ASSERT_EQ(contents.size(), width * height * info.bytesPerPixel());
219     sk_sp<SkData> image_pixels =
220         SkData::MakeWithCopy(contents.data(), contents.size());
221     scoped_refptr<StaticBitmapImage> image =
222         StaticBitmapImage::Create(std::move(image_pixels), info);
223 
224     WebGPUImageUploadSizeInfo wgpu_info =
225         ComputeImageBitmapWebGPUUploadSizeInfo(copy_rect, color_type);
226 
227     const uint64_t result_length = wgpu_info.size_in_bytes;
228     std::vector<uint8_t> results(result_length, 0);
229     bool success = CopyBytesFromImageBitmapForWebGPU(
230         image, base::span<uint8_t>(results.data(), result_length), copy_rect,
231         color_type);
232     ASSERT_EQ(success, true);
233 
234     // Compare content and results
235     uint32_t bytes_per_row = wgpu_info.wgpu_bytes_per_row;
236     uint32_t content_row_index =
237         (copy_rect.Y() * width + copy_rect.X()) * bytes_per_pixel;
238     uint32_t result_row_index = 0;
239     for (int i = 0; i < copy_rect.Height(); ++i) {
240       EXPECT_EQ(0, memcmp(&expected_value[content_row_index],
241                           &results[result_row_index],
242                           copy_rect.Width() * bytes_per_pixel));
243       content_row_index += width * bytes_per_pixel;
244       result_row_index += bytes_per_row;
245     }
246   }
247 };
248 
TEST_F(WebGPUImageBitmapHandlerTest,VerifyColorConvert)249 TEST_F(WebGPUImageBitmapHandlerTest, VerifyColorConvert) {
250   // All supported CanvasPixelFormat mapping to SkColorType
251   const SkColorType srcSkColorFormat[] = {
252       SkColorType::kRGBA_8888_SkColorType,
253       SkColorType::kBGRA_8888_SkColorType,
254       SkColorType::kRGBA_F16_SkColorType,
255   };
256 
257   // Joint of SkColorType and WebGPU texture format
258   const WGPUTextureFormat kDstWebGPUTextureFormat[] = {
259       WGPUTextureFormat_RG16Float,      WGPUTextureFormat_RGBA16Float,
260       WGPUTextureFormat_RGBA32Float,
261 
262       WGPUTextureFormat_RGB10A2Unorm,   WGPUTextureFormat_RG8Unorm,
263       WGPUTextureFormat_RGBA8Unorm,     WGPUTextureFormat_BGRA8Unorm,
264       WGPUTextureFormat_RGBA8UnormSrgb, WGPUTextureFormat_BGRA8UnormSrgb,
265   };
266 
267   const CanvasColorSpace kColorSpaces[] = {
268       CanvasColorSpace::kSRGB,
269       CanvasColorSpace::kRec2020,
270       CanvasColorSpace::kP3,
271   };
272 
273   uint64_t kImageWidth = 3;
274   uint64_t kImageHeight = 2;
275 
276   IntRect image_data_rect(0, 0, kImageWidth, kImageHeight);
277 
278   for (SkColorType src_color_type : srcSkColorFormat) {
279     for (WGPUTextureFormat dst_color_type : kDstWebGPUTextureFormat) {
280       for (CanvasColorSpace color_space : kColorSpaces) {
281         SkImageInfo info =
282             SkImageInfo::Make(kImageWidth, kImageHeight, src_color_type,
283                               SkAlphaType::kUnpremul_SkAlphaType,
284                               CanvasColorSpaceToSkColorSpace(color_space));
285         VerifyCopyBytes(kImageWidth, kImageHeight, info, image_data_rect,
286                         dst_color_type, GetSrcPixelContent(src_color_type),
287                         GetDstContent(dst_color_type));
288       }
289     }
290   }
291 }
292 
293 // Test calculate size
TEST_F(WebGPUImageBitmapHandlerTest,VerifyGetWGPUResourceInfo)294 TEST_F(WebGPUImageBitmapHandlerTest, VerifyGetWGPUResourceInfo) {
295   uint64_t kImageWidth = 63;
296   uint64_t kImageHeight = 1;
297 
298   // Prebaked expected values.
299   uint32_t expected_bytes_per_row = 256;
300   uint64_t expected_size = 256;
301 
302   IntRect test_rect(0, 0, kImageWidth, kImageHeight);
303   WebGPUImageUploadSizeInfo info = ComputeImageBitmapWebGPUUploadSizeInfo(
304       test_rect, WGPUTextureFormat_RGBA8Unorm);
305   ASSERT_EQ(expected_size, info.size_in_bytes);
306   ASSERT_EQ(expected_bytes_per_row, info.wgpu_bytes_per_row);
307 }
308 
309 // Copy full image bitmap test
TEST_F(WebGPUImageBitmapHandlerTest,VerifyCopyBytesFromImageBitmapForWebGPU)310 TEST_F(WebGPUImageBitmapHandlerTest, VerifyCopyBytesFromImageBitmapForWebGPU) {
311   uint64_t kImageWidth = 4;
312   uint64_t kImageHeight = 2;
313   SkImageInfo info = SkImageInfo::Make(
314       kImageWidth, kImageHeight, SkColorType::kRGBA_8888_SkColorType,
315       SkAlphaType::kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB());
316 
317   IntRect image_data_rect(0, 0, kImageWidth, kImageHeight);
318   VerifyCopyBytesForCanvasColorParams(kImageWidth, kImageHeight, info,
319                                       image_data_rect,
320                                       WGPUTextureFormat_RGBA8Unorm);
321 }
322 
323 // Copy sub image bitmap test
TEST_F(WebGPUImageBitmapHandlerTest,VerifyCopyBytesFromSubImageBitmap)324 TEST_F(WebGPUImageBitmapHandlerTest, VerifyCopyBytesFromSubImageBitmap) {
325   uint64_t kImageWidth = 63;
326   uint64_t kImageHeight = 4;
327   SkImageInfo info = SkImageInfo::Make(
328       kImageWidth, kImageHeight, SkColorType::kRGBA_8888_SkColorType,
329       SkAlphaType::kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB());
330 
331   IntRect image_data_rect(2, 2, 60, 2);
332   VerifyCopyBytesForCanvasColorParams(kImageWidth, kImageHeight, info,
333                                       image_data_rect,
334                                       WGPUTextureFormat_RGBA8Unorm);
335 }
336 
337 // Copy image bitmap with premultiply alpha
TEST_F(WebGPUImageBitmapHandlerTest,VerifyCopyBytesWithPremultiplyAlpha)338 TEST_F(WebGPUImageBitmapHandlerTest, VerifyCopyBytesWithPremultiplyAlpha) {
339   uint64_t kImageWidth = 2;
340   uint64_t kImageHeight = 1;
341   SkImageInfo info = SkImageInfo::Make(
342       kImageWidth, kImageHeight, SkColorType::kRGBA_8888_SkColorType,
343       SkAlphaType::kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
344 
345   IntRect image_data_rect(0, 0, 2, 1);
346   VerifyCopyBytesForCanvasColorParams(kImageWidth, kImageHeight, info,
347                                       image_data_rect,
348                                       WGPUTextureFormat_RGBA8Unorm);
349 }
350 
351 class DawnTextureFromImageBitmapTest : public testing::Test {
352  protected:
SetUp()353   void SetUp() override {
354     auto webgpu = std::make_unique<MockWebGPUInterface>();
355     webgpu_ = webgpu.get();
356     auto provider = std::make_unique<WebGraphicsContext3DProviderForTests>(
357         std::move(webgpu));
358 
359     dawn_control_client_ =
360         base::MakeRefCounted<DawnControlClientHolder>(std::move(provider));
361 
362     dawn_texture_provider_ = base::MakeRefCounted<DawnTextureFromImageBitmap>(
363         dawn_control_client_, 1 /* device_client_id */);
364 
365     test_context_provider_ = viz::TestContextProvider::Create();
366     InitializeSharedGpuContext(test_context_provider_.get());
367   }
368 
TearDown()369   void TearDown() override { SharedGpuContext::ResetForTesting(); }
370   MockWebGPUInterface* webgpu_;
371   scoped_refptr<DawnControlClientHolder> dawn_control_client_;
372   scoped_refptr<DawnTextureFromImageBitmap> dawn_texture_provider_;
373   scoped_refptr<viz::TestContextProvider> test_context_provider_;
374   base::test::TaskEnvironment task_environment_;
375 };
376 
TEST_F(DawnTextureFromImageBitmapTest,VerifyAccessTexture)377 TEST_F(DawnTextureFromImageBitmapTest, VerifyAccessTexture) {
378   if (!GPUUploadingPathSupported()) {
379     LOG(ERROR) << "Test skipped because GPU uploading path not supported.";
380     return;
381   }
382   auto bitmap = CreateBitmap();
383 
384   viz::TransferableResource resource;
385   gpu::webgpu::ReservedTexture reservation = {
386       reinterpret_cast<WGPUTexture>(&resource), 1, 1};
387 
388   // Test that ProduceDawnTextureFromImageBitmap calls ReserveTexture and
389   // AssociateMailbox correctly.
390   const GLbyte* mailbox_bytes = nullptr;
391 
392   EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation));
393   EXPECT_CALL(*webgpu_, AssociateMailbox(
394                             dawn_texture_provider_->GetDeviceClientIdForTest(),
395                             _, reservation.id, reservation.generation,
396                             WGPUTextureUsage_CopySrc, _))
397       .WillOnce(testing::SaveArg<5>(&mailbox_bytes));
398 
399   WGPUTexture texture =
400       dawn_texture_provider_->ProduceDawnTextureFromImageBitmap(bitmap);
401 
402   gpu::Mailbox mailbox = gpu::Mailbox::FromVolatile(
403       *reinterpret_cast<const volatile gpu::Mailbox*>(mailbox_bytes));
404 
405   EXPECT_TRUE(mailbox == bitmap->GetMailboxHolder().mailbox);
406   EXPECT_NE(texture, nullptr);
407   EXPECT_EQ(dawn_texture_provider_->GetTextureIdForTest(), 1u);
408   EXPECT_EQ(dawn_texture_provider_->GetTextureGenerationForTest(), 1u);
409 
410   // Test that FinishDawnTextureFromImageBitmapAccess calls DissociateMailbox
411   // correctly.
412   EXPECT_CALL(*webgpu_, DissociateMailbox(
413                             dawn_texture_provider_->GetDeviceClientIdForTest(),
414                             reservation.id, reservation.generation));
415 
416   dawn_texture_provider_->FinishDawnTextureFromImageBitmapAccess();
417 
418   EXPECT_EQ(dawn_texture_provider_->GetTextureIdForTest(), 0u);
419 }
420 }  // namespace blink
421