webgpu_mailbox_unittest.cc 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  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. #include "build/build_config.h"
  5. #include "components/viz/test/test_gpu_service_holder.h"
  6. #include "gpu/command_buffer/client/shared_image_interface.h"
  7. #include "gpu/command_buffer/client/webgpu_implementation.h"
  8. #include "gpu/command_buffer/common/mailbox.h"
  9. #include "gpu/command_buffer/common/shared_image_usage.h"
  10. #include "gpu/command_buffer/service/webgpu_decoder.h"
  11. #include "gpu/command_buffer/tests/webgpu_test.h"
  12. #include "testing/gmock/include/gmock/gmock.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. #include "ui/gfx/color_space.h"
  15. #if BUILDFLAG(IS_MAC)
  16. #include "gpu/command_buffer/tests/gl_manager.h"
  17. #include "ui/gl/gl_context.h"
  18. #endif
  19. namespace gpu {
  20. namespace {
  21. class MockBufferMapCallback {
  22. public:
  23. MOCK_METHOD(void, Call, (WGPUBufferMapAsyncStatus status, void* userdata));
  24. };
  25. std::unique_ptr<testing::StrictMock<MockBufferMapCallback>>
  26. mock_buffer_map_callback;
  27. void ToMockBufferMapCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
  28. mock_buffer_map_callback->Call(status, userdata);
  29. }
  30. class MockUncapturedErrorCallback {
  31. public:
  32. MOCK_METHOD3(Call,
  33. void(WGPUErrorType type, const char* message, void* userdata));
  34. };
  35. std::unique_ptr<testing::StrictMock<MockUncapturedErrorCallback>>
  36. mock_device_error_callback;
  37. void ToMockUncapturedErrorCallback(WGPUErrorType type,
  38. const char* message,
  39. void* userdata) {
  40. mock_device_error_callback->Call(type, message, userdata);
  41. }
  42. struct WebGPUMailboxTestParams : WebGPUTest::Options {
  43. viz::ResourceFormat format;
  44. };
  45. std::ostream& operator<<(std::ostream& os,
  46. const WebGPUMailboxTestParams& options) {
  47. switch (options.format) {
  48. case viz::ResourceFormat::RGBA_8888:
  49. os << "RGBA_8888";
  50. break;
  51. case viz::ResourceFormat::BGRA_8888:
  52. os << "BGRA_8888";
  53. break;
  54. case viz::ResourceFormat::RGBA_F16:
  55. os << "RGBA_F16";
  56. break;
  57. default:
  58. NOTREACHED();
  59. }
  60. if (options.enable_unsafe_webgpu) {
  61. os << "_UnsafeWebGPU";
  62. }
  63. if (options.force_fallback_adapter) {
  64. os << "_FallbackAdapter";
  65. }
  66. return os;
  67. }
  68. } // namespace
  69. class WebGPUMailboxTest
  70. : public WebGPUTest,
  71. public testing::WithParamInterface<WebGPUMailboxTestParams> {
  72. public:
  73. static std::vector<WebGPUMailboxTestParams> TestParams() {
  74. WebGPUMailboxTestParams options = {};
  75. WebGPUMailboxTestParams fallback_options = {};
  76. fallback_options.force_fallback_adapter = true;
  77. // Unsafe WebGPU currently required for SwiftShader fallback.
  78. fallback_options.enable_unsafe_webgpu = true;
  79. std::vector<WebGPUMailboxTestParams> params;
  80. for (viz::ResourceFormat format : {
  81. // TODO(crbug.com/1241369): Handle additional formats.
  82. #if !BUILDFLAG(IS_MAC)
  83. viz::ResourceFormat::RGBA_8888,
  84. #endif // !BUILDFLAG(IS_MAC)
  85. viz::ResourceFormat::BGRA_8888, viz::ResourceFormat::RGBA_F16,
  86. }) {
  87. WebGPUMailboxTestParams o = options;
  88. o.format = format;
  89. params.push_back(o);
  90. o = fallback_options;
  91. o.format = format;
  92. params.push_back(o);
  93. }
  94. return params;
  95. }
  96. protected:
  97. void SetUp() override {
  98. #if BUILDFLAG(IS_MAC)
  99. // Crashing on Mac M1. Currently missing stack trace. crbug.com/1271926
  100. // This must be checked before WebGPUTest::Initialize otherwise context
  101. // switched is locked and we cannot temporarily have this GLContext.
  102. GLManager gl_manager;
  103. gl_manager.Initialize(GLManager::Options());
  104. std::string renderer(gl_manager.context()->GetGLRenderer());
  105. if (renderer.find("Apple M1") != std::string::npos)
  106. mac_m1_ = true;
  107. gl_manager.Destroy();
  108. #endif
  109. WebGPUTest::SetUp();
  110. Initialize(GetParam());
  111. mock_buffer_map_callback =
  112. std::make_unique<testing::StrictMock<MockBufferMapCallback>>();
  113. mock_device_error_callback =
  114. std::make_unique<testing::StrictMock<MockUncapturedErrorCallback>>();
  115. }
  116. void TearDown() override {
  117. mock_buffer_map_callback = nullptr;
  118. mock_device_error_callback = nullptr;
  119. WebGPUTest::TearDown();
  120. }
  121. struct AssociateMailboxCmdStorage {
  122. webgpu::cmds::AssociateMailboxImmediate cmd;
  123. GLbyte data[GL_MAILBOX_SIZE_CHROMIUM];
  124. };
  125. template <typename T>
  126. static error::Error ExecuteCmd(webgpu::WebGPUDecoder* decoder, const T& cmd) {
  127. static_assert(T::kArgFlags == cmd::kFixed,
  128. "T::kArgFlags should equal cmd::kFixed");
  129. int entries_processed = 0;
  130. return decoder->DoCommands(1, (const void*)&cmd,
  131. ComputeNumEntries(sizeof(cmd)),
  132. &entries_processed);
  133. }
  134. template <typename T>
  135. static error::Error ExecuteImmediateCmd(webgpu::WebGPUDecoder* decoder,
  136. const T& cmd,
  137. size_t data_size) {
  138. static_assert(T::kArgFlags == cmd::kAtLeastN,
  139. "T::kArgFlags should equal cmd::kAtLeastN");
  140. int entries_processed = 0;
  141. return decoder->DoCommands(1, (const void*)&cmd,
  142. ComputeNumEntries(sizeof(cmd) + data_size),
  143. &entries_processed);
  144. }
  145. void InitializeTextureColor(wgpu::Device device,
  146. const Mailbox& mailbox,
  147. wgpu::Color clearColor) {
  148. gpu::webgpu::ReservedTexture reservation =
  149. webgpu()->ReserveTexture(device.Get());
  150. webgpu()->AssociateMailbox(
  151. reservation.deviceId, reservation.deviceGeneration, reservation.id,
  152. reservation.generation, WGPUTextureUsage_RenderAttachment,
  153. webgpu::WEBGPU_MAILBOX_NONE, reinterpret_cast<const GLbyte*>(&mailbox));
  154. wgpu::Texture texture = wgpu::Texture::Acquire(reservation.texture);
  155. // Clear the texture using a render pass.
  156. wgpu::RenderPassColorAttachment color_desc = {};
  157. color_desc.view = texture.CreateView();
  158. color_desc.loadOp = wgpu::LoadOp::Clear;
  159. color_desc.storeOp = wgpu::StoreOp::Store;
  160. color_desc.clearColor = clearColor;
  161. wgpu::RenderPassDescriptor render_pass_desc = {};
  162. render_pass_desc.colorAttachmentCount = 1;
  163. render_pass_desc.colorAttachments = &color_desc;
  164. wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
  165. wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&render_pass_desc);
  166. pass.EndPass();
  167. wgpu::CommandBuffer commands = encoder.Finish();
  168. wgpu::Queue queue = device.GetQueue();
  169. queue.Submit(1, &commands);
  170. webgpu()->DissociateMailbox(reservation.id, reservation.generation);
  171. }
  172. #if BUILDFLAG(IS_MAC)
  173. bool mac_m1_ = false;
  174. #endif
  175. };
  176. TEST_P(WebGPUMailboxTest, AssociateMailboxCmd) {
  177. if (!WebGPUSupported()) {
  178. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  179. return;
  180. }
  181. if (!WebGPUSharedImageSupported()) {
  182. LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
  183. return;
  184. }
  185. // Create the shared image
  186. SharedImageInterface* sii = GetSharedImageInterface();
  187. Mailbox mailbox = sii->CreateSharedImage(
  188. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  189. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  190. kNullSurfaceHandle);
  191. wgpu::Device device = GetNewDevice();
  192. webgpu::ReservedTexture reservation = webgpu()->ReserveTexture(device.Get());
  193. GetGpuServiceHolder()->ScheduleGpuTask(base::BindOnce(
  194. [](webgpu::WebGPUDecoder* decoder, webgpu::ReservedTexture reservation,
  195. gpu::Mailbox mailbox) {
  196. // Error case: invalid mailbox
  197. {
  198. gpu::Mailbox bad_mailbox;
  199. AssociateMailboxCmdStorage cmd;
  200. cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
  201. reservation.id, reservation.generation,
  202. WGPUTextureUsage_TextureBinding,
  203. webgpu::WEBGPU_MAILBOX_NONE, bad_mailbox.name);
  204. EXPECT_EQ(
  205. error::kInvalidArguments,
  206. ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(bad_mailbox.name)));
  207. }
  208. // Error case: device client id doesn't exist.
  209. {
  210. AssociateMailboxCmdStorage cmd;
  211. cmd.cmd.Init(reservation.deviceId + 1, reservation.deviceGeneration,
  212. reservation.id, reservation.generation,
  213. WGPUTextureUsage_TextureBinding,
  214. webgpu::WEBGPU_MAILBOX_NONE, mailbox.name);
  215. EXPECT_EQ(
  216. error::kInvalidArguments,
  217. ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
  218. }
  219. // Error case: device generation is invalid.
  220. {
  221. AssociateMailboxCmdStorage cmd;
  222. cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration + 1,
  223. reservation.id, reservation.generation,
  224. WGPUTextureUsage_TextureBinding,
  225. webgpu::WEBGPU_MAILBOX_NONE, mailbox.name);
  226. EXPECT_EQ(
  227. error::kInvalidArguments,
  228. ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
  229. }
  230. // Error case: texture ID invalid for the wire server.
  231. {
  232. AssociateMailboxCmdStorage cmd;
  233. cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
  234. reservation.id + 1, reservation.generation,
  235. WGPUTextureUsage_TextureBinding,
  236. webgpu::WEBGPU_MAILBOX_NONE, mailbox.name);
  237. EXPECT_EQ(
  238. error::kInvalidArguments,
  239. ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
  240. }
  241. // Error case: invalid texture usage.
  242. {
  243. AssociateMailboxCmdStorage cmd;
  244. cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
  245. reservation.id, reservation.generation,
  246. WGPUTextureUsage_Force32, webgpu::WEBGPU_MAILBOX_NONE,
  247. mailbox.name);
  248. EXPECT_EQ(
  249. error::kInvalidArguments,
  250. ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
  251. }
  252. // Control case: test a successful call to AssociateMailbox.
  253. // The control case is not put first because it modifies the internal
  254. // state of the Dawn wire server and would make calls with the same
  255. // texture ID and generation invalid.
  256. {
  257. AssociateMailboxCmdStorage cmd;
  258. cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
  259. reservation.id, reservation.generation,
  260. WGPUTextureUsage_TextureBinding,
  261. webgpu::WEBGPU_MAILBOX_NONE, mailbox.name);
  262. EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(decoder, cmd.cmd,
  263. sizeof(mailbox.name)));
  264. }
  265. // Error case: associated to an already associated texture.
  266. {
  267. AssociateMailboxCmdStorage cmd;
  268. cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
  269. reservation.id, reservation.generation,
  270. WGPUTextureUsage_TextureBinding,
  271. webgpu::WEBGPU_MAILBOX_NONE, mailbox.name);
  272. EXPECT_EQ(
  273. error::kInvalidArguments,
  274. ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
  275. }
  276. // Dissociate the image from the control case to remove its reference.
  277. {
  278. webgpu::cmds::DissociateMailbox cmd;
  279. cmd.Init(reservation.id, reservation.generation);
  280. EXPECT_EQ(error::kNoError, ExecuteCmd(decoder, cmd));
  281. }
  282. },
  283. GetDecoder(), reservation, mailbox));
  284. GetGpuServiceHolder()->gpu_thread_task_runner()->RunsTasksInCurrentSequence();
  285. }
  286. TEST_P(WebGPUMailboxTest, DissociateMailboxCmd) {
  287. if (!WebGPUSupported()) {
  288. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  289. return;
  290. }
  291. if (!WebGPUSharedImageSupported()) {
  292. LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
  293. return;
  294. }
  295. // Create the shared image
  296. SharedImageInterface* sii = GetSharedImageInterface();
  297. Mailbox mailbox = sii->CreateSharedImage(
  298. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  299. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  300. kNullSurfaceHandle);
  301. wgpu::Device device = GetNewDevice();
  302. webgpu::ReservedTexture reservation = webgpu()->ReserveTexture(device.Get());
  303. GetGpuServiceHolder()->ScheduleGpuTask(base::BindOnce(
  304. [](webgpu::WebGPUDecoder* decoder, webgpu::ReservedTexture reservation,
  305. gpu::Mailbox mailbox) {
  306. // Associate a mailbox so we can later dissociate it.
  307. {
  308. AssociateMailboxCmdStorage cmd;
  309. cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
  310. reservation.id, reservation.generation,
  311. WGPUTextureUsage_TextureBinding,
  312. webgpu::WEBGPU_MAILBOX_NONE, mailbox.name);
  313. EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(decoder, cmd.cmd,
  314. sizeof(mailbox.name)));
  315. }
  316. // Error case: wrong texture ID
  317. {
  318. webgpu::cmds::DissociateMailbox cmd;
  319. cmd.Init(reservation.id + 1, reservation.generation);
  320. EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
  321. }
  322. // Error case: wrong texture generation
  323. {
  324. webgpu::cmds::DissociateMailbox cmd;
  325. cmd.Init(reservation.id, reservation.generation + 1);
  326. EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
  327. }
  328. // Success case
  329. {
  330. webgpu::cmds::DissociateMailbox cmd;
  331. cmd.Init(reservation.id, reservation.generation);
  332. EXPECT_EQ(error::kNoError, ExecuteCmd(decoder, cmd));
  333. }
  334. // Error case: dissociate an already dissociated mailbox
  335. {
  336. webgpu::cmds::DissociateMailbox cmd;
  337. cmd.Init(reservation.id, reservation.generation);
  338. EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
  339. }
  340. },
  341. GetDecoder(), reservation, mailbox));
  342. GetGpuServiceHolder()->gpu_thread_task_runner()->RunsTasksInCurrentSequence();
  343. }
  344. // Tests using Associate/DissociateMailbox to share an image with Dawn.
  345. // For simplicity of the test the image is shared between a Dawn device and
  346. // itself: we render to it using the Dawn device, then re-associate it to a
  347. // Dawn texture and read back the values that were written.
  348. TEST_P(WebGPUMailboxTest, WriteToMailboxThenReadFromIt) {
  349. if (!WebGPUSupported()) {
  350. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  351. return;
  352. }
  353. if (!WebGPUSharedImageSupported()) {
  354. LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
  355. return;
  356. }
  357. if (GetParam().format == viz::ResourceFormat::RGBA_F16) {
  358. LOG(ERROR) << "Test skipped because RGBA_F16 isn't supported.";
  359. return;
  360. }
  361. // Create the shared image
  362. SharedImageInterface* sii = GetSharedImageInterface();
  363. Mailbox mailbox = sii->CreateSharedImage(
  364. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  365. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  366. kNullSurfaceHandle);
  367. SyncToken mailbox_produced_token = sii->GenVerifiedSyncToken();
  368. webgpu()->WaitSyncTokenCHROMIUM(mailbox_produced_token.GetConstData());
  369. wgpu::Device device = GetNewDevice();
  370. // Part 1: Write to the texture using Dawn
  371. InitializeTextureColor(device, mailbox, {0.0, 0.0, 1.0, 1.0});
  372. // Part 2: Read back the texture using Dawn
  373. {
  374. // Register the shared image as a Dawn texture in the wire.
  375. gpu::webgpu::ReservedTexture reservation =
  376. webgpu()->ReserveTexture(device.Get());
  377. webgpu()->AssociateMailbox(
  378. reservation.deviceId, reservation.deviceGeneration, reservation.id,
  379. reservation.generation, WGPUTextureUsage_CopySrc,
  380. webgpu::WEBGPU_MAILBOX_NONE, reinterpret_cast<const GLbyte*>(&mailbox));
  381. wgpu::Texture texture = wgpu::Texture::Acquire(reservation.texture);
  382. // Copy the texture in a mappable buffer.
  383. wgpu::BufferDescriptor buffer_desc;
  384. buffer_desc.size = 4;
  385. buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
  386. wgpu::Buffer readback_buffer = device.CreateBuffer(&buffer_desc);
  387. wgpu::ImageCopyTexture copy_src = {};
  388. copy_src.texture = texture;
  389. copy_src.mipLevel = 0;
  390. copy_src.origin = {0, 0, 0};
  391. wgpu::ImageCopyBuffer copy_dst = {};
  392. copy_dst.buffer = readback_buffer;
  393. copy_dst.layout.offset = 0;
  394. copy_dst.layout.bytesPerRow = 256;
  395. wgpu::Extent3D copy_size = {1, 1, 1};
  396. wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
  397. encoder.CopyTextureToBuffer(&copy_src, &copy_dst, &copy_size);
  398. wgpu::CommandBuffer commands = encoder.Finish();
  399. wgpu::Queue queue = device.GetQueue();
  400. queue.Submit(1, &commands);
  401. webgpu()->DissociateMailbox(reservation.id, reservation.generation);
  402. // Map the buffer and assert the pixel is the correct value.
  403. readback_buffer.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapCallback,
  404. nullptr);
  405. EXPECT_CALL(*mock_buffer_map_callback,
  406. Call(WGPUBufferMapAsyncStatus_Success, nullptr))
  407. .Times(1);
  408. WaitForCompletion(device);
  409. const void* data = readback_buffer.GetConstMappedRange();
  410. switch (GetParam().format) {
  411. case viz::ResourceFormat::RGBA_8888:
  412. EXPECT_EQ(0xFFFF0000u, *static_cast<const uint32_t*>(data));
  413. break;
  414. case viz::ResourceFormat::BGRA_8888:
  415. EXPECT_EQ(0xFF0000FFu, *static_cast<const uint32_t*>(data));
  416. break;
  417. default:
  418. NOTREACHED();
  419. }
  420. }
  421. }
  422. // Test that an uninitialized shared image is lazily cleared by Dawn when it is
  423. // read.
  424. TEST_P(WebGPUMailboxTest, ReadUninitializedSharedImage) {
  425. if (!WebGPUSupported()) {
  426. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  427. return;
  428. }
  429. if (!WebGPUSharedImageSupported()) {
  430. LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
  431. return;
  432. }
  433. // Create the shared image.
  434. SharedImageInterface* sii = GetSharedImageInterface();
  435. Mailbox mailbox = sii->CreateSharedImage(
  436. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  437. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  438. kNullSurfaceHandle);
  439. SyncToken mailbox_produced_token = sii->GenVerifiedSyncToken();
  440. webgpu()->WaitSyncTokenCHROMIUM(mailbox_produced_token.GetConstData());
  441. wgpu::Device device = GetNewDevice();
  442. // Set the texture contents to non-zero so we can test a lazy clear occurs.
  443. InitializeTextureColor(device, mailbox, {1.0, 0, 0, 1.0});
  444. // Register the shared image as a Dawn texture in the wire.
  445. gpu::webgpu::ReservedTexture reservation =
  446. webgpu()->ReserveTexture(device.Get());
  447. // Associate the mailbox. Using WEBGPU_MAILBOX_DISCARD will set the contents
  448. // to uncleared.
  449. webgpu()->AssociateMailbox(reservation.deviceId, reservation.deviceGeneration,
  450. reservation.id, reservation.generation,
  451. WGPUTextureUsage_CopySrc,
  452. webgpu::WEBGPU_MAILBOX_DISCARD,
  453. reinterpret_cast<const GLbyte*>(&mailbox));
  454. wgpu::Texture texture = wgpu::Texture::Acquire(reservation.texture);
  455. // Copy the texture in a mappable buffer.
  456. wgpu::BufferDescriptor buffer_desc;
  457. buffer_desc.size = 4;
  458. buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
  459. wgpu::Buffer readback_buffer = device.CreateBuffer(&buffer_desc);
  460. wgpu::ImageCopyTexture copy_src = {};
  461. copy_src.texture = texture;
  462. copy_src.mipLevel = 0;
  463. copy_src.origin = {0, 0, 0};
  464. wgpu::ImageCopyBuffer copy_dst = {};
  465. copy_dst.buffer = readback_buffer;
  466. copy_dst.layout.offset = 0;
  467. copy_dst.layout.bytesPerRow = 256;
  468. wgpu::Extent3D copy_size = {1, 1, 1};
  469. wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
  470. encoder.CopyTextureToBuffer(&copy_src, &copy_dst, &copy_size);
  471. wgpu::CommandBuffer commands = encoder.Finish();
  472. wgpu::Queue queue = device.GetQueue();
  473. queue.Submit(1, &commands);
  474. webgpu()->DissociateMailbox(reservation.id, reservation.generation);
  475. // Map the buffer and assert the pixel is the correct value.
  476. readback_buffer.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapCallback,
  477. nullptr);
  478. EXPECT_CALL(*mock_buffer_map_callback,
  479. Call(WGPUBufferMapAsyncStatus_Success, nullptr))
  480. .Times(1);
  481. WaitForCompletion(device);
  482. const void* data = readback_buffer.GetConstMappedRange(0, 4);
  483. // Contents should be black because the texture was lazily cleared.
  484. EXPECT_EQ(0x00000000u, *static_cast<const uint32_t*>(data));
  485. }
  486. // Test that an uninitialized shared image is lazily cleared by Dawn when it is
  487. // read.
  488. TEST_P(WebGPUMailboxTest, ReadWritableUninitializedSharedImage) {
  489. if (!WebGPUSupported()) {
  490. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  491. return;
  492. }
  493. if (!WebGPUSharedImageSupported()) {
  494. LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
  495. return;
  496. }
  497. // Create the shared image.
  498. SharedImageInterface* sii = GetSharedImageInterface();
  499. Mailbox mailbox = sii->CreateSharedImage(
  500. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  501. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  502. kNullSurfaceHandle);
  503. SyncToken mailbox_produced_token = sii->GenVerifiedSyncToken();
  504. webgpu()->WaitSyncTokenCHROMIUM(mailbox_produced_token.GetConstData());
  505. wgpu::Device device = GetNewDevice();
  506. // Set the texture contents to non-zero so we can test a lazy clear occurs.
  507. InitializeTextureColor(device, mailbox, {1.0, 0, 0, 1.0});
  508. // Register the shared image as a Dawn texture in the wire.
  509. gpu::webgpu::ReservedTexture reservation =
  510. webgpu()->ReserveTexture(device.Get());
  511. // Associate the mailbox. Using WEBGPU_MAILBOX_DISCARD will set the contents
  512. // to uncleared.
  513. webgpu()->AssociateMailbox(
  514. reservation.deviceId, reservation.deviceGeneration, reservation.id,
  515. reservation.generation,
  516. WGPUTextureUsage_CopySrc | WGPUTextureUsage_RenderAttachment,
  517. webgpu::WEBGPU_MAILBOX_DISCARD,
  518. reinterpret_cast<const GLbyte*>(&mailbox));
  519. wgpu::Texture texture = wgpu::Texture::Acquire(reservation.texture);
  520. // Read the texture using a render pass. Load+Store the contents.
  521. // Uninitialized contents should not be loaded.
  522. wgpu::RenderPassColorAttachment color_desc = {};
  523. color_desc.view = texture.CreateView();
  524. color_desc.loadOp = wgpu::LoadOp::Load;
  525. color_desc.storeOp = wgpu::StoreOp::Store;
  526. wgpu::RenderPassDescriptor render_pass_desc = {};
  527. render_pass_desc.colorAttachmentCount = 1;
  528. render_pass_desc.colorAttachments = &color_desc;
  529. wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
  530. wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&render_pass_desc);
  531. pass.EndPass();
  532. // Copy the texture in a mappable buffer.
  533. wgpu::BufferDescriptor buffer_desc;
  534. buffer_desc.size = 4;
  535. buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
  536. wgpu::Buffer readback_buffer = device.CreateBuffer(&buffer_desc);
  537. wgpu::ImageCopyTexture copy_src = {};
  538. copy_src.texture = texture;
  539. copy_src.mipLevel = 0;
  540. copy_src.origin = {0, 0, 0};
  541. wgpu::ImageCopyBuffer copy_dst = {};
  542. copy_dst.buffer = readback_buffer;
  543. copy_dst.layout.offset = 0;
  544. copy_dst.layout.bytesPerRow = 256;
  545. wgpu::Extent3D copy_size = {1, 1, 1};
  546. encoder.CopyTextureToBuffer(&copy_src, &copy_dst, &copy_size);
  547. wgpu::CommandBuffer commands = encoder.Finish();
  548. wgpu::Queue queue = device.GetQueue();
  549. queue.Submit(1, &commands);
  550. webgpu()->DissociateMailbox(reservation.id, reservation.generation);
  551. // Map the buffer and assert the pixel is the correct value.
  552. readback_buffer.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapCallback,
  553. nullptr);
  554. EXPECT_CALL(*mock_buffer_map_callback,
  555. Call(WGPUBufferMapAsyncStatus_Success, nullptr))
  556. .Times(1);
  557. WaitForCompletion(device);
  558. const void* data = readback_buffer.GetConstMappedRange(0, 4);
  559. // Contents should be black because the texture was lazily cleared.
  560. EXPECT_EQ(0x00000000u, *static_cast<const uint32_t*>(data));
  561. }
  562. // Tests that using a shared image aftr it is dissociated produces an error.
  563. TEST_P(WebGPUMailboxTest, ErrorWhenUsingTextureAfterDissociate) {
  564. if (!WebGPUSupported()) {
  565. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  566. return;
  567. }
  568. if (!WebGPUSharedImageSupported()) {
  569. LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
  570. return;
  571. }
  572. // Create a the shared image
  573. SharedImageInterface* sii = GetSharedImageInterface();
  574. Mailbox mailbox = sii->CreateSharedImage(
  575. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  576. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  577. kNullSurfaceHandle);
  578. SyncToken mailbox_produced_token = sii->GenVerifiedSyncToken();
  579. webgpu()->WaitSyncTokenCHROMIUM(mailbox_produced_token.GetConstData());
  580. // Create the device, and expect a validation error.
  581. wgpu::Device device = GetNewDevice();
  582. device.SetUncapturedErrorCallback(ToMockUncapturedErrorCallback, 0);
  583. // Associate and immediately dissociate the image.
  584. gpu::webgpu::ReservedTexture reservation =
  585. webgpu()->ReserveTexture(device.Get());
  586. wgpu::Texture texture = wgpu::Texture::Acquire(reservation.texture);
  587. webgpu()->AssociateMailbox(
  588. reservation.deviceId, reservation.deviceGeneration, reservation.id,
  589. reservation.generation, WGPUTextureUsage_CopySrc,
  590. webgpu::WEBGPU_MAILBOX_NONE, reinterpret_cast<const GLbyte*>(&mailbox));
  591. webgpu()->DissociateMailbox(reservation.id, reservation.generation);
  592. wgpu::TextureDescriptor dst_desc = {};
  593. dst_desc.size = {1, 1};
  594. dst_desc.usage = wgpu::TextureUsage::CopyDst;
  595. switch (GetParam().format) {
  596. case viz::ResourceFormat::RGBA_8888:
  597. dst_desc.format = wgpu::TextureFormat::RGBA8Unorm;
  598. break;
  599. case viz::ResourceFormat::BGRA_8888:
  600. dst_desc.format = wgpu::TextureFormat::BGRA8Unorm;
  601. break;
  602. case viz::ResourceFormat::RGBA_F16:
  603. dst_desc.format = wgpu::TextureFormat::RGBA16Float;
  604. break;
  605. default:
  606. NOTREACHED();
  607. }
  608. wgpu::ImageCopyTexture src_image = {};
  609. src_image.texture = texture;
  610. wgpu::ImageCopyTexture dst_image = {};
  611. dst_image.texture = device.CreateTexture(&dst_desc);
  612. wgpu::Extent3D extent = {1, 1};
  613. // Try using the texture in a copy command; it should produce a validation
  614. // error.
  615. wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
  616. encoder.CopyTextureToTexture(&src_image, &dst_image, &extent);
  617. wgpu::CommandBuffer commandBuffer = encoder.Finish();
  618. // Wait so it's clear the validation error after this when we call Submit.
  619. WaitForCompletion(device);
  620. device.GetQueue().Submit(1, &commandBuffer);
  621. EXPECT_CALL(*mock_device_error_callback,
  622. Call(WGPUErrorType_Validation, testing::_, testing::_))
  623. .Times(1);
  624. WaitForCompletion(device);
  625. }
  626. // This is a regression test for an issue when using multiple shared images
  627. // where a `ScopedAccess` was destroyed after it's `SharedImageRepresentation`.
  628. // The code was similar to the following.
  629. //
  630. // struct Pair {
  631. // unique_ptr<Representation> representation;
  632. // unique_ptr<Access> access;
  633. // };
  634. //
  635. // base::flat_map<Key, Pair> map;
  636. // map.erase(some_iterator);
  637. //
  638. // In the Pair destructor C++ guarantees that `access` is destroyed before
  639. // `representation` but `erase` can move one element over another, causing
  640. // the move-assignment operator to be called. In this case the defaulted
  641. // move-assignment would first move `representation` then `access`. Causing
  642. // incorrect member destruction order for the move-to object.
  643. TEST_P(WebGPUMailboxTest, UseA_UseB_DestroyA_DestroyB) {
  644. if (!WebGPUSupported()) {
  645. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  646. return;
  647. }
  648. if (!WebGPUSharedImageSupported()) {
  649. LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
  650. return;
  651. }
  652. // Create a the shared images.
  653. SharedImageInterface* sii = GetSharedImageInterface();
  654. Mailbox mailbox_a = sii->CreateSharedImage(
  655. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  656. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  657. kNullSurfaceHandle);
  658. Mailbox mailbox_b = sii->CreateSharedImage(
  659. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  660. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  661. kNullSurfaceHandle);
  662. // Get a WebGPU device to associate the shared images to.
  663. wgpu::Device device = GetNewDevice();
  664. // Associate both mailboxes
  665. gpu::webgpu::ReservedTexture reservation_a =
  666. webgpu()->ReserveTexture(device.Get());
  667. webgpu()->AssociateMailbox(
  668. reservation_a.deviceId, reservation_a.deviceGeneration, reservation_a.id,
  669. reservation_a.generation, WGPUTextureUsage_RenderAttachment,
  670. webgpu::WEBGPU_MAILBOX_NONE, reinterpret_cast<const GLbyte*>(&mailbox_a));
  671. gpu::webgpu::ReservedTexture reservation_b =
  672. webgpu()->ReserveTexture(device.Get());
  673. webgpu()->AssociateMailbox(
  674. reservation_b.deviceId, reservation_b.deviceGeneration, reservation_b.id,
  675. reservation_b.generation, WGPUTextureUsage_RenderAttachment,
  676. webgpu::WEBGPU_MAILBOX_NONE, reinterpret_cast<const GLbyte*>(&mailbox_b));
  677. // Dissociate both mailboxes in the same order.
  678. webgpu()->DissociateMailbox(reservation_a.id, reservation_a.generation);
  679. webgpu()->DissociateMailbox(reservation_b.id, reservation_b.generation);
  680. // Send all the previous commands to the WebGPU decoder.
  681. webgpu()->FlushCommands();
  682. }
  683. // Regression test for a bug where the (id, generation) for associated shared
  684. // images was stored globally instead of per-device. This meant that of two
  685. // devices tried to create shared images with the same (id, generation) (which
  686. // is possible because they can be on different Dawn wires) they would conflict.
  687. TEST_P(WebGPUMailboxTest, AssociateOnTwoDevicesAtTheSameTime) {
  688. if (!WebGPUSupported()) {
  689. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  690. return;
  691. }
  692. if (!WebGPUSharedImageSupported()) {
  693. LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
  694. return;
  695. }
  696. #if BUILDFLAG(IS_MAC)
  697. // Crashing on Mac M1. Currently missing stack trace. crbug.com/1271926
  698. if (mac_m1_)
  699. return;
  700. #endif
  701. // Create a the shared images.
  702. SharedImageInterface* sii = GetSharedImageInterface();
  703. Mailbox mailbox_a = sii->CreateSharedImage(
  704. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  705. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  706. kNullSurfaceHandle);
  707. Mailbox mailbox_b = sii->CreateSharedImage(
  708. GetParam().format, {1, 1}, gfx::ColorSpace::CreateSRGB(),
  709. kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, SHARED_IMAGE_USAGE_WEBGPU,
  710. kNullSurfaceHandle);
  711. // Two WebGPU devices to associate the shared images to.
  712. wgpu::Device device_a = GetNewDevice();
  713. wgpu::Device device_b = GetNewDevice();
  714. // Associate both mailboxes
  715. gpu::webgpu::ReservedTexture reservation_a =
  716. webgpu()->ReserveTexture(device_a.Get());
  717. webgpu()->AssociateMailbox(
  718. reservation_a.deviceId, reservation_a.deviceGeneration, reservation_a.id,
  719. reservation_a.generation, WGPUTextureUsage_RenderAttachment,
  720. webgpu::WEBGPU_MAILBOX_NONE, reinterpret_cast<const GLbyte*>(&mailbox_a));
  721. gpu::webgpu::ReservedTexture reservation_b =
  722. webgpu()->ReserveTexture(device_b.Get());
  723. webgpu()->AssociateMailbox(
  724. reservation_b.deviceId, reservation_b.deviceGeneration, reservation_b.id,
  725. reservation_b.generation, WGPUTextureUsage_RenderAttachment,
  726. webgpu::WEBGPU_MAILBOX_NONE, reinterpret_cast<const GLbyte*>(&mailbox_b));
  727. // Dissociate both mailboxes in the same order.
  728. webgpu()->DissociateMailbox(reservation_a.id, reservation_a.generation);
  729. webgpu()->DissociateMailbox(reservation_b.id, reservation_b.generation);
  730. // Send all the previous commands to the WebGPU decoder.
  731. webgpu()->FlushCommands();
  732. }
  733. // Test that passing a descriptor to ReserveTexture produces a client-side
  734. // WGPUTexture that correctly reflects said descriptor.
  735. TEST_P(WebGPUMailboxTest, ReflectionOfDescriptor) {
  736. if (!WebGPUSupported()) {
  737. LOG(ERROR) << "Test skipped because WebGPU isn't supported";
  738. return;
  739. }
  740. wgpu::Device device = GetNewDevice();
  741. // Check that reserving a texture with a full descriptor give the same data
  742. // back through reflection.
  743. wgpu::TextureDescriptor desc1 = {};
  744. desc1.size = {1, 2, 3};
  745. desc1.format = wgpu::TextureFormat::R32Float;
  746. desc1.usage = wgpu::TextureUsage::CopyDst;
  747. desc1.dimension = wgpu::TextureDimension::e2D;
  748. desc1.sampleCount = 1;
  749. desc1.mipLevelCount = 1;
  750. gpu::webgpu::ReservedTexture reservation1 = webgpu()->ReserveTexture(
  751. device.Get(), reinterpret_cast<const WGPUTextureDescriptor*>(&desc1));
  752. wgpu::Texture texture1 = wgpu::Texture::Acquire(reservation1.texture);
  753. ASSERT_EQ(desc1.size.width, texture1.GetWidth());
  754. ASSERT_EQ(desc1.size.height, texture1.GetHeight());
  755. ASSERT_EQ(desc1.size.depthOrArrayLayers, texture1.GetDepthOrArrayLayers());
  756. ASSERT_EQ(desc1.format, texture1.GetFormat());
  757. ASSERT_EQ(desc1.usage, texture1.GetUsage());
  758. ASSERT_EQ(desc1.dimension, texture1.GetDimension());
  759. ASSERT_EQ(desc1.sampleCount, texture1.GetSampleCount());
  760. ASSERT_EQ(desc1.mipLevelCount, texture1.GetMipLevelCount());
  761. // Test with a different descriptor to check data is not hardcoded. Not that
  762. // this is actually not a valid descriptor (diimension == 1D with height !=
  763. // 1), but that it should still be reflected exactly.
  764. wgpu::TextureDescriptor desc2 = {};
  765. desc2.size = {4, 5, 6};
  766. desc2.format = wgpu::TextureFormat::RGBA8Unorm;
  767. desc2.usage = wgpu::TextureUsage::CopySrc;
  768. desc2.dimension = wgpu::TextureDimension::e1D;
  769. desc2.sampleCount = 4;
  770. desc2.mipLevelCount = 3;
  771. gpu::webgpu::ReservedTexture reservation2 = webgpu()->ReserveTexture(
  772. device.Get(), reinterpret_cast<const WGPUTextureDescriptor*>(&desc2));
  773. wgpu::Texture texture2 = wgpu::Texture::Acquire(reservation2.texture);
  774. ASSERT_EQ(desc2.size.width, texture2.GetWidth());
  775. ASSERT_EQ(desc2.size.height, texture2.GetHeight());
  776. ASSERT_EQ(desc2.size.depthOrArrayLayers, texture2.GetDepthOrArrayLayers());
  777. ASSERT_EQ(desc2.format, texture2.GetFormat());
  778. ASSERT_EQ(desc2.usage, texture2.GetUsage());
  779. ASSERT_EQ(desc2.dimension, texture2.GetDimension());
  780. ASSERT_EQ(desc2.sampleCount, texture2.GetSampleCount());
  781. ASSERT_EQ(desc2.mipLevelCount, texture2.GetMipLevelCount());
  782. }
  783. INSTANTIATE_TEST_SUITE_P(,
  784. WebGPUMailboxTest,
  785. ::testing::ValuesIn(WebGPUMailboxTest::TestParams()),
  786. ::testing::PrintToStringParamName());
  787. } // namespace gpu