123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- // Copyright 2017 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 "components/webxr/mailbox_to_surface_bridge_impl.h"
- #include <memory>
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/logging.h"
- #include "base/system/sys_info.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/trace_event/trace_event.h"
- #include "components/viz/common/gpu/context_provider.h"
- #include "content/public/browser/android/compositor.h"
- #include "content/public/browser/browser_task_traits.h"
- #include "content/public/browser/browser_thread.h"
- #include "gpu/GLES2/gl2extchromium.h"
- #include "gpu/command_buffer/client/context_support.h"
- #include "gpu/command_buffer/client/gles2_interface.h"
- #include "gpu/command_buffer/client/shared_image_interface.h"
- #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
- #include "gpu/command_buffer/common/mailbox.h"
- #include "gpu/command_buffer/common/mailbox_holder.h"
- #include "gpu/ipc/client/gpu_channel_host.h"
- #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h"
- #include "gpu/ipc/common/gpu_surface_tracker.h"
- #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
- #include "ui/gfx/color_space.h"
- #include "ui/gfx/geometry/transform.h"
- #include "ui/gl/android/surface_texture.h"
- #include <android/native_window_jni.h>
- #define VOID_OFFSET(x) reinterpret_cast<void*>(x)
- #define SHADER(Src) #Src
- namespace {
- /* clang-format off */
- const char kQuadCopyVertex[] = SHADER(
- precision mediump float;
- attribute vec4 a_Position;
- attribute vec2 a_TexCoordinate;
- varying highp vec2 v_TexCoordinate;
- uniform mat4 u_UvTransform;
- void main() {
- highp vec4 uv_in = vec4(a_TexCoordinate.x, a_TexCoordinate.y, 0, 1);
- v_TexCoordinate = (u_UvTransform * uv_in).xy;
- gl_Position = a_Position;
- }
- );
- const char kQuadCopyFragment[] = SHADER(
- precision highp float;
- uniform sampler2D u_Texture;
- varying vec2 v_TexCoordinate;
- void main() {
- gl_FragColor = texture2D(u_Texture, v_TexCoordinate);
- }
- );
- const float kQuadVertices[] = {
- // x y u, v
- -1.f, 1.f, 0.f, 1.f,
- -1.f, -1.f, 0.f, 0.f,
- 1.f, -1.f, 1.f, 0.f,
- 1.f, 1.f, 1.f, 1.f};
- /* clang-format on */
- static constexpr int kQuadVerticesSize = sizeof(kQuadVertices);
- GLuint CompileShader(gpu::gles2::GLES2Interface* gl,
- GLenum shader_type,
- const GLchar* shader_source) {
- GLuint shader_handle = gl->CreateShader(shader_type);
- if (shader_handle != 0) {
- // Pass in the shader source.
- GLint len = strlen(shader_source);
- gl->ShaderSource(shader_handle, 1, &shader_source, &len);
- // Compile the shader.
- gl->CompileShader(shader_handle);
- // Get the compilation status.
- GLint status = 0;
- gl->GetShaderiv(shader_handle, GL_COMPILE_STATUS, &status);
- if (status == GL_FALSE) {
- GLint info_log_length = 0;
- gl->GetShaderiv(shader_handle, GL_INFO_LOG_LENGTH, &info_log_length);
- auto str_info_log = std::make_unique<GLchar[]>(info_log_length + 1);
- gl->GetShaderInfoLog(shader_handle, info_log_length, nullptr,
- str_info_log.get());
- DLOG(ERROR) << "Error compiling shader: " << str_info_log.get();
- gl->DeleteShader(shader_handle);
- shader_handle = 0;
- }
- }
- return shader_handle;
- }
- GLuint CreateAndLinkProgram(gpu::gles2::GLES2Interface* gl,
- GLuint vertex_shader_handle,
- GLuint fragment_shader_handle) {
- GLuint program_handle = gl->CreateProgram();
- if (program_handle != 0) {
- // Bind the vertex shader to the program.
- gl->AttachShader(program_handle, vertex_shader_handle);
- // Bind the fragment shader to the program.
- gl->AttachShader(program_handle, fragment_shader_handle);
- // Link the two shaders together into a program.
- gl->LinkProgram(program_handle);
- // Get the link status.
- GLint link_status = 0;
- gl->GetProgramiv(program_handle, GL_LINK_STATUS, &link_status);
- // If the link failed, delete the program.
- if (link_status == GL_FALSE) {
- GLint info_log_length;
- gl->GetProgramiv(program_handle, GL_INFO_LOG_LENGTH, &info_log_length);
- auto str_info_log = std::make_unique<GLchar[]>(info_log_length + 1);
- gl->GetProgramInfoLog(program_handle, info_log_length, nullptr,
- str_info_log.get());
- DLOG(ERROR) << "Error compiling program: " << str_info_log.get();
- gl->DeleteProgram(program_handle);
- program_handle = 0;
- }
- }
- return program_handle;
- }
- GLuint ConsumeTexture(gpu::gles2::GLES2Interface* gl,
- const gpu::MailboxHolder& mailbox) {
- TRACE_EVENT0("gpu", "MailboxToSurfaceBridgeImpl::ConsumeTexture");
- gl->WaitSyncTokenCHROMIUM(mailbox.sync_token.GetConstData());
- return gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.mailbox.name);
- }
- } // namespace
- namespace webxr {
- MailboxToSurfaceBridgeImpl::MailboxToSurfaceBridgeImpl() {
- DVLOG(1) << __FUNCTION__;
- }
- MailboxToSurfaceBridgeImpl::~MailboxToSurfaceBridgeImpl() {
- if (surface_handle_) {
- // Unregister from the surface tracker to avoid a resource leak.
- gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get();
- tracker->RemoveSurface(surface_handle_);
- }
- DestroyContext();
- DVLOG(1) << __FUNCTION__;
- }
- bool MailboxToSurfaceBridgeImpl::IsConnected() {
- return context_provider_ && gl_ && context_support_;
- }
- bool MailboxToSurfaceBridgeImpl::IsGpuWorkaroundEnabled(int32_t workaround) {
- DCHECK(IsConnected());
- return context_provider_->GetGpuFeatureInfo().IsWorkaroundEnabled(workaround);
- }
- void MailboxToSurfaceBridgeImpl::OnContextAvailableOnUiThread(
- scoped_refptr<viz::ContextProvider> provider) {
- DVLOG(1) << __FUNCTION__;
- // Must save a reference to the viz::ContextProvider to keep it alive,
- // otherwise the GL context created from it becomes invalid on its
- // destruction.
- context_provider_ = std::move(provider);
- DCHECK(on_context_bound_);
- gl_thread_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(
- &MailboxToSurfaceBridgeImpl::BindContextProviderToCurrentThread,
- base::Unretained(this)));
- }
- void MailboxToSurfaceBridgeImpl::BindContextProviderToCurrentThread() {
- auto result = context_provider_->BindToCurrentThread();
- if (result != gpu::ContextResult::kSuccess) {
- DLOG(ERROR) << "Failed to init viz::ContextProvider";
- return;
- }
- gl_ = context_provider_->ContextGL();
- context_support_ = context_provider_->ContextSupport();
- if (!gl_) {
- DLOG(ERROR) << "Did not get a GL context";
- return;
- }
- if (!context_support_) {
- DLOG(ERROR) << "Did not get a ContextSupport";
- return;
- }
- InitializeRenderer();
- DVLOG(1) << __FUNCTION__ << ": Context ready";
- if (on_context_bound_) {
- std::move(on_context_bound_).Run();
- }
- }
- void MailboxToSurfaceBridgeImpl::CreateSurface(
- gl::SurfaceTexture* surface_texture) {
- ANativeWindow* window = surface_texture->CreateSurface();
- gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get();
- ANativeWindow_acquire(window);
- // Skip ANativeWindow_setBuffersGeometry, the default size appears to work.
- surface_ = std::make_unique<gl::ScopedJavaSurface>(surface_texture);
- surface_handle_ =
- tracker->AddSurfaceForNativeWidget(gpu::GpuSurfaceTracker::SurfaceRecord(
- window, surface_->j_surface(),
- false /* can_be_used_with_surface_control */));
- // Unregistering happens in the destructor.
- ANativeWindow_release(window);
- }
- void MailboxToSurfaceBridgeImpl::CreateAndBindContextProvider(
- base::OnceClosure on_bound_callback) {
- gl_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
- on_context_bound_ = std::move(on_bound_callback);
- // The callback to run in this thread. It is necessary to keep |surface| alive
- // until the context becomes available. So pass it on to the callback, so that
- // it stays alive, and is destroyed on the same thread once done.
- auto callback =
- base::BindOnce(&MailboxToSurfaceBridgeImpl::OnContextAvailableOnUiThread,
- weak_ptr_factory_.GetWeakPtr());
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(
- [](int surface_handle,
- content::Compositor::ContextProviderCallback callback) {
- // Our attributes must be compatible with the shared
- // offscreen surface used by virtualized contexts,
- // otherwise mailbox synchronization doesn't work
- // properly - it assumes a shared underlying GL context.
- // See GetCompositorContextAttributes in
- // content/browser/renderer_host/compositor_impl_android.cc
- // and https://crbug.com/699330.
- gpu::ContextCreationAttribs attributes;
- attributes.alpha_size = -1;
- attributes.red_size = 8;
- attributes.green_size = 8;
- attributes.blue_size = 8;
- attributes.stencil_size = 0;
- attributes.depth_size = 0;
- attributes.samples = 0;
- attributes.sample_buffers = 0;
- attributes.bind_generates_resource = false;
- if (base::SysInfo::IsLowEndDevice()) {
- attributes.alpha_size = 0;
- attributes.red_size = 5;
- attributes.green_size = 6;
- attributes.blue_size = 5;
- }
- content::Compositor::CreateContextProvider(
- surface_handle, attributes,
- gpu::SharedMemoryLimits::ForMailboxContext(),
- std::move(callback));
- },
- surface_handle_, std::move(callback)));
- }
- void MailboxToSurfaceBridgeImpl::ResizeSurface(int width, int height) {
- surface_width_ = width;
- surface_height_ = height;
- if (!IsConnected()) {
- // We're not initialized yet, save the requested size for later.
- needs_resize_ = true;
- return;
- }
- DVLOG(1) << __FUNCTION__ << ": resize Surface to " << surface_width_ << "x"
- << surface_height_;
- gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
- gl_->ResizeCHROMIUM(surface_width_, surface_height_, 1.f,
- color_space.AsGLColorSpace(), false);
- gl_->Viewport(0, 0, surface_width_, surface_height_);
- }
- bool MailboxToSurfaceBridgeImpl::CopyMailboxToSurfaceAndSwap(
- const gpu::MailboxHolder& mailbox) {
- return CopyMailboxToSurfaceAndSwap(mailbox, gfx::Transform());
- }
- bool MailboxToSurfaceBridgeImpl::CopyMailboxToSurfaceAndSwap(
- const gpu::MailboxHolder& mailbox,
- const gfx::Transform& uv_transform) {
- if (!IsConnected()) {
- // We may not have a context yet, i.e. due to surface initialization
- // being incomplete. This is not an error, but we obviously can't draw
- // yet. TODO(klausw): change the caller to defer this until we are ready.
- return false;
- }
- TRACE_EVENT0("gpu", __FUNCTION__);
- if (needs_resize_) {
- ResizeSurface(surface_width_, surface_height_);
- needs_resize_ = false;
- }
- DCHECK(mailbox.mailbox.IsSharedImage());
- // While it's not an error to use a zero-sized Surface, it's not going to
- // produce any visible output. Show a debug mode warning in that case to avoid
- // another annoying debugging session.
- DLOG_IF(WARNING, !surface_width_ || !surface_height_)
- << "Surface is zero-sized. Missing call to ResizeSurface?";
- GLuint sourceTexture = ConsumeTexture(gl_, mailbox);
- gl_->BeginSharedImageAccessDirectCHROMIUM(
- sourceTexture, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
- DrawQuad(sourceTexture, uv_transform);
- gl_->EndSharedImageAccessDirectCHROMIUM(sourceTexture);
- gl_->DeleteTextures(1, &sourceTexture);
- gl_->SwapBuffers(swap_id_++);
- return true;
- }
- void MailboxToSurfaceBridgeImpl::GenSyncToken(gpu::SyncToken* out_sync_token) {
- TRACE_EVENT0("gpu", __FUNCTION__);
- DCHECK(IsConnected());
- gl_->GenSyncTokenCHROMIUM(out_sync_token->GetData());
- }
- void MailboxToSurfaceBridgeImpl::WaitSyncToken(
- const gpu::SyncToken& sync_token) {
- TRACE_EVENT0("gpu", __FUNCTION__);
- DCHECK(IsConnected());
- gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
- }
- void MailboxToSurfaceBridgeImpl::WaitForClientGpuFence(
- gfx::GpuFence* gpu_fence) {
- TRACE_EVENT0("gpu", __FUNCTION__);
- DCHECK(IsConnected());
- GLuint id = gl_->CreateClientGpuFenceCHROMIUM(gpu_fence->AsClientGpuFence());
- gl_->WaitGpuFenceCHROMIUM(id);
- gl_->DestroyGpuFenceCHROMIUM(id);
- }
- void MailboxToSurfaceBridgeImpl::CreateGpuFence(
- const gpu::SyncToken& sync_token,
- base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
- TRACE_EVENT0("gpu", __FUNCTION__);
- DCHECK(IsConnected());
- gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
- GLuint id = gl_->CreateGpuFenceCHROMIUM();
- context_support_->GetGpuFence(id, std::move(callback));
- gl_->DestroyGpuFenceCHROMIUM(id);
- }
- gpu::MailboxHolder MailboxToSurfaceBridgeImpl::CreateSharedImage(
- gpu::GpuMemoryBufferImplAndroidHardwareBuffer* buffer,
- const gfx::ColorSpace& color_space,
- uint32_t usage) {
- TRACE_EVENT0("gpu", __FUNCTION__);
- DCHECK(IsConnected());
- auto* sii = context_provider_->SharedImageInterface();
- DCHECK(sii);
- gpu::MailboxHolder mailbox_holder;
- mailbox_holder.mailbox = sii->CreateSharedImage(buffer, nullptr, color_space,
- kTopLeft_GrSurfaceOrigin,
- kPremul_SkAlphaType, usage);
- mailbox_holder.sync_token = sii->GenVerifiedSyncToken();
- DCHECK(!gpu::NativeBufferNeedsPlatformSpecificTextureTarget(
- buffer->GetFormat()));
- mailbox_holder.texture_target = GL_TEXTURE_2D;
- return mailbox_holder;
- }
- void MailboxToSurfaceBridgeImpl::DestroySharedImage(
- const gpu::MailboxHolder& mailbox_holder) {
- TRACE_EVENT0("gpu", __FUNCTION__);
- DCHECK(IsConnected());
- auto* sii = context_provider_->SharedImageInterface();
- DCHECK(sii);
- sii->DestroySharedImage(mailbox_holder.sync_token, mailbox_holder.mailbox);
- }
- void MailboxToSurfaceBridgeImpl::DestroyContext() {
- gl_ = nullptr;
- context_provider_ = nullptr;
- }
- void MailboxToSurfaceBridgeImpl::InitializeRenderer() {
- GLuint vertex_shader_handle =
- CompileShader(gl_, GL_VERTEX_SHADER, kQuadCopyVertex);
- if (!vertex_shader_handle) {
- DestroyContext();
- return;
- }
- GLuint fragment_shader_handle =
- CompileShader(gl_, GL_FRAGMENT_SHADER, kQuadCopyFragment);
- if (!fragment_shader_handle) {
- DestroyContext();
- return;
- }
- GLuint program_handle =
- CreateAndLinkProgram(gl_, vertex_shader_handle, fragment_shader_handle);
- if (!program_handle) {
- DestroyContext();
- return;
- }
- // Once the program is linked the shader objects are no longer needed
- gl_->DeleteShader(vertex_shader_handle);
- gl_->DeleteShader(fragment_shader_handle);
- GLuint position_handle = gl_->GetAttribLocation(program_handle, "a_Position");
- GLuint texCoord_handle =
- gl_->GetAttribLocation(program_handle, "a_TexCoordinate");
- GLuint texUniform_handle =
- gl_->GetUniformLocation(program_handle, "u_Texture");
- uniform_uv_transform_handle_ =
- gl_->GetUniformLocation(program_handle, "u_UvTransform");
- GLuint vertexBuffer = 0;
- gl_->GenBuffers(1, &vertexBuffer);
- gl_->BindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
- gl_->BufferData(GL_ARRAY_BUFFER, kQuadVerticesSize, kQuadVertices,
- GL_STATIC_DRAW);
- // Set state once only, we assume that nobody else modifies GL state in a way
- // that would interfere with our operations.
- gl_->Disable(GL_CULL_FACE);
- gl_->DepthMask(GL_FALSE);
- gl_->Disable(GL_DEPTH_TEST);
- gl_->Disable(GL_SCISSOR_TEST);
- gl_->Disable(GL_BLEND);
- gl_->Disable(GL_POLYGON_OFFSET_FILL);
- // Not using gl_->Viewport, we assume that it defaults to the whole
- // surface and gets updated by ResizeSurface externally as
- // appropriate.
- gl_->UseProgram(program_handle);
- // Bind vertex attributes
- gl_->BindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
- gl_->EnableVertexAttribArray(position_handle);
- gl_->EnableVertexAttribArray(texCoord_handle);
- static constexpr size_t VERTEX_STRIDE = sizeof(float) * 4;
- static constexpr size_t POSITION_ELEMENTS = 2;
- static constexpr size_t TEXCOORD_ELEMENTS = 2;
- static constexpr size_t POSITION_OFFSET = 0;
- static constexpr size_t TEXCOORD_OFFSET = sizeof(float) * 2;
- gl_->VertexAttribPointer(position_handle, POSITION_ELEMENTS, GL_FLOAT, false,
- VERTEX_STRIDE, VOID_OFFSET(POSITION_OFFSET));
- gl_->VertexAttribPointer(texCoord_handle, TEXCOORD_ELEMENTS, GL_FLOAT, false,
- VERTEX_STRIDE, VOID_OFFSET(TEXCOORD_OFFSET));
- gl_->ActiveTexture(GL_TEXTURE0);
- gl_->Uniform1i(texUniform_handle, 0);
- }
- void MailboxToSurfaceBridgeImpl::DrawQuad(unsigned int texture_handle,
- const gfx::Transform& uv_transform) {
- DCHECK(IsConnected());
- // We're redrawing over the entire viewport, but it's generally more
- // efficient on mobile tiling GPUs to clear anyway as a hint that
- // we're done with the old content. TODO(klausw, https://crbug.com/700389):
- // investigate using gl_->DiscardFramebufferEXT here since that's more
- // efficient on desktop, but it would need a capability check since
- // it's not supported on older devices such as Nexus 5X.
- gl_->Clear(GL_COLOR_BUFFER_BIT);
- float uv_transform_floats[16];
- uv_transform.matrix().getColMajor(uv_transform_floats);
- gl_->UniformMatrix4fv(uniform_uv_transform_handle_, 1, GL_FALSE,
- &uv_transform_floats[0]);
- // Configure texture. This is a 1:1 pixel copy since the surface
- // size is resized to match the source canvas, so we can use
- // GL_NEAREST.
- gl_->BindTexture(GL_TEXTURE_2D, texture_handle);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- if (uv_transform.IsIdentity()) {
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- } else {
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- }
- gl_->DrawArrays(GL_TRIANGLE_FAN, 0, 4);
- }
- std::unique_ptr<device::MailboxToSurfaceBridge>
- MailboxToSurfaceBridgeFactoryImpl::Create() const {
- return std::make_unique<MailboxToSurfaceBridgeImpl>();
- }
- } // namespace webxr
|