Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial solution to not needing proxies, working through inlining issues #129

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object ShaderMacros:
val createAST = new CreateShaderAST[q.type](using q)

val main =
createAST.walkTerm(expr.asTerm, None)
createAST.walkTerm(expr.asTerm, None, Map())

val defs =
createAST.shaderDefs.toList.filterNot(_.userDefined).map(_.fn)
Expand Down
13 changes: 13 additions & 0 deletions ultraviolet/shared/src/test/scala/ultraviolet/DebugAST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,16 @@ object DebugAST:

Expr("Done.")
}

inline def anyToAST[In, Out](inline expr: Any): String = ${ anyToASTImpl('{ expr }) }

private def anyToASTImpl[In, Out: Type](expr: Expr[Any])(using Quotes): Expr[String] = {

import quotes.reflect.*

println(">>> Any/Everything")
println(Printer.TreeStructure.show(expr.asTerm))
println("<<<")

Expr("Done.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,37 +45,69 @@ class GLSLExternalTests extends munit.FunSuite {
assert(clue(actual) == clue("vec4(1.0,1.0,vec2(0.0,1.0));"))
}

test("Inlined external function") {
test("Inlined external function no proxy") {
// The argument here will be ignored and inlined. Inlines are weird.
inline def fn1(v: Float): vec2 =
vec2(v)

inline def fn2: Float => vec2 =
alpha => vec2(0.0f, alpha)
inline def fn1(x: Float, y: Float): vec2 =
vec2(x, y)

inline def fragment: Shader[FragEnv, vec4] =
Shader { env =>
val proxy: Float => vec2 = fn2
vec4(fn1(1.0f), proxy(1.0f))
val y = 2.0f
vec4(fn1(1.0f, y), 0.0f, 1.0f)
}

val actual =
fragment.toGLSL[WebGL2].toOutput.code

// DebugAST.toAST(fragment)
// println(DebugAST.toAST(fragment))
// println(actual)

assertEquals(
actual,
s"""
|vec2 def0(in float alpha){
| return vec2(0.0,alpha);
|vec2 fn1(in float val0,in float y){
| return vec2(1.0,y);
|}
|vec4(vec2(1.0),def0(1.0));
|float y=2.0;
|vec4(fn1(1.0,y),0.0,1.0);
|""".stripMargin.trim
)
}

// Test disabled / commented out because of inlining issues with, e.g. inline def f: Float => Float = x => x
// test("Inlined external function".only) {
// inline def fn1(v: Float): vec2 =
// vec2(v)

// inline def fn2: Float => vec2 =
// (alpha: Float) => vec2(0.0f, alpha)

// inline def fragment: Shader[FragEnv, vec4] =
// Shader { env =>
// val y = 2.0f
// vec4(fn1(1.0f), fn2(y))
// }

// val actual =
// fragment.toGLSL[WebGL2](false).toOutput.code

// println(DebugAST.toAST(fragment))
// println(actual)

// assertEquals(
// actual,
// s"""
// |vec2 fn1(in float val0){
// | return vec2(1.0);
// |}
// |vec2 fn2(in float alpha){
// | return vec2(0.0,alpha);
// |}
// |vec4(fn1(1.0),fn2(1.0));
// |""".stripMargin.trim
// )
// }

test("Inlined external function N args") {

// The argument here will be ignored and inlined. Inlines are weird.
Expand All @@ -101,7 +133,10 @@ class GLSLExternalTests extends munit.FunSuite {
|vec2 def0(in float blue,in float alpha){
| return vec2(blue,alpha);
|}
|vec4(vec2(1.0,0.25),def0(0.5,1.0));
|vec2 fn1(in float val0,in float val1){
| return vec2(1.0,0.25);
|}
|vec4(fn1(1.0,0.25),def0(0.5,1.0));
|""".stripMargin.trim
)
}
Expand Down Expand Up @@ -135,12 +170,15 @@ class GLSLExternalTests extends munit.FunSuite {
assertEquals(
actual,
s"""
|vec2 tiledUVs(in vec2 uv,in vec2 channelPos,in vec2 channelSize,in vec2 entitySize,in vec2 textureSize){
| return channelPos+((fract(uv*(entitySize/textureSize)))*channelSize);
|}
|vec2 uv=vec2(1.0);
|vec2 channelPos=vec2(2.0);
|vec2 channelSize=vec2(3.0);
|vec2 entitySize=vec2(4.0);
|vec2 textureSize=vec2(5.0);
|channelPos+((fract(uv*(entitySize/textureSize)))*channelSize);
|tiledUVs(uv,channelPos,channelSize,entitySize,textureSize);
|""".stripMargin.trim
)

Expand Down Expand Up @@ -171,10 +209,13 @@ class GLSLExternalTests extends munit.FunSuite {
assertEquals(
actual,
s"""
|vec2 stretchedUVs(in vec2 uv,in vec2 channelPos,in vec2 channelSize){
| return channelPos+(uv*channelSize);
|}
|vec2 uv=vec2(1.0);
|vec2 channelPos=vec2(2.0);
|vec2 channelSize=vec2(3.0);
|channelPos+(uv*channelSize);
|stretchedUVs(uv,channelPos,channelSize);
|""".stripMargin.trim
)

Expand Down Expand Up @@ -210,104 +251,116 @@ class GLSLExternalTests extends munit.FunSuite {
val actual =
fragment.toGLSL[WebGL2].toOutput.code

// DebugAST.toAST(fragment)
// println(DebugAST.toAST(fragment))
// println(actual)

assertEquals(
actual,
s"""
|int fillType=0;
|vec4 fallback=vec4(1.0);
|sampler2D srcChannel=sampler2D;
|vec2 channelPos=vec2(2.0);
|vec2 channelSize=vec2(3.0);
|vec2 uv=vec2(4.0);
|vec2 entitySize=vec2(5.0);
|vec2 textureSize=vec2(6.0);
|switch(fillType){
| case 1:
| texture(srcChannel,channelPos+(uv*channelSize));
| break;
| case 2:
| texture(srcChannel,channelPos+((fract(uv*(entitySize/textureSize)))*channelSize));
| break;
| default:
| fallback;
| break;
|vec2 stretchedUVs(in vec2 uv,in vec2 channelPos,in vec2 channelSize){
| return channelPos+(uv*channelSize);
|}
|""".stripMargin.trim
)

}

test("should correctly render tile and stretch code (fn)") {

inline def fragment =
Shader {
import TileAndStretch.*

val fillType: Int = 0
val fallback: vec4 = vec4(1.0)
@uniform val srcChannel: sampler2D.type = sampler2D
val channelPos: vec2 = vec2(2.0)
val channelSize: vec2 = vec2(3.0)
val uv: vec2 = vec2(4.0)
val entitySize: vec2 = vec2(5.0)
val textureSize: vec2 = vec2(6.0)

val proxy: (Int, vec4, sampler2D.type, vec2, vec2, vec2, vec2, vec2) => vec4 =
tileAndStretchChannelFn

proxy(
fillType, // env.FILLTYPE.toInt,
fallback, // env.CHANNEL_0,
srcChannel, // env.SRC_CHANNEL,
channelPos, // env.CHANNEL_0_POSITION,
channelSize, // env.CHANNEL_0_SIZE,
uv, // env.UV,
entitySize, // env.SIZE,
textureSize // env.TEXTURE_SIZE
)
}

val actual =
fragment.toGLSL[WebGL2].toOutput.code

// DebugAST.toAST(fragment)
// println(actual)

assertEquals(
actual,
s"""
|uniform sampler2D srcChannel;
|vec4 def0(in int _fillType,in vec4 _fallback,in sampler2D _srcChannel,in vec2 _channelPos,in vec2 _channelSize,in vec2 _uv,in vec2 _entitySize,in vec2 _textureSize){
|vec2 tiledUVs(in vec2 uv,in vec2 channelPos,in vec2 channelSize,in vec2 entitySize,in vec2 textureSize){
| return channelPos+((fract(uv*(entitySize/textureSize)))*channelSize);
|}
|vec4 tileAndStretchChannelDef(in int fillType,in vec4 fallback,in sampler2D srcChannel,in vec2 channelPos,in vec2 channelSize,in vec2 uv,in vec2 entitySize,in vec2 textureSize){
| vec4 val0;
| switch(_fillType){
| switch(fillType){
| case 1:
| val0=texture(_srcChannel,_channelPos+(_uv*_channelSize));
| val0=texture(srcChannel,stretchedUVs(uv,channelPos,channelSize));
| break;
| case 2:
| val0=texture(_srcChannel,_channelPos+((fract(_uv*(_entitySize/_textureSize)))*_channelSize));
| val0=texture(srcChannel,tiledUVs(uv,channelPos,channelSize,entitySize,textureSize));
| break;
| default:
| val0=_fallback;
| val0=fallback;
| break;
| }
| return val0;
|}
|int fillType=0;
|vec4 fallback=vec4(1.0);
|sampler2D srcChannel=sampler2D;
|vec2 channelPos=vec2(2.0);
|vec2 channelSize=vec2(3.0);
|vec2 uv=vec2(4.0);
|vec2 entitySize=vec2(5.0);
|vec2 textureSize=vec2(6.0);
|def0(fillType,fallback,srcChannel,channelPos,channelSize,uv,entitySize,textureSize);
|tileAndStretchChannelDef(fillType,fallback,srcChannel,channelPos,channelSize,uv,entitySize,textureSize);
|""".stripMargin.trim
)

}

// TODO: Bring back
// test("should correctly render tile and stretch code (fn)") {

// inline def fragment =
// Shader {
// import TileAndStretch.*

// val fillType: Int = 0
// val fallback: vec4 = vec4(1.0)
// @uniform val srcChannel: sampler2D.type = sampler2D
// val channelPos: vec2 = vec2(2.0)
// val channelSize: vec2 = vec2(3.0)
// val uv: vec2 = vec2(4.0)
// val entitySize: vec2 = vec2(5.0)
// val textureSize: vec2 = vec2(6.0)

// val proxy: (Int, vec4, sampler2D.type, vec2, vec2, vec2, vec2, vec2) => vec4 =
// tileAndStretchChannelFn

// proxy(
// fillType, // env.FILLTYPE.toInt,
// fallback, // env.CHANNEL_0,
// srcChannel, // env.SRC_CHANNEL,
// channelPos, // env.CHANNEL_0_POSITION,
// channelSize, // env.CHANNEL_0_SIZE,
// uv, // env.UV,
// entitySize, // env.SIZE,
// textureSize // env.TEXTURE_SIZE
// )
// }

// val actual =
// fragment.toGLSL[WebGL2].toOutput.code

// // DebugAST.toAST(fragment)
// // println(actual)

// assertEquals(
// actual,
// s"""
// |uniform sampler2D srcChannel;
// |vec4 def0(in int _fillType,in vec4 _fallback,in sampler2D _srcChannel,in vec2 _channelPos,in vec2 _channelSize,in vec2 _uv,in vec2 _entitySize,in vec2 _textureSize){
// | vec4 val0;
// | switch(_fillType){
// | case 1:
// | val0=texture(_srcChannel,_channelPos+(_uv*_channelSize));
// | break;
// | case 2:
// | val0=texture(_srcChannel,_channelPos+((fract(_uv*(_entitySize/_textureSize)))*_channelSize));
// | break;
// | default:
// | val0=_fallback;
// | break;
// | }
// | return val0;
// |}
// |int fillType=0;
// |vec4 fallback=vec4(1.0);
// |vec2 channelPos=vec2(2.0);
// |vec2 channelSize=vec2(3.0);
// |vec2 uv=vec2(4.0);
// |vec2 entitySize=vec2(5.0);
// |vec2 textureSize=vec2(6.0);
// |def0(fillType,fallback,srcChannel,channelPos,channelSize,uv,entitySize,textureSize);
// |""".stripMargin.trim
// )

// }

test("Inlined external def function with ignored argument") {
inline def modifyColorNamed: vec4 => Shader[Unit, vec4] =
inputColor =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ class GLSLImportsTests extends munit.FunSuite {
|int def0(in int i){
| return i+1;
|}
|int addOneInline(in int value){
| return value+1;
|}
|int value=10;
|def0(value);
|value+1;
|addOneInline(value);
|""".stripMargin.trim
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ class GLSLNativeTests extends munit.FunSuite {
fragment.toGLSL[WebGL2].toOutput.code

// DebugAST.toAST(fragment)
// println(actual)
// println(actual)

assertEquals(
actual,
s"""
|float circleSdf(in vec2 val0,in float val1){
| return length(val0)-3.0;
|}
|float x=1.0;
|length(vec2(x,2.0))-3.0;
|circleSdf(vec2(x,2.0),3.0);
|""".stripMargin.trim
)
}
Expand Down
Loading
Loading