From 8465cd0d8a272c7b93dc2c7dd555218facf06f01 Mon Sep 17 00:00:00 2001 From: spamegg Date: Wed, 9 Jul 2025 16:48:51 +0300 Subject: [PATCH 1/3] fs2 interop --- build.sbt | 10 ++- .../cyfra/e2e/fs2interop/Fs2Tests.scala | 80 +++++++++++++++++ .../computenode/cyfra/fs2interop/Bridge.scala | 58 ++++++++++++ .../computenode/cyfra/fs2interop/GPipe.scala | 90 +++++++++++++++++++ 4 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala create mode 100644 cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/Bridge.scala create mode 100644 cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala diff --git a/build.sbt b/build.sbt index 44acec5f..493f0471 100644 --- a/build.sbt +++ b/build.sbt @@ -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) @@ -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") diff --git a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala new file mode 100644 index 00000000..b8aed2d3 --- /dev/null +++ b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala @@ -0,0 +1,80 @@ +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 fs2.* + +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 gc: GContext = GContext() + + 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") + + // legacy tests + 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") diff --git a/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/Bridge.scala b/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/Bridge.scala new file mode 100644 index 00000000..41d366c3 --- /dev/null +++ b/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/Bridge.scala @@ -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 diff --git a/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala b/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala new file mode 100644 index 00000000..5eb64cd6 --- /dev/null +++ b/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala @@ -0,0 +1,90 @@ +package io.computenode.cyfra.fs2interop + +import io.computenode.cyfra.core.archive.*, mem.*, GMem.fRGBA +import io.computenode.cyfra.core.{Allocation, layout}, layout.Layout +import io.computenode.cyfra.core.{CyfraRuntime, GBufferRegion, GExecution, GProgram} +import io.computenode.cyfra.dsl.{*, given}, gio.GIO, binding.GBuffer +import io.computenode.cyfra.spirv.SpirvTypes.typeStride +import struct.GStruct, GStruct.Empty, Empty.given + +import fs2.* +import java.nio.ByteBuffer +import org.lwjgl.BufferUtils +import izumi.reflect.Tag + +import scala.reflect.ClassTag + +object GPipe: + def gPipeMap[F[_], C1 <: Value: FromExpr: Tag, C2 <: Value: FromExpr: Tag, S1: ClassTag, S2: ClassTag]( + f: C1 => C2, + )(using cr: CyfraRuntime, bridge1: Bridge[C1, S1], bridge2: Bridge[C2, S2]): Pipe[F, S1, S2] = + (stream: Stream[F, S1]) => + case class Params(inSize: Int) + case class PLayout(in: GBuffer[C1], out: GBuffer[C2]) extends Layout + case class PResult(out: GBuffer[C2]) extends Layout + + val params = Params(inSize = 256) + val inTypeSize = typeStride(Tag.apply[C1]) + val outTypeSize = typeStride(Tag.apply[C2]) + + val gProg = GProgram[Params, PLayout]( + layout = params => PLayout(in = GBuffer[C1](params.inSize), out = GBuffer[C2](params.inSize)), + dispatch = (layout, params) => GProgram.StaticDispatch((params.inSize, 1, 1)), + ): layout => + val invocId = GIO.invocationId + val element = GIO.read(layout.in, invocId) + GIO.write(layout.out, invocId, f(element)) // implicit bug + + val execution = GExecution[Params, PLayout]() + .addProgram(gProg)(params => Params(params.inSize), layout => PLayout(layout.in, layout.out)) + + val region = GBufferRegion + .allocate[PLayout] // implicit bug + .map: pLayout => + execution.execute(params, pLayout) // implicit bug + + // these are allocated once, reused for many chunks + val inBuf = BufferUtils.createByteBuffer(params.inSize * inTypeSize) + val outBuf = BufferUtils.createByteBuffer(params.inSize * outTypeSize) + + stream + .chunkMin(params.inSize) + .flatMap: chunk => + bridge1.toByteBuffer(inBuf, chunk) + region.runUnsafe(init = PLayout(in = GBuffer[C1](inBuf), out = GBuffer[C2](outBuf)), onDone = layout => layout.out.read(outBuf)) // implicit bug + Stream.emits(bridge2.fromByteBuffer(outBuf, new Array[S2](params.inSize))) + + // Syntax sugar for convenient single type version + def gPipeMap[F[_], C <: Value: FromExpr: Tag, S: ClassTag](f: C => C)(using CyfraRuntime, Bridge[C, S]): Pipe[F, S, S] = + gPipeMap[F, C, C, S, S](f) + + // legacy stuff working with GFunction + extension (stream: Stream[Pure, Float]) + def gPipeFloat(fn: Float32 => Float32)(using GContext): Stream[Pure, Float] = + val gf: GFunction[Empty, Float32, Float32] = GFunction(fn) + stream + .chunkMin(256) + .flatMap: chunk => + val gmem = FloatMem(chunk.toArray) + val res = gmem.map(gf).asInstanceOf[FloatMem].toArray + Stream.emits(res) + + extension (stream: Stream[Pure, Int]) + def gPipeInt(fn: Int32 => Int32)(using GContext): Stream[Pure, Int] = + val gf: GFunction[Empty, Int32, Int32] = GFunction(fn) + stream + .chunkMin(256) + .flatMap: chunk => + val gmem = IntMem(chunk.toArray) + val res = gmem.map(gf).asInstanceOf[IntMem].toArray + Stream.emits(res) + + extension (stream: Stream[Pure, fRGBA]) + def gPipeVec4(fn: Vec4[Float32] => Vec4[Float32])(using GContext): Stream[Pure, fRGBA] = + val gf: GFunction[Empty, Vec4[Float32], Vec4[Float32]] = GFunction(fn) + stream + .chunkMin(256) + .flatMap: chunk => + val gmem = Vec4FloatMem(chunk.toArray) + val res = gmem.map(gf).asInstanceOf[Vec4FloatMem].toArray + Stream.emits(res) From 27c5e4fa67b7f686d0aa9cc653c331596ef78129 Mon Sep 17 00:00:00 2001 From: spamegg Date: Wed, 9 Jul 2025 16:52:23 +0300 Subject: [PATCH 2/3] run actions on dev branch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c47d94a8..64d5109b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ on: push: branches: - main + - dev tags: - "v*" pull_request: From 4b24732933a0280d8405c46398df0f6f56ec1e31 Mon Sep 17 00:00:00 2001 From: spamegg Date: Fri, 25 Jul 2025 14:04:26 +0300 Subject: [PATCH 3/3] fix implicit bug in LayoutStruct, rename legacy tests (which are now failing) --- .../computenode/cyfra/core/layout/LayoutStruct.scala | 4 ++-- .../io/computenode/cyfra/e2e/ArithmeticsE2eTest.scala | 8 ++++---- .../io/computenode/cyfra/e2e/FunctionsE2eTest.scala | 6 +++--- .../io/computenode/cyfra/e2e/GStructE2eTest.scala | 8 ++++---- .../scala/io/computenode/cyfra/e2e/GseqE2eTest.scala | 6 +++--- .../scala/io/computenode/cyfra/e2e/WhenE2eTest.scala | 4 ++-- .../io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala | 10 +++++++--- .../scala/io/computenode/cyfra/fs2interop/GPipe.scala | 4 ++-- 8 files changed, 27 insertions(+), 23 deletions(-) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/layout/LayoutStruct.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/layout/LayoutStruct.scala index 3d79b409..f9bd8247 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/layout/LayoutStruct.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/layout/LayoutStruct.scala @@ -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]] }) } 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 diff --git a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/ArithmeticsE2eTest.scala b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/ArithmeticsE2eTest.scala index 17797a77..46830806 100644 --- a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/ArithmeticsE2eTest.scala +++ b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/ArithmeticsE2eTest.scala @@ -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 @@ -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) @@ -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) diff --git a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/FunctionsE2eTest.scala b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/FunctionsE2eTest.scala index 31966edf..54917521 100644 --- a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/FunctionsE2eTest.scala +++ b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/FunctionsE2eTest.scala @@ -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) @@ -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) diff --git a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/GStructE2eTest.scala b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/GStructE2eTest.scala index c1183c2a..c00bf85c 100644 --- a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/GStructE2eTest.scala +++ b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/GStructE2eTest.scala @@ -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)) @@ -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 @@ -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) => @@ -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))) diff --git a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/GseqE2eTest.scala b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/GseqE2eTest.scala index d10cca51..6e9a2c54 100644 --- a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/GseqE2eTest.scala +++ b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/GseqE2eTest.scala @@ -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) @@ -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)) diff --git a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/WhenE2eTest.scala b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/WhenE2eTest.scala index 0a374c26..be30b959 100644 --- a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/WhenE2eTest.scala +++ b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/WhenE2eTest.scala @@ -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 => diff --git a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala index b8aed2d3..2339a22f 100644 --- a/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala +++ b/cyfra-e2e-test/src/test/scala/io/computenode/cyfra/e2e/fs2interop/Fs2Tests.scala @@ -3,8 +3,10 @@ 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.* +import fs2.{io as fs2io, *} extension (f: fRGBA) def neg = (-f._1, -f._2, -f._3, -f._4) @@ -14,7 +16,7 @@ extension (f: fRGBA) 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 gc: GContext = GContext() + given cr: CyfraRuntime = VkCyfraRuntime() test("fs2 through gPipeMap, just ints"): val inSeq = (0 until 256).toSeq @@ -38,7 +40,9 @@ class Fs2Tests extends munit.FunSuite: .foreach: (res, exp) => assert(res.close(exp)(0.001f), s"Expected $exp, got $res") - // legacy tests +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) diff --git a/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala b/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala index 5eb64cd6..d3868465 100644 --- a/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala +++ b/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala @@ -32,8 +32,8 @@ object GPipe: dispatch = (layout, params) => GProgram.StaticDispatch((params.inSize, 1, 1)), ): layout => val invocId = GIO.invocationId - val element = GIO.read(layout.in, invocId) - GIO.write(layout.out, invocId, f(element)) // implicit bug + val element = GIO.read[C1](layout.in, invocId) + GIO.write[C2](layout.out, invocId, f(element)) // implicit bug val execution = GExecution[Params, PLayout]() .addProgram(gProg)(params => Params(params.inSize), layout => PLayout(layout.in, layout.out))