Skip to content
Permalink
Browse files

Allow aggregate component access with .[xyzrgb] (#1049)

Spatial geometry triples (point, vector, normal) now support A.x, A.y,
A.z as synonyms for A[0], A[1], A[2], respectively. It's not the
notation I prefer (obviously, or I would have done it 25 years ago),
but other people really dig it, so fine by me.

Colors now support A.r, A.g, A.b as synonyms for A[0], A[1], A[2],
respectively.  To be honest, this worries me a little because I fear
we will regret it if we ever shift to supporting spectral rendering
(and the components will not necessarily represent red, green, and
blue, and may have arbitrary number). But again, people really seem to
like this, so fine, but don't come complaining to me in a few years
when your shaders all need rewriting for your new spectral renderer.

In both cases, it's for direct reference to lvalues (variables, including
references passed as arguments to functions):

    val = P.x;   // yes
    P.y = val;   // yes
    function_with_float_param (P.z);   // yes

But you can't use this notations on arbitrary expressions:

    val = function_returning_color().r;   // no
    val = (P + N).y;                      // no, double ick

We are also NOT supporting "swizzling."

Note that this is strictly an oslc-side change, underneath C.r compiles
to the same ops as C[0] always did, so newly compiled shader using this
new syntax can be executed with old renderers/shading engines.
  • Loading branch information...
lgritz committed Aug 9, 2019
1 parent f6a0019 commit b6400e2b333e98719d2a09ec425356aa049214b0
@@ -8,6 +8,9 @@ Dependency and standards changes:
we may drop support for OIIO 1.x.

OSL Language and oslc compiler:
* New syntax to reference color and point/vector/normal components as
named struct components, such as C.r, C.g, C.b, or P.x, P.y, P.z.
#1049 (1.11.0)
* oslc compilation speed-ups with faster retrieval of source lines when
pasted into oso output. #938 (1.11.0)
* Writing to function parameters not marked as `output` was only
@@ -330,6 +330,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms arithmetic
logic loop matrix message
mergeinstances-nouserdata mergeinstances-vararray
metadata-braces miscmath missing-shader
named-components
noise noise-cell
noise-gabor noise-gabor2d-filter noise-gabor3d-filter
noise-perlin noise-simplex
@@ -343,6 +344,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms arithmetic
oslc-err-intoverflow oslc-err-write-nonoutput
oslc-err-noreturn oslc-err-notfunc
oslc-err-initlist-args oslc-err-initlist-return
oslc-err-named-components
oslc-err-outputparamvararray oslc-err-paramdefault
oslc-err-struct-array-init oslc-err-struct-ctr
oslc-err-struct-dup oslc-err-struct-print
@@ -60,8 +60,8 @@
Editor: Larry Gritz \\
\emph{lg@imageworks.com}
}
\date{{\large Date: 31 Mar 2018 \\
(with corrections, 23 Jul 2019)
\date{{\large Date: 3 Aug 2019 \\
% (with corrections, 23 Jul 2019)
}
\bigskip
\bigskip
@@ -1620,6 +1620,16 @@ \section{{\cf color}}
It is an error to access a color component with an index outside the
$[0...2]$ range.

Color variables may also have their components referenced using
``named components'' that look like accessing structure fields named
{\cf r}, {\cf g}, and {\cf b}, as synonyms for {\cf [0]}, {\cf [1]}, and
{\cf [2]}, respectively:

\begin{code}
float green = C.g; // get the green component
C.r = 0.5; // set the red component
\end{code}

The following operators may be used with \color values (in order of
decreasing precedence, with each box holding operators of the same
precedence):
@@ -1781,6 +1791,16 @@ \section{Point-like types: {\cf point}, {\cf vector}, {\cf normal}}
It is an error to access a point component with an index outside the
$[0...2]$ range.

Point-like variables may also have their components referenced using
``named components'' that look like accessing structure fields named
{\cf x}, {\cf y}, and {\cf z}, as synonyms for {\cf [0]}, {\cf [1]}, and
{\cf [2]}, respectively:

\begin{code}
float yval = P.y; // get the [1] or y component
P.x = 0.5; // set the [0] or x component
\end{code}

The following operators may be used with point-like values (in order of
decreasing precedence, with each box holding operators of the same
precedence):
@@ -5463,6 +5483,13 @@ \section*{Expressions}

<variable-ref> ::= <identifier> <array-deref-opt>

<array-deref> ::= "[" <expression> "]"

<component-deref> ::= "[" <expression> "]"
\alt "." <component-field>

<component-field> ::= "x" | "y" | "z" | "r" | "g" | "b"

<binary-op> ::= "*" | "/" | "\%"
\alt "+" | "-"
\alt "<<" | ">>"
@@ -134,6 +134,11 @@ class TypeSpec {
/// reliable if it's not a struct, a struct will return an UNKNOWN type.
const TypeDesc &simpletype () const { return m_simple; }

/// Is the type unknown/uninitialized?
bool is_unknown () const noexcept {
return m_simple == OIIO::TypeUnknown && !m_structure && !m_closure;
}

/// Is this typespec a closure? (N.B. if so, you can find out what
/// kind of closure it is with simpletype()).
bool is_closure () const { return m_closure && !is_array(); }
@@ -693,51 +693,61 @@ ASTpostincdec::childname (size_t i) const



ASTindex::ASTindex (OSLCompilerImpl *comp, ASTNode *expr, ASTNode *index)
: ASTNode (index_node, comp, 0, expr, index)
{
ASSERT (expr->nodetype() == variable_ref_node ||
expr->nodetype() == structselect_node);
if (expr->typespec().is_array()) // array dereference
m_typespec = expr->typespec().elementtype();
else if (!expr->typespec().is_closure() &&
expr->typespec().is_triple()) // component access
m_typespec = TypeDesc::FLOAT;
else {
error ("indexing into non-array or non-component type");
ASTindex::ASTindex (OSLCompilerImpl *comp, ASTNode *expr, ASTNode *index,
ASTNode *index2, ASTNode *index3)
: ASTNode (index_node, comp, 0, expr, index /*NO: index2, index3*/)
{
// We only initialized the first child. Add more if additional arguments
// were supplied.
DASSERT (index);
if (index2)
addchild(index2);
if (index3)
addchild(index3);

// Special case: an ASTindex where the `expr` is itself an ASTindex.
// This construction results from named-component access of array
// elements, e.g., `colorarray[i].r`. In that case, what we want to do
// is rearrange to turn this into the two-index variety and discard the
// child index.
if (!index2 && expr->nodetype() == index_node && expr->nchildren() == 2) {
ref newexpr = static_cast<ASTindex*>(expr)->lvalue();
ref newindex = static_cast<ASTindex*>(expr)->index();
ref newindex2 = index;
clearchildren();
addchild(newexpr); expr = newexpr.get();
addchild(newindex); index = newindex.get();
addchild(newindex2); index2 = newindex2.get();
}
}



ASTindex::ASTindex (OSLCompilerImpl *comp, ASTNode *expr,
ASTNode *index, ASTNode *index2)
: ASTNode (index_node, comp, 0, expr, index, index2)
{
ASSERT (expr->nodetype() == variable_ref_node ||
expr->nodetype() == structselect_node);
if (expr->typespec().is_matrix()) // matrix component access
m_typespec = TypeDesc::FLOAT;
else if (expr->typespec().is_array() && // triplearray[][]
expr->typespec().elementtype().is_triple())
m_typespec = TypeDesc::FLOAT;
else {
error ("indexing into non-array or non-component type");
DASSERT (expr->nodetype() == variable_ref_node ||
expr->nodetype() == structselect_node);
DASSERT (m_typespec.is_unknown());

if (!index2) {
// 1-argument: simple array a[i] or component dereference triple[c]
if (expr->typespec().is_array()) // array dereference
m_typespec = expr->typespec().elementtype();
else if (!expr->typespec().is_closure() &&
expr->typespec().is_triple()) // component access
m_typespec = TypeDesc::FLOAT;
} else if (!index3) {
// 2-argument: matrix dereference m[r][c], or component of a
// triple array colorarray[i][c].
if (expr->typespec().is_matrix()) // matrix component access
m_typespec = TypeDesc::FLOAT;
else if (expr->typespec().is_array() && // triplearray[][]
expr->typespec().elementtype().is_triple())
m_typespec = TypeDesc::FLOAT;
} else {
// 3-argument: one component of an array of matrices
// matrixarray[i][r][c]
if (expr->typespec().is_array() && // matrixarray[][]
expr->typespec().elementtype().is_matrix())
m_typespec = TypeDesc::FLOAT;
}
}



ASTindex::ASTindex (OSLCompilerImpl *comp, ASTNode *expr, ASTNode *index,
ASTNode *index2, ASTNode *index3)
: ASTNode (index_node, comp, 0, expr, index, index2, index3)
{
ASSERT (expr->nodetype() == variable_ref_node ||
expr->nodetype() == structselect_node);
if (expr->typespec().is_array() && // matrixarray[][]
expr->typespec().elementtype().is_matrix())
m_typespec = TypeDesc::FLOAT;
else {
if (m_typespec.is_unknown()) {
error ("indexing into non-array or non-component type");
}
}
@@ -756,12 +766,15 @@ ASTindex::childname (size_t i) const
ASTstructselect::ASTstructselect (OSLCompilerImpl *comp, ASTNode *expr,
ustring field)
: ASTNode (structselect_node, comp, 0, expr), m_field(field),
m_structid(-1), m_fieldid(-1), m_fieldsym(NULL)
m_structid(-1), m_fieldid(-1), m_fieldname(field), m_fieldsym(NULL)
{
m_fieldsym = find_fieldsym (m_structid, m_fieldid);
if (m_fieldsym) {
m_fieldname = m_fieldsym->name();
m_typespec = m_fieldsym->typespec();
} else if (m_compindex) {
// It's a named component, like point.x
m_typespec = OIIO::TypeFloat; // These cases are always single floats
}
}

@@ -773,10 +786,30 @@ ASTstructselect::ASTstructselect (OSLCompilerImpl *comp, ASTNode *expr,
Symbol *
ASTstructselect::find_fieldsym (int &structid, int &fieldid)
{
if (! lvalue()->typespec().is_structure() &&
! lvalue()->typespec().is_structure_array()) {
error ("type '%s' does not have a member '%s'",
type_c_str(lvalue()->typespec()), m_field);
auto lv = lvalue().get();
auto lvtype = lv->typespec();

if (lvtype.is_color()
&& (fieldname() == "r" || fieldname() == "g" || fieldname() == "b")) {
ASSERT(fieldid == -1 && !m_compindex);
fieldid = fieldname() == "r" ? 0 : (fieldname() == "g" ? 1 : 2);
m_compindex.reset(new ASTindex(m_compiler, lv,
new ASTliteral(oslcompiler, fieldid)));
m_is_lvalue = true;
return nullptr;
}
else if (lvtype.is_vectriple()
&& (fieldname() == "x" || fieldname() == "y" || fieldname() == "z")) {
ASSERT(fieldid == -1 && !m_compindex);
fieldid = fieldname() == "x" ? 0 : (fieldname() == "y" ? 1 : 2);
m_compindex.reset(new ASTindex(m_compiler, lv,
new ASTliteral(oslcompiler, fieldid)));
m_is_lvalue = true;
return nullptr;
}

if (! lvtype.is_structure() && ! lvtype.is_structure_array()) {
error ("type '%s' does not have a member '%s'", lvtype, m_field);
return NULL;
}

@@ -114,6 +114,10 @@ class ASTNode : public OIIO::RefCnt {
///
virtual const char *opname () const { return NULL; }

/// Return the number of child nodes.
///
size_t nchildren () const { return m_children.size(); }

/// Name of the child node
///
virtual const char *childname (size_t i) const = 0;
@@ -269,24 +273,25 @@ class ASTNode : public OIIO::RefCnt {
/// by node (for example, "float, int, string").
static std::string list_to_types_string (const ASTNode *node);

/// Return the number of child nodes.
///
size_t nchildren () const { return m_children.size(); }

/// Return the i-th child node, or NULL if there is no such node
///
/// Return a pointer for the i-th child node, or nullptr if there is no
/// such node.
ASTNode *child (size_t i) const {
return (i < m_children.size()) ? m_children[i].get() : NULL;
}

/// Add a new node to the list of children.
///
void addchild (ASTNode *n) { m_children.emplace_back(n); }

/// Add a new node to the list of children.
void addchild (ref& n) { m_children.emplace_back(n); }

/// Call the print() method of all the children of this node.
///
void printchildren (std::ostream &out, int indentlevel = 0) const;

/// Clear the children. Use with caution!
void clearchildren () { m_children.clear(); }

/// Follow a list of nodes, type checking each in turn, and return
/// the type of the last one.
static TypeSpec typecheck_list (ref node, TypeSpec expected = TypeSpec());
@@ -559,10 +564,10 @@ class ASTpostincdec : public ASTNode
class ASTindex : public ASTNode
{
public:
ASTindex (OSLCompilerImpl *comp, ASTNode *expr, ASTNode *index);
ASTindex (OSLCompilerImpl *comp, ASTNode *expr, ASTNode *index, ASTNode *index2);
// ASTindex (OSLCompilerImpl *comp, ASTNode *expr, ASTNode *index);
// ASTindex (OSLCompilerImpl *comp, ASTNode *expr, ASTNode *index, ASTNode *index2);
ASTindex (OSLCompilerImpl *comp, ASTNode *expr, ASTNode *index,
ASTNode *index2, ASTNode *index3);
ASTNode *index2 = nullptr, ASTNode *index3 = nullptr);
const char *nodetypename () const { return "index"; }
const char *childname (size_t i) const;
TypeSpec typecheck (TypeSpec expected = TypeSpec());
@@ -573,8 +578,9 @@ class ASTindex : public ASTNode
Symbol *codegen (Symbol *dest, Symbol * &ind,
Symbol * &ind2, Symbol *&ind3);

/// Special code generation of assignment of src to this indexed location
///
/// Special code generation of assignment of src to this indexed
/// location. The ind1, ind2, ind3 symbols are overrides; if not
/// provided, it will use index(), index2(), index3().
void codegen_assign (Symbol *src, Symbol *ind = NULL,
Symbol *ind2 = NULL, Symbol *ind3 = NULL);

@@ -610,6 +616,8 @@ class ASTstructselect : public ASTNode
ustring field () const { return m_field; }
ustring fieldname () const { return m_fieldname; }
Symbol *fieldsym () const { return m_fieldsym; }
int fieldid () const { return m_fieldid; }
ASTindex* compindex() const { return (ASTindex*)m_compindex.get(); }

private:
Symbol *find_fieldsym (int &structid, int &fieldid);
@@ -622,6 +630,7 @@ class ASTstructselect : public ASTNode
int m_fieldid; ///< index of the field within the structure
ustring m_fieldname; ///< Name of the field variable
Symbol *m_fieldsym; ///< Symbol of the field variable
ref m_compindex; ///< Redirection of named component index
};


0 comments on commit b6400e2

Please sign in to comment.
You can’t perform that action at this time.