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