| 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
5 #include "impeller/toolkit/glvk/trampoline.h"
6
7 #include <array>
8
9 #include "flutter/fml/closure.h"
10 #include "flutter/fml/trace_event.h"
11 #include "impeller/base/timing.h"
12 #include "impeller/base/validation.h"
13 #include "impeller/geometry/point.h"
14 #include "impeller/renderer/backend/gles/formats_gles.h"
15 #include "impeller/toolkit/android/proc_table.h"
16 #include "impeller/toolkit/egl/image.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
57 Trampoline::Trampoline() {
58 auto egl_display = std::make_unique<egl::Display>();
59 if (!egl_display->IsValid()) {
60 VALIDATION_LOG
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) {
74 VALIDATION_LOG
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
153 Trampoline::~Trampoline() {
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
165 static UniqueEGLImageKHR CreateEGLImageFromAHBTexture(
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) {
178 VALIDATION_LOG
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
197 bool Trampoline::BlitTextureOpenGLToVulkan(
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
313 AutoTrampolineContext Trampoline::MakeCurrentContext() const {
314 FML_DCHECK(is_valid_);
315 return AutoTrampolineContext{*this};
316 }
317
318 AutoTrampolineContext::AutoTrampolineContext(const Trampoline& trampoline)
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
326 AutoTrampolineContext::~AutoTrampolineContext() {
327 if (!context_->ClearCurrent()) {
328 VALIDATION_LOG << "Could not clear current context.";
329 }
330 }
331
332 } // namespace impeller::glvk
proc_table.h
impeller::AHBTextureSourceVK
A texture source that wraps an instance of AHardwareBuffer.
Definition: ahb_texture_source_vk.h:32
impeller::AHBTextureSourceVK::GetBackingStore
const android::HardwareBuffer * GetBackingStore() const
Definition: ahb_texture_source_vk.cc:438
impeller::TextureSourceVK::GetTextureDescriptor
const TextureDescriptor & GetTextureDescriptor() const
Gets the texture descriptor for this image source.
Definition: texture_source_vk.cc:13
impeller::android::HardwareBuffer::GetHandle
AHardwareBuffer * GetHandle() const
Definition: hardware_buffer.cc:83
impeller::egl::Context::IsCurrent
bool IsCurrent() const
Definition: context.cc:102
impeller::egl::Context::MakeCurrent
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
impeller::egl::Context::ClearCurrent
bool ClearCurrent() const
Clear the thread association of this context.
Definition: context.cc:61
impeller::glvk::AutoTrampolineContext
An RAII object that makes the trampolines EGL context current when constructed and clears the EGL bin...
Definition: trampoline.h:127
impeller::glvk::AutoTrampolineContext::~AutoTrampolineContext
~AutoTrampolineContext()
Destroys the object and clears the previous EGL binding.
Definition: trampoline.cc:326
impeller::glvk::AutoTrampolineContext::AutoTrampolineContext
AutoTrampolineContext(const Trampoline &trampoline)
Constructs a new instance and makes the trampolines EGL context current on the calling thread.
Definition: trampoline.cc:318
impeller::glvk::Trampoline
An object used to interoperate between OpenGL and Vulkan.
Definition: trampoline.h:27
impeller::glvk::Trampoline::~Trampoline
~Trampoline()
Destroys the trampoline. There are no threading restrictions. EGL contexts on already bound to the ca...
Definition: trampoline.cc:153
impeller::glvk::Trampoline::BlitTextureOpenGLToVulkan
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
impeller::glvk::Trampoline::IsValid
bool IsValid() const
Determines if this is a valid trampoline. There is no error recovery mechanism if a trampoline cannot...
Definition: trampoline.cc:161
impeller::glvk::Trampoline::MakeCurrentContext
AutoTrampolineContext MakeCurrentContext() const
Make the EGL context associated with this trampoline current on the calling thread.
Definition: trampoline.cc:313
impeller::glvk::Trampoline::Trampoline
Trampoline()
Constructs a new trampoline. It is recommended that these objects be cached and reused for all conver...
Definition: trampoline.cc:57
formats_gles.h
image.h
impeller::android::GetProcTable
const ProcTable & GetProcTable()
Definition: proc_table.cc:12
impeller::egl::ColorFormat::kRGBA8888
@ kRGBA8888
impeller::egl::Samples::kOne
@ kOne
impeller::egl::SurfaceType::kPBuffer
@ kPBuffer
impeller::egl::StencilBits::kZero
@ kZero
impeller::egl::CreateProcAddressResolver
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
impeller::egl::DepthBits::kZero
@ kZero
impeller::egl::API::kOpenGLES2
@ kOpenGLES2
impeller::glvk
Definition: proc_table.cc:9
impeller::glvk::kAttributeIndexTexCoord
static GLuint kAttributeIndexTexCoord
Definition: trampoline.cc:21
impeller::glvk::kAttributeIndexPosition
static GLuint kAttributeIndexPosition
Definition: trampoline.cc:20
impeller::glvk::CreateEGLImageFromAHBTexture
static UniqueEGLImageKHR CreateEGLImageFromAHBTexture(const EGLDisplay &display, const AHBTextureSourceVK &to_texture)
Definition: trampoline.cc:165
impeller::glvk::kFragShader
static constexpr const char * kFragShader
Definition: trampoline.cc:39
impeller::glvk::kVertShader
static constexpr const char * kVertShader
Definition: trampoline.cc:23
impeller::UniqueEGLImageKHR
fml::UniqueObject< EGLImageKHRWithDisplay, EGLImageKHRWithDisplayTraits > UniqueEGLImageKHR
Definition: image.h:73
point.h
impeller::EGLImageKHRWithDisplay
Definition: image.h:45
impeller::TPoint< Scalar >
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
impeller::egl::ConfigDescriptor
Definition: config.h:46
impeller::egl::ConfigDescriptor::samples
Samples samples
Definition: config.h:48
impeller::egl::ConfigDescriptor::api
API api
Definition: config.h:47
impeller::egl::ConfigDescriptor::color_format
ColorFormat color_format
Definition: config.h:49
impeller::egl::ConfigDescriptor::stencil_bits
StencilBits stencil_bits
Definition: config.h:50
impeller::egl::ConfigDescriptor::depth_bits
DepthBits depth_bits
Definition: config.h:51
impeller::egl::ConfigDescriptor::surface_type
SurfaceType surface_type
Definition: config.h:52
impeller::glvk::Trampoline::GLTextureInfo
Describes an OpenGL texture along with information on how to sample from it.
Definition: trampoline.h:62
impeller::glvk::Trampoline::GLTextureInfo::uv_transformation
Matrix uv_transformation
Definition: trampoline.h:76
impeller::glvk::Trampoline::GLTextureInfo::texture
GLuint texture
Definition: trampoline.h:66
impeller::glvk::Trampoline::GLTextureInfo::target
GLenum target
Definition: trampoline.h:71
timing.h
trampoline.h
validation.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91