changeset 1252:01e9f41d35b1

add:support for push constants
author sam <sam@basx.dev>
date Fri, 26 Jul 2024 23:04:01 +0700
parents 3f98ad20a9d3
children c4f98eb4bb05
files semiconginev2.nim semiconginev2/contrib/settings.nim semiconginev2/gltf.nim semiconginev2/image.nim semiconginev2/rendering.nim semiconginev2/rendering/renderer.nim semiconginev2/rendering/shaders.nim tests/test_rendering.nim
diffstat 8 files changed, 86 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/semiconginev2.nim	Fri Jul 26 20:34:02 2024 +0700
+++ b/semiconginev2.nim	Fri Jul 26 23:04:01 2024 +0700
@@ -55,3 +55,8 @@
   include ./semiconginev2/contrib/settings
   include ./semiconginev2/contrib/algorithms/collision
   include ./semiconginev2/contrib/algorithms/noise
+
+if not defined(release):
+  setLogFilter(lvlAll)
+else:
+  setLogFilter(lvlWarn)
--- a/semiconginev2/contrib/settings.nim	Fri Jul 26 20:34:02 2024 +0700
+++ b/semiconginev2/contrib/settings.nim	Fri Jul 26 23:04:01 2024 +0700
@@ -1,9 +1,8 @@
-const CONFIGROOT: string = "."
-const CONFIGEXTENSION: string = "ini"
-# by default enable hot-reload of runtime-configuration only in debug builds
-const CONFIGHOTRELOAD: bool = not defined(release)
-# milliseconds to wait between checks for settings hotreload
-const CONFIGHOTRELOADINTERVAL: int = 1000
+const CONFIGHOTRELOAD {.booldefine.}: bool = not defined(release)
+const CONFIGHOTRELOADINTERVAL {.intdefine.}: int = 1000
+const CONFIGROOT {.strdefine.}: string = "."
+const CONFIGEXTENSION {.strdefine.}: string = "ini"
+
 
 when CONFIGHOTRELOAD:
   var
@@ -77,8 +76,6 @@
 allsettings = loadAllConfig()
 
 when CONFIGHOTRELOAD == true:
-  import std/times
-
   proc configFileWatchdog() {.thread.} =
     var configModTimes: Table[string, times.Time]
     while true:
@@ -93,8 +90,3 @@
       sleep CONFIGHOTRELOADINTERVAL
   var thethread: Thread[void]
   createThread(thethread, configFileWatchdog)
-
-if not defined(release):
-  setLogFilter(lvlAll)
-else:
-  setLogFilter(lvlWarn)
--- a/semiconginev2/gltf.nim	Fri Jul 26 20:34:02 2024 +0700
+++ b/semiconginev2/gltf.nim	Fri Jul 26 23:04:01 2024 +0700
@@ -145,7 +145,7 @@
   assert imageType == "image/png", "glTF loader currently only supports PNG"
 
   let bufferView = root["bufferViews"][root["images"][imageIndex]["bufferView"].getInt()]
-  result = LoadImage[BGRA](getBufferViewData(bufferView, mainBuffer))
+  result = LoadImageData[BGRA](getBufferViewData(bufferView, mainBuffer))
 
   if textureNode.hasKey("sampler"):
     let sampler = root["samplers"][textureNode["sampler"].getInt()]
--- a/semiconginev2/image.nim	Fri Jul 26 20:34:02 2024 +0700
+++ b/semiconginev2/image.nim	Fri Jul 26 23:04:01 2024 +0700
@@ -16,7 +16,7 @@
     isRenderTarget*: bool = false
     samples*: VkSampleCountFlagBits = VK_SAMPLE_COUNT_1_BIT
 
-proc LoadImage*[T: PixelType](pngData: seq[uint8]): Image[T] =
+proc LoadImageData*[T: PixelType](pngData: string|seq[uint8]): Image[T] =
   when T is Gray:
     let pngType = 0.cint
   elif T is BGRA:
@@ -38,13 +38,13 @@
       swap(result.data[i][0], result.data[i][2])
 
 proc LoadImage*[T: PixelType](path: string, package = DEFAULT_PACKAGE): Image[T] =
-  assert path.splitFile().ext.toLowerAscii == ".png"
+  assert path.splitFile().ext.toLowerAscii == ".png", "Unsupported image type: " & path.splitFile().ext.toLowerAscii
   when T is Gray:
     let pngType = 0.cint
   elif T is BGRA:
     let pngType = 6.cint
 
-  result = LoadImage[T](loadResource_intern(path, package = package).readAll())
+  result = LoadImageData[T](loadResource_intern(path, package = package).readAll())
 
 
 proc toPNG[T: PixelType](image: Image[T]): seq[uint8] =
--- a/semiconginev2/rendering.nim	Fri Jul 26 20:34:02 2024 +0700
+++ b/semiconginev2/rendering.nim	Fri Jul 26 23:04:01 2024 +0700
@@ -18,6 +18,7 @@
 # custom pragmas to classify shader attributes
 template VertexAttribute* {.pragma.}
 template InstanceAttribute* {.pragma.}
+template PushConstantAttribute* {.pragma.}
 template Pass* {.pragma.}
 template PassFlat* {.pragma.}
 template ShaderOutput* {.pragma.}
--- a/semiconginev2/rendering/renderer.nim	Fri Jul 26 20:34:02 2024 +0700
+++ b/semiconginev2/rendering/renderer.nim	Fri Jul 26 23:04:01 2024 +0700
@@ -670,6 +670,38 @@
 ) =
   Render(commandBuffer, pipeline, mesh, EMPTY())
 
+proc RenderWithPushConstant*[TShader, TMesh, TInstance, TPushConstant](
+  commandBuffer: VkCommandBuffer,
+  pipeline: Pipeline[TShader],
+  mesh: TMesh,
+  instances: TInstance,
+  pushConstant: TPushConstant,
+) =
+  vkCmdPushConstants(
+    commandBuffer = commandBuffer,
+    layout = pipeline.layout,
+    stageFlags = VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS),
+    offset = 0,
+    size = 128,
+    pValues = addr(pushConstant)
+  );
+  Render(commandBuffer, pipeline, mesh, instances)
+proc RenderWithPushConstant*[TShader, TMesh, TPushConstant](
+  commandBuffer: VkCommandBuffer,
+  pipeline: Pipeline[TShader],
+  mesh: TMesh,
+  pushConstant: TPushConstant,
+) =
+  vkCmdPushConstants(
+    commandBuffer = commandBuffer,
+    layout = pipeline.layout,
+    stageFlags = VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS),
+    offset = 0,
+    size = 128,
+    pValues = addr(pushConstant)
+  );
+  Render(commandBuffer, pipeline, mesh, EMPTY())
+
 proc asGPUArray*[T](data: openArray[T], bufferType: static BufferType): auto =
   GPUArray[T, bufferType](data: @data)
 
--- a/semiconginev2/rendering/shaders.nim	Fri Jul 26 20:34:02 2024 +0700
+++ b/semiconginev2/rendering/shaders.nim	Fri Jul 26 23:04:01 2024 +0700
@@ -131,6 +131,7 @@
   var fsInput: seq[string]
   var fsOutput: seq[string]
   var uniforms: seq[string]
+  var pushConstants: seq[string]
   var samplers: seq[string]
   var vsInputLocation = 0'u32
   var passLocation = 0
@@ -213,12 +214,22 @@
         descriptorSetIndex.inc
     elif fieldname in ["vertexCode", "fragmentCode"]:
       discard
+    elif hasCustomPragma(value, PushConstantAttribute):
+      assert pushConstants.len == 0, "Only one push constant value allowed"
+      assert value is object, "push constants need to be objects"
+      pushConstants.add "layout( push_constant ) uniform constants"
+      pushConstants.add "{"
+      for constFieldName, constFieldValue in value.fieldPairs():
+        assert typeof(constFieldValue) is SupportedGPUType, "push constant field '" & constFieldName & "' is not a SupportedGPUType"
+        pushConstants.add "  " & GlslType(constFieldValue) & " " & constFieldName & ";"
+      pushConstants.add "} " & fieldname & ";"
     else:
       {.error: "Unsupported shader field '" & typetraits.name(TShader) & "." & fieldname & "' of type " & typetraits.name(typeof(value)).}
 
   result[0] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] &
     vsInput &
     uniforms &
+    pushConstants &
     samplers &
     vsOutput &
     @[shader.vertexCode]).join("\n")
@@ -226,6 +237,7 @@
   result[1] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] &
     fsInput &
     uniforms &
+    pushConstants &
     samplers &
     fsOutput &
     @[shader.fragmentCode]).join("\n")
@@ -360,12 +372,19 @@
 
   var nSets = GetDescriptorSetCount[TShader]()
   result.descriptorSetLayouts = CreateDescriptorSetLayouts[TShader]()
+
+  let pushConstant = VkPushConstantRange(
+    stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS),
+    offset: 0,
+    size: 128, # currently supported everywhere, places for two mat4
+  )
+
   let pipelineLayoutInfo = VkPipelineLayoutCreateInfo(
     sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
     setLayoutCount: nSets,
     pSetLayouts: if nSets == 0: nil else: result.descriptorSetLayouts.ToCPointer,
-    # pushConstantRangeCount: uint32(pushConstants.len),
-      # pPushConstantRanges: pushConstants.ToCPointer,
+    pushConstantRangeCount: 1,
+    pPushConstantRanges: addr(pushConstant),
   )
   checkVkResult vkCreatePipelineLayout(vulkan.device, addr(pipelineLayoutInfo), nil, addr(result.layout))
 
--- a/tests/test_rendering.nim	Fri Jul 26 20:34:02 2024 +0700
+++ b/tests/test_rendering.nim	Fri Jul 26 23:04:01 2024 +0700
@@ -11,15 +11,18 @@
   var renderdata = InitRenderData()
 
   type
+    PushConstant = object
+      scale: float32
     Shader = object
       position {.VertexAttribute.}: Vec3f
       color {.VertexAttribute.}: Vec3f
+      pushConstant {.PushConstantAttribute.}: PushConstant
       fragmentColor {.Pass.}: Vec3f
       outColor {.ShaderOutput.}: Vec4f
       # code
       vertexCode: string = """void main() {
       fragmentColor = color;
-      gl_Position = vec4(position, 1);}"""
+      gl_Position = vec4(position * pushConstant.scale, 1);}"""
       fragmentCode: string = """void main() {
       outColor = vec4(fragmentColor, 1);}"""
     TriangleMesh = object
@@ -43,7 +46,7 @@
 
         WithPipeline(commandbuffer, pipeline):
 
-          Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = mesh)
+          RenderWithPushConstant(commandbuffer = commandbuffer, pipeline = pipeline, mesh = mesh, pushConstant = PushConstant(scale: 0.3 + ((getMonoTime() - start).inMilliseconds().int / 1000)))
 
   # cleanup
   checkVkResult vkDeviceWaitIdle(vulkan.device)
@@ -133,7 +136,7 @@
 
     Uniforms = object
       material: GPUValue[Material, UniformBuffer]
-      texture1: Image[RGBA]
+      texture1: Image[BGRA]
 
     QuadShader = object
       position {.VertexAttribute.}: Vec3f
@@ -154,10 +157,10 @@
       position: GPUArray[Vec3f, VertexBuffer]
       indices: GPUArray[uint16, IndexBuffer]
 
-  let R = RGBA([255'u8, 0'u8, 0'u8, 255'u8])
-  let G = RGBA([0'u8, 255'u8, 0'u8, 255'u8])
-  let B = RGBA([0'u8, 0'u8, 255'u8, 255'u8])
-  let W = RGBA([255'u8, 255'u8, 255'u8, 255'u8])
+  let R = BGRA([255'u8, 0'u8, 0'u8, 255'u8])
+  let G = BGRA([0'u8, 255'u8, 0'u8, 255'u8])
+  let B = BGRA([0'u8, 0'u8, 255'u8, 255'u8])
+  let W = BGRA([255'u8, 255'u8, 255'u8, 255'u8])
   var
     quad = QuadMesh(
       position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(-0.5, 0.5), NewVec3f(0.5, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer),
@@ -166,13 +169,13 @@
     uniforms1 = asDescriptorSet(
       Uniforms(
         material: asGPUValue(Material(baseColor: NewVec3f(1, 1, 1)), UniformBuffer),
-        texture1: Image[RGBA](width: 3, height: 3, data: @[R, G, B, G, B, R, B, R, G], interpolation: VK_FILTER_NEAREST),
+        texture1: Image[BGRA](width: 3, height: 3, data: @[R, G, B, G, B, R, B, R, G], minInterpolation: VK_FILTER_NEAREST, magInterpolation: VK_FILTER_NEAREST),
       )
     )
     uniforms2 = asDescriptorSet(
       Uniforms(
         material: asGPUValue(Material(baseColor: NewVec3f(0.5, 0.5, 0.5)), UniformBuffer),
-        texture1: Image[RGBA](width: 2, height: 2, data: @[R, G, B, W]),
+        texture1: Image[BGRA](width: 2, height: 2, data: @[R, G, B, W]),
     )
     )
 
@@ -271,8 +274,8 @@
         asGPUValue(Material(baseColor: NewVec3f(1, 0, 1)), UniformBuffer),
     ],
     texture1: [
-      Image[Gray](width: 2, height: 2, data: @[W, G, G, W], interpolation: VK_FILTER_NEAREST),
-      Image[Gray](width: 3, height: 3, data: @[W, G, W, G, W, G, W, G, W], interpolation: VK_FILTER_NEAREST),
+      Image[Gray](width: 2, height: 2, data: @[W, G, G, W], minInterpolation: VK_FILTER_NEAREST, magInterpolation: VK_FILTER_NEAREST),
+      Image[Gray](width: 3, height: 3, data: @[W, G, W, G, W, G, W, G, W], minInterpolation: VK_FILTER_NEAREST, magInterpolation: VK_FILTER_NEAREST),
     ],
   ),
   )
@@ -522,7 +525,7 @@
 
   type
     Uniforms = object
-      texture1: Image[RGBA]
+      texture1: Image[BGRA]
     Shader = object
       position {.VertexAttribute.}: Vec3f
       uv {.VertexAttribute.}: Vec2f
@@ -558,7 +561,7 @@
   var pipeline = CreatePipeline[Shader](renderPass = vulkan.swapchain.renderPass)
   var uniforms1 = asDescriptorSet(
     Uniforms(
-      texture1: LoadImage[RGBA]("art.png"),
+      texture1: LoadImage[BGRA]("art.png"),
     )
   )
   UploadImages(renderdata, uniforms1)
@@ -590,7 +593,7 @@
 
   type
     Uniforms = object
-      frameTexture: Image[RGBA]
+      frameTexture: Image[BGRA]
     TriangleShader = object
       position {.VertexAttribute.}: Vec3f
       color {.VertexAttribute.}: Vec3f
@@ -639,7 +642,7 @@
   )
   var uniforms1 = asDescriptorSet(
     Uniforms(
-      frameTexture: Image[RGBA](width: vulkan.swapchain.width, height: vulkan.swapchain.height, isRenderTarget: true),
+      frameTexture: Image[BGRA](width: vulkan.swapchain.width, height: vulkan.swapchain.height, isRenderTarget: true),
     )
   )
   AssignBuffers(renderdata, mesh)