Replace FragmentShader with FragmentShaderBuilder (flutter/engine#29231)
This commit is contained in:
committed by
GitHub
parent
2c8702d096
commit
31e6e6c23e
@@ -362,6 +362,8 @@ FILE: ../../../flutter/lib/ui/painting/color_filter.cc
|
||||
FILE: ../../../flutter/lib/ui/painting/color_filter.h
|
||||
FILE: ../../../flutter/lib/ui/painting/engine_layer.cc
|
||||
FILE: ../../../flutter/lib/ui/painting/engine_layer.h
|
||||
FILE: ../../../flutter/lib/ui/painting/fragment_program.cc
|
||||
FILE: ../../../flutter/lib/ui/painting/fragment_program.h
|
||||
FILE: ../../../flutter/lib/ui/painting/fragment_shader.cc
|
||||
FILE: ../../../flutter/lib/ui/painting/fragment_shader.h
|
||||
FILE: ../../../flutter/lib/ui/painting/gradient.cc
|
||||
|
||||
@@ -30,6 +30,8 @@ source_set("ui") {
|
||||
"painting/color_filter.h",
|
||||
"painting/engine_layer.cc",
|
||||
"painting/engine_layer.h",
|
||||
"painting/fragment_program.cc",
|
||||
"painting/fragment_program.h",
|
||||
"painting/fragment_shader.cc",
|
||||
"painting/fragment_shader.h",
|
||||
"painting/gradient.cc",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "flutter/lib/ui/painting/codec.h"
|
||||
#include "flutter/lib/ui/painting/color_filter.h"
|
||||
#include "flutter/lib/ui/painting/engine_layer.h"
|
||||
#include "flutter/lib/ui/painting/fragment_shader.h"
|
||||
#include "flutter/lib/ui/painting/fragment_program.h"
|
||||
#include "flutter/lib/ui/painting/gradient.h"
|
||||
#include "flutter/lib/ui/painting/image.h"
|
||||
#include "flutter/lib/ui/painting/image_descriptor.h"
|
||||
@@ -67,7 +67,7 @@ void DartUI::InitForGlobal() {
|
||||
DartRuntimeHooks::RegisterNatives(g_natives);
|
||||
EngineLayer::RegisterNatives(g_natives);
|
||||
FontCollection::RegisterNatives(g_natives);
|
||||
FragmentShader::RegisterNatives(g_natives);
|
||||
FragmentProgram::RegisterNatives(g_natives);
|
||||
ImageDescriptor::RegisterNatives(g_natives);
|
||||
ImageFilter::RegisterNatives(g_natives);
|
||||
ImageShader::RegisterNatives(g_natives);
|
||||
|
||||
@@ -3751,108 +3751,128 @@ class ImageShader extends Shader {
|
||||
void _initWithImage(_Image image, int tmx, int tmy, int filterQualityIndex, Float64List matrix4) native 'ImageShader_initWithImage';
|
||||
}
|
||||
|
||||
/// A shader (as used by [Paint.shader]) that runs provided SPIR-V code.
|
||||
/// An instance of [FragmentProgram] creates [Shader] objects (as used by [Paint.shader]) that run SPIR-V code.
|
||||
///
|
||||
/// This API is in beta and does not yet work on web.
|
||||
/// See https://github.com/flutter/flutter/projects/207 for roadmap.
|
||||
///
|
||||
/// [A current specification of valid SPIR-V is here.](https://github.com/flutter/engine/blob/main/lib/spirv/README.md)
|
||||
///
|
||||
/// When initializing or updating the `floatUniforms`, the length of float
|
||||
/// uniforms must match the total number of floats defined as uniforms in
|
||||
/// the shader. They will be updated in the order that they are defined.
|
||||
///
|
||||
/// For example, if there are 3 uniforms: 1 of type float, 1 type float2/vec2,
|
||||
/// and 1 of type vec3/float3, and 1 mat2x2 then the length of `floatUniforms`
|
||||
/// must be 10.
|
||||
///
|
||||
/// The uniforms could be updated as follows:
|
||||
///
|
||||
/// Consider the following snippit of GLSL code.
|
||||
///
|
||||
/// ```
|
||||
/// layout (location = 0) uniform float a;
|
||||
/// layout (location = 1) uniform vec2 b;
|
||||
/// layout (location = 2) uniform vec3 c;
|
||||
/// layout (location = 3) uniform mat2x2 d;
|
||||
/// ```
|
||||
///
|
||||
/// After being compiled to SPIR-V using [shaderc](https://github.com/google/shaderc)
|
||||
/// and provided to the constructor, `floatUniforms` must always have a length
|
||||
/// of 10. One per float-component of each uniform.
|
||||
///
|
||||
/// Dart code to update uniforms.
|
||||
///
|
||||
/// `shader.update(floatUniforms: Float32List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));`
|
||||
///
|
||||
/// Results of shader uniforms.
|
||||
///
|
||||
/// a: 1
|
||||
/// b: [2, 3]
|
||||
/// c: [4, 5, 6]
|
||||
/// d: [7, 8, 9, 10] // 2x2 matrix in column-major order
|
||||
///
|
||||
class FragmentShader extends Shader {
|
||||
class FragmentProgram extends NativeFieldWrapperClass1 {
|
||||
|
||||
// TODO(chriscraws): Add `List<Shader>? children` as a parameter to the
|
||||
// constructor and to [update].
|
||||
// https://github.com/flutter/flutter/issues/85240
|
||||
|
||||
/// Creates a fragment shader from SPIR-V byte data as an input.
|
||||
/// Creates a fragment program from SPIR-V byte data as an input.
|
||||
///
|
||||
/// One instance should be created per SPIR-V input. The constructed object
|
||||
/// should then be reused via the [shader] method to create [Shader] objects
|
||||
/// that can be used by [Shader.paint].
|
||||
///
|
||||
/// [A current specification of valid SPIR-V is here.](https://github.com/flutter/engine/blob/master/lib/spirv/README.md)
|
||||
/// SPIR-V not meeting this specification will throw an exception.
|
||||
///
|
||||
/// `floatUniforms` can be passed optionally to initialize the shader's
|
||||
/// uniforms. If they are not initially set, they will default
|
||||
/// to 0. They can later be updated by invoking the [update] method.
|
||||
///
|
||||
/// `floatUniforms` must be sized correctly, or an [ArgumentError] will
|
||||
/// be thrown. See [FragmentShader] docs for details.
|
||||
///
|
||||
/// The compilation of a shader gets more expensive the more complicated the source is.
|
||||
/// Because of this, it is reccommended to construct a FragmentShader asynchrounously,
|
||||
/// outside of a widget's `build` method, to minimize the chance of UI jank.
|
||||
/// Performance of shader-compilation is platform dependent and is not
|
||||
/// well-specified. Because of this, it is reccommended to construct
|
||||
/// `FragmentProgram` asynchronously, outside of a widget's `build`
|
||||
/// method; this will minimize the chance of UI jank.
|
||||
@pragma('vm:entry-point')
|
||||
FragmentShader({
|
||||
FragmentProgram({
|
||||
required ByteBuffer spirv,
|
||||
Float32List? floatUniforms,
|
||||
bool debugPrint = false,
|
||||
}) : super._() {
|
||||
}) {
|
||||
_constructor();
|
||||
final spv.TranspileResult result = spv.transpile(
|
||||
spirv,
|
||||
spv.TargetLanguage.sksl,
|
||||
);
|
||||
_uniformFloatCount = result.uniformFloatCount;
|
||||
_init(result.src, debugPrint);
|
||||
update(floatUniforms: floatUniforms ?? Float32List(_uniformFloatCount));
|
||||
_uniformFloatCount = result.uniformFloatCount;
|
||||
}
|
||||
|
||||
late final int _uniformFloatCount;
|
||||
|
||||
void _constructor() native 'FragmentShader_constructor';
|
||||
void _init(String sksl, bool debugPrint) native 'FragmentShader_init';
|
||||
void _constructor() native 'FragmentProgram_constructor';
|
||||
void _init(String sksl, bool debugPrint) native 'FragmentProgram_init';
|
||||
|
||||
/// Updates the uniform values that are supplied to the [FragmentShader]
|
||||
/// and refreshes the shader.
|
||||
// TODO(chriscraws): Add `List<ImageShader>? children` as a parameter to [build].
|
||||
// https://github.com/flutter/flutter/issues/85240
|
||||
|
||||
/// Constructs a [Shader] object suitable for use by [Paint.shader] with
|
||||
/// the given uniforms.
|
||||
///
|
||||
/// `floatUniforms` must be sized correctly, or an [ArgumentError] will
|
||||
/// be thrown. See [FragmentShader] docs for details.
|
||||
/// This method is suitable to be called synchronously within a widget's
|
||||
/// `build` method or from [CustomPainter.paint].
|
||||
///
|
||||
/// This method will aquire additional fields as [FragmentShader] is
|
||||
/// implemented further.
|
||||
void update({
|
||||
required Float32List floatUniforms,
|
||||
/// `floatUniforms` can be passed optionally to initialize the shader's
|
||||
/// uniforms. If they are not set they will each default to 0.
|
||||
///
|
||||
/// When initializing `floatUniforms`, the length of float uniforms must match
|
||||
/// the total number of floats defined as uniforms in the shader, or an
|
||||
/// [ArgumentError] will be thrown. Details are below.
|
||||
///
|
||||
/// Consider the following snippit of GLSL code.
|
||||
///
|
||||
/// ```
|
||||
/// layout (location = 0) uniform float a;
|
||||
/// layout (location = 1) uniform vec2 b;
|
||||
/// layout (location = 2) uniform vec3 c;
|
||||
/// layout (location = 3) uniform mat2x2 d;
|
||||
/// ```
|
||||
///
|
||||
/// When compiled to SPIR-V and provided to the constructor, `floatUniforms`
|
||||
/// must have a length of 10. One per float-component of each uniform.
|
||||
///
|
||||
/// `program.shader(floatUniforms: Float32List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));`
|
||||
///
|
||||
/// The uniforms will be set as follows:
|
||||
///
|
||||
/// a: 1
|
||||
/// b: [2, 3]
|
||||
/// c: [4, 5, 6]
|
||||
/// d: [7, 8, 9, 10] // 2x2 matrix in column-major order
|
||||
///
|
||||
/// Once a [Shader] is built, uniform values cannot be changed. Instead,
|
||||
/// [shader] must be called again with new uniform values.
|
||||
Shader shader({
|
||||
Float32List? floatUniforms,
|
||||
}) {
|
||||
if (floatUniforms == null) {
|
||||
floatUniforms = Float32List(_uniformFloatCount);
|
||||
}
|
||||
if (floatUniforms.length != _uniformFloatCount) {
|
||||
throw ArgumentError(
|
||||
'FragmentShader floatUniforms size: ${floatUniforms.length} must match given shader uniform count: $_uniformFloatCount.');
|
||||
}
|
||||
_update(floatUniforms);
|
||||
final _FragmentShader shader = _FragmentShader(this, Float32List.fromList(floatUniforms));
|
||||
_shader(shader, floatUniforms);
|
||||
return shader;
|
||||
}
|
||||
|
||||
void _update(Float32List floatUniforms) native 'FragmentShader_update';
|
||||
void _shader(_FragmentShader shader, Float32List floatUniforms) native 'FragmentProgram_shader';
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
class _FragmentShader extends Shader {
|
||||
/// This class is created by the engine and should not be instantiated
|
||||
/// or extended directly.
|
||||
///
|
||||
/// To create a [_FragmentShader], use a [FragmentProgram].
|
||||
_FragmentShader(this._builder, this._floatUniforms) : super._();
|
||||
|
||||
final FragmentProgram _builder;
|
||||
final Float32List _floatUniforms;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
return other is _FragmentShader
|
||||
&& other._builder == _builder
|
||||
&& _listEquals<double>(other._floatUniforms, _floatUniforms);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(_builder, hashList(_floatUniforms));
|
||||
}
|
||||
|
||||
/// Defines how a list of points is interpreted when drawing a set of triangles.
|
||||
|
||||
74
engine/src/flutter/lib/ui/painting/fragment_program.cc
Normal file
74
engine/src/flutter/lib/ui/painting/fragment_program.cc
Normal file
@@ -0,0 +1,74 @@
|
||||
// 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 <iostream>
|
||||
|
||||
#include "flutter/lib/ui/painting/fragment_program.h"
|
||||
|
||||
#include "flutter/lib/ui/dart_wrapper.h"
|
||||
#include "flutter/lib/ui/ui_dart_state.h"
|
||||
#include "third_party/skia/include/core/SkString.h"
|
||||
#include "third_party/tonic/converter/dart_converter.h"
|
||||
#include "third_party/tonic/dart_args.h"
|
||||
#include "third_party/tonic/dart_binding_macros.h"
|
||||
#include "third_party/tonic/dart_library_natives.h"
|
||||
#include "third_party/tonic/typed_data/typed_list.h"
|
||||
|
||||
using tonic::ToDart;
|
||||
|
||||
namespace flutter {
|
||||
|
||||
static void FragmentProgram_constructor(Dart_NativeArguments args) {
|
||||
DartCallConstructor(&FragmentProgram::Create, args);
|
||||
}
|
||||
|
||||
IMPLEMENT_WRAPPERTYPEINFO(ui, FragmentProgram);
|
||||
|
||||
#define FOR_EACH_BINDING(V) \
|
||||
V(FragmentProgram, init) \
|
||||
V(FragmentProgram, shader)
|
||||
|
||||
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
|
||||
|
||||
void FragmentProgram::RegisterNatives(tonic::DartLibraryNatives* natives) {
|
||||
natives->Register(
|
||||
{{"FragmentProgram_constructor", FragmentProgram_constructor, 1, true},
|
||||
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
|
||||
}
|
||||
|
||||
void FragmentProgram::init(std::string sksl, bool debugPrintSksl) {
|
||||
SkRuntimeEffect::Result result =
|
||||
SkRuntimeEffect::MakeForShader(SkString(sksl));
|
||||
runtime_effect_ = result.effect;
|
||||
|
||||
if (runtime_effect_ == nullptr) {
|
||||
Dart_ThrowException(tonic::ToDart(
|
||||
std::string("Invalid SkSL:\n") + sksl.c_str() +
|
||||
std::string("\nSkSL Error:\n") + result.errorText.c_str()));
|
||||
return;
|
||||
}
|
||||
if (debugPrintSksl) {
|
||||
FML_DLOG(INFO) << std::string("debugPrintSksl:\n") + sksl.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
fml::RefPtr<FragmentShader> FragmentProgram::shader(
|
||||
Dart_Handle shader,
|
||||
const tonic::Float32List& uniforms) {
|
||||
auto sk_shader = runtime_effect_->makeShader(
|
||||
SkData::MakeWithCopy(uniforms.data(),
|
||||
uniforms.num_elements() * sizeof(float)),
|
||||
0, 0, nullptr, false);
|
||||
return FragmentShader::Create(shader, std::move(sk_shader));
|
||||
}
|
||||
|
||||
fml::RefPtr<FragmentProgram> FragmentProgram::Create() {
|
||||
return fml::MakeRefCounted<FragmentProgram>();
|
||||
}
|
||||
|
||||
FragmentProgram::FragmentProgram() = default;
|
||||
|
||||
FragmentProgram::~FragmentProgram() = default;
|
||||
|
||||
} // namespace flutter
|
||||
45
engine/src/flutter/lib/ui/painting/fragment_program.h
Normal file
45
engine/src/flutter/lib/ui/painting/fragment_program.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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_LIB_UI_PAINTING_FRAGMENT_PROGRAM_H_
|
||||
#define FLUTTER_LIB_UI_PAINTING_FRAGMENT_PROGRAM_H_
|
||||
|
||||
#include "flutter/lib/ui/dart_wrapper.h"
|
||||
#include "flutter/lib/ui/painting/fragment_shader.h"
|
||||
#include "third_party/skia/include/effects/SkRuntimeEffect.h"
|
||||
#include "third_party/tonic/dart_library_natives.h"
|
||||
#include "third_party/tonic/typed_data/typed_list.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tonic {
|
||||
class DartLibraryNatives;
|
||||
} // namespace tonic
|
||||
|
||||
namespace flutter {
|
||||
|
||||
class FragmentProgram : public RefCountedDartWrappable<FragmentProgram> {
|
||||
DEFINE_WRAPPERTYPEINFO();
|
||||
FML_FRIEND_MAKE_REF_COUNTED(FragmentProgram);
|
||||
|
||||
public:
|
||||
~FragmentProgram() override;
|
||||
static fml::RefPtr<FragmentProgram> Create();
|
||||
|
||||
void init(std::string sksl, bool debugPrintSksl);
|
||||
|
||||
fml::RefPtr<FragmentShader> shader(Dart_Handle shader,
|
||||
const tonic::Float32List& uniforms);
|
||||
|
||||
static void RegisterNatives(tonic::DartLibraryNatives* natives);
|
||||
|
||||
private:
|
||||
FragmentProgram();
|
||||
sk_sp<SkRuntimeEffect> runtime_effect_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_LIB_UI_PAINTING_FRAGMENT_PROGRAM_H_
|
||||
@@ -19,60 +19,35 @@ using tonic::ToDart;
|
||||
|
||||
namespace flutter {
|
||||
|
||||
static void FragmentShader_constructor(Dart_NativeArguments args) {
|
||||
DartCallConstructor(&FragmentShader::Create, args);
|
||||
}
|
||||
|
||||
IMPLEMENT_WRAPPERTYPEINFO(ui, FragmentShader);
|
||||
|
||||
#define FOR_EACH_BINDING(V) \
|
||||
V(FragmentShader, init) \
|
||||
V(FragmentShader, update)
|
||||
|
||||
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
|
||||
// Since _FragmentShader is a private class, we can't use
|
||||
// IMPLEMENT_WRAPPERTYPEINFO
|
||||
static const tonic::DartWrapperInfo kDartWrapperInfo_ui_FragmentShader = {
|
||||
"ui",
|
||||
"_FragmentShader",
|
||||
sizeof(FragmentShader),
|
||||
};
|
||||
const tonic::DartWrapperInfo& FragmentShader::dart_wrapper_info_ =
|
||||
kDartWrapperInfo_ui_FragmentShader;
|
||||
|
||||
void FragmentShader::RegisterNatives(tonic::DartLibraryNatives* natives) {
|
||||
natives->Register(
|
||||
{{"FragmentShader_constructor", FragmentShader_constructor, 1, true},
|
||||
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
|
||||
natives->Register({});
|
||||
}
|
||||
|
||||
sk_sp<SkShader> FragmentShader::shader(SkSamplingOptions sampling) {
|
||||
// TODO(antrob): Use sampling?
|
||||
// https://github.com/flutter/flutter/issues/88303
|
||||
// Sampling options are ignored, since sampling options don't make sense for
|
||||
// generative shaders.
|
||||
return shader_;
|
||||
}
|
||||
|
||||
void FragmentShader::init(std::string sksl, bool debugPrintSksl) {
|
||||
SkRuntimeEffect::Result result =
|
||||
SkRuntimeEffect::MakeForShader(SkString(sksl));
|
||||
runtime_effect_ = result.effect;
|
||||
|
||||
if (runtime_effect_ == nullptr) {
|
||||
Dart_ThrowException(tonic::ToDart(
|
||||
std::string("Invalid SkSL:\n") + sksl.c_str() +
|
||||
std::string("\nSkSL Error:\n") + result.errorText.c_str()));
|
||||
return;
|
||||
}
|
||||
if (debugPrintSksl) {
|
||||
FML_DLOG(INFO) << std::string("debugPrintSksl:\n") + sksl.c_str();
|
||||
}
|
||||
fml::RefPtr<FragmentShader> FragmentShader::Create(Dart_Handle dart_handle,
|
||||
sk_sp<SkShader> shader) {
|
||||
auto fragment_shader = fml::MakeRefCounted<FragmentShader>(std::move(shader));
|
||||
fragment_shader->AssociateWithDartWrapper(dart_handle);
|
||||
return fragment_shader;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/flutter/flutter/issues/85240):
|
||||
// Add `Dart_Handle children` as a paramter.
|
||||
void FragmentShader::update(const tonic::Float32List& uniforms) {
|
||||
shader_ = runtime_effect_->makeShader(
|
||||
SkData::MakeWithCopy(uniforms.data(),
|
||||
uniforms.num_elements() * sizeof(float)),
|
||||
0, 0, nullptr, false);
|
||||
}
|
||||
|
||||
fml::RefPtr<FragmentShader> FragmentShader::Create() {
|
||||
return fml::MakeRefCounted<FragmentShader>();
|
||||
}
|
||||
|
||||
FragmentShader::FragmentShader() = default;
|
||||
FragmentShader::FragmentShader(sk_sp<SkShader> shader)
|
||||
: shader_(std::move(shader)) {}
|
||||
|
||||
FragmentShader::~FragmentShader() = default;
|
||||
|
||||
|
||||
@@ -29,26 +29,16 @@ class FragmentShader : public Shader {
|
||||
|
||||
public:
|
||||
~FragmentShader() override;
|
||||
static fml::RefPtr<FragmentShader> Create();
|
||||
static fml::RefPtr<FragmentShader> Create(Dart_Handle dart_handle,
|
||||
sk_sp<SkShader> shader);
|
||||
|
||||
sk_sp<SkShader> shader(SkSamplingOptions) override;
|
||||
|
||||
void init(std::string sksl, bool debugPrintSksl);
|
||||
|
||||
void update(const tonic::Float32List& uniforms);
|
||||
|
||||
static void RegisterNatives(tonic::DartLibraryNatives* natives);
|
||||
|
||||
private:
|
||||
FragmentShader();
|
||||
FragmentShader(sk_sp<SkShader> shader);
|
||||
|
||||
void setShader();
|
||||
|
||||
// Since the shader source cannot be updated, the effect can be
|
||||
// created once and re-used.
|
||||
sk_sp<SkRuntimeEffect> runtime_effect_;
|
||||
|
||||
// A new shader is created every time update is called.
|
||||
sk_sp<SkShader> shader_;
|
||||
};
|
||||
|
||||
|
||||
@@ -789,16 +789,16 @@ class ImageDescriptor {
|
||||
}
|
||||
}
|
||||
|
||||
class FragmentShader extends Shader {
|
||||
FragmentShader({
|
||||
class FragmentProgram {
|
||||
FragmentProgram({
|
||||
required ByteBuffer spirv, // ignore: avoid_unused_constructor_parameters
|
||||
Float32List? floatUniforms, // ignore: avoid_unused_constructor_parameters
|
||||
bool debugPrint = false, // ignore: avoid_unused_constructor_parameters
|
||||
}) : super._() {
|
||||
throw UnsupportedError('FragmentShader is not supported for the CanvasKit or HTML renderers.');
|
||||
}) {
|
||||
throw UnsupportedError('FragmentProgram is not supported for the CanvasKit or HTML renderers.');
|
||||
}
|
||||
|
||||
void update({Float32List? floatUniforms}) =>
|
||||
throw UnsupportedError('FragmentShader is not supported for the CanvasKit or HTML renderers.');
|
||||
Shader shader({
|
||||
required Float32List floatUniforms,
|
||||
}) => throw UnsupportedError('FragmentProgram is not supported for the CanvasKit or HTML renderers.');
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,15 @@ import 'shader_test_file_utils.dart';
|
||||
void main() {
|
||||
test('throws exception for invalid shader', () {
|
||||
final ByteBuffer invalidBytes = Uint8List.fromList(<int>[1, 2, 3, 4, 5]).buffer;
|
||||
expect(() => FragmentShader(spirv: invalidBytes), throws);
|
||||
expect(() => FragmentProgram(spirv: invalidBytes), throws);
|
||||
});
|
||||
|
||||
test('simple shader renders correctly', () async {
|
||||
final Uint8List shaderBytes = await spvFile('general_shaders', 'functions.spv').readAsBytes();
|
||||
final FragmentShader shader = FragmentShader(
|
||||
final FragmentProgram program = FragmentProgram(
|
||||
spirv: shaderBytes.buffer,
|
||||
);
|
||||
final Shader shader = program.shader(
|
||||
floatUniforms: Float32List.fromList(<double>[1]),
|
||||
);
|
||||
_expectShaderRendersGreen(shader);
|
||||
@@ -30,18 +32,20 @@ void main() {
|
||||
|
||||
test('shader with functions renders green', () {
|
||||
final ByteBuffer spirv = spvFile('general_shaders', 'functions.spv').readAsBytesSync().buffer;
|
||||
final FragmentShader shader = FragmentShader(
|
||||
final FragmentProgram program = FragmentProgram(
|
||||
spirv: spirv,
|
||||
);
|
||||
final Shader shader = program.shader(
|
||||
floatUniforms: Float32List.fromList(<double>[1]),
|
||||
);
|
||||
_expectShaderRendersGreen(shader);
|
||||
});
|
||||
|
||||
test('shader with uniforms renders and updates correctly', () async {
|
||||
test('shader with uniforms renders correctly', () async {
|
||||
final Uint8List shaderBytes = await spvFile('general_shaders', 'uniforms.spv').readAsBytes();
|
||||
final FragmentShader shader = FragmentShader(spirv: shaderBytes.buffer);
|
||||
final FragmentProgram program = FragmentProgram(spirv: shaderBytes.buffer);
|
||||
|
||||
shader.update(
|
||||
final Shader shader = program.shader(
|
||||
floatUniforms: Float32List.fromList(<double>[
|
||||
0.0, // iFloatUniform
|
||||
0.25, // iVec2Uniform.x
|
||||
@@ -75,6 +79,42 @@ void main() {
|
||||
expect(supportedOpShaders.isNotEmpty, true);
|
||||
_expectShadersRenderGreen(supportedOpShaders);
|
||||
_expectShadersHaveOp(supportedOpShaders, false /* glsl ops */);
|
||||
|
||||
test('equality depends on floatUniforms', () {
|
||||
final ByteBuffer spirv = spvFile('general_shaders', 'simple.spv')
|
||||
.readAsBytesSync().buffer;
|
||||
final FragmentProgram program = FragmentProgram(spirv: spirv);
|
||||
final Float32List ones = Float32List.fromList(<double>[1]);
|
||||
final Float32List zeroes = Float32List.fromList(<double>[0]);
|
||||
|
||||
{
|
||||
final a = program.shader(floatUniforms: ones);
|
||||
final b = program.shader(floatUniforms: ones);
|
||||
expect(a, b);
|
||||
expect(a.hashCode, b.hashCode);
|
||||
}
|
||||
|
||||
{
|
||||
final a = program.shader(floatUniforms: ones);
|
||||
final b = program.shader(floatUniforms: zeroes);
|
||||
expect(a, notEquals(b));
|
||||
expect(a.hashCode, notEquals(b.hashCode));
|
||||
}
|
||||
});
|
||||
|
||||
test('equality depends on spirv', () {
|
||||
final ByteBuffer spirvA = spvFile('general_shaders', 'simple.spv')
|
||||
.readAsBytesSync().buffer;
|
||||
final ByteBuffer spirvB = spvFile('general_shaders', 'uniforms.spv')
|
||||
.readAsBytesSync().buffer;
|
||||
final FragmentProgram programA = FragmentProgram(spirv: spirvA);
|
||||
final FragmentProgram programB = FragmentProgram(spirv: spirvB);
|
||||
final a = programA.shader();
|
||||
final b = programB.shader();
|
||||
|
||||
expect(a, notEquals(b));
|
||||
expect(a.hashCode, notEquals(b.hashCode));
|
||||
});
|
||||
}
|
||||
|
||||
// Expect that all of the spirv shaders in this folder render green.
|
||||
@@ -83,8 +123,10 @@ void main() {
|
||||
void _expectShadersRenderGreen(Map<String, ByteBuffer> shaders) {
|
||||
for (final String key in shaders.keys) {
|
||||
test('$key renders green', () {
|
||||
final FragmentShader shader = FragmentShader(
|
||||
final FragmentProgram program = FragmentProgram(
|
||||
spirv: shaders[key]!,
|
||||
);
|
||||
final Shader shader = program.shader(
|
||||
floatUniforms: Float32List.fromList(<double>[1]),
|
||||
);
|
||||
_expectShaderRendersGreen(shader);
|
||||
@@ -138,7 +180,7 @@ void _expectShaderHasOp(ByteBuffer spirv, String filename, bool glsl) {
|
||||
}
|
||||
|
||||
// Expects that a spirv shader only outputs the color green.
|
||||
Future<void> _expectShaderRendersGreen(FragmentShader shader) async {
|
||||
Future<void> _expectShaderRendersGreen(Shader shader) async {
|
||||
final ByteData renderedBytes = (await _imageByteDataFromShader(
|
||||
shader: shader,
|
||||
imageDimension: _shaderImageDimension,
|
||||
@@ -149,7 +191,7 @@ Future<void> _expectShaderRendersGreen(FragmentShader shader) async {
|
||||
}
|
||||
|
||||
Future<ByteData?> _imageByteDataFromShader({
|
||||
required FragmentShader shader,
|
||||
required Shader shader,
|
||||
int imageDimension = 100,
|
||||
}) async {
|
||||
final PictureRecorder recorder = PictureRecorder();
|
||||
|
||||
Reference in New Issue
Block a user