Skip to content

Commit 7eaabf0

Browse files
committed
Add support for swizzling triple types.
1 parent a95adca commit 7eaabf0

File tree

6 files changed

+302
-18
lines changed

6 files changed

+302
-18
lines changed

src/liboslcomp/ast.cpp

Lines changed: 105 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,65 @@ ASTstructselect::print (std::ostream &out, int indentlevel) const
719719

720720

721721

722+
ASTswizzle::ASTswizzle (OSLCompilerImpl *comp, ASTNode *expr, ustring field) :
723+
ASTfieldselect(swizzle_node, comp, expr, field)
724+
{
725+
if (field.size() == 1) m_typespec = TypeDesc::TypeFloat;
726+
else m_typespec = TypeDesc::TypeVector;
727+
}
728+
729+
730+
731+
size_t ASTswizzle::indices (ustring components, int *indexes, size_t N, bool consts)
732+
{
733+
size_t i = 0;
734+
do {
735+
switch (components[i]) {
736+
case 'r':
737+
case 'x': indexes[i] = 0; break;
738+
case 'g':
739+
case 'y': indexes[i] = 1; break;
740+
case 'b':
741+
case 'z': indexes[i] = 2; break;
742+
743+
case '0':
744+
if (!consts)
745+
return 0;
746+
indexes[i] = const_offset;
747+
break;
748+
case '1':
749+
if (!consts)
750+
return 0;
751+
indexes[i] = const_offset + 1;
752+
break;
753+
754+
case '\0': return i;
755+
756+
default: return 0;
757+
}
758+
} while (++i < N);
759+
760+
return i;
761+
}
762+
763+
764+
765+
const char *
766+
ASTswizzle::childname (size_t i) const
767+
{
768+
return type_c_str(m_typespec);
769+
}
770+
771+
772+
void
773+
ASTswizzle::print (std::ostream &out, int indentlevel) const
774+
{
775+
ASTNode::print (out, indentlevel);
776+
indent (out, indentlevel+1);
777+
out << "components " << field() << "\n";
778+
}
779+
780+
722781
ASTNode* ASTfieldselect::create (OSLCompilerImpl *comp, ASTNode *expr,
723782
ustring field)
724783
{
@@ -728,25 +787,55 @@ ASTNode* ASTfieldselect::create (OSLCompilerImpl *comp, ASTNode *expr,
728787
const TypeSpec &type = expr->nodetype() != structselect_node ? expr->typespec() :
729788
static_cast<ASTstructselect*>(expr)->fieldsym()->typespec();
730789

731-
if (type.aggregate() == TypeDesc::VEC3) {
732-
int component = -1;
733-
switch (field[0]) {
734-
case 'r': component = 0; break;
735-
case 'g': component = 1; break;
736-
case 'b': component = 2; break;
790+
if (type.aggregate() == TypeDesc::VEC3 && field.size() <= 3) {
791+
// Early out swizle to native component ordering.
792+
if (field == "rgb" || field == "xyz")
793+
return expr;
737794

738-
case 'x': component = 0; break;
739-
case 'y': component = 1; break;
740-
case 'z': component = 2; break;
795+
ASTindex* index = expr->nodetype() != index_node ? nullptr : static_cast<ASTindex*>(expr);
741796

742-
default: break;
743-
}
744-
if (component != -1) {
745-
if (expr->nodetype() == index_node) {
746-
static_cast<ASTindex*>(expr)->extend(new ASTliteral (comp, component));
747-
return expr;
797+
int indexes[3];
798+
switch (ASTswizzle::indices (field, indexes, 3, true)) {
799+
case 1: {
800+
if (!index)
801+
return new ASTindex (comp, expr, new ASTliteral (comp, indexes[0]));
802+
index->extend(new ASTliteral (comp, indexes[0]));
803+
return index;
748804
}
749-
return new ASTindex (comp, expr, new ASTliteral (comp, component));
805+
806+
case 3: {
807+
// Don't leak soon to be unused expr node
808+
std::unique_ptr<ASTNode> cleanup(index);
809+
ASTNode* index0 = nullptr;
810+
if (index) {
811+
index0 = index->index().get();
812+
expr = index->lvalue().get();
813+
}
814+
815+
ASTNode *args[3];
816+
for (int i = 0; i < 3; ++i) {
817+
if (indexes[i] >= 0) {
818+
args[i] = new ASTliteral (comp, indexes[i]);
819+
if (i == 0 && index) {
820+
// Re-use expr by extending the ASTindex.
821+
index->extend (args[i]);
822+
args[0] = cleanup.release ();
823+
} else {
824+
args[i] = !index0 ? new ASTindex (comp, expr, args[i]) :
825+
new ASTindex (comp, expr, index0, args[i]);
826+
}
827+
} else {
828+
float cval = indexes[i] - ASTswizzle::const_offset;
829+
ASSERT ((cval==0) || (cval==1));
830+
args[i] = new ASTliteral (comp, cval);
831+
}
832+
}
833+
args[0]->append (args[1]);
834+
args[1]->append (args[2]);
835+
return new ASTswizzle (comp, args[0], field);
836+
}
837+
838+
default: break;
750839
}
751840
}
752841

src/liboslcomp/ast.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class ASTNode : public OIIO::RefCnt {
6262
unknown_node, shader_declaration_node, function_declaration_node,
6363
variable_declaration_node, compound_initializer_node,
6464
variable_ref_node, preincdec_node, postincdec_node,
65-
index_node, structselect_node,
65+
index_node, structselect_node, swizzle_node,
6666
conditional_statement_node,
6767
loop_statement_node, loopmod_statement_node, return_statement_node,
6868
binary_expression_node, unary_expression_node,
@@ -644,6 +644,34 @@ class ASTstructselect : public ASTfieldselect
644644

645645

646646

647+
class ASTswizzle : public ASTfieldselect
648+
{
649+
public:
650+
ASTswizzle (OSLCompilerImpl *comp, ASTNode *expr, ustring field);
651+
652+
const char *nodetypename () const { return "swizzle"; }
653+
const char *childname (size_t i) const;
654+
void print (std::ostream &out, int indentlevel=0) const;
655+
TypeSpec typecheck (TypeSpec expected);
656+
Symbol *codegen (Symbol *dest = NULL);
657+
658+
/// Get component indeces for a swizzle string.
659+
/// consts allows '0' & '1' to be included in the string.
660+
static size_t indices (ustring components, int *indexes, size_t N, bool consts);
661+
662+
/// Offset if the component index is really a constant ('0' or '1').
663+
enum { const_offset = -2 };
664+
665+
/// Special code generation of assignment of src to proper components.
666+
Symbol* codegen_assign (Symbol *src);
667+
668+
size_t indices (int *indexes, size_t N, bool consts) const {
669+
return indices (m_field, indexes, N, consts);
670+
}
671+
};
672+
673+
674+
647675
class ASTconditional_statement : public ASTNode
648676
{
649677
public:

src/liboslcomp/codegen.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,8 @@ ASTassign_expression::codegen (Symbol *dest)
479479
// Assigning to an individual component or array element
480480
index = (ASTindex *) var().get();
481481
dest = NULL;
482+
} else if (var()->nodetype() == swizzle_node) {
483+
return static_cast<ASTswizzle*>(var().get())->codegen_assign(expr()->codegen (dest));
482484
} else if (var()->nodetype() == structselect_node) {
483485
dest = var()->codegen();
484486
} else {
@@ -1202,6 +1204,78 @@ ASTindex::codegen_assign (Symbol *src, Symbol *ind,
12021204

12031205

12041206

1207+
Symbol *
1208+
ASTswizzle::codegen (Symbol *dest)
1209+
{
1210+
// General case, construct a new triple with the swizzle indexes.
1211+
1212+
if (dest == nullptr || ! equivalent (dest->typespec(), typespec()))
1213+
dest = m_compiler->make_temporary (typespec());
1214+
1215+
Symbol *syms[4] = { dest };
1216+
1217+
int nsym = 1;
1218+
for (ref arg = child(0); arg && nsym < 4; ++nsym, arg = arg->next()) {
1219+
syms[nsym] = arg->codegen();
1220+
}
1221+
ASSERT (nsym == 4);
1222+
1223+
// emit the constructor call
1224+
emitcode (typespec().string().c_str(), nsym, syms);
1225+
return dest;
1226+
}
1227+
1228+
1229+
1230+
Symbol *
1231+
ASTswizzle::codegen_assign (Symbol *src)
1232+
{
1233+
// Swizzle assignment.
1234+
1235+
if (!m_is_lvalue) {
1236+
error ("Cannot assign to constant swizzle");
1237+
return nullptr;
1238+
}
1239+
1240+
// First child is an index, which holds the lvalue (this) to store to.
1241+
ASTindex* index = static_cast<ASTindex*>(child(0));
1242+
Symbol *syms[4] = { index->lvalue().get()->codegen() };
1243+
1244+
int nsym = 0;
1245+
if (src->typespec().is_triple()) {
1246+
int idxs[3];
1247+
if (indices(idxs, 3, false /*can't assign to constants*/) != 3) {
1248+
error ("Trying to assign to invalid swizzle");
1249+
return nullptr;
1250+
}
1251+
// Iterate all of the indexes
1252+
for (; index && nsym < 3; ++nsym, index = static_cast<ASTindex*>(index->nextptr())) {
1253+
// tmp[I] = src[N]
1254+
ASSERT (index->nodetype() == index_node);
1255+
Symbol* sym = m_compiler->make_temporary (TypeDesc::TypeFloat);
1256+
emitcode ("compref", sym, src, m_compiler->make_constant (nsym));
1257+
1258+
// sym[0] is taken, so assign to syms[i+1]
1259+
ASSERT (idxs[nsym]+1 < int(sizeof(syms)/sizeof(syms[0])));
1260+
syms[idxs[nsym]+1] = sym;
1261+
}
1262+
} else {
1263+
// Same assignment to all components
1264+
for (int n = m_typespec.aggregate(); nsym < n; ++nsym)
1265+
syms[nsym] = src;
1266+
}
1267+
1268+
ASSERT (nsym == 3);
1269+
1270+
// emit the constructor call
1271+
emitcode (typespec().string().c_str(), nsym+1, syms);
1272+
1273+
// so transitive assignment will work
1274+
return syms[0];
1275+
}
1276+
1277+
1278+
12051279
Symbol *
12061280
ASTstructselect::codegen (Symbol *dest)
12071281
{

src/liboslcomp/typecheck.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,22 @@ ASTindex::typecheck (TypeSpec expected)
321321

322322

323323

324+
TypeSpec
325+
ASTswizzle::typecheck (TypeSpec expected)
326+
{
327+
typecheck_children ();
328+
329+
// Can't be an lvalue if any of the args are constant floats.
330+
for (ref arg = child(0); arg; arg = arg->next()) {
331+
if (!(m_is_lvalue = arg->is_lvalue()))
332+
break;
333+
}
334+
335+
return m_typespec;
336+
}
337+
338+
339+
324340
TypeSpec
325341
ASTstructselect::typecheck (TypeSpec expected)
326342
{

testsuite/swizzle/ref/out.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
11
Compiled swizzle.osl -> swizzle.oso
2+
c: 1 2 3
3+
c.brg = c.grb = c.bgr: 3 1 2
4+
c.rgb = c.gbr: 1 2 3
5+
c.r00: 1 0 0
6+
c.g01: 2 0 1
7+
c.b11: 3 1 1
8+
c.bg1: 3 2 1
9+
c.rgb = c.brg: 3 1 2
10+
c.rgb = c.brg: 2 3 1
11+
c.rgb = c.brg: 1 2 3
12+
c.rgb = c.bgr: 3 2 1
13+
c.bgr = c.bgr: 3 2 1
14+
c.grb = c.bgr: 2 1 3
15+
c.ggr + c.rgr: 3 2 4
216
c.r: 4
317
c.g: 3
418
c.b: 2
@@ -12,4 +26,15 @@ c4[0].b: 3
1226
c4[1].r: -1
1327
c4[1].b: -3
1428
c4[1].g: -2
29+
c.rrr: 4 4 4
30+
c.rgb: 4 3 2
31+
c.rbg: 4 2 3
32+
c.ggg: 3 3 3
33+
c.grb: 3 4 2
34+
c.gbr: 3 2 4
35+
c.bbb: 2 2 2
36+
c.brg: 2 4 3
37+
c.ggr: 3 3 4
38+
ca[0].brg: 3 1 2
39+
c.rgb = 1: 1 1 1
1540

testsuite/swizzle/swizzle.osl

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,44 @@
44

55
shader swizzle ()
66
{
7-
color c = color(4,3,2);
7+
color c = color(1,2,3);
8+
printf("c: %g\n", c);
9+
10+
// transitive
11+
// 1,2,3 -> 2,3,1 -> 3,1,2
12+
c.brg = c.grb = c.bgr;
13+
printf("c.brg = c.grb = c.bgr: %g\n", c);
14+
15+
c.rgb = c.gbr;
16+
printf("c.rgb = c.gbr: %g\n", c);
17+
18+
printf("c.r00: %g\n", c.r00);
19+
printf("c.g01: %g\n", c.g01);
20+
printf("c.b11: %g\n", c.b11);
21+
printf("c.bg1: %g\n", c.bg1);
22+
23+
c = c.brg;
24+
printf("c.rgb = c.brg: %g\n", c);
25+
26+
c = c.brg;
27+
printf("c.rgb = c.brg: %g\n", c);
28+
29+
c = c.brg;
30+
printf("c.rgb = c.brg: %g\n", c);
31+
32+
c.rgb = c.bgr;
33+
printf("c.rgb = c.bgr: %g\n", c);
34+
35+
c.bgr = c.bgr;
36+
printf("c.bgr = c.bgr: %g\n", c);
37+
38+
c.grb = c.bgr;
39+
printf("c.grb = c.bgr: %g\n", c);
40+
41+
printf("c.ggr + c.rgr: %g\n", c.ggr + c.rgr);
42+
43+
c.gbr = c.ggr + c.rgr;
44+
845
printf("c.r: %g\n", c.r);
946
printf("c.g: %g\n", c.g);
1047
printf("c.b: %g\n", c.b);
@@ -26,4 +63,19 @@ shader swizzle ()
2663
printf("c4[%d].%s: %g\n", i, i ? "b" : "g", i ? c4[i].rgb.b : c4[i].rgb.g);
2764
printf("c4[%d].%s: %g\n", i, i ? "g" : "b", i ? c4[i].rgb.g : c4[i].rgb.b);
2865
}
66+
67+
printf("c.rrr: %g\n", c.rrr);
68+
printf("c.rgb: %g\n", c.rgb);
69+
printf("c.rbg: %g\n", c.rbg);
70+
printf("c.ggg: %g\n", c.ggg);
71+
printf("c.grb: %g\n", c.grb);
72+
printf("c.gbr: %g\n", c.gbr);
73+
printf("c.bbb: %g\n", c.bbb);
74+
printf("c.brg: %g\n", c.brg);
75+
printf("c.ggr: %g\n", c.ggr);
76+
77+
printf("ca[0].brg: %g\n", ca[0].brg);
78+
79+
c.rgb = 1;
80+
printf("c.rgb = 1: %g\n", c);
2981
}

0 commit comments

Comments
 (0)