From fe81038bbfd162f5c1d24e7dcb3a98bfdc80288c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 17 Dec 2025 08:53:37 +0100 Subject: [PATCH 1/6] vcpkg: latest greatest --- src/contrib/vcpkg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contrib/vcpkg b/src/contrib/vcpkg index f75c836..e7d7451 160000 --- a/src/contrib/vcpkg +++ b/src/contrib/vcpkg @@ -1 +1 @@ -Subproject commit f75c836a67777a86a2c1116a28b179827f028b66 +Subproject commit e7d7451462697d77ef319ddf2da8ff7320a82662 From feebbacdf4459ea19edcafc1a51d2e854c14faff Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 17 Dec 2025 15:46:38 +0100 Subject: [PATCH 2/6] Fix compile script --- scripts/compile_shader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/compile_shader.py b/scripts/compile_shader.py index a6de99f..ba6f167 100644 --- a/scripts/compile_shader.py +++ b/scripts/compile_shader.py @@ -38,7 +38,7 @@ def copy_shader(source, dest): def main(): parser = argparse.ArgumentParser() parser.add_argument('--verbose', action='store_true', default=False, help='The full output will be shown') - parser.add_argument('--shader', type=str, default='./', help='The folder containing the shaders') + parser.add_argument('--shader', type=str, required=True, default='./', help='The folder containing the shaders') args = parser.parse_args() print("shader folder: " + str(args.shader)) From 37173aee99078138f0277ffc326cfd6c592e37c4 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 19 Dec 2025 14:18:37 +0100 Subject: [PATCH 3/6] Add texture view --- src/runtime/renderer/RHIVulkan.cpp | 70 ++++++++++++++++++++++++++++++ vcpkg.json | 3 +- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/runtime/renderer/RHIVulkan.cpp b/src/runtime/renderer/RHIVulkan.cpp index 9a9a68d..0e002fb 100644 --- a/src/runtime/renderer/RHIVulkan.cpp +++ b/src/runtime/renderer/RHIVulkan.cpp @@ -7,6 +7,9 @@ #include #include +#define STB_IMAGE_IMPLEMENTATION +#include + #include #include #include @@ -126,6 +129,8 @@ namespace segfault::renderer { VkDeviceMemory vertexBufferMemory{}; VkBuffer indexBuffer{}; VkDeviceMemory indexBufferMemory{}; + VkImage textureImage{}; + VkDeviceMemory textureImageMemory{}; RHIImpl() = default; ~RHIImpl() = default; @@ -162,6 +167,8 @@ namespace segfault::renderer { void cleanupSwapChain(); void recreateSwapChain(); uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory); + void createTextureImage(); void createVertexBuffer(); void createIndexBuffer(); void createUniformBuffers(); @@ -1063,6 +1070,68 @@ namespace segfault::renderer { throw std::runtime_error("failed to find suitable memory type!"); } + void RHIImpl::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, + VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, + VkDeviceMemory& imageMemory) { + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = width; + imageInfo.extent.height = height; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = format; + imageInfo.tiling = tiling; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = usage; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { + throw std::runtime_error("failed to create image!"); + } + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(device, image, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); + + if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate image memory!"); + } + + vkBindImageMemory(device, image, imageMemory, 0); + } + + void RHIImpl::createTextureImage() { + int texWidth, texHeight, texChannels; + stbi_uc* pixels = stbi_load("textures/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); + VkDeviceSize imageSize = texWidth * texHeight * 4; + + if (!pixels) { + throw std::runtime_error("failed to load texture image!"); + } + + 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); + + void* data; + vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(device, stagingBufferMemory); + + stbi_image_free(pixels); + + createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + textureImage, textureImageMemory); + } + void RHIImpl::createVertexBuffer() { VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); @@ -1259,6 +1328,7 @@ namespace segfault::renderer { mImpl->createCommandPool(mImpl->queueFamilyIndices); mImpl->createCommandBuffer(); mImpl->createSyncObjects(); + mImpl->createTextureImage(); mImpl->createVertexBuffer(); mImpl->createIndexBuffer(); diff --git a/vcpkg.json b/vcpkg.json index fae77d5..6a0a087 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -3,6 +3,7 @@ "glew", "glm", "gtest", + "nlohmann-json", { "name": "sdl2", "features": [ @@ -10,6 +11,6 @@ ] }, "volk", - "nlohmann-json" + "stb" ] } From aa84a555127f089c42b7f51ce7d641de8b498d9f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 24 Mar 2026 16:18:32 +0100 Subject: [PATCH 4/6] Next samlper for textureview --- assets/textures/SegFault.jpg | Bin 0 -> 24367 bytes src/examples/hello_world/hello_world.cpp | 4 +- src/runtime/application/app.cpp | 12 +- src/runtime/core/filearchive.h | 2 +- src/runtime/core/segfault.h | 6 +- src/runtime/renderer/RHIVulkan.cpp | 275 ++++++++++++++++++----- 6 files changed, 235 insertions(+), 64 deletions(-) create mode 100644 assets/textures/SegFault.jpg diff --git a/assets/textures/SegFault.jpg b/assets/textures/SegFault.jpg new file mode 100644 index 0000000000000000000000000000000000000000..63819a85e5ce8e9dde337b78380929072ac75dd1 GIT binary patch literal 24367 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 readFile(const std::string& filename) { @@ -513,28 +524,7 @@ namespace segfault::renderer { void RHIImpl::createImageViews() { swapChainImageViews.resize(swapChainImages.size()); for (size_t i = 0; i < swapChainImages.size(); i++) { - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = swapChainImages[i]; - - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = swapChainImageFormat; - - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { - core::logMessage(core::LogType::Error, "failed to create image view"); - return; - } + swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat); } } @@ -791,38 +781,13 @@ namespace segfault::renderer { } void RHIImpl::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandPool = commandPool; - allocInfo.commandBufferCount = 1; - - VkCommandBuffer commandBuffer; - vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - vkBeginCommandBuffer(commandBuffer, &beginInfo); + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); VkBufferCopy copyRegion{}; - copyRegion.srcOffset = 0; // Optional - copyRegion.dstOffset = 0; // Optional copyRegion.size = size; vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); - vkEndCommandBuffer(commandBuffer); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - - vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); - vkQueueWaitIdle(graphicsQueue); - - vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); + endSingleTimeCommands(commandBuffer); } void RHIImpl::createFramebuffers() { @@ -896,7 +861,7 @@ namespace segfault::renderer { renderPassInfo.renderArea.offset = { 0, 0 }; renderPassInfo.renderArea.extent = swapChainExtent; - VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; + VkClearValue clearColor = { {{0.5f, 0.5f, 0.5f, 1.0f}} }; renderPassInfo.clearValueCount = 1; renderPassInfo.pClearValues = &clearColor; @@ -1109,10 +1074,10 @@ namespace segfault::renderer { void RHIImpl::createTextureImage() { int texWidth, texHeight, texChannels; - stbi_uc* pixels = stbi_load("textures/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); + stbi_uc *pixels = stbi_load("textures/SegFault.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); VkDeviceSize imageSize = texWidth * texHeight * 4; - if (!pixels) { + if (pixels == nullptr) { throw std::runtime_error("failed to load texture image!"); } @@ -1120,7 +1085,7 @@ namespace segfault::renderer { VkDeviceMemory stagingBufferMemory; createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); - void* data; + void* data{nullptr}; vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); memcpy(data, pixels, static_cast(imageSize)); vkUnmapMemory(device, stagingBufferMemory); @@ -1129,7 +1094,71 @@ namespace segfault::renderer { createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - textureImage, textureImageMemory); + textureImage, textureImageMemory); + + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(device, stagingBuffer, nullptr); + vkFreeMemory(device, stagingBufferMemory, nullptr); + } + + VkImageView RHIImpl::createImageView(VkImage image, VkFormat format) { + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = format; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + VkImageView imageView; + if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { + throw std::runtime_error("failed to create image view!"); + } + + return imageView; + } + + void RHIImpl::createTextureImageView() { + textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB); + } + + void RHIImpl::createTextureSampler() { + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + + VkPhysicalDeviceProperties properties{}; + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; + + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0f; + + if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { + throw std::runtime_error("failed to create texture sampler!"); + } } void RHIImpl::createVertexBuffer() { @@ -1239,6 +1268,137 @@ namespace segfault::renderer { } } + VkCommandBuffer RHIImpl::beginSingleTimeCommands() { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = commandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + return commandBuffer; + } + + void RHIImpl::endSingleTimeCommands(VkCommandBuffer commandBuffer) { + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(graphicsQueue); + + vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); + } + + void RHIImpl::transitionImageLayout(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + endSingleTimeCommands(commandBuffer); + } + + void RHIImpl::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) { + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + + barrier.srcAccessMask = 0; // TODO + barrier.dstAccessMask = 0; // TODO + + vkCmdPipelineBarrier( + commandBuffer, + 0 /* TODO */, 0 /* TODO */, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + 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 { + throw std::invalid_argument("unsupported layout transition!"); + } + + vkCmdPipelineBarrier( + commandBuffer, + sourceStage, destinationStage, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + endSingleTimeCommands(commandBuffer); + } + + void RHIImpl::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + VkBufferImageCopy region{}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + + region.imageOffset = {0, 0, 0}; + region.imageExtent = { + width, + height, + 1 + }; + + vkCmdCopyBufferToImage( + commandBuffer, + buffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ®ion + ); + + endSingleTimeCommands(commandBuffer); + } + RHI::RHI() : mImpl(nullptr) { // empty } @@ -1315,7 +1475,6 @@ namespace segfault::renderer { mImpl->createLogicalDevice(mImpl->enableValidationLayers, mImpl->physicalDevice, mImpl->device, mImpl->queueFamilyIndices); - mImpl->createSwapChain(); mImpl->createImageViews(); mImpl->createRenderPass(); @@ -1329,6 +1488,7 @@ namespace segfault::renderer { mImpl->createCommandBuffer(); mImpl->createSyncObjects(); mImpl->createTextureImage(); + mImpl->createTextureImageView(); mImpl->createVertexBuffer(); mImpl->createIndexBuffer(); @@ -1337,6 +1497,11 @@ namespace segfault::renderer { bool RHI::shutdown() { mImpl->cleanupSwapChain(); + vkDestroyImage(mImpl->device, mImpl->textureImage, nullptr); + vkDestroySampler(mImpl->device, mImpl->textureSampler, nullptr); + vkDestroyImageView(mImpl->device, mImpl->textureImageView, nullptr); + + vkFreeMemory(mImpl->device, mImpl->textureImageMemory, nullptr); for (size_t i = 0; i < RHIImpl::MAX_FRAMES_IN_FLIGHT; i++) { vkDestroyBuffer(mImpl->device, mImpl->uniformBuffers[i], nullptr); vkFreeMemory(mImpl->device, mImpl->uniformBuffersMemory[i], nullptr); From 7dcb9535aaff41d86fb5f6dfacbcaf573ec49699 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 25 Mar 2026 16:24:09 +0100 Subject: [PATCH 5/6] Adapt texture view! --- assets/shaders/default.frag | 4 +- assets/shaders/default.vert | 9 ++- src/runtime/renderer/RHIVulkan.cpp | 89 +++++++++++++++++++++++------- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/assets/shaders/default.frag b/assets/shaders/default.frag index 571c04e..11951ca 100644 --- a/assets/shaders/default.frag +++ b/assets/shaders/default.frag @@ -1,10 +1,10 @@ #version 450 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 = vec4(fragTexCoord, 0.0, 1.0); } - diff --git a/assets/shaders/default.vert b/assets/shaders/default.vert index b68f601..5510aa3 100644 --- a/assets/shaders/default.vert +++ b/assets/shaders/default.vert @@ -8,10 +8,13 @@ layout(binding = 0) uniform UniformBufferObject { layout(location = 0) in vec2 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; + void main() { - gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); - fragColor = inColor; + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); + fragColor = inColor; + fragTexCoord = inTexCoord; } diff --git a/src/runtime/renderer/RHIVulkan.cpp b/src/runtime/renderer/RHIVulkan.cpp index 917b3e2..8ac9b04 100644 --- a/src/runtime/renderer/RHIVulkan.cpp +++ b/src/runtime/renderer/RHIVulkan.cpp @@ -25,9 +25,10 @@ namespace segfault::renderer { struct Vertex { glm::vec2 pos{}; glm::vec3 color{}; + glm::vec2 texCoord; - static std::array getAttributeDescriptions() { - std::array attributeDescriptions{}; + static std::array getAttributeDescriptions() { + std::array attributeDescriptions{}; attributeDescriptions[0].binding = 0; attributeDescriptions[0].location = 0; @@ -39,6 +40,11 @@ namespace segfault::renderer { attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; attributeDescriptions[1].offset = offsetof(Vertex, color); + attributeDescriptions[2].binding = 0; + attributeDescriptions[2].location = 2; + attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[2].offset = offsetof(Vertex, texCoord); + return attributeDescriptions; } @@ -54,10 +60,10 @@ namespace segfault::renderer { }; const std::vector vertices = { - {{-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}} + {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, + {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, + {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}} }; const std::vector indices = { @@ -268,7 +274,11 @@ namespace segfault::renderer { swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); } - return queueFamilyIndices.isComplete() && extensionsSupported && swapChainAdequate; + VkPhysicalDeviceFeatures supportedFeatures; + vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures); + + return queueFamilyIndices.isComplete() && extensionsSupported && swapChainAdequate + && supportedFeatures.samplerAnisotropy; } bool RHIImpl::checkDeviceExtensionSupport(VkPhysicalDevice device) { @@ -397,6 +407,7 @@ namespace segfault::renderer { VkPhysicalDeviceFeatures deviceFeatures{}; VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceFeatures.samplerAnisotropy = VK_TRUE; createInfo.pNext = nullptr; createInfo.pQueueCreateInfos = &queueCreateInfo; @@ -588,10 +599,18 @@ namespace segfault::renderer { uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; uboLayoutBinding.pImmutableSamplers = nullptr; // Optional + VkDescriptorSetLayoutBinding samplerLayoutBinding{}; + samplerLayoutBinding.binding = 1; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.pImmutableSamplers = nullptr; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + std::array bindings = { uboLayoutBinding, samplerLayoutBinding }; VkDescriptorSetLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = 1; - layoutInfo.pBindings = &uboLayoutBinding; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor set layout!"); @@ -1186,14 +1205,17 @@ namespace segfault::renderer { VkBuffer stagingBuffer; VkDeviceMemory stagingBufferMemory; - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingBuffer, stagingBufferMemory); void* data; vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data); memcpy(data, indices.data(), (size_t)bufferSize); vkUnmapMemory(device, stagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); @@ -1209,22 +1231,25 @@ namespace segfault::renderer { uniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]); + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + uniformBuffers[i], uniformBuffersMemory[i]); vkMapMemory(device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]); } } void RHIImpl::createDescriptorPool() { - VkDescriptorPoolSize poolSize{}; - poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - poolSize.descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + std::array poolSizes{}; + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSizes[1].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); VkDescriptorPoolCreateInfo poolInfo{}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = 1; - poolInfo.pPoolSizes = &poolSize; - + poolInfo.poolSizeCount = static_cast(poolSizes.size()); + poolInfo.pPoolSizes = poolSizes.data(); poolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { @@ -1251,7 +1276,31 @@ namespace segfault::renderer { bufferInfo.offset = 0; bufferInfo.range = sizeof(UniformBufferObject); - VkWriteDescriptorSet descriptorWrite{}; + VkDescriptorImageInfo imageInfo{}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = textureImageView; + imageInfo.sampler = textureSampler; + + std::array descriptorWrites{}; + + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = descriptorSets[i]; + descriptorWrites[0].dstBinding = 0; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].pBufferInfo = &bufferInfo; + + descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[1].dstSet = descriptorSets[i]; + descriptorWrites[1].dstBinding = 1; + descriptorWrites[1].dstArrayElement = 0; + descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrites[1].descriptorCount = 1; + descriptorWrites[1].pImageInfo = &imageInfo; + + vkUpdateDescriptorSets(device, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); + /*VkWriteDescriptorSet descriptorWrite{}; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite.dstSet = descriptorSets[i]; descriptorWrite.dstBinding = 0; @@ -1264,7 +1313,7 @@ namespace segfault::renderer { descriptorWrite.pImageInfo = nullptr; // Optional descriptorWrite.pTexelBufferView = nullptr; // Optional - vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); + vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);*/ } } From 2d0a6ec3fe33288e4e1a3350fe0d976cf895cc50 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 26 Mar 2026 17:41:28 +0100 Subject: [PATCH 6/6] Add sampler to default shader --- assets/shaders/default.frag | 5 ++++- scripts/compile_shader.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/assets/shaders/default.frag b/assets/shaders/default.frag index 11951ca..3f57213 100644 --- a/assets/shaders/default.frag +++ b/assets/shaders/default.frag @@ -5,6 +5,9 @@ layout(location = 1) in vec2 fragTexCoord; layout(location = 0) out vec4 outColor; +layout(binding = 1) uniform sampler2D texSampler; + void main() { - outColor = vec4(fragTexCoord, 0.0, 1.0); + outColor = texture(texSampler, fragTexCoord); } + diff --git a/scripts/compile_shader.py b/scripts/compile_shader.py index 604e9eb..ce0cb75 100644 --- a/scripts/compile_shader.py +++ b/scripts/compile_shader.py @@ -63,7 +63,7 @@ def main(): copy_shader(shader_out, "../bin/shaders") elif sys.platform == "win32": out = Path("../bin/") - if out.exists("debug"): + if os.path.exists("debug"): copy_shader(shader_out, "../bin/debug/shaders") else: copy_shader(shader_out, "../bin/release/shaders")