[Impeller] added static check that fragment shader and vertex shaders slots match (#52174)
fixes https://github.com/flutter/flutter/issues/146452
## Pre-launch Checklist
- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide] and the [C++,
Objective-C, Java style guides].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I added new tests to check the change I am making or feature I am
adding, or the PR is [test-exempt]. See [testing the engine] for
instructions on writing and running engine tests.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I signed the [CLA].
- [x] All existing and new tests are passing.
If you need help, consider asking for advice on the #hackers-new channel
on [Discord].
<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
[test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
[C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
[testing the engine]:
https://github.com/flutter/flutter/wiki/Testing-the-engine
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
[Discord]: https://github.com/flutter/flutter/wiki/Chat
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index d1eb6ae..75de228 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -40715,6 +40715,7 @@
ORIGIN: ../../../flutter/impeller/renderer/shader_key.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/shader_library.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/shader_library.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/impeller/renderer/shader_stage_compatibility_checker.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/snapshot.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/snapshot.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/stroke.comp + ../../../flutter/LICENSE
@@ -43596,6 +43597,7 @@
FILE: ../../../flutter/impeller/renderer/shader_key.h
FILE: ../../../flutter/impeller/renderer/shader_library.cc
FILE: ../../../flutter/impeller/renderer/shader_library.h
+FILE: ../../../flutter/impeller/renderer/shader_stage_compatibility_checker.h
FILE: ../../../flutter/impeller/renderer/snapshot.cc
FILE: ../../../flutter/impeller/renderer/snapshot.h
FILE: ../../../flutter/impeller/renderer/stroke.comp
diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h
index eda23ad..7bed090 100644
--- a/impeller/renderer/pipeline.h
+++ b/impeller/renderer/pipeline.h
@@ -13,6 +13,7 @@
#include "impeller/renderer/context.h"
#include "impeller/renderer/pipeline_builder.h"
#include "impeller/renderer/pipeline_descriptor.h"
+#include "impeller/renderer/shader_stage_compatibility_checker.h"
namespace impeller {
@@ -89,6 +90,11 @@
template <class VertexShader_, class FragmentShader_>
class RenderPipelineT {
+ static_assert(
+ ShaderStageCompatibilityChecker<VertexShader_, FragmentShader_>::Check(),
+ "The output slots for the fragment shader don't have matches in the "
+ "vertex shader's output slots. This will result in a linker error.");
+
public:
using VertexShader = VertexShader_;
using FragmentShader = FragmentShader_;
diff --git a/impeller/renderer/shader_stage_compatibility_checker.h b/impeller/renderer/shader_stage_compatibility_checker.h
new file mode 100644
index 0000000..5a16e64
--- /dev/null
+++ b/impeller/renderer/shader_stage_compatibility_checker.h
@@ -0,0 +1,76 @@
+// 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.
+
+#ifndef FLUTTER_IMPELLER_RENDERER_SHADER_STAGE_COMPATIBILITY_CHECKER_H_
+#define FLUTTER_IMPELLER_RENDERER_SHADER_STAGE_COMPATIBILITY_CHECKER_H_
+
+namespace impeller {
+/// This is a classed use to check that the input slots of fragment shaders
+/// match the output slots of the vertex shaders.
+/// If they don't match it will result in linker errors when creating the
+/// pipeline. It's not used at runtime.
+template <typename VertexShaderT, typename FragmentShaderT>
+class ShaderStageCompatibilityChecker {
+ public:
+ static constexpr bool CompileTimeStrEqual(const char* str1,
+ const char* str2) {
+ return *str1 == *str2 &&
+ (*str1 == '\0' || CompileTimeStrEqual(str1 + 1, str2 + 1));
+ }
+
+ /// Returns `true` if the shader input slots for the fragment shader match the
+ /// ones declared as outputs in the vertex shader.
+ static constexpr bool Check() {
+ constexpr size_t num_outputs = VertexShaderT::kAllShaderStageOutputs.size();
+ constexpr size_t num_inputs = FragmentShaderT::kAllShaderStageInputs.size();
+
+ if (num_inputs > num_outputs) {
+ return false;
+ }
+
+ for (size_t i = 0; i < num_inputs; ++i) {
+ const ShaderStageIOSlot* input_slot =
+ FragmentShaderT::kAllShaderStageInputs[i];
+ for (size_t j = 0; j < num_outputs; ++j) {
+ const ShaderStageIOSlot* output_slot =
+ VertexShaderT::kAllShaderStageOutputs[j];
+ if (input_slot->location == output_slot->location) {
+ if (!CompileTimeStrEqual(input_slot->name, output_slot->name) ||
+ input_slot->set != output_slot->set ||
+ input_slot->binding != output_slot->binding ||
+ input_slot->type != output_slot->type ||
+ input_slot->bit_width != output_slot->bit_width ||
+ input_slot->vec_size != output_slot->vec_size ||
+ input_slot->columns != output_slot->columns ||
+ input_slot->offset != output_slot->offset) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+// The following shaders don't define output slots.
+// TODO(https://github.com/flutter/flutter/issues/146852): Make impellerc emit
+// an empty array for output slots.
+struct CheckerboardVertexShader;
+struct ClipVertexShader;
+
+template <typename FragmentShaderT>
+class ShaderStageCompatibilityChecker<CheckerboardVertexShader,
+ FragmentShaderT> {
+ public:
+ static constexpr bool Check() { return true; }
+};
+
+template <typename FragmentShaderT>
+class ShaderStageCompatibilityChecker<ClipVertexShader, FragmentShaderT> {
+ public:
+ static constexpr bool Check() { return true; }
+};
+} // namespace impeller
+#endif // FLUTTER_IMPELLER_RENDERER_SHADER_STAGE_COMPATIBILITY_CHECKER_H_