blob: 441c8707886a555d1ebf6ef46ce263bfff75985a [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tests/Test.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/graphite/CombinationBuilder.h"
#include "include/gpu/graphite/Context.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/gpu/graphite/ContextPriv.h"
#include "src/gpu/graphite/KeyHelpers.h"
#include "src/gpu/graphite/ShaderCodeDictionary.h"
using namespace skgpu::graphite;
#ifdef SK_ENABLE_PRECOMPILE
namespace {
// combines two child blenders using 'blendFrac'
// 1 uniform ("blendFrac")
// 2 children ("a", "b")
// TODO: add a helper function
sk_sp<SkRuntimeEffect> get_combo_effect() {
SkRuntimeEffect::Result result = SkRuntimeEffect::MakeForBlender(SkString(
"uniform float blendFrac;"
"uniform blender a;"
"uniform blender b;"
"half4 main(half4 src, half4 dst) {"
"return (blendFrac * a.eval(src, dst)) + ((1 - blendFrac) * b.eval(src, dst));"
"}"
));
return result.effect;
}
// returns opaque red w/ the red value determined by 'redColor'
// 1 uniform ("redColor)
sk_sp<SkRuntimeEffect> get_red_effect() {
SkRuntimeEffect::Result result = SkRuntimeEffect::MakeForBlender(SkString(
"uniform float redColor;"
"half4 main(half4 src, half4 dst) {"
"return half4(redColor, 0, 0, 1);"
"}"
));
return result.effect;
}
// returns opaque blue w/ the blue value determined by 'blueColor'
// 1 uniform ("blueColor)
sk_sp<SkRuntimeEffect> get_blue_effect() {
SkRuntimeEffect::Result result = SkRuntimeEffect::MakeForBlender(SkString(
"uniform float blueColor;"
"half4 main(half4 src, half4 dst) {"
"return half4(0, 0, blueColor, 1);"
"}"
));
return result.effect;
}
static sk_sp<SkBlender> get_blender(sk_sp<SkRuntimeEffect> comboEffect,
sk_sp<SkRuntimeEffect> redEffect,
sk_sp<SkRuntimeEffect> blueEffect) {
sk_sp<SkBlender> redBlender;
{
SkRuntimeBlendBuilder builder(redEffect);
builder.uniform("redColor") = 1.0f;
redBlender = builder.makeBlender();
}
sk_sp<SkBlender> blueBlender;
{
SkRuntimeBlendBuilder builder(blueEffect);
builder.uniform("blueColor") = 1.0f;
blueBlender = builder.makeBlender();
}
sk_sp<SkBlender> linearBlender;
{
SkRuntimeBlendBuilder builder(comboEffect);
builder.uniform("blendFrac") = 0.5f;
builder.child("a") = redBlender;
builder.child("b") = blueBlender;
linearBlender = builder.makeBlender();
}
return linearBlender;
}
} // anonymous namespace
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RTEffectTest, reporter, context) {
ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
sk_sp<SkRuntimeEffect> comboEffect = get_combo_effect();
sk_sp<SkRuntimeEffect> redEffect = get_red_effect();
sk_sp<SkRuntimeEffect> blueEffect = get_blue_effect();
BlenderID comboId = context->addUserDefinedBlender(comboEffect);
SkASSERT(comboId.isValid());
BlenderID redId = context->addUserDefinedBlender(redEffect);
SkASSERT(redId.isValid());
BlenderID blueId = context->addUserDefinedBlender(blueEffect);
SkASSERT(blueId.isValid());
auto comboEntry = dict->getEntry(comboId);
REPORTER_ASSERT(reporter, comboEntry);
auto redEntry = dict->getEntry(redId);
REPORTER_ASSERT(reporter, redEntry);
auto blueEntry = dict->getEntry(blueId);
REPORTER_ASSERT(reporter, blueEntry);
// TODO:
// add runtime effect shaders and color filters
// check that the shader snippet has the expected properties: uniforms, children, functions
sk_sp<SkBlender> blender = get_blender(comboEffect, redEffect, blueEffect);
// TODO:
// check that the uniforms can be extracted from 'blender' correctly
}
#endif // SK_ENABLE_PRECOMPILE
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(Shader_FindOrCreateSnippetForRuntimeEffect, reporter, context) {
ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
std::unique_ptr<SkRuntimeEffect> testEffect(SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
"half4 main(float2 coords) {"
"return half4(coords.xy01);"
"}"
));
// Create a new runtime-effect snippet.
int snippetID = dict->findOrCreateRuntimeEffectSnippet(testEffect.get());
REPORTER_ASSERT(reporter, snippetID >= kBuiltInCodeSnippetIDCount);
// Verify that it can be looked up and its name is 'RuntimeEffect'. (The name isn't meaningful,
// but this is an easy way to verify that we didn't get an unrelated snippet.)
const ShaderSnippet* snippet = dict->getEntry(snippetID);
REPORTER_ASSERT(reporter, snippet);
REPORTER_ASSERT(reporter, std::string_view(snippet->fName) == "RuntimeEffect");
// If we pass the same effect again, we should get the same snippet ID as before.
int foundSnippetID = dict->findOrCreateRuntimeEffectSnippet(testEffect.get());
REPORTER_ASSERT(reporter, foundSnippetID == snippetID);
}
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(ColorFilter_FindOrCreateSnippetForRuntimeEffect,
reporter,
context) {
ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
std::unique_ptr<SkRuntimeEffect> testEffect(SkMakeRuntimeEffect(
SkRuntimeEffect::MakeForColorFilter,
"half4 main(half4 color) {"
"return color.gbra;"
"}"
));
// Create a new runtime-effect snippet.
int snippetID = dict->findOrCreateRuntimeEffectSnippet(testEffect.get());
REPORTER_ASSERT(reporter, snippetID >= kBuiltInCodeSnippetIDCount);
// Verify that it can be looked up and its name is 'RuntimeEffect'. (The name isn't meaningful,
// but this is an easy way to verify that we didn't get an unrelated snippet.)
const ShaderSnippet* snippet = dict->getEntry(snippetID);
REPORTER_ASSERT(reporter, snippet);
REPORTER_ASSERT(reporter, std::string_view(snippet->fName) == "RuntimeEffect");
// If we pass the same effect again, we should get the same snippet ID as before.
int foundSnippetID = dict->findOrCreateRuntimeEffectSnippet(testEffect.get());
REPORTER_ASSERT(reporter, foundSnippetID == snippetID);
}
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(ShaderUniforms_FindOrCreateSnippetForRuntimeEffect,
reporter, context) {
ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
std::unique_ptr<SkRuntimeEffect> testEffect(SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
"uniform float3x3 MyFloat3x3Uniform;"
"uniform int4 MyInt4ArrayUniform[1];"
"uniform half2 MyHalf2ArrayUniform[99];"
"half4 main(float2 coords) {"
"return half4(coords.xy01);"
"}"
));
// Create a new runtime-effect snippet.
int snippetID = dict->findOrCreateRuntimeEffectSnippet(testEffect.get());
REPORTER_ASSERT(reporter, snippetID >= kBuiltInCodeSnippetIDCount);
// Delete the test effect.
testEffect = nullptr;
// Verify that it can be looked up by its snippet ID.
const ShaderSnippet* snippet = dict->getEntry(snippetID);
REPORTER_ASSERT(reporter, snippet);
// The uniform span should match our expectations even though the runtime effect was deleted.
REPORTER_ASSERT(reporter, snippet->fUniforms.size() == 3);
REPORTER_ASSERT(reporter,
std::string_view(snippet->fUniforms[0].name()) == "MyFloat3x3Uniform");
REPORTER_ASSERT(reporter, snippet->fUniforms[0].type() == SkSLType::kFloat3x3);
REPORTER_ASSERT(reporter, snippet->fUniforms[0].count() == 0);
REPORTER_ASSERT(reporter,
std::string_view(snippet->fUniforms[1].name()) == "MyInt4ArrayUniform");
REPORTER_ASSERT(reporter, snippet->fUniforms[1].type() == SkSLType::kInt4);
REPORTER_ASSERT(reporter, snippet->fUniforms[1].count() == 1);
REPORTER_ASSERT(reporter,
std::string_view(snippet->fUniforms[2].name()) == "MyHalf2ArrayUniform");
REPORTER_ASSERT(reporter, snippet->fUniforms[2].type() == SkSLType::kHalf2);
REPORTER_ASSERT(reporter, snippet->fUniforms[2].count() == 99);
}
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(ColorFilterUniforms_FindOrCreateSnippetForRuntimeEffect,
reporter, context) {
ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
std::unique_ptr<SkRuntimeEffect> testEffect(SkMakeRuntimeEffect(
SkRuntimeEffect::MakeForColorFilter,
"uniform float3x3 MyFloat3x3Uniform;"
"uniform int4 MyInt4ArrayUniform[1];"
"uniform half2 MyHalf2ArrayUniform[99];"
"half4 main(half4 color) {"
"return color.gbra;"
"}"
));
// Create a new runtime-effect snippet.
int snippetID = dict->findOrCreateRuntimeEffectSnippet(testEffect.get());
REPORTER_ASSERT(reporter, snippetID >= kBuiltInCodeSnippetIDCount);
// Delete the test effect.
testEffect = nullptr;
// Verify that it can be looked up by its snippet ID.
const ShaderSnippet* snippet = dict->getEntry(snippetID);
REPORTER_ASSERT(reporter, snippet);
// The uniform span should match our expectations even though the runtime effect was deleted.
REPORTER_ASSERT(reporter, snippet->fUniforms.size() == 3);
REPORTER_ASSERT(reporter,
std::string_view(snippet->fUniforms[0].name()) == "MyFloat3x3Uniform");
REPORTER_ASSERT(reporter, snippet->fUniforms[0].type() == SkSLType::kFloat3x3);
REPORTER_ASSERT(reporter, snippet->fUniforms[0].count() == 0);
REPORTER_ASSERT(reporter,
std::string_view(snippet->fUniforms[1].name()) == "MyInt4ArrayUniform");
REPORTER_ASSERT(reporter, snippet->fUniforms[1].type() == SkSLType::kInt4);
REPORTER_ASSERT(reporter, snippet->fUniforms[1].count() == 1);
REPORTER_ASSERT(reporter,
std::string_view(snippet->fUniforms[2].name()) == "MyHalf2ArrayUniform");
REPORTER_ASSERT(reporter, snippet->fUniforms[2].type() == SkSLType::kHalf2);
REPORTER_ASSERT(reporter, snippet->fUniforms[2].count() == 99);
}