Implement graphics pipeline utilities, port Overv's triangle demo

This commit is contained in:
mittorn 2024-10-14 03:37:21 +03:00
parent 1986d3c171
commit 575d1acbf7
5 changed files with 674 additions and 107 deletions

10
triangle.frag Normal file
View file

@ -0,0 +1,10 @@
#version 450
layout (location = 0) in vec3 inColor;
layout (location = 0) out vec4 outFragColor;
void main()
{
outFragColor = vec4(inColor, 1.0);
}

23
triangle.vert Normal file
View file

@ -0,0 +1,23 @@
#version 450
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inColor;
layout (binding = 0) uniform UBO
{
mat4 transformMatrix;
} ubo;
layout (location = 0) out vec3 outColor;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
outColor = inColor;
gl_Position = ubo.transformMatrix * vec4(inPos.xyz, 1.0);
}

View file

@ -52,7 +52,10 @@ struct ComputeApplicationPipeline: BaseVulkanPipeline
CreatePool(CHAIN_SIZE,
BasicPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,CHAIN_SIZE*2),
BasicPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, CHAIN_SIZE*1));
VkShaderModule shader;
CreateComputePipeline(ShaderFromFile(shader, "image.spv", VK_SHADER_STAGE_COMPUTE_BIT));
// todo: should not we destroy shader internally?
vkDestroyShaderModule(device, shader, NULL);
}
void UpdateDescriptors(VkDescriptorSet dstSet, VkImageView imageView0, VkImageView imageView1, const VkDescriptorBufferInfo &buffer)
{
@ -82,7 +85,6 @@ struct ComputeApplication {
VulkanContext context;
VulkanDevice dev;
ComputeApplicationPipeline computePipeline;
VkShaderModule computeShaderModule;
struct UBO{
float frameNum;
@ -171,54 +173,6 @@ struct ComputeApplication {
computePipeline.UpdateDescriptors(chain[chidx].descriptorSet, chain[chidx].texture0.view, chain[chidx].texture1.view, chain[chidx].uboBuf.descriptor);
}
// Read file into array of bytes, and cast to uint32_t*, then return.
// The data has been padded, so that it fits into an array uint32_t.
uint32_t* readFile(uint32_t& length, const char* filename) {
FILE* fp = fopen(filename, "rb");
if (fp == NULL) {
printf("Could not find or open file: %s\n", filename);
}
// get file size.
fseek(fp, 0, SEEK_END);
long filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
long filesizepadded = long(ceil(filesize / 4.0)) * 4;
// read file contents.
char *str = (char*)malloc(filesizepadded);
fread(str, filesize, sizeof(char), fp);
fclose(fp);
// data padding.
for (int i = filesize; i < filesizepadded; i++) {
str[i] = 0;
}
length = filesizepadded;
return (uint32_t *)str;
}
void createComputePipeline() {
/*
We create a compute pipeline here.
*/
uint32_t filelength;
// the code in comp.spv was created by running the command:
// glslangValidator.exe -V shader.comp
uint32_t* code = readFile(filelength, "image.spv");
/*
Create a shader module. A shader module basically just encapsulates some shader code.
*/
// todo: it is very strange API...
VkPipelineShaderStageCreateInfo shaderStageCreateInfo = LoadShader(dev.device, code, filelength, VK_SHADER_STAGE_COMPUTE_BIT);
computeShaderModule = shaderStageCreateInfo.module;
computePipeline.CreateComputePipeline(shaderStageCreateInfo);
free(code);
}
void prepareImage(int chidx)
{
VkCommandBuffer commandBuffer = dev.CreateCommandBuffer();
@ -319,7 +273,6 @@ struct ComputeApplication {
}
computePipeline.Init(dev.device);
createComputePipeline();
for(int i = 0; i < CHAIN_SIZE; i++)
{
prepareImage(i);
@ -366,7 +319,6 @@ struct ComputeApplication {
/*
Clean up all Vulkan Resources.
*/
vkDestroyShaderModule(dev.device, computeShaderModule, NULL);
computePipeline.Destroy();
dev.Destroy();
context.Destroy();

333
vkgraphics.cpp Normal file
View file

@ -0,0 +1,333 @@
#include "vulkan_utl.h"
#include <X11/Xlib.h>
#include <vulkan/vulkan_xlib.h>
// use glfw for temporary swapchain window
// todo: GET RID OF THIS, IT SOMEHOW INCLUDES GL!!!
#include <GLFW/glfw3.h>
#define GLFW_EXPOSE_NATIVE_X11
#include <GLFW/glfw3native.h>
// todo: REMOVE THIS. Why "simpliest triangle" demos even use this????
// It does not "HELP" drawing 2D triangle!!! It is not useful in engines using own matrix transform code!
#include <glm/gtc/matrix_transform.hpp>
#define MAX_SWAPCHAIN_IMAGES 32
struct Vertex
{
float position[3];
float color[3];
};
struct GraphicsApplicationPipeline: BaseVulkanPipeline
{
void Init(VkDevice dev, VkRenderPass renderPass)
{
device = dev;
CreateDescriptorSetLayout(
BasicBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,1,VK_SHADER_STAGE_VERTEX_BIT)
);
CreatePool(1,
BasicPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1)
);
VkShaderModule vp, fp;
CreateGraphicsPipeline(renderPass, Stages(
ShaderFromFile(vp, "triangle.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
ShaderFromFile(fp, "triangle.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)),
VertexBindings(VertexBinding(0, sizeof(Vertex))),
VertexAttributes(
VertAttrib(0, 0, offsetof(Vertex,position)),
VertAttrib(1, 0, offsetof(Vertex,color))
),
DynamicStates(
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR));
vkDestroyShaderModule(device, vp, NULL);
vkDestroyShaderModule(device, fp, NULL);
}
void UpdateDescriptors(VkDescriptorSet dstSet, const VkDescriptorBufferInfo &buffer)
{
WriteDescriptors(
BufferWrite(dstSet, 0, buffer)
);
}
};
struct GraphicsApplication
{
VulkanContext context;
VulkanDevice dev;
GLFWwindow *window = NULL;
VkSwapchainKHR swapchain;
VkSurfaceKHR surface;
VkImage swapchainImages[MAX_SWAPCHAIN_IMAGES];
unsigned int numSwapchainImages;
VulkanFramebuffer swapchainFbs[MAX_SWAPCHAIN_IMAGES];
VkCommandBuffer commandBuffers[MAX_SWAPCHAIN_IMAGES];
VkFence chainFences[MAX_SWAPCHAIN_IMAGES];
VkSemaphore swapchainRenderSemaphore, swapchainPresentSemaphore;
struct UBO
{
glm::mat4 transformationMatrix;
};
GraphicsApplicationPipeline graphicsPipeline;
VkDescriptorSet descriptorSet;
VulkanBuffer uboBuf;
VulkanBuffer stagingVert;
VulkanBuffer stagingInd;
//VkFence swapchainFence;
void CreateWindow(const char* windowTitle, int& outWidth, int& outHeight, bool resizable)
{
if (!glfwInit()) {
return;
}
const bool wantsWholeArea = outWidth <= 0 || outHeight <= 0;
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, wantsWholeArea || !resizable ? GLFW_FALSE : GLFW_TRUE);
// render full screen without overlapping taskbar
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
int x = 0;
int y = 0;
int w = outWidth;
int h = outHeight;
if (wantsWholeArea) {
int areaW = 0;
int areaH = 0;
glfwGetMonitorWorkarea(monitor, &x, &y, &areaW, &areaH);
auto getPercent = [](int value, int percent) {
assert(percent > 0 && percent <= 100);
return static_cast<int>(static_cast<float>(value) * static_cast<float>(percent) / 100.0f);
};
if (outWidth < 0) {
w = getPercent(areaW, -outWidth);
x = (areaW - w) / 2;
} else {
w = areaW;
}
if (outHeight < 0) {
h = getPercent(areaH, -outHeight);
y = (areaH - h) / 2;
} else {
h = areaH;
}
}
window = glfwCreateWindow(w, h, windowTitle, nullptr, nullptr);
if (!window) {
glfwTerminate();
return;
}
if (wantsWholeArea) {
glfwSetWindowPos(window, x, y);
}
glfwGetWindowSize(window, &outWidth, &outHeight);
glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int, int action, int) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
});
glfwSetErrorCallback([](int error, const char* description) { printf("GLFW Error (%i): %s\n", error, description); });
//glfwShowWindow(window);
}
void DestroyWindow()
{
if(window)
glfwDestroyWindow(window);
glfwTerminate();
}
void CreateSwapchain(int width, int height)
{
const VkXlibSurfaceCreateInfoKHR surfaceinfo = {
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
.flags = 0,
.dpy = glfwGetX11Display(),
.window = glfwGetX11Window(window),
};
VK_CHECK_RESULT(vkCreateXlibSurfaceKHR(context.instance, &surfaceinfo, nullptr, &surface));
const VkSwapchainCreateInfoKHR swapchain_create_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface,
.minImageCount = 4,
// todo: guess format
.imageFormat = VK_FORMAT_B8G8R8A8_UNORM,
.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
.imageExtent = {.width = width, .height = height},
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 1,
.pQueueFamilyIndices = &dev.defaultFamilyIndex,
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
.presentMode = VK_PRESENT_MODE_FIFO_KHR,
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
};
vkCreateSwapchainKHR(dev.device, &swapchain_create_info, nullptr, &swapchain);
numSwapchainImages = MAX_SWAPCHAIN_IMAGES;
VK_CHECK_RESULT(vkGetSwapchainImagesKHR(dev.device, swapchain, &numSwapchainImages, swapchainImages));
for(int i = 0; i < numSwapchainImages; i++)
{
swapchainFbs[i].Create(dev.device);
swapchainFbs[i].CreateDepthAttachment(dev, VK_FORMAT_D32_SFLOAT, width, height);
swapchainFbs[i].Init(swapchainImages[i], VK_FORMAT_B8G8R8A8_UNORM, width, height);
}
VkSemaphoreCreateInfo semaphoreCreateInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
VK_CHECK_RESULT(vkCreateSemaphore(dev.device, &semaphoreCreateInfo, nullptr, &swapchainPresentSemaphore));
VK_CHECK_RESULT(vkCreateSemaphore(dev.device, &semaphoreCreateInfo, nullptr, &swapchainRenderSemaphore));
}
unsigned int AcquireImage()
{
unsigned int index;
vkAcquireNextImageKHR(dev.device, swapchain, UINT64_MAX, swapchainRenderSemaphore, (VkFence)nullptr, &index);
return index;
}
void PresentImage(unsigned int index)
{
VkPresentInfoKHR presentInfo = {VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapchain;
presentInfo.pImageIndices = &index;
presentInfo.pWaitSemaphores = &swapchainPresentSemaphore;
presentInfo.waitSemaphoreCount = 1;
vkQueuePresentKHR(dev.defautQueue, &presentInfo);
}
void DestroySwapchain()
{
for(int i = 0; i < numSwapchainImages; i++)
swapchainFbs[i].Destroy();
vkDestroySwapchainKHR(dev.device, swapchain, NULL);
vkDestroySurfaceKHR(context.instance,surface,NULL);
vkDestroySemaphore(dev.device, swapchainPresentSemaphore, NULL);
vkDestroySemaphore(dev.device, swapchainRenderSemaphore, NULL);
}
// todo: NUKE THIS!!!
void updateUniformData() {
static unsigned int frameNum;
// Rotate based on time
long long millis = frameNum++;
float angle = (millis % 400) / 400.0f * glm::radians(360.f);
glm::mat4 modelMatrix = glm::identity<glm::mat4>();
modelMatrix = glm::rotate(modelMatrix, angle, glm::vec3(0, 0, 1));
//((UBO*)uboBuf.mapped)->transformationMatrix = modelMatrix;
modelMatrix = glm::translate(modelMatrix, glm::vec3(0.5f / 3.0f, -0.5f / 3.0f, 0.0f));
// Set up view
auto viewMatrix = glm::lookAt(glm::vec3(1, 1, 1), glm::vec3(0, 0, 0), glm::vec3(0, 0, -1));
// Set up projection
auto projMatrix = glm::perspective(glm::radians(70.f), 800.0f / 600.0f, 0.1f, 10.0f);
((UBO*)uboBuf.mapped)->transformationMatrix = projMatrix * viewMatrix * modelMatrix;
}
void waitFence(int chidx)
{
VK_CHECK_RESULT(vkWaitForFences(dev.device, 1, &chainFences[chidx], VK_TRUE, 100000000000));
}
void Run()
{
context.Create("streamingengine", "vulkan-playground-server");
dev.Create(context.FindPhysicalDevice(), VK_QUEUE_GRAPHICS_BIT);
dev.CreateDevice(context);
int width = 800;
int height = 600;
CreateWindow("demo", width, height, false);
CreateSwapchain(width,height);
dev.CreateAndMap(uboBuf, sizeof(UBO));
graphicsPipeline.Init(dev.device, swapchainFbs[0].render_pass);
descriptorSet = graphicsPipeline.AllocateSingleDescriptorSet();
graphicsPipeline.UpdateDescriptors(descriptorSet, uboBuf.descriptor);
updateUniformData();
Vertex vertices[] = {
{ { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
{ { -0.5f, 0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
{ { 0.5f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f } }
};
uint32_t indices[] = { 0, 1, 2 };
dev.CreateBuffer(stagingVert, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, sizeof(vertices), vertices);
dev.CreateBuffer(stagingInd, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, sizeof(indices), indices);
for(int i = 0; i < numSwapchainImages; i++)
{
commandBuffers[i] = dev.CreateCommandBuffer();
//VulkanTexture::SetImageLayout(commandBuffers[i], swapchainImages[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
VkClearColorValue color;
color.uint32[0] = 45243524;
color.uint32[1] = 45234545;
color.uint32[2] = 54353453;
//vkCmdClearColorImage(commandBuffers[i], swapchainImages[i],VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, &color, 1, &range );
swapchainFbs[i].BeginRenderPass(commandBuffers[i]);
swapchainFbs[i].SetViewportAndScissor(commandBuffers[i]);
vkCmdBindDescriptorSets(commandBuffers[i],VK_PIPELINE_BIND_POINT_GRAPHICS,graphicsPipeline.pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS,graphicsPipeline.pipeline);
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(commandBuffers[i],0, 1, &stagingVert.buffer, &offset);
vkCmdBindIndexBuffer(commandBuffers[i], stagingInd.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(commandBuffers[i], 3, 1, 0, 0, 0);
vkCmdEndRenderPass(commandBuffers[i]);
VulkanTexture::SetImageLayout(commandBuffers[i], swapchainImages[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
vkEndCommandBuffer(commandBuffers[i]);
VkFenceCreateInfo fenceCreateInfo = {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VK_CHECK_RESULT(vkCreateFence(dev.device, &fenceCreateInfo, NULL, &chainFences[i]));
}
while(true)
{
updateUniformData();
uint32_t idx = AcquireImage();
VkSubmitInfo submitInfo = {VK_STRUCTURE_TYPE_SUBMIT_INFO};
submitInfo.pWaitSemaphores = &swapchainRenderSemaphore;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &swapchainPresentSemaphore;
submitInfo.signalSemaphoreCount = 1;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[idx];
VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
submitInfo.pWaitDstStageMask = &waitDstStageMask;
waitFence(idx);
vkResetFences(dev.device,1, &chainFences[idx]);
vkQueueSubmit(dev.defautQueue, 1, &submitInfo, chainFences[idx]);
PresentImage(idx);
}
vkDeviceWaitIdle(dev.device);
graphicsPipeline.Destroy();
stagingVert.Destroy();
stagingInd.Destroy();
uboBuf.Destroy();
DestroySwapchain();
DestroyWindow();
dev.Destroy();
context.Destroy();
}
};
int main()
{
GraphicsApplication app;
app.Run();
return EXIT_SUCCESS;
}

View file

@ -91,6 +91,9 @@ struct VulkanContext
enabledExtensions[enabledExtensionsCount++] = VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME;
enabledExtensions[enabledExtensionsCount++] = VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME;
enabledExtensions[enabledExtensionsCount++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
enabledExtensions[enabledExtensionsCount++] = VK_KHR_SURFACE_EXTENSION_NAME;
enabledExtensions[enabledExtensionsCount++] = "VK_KHR_xlib_surface";
if (enableValidationLayers) {
/*
We get all supported layers with vkEnumerateInstanceLayerProperties.
@ -157,8 +160,9 @@ struct VulkanContext
.ppEnabledExtensionNames = (const char* const*)enabledExtensions,
};
VkResult res = vkCreateInstance(&instance_info, NULL, &instance);
if (res == VK_SUCCESS && enableValidationLayers) {
VkResult res1 = vkCreateInstance(&instance_info, NULL, &instance);
VK_CHECK_RESULT(res1);
if (enableValidationLayers) {
VkDebugReportCallbackCreateInfoEXT createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
@ -174,7 +178,7 @@ struct VulkanContext
else
printf("Failed to register debug callback: null function\n");
}
return res;
return res1;
}
VkPhysicalDevice FindPhysicalDevice()
{
@ -259,25 +263,6 @@ struct VulkanBuffer
}
};
VkPipelineShaderStageCreateInfo LoadShader(VkDevice device, const uint32_t* code, size_t size, VkShaderStageFlagBits stage)
{
VkShaderModuleCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = size,
.pCode = code
};
VkShaderModule module;
VK_CHECK_RESULT(vkCreateShaderModule(device, &info, NULL, &module));
return VkPipelineShaderStageCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = stage,
.module = module,
.pName = "main"
};
}
struct VulkanDevice
{
VkPhysicalDevice physicalDevice = NULL;
@ -360,7 +345,6 @@ struct VulkanDevice
*out_index = 0;
return false;
}
int GetAvailiableModifiersList(uint64_t *modifiers2, size_t len, VkFormat fmt)
@ -405,6 +389,7 @@ struct VulkanDevice
deviceExtensions[deviceExtensionsCount++] = VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME;
deviceExtensions[deviceExtensionsCount++] = VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME;
deviceExtensions[deviceExtensionsCount++] = VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME;
deviceExtensions[deviceExtensionsCount++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
VkPhysicalDeviceFeatures enabled_features = {
.samplerAnisotropy = VK_TRUE,
@ -433,7 +418,7 @@ struct VulkanDevice
return result;
}
VkResult CreateBuffer(VulkanBuffer &buffer, VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_flags, VkDeviceSize size, void *data)
VkResult CreateBuffer(VulkanBuffer &buffer, VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_flags, VkDeviceSize size, void *data = NULL)
{
buffer.device = device;
@ -562,29 +547,113 @@ struct VulkanDevice
struct VulkanFramebuffer
{
VkDevice device;
VkDevice device = NULL;
struct
{
VkImage image;
VkDeviceMemory mem;
VkImageView view;
VkFormat format;
bool allocated = false;
} depth = {};
VkImageView color_view;
VkImageView depth_view;
VkImageView color_view = NULL;
uint32_t width, height;
VkFramebuffer frame_buffer;
VkRenderPass render_pass;
VkFramebuffer frame_buffer = NULL;
VkRenderPass render_pass = NULL;
void Create(VkDevice d)
{
device = d;
}
void Destroy()
{
vkDestroyImageView(device, color_view, NULL);
vkDestroyImageView(device, depth_view, NULL);
if(!device)
return;
if(color_view)
vkDestroyImageView(device, color_view, NULL);
color_view = NULL;
if(depth.view)
vkDestroyImageView(device, depth.view, NULL);
depth.view = NULL;
if(depth.allocated)
{
if(depth.image)
vkDestroyImage(device, depth.image, NULL);
depth.image = NULL;
vkFreeMemory(device, depth.mem, NULL);
depth.mem = NULL;
depth.allocated = false;
}
vkDestroyFramebuffer(device, frame_buffer, NULL);
if(frame_buffer)
vkDestroyFramebuffer(device, frame_buffer, NULL);
frame_buffer = NULL;
vkDestroyRenderPass(device, render_pass, NULL);
if(render_pass)
vkDestroyRenderPass(device, render_pass, NULL);
render_pass = NULL;
}
void ImportDepthAttachment(VkImage image, VkFormat format)
{
depth.image = image;
VkImageViewCreateInfo image_view = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = depth.image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
VK_CHECK_RESULT(vkCreateImageView(device, &image_view, NULL, &depth.view));
}
void Init(VkImage color_image, VkFormat color_format, VkImage depth_image, VkFormat depth_format, uint32_t w, uint32_t h)
void CreateDepthAttachment(VulkanDevice &dev, VkFormat format, uint32_t w, uint32_t h)
{
depth.format = format;
VkImageCreateInfo image = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = format,
.extent = { .width = w, .height = h, .depth = 1 },
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
};
VkImage dimage;
VK_CHECK_RESULT(vkCreateImage(device, &image, NULL, &dimage));
VkMemoryRequirements mem_reqs;
vkGetImageMemoryRequirements(device, dimage, &mem_reqs);
VkMemoryAllocateInfo mem_alloc = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = mem_reqs.size,
};
if (!dev.GetMemoryType(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&mem_alloc.memoryTypeIndex))
printf("Could not find memory type.\n");
VK_CHECK_RESULT(vkAllocateMemory(device, &mem_alloc, NULL, &depth.mem));
VK_CHECK_RESULT(vkBindImageMemory(device, dimage, depth.mem, 0));
ImportDepthAttachment(dimage, format);
depth.allocated = true;
}
void Init(VkImage color_image, VkFormat color_format, uint32_t w, uint32_t h)
{
width = w;
height = h;
@ -612,7 +681,7 @@ struct VulkanFramebuffer
// Formats
attachmentDescs[0].format = color_format;
attachmentDescs[1].format = depth_format;
attachmentDescs[1].format = depth.format;
VkAttachmentReference colorReferences[1] = {
{ .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }
@ -680,24 +749,7 @@ struct VulkanFramebuffer
VK_CHECK_RESULT(vkCreateImageView(device, &imageView, NULL, &color_view));
VkImageViewCreateInfo depthImageView = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = depth_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = depth_format,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
VK_CHECK_RESULT(vkCreateImageView(device, &depthImageView, NULL, &depth_view));
VkImageView attachments[2] = { color_view, depth_view };
VkImageView attachments[2] = { color_view, depth.view };
VkFramebufferCreateInfo fbufCreateInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
@ -711,6 +763,7 @@ struct VulkanFramebuffer
VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, NULL, &frame_buffer));
}
void BeginRenderPass(VkCommandBuffer cmdBuffer)
{
VkClearValue clearValues[2] = { { .color = { { 0.0f, 0.0f, 0.0f, 0.0f } } },
@ -741,6 +794,11 @@ struct VulkanFramebuffer
.height = height } };
vkCmdSetScissor(cmdBuffer, 0, 1, &scissor);
}
~VulkanFramebuffer()
{
Destroy();
}
};
struct VulkanTexture
@ -761,7 +819,8 @@ struct VulkanTexture
static VkAccessFlags GetAccessFlags(VkImageLayout layout)
{
switch (layout) {
case VK_IMAGE_LAYOUT_UNDEFINED: return 0;
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
case VK_IMAGE_LAYOUT_GENERAL: return 0;
case VK_IMAGE_LAYOUT_PREINITIALIZED: return VK_ACCESS_HOST_WRITE_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
@ -1009,7 +1068,14 @@ struct VulkanTexture
VK_CHECK_RESULT(vkCreateImageView(device, &info, NULL, &view));
}
};
template <typename T, typename... Args>
struct ArrayInitializer
{
T array[sizeof... (Args)];
constexpr static size_t count = sizeof... (Args);
//ArrayInitializer(const Args&... arguments) : array({arguments...}){}
};
#include <math.h>
struct BaseVulkanPipeline
{
@ -1061,6 +1127,189 @@ struct BaseVulkanPipeline
return ret;
}
VkPipelineShaderStageCreateInfo ShaderFromFile(VkShaderModule &outShaderModule, const char *filename, VkShaderStageFlagBits stage, const char *entrypoint = "main")
{
// todo: rewrite this in safer way
FILE* fp = fopen(filename, "rb");
if (fp == NULL) {
printf("Could not find or open file: %s\n", filename);
}
// get file size.
fseek(fp, 0, SEEK_END);
long filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
long filesizepadded = long(ceil(filesize / 4.0)) * 4;
uint32_t contents[filesizepadded/4];
char *str = (char*) contents;
fread(contents, filesize, sizeof(char), fp);
fclose(fp);
// data padding.
for (int i = filesize; i < filesizepadded; i++) {
str[i] = 0;
}
VkShaderModuleCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = filesizepadded,
.pCode = contents
};
VK_CHECK_RESULT(vkCreateShaderModule(device, &info, NULL, &outShaderModule));
return VkPipelineShaderStageCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = stage,
.module = outShaderModule,
.pName = entrypoint,
};
}
static VkPipelineInputAssemblyStateCreateInfo TriangleListAssembly()
{
return {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE
};
}
static VkPipelineRasterizationStateCreateInfo DefaultRasterMode()
{
return {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, // todo: what default is better?
.lineWidth = 1.0f
};
}
static VkPipelineColorBlendAttachmentState DefaultBlendAttachment()
{
return {
.blendEnable = VK_FALSE, .colorWriteMask = 0xf
};
}
static VkPipelineColorBlendStateCreateInfo DefaultColorBlend(const VkPipelineColorBlendAttachmentState& attachment = DefaultBlendAttachment())
{
return {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &attachment
};
}
static VkPipelineDepthStencilStateCreateInfo DefaultDepthStencil()
{
return {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
.front = { .compareOp = VK_COMPARE_OP_ALWAYS },
.back = { .compareOp = VK_COMPARE_OP_ALWAYS }
};
}
static VkPipelineViewportStateCreateInfo DefaultViewportState()
{
return {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1U,
.scissorCount = 1U
};
}
static VkPipelineMultisampleStateCreateInfo DefaultMultisampleState()
{
return {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
}
template <typename... Args>
ArrayInitializer<VkDynamicState, Args...> DynamicStates(const Args&... arguments)
{
return {arguments...};
}
template <typename... Args>
ArrayInitializer<VkPipelineShaderStageCreateInfo, Args...> Stages(const Args&... arguments)
{
return {arguments...};
}
template <typename... Args>
ArrayInitializer<VkVertexInputBindingDescription, Args...> VertexBindings(const Args&... arguments)
{
return {arguments...};
}
template <typename... Args>
ArrayInitializer<VkVertexInputAttributeDescription, Args...> VertexAttributes(const Args&... arguments)
{
return {arguments...};
}
VkVertexInputBindingDescription VertexBinding(uint32_t binding, uint32_t stride, VkVertexInputRate inputRate = VK_VERTEX_INPUT_RATE_VERTEX )
{
return {binding, stride, inputRate};
}
VkVertexInputAttributeDescription VertAttrib(uint32_t location, uint32_t binding, uint32_t offset, VkFormat format = VK_FORMAT_R32G32B32_SFLOAT)
{
return {location, binding, format, offset};
}
template <typename T1, typename T2, typename T3, typename T4>
void CreateGraphicsPipeline(VkRenderPass renderPass, const T1 &stages,
//const VkPipelineVertexInputStateCreateInfo &vertexInputState,
//const VkPipelineDynamicStateCreateInfo &dynamicState,
const T2 &vertexBindings,
const T3 &vertexAttributes,
const T4 &dynamicStates,
const VkPipelineInputAssemblyStateCreateInfo &asmInfo = TriangleListAssembly(),
const VkPipelineRasterizationStateCreateInfo &rasterInfo = DefaultRasterMode(),
const VkPipelineViewportStateCreateInfo &viewportInfo = DefaultViewportState(),
const VkPipelineMultisampleStateCreateInfo &multisampleState = DefaultMultisampleState(),
const VkPipelineDepthStencilStateCreateInfo &depthStencilState = DefaultDepthStencil(),
const VkPipelineColorBlendStateCreateInfo colorBlendState = DefaultColorBlend()
)
{
VkPipelineDynamicStateCreateInfo dynamic_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = dynamicStates.count,
.pDynamicStates = dynamicStates.array
};
VkPipelineVertexInputStateCreateInfo vertex_input_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = vertexBindings.count,
.pVertexBindingDescriptions = vertexBindings.array,
.vertexAttributeDescriptionCount = vertexAttributes.count,
.pVertexAttributeDescriptions = vertexAttributes.array
};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.setLayoutCount = 1;
pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayout;
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, NULL, &pipelineLayout));
VkGraphicsPipelineCreateInfo pipelineInfo = {VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
pipelineInfo.stageCount = stages.count;
pipelineInfo.pStages = stages.array;
pipelineInfo.pVertexInputState = &vertex_input_state;
pipelineInfo.pInputAssemblyState = &asmInfo;
pipelineInfo.pViewportState = &viewportInfo;
pipelineInfo.pRasterizationState = &rasterInfo;
pipelineInfo.pMultisampleState = &multisampleState;
pipelineInfo.pDepthStencilState = &depthStencilState;
pipelineInfo.pColorBlendState = &colorBlendState;
pipelineInfo.pDynamicState = &dynamic_state;
pipelineInfo.renderPass = renderPass;
pipelineInfo.layout = pipelineLayout;
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &pipeline);
}
void CreateComputePipeline(const VkPipelineShaderStageCreateInfo &shaderStageCreateInfo)
{
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};