Add layout(marker) to SkSL
Allows for uniforms to be automatically populated with marked canvas matrices: SkSL: layout (marker=localToWorld) uniform float4x4 localToWorldMatrix; C++: canvas->concat(...); canvas->markCTM("localToWorld"); canvas->concat(...); canvas->drawFoo(...); Any runtime effects created with that SkSL will have their localToWorldMatrix uniform filled in with the CTM, ignoring any transformation that happened before/above the markCTM call. The marker needs to be a sequence of alphanumeric or underscore characters, and match the string used in markCTM. The marker can also be of the form "normals(<string>)", in which case the uniform will be filled in with the transpose of the inverse of the upper-left 3x3 portion of the CTM identified by <string>. This is helpful for transforming normal vectors, as is often done in lighting. Change-Id: I7d1ca4dc3f8fabbe91b9bd2c8632013f26d2321a Reviewed-on: https://skia-review.googlesource.com/c/skia/+/285376 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
3cf3a8c64f
commit
f59a961dc9
@ -56,7 +56,9 @@ public:
|
||||
};
|
||||
|
||||
enum Flags {
|
||||
kArray_Flag = 0x1,
|
||||
kArray_Flag = 0x1,
|
||||
kMarker_Flag = 0x2,
|
||||
kMarkerNormals_Flag = 0x4,
|
||||
};
|
||||
|
||||
SkString fName;
|
||||
@ -65,12 +67,15 @@ public:
|
||||
Type fType;
|
||||
int fCount;
|
||||
uint32_t fFlags;
|
||||
uint32_t fMarker;
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
GrSLType fGPUType;
|
||||
#endif
|
||||
|
||||
bool isArray() const { return SkToBool(fFlags & kArray_Flag); }
|
||||
bool hasMarker() const { return SkToBool(fFlags & kMarker_Flag); }
|
||||
bool hasNormalsMarker() const { return SkToBool(fFlags & kMarkerNormals_Flag); }
|
||||
size_t sizeInBytes() const;
|
||||
};
|
||||
|
||||
|
@ -377,8 +377,8 @@ public:
|
||||
in fragmentProcessor color_map;
|
||||
in fragmentProcessor normal_map;
|
||||
|
||||
uniform float4x4 localToWorld;
|
||||
uniform float4x4 localToWorldAdjInv;
|
||||
layout (marker=local_to_world) uniform float4x4 localToWorld;
|
||||
layout (marker=normals(local_to_world)) uniform float4x4 localToWorldAdjInv;
|
||||
uniform float3 lightPos;
|
||||
|
||||
float3 convert_normal_sample(half4 c) {
|
||||
@ -413,27 +413,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto adj_inv = [](const SkM44& m) {
|
||||
// Normals need to be transformed by the inverse-transpose of the upper-left 3x3 portion
|
||||
// (scale + rotate) of the local to world matrix. (If the local to world only has
|
||||
// uniform scale, we can use its upper-left 3x3 directly, but we don't know if that's
|
||||
// the case here, so go the extra mile.)
|
||||
SkM44 rot_scale(m.rc(0, 0), m.rc(0, 1), m.rc(0, 2), 0,
|
||||
m.rc(1, 0), m.rc(1, 1), m.rc(1, 2), 0,
|
||||
m.rc(2, 0), m.rc(2, 1), m.rc(2, 2), 0,
|
||||
0, 0, 0, 1);
|
||||
SkM44 inv(SkM44::kUninitialized_Constructor);
|
||||
SkAssertResult(rot_scale.invert(&inv));
|
||||
return inv.transpose();
|
||||
};
|
||||
|
||||
struct Uniforms {
|
||||
SkM44 fLocalToWorld;
|
||||
SkM44 fLocalToWorldAdjInv;
|
||||
SkM44 fLocalToWorld; // Automatically populated, via layout(marker)
|
||||
SkM44 fLocalToWorldAdjInv; // Ditto
|
||||
SkV3 fLightPos;
|
||||
} uni;
|
||||
uni.fLocalToWorld = this->localToWorld(canvas);
|
||||
uni.fLocalToWorldAdjInv = adj_inv(uni.fLocalToWorld);
|
||||
uni.fLightPos = fLight.computeWorldPos(fSphere);
|
||||
|
||||
sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "include/effects/SkRuntimeEffect.h"
|
||||
#include "include/private/SkChecksum.h"
|
||||
#include "include/private/SkMutex.h"
|
||||
#include "src/core/SkMatrixProvider.h"
|
||||
#include "src/core/SkRasterPipeline.h"
|
||||
#include "src/core/SkReadBuffer.h"
|
||||
#include "src/core/SkUtils.h"
|
||||
@ -52,6 +53,25 @@ private:
|
||||
SkSL::Compiler* SharedCompiler::gCompiler = nullptr;
|
||||
}
|
||||
|
||||
// Accepts either "[a-zA-Z0-9_]+" or "normals([a-zA-Z0-9_]+)"
|
||||
static bool parse_marker(const SkSL::StringFragment& marker, uint32_t* id, uint32_t* flags) {
|
||||
SkString s = marker;
|
||||
if (s.startsWith("normals(") && s.endsWith(')')) {
|
||||
*flags |= SkRuntimeEffect::Variable::kMarkerNormals_Flag;
|
||||
s.set(marker.fChars + 8, marker.fLength - 9);
|
||||
}
|
||||
if (s.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < s.size(); ++i) {
|
||||
if (!std::isalnum(s[i]) && s[i] != '_') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*id = SkOpts::hash_fn(s.c_str(), s.size(), 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
SkRuntimeEffect::EffectResult SkRuntimeEffect::Make(SkString sksl) {
|
||||
SkSL::SharedCompiler compiler;
|
||||
auto program = compiler->convertProgram(SkSL::Program::kPipelineStage_Kind,
|
||||
@ -202,6 +222,18 @@ SkRuntimeEffect::EffectResult SkRuntimeEffect::Make(SkString sksl) {
|
||||
break;
|
||||
}
|
||||
|
||||
const SkSL::StringFragment& marker(var.fModifiers.fLayout.fMarker);
|
||||
if (marker.fLength) {
|
||||
// Rules that should be enforced by the IR generator:
|
||||
SkASSERT(v.fQualifier == Variable::Qualifier::kUniform);
|
||||
SkASSERT(v.fType == Variable::Type::kFloat4x4);
|
||||
v.fFlags |= Variable::kMarker_Flag;
|
||||
if (!parse_marker(marker, &v.fMarker, &v.fFlags)) {
|
||||
RETURN_FAILURE("Invalid 'marker' string: '%.*s'",
|
||||
(int)marker.fLength, marker.fChars);
|
||||
}
|
||||
}
|
||||
|
||||
if (v.fType != Variable::Type::kBool) {
|
||||
offset = SkAlign4(offset);
|
||||
}
|
||||
@ -738,7 +770,35 @@ public:
|
||||
if (!this->totalLocalMatrix(args.fPreLocalMatrix)->invert(&matrix)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto fp = GrSkSLFP::Make(args.fContext, fEffect, "runtime_shader", fInputs);
|
||||
// If any of our uniforms are late-bound (eg, layout(marker)), we need to clone the blob
|
||||
sk_sp<SkData> inputs = fInputs;
|
||||
|
||||
for (const auto& v : fEffect->inputs()) {
|
||||
if (v.hasMarker()) {
|
||||
if (inputs == fInputs) {
|
||||
inputs = SkData::MakeWithCopy(fInputs->data(), fInputs->size());
|
||||
}
|
||||
SkASSERT(v.fType == SkRuntimeEffect::Variable::Type::kFloat4x4);
|
||||
SkM44* localToMarker = SkTAddOffset<SkM44>(inputs->writable_data(), v.fOffset);
|
||||
if (!args.fMatrixProvider.getLocalToMarker(v.fMarker, localToMarker)) {
|
||||
// We couldn't provide a matrix that was requested by the SkSL
|
||||
SkDebugf("Failed to get marked matrix %u\n", v.fMarker);
|
||||
return nullptr;
|
||||
}
|
||||
if (v.hasNormalsMarker()) {
|
||||
// Normals need to be transformed by the inverse-transpose of the upper-left
|
||||
// 3x3 portion (scale + rotate) of the matrix.
|
||||
localToMarker->setRow(3, {0, 0, 0, 1});
|
||||
localToMarker->setCol(3, {0, 0, 0, 1});
|
||||
if (!localToMarker->invert(localToMarker)) {
|
||||
return nullptr;
|
||||
}
|
||||
*localToMarker = localToMarker->transpose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto fp = GrSkSLFP::Make(args.fContext, fEffect, "runtime_shader", std::move(inputs));
|
||||
for (const auto& child : fChildren) {
|
||||
auto childFP = child ? as_SB(child)->asFragmentProcessor(args) : nullptr;
|
||||
if (!childFP) {
|
||||
|
@ -288,6 +288,17 @@ std::unique_ptr<VarDeclarations> IRGenerator::convertVarDeclarations(const ASTNo
|
||||
if (modifiers.fLayout.fKey && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
|
||||
fErrors.error(decls.fOffset, "'key' is not permitted on 'uniform' variables");
|
||||
}
|
||||
if (modifiers.fLayout.fMarker.fLength) {
|
||||
if (fKind != Program::kPipelineStage_Kind) {
|
||||
fErrors.error(decls.fOffset, "'marker' is only permitted in runtime effects");
|
||||
}
|
||||
if (!(modifiers.fFlags & Modifiers::kUniform_Flag)) {
|
||||
fErrors.error(decls.fOffset, "'marker' is only permitted on 'uniform' variables");
|
||||
}
|
||||
if (*baseType != *fContext.fFloat4x4_Type) {
|
||||
fErrors.error(decls.fOffset, "'marker' is only permitted on float4x4 variables");
|
||||
}
|
||||
}
|
||||
if (modifiers.fFlags & Modifiers::kVarying_Flag) {
|
||||
if (fKind != Program::kPipelineStage_Kind) {
|
||||
fErrors.error(decls.fOffset, "'varying' is only permitted in runtime effects");
|
||||
|
@ -85,6 +85,7 @@ void Parser::InitLayoutMap() {
|
||||
TOKEN(TRIANGLES_ADJACENCY, "triangles_adjacency");
|
||||
TOKEN(MAX_VERTICES, "max_vertices");
|
||||
TOKEN(INVOCATIONS, "invocations");
|
||||
TOKEN(MARKER, "marker");
|
||||
TOKEN(WHEN, "when");
|
||||
TOKEN(KEY, "key");
|
||||
TOKEN(TRACKED, "tracked");
|
||||
@ -771,14 +772,15 @@ Layout Parser::layout() {
|
||||
Layout::Primitive primitive = Layout::kUnspecified_Primitive;
|
||||
int maxVertices = -1;
|
||||
int invocations = -1;
|
||||
StringFragment marker;
|
||||
StringFragment when;
|
||||
Layout::Key key = Layout::kNo_Key;
|
||||
Layout::CType ctype = Layout::CType::kDefault;
|
||||
if (this->checkNext(Token::Kind::TK_LAYOUT)) {
|
||||
if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
|
||||
return Layout(flags, location, offset, binding, index, set, builtin,
|
||||
inputAttachmentIndex, format, primitive, maxVertices, invocations, when,
|
||||
key, ctype);
|
||||
inputAttachmentIndex, format, primitive, maxVertices, invocations, marker,
|
||||
when, key, ctype);
|
||||
}
|
||||
for (;;) {
|
||||
Token t = this->nextToken();
|
||||
@ -894,6 +896,9 @@ Layout Parser::layout() {
|
||||
case LayoutToken::INVOCATIONS:
|
||||
invocations = this->layoutInt();
|
||||
break;
|
||||
case LayoutToken::MARKER:
|
||||
marker = this->layoutCode();
|
||||
break;
|
||||
case LayoutToken::WHEN:
|
||||
when = this->layoutCode();
|
||||
break;
|
||||
@ -921,7 +926,7 @@ Layout Parser::layout() {
|
||||
}
|
||||
}
|
||||
return Layout(flags, location, offset, binding, index, set, builtin, inputAttachmentIndex,
|
||||
format, primitive, maxVertices, invocations, when, key, ctype);
|
||||
format, primitive, maxVertices, invocations, marker, when, key, ctype);
|
||||
}
|
||||
|
||||
/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE |
|
||||
|
@ -68,6 +68,7 @@ public:
|
||||
TRIANGLES_ADJACENCY,
|
||||
MAX_VERTICES,
|
||||
INVOCATIONS,
|
||||
MARKER,
|
||||
WHEN,
|
||||
KEY,
|
||||
TRACKED,
|
||||
|
@ -1823,7 +1823,7 @@ SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, O
|
||||
SkASSERT(fProgram.fSettings.fRTHeightOffset >= 0);
|
||||
fields.emplace_back(Modifiers(Layout(0, -1, fProgram.fSettings.fRTHeightOffset, -1,
|
||||
-1, -1, -1, -1, Layout::Format::kUnspecified,
|
||||
Layout::kUnspecified_Primitive, -1, -1, "",
|
||||
Layout::kUnspecified_Primitive, -1, -1, "", "",
|
||||
Layout::kNo_Key, Layout::CType::kDefault), 0),
|
||||
SKSL_RTHEIGHT_NAME, fContext.fFloat_Type.get());
|
||||
StringFragment name("sksl_synthetic_uniforms");
|
||||
@ -1834,7 +1834,7 @@ SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, O
|
||||
SkASSERT(binding != -1 && set != -1);
|
||||
|
||||
Layout layout(0, -1, -1, binding, -1, set, -1, -1, Layout::Format::kUnspecified,
|
||||
Layout::kUnspecified_Primitive, -1, -1, "", Layout::kNo_Key,
|
||||
Layout::kUnspecified_Primitive, -1, -1, "", "", Layout::kNo_Key,
|
||||
Layout::CType::kDefault);
|
||||
Variable* intfVar = (Variable*) fSynthetics.takeOwnership(std::unique_ptr<Symbol>(
|
||||
new Variable(-1,
|
||||
|
@ -189,7 +189,7 @@ struct Layout {
|
||||
|
||||
Layout(int flags, int location, int offset, int binding, int index, int set, int builtin,
|
||||
int inputAttachmentIndex, Format format, Primitive primitive, int maxVertices,
|
||||
int invocations, StringFragment when, Key key, CType ctype)
|
||||
int invocations, StringFragment marker, StringFragment when, Key key, CType ctype)
|
||||
: fFlags(flags)
|
||||
, fLocation(location)
|
||||
, fOffset(offset)
|
||||
@ -202,6 +202,7 @@ struct Layout {
|
||||
, fPrimitive(primitive)
|
||||
, fMaxVertices(maxVertices)
|
||||
, fInvocations(invocations)
|
||||
, fMarker(marker)
|
||||
, fWhen(when)
|
||||
, fKey(key)
|
||||
, fCType(ctype) {}
|
||||
@ -377,6 +378,10 @@ struct Layout {
|
||||
result += separator + "invocations = " + to_string(fInvocations);
|
||||
separator = ", ";
|
||||
}
|
||||
if (fMarker.fLength) {
|
||||
result += separator + "marker = " + fMarker;
|
||||
separator = ", ";
|
||||
}
|
||||
if (fWhen.fLength) {
|
||||
result += separator + "when = " + fWhen;
|
||||
separator = ", ";
|
||||
@ -403,6 +408,7 @@ struct Layout {
|
||||
fPrimitive == other.fPrimitive &&
|
||||
fMaxVertices == other.fMaxVertices &&
|
||||
fInvocations == other.fInvocations &&
|
||||
fMarker == other.fMarker &&
|
||||
fWhen == other.fWhen &&
|
||||
fKey == other.fKey &&
|
||||
fCType == other.fCType;
|
||||
@ -428,6 +434,8 @@ struct Layout {
|
||||
Primitive fPrimitive;
|
||||
int fMaxVertices;
|
||||
int fInvocations;
|
||||
// marker refers to matrices tagged on the SkCanvas with markCTM
|
||||
StringFragment fMarker;
|
||||
StringFragment fWhen;
|
||||
Key fKey;
|
||||
CType fCType;
|
||||
|
Loading…
Reference in New Issue
Block a user