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