Flutter Engine
 
Loading...
Searching...
No Matches
blit_command_gles.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
15
16namespace impeller {
17
18namespace {
19static void FlipImage(uint8_t* buffer,
20 size_t width,
21 size_t height,
22 size_t stride) {
23 if (buffer == nullptr || stride == 0) {
24 return;
25 }
26
27 const auto byte_width = width * stride;
28
29 for (size_t top = 0; top < height; top++) {
30 size_t bottom = height - top - 1;
31 if (top >= bottom) {
32 break;
33 }
34 auto* top_row = buffer + byte_width * top;
35 auto* bottom_row = buffer + byte_width * bottom;
36 std::swap_ranges(top_row, top_row + byte_width, bottom_row);
37 }
38}
39} // namespace
40
42
43static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) {
44 if (fbo != GL_NONE) {
45 gl.BindFramebuffer(type, GL_NONE);
46 gl.DeleteFramebuffers(1u, &fbo);
47 }
48};
49
50static std::optional<GLuint> ConfigureFBO(
51 const ProcTableGLES& gl,
52 const std::shared_ptr<Texture>& texture,
53 GLenum fbo_type) {
54 auto handle = TextureGLES::Cast(texture.get())->GetGLHandle();
55 if (!handle.has_value()) {
56 return std::nullopt;
57 }
58
59 if (TextureGLES::Cast(*texture).IsWrapped()) {
60 // The texture is attached to the default FBO, so there's no need to
61 // create/configure one.
62 gl.BindFramebuffer(fbo_type, 0);
63 return 0;
64 }
65
66 GLuint fbo;
67 gl.GenFramebuffers(1u, &fbo);
68 gl.BindFramebuffer(fbo_type, fbo);
69
72 VALIDATION_LOG << "Could not attach texture to framebuffer.";
73 DeleteFBO(gl, fbo, fbo_type);
74 return std::nullopt;
75 }
76
77 GLenum status = gl.CheckFramebufferStatus(fbo_type);
78 if (status != GL_FRAMEBUFFER_COMPLETE) {
79 VALIDATION_LOG << "Could not create a complete framebuffer: "
80 << DebugToFramebufferError(status);
81 DeleteFBO(gl, fbo, fbo_type);
82 return std::nullopt;
83 }
84
85 return fbo;
86};
87
89 default;
90
92 return label;
93}
94
96 const ReactorGLES& reactor) const {
97 const auto& gl = reactor.GetProcTable();
98
99 // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
100 // emulate the blit when it's not available in the driver.
101 if (!gl.BlitFramebuffer.IsAvailable()) {
102 // TODO(157064): Emulate the blit using a raster draw call here.
103 VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
104 return false;
105 }
106
107 GLuint read_fbo = GL_NONE;
108 GLuint draw_fbo = GL_NONE;
109 fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
110 DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
111 DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
112 });
113
114 {
115 auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
116 if (!read.has_value()) {
117 return false;
118 }
119 read_fbo = read.value();
120 }
121
122 {
123 auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
124 if (!draw.has_value()) {
125 return false;
126 }
127 draw_fbo = draw.value();
128 }
129
130 gl.Disable(GL_SCISSOR_TEST);
131 gl.Disable(GL_DEPTH_TEST);
132 gl.Disable(GL_STENCIL_TEST);
133
134 gl.BlitFramebuffer(source_region.GetX(), // srcX0
135 source_region.GetY(), // srcY0
136 source_region.GetWidth(), // srcX1
137 source_region.GetHeight(), // srcY1
138 destination_origin.x, // dstX0
139 destination_origin.y, // dstY0
140 source_region.GetWidth(), // dstX1
141 source_region.GetHeight(), // dstY1
142 GL_COLOR_BUFFER_BIT, // mask
143 GL_NEAREST // filter
144 );
145
146 return true;
147};
148
149namespace {
150struct TexImage2DData {
152 GLenum external_format = GL_NONE;
153 GLenum type = GL_NONE;
154 BufferView buffer_view;
155
156 explicit TexImage2DData(PixelFormat pixel_format) {
157 switch (pixel_format) {
159 internal_format = GL_ALPHA;
160 external_format = GL_ALPHA;
161 type = GL_UNSIGNED_BYTE;
162 break;
164 internal_format = GL_RED;
165 external_format = GL_RED;
166 type = GL_UNSIGNED_BYTE;
167 break;
172 internal_format = GL_RGBA;
173 external_format = GL_RGBA;
174 type = GL_UNSIGNED_BYTE;
175 break;
177 internal_format = GL_RGBA;
178 external_format = GL_RGBA;
179 type = GL_FLOAT;
180 break;
182 internal_format = GL_RGBA;
183 external_format = GL_RGBA;
184 type = GL_HALF_FLOAT;
185 break;
187 // Pure stencil textures are only available in OpenGL 4.4+, which is
188 // ~0% of mobile devices. Instead, we use a depth-stencil texture and
189 // only use the stencil component.
190 //
191 // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
193 internal_format = GL_DEPTH_STENCIL;
194 external_format = GL_DEPTH_STENCIL;
195 type = GL_UNSIGNED_INT_24_8;
196 break;
203 return;
204 }
205 is_valid_ = true;
206 }
207
208 TexImage2DData(PixelFormat pixel_format, BufferView p_buffer_view)
209 : TexImage2DData(pixel_format) {
210 buffer_view = std::move(p_buffer_view);
211 }
212
213 bool IsValid() const { return is_valid_; }
214
215 private:
216 bool is_valid_ = false;
217};
218} // namespace
219
221 default;
222
224 return label;
225}
226
228 const ReactorGLES& reactor) const {
229 TextureGLES& texture_gles = TextureGLES::Cast(*destination);
230
231 if (texture_gles.GetType() != TextureGLES::Type::kTexture) {
232 VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
233 "this texture object.";
234 return false;
235 }
236
237 if (texture_gles.IsWrapped()) {
238 VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
239 return false;
240 }
241
242 const auto& tex_descriptor = texture_gles.GetTextureDescriptor();
243
244 if (tex_descriptor.size.IsEmpty()) {
245 return true;
246 }
247
248 if (!tex_descriptor.IsValid() ||
250 BytesPerPixelForPixelFormat(tex_descriptor.format) *
252 return false;
253 }
254
256
257 GLenum texture_type;
258 GLenum texture_target;
259 switch (tex_descriptor.type) {
261 texture_type = GL_TEXTURE_2D;
262 texture_target = GL_TEXTURE_2D;
263 break;
265 VALIDATION_LOG << "Multisample texture uploading is not supported for "
266 "the OpenGLES backend.";
267 return false;
269 texture_type = GL_TEXTURE_CUBE_MAP;
270 texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
271 break;
273 texture_type = GL_TEXTURE_EXTERNAL_OES;
274 texture_target = GL_TEXTURE_EXTERNAL_OES;
275 break;
276 }
277
278 TexImage2DData data = TexImage2DData(tex_descriptor.format, source);
279 if (!data.IsValid()) {
280 VALIDATION_LOG << "Invalid texture format.";
281 return false;
282 }
283
284 auto gl_handle = texture_gles.GetGLHandle();
285 if (!gl_handle.has_value()) {
287 << "Texture was collected before it could be uploaded to the GPU.";
288 return false;
289 }
290 const auto& gl = reactor.GetProcTable();
291 gl.BindTexture(texture_type, gl_handle.value());
292 const GLvoid* tex_data = data.buffer_view.GetBuffer()->OnGetContents() +
293 data.buffer_view.GetRange().offset;
294
295 // GL_INVALID_OPERATION if the texture array has not been
296 // defined by a previous glTexImage2D operation.
297 if (!texture_gles.IsSliceInitialized(slice)) {
298 gl.TexImage2D(texture_target, // target
299 mip_level, // LOD level
300 data.internal_format, // internal format
301 tex_descriptor.size.width, // width
302 tex_descriptor.size.height, // height
303 0u, // border
304 data.external_format, // external format
305 data.type, // type
306 nullptr // data
307 );
308 texture_gles.MarkSliceInitialized(slice);
309 }
310
311 {
312 gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
313 gl.TexSubImage2D(texture_target, // target
314 mip_level, // LOD level
315 destination_region.GetX(), // xoffset
316 destination_region.GetY(), // yoffset
317 destination_region.GetWidth(), // width
318 destination_region.GetHeight(), // height
319 data.external_format, // external format
320 data.type, // type
321 tex_data // data
322
323 );
324 }
325 return true;
326}
327
329 default;
330
332 return label;
333}
334
336 const ReactorGLES& reactor) const {
337 if (source->GetTextureDescriptor().format != PixelFormat::kR8G8B8A8UNormInt) {
338 VALIDATION_LOG << "Only textures with pixel format RGBA are supported yet.";
339 return false;
340 }
341
342 const auto& gl = reactor.GetProcTable();
343 TextureCoordinateSystem coord_system = source->GetCoordinateSystem();
344
345 GLuint read_fbo = GL_NONE;
346 fml::ScopedCleanupClosure delete_fbos(
347 [&gl, &read_fbo]() { DeleteFBO(gl, read_fbo, GL_FRAMEBUFFER); });
348
349 {
350 auto read = ConfigureFBO(gl, source, GL_FRAMEBUFFER);
351 if (!read.has_value()) {
352 return false;
353 }
354 read_fbo = read.value();
355 }
356
358 .UpdateBufferData([&gl, this, coord_system,
359 rows = source->GetSize().height](uint8_t* data,
360
361 size_t length) {
362 gl.ReadPixels(source_region.GetX(), source_region.GetY(),
363 source_region.GetWidth(), source_region.GetHeight(),
364 GL_RGBA, GL_UNSIGNED_BYTE, data + destination_offset);
365 switch (coord_system) {
366 case TextureCoordinateSystem::kUploadFromHost:
367 break;
368 case TextureCoordinateSystem::kRenderToTexture:
369 // The texture is upside down, and must be inverted when copying
370 // byte data out.
371 FlipImage(data + destination_offset, source_region.GetWidth(),
372 source_region.GetHeight(), 4);
373 }
374 });
375
376 return true;
377};
378
379BlitGenerateMipmapCommandGLES::~BlitGenerateMipmapCommandGLES() = default;
380
381std::string BlitGenerateMipmapCommandGLES::GetLabel() const {
382 return label;
383}
384
385bool BlitGenerateMipmapCommandGLES::Encode(const ReactorGLES& reactor) const {
386 auto texture_gles = TextureGLES::Cast(texture.get());
387 if (!texture_gles->GenerateMipmap()) {
388 return false;
389 }
390
391 return true;
392};
393
394////// BlitResizeTextureCommandGLES
395//////////////////////////////////////////////////////
396
397BlitResizeTextureCommandGLES::~BlitResizeTextureCommandGLES() = default;
398
399std::string BlitResizeTextureCommandGLES::GetLabel() const {
400 return "Resize Texture";
401}
402
403bool BlitResizeTextureCommandGLES::Encode(const ReactorGLES& reactor) const {
404 const auto& gl = reactor.GetProcTable();
405
406 // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
407 // emulate the blit when it's not available in the driver.
408 if (!gl.BlitFramebuffer.IsAvailable()) {
409 // TODO(157064): Emulate the blit using a raster draw call here.
410 VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
411 return false;
412 }
413
414 destination->SetCoordinateSystem(source->GetCoordinateSystem());
415
416 GLuint read_fbo = GL_NONE;
417 GLuint draw_fbo = GL_NONE;
418 fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
419 DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
420 DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
421 });
422
423 {
424 auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
425 if (!read.has_value()) {
426 return false;
427 }
428 read_fbo = read.value();
429 }
430
431 {
432 auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
433 if (!draw.has_value()) {
434 return false;
435 }
436 draw_fbo = draw.value();
437 }
438
439 gl.Disable(GL_SCISSOR_TEST);
440 gl.Disable(GL_DEPTH_TEST);
441 gl.Disable(GL_STENCIL_TEST);
442
443 const IRect source_region = IRect::MakeSize(source->GetSize());
444 const IRect destination_region = IRect::MakeSize(destination->GetSize());
445
446 gl.BlitFramebuffer(source_region.GetX(), // srcX0
447 source_region.GetY(), // srcY0
448 source_region.GetWidth(), // srcX1
449 source_region.GetHeight(), // srcY1
450 destination_region.GetX(), // dstX0
451 destination_region.GetY(), // dstY0
452 destination_region.GetWidth(), // dstX1
453 destination_region.GetHeight(), // dstY1
454 GL_COLOR_BUFFER_BIT, // mask
455 GL_LINEAR // filter
456 );
457
458 return true;
459}
460
461} // namespace impeller
GLenum type
GLenum external_format
GLint internal_format
BufferView buffer_view
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition closure.h:32
static TextureGLES & Cast(Texture &base)
void UpdateBufferData(const std::function< void(uint8_t *, size_t length)> &update_buffer_data)
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
bool IsSliceInitialized(size_t slice) const
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type) const
void MarkSliceInitialized(size_t slice) const
Indicates that a specific texture slice has been initialized.
std::optional< GLuint > GetGLHandle() const
const TextureDescriptor & GetTextureDescriptor() const
Definition texture.cc:57
size_t length
FlTexture * texture
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format)
Definition formats.h:466
std::string DebugToFramebufferError(int status)
static std::optional< GLuint > ConfigureFBO(const ProcTableGLES &gl, const std::shared_ptr< Texture > &texture, GLenum fbo_type)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:99
static void DeleteFBO(const ProcTableGLES &gl, GLuint fbo, GLenum type)
TextureCoordinateSystem
Definition formats.h:327
int32_t height
int32_t width
bool Encode(const ReactorGLES &reactor) const override
std::shared_ptr< Texture > destination
bool Encode(const ReactorGLES &reactor) const override
std::shared_ptr< DeviceBuffer > destination
std::shared_ptr< Texture > source
bool Encode(const ReactorGLES &reactor) const override
std::shared_ptr< Texture > destination
std::shared_ptr< Texture > source
Range GetRange() const
Definition buffer_view.h:27
size_t length
Definition range.h:15
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition rect.h:337
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition rect.h:347
constexpr T Area() const
Get the area of the rectangle, equivalent to |GetSize().Area()|.
Definition rect.h:376
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition rect.h:333
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition rect.h:341
std::shared_ptr< const fml::Mapping > data
#define VALIDATION_LOG
Definition validation.h:91