blob: 9bd65abc438673a246bcb7766e9aecf4d822acc0 [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 children
// access method for child shaders (direct sample "eval(xy)" vs indirect "eval(modified-xy)")
RuntimeEffectManager::RuntimeEffectManager() {
static const SkString kMixCode(R"(
uniform shader img1;
uniform shader img2;
half4 main(float2 xy) {
return half4(mix(img1.eval(xy), img2.eval(xy), 0.5)).rgb1;
}
)");
fMixEffect = makeEffect(kMixCode, "RE_BlurFilter_MixEffect");
static const SkString kEdgeExtensionCode(R"(
uniform shader img;
vec4 main(vec2 xy) {
float3 sample = img.eval(0.115 * xy).rgb;
return float4(sample, 1.0);
}
)");
fEdgeExtensionEffect = makeEffect(kEdgeExtensionCode, "RE_EdgeExtensionEffect");
static const SkString kHighSampleBlurCode(R"(
uniform shader img;
half4 main(float2 xy) {
half3 c = img.eval(0.6 * xy).rgb;
return half4(c * 0.5, 1.0);
}
)");
fKawaseHighSampleEffect = makeEffect(kHighSampleBlurCode,
"RE_KawaseBlurDualFilter_HighSampleBlurEffect");
static const SkString kLowSampleBlurCode(R"(
uniform shader img;
half4 main(float2 xy) {
half3 c = img.eval(0.55 * xy).rgb;
return half4(c, 1.0);
}
)");
fKawaseLowSampleEffect = makeEffect(kLowSampleBlurCode,
"RE_KawaseBlurDualFilter_LowSampleBlurEffect");
static const SkString kBlurCode(R"(
uniform shader img;
vec4 main(vec2 xy) {
return float4(img.eval(0.4 * xy).rgb, 0.0);
}
)");
fBlurEffect = makeEffect(kBlurCode, "RE_MouriMap_BlurEffect");
static const SkString kCrosstalkAndChunk16x16Code(R"(
uniform shader img;
vec4 main(vec2 xy) {
float3 linear = img.eval(0.25 * xy).rgb;
return float4(linear, 1.0);
}
)");
fCrosstalkAndChunk16x16Effect = makeEffect(kCrosstalkAndChunk16x16Code,
"RE_MouriMap_CrossTalkAndChunk16x16Effect");
static const SkString kChunk8x8Code(R"(
uniform shader img;
vec4 main(vec2 xy) {
return float4(img.eval(0.33 * xy).rgb, 1.0);
}
)");
fChunk8x8Effect = makeEffect(kChunk8x8Code, "RE_MouriMap_Chunk8x8Effect");
static const SkString kTonemapCode(R"(
uniform shader image;
uniform shader lux;
vec4 main(vec2 xy) {
float localMax = lux.eval(xy * 0.4).r;
float4 rgba = image.eval(0.5 * xy);
float3 linear = rgba.rgb * 0.7;
return float4(linear, rgba.a);
}
)");
fToneMapEffect = makeEffect(kTonemapCode, "RE_MouriMap_TonemapEffect");
}
sk_sp<SkRuntimeEffect> RuntimeEffectManager::getKnownRuntimeEffect(KnownId id) {
switch (id) {
case KnownId::kBlurFilter_MixEffect:
return fMixEffect;
case KnownId::kEdgeExtensionEffect:
return fEdgeExtensionEffect;
case KnownId::kKawaseBlurDualFilter_HighSampleBlurEffect:
return fKawaseHighSampleEffect;
case KnownId::kKawaseBlurDualFilter_LowSampleBlurEffect:
return fKawaseLowSampleEffect;
case KnownId::kMouriMap_BlurEffect:
return fBlurEffect;
case KnownId::kMouriMap_CrossTalkAndChunk16x16Effect:
return fCrosstalkAndChunk16x16Effect;
case KnownId::kMouriMap_Chunk8x8Effect:
return fChunk8x8Effect;
case KnownId::kMouriMap_TonemapEffect:
return fToneMapEffect;
}
SkUNREACHABLE;
}
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::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 = toLinearSrgb(child.eval(xy).rgb);"
"return float4(fromLinearSrgb(linear), %f);"
"}",
arbitraryAlpha);
arbitraryAlpha += 0.05f;
sk_sp<SkRuntimeEffect> effect = makeEffect(linearEffectCode, name.c_str());
fLinearEffects.insert({ name.c_str(), effect });
return effect;
}