Skip to content

Commit ef11eaa

Browse files
committed
Docs adjustments, publishing pipeline
1 parent cab6b4c commit ef11eaa

File tree

16 files changed

+1154
-428
lines changed

16 files changed

+1154
-428
lines changed

.github/workflows/release.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Release
2+
on:
3+
push:
4+
tags: ["v*"]
5+
jobs:
6+
publish:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v6
10+
with:
11+
fetch-depth: 0
12+
- uses: actions/setup-java@v5
13+
with:
14+
distribution: temurin
15+
java-version: 21
16+
cache: sbt
17+
- uses: sbt/setup-sbt@v1
18+
- run: sbt ci-release
19+
env:
20+
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
21+
PGP_SECRET: ${{ secrets.PGP_SECRET }}
22+
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
23+
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}

build.sbt

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
ThisBuild / organization := "com.computenode.cyfra"
22
ThisBuild / scalaVersion := "3.6.4"
3-
ThisBuild / version := "0.2.0-SNAPSHOT"
3+
ThisBuild / version := "0.1.0-RC1"
4+
ThisBuild / licenses := List("LGPL-2.1" -> url("http://www.gnu.org/licenses/lgpl-2.1.html"))
5+
ThisBuild / homepage := Some(url("https://github.com/ComputeNode/cyfra"))
6+
ThisBuild / developers := List(
7+
Developer(id = "szymon-rd", name = "Szymon Rodziewicz", email = "[email protected]", url = url("https://github.com/szymon-rd")),
8+
Developer(
9+
id = "MarconZet",
10+
name = "Marcin Złakowski",
11+
email = "[email protected]",
12+
url = url("https://github.com/MarconZet"),
13+
),
14+
)
415

5-
val lwjglVersion = "3.4.0-SNAPSHOT"
16+
val lwjglVersion = "3.4.0"
617
val jomlVersion = "1.10.0"
718

819
lazy val osName = System.getProperty("os.name").toLowerCase
@@ -36,6 +47,7 @@ lazy val vulkanNatives =
3647
else Seq.empty
3748

3849
lazy val commonSettings = Seq(
50+
moduleName := s"cyfra-${thisProject.value.id}",
3951
scalacOptions ++= Seq("-feature", "-deprecation", "-unchecked", "-language:implicitConversions"),
4052
resolvers += "maven snapshots" at "https://central.sonatype.com/repository/maven-snapshots/",
4153
resolvers += "OSGeo Release Repository" at "https://repo.osgeo.org/repository/release/",
@@ -113,19 +125,23 @@ lazy val foton = (project in file("cyfra-foton"))
113125

114126
lazy val fluids = (project in file("cyfra-fluids"))
115127
.settings(commonSettings, runnerSettings)
128+
.settings(publish / skip := true)
116129
.dependsOn(foton, runtime, dsl, utility)
117130

118131
lazy val analytics = (project in file("cyfra-analytics"))
119132
.settings(commonSettings, runnerSettings, fs2Settings, tapirSettings)
133+
.settings(publish / skip := true)
120134
.dependsOn(foton, runtime, dsl, utility, fs2interop)
121135

122136
lazy val examples = (project in file("cyfra-examples"))
123137
.settings(commonSettings, runnerSettings)
124138
.settings(libraryDependencies += "org.scala-lang.modules" % "scala-parallel-collections_3" % "1.2.0")
139+
.settings(publish / skip := true)
125140
.dependsOn(foton)
126141

127142
lazy val vscode = (project in file("cyfra-vscode"))
128143
.settings(commonSettings)
144+
.settings(publish / skip := true)
129145
.dependsOn(foton)
130146

131147
lazy val fs2interop = (project in file("cyfra-fs2"))
@@ -134,11 +150,13 @@ lazy val fs2interop = (project in file("cyfra-fs2"))
134150

135151
lazy val e2eTest = (project in file("cyfra-e2e-test"))
136152
.settings(commonSettings, runnerSettings)
153+
.settings(publish / skip := true)
137154
.dependsOn(runtime, fs2interop, foton)
138155

139156
lazy val root = (project in file("."))
140157
.settings(name := "Cyfra")
141-
.aggregate(compiler, dsl, foton, core, runtime, vulkan, examples, fs2interop, fluids, analytics)
158+
.settings(publish / skip := true)
159+
.aggregate(compiler, dsl, foton, core, runtime, vulkan, examples, fs2interop, fluids, analytics, utility, spirvTools, vscode)
142160

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

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package io.computenode.cyfra.samples.examples
2+
3+
import io.computenode.cyfra.core.{GBufferRegion, GExecution, GProgram}
4+
import io.computenode.cyfra.core.GProgram.StaticDispatch
5+
import io.computenode.cyfra.core.layout.Layout
6+
import io.computenode.cyfra.dsl.{*, given}
7+
import io.computenode.cyfra.runtime.VkCyfraRuntime
8+
9+
/** Test snippets from gpu-programs.md documentation
10+
*/
11+
object DocSnippets_GpuPrograms:
12+
13+
// === Snippet 1: Basic DoubleLayout and doubleProgram ===
14+
case class DoubleLayout(input: GBuffer[Float32], output: GBuffer[Float32]) derives Layout
15+
16+
val doubleProgram: GProgram[Int, DoubleLayout] = GProgram.static[Int, DoubleLayout](
17+
layout = size => DoubleLayout(input = GBuffer[Float32](size), output = GBuffer[Float32](size)),
18+
dispatchSize = size => size,
19+
): layout =>
20+
val idx = GIO.invocationId
21+
GIO.when(idx < 256):
22+
val value = layout.input.read(idx)
23+
layout.output.write(idx, value * 2.0f)
24+
25+
@main
26+
def testDoubleProgram(): Unit = VkCyfraRuntime.using:
27+
val size = 256
28+
val inputData = (0 until size).map(_.toFloat).toArray
29+
val results = Array.ofDim[Float](size)
30+
31+
val region = GBufferRegion
32+
.allocate[DoubleLayout]
33+
.map: layout =>
34+
doubleProgram.execute(size, layout)
35+
36+
region.runUnsafe(
37+
init = DoubleLayout(input = GBuffer(inputData), output = GBuffer[Float32](size)),
38+
onDone = layout => layout.output.readArray(results),
39+
)
40+
41+
println(s"[testDoubleProgram] Results: ${results.take(5).mkString(", ")}...")
42+
val expected = inputData.map(_ * 2.0f)
43+
val correct = results.zip(expected).forall((r, e) => Math.abs(r - e) < 0.001f)
44+
println(s"[testDoubleProgram] Correct: $correct")
45+
46+
// === Snippet 2: Parameterized Program with MulParams ===
47+
case class MulParams(factor: Float32) extends GStruct[MulParams]
48+
49+
case class MulLayout(input: GBuffer[Float32], output: GBuffer[Float32], params: GUniform[MulParams]) derives Layout
50+
51+
val mulProgram: GProgram[Int, MulLayout] = GProgram.static[Int, MulLayout](
52+
layout = size => MulLayout(input = GBuffer[Float32](size), output = GBuffer[Float32](size), params = GUniform[MulParams]()),
53+
dispatchSize = size => size,
54+
): layout =>
55+
val idx = GIO.invocationId
56+
GIO.when(idx < 256):
57+
val value = layout.input.read(idx)
58+
val factor = layout.params.read.factor
59+
layout.output.write(idx, value * factor)
60+
61+
@main
62+
def testMulProgram(): Unit = VkCyfraRuntime.using:
63+
val size = 256
64+
val inputData = (0 until size).map(_.toFloat).toArray
65+
val results = Array.ofDim[Float](size)
66+
67+
val region = GBufferRegion
68+
.allocate[MulLayout]
69+
.map: layout =>
70+
mulProgram.execute(size, layout)
71+
72+
region.runUnsafe(
73+
init = MulLayout(input = GBuffer(inputData), output = GBuffer[Float32](size), params = GUniform(MulParams(3.0f))),
74+
onDone = layout => layout.output.readArray(results),
75+
)
76+
77+
println(s"[testMulProgram] Input: ${inputData.take(5).mkString(", ")}...")
78+
println(s"[testMulProgram] Output: ${results.take(5).mkString(", ")}...")
79+
val expected = inputData.map(_ * 3.0f)
80+
val correct = results.zip(expected).forall((r, e) => Math.abs(r - e) < 0.001f)
81+
println(s"[testMulProgram] Correct: $correct")
82+
83+
// === Snippet 3: Running Multiple Programs ===
84+
case class AddParams(addend: Float32) extends GStruct[AddParams]
85+
case class AddLayout(input: GBuffer[Float32], output: GBuffer[Float32], params: GUniform[AddParams]) derives Layout
86+
87+
val addProgram: GProgram[Int, AddLayout] = GProgram.static[Int, AddLayout](
88+
layout = size => AddLayout(GBuffer[Float32](size), GBuffer[Float32](size), GUniform[AddParams]()),
89+
dispatchSize = size => size,
90+
): layout =>
91+
val idx = GIO.invocationId
92+
GIO.when(idx < 256):
93+
val value = layout.input.read(idx)
94+
val addend = layout.params.read.addend
95+
layout.output.write(idx, value + addend)
96+
97+
case class PipelineLayout(input: GBuffer[Float32], intermediate: GBuffer[Float32], output: GBuffer[Float32], addParams: GUniform[AddParams])
98+
derives Layout
99+
100+
@main
101+
def testMultiplePrograms(): Unit = VkCyfraRuntime.using:
102+
val size = 256
103+
val inputData = (0 until size).map(_.toFloat).toArray
104+
val results = Array.ofDim[Float](size)
105+
106+
val region = GBufferRegion
107+
.allocate[PipelineLayout]
108+
.map: layout =>
109+
// Program 1: input -> intermediate (doubles values)
110+
val afterDouble = doubleProgram.execute(size, DoubleLayout(layout.input, layout.intermediate))
111+
112+
// Program 2: use intermediate from afterDouble as input, write to output
113+
addProgram.execute(size, AddLayout(afterDouble.output, layout.output, layout.addParams))
114+
115+
// Return the original layout so onDone can access it
116+
layout
117+
118+
region.runUnsafe(
119+
init = PipelineLayout(
120+
input = GBuffer(inputData),
121+
intermediate = GBuffer[Float32](size),
122+
output = GBuffer[Float32](size),
123+
addParams = GUniform(AddParams(10.0f)),
124+
),
125+
onDone = layout => layout.output.readArray(results),
126+
)
127+
128+
println(s"[testMultiplePrograms] Pipeline: input -> double -> add 10")
129+
println(s"[testMultiplePrograms] Input: ${inputData.take(5).mkString(", ")}...")
130+
println(s"[testMultiplePrograms] Output: ${results.take(5).mkString(", ")}...")
131+
val expected = inputData.map(x => x * 2.0f + 10.0f)
132+
val correct = results.zip(expected).forall((r, e) => Math.abs(r - e) < 0.001f)
133+
println(s"[testMultiplePrograms] Correct: $correct")
134+
135+
/** Test snippets from gpu-pipelines.md documentation
136+
*/
137+
object DocSnippets_GpuPipelines:
138+
139+
case class DoubleLayout(input: GBuffer[Float32], output: GBuffer[Float32]) derives Layout
140+
141+
val doubleProgram: GProgram[Int, DoubleLayout] = GProgram[Int, DoubleLayout](
142+
layout = size => DoubleLayout(input = GBuffer[Float32](size), output = GBuffer[Float32](size)),
143+
dispatch = (_, size) => StaticDispatch(((size + 255) / 256, 1, 1)),
144+
workgroupSize = (256, 1, 1),
145+
): layout =>
146+
val idx = GIO.invocationId
147+
GIO.when(idx < 256):
148+
val value = layout.input.read(idx)
149+
layout.output.write(idx, value * 2.0f)
150+
151+
case class AddParams(value: Float32) extends GStruct[AddParams]
152+
case class AddLayout(input: GBuffer[Float32], output: GBuffer[Float32], params: GUniform[AddParams]) derives Layout
153+
154+
val addProgram: GProgram[Int, AddLayout] = GProgram[Int, AddLayout](
155+
layout = size => AddLayout(input = GBuffer[Float32](size), output = GBuffer[Float32](size), params = GUniform[AddParams]()),
156+
dispatch = (_, size) => StaticDispatch(((size + 255) / 256, 1, 1)),
157+
workgroupSize = (256, 1, 1),
158+
): layout =>
159+
val idx = GIO.invocationId
160+
GIO.when(idx < 256):
161+
val value = layout.input.read(idx)
162+
val addValue = layout.params.read.value
163+
layout.output.write(idx, value + addValue)
164+
165+
case class PipelineLayout(input: GBuffer[Float32], doubled: GBuffer[Float32], output: GBuffer[Float32], addParams: GUniform[AddParams])
166+
derives Layout
167+
168+
val doubleAndAddPipeline: GExecution[Int, PipelineLayout, PipelineLayout] =
169+
GExecution[Int, PipelineLayout]()
170+
.addProgram(doubleProgram)(size => size, layout => DoubleLayout(layout.input, layout.doubled))
171+
.addProgram(addProgram)(size => size, layout => AddLayout(layout.doubled, layout.output, layout.addParams))
172+
173+
@main
174+
def testGExecutionPipeline(): Unit = VkCyfraRuntime.using:
175+
val size = 256
176+
val inputData = (0 until size).map(_.toFloat).toArray
177+
val results = Array.ofDim[Float](size)
178+
179+
val region = GBufferRegion
180+
.allocate[PipelineLayout]
181+
.map: layout =>
182+
doubleAndAddPipeline.execute(size, layout)
183+
184+
region.runUnsafe(
185+
init = PipelineLayout(
186+
input = GBuffer(inputData),
187+
doubled = GBuffer[Float32](size),
188+
output = GBuffer[Float32](size),
189+
addParams = GUniform(AddParams(10.0f)),
190+
),
191+
onDone = layout => layout.output.readArray(results),
192+
)
193+
194+
println(s"[testGExecutionPipeline] Pipeline: input -> double -> add 10")
195+
println(s"[testGExecutionPipeline] Input: ${inputData.take(5).mkString(", ")}...")
196+
println(s"[testGExecutionPipeline] Output: ${results.take(5).mkString(", ")}...")
197+
val expected = inputData.map(x => x * 2.0f + 10.0f)
198+
val correct = results.zip(expected).forall((r, e) => Math.abs(r - e) < 0.001f)
199+
println(s"[testGExecutionPipeline] Correct: $correct")
200+
201+
/** Run all documentation snippet tests
202+
*/
203+
object RunAllDocSnippetTests:
204+
@main
205+
def runAllDocSnippets(): Unit =
206+
println("=== Testing Documentation Snippets ===\n")
207+
208+
println("--- gpu-programs.md snippets ---")
209+
DocSnippets_GpuPrograms.testDoubleProgram()
210+
println()
211+
DocSnippets_GpuPrograms.testMulProgram()
212+
println()
213+
DocSnippets_GpuPrograms.testMultiplePrograms()
214+
println()
215+
216+
println("--- gpu-pipelines.md snippets ---")
217+
DocSnippets_GpuPipelines.testGExecutionPipeline()
218+
println()
219+
220+
println("=== All documentation snippet tests completed! ===")

cyfra-foton/src/main/scala/io/computenode/cyfra/foton/GFunction.scala

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ case class GFunction[G <: GStruct[G]: {GStructSchema, Tag}, H <: Value: {Tag, Fr
4747
val out = BufferUtils.createByteBuffer(outTypeSize * input.size)
4848
val uniform = BufferUtils.createByteBuffer(uniformStride)
4949
gCodec.toByteBuffer(uniform, Array(g))
50-
???
5150

5251
GBufferRegion
5352
.allocate[GFunctionLayout[G, H, R]]
@@ -74,21 +73,19 @@ object GFunction:
7473
for _ <- layout.out.write(GIO.invocationId, result)
7574
yield Empty()
7675

77-
val inTypeSize = typeStride(Tag.apply[H])
78-
val outTypeSize = typeStride(Tag.apply[R])
79-
???
76+
val program = GProgram.static[GFunctionParams, GFunctionLayout[G, H, R]](
77+
layout = (params: GFunctionParams) => GFunctionLayout(GBuffer(params.size), GBuffer(params.size), GUniform()),
78+
dispatchSize = _.size,
79+
)(body)
8080

81-
// GFunction(underlying =
82-
// GProgram.apply[GFunctionParams, GFunctionLayout[G, H, R]](
83-
// layout = (p: GFunctionParams) => GFunctionLayout[G, H, R](in = GBuffer[H](p.size), out = GBuffer[R](p.size), uniform = GUniform[G]()),
84-
// dispatch = (l, p) => StaticDispatch((p.size + 255) / 256, 1, 1),
85-
// workgroupSize = (256, 1, 1),
86-
// )(body),
87-
// )
81+
GFunction(program)
8882

8983
def apply[H <: Value: {Tag, FromExpr}, R <: Value: {Tag, FromExpr}](fn: H => R): GFunction[GStruct.Empty, H, R] =
9084
GFunction.forEachIndex[GStruct.Empty, H, R]((g: GStruct.Empty, index: Int32, a: GBuffer[H]) => fn(a.read(index)))
9185

86+
def apply[G <: GStruct[G]: {GStructSchema, Tag}, H <: Value: {Tag, FromExpr}, R <: Value: {Tag, FromExpr}](fn: (G, H) => R): GFunction[G, H, R] =
87+
GFunction.forEachIndex[G, H, R]((g: G, index: Int32, a: GBuffer[H]) => fn(g, a.read(index)))
88+
9289
def from2D[G <: GStruct[G]: {GStructSchema, Tag}, H <: Value: {Tag, FromExpr}, R <: Value: {Tag, FromExpr}](
9390
width: Int,
9491
)(fn: (G, (Int32, Int32), GArray2D[H]) => R): GFunction[G, H, R] =

cyfra-fs2/src/main/scala/io/computenode/cyfra/fs2interop/GPipe.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ object GPipe:
236236
* @param input
237237
* Batch of input arrays
238238
*/
239-
def batch[L <: Layout: LayoutBinding](program: GProgram[Int, L], batchSize: Int, inputStride: Int, outputStride: Int)(
239+
def batch[L: Layout](program: GProgram[Int, L], batchSize: Int, inputStride: Int, outputStride: Int)(
240240
buildLayout: (Array[Float], Int) => Allocation ?=> L,
241241
outputBuffer: L => GBuffer[Float32],
242242
)(input: Array[Array[Float]])(using CyfraRuntime): Array[Array[Float]] =

cyfra-runtime/src/main/scala/io/computenode/cyfra/runtime/VkCyfraRuntime.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,9 @@ class VkCyfraRuntime(spirvToolsRunner: SpirvToolsRunner = SpirvToolsRunner()) ex
4747
def close(): Unit =
4848
shaderCache.values.foreach(_.underlying.destroy())
4949
context.destroy()
50+
51+
object VkCyfraRuntime:
52+
def using[T](f: VkCyfraRuntime ?=> T): T =
53+
val runtime = new VkCyfraRuntime()
54+
try f(using runtime)
55+
finally runtime.close()

cyfra-spirv-tools/src/main/scala/io/computenode/cyfra/spirvtools/SpirvToolsRunner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import io.computenode.cyfra.utility.Logger.logger
66
import java.nio.ByteBuffer
77

88
class SpirvToolsRunner(
9-
val validator: SpirvValidator.Validation = SpirvValidator.Enable(),
9+
val validator: SpirvValidator.Validation = SpirvValidator.Disable,
1010
val optimizer: SpirvOptimizer.Optimization = SpirvOptimizer.Disable,
1111
val disassembler: SpirvDisassembler.Disassembly = SpirvDisassembler.Disable,
1212
val crossCompilation: SpirvCross.CrossCompilation = SpirvCross.Disable,

0 commit comments

Comments
 (0)