Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
buffer_bindings_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 <cstdint>
8#include <cstring>
9#include <vector>
10
20
21namespace impeller {
22
23// This prefix is used in the names of inputs generated by ANGLE's framebuffer
24// fetch emulation.
25static constexpr std::string_view kAngleInputAttachmentPrefix =
26 "ANGLEInputAttachment";
27
29
31
33 const ProcTableGLES& gl,
34 const std::vector<ShaderStageIOSlot>& p_inputs,
35 const std::vector<ShaderStageBufferLayout>& layouts) {
36 std::vector<std::vector<VertexAttribPointer>> vertex_attrib_arrays(
37 layouts.size());
38 // Every layout corresponds to a vertex binding.
39 // As we record, separate the attributes into buckets for each layout in
40 // ascending order. We do this because later on, we'll need to associate each
41 // of the attributes with bound buffers corresponding to the binding.
42 for (auto layout_i = 0u; layout_i < layouts.size(); layout_i++) {
43 const auto& layout = layouts[layout_i];
44 for (const auto& input : p_inputs) {
45 if (input.binding != layout_i) {
46 continue;
47 }
48 VertexAttribPointer attrib;
49 attrib.index = input.location;
50 // Component counts must be 1, 2, 3 or 4. Do that validation now.
51 if (input.vec_size < 1u || input.vec_size > 4u) {
52 return false;
53 }
54 attrib.size = input.vec_size;
55 auto type = ToVertexAttribType(input.type);
56 if (!type.has_value()) {
57 return false;
58 }
59 attrib.type = type.value();
60 attrib.normalized = GL_FALSE;
61 attrib.offset = input.offset;
62 attrib.stride = layout.stride;
63 attrib.vertex_attrib_divisor =
64 layout.input_rate == VertexInputRate::kInstance ? 1u : 0u;
65 vertex_attrib_arrays[layout_i].push_back(attrib);
66 }
67 }
68 vertex_attrib_arrays_ = std::move(vertex_attrib_arrays);
69 return true;
70}
71
72static std::string NormalizeUniformKey(const std::string& key) {
73 std::string result;
74 result.reserve(key.length());
75 for (char ch : key) {
76 if (ch != '_') {
77 result.push_back(toupper(ch));
78 }
79 }
80 return result;
81}
82
83static std::string CreateUniformMemberKey(const std::string& struct_name,
84 const std::string& member,
85 bool is_array) {
86 std::string result;
87 result.reserve(struct_name.length() + member.length() + (is_array ? 4 : 1));
88 result += struct_name;
89 if (!member.empty()) {
90 result += '.';
91 result += member;
92 }
93 if (is_array) {
94 result += "[0]";
95 }
96 return NormalizeUniformKey(result);
97}
98
99static std::string CreateUniformMemberKey(
100 const std::string& non_struct_member) {
101 return NormalizeUniformKey(non_struct_member);
102}
103
105 GLuint program) {
106 if (!gl.IsProgram(program)) {
107 return false;
108 }
109 program_handle_ = program;
110 if (gl.GetDescription()->GetGlVersion().IsAtLeast(Version{3, 0, 0})) {
111 return ReadUniformsBindingsV3(gl, program);
112 }
113 return ReadUniformsBindingsV2(gl, program);
114}
115
116bool BufferBindingsGLES::ReadUniformsBindingsV3(const ProcTableGLES& gl,
117 GLuint program) {
118 program_handle_ = program;
119 GLint uniform_blocks = 0;
120 gl.GetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &uniform_blocks);
121 for (GLint i = 0; i < uniform_blocks; i++) {
122 GLint name_length = 0;
123 gl.GetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_NAME_LENGTH,
124 &name_length);
125
126 std::vector<GLchar> name;
127 name.resize(name_length);
128 GLint length = 0;
129 gl.GetActiveUniformBlockName(program, i, name_length, &length, name.data());
130
131 GLuint block_index = gl.GetUniformBlockIndex(program, name.data());
132 gl.UniformBlockBinding(program_handle_, block_index, i);
133
134 ubo_locations_[std::string{name.data(), static_cast<size_t>(length)}] =
135 std::make_pair(block_index, i);
136 }
137 use_ubo_ = true;
138 return ReadUniformsBindingsV2(gl, program);
139}
140
141bool BufferBindingsGLES::ReadUniformsBindingsV2(const ProcTableGLES& gl,
142 GLuint program) {
143 GLint max_name_size = 0;
144 gl.GetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_name_size);
145
146 GLint uniform_count = 0;
147 gl.GetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniform_count);
148
149 // Query the Program for all active uniform locations, and
150 // record this via normalized key.
151 for (GLint i = 0; i < uniform_count; i++) {
152 std::vector<GLchar> name;
153 name.resize(max_name_size);
154 GLsizei written_count = 0u;
155 GLint uniform_var_size = 0u;
156 GLenum uniform_type = GL_FLOAT;
157 // Note: Active uniforms are defined as uniforms that may have an impact on
158 // the output of the shader. Drivers are allowed to (and often do)
159 // optimize out unused uniforms.
160 gl.GetActiveUniform(program, // program
161 i, // index
162 max_name_size, // buffer_size
163 &written_count, // length
164 &uniform_var_size, // size
165 &uniform_type, // type
166 name.data() // name
167 );
168
169 // Skip unrecognized variables generated by ANGLE.
170 if (gl.GetCapabilities()->IsANGLE()) {
171 if (written_count >=
172 static_cast<GLsizei>(kAngleInputAttachmentPrefix.length()) &&
173 std::string_view(name.data(), kAngleInputAttachmentPrefix.length()) ==
175 continue;
176 }
177 }
178
179 auto location = gl.GetUniformLocation(program, name.data());
180 if (location == -1) {
181 if (use_ubo_) {
182 continue;
183 }
184 VALIDATION_LOG << "Could not query the location of an active uniform.";
185 return false;
186 }
187 if (written_count <= 0) {
188 VALIDATION_LOG << "Uniform name could not be read for active uniform.";
189 return false;
190 }
191 uniform_locations_[NormalizeUniformKey(std::string{
192 name.data(), static_cast<size_t>(written_count)})] = location;
193 }
194 return true;
195}
196
198 size_t binding,
199 size_t vertex_offset,
200 size_t instance) {
201 if (binding >= vertex_attrib_arrays_.size()) {
202 return false;
203 }
204
205 // For an emulated instanced draw (instance > 0), a binding whose attributes
206 // are all vertex-rate (divisor 0) does not change across instances, so the
207 // state bound for instance 0 still applies. Skip the redundant re-binding.
208 if (instance > 0u) {
209 bool has_instance_rate = false;
210 for (const auto& array : vertex_attrib_arrays_[binding]) {
211 if (array.vertex_attrib_divisor != 0u) {
212 has_instance_rate = true;
213 break;
214 }
215 }
216 if (!has_instance_rate) {
217 return true;
218 }
219 }
220
221 if (!gl.GetCapabilities()->IsES()) {
222 FML_DCHECK(vertex_array_object_ == 0);
223 gl.GenVertexArrays(1, &vertex_array_object_);
224 gl.BindVertexArray(vertex_array_object_);
225 }
226
227 for (const auto& array : vertex_attrib_arrays_[binding]) {
228 gl.EnableVertexAttribArray(array.index);
229 // For an emulated instanced draw, an instance-rate attribute is
230 // re-pointed at instance `instance`, since there is no hardware divisor
231 // to advance it. A non-instanced or hardware-instanced draw passes
232 // instance 0 and lets the divisor (if any) do the stepping.
233 size_t attribute_offset = vertex_offset + array.offset;
234 if (array.vertex_attrib_divisor != 0u) {
235 attribute_offset += instance * static_cast<size_t>(array.stride);
236 }
237 gl.VertexAttribPointer(array.index, // index
238 array.size, // size (must be 1, 2, 3, or 4)
239 array.type, // type
240 array.normalized, // normalized
241 array.stride, // stride
242 reinterpret_cast<const GLvoid*>(
243 static_cast<uintptr_t>(attribute_offset)) // ptr
244 );
245 // Set the instancing divisor when the driver supports it. It is core
246 // on ES 3.0+ and comes from GL_EXT_instanced_arrays on ES 2.0. When
247 // unavailable, only per-vertex (divisor 0) bindings are possible,
248 // which is the default. Setting it for every attribute (including
249 // divisor 0) also clears any stale divisor left by a prior pipeline,
250 // which matters on ES, where there is no vertex array object.
251 if (gl.VertexAttribDivisor.IsAvailable()) {
252 gl.VertexAttribDivisor(array.index, array.vertex_attrib_divisor);
253 } else if (gl.VertexAttribDivisorEXT.IsAvailable()) {
254 gl.VertexAttribDivisorEXT(array.index, array.vertex_attrib_divisor);
255 }
256 }
257
258 return true;
259}
260
262 const ProcTableGLES& gl,
263 const std::vector<TextureAndSampler>& bound_textures,
264 const std::vector<BufferResource>& bound_buffers,
265 Range texture_range,
266 Range buffer_range) {
267 for (auto i = 0u; i < buffer_range.length; i++) {
268 if (!BindUniformBuffer(gl, bound_buffers[buffer_range.offset + i])) {
269 return false;
270 }
271 }
272 std::optional<size_t> next_unit_index =
273 BindTextures(gl, bound_textures, texture_range, ShaderStage::kVertex);
274 if (!next_unit_index.has_value()) {
275 return false;
276 }
277 if (!BindTextures(gl, bound_textures, texture_range, ShaderStage::kFragment,
278 *next_unit_index)
279 .has_value()) {
280 return false;
281 }
282
283 return true;
284}
285
287 for (const auto& array : vertex_attrib_arrays_) {
288 for (const auto& attribute : array) {
289 gl.DisableVertexAttribArray(attribute.index);
290 }
291 }
292 if (!gl.GetCapabilities()->IsES()) {
293 gl.DeleteVertexArrays(1, &vertex_array_object_);
294 vertex_array_object_ = 0;
295 }
296
297 return true;
298}
299
300GLint BufferBindingsGLES::ComputeTextureLocation(
301 const ShaderMetadata* metadata) {
302 auto location = binding_map_.find(metadata->name);
303 if (location != binding_map_.end()) {
304 return location->second[0];
305 }
306 auto& locations = binding_map_[metadata->name] = {};
307 auto computed_location =
308 uniform_locations_.find(CreateUniformMemberKey(metadata->name));
309 if (computed_location == uniform_locations_.end()) {
310 locations.push_back(-1);
311 } else {
312 locations.push_back(computed_location->second);
313 }
314 return locations[0];
315}
316
317const std::vector<GLint>& BufferBindingsGLES::ComputeUniformLocations(
318 const ShaderMetadata* metadata) {
319 BindingMap::iterator location = binding_map_.find(metadata->name);
320 if (location != binding_map_.end()) {
321 return location->second;
322 }
323
324 // For each metadata member, look up the binding location and record
325 // it in the binding map.
326 std::vector<GLint>& locations = binding_map_[metadata->name] = {};
327 locations.reserve(metadata->members.size());
328 for (const ShaderStructMemberMetadata& member : metadata->members) {
329 if (member.type == ShaderType::kVoid) {
330 // Void types are used for padding. We are obviously not going to find
331 // mappings for these. Keep going.
332 locations.push_back(-1);
333 continue;
334 }
335
336 const std::string member_key = CreateUniformMemberKey(
337 metadata->name, member.name, member.array_elements.has_value());
338 const absl::flat_hash_map<std::string, GLint>::iterator computed_location =
339 uniform_locations_.find(member_key);
340 if (computed_location == uniform_locations_.end()) {
341 // Uniform was not active.
342 locations.push_back(-1);
343 continue;
344 }
345 locations.push_back(computed_location->second);
346 }
347 return locations;
348}
349
350bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
351 const BufferResource& buffer) {
352 const ShaderMetadata* metadata = buffer.GetMetadata();
353 const DeviceBuffer* device_buffer = buffer.resource.GetBuffer();
354 if (!device_buffer) {
355 VALIDATION_LOG << "Device buffer not found.";
356 return false;
357 }
358 const DeviceBufferGLES& device_buffer_gles =
359 DeviceBufferGLES::Cast(*device_buffer);
360
361 if (use_ubo_) {
362 return BindUniformBufferV3(gl, buffer.resource, metadata,
363 device_buffer_gles);
364 }
365 return BindUniformBufferV2(gl, buffer.resource, metadata, device_buffer_gles);
366}
367
368bool BufferBindingsGLES::BindUniformBufferV3(
369 const ProcTableGLES& gl,
370 const BufferView& buffer,
371 const ShaderMetadata* metadata,
372 const DeviceBufferGLES& device_buffer_gles) {
373 absl::flat_hash_map<std::string, std::pair<GLint, GLuint>>::iterator it =
374 ubo_locations_.find(metadata->name);
375 if (it == ubo_locations_.end()) {
376 // This should only happen if we have GLESv3 but are using v2 shaders,
377 // as GLESv3 shaders compiled by impeller always have
378 // **named** uniform buffer blocks
379 return BindUniformBufferV2(gl, buffer, metadata, device_buffer_gles);
380 }
381 const auto& [block_index, binding_point] = it->second;
382 if (!device_buffer_gles.BindAndUploadDataIfNecessary(
384 return false;
385 }
386 auto handle = device_buffer_gles.GetHandle();
387 if (!handle.has_value()) {
388 return false;
389 }
390 gl.BindBufferRange(GL_UNIFORM_BUFFER, binding_point, handle.value(),
391 buffer.GetRange().offset, buffer.GetRange().length);
392 return true;
393}
394
395bool BufferBindingsGLES::BindUniformBufferV2(
396 const ProcTableGLES& gl,
397 const BufferView& buffer,
398 const ShaderMetadata* metadata,
399 const DeviceBufferGLES& device_buffer_gles) {
400 const uint8_t* buffer_ptr =
401 device_buffer_gles.GetBufferData() + buffer.GetRange().offset;
402
403 if (metadata->members.empty()) {
404 VALIDATION_LOG << "Uniform buffer had no members. This is currently "
405 "unsupported in the OpenGL ES backend. Use a uniform "
406 "buffer block.";
407 return false;
408 }
409
410 const std::vector<GLint>& locations = ComputeUniformLocations(metadata);
411 for (size_t i = 0u; i < metadata->members.size(); i++) {
412 const ShaderStructMemberMetadata& member = metadata->members[i];
413 GLint location = locations[i];
414 // Void type or inactive uniform.
415 if (location == -1 || member.type == ShaderType::kVoid) {
416 continue;
417 }
418
419 size_t element_count = member.array_elements.value_or(1);
420 size_t element_stride = member.byte_length / element_count;
421 auto* buffer_data =
422 reinterpret_cast<const GLfloat*>(buffer_ptr + member.offset);
423
424 // When binding uniform arrays, the elements must be contiguous. Copy
425 // the uniforms to a temp buffer to eliminate any padding needed by the
426 // other backends if the array elements have padding.
427 std::vector<uint8_t> array_element_buffer;
428 if (element_count > 1 && element_stride != member.size) {
429 array_element_buffer.resize(member.size * element_count);
430 for (size_t element_i = 0; element_i < element_count; element_i++) {
431 std::memcpy(array_element_buffer.data() + element_i * member.size,
432 reinterpret_cast<const char*>(buffer_data) +
433 element_i * element_stride,
434 member.size);
435 }
436 buffer_data =
437 reinterpret_cast<const GLfloat*>(array_element_buffer.data());
438 }
439
440 if (member.type != ShaderType::kFloat) {
441 VALIDATION_LOG << "Unsupported uniform data type for key: " << member.name
442 << ", has type " << static_cast<int>(member.type)
443 << ". Only float uniforms are supported.";
444 return false;
445 }
446
447 if (!member.float_type.has_value()) {
448 VALIDATION_LOG << "Float uniform should have a float type.";
449 return false;
450 }
451
452 switch (member.float_type.value()) {
454 gl.Uniform1fv(location, element_count, buffer_data);
455 break;
457 gl.Uniform2fv(location, element_count, buffer_data);
458 break;
460 gl.Uniform3fv(location, element_count, buffer_data);
461 break;
463 gl.Uniform4fv(location, element_count, buffer_data);
464 break;
466 gl.UniformMatrix2fv(location, element_count, GL_FALSE, buffer_data);
467 break;
469 gl.UniformMatrix3fv(location, element_count, GL_FALSE, buffer_data);
470 break;
472 gl.UniformMatrix4fv(location, element_count, GL_FALSE, buffer_data);
473 break;
474 }
475 }
476 return true;
477}
478
479std::optional<size_t> BufferBindingsGLES::BindTextures(
480 const ProcTableGLES& gl,
481 const std::vector<TextureAndSampler>& bound_textures,
482 Range texture_range,
483 ShaderStage stage,
484 size_t unit_start_index) {
485 size_t active_index = unit_start_index;
486 for (auto i = 0u; i < texture_range.length; i++) {
487 const TextureAndSampler& data = bound_textures[texture_range.offset + i];
488 if (data.stage != stage) {
489 continue;
490 }
491 const auto& texture_gles = TextureGLES::Cast(*data.texture.resource);
492 if (data.texture.GetMetadata() == nullptr) {
493 VALIDATION_LOG << "No metadata found for texture binding.";
494 return std::nullopt;
495 }
496
497 auto location = ComputeTextureLocation(data.texture.GetMetadata());
498 if (location == -1) {
499 // The texture binding was optimized out of the shader. Continue.
500 continue;
501 }
502
503 //--------------------------------------------------------------------------
504 /// Set the active texture unit.
505 ///
506 if (active_index >= gl.GetCapabilities()->GetMaxTextureUnits(stage)) {
507 VALIDATION_LOG << "Texture units specified exceed the capabilities for "
508 "this shader stage.";
509 return std::nullopt;
510 }
511 gl.ActiveTexture(GL_TEXTURE0 + active_index);
512
513 //--------------------------------------------------------------------------
514 /// Bind the texture.
515 ///
516 // The `texture_gles` reference is bound `const` because it is reached
517 // via a const view into the bound texture list, but `Bind()` mutates
518 // GLES-specific lazy-init bookkeeping (`slice_mip_initialized_`,
519 // `fence_`). The mutation is implementation detail of the GLES backend
520 // and is invisible to the higher abstraction; the `const_cast` is
521 // confined to this one call site.
522 if (!const_cast<TextureGLES&>(texture_gles).Bind()) {
523 return std::nullopt;
524 }
525
526 //--------------------------------------------------------------------------
527 /// If there is a sampler for the texture at the same index, configure the
528 /// bound texture using that sampler.
529 ///
530 const auto& sampler_gles = SamplerGLES::Cast(*data.sampler);
531 if (!sampler_gles.ConfigureBoundTexture(texture_gles, gl)) {
532 return std::nullopt;
533 }
534
535 //--------------------------------------------------------------------------
536 /// Set the texture uniform location.
537 ///
538 gl.Uniform1i(location, active_index);
539
540 //--------------------------------------------------------------------------
541 /// Bump up the active index at binding.
542 ///
543 active_index++;
544 }
545 return active_index;
546}
547
548} // namespace impeller
static DeviceBufferGLES & Cast(DeviceBuffer &base)
bool BindVertexAttributes(const ProcTableGLES &gl, size_t binding, size_t vertex_offset, size_t instance=0)
bool ReadUniformsBindings(const ProcTableGLES &gl, GLuint program)
bool UnbindVertexAttributes(const ProcTableGLES &gl)
bool RegisterVertexStageInput(const ProcTableGLES &gl, const std::vector< ShaderStageIOSlot > &inputs, const std::vector< ShaderStageBufferLayout > &layouts)
bool BindUniformData(const ProcTableGLES &gl, const std::vector< TextureAndSampler > &bound_textures, const std::vector< BufferResource > &bound_buffers, Range texture_range, Range buffer_range)
const std::shared_ptr< const CapabilitiesGLES > & GetCapabilities() const
const DescriptionGLES * GetDescription() const
static int input(yyscan_t yyscanner)
uint32_t location
VkInstance instance
Definition main.cc:64
#define FML_DCHECK(condition)
Definition logging.h:122
const char * name
Definition fuchsia.cc:50
size_t length
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
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
@ kInstance
The binding is read once per instance.
static std::string CreateUniformMemberKey(const std::string &struct_name, const std::string &member, bool is_array)
static constexpr std::string_view kAngleInputAttachmentPrefix
Resource< BufferView > BufferResource
Definition command.h:55
static std::string NormalizeUniformKey(const std::string &key)
constexpr std::optional< GLenum > ToVertexAttribType(ShaderType type)
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
impeller::ShaderType type
size_t length
Definition range.h:15
size_t offset
Definition range.h:14
constexpr bool IsAtLeast(const Version &other) const
Definition version.h:31
#define VALIDATION_LOG
Definition validation.h:91