blob: b61b681ccc0243f64d636d12f135df49fa689444 [file] [log] [blame]
/*
* Copyright 2025 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tests/graphite/precompile/AndroidRuntimeEffectManager.h"
#include "include/core/SkString.h"
#include "include/effects/SkRuntimeEffect.h"
namespace {
// Note: passing in a name to 'makeEffect' is a difference from Android's factory functions.
sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl, const char* name) {
SkRuntimeEffect::Options options;
options.fName = name;
auto [effect, error] = SkRuntimeEffect::MakeForShader(sksl, options);
if (!effect) {
SkDebugf("%s\n", error.c_str());
}
return effect;
}
} // anonymous namespace
// All the SkSL snippets in this constructor are just stubs for the Android code.
// For Skia's testing purposes they only need to match the real code wrt:
// name
// number of child shaders
// access method for child shaders (direct sample "eval(xy)" vs indirect "eval(modified-xy)")
RuntimeEffectManager::RuntimeEffectManager() {
static const SkString kBlurFilter_MixEffectCode(R"(
uniform shader img1;
uniform shader img2;
half4 main(float2 xy) {
return half4(mix(img1.eval(xy), img2.eval(xy), 0.5)).rgb1;
}
)");
static const SkString kEdgeExtensionEffectCode(R"(
uniform shader img;
vec4 main(vec2 xy) {
float3 sample = img.eval(0.115 * xy).rgb;
return float4(sample, 1.0);
}
)");
static const SkString kGainmapEffectCode(R"(
uniform shader img1;
uniform shader img2;
vec4 main(vec2 xy) {
float3 sample = img1.eval(xy).rgb + img2.eval(xy).rgb;
return float4(sample, 1.0);
}
)");
static const SkString kKawaseBlurDualFilterV2_QuarterResDownSampleBlurEffectCode(R"(
uniform shader img;
half4 main(float2 xy) {
half3 c = img.eval(0.6 * xy).rgb;
return half4(c * 0.5, 1.0);
}
)");
static const SkString kKawaseBlurDualFilterV2_HalfResDownSampleBlurEffectCode(R"(
uniform shader img;
half4 main(float2 xy) {
half3 c = img.eval(0.55 * xy).rgb;
return half4(c, 1.0);
}
)");
static const SkString kKawaseBlurDualFilterV2_UpSampleBlurEffectCode(R"(
uniform shader img;
half4 main(float2 xy) {
half3 c = img.eval(0.55 * xy).rgb;
return half4(c, 1.0);
}
)");
static const SkString kKawaseBlurEffectCode(R"(
uniform shader img;
half4 main(float2 xy) {
half3 c = img.eval(0.55 * xy).rgb;
return half4(c, 1.0);
}
)");
static const SkString kLutEffectCode(R"(
uniform shader img;
uniform shader lut;
half4 main(float2 xy) {
half3 c = img.eval(xy).rgb;
float rV = lut.eval(vec2(255, 0)).r;
float gV = lut.eval(vec2(128, 0)).r;
float bV = lut.eval(vec2(64, 0)).r;
return half4(rV+c.r, gV+c.g, bV+c.b, 1.0);
}
)");
static const SkString kMouriMap_CrossTalkAndChunk16x16EffectCode(R"(
uniform shader img;
vec4 main(vec2 xy) {
float3 linear = img.eval(0.25 * xy).rgb;
return float4(linear, 1.0);
}
)");
static const SkString kMouriMap_Chunk8x8EffectCode(R"(
uniform shader img;
vec4 main(vec2 xy) {
return float4(img.eval(0.33 * xy).rgb, 1.0);
}
)");
static const SkString kMouriMap_BlurEffectCode(R"(
uniform shader img;
vec4 main(vec2 xy) {
return float4(img.eval(0.4 * xy).rgb, 0.0);
}
)");
static const SkString kMouriMap_TonemapEffectCode(R"(
uniform shader image;
uniform shader lux;
vec4 main(vec2 xy) {
float localMax = lux.eval(xy * 0.4).r;
float4 rgba = image.eval(xy);
float3 linear = rgba.rgb * 0.7;
return float4(linear, rgba.a);
}
)");
static const SkString kStretchEffectCode(R"(
uniform shader image;
vec4 main(vec2 xy) {
float4 rgba = image.eval(0.5 * xy);
return rgba;
}
)");
static const SkString kBoxShadowEffectCode(R"(
vec4 main(vec2 xy) {
return float4(.5, .7, .9, .95);
}
)");
#define M(id) f##id = makeEffect(k##id##Code, "RE_" #id);
SK_ALL_ANDROID_KNOWN_EFFECTS(M)
#undef M
}
namespace {
SkString to_str(ui::Dataspace ds) {
switch (ds) {
case ui::Dataspace::UNKNOWN: return SkString("UNKNOWN");
case ui::Dataspace::SRGB: return SkString("SRGB");
case ui::Dataspace::BT2020: return SkString("BT2020");
case ui::Dataspace::BT2020_ITU_PQ: return SkString("BT2020_ITU_PQ");
case ui::Dataspace::BT2020_HLG: return SkString("BT2020_HLG");
case ui::Dataspace::DISPLAY_P3: return SkString("DISPLAY_P3");
case ui::Dataspace::V0_SRGB: return SkString("V0_SRGB");
}
return SkStringPrintf("0x%x", static_cast<uint32_t>(ds));
}
const char* to_str(shaders::LinearEffect::SkSLType type) {
switch (type) {
case shaders::LinearEffect::SkSLType::Shader: return "Shader";
case shaders::LinearEffect::SkSLType::ColorFilter: return "ColorFilter";
}
SkUNREACHABLE;
}
} // anonymous namespace
sk_sp<SkRuntimeEffect> RuntimeEffectManager::getOrCreateLinearRuntimeEffect(
const shaders::LinearEffect& linearEffect) {
SkString name = SkStringPrintf("RE_LinearEffect_%s__%s__%s__%s__%s",
to_str(linearEffect.inputDataspace).c_str(),
to_str(linearEffect.outputDataspace).c_str(),
linearEffect.undoPremultipliedAlpha ? "true" : "false",
to_str(linearEffect.fakeOutputDataspace).c_str(),
to_str(linearEffect.type));
auto result = fLinearEffects.find(name.c_str());
if (result != fLinearEffects.end()) {
return result->second;
}
// Each code snippet must be unique, otherwise Skia will internally find a match
// and uniquify things. To avoid this we just add an arbitrary alpha constant
// to the code.
static float arbitraryAlpha = 0.051f;
SkString linearEffectCode = SkStringPrintf(
"uniform shader child;"
"vec4 main(vec2 xy) {"
"float3 linear = child.eval(xy).rgb;"
"return float4(linear, %f);"
"}",
arbitraryAlpha);
arbitraryAlpha += 0.05f;
sk_sp<SkRuntimeEffect> effect = makeEffect(linearEffectCode, name.c_str());
fLinearEffects.insert({ name.c_str(), effect });
return effect;
}