From 4e606e094af7babe5acd154ff76fe7e5de572a18 Mon Sep 17 00:00:00 2001 From: Halbear Date: Mon, 26 Jan 2026 14:56:23 +0000 Subject: [PATCH] working on colour rendering --- .../Terrain4J/EngineCore/Display/Render.java | 7 +- .../EngineCore/Main/PrimaryRuntime.java | 4 +- .../Vulkan/Structure/DeviceLayers/Device.java | 15 +++- .../Structure/DeviceLayers/Queues/Queue.java | 21 ++++- .../Structure/DisplayToScreen/ImageView.java | 1 + .../DisplayToScreen/SceneRender.java | 84 ++++++++++++++++++- .../Structure/DisplayToScreen/SwapChain.java | 41 ++++++++- .../EngineCore/Vulkan/Util/VulkanUtils.java | 34 +++++++- 8 files changed, 192 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Display/Render.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Display/Render.java index 209c8d3..4e81bc9 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Display/Render.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Display/Render.java @@ -13,6 +13,7 @@ import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.VkCommandBufferSubmitInfo; import org.lwjgl.vulkan.VkSemaphoreSubmitInfo; +import org.tinylog.Logger; import javax.swing.*; import java.util.Arrays; @@ -53,7 +54,7 @@ public class Render { for(int i = 0; i < SwapChainImageCount; i++){ RenderCompleteSemaphores[i] = new Semaphore(RendererContext); } - sceneRender = new SceneRender(); + sceneRender = new SceneRender(RendererContext); } private void RecordingStart(CommandPool commandPool, CommandBuffer commandBuffer){ @@ -66,7 +67,7 @@ public class Render { } public void cleanup() { - RendererContext.WaitIdle(); + RendererContext.GetDevice().waitIdle(); sceneRender.cleanup(); Arrays.asList(RenderCompleteSemaphores).forEach(i->i.cleanup(RendererContext)); Arrays.asList(PresentCompleteSemaphores).forEach(i->i.cleanup(RendererContext)); @@ -87,7 +88,7 @@ public class Render { if (ImageIndex < 0){ return; } - sceneRender.render(RendererContext,CommandBuffer,ImageIndex); + sceneRender.Render(RendererContext,CommandBuffer,ImageIndex); RecordingStop(CommandBuffer); Submit(CommandBuffer, CurrentFrame, ImageIndex); swapChain.PresentImage(PresentQueue, RenderCompleteSemaphores[ImageIndex],ImageIndex); diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Main/PrimaryRuntime.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Main/PrimaryRuntime.java index e796e40..bdf8731 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Main/PrimaryRuntime.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Main/PrimaryRuntime.java @@ -95,7 +95,9 @@ public class PrimaryRuntime { if (ThreadsOpen > 0 || VulkanContext.VulkanLayersOpen > -1) { Logger.debug("Waiting to close Primary Thread, making sure vulkan has shut down properly first and Engine Threads are closed\nThreads Open:[{}]\nVulkan Layers Open:[{}]", ThreadsOpen, VulkanContext.VulkanLayersOpen < 0 ? "All processes closed and vulkan instance removed" : VulkanContext.VulkanLayersOpen); while ((ThreadsOpen > 0 || VulkanContext.VulkanLayersOpen > 0) && VulkanContext.VulkanLayersOpen > -1 && !CanClose) { - + if (!DrawingThread.FetchRenderThread().isAlive() && !PrimaryThread.FetchMainThread().isAlive() && ThreadsOpen >0){ + break; + } } } Logger.debug("Closing Primary Thread, Thread: [{}]",Thread.currentThread().toString()); diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Device.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Device.java index 95df705..8b5fda7 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Device.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Device.java @@ -4,10 +4,7 @@ import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext; import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; -import org.lwjgl.vulkan.VkDevice; -import org.lwjgl.vulkan.VkDeviceCreateInfo; -import org.lwjgl.vulkan.VkDeviceQueueCreateInfo; -import org.lwjgl.vulkan.VkExtensionProperties; +import org.lwjgl.vulkan.*; import org.tinylog.Logger; import java.awt.*; @@ -39,11 +36,21 @@ public class Device { .queueFamilyIndex(i) .pQueuePriorities(Priorities); } + + var features13 = VkPhysicalDeviceVulkan13Features.calloc(MemStack) + .sType$Default() + .dynamicRendering(true) + .synchronization2(true); + var features2 = VkPhysicalDeviceFeatures2.calloc(MemStack).sType$Default(); + features2.pNext(features13.address()); + var DeviceCreateInfo = VkDeviceCreateInfo.calloc(MemStack) .sType$Default() + .pNext(features2.address()) .ppEnabledExtensionNames(RequiredExtensions) .pQueueCreateInfos(QueueCreationInfoBuffer); PointerBuffer pp = MemStack.mallocPointer(1); + vkCheck(vkCreateDevice(PhysDevice.GetPhysicalDevice(), DeviceCreateInfo, null, pp), "Failed to create device"); VulkanDevice = new VkDevice(pp.get(0), PhysDevice.GetPhysicalDevice(), DeviceCreateInfo); } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Queues/Queue.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Queues/Queue.java index 5186a1d..d50f56d 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Queues/Queue.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Queues/Queue.java @@ -7,6 +7,8 @@ import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.*; import org.tinylog.Logger; +import java.nio.IntBuffer; + import static net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils.vkCheck; import static org.lwjgl.vulkan.VK13.*; @@ -55,7 +57,24 @@ public class Queue { super(vulkanContext, GetPresentQueueFamilyIndex(vulkanContext), QueueIndex); } private static int GetPresentQueueFamilyIndex(VulkanContext vulkanContext){ - return -1; + int Index = -1; + try(var MemStack = MemoryStack.stackPush()){ + var QueuePropertiesBuffer = vulkanContext.GetPhysicalDevice().GetQueueFamilyProperties(); + int QueueFamilyCount = QueuePropertiesBuffer.capacity(); + IntBuffer intBuffer = MemStack.mallocInt(1); + for(int i = 0; i < QueueFamilyCount; i++){ + KHRSurface.vkGetPhysicalDeviceSurfaceSupportKHR(vulkanContext.GetPhysicalDevice().GetPhysicalDevice(), i, vulkanContext.GetSurface().GetVkSurface(), intBuffer); + boolean SupportsPresentation = intBuffer.get(0) == VK_TRUE; + if(SupportsPresentation){ + Index = i; + break; + } + } + } + if (Index < 0){ + throw new RuntimeException("Could Not Present Image Queue Family Index"); + } + return Index; } } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/ImageView.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/ImageView.java index 3cc9e78..4ef4dec 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/ImageView.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/ImageView.java @@ -53,6 +53,7 @@ public class ImageView { public long GetVulkanImageView(){ return VulkanImageView; } + public long GetVulkanImage(){return VulkanImage;} public static class ImageViewData{ private int AspectMask; diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SceneRender.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SceneRender.java index b04edff..a3cefb1 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SceneRender.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SceneRender.java @@ -1,7 +1,87 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen; -public class SceneRender { - public SceneRender(){ +import net.halbear.Terrain4J.EngineCore.Display.Render; +import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer; +import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; +import java.util.Arrays; + +import static org.lwjgl.vulkan.KHRSwapchain.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; +import static org.lwjgl.vulkan.KHRSynchronization2.VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR; +import static org.lwjgl.vulkan.KHRSynchronization2.VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR; +import static org.lwjgl.vulkan.VK10.*; +import static org.lwjgl.vulkan.VK13.*; + +public class SceneRender { //dynamic rendering; + private final VkClearValue ClearValueColour; + private VkRenderingAttachmentInfo.Buffer[] AttachmentInfoColour; + private VkRenderingInfo[] RenderInfo; + public SceneRender(VulkanContext vulkanContext){ + ClearValueColour = VkClearValue.calloc().color(c->c.float32(0,0.5f).float32(1,0.7f).float32(2,0.9f).float32(3,1.0f)); + AttachmentInfoColour = CreateColourAttachmentsInfo(vulkanContext,ClearValueColour); + RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour); + } + + public void Render(VulkanContext vulkanContext, CommandBuffer commandBuffer, int ImageIndex){ + try(var MemStack = MemoryStack.stackPush()){ + SwapChain swapChain = vulkanContext.GetSwapChain(); + long SwapChainImage = swapChain.GetVulkanImageView(ImageIndex).GetVulkanImage(); + VkCommandBuffer CommandHandle = commandBuffer.GetVulkanCommandBuffer(); + + VulkanUtils.ImageBarrier(MemStack, CommandHandle, SwapChainImage, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_NONE,VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,VK_IMAGE_ASPECT_COLOR_BIT); + vkCmdBeginRendering(CommandHandle, RenderInfo[ImageIndex]); + vkCmdEndRendering(CommandHandle); + + VulkanUtils.ImageBarrier(MemStack, CommandHandle, SwapChainImage, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_2_NONE, + VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + private static VkRenderingInfo[] CreateRenderInfo(VulkanContext vulkanContext, VkRenderingAttachmentInfo.Buffer[] ColourAttachments){ + SwapChain swapChain = vulkanContext.GetSwapChain(); + int ImageCount = swapChain.GetImageCount(); + var Result = new VkRenderingInfo[ImageCount]; + try(var MemStack = MemoryStack.stackPush()){ + VkExtent2D Extent = swapChain.GetSwapChainExtent(); + var RenderArea = VkRect2D.calloc(MemStack).extent(Extent); + for(int i = 0; i < ImageCount; i++){ + var RenderInfo = VkRenderingInfo.calloc() + .sType$Default() + .renderArea(RenderArea) + .layerCount(1) + .pColorAttachments(ColourAttachments[i]); + Result[i] = RenderInfo; + } + } + return Result; + } + private static VkRenderingAttachmentInfo.Buffer[] CreateColourAttachmentsInfo(VulkanContext vulkanContext, VkClearValue CLearValue){ + SwapChain swapChain = vulkanContext.GetSwapChain(); + int ImageCount = swapChain.GetImageCount(); + var result = new VkRenderingAttachmentInfo.Buffer[ImageCount]; + for(int i = 0; i < ImageCount; i++){ + var Attachments = VkRenderingAttachmentInfo.calloc(1) + .sType$Default() + .imageView(swapChain.GetVulkanImageView(i).GetVulkanImageView()) + .imageLayout(VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR) + .loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR) + .clearValue(CLearValue); + result[i] = Attachments; + } + return result; + } + public void cleanup(){ + Arrays.asList(RenderInfo).forEach(VkRenderingInfo::free); + Arrays.asList(AttachmentInfoColour).forEach(VkRenderingAttachmentInfo.Buffer::free); + ClearValueColour.free(); } } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SwapChain.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SwapChain.java index 53893a3..288a8fb 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SwapChain.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SwapChain.java @@ -4,7 +4,9 @@ import net.halbear.Terrain4J.EngineCore.Display.Window; import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext; import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device; import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation.Semaphore; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Queues.Queue; import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; import org.lwjgl.vulkan.*; import org.tinylog.Logger; @@ -13,8 +15,7 @@ import java.nio.LongBuffer; import java.util.Arrays; import static net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils.vkCheck; -import static org.lwjgl.vulkan.VK10.VK_IMAGE_ASPECT_COLOR_BIT; -import static org.lwjgl.vulkan.VK10.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +import static org.lwjgl.vulkan.VK10.*; public class SwapChain { private final ImageView[] ImageViewArray; @@ -100,7 +101,41 @@ public class SwapChain { } public int FetchNextImage(Device device, Semaphore ImageFetchSemaphore){ - return 0; //testing smth with the git rq + int ImageIndex; + try(var MemStack = MemoryStack.stackPush()){ + IntBuffer IntPointer = MemStack.mallocInt(1); + int Error = KHRSwapchain.vkAcquireNextImageKHR(device.FetchVulkanDevice(), VulkanSwapChain, ~0L, ImageFetchSemaphore.GetSemaphore(), MemoryUtil.NULL, IntPointer); + if (Error == KHRSwapchain.VK_ERROR_OUT_OF_DATE_KHR){ + return -1; + } + else if (Error == KHRSwapchain.VK_SUBOPTIMAL_KHR){ + Logger.warn("Suboptimal surface properties match, not fatal"); + } else if (Error != VK_SUCCESS){ + throw new RuntimeException("Failed to fetch next image in the swapchain: " + Error); + } + ImageIndex = IntPointer.get(0); + } + return ImageIndex; + } + public boolean PresentImage(Queue queue,Semaphore RenderCompleteSemaphores, int ImageIndex){ + boolean resize = false; + try(var MemStack = MemoryStack.stackPush()){ + VkPresentInfoKHR Present = VkPresentInfoKHR.calloc(MemStack) + .sType$Default() + .pWaitSemaphores(MemStack.longs(RenderCompleteSemaphores.GetSemaphore())) + .swapchainCount(1) + .pSwapchains(MemStack.longs(VulkanSwapChain)) + .pImageIndices(MemStack.ints(ImageIndex)); + int Error = KHRSwapchain.vkQueuePresentKHR(queue.GetVulkanQueue(), Present); + if (Error == KHRSwapchain.VK_ERROR_OUT_OF_DATE_KHR){ + resize = true; + } else if (Error == KHRSwapchain.VK_SUBOPTIMAL_KHR){ + Logger.warn("Suboptimal surface properties match, not fatal"); + } else if (Error != VK_SUCCESS){ + throw new RuntimeException("failed to present KHR: " + Error); + } + } + return resize; } public void cleanup(Device device){ diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Util/VulkanUtils.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Util/VulkanUtils.java index 12c2073..c44d4fb 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Util/VulkanUtils.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Util/VulkanUtils.java @@ -1,8 +1,16 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Util; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkCommandBuffer; +import org.lwjgl.vulkan.VkDependencyInfo; +import org.lwjgl.vulkan.VkImageMemoryBarrier; +import org.lwjgl.vulkan.VkImageMemoryBarrier2; +import org.tinylog.Logger; + import java.util.Locale; import static org.lwjgl.vulkan.VK10.*; +import static org.lwjgl.vulkan.VK13.vkCmdPipelineBarrier2; public class VulkanUtils { public static final int MAX_IN_FLIGHT = 2; @@ -21,7 +29,31 @@ public class VulkanUtils { } return result; } - + public static void ImageBarrier(MemoryStack MemStack, VkCommandBuffer CommandHandle, long Image, int OldLayout, + int NewLayout, long SrcStage, long DstStage, long SrcAccess, long DstAccess, + int AspectMask){ + var ImageBarrier = VkImageMemoryBarrier2.calloc(1, MemStack) + .sType$Default() + .oldLayout(OldLayout) + .newLayout(NewLayout) + .srcStageMask(SrcStage) + .dstStageMask(DstStage) + .srcAccessMask(SrcAccess) + .dstAccessMask(DstAccess) + .srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .subresourceRange(it -> it + .aspectMask(AspectMask) + .baseMipLevel(0) + .levelCount(VK_REMAINING_MIP_LEVELS) + .baseArrayLayer(0) + .layerCount(VK_REMAINING_ARRAY_LAYERS)) + .image(Image); + VkDependencyInfo DependencyInfo = VkDependencyInfo.calloc(MemStack) + .sType$Default() + .pImageMemoryBarriers(ImageBarrier); + vkCmdPipelineBarrier2(CommandHandle, DependencyInfo); + } public static void vkCheck(int Error, String ErrorMessage) { if (Error != VK_SUCCESS) { String ErrorCode = switch (Error) {