Skip to content
8 changes: 6 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,14 @@ lazy val compiler = (project in file("cyfra-compiler"))
.settings(commonSettings)
.dependsOn(dsl, utility)

lazy val runtime = (project in file("cyfra-runtime"))
lazy val core = (project in file("cyfra-core"))
.settings(commonSettings)
.dependsOn(compiler, dsl, vulkan, utility, spirvTools)

lazy val runtime = (project in file("cyfra-runtime"))
.settings(commonSettings)
.dependsOn(core)

lazy val foton = (project in file("cyfra-foton"))
.settings(commonSettings)
.dependsOn(compiler, dsl, runtime, utility)
Expand All @@ -98,7 +102,7 @@ lazy val e2eTest = (project in file("cyfra-e2e-test"))

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

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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.computenode.cyfra.core

import io.computenode.cyfra.core.layout.{Layout, LayoutStruct}
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
import izumi.reflect.Tag

import java.nio.ByteBuffer

trait Allocation:
extension (buffer: GBinding[?])
def read(bb: ByteBuffer, offset: Int = 0, length: Int = -1): Unit

def write(bb: ByteBuffer, offset: Int = 0, length: Int = -1): Unit

extension [Params, L <: Layout, RL <: Layout: LayoutStruct](execution: GExecution[Params, L, RL]) def execute(params: Params, layout: L): RL

extension (buffers: GBuffer.type)
def apply[T <: Value: {Tag, FromExpr}](size: Int): GBuffer[T]

def apply[T <: Value: {Tag, FromExpr}](buff: ByteBuffer): GBuffer[T]

extension (buffers: GUniform.type)
def apply[T <: Value: {Tag, FromExpr}](buff: ByteBuffer): GUniform[T]

def apply[T <: Value: {Tag, FromExpr}](): GUniform[T]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.computenode.cyfra.core

import io.computenode.cyfra.core.Allocation

trait CyfraRuntime:

def allocation(): Allocation
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.computenode.cyfra.core

import io.computenode.cyfra.core.Allocation
import io.computenode.cyfra.core.GProgram.BufferSizeSpec
import io.computenode.cyfra.core.layout.{Layout, LayoutStruct}
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 java.nio.ByteBuffer

sealed trait GBufferRegion[ReqAlloc <: Layout: LayoutStruct, ResAlloc <: Layout: LayoutStruct]:
val initAlloc: ReqAlloc

object GBufferRegion:

def allocate[Alloc <: Layout: LayoutStruct]: GBufferRegion[Alloc, Alloc] =
AllocRegion(summon[LayoutStruct[Alloc]].layoutRef)

case class AllocRegion[Alloc <: Layout: LayoutStruct](l: Alloc) extends GBufferRegion[Alloc, Alloc]:
val initAlloc: Alloc = l

case class MapRegion[ReqAlloc <: Layout: LayoutStruct, BodyAlloc <: Layout: LayoutStruct, ResAlloc <: Layout: LayoutStruct](
reqRegion: GBufferRegion[ReqAlloc, BodyAlloc],
f: Allocation => BodyAlloc => ResAlloc,
) extends GBufferRegion[ReqAlloc, ResAlloc]:
val initAlloc: ReqAlloc = reqRegion.initAlloc

extension [ReqAlloc <: Layout: LayoutStruct, ResAlloc <: Layout: LayoutStruct](region: GBufferRegion[ReqAlloc, ResAlloc])
def map[NewAlloc <: Layout: LayoutStruct](f: Allocation ?=> ResAlloc => NewAlloc): GBufferRegion[ReqAlloc, NewAlloc] =
MapRegion(region, (alloc: Allocation) => (resAlloc: ResAlloc) => f(using alloc)(resAlloc))

def runUnsafe(init: Allocation ?=> ReqAlloc, onDone: Allocation ?=> ResAlloc => Unit)(using cyfraRuntime: CyfraRuntime): Unit =
val allocation = cyfraRuntime.allocation()
init(using allocation)

// noinspection ScalaRedundantCast
val steps: Seq[Allocation => Layout => Layout] = Seq.unfold(region: GBufferRegion[?, ?]):
case _: AllocRegion[?] => None
case MapRegion(req, f) =>
Some((f.asInstanceOf[Allocation => Layout => Layout], req))

val bodyAlloc = steps.foldLeft[Layout](region.initAlloc): (acc, step) =>
step(allocation)(acc)

onDone(using allocation)(bodyAlloc.asInstanceOf[ResAlloc])
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.computenode.cyfra.core

import io.computenode.cyfra.core.GExecution.*
import io.computenode.cyfra.core.archive.GContext
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.spirv.compilers.ExpressionCompiler.UniformStructRef
import izumi.reflect.Tag
import GExecution.*

trait GExecution[-Params, ExecLayout <: Layout, +ResLayout <: Layout]:

def flatMap[NRL <: Layout, NP <: Params](f: ResLayout => GExecution[NP, ExecLayout, NRL]): GExecution[NP, ExecLayout, NRL] =
FlatMap(this, (p, r) => f(r))

def map[NRL <: Layout](f: ResLayout => NRL): GExecution[Params, ExecLayout, NRL] =
Map(this, f, identity, identity)

def contramap[NL <: Layout](f: NL => ExecLayout): GExecution[Params, NL, ResLayout] =
Map(this, identity, f, identity)

def contramapParams[NP](f: NP => Params): GExecution[NP, ExecLayout, ResLayout] =
Map(this, identity, identity, f)

def addProgram[ProgramParams, PP <: Params, ProgramLayout <: Layout, P <: GProgram[ProgramParams, ProgramLayout]](
program: P,
)(mapParams: PP => ProgramParams, mapLayout: ExecLayout => ProgramLayout): GExecution[PP, ExecLayout, ResLayout] =
val adapted = program.contramapParams(mapParams).contramap(mapLayout)
flatMap(r => adapted.map(_ => r))

object GExecution:

def apply[Params, L <: Layout]() =
Pure[Params, L, L]()

def forParams[Params, L <: Layout, RL <: Layout](f: Params => GExecution[Params, L, RL]): GExecution[Params, L, RL] =
FlatMap[Params, L, RL, RL](Pure[Params, L, RL](), (params: Params, _: RL) => f(params))

case class Pure[Params, L <: Layout, RL <: Layout]() extends GExecution[Params, L, RL]

case class FlatMap[Params, L <: Layout, RL <: Layout, NRL <: Layout](
execution: GExecution[Params, L, RL],
f: (Params, RL) => GExecution[Params, L, NRL],
) extends GExecution[Params, L, NRL]

case class Map[P, NP, L <: Layout, NL <: Layout, RL <: Layout, NRL <: Layout](
execution: GExecution[P, L, RL],
mapResult: RL => NRL,
contramapLayout: NL => L,
contramapParams: NP => P,
) extends GExecution[NP, NL, NRL]:

override def map[NNRL <: Layout](f: NRL => NNRL): GExecution[NP, NL, NNRL] =
Map(execution, mapResult andThen f, contramapLayout, contramapParams)

override def contramapParams[NNP](f: NNP => NP): GExecution[NNP, NL, NRL] =
Map(execution, mapResult, contramapLayout, f andThen contramapParams)

override def contramap[NNL <: Layout](f: NNL => NL): GExecution[NP, NNL, NRL] =
Map(execution, mapResult, f andThen contramapLayout, contramapParams)
70 changes: 70 additions & 0 deletions cyfra-core/src/main/scala/io/computenode/cyfra/core/GProgram.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.computenode.cyfra.core

import io.computenode.cyfra.core.layout.LayoutStruct
import io.computenode.cyfra.dsl.gio.GIO
import io.computenode.cyfra.core.layout.Layout

import java.nio.ByteBuffer
import GProgram.*
import io.computenode.cyfra.dsl.{Expression, Value}
import io.computenode.cyfra.dsl.Value.{FromExpr, Int32}
import io.computenode.cyfra.dsl.binding.{GBinding, GBuffer, GUniform}
import io.computenode.cyfra.dsl.struct.GStruct
import io.computenode.cyfra.dsl.struct.GStruct.Empty
import izumi.reflect.Tag

sealed trait GProgram[Params, L <: Layout: LayoutStruct] extends GExecution[Params, L, L]:
val layout: InitProgramLayout => Params => L
val dispatch: (L, Params) => ProgramDispatch
val workgroupSize: WorkDimensions

object GProgram:

class GioProgram[Params, L <: Layout: LayoutStruct](
val body: L => GIO[?],
val layout: InitProgramLayout => Params => L,
val dispatch: (L, Params) => ProgramDispatch,
val workgroupSize: WorkDimensions,
) extends GProgram[Params, L]:
private[cyfra] def layoutStruct: LayoutStruct[L] = summon[LayoutStruct[L]]

class SpirvProgram[Params, L <: Layout: LayoutStruct](
val code: ByteBuffer,
val layout: InitProgramLayout => Params => L,
val dispatch: (L, Params) => ProgramDispatch,
val workgroupSize: WorkDimensions,
) extends GProgram[Params, L]:
private[cyfra] def layoutStruct: LayoutStruct[L] = summon[LayoutStruct[L]]

type WorkDimensions = (Int, Int, Int)

sealed trait ProgramDispatch

case class DynamicDispatch[L <: Layout](buffer: GBinding[?], offset: Int) extends ProgramDispatch

case class StaticDispatch(size: WorkDimensions) extends ProgramDispatch

private[cyfra] case class BufferSizeSpec[T <: Value: {Tag, FromExpr}](size: Int) extends GBuffer[T]

private[cyfra] case class ParamUniform[T <: GStruct[T]: {Tag, FromExpr}](value: T) extends GUniform[T]

private[cyfra] case class DynamicUniform[T <: GStruct[T]: {Tag, FromExpr}]() extends GUniform[T]

trait InitProgramLayout:
extension (buffers: GBuffer.type)
def apply[T <: Value: {Tag, FromExpr}](size: Int): GBuffer[T] =
BufferSizeSpec[T](size)

extension (uniforms: GUniform.type)
def apply[T <: GStruct[T]: {Tag, FromExpr}](value: T): GUniform[T] =
ParamUniform[T](value)

def apply[T <: GStruct[T]: {Tag, FromExpr}](): GUniform[T] =
DynamicUniform[T]()

def apply[Params, L <: Layout: LayoutStruct](
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)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.computenode.cyfra.runtime
package io.computenode.cyfra.core.archive

import io.computenode.cyfra.core.archive.mem.{GMem, RamGMem}
import io.computenode.cyfra.dsl.Value
import io.computenode.cyfra.runtime.mem.{GMem, RamGMem}

import scala.concurrent.Future

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package io.computenode.cyfra.runtime
package io.computenode.cyfra.core.archive

import io.computenode.cyfra.core.archive.mem.GMem.totalStride
import io.computenode.cyfra.core.archive.mem.{FloatMem, GMem, IntMem, Vec4FloatMem}
import io.computenode.cyfra.core.archive.{GFunction, UniformContext}
import io.computenode.cyfra.dsl.Value
import io.computenode.cyfra.dsl.Value.{Float32, FromExpr, Int32, Vec4}
import io.computenode.cyfra.dsl.collections.GArray
import io.computenode.cyfra.dsl.struct.*
import io.computenode.cyfra.runtime.mem.GMem.totalStride
import io.computenode.cyfra.runtime.mem.{FloatMem, GMem, IntMem, Vec4FloatMem}
import io.computenode.cyfra.spirv.SpirvTypes.typeStride
import io.computenode.cyfra.spirv.compilers.DSLCompiler
import io.computenode.cyfra.spirv.compilers.ExpressionCompiler.{UniformStructRef, WorkerIndex}
Expand All @@ -27,7 +28,7 @@ class GContext(spirvToolsRunner: SpirvToolsRunner = SpirvToolsRunner()):

implicit val ec: ExecutionContextExecutor = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(16))

def compile[G <: GStruct[G]: Tag: GStructSchema, H <: Value: Tag: FromExpr, R <: Value: Tag: FromExpr](
def compile[G <: GStruct[G]: {Tag, GStructSchema}, H <: Value: {Tag, FromExpr}, R <: Value: {Tag, FromExpr}](
function: GFunction[G, H, R],
): ComputePipeline =
val uniformStructSchema = summon[GStructSchema[G]]
Expand All @@ -45,7 +46,7 @@ class GContext(spirvToolsRunner: SpirvToolsRunner = SpirvToolsRunner()):
val shader = Shader(optimizedShaderCode, org.joml.Vector3i(256, 1, 1), layoutInfo, "main", vkContext.device)
ComputePipeline(shader, vkContext)

def execute[G <: GStruct[G]: Tag: GStructSchema, H <: Value, R <: Value](mem: GMem[H], fn: GFunction[G, H, R])(using
def execute[G <: GStruct[G]: {Tag, GStructSchema}, H <: Value, R <: Value](mem: GMem[H], fn: GFunction[G, H, R])(using
uniformContext: UniformContext[G],
): GMem[R] =
val isUniformEmpty = uniformContext.uniform.schema.fields.isEmpty
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
package io.computenode.cyfra.runtime
package io.computenode.cyfra.core.archive

import io.computenode.cyfra.dsl.{*, given}
import io.computenode.cyfra.core.archive.GFunction
import io.computenode.cyfra.dsl.Value.*
import io.computenode.cyfra.dsl.struct.*
import io.computenode.cyfra.dsl.collections.{GArray, GArray2D}
import io.computenode.cyfra.dsl.struct.*
import io.computenode.cyfra.dsl.{*, given}
import io.computenode.cyfra.vulkan.compute.ComputePipeline
import izumi.reflect.Tag

case class GFunction[G <: GStruct[G]: GStructSchema: Tag, H <: Value: Tag: FromExpr, R <: Value: Tag: FromExpr](fn: (G, Int32, GArray[H]) => R)(
case class GFunction[G <: GStruct[G]: {GStructSchema, Tag}, H <: Value: {Tag, FromExpr}, R <: Value: {Tag, FromExpr}](fn: (G, Int32, GArray[H]) => R)(
implicit context: GContext,
):
def arrayInputs: List[Tag[?]] = List(summon[Tag[H]])
def arrayOutputs: List[Tag[?]] = List(summon[Tag[R]])
val pipeline: ComputePipeline = context.compile(this)

object GFunction:
def apply[H <: Value: Tag: FromExpr, R <: Value: Tag: FromExpr](fn: H => R)(using context: GContext): GFunction[GStruct.Empty, H, R] =
def apply[H <: Value: {Tag, FromExpr}, R <: Value: {Tag, FromExpr}](fn: H => R)(using context: GContext): GFunction[GStruct.Empty, H, R] =
new GFunction[GStruct.Empty, H, R]((_, index: Int32, gArray: GArray[H]) => fn(gArray.at(index)))

def from2D[G <: GStruct[G]: GStructSchema: Tag, H <: Value: Tag: FromExpr, R <: Value: Tag: FromExpr](
def from2D[G <: GStruct[G]: {GStructSchema, Tag}, H <: Value: {Tag, FromExpr}, R <: Value: {Tag, FromExpr}](
width: Int,
)(fn: (G, (Int32, Int32), GArray2D[H]) => R)(using context: GContext): GFunction[G, H, R] =
GFunction[G, H, R]((g: G, index: Int32, a: GArray[H]) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.computenode.cyfra.core.archive

import io.computenode.cyfra.core.archive.UniformContext
import io.computenode.cyfra.dsl.struct.*
import io.computenode.cyfra.dsl.struct.GStruct.Empty
import izumi.reflect.Tag

class UniformContext[G <: GStruct[G]: {Tag, GStructSchema}](val uniform: G)

object UniformContext:
def withUniform[G <: GStruct[G]: {Tag, GStructSchema}, T](uniform: G)(fn: UniformContext[G] ?=> T): T =
fn(using UniformContext(uniform))
given empty: UniformContext[Empty] = new UniformContext(Empty())
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.computenode.cyfra.runtime.mem
package io.computenode.cyfra.core.archive.mem

import io.computenode.cyfra.dsl.Value.Float32
import org.lwjgl.BufferUtils
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package io.computenode.cyfra.runtime.mem
package io.computenode.cyfra.core.archive.mem

import io.computenode.cyfra.core.archive.{GContext, GFunction, UniformContext}
import io.computenode.cyfra.dsl.Value.FromExpr
import io.computenode.cyfra.dsl.struct.*
import io.computenode.cyfra.dsl.{*, given}
import io.computenode.cyfra.runtime.{GContext, GFunction, UniformContext}
import io.computenode.cyfra.spirv.SpirvTypes.typeStride
import izumi.reflect.Tag
import org.lwjgl.BufferUtils
Expand All @@ -13,7 +13,7 @@ import java.nio.ByteBuffer
trait GMem[H <: Value]:
def size: Int
def toReadOnlyBuffer: ByteBuffer
def map[G <: GStruct[G]: Tag: GStructSchema, R <: Value: FromExpr: Tag](
def map[G <: GStruct[G]: {Tag, GStructSchema}, R <: Value: {FromExpr, Tag}](
fn: GFunction[G, H, R],
)(using context: GContext, uc: UniformContext[G]): GMem[R] =
context.execute(this, fn)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.computenode.cyfra.runtime.mem
package io.computenode.cyfra.core.archive.mem

import io.computenode.cyfra.dsl.Value.Int32
import org.lwjgl.BufferUtils
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.computenode.cyfra.runtime.mem
package io.computenode.cyfra.core.archive.mem

import io.computenode.cyfra.dsl.Value

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.computenode.cyfra.runtime.mem
package io.computenode.cyfra.core.archive.mem

import io.computenode.cyfra.core.archive.mem.GMem.fRGBA
import io.computenode.cyfra.dsl.Value.{Float32, Vec4}
import io.computenode.cyfra.runtime.mem.GMem.fRGBA
import org.lwjgl.BufferUtils

import java.nio.ByteBuffer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
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

case class BufferRef[T <: Value: {Tag, FromExpr}](layoutOffset: Int, valueTag: Tag[T]) extends GBuffer[T]
Loading