Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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
7#include <algorithm>
8
17
18namespace impeller {
19
21
22static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) {
23 if (fbo != GL_NONE) {
24 gl.BindFramebuffer(type, GL_NONE);
25 gl.DeleteFramebuffers(1u, &fbo);
26 }
27};
28
29static std::optional<GLuint> ConfigureFBO(
30 const ProcTableGLES& gl,
31 const std::shared_ptr<Texture>& texture,
32 GLenum fbo_type) {
33 auto handle = TextureGLES::Cast(texture.get())->GetGLHandle();
34 if (!handle.has_value()) {
35 return std::nullopt;
36 }
37
38 if (TextureGLES::Cast(*texture).IsWrapped()) {
39 // The texture is attached to the default FBO, so there's no need to
40 // create/configure one.
41 gl.BindFramebuffer(fbo_type, 0);
42 return 0;
43 }
44
45 GLuint fbo;
46 gl.GenFramebuffers(1u, &fbo);
47 gl.BindFramebuffer(fbo_type, fbo);
48
51 VALIDATION_LOG << "Could not attach texture to framebuffer.";
52 DeleteFBO(gl, fbo, fbo_type);
53 return std::nullopt;
54 }
55
56 GLenum status = gl.CheckFramebufferStatus(fbo_type);
57 if (status != GL_FRAMEBUFFER_COMPLETE) {
58 VALIDATION_LOG << "Could not create a complete framebuffer: "
59 << DebugToFramebufferError(status);
60 DeleteFBO(gl, fbo, fbo_type);
61 return std::nullopt;
62 }
63
64 return fbo;
65};
66
68 default;
69
71 return label;
72}
73
75 const ReactorGLES& reactor) const {
76 const auto& gl = reactor.GetProcTable();
77
78 // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
79 // emulate the blit when it's not available in the driver.
80 if (!gl.BlitFramebuffer.IsAvailable()) {
81 // TODO(157064): Emulate the blit using a raster draw call here.
82 VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
83 return false;
84 }
85
86 GLuint read_fbo = GL_NONE;
87 GLuint draw_fbo = GL_NONE;
88 fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
89 DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
90 DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
91 });
92
93 {
94 auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
95 if (!read.has_value()) {
96 return false;
97 }
98 read_fbo = read.value();
99 }
100
101 {
102 auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
103 if (!draw.has_value()) {
104 return false;
105 }
106 draw_fbo = draw.value();
107 }
108
109 gl.Disable(GL_SCISSOR_TEST);
110 gl.Disable(GL_DEPTH_TEST);
111 gl.Disable(GL_STENCIL_TEST);
112
113 const auto destination_right =
115 const auto destination_bottom =
117
118 gl.BlitFramebuffer(source_region.GetX(), // srcX0
119 source_region.GetY(), // srcY0
120 source_region.GetRight(), // srcX1
121 source_region.GetBottom(), // srcY1
122 destination_origin.x, // dstX0
123 destination_origin.y, // dstY0
124 destination_right, // dstX1
125 destination_bottom, // dstY1
126 GL_COLOR_BUFFER_BIT, // mask
127 GL_NEAREST // filter
128 );
129
130 return true;
131};
132
134 default;
135
137 return label;
138}
139
141 const ReactorGLES& reactor) const {
142 TextureGLES& texture_gles = TextureGLES::Cast(*destination);
143
144 if (texture_gles.GetType() != TextureGLES::Type::kTexture) {
145 VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
146 "this texture object.";
147 return false;
148 }
149
150 if (texture_gles.IsWrapped()) {
151 VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
152 return false;
153 }
154
155 const auto& tex_descriptor = texture_gles.GetTextureDescriptor();
156
157 if (tex_descriptor.size.IsEmpty()) {
158 return true;
159 }
160
161 if (!tex_descriptor.IsValid() ||
163 BytesForTextureRegion(tex_descriptor.format,
166 return false;
167 }
168
169 GLenum texture_type;
170 GLenum texture_target;
171 switch (tex_descriptor.type) {
173 texture_type = GL_TEXTURE_2D;
174 texture_target = GL_TEXTURE_2D;
175 break;
177 VALIDATION_LOG << "Multisample texture uploading is not supported for "
178 "the OpenGLES backend.";
179 return false;
181 texture_type = GL_TEXTURE_CUBE_MAP;
182 texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
183 break;
185 texture_type = GL_TEXTURE_EXTERNAL_OES;
186 texture_target = GL_TEXTURE_EXTERNAL_OES;
187 break;
188 }
189
190 std::optional<PixelFormatGLES> gles_format =
191 ToPixelFormatGLES(tex_descriptor.format,
192 /*supports_bgra=*/
193 reactor.GetProcTable().GetDescription()->HasExtension(
194 "GL_EXT_texture_format_BGRA8888"));
195 if (!gles_format.has_value()) {
196 VALIDATION_LOG << "Invalid texture format.";
197 return false;
198 }
199
200 auto gl_handle = texture_gles.GetGLHandle();
201 if (!gl_handle.has_value()) {
203 << "Texture was collected before it could be uploaded to the GPU.";
204 return false;
205 }
206 const auto& gl = reactor.GetProcTable();
207 gl.BindTexture(texture_type, gl_handle.value());
208 const GLvoid* tex_data =
210
211 // Block-compressed textures cannot be allocated empty and then filled with a
212 // sub-image; glCompressedTexImage2D redefines the entire mip level. Require
213 // the upload to cover the full mip level starting at the origin.
214 if (gles_format->is_compressed) {
215 const auto mip_width =
216 std::max<int32_t>(1, tex_descriptor.size.width >> mip_level);
217 const auto mip_height =
218 std::max<int32_t>(1, tex_descriptor.size.height >> mip_level);
219 if (destination_region.GetX() != 0 || destination_region.GetY() != 0 ||
220 destination_region.GetWidth() != mip_width ||
221 destination_region.GetHeight() != mip_height) {
222 VALIDATION_LOG << "Compressed textures must be uploaded as a full mip "
223 "level starting at the origin.";
224 return false;
225 }
226 gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
227 gl.CompressedTexImage2D(texture_target, // target
228 mip_level, // LOD level
229 gles_format->internal_format, // internal format
230 mip_width, // width
231 mip_height, // height
232 0u, // border
233 source.GetRange().length, // image size
234 tex_data); // data
236 return true;
237 }
238
239 // GL_INVALID_OPERATION if the requested mip level has not been defined by
240 // a previous glTexImage2D operation. Allocate the requested mip lazily on
241 // first write, only for the level the upload is actually targeting. The
242 // snapshot pipeline (single base-level allocation followed by
243 // glGenerateMipmap) keeps its existing GL footprint, and per-level uploads
244 // pay only for the levels they touch.
245 if (!texture_gles.IsSliceMipLevelInitialized(slice, mip_level)) {
246 const auto level_width =
247 std::max<int32_t>(1, tex_descriptor.size.width >> mip_level);
248 const auto level_height =
249 std::max<int32_t>(1, tex_descriptor.size.height >> mip_level);
250 gl.TexImage2D(texture_target, // target
251 mip_level, // LOD level
252 gles_format->internal_format, // internal format
253 level_width, // width
254 level_height, // height
255 0u, // border
256 gles_format->external_format, // format
257 gles_format->type, // type
258 nullptr); // data
260 }
261
262 {
263 gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
264 gl.TexSubImage2D(texture_target, // target
265 mip_level, // LOD level
266 destination_region.GetX(), // xoffset
267 destination_region.GetY(), // yoffset
268 destination_region.GetWidth(), // width
269 destination_region.GetHeight(), // height
270 gles_format->external_format, // format
271 gles_format->type, // type
272 tex_data); // data
273 }
274 return true;
275}
276
278 default;
279
281 return label;
282}
283
285 const ReactorGLES& reactor) const {
286 const auto& gl = reactor.GetProcTable();
287
288 PixelFormat source_format = source->GetTextureDescriptor().format;
289 std::optional<PixelFormatGLES> gles_format =
290 ToPixelFormatGLES(source_format,
291 /*supports_bgra=*/
292 reactor.GetProcTable().GetDescription()->HasExtension(
293 "GL_EXT_texture_format_BGRA8888"));
294
295 if (!gles_format.has_value()) {
296 VALIDATION_LOG << "Texture has unsupported pixel format.";
297 return false;
298 }
299
300 GLuint read_fbo = GL_NONE;
301 fml::ScopedCleanupClosure delete_fbos(
302 [&gl, &read_fbo]() { DeleteFBO(gl, read_fbo, GL_FRAMEBUFFER); });
303
304 {
305 auto read = ConfigureFBO(gl, source, GL_FRAMEBUFFER);
306 if (!read.has_value()) {
307 return false;
308 }
309 read_fbo = read.value();
310 }
311
313 .UpdateBufferData([&gl, //
314 this, //
315 format = gles_format->external_format, //
316 type = gles_format->type //
317 ](uint8_t* data, size_t length) {
318 gl.ReadPixels(source_region.GetX(), source_region.GetY(),
319 source_region.GetWidth(), source_region.GetHeight(),
320 format, type, data + destination_offset);
321 });
322
323 return true;
324};
325
327
329 return label;
330}
331
333 auto texture_gles = TextureGLES::Cast(texture.get());
334 if (!texture_gles->GenerateMipmap()) {
335 return false;
336 }
337
338 return true;
339};
340
341////// BlitResizeTextureCommandGLES
342//////////////////////////////////////////////////////
343
345
347 return "Resize Texture";
348}
349
351 const auto& gl = reactor.GetProcTable();
352
353 // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
354 // emulate the blit when it's not available in the driver.
355 if (!gl.BlitFramebuffer.IsAvailable()) {
356 // TODO(157064): Emulate the blit using a raster draw call here.
357 VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
358 return false;
359 }
360
361 GLuint read_fbo = GL_NONE;
362 GLuint draw_fbo = GL_NONE;
363 fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
364 DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
365 DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
366 });
367
368 {
369 auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
370 if (!read.has_value()) {
371 return false;
372 }
373 read_fbo = read.value();
374 }
375
376 {
377 auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
378 if (!draw.has_value()) {
379 return false;
380 }
381 draw_fbo = draw.value();
382 }
383
384 gl.Disable(GL_SCISSOR_TEST);
385 gl.Disable(GL_DEPTH_TEST);
386 gl.Disable(GL_STENCIL_TEST);
387
388 const IRect source_region = IRect::MakeSize(source->GetSize());
389 const IRect destination_region = IRect::MakeSize(destination->GetSize());
390
391 gl.BlitFramebuffer(source_region.GetX(), // srcX0
392 source_region.GetY(), // srcY0
393 source_region.GetWidth(), // srcX1
394 source_region.GetHeight(), // srcY1
395 destination_region.GetX(), // dstX0
396 destination_region.GetY(), // dstY0
397 destination_region.GetWidth(), // dstX1
398 destination_region.GetHeight(), // dstY1
399 GL_COLOR_BUFFER_BIT, // mask
400 GL_LINEAR // filter
401 );
402
403 return true;
404}
405
406} // namespace impeller
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)
virtual uint8_t * OnGetContents() const =0
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
bool IsSliceMipLevelInitialized(size_t slice, size_t mip_level) const
void MarkSliceMipLevelInitialized(size_t slice, size_t mip_level)
Indicates that storage for mip_level of slice has been allocated by a glTexImage2D call (or equivalen...
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type, uint32_t mip_level=0, uint32_t slice=0)
std::optional< GLuint > GetGLHandle() const
const TextureDescriptor & GetTextureDescriptor() const
Definition texture.cc:55
uint32_t uint32_t * format
size_t length
FlTexture * texture
std::optional< PixelFormatGLES > ToPixelFormatGLES(PixelFormat pixel_format, bool supports_bgra)
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)
constexpr size_t BytesForTextureRegion(PixelFormat format, int64_t width, int64_t height)
The number of bytes required to store a width x height texel region in format. Block-compressed forma...
Definition formats.h:727
std::shared_ptr< ReactorGLES > reactor
impeller::ShaderType type
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
bool Encode(const ReactorGLES &reactor) const override
std::shared_ptr< Texture > texture
bool Encode(const ReactorGLES &reactor) const override
std::string GetLabel() const override
std::shared_ptr< Texture > destination
std::shared_ptr< Texture > source
Range GetRange() const
Definition buffer_view.h:27
const DeviceBuffer * GetBuffer() const
size_t length
Definition range.h:15
size_t offset
Definition range.h:14
constexpr auto GetBottom() const
Definition rect.h:391
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition rect.h:371
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition rect.h:381
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition rect.h:367
constexpr auto GetRight() const
Definition rect.h:389
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition rect.h:375
#define VALIDATION_LOG
Definition validation.h:91