| 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/shader_types.h"
12 #include "impeller/renderer/backend/gles/device_buffer_gles.h"
13 #include "impeller/renderer/backend/gles/formats_gles.h"
14 #include "impeller/renderer/backend/gles/sampler_gles.h"
15 #include "impeller/renderer/backend/gles/texture_gles.h"
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
24 BufferBindingsGLES::BufferBindingsGLES() = default;
25
26 BufferBindingsGLES::~BufferBindingsGLES() = default;
27
28 bool BufferBindingsGLES::RegisterVertexStageInput(
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
89 bool BufferBindingsGLES::ReadUniformsBindings(const ProcTableGLES& gl,
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()) ==
125 kAngleInputAttachmentPrefix) {
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
145 bool BufferBindingsGLES::BindVertexAttributes(const ProcTableGLES& gl,
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
162 bool BufferBindingsGLES::BindUniformData(const ProcTableGLES& gl,
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
192 bool BufferBindingsGLES::UnbindVertexAttributes(const ProcTableGLES& gl) const {
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 // When binding uniform arrays, the elements must be contiguous. Copy
283 // the uniforms to a temp buffer to eliminate any padding needed by the
284 // other backends if the array elements have padding.
285 std::vector<uint8_t> array_element_buffer_;
286 if (element_count > 1 && element_stride != member.size) {
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;
336 case ShaderType::kBoolean:
337 case ShaderType::kSignedByte:
338 case ShaderType::kUnsignedByte:
339 case ShaderType::kSignedShort:
340 case ShaderType::kUnsignedShort:
341 case ShaderType::kSignedInt:
342 case ShaderType::kUnsignedInt:
343 case ShaderType::kSignedInt64:
344 case ShaderType::kUnsignedInt64:
345 case ShaderType::kAtomicCounter:
346 case ShaderType::kUnknown:
347 case ShaderType::kVoid:
348 case ShaderType::kHalfFloat:
349 case ShaderType::kDouble:
350 case ShaderType::kStruct:
351 case ShaderType::kImage:
352 case ShaderType::kSampledImage:
353 case ShaderType::kSampler:
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()
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
impeller::ShaderType::kSignedInt64
@ kSignedInt64
texture_gles.h
impeller::Vector2
Point Vector2
Definition: point.h:331
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
type
GLenum type
Definition: blit_command_gles.cc:127
impeller::ShaderType::kSampledImage
@ kSampledImage
impeller::Allocator
An object that allocates device memory.
Definition: allocator.h:23
impeller::ProcTableGLES::GetCapabilities
const std::shared_ptr< const CapabilitiesGLES > & GetCapabilities() const
Definition: proc_table_gles.cc:203
impeller::ProcTableGLES
Definition: proc_table_gles.h:226
impeller::ShaderStage::kFragment
@ kFragment
impeller::ToVertexAttribType
constexpr std::optional< GLenum > ToVertexAttribType(ShaderType type)
Definition: formats_gles.h:140
impeller::ShaderType::kUnsignedInt
@ kUnsignedInt
formats_gles.h
buffer_bindings_gles.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
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: allocation.cc:12
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