Mercurial > games > semicongine
changeset 1443:768bf1a8407b default tip main
add: hello-triangle example, did: update according readme parts
author | sam <sam@basx.dev> |
---|---|
date | Mon, 24 Feb 2025 10:09:41 +0700 |
parents | 8d26eb12f6e8 |
children | |
files | README.md tests/hello_triangle.nim |
diffstat | 2 files changed, 138 insertions(+), 33 deletions(-) [+] |
line wrap: on
line diff
--- a/README.md Sun Feb 16 23:12:32 2025 +0700 +++ b/README.md Mon Feb 24 10:09:41 2025 +0700 @@ -35,12 +35,12 @@ ## Wishlist -[ ] Macro-based internal DSL to convert Nim code into GLSL/slang at compile time -[ ] Better memory management - - Simple buffer resizing - - Mechanism to mark unused buffers - - Use mapped GPU buffers without copying (implement seq with pointers to GPU memory) - - Do not keep copy of content for un-mapped buffers around (only pass data on creating or update) +- Macro-based internal DSL to convert Nim code into GLSL/slang at compile time +- Better memory management + - Simple buffer resizing + - Mechanism to mark unused buffers + - Use mapped GPU buffers without copying (implement seq with pointers to GPU memory) + - Do not keep copy of content for un-mapped buffers around (only pass data on creating or update) ## Hello world example @@ -49,38 +49,46 @@ ```nim -import semicongine +import ../semicongine +import ../semicongine/rendering +import ../semicongine/input # required -InitVulkan() +initEngine("Hello triangle") # set up a simple render pass to render the displayed frame -var renderpass = CreateDirectPresentationRenderPass(depthBuffer = false, samples = VK_SAMPLE_COUNT_1_BIT) +var renderpass = createDirectPresentationRenderPass( + depthBuffer = false, samples = VK_SAMPLE_COUNT_1_BIT +) # the swapchain, needs to be attached to the main renderpass -SetupSwapchain(renderpass = renderpass) +setupSwapchain(renderpass = renderpass) # render data is used for memory management on the GPU -var renderdata = InitRenderData() +var renderdata = initRenderData() type # define a push constant, to have something moving PushConstant = object scale: float32 + # This is how we define shaders: the interface needs to be "typed" # but the shader code itself can freely be written in glsl Shader = object position {.VertexAttribute.}: Vec3f color {.VertexAttribute.}: Vec3f - pushConstant {.PushConstantAttribute.}: PushConstant + pushConstant {.PushConstant.}: PushConstant fragmentColor {.Pass.}: Vec3f outColor {.ShaderOutput.}: Vec4f # code - vertexCode: string = """void main() { + vertexCode: string = + """void main() { fragmentColor = color; gl_Position = vec4(position * pushConstant.scale, 1);}""" - fragmentCode: string = """void main() { + fragmentCode: string = + """void main() { outColor = vec4(fragmentColor, 1);}""" + # And we also need to define our Mesh, which does describe the vertex layout TriangleMesh = object position: GPUArray[Vec3f, VertexBuffer] @@ -88,40 +96,48 @@ # instantiate the mesh and fill with data var mesh = TriangleMesh( - position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(0, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer), - color: asGPUArray([NewVec3f(0, 0, 1), NewVec3f(0, 1, 0), NewVec3f(1, 0, 0)], VertexBuffer), + position: asGPUArray([vec3(-0.5, -0.5), vec3(0, 0.5), vec3(0.5, -0.5)], VertexBuffer), + color: asGPUArray([vec3(0, 0, 1), vec3(0, 1, 0), vec3(1, 0, 0)], VertexBuffer), ) # this allocates GPU data, uploads the data to the GPU and flushes any thing that is host-cached # this is a shortcut version, more fine-grained control is possible -AssignBuffers(renderdata, mesh) -renderdata.FlushAllMemory() +assignBuffers(renderdata, mesh) +renderdata.flushAllMemory() # Now we need to instantiate the shader as a pipeline object that is attached to a renderpass -var pipeline = CreatePipeline[Shader](renderPass = vulkan.swapchain.renderPass) +var pipeline = createPipeline(Shader(), renderPass = renderPass) # the main render-loop will exit if we get a kill-signal from the OS -while UpdateInputs(): - +while updateInputs(): # starts the drawing for the next frame and provides us necesseary framebuffer and commandbuffer objects in this scope - WithNextFrame(framebuffer, commandbuffer): - + withNextFrame(framebuffer, commandbuffer): # start the main (and only) renderpass we have, needs to know the target framebuffer and a commandbuffer - WithRenderPass(vulkan.swapchain.renderPass, framebuffer, commandbuffer, vulkan.swapchain.width, vulkan.swapchain.height, NewVec4f(0, 0, 0, 0)): - + withRenderPass( + renderPass, + framebuffer, + commandbuffer, + frameWidth(), + frameHeight(), + vec4(0, 0, 0, 0), + ): # now activate our shader-pipeline - WithPipeline(commandbuffer, pipeline): - + withPipeline(commandbuffer, pipeline): # and finally, draw the mesh and set a single parameter # more complicated setups with descriptors/uniforms are of course possible - RenderWithPushConstant(commandbuffer = commandbuffer, pipeline = pipeline, mesh = mesh, pushConstant = PushConstant(scale: 0.3)) + renderWithPushConstant( + commandbuffer = commandbuffer, + pipeline = pipeline, + mesh = mesh, + pushConstant = PushConstant(scale: 0.3), + ) # cleanup -checkVkResult vkDeviceWaitIdle(vulkan.device) -DestroyPipeline(pipeline) -DestroyRenderData(renderdata) -vkDestroyRenderPass(vulkan.device, renderpass.vk, nil) -DestroyVulkan() +checkVkResult vkDeviceWaitIdle(engine().vulkan.device) +destroyPipeline(pipeline) +destroyRenderData(renderdata) +destroyRenderPass(renderpass) +destroyVulkan() ```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/hello_triangle.nim Mon Feb 24 10:09:41 2025 +0700 @@ -0,0 +1,89 @@ +import ../semicongine +import ../semicongine/rendering +import ../semicongine/input + +# required +initEngine("Hello triangle") + +# set up a simple render pass to render the displayed frame +var renderpass = createDirectPresentationRenderPass( + depthBuffer = false, samples = VK_SAMPLE_COUNT_1_BIT +) + +# the swapchain, needs to be attached to the main renderpass +setupSwapchain(renderpass = renderpass) + +# render data is used for memory management on the GPU +var renderdata = initRenderData() + +type + # define a push constant, to have something moving + PushConstant = object + scale: float32 + + # This is how we define shaders: the interface needs to be "typed" + # but the shader code itself can freely be written in glsl + Shader = object + position {.VertexAttribute.}: Vec3f + color {.VertexAttribute.}: Vec3f + pushConstant {.PushConstant.}: PushConstant + fragmentColor {.Pass.}: Vec3f + outColor {.ShaderOutput.}: Vec4f + # code + vertexCode: string = + """void main() { + fragmentColor = color; + gl_Position = vec4(position * pushConstant.scale, 1);}""" + fragmentCode: string = + """void main() { + outColor = vec4(fragmentColor, 1);}""" + + # And we also need to define our Mesh, which does describe the vertex layout + TriangleMesh = object + position: GPUArray[Vec3f, VertexBuffer] + color: GPUArray[Vec3f, VertexBuffer] + +# instantiate the mesh and fill with data +var mesh = TriangleMesh( + position: asGPUArray([vec3(-0.5, -0.5), vec3(0, 0.5), vec3(0.5, -0.5)], VertexBuffer), + color: asGPUArray([vec3(0, 0, 1), vec3(0, 1, 0), vec3(1, 0, 0)], VertexBuffer), +) + +# this allocates GPU data, uploads the data to the GPU and flushes any thing that is host-cached +# this is a shortcut version, more fine-grained control is possible +assignBuffers(renderdata, mesh) +renderdata.flushAllMemory() + +# Now we need to instantiate the shader as a pipeline object that is attached to a renderpass +var pipeline = createPipeline(Shader(), renderPass = renderPass) + +# the main render-loop will exit if we get a kill-signal from the OS +while updateInputs(): + # starts the drawing for the next frame and provides us necesseary framebuffer and commandbuffer objects in this scope + withNextFrame(framebuffer, commandbuffer): + # start the main (and only) renderpass we have, needs to know the target framebuffer and a commandbuffer + withRenderPass( + renderPass, + framebuffer, + commandbuffer, + frameWidth(), + frameHeight(), + vec4(0, 0, 0, 0), + ): + # now activate our shader-pipeline + withPipeline(commandbuffer, pipeline): + # and finally, draw the mesh and set a single parameter + # more complicated setups with descriptors/uniforms are of course possible + renderWithPushConstant( + commandbuffer = commandbuffer, + pipeline = pipeline, + mesh = mesh, + pushConstant = PushConstant(scale: 0.3), + ) + +# cleanup +checkVkResult vkDeviceWaitIdle(engine().vulkan.device) +destroyPipeline(pipeline) +destroyRenderData(renderdata) +destroyRenderPass(renderpass) +destroyVulkan()