Flutter Impeller
trampoline.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter 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 
6 
7 #include <array>
8 
9 #include "flutter/fml/closure.h"
10 #include "flutter/fml/trace_event.h"
11 #include "impeller/base/timing.h"
17 
18 namespace impeller::glvk {
19 
20 static GLuint kAttributeIndexPosition = 0u;
21 static GLuint kAttributeIndexTexCoord = 1u;
22 
23 static constexpr const char* kVertShader = R"IMPELLER_SHADER(#version 100
24 
25 precision mediump float;
26 
27 attribute vec2 aPosition;
28 attribute vec2 aTexCoord;
29 
30 varying vec2 vTexCoord;
31 
32 void main() {
33  gl_Position = vec4(aPosition, 0.0, 1.0);
34  vTexCoord = aTexCoord;
35 
36 }
37 )IMPELLER_SHADER";
38 
39 static constexpr const char* kFragShader = R"IMPELLER_SHADER(#version 100
40 
41 #extension GL_OES_EGL_image_external : require
42 
43 precision mediump float;
44 
45 uniform samplerExternalOES uTexture;
46 uniform mat4 uUVTransformation;
47 
48 varying vec2 vTexCoord;
49 
50 void main() {
51  vec2 texture_coords = (uUVTransformation * vec4(vTexCoord, 0, 1)).xy;
52  gl_FragColor = texture2D(uTexture, texture_coords);
53 }
54 
55 )IMPELLER_SHADER";
56 
58  auto egl_display = std::make_unique<egl::Display>();
59  if (!egl_display->IsValid()) {
61  << "Could not create EGL display for external texture interop.";
62  return;
63  }
64 
65  egl::ConfigDescriptor egl_config_desc;
66  egl_config_desc.api = egl::API::kOpenGLES2;
67  egl_config_desc.samples = egl::Samples::kOne;
68  egl_config_desc.color_format = egl::ColorFormat::kRGBA8888;
69  egl_config_desc.stencil_bits = egl::StencilBits::kZero;
70  egl_config_desc.depth_bits = egl::DepthBits::kZero;
71  egl_config_desc.surface_type = egl::SurfaceType::kPBuffer;
72  auto egl_config = egl_display->ChooseConfig(egl_config_desc);
73  if (!egl_config) {
75  << "Could not choose EGL config for external texture interop.";
76  return;
77  }
78 
79  auto egl_surface = egl_display->CreatePixelBufferSurface(*egl_config, 1u, 1u);
80  auto egl_context = egl_display->CreateContext(*egl_config, nullptr);
81 
82  if (!egl_surface || !egl_context) {
83  VALIDATION_LOG << "Could not create EGL surface and/or context for "
84  "external texture interop.";
85  return;
86  }
87 
88  // Make the context current so the proc addresses can be resolved.
89  if (!egl_context->MakeCurrent(*egl_surface)) {
90  VALIDATION_LOG << "Could not make the context current.";
91  return;
92  }
93 
94  auto gl = std::make_unique<ProcTable>(egl::CreateProcAddressResolver());
95 
96  if (!gl->IsValid()) {
97  egl_context->ClearCurrent();
98  VALIDATION_LOG << "Could not setup trampoline proc table.";
99  return;
100  }
101 
102  // Generate program object.
103  auto vert_shader = gl->CreateShader(GL_VERTEX_SHADER);
104  auto frag_shader = gl->CreateShader(GL_FRAGMENT_SHADER);
105 
106  GLint vert_shader_size = strlen(kVertShader);
107  GLint frag_shader_size = strlen(kFragShader);
108 
109  gl->ShaderSource(vert_shader, 1u, &kVertShader, &vert_shader_size);
110  gl->ShaderSource(frag_shader, 1u, &kFragShader, &frag_shader_size);
111 
112  gl->CompileShader(vert_shader);
113  gl->CompileShader(frag_shader);
114 
115  GLint vert_status = GL_FALSE;
116  GLint frag_status = GL_FALSE;
117 
118  gl->GetShaderiv(vert_shader, GL_COMPILE_STATUS, &vert_status);
119  gl->GetShaderiv(frag_shader, GL_COMPILE_STATUS, &frag_status);
120 
121  FML_CHECK(vert_status == GL_TRUE);
122  FML_CHECK(frag_status == GL_TRUE);
123 
124  program_ = gl->CreateProgram();
125  gl->AttachShader(program_, vert_shader);
126  gl->AttachShader(program_, frag_shader);
127 
128  gl->BindAttribLocation(program_, kAttributeIndexPosition, "aPosition");
129  gl->BindAttribLocation(program_, kAttributeIndexTexCoord, "aTexCoord");
130 
131  gl->LinkProgram(program_);
132 
133  GLint link_status = GL_FALSE;
134  gl->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
135  FML_CHECK(link_status == GL_TRUE);
136 
137  texture_uniform_location_ = gl->GetUniformLocation(program_, "uTexture");
138  uv_transformation_location_ =
139  gl->GetUniformLocation(program_, "uUVTransformation");
140 
141  gl->DeleteShader(vert_shader);
142  gl->DeleteShader(frag_shader);
143 
144  egl_context->ClearCurrent();
145 
146  gl_ = std::move(gl);
147  egl_display_ = std::move(egl_display);
148  egl_context_ = std::move(egl_context);
149  egl_surface_ = std::move(egl_surface);
150  is_valid_ = true;
151 }
152 
154  if (!is_valid_) {
155  return;
156  }
157  auto context = MakeCurrentContext();
158  gl_->DeleteProgram(program_);
159 }
160 
161 bool Trampoline::IsValid() const {
162  return is_valid_;
163 }
164 
166  const EGLDisplay& display,
167  const AHBTextureSourceVK& to_texture) {
168  if (!android::GetProcTable().eglGetNativeClientBufferANDROID.IsAvailable()) {
169  VALIDATION_LOG << "Could not get native client buffer.";
170  return {};
171  }
172 
173  EGLClientBuffer client_buffer =
174  android::GetProcTable().eglGetNativeClientBufferANDROID(
175  to_texture.GetBackingStore()->GetHandle());
176 
177  if (!client_buffer) {
179  << "Could not get client buffer from Android hardware buffer.";
180  return {};
181  }
182 
183  auto image = ::eglCreateImageKHR(display, //
184  EGL_NO_CONTEXT, //
185  EGL_NATIVE_BUFFER_ANDROID, //
186  client_buffer, //
187  nullptr //
188  );
189  if (image == NULL) {
190  VALIDATION_LOG << "Could not create EGL Image.";
191  return {};
192  }
193 
194  return UniqueEGLImageKHR(EGLImageKHRWithDisplay{image, display});
195 }
196 
198  const GLTextureInfo& src_texture,
199  const AHBTextureSourceVK& dst_texture) const {
200  TRACE_EVENT0("impeller", __FUNCTION__);
201  if (!is_valid_) {
202  return false;
203  }
204 
205  FML_DCHECK(egl_context_->IsCurrent());
206 
207  auto dst_egl_image =
208  CreateEGLImageFromAHBTexture(egl_display_->GetHandle(), dst_texture);
209  if (!dst_egl_image.is_valid()) {
210  VALIDATION_LOG << "Could not create EGL image from AHB texture.";
211  return false;
212  }
213 
214  const auto& gl = *gl_;
215 
216  GLuint dst_gl_texture = GL_NONE;
217  gl.GenTextures(1u, &dst_gl_texture);
218  gl.BindTexture(GL_TEXTURE_2D, dst_gl_texture);
219  gl.EGLImageTargetTexture2DOES(GL_TEXTURE_2D, dst_egl_image.get().image);
220 
221  GLuint offscreen_fbo = GL_NONE;
222  gl.GenFramebuffers(1u, &offscreen_fbo);
223  gl.BindFramebuffer(GL_FRAMEBUFFER, offscreen_fbo);
224  gl.FramebufferTexture2D(GL_FRAMEBUFFER, //
225  GL_COLOR_ATTACHMENT0, //
226  GL_TEXTURE_2D, //
227  dst_gl_texture, //
228  0 //
229  );
230 
231  FML_CHECK(gl.CheckFramebufferStatus(GL_FRAMEBUFFER) ==
232  GL_FRAMEBUFFER_COMPLETE);
233 
234  gl.Disable(GL_BLEND);
235  gl.Disable(GL_SCISSOR_TEST);
236  gl.Disable(GL_DITHER);
237  gl.Disable(GL_CULL_FACE);
238  gl.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
239 
240  gl.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
241  gl.Clear(GL_COLOR_BUFFER_BIT);
242 
243  const auto& fb_size = dst_texture.GetTextureDescriptor().size;
244  gl.Viewport(0, 0, fb_size.width, fb_size.height);
245 
246  gl.UseProgram(program_);
247 
248  struct VertexData {
249  Point position;
250  Point tex_coord;
251  };
252 
253  // The vertex coordinates assume OpenGL NDC because that's the API we are
254  // using to draw the quad. But the texture will be sampled in Vulkan so the
255  // texture coordinate system assumes Vulkan convention.
256  //
257  // See the following help link for an overview of the different coordinate
258  // systems:
259  // https://github.com/flutter/engine/blob/5810b3fc791f4bb82b9a454014310990eddc1181/impeller/docs/coordinate_system.md
260  static constexpr const VertexData kVertData[] = {
261  {{-1, -1}, {0, 1}}, // bottom left
262  {{-1, +1}, {0, 0}}, // top left
263  {{+1, +1}, {1, 0}}, // top right
264  {{+1, -1}, {1, 1}}, // bottom right
265  };
266 
267  // This is tedious but we assume no vertex array objects (VAO) are available
268  // because of ES 2 versioning constraints.
269  GLuint vertex_buffer = GL_NONE;
270  gl.GenBuffers(1u, &vertex_buffer);
271  gl.BindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
272  gl.BufferData(GL_ARRAY_BUFFER, sizeof(kVertData), kVertData, GL_STATIC_DRAW);
273  gl.EnableVertexAttribArray(kAttributeIndexPosition);
274  gl.EnableVertexAttribArray(kAttributeIndexTexCoord);
275  gl.VertexAttribPointer(
276  kAttributeIndexPosition, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData),
277  reinterpret_cast<void*>(offsetof(VertexData, position)));
278  gl.VertexAttribPointer(
279  kAttributeIndexTexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData),
280  reinterpret_cast<void*>(offsetof(VertexData, tex_coord)));
281 
282  gl.ActiveTexture(GL_TEXTURE0);
283  gl.BindTexture(src_texture.target, src_texture.texture);
284  gl.TexParameteri(src_texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
285  gl.TexParameteri(src_texture.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
286  gl.TexParameteri(src_texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
287  gl.TexParameteri(src_texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
288  gl.Uniform1i(texture_uniform_location_, 0u);
289 
290  auto gl_uv_transformation = src_texture.uv_transformation;
291 
292  gl.UniformMatrix4fv(uv_transformation_location_, 1u, GL_FALSE,
293  reinterpret_cast<GLfloat*>(&gl_uv_transformation));
294 
295  gl.DrawArrays(GL_TRIANGLE_FAN, 0, 4);
296 
297  gl.UseProgram(GL_NONE);
298 
299  gl.Flush();
300 
301  gl.DeleteFramebuffers(1u, &offscreen_fbo);
302  gl.DeleteTextures(1u, &dst_gl_texture);
303  gl.DeleteBuffers(1u, &vertex_buffer);
304 
305  // Theoretically, this does nothing because the surface is a 1x1 pbuffer
306  // surface. But frame capture tools use this to denote a frame boundary in
307  // OpenGL. So add this as a debugging aid anyway.
308  eglSwapBuffers(egl_display_->GetHandle(), egl_surface_->GetHandle());
309 
310  return true;
311 }
312 
314  FML_DCHECK(is_valid_);
315  return AutoTrampolineContext{*this};
316 }
317 
319  : context_(trampoline.egl_context_.get()),
320  surface_(trampoline.egl_surface_.get()) {
321  if (!context_->IsCurrent() && !context_->MakeCurrent(*surface_)) {
322  VALIDATION_LOG << "Could not make context current.";
323  }
324 };
325 
327  if (!context_->ClearCurrent()) {
328  VALIDATION_LOG << "Could not clear current context.";
329  }
330 }
331 
332 } // namespace impeller::glvk
A texture source that wraps an instance of AHardwareBuffer.
const android::HardwareBuffer * GetBackingStore() const
const TextureDescriptor & GetTextureDescriptor() const
Gets the texture descriptor for this image source.
AHardwareBuffer * GetHandle() const
bool IsCurrent() const
Definition: context.cc:102
bool MakeCurrent(const Surface &surface) const
Make the context current on the calling thread. It is the caller responsibility to ensure that any co...
Definition: context.cc:45
bool ClearCurrent() const
Clear the thread association of this context.
Definition: context.cc:61
An RAII object that makes the trampolines EGL context current when constructed and clears the EGL bin...
Definition: trampoline.h:127
~AutoTrampolineContext()
Destroys the object and clears the previous EGL binding.
Definition: trampoline.cc:326
AutoTrampolineContext(const Trampoline &trampoline)
Constructs a new instance and makes the trampolines EGL context current on the calling thread.
Definition: trampoline.cc:318
An object used to interoperate between OpenGL and Vulkan.
Definition: trampoline.h:27
~Trampoline()
Destroys the trampoline. There are no threading restrictions. EGL contexts on already bound to the ca...
Definition: trampoline.cc:153
bool BlitTextureOpenGLToVulkan(const GLTextureInfo &src_texture, const AHBTextureSourceVK &dst_texture) const
Perform a blit operation from the source OpenGL texture to a target Vulkan texture.
Definition: trampoline.cc:197
bool IsValid() const
Determines if this is a valid trampoline. There is no error recovery mechanism if a trampoline cannot...
Definition: trampoline.cc:161
AutoTrampolineContext MakeCurrentContext() const
Make the EGL context associated with this trampoline current on the calling thread.
Definition: trampoline.cc:313
Trampoline()
Constructs a new trampoline. It is recommended that these objects be cached and reused for all conver...
Definition: trampoline.cc:57
const ProcTable & GetProcTable()
Definition: proc_table.cc:12
std::function< void *(const char *)> CreateProcAddressResolver()
Creates a proc address resolver that resolves function pointers to EGL and OpenGL (ES) procs.
Definition: egl.cc:12
static GLuint kAttributeIndexTexCoord
Definition: trampoline.cc:21
static GLuint kAttributeIndexPosition
Definition: trampoline.cc:20
static UniqueEGLImageKHR CreateEGLImageFromAHBTexture(const EGLDisplay &display, const AHBTextureSourceVK &to_texture)
Definition: trampoline.cc:165
static constexpr const char * kFragShader
Definition: trampoline.cc:39
static constexpr const char * kVertShader
Definition: trampoline.cc:23
fml::UniqueObject< EGLImageKHRWithDisplay, EGLImageKHRWithDisplayTraits > UniqueEGLImageKHR
Definition: image.h:73
Describes an OpenGL texture along with information on how to sample from it.
Definition: trampoline.h:62
#define VALIDATION_LOG
Definition: validation.h:91