Flutter Engine
 
Loading...
Searching...
No Matches
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
20namespace impeller {
21
22// This prefix is used in the names of inputs generated by ANGLE's framebuffer
23// fetch emulation.
24static 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
69static 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
80static 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
96static 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
113bool 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
138bool 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
261GLint 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
278const 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
312bool 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
330bool 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
354bool 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
455std::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)
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
static int input(yyscan_t yyscanner)
#define FML_DCHECK(condition)
Definition logging.h:122
const char * name
Definition fuchsia.cc:49
size_t length
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
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:55
static std::string NormalizeUniformKey(const std::string &key)
constexpr std::optional< GLenum > ToVertexAttribType(ShaderType type)
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
#define VALIDATION_LOG
Definition validation.h:91