From 364eea8654a69dd0e79cdfa86c10992e6e275ccd Mon Sep 17 00:00:00 2001 From: Tomasz Kramkowski Date: Wed, 28 Feb 2018 00:35:15 +0000 Subject: init commit --- .gitignore | 13 + Makefile | 36 +++ fragment.frag | 10 + glslv.mk | 8 + nelem.h | 6 + shaders.h | 14 + strlist.c | 24 ++ strlist.h | 16 ++ validation.c | 103 +++++++ validation.h | 18 ++ vertex.vert | 26 ++ vulkan.c | 839 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 1113 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 fragment.frag create mode 100644 glslv.mk create mode 100644 nelem.h create mode 100644 shaders.h create mode 100644 strlist.c create mode 100644 strlist.h create mode 100644 validation.c create mode 100644 validation.h create mode 100644 vertex.vert create mode 100644 vulkan.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c76e195 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.o +*.spv +vulkan + +*.d + +tags +TAGS + +eprintf.h +eprintf.c + +config.mk diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..366e666 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +-include config.mk +include glslv.mk + +PROG := vulkan + +EPRINTF_PATH ?= ../eprintf +PKG_CONFIG ?= pkg-config +LN ?= ln -sf + +LIBS := vulkan glfw3 +CPPFLAGS += -D_POSIX_C_SOURCE=200112L -DGLFW_INCLUDE_VULKAN +CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) -std=c11 -MMD -MP +LDFLAGS += -Wl,--as-needed +LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBS)) +GLSLVFLAGS += -V + +OBJ := vulkan.o eprintf.o strlist.o fragment.o vertex.o +ifeq ("$(WITH_VALIDATION)", "yes") + CPPFLAGS += -DWITH_VALIDATION + OBJ += validation.o +endif + +DEP := $(OBJ:.o=.d) + +all: $(PROG) +$(PROG): $(OBJ) +clean: + $(RM) $(OBJ) $(DEP) $(PROG) + +include $(EPRINTF_PATH)/module.mk +deplinks: $(EPRINTF_FILES) + +-include $(DEP) + +.PHONY: all clean + diff --git a/fragment.frag b/fragment.frag new file mode 100644 index 0000000..91a094d --- /dev/null +++ b/fragment.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 color; +layout(location = 0) out vec4 out_color; + +void main() +{ + out_color = vec4(color, 1.0); +} diff --git a/glslv.mk b/glslv.mk new file mode 100644 index 0000000..0e402f9 --- /dev/null +++ b/glslv.mk @@ -0,0 +1,8 @@ +GLSLV ?= glslangValidator +COMPILE.spv = $(GLSLV) $(GLSLVFLAGS) +%.spv: %.vert + $(COMPILE.spv) $(OUTPUT_OPTION) $< +%.spv: %.frag + $(COMPILE.spv) $(OUTPUT_OPTION) $< +%.o: %.spv + $(LD) -r -b binary $(OUTPUT_OPTION) $< diff --git a/nelem.h b/nelem.h new file mode 100644 index 0000000..99b5f77 --- /dev/null +++ b/nelem.h @@ -0,0 +1,6 @@ +#ifndef VULKAN_NELEM_H +#define VULKAN_NELEM_H + +#define NELEM(a) (sizeof (a) / sizeof (a)[0]) + +#endif // VULKAN_NELEM_H diff --git a/shaders.h b/shaders.h new file mode 100644 index 0000000..3c58910 --- /dev/null +++ b/shaders.h @@ -0,0 +1,14 @@ +#ifndef VULKAN_SHADERS_H +#define VULKAN_SHADERS_H + +#include + +extern size_t _binary_vertex_spv_size; +extern void *_binary_vertex_spv_start; +extern void *_binary_vertex_spv_end; + +extern size_t _binary_fragment_spv_size; +extern void *_binary_fragment_spv_start; +extern void *_binary_fragment_spv_end; + +#endif // VULKAN_SHADERS_H diff --git a/strlist.c b/strlist.c new file mode 100644 index 0000000..58faa99 --- /dev/null +++ b/strlist.c @@ -0,0 +1,24 @@ +#include +#include +#include + +#include "eprintf.h" +#include "strlist.h" + +void sl_append(struct strlist *sl, const char **list, int count) +{ + assert(sl != NULL); + + sl->list = erealloc(sl->list, (sl->count + count) * sizeof *sl->list); + memcpy(sl->list + sl->count, list, count * sizeof *list); + sl->count += count; +} + +void sl_free(struct strlist *sl) +{ + assert(sl != NULL); + + free(sl->list); + sl->list = NULL; + sl->count = 0; +} diff --git a/strlist.h b/strlist.h new file mode 100644 index 0000000..6c60a79 --- /dev/null +++ b/strlist.h @@ -0,0 +1,16 @@ +#ifndef VULKAN_STRLIST_H +#define VULKAN_STRLIST_H + +struct strlist { + const char **list; + int count; +}; + +void sl_append(struct strlist *sl, const char **strs, int nstrs); +static inline void sl_append1(struct strlist *sl, const char *str) +{ + sl_append(sl, &str, 1); +} +void sl_free(struct strlist *sl); + +#endif // VULKAN_STRLIST_H diff --git a/validation.c b/validation.c new file mode 100644 index 0000000..6f24177 --- /dev/null +++ b/validation.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include + + +#include "eprintf.h" +#include "nelem.h" +#define WITH_VALIDATION +#include "validation.h" + +static const char *vlayers[] = { + "VK_LAYER_LUNARG_standard_validation", +}; +static const char *vexts[] = { + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, +}; + +// callback: Validation callback +static VKAPI_ATTR VkBool32 VKAPI_CALL callback( + VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT type, + uint64_t obj, size_t location, int32_t code, const char + *prefix, const char *msg, void *data) +{ + (void)flags; (void)type; (void)obj; (void)location; + (void)code; (void)prefix; (void)data; + weprintf("Validation: %s", msg); + + return VK_FALSE; +} + +// havelayers: Check if all required validation layers are supported +static bool havelayers(void) +{ + VkLayerProperties *instlrp; + uint32_t ninstlrp; + static enum { UNK, NO, YES } have = UNK; + + if (have != UNK) return have == YES; + have = NO; + + vkEnumerateInstanceLayerProperties(&ninstlrp, NULL); + instlrp = emalloc(ninstlrp * sizeof *instlrp); + vkEnumerateInstanceLayerProperties(&ninstlrp, instlrp); + + for (int vl = 0; vl < NELEM(vlayers); vl++) { + for (uint32_t il = 0; il < ninstlrp; il++) + if (strcmp(vlayers[vl], instlrp[il].layerName) == 0) + goto next; + goto fail_layers; + next: ; + } + have = YES; +fail_layers: free(instlrp); + + return have == YES; +} + +// validation_info: Append validation layers and extension +void validation_info(struct strlist *layers, struct strlist *exts) +{ + if (havelayers() && layers != NULL) + sl_append(layers, vlayers, NELEM(vlayers)); + if (exts != NULL) sl_append(exts, vexts, NELEM(vexts)); +} + +// validation_createcb: Create the validation callback +void *validation_createcb(VkInstance inst) +{ + VkDebugReportCallbackCreateInfoEXT rccinf; + PFN_vkCreateDebugReportCallbackEXT func; + VkDebugReportCallbackEXT *cb; + + func = (PFN_vkCreateDebugReportCallbackEXT) + vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT"); + if (func == NULL) + eprintf("Validation: Could not create callback: Failed to get constructor"); + + rccinf = (VkDebugReportCallbackCreateInfoEXT){ + .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + .flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, + .pfnCallback = callback, + }; + + cb = emalloc(sizeof *cb); + if (func(inst, &rccinf, NULL, cb) != VK_SUCCESS) + eprintf("Validation: Could not create callback: Constructor"); + + return cb; +} + +// validation_destroycb: Destroy the validation callback +void validation_destroycb(VkInstance inst, void *cb) +{ + PFN_vkDestroyDebugReportCallbackEXT func; + + func = (PFN_vkDestroyDebugReportCallbackEXT) + vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT"); + if (func == NULL) + eprintf("Validation: Could not destroy callback: Failed to get destructor"); + func(inst, cb, NULL); + free(cb); +} diff --git a/validation.h b/validation.h new file mode 100644 index 0000000..44b1096 --- /dev/null +++ b/validation.h @@ -0,0 +1,18 @@ +#ifndef VULKAN_VALIDATION_H +#define VULKAN_VALIDATION_H + +#include + +#include "strlist.h" + +#ifdef WITH_VALIDATION +void validation_info(struct strlist *layers, struct strlist *exts); +void *validation_createcb(VkInstance inst); +void validation_destroycb(VkInstance inst, void *cb); +#else +#define validation_info(a, b) +#define validation_createcb(a) (NULL) +#define validation_destroycb(a, b) +#endif + +#endif // VULKAN_VALIDATION_H diff --git a/vertex.vert b/vertex.vert new file mode 100644 index 0000000..39c5faa --- /dev/null +++ b/vertex.vert @@ -0,0 +1,26 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +out gl_PerVertex { + vec4 gl_Position; +}; + +layout(location = 0) out vec3 color; + +vec2 positions[3] = vec2[]( + vec2( 0.0, -0.5), + vec2( 0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() +{ + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + color = colors[gl_VertexIndex]; +} diff --git a/vulkan.c b/vulkan.c new file mode 100644 index 0000000..420be16 --- /dev/null +++ b/vulkan.c @@ -0,0 +1,839 @@ +#include +#include +#include +#include +#include +#include + +#include "eprintf.h" +#include "nelem.h" +#include "shaders.h" +#include "strlist.h" +#include "validation.h" + +#define LIST_REALLOC(l) (l)->list = erealloc((l)->list, (l)->count * sizeof *(l)->list) +#define LIST_FOREACH(l, i) for (uint32_t i = 0; i < (l)->count; i++) + +enum { + WINWIDTH = 800, + WINHEIGHT = 600, +}; +static const char *NAME = "Vulkan Test"; + +enum qfams { + QF_GRAPHICS, + QF_PRESENT, + QF_MAX, +}; + +const char *devexts[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, +}; + +struct swp { + VkSwapchainKHR swp; + struct swpimgs { + VkImage *list; + uint32_t count; + } imgs; + VkFormat format; + VkExtent2D extent; +}; + +struct ppln { + VkPipeline ppln; + VkPipelineLayout layout; +}; + +struct views { + VkImageView *list; + uint32_t count; +}; + +struct fbs { + VkFramebuffer *list; + uint32_t count; +}; + +struct cmdbufs { + VkCommandBuffer *list; + uint32_t count; +}; + +struct swpdtl { + VkSurfaceCapabilitiesKHR caps; + struct swpfmts { + VkSurfaceFormatKHR *list; + uint32_t count; + } fmts; + struct swppmodes { + VkPresentModeKHR *list; + uint32_t count; + } pmodes; +}; + +static GLFWwindow *createwin(int width, int height, const char *title) +{ + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + return glfwCreateWindow(width, height, title, NULL, NULL); +} + +static void glfwexts(struct strlist *exts) +{ + const char **gexts; + uint32_t ngexts; + + assert(exts != NULL); + + gexts = glfwGetRequiredInstanceExtensions(&ngexts); + sl_append(exts, gexts, ngexts); +} + +static VkInstance createinst(void) +{ + struct strlist layers = { 0 }, exts = { 0 }; + VkInstanceCreateInfo info; + VkInstance inst; + VkResult res; + + glfwexts(&exts); + validation_info(&layers, &exts); + info = (VkInstanceCreateInfo){ + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &(VkApplicationInfo){ + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pApplicationName = NAME, + .applicationVersion = VK_MAKE_VERSION(1, 0, 0), + .pEngineName = NAME, + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = VK_API_VERSION_1_0, + }, + .enabledExtensionCount = exts.count, + .ppEnabledExtensionNames = exts.list, + .enabledLayerCount = layers.count, + .ppEnabledLayerNames = layers.list, + }; + + + res = vkCreateInstance(&info, NULL, &inst); + if (res != VK_SUCCESS) + eprintf("Could not create instance"); + + sl_free(&layers); + sl_free(&exts); + + return inst; +} + +static bool hasdevexts(VkPhysicalDevice phy) +{ + bool hasext[NELEM(devexts)] = { 0 }; + VkExtensionProperties *extprops; + uint32_t nextprops; + + vkEnumerateDeviceExtensionProperties(phy, NULL, &nextprops, NULL); + extprops = emalloc(nextprops * sizeof *extprops); + vkEnumerateDeviceExtensionProperties(phy, NULL, &nextprops, extprops); + + for (uint32_t pi = 0; pi < nextprops; pi++) { + for (int xi = 0; xi < NELEM(devexts); xi++) { + if (strcmp(devexts[xi], extprops[pi].extensionName) == 0) { + hasext[xi] = true; + break; + } + } + } + + free(extprops); + + for (int i = 0; i < NELEM(hasext); i++) + if (!hasext[i]) + return false; + + return true; +} + +static bool isdevsuitable(VkPhysicalDevice phy, const int *qf, const struct swpdtl *sdtl) +{ + assert(qf != NULL); + + return qf[QF_GRAPHICS] >= 0 && qf[QF_PRESENT] >= 0 && + hasdevexts(phy) && sdtl->fmts.count > 0 && + sdtl->pmodes.count > 0; +} + +static void getdevqfams(int *qf, VkPhysicalDevice phy, VkSurfaceKHR surf) +{ + VkQueueFamilyProperties *qfprops; + uint32_t nqfprops; + + assert(qf != NULL); + + vkGetPhysicalDeviceQueueFamilyProperties(phy, &nqfprops, NULL); + qfprops = emalloc(nqfprops * sizeof *qfprops); + vkGetPhysicalDeviceQueueFamilyProperties(phy, &nqfprops, qfprops); + + for (int i = 0; i < QF_MAX; i++) qf[i] = -1; + + for (uint32_t i = 0; i < nqfprops; i++) { + VkQueueFamilyProperties *qfp = &qfprops[i]; + VkBool32 present = false; + + if (qfp->queueCount <= 0) continue; + if (qf[QF_GRAPHICS] < 0 && qfp->queueFlags & VK_QUEUE_GRAPHICS_BIT) + qf[QF_GRAPHICS] = i; + vkGetPhysicalDeviceSurfaceSupportKHR(phy, i, surf, &present); + if (qf[QF_PRESENT] < 0 && present) qf[QF_PRESENT] = i; + } + + free(qfprops); +} + +static void getswpdtl(struct swpdtl *sdtl, VkPhysicalDevice phy, VkSurfaceKHR surf) +{ + assert(sdtl != NULL); + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phy, surf, &sdtl->caps); + + vkGetPhysicalDeviceSurfaceFormatsKHR(phy, surf, &sdtl->fmts.count, NULL); + LIST_REALLOC(&sdtl->fmts); + vkGetPhysicalDeviceSurfaceFormatsKHR(phy, surf, &sdtl->fmts.count, sdtl->fmts.list); + + vkGetPhysicalDeviceSurfacePresentModesKHR(phy, surf, &sdtl->pmodes.count, NULL); + LIST_REALLOC(&sdtl->pmodes); + vkGetPhysicalDeviceSurfacePresentModesKHR(phy, surf, &sdtl->pmodes.count, sdtl->pmodes.list); +} +static void freeswpdtl(struct swpdtl *sdtl) +{ + assert(sdtl != NULL); + + free(sdtl->fmts.list); + free(sdtl->pmodes.list); +} + +static VkPhysicalDevice pickphy(int *qf, struct swpdtl *sdtl, VkInstance inst, VkSurfaceKHR surf) +{ + VkPhysicalDevice *phys, phy; + uint32_t nphys; + + assert(qf != NULL); + + vkEnumeratePhysicalDevices(inst, &nphys, NULL); + phys = emalloc(nphys * sizeof *phys); + vkEnumeratePhysicalDevices(inst, &nphys, phys); + + phy = VK_NULL_HANDLE; + + for (uint32_t i = 0; i < nphys; i++) { + getdevqfams(qf, phys[i], surf); + getswpdtl(sdtl, phys[i], surf); + if (isdevsuitable(phys[i], qf, sdtl)) { + phy = phys[i]; + break; + } + } + + if (phy == VK_NULL_HANDLE) + eprintf("No suitable GPU found"); + + free(phys); + + return phy; +} + +static int loaddqcinfs(VkDeviceQueueCreateInfo *dqcinfs, const int *qf, const float *prio) +{ + int ndqcinfs = 0; + + for (int fi = 0; fi < QF_MAX; fi++) { + for (int cii = 0; cii < fi; cii++) + if (dqcinfs[cii].queueFamilyIndex == qf[fi]) + goto next; + dqcinfs[ndqcinfs++] = (VkDeviceQueueCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = qf[fi], + .queueCount = 1, + .pQueuePriorities = prio, + }; + next: ; + } + + return ndqcinfs; +} + +static VkDevice createdev(VkPhysicalDevice phy, const int *qf) +{ + VkDeviceQueueCreateInfo dqcinfs[QF_MAX]; + struct strlist layers = { 0 }; + VkDeviceCreateInfo info; + uint32_t ndqcinfs; + VkDevice dev; + VkResult res; + + ndqcinfs = loaddqcinfs(dqcinfs, qf, &(float){ 1 }); + validation_info(&layers, NULL); + info = (VkDeviceCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pQueueCreateInfos = dqcinfs, + .queueCreateInfoCount = ndqcinfs, + .pEnabledFeatures = &(VkPhysicalDeviceFeatures){ 0 }, + .enabledLayerCount = layers.count, + .ppEnabledLayerNames = layers.list, + .enabledExtensionCount = 1, + .ppEnabledExtensionNames = devexts, + }; + res = vkCreateDevice(phy, &info, NULL, &dev); + if (res != VK_SUCCESS) + eprintf("Could not create device"); + + return dev; +} + +static VkSurfaceFormatKHR getswpfmt(struct swpfmts *scf) +{ + assert(scf != NULL); + + if (scf->count == 1 && scf->list[0].format == VK_FORMAT_UNDEFINED) + return (VkSurfaceFormatKHR){ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; + + for (uint32_t i = 0; i < scf->count; i++) + if (scf->list[i].format == VK_FORMAT_B8G8R8A8_UNORM + && scf->list[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + return scf->list[i]; + + return scf->list[0]; +} + +static VkPresentModeKHR getswppmode(struct swppmodes *scp) +{ + VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; + + assert(scp != NULL); + + for (uint32_t i = 0; i < scp->count; i++) { + if (scp->list[i] == VK_PRESENT_MODE_MAILBOX_KHR) + return scp->list[i]; + if (scp->list[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) + mode = scp->list[i]; + } + + return mode; +} + +static VkExtent2D getswpextent(const VkSurfaceCapabilitiesKHR *caps) +{ + VkExtent2D extent = { WINWIDTH, WINHEIGHT }; + + if (caps->currentExtent.width != UINT32_MAX) + return caps->currentExtent; + + if (extent.width > caps->maxImageExtent.width) extent.width = caps->maxImageExtent.width; + if (extent.width < caps->minImageExtent.width) extent.width = caps->minImageExtent.width; + + if (extent.height > caps->maxImageExtent.height) extent.height = caps->maxImageExtent.height; + if (extent.height < caps->minImageExtent.height) extent.height = caps->minImageExtent.height; + + return extent; +} + +static void createswp(struct swp *swp, VkSurfaceKHR surf, VkDevice dev, const int *qf, struct swpdtl *sdtl) +{ + VkSwapchainCreateInfoKHR info; + VkPresentModeKHR pmode; + VkSurfaceFormatKHR fmt; + uint32_t qfi[QF_MAX]; + VkResult res; + + assert(swp != NULL); + assert(qf != NULL); + assert(sdtl != NULL); + + fmt = getswpfmt(&sdtl->fmts); + pmode = getswppmode(&sdtl->pmodes); + swp->extent = getswpextent(&sdtl->caps); + swp->imgs.count = sdtl->caps.minImageCount + 1; + if (sdtl->caps.maxImageCount != 0 && swp->imgs.count > sdtl->caps.maxImageCount) + swp->imgs.count = sdtl->caps.maxImageCount; + + for (int i = 0; i < QF_MAX; i++) qfi[i] = qf[i]; + + info = (VkSwapchainCreateInfoKHR){ + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = surf, + .minImageCount = swp->imgs.count, + .imageFormat = fmt.format, + .imageColorSpace = fmt.colorSpace, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = qf[QF_GRAPHICS] == qf[QF_PRESENT] + ? VK_SHARING_MODE_EXCLUSIVE + : VK_SHARING_MODE_CONCURRENT, + .queueFamilyIndexCount = QF_MAX, + .pQueueFamilyIndices = qfi, + .preTransform = sdtl->caps.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = pmode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + + res = vkCreateSwapchainKHR(dev, &info, NULL, &swp->swp); + if (res != VK_SUCCESS) + eprintf("Could not create swapchain"); + + swp->imgs = (struct swpimgs){ 0 }; + vkGetSwapchainImagesKHR(dev, swp->swp, &swp->imgs.count, NULL); + LIST_REALLOC(&swp->imgs); + vkGetSwapchainImagesKHR(dev, swp->swp, &swp->imgs.count, swp->imgs.list); +} + +static void createviews(struct views *views, VkDevice dev, const struct swpimgs *imgs, VkFormat fmt) +{ + VkImageViewCreateInfo info; + VkResult res; + + assert(views != NULL); + assert(imgs != NULL); + + *views = (struct views){ .count = imgs->count }; + LIST_REALLOC(views); + + LIST_FOREACH(views, i) { + info = (VkImageViewCreateInfo){ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = imgs->list[i], + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = fmt, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + res = vkCreateImageView(dev, &info, NULL, &views->list[i]); + if (res != VK_SUCCESS) + eprintf("Could not create image view"); + } +} + +static void destroyviews(struct views *views, VkDevice dev) +{ + LIST_FOREACH(views, i) vkDestroyImageView(dev, views->list[i], NULL); +} + +static VkShaderModule createsmod(VkDevice dev, const void *data, size_t size) +{ + VkShaderModuleCreateInfo smcinf; + VkShaderModule smod; + VkResult res; + + assert(data != NULL); + + smcinf = (VkShaderModuleCreateInfo){ + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = size, + .pCode = data, + }; + + res = vkCreateShaderModule(dev, &smcinf, NULL, &smod); + if (res != VK_SUCCESS) + eprintf("Could not create shader module"); + + return smod; +} + +static VkRenderPass createpass(VkDevice dev, VkFormat fmt) +{ + VkRenderPassCreateInfo info; + VkRenderPass pass; + VkResult res; + + info = (VkRenderPassCreateInfo){ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = 1, + .pAttachments = &(VkAttachmentDescription){ + .format = fmt, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + }, + .subpassCount = 1, + .pSubpasses = &(VkSubpassDescription){ + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .colorAttachmentCount = 1, + .pColorAttachments = &(VkAttachmentReference){ + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }, + }, + .dependencyCount = 1, + .pDependencies = &(VkSubpassDependency){ + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = 0, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + }, + }; + + res = vkCreateRenderPass(dev, &info, NULL, &pass); + if (res != VK_SUCCESS) + eprintf("Could not create render pass"); + + return pass; +} + +static void createpipeline(struct ppln *ppln, VkDevice dev, const VkExtent2D *extent) +{ + VkGraphicsPipelineCreateInfo gpcinf; + VkPipelineLayoutCreateInfo plcinf; + VkShaderModule fsmod, vsmod; + VkResult res; + + assert(ppln != NULL); + assert(extent != NULL); + + plcinf = (VkPipelineLayoutCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + /* .setLayoutCount = 0, */ + /* .pSetLayouts = NULL, */ + /* .pushConstantRangeCount = 0, */ + /* .pPushConstantRanges = 0, */ + }; + + res = vkCreatePipelineLayout(dev, &plcinf, NULL, &ppln->layout); + if (res != VK_SUCCESS) + eprintf("Could not create pipeline layout"); + + vsmod = createsmod(dev, _binary_vertex_spv_start, _binary_vertex_spv_size); + fsmod = createsmod(dev, _binary_fragment_spv_start, _binary_fragment_spv_size); + + gpcinf = (VkGraphicsPipelineCreateInfo){ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = 2, + .pStages = (VkPipelineShaderStageCreateInfo []){ + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = vsmod, + .pName = "main", + }, { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = fsmod, + .pName = "main", + }, + }, + .pVertexInputState = &(VkPipelineVertexInputStateCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 0, + .pVertexBindingDescriptions = NULL, + .vertexAttributeDescriptionCount = 0, + .pVertexAttributeDescriptions = NULL, + }, + .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE, + }, + .pViewportState = &(VkPipelineViewportStateCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .pViewports = &(VkViewport){ + .x = 0.0f, + .y = 0.0f, + .width = (float)extent->width, + .height = (float)extent->height, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }, + .scissorCount = 1, + .pScissors = &(VkRect2D){ + .offset = { 0, 0 }, + .extent = *extent, + }, + }, + .pRasterizationState = &(VkPipelineRasterizationStateCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .lineWidth = 1.0f, + .cullMode = VK_CULL_MODE_BACK_BIT, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .depthBiasEnable = VK_FALSE, + /* .depthBiasConstantFactor = 0, */ + /* .depthBiasClamp = 0, */ + /* .depthBiasSlopeFactor = 0, */ + }, + .pMultisampleState = &(VkPipelineMultisampleStateCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .sampleShadingEnable = VK_FALSE, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + /* .minSampleShading = 1, */ + /* .pSampleMask = NULL, */ + /* .alphaToCoverageEnable = VK_FALSE, */ + /* .alphaToOneEnable = VK_FALSE, */ + }, + .pDepthStencilState = NULL, + .pColorBlendState = &(VkPipelineColorBlendStateCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = VK_FALSE, + /* .logicOp = VK_LOGIC_OP_COPY, */ + .attachmentCount = 1, + .pAttachments = &(VkPipelineColorBlendAttachmentState){ + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + .blendEnable = VK_FALSE, + /* .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, */ + /* .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, */ + /* .colorBlendOp = VK_BLEND_OP_ADD, */ + /* .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, */ + /* .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, */ + /* .alphaBlendOp = VK_BLEND_OP_ADD, */ + }, + /* .blendConstants[0] = 0, */ + /* .blendConstants[1] = 0, */ + /* .blendConstants[2] = 0, */ + /* .blendConstants[3] = 0, */ + }, + .pDynamicState = NULL, + }; + + res = vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &gpcinf, NULL, &ppln->ppln); + if (res != VK_SUCCESS) + eprintf("Could not create graphics pipeline"); + + vkDestroyShaderModule(dev, fsmod, NULL); + vkDestroyShaderModule(dev, vsmod, NULL); +} + +static void destroyppln(struct ppln *ppln, VkDevice dev) +{ + vkDestroyPipeline(dev, ppln->ppln, NULL); + vkDestroyPipelineLayout(dev, ppln->layout, NULL); +} + +static void createfbs(struct fbs *fbs, VkDevice dev, const struct views *views, VkRenderPass pass, const VkExtent2D *extent) +{ + VkFramebufferCreateInfo info; + VkResult res; + + assert(fbs != NULL); + assert(views != NULL); + + *fbs = (struct fbs){ .count = views->count }; + LIST_REALLOC(fbs); + + LIST_FOREACH(fbs, i) { + info = (VkFramebufferCreateInfo){ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = pass, + .attachmentCount = 1, + .pAttachments = &views->list[i], + .width = extent->width, + .height = extent->height, + .layers = 1, + }; + res = vkCreateFramebuffer(dev, &info, NULL, &fbs->list[i]); + if (res != VK_SUCCESS) + eprintf("Could not create framebuffer"); + } +} + +static void destroyfbs(struct fbs *fbs, VkDevice dev) +{ + LIST_FOREACH(fbs, i) vkDestroyFramebuffer(dev, fbs->list[i], NULL); +} + +static VkCommandPool createcpool(struct cmdbufs *cmdbufs, VkDevice dev, const int *qf, + const struct fbs *fbs, VkRenderPass pass, + VkExtent2D *extent, const struct ppln *ppln) +{ + VkCommandBufferAllocateInfo cbainf; + VkCommandPoolCreateInfo cpcinf; + VkCommandPool cpool; + VkResult res; + + assert(cmdbufs != NULL); + assert(qf != NULL); + + cpcinf = (VkCommandPoolCreateInfo){ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .queueFamilyIndex = qf[QF_GRAPHICS], + }; + + res = vkCreateCommandPool(dev, &cpcinf, NULL, &cpool); + if (res != VK_SUCCESS) + eprintf("Could not create command pool"); + + *cmdbufs = (struct cmdbufs){ .count = fbs->count }; + LIST_REALLOC(cmdbufs); + cbainf = (VkCommandBufferAllocateInfo){ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = cpool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = fbs->count, + }; + res = vkAllocateCommandBuffers(dev, &cbainf, cmdbufs->list); + if (res != VK_SUCCESS) + eprintf("Could not create command buffers"); + + LIST_FOREACH(cmdbufs, i) { + vkBeginCommandBuffer(cmdbufs->list[i], &(VkCommandBufferBeginInfo){ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, + /* .pInheritanceInfo = NULL, */ + }); + vkCmdBeginRenderPass(cmdbufs->list[i], &(VkRenderPassBeginInfo){ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = pass, + .framebuffer = fbs->list[i], + .renderArea.offset = {0, 0}, + .renderArea.extent = *extent, + .clearValueCount = 1, + .pClearValues = &(VkClearValue){ + .color = { + .float32 = { 0, 0, 0, 1 } + } + }, + }, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(cmdbufs->list[i], VK_PIPELINE_BIND_POINT_GRAPHICS, ppln->ppln); + vkCmdDraw(cmdbufs->list[i], 3, 1, 0, 0); + vkCmdEndRenderPass(cmdbufs->list[i]); + }; + + return cpool; +} + +static VkSemaphore createsem(VkDevice dev) +{ + VkSemaphore sem; + VkResult res; + + res = vkCreateSemaphore(dev, &(VkSemaphoreCreateInfo){ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }, NULL, &sem); + if (res != VK_SUCCESS) + eprintf("Could not create semaphore"); + + return sem; +} + +void drawframe(VkDevice dev, VkSwapchainKHR swp, VkQueue graphq, + VkQueue presq, VkSemaphore semavail, + VkSemaphore semfin, const struct cmdbufs *cmdbufs) +{ + VkPresentInfoKHR pinf; + VkSubmitInfo sinf; + uint32_t i; + + vkAcquireNextImageKHR(dev, swp, UINT64_MAX, semavail, VK_NULL_HANDLE, &i); + sinf = (VkSubmitInfo){ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &semavail, + .pWaitDstStageMask = &(VkPipelineStageFlags){ + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT + }, + .commandBufferCount = 1, + .pCommandBuffers = &cmdbufs->list[i], + .signalSemaphoreCount = 1, + .pSignalSemaphores = &semfin, + }; + vkQueueSubmit(graphq, 1, &sinf, VK_NULL_HANDLE); + + pinf = (VkPresentInfoKHR){ + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &semfin, + .swapchainCount = 1, + .pSwapchains = &swp, + .pImageIndices = &i, + /* .pResults = NULL, */ + }; + + vkQueuePresentKHR(presq, &pinf); +} + +int main(void) +{ + struct cmdbufs cmdbufs = { 0 }; + VkSemaphore semavail, semfin; + struct swpdtl sdtl = { 0 }; + struct ppln ppln = { 0 }; + struct fbs fbs = { 0 }; + VkQueue graphq, presq; + VkPhysicalDevice phy; + VkCommandPool cpool; + struct views views; + VkRenderPass pass; + VkSurfaceKHR surf; + GLFWwindow *win; + VkInstance inst; + int qf[QF_MAX]; + struct swp swp; + VkDevice dev; + VkResult res; + void *cb; + + setprogname("vulkan"); + + glfwInit(); + win = createwin(WINWIDTH, WINHEIGHT, NAME); + inst = createinst(); + cb = validation_createcb(inst); + res = glfwCreateWindowSurface(inst, win, NULL, &surf); + if (res != VK_SUCCESS) + eprintf("Could not create window surface"); + phy = pickphy(qf, &sdtl, inst, surf); + dev = createdev(phy, qf); + vkGetDeviceQueue(dev, qf[QF_GRAPHICS], 0, &graphq); + vkGetDeviceQueue(dev, qf[QF_PRESENT], 0, &presq); + createswp(&swp, surf, dev, qf, &sdtl); + createviews(&views, dev, &swp.imgs, swp.format); + pass = createpass(dev, swp.format); + createpipeline(&ppln, dev, &swp.extent); + createfbs(&fbs, dev, &views, pass, &swp.extent); + cpool = createcpool(&cmdbufs, dev, qf, &fbs, pass, &swp.extent, &ppln); + semavail = createsem(dev); + semfin = createsem(dev); + + while (!glfwWindowShouldClose(win)) { + glfwPollEvents(); + drawframe(dev, swp.swp, graphq, presq, semavail, semfin, &cmdbufs); + } + + vkDeviceWaitIdle(dev); + + vkDestroySemaphore(dev, semfin, NULL); + vkDestroySemaphore(dev, semavail, NULL); + vkDestroyCommandPool(dev, cpool, NULL); + destroyfbs(&fbs, dev); + destroyppln(&ppln, dev); + vkDestroyRenderPass(dev, pass, NULL); + destroyviews(&views, dev); + free(views.list); + vkDestroySwapchainKHR(dev, swp.swp, NULL); + vkDestroySurfaceKHR(inst, surf, NULL); + vkDestroyDevice(dev, NULL); + freeswpdtl(&sdtl); + validation_destroycb(inst, cb); + vkDestroyInstance(inst, NULL); + glfwDestroyWindow(win); + glfwTerminate(); +} -- cgit v1.2.3-70-g09d2