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 {
|
enum Flags {
|
||||||
kArray_Flag = 0x1,
|
kArray_Flag = 0x1,
|
||||||
|
kMarker_Flag = 0x2,
|
||||||
|
kMarkerNormals_Flag = 0x4,
|
||||||
};
|
};
|
||||||
|
|
||||||
SkString fName;
|
SkString fName;
|
||||||
@ -65,12 +67,15 @@ public:
|
|||||||
Type fType;
|
Type fType;
|
||||||
int fCount;
|
int fCount;
|
||||||
uint32_t fFlags;
|
uint32_t fFlags;
|
||||||
|
uint32_t fMarker;
|
||||||
|
|
||||||
#if SK_SUPPORT_GPU
|
#if SK_SUPPORT_GPU
|
||||||
GrSLType fGPUType;
|
GrSLType fGPUType;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool isArray() const { return SkToBool(fFlags & kArray_Flag); }
|
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;
|
size_t sizeInBytes() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -377,8 +377,8 @@ public:
|
|||||||
in fragmentProcessor color_map;
|
in fragmentProcessor color_map;
|
||||||
in fragmentProcessor normal_map;
|
in fragmentProcessor normal_map;
|
||||||
|
|
||||||
uniform float4x4 localToWorld;
|
layout (marker=local_to_world) uniform float4x4 localToWorld;
|
||||||
uniform float4x4 localToWorldAdjInv;
|
layout (marker=normals(local_to_world)) uniform float4x4 localToWorldAdjInv;
|
||||||
uniform float3 lightPos;
|
uniform float3 lightPos;
|
||||||
|
|
||||||
float3 convert_normal_sample(half4 c) {
|
float3 convert_normal_sample(half4 c) {
|
||||||
@ -413,27 +413,11 @@ public:
|
|||||||
return;
|
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 {
|
struct Uniforms {
|
||||||
SkM44 fLocalToWorld;
|
SkM44 fLocalToWorld; // Automatically populated, via layout(marker)
|
||||||
SkM44 fLocalToWorldAdjInv;
|
SkM44 fLocalToWorldAdjInv; // Ditto
|
||||||
SkV3 fLightPos;
|
SkV3 fLightPos;
|
||||||
} uni;
|
} uni;
|
||||||
uni.fLocalToWorld = this->localToWorld(canvas);
|
|
||||||
uni.fLocalToWorldAdjInv = adj_inv(uni.fLocalToWorld);
|
|
||||||
uni.fLightPos = fLight.computeWorldPos(fSphere);
|
uni.fLightPos = fLight.computeWorldPos(fSphere);
|
||||||
|
|
||||||
sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
|
sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "include/effects/SkRuntimeEffect.h"
|
#include "include/effects/SkRuntimeEffect.h"
|
||||||
#include "include/private/SkChecksum.h"
|
#include "include/private/SkChecksum.h"
|
||||||
#include "include/private/SkMutex.h"
|
#include "include/private/SkMutex.h"
|
||||||
|
#include "src/core/SkMatrixProvider.h"
|
||||||
#include "src/core/SkRasterPipeline.h"
|
#include "src/core/SkRasterPipeline.h"
|
||||||
#include "src/core/SkReadBuffer.h"
|
#include "src/core/SkReadBuffer.h"
|
||||||
#include "src/core/SkUtils.h"
|
#include "src/core/SkUtils.h"
|
||||||
@ -52,6 +53,25 @@ private:
|
|||||||
SkSL::Compiler* SharedCompiler::gCompiler = nullptr;
|
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) {
|
SkRuntimeEffect::EffectResult SkRuntimeEffect::Make(SkString sksl) {
|
||||||
SkSL::SharedCompiler compiler;
|
SkSL::SharedCompiler compiler;
|
||||||
auto program = compiler->convertProgram(SkSL::Program::kPipelineStage_Kind,
|
auto program = compiler->convertProgram(SkSL::Program::kPipelineStage_Kind,
|
||||||
@ -202,6 +222,18 @@ SkRuntimeEffect::EffectResult SkRuntimeEffect::Make(SkString sksl) {
|
|||||||
break;
|
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) {
|
if (v.fType != Variable::Type::kBool) {
|
||||||
offset = SkAlign4(offset);
|
offset = SkAlign4(offset);
|
||||||
}
|
}
|
||||||
@ -738,7 +770,35 @@ public:
|
|||||||
if (!this->totalLocalMatrix(args.fPreLocalMatrix)->invert(&matrix)) {
|
if (!this->totalLocalMatrix(args.fPreLocalMatrix)->invert(&matrix)) {
|
||||||
return nullptr;
|
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) {
|
for (const auto& child : fChildren) {
|
||||||
auto childFP = child ? as_SB(child)->asFragmentProcessor(args) : nullptr;
|
auto childFP = child ? as_SB(child)->asFragmentProcessor(args) : nullptr;
|
||||||
if (!childFP) {
|
if (!childFP) {
|
||||||
|
@ -288,6 +288,17 @@ std::unique_ptr<VarDeclarations> IRGenerator::convertVarDeclarations(const ASTNo
|
|||||||
if (modifiers.fLayout.fKey && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
|
if (modifiers.fLayout.fKey && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
|
||||||
fErrors.error(decls.fOffset, "'key' is not permitted on 'uniform' variables");
|
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 (modifiers.fFlags & Modifiers::kVarying_Flag) {
|
||||||
if (fKind != Program::kPipelineStage_Kind) {
|
if (fKind != Program::kPipelineStage_Kind) {
|
||||||
fErrors.error(decls.fOffset, "'varying' is only permitted in runtime effects");
|
fErrors.error(decls.fOffset, "'varying' is only permitted in runtime effects");
|
||||||
|
@ -85,6 +85,7 @@ void Parser::InitLayoutMap() {
|
|||||||
TOKEN(TRIANGLES_ADJACENCY, "triangles_adjacency");
|
TOKEN(TRIANGLES_ADJACENCY, "triangles_adjacency");
|
||||||
TOKEN(MAX_VERTICES, "max_vertices");
|
TOKEN(MAX_VERTICES, "max_vertices");
|
||||||
TOKEN(INVOCATIONS, "invocations");
|
TOKEN(INVOCATIONS, "invocations");
|
||||||
|
TOKEN(MARKER, "marker");
|
||||||
TOKEN(WHEN, "when");
|
TOKEN(WHEN, "when");
|
||||||
TOKEN(KEY, "key");
|
TOKEN(KEY, "key");
|
||||||
TOKEN(TRACKED, "tracked");
|
TOKEN(TRACKED, "tracked");
|
||||||
@ -771,14 +772,15 @@ Layout Parser::layout() {
|
|||||||
Layout::Primitive primitive = Layout::kUnspecified_Primitive;
|
Layout::Primitive primitive = Layout::kUnspecified_Primitive;
|
||||||
int maxVertices = -1;
|
int maxVertices = -1;
|
||||||
int invocations = -1;
|
int invocations = -1;
|
||||||
|
StringFragment marker;
|
||||||
StringFragment when;
|
StringFragment when;
|
||||||
Layout::Key key = Layout::kNo_Key;
|
Layout::Key key = Layout::kNo_Key;
|
||||||
Layout::CType ctype = Layout::CType::kDefault;
|
Layout::CType ctype = Layout::CType::kDefault;
|
||||||
if (this->checkNext(Token::Kind::TK_LAYOUT)) {
|
if (this->checkNext(Token::Kind::TK_LAYOUT)) {
|
||||||
if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
|
if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
|
||||||
return Layout(flags, location, offset, binding, index, set, builtin,
|
return Layout(flags, location, offset, binding, index, set, builtin,
|
||||||
inputAttachmentIndex, format, primitive, maxVertices, invocations, when,
|
inputAttachmentIndex, format, primitive, maxVertices, invocations, marker,
|
||||||
key, ctype);
|
when, key, ctype);
|
||||||
}
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Token t = this->nextToken();
|
Token t = this->nextToken();
|
||||||
@ -894,6 +896,9 @@ Layout Parser::layout() {
|
|||||||
case LayoutToken::INVOCATIONS:
|
case LayoutToken::INVOCATIONS:
|
||||||
invocations = this->layoutInt();
|
invocations = this->layoutInt();
|
||||||
break;
|
break;
|
||||||
|
case LayoutToken::MARKER:
|
||||||
|
marker = this->layoutCode();
|
||||||
|
break;
|
||||||
case LayoutToken::WHEN:
|
case LayoutToken::WHEN:
|
||||||
when = this->layoutCode();
|
when = this->layoutCode();
|
||||||
break;
|
break;
|
||||||
@ -921,7 +926,7 @@ Layout Parser::layout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Layout(flags, location, offset, binding, index, set, builtin, inputAttachmentIndex,
|
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 |
|
/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE |
|
||||||
|
@ -68,6 +68,7 @@ public:
|
|||||||
TRIANGLES_ADJACENCY,
|
TRIANGLES_ADJACENCY,
|
||||||
MAX_VERTICES,
|
MAX_VERTICES,
|
||||||
INVOCATIONS,
|
INVOCATIONS,
|
||||||
|
MARKER,
|
||||||
WHEN,
|
WHEN,
|
||||||
KEY,
|
KEY,
|
||||||
TRACKED,
|
TRACKED,
|
||||||
|
@ -1823,7 +1823,7 @@ SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, O
|
|||||||
SkASSERT(fProgram.fSettings.fRTHeightOffset >= 0);
|
SkASSERT(fProgram.fSettings.fRTHeightOffset >= 0);
|
||||||
fields.emplace_back(Modifiers(Layout(0, -1, fProgram.fSettings.fRTHeightOffset, -1,
|
fields.emplace_back(Modifiers(Layout(0, -1, fProgram.fSettings.fRTHeightOffset, -1,
|
||||||
-1, -1, -1, -1, Layout::Format::kUnspecified,
|
-1, -1, -1, -1, Layout::Format::kUnspecified,
|
||||||
Layout::kUnspecified_Primitive, -1, -1, "",
|
Layout::kUnspecified_Primitive, -1, -1, "", "",
|
||||||
Layout::kNo_Key, Layout::CType::kDefault), 0),
|
Layout::kNo_Key, Layout::CType::kDefault), 0),
|
||||||
SKSL_RTHEIGHT_NAME, fContext.fFloat_Type.get());
|
SKSL_RTHEIGHT_NAME, fContext.fFloat_Type.get());
|
||||||
StringFragment name("sksl_synthetic_uniforms");
|
StringFragment name("sksl_synthetic_uniforms");
|
||||||
@ -1834,7 +1834,7 @@ SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, O
|
|||||||
SkASSERT(binding != -1 && set != -1);
|
SkASSERT(binding != -1 && set != -1);
|
||||||
|
|
||||||
Layout layout(0, -1, -1, binding, -1, set, -1, -1, Layout::Format::kUnspecified,
|
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);
|
Layout::CType::kDefault);
|
||||||
Variable* intfVar = (Variable*) fSynthetics.takeOwnership(std::unique_ptr<Symbol>(
|
Variable* intfVar = (Variable*) fSynthetics.takeOwnership(std::unique_ptr<Symbol>(
|
||||||
new Variable(-1,
|
new Variable(-1,
|
||||||
|
@ -189,7 +189,7 @@ struct Layout {
|
|||||||
|
|
||||||
Layout(int flags, int location, int offset, int binding, int index, int set, int builtin,
|
Layout(int flags, int location, int offset, int binding, int index, int set, int builtin,
|
||||||
int inputAttachmentIndex, Format format, Primitive primitive, int maxVertices,
|
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)
|
: fFlags(flags)
|
||||||
, fLocation(location)
|
, fLocation(location)
|
||||||
, fOffset(offset)
|
, fOffset(offset)
|
||||||
@ -202,6 +202,7 @@ struct Layout {
|
|||||||
, fPrimitive(primitive)
|
, fPrimitive(primitive)
|
||||||
, fMaxVertices(maxVertices)
|
, fMaxVertices(maxVertices)
|
||||||
, fInvocations(invocations)
|
, fInvocations(invocations)
|
||||||
|
, fMarker(marker)
|
||||||
, fWhen(when)
|
, fWhen(when)
|
||||||
, fKey(key)
|
, fKey(key)
|
||||||
, fCType(ctype) {}
|
, fCType(ctype) {}
|
||||||
@ -377,6 +378,10 @@ struct Layout {
|
|||||||
result += separator + "invocations = " + to_string(fInvocations);
|
result += separator + "invocations = " + to_string(fInvocations);
|
||||||
separator = ", ";
|
separator = ", ";
|
||||||
}
|
}
|
||||||
|
if (fMarker.fLength) {
|
||||||
|
result += separator + "marker = " + fMarker;
|
||||||
|
separator = ", ";
|
||||||
|
}
|
||||||
if (fWhen.fLength) {
|
if (fWhen.fLength) {
|
||||||
result += separator + "when = " + fWhen;
|
result += separator + "when = " + fWhen;
|
||||||
separator = ", ";
|
separator = ", ";
|
||||||
@ -403,6 +408,7 @@ struct Layout {
|
|||||||
fPrimitive == other.fPrimitive &&
|
fPrimitive == other.fPrimitive &&
|
||||||
fMaxVertices == other.fMaxVertices &&
|
fMaxVertices == other.fMaxVertices &&
|
||||||
fInvocations == other.fInvocations &&
|
fInvocations == other.fInvocations &&
|
||||||
|
fMarker == other.fMarker &&
|
||||||
fWhen == other.fWhen &&
|
fWhen == other.fWhen &&
|
||||||
fKey == other.fKey &&
|
fKey == other.fKey &&
|
||||||
fCType == other.fCType;
|
fCType == other.fCType;
|
||||||
@ -428,6 +434,8 @@ struct Layout {
|
|||||||
Primitive fPrimitive;
|
Primitive fPrimitive;
|
||||||
int fMaxVertices;
|
int fMaxVertices;
|
||||||
int fInvocations;
|
int fInvocations;
|
||||||
|
// marker refers to matrices tagged on the SkCanvas with markCTM
|
||||||
|
StringFragment fMarker;
|
||||||
StringFragment fWhen;
|
StringFragment fWhen;
|
||||||
Key fKey;
|
Key fKey;
|
||||||
CType fCType;
|
CType fCType;
|
||||||
|
Loading…
Reference in New Issue
Block a user