File: egl_surface_io_surface.cc

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (223 lines) | stat: -rw-r--r-- 7,232 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gl/egl_surface_io_surface.h"

#include "base/logging.h"
#include "base/notreached.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gl/gl_bindings.h"

// Enums for the EGL_ANGLE_iosurface_client_buffer extension
#define EGL_IOSURFACE_ANGLE 0x3454
#define EGL_IOSURFACE_PLANE_ANGLE 0x345A
#define EGL_TEXTURE_RECTANGLE_ANGLE 0x345B
#define EGL_TEXTURE_TYPE_ANGLE 0x345C
#define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
#define EGL_BIND_TO_TEXTURE_TARGET_ANGLE 0x348D

using gfx::BufferFormat;

namespace gl {

namespace {

struct InternalFormatType {
  InternalFormatType(GLenum format, GLenum type) : format(format), type(type) {}

  GLenum format;
  GLenum type;
};

// Convert a BufferFormat to a (internal format, type) combination from the
// EGL_ANGLE_iosurface_client_buffer extension spec.
InternalFormatType BufferFormatToInternalFormatType(BufferFormat format) {
  switch (format) {
    case BufferFormat::R_8:
      return {GL_RED, GL_UNSIGNED_BYTE};
    case BufferFormat::R_16:
      return {GL_RED, GL_UNSIGNED_SHORT};
    case BufferFormat::RG_88:
      return {GL_RG, GL_UNSIGNED_BYTE};
    case BufferFormat::RG_1616:
      return {GL_RG, GL_UNSIGNED_SHORT};
    case BufferFormat::BGRX_8888:
    case BufferFormat::RGBX_8888:
      return {GL_RGB, GL_UNSIGNED_BYTE};
    case BufferFormat::BGRA_8888:
      return {GL_BGRA_EXT, GL_UNSIGNED_BYTE};
    case BufferFormat::RGBA_8888:
      return {GL_RGBA, GL_UNSIGNED_BYTE};
    case BufferFormat::RGBA_F16:
      return {GL_RGBA, GL_HALF_FLOAT};
    case BufferFormat::BGRA_1010102:
      return {GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV};
    case BufferFormat::BGR_565:
    case BufferFormat::RGBA_4444:
    case BufferFormat::RGBA_1010102:
    case BufferFormat::YVU_420:
    case BufferFormat::YUV_420_BIPLANAR:
    case BufferFormat::YUVA_420_TRIPLANAR:
    case BufferFormat::P010:
      break;
      return {GL_NONE, GL_NONE};
  }

  LOG(ERROR) << "Invalid format: " << BufferFormatToString(format);
  return {GL_NONE, GL_NONE};
}

}  // anonymous namespace

////////////////////////////////////////////////////////////////////////////////
// ScopedEGLSurfaceIOSurface

// static
std::unique_ptr<ScopedEGLSurfaceIOSurface> ScopedEGLSurfaceIOSurface::Create(
    EGLDisplay display,
    unsigned target,
    IOSurfaceRef io_surface,
    uint32_t plane,
    gfx::BufferFormat format) {
  if (display == EGL_NO_DISPLAY) {
    LOG(ERROR) << "Invalid GLDisplayEGL.";
    return nullptr;
  }

  std::unique_ptr<ScopedEGLSurfaceIOSurface> result(
      new ScopedEGLSurfaceIOSurface(display));

  if (!result->ValidateTarget(target)) {
    return nullptr;
  }

  if (!result->CreatePBuffer(io_surface, plane, format)) {
    LOG(ERROR) << "Failed to create PBuffer for IOSurface.";
    return nullptr;
  }

  return result;
}

ScopedEGLSurfaceIOSurface::ScopedEGLSurfaceIOSurface(EGLDisplay display)
    : display_(display) {
  // When creating a pbuffer we need to supply an EGLConfig. On ANGLE and
  // Swiftshader on Mac, there's only ever one config. Query it from EGL.
  EGLint numConfigs = 0;
  EGLBoolean result =
      eglChooseConfig(display_, nullptr, &dummy_config_, 1, &numConfigs);
  DCHECK_EQ(result, static_cast<EGLBoolean>(EGL_TRUE));
  DCHECK_EQ(numConfigs, 1);
  DCHECK_NE(dummy_config_, EGL_NO_CONFIG_KHR);

  // If EGL_BIND_TO_TEXTURE_TARGET_ANGLE has already been queried, then don't
  // query it again, since that depends only on the ANGLE backend (and we will
  // not be mixing backends in a single process).
  if (texture_target_ == EGL_NO_TEXTURE) {
    texture_target_ = EGL_TEXTURE_RECTANGLE_ANGLE;
    eglGetConfigAttrib(display_, dummy_config_,
                       EGL_BIND_TO_TEXTURE_TARGET_ANGLE, &texture_target_);
  }
  DCHECK_NE(texture_target_, EGL_NO_TEXTURE);
}

bool ScopedEGLSurfaceIOSurface::ValidateTarget(unsigned target) const {
  switch (target) {
    case GL_TEXTURE_2D:
      if (texture_target_ != EGL_TEXTURE_2D) {
        LOG(ERROR) << "eglBindTexImage requires 2D, got: " << target;
        return false;
      }
      break;
    case GL_TEXTURE_RECTANGLE_ARB:
      if (texture_target_ != EGL_TEXTURE_RECTANGLE_ANGLE) {
        LOG(ERROR) << "eglBindTexImage requires RECTANGLE, got: " << target;
        return false;
      }
      break;
    default:
      LOG(ERROR) << "Invalid texture target: " << target;
      return false;
  }
  return true;
}

bool ScopedEGLSurfaceIOSurface::CreatePBuffer(IOSurfaceRef io_surface,
                                              uint32_t plane,
                                              gfx::BufferFormat format) {
  // Create the pbuffer representing this IOSurface lazily because we don't know
  // in the constructor if we're going to be used to bind plane 0 to a texture,
  // or to transform YUV to RGB.
  if (pbuffer_ == EGL_NO_SURFACE) {
    InternalFormatType formatType = BufferFormatToInternalFormatType(format);
    if (formatType.format == GL_NONE || formatType.type == GL_NONE) {
      LOG(ERROR) << "Invalid resource format.";
      return false;
    }
    EGLint width =
        static_cast<EGLint>(IOSurfaceGetWidthOfPlane(io_surface, plane));
    EGLint height =
        static_cast<EGLint>(IOSurfaceGetHeightOfPlane(io_surface, plane));

    // clang-format off
    const EGLint attribs[] = {
      EGL_WIDTH,                         width,
      EGL_HEIGHT,                        height,
      EGL_IOSURFACE_PLANE_ANGLE,         static_cast<EGLint>(plane),
      EGL_TEXTURE_TARGET,                texture_target_,
      EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, static_cast<EGLint>(formatType.format),
      EGL_TEXTURE_FORMAT,                EGL_TEXTURE_RGBA,
      EGL_TEXTURE_TYPE_ANGLE,            static_cast<EGLint>(formatType.type),
      EGL_NONE,                          EGL_NONE,
    };
    // clang-format on

    pbuffer_ = eglCreatePbufferFromClientBuffer(
        display_, EGL_IOSURFACE_ANGLE, io_surface, dummy_config_, attribs);
    if (pbuffer_ == EGL_NO_SURFACE) {
      LOG(ERROR) << "eglCreatePbufferFromClientBuffer failed, EGL error is "
                 << eglGetError();
      return false;
    }
  }

  return true;
}

bool ScopedEGLSurfaceIOSurface::BindTexImage() {
  CHECK(!texture_bound_);
  EGLBoolean result = eglBindTexImage(display_, pbuffer_, EGL_BACK_BUFFER);
  if (result != EGL_TRUE) {
    LOG(ERROR) << "eglBindTexImage failed, EGL error is " << eglGetError();
    return false;
  }

  texture_bound_ = true;
  return true;
}

void ScopedEGLSurfaceIOSurface::ReleaseTexImage() {
  if (!texture_bound_)
    return;

  EGLBoolean result = eglReleaseTexImage(display_, pbuffer_, EGL_BACK_BUFFER);
  DCHECK_EQ(result, static_cast<EGLBoolean>(EGL_TRUE));
  texture_bound_ = false;
}

void ScopedEGLSurfaceIOSurface::DestroyPBuffer() {
  if (pbuffer_ == EGL_NO_SURFACE)
    return;

  EGLBoolean result = eglDestroySurface(display_, pbuffer_);
  DCHECK_EQ(result, static_cast<EGLBoolean>(EGL_TRUE));
  pbuffer_ = EGL_NO_SURFACE;
}

ScopedEGLSurfaceIOSurface::~ScopedEGLSurfaceIOSurface() {
  ReleaseTexImage();
  DestroyPBuffer();
}

}  // namespace gl