Flutter Impeller
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 
20 namespace impeller {
21 
22 // This prefix is used in the names of inputs generated by ANGLE's framebuffer
23 // fetch emulation.
24 static 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 
69 static 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 
80 static 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 
96 static 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 
113 bool 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 
138 bool 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 
261 GLint 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 
278 const 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  size_t element_count = member.array_elements.value_or(1);
298  const std::string member_key =
299  CreateUniformMemberKey(metadata->name, member.name, element_count > 1);
300  const absl::flat_hash_map<std::string, GLint>::iterator computed_location =
301  uniform_locations_.find(member_key);
302  if (computed_location == uniform_locations_.end()) {
303  // Uniform was not active.
304  locations.push_back(-1);
305  continue;
306  }
307  locations.push_back(computed_location->second);
308  }
309  return locations;
310 }
311 
312 bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
313  const BufferResource& buffer) {
314  const ShaderMetadata* metadata = buffer.GetMetadata();
315  const DeviceBuffer* device_buffer = buffer.resource.GetBuffer();
316  if (!device_buffer) {
317  VALIDATION_LOG << "Device buffer not found.";
318  return false;
319  }
320  const DeviceBufferGLES& device_buffer_gles =
321  DeviceBufferGLES::Cast(*device_buffer);
322 
323  if (use_ubo_) {
324  return BindUniformBufferV3(gl, buffer.resource, metadata,
325  device_buffer_gles);
326  }
327  return BindUniformBufferV2(gl, buffer.resource, metadata, device_buffer_gles);
328 }
329 
330 bool BufferBindingsGLES::BindUniformBufferV3(
331  const ProcTableGLES& gl,
332  const BufferView& buffer,
333  const ShaderMetadata* metadata,
334  const DeviceBufferGLES& device_buffer_gles) {
335  absl::flat_hash_map<std::string, std::pair<GLint, GLuint>>::iterator it =
336  ubo_locations_.find(metadata->name);
337  if (it == ubo_locations_.end()) {
338  return BindUniformBufferV2(gl, buffer, metadata, device_buffer_gles);
339  }
340  const auto& [block_index, binding_point] = it->second;
341  if (!device_buffer_gles.BindAndUploadDataIfNecessary(
343  return false;
344  }
345  auto handle = device_buffer_gles.GetHandle();
346  if (!handle.has_value()) {
347  return false;
348  }
349  gl.BindBufferRange(GL_UNIFORM_BUFFER, binding_point, handle.value(),
350  buffer.GetRange().offset, buffer.GetRange().length);
351  return true;
352 }
353 
354 bool BufferBindingsGLES::BindUniformBufferV2(
355  const ProcTableGLES& gl,
356  const BufferView& buffer,
357  const ShaderMetadata* metadata,
358  const DeviceBufferGLES& device_buffer_gles) {
359  const uint8_t* buffer_ptr =
360  device_buffer_gles.GetBufferData() + buffer.GetRange().offset;
361 
362  if (metadata->members.empty()) {
363  VALIDATION_LOG << "Uniform buffer had no members. This is currently "
364  "unsupported in the OpenGL ES backend. Use a uniform "
365  "buffer block.";
366  return false;
367  }
368 
369  const std::vector<GLint>& locations = ComputeUniformLocations(metadata);
370  for (size_t i = 0u; i < metadata->members.size(); i++) {
371  const ShaderStructMemberMetadata& member = metadata->members[i];
372  GLint location = locations[i];
373  // Void type or inactive uniform.
374  if (location == -1 || member.type == ShaderType::kVoid) {
375  continue;
376  }
377 
378  // The reflector/runtime stage data is confused as to whether 0 means
379  // no elements or whether it is not an array. Specifically:
380  // * The built-in generated header files use std::nullopt to mean not
381  // an array. Setting the array_elements count to 1 generates incorrect
382  // code that tries to create 1 length arrays.
383  // * The runtime stage flatbuffer serializes the std::nullopt as 0,
384  // and thus needs to treat array length of 0 as a scalar element.
385  size_t element_count = member.array_elements.value_or(1);
386  if (element_count == 0) {
387  element_count = 1;
388  }
389  size_t element_stride = member.byte_length / element_count;
390  auto* buffer_data =
391  reinterpret_cast<const GLfloat*>(buffer_ptr + member.offset);
392 
393  // When binding uniform arrays, the elements must be contiguous. Copy
394  // the uniforms to a temp buffer to eliminate any padding needed by the
395  // other backends if the array elements have padding.
396  std::vector<uint8_t> array_element_buffer;
397  if (element_count > 1 && element_stride != member.size) {
398  array_element_buffer.resize(member.size * element_count);
399  for (size_t element_i = 0; element_i < element_count; element_i++) {
400  std::memcpy(array_element_buffer.data() + element_i * member.size,
401  reinterpret_cast<const char*>(buffer_data) +
402  element_i * element_stride,
403  member.size);
404  }
405  buffer_data =
406  reinterpret_cast<const GLfloat*>(array_element_buffer.data());
407  }
408 
409  if (member.type != ShaderType::kFloat) {
410  VALIDATION_LOG << "Could not bind uniform buffer data for key: "
411  << member.name << " : " << static_cast<int>(member.type);
412  return false;
413  }
414 
415  switch (member.size) {
416  case sizeof(Matrix):
417  gl.UniformMatrix4fv(location, // location
418  element_count, // count
419  GL_FALSE, // normalize
420  buffer_data // data
421  );
422  continue;
423  case sizeof(Vector4):
424  gl.Uniform4fv(location, // location
425  element_count, // count
426  buffer_data // data
427  );
428  continue;
429  case sizeof(Vector3):
430  gl.Uniform3fv(location, // location
431  element_count, // count
432  buffer_data // data
433  );
434  continue;
435  case sizeof(Vector2):
436  gl.Uniform2fv(location, // location
437  element_count, // count
438  buffer_data // data
439  );
440  continue;
441  case sizeof(Scalar):
442  gl.Uniform1fv(location, // location
443  element_count, // count
444  buffer_data // data
445  );
446  continue;
447  default:
448  VALIDATION_LOG << "Invalid member size binding: " << member.size;
449  return false;
450  }
451  }
452  return true;
453 }
454 
455 std::optional<size_t> BufferBindingsGLES::BindTextures(
456  const ProcTableGLES& gl,
457  const std::vector<TextureAndSampler>& bound_textures,
458  Range texture_range,
459  ShaderStage stage,
460  size_t unit_start_index) {
461  size_t active_index = unit_start_index;
462  for (auto i = 0u; i < texture_range.length; i++) {
463  const TextureAndSampler& data = bound_textures[texture_range.offset + i];
464  if (data.stage != stage) {
465  continue;
466  }
467  const auto& texture_gles = TextureGLES::Cast(*data.texture.resource);
468  if (data.texture.GetMetadata() == nullptr) {
469  VALIDATION_LOG << "No metadata found for texture binding.";
470  return std::nullopt;
471  }
472 
473  auto location = ComputeTextureLocation(data.texture.GetMetadata());
474  if (location == -1) {
475  // The texture binding was optimized out of the shader. Continue.
476  continue;
477  }
478 
479  //--------------------------------------------------------------------------
480  /// Set the active texture unit.
481  ///
482  if (active_index >= gl.GetCapabilities()->GetMaxTextureUnits(stage)) {
483  VALIDATION_LOG << "Texture units specified exceed the capabilities for "
484  "this shader stage.";
485  return std::nullopt;
486  }
487  gl.ActiveTexture(GL_TEXTURE0 + active_index);
488 
489  //--------------------------------------------------------------------------
490  /// Bind the texture.
491  ///
492  if (!texture_gles.Bind()) {
493  return std::nullopt;
494  }
495 
496  //--------------------------------------------------------------------------
497  /// If there is a sampler for the texture at the same index, configure the
498  /// bound texture using that sampler.
499  ///
500  const auto& sampler_gles = SamplerGLES::Cast(*data.sampler);
501  if (!sampler_gles.ConfigureBoundTexture(texture_gles, gl)) {
502  return std::nullopt;
503  }
504 
505  //--------------------------------------------------------------------------
506  /// Set the texture uniform location.
507  ///
508  gl.Uniform1i(location, active_index);
509 
510  //--------------------------------------------------------------------------
511  /// Bump up the active index at binding.
512  ///
513  active_index++;
514  }
515  return active_index;
516 }
517 
518 } // namespace impeller
GLenum type
static DeviceBufferGLES & Cast(DeviceBuffer &base)
Definition: backend_cast.h:13
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
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:19
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:54
constexpr std::optional< GLenum > ToVertexAttribType(ShaderType type)
Definition: formats_gles.h:140
static std::string NormalizeUniformKey(const std::string &key)
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
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:68
#define VALIDATION_LOG
Definition: validation.h:91