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 
16 
17 namespace impeller {
18 
19 // This prefix is used in the names of inputs generated by ANGLE's framebuffer
20 // fetch emulation.
21 static constexpr std::string_view kAngleInputAttachmentPrefix =
22  "ANGLEInputAttachment";
23 
25 
27 
29  const ProcTableGLES& gl,
30  const std::vector<ShaderStageIOSlot>& p_inputs,
31  const std::vector<ShaderStageBufferLayout>& layouts) {
32  std::vector<VertexAttribPointer> vertex_attrib_arrays;
33  for (auto i = 0u; i < p_inputs.size(); i++) {
34  const auto& input = p_inputs[i];
35  const auto& layout = layouts[input.binding];
36  VertexAttribPointer attrib;
37  attrib.index = input.location;
38  // Component counts must be 1, 2, 3 or 4. Do that validation now.
39  if (input.vec_size < 1u || input.vec_size > 4u) {
40  return false;
41  }
42  attrib.size = input.vec_size;
43  auto type = ToVertexAttribType(input.type);
44  if (!type.has_value()) {
45  return false;
46  }
47  attrib.type = type.value();
48  attrib.normalized = GL_FALSE;
49  attrib.offset = input.offset;
50  attrib.stride = layout.stride;
51  vertex_attrib_arrays.emplace_back(attrib);
52  }
53  vertex_attrib_arrays_ = std::move(vertex_attrib_arrays);
54  return true;
55 }
56 
57 static std::string NormalizeUniformKey(const std::string& key) {
58  std::string result;
59  result.reserve(key.length());
60  for (char ch : key) {
61  if (ch != '_') {
62  result.push_back(toupper(ch));
63  }
64  }
65  return result;
66 }
67 
68 static std::string CreateUniformMemberKey(const std::string& struct_name,
69  const std::string& member,
70  bool is_array) {
71  std::string result;
72  result.reserve(struct_name.length() + member.length() + (is_array ? 4 : 1));
73  result += struct_name;
74  if (!member.empty()) {
75  result += '.';
76  result += member;
77  }
78  if (is_array) {
79  result += "[0]";
80  }
81  return NormalizeUniformKey(result);
82 }
83 
84 static std::string CreateUniformMemberKey(
85  const std::string& non_struct_member) {
86  return NormalizeUniformKey(non_struct_member);
87 }
88 
90  GLuint program) {
91  if (!gl.IsProgram(program)) {
92  return false;
93  }
94  GLint max_name_size = 0;
95  gl.GetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_name_size);
96 
97  GLint uniform_count = 0;
98  gl.GetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniform_count);
99 
100  // Query the Program for all active uniform locations, and
101  // record this via normalized key.
102  for (GLint i = 0; i < uniform_count; i++) {
103  std::vector<GLchar> name;
104  name.resize(max_name_size);
105  GLsizei written_count = 0u;
106  GLint uniform_var_size = 0u;
107  GLenum uniform_type = GL_FLOAT;
108  // Note: Active uniforms are defined as uniforms that may have an impact on
109  // the output of the shader. Drivers are allowed to (and often do)
110  // optimize out unused uniforms.
111  gl.GetActiveUniform(program, // program
112  i, // index
113  max_name_size, // buffer_size
114  &written_count, // length
115  &uniform_var_size, // size
116  &uniform_type, // type
117  name.data() // name
118  );
119 
120  // Skip unrecognized variables generated by ANGLE.
121  if (gl.GetCapabilities()->IsANGLE()) {
122  if (written_count >=
123  static_cast<GLsizei>(kAngleInputAttachmentPrefix.length()) &&
124  std::string_view(name.data(), kAngleInputAttachmentPrefix.length()) ==
126  continue;
127  }
128  }
129 
130  auto location = gl.GetUniformLocation(program, name.data());
131  if (location == -1) {
132  VALIDATION_LOG << "Could not query the location of an active uniform.";
133  return false;
134  }
135  if (written_count <= 0) {
136  VALIDATION_LOG << "Uniform name could not be read for active uniform.";
137  return false;
138  }
139  uniform_locations_[NormalizeUniformKey(std::string{
140  name.data(), static_cast<size_t>(written_count)})] = location;
141  }
142  return true;
143 }
144 
146  size_t vertex_offset) const {
147  for (const auto& array : vertex_attrib_arrays_) {
148  gl.EnableVertexAttribArray(array.index);
149  gl.VertexAttribPointer(array.index, // index
150  array.size, // size (must be 1, 2, 3, or 4)
151  array.type, // type
152  array.normalized, // normalized
153  array.stride, // stride
154  reinterpret_cast<const GLvoid*>(static_cast<GLsizei>(
155  vertex_offset + array.offset)) // pointer
156  );
157  }
158 
159  return true;
160 }
161 
163  Allocator& transients_allocator,
164  const Bindings& vertex_bindings,
165  const Bindings& fragment_bindings) {
166  for (const auto& buffer : vertex_bindings.buffers) {
167  if (!BindUniformBuffer(gl, transients_allocator, buffer.view)) {
168  return false;
169  }
170  }
171  for (const auto& buffer : fragment_bindings.buffers) {
172  if (!BindUniformBuffer(gl, transients_allocator, buffer.view)) {
173  return false;
174  }
175  }
176 
177  std::optional<size_t> next_unit_index =
178  BindTextures(gl, vertex_bindings, ShaderStage::kVertex);
179  if (!next_unit_index.has_value()) {
180  return false;
181  }
182 
183  if (!BindTextures(gl, fragment_bindings, ShaderStage::kFragment,
184  *next_unit_index)
185  .has_value()) {
186  return false;
187  }
188 
189  return true;
190 }
191 
193  for (const auto& array : vertex_attrib_arrays_) {
194  gl.DisableVertexAttribArray(array.index);
195  }
196  return true;
197 }
198 
199 GLint BufferBindingsGLES::ComputeTextureLocation(
200  const ShaderMetadata* metadata) {
201  auto location = binding_map_.find(metadata->name);
202  if (location != binding_map_.end()) {
203  return location->second[0];
204  }
205  auto& locations = binding_map_[metadata->name] = {};
206  auto computed_location =
207  uniform_locations_.find(CreateUniformMemberKey(metadata->name));
208  if (computed_location == uniform_locations_.end()) {
209  locations.push_back(-1);
210  } else {
211  locations.push_back(computed_location->second);
212  }
213  return locations[0];
214 }
215 
216 const std::vector<GLint>& BufferBindingsGLES::ComputeUniformLocations(
217  const ShaderMetadata* metadata) {
218  auto location = binding_map_.find(metadata->name);
219  if (location != binding_map_.end()) {
220  return location->second;
221  }
222 
223  // For each metadata member, look up the binding location and record
224  // it in the binding map.
225  auto& locations = binding_map_[metadata->name] = {};
226  for (const auto& member : metadata->members) {
227  if (member.type == ShaderType::kVoid) {
228  // Void types are used for padding. We are obviously not going to find
229  // mappings for these. Keep going.
230  locations.push_back(-1);
231  continue;
232  }
233 
234  size_t element_count = member.array_elements.value_or(1);
235  const auto member_key =
236  CreateUniformMemberKey(metadata->name, member.name, element_count > 1);
237  const auto computed_location = uniform_locations_.find(member_key);
238  if (computed_location == uniform_locations_.end()) {
239  // Uniform was not active.
240  locations.push_back(-1);
241  continue;
242  }
243  locations.push_back(computed_location->second);
244  }
245  return locations;
246 }
247 
248 bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
249  Allocator& transients_allocator,
250  const BufferResource& buffer) {
251  const auto* metadata = buffer.GetMetadata();
252  auto device_buffer = buffer.resource.buffer;
253  if (!device_buffer) {
254  VALIDATION_LOG << "Device buffer not found.";
255  return false;
256  }
257  const auto& device_buffer_gles = DeviceBufferGLES::Cast(*device_buffer);
258  const uint8_t* buffer_ptr =
259  device_buffer_gles.GetBufferData() + buffer.resource.range.offset;
260 
261  if (metadata->members.empty()) {
262  VALIDATION_LOG << "Uniform buffer had no members. This is currently "
263  "unsupported in the OpenGL ES backend. Use a uniform "
264  "buffer block.";
265  return false;
266  }
267 
268  const auto& locations = ComputeUniformLocations(metadata);
269  for (auto i = 0u; i < metadata->members.size(); i++) {
270  const auto& member = metadata->members[i];
271  auto location = locations[i];
272  // Void type or inactive uniform.
273  if (location == -1 || member.type == ShaderType::kVoid) {
274  continue;
275  }
276 
277  size_t element_count = member.array_elements.value_or(1);
278  size_t element_stride = member.byte_length / element_count;
279  auto* buffer_data =
280  reinterpret_cast<const GLfloat*>(buffer_ptr + member.offset);
281 
282  std::vector<uint8_t> array_element_buffer;
283  if (element_count > 1) {
284  // When binding uniform arrays, the elements must be contiguous. Copy
285  // the uniforms to a temp buffer to eliminate any padding needed by the
286  // other backends.
287  array_element_buffer.resize(member.size * element_count);
288  for (size_t element_i = 0; element_i < element_count; element_i++) {
289  std::memcpy(array_element_buffer.data() + element_i * member.size,
290  reinterpret_cast<const char*>(buffer_data) +
291  element_i * element_stride,
292  member.size);
293  }
294  buffer_data =
295  reinterpret_cast<const GLfloat*>(array_element_buffer.data());
296  }
297 
298  switch (member.type) {
299  case ShaderType::kFloat:
300  switch (member.size) {
301  case sizeof(Matrix):
302  gl.UniformMatrix4fv(location, // location
303  element_count, // count
304  GL_FALSE, // normalize
305  buffer_data // data
306  );
307  continue;
308  case sizeof(Vector4):
309  gl.Uniform4fv(location, // location
310  element_count, // count
311  buffer_data // data
312  );
313  continue;
314  case sizeof(Vector3):
315  gl.Uniform3fv(location, // location
316  element_count, // count
317  buffer_data // data
318  );
319  continue;
320  case sizeof(Vector2):
321  gl.Uniform2fv(location, // location
322  element_count, // count
323  buffer_data // data
324  );
325  continue;
326  case sizeof(Scalar):
327  gl.Uniform1fv(location, // location
328  element_count, // count
329  buffer_data // data
330  );
331  continue;
332  }
333  VALIDATION_LOG << "Size " << member.size
334  << " could not be mapped ShaderType::kFloat for key: "
335  << member.name;
347  case ShaderType::kVoid:
349  case ShaderType::kDouble:
350  case ShaderType::kStruct:
351  case ShaderType::kImage:
354  VALIDATION_LOG << "Could not bind uniform buffer data for key: "
355  << member.name << " : " << static_cast<int>(member.type);
356  return false;
357  }
358  }
359  return true;
360 }
361 
362 std::optional<size_t> BufferBindingsGLES::BindTextures(
363  const ProcTableGLES& gl,
364  const Bindings& bindings,
365  ShaderStage stage,
366  size_t unit_start_index) {
367  size_t active_index = unit_start_index;
368  for (const auto& data : bindings.sampled_images) {
369  const auto& texture_gles = TextureGLES::Cast(*data.texture.resource);
370  if (data.texture.GetMetadata() == nullptr) {
371  VALIDATION_LOG << "No metadata found for texture binding.";
372  return std::nullopt;
373  }
374 
375  auto location = ComputeTextureLocation(data.texture.GetMetadata());
376  if (location == -1) {
377  return std::nullopt;
378  }
379 
380  //--------------------------------------------------------------------------
381  /// Set the active texture unit.
382  ///
383  if (active_index >= gl.GetCapabilities()->GetMaxTextureUnits(stage)) {
384  VALIDATION_LOG << "Texture units specified exceed the capabilities for "
385  "this shader stage.";
386  return std::nullopt;
387  }
388  gl.ActiveTexture(GL_TEXTURE0 + active_index);
389 
390  //--------------------------------------------------------------------------
391  /// Bind the texture.
392  ///
393  if (!texture_gles.Bind()) {
394  return std::nullopt;
395  }
396 
397  //--------------------------------------------------------------------------
398  /// If there is a sampler for the texture at the same index, configure the
399  /// bound texture using that sampler.
400  ///
401  const auto& sampler_gles = SamplerGLES::Cast(*data.sampler);
402  if (!sampler_gles.ConfigureBoundTexture(texture_gles, gl)) {
403  return std::nullopt;
404  }
405 
406  //--------------------------------------------------------------------------
407  /// Set the texture uniform location.
408  ///
409  gl.Uniform1i(location, active_index);
410 
411  //--------------------------------------------------------------------------
412  /// Bump up the active index at binding.
413  ///
414  active_index++;
415  }
416  return active_index;
417 }
418 
419 } // namespace impeller
impeller::BufferBindingsGLES::BindUniformData
bool BindUniformData(const ProcTableGLES &gl, Allocator &transients_allocator, const Bindings &vertex_bindings, const Bindings &fragment_bindings)
Definition: buffer_bindings_gles.cc:162
impeller::BufferBindingsGLES::ReadUniformsBindings
bool ReadUniformsBindings(const ProcTableGLES &gl, GLuint program)
Definition: buffer_bindings_gles.cc:89
impeller::ShaderType::kAtomicCounter
@ kAtomicCounter
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::ShaderType::kDouble
@ kDouble
impeller::BufferBindingsGLES::RegisterVertexStageInput
bool RegisterVertexStageInput(const ProcTableGLES &gl, const std::vector< ShaderStageIOSlot > &inputs, const std::vector< ShaderStageBufferLayout > &layouts)
Definition: buffer_bindings_gles.cc:28
impeller::ShaderMetadata
Definition: shader_types.h:72
impeller::BufferBindingsGLES::BufferBindingsGLES
BufferBindingsGLES()
impeller::ShaderType::kSignedInt64
@ kSignedInt64
texture_gles.h
impeller::Vector2
Point Vector2
Definition: point.h:320
impeller::ShaderType::kImage
@ kImage
impeller::ShaderStage
ShaderStage
Definition: shader_types.h:22
impeller::ShaderType::kVoid
@ kVoid
impeller::ShaderType::kSignedByte
@ kSignedByte
impeller::ShaderMetadata::name
std::string name
Definition: shader_types.h:74
validation.h
sampler_gles.h
impeller::ShaderType::kUnsignedShort
@ kUnsignedShort
impeller::NormalizeUniformKey
static std::string NormalizeUniformKey(const std::string &key)
Definition: buffer_bindings_gles.cc:57
impeller::ShaderType::kUnsignedInt64
@ kUnsignedInt64
impeller::ShaderType::kUnsignedByte
@ kUnsignedByte
impeller::ShaderType::kFloat
@ kFloat
device_buffer_gles.h
impeller::kAngleInputAttachmentPrefix
static constexpr std::string_view kAngleInputAttachmentPrefix
Definition: buffer_bindings_gles.cc:21
impeller::BufferBindingsGLES::UnbindVertexAttributes
bool UnbindVertexAttributes(const ProcTableGLES &gl) const
Definition: buffer_bindings_gles.cc:192
impeller::ShaderType::kBoolean
@ kBoolean
impeller::ShaderType::kSampler
@ kSampler
impeller::ShaderType::kSampledImage
@ kSampledImage
impeller::Allocator
An object that allocates device memory.
Definition: allocator.h:22
impeller::ProcTableGLES::GetCapabilities
const std::shared_ptr< const CapabilitiesGLES > & GetCapabilities() const
Definition: proc_table_gles.cc:203
impeller::ProcTableGLES
Definition: proc_table_gles.h:229
impeller::ShaderStage::kFragment
@ kFragment
impeller::ToVertexAttribType
constexpr std::optional< GLenum > ToVertexAttribType(ShaderType type)
Definition: formats_gles.h:139
impeller::ShaderType::kUnsignedInt
@ kUnsignedInt
formats_gles.h
buffer_bindings_gles.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::ShaderType::kStruct
@ kStruct
impeller::Bindings::buffers
std::vector< BufferAndUniformSlot > buffers
Definition: command.h:75
impeller::ShaderType::kSignedShort
@ kSignedShort
impeller::ShaderType::kHalfFloat
@ kHalfFloat
impeller::ShaderType::kSignedInt
@ kSignedInt
impeller::ShaderStage::kVertex
@ kVertex
impeller::BufferBindingsGLES::BindVertexAttributes
bool BindVertexAttributes(const ProcTableGLES &gl, size_t vertex_offset) const
Definition: buffer_bindings_gles.cc:145
impeller::BackendCast< DeviceBufferGLES, DeviceBuffer >::Cast
static DeviceBufferGLES & Cast(DeviceBuffer &base)
Definition: backend_cast.h:13
impeller::ShaderType::kUnknown
@ kUnknown
impeller::BufferBindingsGLES::~BufferBindingsGLES
~BufferBindingsGLES()
shader_types.h
impeller
Definition: aiks_blur_unittests.cc:20
impeller::CreateUniformMemberKey
static std::string CreateUniformMemberKey(const std::string &struct_name, const std::string &member, bool is_array)
Definition: buffer_bindings_gles.cc:68
impeller::Bindings
Definition: command.h:73
impeller::BufferResource
Resource< BufferView > BufferResource
Definition: command.h:57