blob: 738c5a6ef1e1840fa997aafe28a801e7cb492a41 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "experimental/graphite/src/ContextUtils.h"
#include <string>
#include "experimental/graphite/src/ContextPriv.h"
#include "experimental/graphite/src/DrawTypes.h"
#include "experimental/graphite/src/PaintParams.h"
#include "experimental/graphite/src/Uniform.h"
#include "experimental/graphite/src/UniformManager.h"
#include "include/core/SkPaint.h"
#include "include/private/SkShaderCodeDictionary.h"
#include "include/private/SkUniquePaintParamsID.h"
#include "src/core/SkKeyHelpers.h"
namespace skgpu {
namespace {
// TODO: For the sprint we only support 4 stops in the gradients
static constexpr int kMaxStops = 4;
// TODO: For the sprint we unify all the gradient uniforms into a standard set of 6:
// kMaxStops colors
// kMaxStops offsets
// 2 points
// 2 radii
static constexpr int kNumGradientUniforms = 6;
static constexpr Uniform kGradientUniforms[kNumGradientUniforms] {
{"colors", SLType::kHalf4 , kMaxStops },
{"offsets", SLType::kFloat, kMaxStops },
{"point0", SLType::kFloat2 },
{"point1", SLType::kFloat2 },
{"radius0", SLType::kFloat },
{"radius1", SLType::kFloat },
};
static constexpr int kNumSolidUniforms = 1;
static constexpr Uniform kSolidUniforms[kNumSolidUniforms] {
{"color", SLType::kFloat4 }
};
static const char* kGradientSkSL =
// TODO: This should use local coords
"float2 pos = sk_FragCoord.xy;\n"
"float2 delta = point1 - point0;\n"
"float2 pt = pos - point0;\n"
"float t = dot(pt, delta) / dot(delta, delta);\n"
"float4 result = colors[0];\n"
"result = mix(result, colors[1],\n"
" clamp((t-offsets[0])/(offsets[1]-offsets[0]),\n"
" 0, 1));\n"
"result = mix(result, colors[2],\n"
" clamp((t-offsets[1])/(offsets[2]-offsets[1]),\n"
" 0, 1));\n"
"result = mix(result, colors[3],\n"
" clamp((t-offsets[2])/(offsets[3]-offsets[2]),\n"
" 0, 1));\n"
"outColor = half4(result);\n";
static const char* kSolidColorSkSL = " outColor = half4(color);\n";
// TODO: kNone is for depth-only draws, so should actually have a fragment output type
// that only defines a [[depth]] attribute but no color calculation.
static const char* kNoneSkSL = "outColor = half4(0.0, 0.0, 1.0, 1.0);\n";
sk_sp<UniformData> make_gradient_uniform_data_common(const void* srcs[kNumGradientUniforms]) {
UniformManager mgr(Layout::kMetal);
// TODO: Given that, for the sprint, we always know the uniforms we could cache 'dataSize'
// for each layout and skip the first call.
size_t dataSize = mgr.writeUniforms(SkSpan<const Uniform>(kGradientUniforms,
kNumGradientUniforms),
nullptr, nullptr, nullptr);
sk_sp<UniformData> result = UniformData::Make(kNumGradientUniforms,
kGradientUniforms,
dataSize);
mgr.writeUniforms(SkSpan<const Uniform>(kGradientUniforms, kNumGradientUniforms),
srcs, result->offsets(), result->data());
return result;
}
sk_sp<UniformData> make_linear_gradient_uniform_data(SkPoint startPoint,
SkPoint endPoint,
SkColor4f colors[kMaxStops],
float offsets[kMaxStops]) {
float unusedRadii[2] = { 0.0f, 0.0f };
const void* srcs[kNumGradientUniforms] = {
colors,
offsets,
&startPoint,
&endPoint,
&unusedRadii[0],
&unusedRadii[1],
};
return make_gradient_uniform_data_common(srcs);
};
sk_sp<UniformData> make_radial_gradient_uniform_data(SkPoint point,
float radius,
SkColor4f colors[kMaxStops],
float offsets[kMaxStops]) {
SkPoint unusedPoint = {0.0f, 0.0f};
float unusedRadius = 0.0f;
const void* srcs[kNumGradientUniforms] = {
colors,
offsets,
&point,
&unusedPoint,
&radius,
&unusedRadius,
};
return make_gradient_uniform_data_common(srcs);
};
sk_sp<UniformData> make_sweep_gradient_uniform_data(SkPoint point,
SkColor4f colors[kMaxStops],
float offsets[kMaxStops]) {
SkPoint unusedPoint = {0.0f, 0.0f};
float unusedRadii[2] = {0.0f, 0.0f};
const void* srcs[kNumGradientUniforms] = {
colors,
offsets,
&point,
&unusedPoint,
&unusedRadii[0],
&unusedRadii[1],
};
return make_gradient_uniform_data_common(srcs);
};
sk_sp<UniformData> make_conical_gradient_uniform_data(SkPoint point0,
SkPoint point1,
float radius0,
float radius1,
SkColor4f colors[kMaxStops],
float offsets[kMaxStops]) {
const void* srcs[kNumGradientUniforms] = {
colors,
offsets,
&point0,
&point1,
&radius0,
&radius1,
};
return make_gradient_uniform_data_common(srcs);
};
void to_color4fs(int numColors, SkColor colors[kMaxStops], SkColor4f color4fs[kMaxStops]) {
SkASSERT(numColors >= 2 && numColors <= kMaxStops);
int i;
for (i = 0; i < numColors; ++i) {
color4fs[i] = SkColor4f::FromColor(colors[i]);
}
for ( ; i < kMaxStops; ++i) {
color4fs[i] = color4fs[numColors-1];
}
}
void expand_stops(int numStops, float offsets[kMaxStops]) {
SkASSERT(numStops >= 2 && numStops <= kMaxStops);
for (int i = numStops ; i < kMaxStops; ++i) {
offsets[i] = offsets[numStops-1];
}
}
sk_sp<UniformData> make_solid_uniform_data(SkColor4f color) {
UniformManager mgr(Layout::kMetal);
size_t dataSize = mgr.writeUniforms(SkSpan<const Uniform>(kSolidUniforms, kNumSolidUniforms),
nullptr, nullptr, nullptr);
sk_sp<UniformData> result = UniformData::Make(kNumSolidUniforms, kSolidUniforms, dataSize);
const void* srcs[kNumSolidUniforms] = { &color };
mgr.writeUniforms(SkSpan<const Uniform>(kSolidUniforms, kNumSolidUniforms),
srcs, result->offsets(), result->data());
return result;
}
} // anonymous namespace
sk_sp<UniformData> UniformData::Make(int count,
const Uniform* uniforms,
size_t dataSize) {
// TODO: the offsets and data should just be allocated right after UniformData in an arena
uint32_t* offsets = new uint32_t[count];
char* data = new char[dataSize];
return sk_sp<UniformData>(new UniformData(count, uniforms, offsets, data, dataSize));
}
std::tuple<SkUniquePaintParamsID, sk_sp<UniformData>> ExtractPaintData(Context* context,
const PaintParams& p) {
SkPaintParamsKey key;
sk_sp<UniformData> uniforms;
if (auto s = p.shader()) {
SkColor colors[kMaxStops];
SkColor4f color4fs[kMaxStops];
float offsets[kMaxStops];
SkShader::GradientInfo gradInfo;
gradInfo.fColorCount = kMaxStops;
gradInfo.fColors = colors;
gradInfo.fColorOffsets = offsets;
SkShader::GradientType type = s->asAGradient(&gradInfo);
if (gradInfo.fColorCount > kMaxStops) {
type = SkShader::GradientType::kNone_GradientType;
}
switch (type) {
case SkShader::kLinear_GradientType: {
to_color4fs(gradInfo.fColorCount, colors, color4fs);
expand_stops(gradInfo.fColorCount, offsets);
GradientShaderBlocks::AddToKey(&key, type, gradInfo.fTileMode);
uniforms = make_linear_gradient_uniform_data(gradInfo.fPoint[0],
gradInfo.fPoint[1],
color4fs,
offsets);
} break;
case SkShader::kRadial_GradientType: {
to_color4fs(gradInfo.fColorCount, colors, color4fs);
expand_stops(gradInfo.fColorCount, offsets);
GradientShaderBlocks::AddToKey(&key, type, gradInfo.fTileMode);
uniforms = make_radial_gradient_uniform_data(gradInfo.fPoint[0],
gradInfo.fRadius[0],
color4fs,
offsets);
} break;
case SkShader::kSweep_GradientType:
to_color4fs(gradInfo.fColorCount, colors, color4fs);
expand_stops(gradInfo.fColorCount, offsets);
GradientShaderBlocks::AddToKey(&key, type, gradInfo.fTileMode);
uniforms = make_sweep_gradient_uniform_data(gradInfo.fPoint[0],
color4fs,
offsets);
break;
case SkShader::GradientType::kConical_GradientType:
to_color4fs(gradInfo.fColorCount, colors, color4fs);
expand_stops(gradInfo.fColorCount, offsets);
GradientShaderBlocks::AddToKey(&key, type, gradInfo.fTileMode);
uniforms = make_conical_gradient_uniform_data(gradInfo.fPoint[0],
gradInfo.fPoint[1],
gradInfo.fRadius[0],
gradInfo.fRadius[1],
color4fs,
offsets);
break;
case SkShader::GradientType::kColor_GradientType:
// TODO: The solid color gradient type should use its color, not
// the paint color
case SkShader::GradientType::kNone_GradientType:
default:
SolidColorShaderBlock::AddToKey(&key);
uniforms = make_solid_uniform_data(p.color());
break;
}
} else {
// Solid colored paint
SolidColorShaderBlock::AddToKey(&key);
uniforms = make_solid_uniform_data(p.color());
}
BlendModeBlock::AddToKey(&key, p.blendMode());
auto entry = context->priv().shaderCodeDictionary()->findOrCreate(key);
return { entry->uniqueID(), std::move(uniforms) };
}
SkSpan<const Uniform> GetUniforms(CodeSnippetID snippetID) {
switch (snippetID) {
case CodeSnippetID::kDepthStencilOnlyDraw:
return {nullptr, 0};
case CodeSnippetID::kLinearGradientShader:
return SkMakeSpan(kGradientUniforms, kNumGradientUniforms);
case CodeSnippetID::kSolidColorShader:
case CodeSnippetID::kRadialGradientShader:
case CodeSnippetID::kSweepGradientShader:
case CodeSnippetID::kConicalGradientShader:
default:
return SkMakeSpan(kSolidUniforms, kNumSolidUniforms);
}
}
const char* GetShaderSkSL(CodeSnippetID snippetID) {
switch (snippetID) {
case CodeSnippetID::kDepthStencilOnlyDraw:
return kNoneSkSL;
case CodeSnippetID::kLinearGradientShader:
return kGradientSkSL;
case CodeSnippetID::kSolidColorShader:
case CodeSnippetID::kRadialGradientShader:
case CodeSnippetID::kSweepGradientShader:
case CodeSnippetID::kConicalGradientShader:
default:
return kSolidColorSkSL;
}
}
} // namespace skgpu