Skip to content

Commit e435edf

Browse files
walley892gaaclarke
andauthored
Account for vec3 padding in Metal (flutter#181563)
Re-landing of flutter#181340 incorporating flutter#181550. Vec3 uniforms on the metal backend are vec4-aligned. This PR accounts for that padding. --------- Co-authored-by: gaaclarke <30870216+gaaclarke@users.noreply.github.com>
1 parent b6fa57d commit e435edf

File tree

15 files changed

+233
-138
lines changed

15 files changed

+233
-138
lines changed

engine/src/flutter/impeller/compiler/compiler_test.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ bool CompilerTestBase::CanCompileAndReflect(
106106
entry_point_name);
107107

108108
Reflector::Options reflector_options;
109+
reflector_options.target_platform = GetParam();
109110
reflector_options.header_file_name = ReflectionHeaderName(fixture_name);
110111
reflector_options.shader_name = "shader_name";
111112

engine/src/flutter/impeller/compiler/reflector.cc

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "impeller/geometry/matrix.h"
2727
#include "impeller/geometry/scalar.h"
2828
#include "impeller/runtime_stage/runtime_stage.h"
29+
#include "runtime_stage_types_flatbuffers.h"
2930
#include "spirv_common.hpp"
3031

3132
namespace impeller {
@@ -372,6 +373,27 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
372373
uniform_description.columns = spir_type.columns;
373374
uniform_description.bit_width = spir_type.width;
374375
uniform_description.array_elements = GetArrayElements(spir_type);
376+
377+
if (TargetPlatformIsMetal(options_.target_platform) &&
378+
uniform_description.type == spirv_cross::SPIRType::BaseType::Float) {
379+
// Metal aligns float3 to 16 bytes.
380+
// Metal aligns float3x3 COLUMNS to 16 bytes.
381+
// For float3: Size 12. Padding 4. Stride 16.
382+
// For float3x3: Size 36. Padding 12 (4 per col). Stride 48.
383+
384+
if (spir_type.vecsize == 3 &&
385+
(spir_type.columns == 1 || spir_type.columns == 3)) {
386+
for (size_t c = 0; c < spir_type.columns; c++) {
387+
for (size_t v = 0; v < 3; v++) {
388+
uniform_description.padding_layout.push_back(
389+
fb::PaddingType::kFloat);
390+
}
391+
uniform_description.padding_layout.push_back(
392+
fb::PaddingType::kPadding);
393+
}
394+
}
395+
}
396+
375397
FML_CHECK(data->backend != RuntimeStageBackend::kVulkan ||
376398
spir_type.basetype ==
377399
spirv_cross::SPIRType::BaseType::SampledImage)
@@ -396,7 +418,7 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
396418
size_t binding =
397419
compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
398420
auto members = ReadStructMembers(ubo.type_id);
399-
std::vector<uint8_t> struct_layout;
421+
std::vector<fb::PaddingType> padding_layout;
400422
size_t float_count = 0;
401423

402424
for (size_t i = 0; i < members.size(); i += 1) {
@@ -407,7 +429,7 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
407429
size_t padding_count =
408430
(member.size + sizeof(float) - 1) / sizeof(float);
409431
while (padding_count > 0) {
410-
struct_layout.push_back(0);
432+
padding_layout.push_back(fb::PaddingType::kPadding);
411433
padding_count--;
412434
}
413435
break;
@@ -418,18 +440,18 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
418440
// and 0 layout property per byte of padding
419441
for (auto i = 0; i < member.array_elements; i++) {
420442
for (auto j = 0u; j < member.size / sizeof(float); j++) {
421-
struct_layout.push_back(1);
443+
padding_layout.push_back(fb::PaddingType::kFloat);
422444
}
423445
for (auto j = 0u; j < member.element_padding / sizeof(float);
424446
j++) {
425-
struct_layout.push_back(0);
447+
padding_layout.push_back(fb::PaddingType::kPadding);
426448
}
427449
}
428450
} else {
429451
size_t member_float_count = member.byte_length / sizeof(float);
430452
float_count += member_float_count;
431453
while (member_float_count > 0) {
432-
struct_layout.push_back(1);
454+
padding_layout.push_back(fb::PaddingType::kFloat);
433455
member_float_count--;
434456
}
435457
}
@@ -446,7 +468,7 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
446468
.location = binding,
447469
.binding = binding,
448470
.type = spirv_cross::SPIRType::Struct,
449-
.struct_layout = std::move(struct_layout),
471+
.padding_layout = std::move(padding_layout),
450472
.struct_float_count = float_count,
451473
});
452474
}

engine/src/flutter/impeller/compiler/runtime_stage_data.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ std::unique_ptr<fb::RuntimeStageT> RuntimeStageData::CreateStageFlatbuffer(
332332
desc->array_elements = uniform.array_elements.value();
333333
}
334334

335-
for (const auto& byte_type : uniform.struct_layout) {
336-
desc->struct_layout.push_back(static_cast<fb::StructByteType>(byte_type));
335+
for (const auto& byte_type : uniform.padding_layout) {
336+
desc->padding_layout.push_back(static_cast<fb::PaddingType>(byte_type));
337337
}
338338
desc->struct_float_count = uniform.struct_float_count;
339339

engine/src/flutter/impeller/compiler/types.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <optional>
1313
#include <string>
1414

15+
#include "runtime_stage_types_flatbuffers.h"
1516
#include "shaderc/shaderc.hpp"
1617
#include "spirv_cross.hpp"
1718
#include "spirv_msl.hpp"
@@ -55,7 +56,11 @@ struct UniformDescription {
5556
size_t columns = 0u;
5657
size_t bit_width = 0u;
5758
std::optional<size_t> array_elements = std::nullopt;
58-
std::vector<uint8_t> struct_layout = {};
59+
/// The layout of padding bytes in the uniform buffer.
60+
/// The format matches the values in the flatbuffer
61+
/// UniformDescription::padding_layout.
62+
/// \see RuntimeEffectContents::EmplaceUniform
63+
std::vector<fb::PaddingType> padding_layout = {};
5964
size_t struct_float_count = 0u;
6065
};
6166

engine/src/flutter/impeller/core/runtime_types.cc

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,37 @@
66

77
namespace impeller {
88

9-
size_t RuntimeUniformDescription::GetSize() const {
10-
size_t size = dimensions.rows * dimensions.cols * bit_width / 8u;
9+
size_t RuntimeUniformDescription::GetDartSize() const {
10+
size_t size = 0;
11+
if (!padding_layout.empty()) {
12+
for (impeller::RuntimePaddingType byte_type : padding_layout) {
13+
if (byte_type == RuntimePaddingType::kFloat) {
14+
size += sizeof(float);
15+
}
16+
}
17+
} else {
18+
size = dimensions.rows * dimensions.cols * bit_width / 8u;
19+
}
20+
if (array_elements.value_or(0) > 0) {
21+
// Covered by check on the line above.
22+
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
23+
size *= array_elements.value();
24+
}
25+
return size;
26+
}
27+
28+
size_t RuntimeUniformDescription::GetGPUSize() const {
29+
size_t size = 0;
30+
if (padding_layout.empty()) {
31+
size = dimensions.rows * dimensions.cols * bit_width / 8u;
32+
} else {
33+
size = sizeof(float) * padding_layout.size();
34+
}
1135
if (array_elements.value_or(0) > 0) {
1236
// Covered by check on the line above.
1337
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1438
size *= array_elements.value();
1539
}
16-
size += sizeof(float) * struct_layout.size();
1740
return size;
1841
}
1942

engine/src/flutter/impeller/core/runtime_types.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ struct RuntimeUniformDimensions {
3838
size_t cols = 0;
3939
};
4040

41+
enum class RuntimePaddingType : uint8_t {
42+
kPadding = 0,
43+
kFloat = 1,
44+
};
45+
4146
struct RuntimeUniformDescription {
4247
std::string name;
4348
size_t location = 0u;
@@ -47,11 +52,16 @@ struct RuntimeUniformDescription {
4752
RuntimeUniformDimensions dimensions = {};
4853
size_t bit_width = 0u;
4954
std::optional<size_t> array_elements;
50-
std::vector<uint8_t> struct_layout = {};
55+
std::vector<RuntimePaddingType> padding_layout = {};
5156
size_t struct_float_count = 0u;
5257

53-
/// @brief Computes the total number of bytes that this uniform requires.
54-
size_t GetSize() const;
58+
/// @brief Computes the total number of bytes that this uniform requires for
59+
/// representation in the Dart float buffer.
60+
size_t GetDartSize() const;
61+
62+
/// @brief Computes the total number of bytes that this uniform requires for
63+
/// representation in the GPU.
64+
size_t GetGPUSize() const;
5565
};
5666

5767
} // namespace impeller

engine/src/flutter/impeller/display_list/dl_runtime_effect_impeller.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ size_t DlRuntimeEffectImpeller::uniform_size() const {
3838

3939
size_t total = 0;
4040
for (const auto& uniform : runtime_stage_->GetUniforms()) {
41-
total += uniform.GetSize();
41+
total += uniform.GetGPUSize();
4242
}
4343
return total;
4444
}

engine/src/flutter/impeller/entity/contents/runtime_effect_contents.cc

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,35 +24,48 @@
2424

2525
namespace impeller {
2626

27-
namespace {
28-
constexpr char kPaddingType = 0;
29-
constexpr char kFloatType = 1;
30-
} // namespace
31-
3227
// static
33-
BufferView RuntimeEffectContents::EmplaceVulkanUniform(
34-
const std::shared_ptr<const std::vector<uint8_t>>& input_data,
28+
BufferView RuntimeEffectContents::EmplaceUniform(
29+
const uint8_t* source_data,
3530
HostBuffer& data_host_buffer,
36-
const RuntimeUniformDescription& uniform,
37-
size_t minimum_uniform_alignment) {
38-
// TODO(jonahwilliams): rewrite this to emplace directly into
39-
// HostBuffer.
40-
std::vector<float> uniform_buffer;
41-
uniform_buffer.reserve(uniform.struct_layout.size());
42-
size_t uniform_byte_index = 0u;
43-
for (char byte_type : uniform.struct_layout) {
44-
if (byte_type == kPaddingType) {
45-
uniform_buffer.push_back(0.f);
46-
} else {
47-
FML_DCHECK(byte_type == kFloatType);
48-
uniform_buffer.push_back(reinterpret_cast<const float*>(
49-
input_data->data())[uniform_byte_index++]);
50-
}
31+
const RuntimeUniformDescription& uniform) {
32+
size_t minimum_uniform_alignment =
33+
data_host_buffer.GetMinimumUniformAlignment();
34+
size_t alignment = std::max(uniform.bit_width / 8, minimum_uniform_alignment);
35+
36+
if (uniform.padding_layout.empty()) {
37+
return data_host_buffer.Emplace(source_data, uniform.GetGPUSize(),
38+
alignment);
5139
}
5240

41+
// If the uniform has a padding layout, we need to repack the data.
42+
// We can do this by using the EmplaceProc to write directly to the
43+
// HostBuffer.
5344
return data_host_buffer.Emplace(
54-
reinterpret_cast<const void*>(uniform_buffer.data()),
55-
sizeof(float) * uniform_buffer.size(), minimum_uniform_alignment);
45+
uniform.GetGPUSize(), alignment,
46+
[&uniform, source_data](uint8_t* destination) {
47+
size_t count = uniform.array_elements.value_or(1);
48+
if (count == 0) {
49+
// Make sure to run at least once.
50+
count = 1;
51+
}
52+
size_t uniform_byte_index = 0u;
53+
size_t struct_float_index = 0u;
54+
auto* float_destination = reinterpret_cast<float*>(destination);
55+
auto* float_source = reinterpret_cast<const float*>(source_data);
56+
57+
for (size_t i = 0; i < count; i++) {
58+
for (RuntimePaddingType byte_type : uniform.padding_layout) {
59+
if (byte_type == RuntimePaddingType::kPadding) {
60+
float_destination[struct_float_index++] = 0.f;
61+
} else {
62+
FML_DCHECK(byte_type == RuntimePaddingType::kFloat);
63+
float_destination[struct_float_index++] =
64+
float_source[uniform_byte_index++];
65+
}
66+
}
67+
}
68+
});
5669
}
5770

5871
void RuntimeEffectContents::SetRuntimeStage(
@@ -284,12 +297,8 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
284297
<< "Uniform " << uniform.name
285298
<< " had unexpected type kFloat for Vulkan backend.";
286299

287-
size_t alignment =
288-
std::max(uniform.bit_width / 8,
289-
data_host_buffer.GetMinimumUniformAlignment());
290-
BufferView buffer_view =
291-
data_host_buffer.Emplace(uniform_data_->data() + buffer_offset,
292-
uniform.GetSize(), alignment);
300+
BufferView buffer_view = EmplaceUniform(
301+
uniform_data_->data() + buffer_offset, data_host_buffer, uniform);
293302

294303
ShaderUniformSlot uniform_slot;
295304
uniform_slot.name = uniform.name.c_str();
@@ -298,7 +307,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
298307
DescriptorType::kUniformBuffer, uniform_slot,
299308
std::move(metadata), std::move(buffer_view));
300309
buffer_index++;
301-
buffer_offset += uniform.GetSize();
310+
buffer_offset += uniform.GetDartSize();
302311
buffer_location++;
303312
break;
304313
}
@@ -309,12 +318,10 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
309318
uniform_slot.binding = uniform.location;
310319
uniform_slot.name = uniform.name.c_str();
311320

312-
pass.BindResource(ShaderStage::kFragment,
313-
DescriptorType::kUniformBuffer, uniform_slot,
314-
nullptr,
315-
EmplaceVulkanUniform(
316-
uniform_data_, data_host_buffer, uniform,
317-
data_host_buffer.GetMinimumUniformAlignment()));
321+
pass.BindResource(
322+
ShaderStage::kFragment, DescriptorType::kUniformBuffer,
323+
uniform_slot, nullptr,
324+
EmplaceUniform(uniform_data_->data(), data_host_buffer, uniform));
318325
}
319326
}
320327
}

engine/src/flutter/impeller/entity/contents/runtime_effect_contents.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,17 @@ class RuntimeEffectContents final : public ColorSourceContents {
3737
bool BootstrapShader(const ContentContext& renderer) const;
3838

3939
// Visible for testing
40-
static BufferView EmplaceVulkanUniform(
41-
const std::shared_ptr<const std::vector<uint8_t>>& input_data,
42-
HostBuffer& host_buffer,
43-
const RuntimeUniformDescription& uniform,
44-
size_t minimum_uniform_alignment);
40+
/// Copies the uniform data into the host buffer.
41+
///
42+
/// If the `uniform` has a `padding_layout`, it is used to repack the data.
43+
///
44+
/// @param source_data The pointer to the start of the uniform data in the
45+
/// source.
46+
/// @param host_buffer The host buffer to emplace the uniform data into.
47+
/// @param uniform The description of the uniform being emplaced.
48+
static BufferView EmplaceUniform(const uint8_t* source_data,
49+
HostBuffer& host_buffer,
50+
const RuntimeUniformDescription& uniform);
4551

4652
private:
4753
bool RegisterShader(const ContentContext& renderer) const;

engine/src/flutter/impeller/entity/entity_unittests.cc

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,12 +1922,9 @@ TEST_P(EntityTest, RuntimeEffectSetsRightSizeWhenUniformIsStruct) {
19221922
uniform_data->resize(sizeof(FragUniforms));
19231923
memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
19241924

1925-
auto buffer_view = RuntimeEffectContents::EmplaceVulkanUniform(
1926-
uniform_data, GetContentContext()->GetTransientsDataBuffer(),
1927-
runtime_stage->GetUniforms()[0],
1928-
GetContentContext()
1929-
->GetTransientsDataBuffer()
1930-
.GetMinimumUniformAlignment());
1925+
auto buffer_view = RuntimeEffectContents::EmplaceUniform(
1926+
uniform_data->data(), GetContentContext()->GetTransientsDataBuffer(),
1927+
runtime_stage->GetUniforms()[0]);
19311928

19321929
// 16 bytes:
19331930
// 8 bytes for iResolution

0 commit comments

Comments
 (0)