Skip to content
Closed
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
push:
branches:
- main
- dev
tags:
- "v*"
pull_request:
Expand Down
10 changes: 8 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ lazy val commonSettings = Seq(

lazy val runnerSettings = Seq(libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j2-impl" % "2.24.3")

lazy val fs2Settings = Seq(libraryDependencies ++= Seq("co.fs2" %% "fs2-core" % "3.12.0", "co.fs2" %% "fs2-io" % "3.12.0"))

lazy val utility = (project in file("cyfra-utility"))
.settings(commonSettings)

Expand Down Expand Up @@ -96,13 +98,17 @@ lazy val vscode = (project in file("cyfra-vscode"))
.settings(commonSettings)
.dependsOn(foton)

lazy val fs2interop = (project in file("cyfra-fs2"))
.settings(commonSettings, fs2Settings)
.dependsOn(runtime)

lazy val e2eTest = (project in file("cyfra-e2e-test"))
.settings(commonSettings, runnerSettings)
.dependsOn(runtime)
.dependsOn(runtime, fs2interop)

lazy val root = (project in file("."))
.settings(name := "Cyfra")
.aggregate(compiler, dsl, foton, core, runtime, vulkan, examples)
.aggregate(compiler, dsl, foton, core, runtime, vulkan, examples, fs2interop)

e2eTest / Test / javaOptions ++= Seq("-Dorg.lwjgl.system.stackSize=1024", "-DuniqueLibraryNames=true")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ object LayoutStruct:
ftype match
case '[type tg <: GBuffer[?]; tg] =>
'{
BufferRef[t](${ Expr(i) }, ${ tag.asExprOf[Tag[t]] })(using summon[Tag[t]], ${ fromExpr.asExprOf[FromExpr[t]] })
BufferRef[t](${ Expr(i) }, ${ tag.asExprOf[Tag[t]] })(using ${ tag.asExprOf[Tag[t]] }, ${ fromExpr.asExprOf[FromExpr[t]] })
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I "fixed" the bug here, but I'm not sure if it's correct... it just compiles now.

}
case '[type tg <: GUniform[?]; tg] =>
'{
UniformRef[t](${ Expr(i) }, ${ tag.asExprOf[Tag[t]] })(using summon[Tag[t]], ${ fromExpr.asExprOf[FromExpr[t]] })
UniformRef[t](${ Expr(i) }, ${ tag.asExprOf[Tag[t]] })(using ${ tag.asExprOf[Tag[t]] }, ${ fromExpr.asExprOf[FromExpr[t]] })
}

val constructor = sym.primaryConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import io.computenode.cyfra.dsl.algebra.VectorAlgebra
import io.computenode.cyfra.dsl.struct.GStruct
import io.computenode.cyfra.dsl.{*, given}

class ArithmeticsE2eTest extends munit.FunSuite:
class ArithmeticsE2eLegacyTest extends munit.FunSuite:
given gc: GContext = GContext()

test("Float32 arithmetics"):
test("Float32 arithmetics (legacy)"):
val gf: GFunction[GStruct.Empty, Float32, Float32] = GFunction: fl =>
(fl + 1.2f) * (fl - 3.4f) / 5.6f

Expand All @@ -25,7 +25,7 @@ class ArithmeticsE2eTest extends munit.FunSuite:
.foreach: (res, exp) =>
assert(Math.abs(res - exp) < 0.001f, s"Expected $exp but got $res")

test("Int32 arithmetics"):
test("Int32 arithmetics (legacy)"):
val gf: GFunction[GStruct.Empty, Int32, Int32] = GFunction: n =>
((n + 2) * (n - 3) / 5).mod(7)

Expand All @@ -43,7 +43,7 @@ class ArithmeticsE2eTest extends munit.FunSuite:
.foreach: (res, exp) =>
assert(res == exp, s"Expected $exp but got $res")

test("Vec4Float32 arithmetics"):
test("Vec4Float32 arithmetics (legacy)"):
val f1 = (1.2f, 2.3f, 3.4f, 4.5f)
val f2 = (5.6f, 6.7f, 7.8f, 8.9f)
val f3 = (-5.3f, 6.2f, -4.7f, 9.1f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import io.computenode.cyfra.dsl.struct.GStruct
import io.computenode.cyfra.dsl.{*, given}
import GMem.fRGBA

class FunctionsE2eTest extends munit.FunSuite:
class FunctionsE2eLegacyTest extends munit.FunSuite:
given gc: GContext = GContext()

test("Functions"):
test("Functions (legacy)"):
val gf: GFunction[GStruct.Empty, Float32, Float32] = GFunction: f =>
val res1 = pow(sqrt(exp(sin(cos(tan(f))))), 2f)
val res2 = logn(asin(acos(atan(f) / 2f) / 4f) + 2f)
Expand All @@ -28,7 +28,7 @@ class FunctionsE2eTest extends munit.FunSuite:
.foreach: (res, exp) =>
assert(Math.abs(res - exp) < 0.05f, s"Expected $exp but got $res")

test("smoothstep clamp mix reflect refract normalize"):
test("smoothstep clamp mix reflect refract normalize (legacy)"):
val gf: GFunction[GStruct.Empty, Float32, Float32] = GFunction: f =>
val f1 = smoothstep(f - 1.2f, f + 3.4f, f)
val f2 = mix(f, f + 1f, f1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.computenode.cyfra.core.archive.*
import mem.*
import io.computenode.cyfra.dsl.{*, given}

class GStructE2eTest extends munit.FunSuite:
class GStructE2eLegacyTest extends munit.FunSuite:
case class Custom(f: Float32, v: Vec4[Float32]) extends GStruct[Custom]
val custom1 = Custom(2f, (1f, 2f, 3f, 4f))
val custom2 = Custom(-0.5f, (-0.5f, -1.5f, -2.5f, -3.5f))
Expand All @@ -16,7 +16,7 @@ class GStructE2eTest extends munit.FunSuite:

given gc: GContext = GContext()

test("GStruct passed as uniform"):
test("GStruct passed as uniform (legacy)"):
UniformContext.withUniform(custom1):
val gf: GFunction[Custom, Float32, Float32] = GFunction:
case (Custom(f, v), index, gArray) => v.*(f).dot(v) + gArray.at(index) * f
Expand All @@ -31,7 +31,7 @@ class GStructE2eTest extends munit.FunSuite:
.foreach: (res, exp) =>
assert(Math.abs(res - exp) < 0.001f, s"Expected $exp but got $res")

test("GStruct of GStructs".ignore):
test("GStruct of GStructs (legacy)".ignore):
UniformContext.withUniform(nested):
val gf: GFunction[Nested, Float32, Float32] = GFunction:
case (Nested(Custom(f1, v1), Custom(f2, v2)), index, gArray) =>
Expand All @@ -47,7 +47,7 @@ class GStructE2eTest extends munit.FunSuite:
.foreach: (res, exp) =>
assert(Math.abs(res - exp) < 0.001f, s"Expected $exp but got $res")

test("GSeq of GStructs"):
test("GSeq of GStructs (legacy)"):
val gf: GFunction[GStruct.Empty, Float32, Float32] = GFunction: fl =>
GSeq
.gen(custom1, c => Custom(c.f * 2f, c.v.*(2f)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import io.computenode.cyfra.core.archive.*
import mem.*
import io.computenode.cyfra.dsl.{*, given}

class GseqE2eTest extends munit.FunSuite:
class GseqE2eLegacyTest extends munit.FunSuite:
given gc: GContext = GContext()

test("GSeq gen limit map fold"):
test("GSeq gen limit map fold (legacy)"):
val gf: GFunction[GStruct.Empty, Float32, Float32] = GFunction: f =>
GSeq
.gen(f, _ + 1.0f)
Expand All @@ -27,7 +27,7 @@ class GseqE2eTest extends munit.FunSuite:
.foreach: (res, exp) =>
assert(Math.abs(res - exp) < 0.001f, s"Expected $exp but got $res")

test("GSeq of takeWhile filter count"):
test("GSeq of takeWhile filter count (legacy)"):
val gf: GFunction[GStruct.Empty, Int32, Int32] = GFunction: n =>
GSeq
.of(List.iterate(n, 10)(_ + 1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import io.computenode.cyfra.core.archive.*
import mem.*
import io.computenode.cyfra.dsl.{*, given}

class WhenE2eTest extends munit.FunSuite:
class WhenE2eLegacyTest extends munit.FunSuite:
given gc: GContext = GContext()

test("when elseWhen otherwise"):
test("when elseWhen otherwise (legacy)"):
val oneHundred = 100.0f
val twoHundred = 200.0f
val gf: GFunction[GStruct.Empty, Float32, Float32] = GFunction: f =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.computenode.cyfra.e2e.fs2interop

import io.computenode.cyfra.core.archive.*, mem.*, GMem.fRGBA
import io.computenode.cyfra.dsl.{*, given}, algebra.VectorAlgebra
import io.computenode.cyfra.fs2interop.*, GPipe.*, Bridge.given
import io.computenode.cyfra.core.CyfraRuntime
import io.computenode.cyfra.runtime.VkCyfraRuntime

import fs2.{io as fs2io, *}

extension (f: fRGBA)
def neg = (-f._1, -f._2, -f._3, -f._4)
def scl(s: Float) = (f._1 * s, f._2 * s, f._3 * s, f._4 * s)
def add(g: fRGBA) = (f._1 + g._1, f._2 + g._2, f._3 + g._3, f._4 + g._4)
def close(g: fRGBA)(eps: Float): Boolean =
Math.abs(f._1 - g._1) < eps && Math.abs(f._2 - g._2) < eps && Math.abs(f._3 - g._3) < eps && Math.abs(f._4 - g._4) < eps

class Fs2Tests extends munit.FunSuite:
given cr: CyfraRuntime = VkCyfraRuntime()

test("fs2 through gPipeMap, just ints"):
val inSeq = (0 until 256).toSeq
val stream = Stream.emits(inSeq)
val pipe = gPipeMap[Pure, Int32, Int](_ + 1)
val result = stream.through(pipe).compile.toList
val expected = inSeq.map(_ + 1)
result
.zip(expected)
.foreach: (res, exp) =>
assert(res == exp, s"Expected $exp, got $res")

test("fs2 through gPipeMap, floats and vectors"):
val inSeq = (0 to 255).map(_.toFloat).toSeq
val stream = Stream.emits(inSeq)
val pipe = gPipeMap[Pure, Float32, Vec4[Float32], Float, fRGBA](f => (f, f + 1f, f + 2f, f + 3f))
val result = stream.through(pipe).compile.toList
val expected = inSeq.map(f => (f, f + 1f, f + 2f, f + 3f))
result
.zip(expected)
.foreach: (res, exp) =>
assert(res.close(exp)(0.001f), s"Expected $exp, got $res")

class Fs2LegacyTests extends munit.FunSuite:
given gc: GContext = GContext()

test("fs2 Float stream (legacy)"):
val inSeq = (0 to 255).map(_.toFloat)
val inStream = Stream.emits(inSeq)
val outStream = inStream.gPipeFloat(_ + 1f).toList
val expected = inStream.map(_ + 1f).toList
outStream
.zip(expected)
.foreach: (res, exp) =>
assert(Math.abs(res - exp) < 0.001f, s"Expected $exp but got $res")

test("fs2 Int stream (legacy)"):
val inSeq = 0 to 255
val inStream = Stream.emits(inSeq)
val outStream = inStream.gPipeInt(_ + 1).toList
val expected = inStream.map(_ + 1).toList
outStream
.zip(expected)
.foreach: (res, exp) =>
assert(res == exp, s"Expected $exp but got $res")

test("fs2 Vec4Float stream (legacy)"):
val k = -2.1f
val f = (1.2f, 2.3f, 3.4f, 4.5f)
val v = VectorAlgebra.vec4.tupled(f)

val inSeq: Seq[fRGBA] = (0 to 1023)
.map(_.toFloat)
.grouped(4)
.map:
case Seq(a, b, c, d) => (a, b, c, d)
.toSeq
val inStream = Stream.emits(inSeq)
val outStream = inStream.gPipeVec4(vec => (-vec).*(k).+(v)).toList
val expected = inStream.map(vec => vec.neg.scl(k).add(f)).toList

outStream
.zip(expected)
.foreach: (res, exp) =>
assert(res.close(exp)(0.001f), s"Expected $exp but got $res")
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package io.computenode.cyfra.fs2interop

import io.computenode.cyfra.core.archive.mem.GMem.fRGBA
import io.computenode.cyfra.dsl.*

import fs2.*
import java.nio.ByteBuffer
import org.lwjgl.BufferUtils
import izumi.reflect.Tag

import scala.reflect.ClassTag

trait Bridge[CyfraType <: Value: FromExpr: Tag, ScalaType: ClassTag]:
def toByteBuffer(inBuf: ByteBuffer, chunk: Chunk[ScalaType]): ByteBuffer
def fromByteBuffer(outBuf: ByteBuffer, arr: Array[ScalaType]): Array[ScalaType]

object Bridge:
given Bridge[Int32, Int]:
def toByteBuffer(inBuf: ByteBuffer, chunk: Chunk[Int]): ByteBuffer =
inBuf.asIntBuffer().put(chunk.toArray[Int]).flip()
inBuf
def fromByteBuffer(outBuf: ByteBuffer, arr: Array[Int]): Array[Int] =
outBuf.asIntBuffer().get(arr).flip()
arr

given Bridge[Float32, Float]:
def toByteBuffer(inBuf: ByteBuffer, chunk: Chunk[Float]): ByteBuffer =
inBuf.asFloatBuffer().put(chunk.toArray[Float]).flip()
inBuf
def fromByteBuffer(outBuf: ByteBuffer, arr: Array[Float]): Array[Float] =
outBuf.asFloatBuffer().get(arr).flip()
arr

given Bridge[Vec4[Float32], fRGBA]:
def toByteBuffer(inBuf: ByteBuffer, chunk: Chunk[fRGBA]): ByteBuffer =
val vecs = chunk.toArray[fRGBA]
vecs.foreach:
case (x, y, z, a) =>
inBuf.putFloat(x)
inBuf.putFloat(y)
inBuf.putFloat(z)
inBuf.putFloat(a)
inBuf.flip()
inBuf

def fromByteBuffer(outBuf: ByteBuffer, arr: Array[fRGBA]): Array[fRGBA] =
val res = outBuf.asFloatBuffer()
for i <- 0 until arr.size do arr(i) = (res.get(), res.get(), res.get(), res.get())
outBuf.flip()
arr

given Bridge[GBoolean, Boolean]:
def toByteBuffer(inBuf: ByteBuffer, chunk: Chunk[Boolean]): ByteBuffer =
inBuf.put(chunk.toArray.asInstanceOf[Array[Byte]]).flip()
inBuf
def fromByteBuffer(outBuf: ByteBuffer, arr: Array[Boolean]): Array[Boolean] =
outBuf.get(arr.asInstanceOf[Array[Byte]]).flip()
arr
Loading