diff options
| author | Anna (navi) Figueiredo Gomes <navi@vlhl.dev> | 2023-12-27 01:11:12 +0100 | 
|---|---|---|
| committer | Anna (navi) Figueiredo Gomes <navi@vlhl.dev> | 2023-12-27 01:11:12 +0100 | 
| commit | 01ba05cad0f685fa953d7db490f0105d09a6b6d8 (patch) | |
| tree | e659819afa9096e28b3b513de3fb5206eb280d5a | |
| download | vlkn-01ba05cad0f685fa953d7db490f0105d09a6b6d8.tar.xz | |
initial
Signed-off-by: Anna (navi) Figueiredo Gomes <navi@vlhl.dev>
| -rw-r--r-- | Makefile | 16 | ||||
| -rw-r--r-- | main.c | 836 | ||||
| -rw-r--r-- | shader.frag | 8 | ||||
| -rw-r--r-- | shader.vert | 20 | 
4 files changed, 880 insertions, 0 deletions
| diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d7a272c --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +CFLAGS = -O2 -g +LDFLAGS != pkg-config --libs vulkan sdl2 + +vulkan: main.c vert.spv frag.spv +	gcc $(CFLAGS) -o vk main.c $(LDFLAGS) + +%.spv: shader.% +	glslc $^ -o $@ + +.PHONY: test clean + +test: vulkan +	./vk + +clean: +	rm -f vk *.spv @@ -0,0 +1,836 @@ +#include <stdio.h> +#include <vulkan/vulkan.h> +#include <SDL2/SDL.h> +#include <SDL2/SDL_vulkan.h> +#include <stdbool.h> +#include <string.h> + +static SDL_Window *window = NULL; +static VkInstance instance = VK_NULL_HANDLE; +static VkPhysicalDevice phy_gpu = VK_NULL_HANDLE; +static VkDevice gpu = VK_NULL_HANDLE; +static VkSurfaceKHR surface = VK_NULL_HANDLE; +static VkQueue gfx_queue; +static VkQueue present_queue; +static VkSwapchainKHR swapchain; +static VkFormat swapchain_format; +static VkExtent2D swapchain_extent; +static VkRenderPass render_pass; +static VkPipelineLayout pipeline_layout; +static VkCommandPool command_pool; +static VkCommandBuffer command_buffer; +static VkPipeline graphics_pipeline; +static VkSemaphore image_avail; +static VkSemaphore render_finished; +static VkFence in_flight_fence; +static struct { +	size_t len; +	VkImage data[]; +} *swapchain_images; +static struct { +	size_t len; +	VkImageView data[]; +} *swapchain_image_views; +static struct { +	size_t len; +	VkFramebuffer data[]; +} *swapchain_buffers; + +struct code { +	size_t len; +	uint8_t data[]; +}; + +struct swapchain_details { +	VkSurfaceCapabilitiesKHR caps; +	struct surface_formats { +		size_t len; +		VkSurfaceFormatKHR data[]; +	} *formats; +	struct present_modes { +		size_t len; +		VkPresentModeKHR data[]; +	} *present_modes; +}; + +void free_swapchain_details(struct swapchain_details details) { +	free(details.formats); +	free(details.present_modes); +} + +struct swapchain_details query_swapchain(VkPhysicalDevice dev) { +	struct swapchain_details details = {0}; +	vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, surface, &details.caps); + +	uint32_t format_count = 0; +	vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &format_count, NULL); +	if (format_count != 0) { +		details.formats = malloc(sizeof(*details.formats) + sizeof(*details.formats->data) * format_count); +		vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &format_count, details.formats->data); +		details.formats->len = format_count; +	} + +	uint32_t present_mode_count = 0; +	vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &present_mode_count, NULL); +	if (present_mode_count != 0) { +		details.present_modes = malloc(sizeof(*details.present_modes) + sizeof(*details.present_modes->data) * format_count); +		vkGetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &present_mode_count, details.present_modes->data); +		details.present_modes->len = present_mode_count; +	} + +	return details; +} + +VkSurfaceFormatKHR pick_swapchain_format(const struct surface_formats *formats) { +	for (size_t i = 0; i < formats->len; i++) +		if (formats->data[i].format == VK_FORMAT_B8G8R8A8_SRGB +				&& formats->data[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) +			return formats->data[i]; +	return formats->data[0]; +} + +VkPresentModeKHR pick_present_mode(const struct present_modes *modes) { +	for (size_t i = 0; i < modes->len; i++) +		if (modes->data[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR) +			return modes->data[i]; + +	return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D pick_swap_extent(const VkSurfaceCapabilitiesKHR *caps) { +	if (caps->currentExtent.width != UINT32_MAX) +		return caps->currentExtent; + +	int32_t w, h; +	SDL_GetWindowSize(window, &w, &h); + +	VkExtent2D actual_extent = { +		.width = w > caps->minImageExtent.width ? w < caps->maxImageExtent.width ? w : caps->maxImageExtent.width : caps->minImageExtent.width, +		.height = h > caps->minImageExtent.height ? h < caps->maxImageExtent.height ? h : caps->maxImageExtent.height : caps->minImageExtent.height, +	}; + +	return actual_extent; +} + +struct queue_indices { +	enum { +		GRAPHICS = (1 << 0), +		PRESENT = (1 << 1) +	} found_queues; + +	uint32_t graphics; +	uint32_t present; +}; + +bool found_queue_indx(struct queue_indices inds) { +	return inds.found_queues == (GRAPHICS | PRESENT); +} + +void cleanup() { +	vkDeviceWaitIdle(gpu); +	vkDestroySemaphore(gpu, image_avail, NULL); +	vkDestroySemaphore(gpu, render_finished, NULL); +	vkDestroyFence(gpu, in_flight_fence, NULL); +	vkDestroyCommandPool(gpu, command_pool, NULL); +	for (size_t i = 0; i < swapchain_buffers->len; i++) { +		vkDestroyFramebuffer(gpu, swapchain_buffers->data[i], NULL); +	} +	vkDestroyPipeline(gpu, graphics_pipeline, NULL); +	vkDestroyPipelineLayout(gpu, pipeline_layout, NULL); +	vkDestroyRenderPass(gpu, render_pass, NULL); +	for (size_t i = 0; i < swapchain_image_views->len; i++) { +		vkDestroyImageView(gpu, swapchain_image_views->data[i], NULL); +	} +	vkDestroySwapchainKHR(gpu, swapchain, NULL); +	vkDestroySurfaceKHR(instance, surface, NULL); +	vkDestroyDevice(gpu, NULL); +	vkDestroyInstance(instance, NULL); +	SDL_DestroyWindow(window); +} + +void create_instance() { +	VkApplicationInfo app_info = { +		.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, +		.pApplicationName = "vk", +		.applicationVersion = VK_MAKE_VERSION(1, 0, 0), +		.pEngineName = "void", +		.engineVersion = VK_MAKE_VERSION(1, 0, 0), +		.apiVersion = VK_API_VERSION_1_0 +	}; + +	uint32_t sdl_exts = 0; +	SDL_Vulkan_GetInstanceExtensions(window, &sdl_exts, NULL); + +	const char *sdl_exts_names[sdl_exts]; +	SDL_Vulkan_GetInstanceExtensions(window, &sdl_exts, sdl_exts_names); + +	for (size_t i = 0; i < sdl_exts; i++) { +		puts(sdl_exts_names[i]); +	}; + +	VkInstanceCreateInfo create_info = { +		.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, +		.pApplicationInfo = &app_info, +		.enabledLayerCount = 0, +		.enabledExtensionCount = sdl_exts, +		.ppEnabledExtensionNames = sdl_exts_names, +	}; + +	VkResult res = vkCreateInstance(&create_info, NULL, &instance); +	if (res != VK_SUCCESS) { +		fputs("failed to create vk instance", stderr); +		exit(-1); +	} +} + +struct queue_indices find_queue_families(VkPhysicalDevice dev) { +	struct queue_indices queues_ind = {0}; + +	uint32_t queue_count = 0; +	vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_count, NULL); + +	VkQueueFamilyProperties *queues = calloc(queue_count, sizeof(*queues)); +	vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_count, queues); + +	for (size_t i = 0; i < queue_count; i++) { +		if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { +			queues_ind.found_queues |= GRAPHICS; +			queues_ind.graphics = i; +		} + +		VkBool32 present_support = false; +		vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, surface, &present_support); + +		if (present_support) { +			queues_ind.found_queues |= PRESENT; +			queues_ind.present = i; +		} +	} + +	free(queues); +	return queues_ind; +} + +bool is_device_ok(VkPhysicalDevice dev) { +	uint32_t ext_count; + +	vkEnumerateDeviceExtensionProperties(dev, NULL, &ext_count, NULL); + +	VkExtensionProperties *props = calloc(ext_count, sizeof(*props)); +	vkEnumerateDeviceExtensionProperties(dev, NULL, &ext_count, props); + +	bool swapchain = false; +	for (size_t i = 0; i < ext_count; i++) { +		if (strcmp(props[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) { +			swapchain = true; +			break; +		} +	} + +	free(props); + +	bool adequate_swapchain = false; +	if (swapchain) { +		struct swapchain_details details = query_swapchain(dev); +		adequate_swapchain = details.present_modes != NULL && details.formats != NULL; +		free_swapchain_details(details); +	} + +	return found_queue_indx(find_queue_families(dev)) && swapchain && adequate_swapchain; +} + +void pick_physical_dev() { +	uint32_t dev_count = 0; +	vkEnumeratePhysicalDevices(instance, &dev_count, NULL); + +	if (dev_count == 0) { +		fputs("no vulkan enabled gpus", stderr); +		exit(-1); +	} + +	VkPhysicalDevice *devs = calloc(dev_count, sizeof(*devs)); +	vkEnumeratePhysicalDevices(instance, &dev_count, devs); + +	for (size_t i = 0; i < dev_count; i++) +		if (is_device_ok(devs[i])) { +			phy_gpu = devs[i]; +			break; +		} + +	free(devs); + +	if (phy_gpu == VK_NULL_HANDLE) { +		fputs("couldn't find appropriate gpu", stderr); +		exit(-1); +	} +} + +void create_logical_dev() { +	struct queue_indices inds = find_queue_families(phy_gpu); + +	float queue_prio = 1.0f; + +	VkDeviceQueueCreateInfo queue_create_info[] = { +		{ +			.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, +			.queueFamilyIndex = inds.graphics, +			.queueCount = 1, +			.pQueuePriorities = &queue_prio +		}, +		{ +			.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, +			.queueFamilyIndex = inds.present, +			.queueCount = 1, +			.pQueuePriorities = &queue_prio +		}, +	}; + +	VkPhysicalDeviceFeatures dev_features = { 0 }; + +	const char * const ext = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + +	VkDeviceCreateInfo create_info = { +		.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, +		.pQueueCreateInfos = queue_create_info, +		.queueCreateInfoCount = 2, +		.pEnabledFeatures = &dev_features, +		.enabledExtensionCount = 1, +		.ppEnabledExtensionNames = &ext, +	}; + +	VkResult res = vkCreateDevice(phy_gpu, &create_info, NULL, &gpu); +	if (res != VK_SUCCESS) { +		fputs("failed to create logical device", stderr); +		exit(-1); +	} + +	vkGetDeviceQueue(gpu, inds.graphics, 0, &gfx_queue); +	vkGetDeviceQueue(gpu, inds.present, 0, &present_queue); +} + +void create_swapchain() { +	struct swapchain_details details = query_swapchain(phy_gpu); + +	VkSurfaceFormatKHR surface_format = pick_swapchain_format(details.formats); +	VkPresentModeKHR present_mode = pick_present_mode(details.present_modes); +	VkExtent2D extent = pick_swap_extent(&details.caps); + +	swapchain_format = surface_format.format; +	swapchain_extent = extent; + +	uint32_t image_count = details.caps.minImageCount + 1; +	if (details.caps.maxImageCount > 0 && image_count > details.caps.maxImageCount) +		image_count = details.caps.maxImageCount; + +	VkSwapchainCreateInfoKHR create_info = { +		.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, +		.surface = surface, +		.minImageCount = image_count, +		.imageFormat = surface_format.format, +		.imageColorSpace = surface_format.colorSpace, +		.imageExtent = extent, +		.imageArrayLayers = 1, +		.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, +		.preTransform = details.caps.currentTransform, +		.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, +		.presentMode = present_mode, +		.clipped = VK_TRUE, +		.oldSwapchain = VK_NULL_HANDLE, +	}; + +	struct queue_indices indx = find_queue_families(phy_gpu); +	uint32_t fam_indx[] = { indx.graphics, indx.present }; + +	if (indx.graphics != indx.present) { +		create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; +		create_info.queueFamilyIndexCount = 2; +		create_info.pQueueFamilyIndices = fam_indx; +	} else { +		create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; +	} + +	VkResult res = vkCreateSwapchainKHR(gpu, &create_info, NULL, &swapchain); +	if (res != VK_SUCCESS) { +		fputs("failed to create swapchain", stderr); +		exit(-1); +	} + +	free_swapchain_details(details); + +	uint32_t img_count = 0; +	vkGetSwapchainImagesKHR(gpu, swapchain, &img_count, NULL); + +	swapchain_images = malloc(sizeof(*swapchain_images) + sizeof(*swapchain_images->data) * img_count); +	swapchain_images->len = img_count; +	vkGetSwapchainImagesKHR(gpu, swapchain, &img_count, swapchain_images->data); + +} + +void create_image_views() { +	swapchain_image_views = malloc(sizeof(*swapchain_image_views) + sizeof(*swapchain_image_views->data) * swapchain_images->len); +	swapchain_image_views->len = swapchain_images->len; + +	for (size_t i = 0; i < swapchain_images->len; i++) { +		VkImageViewCreateInfo create_info = { +			.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, +			.image = swapchain_images->data[i], +			.viewType = VK_IMAGE_VIEW_TYPE_2D, +			.format = swapchain_format, +			.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 +			} +		}; + +		VkResult res = vkCreateImageView(gpu, &create_info, NULL, &swapchain_image_views->data[i]); +		if (res != VK_SUCCESS) { +			fputs("failed to create image view", stderr); +			exit(-1); +		} +	} +} + +struct code *readfile(const char *filename) { +	FILE *fp = fopen(filename, "rb"); +	if (!fp) { +		fprintf(stderr, "failed to open file %s", filename); +		exit(-1); +	} + +	fseek(fp, 0, SEEK_END); +	size_t len = ftell(fp); +	rewind(fp); + +	struct code *bytes = malloc(sizeof(*bytes) + sizeof(*bytes->data) * len); +	bytes->len = len; +	fread(bytes->data, sizeof(*bytes->data), len, fp); + +	fclose(fp); +	return bytes; +} + +VkShaderModule create_shader_module(const struct code *code) { +	VkShaderModuleCreateInfo create_info = { +		.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, +		.codeSize = code->len, +		.pCode = (uint32_t *)code->data +	}; + +	VkShaderModule module; +	VkResult res = vkCreateShaderModule(gpu, &create_info, NULL, &module); +	if (res != VK_SUCCESS) { +		fputs("failed to create shader module.", stderr); +		exit(-1); +	} + +	return module; +} + +void create_graphics_pipeline() { +	struct code *vert_shader_code = readfile("vert.spv"); +	struct code *frag_shader_code = readfile("frag.spv"); + +	VkShaderModule vert_shader = create_shader_module(vert_shader_code); +	VkShaderModule frag_shader = create_shader_module(frag_shader_code); + +	VkPipelineShaderStageCreateInfo vert_shader_info = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, +		.stage = VK_SHADER_STAGE_VERTEX_BIT, +		.module = vert_shader, +		.pName = "main" +	}; + +	VkPipelineShaderStageCreateInfo frag_shader_info = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, +		.stage = VK_SHADER_STAGE_FRAGMENT_BIT, +		.module = frag_shader, +		.pName = "main" +	}; + +	VkPipelineShaderStageCreateInfo shader_stages[] = { vert_shader_info, frag_shader_info }; + +	VkPipelineVertexInputStateCreateInfo vertex_input_info = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, +	}; + +	VkPipelineInputAssemblyStateCreateInfo input_assembly = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, +		.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, +		.primitiveRestartEnable = VK_FALSE +	}; + +	VkViewport viewport = { +		.x = 0.0f, +		.y = 0.0f, +		.width = swapchain_extent.width, +		.height = swapchain_extent.height, +		.minDepth = 0.0f, +		.maxDepth = 1.0f +	}; + +	VkRect2D scissor = { +		.offset = { 0, 0 }, +		.extent = swapchain_extent +	}; + +	VkDynamicState dyn_states[] = { +		VK_DYNAMIC_STATE_VIEWPORT, +		VK_DYNAMIC_STATE_SCISSOR +	}; + +	VkPipelineDynamicStateCreateInfo dynamic_state_info = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, +		.dynamicStateCount = 2, +		.pDynamicStates = dyn_states +	}; + +	VkPipelineViewportStateCreateInfo viewport_state = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, +		.viewportCount = 1, +		.scissorCount = 1 +	}; + +	VkPipelineRasterizationStateCreateInfo rasterizer = { +		.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, +	}; + +	VkPipelineMultisampleStateCreateInfo multisampling = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, +		.sampleShadingEnable = VK_FALSE, +		.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, +		.minSampleShading = 1.0f, +	}; + +	VkPipelineColorBlendAttachmentState color_state = { +		.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, +		.blendEnable = VK_FALSE +	}; + +	VkPipelineColorBlendStateCreateInfo color_blending = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, +		.logicOpEnable = VK_FALSE, +		.attachmentCount = 1, +		.pAttachments = &color_state +	}; + +	VkPipelineLayoutCreateInfo pipeline_layout_create_info = { +		.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, +	}; + +	VkResult res = vkCreatePipelineLayout(gpu, &pipeline_layout_create_info, NULL, &pipeline_layout); +	if (res != VK_SUCCESS) { +		fputs("failed to create pipeline layout", stderr); +		exit(-1); +	} + +	VkGraphicsPipelineCreateInfo pipeline_info = { +		.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, +		.stageCount = 2, +		.pStages = shader_stages, +		.pVertexInputState = &vertex_input_info, +		.pInputAssemblyState = &input_assembly, +		.pViewportState = &viewport_state, +		.pRasterizationState = &rasterizer, +		.pMultisampleState = &multisampling, +		.pColorBlendState = &color_blending, +		.pDynamicState = &dynamic_state_info, +		.layout = pipeline_layout, +		.renderPass = render_pass, +		.subpass = 0, +		.basePipelineHandle = VK_NULL_HANDLE, +		.basePipelineIndex = -1 +	}; + +	res = vkCreateGraphicsPipelines(gpu, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &graphics_pipeline); +	if (res != VK_SUCCESS) { +		fputs("failed to create graphics pipeline", stderr); +		exit(-1); +	} + +	vkDestroyShaderModule(gpu, frag_shader, NULL); +	vkDestroyShaderModule(gpu, vert_shader, NULL); +} + +void create_render_pass() { +	VkAttachmentDescription color_attach = { +		.format = swapchain_format, +		.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 +	}; + +	VkAttachmentReference color_attach_ref = { +		.attachment = 0, +		.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, +	}; + +	VkSubpassDescription subpass = { +		.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, +		.colorAttachmentCount = 1, +		.pColorAttachments = &color_attach_ref +	}; + +	VkSubpassDependency dep = { +		.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_WRITE_BIT +	}; + +	VkRenderPassCreateInfo create_info = { +		.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, +		.attachmentCount = 1, +		.pAttachments = &color_attach, +		.subpassCount = 1, +		.pSubpasses = &subpass, +		.dependencyCount = 1, +		.pDependencies = &dep +	}; + +	VkResult res = vkCreateRenderPass(gpu, &create_info, NULL, &render_pass); +	if (res != VK_SUCCESS) { +		fputs("failed to create render pass", stderr); +		exit(-1); +	} +} + +void create_framebuffers() { +	swapchain_buffers = malloc(sizeof(*swapchain_buffers) + sizeof(*swapchain_buffers->data) * swapchain_image_views->len); +	swapchain_buffers->len = swapchain_image_views->len; + +	for (size_t i = 0; i < swapchain_image_views->len; i++) { +		VkImageView attachs[] = { +			swapchain_image_views->data[i] +		}; + +		VkFramebufferCreateInfo framebuffer_info = { +			.sType =VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, +			.renderPass = render_pass, +			.attachmentCount = 1, +			.pAttachments = attachs, +			.width = swapchain_extent.width, +			.height = swapchain_extent.height, +			.layers = 1 +		}; + +		VkResult res = vkCreateFramebuffer(gpu, &framebuffer_info, NULL, &swapchain_buffers->data[i]); +		if (res != VK_SUCCESS) { +			fputs("failed to create framebuffer", stderr); +			exit(-1); +		} +	} +} + +void create_command_pool() { +	struct queue_indices indx = find_queue_families(phy_gpu); + +	VkCommandPoolCreateInfo pool_info = { +		.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, +		.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, +		.queueFamilyIndex = indx.graphics +	}; + +	VkResult res = vkCreateCommandPool(gpu, &pool_info, NULL, &command_pool); +	if (res != VK_SUCCESS) { +		fputs("failed to create command pool", stderr); +		exit(-1); +	} +} + +void create_command_buffer() { +	VkCommandBufferAllocateInfo alloc_info = { +		.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, +		.commandPool = command_pool, +		.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, +		.commandBufferCount = 1 +	}; + +	VkResult res = vkAllocateCommandBuffers(gpu, &alloc_info, &command_buffer); +	if (res != VK_SUCCESS) { +		fputs("failed to allocate command buffer", stderr); +		exit(-1); +	} +} + +void record_command_buffer(VkCommandBuffer buffer, uint32_t image_index) { +	VkCommandBufferBeginInfo begin_info = { +		.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, +	}; + +	VkResult res = vkBeginCommandBuffer(buffer, &begin_info); +	if (res != VK_SUCCESS) { +		fputs("failed to begin command buffer", stderr); +		exit(-1); +	} + +	VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; +	VkRenderPassBeginInfo render_pass_info = { +		.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, +		.renderPass = render_pass, +		.framebuffer = swapchain_buffers->data[image_index], +		.renderArea = { +			.extent = swapchain_extent, +			.offset = {0, 0} +		}, +		.clearValueCount = 1, +		.pClearValues = &clear_color +	}; + +	vkCmdBeginRenderPass(buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); +	vkCmdBindPipeline(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); + +	VkViewport viewport = { +		.x = 0.0f, +		.y = 0.0f, +		.width = swapchain_extent.width, +		.height = swapchain_extent.height, +		.minDepth = 0.0f, +		.maxDepth = 1.0f, +	}; +	vkCmdSetViewport(buffer, 0, 1, &viewport); + +	VkRect2D scissor = { +		.offset = {0, 0}, +		.extent = swapchain_extent +	}; + +	vkCmdSetScissor(buffer, 0, 1, &scissor); +	vkCmdDraw(buffer, 3, 1, 0, 0); + +	vkCmdEndRenderPass(buffer); + +	res = vkEndCommandBuffer(buffer); +	if (res != VK_SUCCESS) { +		fputs("failed to record command buffer", stderr); +		exit(-1); +	} +} + +void create_sync_objects() { +	VkSemaphoreCreateInfo semaphore_info = { +		.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO +	}; +	VkFenceCreateInfo fence_info = { +		.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, +		.flags = VK_FENCE_CREATE_SIGNALED_BIT +	}; +	if (vkCreateSemaphore(gpu, &semaphore_info, NULL, &image_avail) != VK_SUCCESS +			|| vkCreateSemaphore(gpu, &semaphore_info, NULL, &render_finished) != VK_SUCCESS +			|| vkCreateFence(gpu, &fence_info, NULL, &in_flight_fence) != VK_SUCCESS) { +		fputs("failed to create semaphores and fence", stderr); +		exit(-1); +	} +} + +void init() { +	SDL_Init(SDL_INIT_VIDEO); +	window = SDL_CreateWindow("vk", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 800, SDL_WINDOW_VULKAN); +	if (!window) { +		fputs("failed to create sdl window", stderr); +		exit(-1); +	} + +	uint32_t extension_count = 0; +	vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL); + +	VkExtensionProperties *exts = calloc(extension_count, sizeof(*exts)); +	vkEnumerateInstanceExtensionProperties(NULL, &extension_count, exts); + +	puts("available extensions:"); +	for (size_t i = 0; i < extension_count; i++) { +		printf("\t%s\n", exts[i].extensionName); +	} + +	free(exts); + +	create_instance(); + +	if (SDL_Vulkan_CreateSurface(window, instance, &surface) != SDL_TRUE) { +		fputs("failed to create surface", stderr); +		exit(-1); +	} + +	pick_physical_dev(); +	create_logical_dev(); +	create_swapchain(); +	create_image_views(); +	create_render_pass(); +	create_graphics_pipeline(); +	create_framebuffers(); +	create_command_pool(); +	create_command_buffer(); +	create_sync_objects(); +} + +void draw() { +	vkWaitForFences(gpu, 1, &in_flight_fence, VK_TRUE, UINT64_MAX); +	vkResetFences(gpu, 1, &in_flight_fence); + +	uint32_t image_index; +	vkAcquireNextImageKHR(gpu, swapchain, UINT64_MAX, image_avail, VK_NULL_HANDLE, &image_index); +	vkResetCommandBuffer(command_buffer, 0); +	record_command_buffer(command_buffer, image_index); + +	VkSemaphore wait_semaphores[] = { image_avail }; +	VkSemaphore signal_semaphores[] = { render_finished }; +	VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; +	VkSubmitInfo submit_info = { +		.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, +		.waitSemaphoreCount = 1, +		.pWaitSemaphores = wait_semaphores, +		.pWaitDstStageMask = wait_stages, +		.signalSemaphoreCount = 1, +		.pSignalSemaphores = signal_semaphores +	}; + +	VkResult res = vkQueueSubmit(gfx_queue, 1, &submit_info, in_flight_fence); +	if (res != VK_SUCCESS) { +		fputs("failed to submit draw command buffer", stderr); +		exit(-1); +	} + +	VkSwapchainKHR swapchains[] = { swapchain }; +	VkPresentInfoKHR present_info = { +		.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, +		.waitSemaphoreCount = 1, +		.pWaitSemaphores = signal_semaphores, +		.swapchainCount = 1, +		.pSwapchains = swapchains, +		.pImageIndices = &image_index +	}; + +	vkQueuePresentKHR(present_queue, &present_info); +} + +int main() { +	init(); + +	SDL_Event e; +	while (SDL_PollEvent(&e), e.type != SDL_QUIT) +		draw(); + +	cleanup(); +	return 0; +} diff --git a/shader.frag b/shader.frag new file mode 100644 index 0000000..c9e559f --- /dev/null +++ b/shader.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; +layout(location = 0) out vec4 outColor; + +void main() { +	outColor = vec4(fragColor, 1.0); +} diff --git a/shader.vert b/shader.vert new file mode 100644 index 0000000..9e69113 --- /dev/null +++ b/shader.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +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); +	fragColor = colors[gl_VertexIndex]; +} | 
