chromium/gpu/command_buffer/service/abstract_texture_android.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/service/abstract_texture_android.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/scoped_binders.h"
#include "ui/gl/scoped_make_current.h"

namespace gpu {
namespace {
GLuint CreateTextureWithLinearFilter() {
  const auto target = GL_TEXTURE_EXTERNAL_OES;
  GLuint service_id = 0;
  auto* api = gl::g_current_gl_context;
  api->glGenTexturesFn(1, &service_id);
  gl::ScopedTextureBinder binder(target, service_id);
  api->glTexParameteriFn(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  api->glTexParameteriFn(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  api->glTexParameteriFn(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  api->glTexParameteriFn(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  return service_id;
}
}  // namespace

std::unique_ptr<AbstractTextureAndroid>
AbstractTextureAndroid::CreateForValidating(gfx::Size size) {
  GLuint service_id = CreateTextureWithLinearFilter();

  auto* texture = gpu::gles2::CreateGLES2TextureWithLightRef(
      service_id, GL_TEXTURE_EXTERNAL_OES);
  gfx::Rect cleared_rect;
  texture->SetLevelInfo(GL_TEXTURE_EXTERNAL_OES, 0, GL_RGBA, size.width(),
                        size.height(), 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                        cleared_rect);
  texture->SetImmutable(true, false);

  return std::make_unique<AbstractTextureAndroid>(texture);
}

std::unique_ptr<AbstractTextureAndroid>
AbstractTextureAndroid::CreateForPassthrough(gfx::Size size) {
  GLuint service_id = CreateTextureWithLinearFilter();
  auto texture = base::MakeRefCounted<gles2::TexturePassthrough>(
      service_id, GL_TEXTURE_EXTERNAL_OES);

  return std::make_unique<AbstractTextureAndroid>(std::move(texture), size);
}

std::unique_ptr<AbstractTextureAndroid>
AbstractTextureAndroid::CreateForTesting(GLuint texture_id) {
  auto texture = std::make_unique<gpu::TextureBase>(texture_id);
  return std::make_unique<AbstractTextureAndroid>(std::move(texture));
}

AbstractTextureAndroid::AbstractTextureAndroid(
    std::unique_ptr<gpu::TextureBase> texture)
    : texture_for_testing_(std::move(texture)) {}
AbstractTextureAndroid::AbstractTextureAndroid(gles2::Texture* texture)
    : texture_(texture), api_(gl::g_current_gl_context) {}
AbstractTextureAndroid::AbstractTextureAndroid(
    scoped_refptr<gles2::TexturePassthrough> texture,
    const gfx::Size& size)
    : texture_passthrough_(std::move(texture)),
      texture_passthrough_size_(size),
      api_(gl::g_current_gl_context) {
  DCHECK(texture_passthrough_ &&
         texture_passthrough_->target() == GL_TEXTURE_EXTERNAL_OES);
}

AbstractTextureAndroid::~AbstractTextureAndroid() {
  // If context is not lost, then the texture should be destroyed on same
  // context it was create on.
  if ((texture_ || texture_passthrough_) && have_context_) {
    DCHECK_EQ(api_, gl::g_current_gl_context);
  }

  if (texture_) {
    texture_.ExtractAsDangling()->RemoveLightweightRef(have_context_);
  }
}

void AbstractTextureAndroid::NotifyOnContextLost() {
  if (texture_passthrough_) {
    texture_passthrough_->MarkContextLost();
  }
  have_context_ = false;
}

void AbstractTextureAndroid::BindToServiceId(GLuint service_id) {
  if (texture_) {
    texture_->BindToServiceId(service_id);
    texture_->SetLevelCleared(texture_->target(), /*level=*/0, true);
  } else if (texture_passthrough_) {
    texture_passthrough_->BindToServiceId(service_id);

    if (gl::g_current_gl_driver->ext.b_GL_ANGLE_texture_external_update) {
      // Notify the texture that its size has changed.
      unsigned int target = texture_passthrough_->target();
      GLint prev_texture = 0;
      glGetIntegerv(gles2::GetTextureBindingQuery(target), &prev_texture);
      glBindTexture(target, texture_passthrough_->service_id());

      glTexImage2DExternalANGLE(
          target, /*level=*/0, /*internalformat=*/GL_RGBA,
          texture_passthrough_size_.width(), texture_passthrough_size_.height(),
          /*border=*/0, /*format=*/GL_RGBA, /*type=*/GL_UNSIGNED_BYTE);

      glBindTexture(target, prev_texture);
    }
  }
}

TextureBase* AbstractTextureAndroid::GetTextureBase() const {
  if (texture_) {
    return texture_;
  }
  if (texture_passthrough_) {
    return texture_passthrough_.get();
  }
  if (texture_for_testing_) {
    return texture_for_testing_.get();
  }
  return nullptr;
}

}  // namespace gpu