mailbox_to_surface_bridge_impl.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. // Copyright 2017 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 "components/webxr/mailbox_to_surface_bridge_impl.h"
  5. #include <memory>
  6. #include <string>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/logging.h"
  10. #include "base/system/sys_info.h"
  11. #include "base/threading/sequenced_task_runner_handle.h"
  12. #include "base/trace_event/trace_event.h"
  13. #include "components/viz/common/gpu/context_provider.h"
  14. #include "content/public/browser/android/compositor.h"
  15. #include "content/public/browser/browser_task_traits.h"
  16. #include "content/public/browser/browser_thread.h"
  17. #include "gpu/GLES2/gl2extchromium.h"
  18. #include "gpu/command_buffer/client/context_support.h"
  19. #include "gpu/command_buffer/client/gles2_interface.h"
  20. #include "gpu/command_buffer/client/shared_image_interface.h"
  21. #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
  22. #include "gpu/command_buffer/common/mailbox.h"
  23. #include "gpu/command_buffer/common/mailbox_holder.h"
  24. #include "gpu/ipc/client/gpu_channel_host.h"
  25. #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h"
  26. #include "gpu/ipc/common/gpu_surface_tracker.h"
  27. #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
  28. #include "ui/gfx/color_space.h"
  29. #include "ui/gfx/geometry/transform.h"
  30. #include "ui/gl/android/surface_texture.h"
  31. #include <android/native_window_jni.h>
  32. #define VOID_OFFSET(x) reinterpret_cast<void*>(x)
  33. #define SHADER(Src) #Src
  34. namespace {
  35. /* clang-format off */
  36. const char kQuadCopyVertex[] = SHADER(
  37. precision mediump float;
  38. attribute vec4 a_Position;
  39. attribute vec2 a_TexCoordinate;
  40. varying highp vec2 v_TexCoordinate;
  41. uniform mat4 u_UvTransform;
  42. void main() {
  43. highp vec4 uv_in = vec4(a_TexCoordinate.x, a_TexCoordinate.y, 0, 1);
  44. v_TexCoordinate = (u_UvTransform * uv_in).xy;
  45. gl_Position = a_Position;
  46. }
  47. );
  48. const char kQuadCopyFragment[] = SHADER(
  49. precision highp float;
  50. uniform sampler2D u_Texture;
  51. varying vec2 v_TexCoordinate;
  52. void main() {
  53. gl_FragColor = texture2D(u_Texture, v_TexCoordinate);
  54. }
  55. );
  56. const float kQuadVertices[] = {
  57. // x y u, v
  58. -1.f, 1.f, 0.f, 1.f,
  59. -1.f, -1.f, 0.f, 0.f,
  60. 1.f, -1.f, 1.f, 0.f,
  61. 1.f, 1.f, 1.f, 1.f};
  62. /* clang-format on */
  63. static constexpr int kQuadVerticesSize = sizeof(kQuadVertices);
  64. GLuint CompileShader(gpu::gles2::GLES2Interface* gl,
  65. GLenum shader_type,
  66. const GLchar* shader_source) {
  67. GLuint shader_handle = gl->CreateShader(shader_type);
  68. if (shader_handle != 0) {
  69. // Pass in the shader source.
  70. GLint len = strlen(shader_source);
  71. gl->ShaderSource(shader_handle, 1, &shader_source, &len);
  72. // Compile the shader.
  73. gl->CompileShader(shader_handle);
  74. // Get the compilation status.
  75. GLint status = 0;
  76. gl->GetShaderiv(shader_handle, GL_COMPILE_STATUS, &status);
  77. if (status == GL_FALSE) {
  78. GLint info_log_length = 0;
  79. gl->GetShaderiv(shader_handle, GL_INFO_LOG_LENGTH, &info_log_length);
  80. auto str_info_log = std::make_unique<GLchar[]>(info_log_length + 1);
  81. gl->GetShaderInfoLog(shader_handle, info_log_length, nullptr,
  82. str_info_log.get());
  83. DLOG(ERROR) << "Error compiling shader: " << str_info_log.get();
  84. gl->DeleteShader(shader_handle);
  85. shader_handle = 0;
  86. }
  87. }
  88. return shader_handle;
  89. }
  90. GLuint CreateAndLinkProgram(gpu::gles2::GLES2Interface* gl,
  91. GLuint vertex_shader_handle,
  92. GLuint fragment_shader_handle) {
  93. GLuint program_handle = gl->CreateProgram();
  94. if (program_handle != 0) {
  95. // Bind the vertex shader to the program.
  96. gl->AttachShader(program_handle, vertex_shader_handle);
  97. // Bind the fragment shader to the program.
  98. gl->AttachShader(program_handle, fragment_shader_handle);
  99. // Link the two shaders together into a program.
  100. gl->LinkProgram(program_handle);
  101. // Get the link status.
  102. GLint link_status = 0;
  103. gl->GetProgramiv(program_handle, GL_LINK_STATUS, &link_status);
  104. // If the link failed, delete the program.
  105. if (link_status == GL_FALSE) {
  106. GLint info_log_length;
  107. gl->GetProgramiv(program_handle, GL_INFO_LOG_LENGTH, &info_log_length);
  108. auto str_info_log = std::make_unique<GLchar[]>(info_log_length + 1);
  109. gl->GetProgramInfoLog(program_handle, info_log_length, nullptr,
  110. str_info_log.get());
  111. DLOG(ERROR) << "Error compiling program: " << str_info_log.get();
  112. gl->DeleteProgram(program_handle);
  113. program_handle = 0;
  114. }
  115. }
  116. return program_handle;
  117. }
  118. GLuint ConsumeTexture(gpu::gles2::GLES2Interface* gl,
  119. const gpu::MailboxHolder& mailbox) {
  120. TRACE_EVENT0("gpu", "MailboxToSurfaceBridgeImpl::ConsumeTexture");
  121. gl->WaitSyncTokenCHROMIUM(mailbox.sync_token.GetConstData());
  122. return gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.mailbox.name);
  123. }
  124. } // namespace
  125. namespace webxr {
  126. MailboxToSurfaceBridgeImpl::MailboxToSurfaceBridgeImpl() {
  127. DVLOG(1) << __FUNCTION__;
  128. }
  129. MailboxToSurfaceBridgeImpl::~MailboxToSurfaceBridgeImpl() {
  130. if (surface_handle_) {
  131. // Unregister from the surface tracker to avoid a resource leak.
  132. gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get();
  133. tracker->RemoveSurface(surface_handle_);
  134. }
  135. DestroyContext();
  136. DVLOG(1) << __FUNCTION__;
  137. }
  138. bool MailboxToSurfaceBridgeImpl::IsConnected() {
  139. return context_provider_ && gl_ && context_support_;
  140. }
  141. bool MailboxToSurfaceBridgeImpl::IsGpuWorkaroundEnabled(int32_t workaround) {
  142. DCHECK(IsConnected());
  143. return context_provider_->GetGpuFeatureInfo().IsWorkaroundEnabled(workaround);
  144. }
  145. void MailboxToSurfaceBridgeImpl::OnContextAvailableOnUiThread(
  146. scoped_refptr<viz::ContextProvider> provider) {
  147. DVLOG(1) << __FUNCTION__;
  148. // Must save a reference to the viz::ContextProvider to keep it alive,
  149. // otherwise the GL context created from it becomes invalid on its
  150. // destruction.
  151. context_provider_ = std::move(provider);
  152. DCHECK(on_context_bound_);
  153. gl_thread_task_runner_->PostTask(
  154. FROM_HERE,
  155. base::BindOnce(
  156. &MailboxToSurfaceBridgeImpl::BindContextProviderToCurrentThread,
  157. base::Unretained(this)));
  158. }
  159. void MailboxToSurfaceBridgeImpl::BindContextProviderToCurrentThread() {
  160. auto result = context_provider_->BindToCurrentThread();
  161. if (result != gpu::ContextResult::kSuccess) {
  162. DLOG(ERROR) << "Failed to init viz::ContextProvider";
  163. return;
  164. }
  165. gl_ = context_provider_->ContextGL();
  166. context_support_ = context_provider_->ContextSupport();
  167. if (!gl_) {
  168. DLOG(ERROR) << "Did not get a GL context";
  169. return;
  170. }
  171. if (!context_support_) {
  172. DLOG(ERROR) << "Did not get a ContextSupport";
  173. return;
  174. }
  175. InitializeRenderer();
  176. DVLOG(1) << __FUNCTION__ << ": Context ready";
  177. if (on_context_bound_) {
  178. std::move(on_context_bound_).Run();
  179. }
  180. }
  181. void MailboxToSurfaceBridgeImpl::CreateSurface(
  182. gl::SurfaceTexture* surface_texture) {
  183. ANativeWindow* window = surface_texture->CreateSurface();
  184. gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get();
  185. ANativeWindow_acquire(window);
  186. // Skip ANativeWindow_setBuffersGeometry, the default size appears to work.
  187. surface_ = std::make_unique<gl::ScopedJavaSurface>(surface_texture);
  188. surface_handle_ =
  189. tracker->AddSurfaceForNativeWidget(gpu::GpuSurfaceTracker::SurfaceRecord(
  190. window, surface_->j_surface(),
  191. false /* can_be_used_with_surface_control */));
  192. // Unregistering happens in the destructor.
  193. ANativeWindow_release(window);
  194. }
  195. void MailboxToSurfaceBridgeImpl::CreateAndBindContextProvider(
  196. base::OnceClosure on_bound_callback) {
  197. gl_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
  198. on_context_bound_ = std::move(on_bound_callback);
  199. // The callback to run in this thread. It is necessary to keep |surface| alive
  200. // until the context becomes available. So pass it on to the callback, so that
  201. // it stays alive, and is destroyed on the same thread once done.
  202. auto callback =
  203. base::BindOnce(&MailboxToSurfaceBridgeImpl::OnContextAvailableOnUiThread,
  204. weak_ptr_factory_.GetWeakPtr());
  205. content::GetUIThreadTaskRunner({})->PostTask(
  206. FROM_HERE, base::BindOnce(
  207. [](int surface_handle,
  208. content::Compositor::ContextProviderCallback callback) {
  209. // Our attributes must be compatible with the shared
  210. // offscreen surface used by virtualized contexts,
  211. // otherwise mailbox synchronization doesn't work
  212. // properly - it assumes a shared underlying GL context.
  213. // See GetCompositorContextAttributes in
  214. // content/browser/renderer_host/compositor_impl_android.cc
  215. // and https://crbug.com/699330.
  216. gpu::ContextCreationAttribs attributes;
  217. attributes.alpha_size = -1;
  218. attributes.red_size = 8;
  219. attributes.green_size = 8;
  220. attributes.blue_size = 8;
  221. attributes.stencil_size = 0;
  222. attributes.depth_size = 0;
  223. attributes.samples = 0;
  224. attributes.sample_buffers = 0;
  225. attributes.bind_generates_resource = false;
  226. if (base::SysInfo::IsLowEndDevice()) {
  227. attributes.alpha_size = 0;
  228. attributes.red_size = 5;
  229. attributes.green_size = 6;
  230. attributes.blue_size = 5;
  231. }
  232. content::Compositor::CreateContextProvider(
  233. surface_handle, attributes,
  234. gpu::SharedMemoryLimits::ForMailboxContext(),
  235. std::move(callback));
  236. },
  237. surface_handle_, std::move(callback)));
  238. }
  239. void MailboxToSurfaceBridgeImpl::ResizeSurface(int width, int height) {
  240. surface_width_ = width;
  241. surface_height_ = height;
  242. if (!IsConnected()) {
  243. // We're not initialized yet, save the requested size for later.
  244. needs_resize_ = true;
  245. return;
  246. }
  247. DVLOG(1) << __FUNCTION__ << ": resize Surface to " << surface_width_ << "x"
  248. << surface_height_;
  249. gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
  250. gl_->ResizeCHROMIUM(surface_width_, surface_height_, 1.f,
  251. color_space.AsGLColorSpace(), false);
  252. gl_->Viewport(0, 0, surface_width_, surface_height_);
  253. }
  254. bool MailboxToSurfaceBridgeImpl::CopyMailboxToSurfaceAndSwap(
  255. const gpu::MailboxHolder& mailbox) {
  256. return CopyMailboxToSurfaceAndSwap(mailbox, gfx::Transform());
  257. }
  258. bool MailboxToSurfaceBridgeImpl::CopyMailboxToSurfaceAndSwap(
  259. const gpu::MailboxHolder& mailbox,
  260. const gfx::Transform& uv_transform) {
  261. if (!IsConnected()) {
  262. // We may not have a context yet, i.e. due to surface initialization
  263. // being incomplete. This is not an error, but we obviously can't draw
  264. // yet. TODO(klausw): change the caller to defer this until we are ready.
  265. return false;
  266. }
  267. TRACE_EVENT0("gpu", __FUNCTION__);
  268. if (needs_resize_) {
  269. ResizeSurface(surface_width_, surface_height_);
  270. needs_resize_ = false;
  271. }
  272. DCHECK(mailbox.mailbox.IsSharedImage());
  273. // While it's not an error to use a zero-sized Surface, it's not going to
  274. // produce any visible output. Show a debug mode warning in that case to avoid
  275. // another annoying debugging session.
  276. DLOG_IF(WARNING, !surface_width_ || !surface_height_)
  277. << "Surface is zero-sized. Missing call to ResizeSurface?";
  278. GLuint sourceTexture = ConsumeTexture(gl_, mailbox);
  279. gl_->BeginSharedImageAccessDirectCHROMIUM(
  280. sourceTexture, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
  281. DrawQuad(sourceTexture, uv_transform);
  282. gl_->EndSharedImageAccessDirectCHROMIUM(sourceTexture);
  283. gl_->DeleteTextures(1, &sourceTexture);
  284. gl_->SwapBuffers(swap_id_++);
  285. return true;
  286. }
  287. void MailboxToSurfaceBridgeImpl::GenSyncToken(gpu::SyncToken* out_sync_token) {
  288. TRACE_EVENT0("gpu", __FUNCTION__);
  289. DCHECK(IsConnected());
  290. gl_->GenSyncTokenCHROMIUM(out_sync_token->GetData());
  291. }
  292. void MailboxToSurfaceBridgeImpl::WaitSyncToken(
  293. const gpu::SyncToken& sync_token) {
  294. TRACE_EVENT0("gpu", __FUNCTION__);
  295. DCHECK(IsConnected());
  296. gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
  297. }
  298. void MailboxToSurfaceBridgeImpl::WaitForClientGpuFence(
  299. gfx::GpuFence* gpu_fence) {
  300. TRACE_EVENT0("gpu", __FUNCTION__);
  301. DCHECK(IsConnected());
  302. GLuint id = gl_->CreateClientGpuFenceCHROMIUM(gpu_fence->AsClientGpuFence());
  303. gl_->WaitGpuFenceCHROMIUM(id);
  304. gl_->DestroyGpuFenceCHROMIUM(id);
  305. }
  306. void MailboxToSurfaceBridgeImpl::CreateGpuFence(
  307. const gpu::SyncToken& sync_token,
  308. base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
  309. TRACE_EVENT0("gpu", __FUNCTION__);
  310. DCHECK(IsConnected());
  311. gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
  312. GLuint id = gl_->CreateGpuFenceCHROMIUM();
  313. context_support_->GetGpuFence(id, std::move(callback));
  314. gl_->DestroyGpuFenceCHROMIUM(id);
  315. }
  316. gpu::MailboxHolder MailboxToSurfaceBridgeImpl::CreateSharedImage(
  317. gpu::GpuMemoryBufferImplAndroidHardwareBuffer* buffer,
  318. const gfx::ColorSpace& color_space,
  319. uint32_t usage) {
  320. TRACE_EVENT0("gpu", __FUNCTION__);
  321. DCHECK(IsConnected());
  322. auto* sii = context_provider_->SharedImageInterface();
  323. DCHECK(sii);
  324. gpu::MailboxHolder mailbox_holder;
  325. mailbox_holder.mailbox = sii->CreateSharedImage(buffer, nullptr, color_space,
  326. kTopLeft_GrSurfaceOrigin,
  327. kPremul_SkAlphaType, usage);
  328. mailbox_holder.sync_token = sii->GenVerifiedSyncToken();
  329. DCHECK(!gpu::NativeBufferNeedsPlatformSpecificTextureTarget(
  330. buffer->GetFormat()));
  331. mailbox_holder.texture_target = GL_TEXTURE_2D;
  332. return mailbox_holder;
  333. }
  334. void MailboxToSurfaceBridgeImpl::DestroySharedImage(
  335. const gpu::MailboxHolder& mailbox_holder) {
  336. TRACE_EVENT0("gpu", __FUNCTION__);
  337. DCHECK(IsConnected());
  338. auto* sii = context_provider_->SharedImageInterface();
  339. DCHECK(sii);
  340. sii->DestroySharedImage(mailbox_holder.sync_token, mailbox_holder.mailbox);
  341. }
  342. void MailboxToSurfaceBridgeImpl::DestroyContext() {
  343. gl_ = nullptr;
  344. context_provider_ = nullptr;
  345. }
  346. void MailboxToSurfaceBridgeImpl::InitializeRenderer() {
  347. GLuint vertex_shader_handle =
  348. CompileShader(gl_, GL_VERTEX_SHADER, kQuadCopyVertex);
  349. if (!vertex_shader_handle) {
  350. DestroyContext();
  351. return;
  352. }
  353. GLuint fragment_shader_handle =
  354. CompileShader(gl_, GL_FRAGMENT_SHADER, kQuadCopyFragment);
  355. if (!fragment_shader_handle) {
  356. DestroyContext();
  357. return;
  358. }
  359. GLuint program_handle =
  360. CreateAndLinkProgram(gl_, vertex_shader_handle, fragment_shader_handle);
  361. if (!program_handle) {
  362. DestroyContext();
  363. return;
  364. }
  365. // Once the program is linked the shader objects are no longer needed
  366. gl_->DeleteShader(vertex_shader_handle);
  367. gl_->DeleteShader(fragment_shader_handle);
  368. GLuint position_handle = gl_->GetAttribLocation(program_handle, "a_Position");
  369. GLuint texCoord_handle =
  370. gl_->GetAttribLocation(program_handle, "a_TexCoordinate");
  371. GLuint texUniform_handle =
  372. gl_->GetUniformLocation(program_handle, "u_Texture");
  373. uniform_uv_transform_handle_ =
  374. gl_->GetUniformLocation(program_handle, "u_UvTransform");
  375. GLuint vertexBuffer = 0;
  376. gl_->GenBuffers(1, &vertexBuffer);
  377. gl_->BindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
  378. gl_->BufferData(GL_ARRAY_BUFFER, kQuadVerticesSize, kQuadVertices,
  379. GL_STATIC_DRAW);
  380. // Set state once only, we assume that nobody else modifies GL state in a way
  381. // that would interfere with our operations.
  382. gl_->Disable(GL_CULL_FACE);
  383. gl_->DepthMask(GL_FALSE);
  384. gl_->Disable(GL_DEPTH_TEST);
  385. gl_->Disable(GL_SCISSOR_TEST);
  386. gl_->Disable(GL_BLEND);
  387. gl_->Disable(GL_POLYGON_OFFSET_FILL);
  388. // Not using gl_->Viewport, we assume that it defaults to the whole
  389. // surface and gets updated by ResizeSurface externally as
  390. // appropriate.
  391. gl_->UseProgram(program_handle);
  392. // Bind vertex attributes
  393. gl_->BindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
  394. gl_->EnableVertexAttribArray(position_handle);
  395. gl_->EnableVertexAttribArray(texCoord_handle);
  396. static constexpr size_t VERTEX_STRIDE = sizeof(float) * 4;
  397. static constexpr size_t POSITION_ELEMENTS = 2;
  398. static constexpr size_t TEXCOORD_ELEMENTS = 2;
  399. static constexpr size_t POSITION_OFFSET = 0;
  400. static constexpr size_t TEXCOORD_OFFSET = sizeof(float) * 2;
  401. gl_->VertexAttribPointer(position_handle, POSITION_ELEMENTS, GL_FLOAT, false,
  402. VERTEX_STRIDE, VOID_OFFSET(POSITION_OFFSET));
  403. gl_->VertexAttribPointer(texCoord_handle, TEXCOORD_ELEMENTS, GL_FLOAT, false,
  404. VERTEX_STRIDE, VOID_OFFSET(TEXCOORD_OFFSET));
  405. gl_->ActiveTexture(GL_TEXTURE0);
  406. gl_->Uniform1i(texUniform_handle, 0);
  407. }
  408. void MailboxToSurfaceBridgeImpl::DrawQuad(unsigned int texture_handle,
  409. const gfx::Transform& uv_transform) {
  410. DCHECK(IsConnected());
  411. // We're redrawing over the entire viewport, but it's generally more
  412. // efficient on mobile tiling GPUs to clear anyway as a hint that
  413. // we're done with the old content. TODO(klausw, https://crbug.com/700389):
  414. // investigate using gl_->DiscardFramebufferEXT here since that's more
  415. // efficient on desktop, but it would need a capability check since
  416. // it's not supported on older devices such as Nexus 5X.
  417. gl_->Clear(GL_COLOR_BUFFER_BIT);
  418. float uv_transform_floats[16];
  419. uv_transform.matrix().getColMajor(uv_transform_floats);
  420. gl_->UniformMatrix4fv(uniform_uv_transform_handle_, 1, GL_FALSE,
  421. &uv_transform_floats[0]);
  422. // Configure texture. This is a 1:1 pixel copy since the surface
  423. // size is resized to match the source canvas, so we can use
  424. // GL_NEAREST.
  425. gl_->BindTexture(GL_TEXTURE_2D, texture_handle);
  426. gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  427. gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  428. gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  429. if (uv_transform.IsIdentity()) {
  430. gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  431. } else {
  432. gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  433. }
  434. gl_->DrawArrays(GL_TRIANGLE_FAN, 0, 4);
  435. }
  436. std::unique_ptr<device::MailboxToSurfaceBridge>
  437. MailboxToSurfaceBridgeFactoryImpl::Create() const {
  438. return std::make_unique<MailboxToSurfaceBridgeImpl>();
  439. }
  440. } // namespace webxr