| 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 <algorithm>
8 #include <cstring>
9 #include <vector>
10
11 #include "impeller/base/config.h"
12 #include "impeller/base/validation.h"
13 #include "impeller/renderer/backend/gles/device_buffer_gles.h"
14 #include "impeller/renderer/backend/gles/formats_gles.h"
15 #include "impeller/renderer/backend/gles/sampler_gles.h"
16 #include "impeller/renderer/backend/gles/texture_gles.h"
17
18 namespace impeller {
19
20 BufferBindingsGLES::BufferBindingsGLES() = default;
21
22 BufferBindingsGLES::~BufferBindingsGLES() = default;
23
24 bool BufferBindingsGLES::RegisterVertexStageInput(
25 const ProcTableGLES& gl,
26 const std::vector<ShaderStageIOSlot>& p_inputs,
27 const std::vector<ShaderStageBufferLayout>& layouts) {
28 std::vector<VertexAttribPointer> vertex_attrib_arrays;
29 for (auto i = 0u; i < p_inputs.size(); i++) {
30 const auto& input = p_inputs[i];
31 const auto& layout = layouts[input.binding];
32 VertexAttribPointer attrib;
33 attrib.index = input.location;
34 // Component counts must be 1, 2, 3 or 4. Do that validation now.
35 if (input.vec_size < 1u || input.vec_size > 4u) {
36 return false;
37 }
38 attrib.size = input.vec_size;
39 auto type = ToVertexAttribType(input.type);
40 if (!type.has_value()) {
41 return false;
42 }
43 attrib.type = type.value();
44 attrib.normalized = GL_FALSE;
45 attrib.offset = input.offset;
46 attrib.stride = layout.stride;
47 vertex_attrib_arrays.emplace_back(attrib);
48 }
49 vertex_attrib_arrays_ = std::move(vertex_attrib_arrays);
50 return true;
51 }
52
53 static std::string NormalizeUniformKey(const std::string& key) {
54 std::string result;
55 result.reserve(key.length());
56 for (char ch : key) {
57 if (ch != '_') {
58 result.push_back(toupper(ch));
59 }
60 }
61 return result;
62 }
63
64 static std::string CreateUniformMemberKey(const std::string& struct_name,
65 const std::string& member,
66 bool is_array) {
67 std::string result;
68 result.reserve(struct_name.length() + member.length() + (is_array ? 4 : 1));
69 result += struct_name;
70 result += '.';
71 result += member;
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 for (GLint i = 0; i < uniform_count; i++) {
94 std::vector<GLchar> name;
95 name.resize(max_name_size);
96 GLsizei written_count = 0u;
97 GLint uniform_var_size = 0u;
98 GLenum uniform_type = GL_FLOAT;
99 // Note: Active uniforms are defined as uniforms that may have an impact on
100 // the output of the shader. Drivers are allowed to (and often do)
101 // optimize out unused uniforms.
102 gl.GetActiveUniform(program, // program
103 i, // index
104 max_name_size, // buffer_size
105 &written_count, // length
106 &uniform_var_size, // size
107 &uniform_type, // type
108 name.data() // name
109 );
110 auto location = gl.GetUniformLocation(program, name.data());
111 if (location == -1) {
112 VALIDATION_LOG << "Could not query the location of an active uniform.";
113 return false;
114 }
115 if (written_count <= 0) {
116 VALIDATION_LOG << "Uniform name could not be read for active uniform.";
117 return false;
118 }
119 uniform_locations_[NormalizeUniformKey(std::string{
120 name.data(), static_cast<size_t>(written_count)})] = location;
121 }
122 return true;
123 }
124
125 bool BufferBindingsGLES::BindVertexAttributes(const ProcTableGLES& gl,
126 size_t vertex_offset) const {
127 for (const auto& array : vertex_attrib_arrays_) {
128 gl.EnableVertexAttribArray(array.index);
129 gl.VertexAttribPointer(array.index, // index
130 array.size, // size (must be 1, 2, 3, or 4)
131 array.type, // type
132 array.normalized, // normalized
133 array.stride, // stride
134 reinterpret_cast<const GLvoid*>(static_cast<GLsizei>(
135 vertex_offset + array.offset)) // pointer
136 );
137 }
138
139 return true;
140 }
141
142 bool BufferBindingsGLES::BindUniformData(
143 const ProcTableGLES& gl,
144 Allocator& transients_allocator,
145 const Bindings& vertex_bindings,
146 const Bindings& fragment_bindings) const {
147 for (const auto& buffer : vertex_bindings.buffers) {
148 if (!BindUniformBuffer(gl, transients_allocator, buffer.second.view)) {
149 return false;
150 }
151 }
152 for (const auto& buffer : fragment_bindings.buffers) {
153 if (!BindUniformBuffer(gl, transients_allocator, buffer.second.view)) {
154 return false;
155 }
156 }
157
158 if (!BindTextures(gl, vertex_bindings, ShaderStage::kVertex)) {
159 return false;
160 }
161
162 if (!BindTextures(gl, fragment_bindings, ShaderStage::kFragment)) {
163 return false;
164 }
165
166 return true;
167 }
168
169 bool BufferBindingsGLES::UnbindVertexAttributes(const ProcTableGLES& gl) const {
170 for (const auto& array : vertex_attrib_arrays_) {
171 gl.DisableVertexAttribArray(array.index);
172 }
173 return true;
174 }
175
176 bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
177 Allocator& transients_allocator,
178 const BufferResource& buffer) const {
179 const auto* metadata = buffer.GetMetadata();
180 auto device_buffer =
181 buffer.resource.buffer->GetDeviceBuffer(transients_allocator);
182 if (!device_buffer) {
183 VALIDATION_LOG << "Device buffer not found.";
184 return false;
185 }
186 const auto& device_buffer_gles = DeviceBufferGLES::Cast(*device_buffer);
187 const uint8_t* buffer_ptr =
188 device_buffer_gles.GetBufferData() + buffer.resource.range.offset;
189
190 if (metadata->members.empty()) {
191 VALIDATION_LOG << "Uniform buffer had no members. This is currently "
192 "unsupported in the OpenGL ES backend. Use a uniform "
193 "buffer block.";
194 return false;
195 }
196
197 for (const auto& member : metadata->members) {
198 if (member.type == ShaderType::kVoid) {
199 // Void types are used for padding. We are obviously not going to find
200 // mappings for these. Keep going.
201 continue;
202 }
203
204 size_t element_count = member.array_elements.value_or(1);
205
206 const auto member_key =
207 CreateUniformMemberKey(metadata->name, member.name, element_count > 1);
208 const auto location = uniform_locations_.find(member_key);
209 if (location == uniform_locations_.end()) {
210 // The list of uniform locations only contains "active" uniforms that are
211 // not optimized out. So this situation is expected to happen when unused
212 // uniforms are present in the shader.
213 continue;
214 }
215
216 size_t element_stride = member.byte_length / element_count;
217
218 auto* buffer_data =
219 reinterpret_cast<const GLfloat*>(buffer_ptr + member.offset);
220
221 std::vector<uint8_t> array_element_buffer;
222 if (element_count > 1) {
223 // When binding uniform arrays, the elements must be contiguous. Copy the
224 // uniforms to a temp buffer to eliminate any padding needed by the other
225 // backends.
226 array_element_buffer.resize(member.size * element_count);
227 for (size_t element_i = 0; element_i < element_count; element_i++) {
228 std::memcpy(array_element_buffer.data() + element_i * member.size,
229 reinterpret_cast<const char*>(buffer_data) +
230 element_i * element_stride,
231 member.size);
232 }
233 buffer_data =
234 reinterpret_cast<const GLfloat*>(array_element_buffer.data());
235 }
236
237 switch (member.type) {
238 case ShaderType::kFloat:
239 switch (member.size) {
240 case sizeof(Matrix):
241 gl.UniformMatrix4fv(location->second, // location
242 element_count, // count
243 GL_FALSE, // normalize
244 buffer_data // data
245 );
246 continue;
247 case sizeof(Vector4):
248 gl.Uniform4fv(location->second, // location
249 element_count, // count
250 buffer_data // data
251 );
252 continue;
253 case sizeof(Vector3):
254 gl.Uniform3fv(location->second, // location
255 element_count, // count
256 buffer_data // data
257 );
258 continue;
259 case sizeof(Vector2):
260 gl.Uniform2fv(location->second, // location
261 element_count, // count
262 buffer_data // data
263 );
264 continue;
265 case sizeof(Scalar):
266 gl.Uniform1fv(location->second, // location
267 element_count, // count
268 buffer_data // data
269 );
270 continue;
271 }
272 case ShaderType::kBoolean:
273 case ShaderType::kSignedByte:
274 case ShaderType::kUnsignedByte:
275 case ShaderType::kSignedShort:
276 case ShaderType::kUnsignedShort:
277 case ShaderType::kSignedInt:
278 case ShaderType::kUnsignedInt:
279 case ShaderType::kSignedInt64:
280 case ShaderType::kUnsignedInt64:
281 case ShaderType::kAtomicCounter:
282 case ShaderType::kUnknown:
283 case ShaderType::kVoid:
284 case ShaderType::kHalfFloat:
285 case ShaderType::kDouble:
286 case ShaderType::kStruct:
287 case ShaderType::kImage:
288 case ShaderType::kSampledImage:
289 case ShaderType::kSampler:
290 VALIDATION_LOG << "Could not bind uniform buffer data for key: "
291 << member_key;
292 return false;
293 }
294 }
295 return true;
296 }
297
298 bool BufferBindingsGLES::BindTextures(const ProcTableGLES& gl,
299 const Bindings& bindings,
300 ShaderStage stage) const {
301 size_t active_index = 0;
302 for (const auto& data : bindings.sampled_images) {
303 const auto& texture_gles = TextureGLES::Cast(*data.second.texture.resource);
304 if (data.second.texture.GetMetadata() == nullptr) {
305 VALIDATION_LOG << "No metadata found for texture binding.";
306 return false;
307 }
308
309 const auto uniform_key =
310 CreateUniformMemberKey(data.second.texture.GetMetadata()->name);
311 auto uniform = uniform_locations_.find(uniform_key);
312 if (uniform == uniform_locations_.end()) {
313 VALIDATION_LOG << "Could not find uniform for key: " << uniform_key;
314 return false;
315 }
316
317 //--------------------------------------------------------------------------
318 /// Set the active texture unit.
319 ///
320 if (active_index >= gl.GetCapabilities()->GetMaxTextureUnits(stage)) {
321 VALIDATION_LOG << "Texture units specified exceed the capabilities for "
322 "this shader stage.";
323 return false;
324 }
325 gl.ActiveTexture(GL_TEXTURE0 + active_index);
326
327 //--------------------------------------------------------------------------
328 /// Bind the texture.
329 ///
330 if (!texture_gles.Bind()) {
331 return false;
332 }
333
334 //--------------------------------------------------------------------------
335 /// If there is a sampler for the texture at the same index, configure the
336 /// bound texture using that sampler.
337 ///
338 const auto& sampler_gles = SamplerGLES::Cast(*data.second.sampler.resource);
339 if (!sampler_gles.ConfigureBoundTexture(texture_gles, gl)) {
340 return false;
341 }
342
343 //--------------------------------------------------------------------------
344 /// Set the texture uniform location.
345 ///
346 gl.Uniform1i(uniform->second, active_index);
347
348 //--------------------------------------------------------------------------
349 /// Bump up the active index at binding.
350 ///
351 active_index++;
352 }
353 return true;
354 }
355
356 } // namespace impeller
impeller::BufferBindingsGLES::ReadUniformsBindings
bool ReadUniformsBindings(const ProcTableGLES &gl, GLuint program)
Definition: buffer_bindings_gles.cc:83
impeller::ShaderType::kAtomicCounter
@ kAtomicCounter
impeller::Resource::resource
ResourceType resource
Definition: command.h:37
impeller::Scalar
float Scalar
Definition: scalar.h:15
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:24
impeller::BufferBindingsGLES::BufferBindingsGLES
BufferBindingsGLES()
impeller::Range::offset
size_t offset
Definition: range.h:14
impeller::ShaderType::kSignedInt64
@ kSignedInt64
texture_gles.h
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::ShaderType::kImage
@ kImage
impeller::ShaderStage
ShaderStage
Definition: shader_types.h:20
impeller::ShaderType::kVoid
@ kVoid
impeller::Bindings::buffers
std::map< size_t, BufferAndUniformSlot > buffers
Definition: command.h:79
impeller::ShaderType::kSignedByte
@ kSignedByte
impeller::BufferView::range
Range range
Definition: buffer_view.h:16
validation.h
sampler_gles.h
impeller::ShaderType::kUnsignedShort
@ kUnsignedShort
impeller::NormalizeUniformKey
static std::string NormalizeUniformKey(const std::string &key)
Definition: buffer_bindings_gles.cc:53
impeller::Resource< BufferView >
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:169
impeller::ShaderType::kBoolean
@ kBoolean
impeller::ShaderType::kSampler
@ kSampler
impeller::BufferView::buffer
std::shared_ptr< const Buffer > buffer
Definition: buffer_view.h:14
impeller::ShaderType::kSampledImage
@ kSampledImage
impeller::Allocator
An object that allocates device memory.
Definition: allocator.h:25
impeller::ProcTableGLES
Definition: proc_table_gles.h:188
impeller::ShaderStage::kFragment
@ kFragment
impeller::ToVertexAttribType
constexpr std::optional< GLenum > ToVertexAttribType(ShaderType type)
Definition: formats_gles.h:138
impeller::ShaderType::kUnsignedInt
@ kUnsignedInt
formats_gles.h
buffer_bindings_gles.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:60
impeller::ShaderType::kStruct
@ kStruct
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:125
impeller::BackendCast< DeviceBufferGLES, DeviceBuffer >::Cast
static DeviceBufferGLES & Cast(DeviceBuffer &base)
Definition: backend_cast.h:14
impeller::ShaderType::kUnknown
@ kUnknown
impeller::BufferBindingsGLES::~BufferBindingsGLES
~BufferBindingsGLES()
impeller::Resource::GetMetadata
const ShaderMetadata * GetMetadata() const
Definition: command.h:48
config.h
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:64
impeller::Bindings
Definition: command.h:77
impeller::BufferBindingsGLES::BindUniformData
bool BindUniformData(const ProcTableGLES &gl, Allocator &transients_allocator, const Bindings &vertex_bindings, const Bindings &fragment_bindings) const
Definition: buffer_bindings_gles.cc:142