depth buffering

This commit is contained in:
2024-07-11 19:06:13 +02:00
parent 83c4ce7118
commit b9ec9f198f
5 changed files with 600 additions and 104 deletions

View File

@@ -1,6 +1,6 @@
LDFLAGS = -lglfw -lvulkan -ldl -lpthread -lm
CFLAGS = -g -pedantic -Wall -Wextra -Wshadow -Wunused-macros
CFLAGS = -g -pedantic -Wall -Wextra -Wshadow -Wunused-macros
VulkanApplication: main.c
gcc $(CFLAGS) -o VulkanApplication main.c $(LDFLAGS)

BIN
images/statue.ppm Normal file

Binary file not shown.

690
main.c
View File

@@ -63,21 +63,35 @@ struct UniformBufferObject {
} UniformBufferObject;
typedef struct Vertex {
Vec2 pos;
Vec3 pos;
Vec3 color;
Vec2 texCoord;
} Vertex;
#define VERTEX_COUNT 4
static const Vertex vertices[VERTEX_COUNT] = {
{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}},
{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}}
struct PPM {
uint32_t width;
uint32_t height;
uint32_t maxVal;
void* pixels;
};
#define INDEX_COUNT 6
#define VERTEX_COUNT 8
static const Vertex vertices[VERTEX_COUNT] = {
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
{{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
};
#define INDEX_COUNT 12
static const uint16_t indices[INDEX_COUNT] = {
0, 1, 2, 2, 3, 0
0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4
};
struct VulkanData {
@@ -117,6 +131,13 @@ struct VulkanData {
void* uniformBuffersMapped[MAX_FRAMES_IN_FLIGHT];
VkDescriptorPool descriptorPool;
VkDescriptorSet descriptorSets[MAX_FRAMES_IN_FLIGHT];
VkImage textureImage;
VkDeviceMemory textureImageMemory;
VkImageView textureImageView;
VkSampler textureSampler;
VkImage depthImage;
VkDeviceMemory depthImageMemory;
VkImageView depthImageView;
};
struct SwapChainSupportDetails {
@@ -127,7 +148,7 @@ struct SwapChainSupportDetails {
struct Optional {
uint32_t v;
uint32_t is_some;
bool is_some;
};
struct QueueFamilyIndices {
@@ -240,6 +261,73 @@ GLFWwindow* initWindow(struct VulkanData* data) {
return window;
}
static VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, struct VulkanData* data) {
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.subresourceRange.aspectMask = aspectFlags,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = 1,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = 1,
};
VkImageView imageView;
if (vkCreateImageView(data->device, &viewInfo, NULL, &imageView) != VK_SUCCESS) {
fprintf(stderr, "ERROR: Failed to create texture image view\n");
exit(1);
}
return imageView;
}
static VkCommandBuffer beginSingleTimeCommands(bool graphics, struct VulkanData* data) {
VkCommandBufferAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
if (graphics) {
allocInfo.commandPool = data->commandPool;
} else {
allocInfo.commandPool = data->transferCommandPool;
}
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(data->device, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkBeginCommandBuffer(commandBuffer, &beginInfo);
return commandBuffer;
}
static void endSingleTimeCommands(VkCommandBuffer commandBuffer, bool graphics, struct VulkanData* data) {
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &commandBuffer,
};
if (graphics) {
vkQueueSubmit(data->graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(data->graphicsQueue);
vkFreeCommandBuffers(data->device, data->commandPool, 1, &commandBuffer);
} else {
vkQueueSubmit(data->transferQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(data->transferQueue);
vkFreeCommandBuffers(data->device, data->transferCommandPool, 1, &commandBuffer);
}
}
static bool checkValidationLayerSupport() {
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, NULL);
@@ -323,6 +411,20 @@ static void createInstance(struct VulkanData* data) {
printf("\t%s\n", extensions[i]);
}
// const VkBool32 setting_validate_core = VK_TRUE;
// const VkBool32 setting_validate_best_practices = VK_TRUE;
// const VkLayerSettingEXT settings[] = {
// {VALIDATION_LAYERS[0], "validate_core", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &setting_validate_core},
// {VALIDATION_LAYERS[0], "validate_best_practices", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &setting_validate_best_practices}
// };
// const VkLayerSettingsCreateInfoEXT layer_settings_create_info = {
// VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT,
// NULL,
// 1,
// settings
// };
VkInstanceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
@@ -341,7 +443,7 @@ static void createInstance(struct VulkanData* data) {
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
.pfnUserCallback = debugCallback,
};
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
createInfo.pNext = &debugCreateInfo;
} else {
createInfo.enabledLayerCount = 0;
createInfo.pNext = NULL;
@@ -494,7 +596,7 @@ static bool isDeviceSuitable(VkPhysicalDevice physicalDevice, VkSurfaceKHR surfa
vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
printf("GPU: %s\n", deviceProperties.deviceName);
VkPhysicalDeviceFeatures deviceFeatures;
int featureCount = sizeof(deviceFeatures) / 4;
int featureCount = sizeof(deviceFeatures) / sizeof(deviceFeatures.robustBufferAccess);
VkBool32* p = &deviceFeatures.robustBufferAccess;
for (int i = 0; i < featureCount; i++) {
*p = VK_FALSE;
@@ -512,7 +614,7 @@ static bool isDeviceSuitable(VkPhysicalDevice physicalDevice, VkSurfaceKHR surfa
return
deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
deviceFeatures.geometryShader &&
deviceFeatures.geometryShader && deviceFeatures.samplerAnisotropy &&
swapChainAdequate &&
familyIndices.graphicsFamily.is_some && familyIndices.presentFamily.is_some && familyIndices.transferFamily.is_some;
}
@@ -583,6 +685,7 @@ static void createLogicalDevice(struct VulkanData* data) {
*p = VK_FALSE;
p++;
}
deviceFeatures.samplerAnisotropy = VK_TRUE;
VkDeviceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
@@ -708,24 +811,7 @@ static void createSwapChain(struct VulkanData* data, GLFWwindow* window) {
static void createImageViews(struct VulkanData* data) {
for (int i = 0; i < data->imageCount; i++) {
VkImageViewCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = data->swapChainImages[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = data->swapChainImageFormat,
.components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.components.a = VK_COMPONENT_SWIZZLE_IDENTITY,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = 1,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = 1,
};
if (vkCreateImageView(data->device, &createInfo, NULL, &data->swapChainImageViews[i]) != VK_SUCCESS) {
fprintf(stderr, "failed to create image views!");
}
data->swapChainImageViews[i] = createImageView(data->swapChainImages[i], data->swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, data);
}
}
@@ -753,6 +839,34 @@ static uint32_t fileSize(const char* fileName) {
return size;
}
VkFormat findSupportedFormat(const VkFormat* candidates, int candidateCount, VkImageTiling tiling, VkFormatFeatureFlags features, struct VulkanData* data) {
for (int i = 0; i < candidateCount; i++) {
VkFormat format = candidates[i];
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(data->physicalDevice, format, &props);
if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
return format;
} else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
return format;
}
}
fprintf(stderr, "ERROR: Failed to find supported format\n");
exit(1);
}
VkFormat findDepthFormat(struct VulkanData* data) {
const VkFormat candidates[] = {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT};
return findSupportedFormat(
candidates,
3,
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT,
data
);
}
static VkShaderModule createShaderModule(const char* code, uint32_t size, VkDevice device) {
VkShaderModuleCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
@@ -795,6 +909,19 @@ static void createGraphicsPipeline(struct VulkanData* data) {
.pSpecializationInfo = NULL,
};
VkPipelineDepthStencilStateCreateInfo depthStencil = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_LESS,
.depthBoundsTestEnable = VK_FALSE,
.minDepthBounds = 0., // Optional
.maxDepthBounds = 1., // Optional
.stencilTestEnable = VK_FALSE,
// .front = NULL, // Optional
// .back = NULL, // Optional
};
VkPipelineShaderStageCreateInfo shaderStages[2] = {vertShaderStageInfo, fragShaderStageInfo};
uint32_t dynamicStateCount = 2;
@@ -810,11 +937,11 @@ static void createGraphicsPipeline(struct VulkanData* data) {
};
VkVertexInputBindingDescription bindingDescription = getBindingDescription();
VkVertexInputAttributeDescription attributeDescriptions[2] = {
VkVertexInputAttributeDescription attributeDescriptions[] = {
{
.binding = 0,
.location = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(Vertex, pos),
},
{
@@ -822,13 +949,19 @@ static void createGraphicsPipeline(struct VulkanData* data) {
.location = 1,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(Vertex, color),
},
{
.binding = 0,
.location = 2,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(Vertex, texCoord),
}
};
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.vertexAttributeDescriptionCount = 2,
.vertexAttributeDescriptionCount = 3,
.pVertexBindingDescriptions = &bindingDescription,
.pVertexAttributeDescriptions = attributeDescriptions,
};
@@ -867,8 +1000,7 @@ static void createGraphicsPipeline(struct VulkanData* data) {
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
// .cullMode = VK_CULL_MODE_BACK_BIT,
.cullMode = 0,
.cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f, // Optional
@@ -931,7 +1063,7 @@ static void createGraphicsPipeline(struct VulkanData* data) {
.pViewportState = &viewportState,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pDepthStencilState = NULL, // Optional
.pDepthStencilState = &depthStencil,
.pColorBlendState = &colorBlending,
.pDynamicState = &dynamicState,
.layout = data->pipelineLayout,
@@ -966,26 +1098,44 @@ static void createRenderPass(struct VulkanData* data) {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
VkAttachmentDescription depthAttachment = {
.format = findDepthFormat(data),
.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_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
};
VkAttachmentReference depthAttachmentRef = {
.attachment = 1,
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
};
VkSubpassDescription subpass = {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
.pColorAttachments = &colorAttachmentRef,
.pDepthStencilAttachment = &depthAttachmentRef,
};
VkSubpassDependency dependency = {
.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,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
};
VkAttachmentDescription attachments[2] = {colorAttachment, depthAttachment};
VkRenderPassCreateInfo renderPassInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &colorAttachment,
.attachmentCount = 2,
.pAttachments = attachments,
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
@@ -1001,13 +1151,14 @@ static void createRenderPass(struct VulkanData* data) {
static void createFramebuffers(struct VulkanData* data) {
for (int i = 0; i < data->imageCount; i++) {
VkImageView attachments[] = {
data->swapChainImageViews[i]
data->swapChainImageViews[i],
data->depthImageView,
};
VkFramebufferCreateInfo framebufferInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = data->renderPass,
.attachmentCount = 1,
.attachmentCount = 2,
.pAttachments = attachments,
.width = data->swapChainExtent.width,
.height = data->swapChainExtent.height,
@@ -1021,6 +1172,59 @@ static void createFramebuffers(struct VulkanData* data) {
}
}
static uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties, struct VulkanData* data) {
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(data->physicalDevice, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties)) {
return i;
}
}
fprintf(stderr, "ERROR: Failed to find suitable memory type\n");
exit(1);
}
static void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage* image, VkDeviceMemory* imageMemory, struct VulkanData* data) {
VkImageCreateInfo imageInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.extent.width = width,
.extent.height = height,
.extent.depth = 1,
.mipLevels = 1,
.arrayLayers = 1,
.format = format,
.tiling = tiling,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.samples = VK_SAMPLE_COUNT_1_BIT,
.flags = 0,
};
if (vkCreateImage(data->device, &imageInfo, NULL, image) != VK_SUCCESS) {
fprintf(stderr, "ERROR: Failed to create image\n");
exit(1);
}
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(data->device, *image, &memRequirements);
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memRequirements.size,
.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties, data),
};
if (vkAllocateMemory(data->device, &allocInfo, NULL, imageMemory) != VK_SUCCESS) {
fprintf(stderr, "ERROR: Failed to allocate image memory\n");
exit(1);
}
vkBindImageMemory(data->device, *image, *imageMemory, 0);
}
static void createCommandPool(struct VulkanData* data) {
struct QueueFamilyIndices queueFamilyIndices = findQueueFamilies(data->physicalDevice, data->surface);
@@ -1074,7 +1278,10 @@ static void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageInd
exit(1);
}
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkClearValue clearValues[] = {
{ .color = {{0.0f, 0.0f, 0.0f, 1.0f}}, },
{ .depthStencil = {1., 0}, }
};
VkRenderPassBeginInfo renderPassInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
@@ -1082,8 +1289,8 @@ static void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageInd
.framebuffer = data->swapChainFramebuffers[imageIndex],
.renderArea.offset = {0, 0},
.renderArea.extent = data->swapChainExtent,
.clearValueCount = 1,
.pClearValues = &clearColor,
.clearValueCount = 2,
.pClearValues = clearValues,
};
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
@@ -1119,6 +1326,9 @@ static void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageInd
}
static void cleanupSwapChain(struct VulkanData* data) {
vkDestroyImageView(data->device, data->depthImageView, NULL);
vkDestroyImage(data->device, data->depthImage, NULL);
vkFreeMemory(data->device, data->depthImageMemory, NULL);
for (int i = 0; i < data->imageCount; i++) {
vkDestroyFramebuffer(data->device, data->swapChainFramebuffers[i], NULL);
}
@@ -1130,6 +1340,22 @@ static void cleanupSwapChain(struct VulkanData* data) {
vkDestroySwapchainKHR(data->device, data->swapChain, NULL);
}
static void createDepthResources(struct VulkanData* data) {
VkFormat depthFormat = findDepthFormat(data);
createImage(
data->swapChainExtent.width,
data->swapChainExtent.height,
depthFormat,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&data->depthImage,
&data->depthImageMemory,
data
);
data->depthImageView = createImageView(data->depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, data);
}
static void recreateSwapChain(struct VulkanData* data, GLFWwindow* window) {
int width = 0, height = 0;
glfwGetFramebufferSize(window, &width, &height);
@@ -1141,6 +1367,7 @@ static void recreateSwapChain(struct VulkanData* data, GLFWwindow* window) {
cleanupSwapChain(data);
createSwapChain(data, window);
createImageViews(data);
createDepthResources(data);
createFramebuffers(data);
}
@@ -1246,17 +1473,81 @@ static void createSyncObjects(struct VulkanData* data) {
}
}
static uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties, struct VulkanData* data) {
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(data->physicalDevice, &memProperties);
static void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, struct VulkanData* data) {
VkCommandBuffer commandBuffer = beginSingleTimeCommands(false, data);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties)) {
return i;
}
VkBufferImageCopy region = {
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.imageSubresource.mipLevel = 0,
.imageSubresource.baseArrayLayer = 0,
.imageSubresource.layerCount = 1,
.imageOffset = {0, 0, 0},
.imageExtent = {width, height, 1},
};
vkCmdCopyBufferToImage(
commandBuffer,
buffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&region
);
endSingleTimeCommands(commandBuffer, false, data);
}
static void transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, struct VulkanData* data) {
VkCommandBuffer commandBuffer = beginSingleTimeCommands(true, data);
VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = oldLayout,
.newLayout = newLayout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = 1,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = 1,
.srcAccessMask = 0,
.dstAccessMask = 0,
};
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
fprintf(stderr, "ERROR: Unsupported layout transition\n");
exit(1);
}
fprintf(stderr, "ERROR: Failed to find suitable memory type\n");
exit(1);
vkCmdPipelineBarrier(
commandBuffer,
sourceStage, destinationStage,
0,
0, NULL,
0, NULL,
1, &barrier
);
endSingleTimeCommands(commandBuffer, true, data);
}
static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer* buffer, VkDeviceMemory* bufferMemory, struct VulkanData* data) {
@@ -1294,21 +1585,7 @@ static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPr
}
static void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size, struct VulkanData* data) {
VkCommandBufferAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandPool = data->transferCommandPool,
.commandBufferCount = 1,
};
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(data->device, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkBeginCommandBuffer(commandBuffer, &beginInfo);
VkCommandBuffer commandBuffer = beginSingleTimeCommands(false, data);
VkBufferCopy copyRegion = {
.srcOffset = 0,
@@ -1316,16 +1593,8 @@ static void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size
.size = size,
};
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &commandBuffer,
};
vkQueueSubmit(data->transferQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(data->transferQueue);
vkFreeCommandBuffers(data->device, data->transferCommandPool, 1, &commandBuffer);
endSingleTimeCommands(commandBuffer, false, data);
}
static void createIndexBuffer(struct VulkanData* data) {
@@ -1374,10 +1643,20 @@ static void createDescriptorSetLayout(struct VulkanData* data) {
.pImmutableSamplers = NULL,
};
VkDescriptorSetLayoutBinding samplerLayoutBinding = {
.binding = 1,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImmutableSamplers = NULL,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
};
VkDescriptorSetLayoutBinding bindings[2] = {uboLayoutBinding, samplerLayoutBinding};
VkDescriptorSetLayoutCreateInfo layoutInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &uboLayoutBinding,
.bindingCount = 2,
.pBindings = bindings,
};
if (vkCreateDescriptorSetLayout(data->device, &layoutInfo, NULL, &data->descriptorSetLayout) != VK_SUCCESS) {
@@ -1396,19 +1675,25 @@ static void createUniformBuffers(struct VulkanData* data) {
}
static void createDescriptorPool(struct VulkanData* data) {
VkDescriptorPoolSize poolSize = {
VkDescriptorPoolSize uboPoolSize = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = MAX_FRAMES_IN_FLIGHT,
};
VkDescriptorPoolSize samplerPoolSize = {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = MAX_FRAMES_IN_FLIGHT,
};
VkDescriptorPoolSize poolSizes[2] = {uboPoolSize, samplerPoolSize};
VkDescriptorPoolCreateInfo poolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.poolSizeCount = 2,
.pPoolSizes = poolSizes,
.maxSets = MAX_FRAMES_IN_FLIGHT,
.flags = 0,
};
if (vkCreateDescriptorPool(data->device, &poolInfo, NULL, &data->descriptorPool) != VK_SUCCESS) {
fprintf(stderr, "ERROR: Failed to create descriptor pool\n");
@@ -1440,25 +1725,221 @@ static void createDescriptorSets(struct VulkanData* data) {
.range = sizeof(UniformBufferObject),
};
VkWriteDescriptorSet descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = data->descriptorSets[i],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &bufferInfo,
.pImageInfo = NULL,
.pTexelBufferView = NULL,
VkDescriptorImageInfo imageInfo = {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = data->textureImageView,
.sampler = data->textureSampler,
};
vkUpdateDescriptorSets(data->device, 1, &descriptorWrite, 0, NULL);
VkWriteDescriptorSet descriptorWrites[2] = {
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = data->descriptorSets[i],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &bufferInfo,
.pImageInfo = NULL,
.pTexelBufferView = NULL,
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = data->descriptorSets[i],
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pBufferInfo = NULL,
.pImageInfo = &imageInfo,
.pTexelBufferView = NULL,
}
};
vkUpdateDescriptorSets(data->device, 2, descriptorWrites, 0, NULL);
}
}
// Because of the format support and alpha of 1 is added
static struct PPM readPPM(char* fileName) {
FILE* file = fopen(fileName, "r");
char header[2];
fread(header, sizeof(header[0]), 2, file);
if (header[0] != 'P' || header[1] != '6') {
fprintf(stderr, "ERROR: Unrecognized file format\n");
exit(1);
}
if (fseek(file, 1, SEEK_CUR)) {
fprintf(stderr, "ERROR: Failed to read image: \"%s\"\n", fileName);
exit(1);
}
int len = 0;
char c = '0';
while (true) {
if (fread(&c, 1, 1, file) != 1) {
fprintf(stderr, "ERROR: Failed to read width of image: \"%s\" with char: %c\n", fileName, c);
exit(1);
}
if (c < '0' || c > '9') {
break;
}
len++;
}
fseek(file, -len - 1, SEEK_CUR);
char widthStr[len + 1];
fread(widthStr, sizeof(widthStr[0]), len, file);
widthStr[len] = 0;
int width = atoi(widthStr);
fseek(file, 1, SEEK_CUR);
len = 0;
c = '0';
while (true) {
if (fread(&c, 1, 1, file) != 1) {
fprintf(stderr, "ERROR: Failed to read height of image: \"%s\"\n", fileName);
exit(1);
}
if (c < '0' || c > '9') {
break;
}
len++;
}
fseek(file, -len - 1, SEEK_CUR);
char heightStr[len + 1];
fread(heightStr, sizeof(heightStr[0]), len, file);
heightStr[len] = 0;
int height = atoi(heightStr);
fseek(file, 1, SEEK_CUR);
len = 0;
c = '0';
while (true) {
if (fread(&c, 1, 1, file) != 1) {
fprintf(stderr, "ERROR: Failed to read maximum value for pixels in image: \"%s\"\n", fileName);
exit(1);
}
if (c < '0' || c > '9') {
break;
}
len++;
}
fseek(file, -len - 1, SEEK_CUR);
char maxValStr[len + 1];
fread(maxValStr, sizeof(maxValStr[0]), len, file);
maxValStr[len] = 0;
int maxVal = atoi(maxValStr);
printf("INFO: Image \"%s\" has a width and height of (%d, %d) and maximum value for pixels of %d\n", fileName, width, height, maxVal);
fseek(file, 1, SEEK_CUR);
int pixelCount = width * height;
struct PPM ppm = {
.width = width,
.height = height,
.maxVal = maxVal,
.pixels = NULL,
};
if (maxVal <= UCHAR_MAX) {
uint8_t* pixels = malloc(pixelCount * 4);
uint8_t* pixel_p = pixels;
for (int i = 0; i < pixelCount; i++) {
fread(pixel_p, sizeof(uint8_t), 3, file);
pixel_p += 3;
*pixel_p = UCHAR_MAX;
pixel_p++;
}
ppm.pixels = pixels;
} else if (maxVal <= USHRT_MAX) {
uint16_t* pixels = malloc(pixelCount * sizeof(uint16_t) * 4);
uint16_t* pixel_p = pixels;
for (int i = 0; i < pixelCount; i++) {
fread(pixel_p, sizeof(uint16_t), 3, file);
pixel_p += 3;
*pixel_p = USHRT_MAX;
pixel_p++;
}
ppm.pixels = pixels;
} else {
fprintf(stderr, "ERROR: Specified maximum value for pixels is too large\n");
exit(1);
}
return ppm;
}
static void createTextureImage(struct VulkanData* data) {
struct PPM ppm = readPPM("images/statue.ppm");
int nSize = 1;
VkFormat format = VK_FORMAT_R8G8B8A8_SRGB;
if (ppm.maxVal > 255) {
nSize = 2;
format = VK_FORMAT_R16G16B16_UINT;
}
VkDeviceSize imageSize = ppm.width * ppm.height * nSize * 4;
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffer, &stagingBufferMemory, data);
void* imageData;
vkMapMemory(data->device, stagingBufferMemory, 0, imageSize, 0, &imageData);
memcpy(imageData, ppm.pixels, imageSize);
vkUnmapMemory(data->device, stagingBufferMemory);
free(ppm.pixels);
createImage(ppm.width, ppm.height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &data->textureImage, &data->textureImageMemory, data);
transitionImageLayout(data->textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, data);
copyBufferToImage(stagingBuffer, data->textureImage, ppm.width, ppm.height, data);
transitionImageLayout(data->textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, data);
vkDestroyBuffer(data->device, stagingBuffer, NULL);
vkFreeMemory(data->device, stagingBufferMemory, NULL);
}
static void createTextureImageView(struct VulkanData* data) {
data->textureImageView = createImageView(data->textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, data);
}
static void createTextureSampler(struct VulkanData* data) {
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(data->physicalDevice, &properties);
VkSamplerCreateInfo samplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.anisotropyEnable = VK_TRUE,
.maxAnisotropy = properties.limits.maxSamplerAnisotropy, // set to 1 to disable
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
.compareEnable = VK_FALSE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.mipLodBias = 0.,
.minLod = 0.,
.maxLod = 0.,
};
if (vkCreateSampler(data->device, &samplerInfo, NULL, &data->textureSampler) != VK_SUCCESS) {
fprintf(stderr, "ERROR: Failed to create texture sampler\n");
exit(1);
}
}
bool hasStencilComponent(VkFormat format) {
return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
}
static void initVulkan(GLFWwindow* window, struct VulkanData* data) {
data->currentFrame = 0;
data->framebufferResized = 0;
data->framebufferResized = false;
createInstance(data);
setupDebugMessenger(data);
createSurface(data, window);
@@ -1469,8 +1950,12 @@ static void initVulkan(GLFWwindow* window, struct VulkanData* data) {
createRenderPass(data);
createDescriptorSetLayout(data);
createGraphicsPipeline(data);
createFramebuffers(data);
createCommandPool(data);
createDepthResources(data);
createFramebuffers(data);
createTextureImage(data);
createTextureImageView(data);
createTextureSampler(data);
createVertexBuffer(data);
createIndexBuffer(data);
createUniformBuffers(data);
@@ -1497,6 +1982,11 @@ static void mainLoop(GLFWwindow* window, struct VulkanData* data, struct timespe
static void cleanup(GLFWwindow* window, struct VulkanData* data) {
cleanupSwapChain(data);
vkDestroySampler(data->device, data->textureSampler, NULL);
vkDestroyImageView(data->device, data->textureImageView, NULL);
vkDestroyImage(data->device, data->textureImage, NULL);
vkFreeMemory(data->device, data->textureImageMemory, NULL);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroyBuffer(data->device, data->uniformBuffers[i], NULL);
vkFreeMemory(data->device, data->uniformBuffersMemory[i], NULL);

View File

@@ -1,9 +1,12 @@
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor;
layout(binding = 1) uniform sampler2D texSampler;
void main() {
outColor = vec4(fragColor, 1.0);
outColor = texture(texSampler, fragTexCoord);
}

View File

@@ -1,9 +1,11 @@
#version 450
layout(location = 0) in vec2 inPosition;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;
layout(binding = 0) uniform UniformBufferObject {
mat4 model;
@@ -12,6 +14,7 @@ layout(binding = 0) uniform UniformBufferObject {
} ubo;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
fragColor = inColor;
fragTexCoord = inTexCoord;
}