Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit ade9f18

Browse files
authored
[Impeller] Vulkan runtime effects/fragment program API (#49543)
Fixes flutter/flutter#122823 Fixes flutter/flutter#129659 Fixes flutter/flutter#123741 This patch makes runtime stage/fragment program stuff work on Vulkan for Android. It will need flutter/flutter#140976 for that to become a reality for flutter_tools users. Compiling with relaxed Vulkan semantics still has an issue: shaders that use `sampler2D` with an explicitly set `location` on the `layout` will fail to compile with an error documented in flutter/flutter#141219. I think there might still be some issues with fragment programs on Vulkan, but this should at least be a good starting point and unblocks ink_sparkle.frag usage in the framework. I've deleted some runtime_stage related code that would never get used - for example, enum related code that indicates we might support a bunch of data types that we do not and probably never will support in this API.
1 parent 922d619 commit ade9f18

33 files changed

+602
-392
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2704,10 +2704,6 @@ TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) {
27042704

27052705
// Regression test for https://github.com/flutter/flutter/issues/126701 .
27062706
TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
2707-
if (!BackendSupportsFragmentProgram()) {
2708-
GTEST_SKIP_("This backend doesn't support runtime effects.");
2709-
}
2710-
27112707
auto runtime_stages =
27122708
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
27132709

@@ -2741,10 +2737,6 @@ TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
27412737
}
27422738

27432739
TEST_P(AiksTest, DrawPaintTransformsBounds) {
2744-
if (!BackendSupportsFragmentProgram()) {
2745-
GTEST_SKIP_("This backend doesn't support runtime effects.");
2746-
}
2747-
27482740
auto runtime_stages = OpenAssetAsRuntimeStage("gradient.frag.iplr");
27492741
auto runtime_stage = runtime_stages[RuntimeStageBackend::kMetal];
27502742
ASSERT_TRUE(runtime_stage);

impeller/compiler/compiler.cc

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "impeller/compiler/includer.h"
2020
#include "impeller/compiler/logger.h"
2121
#include "impeller/compiler/spirv_compiler.h"
22+
#include "impeller/compiler/types.h"
2223
#include "impeller/compiler/uniform_sorter.h"
2324
#include "impeller/compiler/utilities.h"
2425

@@ -122,15 +123,13 @@ static CompilerBackend CreateMSLCompiler(
122123
static CompilerBackend CreateVulkanCompiler(
123124
const spirv_cross::ParsedIR& ir,
124125
const SourceOptions& source_options) {
125-
// TODO(dnfield): It seems like what we'd want is a CompilerGLSL with
126-
// vulkan_semantics set to true, but that has regressed some things on GLES
127-
// somehow. In the mean time, go back to using CompilerMSL, but set the Metal
128-
// Language version to something really high so that we don't get weird
129-
// complaints about using Metal features while trying to build Vulkan shaders.
130-
// https://github.com/flutter/flutter/issues/123795
131-
return CreateMSLCompiler(
132-
ir, source_options,
133-
spirv_cross::CompilerMSL::Options::make_msl_version(3, 0, 0));
126+
auto gl_compiler = std::make_shared<spirv_cross::CompilerGLSL>(ir);
127+
spirv_cross::CompilerGLSL::Options sl_options;
128+
sl_options.force_zero_initialized_variables = true;
129+
sl_options.vertex.fixup_clipspace = true;
130+
sl_options.vulkan_semantics = true;
131+
gl_compiler->set_common_options(sl_options);
132+
return CompilerBackend(gl_compiler);
134133
}
135134

136135
static CompilerBackend CreateGLSLCompiler(const spirv_cross::ParsedIR& ir,
@@ -302,18 +301,23 @@ Compiler::Compiler(const std::shared_ptr<const fml::Mapping>& source_mapping,
302301
} break;
303302
case TargetPlatform::kOpenGLES:
304303
case TargetPlatform::kOpenGLDesktop:
305-
case TargetPlatform::kVulkan: {
304+
case TargetPlatform::kVulkan:
305+
case TargetPlatform::kRuntimeStageVulkan: {
306306
SPIRVCompilerTargetEnv target;
307307

308308
target.env = shaderc_target_env::shaderc_target_env_vulkan;
309309
target.version = shaderc_env_version::shaderc_env_version_vulkan_1_1;
310310
target.spirv_version = shaderc_spirv_version::shaderc_spirv_version_1_3;
311311

312+
if (source_options.target_platform ==
313+
TargetPlatform::kRuntimeStageVulkan) {
314+
spirv_options.macro_definitions.push_back("IMPELLER_GRAPHICS_BACKEND");
315+
spirv_options.relaxed_vulkan_rules = true;
316+
}
312317
spirv_options.target = target;
313318
} break;
314319
case TargetPlatform::kRuntimeStageMetal:
315-
case TargetPlatform::kRuntimeStageGLES:
316-
case TargetPlatform::kRuntimeStageVulkan: {
320+
case TargetPlatform::kRuntimeStageGLES: {
317321
SPIRVCompilerTargetEnv target;
318322

319323
target.env = shaderc_target_env::shaderc_target_env_opengl;
@@ -398,7 +402,8 @@ Compiler::Compiler(const std::shared_ptr<const fml::Mapping>& source_mapping,
398402
// If the target is Vulkan, our shading language is SPIRV which we already
399403
// have. We just need to strip it of debug information. If it isn't, we need
400404
// to invoke the appropriate compiler to compile the SPIRV to the target SL.
401-
if (source_options.target_platform == TargetPlatform::kVulkan) {
405+
if (source_options.target_platform == TargetPlatform::kVulkan ||
406+
source_options.target_platform == TargetPlatform::kRuntimeStageVulkan) {
402407
auto stripped_spirv_options = spirv_options;
403408
stripped_spirv_options.generate_debug_info = false;
404409
sl_mapping_ = spv_compiler.CompileToSPV(

impeller/compiler/compiler_backend.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ CompilerBackend::CompilerBackend(MSLCompiler compiler)
1313
: CompilerBackend(Type::kMSL, compiler) {}
1414

1515
CompilerBackend::CompilerBackend(GLSLCompiler compiler)
16-
: CompilerBackend(Type::kGLSL, compiler) {}
16+
: CompilerBackend(compiler->get_common_options().vulkan_semantics
17+
? Type::kGLSLVulkan
18+
: Type::kGLSL,
19+
compiler) {}
1720

1821
CompilerBackend::CompilerBackend(SkSLCompiler compiler)
1922
: CompilerBackend(Type::kSkSL, compiler) {}

impeller/compiler/compiler_backend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct CompilerBackend {
2727
enum class Type {
2828
kMSL,
2929
kGLSL,
30+
kGLSLVulkan,
3031
kSkSL,
3132
};
3233

impeller/compiler/reflector.cc

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <sstream>
1313

1414
#include "flutter/fml/logging.h"
15+
#include "fml/backtrace.h"
1516
#include "impeller/base/strings.h"
1617
#include "impeller/base/validation.h"
1718
#include "impeller/compiler/code_gen_template.h"
@@ -23,54 +24,12 @@
2324
#include "impeller/geometry/half.h"
2425
#include "impeller/geometry/matrix.h"
2526
#include "impeller/geometry/scalar.h"
27+
#include "impeller/runtime_stage/runtime_stage.h"
28+
#include "spirv_common.hpp"
2629

2730
namespace impeller {
2831
namespace compiler {
2932

30-
static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) {
31-
using Type = spirv_cross::SPIRType::BaseType;
32-
switch (type) {
33-
case Type::Void:
34-
return "ShaderType::kVoid";
35-
case Type::Boolean:
36-
return "ShaderType::kBoolean";
37-
case Type::SByte:
38-
return "ShaderType::kSignedByte";
39-
case Type::UByte:
40-
return "ShaderType::kUnsignedByte";
41-
case Type::Short:
42-
return "ShaderType::kSignedShort";
43-
case Type::UShort:
44-
return "ShaderType::kUnsignedShort";
45-
case Type::Int:
46-
return "ShaderType::kSignedInt";
47-
case Type::UInt:
48-
return "ShaderType::kUnsignedInt";
49-
case Type::Int64:
50-
return "ShaderType::kSignedInt64";
51-
case Type::UInt64:
52-
return "ShaderType::kUnsignedInt64";
53-
case Type::AtomicCounter:
54-
return "ShaderType::kAtomicCounter";
55-
case Type::Half:
56-
return "ShaderType::kHalfFloat";
57-
case Type::Float:
58-
return "ShaderType::kFloat";
59-
case Type::Double:
60-
return "ShaderType::kDouble";
61-
case Type::Struct:
62-
return "ShaderType::kStruct";
63-
case Type::Image:
64-
return "ShaderType::kImage";
65-
case Type::SampledImage:
66-
return "ShaderType::kSampledImage";
67-
case Type::Sampler:
68-
return "ShaderType::kSampler";
69-
default:
70-
return "ShaderType::kUnknown";
71-
}
72-
}
73-
7433
static std::string ExecutionModelToString(spv::ExecutionModel model) {
7534
switch (model) {
7635
case spv::ExecutionModel::ExecutionModelVertex:
@@ -370,7 +329,6 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
370329
// Sort the IR so that the uniforms are in declaration order.
371330
std::vector<spirv_cross::ID> uniforms =
372331
SortUniforms(ir_.get(), compiler_.GetCompiler());
373-
374332
for (auto& sorted_id : uniforms) {
375333
auto var = ir_->ids[sorted_id].get<spirv_cross::SPIRVariable>();
376334
const auto spir_type = compiler_->get_type(var.basetype);
@@ -383,9 +341,69 @@ std::shared_ptr<RuntimeStageData::Shader> Reflector::GenerateRuntimeStageData()
383341
uniform_description.columns = spir_type.columns;
384342
uniform_description.bit_width = spir_type.width;
385343
uniform_description.array_elements = GetArrayElements(spir_type);
344+
FML_CHECK(data->backend != RuntimeStageBackend::kVulkan ||
345+
spir_type.basetype ==
346+
spirv_cross::SPIRType::BaseType::SampledImage)
347+
<< "Vulkan runtime effect had unexpected uniforms outside of the "
348+
"uniform buffer object.";
386349
data->uniforms.emplace_back(std::move(uniform_description));
387350
}
388351

352+
const auto ubos = compiler_->get_shader_resources().uniform_buffers;
353+
if (data->backend == RuntimeStageBackend::kVulkan && !ubos.empty()) {
354+
if (ubos.size() != 1 && ubos[0].name != RuntimeStage::kVulkanUBOName) {
355+
VALIDATION_LOG << "Expected a single UBO resource named "
356+
"'"
357+
<< RuntimeStage::kVulkanUBOName
358+
<< "' "
359+
"for Vulkan runtime stage backend.";
360+
return nullptr;
361+
}
362+
363+
const auto& ubo = ubos[0];
364+
365+
auto members = ReadStructMembers(ubo.type_id);
366+
std::vector<uint8_t> struct_layout;
367+
size_t float_count = 0;
368+
369+
for (size_t i = 0; i < members.size(); i += 1) {
370+
const auto& member = members[i];
371+
std::vector<int> bytes;
372+
switch (member.underlying_type) {
373+
case StructMember::UnderlyingType::kPadding: {
374+
size_t padding_count =
375+
(member.size + sizeof(float) - 1) / sizeof(float);
376+
while (padding_count > 0) {
377+
struct_layout.push_back(0);
378+
padding_count--;
379+
}
380+
break;
381+
}
382+
case StructMember::UnderlyingType::kFloat: {
383+
size_t member_float_count = member.byte_length / sizeof(float);
384+
float_count += member_float_count;
385+
while (member_float_count > 0) {
386+
struct_layout.push_back(1);
387+
member_float_count--;
388+
}
389+
break;
390+
}
391+
case StructMember::UnderlyingType::kOther:
392+
VALIDATION_LOG << "Non-floating-type struct member " << member.name
393+
<< " is not supported.";
394+
return nullptr;
395+
}
396+
}
397+
data->uniforms.emplace_back(UniformDescription{
398+
.name = ubo.name,
399+
.location = 64, // Magic constant that must match the descriptor set
400+
// location for fragment programs.
401+
.type = spirv_cross::SPIRType::Struct,
402+
.struct_layout = std::move(struct_layout),
403+
.struct_float_count = float_count,
404+
});
405+
}
406+
389407
// We only need to worry about storing vertex attributes.
390408
if (entrypoints.front().execution_model == spv::ExecutionModelVertex) {
391409
const auto inputs = compiler_->get_shader_resources().stage_inputs;
@@ -532,6 +550,8 @@ static std::string ToString(CompilerBackend::Type type) {
532550
return "Metal Shading Language";
533551
case CompilerBackend::Type::kGLSL:
534552
return "OpenGL Shading Language";
553+
case CompilerBackend::Type::kGLSLVulkan:
554+
return "OpenGL Shading Language (Relaxed Vulkan Semantics)";
535555
case CompilerBackend::Type::kSkSL:
536556
return "SkSL Shading Language";
537557
}
@@ -627,7 +647,7 @@ std::optional<nlohmann::json::object_t> Reflector::ReflectType(
627647

628648
const auto type = compiler_->get_type(type_id);
629649

630-
result["type_name"] = BaseTypeToString(type.basetype);
650+
result["type_name"] = StructMember::BaseTypeToString(type.basetype);
631651
result["bit_width"] = type.width;
632652
result["vec_size"] = type.vecsize;
633653
result["columns"] = type.columns;
@@ -637,7 +657,8 @@ std::optional<nlohmann::json::object_t> Reflector::ReflectType(
637657
auto member = nlohmann::json::object_t{};
638658
member["name"] = struct_member.name;
639659
member["type"] = struct_member.type;
640-
member["base_type"] = BaseTypeToString(struct_member.base_type);
660+
member["base_type"] =
661+
StructMember::BaseTypeToString(struct_member.base_type);
641662
member["offset"] = struct_member.offset;
642663
member["size"] = struct_member.size;
643664
member["byte_length"] = struct_member.byte_length;
@@ -1117,7 +1138,8 @@ nlohmann::json::object_t Reflector::EmitStructDefinition(
11171138
auto& member = members.emplace_back(nlohmann::json::object_t{});
11181139
member["name"] = struct_member.name;
11191140
member["type"] = struct_member.type;
1120-
member["base_type"] = BaseTypeToString(struct_member.base_type);
1141+
member["base_type"] =
1142+
StructMember::BaseTypeToString(struct_member.base_type);
11211143
member["offset"] = struct_member.offset;
11221144
member["byte_length"] = struct_member.byte_length;
11231145
if (struct_member.array_elements.has_value()) {
@@ -1142,7 +1164,7 @@ static VertexType VertexTypeFromInputResource(
11421164
const spirv_cross::Resource* resource) {
11431165
VertexType result;
11441166
result.variable_name = resource->name;
1145-
const auto type = compiler.get_type(resource->type_id);
1167+
const auto& type = compiler.get_type(resource->type_id);
11461168
result.base_type = type.basetype;
11471169
const auto total_size = type.columns * type.vecsize * type.width / 8u;
11481170
result.byte_length = total_size;

0 commit comments

Comments
 (0)