From ae6d05c3917be8f0fd0d4a4fb1a76f9c1ac684ae Mon Sep 17 00:00:00 2001 From: knokko Date: Mon, 30 Oct 2023 10:05:43 +0100 Subject: [PATCH] Add HelloTriangle sample --- .../knokko/boiler/samples/HelloTriangle.java | 269 ++++++++++++++++++ .../boiler/samples/graphics/triangle.frag | 9 + .../boiler/samples/graphics/triangle.frag.spv | Bin 0 -> 568 bytes .../boiler/samples/graphics/triangle.vert | 11 + .../boiler/samples/graphics/triangle.vert.spv | Bin 0 -> 1080 bytes 5 files changed, 289 insertions(+) create mode 100644 samples/src/main/java/com/github/knokko/boiler/samples/HelloTriangle.java create mode 100644 samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.frag create mode 100644 samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.frag.spv create mode 100644 samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.vert create mode 100644 samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.vert.spv diff --git a/samples/src/main/java/com/github/knokko/boiler/samples/HelloTriangle.java b/samples/src/main/java/com/github/knokko/boiler/samples/HelloTriangle.java new file mode 100644 index 0000000..54e4fba --- /dev/null +++ b/samples/src/main/java/com/github/knokko/boiler/samples/HelloTriangle.java @@ -0,0 +1,269 @@ +package com.github.knokko.boiler.samples; + +import com.github.knokko.boiler.builder.BoilerBuilder; +import com.github.knokko.boiler.builder.BoilerSwapchainBuilder; +import com.github.knokko.boiler.builder.instance.ValidationFeatures; +import com.github.knokko.boiler.commands.CommandRecorder; +import com.github.knokko.boiler.pipelines.GraphicsPipelineBuilder; +import com.github.knokko.boiler.pipelines.ShaderInfo; +import com.github.knokko.boiler.swapchain.SwapchainResourceManager; +import com.github.knokko.boiler.sync.WaitSemaphore; +import org.lwjgl.vulkan.*; + +import static com.github.knokko.boiler.exceptions.VulkanFailureException.assertVkSuccess; +import static java.lang.Thread.sleep; +import static org.lwjgl.glfw.GLFW.glfwPollEvents; +import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose; +import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.system.MemoryUtil.memFloatBuffer; +import static org.lwjgl.vulkan.KHRSurface.VK_PRESENT_MODE_MAILBOX_KHR; +import static org.lwjgl.vulkan.KHRSwapchain.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; +import static org.lwjgl.vulkan.VK10.*; + +public class HelloTriangle { + + public static void main(String[] args) throws InterruptedException { + var boiler = new BoilerBuilder( + VK_API_VERSION_1_0, "HelloTriangle", VK_MAKE_VERSION(0, 1, 0) + ) + .validation(new ValidationFeatures(false, false, false, true, true)) + .window(0L, 1000, 800, new BoilerSwapchainBuilder(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) + .build(); + + int numFramesInFlight = 3; + var commandPool = boiler.commands.createPool( + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + boiler.queueFamilies().graphics().index(), + "Drawing" + ); + var commandBuffers = boiler.commands.createPrimaryBuffers(commandPool, numFramesInFlight, "Drawing"); + long[] commandFences = boiler.sync.createFences(true, numFramesInFlight, "Fence"); + long graphicsPipeline; + long pipelineLayout; + long renderPass; + + try (var stack = stackPush()) { + pipelineLayout = boiler.pipelines.createLayout(stack, null, "DrawingLayout"); + + var attachments = VkAttachmentDescription.calloc(1, stack); + var colorAttachment = attachments.get(0); + colorAttachment.format(boiler.swapchainSettings.surfaceFormat().format()); + colorAttachment.samples(VK_SAMPLE_COUNT_1_BIT); + colorAttachment.loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR); + colorAttachment.storeOp(VK_ATTACHMENT_STORE_OP_STORE); + colorAttachment.stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE); + colorAttachment.stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE); + colorAttachment.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED); + colorAttachment.finalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + var colorReference = VkAttachmentReference.calloc(1, stack); + colorReference.attachment(0); + colorReference.layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + var subpass = VkSubpassDescription.calloc(1, stack); + subpass.pipelineBindPoint(VK_PIPELINE_BIND_POINT_GRAPHICS); + subpass.pInputAttachments(null); + subpass.colorAttachmentCount(1); + subpass.pColorAttachments(colorReference); + subpass.pResolveAttachments(null); + subpass.pDepthStencilAttachment(null); + subpass.pPreserveAttachments(null); + + var dependency = VkSubpassDependency.calloc(1, stack); + dependency.srcSubpass(VK_SUBPASS_EXTERNAL); + dependency.dstSubpass(0); + dependency.srcStageMask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + dependency.srcAccessMask(0); + dependency.dstStageMask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + dependency.dstAccessMask(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + var ciRenderPass = VkRenderPassCreateInfo.calloc(stack); + ciRenderPass.sType$Default(); + ciRenderPass.pAttachments(attachments); + ciRenderPass.pSubpasses(subpass); + ciRenderPass.pDependencies(dependency); + + var pRenderPass = stack.callocLong(1); + assertVkSuccess(vkCreateRenderPass( + boiler.vkDevice(), ciRenderPass, null, pRenderPass + ), "CreateRenderPass", "TrianglePass"); + renderPass = pRenderPass.get(0); + } + + try (var stack = stackPush()) { + var vertexModule = boiler.pipelines.createShaderModule( + stack, "com/github/knokko/boiler/samples/graphics/triangle.vert.spv", "TriangleVertices" + ); + var fragmentModule = boiler.pipelines.createShaderModule( + stack, "com/github/knokko/boiler/samples/graphics/triangle.frag.spv", "TriangleFragments" + ); + + var vertexBindings = VkVertexInputBindingDescription.calloc(1, stack); + vertexBindings.binding(0); + vertexBindings.stride(4 * (2 + 3)); + vertexBindings.inputRate(VK_VERTEX_INPUT_RATE_VERTEX); + + var vertexAttributes = VkVertexInputAttributeDescription.calloc(2, stack); + var attributePosition = vertexAttributes.get(0); + attributePosition.location(0); + attributePosition.binding(0); + attributePosition.format(VK_FORMAT_R32G32_SFLOAT); + attributePosition.offset(0); + var attributeColor = vertexAttributes.get(1); + attributeColor.location(1); + attributeColor.binding(0); + attributeColor.format(VK_FORMAT_R32G32B32_SFLOAT); + attributeColor.offset(4 * 2); + + var ciVertexInput = VkPipelineVertexInputStateCreateInfo.calloc(stack); + ciVertexInput.sType$Default(); + ciVertexInput.pVertexBindingDescriptions(vertexBindings); + ciVertexInput.pVertexAttributeDescriptions(vertexAttributes); + + var pipelineBuilder = new GraphicsPipelineBuilder(boiler, stack); + pipelineBuilder.shaderStages( + new ShaderInfo(VK_SHADER_STAGE_VERTEX_BIT, vertexModule, null), + new ShaderInfo(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentModule, null) + ); + pipelineBuilder.ciPipeline.pVertexInputState(ciVertexInput); + pipelineBuilder.simpleInputAssembly(); + pipelineBuilder.dynamicViewports(1); + pipelineBuilder.simpleRasterization(VK_CULL_MODE_NONE); + pipelineBuilder.noMultisampling(); + pipelineBuilder.noDepthStencil(); + pipelineBuilder.noColorBlending(1); + pipelineBuilder.dynamicStates(VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR); + + pipelineBuilder.ciPipeline.renderPass(renderPass); + pipelineBuilder.ciPipeline.layout(pipelineLayout); + + graphicsPipeline = pipelineBuilder.build("TrianglePipeline"); + + vkDestroyShaderModule(boiler.vkDevice(), vertexModule, null); + vkDestroyShaderModule(boiler.vkDevice(), fragmentModule, null); + } + + var vertexBuffer = boiler.buffers.createMapped( + 3 * 4 * (2 + 3), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, "TriangleVertices" + ); + var vertices = memFloatBuffer(vertexBuffer.hostAddress(), 3 * (2 + 3)); + // Put color (1, 0, 0) at position (-1, 1) + vertices.put(-1f); + vertices.put(1f); + vertices.put(1f); + vertices.put(0f); + vertices.put(0f); + // Put color (0, 0, 1) at position (1, 1) + vertices.put(1f); + vertices.put(1f); + vertices.put(0f); + vertices.put(0f); + vertices.put(1f); + // Put color (0, 1, 0) at position (0, -1) + vertices.put(0f); + vertices.put(-1f); + vertices.put(0f); + vertices.put(1f); + vertices.put(0f); + + long frameCounter = 0; + var swapchainResources = new SwapchainResourceManager<>(swapchainImage -> { + try (var stack = stackPush()) { + long imageView = boiler.images.createSimpleView( + stack, swapchainImage.vkImage(), boiler.swapchainSettings.surfaceFormat().format(), + VK_IMAGE_ASPECT_COLOR_BIT, "SwapchainView " + swapchainImage.imageIndex() + ); + + long framebuffer = boiler.images.createFramebuffer( + stack, renderPass, swapchainImage.width(), swapchainImage.height(), + "TriangleFramebuffer", imageView + ); + + return new AssociatedSwapchainResources(framebuffer, imageView); + } + }, resources -> { + vkDestroyFramebuffer(boiler.vkDevice(), resources.framebuffer, null); + vkDestroyImageView(boiler.vkDevice(), resources.imageView, null); + }); + + long referenceTime = System.currentTimeMillis(); + long referenceFrames = 0; + + while (!glfwWindowShouldClose(boiler.glfwWindow())) { + glfwPollEvents(); + + long currentTime = System.currentTimeMillis(); + if (currentTime > 1000 + referenceTime) { + System.out.println("FPS is " + (frameCounter - referenceFrames)); + referenceTime = currentTime; + referenceFrames = frameCounter; + } + + try (var stack = stackPush()) { + var swapchainImage = boiler.swapchains.acquireNextImage(VK_PRESENT_MODE_MAILBOX_KHR); + if (swapchainImage == null) { + //noinspection BusyWait + sleep(100); + continue; + } + + var imageResources = swapchainResources.get(swapchainImage); + WaitSemaphore[] waitSemaphores = { new WaitSemaphore( + swapchainImage.acquireSemaphore(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT + )}; + + int frameIndex = (int) (frameCounter % numFramesInFlight); + var commandBuffer = commandBuffers[frameIndex]; + long fence = commandFences[frameIndex]; + boiler.sync.waitAndReset(stack, fence, 100_000_000); + + var recorder = CommandRecorder.begin(commandBuffer, boiler, stack, "DrawCommands"); + + var pColorClear = VkClearValue.calloc(1, stack); + pColorClear.color().float32(stack.floats(0.2f, 0.2f, 0.2f, 1f)); + + var biRenderPass = VkRenderPassBeginInfo.calloc(stack); + biRenderPass.sType$Default(); + biRenderPass.renderPass(renderPass); + biRenderPass.framebuffer(imageResources.framebuffer); + biRenderPass.renderArea().offset().set(0, 0); + biRenderPass.renderArea().extent().set(swapchainImage.width(), swapchainImage.height()); + biRenderPass.clearValueCount(1); + biRenderPass.pClearValues(pColorClear); + + vkCmdBeginRenderPass(commandBuffer, biRenderPass, VK_SUBPASS_CONTENTS_INLINE); + recorder.dynamicViewportAndScissor(swapchainImage.width(), swapchainImage.height()); + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + vkCmdBindVertexBuffers(commandBuffer, 0, stack.longs(vertexBuffer.vkBuffer()), stack.longs(0)); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + vkCmdEndRenderPass(commandBuffer); + assertVkSuccess(vkEndCommandBuffer(commandBuffer), "TriangleDrawing", null); + + boiler.queueFamilies().graphics().queues().get(0).submit( + commandBuffer, "SubmitDraw", waitSemaphores, fence, swapchainImage.presentSemaphore() + ); + + boiler.swapchains.presentImage(swapchainImage); + frameCounter += 1; + } + } + + vkDeviceWaitIdle(boiler.vkDevice()); + for (long fence : commandFences) vkDestroyFence(boiler.vkDevice(), fence, null); + + vkDestroyPipelineLayout(boiler.vkDevice(), pipelineLayout, null); + vkDestroyPipeline(boiler.vkDevice(), graphicsPipeline, null); + vkDestroyRenderPass(boiler.vkDevice(), renderPass, null); + vkDestroyCommandPool(boiler.vkDevice(), commandPool, null); + vertexBuffer.destroy(boiler.vmaAllocator()); + boiler.destroyInitialObjects(); + } + + private record AssociatedSwapchainResources( + long framebuffer, + long imageView + ) {} +} + diff --git a/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.frag b/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.frag new file mode 100644 index 0000000..470f251 --- /dev/null +++ b/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 inColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(inColor, 1.0); +} diff --git a/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.frag.spv b/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..b83092dd81313d8405710a7d2fc664a90be89a00 GIT binary patch literal 568 zcmYk2%}T>i5QWF4X=`i$Ea*-uE`{PkMG$o%l1-qB4-lly;zCSIn+ooHHlNCk;Q4M- z$%V<>Idf*txe01#O|w1gSl33jJwvMrF<}j#qxdzRtc&^N^6COc&jL?`)3>J8m5KK^ zm($o+94H3jflENQKwHheHB`5@MR5{Mr&0Wr%$Cb!U3_L~lDRxh=5CeF3ipx1YgKqI zpJkhQl@N+i)eSB5+KWx`xX2bO$qA`@FD~Cgk!t*%(A1&E>#iQjB|cBRWApbT-5bKj zievG44>j6jeOt=>5%AlxMgt6fTNyqz{Eo7IgU8`h;{ohkHQb)U?CarLFTf949CrK& z36<}Jvb(Eb{zWLW{Ss69#|9;a#*Q~EG4?;pJ^l|B%)_UTk9(=|9(uoWd87CP3!g7A literal 0 HcmV?d00001 diff --git a/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.vert b/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.vert new file mode 100644 index 0000000..2ce21ef --- /dev/null +++ b/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.vert @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec3 inColor; + +layout(location = 0) out vec3 outColor; + +void main() { + gl_Position = vec4(inPosition, 0.0, 1.0); + outColor = inColor; +} diff --git a/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.vert.spv b/samples/src/main/resources/com/github/knokko/boiler/samples/graphics/triangle.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..a24afae2e69c5d02a12efa7a64fed6e50dfac37c GIT binary patch literal 1080 zcmYk4NlODk5QW<$)q z_F_|Q|BeQ9;d&o3Igu_xEArHfpxpQ8)ig;wZ6c9JOpGZsztqCU0~g z&!)|!*BVR+lrcCn5Z4LKcuk^*xbqx$^7uop&;rLMmUV4zvsCDa%jXUbw`u-lzciVe zA_iy0t`pn7vEBS7ZN~i=8z9z8l71}EJ-It4(2pJ1v_H~e>?G^u*IANvioBZJkxdKe zPFdW8Wx31`3b`9kQoDSup=da!L-aPvKgA$8?VO8a}9l|H)#W6re8 zEBZ1Ae@4QA*Q_*i;7q?cX*ht>51(CtQBUrogya1s=Y3yNzb2d6aHao-G&NwvH%l9w z{`lJ=Kufz literal 0 HcmV?d00001