blob: 3004dce5715c74e8107e144fe028b3c2f10b1220 [file] [log] [blame]
// 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.
#include <cstring>
#include "flutter/testing/testing.h"
#include "gtest/gtest.h"
#include "impeller/base/validation.h"
#include "impeller/compiler/compiler.h"
#include "impeller/compiler/compiler_test.h"
#include "impeller/compiler/source_options.h"
#include "impeller/compiler/types.h"
namespace impeller {
namespace compiler {
namespace testing {
TEST(CompilerTest, Defines) {
std::shared_ptr<const fml::Mapping> fixture =
flutter::testing::OpenFixtureAsMapping("check_gles_definition.frag");
SourceOptions options;
options.source_language = SourceLanguage::kGLSL;
options.target_platform = TargetPlatform::kRuntimeStageGLES;
options.entry_point_name = "main";
options.type = SourceType::kFragmentShader;
Reflector::Options reflector_options;
reflector_options.target_platform = TargetPlatform::kRuntimeStageGLES;
Compiler compiler = Compiler(fixture, options, reflector_options);
// Should fail as the shader has a compilation error in it.
EXPECT_EQ(compiler.GetSPIRVAssembly(), nullptr);
// Should succeed as the compilation error is ifdef'd out.
options.target_platform = TargetPlatform::kRuntimeStageVulkan;
reflector_options.target_platform = TargetPlatform::kRuntimeStageVulkan;
Compiler compiler_2 = Compiler(fixture, options, reflector_options);
EXPECT_NE(compiler_2.GetSPIRVAssembly(), nullptr);
}
TEST(CompilerTest, ShaderKindMatchingIsSuccessful) {
ASSERT_EQ(SourceTypeFromFileName("hello.vert"), SourceType::kVertexShader);
ASSERT_EQ(SourceTypeFromFileName("hello.frag"), SourceType::kFragmentShader);
ASSERT_EQ(SourceTypeFromFileName("hello.comp"), SourceType::kComputeShader);
ASSERT_EQ(SourceTypeFromFileName("hello.msl"), SourceType::kUnknown);
ASSERT_EQ(SourceTypeFromFileName("hello.glsl"), SourceType::kUnknown);
}
TEST_P(CompilerTest, CanCompile) {
if (GetParam() == TargetPlatform::kSkSL) {
GTEST_SKIP() << "Not supported with SkSL";
}
ASSERT_TRUE(CanCompileAndReflect("sample.vert"));
ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader));
ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader,
SourceLanguage::kGLSL));
}
TEST_P(CompilerTest, CanCompileHLSL) {
if (GetParam() == TargetPlatform::kSkSL) {
GTEST_SKIP() << "Not supported with SkSL";
}
ASSERT_TRUE(CanCompileAndReflect(
"simple.vert.hlsl", SourceType::kVertexShader, SourceLanguage::kHLSL));
}
TEST_P(CompilerTest, CanCompileHLSLWithMultipleStages) {
if (GetParam() == TargetPlatform::kSkSL) {
GTEST_SKIP() << "Not supported with SkSL";
}
ASSERT_TRUE(CanCompileAndReflect("multiple_stages.hlsl",
SourceType::kVertexShader,
SourceLanguage::kHLSL, "VertexShader"));
ASSERT_TRUE(CanCompileAndReflect("multiple_stages.hlsl",
SourceType::kFragmentShader,
SourceLanguage::kHLSL, "FragmentShader"));
}
TEST_P(CompilerTest, CanCompileComputeShader) {
if (!TargetPlatformIsMetal(GetParam())) {
GTEST_SKIP()
<< "Only enabled on Metal backends till ES 3.2 support is added.";
}
ASSERT_TRUE(CanCompileAndReflect("sample.comp"));
ASSERT_TRUE(CanCompileAndReflect("sample.comp", SourceType::kComputeShader));
}
TEST_P(CompilerTest, MustFailDueToExceedingResourcesLimit) {
if (GetParam() == TargetPlatform::kSkSL) {
GTEST_SKIP() << "Not supported with SkSL";
}
ScopedValidationDisable disable_validation;
ASSERT_FALSE(
CanCompileAndReflect("resources_limit.vert", SourceType::kVertexShader));
}
TEST_P(CompilerTest, MustFailDueToMultipleLocationPerStructMember) {
if (GetParam() == TargetPlatform::kSkSL) {
GTEST_SKIP() << "Not supported with SkSL";
}
ScopedValidationDisable disable_validation;
ASSERT_FALSE(CanCompileAndReflect("struct_def_bug.vert"));
}
TEST_P(CompilerTest, BindingBaseForFragShader) {
if (!TargetPlatformIsVulkan(GetParam())) {
GTEST_SKIP();
}
ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader));
ASSERT_TRUE(CanCompileAndReflect("sample.frag", SourceType::kFragmentShader));
auto get_binding = [&](const char* fixture) -> uint32_t {
auto json_fd = GetReflectionJson(fixture);
nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
return shader_json["buffers"][0]["binding"].get<uint32_t>();
};
auto vert_uniform_binding = get_binding("sample.vert");
auto frag_uniform_binding = get_binding("sample.frag");
ASSERT_GT(frag_uniform_binding, vert_uniform_binding);
}
namespace {
struct UniformInfo {
std::string uniform_name;
uint32_t location;
std::string type_name;
uint32_t columns;
uint32_t vec_size;
static UniformInfo fromJson(const nlohmann::json& json) {
return {
.uniform_name = json["name"].get<std::string>(),
.location = json["location"].get<uint32_t>(),
.type_name = json["type"]["type_name"].get<std::string>(),
.columns = json["type"]["columns"].get<uint32_t>(),
.vec_size = json["type"]["vec_size"].get<uint32_t>(),
};
}
static UniformInfo Sampler(const std::string& name, uint32_t location) {
return UniformInfo{
.uniform_name = name,
.location = location,
.type_name = "ShaderType::kSampledImage",
.columns = 1u,
.vec_size = 1u,
};
}
static UniformInfo Float(const std::string& name, uint32_t location) {
return FloatInfo(name, location, 1u, 1u);
}
static UniformInfo Vec2(const std::string& name, uint32_t location) {
return FloatInfo(name, location, 1u, 2u);
}
static UniformInfo Vec3(const std::string& name, uint32_t location) {
return FloatInfo(name, location, 1u, 3u);
}
static UniformInfo Vec4(const std::string& name, uint32_t location) {
return FloatInfo(name, location, 1u, 4u);
}
static UniformInfo Mat4(const std::string& name, uint32_t location) {
return FloatInfo(name, location, 4u, 4u);
}
constexpr bool operator==(const UniformInfo& other) const {
return (uniform_name == other.uniform_name && //
location == other.location && //
type_name == other.type_name && //
columns == other.columns && //
vec_size == other.vec_size);
}
private:
static UniformInfo FloatInfo(const std::string& name,
uint32_t location,
uint32_t columns,
uint32_t vec_size) {
return UniformInfo{
.uniform_name = name,
.location = location,
.type_name = "ShaderType::kFloat",
.columns = columns,
.vec_size = vec_size,
};
}
};
inline std::ostream& operator<<(std::ostream& out, const UniformInfo& info) {
out << "UniformInfo {" << std::endl
<< " uniform_name: " << info.uniform_name << std::endl
<< " location: " << info.location << std::endl
<< " type_name: " << info.type_name << std::endl
<< " columns: " << info.columns << std::endl
<< " vec_size: " << info.vec_size << std::endl
<< "}";
return out;
}
} // namespace
TEST_P(CompilerTestRuntime, UniformsAppearInJson) {
ASSERT_TRUE(CanCompileAndReflect("sample_with_uniforms.frag",
SourceType::kFragmentShader,
SourceLanguage::kGLSL));
auto json_fd = GetReflectionJson("sample_with_uniforms.frag");
ASSERT_TRUE(json_fd);
nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
auto sampler_list = shader_json["sampled_images"];
auto float_list = shader_json["uniforms"];
ASSERT_EQ(sampler_list.size(), 2u);
ASSERT_EQ(float_list.size(), 6u);
{
// clang-format off
std::array expected_infos = {
UniformInfo::Sampler("uFirstSampler", 1u),
UniformInfo::Sampler("uSampler", 7u),
};
// clang-format on
ASSERT_EQ(sampler_list.size(), expected_infos.size());
for (size_t i = 0; i < expected_infos.size(); i++) {
EXPECT_EQ(UniformInfo::fromJson(sampler_list[i]), expected_infos[i])
<< "index: " << i;
}
}
{
// clang-format off
std::array expected_infos = {
UniformInfo::Float("uFirstFloat", 0u),
UniformInfo::Float("uFloat", 2u),
UniformInfo::Vec2("uVec2", 3u),
UniformInfo::Vec3("uVec3", 4u),
UniformInfo::Vec4("uVec4", 5u),
UniformInfo::Mat4("uMat4", 6u),
};
// clang-format on
ASSERT_EQ(float_list.size(), expected_infos.size());
for (size_t i = 0; i < expected_infos.size(); i++) {
EXPECT_EQ(UniformInfo::fromJson(float_list[i]), expected_infos[i])
<< "index: " << i;
}
}
}
TEST_P(CompilerTestRuntime, PositionedUniformsAppearInJson) {
ASSERT_TRUE(CanCompileAndReflect("sample_with_positioned_uniforms.frag",
SourceType::kFragmentShader,
SourceLanguage::kGLSL));
auto json_fd = GetReflectionJson("sample_with_positioned_uniforms.frag");
ASSERT_TRUE(json_fd);
nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
auto sampler_list = shader_json["sampled_images"];
auto float_list = shader_json["uniforms"];
ASSERT_EQ(sampler_list.size(), 3u);
ASSERT_EQ(float_list.size(), 7u);
{
// clang-format off
std::array expected_infos = {
UniformInfo::Sampler("uSamplerNotPositioned1", 1u),
UniformInfo::Sampler("uSampler", 0u),
UniformInfo::Sampler("uSamplerNotPositioned2", 3u),
};
// clang-format on
ASSERT_EQ(sampler_list.size(), expected_infos.size());
for (size_t i = 0; i < expected_infos.size(); i++) {
EXPECT_EQ(UniformInfo::fromJson(sampler_list[i]), expected_infos[i])
<< "index: " << i;
}
}
{
// clang-format off
std::array expected_infos = {
UniformInfo::Float("uFloatNotPositioned1", 0u),
UniformInfo::Float("uFloat", 6u),
UniformInfo::Vec2("uVec2", 5u),
UniformInfo::Vec3("uVec3", 3u),
UniformInfo::Vec4("uVec4", 2u),
UniformInfo::Mat4("uMat4", 1u),
UniformInfo::Float("uFloatNotPositioned2", 2u),
};
// clang-format on
ASSERT_EQ(float_list.size(), expected_infos.size());
for (size_t i = 0; i < expected_infos.size(); i++) {
EXPECT_EQ(UniformInfo::fromJson(float_list[i]), expected_infos[i])
<< "index: " << i;
}
}
}
TEST_P(CompilerTest, UniformsHaveBindingAndSet) {
if (GetParam() == TargetPlatform::kSkSL) {
GTEST_SKIP() << "Not supported with SkSL";
}
ASSERT_TRUE(CanCompileAndReflect("sample_with_binding.vert",
SourceType::kVertexShader));
ASSERT_TRUE(CanCompileAndReflect("sample.frag", SourceType::kFragmentShader));
struct binding_and_set {
uint32_t binding;
uint32_t set;
};
auto get_binding = [&](const char* fixture) -> binding_and_set {
auto json_fd = GetReflectionJson(fixture);
nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
uint32_t binding = shader_json["buffers"][0]["binding"].get<uint32_t>();
uint32_t set = shader_json["buffers"][0]["set"].get<uint32_t>();
return {binding, set};
};
auto vert_uniform_binding = get_binding("sample_with_binding.vert");
auto frag_uniform_binding = get_binding("sample.frag");
ASSERT_EQ(frag_uniform_binding.set, 0u);
ASSERT_EQ(vert_uniform_binding.set, 3u);
ASSERT_EQ(vert_uniform_binding.binding, 17u);
}
TEST_P(CompilerTestSkSL, SkSLTextureLookUpOrderOfOperations) {
ASSERT_TRUE(
CanCompileAndReflect("texture_lookup.frag", SourceType::kFragmentShader));
auto shader = GetShaderFile("texture_lookup.frag", GetParam());
std::string_view shader_mapping(
reinterpret_cast<const char*>(shader->GetMapping()), shader->GetSize());
constexpr std::string_view expected =
"textureA.eval(textureA_size * ( vec2(1.0) + flutter_FragCoord.xy));";
EXPECT_NE(shader_mapping.find(expected), std::string::npos);
}
TEST_P(CompilerTestSkSL, CanCompileStructs) {
ASSERT_TRUE(CanCompileAndReflect("struct_internal.frag",
SourceType::kFragmentShader));
}
#define INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
INSTANTIATE_TEST_SUITE_P( \
suite_name, CompilerTest, \
::testing::Values(TargetPlatform::kOpenGLES, \
TargetPlatform::kOpenGLDesktop, \
TargetPlatform::kMetalDesktop, \
TargetPlatform::kMetalIOS, TargetPlatform::kSkSL), \
[](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
return TargetPlatformToString(info.param); \
});
INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite);
#define INSTANTIATE_RUNTIME_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
INSTANTIATE_TEST_SUITE_P( \
suite_name, CompilerTestRuntime, \
::testing::Values(TargetPlatform::kRuntimeStageMetal), \
[](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
return TargetPlatformToString(info.param); \
});
INSTANTIATE_RUNTIME_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite);
#define INSTANTIATE_SKSL_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
INSTANTIATE_TEST_SUITE_P( \
suite_name, CompilerTestSkSL, ::testing::Values(TargetPlatform::kSkSL), \
[](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
return TargetPlatformToString(info.param); \
});
INSTANTIATE_SKSL_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite);
} // namespace testing
} // namespace compiler
} // namespace impeller