| 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
5 #include "impeller/renderer/backend/gles/buffer_bindings_gles.h"
6
7 #include <cstring>
8 #include <vector>
9
10 #include "impeller/base/validation.h"
11 #include "impeller/core/buffer_view.h"
12 #include "impeller/core/device_buffer.h"
13 #include "impeller/core/shader_types.h"
14 #include "impeller/renderer/backend/gles/device_buffer_gles.h"
15 #include "impeller/renderer/backend/gles/formats_gles.h"
16 #include "impeller/renderer/backend/gles/sampler_gles.h"
17 #include "impeller/renderer/backend/gles/texture_gles.h"
18 #include "impeller/renderer/command.h"
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
27 BufferBindingsGLES::BufferBindingsGLES() = default;
28
29 BufferBindingsGLES::~BufferBindingsGLES() = default;
30
31 bool BufferBindingsGLES::RegisterVertexStageInput(
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
101 bool BufferBindingsGLES::ReadUniformsBindings(const ProcTableGLES& gl,
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()) ==
171 kAngleInputAttachmentPrefix) {
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
194 bool BufferBindingsGLES::BindVertexAttributes(const ProcTableGLES& gl,
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
222 bool BufferBindingsGLES::BindUniformData(
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
247 bool BufferBindingsGLES::UnbindVertexAttributes(const ProcTableGLES& gl) {
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(
342 DeviceBufferGLES::BindingType::kUniformBuffer)) {
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
type
GLenum type
Definition: blit_command_gles.cc:153
buffer_bindings_gles.h
buffer_view.h
impeller::BackendCast< DeviceBufferGLES, DeviceBuffer >::Cast
static DeviceBufferGLES & Cast(DeviceBuffer &base)
Definition: backend_cast.h:13
impeller::BufferBindingsGLES::BindVertexAttributes
bool BindVertexAttributes(const ProcTableGLES &gl, size_t binding, size_t vertex_offset)
Definition: buffer_bindings_gles.cc:194
impeller::BufferBindingsGLES::ReadUniformsBindings
bool ReadUniformsBindings(const ProcTableGLES &gl, GLuint program)
Definition: buffer_bindings_gles.cc:101
impeller::BufferBindingsGLES::BufferBindingsGLES
BufferBindingsGLES()
impeller::BufferBindingsGLES::UnbindVertexAttributes
bool UnbindVertexAttributes(const ProcTableGLES &gl)
Definition: buffer_bindings_gles.cc:247
impeller::BufferBindingsGLES::RegisterVertexStageInput
bool RegisterVertexStageInput(const ProcTableGLES &gl, const std::vector< ShaderStageIOSlot > &inputs, const std::vector< ShaderStageBufferLayout > &layouts)
Definition: buffer_bindings_gles.cc:31
impeller::BufferBindingsGLES::BindUniformData
bool BindUniformData(const ProcTableGLES &gl, const std::vector< TextureAndSampler > &bound_textures, const std::vector< BufferResource > &bound_buffers, Range texture_range, Range buffer_range)
Definition: buffer_bindings_gles.cc:222
impeller::BufferBindingsGLES::~BufferBindingsGLES
~BufferBindingsGLES()
impeller::DescriptionGLES::GetGlVersion
Version GetGlVersion() const
Definition: description_gles.cc:164
impeller::DeviceBufferGLES::BindingType::kUniformBuffer
@ kUniformBuffer
impeller::ProcTableGLES
Definition: proc_table_gles.h:283
impeller::ProcTableGLES::GetCapabilities
const std::shared_ptr< const CapabilitiesGLES > & GetCapabilities() const
Definition: proc_table_gles.cc:212
impeller::ProcTableGLES::GetDescription
const DescriptionGLES * GetDescription() const
Definition: proc_table_gles.cc:208
command.h
device_buffer.h
device_buffer_gles.h
formats_gles.h
impeller
Definition: allocation.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::CreateUniformMemberKey
static std::string CreateUniformMemberKey(const std::string &struct_name, const std::string &member, bool is_array)
Definition: buffer_bindings_gles.cc:80
impeller::kAngleInputAttachmentPrefix
static constexpr std::string_view kAngleInputAttachmentPrefix
Definition: buffer_bindings_gles.cc:24
impeller::BufferResource
Resource< BufferView > BufferResource
Definition: command.h:55
impeller::ToVertexAttribType
constexpr std::optional< GLenum > ToVertexAttribType(ShaderType type)
Definition: formats_gles.h:140
impeller::NormalizeUniformKey
static std::string NormalizeUniformKey(const std::string &key)
Definition: buffer_bindings_gles.cc:69
impeller::ShaderStage
ShaderStage
Definition: shader_types.h:22
impeller::ShaderStage::kFragment
@ kFragment
impeller::ShaderStage::kVertex
@ kVertex
impeller::ShaderType::kFloat
@ kFloat
impeller::ShaderType::kVoid
@ kVoid
sampler_gles.h
shader_types.h
impeller::Range
Definition: range.h:13
impeller::Range::length
size_t length
Definition: range.h:15
impeller::Range::offset
size_t offset
Definition: range.h:14
impeller::ShaderMetadata
Definition: shader_types.h:72
impeller::ShaderMetadata::name
std::string name
Definition: shader_types.h:74
impeller::Version
Definition: version.h:16
impeller::Version::IsAtLeast
constexpr bool IsAtLeast(const Version &other) const
Definition: version.h:31
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:69
texture_gles.h
validation.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91