| 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/renderer/backend/gles/device_buffer_gles.h"
12 #include "impeller/renderer/backend/gles/formats_gles.h"
13 #include "impeller/renderer/backend/gles/sampler_gles.h"
14 #include "impeller/renderer/backend/gles/texture_gles.h"
15
16 namespace impeller {
17
18 BufferBindingsGLES::BufferBindingsGLES() = default;
19
20 BufferBindingsGLES::~BufferBindingsGLES() = default;
21
22 bool BufferBindingsGLES::RegisterVertexStageInput(
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
83 bool BufferBindingsGLES::ReadUniformsBindings(const ProcTableGLES& gl,
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
128 bool BufferBindingsGLES::BindVertexAttributes(const ProcTableGLES& gl,
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
145 bool BufferBindingsGLES::BindUniformData(const ProcTableGLES& gl,
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
175 bool BufferBindingsGLES::UnbindVertexAttributes(const ProcTableGLES& gl) const {
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;
320 case ShaderType::kBoolean:
321 case ShaderType::kSignedByte:
322 case ShaderType::kUnsignedByte:
323 case ShaderType::kSignedShort:
324 case ShaderType::kUnsignedShort:
325 case ShaderType::kSignedInt:
326 case ShaderType::kUnsignedInt:
327 case ShaderType::kSignedInt64:
328 case ShaderType::kUnsignedInt64:
329 case ShaderType::kAtomicCounter:
330 case ShaderType::kUnknown:
331 case ShaderType::kVoid:
332 case ShaderType::kHalfFloat:
333 case ShaderType::kDouble:
334 case ShaderType::kStruct:
335 case ShaderType::kImage:
336 case ShaderType::kSampledImage:
337 case ShaderType::kSampler:
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