[Impeller] Support uniform struct arrays (#36194)
diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 56a14f7..becce0a 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h
@@ -50,7 +50,7 @@ {% for def in struct_definitions %} struct {{def.name}} { {% for member in def.members %} - {{member.type}} {{member.name}}; // (offset {{member.offset}}, size {{member.byte_length}}) +{% if member.element_padding > 0 %}Padded<{{member.type}}, {{member.element_padding}}>{% else %}{{member.type}}{% endif %} {{" " + member.name}}{% if member.array_elements > 1 %}[{{member.array_elements}}]{% endif %}; // (offset {{member.offset}}, size {{member.byte_length}}) {% endfor %} }; // struct {{def.name}} (size {{def.byte_length}}) {% endfor %} @@ -205,10 +205,12 @@ std::vector<ShaderStructMemberMetadata> { {% for member in buffer.type.members %} ShaderStructMemberMetadata { - {{ member.base_type }}, // type - "{{ member.name }}", // name - {{ member.offset }}, // offset - {{ member.size }}, // size + {{ member.base_type }}, // type + "{{ member.name }}", // name + {{ member.offset }}, // offset + {{ member.size }}, // size + {{ member.byte_length }}, // byte_length + {{ member.array_elements }}, // array_elements }, {% endfor %} } // members
diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index b6fc7b5..79a847a 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc
@@ -474,7 +474,9 @@ member["type"] = struct_member.type; member["base_type"] = struct_member.base_type; member["offset"] = struct_member.offset; - member["size"] = struct_member.byte_length; + member["size"] = struct_member.size; + member["byte_length"] = struct_member.byte_length; + member["array_elements"] = struct_member.array_elements; members.emplace_back(std::move(member)); } } @@ -571,6 +573,7 @@ const auto& member = compiler_->get_type(struct_type.member_types[i]); const auto struct_member_offset = compiler_->type_struct_member_offset(struct_type, i); + auto array_elements = std::max(1u, GetArrayElements(member)); if (struct_member_offset > current_byte_offset) { const auto alignment_pad = struct_member_offset - current_byte_offset; @@ -580,7 +583,10 @@ SPrintF("_PADDING_%s_", GetMemberNameAtIndex(struct_type, i).c_str()), // name current_byte_offset, // offset - alignment_pad // byte_length + alignment_pad, // size + alignment_pad, // byte_length + 1, // array_elements + 0, // element_padding }); current_byte_offset += alignment_pad; } @@ -598,14 +604,19 @@ member.columns == 4 && // member.vecsize == 4 // ) { + uint32_t stride = GetArrayStride<sizeof(Matrix)>(struct_type, member, i); + uint32_t element_padding = stride - sizeof(Matrix); result.emplace_back(StructMember{ "Matrix", // type BaseTypeToString(member.basetype), // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset - sizeof(Matrix) // byte_length + sizeof(Matrix), // size + stride * array_elements, // byte_length + array_elements, // array_elements + element_padding, // element_padding }); - current_byte_offset += sizeof(Matrix); + current_byte_offset += stride * array_elements; continue; } @@ -615,14 +626,19 @@ member.columns == 1 && // member.vecsize == 2 // ) { + uint32_t stride = GetArrayStride<sizeof(Point)>(struct_type, member, i); + uint32_t element_padding = stride - sizeof(Point); result.emplace_back(StructMember{ "Point", // type BaseTypeToString(member.basetype), // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset - sizeof(Point) // byte_length + sizeof(Point), // size + stride * array_elements, // byte_length + array_elements, // array_elements + element_padding, // element_padding }); - current_byte_offset += sizeof(Point); + current_byte_offset += stride * array_elements; continue; } @@ -632,14 +648,19 @@ member.columns == 1 && // member.vecsize == 3 // ) { + uint32_t stride = GetArrayStride<sizeof(Vector3)>(struct_type, member, i); + uint32_t element_padding = stride - sizeof(Vector3); result.emplace_back(StructMember{ "Vector3", // type BaseTypeToString(member.basetype), // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset - sizeof(Vector3) // byte_length + sizeof(Vector3), // size + stride * array_elements, // byte_length + array_elements, // array_elements + element_padding, // element_padding }); - current_byte_offset += sizeof(Vector3); + current_byte_offset += stride * array_elements; continue; } @@ -649,14 +670,19 @@ member.columns == 1 && // member.vecsize == 4 // ) { + uint32_t stride = GetArrayStride<sizeof(Vector4)>(struct_type, member, i); + uint32_t element_padding = stride - sizeof(Vector4); result.emplace_back(StructMember{ "Vector4", // type BaseTypeToString(member.basetype), // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset - sizeof(Vector4) // byte_length + sizeof(Vector4), // size + stride * array_elements, // byte_length + array_elements, // array_elements + element_padding, // element_padding }); - current_byte_offset += sizeof(Vector4); + current_byte_offset += stride * array_elements; continue; } @@ -667,15 +693,23 @@ member.columns == 1 && // member.vecsize == 1 // ) { + uint32_t stride = GetArrayStride<0>(struct_type, member, i); + if (stride == 0) { + stride = maybe_known_type.value().byte_size; + } + uint32_t element_padding = stride - maybe_known_type.value().byte_size; // Add the type directly. result.emplace_back(StructMember{ maybe_known_type.value().name, // type BaseTypeToString(member.basetype), // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset - maybe_known_type.value().byte_size // byte_length + maybe_known_type.value().byte_size, // size + stride * array_elements, // byte_length + array_elements, // array_elements + element_padding, // element_padding }); - current_byte_offset += maybe_known_type.value().byte_size; + current_byte_offset += stride * array_elements; continue; } } @@ -683,16 +717,23 @@ // Catch all for unknown types. Just add the necessary padding to the struct // and move on. { - const size_t byte_length = - (member.width * member.columns * member.vecsize) / 8u; + const size_t size = (member.width * member.columns * member.vecsize) / 8u; + uint32_t stride = GetArrayStride<0>(struct_type, member, i); + if (stride == 0) { + stride = size; + } + auto element_padding = stride - size; result.emplace_back(StructMember{ - TypeNameWithPaddingOfSize(byte_length), // type - BaseTypeToString(member.basetype), // basetype - GetMemberNameAtIndex(struct_type, i), // name - struct_member_offset, // offset - byte_length // byte_length + TypeNameWithPaddingOfSize(size), // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + size, // size + stride * array_elements, // byte_length + array_elements, // array_elements + 0, // element_padding }); - current_byte_offset += byte_length; + current_byte_offset += stride * array_elements; continue; } } @@ -709,7 +750,10 @@ spirv_cross::SPIRType::BaseType::Void), // basetype "_PADDING_", // name current_byte_offset, // offset - padding // byte_length + padding, // size + padding, // byte_length + 1, // array_elements + 0, // element_padding }); } } @@ -746,13 +790,15 @@ result["name"] = struc->name; result["byte_length"] = struc->byte_length; auto& members = result["members"] = nlohmann::json::array_t{}; - for (const auto& struc_member : struc->members) { + for (const auto& struct_member : struc->members) { auto& member = members.emplace_back(nlohmann::json::object_t{}); - member["name"] = struc_member.name; - member["type"] = struc_member.type; - member["base_type"] = struc_member.base_type; - member["offset"] = struc_member.offset; - member["byte_length"] = struc_member.byte_length; + member["name"] = struct_member.name; + member["type"] = struct_member.type; + member["base_type"] = struct_member.base_type; + member["offset"] = struct_member.offset; + member["byte_length"] = struct_member.byte_length; + member["array_elements"] = struct_member.array_elements; + member["element_padding"] = struct_member.element_padding; } return result; } @@ -863,7 +909,10 @@ vertex_type.base_type_name, // base type vertex_type.variable_name, // name struc.byte_length, // offset - vertex_type.byte_length // byte_length + vertex_type.byte_length, // size + vertex_type.byte_length, // byte_length + 1, // array_elements + 0, // element_padding }; struc.byte_length += vertex_type.byte_length; struc.members.emplace_back(std::move(member));
diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 2c0de13..a58d8d1 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h
@@ -23,18 +23,27 @@ std::string base_type; std::string name; size_t offset = 0u; + size_t size = 0u; size_t byte_length = 0u; + size_t array_elements = 1u; + size_t element_padding = 0u; StructMember(std::string p_type, std::string p_base_type, std::string p_name, size_t p_offset, - size_t p_byte_length) + size_t p_size, + size_t p_byte_length, + size_t p_array_elements, + size_t p_element_padding) : type(std::move(p_type)), base_type(std::move(p_base_type)), name(std::move(p_name)), offset(p_offset), - byte_length(p_byte_length) {} + size(p_size), + byte_length(p_byte_length), + array_elements(p_array_elements), + element_padding(p_element_padding) {} }; class Reflector { @@ -142,6 +151,17 @@ uint32_t GetArrayElements(const spirv_cross::SPIRType& type) const; + template <uint32_t Size> + uint32_t GetArrayStride(const spirv_cross::SPIRType& struct_type, + const spirv_cross::SPIRType& member_type, + uint32_t index) const { + auto element_count = GetArrayElements(member_type); + if (element_count <= 1) { + return Size; + } + return compiler_->type_struct_member_array_stride(struct_type, index); + }; + FML_DISALLOW_COPY_AND_ASSIGN(Reflector); };
diff --git a/impeller/docs/ubo_gles2.md b/impeller/docs/ubo_gles2.md index ef6c93a..4fb4831 100644 --- a/impeller/docs/ubo_gles2.md +++ b/impeller/docs/ubo_gles2.md
@@ -31,6 +31,7 @@ std::string name; // the uniform member name "frame_info.mvp" size_t offset; size_t size; + size_t array_elements; }; ```
diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index a31b604..c125990 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn
@@ -8,6 +8,8 @@ impeller_shaders("shader_fixtures") { name = "fixtures" shaders = [ + "array.frag", + "array.vert", "box_fade.frag", "box_fade.vert", "colors.vert",
diff --git a/impeller/fixtures/array.frag b/impeller/fixtures/array.frag new file mode 100644 index 0000000..def284a --- /dev/null +++ b/impeller/fixtures/array.frag
@@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FragInfo { + vec2 circle_positions[4]; + vec4 colors[4]; +} +frag_info; + +in vec2 v_position; + +out vec4 frag_color; + +float SphereDistance(vec2 position, float radius) { + return length(v_position - position) - radius; +} + +void main() { + for (int i = 0; i < 4; i++) { + if (SphereDistance(frag_info.circle_positions[i].xy, 20) <= 0) { + frag_color = frag_info.colors[i]; + return; + } + } + frag_color = vec4(0); +}
diff --git a/impeller/fixtures/array.vert b/impeller/fixtures/array.vert new file mode 100644 index 0000000..2babce3 --- /dev/null +++ b/impeller/fixtures/array.vert
@@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform VertInfo { + mat4 mvp; +} +vert_info; + +in vec2 position; + +out vec2 v_position; + +void main() { + gl_Position = vert_info.mvp * vec4(position, 0.0, 1.0); + v_position = position; +}
diff --git a/impeller/renderer/backend/gles/buffer_bindings_gles.cc b/impeller/renderer/backend/gles/buffer_bindings_gles.cc index 5119b5c..ceb13db 100644 --- a/impeller/renderer/backend/gles/buffer_bindings_gles.cc +++ b/impeller/renderer/backend/gles/buffer_bindings_gles.cc
@@ -5,7 +5,9 @@ #include "impeller/renderer/backend/gles/buffer_bindings_gles.h" #include <algorithm> +#include <cstring> #include <sstream> +#include <vector> #include "impeller/base/config.h" #include "impeller/base/validation.h" @@ -70,9 +72,13 @@ } static std::string CreateUnifiormMemberKey(const std::string& struct_name, - const std::string& member) { + const std::string& member, + bool is_array) { std::stringstream stream; stream << struct_name << "." << member; + if (is_array) { + stream << "[0]"; + } return NormalizeUniformKey(stream.str()); } @@ -114,10 +120,6 @@ VALIDATION_LOG << "Uniform name could not be read for active uniform."; return false; } - if (uniform_var_size != 1) { - VALIDATION_LOG << "Array uniform types are not supported."; - return false; - } uniform_locations_[NormalizeUniformKey(std::string{ name.data(), static_cast<size_t>(written_count)})] = location; } @@ -208,51 +210,69 @@ // mappings for these. Keep going. continue; } - const auto member_key = - CreateUnifiormMemberKey(metadata->name, member.name); + + const auto member_key = CreateUnifiormMemberKey(metadata->name, member.name, + member.array_elements > 1); const auto location = uniform_locations_.find(member_key); if (location == uniform_locations_.end()) { VALIDATION_LOG << "Location for uniform member not known: " << member_key; return false; } + size_t element_stride = member.byte_length / member.array_elements; + + auto* buffer_data = + reinterpret_cast<const GLfloat*>(buffer_ptr + member.offset); + + std::vector<uint8_t> array_element_buffer; + if (member.array_elements > 1) { + // When binding uniform arrays, the elements must be contiguous. Copy the + // uniforms to a temp buffer to eliminate any padding needed by the other + // backends. + array_element_buffer.resize(member.size * member.array_elements); + for (size_t element_i = 0; element_i < member.array_elements; + element_i++) { + std::memcpy(array_element_buffer.data() + element_i * member.size, + reinterpret_cast<const char*>(buffer_data) + + element_i * element_stride, + member.size); + } + buffer_data = + reinterpret_cast<const GLfloat*>(array_element_buffer.data()); + } + switch (member.type) { case ShaderType::kFloat: switch (member.size) { case sizeof(Matrix): - gl.UniformMatrix4fv(location->second, // location - 1u, // count - GL_FALSE, // normalize - reinterpret_cast<const GLfloat*>( - buffer_ptr + member.offset) // data + gl.UniformMatrix4fv(location->second, // location + member.array_elements, // count + GL_FALSE, // normalize + buffer_data // data ); continue; case sizeof(Vector4): - gl.Uniform4fv(location->second, // location - 1u, // count - reinterpret_cast<const GLfloat*>( - buffer_ptr + member.offset) // data + gl.Uniform4fv(location->second, // location + member.array_elements, // count + buffer_data // data ); continue; case sizeof(Vector3): - gl.Uniform3fv(location->second, // location - 1u, // count - reinterpret_cast<const GLfloat*>( - buffer_ptr + member.offset) // data + gl.Uniform3fv(location->second, // location + member.array_elements, // count + buffer_data // data ); continue; case sizeof(Vector2): - gl.Uniform2fv(location->second, // location - 1u, // count - reinterpret_cast<const GLfloat*>( - buffer_ptr + member.offset) // data + gl.Uniform2fv(location->second, // location + member.array_elements, // count + buffer_data // data ); continue; case sizeof(Scalar): - gl.Uniform1fv(location->second, // location - 1u, // count - reinterpret_cast<const GLfloat*>( - buffer_ptr + member.offset) // data + gl.Uniform1fv(location->second, // location + member.array_elements, // count + buffer_data // data ); continue; }
diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index af01a5d..315ec54 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc
@@ -5,6 +5,8 @@ #include "flutter/fml/time/time_point.h" #include "flutter/testing/testing.h" #include "impeller/base/strings.h" +#include "impeller/fixtures/array.frag.h" +#include "impeller/fixtures/array.vert.h" #include "impeller/fixtures/box_fade.frag.h" #include "impeller/fixtures/box_fade.vert.h" #include "impeller/fixtures/colors.frag.h" @@ -736,5 +738,61 @@ OpenPlaygroundHere(callback); } +TEST_P(RendererTest, ArrayUniforms) { + using VS = ArrayVertexShader; + using FS = ArrayFragmentShader; + + auto context = GetContext(); + auto pipeline_descriptor = + PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(pipeline_descriptor.has_value()); + pipeline_descriptor->SetSampleCount(SampleCount::kCount4); + auto pipeline = + context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).get(); + ASSERT_TRUE(pipeline && pipeline->IsValid()); + + SinglePassCallback callback = [&](RenderPass& pass) { + auto size = pass.GetRenderTargetSize(); + + Command cmd; + cmd.pipeline = pipeline; + cmd.label = "Google Dots"; + VertexBufferBuilder<VS::PerVertexData> builder; + builder.AddVertices({{Point()}, + {Point(0, size.height)}, + {Point(size.width, 0)}, + {Point(size.width, 0)}, + {Point(0, size.height)}, + {Point(size.width, size.height)}}); + cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer())); + + VS::VertInfo vs_uniform; + vs_uniform.mvp = + Matrix::MakeOrthographic(size) * Matrix::MakeScale(GetContentScale()); + VS::BindVertInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(vs_uniform)); + + auto time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); + auto y_pos = [&time](float x) { + return 400 + 10 * std::cos(time * 5 + x / 6); + }; + + FS::FragInfo fs_uniform = { + .circle_positions = {Point(430, y_pos(0)), Point(480, y_pos(1)), + Point(530, y_pos(2)), Point(580, y_pos(3))}, + .colors = {Color::MakeRGBA8(66, 133, 244, 255), + Color::MakeRGBA8(219, 68, 55, 255), + Color::MakeRGBA8(244, 180, 0, 255), + Color::MakeRGBA8(15, 157, 88, 255)}, + }; + FS::BindFragInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(fs_uniform)); + + pass.AddCommand(cmd); + return true; + }; + OpenPlaygroundHere(callback); +} + } // namespace testing } // namespace impeller
diff --git a/impeller/renderer/shader_types.h b/impeller/renderer/shader_types.h index fe4af81..cf2e9c9 100644 --- a/impeller/renderer/shader_types.h +++ b/impeller/renderer/shader_types.h
@@ -67,6 +67,8 @@ std::string name; size_t offset; size_t size; + size_t byte_length; + size_t array_elements; }; struct ShaderMetadata { @@ -122,6 +124,17 @@ uint8_t pad_[Size]; }; +/// @brief Struct used for padding uniform buffer array elements. +template <typename T, + size_t Size, + class = std::enable_if_t<std::is_standard_layout_v<T>>> +struct Padded { + T value; + Padding<Size> _PADDING_; + + Padded(T p_value) : value(p_value){}; // NOLINT(google-explicit-constructor) +}; + inline constexpr Vector4 ToVector(Color color) { return {color.red, color.green, color.blue, color.alpha}; }