vulkan-playground/vkgraphics.cpp

290 lines
10 KiB
C++

#include "vulkan_pipeline_utl.h"
#include "vulkan_utl.h"
#include "vulkan_framebuffer_utl.h"
#include "vulkan_texture_utl.h"
#include "vulkan_contructors.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
),
DepthStencil(true, true)
);
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[MAX_SWAPCHAIN_IMAGES], swapchainPresentSemaphore[MAX_SWAPCHAIN_IMAGES];
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;
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
// render full screen without overlapping taskbar
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
window = glfwCreateWindow(outWidth, outHeight, windowTitle, nullptr, nullptr);
if (!window) {
glfwTerminate();
return;
}
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); });
}
void DestroyWindow()
{
if(window)
glfwDestroyWindow(window);
glfwTerminate();
}
void CreateSwapchain(uint32_t width, uint32_t height)
{
VK_CHECK_RESULT(CallWith($M(VkXlibSurfaceCreateInfoKHR{VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR},
$(dpy) = glfwGetX11Display(), $(window) = glfwGetX11Window(window)),
vkCreateXlibSurfaceKHR(context.instance, &ref, nullptr, &surface)));
CallWith($M(
VkSwapchainCreateInfoKHR{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR},
$(surface) = surface, $(minImageCount) = 4,
$(imageFormat) = VK_FORMAT_B8G8R8A8_UNORM,
$(imageColorSpace) = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
$(imageExtent) = VkExtent2D{width, height},
$(imageArrayLayers),$(queueFamilyIndexCount),$(clipped),
$(imageUsage) = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
$(pQueueFamilyIndices) = &dev.defaultFamilyIndex,
$(preTransform) = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
$(compositeAlpha) = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
$(presentMode) = VK_PRESENT_MODE_FIFO_KHR),
vkCreateSwapchainKHR(dev.device, &ref, nullptr, &swapchain));
numSwapchainImages = MAX_SWAPCHAIN_IMAGES;
VK_CHECK_RESULT(vkGetSwapchainImagesKHR(dev.device, swapchain, &numSwapchainImages, swapchainImages));
VkSemaphoreCreateInfo semaphoreCreateInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
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);
VK_CHECK_RESULT(vkCreateSemaphore(dev.device, &semaphoreCreateInfo, nullptr, &swapchainPresentSemaphore[i]));
VK_CHECK_RESULT(vkCreateSemaphore(dev.device, &semaphoreCreateInfo, nullptr, &swapchainRenderSemaphore[i]));
}
}
unsigned int AcquireImage(int sem_index, VkFence fence = NULL)
{
unsigned int index;
vkAcquireNextImageKHR(dev.device, swapchain, UINT64_MAX, swapchainRenderSemaphore[sem_index], fence, &index);
return index;
}
void PresentImage(unsigned int index)
{
CallWith(PresentInfo(&swapchain, &index, &swapchainPresentSemaphore[index]),
vkQueuePresentKHR(dev.defautQueue, &ref));
}
void DestroySwapchain()
{
for(int i = 0; i < numSwapchainImages; i++)
{
swapchainFbs[i].Destroy();
vkDestroySemaphore(dev.device, swapchainPresentSemaphore[i], NULL);
vkDestroySemaphore(dev.device, swapchainRenderSemaphore[i], NULL);
}
vkDestroySwapchainKHR(dev.device, swapchain, NULL);
vkDestroySurfaceKHR(context.instance,surface,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()
{
#ifndef NDEBUG
#define ENABLE_VALIDATION_LAYERS 1
#else
#define ENABLE_VALIDATION_LAYERS 0
#endif
context.Create("streamingengine", "vulkan-playground-server", ENABLE_VALIDATION_LAYERS);
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 });
//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]);
VK_CHECK_RESULT(CallWith(FenceInfo(VK_FENCE_CREATE_SIGNALED_BIT),
vkCreateFence(dev.device, &ref, NULL, &chainFences[i])));
}
uint32_t idx = 0;
int sem_idx = 0;
while(true)
{
updateUniformData();
sem_idx = (sem_idx + 1) % numSwapchainImages;
waitFence(sem_idx);
idx = AcquireImage(sem_idx);
VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
vkResetFences(dev.device,1, &chainFences[sem_idx]);
CallWith(
SubmitInfo(
commandBuffers[idx],$(waitSemaphoreCount), $(signalSemaphoreCount),
$(pWaitSemaphores) = &swapchainRenderSemaphore[sem_idx],
$(pSignalSemaphores) = &swapchainPresentSemaphore[idx],
$(pWaitDstStageMask) = &waitDstStageMask),
vkQueueSubmit(dev.defautQueue, 1, &ref, chainFences[sem_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;
}