blob: 0b9579f41ab9bad7464ca305ee6a31666ada9839 [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/UniformManager.h"
#include "include/core/SkPaint.h"
#include "include/private/SkShaderCodeDictionary.h"
#include "include/private/SkUniquePaintParamsID.h"
#include "src/core/SkBlenderBase.h"
#include "src/core/SkKeyHelpers.h"
#include "src/core/SkUniform.h"
#include "src/core/SkUniformData.h"
namespace skgpu {
using GradientData = GradientShaderBlocks::GradientData;
namespace {
// 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 SkUniform kGradientUniforms[kNumGradientUniforms] {
{"colors", SkSLType::kHalf4 , GradientData::kMaxStops },
{"offsets", SkSLType::kFloat, GradientData::kMaxStops },
{"point0", SkSLType::kFloat2 },
{"point1", SkSLType::kFloat2 },
{"radius0", SkSLType::kFloat },
{"radius1", SkSLType::kFloat },
};
static constexpr int kNumSolidUniforms = 1;
static constexpr SkUniform kSolidUniforms[kNumSolidUniforms] {
{"color", SkSLType::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<SkUniformData> make_gradient_uniform_data_common(SkSpan<const SkUniform> uniforms,
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(uniforms, nullptr, nullptr, nullptr);
sk_sp<SkUniformData> result = SkUniformData::Make(uniforms, dataSize);
mgr.writeUniforms(result->uniforms(), srcs, result->offsets(), result->data());
return result;
}
sk_sp<SkUniformData> make_linear_gradient_uniform_data(SkPoint startPoint,
SkPoint endPoint,
SkColor4f colors[GradientData::kMaxStops],
float offsets[GradientData::kMaxStops]) {
static constexpr size_t kExpectedNumUniforms = 6;
SkSpan<const SkUniform> uniforms = skgpu::GetUniforms(CodeSnippetID::kLinearGradientShader);
SkASSERT(uniforms.size() == kExpectedNumUniforms);
float unusedRadii[2] = { 0.0f, 0.0f };
const void* srcs[kExpectedNumUniforms] = {
colors,
offsets,
&startPoint,
&endPoint,
&unusedRadii[0],
&unusedRadii[1],
};
return make_gradient_uniform_data_common(uniforms, srcs);
};
sk_sp<SkUniformData> make_radial_gradient_uniform_data(SkPoint point,
float radius,
SkColor4f colors[GradientData::kMaxStops],
float offsets[GradientData::kMaxStops]) {
static constexpr size_t kExpectedNumUniforms = 6;
SkSpan<const SkUniform> uniforms = skgpu::GetUniforms(CodeSnippetID::kRadialGradientShader);
SkASSERT(uniforms.size() == kExpectedNumUniforms);
SkPoint unusedPoint = {0.0f, 0.0f};
float unusedRadius = 0.0f;
const void* srcs[kExpectedNumUniforms] = {
colors,
offsets,
&point,
&unusedPoint,
&radius,
&unusedRadius,
};
return make_gradient_uniform_data_common(uniforms, srcs);
};
sk_sp<SkUniformData> make_sweep_gradient_uniform_data(SkPoint point,
SkColor4f colors[GradientData::kMaxStops],
float offsets[GradientData::kMaxStops]) {
static constexpr size_t kExpectedNumUniforms = 6;
SkSpan<const SkUniform> uniforms = skgpu::GetUniforms(CodeSnippetID::kSweepGradientShader);
SkASSERT(uniforms.size() == kExpectedNumUniforms);
SkPoint unusedPoint = {0.0f, 0.0f};
float unusedRadii[2] = {0.0f, 0.0f};
const void* srcs[kExpectedNumUniforms] = {
colors,
offsets,
&point,
&unusedPoint,
&unusedRadii[0],
&unusedRadii[1],
};
return make_gradient_uniform_data_common(uniforms, srcs);
};
sk_sp<SkUniformData> make_conical_gradient_uniform_data(SkPoint point0,
SkPoint point1,
float radius0,
float radius1,
SkColor4f colors[GradientData::kMaxStops],
float offsets[GradientData::kMaxStops]) {
static constexpr size_t kExpectedNumUniforms = 6;
SkSpan<const SkUniform> uniforms = skgpu::GetUniforms(CodeSnippetID::kConicalGradientShader);
SkASSERT(uniforms.size() == kExpectedNumUniforms);
const void* srcs[kExpectedNumUniforms] = {
colors,
offsets,
&point0,
&point1,
&radius0,
&radius1,
};
return make_gradient_uniform_data_common(uniforms, srcs);
};
sk_sp<SkUniformData> make_solid_uniform_data(SkColor4f color) {
static constexpr size_t kExpectedNumUniforms = 1;
SkSpan<const SkUniform> uniforms = skgpu::GetUniforms(CodeSnippetID::kSolidColorShader);
SkASSERT(uniforms.size() == kExpectedNumUniforms);
UniformManager mgr(Layout::kMetal);
size_t dataSize = mgr.writeUniforms(uniforms, nullptr, nullptr, nullptr);
sk_sp<SkUniformData> result = SkUniformData::Make(uniforms, dataSize);
const void* srcs[kExpectedNumUniforms] = { &color };
mgr.writeUniforms(result->uniforms(), srcs, result->offsets(), result->data());
return result;
}
} // anonymous namespace
std::tuple<SkUniquePaintParamsID, std::unique_ptr<SkUniformBlock>> ExtractPaintData(
SkShaderCodeDictionary* dictionary, const PaintParams& p) {
SkPaintParamsKey key;
sk_sp<SkUniformData> uniforms;
std::unique_ptr<SkUniformBlock> block = std::make_unique<SkUniformBlock>();
// TODO: add UniformData generation to PaintParams::toKey and use it here
if (auto s = p.shader()) {
SkColor colors[GradientData::kMaxStops];
float offsets[GradientData::kMaxStops];
SkShader::GradientInfo gradInfo;
gradInfo.fColorCount = GradientData::kMaxStops;
gradInfo.fColors = colors;
gradInfo.fColorOffsets = offsets;
SkShader::GradientType type = s->asAGradient(&gradInfo);
if (gradInfo.fColorCount > GradientData::kMaxStops) {
type = SkShader::GradientType::kNone_GradientType;
}
GradientData data(type, gradInfo.fPoint, gradInfo.fRadius,
gradInfo.fTileMode, gradInfo.fColorCount,
colors, offsets);
switch (type) {
case SkShader::kLinear_GradientType: {
GradientShaderBlocks::AddToKey(SkBackend::kGraphite,
&key,
block.get(),
data);
// TODO: move this into GradientShaderBlocks::AddToKey
uniforms = make_linear_gradient_uniform_data(data.fPoints[0],
data.fPoints[1],
data.fColor4fs,
data.fOffsets);
} break;
case SkShader::kRadial_GradientType: {
GradientShaderBlocks::AddToKey(SkBackend::kGraphite,
&key,
block.get(),
data);
// TODO: move this into GradientShaderBlocks::AddToKey
uniforms = make_radial_gradient_uniform_data(data.fPoints[0],
data.fRadii[0],
data.fColor4fs,
data.fOffsets);
} break;
case SkShader::kSweep_GradientType:
GradientShaderBlocks::AddToKey(SkBackend::kGraphite,
&key,
block.get(),
data);
// TODO: move this into GradientShaderBlocks::AddToKey
uniforms = make_sweep_gradient_uniform_data(data.fPoints[0],
data.fColor4fs,
data.fOffsets);
break;
case SkShader::GradientType::kConical_GradientType:
GradientShaderBlocks::AddToKey(SkBackend::kGraphite,
&key,
block.get(),
data);
// TODO: move this into GradientShaderBlocks::AddToKey
uniforms = make_conical_gradient_uniform_data(data.fPoints[0],
data.fPoints[1],
data.fRadii[0],
data.fRadii[1],
data.fColor4fs,
data.fOffsets);
break;
case SkShader::GradientType::kColor_GradientType: [[fallthrough]];
// TODO: The solid color gradient type should use its color, not
// the paint color
case SkShader::GradientType::kNone_GradientType: [[fallthrough]];
default:
SolidColorShaderBlock::AddToKey(SkBackend::kGraphite, &key, block.get(), p.color());
// TODO: move this into SolidColorShaderBlock::AddToKey
uniforms = make_solid_uniform_data(p.color());
break;
}
} else {
// Solid colored paint
SolidColorShaderBlock::AddToKey(SkBackend::kGraphite, &key, block.get(), p.color());
// TODO: move this into SolidColorShaderBlock::AddToKey
uniforms = make_solid_uniform_data(p.color());
}
if (p.blender()) {
as_BB(p.blender())->addToKey(dictionary, SkBackend::kGraphite, &key, block.get());
} else {
BlendModeBlock::AddToKey(SkBackend::kGraphite, &key, block.get(), SkBlendMode::kSrcOver);
}
auto entry = dictionary->findOrCreate(key);
block->add(std::move(uniforms));
return { entry->uniqueID(), std::move(block) };
}
SkSpan<const SkUniform> GetUniforms(CodeSnippetID snippetID) {
switch (snippetID) {
case CodeSnippetID::kDepthStencilOnlyDraw:
return {nullptr, 0};
case CodeSnippetID::kLinearGradientShader: [[fallthrough]];
case CodeSnippetID::kRadialGradientShader: [[fallthrough]];
case CodeSnippetID::kSweepGradientShader: [[fallthrough]];
case CodeSnippetID::kConicalGradientShader:
return SkMakeSpan(kGradientUniforms, kNumGradientUniforms);
case CodeSnippetID::kSolidColorShader:
default:
return SkMakeSpan(kSolidUniforms, kNumSolidUniforms);
}
}
const char* GetShaderSkSL(CodeSnippetID snippetID) {
switch (snippetID) {
case CodeSnippetID::kDepthStencilOnlyDraw:
return kNoneSkSL;
case CodeSnippetID::kLinearGradientShader: [[fallthrough]];
case CodeSnippetID::kRadialGradientShader: [[fallthrough]];
case CodeSnippetID::kSweepGradientShader: [[fallthrough]];
case CodeSnippetID::kConicalGradientShader:
return kGradientSkSL;
case CodeSnippetID::kSolidColorShader:
default:
return kSolidColorSkSL;
}
}
} // namespace skgpu