From 4fcda01ba29b38da4d1fc116136134225b20cf64 Mon Sep 17 00:00:00 2001 From: Piotr Krygier Date: Mon, 2 Oct 2023 13:21:17 +0200 Subject: [PATCH] Add texture handler Added functionality to load images as textures and bind them to meshes. --- CMakeLists.txt | 2 +- cmake/Findstb.cmake | 3 + dependencies.dep | 4 + graphics/CMakeLists.txt | 4 + graphics/shaders/shader.frag | 6 +- graphics/shaders/shader.vert | 9 +- graphics/src/mesh_controller.c | 5 - graphics/src/rse_graphics.c | 17 +- graphics/src/rse_vulkan_commands.c | 115 ++++++++++ graphics/src/rse_vulkan_commands.h | 24 +++ graphics/src/rse_vulkan_image.c | 332 +++++++++++++++++++++++++++++ graphics/src/rse_vulkan_image.h | 33 +++ graphics/src/vulkan_base.c | 16 ++ graphics/src/vulkan_buffers.c | 191 +++++------------ graphics/src/vulkan_buffers.h | 26 +++ graphics/src/vulkan_commons.h | 1 + graphics/src/vulkan_descriptors.c | 45 ++-- graphics/src/vulkan_pipeline.c | 53 +++-- red_scarf_engine/src/main.c | 6 +- tests/CMakeLists.txt | 2 +- tests/src/rse_graphics_tests.c | 186 ++++++++++++++++ utilities/file_utils.h | 4 +- utilities/src/file_utils.c | 12 +- utilities/src/logger.c | 1 - 24 files changed, 897 insertions(+), 200 deletions(-) create mode 100644 cmake/Findstb.cmake create mode 100644 graphics/src/rse_vulkan_commands.c create mode 100644 graphics/src/rse_vulkan_commands.h create mode 100644 graphics/src/rse_vulkan_image.c create mode 100644 graphics/src/rse_vulkan_image.h create mode 100644 tests/src/rse_graphics_tests.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 900a5ce4..b65c92aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ set(CMAKE_C_STANDARD 23)# turn on the dynamic depends for ninja set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror") add_subdirectory(red_scarf_engine) diff --git a/cmake/Findstb.cmake b/cmake/Findstb.cmake new file mode 100644 index 00000000..9dfae165 --- /dev/null +++ b/cmake/Findstb.cmake @@ -0,0 +1,3 @@ +set(STB_DIR "${PROJECT_SOURCE_DIR}/third_party/stb/") + +set(stb_INCLUDE_DIR "${STB_DIR}/") \ No newline at end of file diff --git a/dependencies.dep b/dependencies.dep index 5cbc91d4..c3569080 100644 --- a/dependencies.dep +++ b/dependencies.dep @@ -15,3 +15,7 @@ tag = v3.0.1 git_url = https://github.com/Snaipe/Criterion.git tag = v2.4.2 custom_commands = cd Criterion && meson build && cd build && ninja + +[stb] +git_url = https://github.com/nothings/stb.git +tag = master diff --git a/graphics/CMakeLists.txt b/graphics/CMakeLists.txt index f8c4b5a8..1608148c 100644 --- a/graphics/CMakeLists.txt +++ b/graphics/CMakeLists.txt @@ -1,6 +1,7 @@ find_package(Vulkan REQUIRED) find_package(vma REQUIRED) find_package(glfw3 REQUIRED) +find_package(stb REQUIRED) find_package(Vulkan COMPONENTS glslc) find_program(glslc_executable NAMES glslc HINTS ${Vulkan_GLSLC_EXECUTABLE}/..) @@ -42,6 +43,7 @@ ${CMAKE_SOURCE_DIR} ${Vulkan_INCLUDE_DIR} ${vma_INCLUDE_DIR} ${glfw3_INCLUDE_DIR} + ${stb_INCLUDE_DIR} ) target_sources(rse_graphics PRIVATE @@ -49,12 +51,14 @@ src/vma_port.cpp src/window.c src/vulkan_base.c src/vulkan_pipeline.c +src/rse_vulkan_commands.c src/vulkan_buffers.c src/vulkan_descriptors.c src/rse_graphics.c src/rse_math.c src/mesh_controller.c src/vulkan_commons.c +src/rse_vulkan_image.c ) target_link_libraries(rse_graphics PRIVATE diff --git a/graphics/shaders/shader.frag b/graphics/shaders/shader.frag index e948fd6e..f7514858 100644 --- a/graphics/shaders/shader.frag +++ b/graphics/shaders/shader.frag @@ -1,8 +1,12 @@ #version 450 +layout(binding = 1) uniform sampler2D texSampler; + layout(location = 0) in vec3 fragColor; +layout(location = 1) in vec2 fragTexCoord; + layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(fragColor, 1.0); + outColor = texture(texSampler, fragTexCoord); } \ No newline at end of file diff --git a/graphics/shaders/shader.vert b/graphics/shaders/shader.vert index 0bef39f9..801de257 100644 --- a/graphics/shaders/shader.vert +++ b/graphics/shaders/shader.vert @@ -8,12 +8,14 @@ layout(binding = 0) uniform UniformBufferObject { layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inColor; +layout(location = 2) in vec2 inTexCoord; -layout(location = 2) in vec3 instancePosition; -layout(location = 3) in vec3 instanceRotation; -layout(location = 4) in float instanceScale; +layout(location = 3) in vec3 instancePosition; +layout(location = 4) in vec3 instanceRotation; +layout(location = 5) in float instanceScale; layout(location = 0) out vec3 fragColor; +layout(location = 1) out vec2 fragTexCoord; void main() { @@ -59,4 +61,5 @@ void main() gl_Position = ubo.proj * ubo.view * ubo.model * pos; fragColor = inColor; + fragTexCoord = inTexCoord; } \ No newline at end of file diff --git a/graphics/src/mesh_controller.c b/graphics/src/mesh_controller.c index 6772bdfb..63e51f40 100644 --- a/graphics/src/mesh_controller.c +++ b/graphics/src/mesh_controller.c @@ -115,9 +115,4 @@ size_t get_instances_count(uint16_t mesh_id) size_t get_vertex_offset(uint16_t mesh_id) { return g_meshes[mesh_id].vertex_data.buffer_offset; -} - -VkDrawIndexedIndirectCommand get_index_data_buffer(uint16_t mesh_id) -{ - } \ No newline at end of file diff --git a/graphics/src/rse_graphics.c b/graphics/src/rse_graphics.c index b2f3b0bb..f9ce3b4c 100644 --- a/graphics/src/rse_graphics.c +++ b/graphics/src/rse_graphics.c @@ -4,6 +4,7 @@ #include "mesh_controller.h" #include "vulkan_commons.h" +#include "rse_vulkan_image.h" #include "rse_math.h" @@ -43,14 +44,14 @@ uint8_t rse_graphics_run(void) return -1; // FIXME: Temporary array of vertices, for testing purposes - struct rse_vertex_t vertices[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}}, - {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 1.0f}}, - {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}}, - {{-0.5f, 0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}}}; - struct rse_vertex_t vertices2[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}}, - {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}}, - {{0.5f, 0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}}, - {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 0.0f}}}; + struct rse_vertex_t vertices[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}}, + {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}, + {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}}; + struct rse_vertex_t vertices2[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}}, + {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{0.5f, 0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}}}; // FIXME: Temporary array of vertices, for testing purposes uint16_t indices[] = {0, 1, 2, 2, 3, 0}; diff --git a/graphics/src/rse_vulkan_commands.c b/graphics/src/rse_vulkan_commands.c new file mode 100644 index 00000000..9321c7a2 --- /dev/null +++ b/graphics/src/rse_vulkan_commands.c @@ -0,0 +1,115 @@ +#include "rse_vulkan_commands.h" + +#include "vulkan_errors.h" +#include "locale_vulkan.h" +#include "utilities/logger.h" + +VkCommandPool g_command_pool = VK_NULL_HANDLE; + +/** + * @brief Create Command Pools + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED + */ +static uint8_t create_command_pools() +{ + VkCommandPoolCreateInfo create_info; + + create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + create_info.pNext = NULL; + create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + create_info.queueFamilyIndex = g_vulkan_state.queue_family_indices[0]; /* TODO: Change when more families are needed */ + + if (VK_SUCCESS != vkCreateCommandPool(g_vulkan_state.device, &create_info, NULL, &g_command_pool)) { + LOGF(vulkan_error_messages[VULKAN_COMMAND_POOL_CREATION_FAILED]); + return VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +/** + * @brief Allocate Command Buffers + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED + */ +static uint8_t allocate_command_buffers() +{ + VkCommandBufferAllocateInfo allocate_info; + + /* Allocate memory for command buffers */ + allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocate_info.pNext = NULL; + allocate_info.commandPool = g_command_pool; + allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; /* TODO: When needed, add secondary command buffer */ + allocate_info.commandBufferCount = SWAP_BUFFER_COUNT; + + if (VK_SUCCESS != vkAllocateCommandBuffers(g_vulkan_state.device, + &allocate_info, + g_vulkan_state.command_buffers)) { + LOGF(vulkan_error_messages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED]); + return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED; + } + return VULKAN_ERROR_NO_ERROR; +} + +VkCommandBuffer rse_begin_single_time_command() +{ + VkCommandBuffer command_buffer = VK_NULL_HANDLE; + VkCommandBufferBeginInfo begin_info = {}; + VkCommandBufferAllocateInfo alloc_info = {}; + + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.pNext = NULL; + alloc_info.commandPool = g_command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = 1; + + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.pNext = NULL; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + begin_info.pInheritanceInfo = NULL; + + vkAllocateCommandBuffers(g_vulkan_state.device, &alloc_info, &command_buffer); + vkBeginCommandBuffer(command_buffer, &begin_info); + + return command_buffer; +} + +void rse_end_single_time_comands(VkCommandBuffer command_buffer) +{ + VkSubmitInfo submit_info = {}; + + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = NULL; + submit_info.waitSemaphoreCount = 0; + submit_info.pWaitSemaphores = NULL; + submit_info.pWaitDstStageMask = NULL; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + submit_info.signalSemaphoreCount = 0; + submit_info.pSignalSemaphores = NULL; + + vkEndCommandBuffer(command_buffer); + + vkQueueSubmit(g_vulkan_state.graphics_queue, 1, &submit_info, VK_NULL_HANDLE); + vkQueueWaitIdle(g_vulkan_state.graphics_queue); + + vkFreeCommandBuffers(g_vulkan_state.device, g_command_pool, 1, &command_buffer); +} + +uint8_t init_commands() +{ + uint8_t status = VULKAN_ERROR_NO_ERROR; + STATUS_CHECK(create_command_pools()); + STATUS_CHECK(allocate_command_buffers()); + + return status; +} + +void destroy_commands() +{ + vkDestroyCommandPool(g_vulkan_state.device, g_command_pool, NULL); +} \ No newline at end of file diff --git a/graphics/src/rse_vulkan_commands.h b/graphics/src/rse_vulkan_commands.h new file mode 100644 index 00000000..838d4c54 --- /dev/null +++ b/graphics/src/rse_vulkan_commands.h @@ -0,0 +1,24 @@ +/** + * @file rse_vulkan_commands.h + * @author Piotr Krygier (everyonecancode@gmail.com) + * @brief Vukan Commands allocation and execution + * @version 0.1 + * @date 2023-09-29 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef RSE_VULKAN_COMMANDS_H +#define RSE_VULKAN_COMMANDS_H + +#include "vulkan_commons.h" +#include "vk_mem_alloc.h" + +uint8_t init_commands(); +void destroy_commands(); + +VkCommandBuffer rse_begin_single_time_command(); +void rse_end_single_time_comands(VkCommandBuffer command_buffer); + +#endif /* RSE_VULKAN_COMMANDS_H */ \ No newline at end of file diff --git a/graphics/src/rse_vulkan_image.c b/graphics/src/rse_vulkan_image.c new file mode 100644 index 00000000..80265465 --- /dev/null +++ b/graphics/src/rse_vulkan_image.c @@ -0,0 +1,332 @@ +#include "rse_vulkan_image.h" + +#include "utilities/file_utils.h" +#include "utilities/logger.h" +#include "locale_vulkan.h" +#include "vulkan_errors.h" +#include "rse_vulkan_commands.h" +#include "vulkan_buffers.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +#include "vma/vk_mem_alloc.h" + +#include +#include +#include +#include + +#define IMAGE_TAKEN 1U +#define IMAGE_FREE 0U + +/** + * @brief Wrapper for Vulkan buffer. Holds allocation data; + * + */ +struct rse_vulkan_image_t +{ + uint8_t id_taken; + size_t allocated_size; + VkImage image; + VkImageView image_view; + VmaAllocation allocation; + VmaAllocationInfo allocation_info; +}; + +/* For now we will only have one image view per image */ +extern struct rse_vulkan_state_t g_vulkan_state; + +struct rse_vulkan_image_t g_texture_images[RSE_MAX_IMAGE_COUNT]; +VkSampler g_sampler; + +static uint8_t create_sampler() +{ + VkSamplerCreateInfo create_info = {0}; + + create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + create_info.pNext = NULL; + create_info.flags = 0U; + create_info.magFilter = VK_FILTER_LINEAR; + create_info.minFilter = VK_FILTER_LINEAR; + create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + create_info.mipLodBias = 0.0f; + create_info.anisotropyEnable = VK_FALSE; // TODO: Enable this + create_info.maxAnisotropy = 1.0f; + create_info.compareEnable = VK_FALSE; + create_info.compareOp = VK_COMPARE_OP_ALWAYS; + create_info.minLod = 0.0f; + create_info.maxLod = 0.0f; + create_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + create_info.unnormalizedCoordinates = VK_FALSE; + + if (VK_SUCCESS != vkCreateSampler(g_vulkan_state.device, + &create_info, + NULL, + &g_sampler)) { + LOGF("Failed to create image view"); + return 1; + } + + return VULKAN_ERROR_NO_ERROR; +} + +static VkDeviceSize format_to_pixel_size(VkFormat format) +{ + switch (format) + { + case VK_FORMAT_R8G8B8A8_SRGB: + return 4; + break; + + default: + LOGE("Failed translate vulkan format to pixel width"); + break; + } + + return 0; +} + + +static uint8_t create_vulkan_image(uint32_t width, uint32_t height, VkFormat format, unsigned char* pixels) +{ + struct rse_vulkan_buffer_t staging_buffer = {0}; + VkDeviceSize staging_buffer_size = 0U; + VkImageCreateInfo image_info = {0}; + VmaAllocationCreateInfo alloc_create_info = {0}; + VkImageSubresourceRange range = {0}; + VkImageMemoryBarrier image_barrier = {0}; + VkCommandBuffer command_buffer = {0}; + VkBufferImageCopy copy_region = {0}; + VkExtent3D image_extent = {0}; + VkImageViewCreateInfo image_view_create_info = {0}; + struct rse_vulkan_image_t* free_texture_image = NULL; + size_t i = 0U; + + while(free_texture_image == NULL || i > RSE_MAX_IMAGE_COUNT) { + if(g_texture_images[i].id_taken == IMAGE_FREE) { + free_texture_image = &g_texture_images[i]; + } else { + ++i; + } + } + + /* Check if we found free image handle */ + if (free_texture_image == NULL) { + LOGE("Failed to find free texture image handle"); + return 1; + } + + staging_buffer_size = width * height * format_to_pixel_size(format); + + image_extent.width = width; + image_extent.height = height; + image_extent.depth = 1U; + + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.pNext = NULL; + image_info.flags = 0U; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = format; + image_info.extent = image_extent; + image_info.mipLevels = 1U; + image_info.arrayLayers = 1U; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; //TODO: Enable multisampling + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; //TODO: Change if used by more than one queue family + image_info.queueFamilyIndexCount = 0U; + image_info.pQueueFamilyIndices = NULL; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + alloc_create_info.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO; + alloc_create_info.requiredFlags = 0U; + alloc_create_info.preferredFlags = 0U; + alloc_create_info.memoryTypeBits = 0U; + alloc_create_info.pool = NULL; + alloc_create_info.pUserData = NULL; + alloc_create_info.priority = 1.0f; + + if (VK_SUCCESS != vmaCreateImage(g_vulkan_state.allocator, + &image_info, + &alloc_create_info, + &free_texture_image->image, + &free_texture_image->allocation, + &free_texture_image->allocation_info)) { + LOGF("Failed to create image"); + return 1; + } + + /* Put image into correct layout to copy pixels from buffer to image memory */ + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = 1; + range.baseArrayLayer = 0; + range.layerCount = 1; + + image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_barrier.pNext = NULL; + image_barrier.srcAccessMask = 0U; + image_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_barrier.srcQueueFamilyIndex = 0U; + image_barrier.dstQueueFamilyIndex = 0U; + image_barrier.image = free_texture_image->image; + image_barrier.subresourceRange = range; + + command_buffer = rse_begin_single_time_command(); + vkCmdPipelineBarrier(command_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, + NULL, + 0, + NULL, + 1, + &image_barrier); + rse_end_single_time_comands(command_buffer); + + /* Copy pixel data to GPU memory */ + if (VK_SUCCESS != create_buffer(staging_buffer_size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + VMA_ALLOCATION_CREATE_MAPPED_BIT + | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, + &staging_buffer)) { + LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]); + return VULKAN_ERROR_BUFFER_CREATION_FAILED; + } + + memcpy(staging_buffer.allocation_info.pMappedData, pixels, staging_buffer_size); + + copy_region.bufferOffset = 0U; + copy_region.bufferRowLength = 0U; + copy_region.bufferImageHeight = 0U; + copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.imageSubresource.mipLevel = 0U; + copy_region.imageSubresource.baseArrayLayer = 0U; + copy_region.imageSubresource.layerCount = 1U; + copy_region.imageOffset.x = 0; + copy_region.imageOffset.y = 0; + copy_region.imageOffset.z = 0; + copy_region.imageExtent = image_extent; + + command_buffer = rse_begin_single_time_command(); + vkCmdCopyBufferToImage(command_buffer, + staging_buffer.buffer, + free_texture_image->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ©_region); + rse_end_single_time_comands(command_buffer); + destroy_buffer(&staging_buffer); + + /* Change image layout for shaders to be able to read it */ + image_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + command_buffer = rse_begin_single_time_command(); + vkCmdPipelineBarrier(command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, + 0, + NULL, + 0, + NULL, + 1, + &image_barrier); + rse_end_single_time_comands(command_buffer); + + /* Fill image view */ + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = NULL; + image_view_create_info.flags = 0U; + image_view_create_info.image = free_texture_image->image; + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_create_info.format = format; + // image_view_create_info.components = 0; + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = 1; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = 1; + + if (VK_SUCCESS != vkCreateImageView(g_vulkan_state.device, + &image_view_create_info, + NULL, + &free_texture_image->image_view)) { + LOGF("Failed to create image view"); + return 1; + } + + free_texture_image->id_taken = IMAGE_TAKEN; + return VULKAN_ERROR_NO_ERROR; +} + +uint8_t init_vulkan_images() +{ + uint8_t status = VULKAN_ERROR_NO_ERROR; + + // memset(g_texture_images, 0, sizeof(struct rse_vulkan_image_t) * RSE_MAX_IMAGE_COUNT); + + STATUS_CHECK(create_sampler()); + + return status; +} + +void rse_load_texture(const char* file_path) +{ + int width = 0; + int height = 0; + int channels = 0; + stbi_uc* pixel_buffer = NULL; + + + pixel_buffer = stbi_load(file_path, &width, &height, &channels, STBI_rgb_alpha); + + if (pixel_buffer == NULL) { + LOGE("Failed to load texture from file: %s", file_path); + return; + } + + /* Create actual Vulkan Image */ + create_vulkan_image(width, + height, + VK_FORMAT_R8G8B8A8_SRGB, + pixel_buffer); + + stbi_image_free(pixel_buffer); + +} + +void rse_destroy_textures() +{ + size_t i = 0U; + + vkDestroySampler(g_vulkan_state.device, g_sampler, NULL); + + for (i = 0U; i < RSE_MAX_IMAGE_COUNT; ++i) { + if (g_texture_images[i].id_taken == IMAGE_TAKEN) { + vkDestroyImageView(g_vulkan_state.device, g_texture_images[i].image_view, NULL); + vmaDestroyImage(g_vulkan_state.allocator, g_texture_images[i].image, g_texture_images[i].allocation); + } + } +} + +VkImageView rse_get_image_view(size_t image_id) +{ + return g_texture_images[image_id].image_view; +} + +VkSampler rse_get_texture_sampler() +{ + return g_sampler; +} \ No newline at end of file diff --git a/graphics/src/rse_vulkan_image.h b/graphics/src/rse_vulkan_image.h new file mode 100644 index 00000000..f5bc659f --- /dev/null +++ b/graphics/src/rse_vulkan_image.h @@ -0,0 +1,33 @@ +/** + * @file rse_vulkan_image.h + * @author Piotr Krygier (everyonecancode@gmail.com) + * @brief Library for loading and handling textures + * @version 0.1 + * @date 2023-09-27 + * + * @copyright Copyright (c) 2023 + * + */ + + +#ifndef RSE_TEXTURE_H +#define RSE_TEXTURE_H + +#include "utilities/file_utils.h" +#include "vulkan_commons.h" + +#include + +#define RSE_MAX_IMAGE_COUNT 256 + +uint8_t init_vulkan_images(void); + +void rse_load_texture(const char* path); + +void rse_destroy_textures(void); + +VkImageView rse_get_image_view(size_t image_id); + +VkSampler rse_get_texture_sampler(); + +#endif /* RSE_TEXTURE_H */ \ No newline at end of file diff --git a/graphics/src/vulkan_base.c b/graphics/src/vulkan_base.c index f5ce8d3e..457af1a2 100644 --- a/graphics/src/vulkan_base.c +++ b/graphics/src/vulkan_base.c @@ -31,9 +31,11 @@ #include "window.h" #include "vulkan_commons.h" +#include "rse_vulkan_commands.h" #include "vulkan_buffers.h" #include "vulkan_descriptors.h" #include "vulkan_pipeline.h" +#include "rse_vulkan_image.h" #include #include @@ -564,12 +566,22 @@ uint8_t init_vulkan() STATUS_CHECK(create_pipeline()); + STATUS_CHECK(init_commands()); + STATUS_CHECK(create_buffers()); + + STATUS_CHECK(init_vulkan_images()); + + //FIXME: Images have to be add after vulkan initialization but before creating descriptors. + // I have to fix it somehow to be able to add textures from anywhere in the code in some initialization step + // at the beginnning. + rse_load_texture("../../miotacz_piorunów.png"); STATUS_CHECK(create_descriptors()); STATUS_CHECK(create_sync_objects()); + rse_init_time(); return status; } @@ -657,6 +669,10 @@ void deinit_vulkan() vkDestroyFence(g_vulkan_state.device, g_in_flight_fences[i], NULL); } + rse_destroy_textures(); + + destroy_commands(); + destroy_pipeline(); vmaDestroyAllocator(g_vulkan_state.allocator); diff --git a/graphics/src/vulkan_buffers.c b/graphics/src/vulkan_buffers.c index 6c43a681..c8b5a2a4 100644 --- a/graphics/src/vulkan_buffers.c +++ b/graphics/src/vulkan_buffers.c @@ -5,6 +5,7 @@ #include "utilities/logger.h" #include "mesh_controller.h" #include "rse_math.h" +#include "rse_vulkan_commands.h" #include #include @@ -19,141 +20,28 @@ struct rse_vulkan_buffer_t g_index_buffer; struct rse_vulkan_buffer_t g_instance_buffers[MAX_MESH_NUMBER]; struct rse_vulkan_buffer_t g_draw_indirect_command_buffer; -VkCommandPool g_command_pool = VK_NULL_HANDLE; - -/** - * @brief Helper function. Creates a buffer object - * - * @param size Size of the target buffer - * @param buffer_usage Buffer usage flags - * @param memory_usage memory usage flags (mostly VMA_MEMORY_USAGE_AUTO) - * @param allocation_flags VMA allocation flags - * @param buffer Buffer, that will be allocated - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED - */ -static uint8_t create_buffer(const VkDeviceSize size, - VkBufferUsageFlags buffer_usage, - VmaMemoryUsage memory_usage, - const VmaAllocationCreateFlags allocation_flags, - struct rse_vulkan_buffer_t* buffer) -{ - VkBufferCreateInfo vertex_buffer_info = {}; - VmaAllocationCreateInfo create_info = {}; - - vertex_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - vertex_buffer_info.pNext = NULL; - vertex_buffer_info.flags = 0U; - vertex_buffer_info.size = size; - vertex_buffer_info.usage = buffer_usage; - vertex_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - vertex_buffer_info.queueFamilyIndexCount = 0U; - vertex_buffer_info.pQueueFamilyIndices = NULL; - - create_info.flags = allocation_flags; - create_info.usage = memory_usage; - create_info.memoryTypeBits = 0U; - create_info.requiredFlags = 0U; - create_info.preferredFlags = 0U; - create_info.pool = VK_NULL_HANDLE; - create_info.pUserData = VK_NULL_HANDLE; - create_info.priority = 0.0f; - - if (VK_SUCCESS != vmaCreateBuffer(g_vulkan_state.allocator, - &vertex_buffer_info, - &create_info, - &buffer->buffer, - &buffer->allocation, - &buffer->allocation_info)) { - LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]); - return VULKAN_ERROR_BUFFER_CREATION_FAILED; - } - - return VULKAN_ERROR_NO_ERROR; -} - /** * @brief Copy one buffer's data to another * * @param src Source buffer * @param dst Destination buffer * @param size Size of buffer to copy - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: */ -static uint8_t copy_buffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, VkDeviceSize dest_offset) +static void copy_buffer(VkBuffer src, VkBuffer dst, VkDeviceSize size, VkDeviceSize dest_offset) { /* Vulkan buffers can only be copied using command buffers */ - VkResult status = VK_FALSE; VkBufferCopy copy_region = {}; - VkSubmitInfo submit_info = {}; - VkCommandBuffer copy_command_buffer = NULL; - VkCommandBufferBeginInfo begin_info = {}; - VkCommandBufferAllocateInfo alloc_info = {}; + VkCommandBuffer command_buffer = {0}; - alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - alloc_info.pNext = NULL; - alloc_info.commandPool = g_command_pool; - alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - alloc_info.commandBufferCount = 1; + command_buffer = rse_begin_single_time_command(); - begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - begin_info.pNext = NULL; - begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin_info.pInheritanceInfo = NULL; - - copy_region.srcOffset = 0; + copy_region.srcOffset = 0U; copy_region.dstOffset = dest_offset; copy_region.size = size; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.pNext = NULL; - submit_info.waitSemaphoreCount = 0; - submit_info.pWaitSemaphores = NULL; - submit_info.pWaitDstStageMask = NULL; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = ©_command_buffer; - submit_info.signalSemaphoreCount = 0; - submit_info.pSignalSemaphores = NULL; + vkCmdCopyBuffer(command_buffer, src, dst, 1, ©_region); - // FIXME: Add result checks - status = vkAllocateCommandBuffers(g_vulkan_state.device, &alloc_info, ©_command_buffer); - status = vkBeginCommandBuffer(copy_command_buffer, &begin_info); - vkCmdCopyBuffer(copy_command_buffer, src, dst, 1, ©_region); - status = vkEndCommandBuffer(copy_command_buffer); - status = vkQueueSubmit(g_vulkan_state.graphics_queue, 1, &submit_info, VK_NULL_HANDLE); - /* TODO: Use fences to wait instead of idle */ - status = vkQueueWaitIdle(g_vulkan_state.graphics_queue); - vkFreeCommandBuffers(g_vulkan_state.device, g_command_pool, 1, ©_command_buffer); - - if (status != VK_SUCCESS) { - return VULKAN_ERROR_NO_ERROR; - } - return VULKAN_ERROR_NO_ERROR; -} - - - -/** - * @brief Create Command Pools - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED - */ -static uint8_t create_command_pools() -{ - VkCommandPoolCreateInfo create_info; - - create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - create_info.pNext = NULL; - create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - create_info.queueFamilyIndex = g_vulkan_state.queue_family_indices[0]; /* TODO: Change when more families are needed */ - - if (VK_SUCCESS != vkCreateCommandPool(g_vulkan_state.device, &create_info, NULL, &g_command_pool)) { - LOGF(vulkan_error_messages[VULKAN_COMMAND_POOL_CREATION_FAILED]); - return VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED; - } - - return VULKAN_ERROR_NO_ERROR; + rse_end_single_time_comands(command_buffer); } @@ -262,32 +150,52 @@ static uint8_t create_uniform_buffer() return VULKAN_ERROR_NO_ERROR; } -/** - * @brief Allocate Command Buffers - * - * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: - * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED - */ -static uint8_t allocate_command_buffers() +uint8_t create_buffer(const VkDeviceSize size, + VkBufferUsageFlags buffer_usage, + VmaMemoryUsage memory_usage, + const VmaAllocationCreateFlags allocation_flags, + struct rse_vulkan_buffer_t* buffer) { - VkCommandBufferAllocateInfo allocate_info; + VkBufferCreateInfo vertex_buffer_info = {}; + VmaAllocationCreateInfo create_info = {}; - /* Allocate memory for command buffers */ - allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocate_info.pNext = NULL; - allocate_info.commandPool = g_command_pool; - allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; /* TODO: When needed, add secondary command buffer */ - allocate_info.commandBufferCount = SWAP_BUFFER_COUNT; + vertex_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + vertex_buffer_info.pNext = NULL; + vertex_buffer_info.flags = 0U; + vertex_buffer_info.size = size; + vertex_buffer_info.usage = buffer_usage; + vertex_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vertex_buffer_info.queueFamilyIndexCount = 0U; + vertex_buffer_info.pQueueFamilyIndices = NULL; - if (VK_SUCCESS != vkAllocateCommandBuffers(g_vulkan_state.device, - &allocate_info, - g_vulkan_state.command_buffers)) { - LOGF(vulkan_error_messages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED]); - return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED; + create_info.flags = allocation_flags; + create_info.usage = memory_usage; + create_info.memoryTypeBits = 0U; + create_info.requiredFlags = 0U; + create_info.preferredFlags = 0U; + create_info.pool = VK_NULL_HANDLE; + create_info.pUserData = VK_NULL_HANDLE; + create_info.priority = 0.0f; + + if (VK_SUCCESS != vmaCreateBuffer(g_vulkan_state.allocator, + &vertex_buffer_info, + &create_info, + &buffer->buffer, + &buffer->allocation, + &buffer->allocation_info)) { + LOGF(vulkan_error_messages[VULKAN_BUFFER_CREATION_FAILED]); + return VULKAN_ERROR_BUFFER_CREATION_FAILED; } + return VULKAN_ERROR_NO_ERROR; } +void destroy_buffer(struct rse_vulkan_buffer_t* buffer) +{ + vmaDestroyBuffer(g_vulkan_state.allocator, buffer->buffer, buffer->allocation); +} + + void update_uniform_buffers() { struct rse_uniform_buffer_object_t ubo = {}; @@ -492,14 +400,12 @@ uint8_t record_command_buffer(uint32_t image_index) uint8_t create_buffers() { - uint8_t status = VULKAN_ERROR_NO_ERROR;\ + uint8_t status = VULKAN_ERROR_NO_ERROR; - STATUS_CHECK(create_command_pools()); STATUS_CHECK(create_vertex_buffer()); STATUS_CHECK(create_index_buffer()); STATUS_CHECK(create_instance_buffers()); STATUS_CHECK(create_uniform_buffer()); - STATUS_CHECK(allocate_command_buffers()); return status; } @@ -509,6 +415,7 @@ void reset_command_buffer() vkResetCommandBuffer(g_vulkan_state.command_buffers[g_vulkan_state.current_frame], /*VkCommandBufferResetFlagBits*/ 0); } + void destroy_buffers() { size_t i = 0U; @@ -526,6 +433,4 @@ void destroy_buffers() for(i = 0; i < MAX_MESH_NUMBER; ++i) { vmaDestroyBuffer(g_vulkan_state.allocator, g_instance_buffers[i].buffer, g_instance_buffers[i].allocation); } - - vkDestroyCommandPool(g_vulkan_state.device, g_command_pool, NULL); } \ No newline at end of file diff --git a/graphics/src/vulkan_buffers.h b/graphics/src/vulkan_buffers.h index 056ff48a..9a13c145 100644 --- a/graphics/src/vulkan_buffers.h +++ b/graphics/src/vulkan_buffers.h @@ -78,4 +78,30 @@ void reset_command_buffer(); */ void destroy_buffers(); + +/** + * @brief Helper function. Creates a buffer object + * + * @param size Size of the target buffer + * @param buffer_usage Buffer usage flags + * @param memory_usage memory usage flags (mostly VMA_MEMORY_USAGE_AUTO) + * @param allocation_flags VMA allocation flags + * @param buffer Buffer, that will be allocated + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED + */ +uint8_t create_buffer(const VkDeviceSize size, + VkBufferUsageFlags buffer_usage, + VmaMemoryUsage memory_usage, + const VmaAllocationCreateFlags allocation_flags, + struct rse_vulkan_buffer_t* buffer); + + +/** + * @brief Destroy provided buffer + * + * @param buffer buffer to destroy + */ +void destroy_buffer(struct rse_vulkan_buffer_t* buffer); + #endif /* RSE_VULKAN_BUFFERS_H */ \ No newline at end of file diff --git a/graphics/src/vulkan_commons.h b/graphics/src/vulkan_commons.h index ac526c20..3ffb72fb 100644 --- a/graphics/src/vulkan_commons.h +++ b/graphics/src/vulkan_commons.h @@ -30,6 +30,7 @@ struct rse_vertex_t { struct vec3_t pos; struct vec3_t color; + struct vec3_t tex_coords; }; /** diff --git a/graphics/src/vulkan_descriptors.c b/graphics/src/vulkan_descriptors.c index 40b53f43..51310650 100644 --- a/graphics/src/vulkan_descriptors.c +++ b/graphics/src/vulkan_descriptors.c @@ -4,6 +4,7 @@ #include "utilities/logger.h" #include "vulkan_errors.h" #include "vulkan_commons.h" +#include "rse_vulkan_image.h" #include "vulkan/vulkan.h" @@ -20,15 +21,18 @@ static uint8_t create_descriptor_sets(); */ uint8_t create_descriptor_pool() { - VkDescriptorPoolSize pool_size = {}; + VkDescriptorPoolSize pool_sizes[2] = {}; VkDescriptorPoolCreateInfo pool_info = {}; - pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - pool_size.descriptorCount = (uint32_t)(SWAP_BUFFER_COUNT); + pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + pool_sizes[0].descriptorCount = (uint32_t)(SWAP_BUFFER_COUNT); + + pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_sizes[1].descriptorCount = (uint32_t)(SWAP_BUFFER_COUNT); pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - pool_info.poolSizeCount = 1; - pool_info.pPoolSizes = &pool_size; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = pool_sizes; pool_info.maxSets = (uint32_t)(SWAP_BUFFER_COUNT); if (vkCreateDescriptorPool(g_vulkan_state.device, &pool_info, NULL, &gDescriptorPool) != VK_SUCCESS) { @@ -64,21 +68,34 @@ uint8_t create_descriptor_sets() for (size_t i = 0; i < SWAP_BUFFER_COUNT; i++) { VkDescriptorBufferInfo buffer_info = {}; - VkWriteDescriptorSet descriptor_write = {}; + VkWriteDescriptorSet descriptor_writes[2] = {}; + VkDescriptorImageInfo image_info = {}; buffer_info.buffer = g_vulkan_state.uniform_buffers[i].buffer; buffer_info.offset = 0; buffer_info.range = sizeof(struct rse_uniform_buffer_object_t); - descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptor_write.dstSet = g_vulkan_state.descriptor_sets[i]; - descriptor_write.dstBinding = 0; - descriptor_write.dstArrayElement = 0; - descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptor_write.descriptorCount = 1; - descriptor_write.pBufferInfo = &buffer_info; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info.imageView = rse_get_image_view(0); + image_info.sampler = rse_get_texture_sampler(); - vkUpdateDescriptorSets(g_vulkan_state.device, 1, &descriptor_write, 0, NULL); + descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_writes[0].dstSet = g_vulkan_state.descriptor_sets[i]; + descriptor_writes[0].dstBinding = 0; + descriptor_writes[0].dstArrayElement = 0; + descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_writes[0].descriptorCount = 1; + descriptor_writes[0].pBufferInfo = &buffer_info; + + descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_writes[1].dstSet = g_vulkan_state.descriptor_sets[i]; + descriptor_writes[1].dstBinding = 1; + descriptor_writes[1].dstArrayElement = 0; + descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_writes[1].descriptorCount = 1; + descriptor_writes[1].pImageInfo = &image_info; + + vkUpdateDescriptorSets(g_vulkan_state.device, 2, descriptor_writes, 0, NULL); } return VULKAN_ERROR_NO_ERROR; diff --git a/graphics/src/vulkan_pipeline.c b/graphics/src/vulkan_pipeline.c index f895e9f9..8406521f 100644 --- a/graphics/src/vulkan_pipeline.c +++ b/graphics/src/vulkan_pipeline.c @@ -72,7 +72,7 @@ static VkVertexInputBindingDescription get_binding_description() static VkVertexInputAttributeDescription* get_attribute_descriptions() { - static VkVertexInputAttributeDescription attributeDescriptions[2]; + static VkVertexInputAttributeDescription attributeDescriptions[3]; attributeDescriptions[0].binding = 0; attributeDescriptions[0].location = 0; /* inPosition in shader.vert */ @@ -84,6 +84,11 @@ static VkVertexInputAttributeDescription* get_attribute_descriptions() attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; attributeDescriptions[1].offset = offsetof(struct rse_vertex_t, color); + attributeDescriptions[2].binding = 0; + attributeDescriptions[2].location = 2; /* inTexCoords in shader.vert */ + attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[2].offset = offsetof(struct rse_vertex_t, tex_coords); + return attributeDescriptions; } @@ -258,7 +263,9 @@ uint8_t create_render_pass() uint8_t create_descriptor_set_layout() { VkDescriptorSetLayoutCreateInfo layout_info = {}; + VkDescriptorSetLayoutBinding bindings[2]; VkDescriptorSetLayoutBinding ubo_layout_binding = {}; + VkDescriptorSetLayoutBinding sampler_layout_binding = {}; ubo_layout_binding.binding = 0; ubo_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; @@ -266,11 +273,19 @@ uint8_t create_descriptor_set_layout() ubo_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; ubo_layout_binding.pImmutableSamplers = NULL; + sampler_layout_binding.binding = 1; + sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + sampler_layout_binding.descriptorCount = 1; + sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + sampler_layout_binding.pImmutableSamplers = NULL; + + bindings[0] = ubo_layout_binding; + bindings[1] = sampler_layout_binding; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_info.pNext = NULL; layout_info.flags = 0U; - layout_info.bindingCount = 1U; - layout_info.pBindings = &ubo_layout_binding; + layout_info.bindingCount = 2U; + layout_info.pBindings = bindings; if (vkCreateDescriptorSetLayout(g_vulkan_state.device, &layout_info, NULL, &g_vulkan_state.descriptor_set_layout) != VK_SUCCESS) { LOGF(vulkan_error_messages[VULKAN_DESCRIPTORSETLAYOUT_CREATION_FAILED]); @@ -440,22 +455,22 @@ void cleanup_swapchain() static uint8_t fill_shader_stages(VkPipelineShaderStageCreateInfo* shader_stages) { char* buffer; - size_t buffer_size; + long buffer_size; VkShaderModule vertex_shader_module; VkShaderModule fragments_shader_module; VkPipelineShaderStageCreateInfo vert_shader_stage_info = {}; VkPipelineShaderStageCreateInfo frag_shader_stage_info = {}; - read_file(g_shader_paths[VERTEX], &buffer_size, NULL); + rse_read_file(g_shader_paths[VERTEX], &buffer_size, NULL); buffer = malloc(buffer_size); - read_file(g_shader_paths[VERTEX], &buffer_size, buffer); + rse_read_file(g_shader_paths[VERTEX], &buffer_size, buffer); vertex_shader_module = create_shader_module(buffer, buffer_size); free(buffer); - read_file(g_shader_paths[FRAGMENTS], &buffer_size, NULL); + rse_read_file(g_shader_paths[FRAGMENTS], &buffer_size, NULL); buffer = malloc(buffer_size); - read_file(g_shader_paths[FRAGMENTS], &buffer_size, buffer); + rse_read_file(g_shader_paths[FRAGMENTS], &buffer_size, buffer); fragments_shader_module = create_shader_module(buffer, buffer_size); free(buffer); @@ -539,7 +554,7 @@ void fill_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_i VkVertexInputBindingDescription vertex_binding_description = get_binding_description(); VkVertexInputAttributeDescription* vertex_attribute_descriptions = get_attribute_descriptions(); VkVertexInputBindingDescription* bindingDescriptions = malloc(sizeof(VkVertexInputBindingDescription) * 2); - VkVertexInputAttributeDescription* attributeDescriptions = malloc(sizeof(VkVertexInputAttributeDescription) * 5); + VkVertexInputAttributeDescription* attributeDescriptions = malloc(sizeof(VkVertexInputAttributeDescription) * 6); bindingDescriptions[0] = vertex_binding_description; @@ -547,22 +562,22 @@ void fill_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_i bindingDescriptions[1].stride = sizeof(struct rse_instance_data_t); //TODO: change bindingDescriptions[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; - memcpy(attributeDescriptions, vertex_attribute_descriptions, sizeof(VkVertexInputAttributeDescription) * 2); - - attributeDescriptions[2].binding = 1; - attributeDescriptions[2].location = 2; - attributeDescriptions[2].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[2].offset = offsetof(struct rse_instance_data_t, pos); + memcpy(attributeDescriptions, vertex_attribute_descriptions, sizeof(VkVertexInputAttributeDescription) * 3); attributeDescriptions[3].binding = 1; attributeDescriptions[3].location = 3; attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[3].offset = offsetof(struct rse_instance_data_t, rot); + attributeDescriptions[3].offset = offsetof(struct rse_instance_data_t, pos); attributeDescriptions[4].binding = 1; attributeDescriptions[4].location = 4; - attributeDescriptions[4].format = VK_FORMAT_R32_SFLOAT; - attributeDescriptions[4].offset = offsetof(struct rse_instance_data_t, scale); + attributeDescriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[4].offset = offsetof(struct rse_instance_data_t, rot); + + attributeDescriptions[5].binding = 1; + attributeDescriptions[5].location = 5; + attributeDescriptions[5].format = VK_FORMAT_R32_SFLOAT; + attributeDescriptions[5].offset = offsetof(struct rse_instance_data_t, scale); // ************************ vertex_input_info->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; @@ -571,7 +586,7 @@ void fill_vertex_input_info(VkPipelineVertexInputStateCreateInfo* vertex_input_i vertex_input_info->vertexBindingDescriptionCount = 2U; vertex_input_info->pVertexBindingDescriptions = bindingDescriptions; - vertex_input_info->vertexAttributeDescriptionCount = 5U; + vertex_input_info->vertexAttributeDescriptionCount = 6U; vertex_input_info->pVertexAttributeDescriptions = attributeDescriptions; } diff --git a/red_scarf_engine/src/main.c b/red_scarf_engine/src/main.c index 4a5fa867..b438f964 100644 --- a/red_scarf_engine/src/main.c +++ b/red_scarf_engine/src/main.c @@ -1,6 +1,7 @@ #include "graphics/rse_graphics.h" #include "utilities/rse_task.h" + int main(int argc, char** argv) { rse_thread_t graphics_task; @@ -10,7 +11,8 @@ int main(int argc, char** argv) rse_graphics_init(); rse_create_task(&graphics_task, (thread_worker_t)rse_graphics_run, NULL); - rse_task_join(&graphics_task); - rse_task_exit(); //TODO: Figure out why program is so much faster if we don't join the graphics_task. WTF. + rse_task_exit(); + + } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3e9d8c9c..aabfea54 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,7 @@ find_package(Criterion) add_executable(tests -src/rse_math_tests.c) +src/rse_graphics_tests.c) target_include_directories(tests PRIVATE ${criterion_INCLUDE_DIR} diff --git a/tests/src/rse_graphics_tests.c b/tests/src/rse_graphics_tests.c new file mode 100644 index 00000000..399b2b5f --- /dev/null +++ b/tests/src/rse_graphics_tests.c @@ -0,0 +1,186 @@ +#include "criterion/criterion.h" +#include "graphics/src/rse_math.h" + + +#include +#include + +/*-----------------RSE MATH TEST------------------------*/ + +TestSuite(rse_math); + +Test(rse_math, normalize) +{ + float res_x = 0.8017f; + float res_y = 0.2672f; + float res_z = 0.5345f; + struct vec3_t test_vec = {3.0f, 1.0f, 2.0f}; + + struct vec3_t result = rse_math_normalize_vec3(&test_vec); + + cr_expect_float_eq(result.x, res_x, 0.0001, "Expected %f, got %f", res_x, result.x); + cr_expect_float_eq(result.y, res_y, 0.0001, "Expected %f, got %f", res_y, result.y); + cr_expect_float_eq(result.z, res_z, 0.0001, "Expected %f, got %f", res_z, result.z); +} + +Test(rse_math, cross) +{ + float res_x = -3.0f; + float res_y = 6.0f; + float res_z = -3.0f; + struct vec3_t test_vec1 = {2.0f, 3.0f, 4.0f}; + struct vec3_t test_vec2 = {5.0f, 6.0f, 7.0f}; + + struct vec3_t result = rse_math_cross_vec3(&test_vec1, &test_vec2); + + cr_expect_float_eq(result.x, res_x, 0.0001, "Expected %f, got %f", res_x, result.x); + cr_expect_float_eq(result.y, res_y, 0.0001, "Expected %f, got %f", res_y, result.y); + cr_expect_float_eq(result.z, res_z, 0.0001, "Expected %f, got %f", res_z, result.z); +} + +Test(rse_math, look_at) +{ + struct vec3_t eye; + struct vec3_t center; + struct vec3_t up; + struct mat4_t result; + struct mat4_t expected; + + eye.x = 0.0f; + eye.y = 0.0f; + eye.z = 5.0f; + + center.x = 0.0f; + center.y = 0.0f; + center.z = 0.0f; + + up.x = 0.0f; + up.y = 0.1f; + up.z = 0.0f; + + expected.x_1 = 1.0f; + expected.y_1 = 0.0f; + expected.z_1 = 0.0f; + expected.w_1 = 0.0f; + + expected.x_2 = 0.0f; + expected.y_2 = 1.0f; + expected.z_2 = 0.0f; + expected.w_2 = 0.0f; + + expected.x_3 = 0.0f; + expected.y_3 = 0.0f; + expected.z_3 = 1.0f; + expected.w_3 = 0.0f; + + expected.x_4 = 0.0f; + expected.y_4 = 0.0f; + expected.z_4 = -5.0f; + expected.w_4 = 1.0f; + + result = rse_math_look_at(&eye, ¢er, &up); + + cr_expect_float_eq(result.x_1, expected.x_1, 0.0001, "Expected x_1 %f, got %f", expected.x_1, result.x_1); + cr_expect_float_eq(result.y_1, expected.y_1, 0.0001, "Expected y_1 %f, got %f", expected.y_1, result.y_1); + cr_expect_float_eq(result.z_1, expected.z_1, 0.0001, "Expected z_1 %f, got %f", expected.z_1, result.z_1); + cr_expect_float_eq(result.w_1, expected.w_1, 0.0001, "Expected w_1 %f, got %f", expected.w_1, result.w_1); + + cr_expect_float_eq(result.x_2, expected.x_2, 0.0001, "Expected x_2 %f, got %f", expected.x_2, result.x_2); + cr_expect_float_eq(result.y_2, expected.y_2, 0.0001, "Expected y_2 %f, got %f", expected.y_2, result.y_2); + cr_expect_float_eq(result.z_2, expected.z_2, 0.0001, "Expected z_2 %f, got %f", expected.z_2, result.z_2); + cr_expect_float_eq(result.w_2, expected.w_2, 0.0001, "Expected w_2 %f, got %f", expected.w_2, result.w_2); + + cr_expect_float_eq(result.x_3, expected.x_3, 0.0001, "Expected x_3 %f, got %f", expected.x_3, result.x_3); + cr_expect_float_eq(result.y_3, expected.y_3, 0.0001, "Expected y_3 %f, got %f", expected.y_3, result.y_3); + cr_expect_float_eq(result.z_3, expected.z_3, 0.0001, "Expected z_3 %f, got %f", expected.z_3, result.z_3); + cr_expect_float_eq(result.w_3, expected.w_3, 0.0001, "Expected w_3 %f, got %f", expected.w_3, result.w_3); + + cr_expect_float_eq(result.x_4, expected.x_4, 0.0001, "Expected x_4 %f, got %f", expected.x_4, result.x_4); + cr_expect_float_eq(result.y_4, expected.y_4, 0.0001, "Expected y_4 %f, got %f", expected.y_4, result.y_4); + cr_expect_float_eq(result.z_4, expected.z_4, 0.0001, "Expected z_4 %f, got %f", expected.z_4, result.z_4); + cr_expect_float_eq(result.w_4, expected.w_4, 0.0001, "Expected w_4 %f, got %f", expected.w_4, result.w_4); + +} + +Test(rse_math, radians) +{ + float degrees; + float radians; + float expected_radians; + + degrees = 45.0f; + expected_radians = 0.7853; + + radians = rse_math_deg_to_radians(degrees); + + cr_expect_float_eq(radians, expected_radians, 0.0001, "Expected %f, got %f", expected_radians, radians); +} + +Test(rse_math, perspective) +{ + float fovy; + float width; + float height; + float z_near; + float z_far; + struct mat4_t result; + struct mat4_t expected; + + fovy = rse_math_deg_to_radians(45.0f); + width = 640.0f; + height = 480.0f; + z_near = 0.1f; + z_far = 20.0f; + + expected.x_1 = -1.81066012; + expected.y_1 = 0; + expected.z_1 = 0; + expected.w_1 = 0; + + expected.x_2 = 0; + expected.y_2 = 2.41421342; + expected.z_2 = 0; + expected.w_2 = 0; + + expected.x_3 = 0; + expected.y_3 = 0; + expected.z_3 = -1.0100503; + expected.w_3 = -1; + + expected.x_4 = 0; + expected.y_4 = 0; + expected.z_4 = -0.201005027; + expected.w_4 = 0; + + result = rse_math_perspective(fovy, width / height, z_near, z_far); + + cr_expect_float_eq(result.x_1, expected.x_1, 0.0001, "Expected x_1 %f, got %f", expected.x_1, result.x_1); + cr_expect_float_eq(result.y_1, expected.y_1, 0.0001, "Expected y_1 %f, got %f", expected.y_1, result.y_1); + cr_expect_float_eq(result.z_1, expected.z_1, 0.0001, "Expected z_1 %f, got %f", expected.z_1, result.z_1); + cr_expect_float_eq(result.w_1, expected.w_1, 0.0001, "Expected w_1 %f, got %f", expected.w_1, result.w_1); + + cr_expect_float_eq(result.x_2, expected.x_2, 0.0001, "Expected x_2 %f, got %f", expected.x_2, result.x_2); + cr_expect_float_eq(result.y_2, expected.y_2, 0.0001, "Expected y_2 %f, got %f", expected.y_2, result.y_2); + cr_expect_float_eq(result.z_2, expected.z_2, 0.0001, "Expected z_2 %f, got %f", expected.z_2, result.z_2); + cr_expect_float_eq(result.w_2, expected.w_2, 0.0001, "Expected w_2 %f, got %f", expected.w_2, result.w_2); + + cr_expect_float_eq(result.x_3, expected.x_3, 0.0001, "Expected x_3 %f, got %f", expected.x_3, result.x_3); + cr_expect_float_eq(result.y_3, expected.y_3, 0.0001, "Expected y_3 %f, got %f", expected.y_3, result.y_3); + cr_expect_float_eq(result.z_3, expected.z_3, 0.0001, "Expected z_3 %f, got %f", expected.z_3, result.z_3); + cr_expect_float_eq(result.w_3, expected.w_3, 0.0001, "Expected w_3 %f, got %f", expected.w_3, result.w_3); + + cr_expect_float_eq(result.x_4, expected.x_4, 0.0001, "Expected x_4 %f, got %f", expected.x_4, result.x_4); + cr_expect_float_eq(result.y_4, expected.y_4, 0.0001, "Expected y_4 %f, got %f", expected.y_4, result.y_4); + cr_expect_float_eq(result.z_4, expected.z_4, 0.0001, "Expected z_4 %f, got %f", expected.z_4, result.z_4); + cr_expect_float_eq(result.w_4, expected.w_4, 0.0001, "Expected w_4 %f, got %f", expected.w_4, result.w_4); + +} + +/*-----------------RSE TEXTURES TEST------------------------*/ +#include "graphics/src/rse_texture.h" +TestSuite(rse_textures); + +Test(rse_textures, load_png_image) +{ + rse_load_texture("../../miotacz_piorunów.png"); +} \ No newline at end of file diff --git a/utilities/file_utils.h b/utilities/file_utils.h index 38d971b4..74544936 100644 --- a/utilities/file_utils.h +++ b/utilities/file_utils.h @@ -11,7 +11,9 @@ * @param bytes_count Returned bytes count * @param buffer Returned filled buffer */ -void read_file(const char* file_path, long* bytes_count, char* buffer); +void rse_read_file(const char* file_path, long* bytes_count, char* buffer); + +void rse_write_to_file(const char* file_name, long bytes_count, char* buffer); #endif /* RSE_FILE_UTILS_H */ diff --git a/utilities/src/file_utils.c b/utilities/src/file_utils.c index 1fe346e0..85b1cc18 100644 --- a/utilities/src/file_utils.c +++ b/utilities/src/file_utils.c @@ -3,7 +3,7 @@ #include #include -void read_file(const char* file_name, long* bytes_count, char* buffer) +void rse_read_file(const char* file_name, long* bytes_count, char* buffer) { FILE* file; @@ -20,3 +20,13 @@ void read_file(const char* file_name, long* bytes_count, char* buffer) fread(buffer, *bytes_count, 1, file); fclose(file); } + +void rse_write_to_file(const char* file_name, long bytes_count, char* buffer) +{ + FILE* file; + + file = fopen(file_name, "wb"); + + fwrite(buffer, bytes_count, 1, file); + fclose(file); +} \ No newline at end of file diff --git a/utilities/src/logger.c b/utilities/src/logger.c index 0c74c07b..aa2029f5 100644 --- a/utilities/src/logger.c +++ b/utilities/src/logger.c @@ -7,7 +7,6 @@ const char* log_levels[] = { void _log_log(unsigned int level, const char* file, int line, const char* fmt, ...) { - char buf[256]; va_list list; va_start(list, fmt);