From bfbde173bbfad3829c38b3134039b4ed89fa8a5c Mon Sep 17 00:00:00 2001 From: MarconZet <25779550+MarconZet@users.noreply.github.com> Date: Tue, 23 Dec 2025 21:51:58 +0100 Subject: [PATCH] First version of core refactor --- .../computenode/cyfra/core/Allocation.scala | 14 +- ...oProgram.scala => ExpressionProgram.scala} | 7 +- .../cyfra/core/GBufferRegion.scala | 5 +- .../computenode/cyfra/core/GExecution.scala | 4 +- .../io/computenode/cyfra/core/GProgram.scala | 24 +- .../computenode/cyfra/core/SpirvProgram.scala | 8 +- .../cyfra/core/binding/BufferRef.scala | 7 +- .../cyfra/core/binding/GBinding.scala | 17 ++ .../cyfra/core/binding/UniformRef.scala | 8 +- .../core/expression/BuildInFunction.scala | 76 ++++++ .../core/expression/CustomFunction.scala | 14 ++ .../cyfra/core/expression/Expression.scala | 34 +++ .../core/expression/ExpressionBlock.scala | 103 ++++++++ .../core/expression/ExpressionHolder.scala | 4 + .../cyfra/core/expression/JumpTarget.scala | 7 + .../cyfra/core/expression/Value.scala | 54 +++++ .../cyfra/core/expression/Var.scala | 8 + .../core/expression/ops/AlgebraOps.scala | 196 +++++++++++++++ .../core/expression/ops/BitwiseOps.scala | 50 ++++ .../core/expression/ops/BooleanOps.scala | 94 ++++++++ .../expression/ops/NegativeElementOps.scala | 20 ++ .../cyfra/core/expression/types.scala | 224 ++++++++++++++++++ .../cyfra/core/expression/typesImpl.scala | 26 ++ .../cyfra/core/expression/typesValue.scala | 100 ++++++++ .../cyfra/core/layout/LayoutBinding.scala | 4 +- .../cyfra/core/layout/LayoutStruct.scala | 90 +------ .../io/computenode/cyfra/core/main.scala | 15 ++ .../cyfra/fs2interop}/GCodec.scala | 2 +- .../cyfra/runtime/VkCyfraRuntime.scala | 10 +- .../computenode/cyfra/runtime/VkShader.scala | 2 +- .../computenode/cyfra/utility/Utility.scala | 5 + .../computenode/cyfra/utility/cats/Free.scala | 2 + 32 files changed, 1090 insertions(+), 144 deletions(-) rename cyfra-core/src/main/scala/io/computenode/cyfra/core/{GioProgram.scala => ExpressionProgram.scala} (61%) create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/GBinding.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/BuildInFunction.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/CustomFunction.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Expression.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ExpressionBlock.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ExpressionHolder.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/JumpTarget.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Value.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Var.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/AlgebraOps.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/BitwiseOps.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/BooleanOps.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/NegativeElementOps.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/types.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/typesImpl.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/typesValue.scala create mode 100644 cyfra-core/src/main/scala/io/computenode/cyfra/core/main.scala rename {cyfra-core/src/main/scala/io/computenode/cyfra/core => cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop}/GCodec.scala (99%) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/Allocation.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/Allocation.scala index ea7200e1..6a07173c 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/Allocation.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/Allocation.scala @@ -1,10 +1,8 @@ package io.computenode.cyfra.core +import io.computenode.cyfra.core.binding.{GBinding, GBuffer, GUniform} +import io.computenode.cyfra.core.expression.Value import io.computenode.cyfra.core.layout.{Layout, LayoutBinding} -import io.computenode.cyfra.dsl.Value -import io.computenode.cyfra.dsl.Value.FromExpr -import io.computenode.cyfra.dsl.binding.{GBinding, GBuffer, GUniform} -import io.computenode.cyfra.dsl.struct.{GStruct, GStructSchema} import izumi.reflect.Tag import java.nio.ByteBuffer @@ -21,11 +19,11 @@ trait Allocation: def execute(params: Params, layout: EL): RL extension (buffers: GBuffer.type) - def apply[T <: Value: {Tag, FromExpr}](length: Int): GBuffer[T] + def apply[T: Value](length: Int): GBuffer[T] - def apply[T <: Value: {Tag, FromExpr}](buff: ByteBuffer): GBuffer[T] + def apply[T: Value](buff: ByteBuffer): GBuffer[T] extension (buffers: GUniform.type) - def apply[T <: GStruct[T]: {Tag, FromExpr, GStructSchema}](buff: ByteBuffer): GUniform[T] + def apply[T: Value](buff: ByteBuffer): GUniform[T] - def apply[T <: GStruct[T]: {Tag, FromExpr, GStructSchema}](): GUniform[T] + def apply[T: Value](): GUniform[T] diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GioProgram.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/ExpressionProgram.scala similarity index 61% rename from cyfra-core/src/main/scala/io/computenode/cyfra/core/GioProgram.scala rename to cyfra-core/src/main/scala/io/computenode/cyfra/core/ExpressionProgram.scala index 03158fea..942554d7 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GioProgram.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/ExpressionProgram.scala @@ -2,12 +2,11 @@ package io.computenode.cyfra.core import io.computenode.cyfra.core.GProgram.* import io.computenode.cyfra.core.layout.* -import io.computenode.cyfra.dsl.Value.GBoolean -import io.computenode.cyfra.dsl.gio.GIO +import io.computenode.cyfra.core.expression.ExpressionBlock import izumi.reflect.Tag -case class GioProgram[Params, L <: Layout: {LayoutBinding, LayoutStruct}]( - body: L => GIO[?], +case class ExpressionProgram[Params, L <: Layout: {LayoutBinding, LayoutStruct}]( + body: L => ExpressionBlock[Unit], layout: InitProgramLayout => Params => L, dispatch: (L, Params) => ProgramDispatch, workgroupSize: WorkDimensions, diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GBufferRegion.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/GBufferRegion.scala index cfc041cf..4ae7927d 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GBufferRegion.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/GBufferRegion.scala @@ -4,9 +4,8 @@ import io.computenode.cyfra.core.Allocation import io.computenode.cyfra.core.GBufferRegion.MapRegion import io.computenode.cyfra.core.GProgram.BufferLengthSpec import io.computenode.cyfra.core.layout.{Layout, LayoutBinding} -import io.computenode.cyfra.dsl.Value -import io.computenode.cyfra.dsl.Value.FromExpr -import io.computenode.cyfra.dsl.binding.GBuffer +import io.computenode.cyfra.core.expression.Value +import io.computenode.cyfra.core.binding.GBuffer import izumi.reflect.Tag import scala.util.chaining.given diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GExecution.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/GExecution.scala index 9fab9d52..c8518127 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GExecution.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/GExecution.scala @@ -2,9 +2,7 @@ package io.computenode.cyfra.core import io.computenode.cyfra.core.GExecution.* import io.computenode.cyfra.core.layout.* -import io.computenode.cyfra.dsl.binding.GBuffer -import io.computenode.cyfra.dsl.gio.GIO -import io.computenode.cyfra.dsl.struct.{GStruct, GStructSchema} +import io.computenode.cyfra.core.binding.GBuffer import izumi.reflect.Tag import GExecution.* diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GProgram.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/GProgram.scala index ffd87858..44f541b5 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GProgram.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/GProgram.scala @@ -1,15 +1,13 @@ package io.computenode.cyfra.core import io.computenode.cyfra.core.layout.{Layout, LayoutBinding, LayoutStruct} -import io.computenode.cyfra.dsl.gio.GIO import java.nio.ByteBuffer import GProgram.* -import io.computenode.cyfra.dsl.{Expression, Value} -import io.computenode.cyfra.dsl.Value.{FromExpr, GBoolean, Int32} -import io.computenode.cyfra.dsl.binding.{GBinding, GBuffer, GUniform} -import io.computenode.cyfra.dsl.struct.{GStruct, GStructSchema} -import io.computenode.cyfra.dsl.struct.GStruct.Empty +import io.computenode.cyfra.core.binding.GUniform +import io.computenode.cyfra.core.binding.GBuffer +import io.computenode.cyfra.core.binding.GBinding +import io.computenode.cyfra.core.expression.{ExpressionBlock, Value} import izumi.reflect.Tag import java.io.FileInputStream @@ -33,8 +31,8 @@ object GProgram: layout: InitProgramLayout ?=> Params => L, dispatch: (L, Params) => ProgramDispatch, workgroupSize: WorkDimensions = (128, 1, 1), - )(body: L => GIO[?]): GProgram[Params, L] = - new GioProgram[Params, L](body, s => layout(using s), dispatch, workgroupSize) + )(body: L => ExpressionBlock[Unit]): GProgram[Params, L] = + new ExpressionProgram[Params, L](body, s => layout(using s), dispatch, workgroupSize) def fromSpirvFile[Params, L <: Layout: {LayoutBinding, LayoutStruct}]( layout: InitProgramLayout ?=> Params => L, @@ -49,16 +47,16 @@ object GProgram: bb.flip() SpirvProgram(layout, dispatch, bb) - private[cyfra] class BufferLengthSpec[T <: Value: {Tag, FromExpr}](val length: Int) extends GBuffer[T]: + private[cyfra] class BufferLengthSpec[T: Value](val length: Int) extends GBuffer[T]: private[cyfra] def materialise()(using Allocation): GBuffer[T] = GBuffer.apply[T](length) - private[cyfra] class DynamicUniform[T <: GStruct[T]: {Tag, FromExpr, GStructSchema}]() extends GUniform[T] + private[cyfra] class DynamicUniform[T: Value]() extends GUniform[T] trait InitProgramLayout: extension (_buffers: GBuffer.type) - def apply[T <: Value: {Tag, FromExpr}](length: Int): GBuffer[T] = + def apply[T: Value](length: Int): GBuffer[T] = BufferLengthSpec[T](length) extension (_uniforms: GUniform.type) - def apply[T <: GStruct[T]: {Tag, FromExpr, GStructSchema}](): GUniform[T] = + def apply[T: Value](): GUniform[T] = DynamicUniform[T]() - def apply[T <: GStruct[?]: {Tag, FromExpr, GStructSchema}](value: T): GUniform[T] + def apply[T: Value](value: T): GUniform[T] diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/SpirvProgram.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/SpirvProgram.scala index 0cfacd43..5ee266bf 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/SpirvProgram.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/SpirvProgram.scala @@ -4,10 +4,8 @@ import io.computenode.cyfra.core.layout.{Layout, LayoutBinding, LayoutStruct} import io.computenode.cyfra.core.GProgram.{InitProgramLayout, ProgramDispatch, WorkDimensions} import io.computenode.cyfra.core.SpirvProgram.Operation.ReadWrite import io.computenode.cyfra.core.SpirvProgram.{Binding, ShaderLayout} -import io.computenode.cyfra.dsl.Value -import io.computenode.cyfra.dsl.Value.{FromExpr, GBoolean} -import io.computenode.cyfra.dsl.binding.GBinding -import io.computenode.cyfra.dsl.gio.GIO +import io.computenode.cyfra.core.expression.Value +import io.computenode.cyfra.core.binding.GBinding import izumi.reflect.Tag import java.io.File @@ -44,7 +42,7 @@ case class SpirvProgram[Params, L <: Layout: {LayoutBinding, LayoutStruct}] priv ) val layout = shaderBindings(summon[LayoutStruct[L]].layoutRef) layout.flatten.foreach: binding => - md.update(binding.binding.tag.toString.getBytes) +// md.update(binding.binding.tag.toString.getBytes) md.update(binding.operation.toString.getBytes) val digest = md.digest() val bb = java.nio.ByteBuffer.wrap(digest) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/BufferRef.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/BufferRef.scala index 1ad1c3af..19cd6198 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/BufferRef.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/BufferRef.scala @@ -1,9 +1,6 @@ package io.computenode.cyfra.core.binding -import io.computenode.cyfra.dsl.Value -import io.computenode.cyfra.dsl.Value.FromExpr -import io.computenode.cyfra.dsl.binding.GBuffer import izumi.reflect.Tag -import izumi.reflect.macrortti.LightTypeTag +import io.computenode.cyfra.core.expression.Value -case class BufferRef[T <: Value: {Tag, FromExpr}](layoutOffset: Int, valueTag: Tag[T]) extends GBuffer[T] +case class BufferRef[T: Value](layoutOffset: Int, valueTag: Tag[T]) extends GBuffer[T] diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/GBinding.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/GBinding.scala new file mode 100644 index 00000000..c8e2af7a --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/GBinding.scala @@ -0,0 +1,17 @@ +package io.computenode.cyfra.core.binding + +import io.computenode.cyfra.core.expression.Value + +sealed trait GBinding[T: Value] + +object GBinding + +trait GBuffer[T: Value] extends GBinding[T] + +object GBuffer + +trait GUniform[T: Value] extends GBinding[T] + +object GUniform: + class ParamUniform[T: Value] extends GUniform[T] + def fromParams[T: Value]: ParamUniform[T] = ParamUniform[T]() diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/UniformRef.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/UniformRef.scala index 8fc86c2f..3da158ea 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/UniformRef.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/binding/UniformRef.scala @@ -1,10 +1,6 @@ package io.computenode.cyfra.core.binding -import io.computenode.cyfra.dsl.Value -import io.computenode.cyfra.dsl.Value.FromExpr -import io.computenode.cyfra.dsl.binding.{GBuffer, GUniform} -import io.computenode.cyfra.dsl.struct.{GStruct, GStructSchema} import izumi.reflect.Tag -import izumi.reflect.macrortti.LightTypeTag +import io.computenode.cyfra.core.expression.Value -case class UniformRef[T <: GStruct[?]: {Tag, FromExpr, GStructSchema}](layoutOffset: Int, valueTag: Tag[T]) extends GUniform[T] +case class UniformRef[T: Value](layoutOffset: Int, valueTag: Tag[T]) extends GUniform[T] diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/BuildInFunction.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/BuildInFunction.scala new file mode 100644 index 00000000..7179e448 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/BuildInFunction.scala @@ -0,0 +1,76 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.core.expression.* + +abstract class BuildInFunction[-R](val isPure: Boolean): + override def toString: String = s"builtin ${this.getClass.getSimpleName.replace("$", "")}" + +object BuildInFunction: + abstract class BuildInFunction0[-R](isPure: Boolean) extends BuildInFunction[R](isPure) + abstract class BuildInFunction1[-A1, -R](isPure: Boolean) extends BuildInFunction[R](isPure) + abstract class BuildInFunction1R[-R](isPure: Boolean) extends BuildInFunction1[R, R](isPure) + abstract class BuildInFunction2[-A1, -A2, -R](isPure: Boolean) extends BuildInFunction[R](isPure) + abstract class BuildInFunction2R[-R](isPure: Boolean) extends BuildInFunction2[R, R, R](isPure) + abstract class BuildInFunction3[-A1, -A2, -A3, -R](isPure: Boolean) extends BuildInFunction[R](isPure) + abstract class BuildInFunction4[-A1, -A2, -A3, -A4, -R](isPure: Boolean) extends BuildInFunction[R](isPure) + + // Concreate type operations + case object Add extends BuildInFunction2R[Any](true) + case object Sub extends BuildInFunction2R[Any](true) + case object Mul extends BuildInFunction2R[Any](true) + case object Div extends BuildInFunction2R[Any](true) + case object Mod extends BuildInFunction2R[Any](true) + + // Negative type operations + case object Neg extends BuildInFunction1R[Any](true) + case object Rem extends BuildInFunction2R[Any](true) + + // Vector/Matrix operations + case object VectorTimesScalar extends BuildInFunction2[Any, Any, Any](true) + case object MatrixTimesScalar extends BuildInFunction2[Any, Any, Any](true) + case object VectorTimesMatrix extends BuildInFunction2[Any, Any, Any](true) + case object MatrixTimesVector extends BuildInFunction2[Any, Any, Any](true) + case object MatrixTimesMatrix extends BuildInFunction2R[Any](true) + case object OuterProduct extends BuildInFunction2[Any, Any, Any](true) + case object Dot extends BuildInFunction2[Any, Any, Any](true) + + // Bitwise operations + case object ShiftRightLogical extends BuildInFunction2R[Any](true) + case object ShiftRightArithmetic extends BuildInFunction2R[Any](true) + case object ShiftLeftLogical extends BuildInFunction2R[Any](true) + case object BitwiseOr extends BuildInFunction2R[Any](true) + case object BitwiseXor extends BuildInFunction2R[Any](true) + case object BitwiseAnd extends BuildInFunction2R[Any](true) + case object BitwiseNot extends BuildInFunction1R[Any](true) + case object BitFieldInsert extends BuildInFunction4[Any, Any, Any, Any, Any](true) + case object BitFieldSExtract extends BuildInFunction3[Any, Any, Any, Any](true) + case object BitFieldUExtract extends BuildInFunction3[Any, Any, Any, Any](true) + case object BitReverse extends BuildInFunction1R[Any](true) + case object BitCount extends BuildInFunction1[Any, Any](true) + + // Logical operations on booleans + case object LogicalAny extends BuildInFunction1[Any, Bool](true) + case object LogicalAll extends BuildInFunction1[Any, Bool](true) + case object LogicalEqual extends BuildInFunction2R[Any](true) + case object LogicalNotEqual extends BuildInFunction2R[Any](true) + case object LogicalOr extends BuildInFunction2R[Any](true) + case object LogicalAnd extends BuildInFunction2R[Any](true) + case object LogicalNot extends BuildInFunction1R[Any](true) + + // Floating-point checks + case object IsNan extends BuildInFunction1[Any, Any](true) + case object IsInf extends BuildInFunction1[Any, Any](true) + case object IsFinite extends BuildInFunction1[Any, Any](true) + case object IsNormal extends BuildInFunction1[Any, Any](true) + case object SignBitSet extends BuildInFunction1[Any, Any](true) + + // Comparisons + case object Equal extends BuildInFunction2[Any, Any, Any](true) + case object NotEqual extends BuildInFunction2[Any, Any, Any](true) + case object LessThan extends BuildInFunction2[Any, Any, Any](true) + case object GreaterThan extends BuildInFunction2[Any, Any, Any](true) + case object LessThanEqual extends BuildInFunction2[Any, Any, Any](true) + case object GreaterThanEqual extends BuildInFunction2[Any, Any, Any](true) + + // Select + case object Select extends BuildInFunction3[Any, Any, Any, Any](true) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/CustomFunction.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/CustomFunction.scala new file mode 100644 index 00000000..e11ba1b0 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/CustomFunction.scala @@ -0,0 +1,14 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.utility.Utility.nextId + +case class CustomFunction[A: Value] private[cyfra] (name: String, arg: List[Var[?]], body: ExpressionBlock[A]): + def v : Value[A] = summon[Value[A]] + val id: Int = nextId() + lazy val isPure: Boolean = body.isPureWith(arg.map(_.id).toSet) + +object CustomFunction: + def apply[A: Value, B: Value](func: Var[A] => ExpressionBlock[B]): CustomFunction[B] = + val arg = Var[A]() + val body = func(arg) + CustomFunction(s"custom${nextId() + 1}", List(arg), body) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Expression.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Expression.scala new file mode 100644 index 00000000..b1444e20 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Expression.scala @@ -0,0 +1,34 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.core.binding.{GBuffer, GUniform} +import io.computenode.cyfra.core.expression.given +import io.computenode.cyfra.utility.Utility.nextId +import io.computenode.cyfra.core.expression.{Bool, Float16, Float32, Int16, Int32, UInt16, UInt32, given} + +sealed trait Expression[A: Value]: + val id: Int = nextId() + def v: Value[A] = summon[Value[A]] + +object Expression: + case class Constant[A: Value](value: Any) extends Expression[A] + case class VarDeclare[A: Value](variable: Var[A]) extends Expression[Unit]: + def v2: Value[A] = summon[Value[A]] + case class VarRead[A: Value](variable: Var[A]) extends Expression[A] + case class VarWrite[A: Value](variable: Var[A], value: Expression[A]) extends Expression[Unit]: + def v2: Value[A] = summon[Value[A]] + case class ReadBuffer[A: Value](buffer: GBuffer[A], index: Expression[UInt32]) extends Expression[A] + case class WriteBuffer[A: Value](buffer: GBuffer[A], index: Expression[UInt32], value: Expression[A]) extends Expression[Unit]: + def v2: Value[A] = summon[Value[A]] + case class ReadUniform[A: Value](uniform: GUniform[A]) extends Expression[A] + case class WriteUniform[A: Value](uniform: GUniform[A], value: Expression[A]) extends Expression[Unit]: + def v2: Value[A] = summon[Value[A]] + case class BuildInOperation[A: Value](func: BuildInFunction[A], args: List[Expression[?]]) extends Expression[A] + case class CustomCall[A: Value](func: CustomFunction[A], args: List[Var[?]]) extends Expression[A] + case class Branch[T: Value](cond: Expression[Bool], ifTrue: ExpressionBlock[T], ifFalse: ExpressionBlock[T], break: JumpTarget[T]) + extends Expression[T] + case class Loop(mainBody: ExpressionBlock[Unit], continueBody: ExpressionBlock[Unit], break: JumpTarget[Unit], continue: JumpTarget[Unit]) + extends Expression[Unit] + case class Jump[A: Value](target: JumpTarget[A], value: Expression[A]) extends Expression[Unit]: + def v2: Value[A] = summon[Value[A]] + case class ConditionalJump[A: Value](cond: Expression[Bool], target: JumpTarget[A], value: Expression[A]) extends Expression[Unit]: + def v2: Value[A] = summon[Value[A]] diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ExpressionBlock.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ExpressionBlock.scala new file mode 100644 index 00000000..6eef6702 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ExpressionBlock.scala @@ -0,0 +1,103 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.core.expression.Expression +import io.computenode.cyfra.core.expression.given +import io.computenode.cyfra.utility.cats.Monad + +import scala.util.boundary +import scala.util.boundary.break + +case class ExpressionBlock[A](result: Expression[A], body: List[Expression[?]]): + lazy val isPure: Boolean = isPureWith(Set.empty) + + def isPureWith(externalVarsIDs: Set[Int]): Boolean = boundary[Boolean]: + body.foldRight(externalVarsIDs): (expr, vars) => + expr match + case Expression.Constant(_) => vars + case Expression.VarDeclare(variable) => vars + variable.id + case Expression.VarRead(variable) => + if !vars.contains(variable.id) then break(false) + vars + case Expression.VarWrite(variable, _) => + if !vars.contains(variable.id) then break(false) + vars + case Expression.ReadBuffer(_, _) => vars + case Expression.WriteBuffer(_, _, _) => break(false) + case Expression.ReadUniform(_) => vars + case Expression.WriteUniform(_, _) => break(false) + case Expression.BuildInOperation(func, _) => + if !func.isPure then break(false) + vars + case Expression.CustomCall(func, _) => + if !func.isPure then break(false) + vars + case Expression.Branch(_, ifTrue, ifFalse, _) => + if !ifTrue.isPure then break(false) + if !ifFalse.isPure then break(false) + vars + case Expression.Loop(mainBody, continueBody, _, _) => + if !mainBody.isPure then break(false) + if !continueBody.isPure then break(false) + vars + case Expression.Jump(_, _) => vars + case Expression.ConditionalJump(_, _, _) => vars + true + + def add[B](that: Expression[B]): ExpressionBlock[B] = + ExpressionBlock(that, that :: this.body) + + def extend[B](that: ExpressionBlock[B]): ExpressionBlock[B] = + ExpressionBlock(that.result, that.body ++ this.body) + + def traverse[T](f: Expression[?] => Option[T], enterFunctions: Boolean = false): List[Option[T]] = + body.flatMap: + case x @ Expression.Loop(mainBody, continueBody, _, _) => + continueBody.traverse(f, enterFunctions) ++ mainBody.traverse(f, enterFunctions) :+ f(x) + case x @ Expression.Branch(_, ifTrue, ifFalse, _) => + ifFalse.traverse(f, enterFunctions) ++ ifTrue.traverse(f, enterFunctions) :+ f(x) + case x @ Expression.CustomCall(func, _) if enterFunctions => + func.body.traverse(f, enterFunctions) :+ f(x) + case other => List(f(other)) + + def collect[T](pf: PartialFunction[Expression[?], T]): List[T] = + traverse: + case ir if pf.isDefinedAt(ir) => Some(pf(ir)) + case _ => None + .flatten + + def mkString: List[String] = + traverse: x => + val prefix = s"%${x.id} = " + val suffix = x match + case Expression.Constant(value) => s"const $value" + case Expression.VarDeclare(variable) => s"declare $variable" + case Expression.VarRead(variable) => s"read $variable" + case Expression.VarWrite(variable, value) => s"write $variable <- %${value.id}" + case Expression.ReadBuffer(buffer, index) => s"read $buffer[%${index.id}]" + case Expression.WriteBuffer(buffer, index, value) => s"write $buffer[%${index.id}] <- %${value.id}" + case Expression.ReadUniform(uniform) => s"read $uniform" + case Expression.WriteUniform(uniform, value) => s"write $uniform <- %${value.id}" + case Expression.BuildInOperation(func, args) => s"$func ${args.map(_.id).mkString("%", " %", "")}" + case Expression.CustomCall(func, args) => s"call #${func.id} ${args.map(_.id).mkString("%", " %", "")}" + case Expression.Branch(cond, ifTrue, ifFalse, break) => s"branch %${cond.id} ? [%${ifTrue._1.id}] : [%${ifFalse._1.id}] -> jt#${break.id}" + case Expression.Loop(mainBody, continueBody, break, continue) => + s"loop body[%${mainBody._1.id}] cont[%${continueBody._1.id}] break#${break.id} continue#${continue.id}" + case Expression.Jump(target, value) => s"jump jt#${target.id} <- %${value.id}" + case Expression.ConditionalJump(cond, target, value) => s"cjump %${cond.id} ? jt#${target.id} <- %${value.id}" + Some(prefix + suffix) + .flatten + +object ExpressionBlock: + def apply[A](expression: Expression[A]): ExpressionBlock[A] = + ExpressionBlock(expression, List(expression)) + given Monad[ExpressionBlock] with + def flatMap[A, B](fa: ExpressionBlock[A])(f: A => ExpressionBlock[B]): ExpressionBlock[B] = + given t: Value[A] = fa.result.v + val ExpressionBlock(res, body) = f(t.indirect(fa.result)) + ExpressionBlock(res, body ++ fa.body) + def pure[A](x: A): ExpressionBlock[A] = x match + case h: ExpressionHolder[A] => h.block + case _: Unit => + val zero = unitZero.asInstanceOf[Expression[A]] + ExpressionBlock(zero, List(zero)) + case x: Any => ExpressionBlock[Any](Expression.Constant[Any](x), Nil).asInstanceOf[ExpressionBlock[A]] diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ExpressionHolder.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ExpressionHolder.scala new file mode 100644 index 00000000..d0f5e9ef --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ExpressionHolder.scala @@ -0,0 +1,4 @@ +package io.computenode.cyfra.core.expression + +trait ExpressionHolder[A: Value]: + def block: ExpressionBlock[A] diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/JumpTarget.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/JumpTarget.scala new file mode 100644 index 00000000..223100bb --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/JumpTarget.scala @@ -0,0 +1,7 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.utility.Utility.nextId + +class JumpTarget[A: Value]: + val id: Int = nextId() + override def toString: String = s"jt#$id" diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Value.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Value.scala new file mode 100644 index 00000000..1c25d8cb --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Value.scala @@ -0,0 +1,54 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.core.expression.{Expression, ExpressionBlock} +import io.computenode.cyfra.core.expression.BuildInFunction.{BuildInFunction0, BuildInFunction1, BuildInFunction2, BuildInFunction3, BuildInFunction4} +import io.computenode.cyfra.utility.cats.Monad +import izumi.reflect.Tag + +trait Value[A]: + def indirect(ir: Expression[A]): A = extract(ExpressionBlock(ir, List())) + def extract(block: ExpressionBlock[A]): A = + if !block.isPure then throw RuntimeException("Cannot embed impure expression") + extractUnsafe(block) + + protected def extractUnsafe(ir: ExpressionBlock[A]): A + def tag: Tag[A] + + def pure(x: A): ExpressionBlock[A] = + summon[Monad[ExpressionBlock]].pure(x) + +object Value: + def map[Res: Value as vr](f: BuildInFunction0[Res]): Res = + val next = Expression.BuildInOperation(f, Nil) + vr.extract(ExpressionBlock(next, List(next))) + + extension [A: Value as v](x: A) + def map[Res: Value as vb](f: BuildInFunction1[A, Res]): Res = + val arg = v.pure(x) + val next = Expression.BuildInOperation(f, List(arg.result)) + vb.extract(arg.add(next)) + + def map[A2: Value as v2, Res: Value as vb](x2: A2)(f: BuildInFunction2[A, A2, Res]): Res = + val arg1 = v.pure(x) + val arg2 = summon[Value[A2]].pure(x2) + val next = Expression.BuildInOperation(f, List(arg1.result, arg2.result)) + vb.extract(arg1.extend(arg2).add(next)) + + def map[A2: Value as v2, A3: Value as v3, Res: Value as vb](x2: A2, x3: A3)(f: BuildInFunction3[A, A2, A3, Res]): Res = + val arg1 = v.pure(x) + val arg2 = summon[Value[A2]].pure(x2) + val arg3 = summon[Value[A3]].pure(x3) + val next = Expression.BuildInOperation(f, List(arg1.result, arg2.result, arg3.result)) + vb.extract(arg1.extend(arg2).extend(arg3).add(next)) + + def map[A2: Value as v2, A3: Value as v3, A4: Value as v4, Res: Value as vb](x2: A2, x3: A3, x4: A4)( + f: BuildInFunction4[A, A2, A3, A4, Res], + ): Res = + val arg1 = v.pure(x) + val arg2 = summon[Value[A2]].pure(x2) + val arg3 = summon[Value[A3]].pure(x3) + val arg4 = summon[Value[A4]].pure(x4) + val next = Expression.BuildInOperation(f, List(arg1.result, arg2.result, arg3.result, arg4.result)) + vb.extract(arg1.extend(arg2).extend(arg3).extend(arg4).add(next)) + + def irs: ExpressionBlock[A] = v.pure(x) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Var.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Var.scala new file mode 100644 index 00000000..e620ff3b --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/Var.scala @@ -0,0 +1,8 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.utility.Utility.nextId + +class Var[T: Value]: + val id: Int = nextId() + override def toString: String = s"var#$id" + diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/AlgebraOps.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/AlgebraOps.scala new file mode 100644 index 00000000..8c771ded --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/AlgebraOps.scala @@ -0,0 +1,196 @@ +package io.computenode.cyfra.core.expression.ops + +import io.computenode.cyfra.core.expression.* +import io.computenode.cyfra.core.expression.Value.map +import io.computenode.cyfra.core.expression.{BuildInFunction, Value} + +import scala.annotation.targetName + +given [T <: NumericalType: Value]: NumericalOps[T] with {} +given [T <: NumericalType: Value]: NumericalOps[Vec2[T]] with {} +given [T <: NumericalType: Value]: NumericalOps[Vec3[T]] with {} +given [T <: NumericalType: Value]: NumericalOps[Vec4[T]] with {} + +trait NumericalOps[T] + +extension [T: {NumericalOps, Value}](self: T) + @targetName("add") + def +(that: T): T = self.map(that)(BuildInFunction.Add) + @targetName("sub") + def -(that: T): T = self.map(that)(BuildInFunction.Sub) + @targetName("mul") + def *(that: T): T = self.map(that)(BuildInFunction.Mul) + @targetName("div") + def /(that: T): T = self.map(that)(BuildInFunction.Div) + @targetName("mod") + def %(that: T): T = self.map(that)(BuildInFunction.Mod) + +// Vector * Scalar +extension [T <: FloatType: Value, V <: Vec[T]: Value](vec: V) + @targetName("vectorTimesScalar") + def *(scalar: T): V = vec.map(scalar)(BuildInFunction.VectorTimesScalar) + +extension [T <: FloatType: Value, V <: Vec[T]: Value](scalar: T) + @targetName("scalarTimesVector") + def *(vec: V): V = vec.map(scalar)(BuildInFunction.VectorTimesScalar) + +// Matrix * Scalar +extension [T <: FloatType: Value, M <: Mat[T]: Value](mat: M) + @targetName("matrixTimesScalar") + def *(scalar: T): M = mat.map(scalar)(BuildInFunction.MatrixTimesScalar) + +extension [T <: FloatType: Value, M <: Mat[T]: Value](scalar: T) + @targetName("scalarTimesMatrix") + def *(mat: M): M = mat.map(scalar)(BuildInFunction.MatrixTimesScalar) + +// Dot product: Vec * Vec -> Scalar +extension [T <: FloatType: Value, V <: Vec[T]: Value](v1: V) + @targetName("dotProduct") + infix def dot(v2: V): T = v1.map[V, T](v2)(BuildInFunction.Dot) + +// Vector * Matrix/Vector operations +extension [T <: FloatType: Value]( + vec: Vec2[T] +)(using Value[Mat2x2[T]], Value[Vec2[T]], Value[Mat2x3[T]], Value[Vec3[T]], Value[Mat2x4[T]], Value[Vec4[T]]) + @targetName("vec2TimesMat2x2") + def *(mat: Mat2x2[T]): Vec2[T] = vec.map[Mat2x2[T], Vec2[T]](mat)(BuildInFunction.VectorTimesMatrix) + @targetName("vec2TimesMat2x3") + def *(mat: Mat2x3[T]): Vec3[T] = vec.map[Mat2x3[T], Vec3[T]](mat)(BuildInFunction.VectorTimesMatrix) + @targetName("vec2TimesMat2x4") + def *(mat: Mat2x4[T]): Vec4[T] = vec.map[Mat2x4[T], Vec4[T]](mat)(BuildInFunction.VectorTimesMatrix) + +extension [T <: FloatType: Value]( + vec: Vec3[T] +)(using Value[Mat3x2[T]], Value[Vec2[T]], Value[Mat3x3[T]], Value[Vec3[T]], Value[Mat3x4[T]], Value[Vec4[T]]) + @targetName("vec3TimesMat3x2") + def *(mat: Mat3x2[T]): Vec2[T] = vec.map[Mat3x2[T], Vec2[T]](mat)(BuildInFunction.VectorTimesMatrix) + @targetName("vec3TimesMat3x3") + def *(mat: Mat3x3[T]): Vec3[T] = vec.map[Mat3x3[T], Vec3[T]](mat)(BuildInFunction.VectorTimesMatrix) + @targetName("vec3TimesMat3x4") + def *(mat: Mat3x4[T]): Vec4[T] = vec.map[Mat3x4[T], Vec4[T]](mat)(BuildInFunction.VectorTimesMatrix) + +extension [T <: FloatType: Value]( + vec: Vec4[T] +)(using Value[Mat4x2[T]], Value[Vec2[T]], Value[Mat4x3[T]], Value[Vec3[T]], Value[Mat4x4[T]], Value[Vec4[T]]) + @targetName("vec4TimesMat4x2") + def *(mat: Mat4x2[T]): Vec2[T] = vec.map[Mat4x2[T], Vec2[T]](mat)(BuildInFunction.VectorTimesMatrix) + @targetName("vec4TimesMat4x3") + def *(mat: Mat4x3[T]): Vec3[T] = vec.map[Mat4x3[T], Vec3[T]](mat)(BuildInFunction.VectorTimesMatrix) + @targetName("vec4TimesMat4x4") + def *(mat: Mat4x4[T]): Vec4[T] = vec.map[Mat4x4[T], Vec4[T]](mat)(BuildInFunction.VectorTimesMatrix) + +// Matrix * Matrix/Vector operations +extension [T <: FloatType: Value](left: Mat2x2[T])(using Value[Mat2x2[T]], Value[Mat2x3[T]], Value[Mat2x4[T]], Value[Vec2[T]]) + @targetName("mat2x2TimesVec2") + def *(vec: Vec2[T]): Vec2[T] = left.map[Vec2[T], Vec2[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat2x2TimesMat2x2") + def *(right: Mat2x2[T]): Mat2x2[T] = left.map[Mat2x2[T], Mat2x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat2x2TimesMat2x3") + def *(right: Mat2x3[T]): Mat2x3[T] = left.map[Mat2x3[T], Mat2x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat2x2TimesMat2x4") + def *(right: Mat2x4[T]): Mat2x4[T] = left.map[Mat2x4[T], Mat2x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +extension [T <: FloatType: Value]( + left: Mat2x3[T] +)(using Value[Mat2x3[T]], Value[Mat3x2[T]], Value[Mat2x2[T]], Value[Mat3x3[T]], Value[Mat3x4[T]], Value[Mat2x4[T]], Value[Vec2[T]], Value[Vec3[T]]) + @targetName("mat2x3TimesVec3") + def *(vec: Vec3[T]): Vec2[T] = left.map[Vec3[T], Vec2[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat2x3TimesMat3x2") + def *(right: Mat3x2[T]): Mat2x2[T] = left.map[Mat3x2[T], Mat2x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat2x3TimesMat3x3") + def *(right: Mat3x3[T]): Mat2x3[T] = left.map[Mat3x3[T], Mat2x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat2x3TimesMat3x4") + def *(right: Mat3x4[T]): Mat2x4[T] = left.map[Mat3x4[T], Mat2x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +extension [T <: FloatType: Value]( + left: Mat2x4[T] +)(using Value[Mat2x4[T]], Value[Mat4x2[T]], Value[Mat2x2[T]], Value[Mat4x3[T]], Value[Mat2x3[T]], Value[Mat4x4[T]], Value[Vec2[T]], Value[Vec4[T]]) + @targetName("mat2x4TimesVec4") + def *(vec: Vec4[T]): Vec2[T] = left.map[Vec4[T], Vec2[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat2x4TimesMat4x2") + def *(right: Mat4x2[T]): Mat2x2[T] = left.map[Mat4x2[T], Mat2x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat2x4TimesMat4x3") + def *(right: Mat4x3[T]): Mat2x3[T] = left.map[Mat4x3[T], Mat2x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat2x4TimesMat4x4") + def *(right: Mat4x4[T]): Mat2x4[T] = left.map[Mat4x4[T], Mat2x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +extension [T <: FloatType: Value]( + left: Mat3x2[T] +)(using Value[Mat3x2[T]], Value[Mat2x2[T]], Value[Mat2x3[T]], Value[Mat3x3[T]], Value[Mat2x4[T]], Value[Mat3x4[T]], Value[Vec2[T]], Value[Vec3[T]]) + @targetName("mat3x2TimesVec2") + def *(vec: Vec2[T]): Vec3[T] = left.map[Vec2[T], Vec3[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat3x2TimesMat2x2") + def *(right: Mat2x2[T]): Mat3x2[T] = left.map[Mat2x2[T], Mat3x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat3x2TimesMat2x3") + def *(right: Mat2x3[T]): Mat3x3[T] = left.map[Mat2x3[T], Mat3x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat3x2TimesMat2x4") + def *(right: Mat2x4[T]): Mat3x4[T] = left.map[Mat2x4[T], Mat3x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +extension [T <: FloatType: Value](left: Mat3x3[T])(using Value[Mat3x3[T]], Value[Mat3x2[T]], Value[Mat3x4[T]], Value[Vec3[T]]) + @targetName("mat3x3TimesVec3") + def *(vec: Vec3[T]): Vec3[T] = left.map[Vec3[T], Vec3[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat3x3TimesMat3x2") + def *(right: Mat3x2[T]): Mat3x2[T] = left.map[Mat3x2[T], Mat3x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat3x3TimesMat3x3") + def *(right: Mat3x3[T]): Mat3x3[T] = left.map[Mat3x3[T], Mat3x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat3x3TimesMat3x4") + def *(right: Mat3x4[T]): Mat3x4[T] = left.map[Mat3x4[T], Mat3x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +extension [T <: FloatType: Value]( + left: Mat3x4[T] +)(using Value[Mat3x4[T]], Value[Mat4x2[T]], Value[Mat3x2[T]], Value[Mat4x3[T]], Value[Mat3x3[T]], Value[Mat4x4[T]], Value[Vec3[T]], Value[Vec4[T]]) + @targetName("mat3x4TimesVec4") + def *(vec: Vec4[T]): Vec3[T] = left.map[Vec4[T], Vec3[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat3x4TimesMat4x2") + def *(right: Mat4x2[T]): Mat3x2[T] = left.map[Mat4x2[T], Mat3x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat3x4TimesMat4x3") + def *(right: Mat4x3[T]): Mat3x3[T] = left.map[Mat4x3[T], Mat3x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat3x4TimesMat4x4") + def *(right: Mat4x4[T]): Mat3x4[T] = left.map[Mat4x4[T], Mat3x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +extension [T <: FloatType: Value]( + left: Mat4x2[T] +)(using Value[Mat4x2[T]], Value[Mat2x2[T]], Value[Mat2x3[T]], Value[Mat4x3[T]], Value[Mat2x4[T]], Value[Mat4x4[T]], Value[Vec2[T]], Value[Vec4[T]]) + @targetName("mat4x2TimesVec2") + def *(vec: Vec2[T]): Vec4[T] = left.map[Vec2[T], Vec4[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat4x2TimesMat2x2") + def *(right: Mat2x2[T]): Mat4x2[T] = left.map[Mat2x2[T], Mat4x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat4x2TimesMat2x3") + def *(right: Mat2x3[T]): Mat4x3[T] = left.map[Mat2x3[T], Mat4x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat4x2TimesMat2x4") + def *(right: Mat2x4[T]): Mat4x4[T] = left.map[Mat2x4[T], Mat4x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +extension [T <: FloatType: Value]( + left: Mat4x3[T] +)(using Value[Mat4x3[T]], Value[Mat3x2[T]], Value[Mat4x2[T]], Value[Mat3x3[T]], Value[Mat3x4[T]], Value[Mat4x4[T]], Value[Vec3[T]], Value[Vec4[T]]) + @targetName("mat4x3TimesVec3") + def *(vec: Vec3[T]): Vec4[T] = left.map[Vec3[T], Vec4[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat4x3TimesMat3x2") + def *(right: Mat3x2[T]): Mat4x2[T] = left.map[Mat3x2[T], Mat4x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat4x3TimesMat3x3") + def *(right: Mat3x3[T]): Mat4x3[T] = left.map[Mat3x3[T], Mat4x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat4x3TimesMat3x4") + def *(right: Mat3x4[T]): Mat4x4[T] = left.map[Mat3x4[T], Mat4x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +extension [T <: FloatType: Value](left: Mat4x4[T])(using Value[Mat4x4[T]], Value[Mat4x2[T]], Value[Mat4x3[T]], Value[Vec4[T]]) + @targetName("mat4x4TimesVec4") + def *(vec: Vec4[T]): Vec4[T] = left.map[Vec4[T], Vec4[T]](vec)(BuildInFunction.MatrixTimesVector) + @targetName("mat4x4TimesMat4x2") + def *(right: Mat4x2[T]): Mat4x2[T] = left.map[Mat4x2[T], Mat4x2[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat4x4TimesMat4x3") + def *(right: Mat4x3[T]): Mat4x3[T] = left.map[Mat4x3[T], Mat4x3[T]](right)(BuildInFunction.MatrixTimesMatrix) + @targetName("mat4x4TimesMat4x4") + def *(right: Mat4x4[T]): Mat4x4[T] = left.map[Mat4x4[T], Mat4x4[T]](right)(BuildInFunction.MatrixTimesMatrix) + +// Outer product: Vec * Vec -> Matrix +extension [T <: FloatType: Value](v1: Vec2[T])(using Value[Vec2[T]], Value[Mat2x2[T]]) + @targetName("outerProductVec2") + infix def outer(v2: Vec2[T]): Mat2x2[T] = v1.map[Vec2[T], Mat2x2[T]](v2)(BuildInFunction.OuterProduct) + +extension [T <: FloatType: Value](v1: Vec3[T])(using Value[Vec3[T]], Value[Mat3x3[T]]) + @targetName("outerProductVec3") + infix def outer(v2: Vec3[T]): Mat3x3[T] = v1.map[Vec3[T], Mat3x3[T]](v2)(BuildInFunction.OuterProduct) + +extension [T <: FloatType: Value](v1: Vec4[T])(using Value[Vec4[T]], Value[Mat4x4[T]]) + @targetName("outerProductVec4") + infix def outer(v2: Vec4[T]): Mat4x4[T] = v1.map[Vec4[T], Mat4x4[T]](v2)(BuildInFunction.OuterProduct) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/BitwiseOps.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/BitwiseOps.scala new file mode 100644 index 00000000..a5a3cee4 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/BitwiseOps.scala @@ -0,0 +1,50 @@ +package io.computenode.cyfra.core.expression.ops + +import io.computenode.cyfra.core.expression.* +import io.computenode.cyfra.core.expression.Value.map +import io.computenode.cyfra.core.expression.{BuildInFunction, Value} + +import scala.annotation.targetName + +given [T <: IntegerType: Value]: BitwiseOps[T] with {} +given [T <: IntegerType: Value]: BitwiseOps[Vec2[T]] with {} +given [T <: IntegerType: Value]: BitwiseOps[Vec3[T]] with {} +given [T <: IntegerType: Value]: BitwiseOps[Vec4[T]] with {} + +trait BitwiseOps[T] + +extension [T: {BitwiseOps, Value}](self: T) + @targetName("shiftRightLogical") + infix def >>>(shift: T): T = self.map(shift)(BuildInFunction.ShiftRightLogical) + + @targetName("shiftRightArithmetic") + infix def >>(shift: T): T = self.map(shift)(BuildInFunction.ShiftRightArithmetic) + + @targetName("shiftLeftLogical") + infix def <<(shift: T): T = self.map(shift)(BuildInFunction.ShiftLeftLogical) + + @targetName("bitwiseOr") + def |(that: T): T = self.map(that)(BuildInFunction.BitwiseOr) + + @targetName("bitwiseXor") + def ^(that: T): T = self.map(that)(BuildInFunction.BitwiseXor) + + @targetName("bitwiseAnd") + def &(that: T): T = self.map(that)(BuildInFunction.BitwiseAnd) + + @targetName("bitwiseNot") + def unary_~ : T = self.map(BuildInFunction.BitwiseNot) + + def bitFieldInsert[Offset: Value, Count: Value](insert: T, offset: Offset, count: Count): T = + self.map[T, Offset, Count, T](insert, offset, count)(BuildInFunction.BitFieldInsert) + + def bitFieldSExtract[Offset: Value, Count: Value](offset: Offset, count: Count): T = + self.map[Offset, Count, T](offset, count)(BuildInFunction.BitFieldSExtract) + + def bitFieldUExtract[Offset: Value, Count: Value](offset: Offset, count: Count): T = + self.map[Offset, Count, T](offset, count)(BuildInFunction.BitFieldUExtract) + + def bitReverse: T = self.map(BuildInFunction.BitReverse) + + def bitCount: T = self.map[T](BuildInFunction.BitCount) + diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/BooleanOps.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/BooleanOps.scala new file mode 100644 index 00000000..06d61300 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/BooleanOps.scala @@ -0,0 +1,94 @@ +package io.computenode.cyfra.core.expression.ops + +import io.computenode.cyfra.core.expression.* +import io.computenode.cyfra.core.expression.Value.map +import io.computenode.cyfra.core.expression.{BuildInFunction, Value} +import io.computenode.cyfra.core.expression.given + +import scala.annotation.targetName + +// Logical operations on booleans +given [T <: Bool: Value]: BooleanOps[T] with {} +given [T <: Bool: Value]: BooleanOps[Vec2[T]] with {} +given [T <: Bool: Value]: BooleanOps[Vec3[T]] with {} +given [T <: Bool: Value]: BooleanOps[Vec4[T]] with {} + +trait BooleanOps[T] + +extension [T: {BooleanOps, Value}](self: T) + @targetName("logicalOr") + def ||(that: T): T = self.map(that)(BuildInFunction.LogicalOr) + + @targetName("logicalAnd") + def &&(that: T): T = self.map(that)(BuildInFunction.LogicalAnd) + + @targetName("logicalNot") + def unary_! : T = self.map(BuildInFunction.LogicalNot) + + @targetName("logicalEqual") + def ===(that: T): T = self.map(that)(BuildInFunction.LogicalEqual) + + @targetName("logicalNotEqual") + def !==(that: T): T = self.map(that)(BuildInFunction.LogicalNotEqual) + +extension [V <: Vec[Bool]: Value](self: V) + def any: Bool = self.map[Bool](BuildInFunction.LogicalAny) + + def all: Bool = self.map[Bool](BuildInFunction.LogicalAll) + +// Floating-point checks +given [T <: FloatType: Value]: FloatCheckOps[T] with {} +given [T <: FloatType: Value]: FloatCheckOps[Vec2[T]] with {} +given [T <: FloatType: Value]: FloatCheckOps[Vec3[T]] with {} +given [T <: FloatType: Value]: FloatCheckOps[Vec4[T]] with {} + +trait FloatCheckOps[T] + +extension [T: {FloatCheckOps, Value}](self: T) + def isNan: Bool = self.map[Bool](BuildInFunction.IsNan) + + def isInf: Bool = self.map[Bool](BuildInFunction.IsInf) + + def isFinite: Bool = self.map[Bool](BuildInFunction.IsFinite) + + def isNormal: Bool = self.map[Bool](BuildInFunction.IsNormal) + + def signBitSet: Bool = self.map[Bool](BuildInFunction.SignBitSet) + +// Unified comparisons (works for floats, signed ints, and unsigned ints) +// Type detection happens later in the program, floats use ordered operations +given [T <: NumericalType: Value]: ComparisonOps[T] with {} +given [T <: NumericalType: Value]: ComparisonOps[Vec2[T]] with {} +given [T <: NumericalType: Value]: ComparisonOps[Vec3[T]] with {} +given [T <: NumericalType: Value]: ComparisonOps[Vec4[T]] with {} + +trait ComparisonOps[T] + +extension [T: {ComparisonOps, Value}](self: T) + @targetName("equal") + def ===(that: T): Bool = self.map[T, Bool](that)(BuildInFunction.Equal) + + @targetName("notEqual") + def !==(that: T): Bool = self.map[T, Bool](that)(BuildInFunction.NotEqual) + + @targetName("lessThan") + def <(that: T): Bool = self.map[T, Bool](that)(BuildInFunction.LessThan) + + @targetName("greaterThan") + def >(that: T): Bool = self.map[T, Bool](that)(BuildInFunction.GreaterThan) + + @targetName("lessThanEqual") + def <=(that: T): Bool = self.map[T, Bool](that)(BuildInFunction.LessThanEqual) + + @targetName("greaterThanEqual") + def >=(that: T): Bool = self.map[T, Bool](that)(BuildInFunction.GreaterThanEqual) + +// Select operation +extension [T: Value](cond: Bool) + def select(obj1: T, obj2: T): T = + cond.map[T, T, T](obj1, obj2)(BuildInFunction.Select) + +extension [V <: Vec[Bool]: Value, T <: Vec[?]: Value](cond: V) + def select(obj1: T, obj2: T): T = + cond.map[T, T, T](obj1, obj2)(BuildInFunction.Select) + diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/NegativeElementOps.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/NegativeElementOps.scala new file mode 100644 index 00000000..2a864126 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/ops/NegativeElementOps.scala @@ -0,0 +1,20 @@ +package io.computenode.cyfra.core.expression.ops + +import io.computenode.cyfra.core.expression.* +import io.computenode.cyfra.core.expression.Value.map +import io.computenode.cyfra.core.expression.{BuildInFunction, Value} + +import scala.annotation.targetName + +given [T <: NegativeType: Value]: NegativeElementOps[T] with {} +given [T <: NegativeType: Value]: NegativeElementOps[Vec2[T]] with {} +given [T <: NegativeType: Value]: NegativeElementOps[Vec3[T]] with {} +given [T <: NegativeType: Value]: NegativeElementOps[Vec4[T]] with {} + +trait NegativeElementOps[T] + +extension [T: {NegativeElementOps, Value}](self: T) + @targetName("neg") + def unary_- : T = self.map(BuildInFunction.Neg) + @targetName("rem") + infix def rem(that: T): T = self.map(that)(BuildInFunction.Rem) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/types.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/types.scala new file mode 100644 index 00000000..1367965b --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/types.scala @@ -0,0 +1,224 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.core.expression.ops.* +import io.computenode.cyfra.core.expression.ops.given +import io.computenode.cyfra.core.expression.Value.map + +sealed trait Scalar + +abstract class Bool extends Scalar + +sealed trait NumericalType extends Scalar +sealed trait NegativeType extends NumericalType + +sealed trait FloatType extends NegativeType +abstract class Float16 extends FloatType +abstract class Float32 extends FloatType + +sealed trait IntegerType extends NumericalType + +sealed trait SignedIntType extends IntegerType with NegativeType +abstract class Int16 extends SignedIntType +abstract class Int32 extends SignedIntType + +sealed trait UnsignedIntType extends IntegerType +abstract class UInt16 extends UnsignedIntType +abstract class UInt32 extends UnsignedIntType + +sealed trait Vec[T <: Scalar: Value] +abstract class Vec2[T <: Scalar: Value] extends Vec[T] +abstract class Vec3[T <: Scalar: Value] extends Vec[T] +abstract class Vec4[T <: Scalar: Value] extends Vec[T] + +sealed trait Mat[T <: Scalar: Value] +abstract class Mat2x2[T <: Scalar: Value] extends Mat[T] +abstract class Mat2x3[T <: Scalar: Value] extends Mat[T] +abstract class Mat2x4[T <: Scalar: Value] extends Mat[T] +abstract class Mat3x2[T <: Scalar: Value] extends Mat[T] +abstract class Mat3x3[T <: Scalar: Value] extends Mat[T] +abstract class Mat3x4[T <: Scalar: Value] extends Mat[T] +abstract class Mat4x2[T <: Scalar: Value] extends Mat[T] +abstract class Mat4x3[T <: Scalar: Value] extends Mat[T] +abstract class Mat4x4[T <: Scalar: Value] extends Mat[T] + +private def const[A: Value](value: Any): A = + summon[Value[A]].extract(ExpressionBlock(Expression.Constant[A](value))) + +object Float16: + def apply(value: Float): Float16 = const(value) + +object Float32: + def apply(value: Float): Float32 = const(value) + +object Int16: + def apply(value: Int): Int16 = const(value) + +object Int32: + def apply(value: Int): Int32 = const(value) + +object UInt16: + def apply(value: Int): UInt16 = const(value) + +object UInt32: + def apply(value: Int): UInt32 = const(value) + +object Bool: + def apply(value: Boolean): Bool = const(value) + +object Vec2: + def apply[A <: FloatType: Value](x: Float, y: Float): Vec2[A] = const((x, y)) + def apply[A <: IntegerType: Value](x: Int, y: Int): Vec2[A] = const((x, y)) + +object Vec3: + def apply[A <: FloatType: Value](x: Float, y: Float, z: Float): Vec3[A] = const((x, y, z)) + def apply[A <: IntegerType: Value](x: Int, y: Int, z: Int): Vec3[A] = const((x, y, z)) + +object Vec4: + def apply[A <: FloatType: Value](x: Float, y: Float, z: Float, w: Float): Vec4[A] = const((x, y, z, w)) + def apply[A <: IntegerType: Value](x: Int, y: Int, z: Int, w: Int): Vec4[A] = const((x, y, z, w)) + +object Mat2x2: + def apply[A <: FloatType: Value](m00: Float, m01: Float, m10: Float, m11: Float): Mat2x2[A] = const((m00, m01, m10, m11)) + def apply[A <: IntegerType: Value](m00: Int, m01: Int, m10: Int, m11: Int): Mat2x2[A] = const((m00, m01, m10, m11)) + +object Mat2x3: + def apply[A <: FloatType: Value](m00: Float, m01: Float, m02: Float, m10: Float, m11: Float, m12: Float): Mat2x3[A] = const( + (m00, m01, m02, m10, m11, m12), + ) + def apply[A <: IntegerType: Value](m00: Int, m01: Int, m02: Int, m10: Int, m11: Int, m12: Int): Mat2x3[A] = const((m00, m01, m02, m10, m11, m12)) + +object Mat2x4: + def apply[A <: FloatType: Value](m00: Float, m01: Float, m02: Float, m03: Float, m10: Float, m11: Float, m12: Float, m13: Float): Mat2x4[A] = const( + (m00, m01, m02, m03, m10, m11, m12, m13), + ) + def apply[A <: IntegerType: Value](m00: Int, m01: Int, m02: Int, m03: Int, m10: Int, m11: Int, m12: Int, m13: Int): Mat2x4[A] = const( + (m00, m01, m02, m03, m10, m11, m12, m13), + ) + +object Mat3x2: + def apply[A <: FloatType: Value](m00: Float, m01: Float, m10: Float, m11: Float, m20: Float, m21: Float): Mat3x2[A] = const( + (m00, m01, m10, m11, m20, m21), + ) + def apply[A <: IntegerType: Value](m00: Int, m01: Int, m10: Int, m11: Int, m20: Int, m21: Int): Mat3x2[A] = const((m00, m01, m10, m11, m20, m21)) + +object Mat3x3: + def apply[A <: FloatType: Value]( + m00: Float, + m01: Float, + m02: Float, + m10: Float, + m11: Float, + m12: Float, + m20: Float, + m21: Float, + m22: Float, + ): Mat3x3[A] = const((m00, m01, m02, m10, m11, m12, m20, m21, m22)) + def apply[A <: IntegerType: Value](m00: Int, m01: Int, m02: Int, m10: Int, m11: Int, m12: Int, m20: Int, m21: Int, m22: Int): Mat3x3[A] = const( + (m00, m01, m02, m10, m11, m12, m20, m21, m22), + ) + +object Mat3x4: + def apply[A <: FloatType: Value]( + m00: Float, + m01: Float, + m02: Float, + m03: Float, + m10: Float, + m11: Float, + m12: Float, + m13: Float, + m20: Float, + m21: Float, + m22: Float, + m23: Float, + ): Mat3x4[A] = const((m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23)) + def apply[A <: IntegerType: Value]( + m00: Int, + m01: Int, + m02: Int, + m03: Int, + m10: Int, + m11: Int, + m12: Int, + m13: Int, + m20: Int, + m21: Int, + m22: Int, + m23: Int, + ): Mat3x4[A] = const((m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23)) + +object Mat4x2: + def apply[A <: FloatType: Value](m00: Float, m01: Float, m10: Float, m11: Float, m20: Float, m21: Float, m30: Float, m31: Float): Mat4x2[A] = const( + (m00, m01, m10, m11, m20, m21, m30, m31), + ) + def apply[A <: IntegerType: Value](m00: Int, m01: Int, m10: Int, m11: Int, m20: Int, m21: Int, m30: Int, m31: Int): Mat4x2[A] = const( + (m00, m01, m10, m11, m20, m21, m30, m31), + ) + +object Mat4x3: + def apply[A <: FloatType: Value]( + m00: Float, + m01: Float, + m02: Float, + m10: Float, + m11: Float, + m12: Float, + m20: Float, + m21: Float, + m22: Float, + m30: Float, + m31: Float, + m32: Float, + ): Mat4x3[A] = const((m00, m01, m02, m10, m11, m12, m20, m21, m22, m30, m31, m32)) + def apply[A <: IntegerType: Value]( + m00: Int, + m01: Int, + m02: Int, + m10: Int, + m11: Int, + m12: Int, + m20: Int, + m21: Int, + m22: Int, + m30: Int, + m31: Int, + m32: Int, + ): Mat4x3[A] = const((m00, m01, m02, m10, m11, m12, m20, m21, m22, m30, m31, m32)) + +object Mat4x4: + def apply[A <: FloatType: Value]( + m00: Float, + m01: Float, + m02: Float, + m03: Float, + m10: Float, + m11: Float, + m12: Float, + m13: Float, + m20: Float, + m21: Float, + m22: Float, + m23: Float, + m30: Float, + m31: Float, + m32: Float, + m33: Float, + ): Mat4x4[A] = const((m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33)) + def apply[A <: IntegerType: Value]( + m00: Int, + m01: Int, + m02: Int, + m03: Int, + m10: Int, + m11: Int, + m12: Int, + m13: Int, + m20: Int, + m21: Int, + m22: Int, + m23: Int, + m30: Int, + m31: Int, + m32: Int, + m33: Int, + ): Mat4x4[A] = const((m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33)) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/typesImpl.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/typesImpl.scala new file mode 100644 index 00000000..16f30c17 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/typesImpl.scala @@ -0,0 +1,26 @@ +package io.computenode.cyfra.core.expression + +import io.computenode.cyfra.core.expression.* + +final class Float16Impl(val block: ExpressionBlock[Float16]) extends Float16 with ExpressionHolder[Float16] +final class Float32Impl(val block: ExpressionBlock[Float32]) extends Float32 with ExpressionHolder[Float32] +final class Int16Impl(val block: ExpressionBlock[Int16]) extends Int16 with ExpressionHolder[Int16] +final class Int32Impl(val block: ExpressionBlock[Int32]) extends Int32 with ExpressionHolder[Int32] +final class UInt16Impl(val block: ExpressionBlock[UInt16]) extends UInt16 with ExpressionHolder[UInt16] +final class UInt32Impl(val block: ExpressionBlock[UInt32]) extends UInt32 with ExpressionHolder[UInt32] +final class BoolImpl(val block: ExpressionBlock[Bool]) extends Bool with ExpressionHolder[Bool] + +final class Vec2Impl[T <: Scalar: Value](val block: ExpressionBlock[Vec2[T]]) extends Vec2[T] with ExpressionHolder[Vec2[T]] +final class Vec3Impl[T <: Scalar: Value](val block: ExpressionBlock[Vec3[T]]) extends Vec3[T] with ExpressionHolder[Vec3[T]] +final class Vec4Impl[T <: Scalar: Value](val block: ExpressionBlock[Vec4[T]]) extends Vec4[T] with ExpressionHolder[Vec4[T]] + +final class Mat2x2Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat2x2[T]]) extends Mat2x2[T] with ExpressionHolder[Mat2x2[T]] +final class Mat2x3Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat2x3[T]]) extends Mat2x3[T] with ExpressionHolder[Mat2x3[T]] +final class Mat2x4Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat2x4[T]]) extends Mat2x4[T] with ExpressionHolder[Mat2x4[T]] +final class Mat3x2Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat3x2[T]]) extends Mat3x2[T] with ExpressionHolder[Mat3x2[T]] +final class Mat3x3Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat3x3[T]]) extends Mat3x3[T] with ExpressionHolder[Mat3x3[T]] +final class Mat3x4Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat3x4[T]]) extends Mat3x4[T] with ExpressionHolder[Mat3x4[T]] +final class Mat4x2Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat4x2[T]]) extends Mat4x2[T] with ExpressionHolder[Mat4x2[T]] +final class Mat4x3Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat4x3[T]]) extends Mat4x3[T] with ExpressionHolder[Mat4x3[T]] +final class Mat4x4Impl[T <: Scalar: Value](val block: ExpressionBlock[Mat4x4[T]]) extends Mat4x4[T] with ExpressionHolder[Mat4x4[T]] + diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/typesValue.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/typesValue.scala new file mode 100644 index 00000000..b99e15e8 --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/expression/typesValue.scala @@ -0,0 +1,100 @@ +package io.computenode.cyfra.core.expression + +import izumi.reflect.Tag + +given Value[Float16] with + protected def extractUnsafe(ir: ExpressionBlock[Float16]): Float16 = new Float16Impl(ir) + def tag: Tag[Float16] = Tag[Float16] + +given Value[Float32] with + protected def extractUnsafe(ir: ExpressionBlock[Float32]): Float32 = new Float32Impl(ir) + def tag: Tag[Float32] = Tag[Float32] + +given Value[Int16] with + protected def extractUnsafe(ir: ExpressionBlock[Int16]): Int16 = new Int16Impl(ir) + def tag: Tag[Int16] = Tag[Int16] + +given Value[Int32] with + protected def extractUnsafe(ir: ExpressionBlock[Int32]): Int32 = new Int32Impl(ir) + def tag: Tag[Int32] = Tag[Int32] + +given Value[UInt16] with + protected def extractUnsafe(ir: ExpressionBlock[UInt16]): UInt16 = new UInt16Impl(ir) + def tag: Tag[UInt16] = Tag[UInt16] + +given Value[UInt32] with + protected def extractUnsafe(ir: ExpressionBlock[UInt32]): UInt32 = new UInt32Impl(ir) + def tag: Tag[UInt32] = Tag[UInt32] + +given Value[Bool] with + protected def extractUnsafe(ir: ExpressionBlock[Bool]): Bool = new BoolImpl(ir) + def tag: Tag[Bool] = Tag[Bool] + +val unitZero = Expression.Constant[Unit](()) +given Value[Unit] with + protected def extractUnsafe(ir: ExpressionBlock[Unit]): Unit = () + def tag: Tag[Unit] = Tag[Unit] + +given Value[Any] with + protected def extractUnsafe(ir: ExpressionBlock[Any]): Any = ir.result.asInstanceOf[Expression.Constant[Any]].value + def tag: Tag[Any] = Tag[Any] + +given [T <: Scalar: Value]: Value[Vec2[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Vec2[T]]): Vec2[T] = new Vec2Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Vec2[T]] = Tag[Vec2[T]] + +given [T <: Scalar: Value]: Value[Vec3[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Vec3[T]]): Vec3[T] = new Vec3Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Vec3[T]] = Tag[Vec3[T]] + +given [T <: Scalar: Value]: Value[Vec4[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Vec4[T]]): Vec4[T] = new Vec4Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Vec4[T]] = Tag[Vec4[T]] + +given [T <: Scalar: Value]: Value[Mat2x2[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat2x2[T]]): Mat2x2[T] = new Mat2x2Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat2x2[T]] = Tag[Mat2x2[T]] + +given [T <: Scalar: Value]: Value[Mat2x3[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat2x3[T]]): Mat2x3[T] = new Mat2x3Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat2x3[T]] = Tag[Mat2x3[T]] + +given [T <: Scalar: Value]: Value[Mat2x4[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat2x4[T]]): Mat2x4[T] = new Mat2x4Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat2x4[T]] = Tag[Mat2x4[T]] + +given [T <: Scalar: Value]: Value[Mat3x2[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat3x2[T]]): Mat3x2[T] = new Mat3x2Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat3x2[T]] = Tag[Mat3x2[T]] + +given [T <: Scalar: Value]: Value[Mat3x3[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat3x3[T]]): Mat3x3[T] = new Mat3x3Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat3x3[T]] = Tag[Mat3x3[T]] + +given [T <: Scalar: Value]: Value[Mat3x4[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat3x4[T]]): Mat3x4[T] = new Mat3x4Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat3x4[T]] = Tag[Mat3x4[T]] + +given [T <: Scalar: Value]: Value[Mat4x2[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat4x2[T]]): Mat4x2[T] = new Mat4x2Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat4x2[T]] = Tag[Mat4x2[T]] + +given [T <: Scalar: Value]: Value[Mat4x3[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat4x3[T]]): Mat4x3[T] = new Mat4x3Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat4x3[T]] = Tag[Mat4x3[T]] + +given [T <: Scalar: Value]: Value[Mat4x4[T]] with + protected def extractUnsafe(ir: ExpressionBlock[Mat4x4[T]]): Mat4x4[T] = new Mat4x4Impl[T](ir) + given Tag[T] = summon[Value[T]].tag + def tag: Tag[Mat4x4[T]] = Tag[Mat4x4[T]] diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/layout/LayoutBinding.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/layout/LayoutBinding.scala index 5a7eaa52..524c5b30 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/layout/LayoutBinding.scala +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/layout/LayoutBinding.scala @@ -1,11 +1,11 @@ package io.computenode.cyfra.core.layout -import io.computenode.cyfra.dsl.binding.GBinding - import scala.Tuple.* import scala.compiletime.{constValue, erasedValue, error} import scala.deriving.Mirror +import io.computenode.cyfra.core.binding.GBinding + trait LayoutBinding[L <: Layout]: def fromBindings(bindings: Seq[GBinding[?]]): L def toBindings(layout: L): Seq[GBinding[?]] 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 1b460121..4101d5cd 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 @@ -1,10 +1,5 @@ package io.computenode.cyfra.core.layout -import io.computenode.cyfra.core.binding.{BufferRef, UniformRef} -import io.computenode.cyfra.dsl.Value -import io.computenode.cyfra.dsl.Value.FromExpr -import io.computenode.cyfra.dsl.binding.{GBinding, GBuffer, GUniform} -import io.computenode.cyfra.dsl.struct.{GStruct, GStructSchema} import izumi.reflect.Tag import izumi.reflect.macrortti.LightTypeTag @@ -12,91 +7,10 @@ import scala.compiletime.{error, summonAll} import scala.deriving.Mirror import scala.quoted.{Expr, Quotes, Type} -case class LayoutStruct[T <: Layout: Tag](private[cyfra] val layoutRef: T, private[cyfra] val elementTypes: List[Tag[? <: Value]]) +case class LayoutStruct[T <: Layout: Tag](private[cyfra] val layoutRef: T, private[cyfra] val elementTypes: List[Tag[?]]) object LayoutStruct: inline given derived[T <: Layout: Tag]: LayoutStruct[T] = ${ derivedImpl } - def derivedImpl[T <: Layout: Type](using quotes: Quotes): Expr[LayoutStruct[T]] = - import quotes.reflect.* - - val tpe = TypeRepr.of[T] - val sym = tpe.typeSymbol - - if !sym.isClassDef || !sym.flags.is(Flags.Case) then report.errorAndAbort("LayoutStruct can only be derived for case classes") - - val fieldTypes = sym.caseFields - .map(_.tree) - .map: - case ValDef(_, tpt, _) => tpt.tpe - case _ => report.errorAndAbort("Unexpected field type in case class") - - if !fieldTypes.forall(_ <:< TypeRepr.of[GBinding[?]]) then - report.errorAndAbort("LayoutStruct can only be derived for case classes with GBinding elements") - - val valueTypes = fieldTypes.map: ftype => - ftype match - case AppliedType(_, args) if args.nonEmpty => - val valueType = args.head - // Ensure we're working with the original type parameter, not the instance type - val resolvedType = valueType match - case tr if tr.typeSymbol.isTypeParam => - // Find the corresponding type parameter from the original class - tpe.typeArgs.find(_.typeSymbol.name == tr.typeSymbol.name).getOrElse(tr) - case tr => tr - (ftype, resolvedType) - case _ => - report.errorAndAbort("GBinding must have a value type") - - // summon izumi tags - val typeGivens = valueTypes.map: - case (ftype, farg) => - farg.asType match - case '[type t <: Value; t] => - ( - ftype.asType, - farg.asType, - Expr.summon[Tag[t]] match - case Some(tagExpr) => tagExpr - case None => report.errorAndAbort(s"Cannot summon Tag for type ${farg.show}"), - Expr.summon[FromExpr[t]] match - case Some(fromExpr) => fromExpr - case None => report.errorAndAbort(s"Cannot summon FromExpr for type ${farg.show}"), - ) - - val buffers = typeGivens.zipWithIndex.map: - case ((ftype, tpe, tag, fromExpr), i) => - (tpe, ftype) match - case ('[type t <: Value; t], '[type tg <: GBuffer[?]; tg]) => - '{ - BufferRef[t](${ Expr(i) }, ${ tag.asExprOf[Tag[t]] })(using ${ tag.asExprOf[Tag[t]] }, ${ fromExpr.asExprOf[FromExpr[t]] }) - } - case ('[type t <: GStruct[?]; t], '[type tg <: GUniform[?]; tg]) => - val structSchema = Expr.summon[GStructSchema[t]] match - case Some(s) => s - case None => report.errorAndAbort(s"Cannot summon GStructSchema for type") - '{ - UniformRef[t](${ Expr(i) }, ${ tag.asExprOf[Tag[t]] })(using - ${ tag.asExprOf[Tag[t]] }, - ${ fromExpr.asExprOf[FromExpr[t]] }, - ${ structSchema }, - ) - } - - val constructor = sym.primaryConstructor - report.info(s"Constructor: ${constructor.fullName} with params ${constructor.paramSymss.flatten.map(_.name).mkString(", ")}") - - val typeArgs = tpe.typeArgs - - val layoutInstance = - if typeArgs.isEmpty then Apply(Select(New(TypeIdent(sym)), constructor), buffers.map(_.asTerm)) - else Apply(TypeApply(Select(New(TypeIdent(sym)), constructor), typeArgs.map(arg => TypeTree.of(using arg.asType))), buffers.map(_.asTerm)) - - val layoutRef = layoutInstance.asExprOf[T] - - val soleTags = typeGivens.map(_._3.asExprOf[Tag[? <: Value]]).toList - - '{ - LayoutStruct[T]($layoutRef, ${ Expr.ofList(soleTags) }) - } + def derivedImpl[T <: Layout: Type](using quotes: Quotes): Expr[LayoutStruct[T]] = ??? diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/main.scala b/cyfra-core/src/main/scala/io/computenode/cyfra/core/main.scala new file mode 100644 index 00000000..9ef6845b --- /dev/null +++ b/cyfra-core/src/main/scala/io/computenode/cyfra/core/main.scala @@ -0,0 +1,15 @@ +package io.computenode.cyfra.core + +import io.computenode.cyfra.core.expression.* +import io.computenode.cyfra.core.expression.ops.* +import io.computenode.cyfra.core.expression.ops.given +import io.computenode.cyfra.core.expression.given + +@main +def main(): Unit = + val x: Mat4x4[Float32] = Mat4x4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + val y: Vec4[Float32] = Vec4(1.0f, 2.0f, 3.0f, 4.0f) + val c = x * y + println("Hello, Cyfra!") + println(summon[Value[Mat4x4[Float32]]].tag) + println(c) diff --git a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GCodec.scala b/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GCodec.scala similarity index 99% rename from cyfra-core/src/main/scala/io/computenode/cyfra/core/GCodec.scala rename to cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GCodec.scala index 9d4d4bb9..01826c51 100644 --- a/cyfra-core/src/main/scala/io/computenode/cyfra/core/GCodec.scala +++ b/cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GCodec.scala @@ -1,5 +1,5 @@ // scala -package io.computenode.cyfra.core +package io.computenode.cyfra.fs2interop import io.computenode.cyfra.dsl.* import io.computenode.cyfra.dsl.macros.Source diff --git a/cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkCyfraRuntime.scala b/cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkCyfraRuntime.scala index 2e96e221..b9354c4f 100644 --- a/cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkCyfraRuntime.scala +++ b/cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkCyfraRuntime.scala @@ -2,7 +2,7 @@ package io.computenode.cyfra.runtime import io.computenode.cyfra.core.GProgram.InitProgramLayout import io.computenode.cyfra.core.layout.{Layout, LayoutBinding, LayoutStruct} -import io.computenode.cyfra.core.{Allocation, CyfraRuntime, GExecution, GProgram, GioProgram, SpirvProgram} +import io.computenode.cyfra.core.{Allocation, CyfraRuntime, GExecution, GProgram, ExpressionProgram, SpirvProgram} import io.computenode.cyfra.spirv.compilers.DSLCompiler import io.computenode.cyfra.spirvtools.SpirvToolsRunner import io.computenode.cyfra.vulkan.VulkanContext @@ -21,9 +21,9 @@ class VkCyfraRuntime(spirvToolsRunner: SpirvToolsRunner = SpirvToolsRunner()) ex private[cyfra] def getOrLoadProgram[Params, L <: Layout: {LayoutBinding, LayoutStruct}](program: GProgram[Params, L]): VkShader[L] = synchronized: val spirvProgram: SpirvProgram[Params, L] = program match - case p: GioProgram[Params, L] if gProgramCache.contains(p) => + case p: ExpressionProgram[Params, L] if gProgramCache.contains(p) => gProgramCache(p).asInstanceOf[SpirvProgram[Params, L]] - case p: GioProgram[Params, L] => compile(p) + case p: ExpressionProgram[Params, L] => compile(p) case p: SpirvProgram[Params, L] => p case _ => throw new IllegalArgumentException(s"Unsupported program type: ${program.getClass.getName}") @@ -31,9 +31,9 @@ class VkCyfraRuntime(spirvToolsRunner: SpirvToolsRunner = SpirvToolsRunner()) ex shaderCache.getOrElseUpdate(spirvProgram.shaderHash, VkShader(spirvProgram)).asInstanceOf[VkShader[L]] private def compile[Params, L <: Layout: {LayoutBinding as lbinding, LayoutStruct as lstruct}]( - program: GioProgram[Params, L], + program: ExpressionProgram[Params, L], ): SpirvProgram[Params, L] = - val GioProgram(_, layout, dispatch, _) = program + val ExpressionProgram(_, layout, dispatch, _) = program val bindings = lbinding.toBindings(lstruct.layoutRef).toList val compiled = DSLCompiler.compile(program.body(summon[LayoutStruct[L]].layoutRef), bindings) val optimizedShaderCode = spirvToolsRunner.processShaderCodeWithSpirvTools(compiled) diff --git a/cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkShader.scala b/cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkShader.scala index 492266e9..0505cd13 100644 --- a/cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkShader.scala +++ b/cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkShader.scala @@ -1,6 +1,6 @@ package io.computenode.cyfra.runtime -import io.computenode.cyfra.core.{GProgram, GioProgram, SpirvProgram} +import io.computenode.cyfra.core.{GProgram, ExpressionProgram, SpirvProgram} import io.computenode.cyfra.core.SpirvProgram.* import io.computenode.cyfra.core.GProgram.InitProgramLayout import io.computenode.cyfra.core.layout.{Layout, LayoutBinding, LayoutStruct} diff --git a/cyfra-utility/src/main/scala/io/computenode/cyfra/utility/Utility.scala b/cyfra-utility/src/main/scala/io/computenode/cyfra/utility/Utility.scala index a081d60a..8e0efbdc 100644 --- a/cyfra-utility/src/main/scala/io/computenode/cyfra/utility/Utility.scala +++ b/cyfra-utility/src/main/scala/io/computenode/cyfra/utility/Utility.scala @@ -2,6 +2,8 @@ package io.computenode.cyfra.utility import io.computenode.cyfra.utility.Logger.logger +import java.util.concurrent.atomic.AtomicInteger + object Utility: def timed[T](tag: String = "Time taken")(fn: => T): T = @@ -10,3 +12,6 @@ object Utility: val end = System.currentTimeMillis() logger.debug(s"$tag: ${end - start}ms") res + + private val aint = AtomicInteger(0) + def nextId(): Int = aint.getAndIncrement() diff --git a/cyfra-utility/src/main/scala/io/computenode/cyfra/utility/cats/Free.scala b/cyfra-utility/src/main/scala/io/computenode/cyfra/utility/cats/Free.scala index 3656d7eb..1b738d4b 100644 --- a/cyfra-utility/src/main/scala/io/computenode/cyfra/utility/cats/Free.scala +++ b/cyfra-utility/src/main/scala/io/computenode/cyfra/utility/cats/Free.scala @@ -1,5 +1,7 @@ package io.computenode.cyfra.utility.cats +import io.computenode.cyfra.utility.cats.Free.* + sealed abstract class Free[S[_], A] extends Product with Serializable: final def map[B](f: A => B): Free[S, B] =