mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Account for vec3 padding in Metal (#181563)
Re-landing of https://github.com/flutter/flutter/pull/181340 incorporating https://github.com/flutter/flutter/pull/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>
This commit is contained in:
parent
b6fa57dd2e
commit
e435edfbcd
@ -106,6 +106,7 @@ bool CompilerTestBase::CanCompileAndReflect(
|
||||
entry_point_name);
|
||||
|
||||
Reflector::Options reflector_options;
|
||||
reflector_options.target_platform = GetParam();
|
||||
reflector_options.header_file_name = ReflectionHeaderName(fixture_name);
|
||||
reflector_options.shader_name = "shader_name";
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "impeller/geometry/matrix.h"
|
||||
#include "impeller/geometry/scalar.h"
|
||||
#include "impeller/runtime_stage/runtime_stage.h"
|
||||
#include "runtime_stage_types_flatbuffers.h"
|
||||
#include "spirv_common.hpp"
|
||||
|
||||
namespace impeller {
|
||||
@ -372,6 +373,27 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
|
||||
uniform_description.columns = spir_type.columns;
|
||||
uniform_description.bit_width = spir_type.width;
|
||||
uniform_description.array_elements = GetArrayElements(spir_type);
|
||||
|
||||
if (TargetPlatformIsMetal(options_.target_platform) &&
|
||||
uniform_description.type == spirv_cross::SPIRType::BaseType::Float) {
|
||||
// Metal aligns float3 to 16 bytes.
|
||||
// Metal aligns float3x3 COLUMNS to 16 bytes.
|
||||
// For float3: Size 12. Padding 4. Stride 16.
|
||||
// For float3x3: Size 36. Padding 12 (4 per col). Stride 48.
|
||||
|
||||
if (spir_type.vecsize == 3 &&
|
||||
(spir_type.columns == 1 || spir_type.columns == 3)) {
|
||||
for (size_t c = 0; c < spir_type.columns; c++) {
|
||||
for (size_t v = 0; v < 3; v++) {
|
||||
uniform_description.padding_layout.push_back(
|
||||
fb::PaddingType::kFloat);
|
||||
}
|
||||
uniform_description.padding_layout.push_back(
|
||||
fb::PaddingType::kPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FML_CHECK(data->backend != RuntimeStageBackend::kVulkan ||
|
||||
spir_type.basetype ==
|
||||
spirv_cross::SPIRType::BaseType::SampledImage)
|
||||
@ -396,7 +418,7 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
|
||||
size_t binding =
|
||||
compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding);
|
||||
auto members = ReadStructMembers(ubo.type_id);
|
||||
std::vector<uint8_t> struct_layout;
|
||||
std::vector<fb::PaddingType> padding_layout;
|
||||
size_t float_count = 0;
|
||||
|
||||
for (size_t i = 0; i < members.size(); i += 1) {
|
||||
@ -407,7 +429,7 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
|
||||
size_t padding_count =
|
||||
(member.size + sizeof(float) - 1) / sizeof(float);
|
||||
while (padding_count > 0) {
|
||||
struct_layout.push_back(0);
|
||||
padding_layout.push_back(fb::PaddingType::kPadding);
|
||||
padding_count--;
|
||||
}
|
||||
break;
|
||||
@ -418,18 +440,18 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
|
||||
// and 0 layout property per byte of padding
|
||||
for (auto i = 0; i < member.array_elements; i++) {
|
||||
for (auto j = 0u; j < member.size / sizeof(float); j++) {
|
||||
struct_layout.push_back(1);
|
||||
padding_layout.push_back(fb::PaddingType::kFloat);
|
||||
}
|
||||
for (auto j = 0u; j < member.element_padding / sizeof(float);
|
||||
j++) {
|
||||
struct_layout.push_back(0);
|
||||
padding_layout.push_back(fb::PaddingType::kPadding);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t member_float_count = member.byte_length / sizeof(float);
|
||||
float_count += member_float_count;
|
||||
while (member_float_count > 0) {
|
||||
struct_layout.push_back(1);
|
||||
padding_layout.push_back(fb::PaddingType::kFloat);
|
||||
member_float_count--;
|
||||
}
|
||||
}
|
||||
@ -446,7 +468,7 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
|
||||
.location = binding,
|
||||
.binding = binding,
|
||||
.type = spirv_cross::SPIRType::Struct,
|
||||
.struct_layout = std::move(struct_layout),
|
||||
.padding_layout = std::move(padding_layout),
|
||||
.struct_float_count = float_count,
|
||||
});
|
||||
}
|
||||
|
||||
@ -332,8 +332,8 @@ std::unique_ptr<fb::RuntimeStageT> RuntimeStageData::CreateStageFlatbuffer(
|
||||
desc->array_elements = uniform.array_elements.value();
|
||||
}
|
||||
|
||||
for (const auto& byte_type : uniform.struct_layout) {
|
||||
desc->struct_layout.push_back(static_cast<fb::StructByteType>(byte_type));
|
||||
for (const auto& byte_type : uniform.padding_layout) {
|
||||
desc->padding_layout.push_back(static_cast<fb::PaddingType>(byte_type));
|
||||
}
|
||||
desc->struct_float_count = uniform.struct_float_count;
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "runtime_stage_types_flatbuffers.h"
|
||||
#include "shaderc/shaderc.hpp"
|
||||
#include "spirv_cross.hpp"
|
||||
#include "spirv_msl.hpp"
|
||||
@ -55,7 +56,11 @@ struct UniformDescription {
|
||||
size_t columns = 0u;
|
||||
size_t bit_width = 0u;
|
||||
std::optional<size_t> array_elements = std::nullopt;
|
||||
std::vector<uint8_t> struct_layout = {};
|
||||
/// The layout of padding bytes in the uniform buffer.
|
||||
/// The format matches the values in the flatbuffer
|
||||
/// UniformDescription::padding_layout.
|
||||
/// \see RuntimeEffectContents::EmplaceUniform
|
||||
std::vector<fb::PaddingType> padding_layout = {};
|
||||
size_t struct_float_count = 0u;
|
||||
};
|
||||
|
||||
|
||||
@ -6,14 +6,37 @@
|
||||
|
||||
namespace impeller {
|
||||
|
||||
size_t RuntimeUniformDescription::GetSize() const {
|
||||
size_t size = dimensions.rows * dimensions.cols * bit_width / 8u;
|
||||
size_t RuntimeUniformDescription::GetDartSize() const {
|
||||
size_t size = 0;
|
||||
if (!padding_layout.empty()) {
|
||||
for (impeller::RuntimePaddingType byte_type : padding_layout) {
|
||||
if (byte_type == RuntimePaddingType::kFloat) {
|
||||
size += sizeof(float);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size = dimensions.rows * dimensions.cols * bit_width / 8u;
|
||||
}
|
||||
if (array_elements.value_or(0) > 0) {
|
||||
// Covered by check on the line above.
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
size *= array_elements.value();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t RuntimeUniformDescription::GetGPUSize() const {
|
||||
size_t size = 0;
|
||||
if (padding_layout.empty()) {
|
||||
size = dimensions.rows * dimensions.cols * bit_width / 8u;
|
||||
} else {
|
||||
size = sizeof(float) * padding_layout.size();
|
||||
}
|
||||
if (array_elements.value_or(0) > 0) {
|
||||
// Covered by check on the line above.
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
size *= array_elements.value();
|
||||
}
|
||||
size += sizeof(float) * struct_layout.size();
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,11 @@ struct RuntimeUniformDimensions {
|
||||
size_t cols = 0;
|
||||
};
|
||||
|
||||
enum class RuntimePaddingType : uint8_t {
|
||||
kPadding = 0,
|
||||
kFloat = 1,
|
||||
};
|
||||
|
||||
struct RuntimeUniformDescription {
|
||||
std::string name;
|
||||
size_t location = 0u;
|
||||
@ -47,11 +52,16 @@ struct RuntimeUniformDescription {
|
||||
RuntimeUniformDimensions dimensions = {};
|
||||
size_t bit_width = 0u;
|
||||
std::optional<size_t> array_elements;
|
||||
std::vector<uint8_t> struct_layout = {};
|
||||
std::vector<RuntimePaddingType> padding_layout = {};
|
||||
size_t struct_float_count = 0u;
|
||||
|
||||
/// @brief Computes the total number of bytes that this uniform requires.
|
||||
size_t GetSize() const;
|
||||
/// @brief Computes the total number of bytes that this uniform requires for
|
||||
/// representation in the Dart float buffer.
|
||||
size_t GetDartSize() const;
|
||||
|
||||
/// @brief Computes the total number of bytes that this uniform requires for
|
||||
/// representation in the GPU.
|
||||
size_t GetGPUSize() const;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -38,7 +38,7 @@ size_t DlRuntimeEffectImpeller::uniform_size() const {
|
||||
|
||||
size_t total = 0;
|
||||
for (const auto& uniform : runtime_stage_->GetUniforms()) {
|
||||
total += uniform.GetSize();
|
||||
total += uniform.GetGPUSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
@ -24,35 +24,48 @@
|
||||
|
||||
namespace impeller {
|
||||
|
||||
namespace {
|
||||
constexpr char kPaddingType = 0;
|
||||
constexpr char kFloatType = 1;
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
BufferView RuntimeEffectContents::EmplaceVulkanUniform(
|
||||
const std::shared_ptr<const std::vector<uint8_t>>& input_data,
|
||||
BufferView RuntimeEffectContents::EmplaceUniform(
|
||||
const uint8_t* source_data,
|
||||
HostBuffer& data_host_buffer,
|
||||
const RuntimeUniformDescription& uniform,
|
||||
size_t minimum_uniform_alignment) {
|
||||
// TODO(jonahwilliams): rewrite this to emplace directly into
|
||||
// HostBuffer.
|
||||
std::vector<float> uniform_buffer;
|
||||
uniform_buffer.reserve(uniform.struct_layout.size());
|
||||
size_t uniform_byte_index = 0u;
|
||||
for (char byte_type : uniform.struct_layout) {
|
||||
if (byte_type == kPaddingType) {
|
||||
uniform_buffer.push_back(0.f);
|
||||
} else {
|
||||
FML_DCHECK(byte_type == kFloatType);
|
||||
uniform_buffer.push_back(reinterpret_cast<const float*>(
|
||||
input_data->data())[uniform_byte_index++]);
|
||||
}
|
||||
const RuntimeUniformDescription& uniform) {
|
||||
size_t minimum_uniform_alignment =
|
||||
data_host_buffer.GetMinimumUniformAlignment();
|
||||
size_t alignment = std::max(uniform.bit_width / 8, minimum_uniform_alignment);
|
||||
|
||||
if (uniform.padding_layout.empty()) {
|
||||
return data_host_buffer.Emplace(source_data, uniform.GetGPUSize(),
|
||||
alignment);
|
||||
}
|
||||
|
||||
// If the uniform has a padding layout, we need to repack the data.
|
||||
// We can do this by using the EmplaceProc to write directly to the
|
||||
// HostBuffer.
|
||||
return data_host_buffer.Emplace(
|
||||
reinterpret_cast<const void*>(uniform_buffer.data()),
|
||||
sizeof(float) * uniform_buffer.size(), minimum_uniform_alignment);
|
||||
uniform.GetGPUSize(), alignment,
|
||||
[&uniform, source_data](uint8_t* destination) {
|
||||
size_t count = uniform.array_elements.value_or(1);
|
||||
if (count == 0) {
|
||||
// Make sure to run at least once.
|
||||
count = 1;
|
||||
}
|
||||
size_t uniform_byte_index = 0u;
|
||||
size_t struct_float_index = 0u;
|
||||
auto* float_destination = reinterpret_cast<float*>(destination);
|
||||
auto* float_source = reinterpret_cast<const float*>(source_data);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
for (RuntimePaddingType byte_type : uniform.padding_layout) {
|
||||
if (byte_type == RuntimePaddingType::kPadding) {
|
||||
float_destination[struct_float_index++] = 0.f;
|
||||
} else {
|
||||
FML_DCHECK(byte_type == RuntimePaddingType::kFloat);
|
||||
float_destination[struct_float_index++] =
|
||||
float_source[uniform_byte_index++];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RuntimeEffectContents::SetRuntimeStage(
|
||||
@ -284,12 +297,8 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
|
||||
<< "Uniform " << uniform.name
|
||||
<< " had unexpected type kFloat for Vulkan backend.";
|
||||
|
||||
size_t alignment =
|
||||
std::max(uniform.bit_width / 8,
|
||||
data_host_buffer.GetMinimumUniformAlignment());
|
||||
BufferView buffer_view =
|
||||
data_host_buffer.Emplace(uniform_data_->data() + buffer_offset,
|
||||
uniform.GetSize(), alignment);
|
||||
BufferView buffer_view = EmplaceUniform(
|
||||
uniform_data_->data() + buffer_offset, data_host_buffer, uniform);
|
||||
|
||||
ShaderUniformSlot uniform_slot;
|
||||
uniform_slot.name = uniform.name.c_str();
|
||||
@ -298,7 +307,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
|
||||
DescriptorType::kUniformBuffer, uniform_slot,
|
||||
std::move(metadata), std::move(buffer_view));
|
||||
buffer_index++;
|
||||
buffer_offset += uniform.GetSize();
|
||||
buffer_offset += uniform.GetDartSize();
|
||||
buffer_location++;
|
||||
break;
|
||||
}
|
||||
@ -309,12 +318,10 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
|
||||
uniform_slot.binding = uniform.location;
|
||||
uniform_slot.name = uniform.name.c_str();
|
||||
|
||||
pass.BindResource(ShaderStage::kFragment,
|
||||
DescriptorType::kUniformBuffer, uniform_slot,
|
||||
nullptr,
|
||||
EmplaceVulkanUniform(
|
||||
uniform_data_, data_host_buffer, uniform,
|
||||
data_host_buffer.GetMinimumUniformAlignment()));
|
||||
pass.BindResource(
|
||||
ShaderStage::kFragment, DescriptorType::kUniformBuffer,
|
||||
uniform_slot, nullptr,
|
||||
EmplaceUniform(uniform_data_->data(), data_host_buffer, uniform));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,11 +37,17 @@ class RuntimeEffectContents final : public ColorSourceContents {
|
||||
bool BootstrapShader(const ContentContext& renderer) const;
|
||||
|
||||
// Visible for testing
|
||||
static BufferView EmplaceVulkanUniform(
|
||||
const std::shared_ptr<const std::vector<uint8_t>>& input_data,
|
||||
HostBuffer& host_buffer,
|
||||
const RuntimeUniformDescription& uniform,
|
||||
size_t minimum_uniform_alignment);
|
||||
/// Copies the uniform data into the host buffer.
|
||||
///
|
||||
/// If the `uniform` has a `padding_layout`, it is used to repack the data.
|
||||
///
|
||||
/// @param source_data The pointer to the start of the uniform data in the
|
||||
/// source.
|
||||
/// @param host_buffer The host buffer to emplace the uniform data into.
|
||||
/// @param uniform The description of the uniform being emplaced.
|
||||
static BufferView EmplaceUniform(const uint8_t* source_data,
|
||||
HostBuffer& host_buffer,
|
||||
const RuntimeUniformDescription& uniform);
|
||||
|
||||
private:
|
||||
bool RegisterShader(const ContentContext& renderer) const;
|
||||
|
||||
@ -1922,12 +1922,9 @@ TEST_P(EntityTest, RuntimeEffectSetsRightSizeWhenUniformIsStruct) {
|
||||
uniform_data->resize(sizeof(FragUniforms));
|
||||
memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
|
||||
|
||||
auto buffer_view = RuntimeEffectContents::EmplaceVulkanUniform(
|
||||
uniform_data, GetContentContext()->GetTransientsDataBuffer(),
|
||||
runtime_stage->GetUniforms()[0],
|
||||
GetContentContext()
|
||||
->GetTransientsDataBuffer()
|
||||
.GetMinimumUniformAlignment());
|
||||
auto buffer_view = RuntimeEffectContents::EmplaceUniform(
|
||||
uniform_data->data(), GetContentContext()->GetTransientsDataBuffer(),
|
||||
runtime_stage->GetUniforms()[0]);
|
||||
|
||||
// 16 bytes:
|
||||
// 8 bytes for iResolution
|
||||
|
||||
@ -81,9 +81,18 @@ absl::StatusOr<RuntimeStage> RuntimeStage::Create(
|
||||
static_cast<size_t>(i->rows()), static_cast<size_t>(i->columns())};
|
||||
desc.bit_width = i->bit_width();
|
||||
desc.array_elements = i->array_elements();
|
||||
if (i->struct_layout()) {
|
||||
for (const auto& byte_type : *i->struct_layout()) {
|
||||
desc.struct_layout.push_back(static_cast<uint8_t>(byte_type));
|
||||
if (i->padding_layout()) {
|
||||
for (const auto& byte_type : *i->padding_layout()) {
|
||||
impeller::RuntimePaddingType type;
|
||||
switch (byte_type) {
|
||||
case fb::PaddingType::kPadding:
|
||||
type = impeller::RuntimePaddingType::kPadding;
|
||||
break;
|
||||
case fb::PaddingType::kFloat:
|
||||
type = impeller::RuntimePaddingType::kFloat;
|
||||
break;
|
||||
}
|
||||
desc.padding_layout.push_back(type);
|
||||
}
|
||||
}
|
||||
desc.struct_float_count = i->struct_float_count();
|
||||
|
||||
@ -8,7 +8,7 @@ namespace impeller.fb;
|
||||
// incremented any time there is a change to this file which changes the
|
||||
// resulting flat buffer format.
|
||||
enum RuntimeStagesFormatVersion:uint32 {
|
||||
kVersion = 1
|
||||
kVersion = 2
|
||||
}
|
||||
|
||||
enum Stage:byte {
|
||||
@ -29,7 +29,7 @@ enum UniformDataType:uint32 {
|
||||
// A struct is made up solely of 4 byte floats and 4-byte paddings between
|
||||
// them.
|
||||
// This enum describes whether a particular byte is a float or padding.
|
||||
enum StructByteType:uint8 {
|
||||
enum PaddingType:uint8 {
|
||||
kPadding = 0,
|
||||
kFloat = 1,
|
||||
}
|
||||
@ -43,7 +43,7 @@ table UniformDescription {
|
||||
rows: uint64;
|
||||
columns: uint64;
|
||||
array_elements: uint64;
|
||||
struct_layout: [StructByteType];
|
||||
padding_layout: [PaddingType];
|
||||
struct_float_count: uint64;
|
||||
}
|
||||
|
||||
|
||||
@ -94,6 +94,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 0u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_alpha");
|
||||
@ -102,6 +103,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 1u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_sparkle_color");
|
||||
@ -110,6 +112,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 2u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_sparkle_alpha");
|
||||
@ -118,6 +121,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 3u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_blur");
|
||||
@ -126,6 +130,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 4u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_radius_scale");
|
||||
@ -134,6 +139,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 6u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_max_radius");
|
||||
@ -142,6 +148,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 7u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_resolution_scale");
|
||||
@ -150,6 +157,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 8u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_noise_scale");
|
||||
@ -158,6 +166,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 9u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_noise_phase");
|
||||
@ -166,6 +175,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 10u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
|
||||
{
|
||||
@ -175,6 +185,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 11u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_circle2");
|
||||
@ -183,6 +194,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 12u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_circle3");
|
||||
@ -191,6 +203,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 13u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_rotation1");
|
||||
@ -199,6 +212,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 14u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_rotation2");
|
||||
@ -207,6 +221,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 15u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
{
|
||||
auto uni = stage->GetUniform("u_rotation3");
|
||||
@ -215,6 +230,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
EXPECT_EQ(uni->dimensions.cols, 1u);
|
||||
EXPECT_EQ(uni->location, 16u);
|
||||
EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
|
||||
EXPECT_TRUE(uni->padding_layout.empty());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -234,14 +250,15 @@ TEST_P(RuntimeStageTest, CanReadUniforms) {
|
||||
// introduced.
|
||||
// This means 36 * 4 = 144 bytes total.
|
||||
|
||||
EXPECT_EQ(uni->GetSize(), 144u);
|
||||
std::vector<uint8_t> layout(uni->GetSize() / sizeof(float), 1);
|
||||
layout[5] = 0;
|
||||
layout[6] = 0;
|
||||
layout[7] = 0;
|
||||
layout[23] = 0;
|
||||
EXPECT_EQ(uni->GetGPUSize(), 144u);
|
||||
std::vector<RuntimePaddingType> layout(uni->GetGPUSize() / sizeof(float),
|
||||
RuntimePaddingType::kFloat);
|
||||
layout[5] = RuntimePaddingType::kPadding;
|
||||
layout[6] = RuntimePaddingType::kPadding;
|
||||
layout[7] = RuntimePaddingType::kPadding;
|
||||
layout[23] = RuntimePaddingType::kPadding;
|
||||
|
||||
EXPECT_THAT(uni->struct_layout, ::testing::ElementsAreArray(layout));
|
||||
EXPECT_THAT(uni->padding_layout, ::testing::ElementsAreArray(layout));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +82,8 @@ Dart_Handle ConvertUniformDescriptionToMap(
|
||||
FML_DCHECK(!Dart_IsError(result));
|
||||
}
|
||||
{ // 2
|
||||
Dart_Handle size = Dart_NewIntegerFromUint64(uniform_description.GetSize());
|
||||
Dart_Handle size =
|
||||
Dart_NewIntegerFromUint64(uniform_description.GetDartSize());
|
||||
FML_DCHECK(!Dart_IsError(size));
|
||||
[[maybe_unused]] Dart_Handle result =
|
||||
Dart_ListSetAt(keys, 2, Dart_NewStringFromCString("size"));
|
||||
@ -161,7 +162,7 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) {
|
||||
impeller::RuntimeUniformType::kSampledImage) {
|
||||
sampled_image_count++;
|
||||
} else {
|
||||
other_uniforms_bytes += uniform_description.GetSize();
|
||||
other_uniforms_bytes += uniform_description.GetDartSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ void main() async {
|
||||
);
|
||||
});
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformFloat offset overflow', () async {
|
||||
test('FragmentProgram getUniformFloat offset overflow', () async {
|
||||
expect(
|
||||
() => shader.getUniformFloat('iVec2Uniform', 2),
|
||||
throwsA(
|
||||
@ -101,9 +101,9 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformFloat offset underflow', () async {
|
||||
test('FragmentProgram getUniformFloat offset underflow', () async {
|
||||
expect(
|
||||
() => shader.getUniformFloat('iVec2Uniform', -1),
|
||||
throwsA(
|
||||
@ -114,14 +114,14 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformVec2', () async {
|
||||
test('FragmentProgram getUniformVec2', () async {
|
||||
final UniformVec2Slot slot = shader.getUniformVec2('iVec2Uniform');
|
||||
slot.set(6.0, 7.0);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformVec2 wrong size', () async {
|
||||
test('FragmentProgram getUniformVec2 wrong size', () async {
|
||||
expect(
|
||||
() => shader.getUniformVec2('iVec3Uniform'),
|
||||
throwsA(
|
||||
@ -142,14 +142,14 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformVec3', () async {
|
||||
test('FragmentProgram getUniformVec3', () async {
|
||||
final UniformVec3Slot slot = shader.getUniformVec3('iVec3Uniform');
|
||||
slot.set(0.8, 0.1, 0.3);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformVec3 wrong size', () async {
|
||||
test('FragmentProgram getUniformVec3 wrong size', () async {
|
||||
expect(
|
||||
() => shader.getUniformVec3('iVec2Uniform'),
|
||||
throwsA(
|
||||
@ -170,14 +170,14 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformVec4', () async {
|
||||
test('FragmentProgram getUniformVec4', () async {
|
||||
final UniformVec4Slot slot = shader.getUniformVec4('iVec4Uniform');
|
||||
slot.set(11.0, 22.0, 19.0, 96.0);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformVec4 wrong size', () async {
|
||||
test('FragmentProgram getUniformVec4 wrong size', () async {
|
||||
expect(
|
||||
() => shader.getUniformVec4('iVec3Uniform'),
|
||||
throwsA(
|
||||
@ -188,18 +188,18 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformArray float', () async {
|
||||
test('FragmentProgram getUniformArray float', () async {
|
||||
final UniformArray<UniformFloatSlot> slots = shader.getUniformFloatArray(
|
||||
'iFloatArrayUniform',
|
||||
);
|
||||
expect(slots.length, 10);
|
||||
slots[0].set(1.0);
|
||||
slots[1].set(1.0);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformArray not found', () async {
|
||||
test('FragmentProgram getUniformArray not found', () async {
|
||||
expect(
|
||||
() => shader.getUniformFloatArray('unknown'),
|
||||
throwsA(
|
||||
@ -210,15 +210,15 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformArrayVec2', () async {
|
||||
test('FragmentProgram getUniformArrayVec2', () async {
|
||||
final UniformArray<UniformVec2Slot> slots = shader.getUniformVec2Array('iVec2ArrayUniform');
|
||||
expect(slots.length, 3);
|
||||
slots[0].set(1.0, 1.0);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformArrayVec2 wrong type', () async {
|
||||
test('FragmentProgram getUniformArrayVec2 wrong type', () async {
|
||||
expect(
|
||||
() => shader.getUniformVec2Array('iVec3ArrayUniform'),
|
||||
throwsA(
|
||||
@ -229,15 +229,15 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformArrayVec3', () async {
|
||||
test('FragmentProgram getUniformArrayVec3', () async {
|
||||
final UniformArray<UniformVec3Slot> slots = shader.getUniformVec3Array('iVec3ArrayUniform');
|
||||
expect(slots.length, 3);
|
||||
slots[0].set(1.0, 1.0, 1.0);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformArrayVec3 wrong type', () async {
|
||||
test('FragmentProgram getUniformArrayVec3 wrong type', () async {
|
||||
expect(
|
||||
() => shader.getUniformVec3Array('iFloatArrayUniform'),
|
||||
throwsA(
|
||||
@ -248,15 +248,15 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformArrayVec4', () async {
|
||||
test('FragmentProgram getUniformArrayVec4', () async {
|
||||
final UniformArray<UniformVec4Slot> slots = shader.getUniformVec4Array('iVec4ArrayUniform');
|
||||
expect(slots.length, 3);
|
||||
slots[0].set(1.0, 1.0, 1.0, 1.0);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
|
||||
_runSkiaTest('FragmentProgram getUniformArrayVec4 wrong type', () async {
|
||||
test('FragmentProgram getUniformArrayVec4 wrong type', () async {
|
||||
expect(
|
||||
() => shader.getUniformVec4Array('iFloatArrayUniform'),
|
||||
throwsA(
|
||||
@ -267,7 +267,7 @@ void main() async {
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}, skip: !_isMacMetal());
|
||||
});
|
||||
|
||||
test('FragmentProgram getImageSampler', () async {
|
||||
@ -695,39 +695,31 @@ void main() async {
|
||||
shader.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'FragmentShader shader with array uniforms renders correctly',
|
||||
() async {
|
||||
final FragmentProgram program = await FragmentProgram.fromAsset('uniform_arrays.frag.iplr');
|
||||
test('FragmentShader shader with array uniforms renders correctly', () async {
|
||||
final FragmentProgram program = await FragmentProgram.fromAsset('uniform_arrays.frag.iplr');
|
||||
|
||||
final FragmentShader shader = program.fragmentShader();
|
||||
for (var i = 0; i < 20; i++) {
|
||||
shader.setFloat(i, i.toDouble());
|
||||
}
|
||||
final FragmentShader shader = program.fragmentShader();
|
||||
for (var i = 0; i < 20; i++) {
|
||||
shader.setFloat(i, i.toDouble());
|
||||
}
|
||||
|
||||
await _expectShaderRendersGreen(shader);
|
||||
shader.dispose();
|
||||
},
|
||||
skip: Platform.executableArguments.contains('--impeller-backend=metal'),
|
||||
);
|
||||
await _expectShaderRendersGreen(shader);
|
||||
shader.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'FragmentShader shader with mat2 uniform renders correctly',
|
||||
() async {
|
||||
final FragmentProgram program = await FragmentProgram.fromAsset('uniform_mat2.frag.iplr');
|
||||
test('FragmentShader shader with mat2 uniform renders correctly', () async {
|
||||
final FragmentProgram program = await FragmentProgram.fromAsset('uniform_mat2.frag.iplr');
|
||||
|
||||
final FragmentShader shader = program.fragmentShader();
|
||||
final FragmentShader shader = program.fragmentShader();
|
||||
|
||||
shader.setFloat(0, 4.0); // m00
|
||||
shader.setFloat(1, 8.0); // m01
|
||||
shader.setFloat(2, 16.0); // m10
|
||||
shader.setFloat(3, 32.0); // m11
|
||||
shader.setFloat(0, 4.0); // m00
|
||||
shader.setFloat(1, 8.0); // m01
|
||||
shader.setFloat(2, 16.0); // m10
|
||||
shader.setFloat(3, 32.0); // m11
|
||||
|
||||
await _expectShaderRendersGreen(shader);
|
||||
shader.dispose();
|
||||
},
|
||||
skip: Platform.executableArguments.contains('--impeller-backend=metal'),
|
||||
);
|
||||
await _expectShaderRendersGreen(shader);
|
||||
shader.dispose();
|
||||
});
|
||||
|
||||
_runImpellerTest(
|
||||
'ImageFilter.shader errors if shader does not have correct uniform layout',
|
||||
@ -878,6 +870,11 @@ void _runImpellerTest(String name, Future<void> Function() callback, {Object? sk
|
||||
}, skip: skip);
|
||||
}
|
||||
|
||||
// TODO(walley892): remove this function and associated test skips, https://github.com/flutter/flutter/issues/181562
|
||||
bool _isMacMetal() {
|
||||
return Platform.isMacOS && Platform.executableArguments.contains('--impeller-backend=metal');
|
||||
}
|
||||
|
||||
// Expect that all of the shaders in this folder render green.
|
||||
// Keeping the outer loop of the test synchronous allows for easy printing
|
||||
// of the file name within the test case.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user