| 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 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
311 bool 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
329 bool 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 return BindUniformBufferV2(gl, buffer, metadata, device_buffer_gles);
338 }
339 const auto& [block_index, binding_point] = it->second;
340 if (!device_buffer_gles.BindAndUploadDataIfNecessary(
341 DeviceBufferGLES::BindingType::kUniformBuffer)) {
342 return false;
343 }
344 auto handle = device_buffer_gles.GetHandle();
345 if (!handle.has_value()) {
346 return false;
347 }
348 gl.BindBufferRange(GL_UNIFORM_BUFFER, binding_point, handle.value(),
349 buffer.GetRange().offset, buffer.GetRange().length);
350 return true;
351 }
352
353 bool BufferBindingsGLES::BindUniformBufferV2(
354 const ProcTableGLES& gl,
355 const BufferView& buffer,
356 const ShaderMetadata* metadata,
357 const DeviceBufferGLES& device_buffer_gles) {
358 const uint8_t* buffer_ptr =
359 device_buffer_gles.GetBufferData() + buffer.GetRange().offset;
360
361 if (metadata->members.empty()) {
362 VALIDATION_LOG << "Uniform buffer had no members. This is currently "
363 "unsupported in the OpenGL ES backend. Use a uniform "
364 "buffer block.";
365 return false;
366 }
367
368 const std::vector<GLint>& locations = ComputeUniformLocations(metadata);
369 for (size_t i = 0u; i < metadata->members.size(); i++) {
370 const ShaderStructMemberMetadata& member = metadata->members[i];
371 GLint location = locations[i];
372 // Void type or inactive uniform.
373 if (location == -1 || member.type == ShaderType::kVoid) {
374 continue;
375 }
376
377 size_t element_count = member.array_elements.value_or(1);
378 size_t element_stride = member.byte_length / element_count;
379 auto* buffer_data =
380 reinterpret_cast<const GLfloat*>(buffer_ptr + member.offset);
381
382 // When binding uniform arrays, the elements must be contiguous. Copy
383 // the uniforms to a temp buffer to eliminate any padding needed by the
384 // other backends if the array elements have padding.
385 std::vector<uint8_t> array_element_buffer;
386 if (element_count > 1 && element_stride != member.size) {
387 array_element_buffer.resize(member.size * element_count);
388 for (size_t element_i = 0; element_i < element_count; element_i++) {
389 std::memcpy(array_element_buffer.data() + element_i * member.size,
390 reinterpret_cast<const char*>(buffer_data) +
391 element_i * element_stride,
392 member.size);
393 }
394 buffer_data =
395 reinterpret_cast<const GLfloat*>(array_element_buffer.data());
396 }
397
398 if (member.type != ShaderType::kFloat) {
399 VALIDATION_LOG << "Unsupported uniform data type for key: " << member.name
400 << ", has type " << static_cast<int>(member.type)
401 << ". Only float uniforms are supported.";
402 return false;
403 }
404
405 switch (member.size) {
406 case sizeof(Matrix):
407 gl.UniformMatrix4fv(location, // location
408 element_count, // count
409 GL_FALSE, // normalize
410 buffer_data // data
411 );
412 continue;
413 case sizeof(Vector4):
414 gl.Uniform4fv(location, // location
415 element_count, // count
416 buffer_data // data
417 );
418 continue;
419 case sizeof(Vector3):
420 gl.Uniform3fv(location, // location
421 element_count, // count
422 buffer_data // data
423 );
424 continue;
425 case sizeof(Vector2):
426 gl.Uniform2fv(location, // location
427 element_count, // count
428 buffer_data // data
429 );
430 continue;
431 case sizeof(Scalar):
432 gl.Uniform1fv(location, // location
433 element_count, // count
434 buffer_data // data
435 );
436 continue;
437 default:
438 VALIDATION_LOG << "Invalid member size binding: " << member.size;
439 return false;
440 }
441 }
442 return true;
443 }
444
445 std::optional<size_t> BufferBindingsGLES::BindTextures(
446 const ProcTableGLES& gl,
447 const std::vector<TextureAndSampler>& bound_textures,
448 Range texture_range,
449 ShaderStage stage,
450 size_t unit_start_index) {
451 size_t active_index = unit_start_index;
452 for (auto i = 0u; i < texture_range.length; i++) {
453 const TextureAndSampler& data = bound_textures[texture_range.offset + i];
454 if (data.stage != stage) {
455 continue;
456 }
457 const auto& texture_gles = TextureGLES::Cast(*data.texture.resource);
458 if (data.texture.GetMetadata() == nullptr) {
459 VALIDATION_LOG << "No metadata found for texture binding.";
460 return std::nullopt;
461 }
462
463 auto location = ComputeTextureLocation(data.texture.GetMetadata());
464 if (location == -1) {
465 // The texture binding was optimized out of the shader. Continue.
466 continue;
467 }
468
469 //--------------------------------------------------------------------------
470 /// Set the active texture unit.
471 ///
472 if (active_index >= gl.GetCapabilities()->GetMaxTextureUnits(stage)) {
473 VALIDATION_LOG << "Texture units specified exceed the capabilities for "
474 "this shader stage.";
475 return std::nullopt;
476 }
477 gl.ActiveTexture(GL_TEXTURE0 + active_index);
478
479 //--------------------------------------------------------------------------
480 /// Bind the texture.
481 ///
482 if (!texture_gles.Bind()) {
483 return std::nullopt;
484 }
485
486 //--------------------------------------------------------------------------
487 /// If there is a sampler for the texture at the same index, configure the
488 /// bound texture using that sampler.
489 ///
490 const auto& sampler_gles = SamplerGLES::Cast(*data.sampler);
491 if (!sampler_gles.ConfigureBoundTexture(texture_gles, gl)) {
492 return std::nullopt;
493 }
494
495 //--------------------------------------------------------------------------
496 /// Set the texture uniform location.
497 ///
498 gl.Uniform1i(location, active_index);
499
500 //--------------------------------------------------------------------------
501 /// Bump up the active index at binding.
502 ///
503 active_index++;
504 }
505 return active_index;
506 }
507
508 } // 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:296
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
location
uint32_t location
Definition: compiler_unittests.cc:129
device_buffer.h
device_buffer_gles.h
formats_gles.h
impeller
Definition: allocation.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:429
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