Record Class Expr.Loop

java.lang.Object
java.lang.Record
limn.render3d.shader.Expr.Loop
Record Components:
states - the names of the state's values, as of(limn.render3d.shader.Expr, int, java.util.function.Function<? super limn.render3d.shader.Expr, ? extends limn.render3d.shader.Expr>) gives them: ir_state, the loop's level (one more than the highest level of a loop in its turn, 0 for none), _ and the value's place
initial - each value before the first turn; a float or vector type
maxIterations - how many turns at most, 1 to MAX_ITERATIONS
next - each value after one turn, of its initial value's type
until - a float that ends the loop where it is above zero, or null to take every turn
index - which value of the state this node is, from 0
All Implemented Interfaces:
Expr
Enclosing interface:
Expr

public static record Expr.Loop(List<String> states, List<Expr> initial, int maxIterations, List<Expr> next, @Nullable Expr until, int index) extends Record implements Expr
A value computed by a bounded loop: a state of one value or several, their initial values, and the state after one turn, taken at most maxIterations times. Build one with of(limn.render3d.shader.Expr, int, java.util.function.Function<? super limn.render3d.shader.Expr, ? extends limn.render3d.shader.Expr>), which hands the turn its state as nodes.

Each turn first asks until, when there is one, of the state as it stands: above zero ends the loop; otherwise every value of the state becomes its next at once, each computed from the state before the turn. The loop's values are the state when it ends. The node is the first of them, and value(int) reads the others: a loop read for two of its values is still one loop, computed once. A part of next or until that does not read the state is computed once, before the loop.


 // An escape-time fractal: z ↦ z² + c, counting turns, until |z|² passes 256.
 // The state is (z.x, z.y, turns, 0).
 Expr.Loop escape = Expr.Loop.of(Expr.Lit.of(0, 0, 0, 0), 256,
         s -> vec4(plus(square(xy(s)), c), plus(z(s), 1), 0),   // the next state
         s -> minus(dot(xy(s), xy(s)), 256));                   // above zero: escaped

 // A ray marched through a volume: the colour gathered and the distance travelled.
 Expr.Loop march = Expr.Loop.of(List.of(Expr.Lit.of(0, 0, 0, 0), Expr.Lit.of(0)), 64,
         s -> List.of(plus(s.get(0), gathered(s.get(0), s.get(1))), plus(s.get(1), step)),
         s -> minus(alpha(s.get(0)), 0.99f));                   // all but opaque: stop
 Expr colour = march;                                         // the first value
 Expr travelled = march.value(1);                             // the second
 

The state is nodes, not names. of(limn.render3d.shader.Expr, int, java.util.function.Function<? super limn.render3d.shader.Expr, ? extends limn.render3d.shader.Expr>) gives next and until each value of the state as a Expr.Ref of its initial value's type, under names it chooses, which a surface's inputs cannot take (they may not start ir_). Those are the record's states: a record keeps its components, and the IR has to say which references a loop binds, but nothing else names them. A loop is named by its level: one more than the highest level of a loop in its turn, 0 for a turn that holds none. So a loop in another's turn, built there or apart, on any thread, has names of its own and may read the outer state. The state means nothing outside its loop: SurfaceOutputs refuses a surface that reads it where no loop binds it, and of(limn.render3d.shader.Expr, int, java.util.function.Function<? super limn.render3d.shader.Expr, ? extends limn.render3d.shader.Expr>) refuses a turn that reads the state of a loop already built, which a part of that loop's turn read back from the record, or a reference made by hand under a state's name, does. A loop made with the canonical constructor is the IR as given: in its turn a state's name is its own state, wherever the reference came from. A loop that of builds inside such a turn cannot name that state by hand, which it refuses as it refuses any name made by hand; build that one with the constructor as well. Expr.Raw text that names a state is not checked at all: it is the escape from portability, and the generator names a loop's variables itself, so such text may read an identifier no shader declares.

Bounded across a whole surface. The count is a constant from 1 to MAX_ITERATIONS, and so is every turn a pixel takes in all the loops of one surface. The count is what the generated shader runs. A loop runs once for each turn of the innermost loop whose state it reads, however deep it was built; when it reads none, once for the surface, or once a light when only the diffuse response reads it. Loops side by side, and a loop in another's initial value, add their turns. A loop read twice, for one value or several, or two built alike in one turn or both reading no state, is computed and counted once; two built alike in the turns of two loops side by side read two states, and run in each. So a loop of 32 turns may hold one of 32 that reads its state, not one of 64 nor two of 32 side by side, and it may hold a loop of 512 that reads none. A loop holds itself and what it holds to the cap, and SurfaceOutputs holds the whole surface, counting the turns of its diffuse response once for each of the eight lights a pass shades with at most. So no surface asks a pixel for more than 1024 turns, in any profile. The cap bounds turns, not what a turn costs, which is the surface's as it is without a loop. until is a float rather than a boolean, so a threshold is a subtraction away; an iteration count, when a surface wants one, is a value of its state.

An early end is non-uniform control flow. A loop with an until ends at different turns in neighbouring pixels, where the shading languages leave a derivative and a texture lookup that picks its own level of detail undefined, so its next and until may hold no Expr.Sample, no call to dFdx, dFdy or fwidth, and no call to texture, textureProj, textureOffset or textureProjOffset; textureLod, textureGrad, their variants, texelFetch and textureSize are defined there. A generator also refuses there any Expr.Raw text and every call it does not know to be defined, its own helpers that sample or take derivatives among them. A loop without an until runs every turn in every pixel and may hold them all.

  • Field Details

    • MAX_ITERATIONS

      public static final int MAX_ITERATIONS
      The most turns a pixel may take across every loop of a surface.
      See Also:
  • Constructor Details

    • Loop

      public Loop(List<String> states, List<Expr> initial, int maxIterations, List<Expr> next, @Nullable Expr until, int index)
      Checks the loop as the class describes.
      Throws:
      IllegalArgumentException - if the count, the turns with the loops it holds, a type (each value's wherever it is read included), the number of values, a name or the index is out of these bounds, or a loop that can end early samples or takes a derivative
  • Method Details

    • of

      public static Expr.Loop of(Expr initial, int maxIterations, Function<? super Expr,? extends Expr> next)
      A loop of one value that takes every turn, in every pixel.
      Parameters:
      initial - the state before the first turn; a float or vector type
      maxIterations - how many turns, 1 to MAX_ITERATIONS
      next - the state after one turn, given the state
      Returns:
      the loop, whose value is the state after the last turn
      Throws:
      IllegalArgumentException - as the constructor, or if the turn reads the state of a loop already built where no loop in the turn binds it
    • of

      public static Expr.Loop of(Expr initial, int maxIterations, Function<? super Expr,? extends Expr> next, Function<? super Expr,? extends Expr> until)
      A loop of one value that may end early: before each turn, until of the state above zero ends it.
      Parameters:
      initial - the state before the first turn; a float or vector type
      maxIterations - how many turns at most, 1 to MAX_ITERATIONS
      next - the state after one turn, given the state
      until - a float above zero where the loop ends, given the state
      Returns:
      the loop, whose value is the state when it ends
      Throws:
      IllegalArgumentException - as the constructor, or if the turn reads the state of a loop already built where no loop in the turn binds it
    • of

      public static Expr.Loop of(List<? extends Expr> initial, int maxIterations, Function<? super List<Expr>,? extends List<? extends Expr>> next)
      A loop of several values that takes every turn, in every pixel.
      Parameters:
      initial - each value before the first turn; floats or vectors, one or more
      maxIterations - how many turns, 1 to MAX_ITERATIONS
      next - the values after one turn, in the same order, given the state's
      Returns:
      the loop, whose first value it is; value(int) reads the others
      Throws:
      IllegalArgumentException - as the constructor, or if the turn reads the state of a loop already built where no loop in the turn binds it
    • of

      public static Expr.Loop of(List<? extends Expr> initial, int maxIterations, Function<? super List<Expr>,? extends List<? extends Expr>> next, Function<? super List<Expr>,? extends Expr> until)
      A loop of several values that may end early: before each turn, until of the state above zero ends it.
      Parameters:
      initial - each value before the first turn; floats or vectors, one or more
      maxIterations - how many turns at most, 1 to MAX_ITERATIONS
      next - the values after one turn, in the same order, given the state's
      until - a float above zero where the loop ends, given the state's values
      Returns:
      the loop, whose first value it is; value(int) reads the others
      Throws:
      IllegalArgumentException - as the constructor, or if the turn reads the state of a loop already built where no loop in the turn binds it
    • value

      public Expr.Loop value(int index)
      Value index of this loop's state when it ends: the same loop, computed once however many of its values are read.
      Parameters:
      index - the value's place in the state, from 0
      Returns:
      the node that reads it
      Throws:
      IllegalArgumentException - if the loop has no such value
    • type

      public ShaderType type()
      Specified by:
      type in interface Expr
    • equals

      public boolean equals(@Nullable Object other)
      Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. Reference components are compared with Objects::equals(Object,Object); primitive components are compared with '=='.
      Specified by:
      equals in class Record
      Parameters:
      other - the object with which to compare
      Returns:
      true if this object is the same as the other argument; false otherwise.
    • hashCode

      public int hashCode()
      Returns a hash code value for this object. The value is derived from the hash code of each of the record components.
      Specified by:
      hashCode in class Record
      Returns:
      a hash code value for this object
    • toString

      public String toString()
      Returns a string representation of this record class. The representation contains the name of the class, followed by the name and value of each of the record components.
      Specified by:
      toString in class Record
      Returns:
      a string representation of this object
    • states

      public List<String> states()
      Returns the value of the states record component.
      Returns:
      the value of the states record component
    • initial

      public List<Expr> initial()
      Returns the value of the initial record component.
      Returns:
      the value of the initial record component
    • maxIterations

      public int maxIterations()
      Returns the value of the maxIterations record component.
      Returns:
      the value of the maxIterations record component
    • next

      public List<Expr> next()
      Returns the value of the next record component.
      Returns:
      the value of the next record component
    • until

      public @Nullable Expr until()
      Returns the value of the until record component.
      Returns:
      the value of the until record component
    • index

      public int index()
      Returns the value of the index record component.
      Returns:
      the value of the index record component