1495 lines
62 KiB
Java
1495 lines
62 KiB
Java
package com.github.hydos.ginger;
|
|
|
|
import static java.util.stream.Collectors.toSet;
|
|
import static org.lwjgl.assimp.Assimp.*;
|
|
import static org.lwjgl.glfw.GLFW.*;
|
|
import static org.lwjgl.glfw.GLFWVulkan.*;
|
|
import static org.lwjgl.stb.STBImage.*;
|
|
import static org.lwjgl.system.MemoryStack.*;
|
|
import static org.lwjgl.vulkan.KHRSurface.*;
|
|
import static org.lwjgl.vulkan.KHRSwapchain.*;
|
|
import static org.lwjgl.vulkan.VK10.*;
|
|
|
|
import java.io.File;
|
|
import java.lang.Math;
|
|
import java.net.*;
|
|
import java.nio.*;
|
|
import java.nio.file.Paths;
|
|
import java.util.*;
|
|
import java.util.stream.*;
|
|
|
|
import org.joml.*;
|
|
import org.lwjgl.PointerBuffer;
|
|
import org.lwjgl.system.*;
|
|
import org.lwjgl.vulkan.*;
|
|
|
|
import com.github.hydos.ginger.engine.common.info.RenderAPI;
|
|
import com.github.hydos.ginger.engine.common.io.Window;
|
|
import com.github.hydos.ginger.engine.vulkan.misc.*;
|
|
import com.github.hydos.ginger.engine.vulkan.misc.VKModelLoader.VKMesh;
|
|
import com.github.hydos.ginger.engine.vulkan.swapchain.VKSwapchainManager;
|
|
|
|
public class VulkanExample
|
|
{
|
|
public static class VulkanDemoGinger2
|
|
{
|
|
private static final int UINT32_MAX = 0xFFFFFFFF;
|
|
private static final long UINT64_MAX = 0xFFFFFFFFFFFFFFFFL;
|
|
private static final int MAX_FRAMES_IN_FLIGHT = 2;
|
|
private static final Set<String> DEVICE_EXTENSIONS = Stream.of(VK_KHR_SWAPCHAIN_EXTENSION_NAME)
|
|
.collect(toSet());
|
|
|
|
public static class QueueFamilyIndices
|
|
{
|
|
// We use Integer to use null as the empty value
|
|
public Integer graphicsFamily;
|
|
public Integer presentFamily;
|
|
|
|
private boolean isComplete()
|
|
{ return graphicsFamily != null && presentFamily != null; }
|
|
|
|
public int[] unique()
|
|
{ return IntStream.of(graphicsFamily, presentFamily).distinct().toArray(); }
|
|
}
|
|
|
|
public static class SwapChainSupportDetails
|
|
{
|
|
public VkSurfaceCapabilitiesKHR capabilities;
|
|
public VkSurfaceFormatKHR.Buffer formats;
|
|
public IntBuffer presentModes;
|
|
}
|
|
|
|
private static class UniformBufferObject
|
|
{
|
|
private static final int SIZEOF = 3 * 16 * Float.BYTES;
|
|
private Matrix4f model;
|
|
private Matrix4f view;
|
|
private Matrix4f proj;
|
|
|
|
public UniformBufferObject()
|
|
{
|
|
model = new Matrix4f();
|
|
view = new Matrix4f();
|
|
proj = new Matrix4f();
|
|
}
|
|
}
|
|
|
|
public static class Vertex
|
|
{
|
|
private static final int SIZEOF = (3 + 3 + 2) * Float.BYTES;
|
|
private static final int OFFSETOF_POS = 0;
|
|
private static final int OFFSETOF_COLOR = 3 * Float.BYTES;
|
|
private static final int OFFSETOF_TEXTCOORDS = (3 + 3) * Float.BYTES;
|
|
private Vector3fc pos;
|
|
private Vector3fc color;
|
|
private Vector2fc texCoords;
|
|
|
|
public Vertex(Vector3fc pos, Vector3fc color, Vector2fc texCoords)
|
|
{
|
|
this.pos = pos;
|
|
this.color = color;
|
|
this.texCoords = texCoords;
|
|
}
|
|
|
|
public static VkVertexInputBindingDescription.Buffer getBindingDescription()
|
|
{
|
|
VkVertexInputBindingDescription.Buffer bindingDescription = VkVertexInputBindingDescription.callocStack(1);
|
|
bindingDescription.binding(0);
|
|
bindingDescription.stride(Vertex.SIZEOF);
|
|
bindingDescription.inputRate(VK_VERTEX_INPUT_RATE_VERTEX);
|
|
return bindingDescription;
|
|
}
|
|
|
|
public static VkVertexInputAttributeDescription.Buffer getAttributeDescriptions()
|
|
{
|
|
VkVertexInputAttributeDescription.Buffer attributeDescriptions = VkVertexInputAttributeDescription.callocStack(3);
|
|
// Position
|
|
VkVertexInputAttributeDescription posDescription = attributeDescriptions.get(0);
|
|
posDescription.binding(0);
|
|
posDescription.location(0);
|
|
posDescription.format(VK_FORMAT_R32G32B32_SFLOAT);
|
|
posDescription.offset(OFFSETOF_POS);
|
|
// Color
|
|
VkVertexInputAttributeDescription colorDescription = attributeDescriptions.get(1);
|
|
colorDescription.binding(0);
|
|
colorDescription.location(1);
|
|
colorDescription.format(VK_FORMAT_R32G32B32_SFLOAT);
|
|
colorDescription.offset(OFFSETOF_COLOR);
|
|
// Texture coordinates
|
|
VkVertexInputAttributeDescription texCoordsDescription = attributeDescriptions.get(2);
|
|
texCoordsDescription.binding(0);
|
|
texCoordsDescription.location(2);
|
|
texCoordsDescription.format(VK_FORMAT_R32G32_SFLOAT);
|
|
texCoordsDescription.offset(OFFSETOF_TEXTCOORDS);
|
|
return attributeDescriptions.rewind();
|
|
}
|
|
}
|
|
// ======= FIELDS ======= //
|
|
|
|
private VkInstance instance;
|
|
public static long surface;
|
|
public static VkPhysicalDevice physicalDevice;
|
|
public static int msaaSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
public static VkDevice device;
|
|
private static VkQueue graphicsQueue;
|
|
private VkQueue presentQueue;
|
|
public static long swapChain;
|
|
public static List<Long> swapChainImages;
|
|
public static int swapChainImageFormat;
|
|
public static VkExtent2D swapChainExtent;
|
|
public static List<Long> swapChainImageViews;
|
|
public static List<Long> swapChainFramebuffers;
|
|
public static long renderPass;
|
|
public static long descriptorPool;
|
|
public static long descriptorSetLayout;
|
|
private static List<Long> descriptorSets;
|
|
public static long pipelineLayout;
|
|
public static long graphicsPipeline;
|
|
public static long commandPool;
|
|
public static long colorImage;
|
|
public static long colorImageMemory;
|
|
public static long colorImageView;
|
|
public static long depthImage;
|
|
public static long depthImageMemory;
|
|
public static long depthImageView;
|
|
private int mipLevels;
|
|
private long textureImage;
|
|
private long textureImageMemory;
|
|
private static long textureImageView;
|
|
private static long textureSampler;
|
|
private Vertex[] vertices;
|
|
public static int[] indices;
|
|
private static long vertexBuffer;
|
|
private long vertexBufferMemory;
|
|
private static long indexBuffer;
|
|
private long indexBufferMemory;
|
|
public static List<Long> uniformBuffers;
|
|
public static List<Long> uniformBuffersMemory;
|
|
public static List<VkCommandBuffer> commandBuffers;
|
|
private List<Frame> inFlightFrames;
|
|
private Map<Integer, Frame> imagesInFlight;
|
|
private int currentFrame;
|
|
boolean framebufferResize;
|
|
// ======= METHODS ======= //
|
|
|
|
public void run()
|
|
{
|
|
initWindow();
|
|
initVulkan();
|
|
mainLoop();
|
|
cleanup();
|
|
}
|
|
|
|
private void initWindow()
|
|
{
|
|
Window.create(1200, 800, "Vulkan Ginger2", 60, RenderAPI.Vulkan);
|
|
glfwSetFramebufferSizeCallback(Window.getWindow(), this::framebufferResizeCallback);
|
|
}
|
|
|
|
private void framebufferResizeCallback(long window, int width, int height)
|
|
{
|
|
// HelloTriangleApplication app = MemoryUtil.memGlobalRefToObject(glfwGetWindowUserPointer(window));
|
|
// app.framebufferResize = true;
|
|
framebufferResize = true;
|
|
}
|
|
|
|
private void initVulkan()
|
|
{
|
|
createInstance();
|
|
createSurface();
|
|
pickPhysicalDevice();
|
|
createLogicalDevice();
|
|
createCommandPool();
|
|
createTextureImage();
|
|
createTextureImageView();
|
|
createTextureSampler();
|
|
loadModel();
|
|
createVertexBuffer();
|
|
createIndexBuffer();
|
|
createDescriptorSetLayout();
|
|
VKSwapchainManager.createSwapChainObjects();
|
|
createSyncObjects();
|
|
}
|
|
|
|
private void mainLoop()
|
|
{
|
|
while (!Window.closed())
|
|
{
|
|
if (Window.shouldRender())
|
|
{ drawFrame(); }
|
|
glfwPollEvents();
|
|
}
|
|
// Wait for the device to complete all operations before release resources
|
|
vkDeviceWaitIdle(device);
|
|
}
|
|
|
|
private void cleanup()
|
|
{
|
|
VKSwapchainManager.cleanupSwapChain();
|
|
vkDestroySampler(device, textureSampler, null);
|
|
vkDestroyImageView(device, textureImageView, null);
|
|
vkDestroyImage(device, textureImage, null);
|
|
vkFreeMemory(device, textureImageMemory, null);
|
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, null);
|
|
vkDestroyBuffer(device, indexBuffer, null);
|
|
vkFreeMemory(device, indexBufferMemory, null);
|
|
vkDestroyBuffer(device, vertexBuffer, null);
|
|
vkFreeMemory(device, vertexBufferMemory, null);
|
|
inFlightFrames.forEach(frame ->
|
|
{
|
|
vkDestroySemaphore(device, frame.renderFinishedSemaphore(), null);
|
|
vkDestroySemaphore(device, frame.imageAvailableSemaphore(), null);
|
|
vkDestroyFence(device, frame.fence(), null);
|
|
});
|
|
inFlightFrames.clear();
|
|
vkDestroyCommandPool(device, commandPool, null);
|
|
vkDestroyDevice(device, null);
|
|
vkDestroySurfaceKHR(instance, surface, null);
|
|
vkDestroyInstance(instance, null);
|
|
glfwDestroyWindow(Window.getWindow());
|
|
glfwTerminate();
|
|
}
|
|
|
|
private void createInstance()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
// Use calloc to initialize the structs with 0s. Otherwise, the program can crash due to random values
|
|
VkApplicationInfo appInfo = VkApplicationInfo.callocStack(stack);
|
|
appInfo.sType(VK_STRUCTURE_TYPE_APPLICATION_INFO);
|
|
appInfo.pApplicationName(stack.UTF8Safe("Hello Triangle"));
|
|
appInfo.applicationVersion(VK_MAKE_VERSION(1, 0, 0));
|
|
appInfo.pEngineName(stack.UTF8Safe("No Engine"));
|
|
appInfo.engineVersion(VK_MAKE_VERSION(1, 0, 0));
|
|
appInfo.apiVersion(VK_API_VERSION_1_0);
|
|
VkInstanceCreateInfo createInfo = VkInstanceCreateInfo.callocStack(stack);
|
|
createInfo.sType(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
|
|
createInfo.pApplicationInfo(appInfo);
|
|
// enabledExtensionCount is implicitly set when you call ppEnabledExtensionNames
|
|
createInfo.ppEnabledExtensionNames(getRequiredExtensions());
|
|
// We need to retrieve the pointer of the created instance
|
|
PointerBuffer instancePtr = stack.mallocPointer(1);
|
|
if (vkCreateInstance(createInfo, null, instancePtr) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create instance"); }
|
|
instance = new VkInstance(instancePtr.get(0), createInfo);
|
|
}
|
|
}
|
|
|
|
private void createSurface()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
LongBuffer pSurface = stack.longs(VK_NULL_HANDLE);
|
|
if (glfwCreateWindowSurface(instance, Window.getWindow(), null, pSurface) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create window surface"); }
|
|
surface = pSurface.get(0);
|
|
}
|
|
}
|
|
|
|
private void pickPhysicalDevice()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
IntBuffer deviceCount = stack.ints(0);
|
|
vkEnumeratePhysicalDevices(instance, deviceCount, null);
|
|
if (deviceCount.get(0) == 0)
|
|
{ throw new RuntimeException("Failed to find GPUs with Vulkan support"); }
|
|
PointerBuffer ppPhysicalDevices = stack.mallocPointer(deviceCount.get(0));
|
|
vkEnumeratePhysicalDevices(instance, deviceCount, ppPhysicalDevices);
|
|
VkPhysicalDevice device = null;
|
|
for (int i = 0; i < ppPhysicalDevices.capacity(); i++)
|
|
{
|
|
device = new VkPhysicalDevice(ppPhysicalDevices.get(i), instance);
|
|
if (isDeviceSuitable(device))
|
|
{ break; }
|
|
}
|
|
if (device == null)
|
|
{ throw new RuntimeException("Failed to find a suitable GPU"); }
|
|
physicalDevice = device;
|
|
msaaSamples = getMaxUsableSampleCount();
|
|
}
|
|
}
|
|
|
|
private void createLogicalDevice()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
|
|
int[] uniqueQueueFamilies = indices.unique();
|
|
VkDeviceQueueCreateInfo.Buffer queueCreateInfos = VkDeviceQueueCreateInfo.callocStack(uniqueQueueFamilies.length, stack);
|
|
for (int i = 0; i < uniqueQueueFamilies.length; i++)
|
|
{
|
|
VkDeviceQueueCreateInfo queueCreateInfo = queueCreateInfos.get(i);
|
|
queueCreateInfo.sType(VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO);
|
|
queueCreateInfo.queueFamilyIndex(uniqueQueueFamilies[i]);
|
|
queueCreateInfo.pQueuePriorities(stack.floats(1.0f));
|
|
}
|
|
VkPhysicalDeviceFeatures deviceFeatures = VkPhysicalDeviceFeatures.callocStack(stack);
|
|
deviceFeatures.samplerAnisotropy(true);
|
|
deviceFeatures.sampleRateShading(true); // Enable sample shading feature for the device
|
|
VkDeviceCreateInfo createInfo = VkDeviceCreateInfo.callocStack(stack);
|
|
createInfo.sType(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO);
|
|
createInfo.pQueueCreateInfos(queueCreateInfos);
|
|
// queueCreateInfoCount is automatically set
|
|
createInfo.pEnabledFeatures(deviceFeatures);
|
|
createInfo.ppEnabledExtensionNames(asPointerBuffer(DEVICE_EXTENSIONS));
|
|
PointerBuffer pDevice = stack.pointers(VK_NULL_HANDLE);
|
|
if (vkCreateDevice(physicalDevice, createInfo, null, pDevice) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create logical device"); }
|
|
device = new VkDevice(pDevice.get(0), physicalDevice, createInfo);
|
|
PointerBuffer pQueue = stack.pointers(VK_NULL_HANDLE);
|
|
vkGetDeviceQueue(device, indices.graphicsFamily, 0, pQueue);
|
|
graphicsQueue = new VkQueue(pQueue.get(0), device);
|
|
vkGetDeviceQueue(device, indices.presentFamily, 0, pQueue);
|
|
presentQueue = new VkQueue(pQueue.get(0), device);
|
|
}
|
|
}
|
|
|
|
public static void createImageViews()
|
|
{
|
|
swapChainImageViews = new ArrayList<>(swapChainImages.size());
|
|
for (long swapChainImage : swapChainImages)
|
|
{ swapChainImageViews.add(createImageView(swapChainImage, swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1)); }
|
|
}
|
|
|
|
public static void createRenderPass()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkAttachmentDescription.Buffer attachments = VkAttachmentDescription.callocStack(3, stack);
|
|
VkAttachmentReference.Buffer attachmentRefs = VkAttachmentReference.callocStack(3, stack);
|
|
// Color attachments
|
|
// MSAA Image
|
|
VkAttachmentDescription colorAttachment = attachments.get(0);
|
|
colorAttachment.format(swapChainImageFormat);
|
|
colorAttachment.samples(msaaSamples);
|
|
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_COLOR_ATTACHMENT_OPTIMAL);
|
|
VkAttachmentReference colorAttachmentRef = attachmentRefs.get(0);
|
|
colorAttachmentRef.attachment(0);
|
|
colorAttachmentRef.layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
// Present Image
|
|
VkAttachmentDescription colorAttachmentResolve = attachments.get(2);
|
|
colorAttachmentResolve.format(swapChainImageFormat);
|
|
colorAttachmentResolve.samples(VK_SAMPLE_COUNT_1_BIT);
|
|
colorAttachmentResolve.loadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
colorAttachmentResolve.storeOp(VK_ATTACHMENT_STORE_OP_STORE);
|
|
colorAttachmentResolve.stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
colorAttachmentResolve.stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
|
|
colorAttachmentResolve.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED);
|
|
colorAttachmentResolve.finalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
|
VkAttachmentReference colorAttachmentResolveRef = attachmentRefs.get(2);
|
|
colorAttachmentResolveRef.attachment(2);
|
|
colorAttachmentResolveRef.layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
// Depth-Stencil attachments
|
|
VkAttachmentDescription depthAttachment = attachments.get(1);
|
|
depthAttachment.format(findDepthFormat());
|
|
depthAttachment.samples(msaaSamples);
|
|
depthAttachment.loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR);
|
|
depthAttachment.storeOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
|
|
depthAttachment.stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
depthAttachment.stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
|
|
depthAttachment.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED);
|
|
depthAttachment.finalLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
|
VkAttachmentReference depthAttachmentRef = attachmentRefs.get(1);
|
|
depthAttachmentRef.attachment(1);
|
|
depthAttachmentRef.layout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
|
VkSubpassDescription.Buffer subpass = VkSubpassDescription.callocStack(1, stack);
|
|
subpass.pipelineBindPoint(VK_PIPELINE_BIND_POINT_GRAPHICS);
|
|
subpass.colorAttachmentCount(1);
|
|
subpass.pColorAttachments(VkAttachmentReference.callocStack(1, stack).put(0, colorAttachmentRef));
|
|
subpass.pDepthStencilAttachment(depthAttachmentRef);
|
|
subpass.pResolveAttachments(VkAttachmentReference.callocStack(1, stack).put(0, colorAttachmentResolveRef));
|
|
VkSubpassDependency.Buffer dependency = VkSubpassDependency.callocStack(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_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
|
|
VkRenderPassCreateInfo renderPassInfo = VkRenderPassCreateInfo.callocStack(stack);
|
|
renderPassInfo.sType(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO);
|
|
renderPassInfo.pAttachments(attachments);
|
|
renderPassInfo.pSubpasses(subpass);
|
|
renderPassInfo.pDependencies(dependency);
|
|
LongBuffer pRenderPass = stack.mallocLong(1);
|
|
if (vkCreateRenderPass(device, renderPassInfo, null, pRenderPass) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create render pass"); }
|
|
renderPass = pRenderPass.get(0);
|
|
}
|
|
}
|
|
|
|
private void createDescriptorSetLayout()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkDescriptorSetLayoutBinding.Buffer bindings = VkDescriptorSetLayoutBinding.callocStack(2, stack);
|
|
VkDescriptorSetLayoutBinding uboLayoutBinding = bindings.get(0);
|
|
uboLayoutBinding.binding(0);
|
|
uboLayoutBinding.descriptorCount(1);
|
|
uboLayoutBinding.descriptorType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
|
uboLayoutBinding.pImmutableSamplers(null);
|
|
uboLayoutBinding.stageFlags(VK_SHADER_STAGE_VERTEX_BIT);
|
|
VkDescriptorSetLayoutBinding samplerLayoutBinding = bindings.get(1);
|
|
samplerLayoutBinding.binding(1);
|
|
samplerLayoutBinding.descriptorCount(1);
|
|
samplerLayoutBinding.descriptorType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
|
samplerLayoutBinding.pImmutableSamplers(null);
|
|
samplerLayoutBinding.stageFlags(VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
VkDescriptorSetLayoutCreateInfo layoutInfo = VkDescriptorSetLayoutCreateInfo.callocStack(stack);
|
|
layoutInfo.sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
|
|
layoutInfo.pBindings(bindings);
|
|
LongBuffer pDescriptorSetLayout = stack.mallocLong(1);
|
|
if (vkCreateDescriptorSetLayout(device, layoutInfo, null, pDescriptorSetLayout) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create descriptor set layout"); }
|
|
descriptorSetLayout = pDescriptorSetLayout.get(0);
|
|
}
|
|
}
|
|
|
|
public static void createFramebuffers()
|
|
{
|
|
swapChainFramebuffers = new ArrayList<>(swapChainImageViews.size());
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
LongBuffer attachments = stack.longs(colorImageView, depthImageView, VK_NULL_HANDLE);
|
|
LongBuffer pFramebuffer = stack.mallocLong(1);
|
|
// Lets allocate the create info struct once and just update the pAttachments field each iteration
|
|
VkFramebufferCreateInfo framebufferInfo = VkFramebufferCreateInfo.callocStack(stack);
|
|
framebufferInfo.sType(VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO);
|
|
framebufferInfo.renderPass(renderPass);
|
|
framebufferInfo.width(swapChainExtent.width());
|
|
framebufferInfo.height(swapChainExtent.height());
|
|
framebufferInfo.layers(1);
|
|
for (long imageView : swapChainImageViews)
|
|
{
|
|
attachments.put(2, imageView);
|
|
framebufferInfo.pAttachments(attachments);
|
|
if (vkCreateFramebuffer(device, framebufferInfo, null, pFramebuffer) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create framebuffer"); }
|
|
swapChainFramebuffers.add(pFramebuffer.get(0));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void createCommandPool()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
|
|
VkCommandPoolCreateInfo poolInfo = VkCommandPoolCreateInfo.callocStack(stack);
|
|
poolInfo.sType(VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO);
|
|
poolInfo.queueFamilyIndex(queueFamilyIndices.graphicsFamily);
|
|
LongBuffer pCommandPool = stack.mallocLong(1);
|
|
if (vkCreateCommandPool(device, poolInfo, null, pCommandPool) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create command pool"); }
|
|
commandPool = pCommandPool.get(0);
|
|
}
|
|
}
|
|
|
|
public static void createColorResources()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
LongBuffer pColorImage = stack.mallocLong(1);
|
|
LongBuffer pColorImageMemory = stack.mallocLong(1);
|
|
createImage(swapChainExtent.width(), swapChainExtent.height(),
|
|
1,
|
|
msaaSamples,
|
|
swapChainImageFormat,
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
pColorImage,
|
|
pColorImageMemory);
|
|
colorImage = pColorImage.get(0);
|
|
colorImageMemory = pColorImageMemory.get(0);
|
|
colorImageView = createImageView(colorImage, swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
|
|
transitionImageLayout(colorImage, swapChainImageFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1);
|
|
}
|
|
}
|
|
|
|
public static void createDepthResources()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
int depthFormat = findDepthFormat();
|
|
LongBuffer pDepthImage = stack.mallocLong(1);
|
|
LongBuffer pDepthImageMemory = stack.mallocLong(1);
|
|
createImage(
|
|
swapChainExtent.width(), swapChainExtent.height(),
|
|
1,
|
|
msaaSamples,
|
|
depthFormat,
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
pDepthImage,
|
|
pDepthImageMemory);
|
|
depthImage = pDepthImage.get(0);
|
|
depthImageMemory = pDepthImageMemory.get(0);
|
|
depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
|
|
// Explicitly transitioning the depth image
|
|
transitionImageLayout(depthImage, depthFormat,
|
|
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
1);
|
|
}
|
|
}
|
|
|
|
private static int findSupportedFormat(IntBuffer formatCandidates, int tiling, int features)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkFormatProperties props = VkFormatProperties.callocStack(stack);
|
|
for (int i = 0; i < formatCandidates.capacity(); ++i)
|
|
{
|
|
int format = formatCandidates.get(i);
|
|
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, props);
|
|
if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures() & features) == features)
|
|
{
|
|
return format;
|
|
}
|
|
else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures() & features) == features)
|
|
{ return format; }
|
|
}
|
|
}
|
|
throw new RuntimeException("Failed to find supported format");
|
|
}
|
|
|
|
private static int findDepthFormat()
|
|
{ return findSupportedFormat(
|
|
stackGet().ints(VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT),
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); }
|
|
|
|
private static boolean hasStencilComponent(int format)
|
|
{ return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; }
|
|
|
|
private double log2(double n)
|
|
{ return Math.log(n) / Math.log(2); }
|
|
|
|
private void createTextureImage()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
String filename = Paths.get(new URI(ClassLoader.getSystemClassLoader().getResource("textures/chalet.jpg").toExternalForm())).toString();
|
|
IntBuffer pWidth = stack.mallocInt(1);
|
|
IntBuffer pHeight = stack.mallocInt(1);
|
|
IntBuffer pChannels = stack.mallocInt(1);
|
|
ByteBuffer pixels = stbi_load(filename, pWidth, pHeight, pChannels, STBI_rgb_alpha);
|
|
long imageSize = pWidth.get(0) * pHeight.get(0) * 4; // pChannels.get(0);
|
|
mipLevels = (int) Math.floor(log2(Math.max(pWidth.get(0), pHeight.get(0)))) + 1;
|
|
if (pixels == null)
|
|
{ throw new RuntimeException("Failed to load texture image " + filename); }
|
|
LongBuffer pStagingBuffer = stack.mallocLong(1);
|
|
LongBuffer pStagingBufferMemory = stack.mallocLong(1);
|
|
createBuffer(imageSize,
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
pStagingBuffer,
|
|
pStagingBufferMemory);
|
|
PointerBuffer data = stack.mallocPointer(1);
|
|
vkMapMemory(device, pStagingBufferMemory.get(0), 0, imageSize, 0, data);
|
|
{
|
|
memcpy(data.getByteBuffer(0, (int) imageSize), pixels, imageSize);
|
|
}
|
|
vkUnmapMemory(device, pStagingBufferMemory.get(0));
|
|
stbi_image_free(pixels);
|
|
LongBuffer pTextureImage = stack.mallocLong(1);
|
|
LongBuffer pTextureImageMemory = stack.mallocLong(1);
|
|
createImage(pWidth.get(0), pHeight.get(0),
|
|
mipLevels,
|
|
VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
pTextureImage,
|
|
pTextureImageMemory);
|
|
textureImage = pTextureImage.get(0);
|
|
textureImageMemory = pTextureImageMemory.get(0);
|
|
transitionImageLayout(textureImage,
|
|
VK_FORMAT_R8G8B8A8_SRGB,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
mipLevels);
|
|
copyBufferToImage(pStagingBuffer.get(0), textureImage, pWidth.get(0), pHeight.get(0));
|
|
// Transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating mipmaps
|
|
generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, pWidth.get(0), pHeight.get(0), mipLevels);
|
|
vkDestroyBuffer(device, pStagingBuffer.get(0), null);
|
|
vkFreeMemory(device, pStagingBufferMemory.get(0), null);
|
|
}
|
|
catch (URISyntaxException e)
|
|
{
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private void generateMipmaps(long image, int imageFormat, int width, int height, int mipLevels)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
// Check if image format supports linear blitting
|
|
VkFormatProperties formatProperties = VkFormatProperties.mallocStack(stack);
|
|
vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, formatProperties);
|
|
if ((formatProperties.optimalTilingFeatures() & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) == 0)
|
|
{ throw new RuntimeException("Texture image format does not support linear blitting"); }
|
|
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
|
|
VkImageMemoryBarrier.Buffer barrier = VkImageMemoryBarrier.callocStack(1, stack);
|
|
barrier.sType(VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER);
|
|
barrier.image(image);
|
|
barrier.srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
|
|
barrier.dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
|
|
barrier.dstAccessMask(VK_QUEUE_FAMILY_IGNORED);
|
|
barrier.subresourceRange().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
|
|
barrier.subresourceRange().baseArrayLayer(0);
|
|
barrier.subresourceRange().layerCount(1);
|
|
barrier.subresourceRange().levelCount(1);
|
|
int mipWidth = width;
|
|
int mipHeight = height;
|
|
for (int i = 1; i < mipLevels; i++)
|
|
{
|
|
barrier.subresourceRange().baseMipLevel(i - 1);
|
|
barrier.oldLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
barrier.newLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
barrier.srcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT);
|
|
barrier.dstAccessMask(VK_ACCESS_TRANSFER_READ_BIT);
|
|
vkCmdPipelineBarrier(commandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
|
null,
|
|
null,
|
|
barrier);
|
|
VkImageBlit.Buffer blit = VkImageBlit.callocStack(1, stack);
|
|
blit.srcOffsets(0).set(0, 0, 0);
|
|
blit.srcOffsets(1).set(mipWidth, mipHeight, 1);
|
|
blit.srcSubresource().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
|
|
blit.srcSubresource().mipLevel(i - 1);
|
|
blit.srcSubresource().baseArrayLayer(0);
|
|
blit.srcSubresource().layerCount(1);
|
|
blit.dstOffsets(0).set(0, 0, 0);
|
|
blit.dstOffsets(1).set(mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1);
|
|
blit.dstSubresource().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
|
|
blit.dstSubresource().mipLevel(i);
|
|
blit.dstSubresource().baseArrayLayer(0);
|
|
blit.dstSubresource().layerCount(1);
|
|
vkCmdBlitImage(commandBuffer,
|
|
image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
blit,
|
|
VK_FILTER_LINEAR);
|
|
barrier.oldLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
barrier.newLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
barrier.srcAccessMask(VK_ACCESS_TRANSFER_READ_BIT);
|
|
barrier.dstAccessMask(VK_ACCESS_SHADER_READ_BIT);
|
|
vkCmdPipelineBarrier(commandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
|
|
null,
|
|
null,
|
|
barrier);
|
|
if (mipWidth > 1)
|
|
{ mipWidth /= 2; }
|
|
if (mipHeight > 1)
|
|
{ mipHeight /= 2; }
|
|
}
|
|
barrier.subresourceRange().baseMipLevel(mipLevels - 1);
|
|
barrier.oldLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
barrier.newLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
barrier.srcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT);
|
|
barrier.dstAccessMask(VK_ACCESS_SHADER_READ_BIT);
|
|
vkCmdPipelineBarrier(commandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
|
|
null,
|
|
null,
|
|
barrier);
|
|
endSingleTimeCommands(commandBuffer);
|
|
}
|
|
}
|
|
|
|
private int getMaxUsableSampleCount()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkPhysicalDeviceProperties physicalDeviceProperties = VkPhysicalDeviceProperties.mallocStack(stack);
|
|
vkGetPhysicalDeviceProperties(physicalDevice, physicalDeviceProperties);
|
|
int sampleCountFlags = physicalDeviceProperties.limits().framebufferColorSampleCounts()
|
|
& physicalDeviceProperties.limits().framebufferDepthSampleCounts();
|
|
if ((sampleCountFlags & VK_SAMPLE_COUNT_64_BIT) != 0)
|
|
{ return VK_SAMPLE_COUNT_64_BIT; }
|
|
if ((sampleCountFlags & VK_SAMPLE_COUNT_32_BIT) != 0)
|
|
{ return VK_SAMPLE_COUNT_32_BIT; }
|
|
if ((sampleCountFlags & VK_SAMPLE_COUNT_16_BIT) != 0)
|
|
{ return VK_SAMPLE_COUNT_16_BIT; }
|
|
if ((sampleCountFlags & VK_SAMPLE_COUNT_8_BIT) != 0)
|
|
{ return VK_SAMPLE_COUNT_8_BIT; }
|
|
if ((sampleCountFlags & VK_SAMPLE_COUNT_4_BIT) != 0)
|
|
{ return VK_SAMPLE_COUNT_4_BIT; }
|
|
if ((sampleCountFlags & VK_SAMPLE_COUNT_2_BIT) != 0)
|
|
{ return VK_SAMPLE_COUNT_2_BIT; }
|
|
return VK_SAMPLE_COUNT_1_BIT;
|
|
}
|
|
}
|
|
|
|
private void createTextureImageView()
|
|
{ textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels); }
|
|
|
|
private void createTextureSampler()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkSamplerCreateInfo samplerInfo = VkSamplerCreateInfo.callocStack(stack);
|
|
samplerInfo.sType(VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO);
|
|
samplerInfo.magFilter(VK_FILTER_LINEAR);
|
|
samplerInfo.minFilter(VK_FILTER_LINEAR);
|
|
samplerInfo.addressModeU(VK_SAMPLER_ADDRESS_MODE_REPEAT);
|
|
samplerInfo.addressModeV(VK_SAMPLER_ADDRESS_MODE_REPEAT);
|
|
samplerInfo.addressModeW(VK_SAMPLER_ADDRESS_MODE_REPEAT);
|
|
samplerInfo.anisotropyEnable(true);
|
|
samplerInfo.maxAnisotropy(16.0f);
|
|
samplerInfo.borderColor(VK_BORDER_COLOR_INT_OPAQUE_BLACK);
|
|
samplerInfo.unnormalizedCoordinates(false);
|
|
samplerInfo.compareEnable(false);
|
|
samplerInfo.compareOp(VK_COMPARE_OP_ALWAYS);
|
|
samplerInfo.mipmapMode(VK_SAMPLER_MIPMAP_MODE_LINEAR);
|
|
samplerInfo.minLod(0); // Optional
|
|
samplerInfo.maxLod(mipLevels);
|
|
samplerInfo.mipLodBias(0); // Optional
|
|
LongBuffer pTextureSampler = stack.mallocLong(1);
|
|
if (vkCreateSampler(device, samplerInfo, null, pTextureSampler) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create texture sampler"); }
|
|
textureSampler = pTextureSampler.get(0);
|
|
}
|
|
}
|
|
|
|
private static long createImageView(long image, int format, int aspectFlags, int mipLevels)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkImageViewCreateInfo viewInfo = VkImageViewCreateInfo.callocStack(stack);
|
|
viewInfo.sType(VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO);
|
|
viewInfo.image(image);
|
|
viewInfo.viewType(VK_IMAGE_VIEW_TYPE_2D);
|
|
viewInfo.format(format);
|
|
viewInfo.subresourceRange().aspectMask(aspectFlags);
|
|
viewInfo.subresourceRange().baseMipLevel(0);
|
|
viewInfo.subresourceRange().levelCount(mipLevels);
|
|
viewInfo.subresourceRange().baseArrayLayer(0);
|
|
viewInfo.subresourceRange().layerCount(1);
|
|
LongBuffer pImageView = stack.mallocLong(1);
|
|
if (vkCreateImageView(device, viewInfo, null, pImageView) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create texture image view"); }
|
|
return pImageView.get(0);
|
|
}
|
|
}
|
|
|
|
private static void createImage(int width, int height, int mipLevels, int numSamples, int format, int tiling, int usage, int memProperties,
|
|
LongBuffer pTextureImage, LongBuffer pTextureImageMemory)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkImageCreateInfo imageInfo = VkImageCreateInfo.callocStack(stack);
|
|
imageInfo.sType(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO);
|
|
imageInfo.imageType(VK_IMAGE_TYPE_2D);
|
|
imageInfo.extent().width(width);
|
|
imageInfo.extent().height(height);
|
|
imageInfo.extent().depth(1);
|
|
imageInfo.mipLevels(mipLevels);
|
|
imageInfo.arrayLayers(1);
|
|
imageInfo.format(format);
|
|
imageInfo.tiling(tiling);
|
|
imageInfo.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED);
|
|
imageInfo.usage(usage);
|
|
imageInfo.samples(numSamples);
|
|
imageInfo.sharingMode(VK_SHARING_MODE_EXCLUSIVE);
|
|
if (vkCreateImage(device, imageInfo, null, pTextureImage) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create image"); }
|
|
VkMemoryRequirements memRequirements = VkMemoryRequirements.mallocStack(stack);
|
|
vkGetImageMemoryRequirements(device, pTextureImage.get(0), memRequirements);
|
|
VkMemoryAllocateInfo allocInfo = VkMemoryAllocateInfo.callocStack(stack);
|
|
allocInfo.sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO);
|
|
allocInfo.allocationSize(memRequirements.size());
|
|
allocInfo.memoryTypeIndex(findMemoryType(memRequirements.memoryTypeBits(), memProperties));
|
|
if (vkAllocateMemory(device, allocInfo, null, pTextureImageMemory) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to allocate image memory"); }
|
|
vkBindImageMemory(device, pTextureImage.get(0), pTextureImageMemory.get(0), 0);
|
|
}
|
|
}
|
|
|
|
private static void transitionImageLayout(long image, int format, int oldLayout, int newLayout, int mipLevels)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkImageMemoryBarrier.Buffer barrier = VkImageMemoryBarrier.callocStack(1, stack);
|
|
barrier.sType(VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER);
|
|
barrier.oldLayout(oldLayout);
|
|
barrier.newLayout(newLayout);
|
|
barrier.srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
|
|
barrier.dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
|
|
barrier.image(image);
|
|
barrier.subresourceRange().baseMipLevel(0);
|
|
barrier.subresourceRange().levelCount(mipLevels);
|
|
barrier.subresourceRange().baseArrayLayer(0);
|
|
barrier.subresourceRange().layerCount(1);
|
|
if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
|
|
{
|
|
barrier.subresourceRange().aspectMask(VK_IMAGE_ASPECT_DEPTH_BIT);
|
|
if (hasStencilComponent(format))
|
|
{ barrier.subresourceRange().aspectMask(
|
|
barrier.subresourceRange().aspectMask() | VK_IMAGE_ASPECT_STENCIL_BIT); }
|
|
}
|
|
else
|
|
{
|
|
barrier.subresourceRange().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
|
|
}
|
|
int sourceStage;
|
|
int destinationStage;
|
|
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
{
|
|
barrier.srcAccessMask(0);
|
|
barrier.dstAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT);
|
|
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
|
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
}
|
|
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
|
{
|
|
barrier.srcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT);
|
|
barrier.dstAccessMask(VK_ACCESS_SHADER_READ_BIT);
|
|
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
}
|
|
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
|
|
{
|
|
barrier.srcAccessMask(0);
|
|
barrier.dstAccessMask(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT);
|
|
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
|
destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
|
}
|
|
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
|
|
{
|
|
barrier.srcAccessMask(0);
|
|
barrier.dstAccessMask(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
|
|
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
|
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
}
|
|
else
|
|
{
|
|
throw new IllegalArgumentException("Unsupported layout transition");
|
|
}
|
|
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
|
|
vkCmdPipelineBarrier(commandBuffer,
|
|
sourceStage, destinationStage,
|
|
0,
|
|
null,
|
|
null,
|
|
barrier);
|
|
endSingleTimeCommands(commandBuffer);
|
|
}
|
|
}
|
|
|
|
private void copyBufferToImage(long buffer, long image, int width, int height)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
|
|
VkBufferImageCopy.Buffer region = VkBufferImageCopy.callocStack(1, stack);
|
|
region.bufferOffset(0);
|
|
region.bufferRowLength(0); // Tightly packed
|
|
region.bufferImageHeight(0); // Tightly packed
|
|
region.imageSubresource().aspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
|
|
region.imageSubresource().mipLevel(0);
|
|
region.imageSubresource().baseArrayLayer(0);
|
|
region.imageSubresource().layerCount(1);
|
|
region.imageOffset().set(0, 0, 0);
|
|
region.imageExtent(VkExtent3D.callocStack(stack).set(width, height, 1));
|
|
vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region);
|
|
endSingleTimeCommands(commandBuffer);
|
|
}
|
|
}
|
|
|
|
private void memcpy(ByteBuffer dst, ByteBuffer src, long size)
|
|
{
|
|
src.limit((int) size);
|
|
dst.put(src);
|
|
src.limit(src.capacity()).rewind();
|
|
}
|
|
|
|
private void loadModel()
|
|
{
|
|
File modelFile = new File(ClassLoader.getSystemClassLoader().getResource("models/chalet.obj").getFile());
|
|
VKMesh model = VKModelLoader.loadModel(modelFile, aiProcess_FlipUVs | aiProcess_DropNormals);
|
|
final int vertexCount = model.positions.size();
|
|
vertices = new Vertex[vertexCount];
|
|
final Vector3fc color = new Vector3f(1.0f, 1.0f, 1.0f);
|
|
for (int i = 0; i < vertexCount; i++)
|
|
{ vertices[i] = new Vertex(
|
|
model.positions.get(i),
|
|
color,
|
|
model.texCoords.get(i)); }
|
|
indices = new int[model.indices.size()];
|
|
for (int i = 0; i < indices.length; i++)
|
|
{ indices[i] = model.indices.get(i); }
|
|
}
|
|
|
|
private void createVertexBuffer()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
long bufferSize = Vertex.SIZEOF * vertices.length;
|
|
LongBuffer pBuffer = stack.mallocLong(1);
|
|
LongBuffer pBufferMemory = stack.mallocLong(1);
|
|
createBuffer(bufferSize,
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
pBuffer,
|
|
pBufferMemory);
|
|
long stagingBuffer = pBuffer.get(0);
|
|
long stagingBufferMemory = pBufferMemory.get(0);
|
|
PointerBuffer data = stack.mallocPointer(1);
|
|
vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, data);
|
|
{
|
|
memcpy(data.getByteBuffer(0, (int) bufferSize), vertices);
|
|
}
|
|
vkUnmapMemory(device, stagingBufferMemory);
|
|
createBuffer(bufferSize,
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
|
VK_MEMORY_HEAP_DEVICE_LOCAL_BIT,
|
|
pBuffer,
|
|
pBufferMemory);
|
|
vertexBuffer = pBuffer.get(0);
|
|
vertexBufferMemory = pBufferMemory.get(0);
|
|
copyBuffer(stagingBuffer, vertexBuffer, bufferSize);
|
|
vkDestroyBuffer(device, stagingBuffer, null);
|
|
vkFreeMemory(device, stagingBufferMemory, null);
|
|
}
|
|
}
|
|
|
|
private void createIndexBuffer()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
long bufferSize = Integer.BYTES * indices.length;
|
|
LongBuffer pBuffer = stack.mallocLong(1);
|
|
LongBuffer pBufferMemory = stack.mallocLong(1);
|
|
createBuffer(bufferSize,
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
pBuffer,
|
|
pBufferMemory);
|
|
long stagingBuffer = pBuffer.get(0);
|
|
long stagingBufferMemory = pBufferMemory.get(0);
|
|
PointerBuffer data = stack.mallocPointer(1);
|
|
vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, data);
|
|
{
|
|
memcpy(data.getByteBuffer(0, (int) bufferSize), indices);
|
|
}
|
|
vkUnmapMemory(device, stagingBufferMemory);
|
|
createBuffer(bufferSize,
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
VK_MEMORY_HEAP_DEVICE_LOCAL_BIT,
|
|
pBuffer,
|
|
pBufferMemory);
|
|
indexBuffer = pBuffer.get(0);
|
|
indexBufferMemory = pBufferMemory.get(0);
|
|
copyBuffer(stagingBuffer, indexBuffer, bufferSize);
|
|
vkDestroyBuffer(device, stagingBuffer, null);
|
|
vkFreeMemory(device, stagingBufferMemory, null);
|
|
}
|
|
}
|
|
|
|
public static void createUniformBuffers()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
uniformBuffers = new ArrayList<>(swapChainImages.size());
|
|
uniformBuffersMemory = new ArrayList<>(swapChainImages.size());
|
|
LongBuffer pBuffer = stack.mallocLong(1);
|
|
LongBuffer pBufferMemory = stack.mallocLong(1);
|
|
for (int i = 0; i < swapChainImages.size(); i++)
|
|
{
|
|
createBuffer(UniformBufferObject.SIZEOF,
|
|
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
|
pBuffer,
|
|
pBufferMemory);
|
|
uniformBuffers.add(pBuffer.get(0));
|
|
uniformBuffersMemory.add(pBufferMemory.get(0));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void createDescriptorPool()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkDescriptorPoolSize.Buffer poolSizes = VkDescriptorPoolSize.callocStack(2, stack);
|
|
VkDescriptorPoolSize uniformBufferPoolSize = poolSizes.get(0);
|
|
uniformBufferPoolSize.type(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
|
uniformBufferPoolSize.descriptorCount(swapChainImages.size());
|
|
VkDescriptorPoolSize textureSamplerPoolSize = poolSizes.get(1);
|
|
textureSamplerPoolSize.type(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
|
textureSamplerPoolSize.descriptorCount(swapChainImages.size());
|
|
VkDescriptorPoolCreateInfo poolInfo = VkDescriptorPoolCreateInfo.callocStack(stack);
|
|
poolInfo.sType(VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO);
|
|
poolInfo.pPoolSizes(poolSizes);
|
|
poolInfo.maxSets(swapChainImages.size());
|
|
LongBuffer pDescriptorPool = stack.mallocLong(1);
|
|
if (vkCreateDescriptorPool(device, poolInfo, null, pDescriptorPool) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create descriptor pool"); }
|
|
descriptorPool = pDescriptorPool.get(0);
|
|
}
|
|
}
|
|
|
|
public static void createDescriptorSets()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
LongBuffer layouts = stack.mallocLong(swapChainImages.size());
|
|
for (int i = 0; i < layouts.capacity(); i++)
|
|
{ layouts.put(i, descriptorSetLayout); }
|
|
VkDescriptorSetAllocateInfo allocInfo = VkDescriptorSetAllocateInfo.callocStack(stack);
|
|
allocInfo.sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO);
|
|
allocInfo.descriptorPool(descriptorPool);
|
|
allocInfo.pSetLayouts(layouts);
|
|
LongBuffer pDescriptorSets = stack.mallocLong(swapChainImages.size());
|
|
if (vkAllocateDescriptorSets(device, allocInfo, pDescriptorSets) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to allocate descriptor sets"); }
|
|
descriptorSets = new ArrayList<>(pDescriptorSets.capacity());
|
|
VkDescriptorBufferInfo.Buffer bufferInfo = VkDescriptorBufferInfo.callocStack(1, stack);
|
|
bufferInfo.offset(0);
|
|
bufferInfo.range(UniformBufferObject.SIZEOF);
|
|
VkDescriptorImageInfo.Buffer imageInfo = VkDescriptorImageInfo.callocStack(1, stack);
|
|
imageInfo.imageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
imageInfo.imageView(textureImageView);
|
|
imageInfo.sampler(textureSampler);
|
|
VkWriteDescriptorSet.Buffer descriptorWrites = VkWriteDescriptorSet.callocStack(2, stack);
|
|
VkWriteDescriptorSet uboDescriptorWrite = descriptorWrites.get(0);
|
|
uboDescriptorWrite.sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET);
|
|
uboDescriptorWrite.dstBinding(0);
|
|
uboDescriptorWrite.dstArrayElement(0);
|
|
uboDescriptorWrite.descriptorType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
|
uboDescriptorWrite.descriptorCount(1);
|
|
uboDescriptorWrite.pBufferInfo(bufferInfo);
|
|
VkWriteDescriptorSet samplerDescriptorWrite = descriptorWrites.get(1);
|
|
samplerDescriptorWrite.sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET);
|
|
samplerDescriptorWrite.dstBinding(1);
|
|
samplerDescriptorWrite.dstArrayElement(0);
|
|
samplerDescriptorWrite.descriptorType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
|
samplerDescriptorWrite.descriptorCount(1);
|
|
samplerDescriptorWrite.pImageInfo(imageInfo);
|
|
for (int i = 0; i < pDescriptorSets.capacity(); i++)
|
|
{
|
|
long descriptorSet = pDescriptorSets.get(i);
|
|
bufferInfo.buffer(uniformBuffers.get(i));
|
|
uboDescriptorWrite.dstSet(descriptorSet);
|
|
samplerDescriptorWrite.dstSet(descriptorSet);
|
|
vkUpdateDescriptorSets(device, descriptorWrites, null);
|
|
descriptorSets.add(descriptorSet);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void createBuffer(long size, int usage, int properties, LongBuffer pBuffer, LongBuffer pBufferMemory)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkBufferCreateInfo bufferInfo = VkBufferCreateInfo.callocStack(stack);
|
|
bufferInfo.sType(VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO);
|
|
bufferInfo.size(size);
|
|
bufferInfo.usage(usage);
|
|
bufferInfo.sharingMode(VK_SHARING_MODE_EXCLUSIVE);
|
|
if (vkCreateBuffer(device, bufferInfo, null, pBuffer) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create vertex buffer"); }
|
|
VkMemoryRequirements memRequirements = VkMemoryRequirements.mallocStack(stack);
|
|
vkGetBufferMemoryRequirements(device, pBuffer.get(0), memRequirements);
|
|
VkMemoryAllocateInfo allocInfo = VkMemoryAllocateInfo.callocStack(stack);
|
|
allocInfo.sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO);
|
|
allocInfo.allocationSize(memRequirements.size());
|
|
allocInfo.memoryTypeIndex(findMemoryType(memRequirements.memoryTypeBits(), properties));
|
|
if (vkAllocateMemory(device, allocInfo, null, pBufferMemory) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to allocate vertex buffer memory"); }
|
|
vkBindBufferMemory(device, pBuffer.get(0), pBufferMemory.get(0), 0);
|
|
}
|
|
}
|
|
|
|
private static VkCommandBuffer beginSingleTimeCommands()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkCommandBufferAllocateInfo allocInfo = VkCommandBufferAllocateInfo.callocStack(stack);
|
|
allocInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO);
|
|
allocInfo.level(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
|
|
allocInfo.commandPool(commandPool);
|
|
allocInfo.commandBufferCount(1);
|
|
PointerBuffer pCommandBuffer = stack.mallocPointer(1);
|
|
vkAllocateCommandBuffers(device, allocInfo, pCommandBuffer);
|
|
VkCommandBuffer commandBuffer = new VkCommandBuffer(pCommandBuffer.get(0), device);
|
|
VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.callocStack(stack);
|
|
beginInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
|
|
beginInfo.flags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
|
|
vkBeginCommandBuffer(commandBuffer, beginInfo);
|
|
return commandBuffer;
|
|
}
|
|
}
|
|
|
|
private static void endSingleTimeCommands(VkCommandBuffer commandBuffer)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
vkEndCommandBuffer(commandBuffer);
|
|
VkSubmitInfo.Buffer submitInfo = VkSubmitInfo.callocStack(1, stack);
|
|
submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO);
|
|
submitInfo.pCommandBuffers(stack.pointers(commandBuffer));
|
|
vkQueueSubmit(graphicsQueue, submitInfo, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(graphicsQueue);
|
|
vkFreeCommandBuffers(device, commandPool, commandBuffer);
|
|
}
|
|
}
|
|
|
|
private void copyBuffer(long srcBuffer, long dstBuffer, long size)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
|
|
VkBufferCopy.Buffer copyRegion = VkBufferCopy.callocStack(1, stack);
|
|
copyRegion.size(size);
|
|
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, copyRegion);
|
|
endSingleTimeCommands(commandBuffer);
|
|
}
|
|
}
|
|
|
|
private void memcpy(ByteBuffer buffer, Vertex[] vertices)
|
|
{
|
|
for (Vertex vertex : vertices)
|
|
{
|
|
buffer.putFloat(vertex.pos.x());
|
|
buffer.putFloat(vertex.pos.y());
|
|
buffer.putFloat(vertex.pos.z());
|
|
buffer.putFloat(vertex.color.x());
|
|
buffer.putFloat(vertex.color.y());
|
|
buffer.putFloat(vertex.color.z());
|
|
buffer.putFloat(vertex.texCoords.x());
|
|
buffer.putFloat(vertex.texCoords.y());
|
|
}
|
|
}
|
|
|
|
private void memcpy(ByteBuffer buffer, int[] indices)
|
|
{
|
|
for (int index : indices)
|
|
{ buffer.putInt(index); }
|
|
buffer.rewind();
|
|
}
|
|
|
|
private void memcpy(ByteBuffer buffer, UniformBufferObject ubo)
|
|
{
|
|
final int mat4Size = 16 * Float.BYTES;
|
|
ubo.model.get(0, buffer);
|
|
ubo.view.get(AlignmentUtils.alignas(mat4Size, AlignmentUtils.alignof(ubo.view)), buffer);
|
|
ubo.proj.get(AlignmentUtils.alignas(mat4Size * 2, AlignmentUtils.alignof(ubo.view)), buffer);
|
|
}
|
|
|
|
private static int findMemoryType(int typeFilter, int properties)
|
|
{
|
|
VkPhysicalDeviceMemoryProperties memProperties = VkPhysicalDeviceMemoryProperties.mallocStack();
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, memProperties);
|
|
for (int i = 0; i < memProperties.memoryTypeCount(); i++)
|
|
{
|
|
if ((typeFilter & (1 << i)) != 0 && (memProperties.memoryTypes(i).propertyFlags() & properties) == properties)
|
|
{ return i; }
|
|
}
|
|
throw new RuntimeException("Failed to find suitable memory type");
|
|
}
|
|
|
|
public static void createCommandBuffers()
|
|
{
|
|
final int commandBuffersCount = swapChainFramebuffers.size();
|
|
commandBuffers = new ArrayList<>(commandBuffersCount);
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkCommandBufferAllocateInfo allocInfo = VkCommandBufferAllocateInfo.callocStack(stack);
|
|
allocInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO);
|
|
allocInfo.commandPool(commandPool);
|
|
allocInfo.level(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
|
|
allocInfo.commandBufferCount(commandBuffersCount);
|
|
PointerBuffer pCommandBuffers = stack.mallocPointer(commandBuffersCount);
|
|
if (vkAllocateCommandBuffers(device, allocInfo, pCommandBuffers) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to allocate command buffers"); }
|
|
for (int i = 0; i < commandBuffersCount; i++)
|
|
{ commandBuffers.add(new VkCommandBuffer(pCommandBuffers.get(i), device)); }
|
|
VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.callocStack(stack);
|
|
beginInfo.sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
|
|
VkRenderPassBeginInfo renderPassInfo = VkRenderPassBeginInfo.callocStack(stack);
|
|
renderPassInfo.sType(VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO);
|
|
renderPassInfo.renderPass(renderPass);
|
|
VkRect2D renderArea = VkRect2D.callocStack(stack);
|
|
renderArea.offset(VkOffset2D.callocStack(stack).set(0, 0));
|
|
renderArea.extent(swapChainExtent);
|
|
renderPassInfo.renderArea(renderArea);
|
|
VkClearValue.Buffer clearValues = VkClearValue.callocStack(2, stack);
|
|
clearValues.get(0).color().float32(stack.floats(Window.getColour().x / 255, Window.getColour().y / 255, Window.getColour().z / 255, 1.0f)); //The screens clear colour
|
|
clearValues.get(1).depthStencil().set(1.0f, 0);
|
|
renderPassInfo.pClearValues(clearValues);
|
|
for (int i = 0; i < commandBuffersCount; i++)
|
|
{
|
|
VkCommandBuffer commandBuffer = commandBuffers.get(i);
|
|
if (vkBeginCommandBuffer(commandBuffer, beginInfo) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to begin recording command buffer"); }
|
|
renderPassInfo.framebuffer(swapChainFramebuffers.get(i));
|
|
vkCmdBeginRenderPass(commandBuffer, renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
{
|
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
|
|
LongBuffer vertexBuffers = stack.longs(vertexBuffer);
|
|
LongBuffer offsets = stack.longs(0);
|
|
vkCmdBindVertexBuffers(commandBuffer, 0, vertexBuffers, offsets);
|
|
vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT32);
|
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
pipelineLayout,
|
|
0, stack.longs(
|
|
descriptorSets.get(i)),
|
|
null);
|
|
vkCmdDrawIndexed(commandBuffer, indices.length, 1, 0, 0, 0);
|
|
}
|
|
vkCmdEndRenderPass(commandBuffer);
|
|
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to record command buffer"); }
|
|
}
|
|
}
|
|
}
|
|
|
|
private void createSyncObjects()
|
|
{
|
|
inFlightFrames = new ArrayList<>(MAX_FRAMES_IN_FLIGHT);
|
|
imagesInFlight = new HashMap<>(swapChainImages.size());
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
VkSemaphoreCreateInfo semaphoreInfo = VkSemaphoreCreateInfo.callocStack(stack);
|
|
semaphoreInfo.sType(VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO);
|
|
VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.callocStack(stack);
|
|
fenceInfo.sType(VK_STRUCTURE_TYPE_FENCE_CREATE_INFO);
|
|
fenceInfo.flags(VK_FENCE_CREATE_SIGNALED_BIT);
|
|
LongBuffer pImageAvailableSemaphore = stack.mallocLong(1);
|
|
LongBuffer pRenderFinishedSemaphore = stack.mallocLong(1);
|
|
LongBuffer pFence = stack.mallocLong(1);
|
|
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
|
{
|
|
if (vkCreateSemaphore(device, semaphoreInfo, null, pImageAvailableSemaphore) != VK_SUCCESS
|
|
|| vkCreateSemaphore(device, semaphoreInfo, null, pRenderFinishedSemaphore) != VK_SUCCESS
|
|
|| vkCreateFence(device, fenceInfo, null, pFence) != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to create synchronization objects for the frame " + i); }
|
|
inFlightFrames.add(new Frame(pImageAvailableSemaphore.get(0), pRenderFinishedSemaphore.get(0), pFence.get(0)));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void updateUniformBuffer(int currentImage)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
UniformBufferObject ubo = new UniformBufferObject();
|
|
ubo.model.rotate((float) (glfwGetTime() * Math.toRadians(90)), 0.0f, 0.0f, 1.0f);
|
|
ubo.view.lookAt(2.0f, 2.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
|
|
ubo.proj.perspective((float) Math.toRadians(45),
|
|
(float) swapChainExtent.width() / (float) swapChainExtent.height(), 0.1f, 10.0f);
|
|
ubo.proj.m11(ubo.proj.m11() * -1);
|
|
PointerBuffer data = stack.mallocPointer(1);
|
|
vkMapMemory(device, uniformBuffersMemory.get(currentImage), 0, UniformBufferObject.SIZEOF, 0, data);
|
|
{
|
|
memcpy(data.getByteBuffer(0, UniformBufferObject.SIZEOF), ubo);
|
|
}
|
|
vkUnmapMemory(device, uniformBuffersMemory.get(currentImage));
|
|
}
|
|
}
|
|
|
|
private void drawFrame()
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
Frame thisFrame = inFlightFrames.get(currentFrame);
|
|
vkWaitForFences(device, thisFrame.pFence(), true, UINT64_MAX);
|
|
IntBuffer pImageIndex = stack.mallocInt(1);
|
|
int vkResult = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX,
|
|
thisFrame.imageAvailableSemaphore(), VK_NULL_HANDLE, pImageIndex);
|
|
if (vkResult == VK_ERROR_OUT_OF_DATE_KHR)
|
|
{
|
|
VKSwapchainManager.recreateSwapChain();
|
|
return;
|
|
}
|
|
else if (vkResult != VK_SUCCESS)
|
|
{ throw new RuntimeException("Cannot get image"); }
|
|
final int imageIndex = pImageIndex.get(0);
|
|
updateUniformBuffer(imageIndex);
|
|
if (imagesInFlight.containsKey(imageIndex))
|
|
{ vkWaitForFences(device, imagesInFlight.get(imageIndex).fence(), true, UINT64_MAX); }
|
|
imagesInFlight.put(imageIndex, thisFrame);
|
|
VkSubmitInfo submitInfo = VkSubmitInfo.callocStack(stack);
|
|
submitInfo.sType(VK_STRUCTURE_TYPE_SUBMIT_INFO);
|
|
submitInfo.waitSemaphoreCount(1);
|
|
submitInfo.pWaitSemaphores(thisFrame.pImageAvailableSemaphore());
|
|
submitInfo.pWaitDstStageMask(stack.ints(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT));
|
|
submitInfo.pSignalSemaphores(thisFrame.pRenderFinishedSemaphore());
|
|
submitInfo.pCommandBuffers(stack.pointers(commandBuffers.get(imageIndex)));
|
|
vkResetFences(device, thisFrame.pFence());
|
|
if ((vkResult = vkQueueSubmit(graphicsQueue, submitInfo, thisFrame.fence())) != VK_SUCCESS)
|
|
{
|
|
vkResetFences(device, thisFrame.pFence());
|
|
throw new RuntimeException("Failed to submit draw command buffer: " + vkResult);
|
|
}
|
|
VkPresentInfoKHR presentInfo = VkPresentInfoKHR.callocStack(stack);
|
|
presentInfo.sType(VK_STRUCTURE_TYPE_PRESENT_INFO_KHR);
|
|
presentInfo.pWaitSemaphores(thisFrame.pRenderFinishedSemaphore());
|
|
presentInfo.swapchainCount(1);
|
|
presentInfo.pSwapchains(stack.longs(swapChain));
|
|
presentInfo.pImageIndices(pImageIndex);
|
|
vkResult = vkQueuePresentKHR(presentQueue, presentInfo);
|
|
if (vkResult == VK_ERROR_OUT_OF_DATE_KHR || vkResult == VK_SUBOPTIMAL_KHR || framebufferResize)
|
|
{
|
|
framebufferResize = false;
|
|
VKSwapchainManager.recreateSwapChain();
|
|
}
|
|
else if (vkResult != VK_SUCCESS)
|
|
{ throw new RuntimeException("Failed to present swap chain image"); }
|
|
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
|
}
|
|
}
|
|
|
|
public static VkSurfaceFormatKHR chooseSwapSurfaceFormat(VkSurfaceFormatKHR.Buffer availableFormats)
|
|
{ return availableFormats.stream()
|
|
.filter(availableFormat -> availableFormat.format() == VK_FORMAT_B8G8R8_SRGB)
|
|
.filter(availableFormat -> availableFormat.colorSpace() == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
|
.findAny()
|
|
.orElse(availableFormats.get(0)); }
|
|
|
|
public static int chooseSwapPresentMode(IntBuffer availablePresentModes)
|
|
{
|
|
for (int i = 0; i < availablePresentModes.capacity(); i++)
|
|
{
|
|
if (availablePresentModes.get(i) == VK_PRESENT_MODE_MAILBOX_KHR)
|
|
{ return availablePresentModes.get(i); }
|
|
}
|
|
return VK_PRESENT_MODE_FIFO_KHR;
|
|
}
|
|
|
|
public static VkExtent2D chooseSwapExtent(VkSurfaceCapabilitiesKHR capabilities)
|
|
{
|
|
if (capabilities.currentExtent().width() != UINT32_MAX)
|
|
{ return capabilities.currentExtent(); }
|
|
IntBuffer width = stackGet().ints(0);
|
|
IntBuffer height = stackGet().ints(0);
|
|
glfwGetFramebufferSize(Window.getWindow(), width, height);
|
|
VkExtent2D actualExtent = VkExtent2D.mallocStack().set(width.get(0), height.get(0));
|
|
VkExtent2D minExtent = capabilities.minImageExtent();
|
|
VkExtent2D maxExtent = capabilities.maxImageExtent();
|
|
actualExtent.width(clamp(minExtent.width(), maxExtent.width(), actualExtent.width()));
|
|
actualExtent.height(clamp(minExtent.height(), maxExtent.height(), actualExtent.height()));
|
|
return actualExtent;
|
|
}
|
|
|
|
private static int clamp(int min, int max, int value)
|
|
{ return Math.max(min, Math.min(max, value)); }
|
|
|
|
private boolean isDeviceSuitable(VkPhysicalDevice device)
|
|
{
|
|
QueueFamilyIndices indices = findQueueFamilies(device);
|
|
boolean extensionsSupported = checkDeviceExtensionSupport(device);
|
|
boolean swapChainAdequate = false;
|
|
boolean anisotropySupported = false;
|
|
if (extensionsSupported)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device, stack);
|
|
swapChainAdequate = swapChainSupport.formats.hasRemaining() && swapChainSupport.presentModes.hasRemaining();
|
|
VkPhysicalDeviceFeatures supportedFeatures = VkPhysicalDeviceFeatures.mallocStack(stack);
|
|
vkGetPhysicalDeviceFeatures(device, supportedFeatures);
|
|
anisotropySupported = supportedFeatures.samplerAnisotropy();
|
|
}
|
|
}
|
|
return indices.isComplete() && extensionsSupported && swapChainAdequate && anisotropySupported;
|
|
}
|
|
|
|
@SuppressWarnings("unlikely-arg-type")
|
|
private boolean checkDeviceExtensionSupport(VkPhysicalDevice device)
|
|
{
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
IntBuffer extensionCount = stack.ints(0);
|
|
vkEnumerateDeviceExtensionProperties(device, (String) null, extensionCount, null);
|
|
VkExtensionProperties.Buffer availableExtensions = VkExtensionProperties.mallocStack(extensionCount.get(0), stack);
|
|
return availableExtensions.stream().collect(toSet()).containsAll(DEVICE_EXTENSIONS);
|
|
}
|
|
}
|
|
|
|
public static SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, MemoryStack stack)
|
|
{
|
|
SwapChainSupportDetails details = new SwapChainSupportDetails();
|
|
details.capabilities = VkSurfaceCapabilitiesKHR.mallocStack(stack);
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, details.capabilities);
|
|
IntBuffer count = stack.ints(0);
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, count, null);
|
|
if (count.get(0) != 0)
|
|
{
|
|
details.formats = VkSurfaceFormatKHR.mallocStack(count.get(0), stack);
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, count, details.formats);
|
|
}
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, count, null);
|
|
if (count.get(0) != 0)
|
|
{
|
|
details.presentModes = stack.mallocInt(count.get(0));
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, count, details.presentModes);
|
|
}
|
|
return details;
|
|
}
|
|
|
|
public static QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device)
|
|
{
|
|
QueueFamilyIndices indices = new QueueFamilyIndices();
|
|
try (MemoryStack stack = stackPush())
|
|
{
|
|
IntBuffer queueFamilyCount = stack.ints(0);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, null);
|
|
VkQueueFamilyProperties.Buffer queueFamilies = VkQueueFamilyProperties.mallocStack(queueFamilyCount.get(0), stack);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, queueFamilyCount, queueFamilies);
|
|
IntBuffer presentSupport = stack.ints(VK_FALSE);
|
|
for (int i = 0; i < queueFamilies.capacity() || !indices.isComplete(); i++)
|
|
{
|
|
if ((queueFamilies.get(i).queueFlags() & VK_QUEUE_GRAPHICS_BIT) != 0)
|
|
{ indices.graphicsFamily = i; }
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, presentSupport);
|
|
if (presentSupport.get(0) == VK_TRUE)
|
|
{ indices.presentFamily = i; }
|
|
}
|
|
return indices;
|
|
}
|
|
}
|
|
|
|
private PointerBuffer asPointerBuffer(Collection<String> collection)
|
|
{
|
|
MemoryStack stack = stackGet();
|
|
PointerBuffer buffer = stack.mallocPointer(collection.size());
|
|
collection.stream()
|
|
.map(stack::UTF8)
|
|
.forEach(buffer::put);
|
|
return buffer.rewind();
|
|
}
|
|
|
|
public static PointerBuffer asPointerBuffer(List<? extends Pointer> list)
|
|
{
|
|
MemoryStack stack = stackGet();
|
|
PointerBuffer buffer = stack.mallocPointer(list.size());
|
|
list.forEach(buffer::put);
|
|
return buffer.rewind();
|
|
}
|
|
|
|
private PointerBuffer getRequiredExtensions()
|
|
{
|
|
PointerBuffer glfwExtensions = glfwGetRequiredInstanceExtensions();
|
|
return glfwExtensions;
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args)
|
|
{
|
|
VulkanDemoGinger2 app = new VulkanDemoGinger2();
|
|
app.run();
|
|
}
|
|
}
|