diff --git a/24_texture_image.cpp b/24_texture_image.cpp new file mode 100644 index 0000000..7c9974a --- /dev/null +++ b/24_texture_image.cpp @@ -0,0 +1,1079 @@ +#include +#include +#include +#include +#if defined(__INTELLISENSE__) || !defined(USE_CPP20_MODULES) +#include "vulkan/vulkan.hpp" +#include +#include +#else +import vulkan_hpp; +#endif + +#define STB_IMAGE_IMPLEMENTATION +#include +#define GLFW_INCLUDE_VULKAN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr uint32_t WIDTH = 800; +constexpr uint32_t HEIGHT = 600; +constexpr int32_t MAX_FRAMES_IN_FLIGHT = 2; + +struct Vertex { + glm::vec2 position; + glm::vec3 color; + + static vk::VertexInputBindingDescription getBindingDescription() { + return { + .binding = 0, + .stride = sizeof(Vertex), + .inputRate = vk::VertexInputRate::eVertex, + }; + } + + static std::array getAttributeDescriptions() { + return { + vk::VertexInputAttributeDescription{ + .location = 0, + .binding = 0, + .format = vk::Format::eR32G32Sfloat, + .offset = offsetof(Vertex, position), + }, + vk::VertexInputAttributeDescription{ + .location = 1, + .binding = 0, + .format = vk::Format::eR32G32B32Sfloat, + .offset = offsetof(Vertex, color), + } + }; + } +}; + +struct UniformBufferObject { + alignas(16) glm::mat4 model; + alignas(16) glm::mat4 view; + alignas(16) glm::mat4 proj; +}; + +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}} +}; + +const std::vector indices = { 0, 1, 2, 2, 3, 0 }; + +const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation" +}; + +#ifdef NDEBUG +constexpr bool enableValidationLayers = false; +#else +constexpr bool enableValidationLayers = true; +#endif + +static std::vector readFile(const std::string &filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + if (!file.is_open()) { + throw std::runtime_error("failed to open file!"); + } + + std::vector buffer(file.tellg()); + file.seekg(0, std::ios::beg); + file.read(buffer.data(), static_cast(buffer.size())); + file.close(); + + return buffer; +} + +class HelloTriangleApplication { + public: + void run() { + initWindow(); + initVulkan(); + mainLoop(); + cleanup(); + } + private: + void initWindow() { + glfwInit(); + // Don't create an OpenGL context + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); + glfwSetWindowUserPointer(window, this); + glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); + } + static void framebufferResizeCallback(GLFWwindow *window, int width, int height) { + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); + app->framebufferResized = true; + } + void initVulkan() { + createInstance(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); + createSwapChain(); + createImageViews(); + createDescriptorSetLayout(); + createGraphicsPipeline(); + createCommandPool(); + createTextureImage(); + createVertexBuffer(); + createIndexBuffer(); + createUniformBuffers(); + createDescriptorPool(); + createDescriptorSets(); + createCommandBuffers(); + createSyncObjects(); + } + void mainLoop() { + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + drawFrame(); + } + + device.waitIdle(); + } + void drawFrame() { + // Wait till fence is signalled + while (vk::Result::eTimeout == device.waitForFences(*drawFences[frameIndex], vk::True, UINT64_MAX)) {} + + vk::Result result; + uint32_t imageIndex; + try { + std::tie(result, imageIndex) = swapChain.acquireNextImage(UINT64_MAX, *presentCompleteSemaphores[frameIndex], nullptr); + if (result == vk::Result::eErrorOutOfDateKHR) { + recreateSwapChain(); + return; + } + if (result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR) { + throw std::runtime_error("failed to acquire swap chain image!"); + } + } catch (const vk::SystemError &e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } + } + + device.resetFences(*drawFences[frameIndex]); + + updateUniformBuffer(frameIndex); + + recordCommandBuffer(imageIndex); + + vk::PipelineStageFlags waitDestinationStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput); + const vk::SubmitInfo submitInfo = { + .waitSemaphoreCount = 1, + .pWaitSemaphores = &*presentCompleteSemaphores[frameIndex], + .pWaitDstStageMask = &waitDestinationStageMask, + .commandBufferCount = 1, + .pCommandBuffers = &*commandBuffers[frameIndex], + .signalSemaphoreCount = 1, + .pSignalSemaphores = &*renderFinishedSemaphores[imageIndex], + }; + graphicsQueue.submit(submitInfo, *drawFences[frameIndex]); + + try { + // Presentation + vk::PresentInfoKHR presentInfoKHR = { + .waitSemaphoreCount = 1, + .pWaitSemaphores = &*renderFinishedSemaphores[imageIndex], + .swapchainCount = 1, + .pSwapchains = &*swapChain, + .pImageIndices = &imageIndex, + }; + result = graphicsQueue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError &e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } + } + + frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT; + } + void cleanup() { + cleanupSwapChain(); + glfwDestroyWindow(window); + glfwTerminate(); + } + void createInstance() { + constexpr vk::ApplicationInfo appInfo { + .pApplicationName = "Hello Triangle", + .applicationVersion = VK_MAKE_VERSION(1, 0, 0), + .pEngineName = "No Engine", + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = vk::ApiVersion14, + }; + + // Get the required layers + std::vector requiredLayers; + if (enableValidationLayers) { + requiredLayers.assign(validationLayers.begin(), validationLayers.end()); + } + + // Check if the required layers are supported by the Vulkan implementation. + auto layerProperties = context.enumerateInstanceLayerProperties(); + if (std::ranges::any_of(requiredLayers, [&layerProperties](auto const& requiredLayer) { + return std::ranges::none_of(layerProperties, + [requiredLayer](auto const& layerProperty) + { return strcmp(layerProperty.layerName, requiredLayer) == 0; }); + })) + { + throw std::runtime_error("One or more required layers are not supported!"); + } + + // Get the required instance extensions from GLFW. + uint32_t glfwExtensionCount = 0; + auto glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + // Check if the required GLFW extensions are supported by the Vulkan implementation. + auto extensionProperties = context.enumerateInstanceExtensionProperties(); + for (uint32_t i = 0; i < glfwExtensionCount; ++i) + { + if (std::ranges::none_of(extensionProperties, + [glfwExtension = glfwExtensions[i]](auto const& extensionProperty) + { return strcmp(extensionProperty.extensionName, glfwExtension) == 0; })) + { + throw std::runtime_error("Required GLFW extension not supported: " + std::string(glfwExtensions[i])); + } + } + vk::InstanceCreateInfo createInfo { + .pApplicationInfo = &appInfo, + .enabledLayerCount = static_cast(requiredLayers.size()), + .ppEnabledLayerNames = requiredLayers.data(), + .enabledExtensionCount = glfwExtensionCount, + .ppEnabledExtensionNames = glfwExtensions, + }; + + instance = vk::raii::Instance(context, createInfo); + } + void createSurface() { + VkSurfaceKHR _surface; + if (glfwCreateWindowSurface(*instance, window, nullptr, &_surface) != 0) { + throw std::runtime_error("failed to create window surface!"); + } + + surface = vk::raii::SurfaceKHR(instance, _surface); + } + void pickPhysicalDevice() { + std::vector deviceExtensions = { + vk::KHRSwapchainExtensionName, + vk::KHRSpirv14ExtensionName, + vk::KHRCreateRenderpass2ExtensionName, + }; + + auto devices = instance.enumeratePhysicalDevices(); + if (devices.empty()) { + throw std::runtime_error("failed to find GPUs with Vulkan support!"); + } + + for (const auto &device : devices) { + auto deviceProperties = device.getProperties(); + auto deviceFeatures = device.getFeatures(); + auto queueFamilies = device.getQueueFamilyProperties(); + auto extensions = device.enumerateDeviceExtensionProperties(); + bool isSuitable = deviceProperties.apiVersion >= VK_API_VERSION_1_3; + bool extensionFound = true; + + const vk::QueueFamilyProperties *qf = nullptr; + for (const auto &qfp : queueFamilies) { + if ((qfp.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast(0)) { + qf = &qfp; + break; + } + } + + isSuitable = isSuitable && (qf != nullptr); + + for (const auto &extension : deviceExtensions) { + auto extensionIter = std::ranges::find_if(extensions, [extension](auto const & ext) {return strcmp(ext.extensionName, extension) == 0;}); + extensionFound = extensionFound && extensionIter != extensions.end(); + } + + isSuitable = isSuitable && extensionFound; + + if (isSuitable) { + physicalDevice = device; + return; + } + + throw std::runtime_error("failed to find a suitable GPU"); + } + } + void createLogicalDevice() { + std::vector queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); + graphicsQueueIndex = findQueueFamilies(physicalDevice); + float queuePriority = 0.5f; + vk::DeviceQueueCreateInfo deviceQueueCreateInfo { + .queueFamilyIndex = graphicsQueueIndex, + .queueCount = 1, + .pQueuePriorities = &queuePriority, + }; + + // Create a chain of feature structures + vk::StructureChain featureChain = { + {}, // vk::PhysicalDeviceFeatures2 (empty for now) + {.synchronization2 = true, + .dynamicRendering = true}, // Enable dynamic rendering and synchronization2 from Vulkan 1.3 + {.shaderDrawParameters = true}, // Enable shader draw parameters from Vulkan 1.2 + {.extendedDynamicState = true} // Enable extended dynamic state from the extension + }; + + std::vector deviceExtensions = { + vk::KHRSwapchainExtensionName, + vk::KHRSpirv14ExtensionName, + vk::KHRSynchronization2ExtensionName, + vk::KHRCreateRenderpass2ExtensionName + }; + + vk::DeviceCreateInfo deviceCreateInfo { + .pNext = &featureChain.get(), + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &deviceQueueCreateInfo, + .enabledExtensionCount = static_cast(deviceExtensions.size()), + .ppEnabledExtensionNames = deviceExtensions.data(), + }; + + device = vk::raii::Device(physicalDevice, deviceCreateInfo); + graphicsQueue = vk::raii::Queue(device, graphicsQueueIndex, 0); + } + void createSwapChain() { + auto surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface); + swapChainSurfaceFormat = chooseSwapSurfaceFormat(physicalDevice.getSurfaceFormatsKHR(surface)); + swapChainExtent = chooseSwapExtent(surfaceCapabilities); + auto minImageCount = std::max(3u, surfaceCapabilities.minImageCount); + minImageCount = (surfaceCapabilities.maxImageCount > 0 && + minImageCount > surfaceCapabilities.maxImageCount) ? + surfaceCapabilities.maxImageCount : + minImageCount; + + vk::SwapchainCreateInfoKHR swapChainCreateInfo { + .flags = vk::SwapchainCreateFlagsKHR(), + .surface = surface, + .minImageCount = minImageCount, + .imageFormat = swapChainSurfaceFormat.format, + .imageColorSpace = swapChainSurfaceFormat.colorSpace, + .imageExtent = swapChainExtent, + .imageArrayLayers = 1, + .imageUsage = vk::ImageUsageFlagBits::eColorAttachment, + .imageSharingMode = vk::SharingMode::eExclusive, + .preTransform = surfaceCapabilities.currentTransform, + .compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque, + .presentMode = chooseSwapPresentMode(physicalDevice.getSurfacePresentModesKHR(surface)), + .clipped = true, + .oldSwapchain = nullptr, + }; + + swapChain = vk::raii::SwapchainKHR(device, swapChainCreateInfo); + swapChainImages = swapChain.getImages(); + swapChainImageFormat = swapChainSurfaceFormat.format; + } + void createImageViews() { + swapChainImageViews.clear(); + + vk::ImageViewCreateInfo imageViewCreateInfo{ + .viewType = vk::ImageViewType::e2D, + .format = swapChainImageFormat, + .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } + }; + + for (auto image : swapChainImages) { + imageViewCreateInfo.image = image; + swapChainImageViews.emplace_back(vk::raii::ImageView(device, imageViewCreateInfo)); + } + } + void createDescriptorSetLayout() { + vk::DescriptorSetLayoutBinding uboLayoutBinding(0, vk::DescriptorType::eUniformBuffer, + 1, vk::ShaderStageFlagBits::eVertex, nullptr); + vk::DescriptorSetLayoutCreateInfo layoutInfo{ + .bindingCount = 1, + .pBindings = &uboLayoutBinding, + }; + descriptorSetLayout = vk::raii::DescriptorSetLayout(device, layoutInfo); + } + void createGraphicsPipeline() { + vk::raii::ShaderModule shaderModule = createShaderModule(readFile("shaders/22_shader_ubo.spv")); + + vk::PipelineShaderStageCreateInfo vertShaderStageInfo = { + .stage = vk::ShaderStageFlagBits::eVertex, + .module = shaderModule, + .pName = "vertMain", + }; + vk::PipelineShaderStageCreateInfo fragShaderStageInfo = { + .stage = vk::ShaderStageFlagBits::eFragment, + .module = shaderModule, + .pName = "fragMain", + }; + + vk::PipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; + + // Vertex input + auto bindingDescription = Vertex::getBindingDescription(); + auto attributeDescriptions = Vertex::getAttributeDescriptions(); + vk::PipelineVertexInputStateCreateInfo vertexInputInfo{ + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &bindingDescription, + .vertexAttributeDescriptionCount = attributeDescriptions.size(), + .pVertexAttributeDescriptions = attributeDescriptions.data(), + }; + + // Input assembly + vk::PipelineInputAssemblyStateCreateInfo inputAssembly = { + .topology = vk::PrimitiveTopology::eTriangleList, + }; + + // Dynamic state + std::vector dynamicStates = { + vk::DynamicState::eViewport, + vk::DynamicState::eScissor, + }; + vk::PipelineDynamicStateCreateInfo dynamicState = { + .dynamicStateCount = static_cast(dynamicStates.size()), + .pDynamicStates = dynamicStates.data(), + }; + // No need to specify viewport and scissor because they will be specified dynamically + vk::PipelineViewportStateCreateInfo viewportState = { + .viewportCount = 1, + .scissorCount = 1, + }; + + // Rasterisation + vk::PipelineRasterizationStateCreateInfo rasterizer = { + .depthClampEnable = vk::False, + .rasterizerDiscardEnable = vk::False, + .polygonMode = vk::PolygonMode::eFill, + .cullMode = vk::CullModeFlagBits::eBack, + .frontFace = vk::FrontFace::eCounterClockwise, + .depthBiasEnable = vk::False, + .depthBiasSlopeFactor = 1.0f, + .lineWidth = 1.0f + }; + + // Multisampling + vk::PipelineMultisampleStateCreateInfo multisampling = { + .rasterizationSamples = vk::SampleCountFlagBits::e1, + .sampleShadingEnable = vk::False, + }; + + // Color blending + vk::PipelineColorBlendAttachmentState colorBlendAttachment = { + .blendEnable = vk::False, + .colorWriteMask = vk::ColorComponentFlagBits::eR | + vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | + vk::ColorComponentFlagBits::eA, + }; + vk::PipelineColorBlendStateCreateInfo colorBlending = { + .logicOpEnable = vk::False, + .logicOp = vk::LogicOp::eCopy, + .attachmentCount = 1, + .pAttachments = &colorBlendAttachment, + }; + + // Pipeline layout + vk::PipelineLayoutCreateInfo pipelineLayoutInfo = { + .setLayoutCount = 1, + .pSetLayouts = &*descriptorSetLayout, + .pushConstantRangeCount = 0, + }; + pipelineLayout = vk::raii::PipelineLayout(device, pipelineLayoutInfo); + + // Dynamic rendering pipeline + vk::PipelineRenderingCreateInfo pipelineRenderingCreateInfo = { + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &swapChainImageFormat, + }; + vk::GraphicsPipelineCreateInfo pipelineInfo = { + .pNext = &pipelineRenderingCreateInfo, + .stageCount = 2, + .pStages = shaderStages, + .pVertexInputState = &vertexInputInfo, + .pInputAssemblyState = &inputAssembly, + .pViewportState = &viewportState, + .pRasterizationState = &rasterizer, + .pMultisampleState = &multisampling, + .pColorBlendState = &colorBlending, + .pDynamicState = &dynamicState, + .layout = pipelineLayout, + .renderPass = nullptr, + }; + + // Create pipeline + graphicsPipeline = vk::raii::Pipeline(device, nullptr, pipelineInfo); + } + void createCommandPool() { + vk::CommandPoolCreateInfo poolInfo = { + .flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer, + .queueFamilyIndex = graphicsQueueIndex, + }; + commandPool = vk::raii:: CommandPool(device, poolInfo); + } + void createTextureImage() { + int texWidth, texHeight, texChannels; + stbi_uc* pixels = stbi_load("textures/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); + if (!pixels) { + throw std::runtime_error("failed to load texture image!"); + } + + vk::DeviceSize imageSize = texWidth * texHeight * 4; + + vk::raii::Buffer stagingBuffer({}); + vk::raii::DeviceMemory stagingBufferMemory({}); + createBuffer(imageSize, vk::BufferUsageFlagBits::eTransferSrc, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostVisible, + stagingBuffer, stagingBufferMemory); + void *data = stagingBufferMemory.mapMemory(0, imageSize); + memcpy(data, pixels, imageSize); + stagingBufferMemory.unmapMemory(); + + stbi_image_free(pixels); + + createImage(texWidth, texHeight, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eOptimal, + vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, + vk::MemoryPropertyFlagBits::eDeviceLocal, textureImage, textureImageMemory); + + // Transition layout to copy data + transitionImageLayout(textureImage, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal); + copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); + + // Transition layout to enable sampling + transitionImageLayout(textureImage, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal); + } + void createImage(uint32_t width, uint32_t height, vk::Format format, vk::ImageTiling tiling, + vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties, + vk::raii::Image& image, vk::raii::DeviceMemory& imageMemory) { + vk::ImageCreateInfo imageInfo { + .imageType = vk::ImageType::e2D, + .format = format, + .extent = {width, height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + .tiling = tiling, + .usage = usage, + .sharingMode = vk::SharingMode::eExclusive, + }; + image = vk::raii::Image(device, imageInfo); + + vk::MemoryRequirements memRequirements = image.getMemoryRequirements(); + vk::MemoryAllocateInfo allocInfo { + .allocationSize = memRequirements.size, + .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties) + }; + imageMemory = vk::raii::DeviceMemory(device, allocInfo); + image.bindMemory(*imageMemory, 0); + } + void copyBufferToImage(const vk::raii::Buffer& buffer, vk::raii::Image& image, uint32_t width, uint32_t height) { + vk::raii::CommandBuffer commandBuffer = beginSingleTimeCommands(); + vk::BufferImageCopy region { + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = { vk::ImageAspectFlagBits::eColor, 0, 0, 1 }, + .imageOffset = { 0, 0, 0 }, + .imageExtent = { width, height, 1 } + }; + commandBuffer.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, {region}); + endSingleTimeCommands(commandBuffer); + } + void createVertexBuffer() { + vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); + vk::raii::Buffer stagingBuffer = nullptr; + vk::raii::DeviceMemory stagingBufferMemory = nullptr; + + // Create staging buffer visible to host (CPU) + createBuffer( + bufferSize, + vk::BufferUsageFlagBits::eTransferSrc, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, + stagingBuffer, + stagingBufferMemory + ); + void *data = stagingBufferMemory.mapMemory(0, bufferSize); + memcpy(data, vertices.data(), (size_t)bufferSize); + stagingBufferMemory.unmapMemory(); + + createBuffer( + bufferSize, + vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, + vk::MemoryPropertyFlagBits::eDeviceLocal, + vertexBuffer, + vertexBufferMemory + ); + copyBuffer(stagingBuffer, vertexBuffer, bufferSize); + } + void createIndexBuffer() { + vk::DeviceSize bufferSize = sizeof(indices[0]) * indices.size(); + vk::raii::Buffer stagingBuffer = nullptr; + vk::raii::DeviceMemory stagingBufferMemory = nullptr; + + // Create staging buffer visible to host (CPU) + createBuffer( + bufferSize, + vk::BufferUsageFlagBits::eTransferSrc, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, + stagingBuffer, + stagingBufferMemory + ); + void *data = stagingBufferMemory.mapMemory(0, bufferSize); + memcpy(data, indices.data(), (size_t)bufferSize); + stagingBufferMemory.unmapMemory(); + + createBuffer( + bufferSize, + vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, + vk::MemoryPropertyFlagBits::eDeviceLocal, + indexBuffer, + indexBufferMemory + ); + copyBuffer(stagingBuffer, indexBuffer, bufferSize); + } + void createUniformBuffers() { + uniformBuffers.clear(); + uniformBuffersMemory.clear(); + unifromBuffersMapped.clear(); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { + vk::DeviceSize bufferSize = sizeof(UniformBufferObject); + vk::raii::Buffer buffer({}); + vk::raii::DeviceMemory bufferMem({}); + createBuffer(bufferSize, vk::BufferUsageFlagBits::eUniformBuffer, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, + buffer, bufferMem); + uniformBuffers.emplace_back(std::move(buffer)); + uniformBuffersMemory.emplace_back(std::move(bufferMem)); + unifromBuffersMapped.emplace_back(uniformBuffersMemory[i].mapMemory(0, bufferSize)); + } + } + void createDescriptorPool() { + vk::DescriptorPoolSize poolSize(vk::DescriptorType::eUniformBuffer, MAX_FRAMES_IN_FLIGHT); + vk::DescriptorPoolCreateInfo poolInfo = { + .flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, + .maxSets = MAX_FRAMES_IN_FLIGHT, + .poolSizeCount = 1, + .pPoolSizes = &poolSize, + }; + descriptorPool = vk::raii::DescriptorPool(device, poolInfo); + } + void createDescriptorSets() { + std::vector layouts(MAX_FRAMES_IN_FLIGHT, *descriptorSetLayout); + vk::DescriptorSetAllocateInfo allocInfo { + .descriptorPool = descriptorPool, + .descriptorSetCount = static_cast(layouts.size()), + .pSetLayouts = layouts.data(), + }; + descriptorSets.clear(); + descriptorSets = device.allocateDescriptorSets(allocInfo); + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { + vk::DescriptorBufferInfo bufferInfo { + .buffer = uniformBuffers[i], + .offset = 0, + .range = sizeof(UniformBufferObject), + }; + + vk::WriteDescriptorSet descriptorWrite { + .dstSet = descriptorSets[i], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &bufferInfo, + }; + + device.updateDescriptorSets(descriptorWrite, {}); + } + } + void createCommandBuffers() { + commandBuffers.clear(); + vk::CommandBufferAllocateInfo allocInfo = { + .commandPool = commandPool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = MAX_FRAMES_IN_FLIGHT, + }; + commandBuffers = vk::raii::CommandBuffers(device, allocInfo); + } + void createSyncObjects() { + assert(presentCompleteSemaphores.empty() && renderFinishedSemaphores.empty() && drawFences.empty()); + + for (size_t i = 0; i < swapChainImages.size(); ++i) { + renderFinishedSemaphores.emplace_back(device, vk::SemaphoreCreateInfo()); + } + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { + presentCompleteSemaphores.emplace_back(device, vk::SemaphoreCreateInfo()); + drawFences.emplace_back(device, vk::FenceCreateInfo{ .flags = vk::FenceCreateFlagBits::eSignaled }); + } + } + void updateUniformBuffer(uint32_t currentImage) { + static auto startTime = std::chrono::high_resolution_clock::now(); + + auto currentTime = std::chrono::high_resolution_clock::now(); + float time = std::chrono::duration(currentTime - startTime).count(); + + UniformBufferObject ubo{}; + ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.proj = glm::perspective(glm::radians(45.0f), + static_cast(swapChainExtent.width) / static_cast(swapChainExtent.height), + 0.1f, 10.0f); + ubo.proj[1][1] *= -1; + + memcpy(unifromBuffersMapped[currentImage], &ubo, sizeof(ubo)); + } + void recordCommandBuffer(uint32_t imageIndex) { + auto &commandBuffer = commandBuffers[frameIndex]; + + // Begin recording the command buffer + commandBuffer.begin({}); + + // Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL + transitionSwapChainImageLayout( + imageIndex, + vk::ImageLayout::eUndefined, + vk::ImageLayout::eColorAttachmentOptimal, + {}, // srcAccessMask (no need to wait for previous operations) + vk::AccessFlagBits2::eColorAttachmentWrite, // dstAccessMask + vk::PipelineStageFlagBits2::eColorAttachmentOutput, // srcStage + vk::PipelineStageFlagBits2::eColorAttachmentOutput // dstStage + ); + + vk::ClearValue clearColor = vk::ClearColorValue(0.0f, 0.0f, 0.0f, 1.0f); + vk::RenderingAttachmentInfo attachmentInfo = { + .imageView = swapChainImageViews[imageIndex], + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = clearColor, + }; + vk::RenderingInfo renderingInfo = { + .renderArea = { .offset = { 0, 0 }, .extent = swapChainExtent }, + .layerCount = 1, + .colorAttachmentCount = 1, + .pColorAttachments = &attachmentInfo, + }; + + commandBuffer.beginRendering(renderingInfo); + commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, graphicsPipeline); + commandBuffer.bindVertexBuffers(0, *vertexBuffer, {0}); + commandBuffer.bindIndexBuffer(*indexBuffer, 0, vk::IndexType::eUint16); + // Viewport and scissor are dynamic so we need to set them + vk::Viewport viewport = { + .x = 0.0f, + .y = 0.0f, + .width = static_cast(swapChainExtent.width), + .height = static_cast(swapChainExtent.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + commandBuffer.setViewport(0, viewport); + commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(0, 0), swapChainExtent)); + + commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, + *descriptorSets[frameIndex], nullptr); + + // Issue the draw command + commandBuffer.drawIndexed(indices.size(), 1, 0, 0, 0); + + commandBuffer.endRendering(); + + // After rendering, transition the swapchain image to PRESENT_SRC + transitionSwapChainImageLayout( + imageIndex, + vk::ImageLayout::eColorAttachmentOptimal, + vk::ImageLayout::ePresentSrcKHR, + vk::AccessFlagBits2::eColorAttachmentWrite, // srcAccessMask + {}, // dstAccessMask + vk::PipelineStageFlagBits2::eColorAttachmentOutput, // srcStage + vk::PipelineStageFlagBits2::eBottomOfPipe // dstStage + ); + + // Finish recording the command buffer + commandBuffer.end(); + } + void createBuffer( + vk::DeviceSize size, + vk::BufferUsageFlags usage, + vk::MemoryPropertyFlags properties, + vk::raii::Buffer &buffer, + vk::raii::DeviceMemory &bufferMemory + ) { + vk::BufferCreateInfo bufferInfo = { + .size = size, + .usage = usage, + .sharingMode = vk::SharingMode::eExclusive, + }; + buffer = vk::raii::Buffer(device, bufferInfo); + + vk::MemoryRequirements memRequirements = buffer.getMemoryRequirements(); + vk::MemoryAllocateInfo memAllocateInfo = { + .allocationSize = memRequirements.size, + .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties) + }; + bufferMemory = vk::raii::DeviceMemory(device, memAllocateInfo); + buffer.bindMemory(bufferMemory, 0); + } + void copyBuffer(vk::raii::Buffer &srcBuffer, vk::raii::Buffer &dstBuffer, vk::DeviceSize size) { + vk::raii::CommandBuffer commandCopyBuffer = beginSingleTimeCommands(); + commandCopyBuffer.copyBuffer(*srcBuffer, *dstBuffer, vk::BufferCopy(0, 0, size)); + endSingleTimeCommands(commandCopyBuffer); + } + vk::raii::CommandBuffer beginSingleTimeCommands() { + vk::CommandBufferAllocateInfo allocInfo { + .commandPool = commandPool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = 1 + }; + vk::raii::CommandBuffer commandBuffer = std::move(device.allocateCommandBuffers(allocInfo).front()); + + vk::CommandBufferBeginInfo beginInfo{ .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit }; + commandBuffer.begin(beginInfo); + + return commandBuffer; + } + + void endSingleTimeCommands(vk::raii::CommandBuffer& commandBuffer) { + commandBuffer.end(); + + vk::SubmitInfo submitInfo{ .commandBufferCount = 1, .pCommandBuffers = &*commandBuffer }; + graphicsQueue.submit(submitInfo, nullptr); + graphicsQueue.waitIdle(); + } + uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties) { + vk::PhysicalDeviceMemoryProperties memProperties = physicalDevice.getMemoryProperties(); + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)) { + return i; + } + } + + throw std::runtime_error("failed to find suitable memory type!"); + } + void transitionImageLayout(const vk::raii::Image &image, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { + vk::raii::CommandBuffer commandBuffer = beginSingleTimeCommands(); + + vk::ImageMemoryBarrier barrier{ + .oldLayout = oldLayout, + .newLayout = newLayout, + .image = image, + .subresourceRange = {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1} + }; + + vk::PipelineStageFlags sourceStage; + vk::PipelineStageFlags destinationStage; + + if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal) + { + barrier.srcAccessMask = {}; + barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite; + + sourceStage = vk::PipelineStageFlagBits::eTopOfPipe; + destinationStage = vk::PipelineStageFlagBits::eTransfer; + } + else if (oldLayout == vk::ImageLayout::eTransferDstOptimal && newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) + { + barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; + barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; + + sourceStage = vk::PipelineStageFlagBits::eTransfer; + destinationStage = vk::PipelineStageFlagBits::eFragmentShader; + } + else + { + throw std::invalid_argument("unsupported layout transition!"); + } + + commandBuffer.pipelineBarrier(sourceStage, destinationStage, {}, {}, nullptr, barrier); + endSingleTimeCommands(commandBuffer); + } + void transitionSwapChainImageLayout( + uint32_t imageIndex, + vk::ImageLayout oldLayout, + vk::ImageLayout newLayout, + vk::AccessFlags2 srcAccessMask, + vk::AccessFlags2 dstAccessMask, + vk::PipelineStageFlags2 srcStageMask, + vk::PipelineStageFlags2 dstStageMask + ) { + vk::ImageMemoryBarrier2 barrier = { + .srcStageMask = srcStageMask, + .srcAccessMask = srcAccessMask, + .dstStageMask = dstStageMask, + .dstAccessMask = dstAccessMask, + .oldLayout = oldLayout, + .newLayout = newLayout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = swapChainImages[imageIndex], + .subresourceRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + }; + vk::DependencyInfo dependencyInfo = { + .dependencyFlags = {}, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &barrier + }; + commandBuffers[frameIndex].pipelineBarrier2(dependencyInfo); + } + void cleanupSwapChain() { + swapChainImageViews.clear(); + swapChain = nullptr; + } + void recreateSwapChain() { + int width = 0, height = 0; + glfwGetFramebufferSize(window, &width, &height); + while (width == 0 || height == 0) { + glfwGetFramebufferSize(window, &width, &height); + glfwWaitEvents(); + } + + device.waitIdle(); + cleanupSwapChain(); + createSwapChain(); + createImageViews(); + } + [[nodiscard]] vk::raii::ShaderModule createShaderModule(const std::vector &code) const { + vk::ShaderModuleCreateInfo createInfo { + .codeSize = code.size() * sizeof(char), + .pCode = reinterpret_cast(code.data()), + }; + + return vk::raii::ShaderModule{device, createInfo}; + } + vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == vk::Format::eB8G8R8A8Srgb && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { + return availableFormat; + } + } + + return availableFormats[0]; + } + vk::PresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes) { + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == vk::PresentModeKHR::eMailbox) { + return availablePresentMode; + } + } + + return vk::PresentModeKHR::eFifo; + } + vk::Extent2D chooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + return { + std::clamp(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width), + std::clamp(height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height), + }; + } + uint32_t findQueueFamilies(vk::raii::PhysicalDevice physicalDevice) { + // find the index of the first queue family that supports graphics + std::vector queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); + + // get the first index into queueFamilyProperties which both supports graphics and present + uint32_t queueIndex = ~0; + for (uint32_t qfpIndex = 0; qfpIndex < queueFamilyProperties.size(); ++qfpIndex) { + if ((queueFamilyProperties[qfpIndex].queueFlags & vk::QueueFlagBits::eGraphics) && + physicalDevice.getSurfaceSupportKHR(qfpIndex, *surface)) { + queueIndex = qfpIndex; + break; + } + } + + if (queueIndex == ~0) { + throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); + } + + return queueIndex; + } + + uint32_t frameIndex = 0; + bool framebufferResized = false; + GLFWwindow *window; + vk::raii::Context context; + vk::raii::Instance instance = nullptr; + vk::raii::PhysicalDevice physicalDevice = nullptr; + uint32_t graphicsQueueIndex; + vk::raii::Device device = nullptr; + vk::raii::Queue graphicsQueue = nullptr; + vk::raii::SurfaceKHR surface = nullptr; + vk::raii::SwapchainKHR swapChain = nullptr; + vk::SurfaceFormatKHR swapChainSurfaceFormat; + vk::Extent2D swapChainExtent; + vk::Format swapChainImageFormat = vk::Format::eUndefined; + std::vector swapChainImages; + std::vector swapChainImageViews; + vk::raii::DescriptorSetLayout descriptorSetLayout = nullptr; + vk::raii::DescriptorPool descriptorPool = nullptr; + std::vector descriptorSets; + vk::raii::PipelineLayout pipelineLayout = nullptr; + vk::raii::Pipeline graphicsPipeline = nullptr; + vk::raii::CommandPool commandPool = nullptr; + vk::raii::Buffer vertexBuffer = nullptr; + vk::raii::DeviceMemory vertexBufferMemory = nullptr; + vk::raii::Buffer indexBuffer = nullptr; + vk::raii::DeviceMemory indexBufferMemory = nullptr; + vk::raii::Image textureImage = nullptr; + vk::raii::DeviceMemory textureImageMemory = nullptr; + std::vector uniformBuffers; + std::vector uniformBuffersMemory; + std::vector unifromBuffersMapped; + std::vector commandBuffers; + std::vector presentCompleteSemaphores; + std::vector renderFinishedSemaphores; + std::vector drawFences; +}; + +int main() { + HelloTriangleApplication app; + + try { + app.run(); + } catch (const std::exception &e) { + std::cout << e.what() << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a156d1..fb6534c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -239,22 +239,22 @@ add_chapter (23_descriptor_sets add_chapter (24_texture_image SHADER 22_shader_ubo - TEXTURES ../../images/texture.jpg + TEXTURES images/texture.jpg LIBS glm::glm) # add_chapter (25_sampler # SHADER 22_shader_ubo -# TEXTURES ../../images/texture.jpg +# TEXTURES images/texture.jpg # LIBS glm::glm) # # add_chapter (26_texture_mapping # SHADER 26_shader_textures -# TEXTURES ../../images/texture.jpg +# TEXTURES images/texture.jpg # LIBS glm::glm) # # add_chapter (27_depth_buffering # SHADER 27_shader_depth -# TEXTURES ../../images/texture.jpg +# TEXTURES images/texture.jpg # LIBS glm::glm) # # add_chapter (28_model_loading diff --git a/assets/images/38_TASK01_renderdoc_color.gif b/assets/images/38_TASK01_renderdoc_color.gif new file mode 100644 index 0000000..e6bf944 Binary files /dev/null and b/assets/images/38_TASK01_renderdoc_color.gif differ diff --git a/assets/images/38_TASK01_renderdoc_events.png b/assets/images/38_TASK01_renderdoc_events.png new file mode 100644 index 0000000..8dadc06 Binary files /dev/null and b/assets/images/38_TASK01_renderdoc_events.png differ diff --git a/assets/images/38_TASK01_renderdoc_launch.png b/assets/images/38_TASK01_renderdoc_launch.png new file mode 100644 index 0000000..30edf61 Binary files /dev/null and b/assets/images/38_TASK01_renderdoc_launch.png differ diff --git a/assets/images/38_TASK02_blas_build.png b/assets/images/38_TASK02_blas_build.png new file mode 100644 index 0000000..f726ec7 Binary files /dev/null and b/assets/images/38_TASK02_blas_build.png differ diff --git a/assets/images/38_TASK02_blas_structures.png b/assets/images/38_TASK02_blas_structures.png new file mode 100644 index 0000000..e3a8117 Binary files /dev/null and b/assets/images/38_TASK02_blas_structures.png differ diff --git a/assets/images/38_TASK03_tlas_structures.png b/assets/images/38_TASK03_tlas_structures.png new file mode 100644 index 0000000..656abb3 Binary files /dev/null and b/assets/images/38_TASK03_tlas_structures.png differ diff --git a/assets/images/38_TASK04_nsight_capture.png b/assets/images/38_TASK04_nsight_capture.png new file mode 100644 index 0000000..f1e2f17 Binary files /dev/null and b/assets/images/38_TASK04_nsight_capture.png differ diff --git a/assets/images/38_TASK04_nsight_inspector.png b/assets/images/38_TASK04_nsight_inspector.png new file mode 100644 index 0000000..c587eef Binary files /dev/null and b/assets/images/38_TASK04_nsight_inspector.png differ diff --git a/assets/images/38_TASK04_nsight_launch.png b/assets/images/38_TASK04_nsight_launch.png new file mode 100644 index 0000000..57a5e88 Binary files /dev/null and b/assets/images/38_TASK04_nsight_launch.png differ diff --git a/assets/images/38_TASK04_nsight_main.png b/assets/images/38_TASK04_nsight_main.png new file mode 100644 index 0000000..d293b34 Binary files /dev/null and b/assets/images/38_TASK04_nsight_main.png differ diff --git a/assets/images/38_TASK06_concept_shadows.png b/assets/images/38_TASK06_concept_shadows.png new file mode 100644 index 0000000..ddc1949 Binary files /dev/null and b/assets/images/38_TASK06_concept_shadows.png differ diff --git a/assets/images/38_TASK06_shadows_static.gif b/assets/images/38_TASK06_shadows_static.gif new file mode 100644 index 0000000..0773084 Binary files /dev/null and b/assets/images/38_TASK06_shadows_static.gif differ diff --git a/assets/images/38_TASK07_shadows_dynamic.gif b/assets/images/38_TASK07_shadows_dynamic.gif new file mode 100644 index 0000000..863e8cc Binary files /dev/null and b/assets/images/38_TASK07_shadows_dynamic.gif differ diff --git a/assets/images/38_TASK08_alphacut_after.png b/assets/images/38_TASK08_alphacut_after.png new file mode 100644 index 0000000..def9362 Binary files /dev/null and b/assets/images/38_TASK08_alphacut_after.png differ diff --git a/assets/images/38_TASK08_alphacut_before.png b/assets/images/38_TASK08_alphacut_before.png new file mode 100644 index 0000000..6d13495 Binary files /dev/null and b/assets/images/38_TASK08_alphacut_before.png differ diff --git a/assets/images/38_TASK09_instance_custom_index.png b/assets/images/38_TASK09_instance_custom_index.png new file mode 100644 index 0000000..518ec46 Binary files /dev/null and b/assets/images/38_TASK09_instance_custom_index.png differ diff --git a/assets/images/38_TASK09_instance_lut.png b/assets/images/38_TASK09_instance_lut.png new file mode 100644 index 0000000..74291c5 Binary files /dev/null and b/assets/images/38_TASK09_instance_lut.png differ diff --git a/assets/images/38_TASK10_alphacut_shadows.png b/assets/images/38_TASK10_alphacut_shadows.png new file mode 100644 index 0000000..3a55eb7 Binary files /dev/null and b/assets/images/38_TASK10_alphacut_shadows.png differ diff --git a/assets/images/38_TASK11_alphacut_reflections.png b/assets/images/38_TASK11_alphacut_reflections.png new file mode 100644 index 0000000..68cb77b Binary files /dev/null and b/assets/images/38_TASK11_alphacut_reflections.png differ diff --git a/assets/images/38_TASK11_concept_reflections.png b/assets/images/38_TASK11_concept_reflections.png new file mode 100644 index 0000000..0a95288 Binary files /dev/null and b/assets/images/38_TASK11_concept_reflections.png differ diff --git a/assets/images/38_bounding_boxes.png b/assets/images/38_bounding_boxes.png new file mode 100644 index 0000000..ad957f8 Binary files /dev/null and b/assets/images/38_bounding_boxes.png differ diff --git a/assets/images/aliasing.png b/assets/images/aliasing.png new file mode 100644 index 0000000..cdb3c26 Binary files /dev/null and b/assets/images/aliasing.png differ diff --git a/assets/images/anisotropic_filtering.png b/assets/images/anisotropic_filtering.png new file mode 100644 index 0000000..9c946dd Binary files /dev/null and b/assets/images/anisotropic_filtering.png differ diff --git a/assets/images/antialiasing.png b/assets/images/antialiasing.png new file mode 100644 index 0000000..644f569 Binary files /dev/null and b/assets/images/antialiasing.png differ diff --git a/assets/images/bistro.png b/assets/images/bistro.png new file mode 100644 index 0000000..ca248e9 Binary files /dev/null and b/assets/images/bistro.png differ diff --git a/assets/images/component_based_architecture_diagram.png b/assets/images/component_based_architecture_diagram.png new file mode 100644 index 0000000..b1f089e Binary files /dev/null and b/assets/images/component_based_architecture_diagram.png differ diff --git a/assets/images/compute_shader_particles.png b/assets/images/compute_shader_particles.png new file mode 100644 index 0000000..139c246 Binary files /dev/null and b/assets/images/compute_shader_particles.png differ diff --git a/assets/images/compute_space.svg b/assets/images/compute_space.svg new file mode 100644 index 0000000..2f9c1a6 --- /dev/null +++ b/assets/images/compute_space.svg @@ -0,0 +1,4 @@ + + + +
Dispatch
Dispatch
Workgroup
[0,0,2]
Workgroup...
Workgroup
[1,0,2]
Workgroup...
Workgroup
[2,0,2]
Workgroup...
Workgroup
[2,1,2]
Workgroup...
Workgroup
[2,2,2]
Workgroup...
Dispatch
Dispatch
Workgroup
[0,0,1]
Workgroup...
Workgroup
[1,0,1]
Workgroup...
Workgroup
[2,0,1]
Workgroup...
Workgroup
[2,1,1]
Workgroup...
Workgroup
[2,2,1]
Workgroup...
X
X
Y
Y
Workgroup
[0,1,1]
Workgroup...
Workgroup
[1,1,1]
Workgroup...
Workgroup
[0,2,1]
Workgroup...
Workgroup
[1,2,1]
Workgroup...
Dispatch
Dispatch
Workgroup
[0,0,0]
Workgroup...
Workgroup
[1,0,0]
Workgroup...
Workgroup
[2,0,0]
Workgroup...
Workgroup
[0,1,0]
Workgroup...
Workgroup
[1,1,0]
Workgroup...
Workgroup
[2,1,0]
Workgroup...
Workgroup
[0,2,0]
Workgroup...
Workgroup
[1,2,0]
Workgroup...
Workgroup
[2,2,0]
Workgroup...
Z
Z

Invocation
[0,0,0]
Invocation...
Invocation
[1,0,0]
Invocation...
Invocation
[0,1,0]
Invocation...
Invocation
[1,1,0]
Invocation...

Invocation
[0,0,0]
Invocation...
Invocation
[1,0,0]
Invocation...
Invocation
[0,1,0]
Invocation...
Invocation
[1,1,0]
Invocation...
Work group [0,0,0]
Work group [0,0,0]
Invocation
[0,0,0]
Invocation...
Invocation
[1,0,0]
Invocation...
Invocation
[0,1,0]
Invocation...
Invocation
[1,1,0]
Invocation...
Invocation
[0,0,0]
Invocation...
Invocation
[1,0,0]
Invocation...
Invocation
[0,1,0]
Invocation...
Invocation
[1,1,0]
Invocation...
Invocation
[0,0,0]
Invocation...
Invocation
[1,0,0]
Invocation...
Invocation
[0,1,0]
Invocation...
Invocation
[1,1,0]
Invocation...
Invocation
[0,0,0]
Invocation...
Invocation
[1,0,0]
Invocation...
Invocation
[0,1,0]
Invocation...
Invocation
[1,1,0]
Invocation...
Invocation
[0,0,0]
Invocation...
Invocation
[1,0,0]
Invocation...
Invocation
[0,1,0]
Invocation...
Invocation
[1,1,0]
Invocation...
Work group [2,2,0]
Work group [2,2,0]
Invocation
[0,0,0]
Invocation...
Invocation
[1,0,0]
Invocation...
Invocation
[0,1,0]
Invocation...
Invocation
[1,1,0]
Invocation...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/assets/images/compute_ssbo_read_write.svg b/assets/images/compute_ssbo_read_write.svg new file mode 100644 index 0000000..b3a5f50 --- /dev/null +++ b/assets/images/compute_ssbo_read_write.svg @@ -0,0 +1,4 @@ + + + +
Frame 0
DeltaTime = 0.16
Frame 0...
Read
Read
Write
Write
SSBO[0]
Particle[0].Position = 0.16
SSBO[0]...
SSBO[1]
Particle[0].Position = 0.0
SSBO[1]...
Frame 1
DeltaTime = 0.17
Frame 1...
SSBO[1]
Particle[0].Position = 0.33
SSBO[1]...
SSBO[0]
Particle[0].Position = 0.16
SSBO[0]...
Frame 2
DeltaTime = 0.15
Frame 2...
SSBO[0]
Particle[0].Position = 0.48
SSBO[0]...
SSBO[1]
Particle[0].Position = 0.33
SSBO[1]...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/assets/images/cube_demo.png b/assets/images/cube_demo.png new file mode 100644 index 0000000..21e928e Binary files /dev/null and b/assets/images/cube_demo.png differ diff --git a/assets/images/cube_demo_mac.png b/assets/images/cube_demo_mac.png new file mode 100644 index 0000000..9a07593 Binary files /dev/null and b/assets/images/cube_demo_mac.png differ diff --git a/assets/images/cube_demo_nowindow.png b/assets/images/cube_demo_nowindow.png new file mode 100644 index 0000000..1783f66 Binary files /dev/null and b/assets/images/cube_demo_nowindow.png differ diff --git a/assets/images/data_oriented_design_diagram.svg b/assets/images/data_oriented_design_diagram.svg new file mode 100644 index 0000000..d061142 --- /dev/null +++ b/assets/images/data_oriented_design_diagram.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + Data-Oriented Design + + + Object-Oriented Approach + + + + GameObject 1 + position: (10, 20, 30) + rotation: (0, 45, 0) + scale: (1, 1, 1) + + + GameObject 2 + position: (50, 0, 10) + rotation: (90, 0, 0) + scale: (2, 2, 2) + + + GameObject 3 + position: (0, 10, 50) + rotation: (0, 180, 0) + scale: (1, 3, 1) + + + VS + + + Data-Oriented Approach + + + + Positions Array + [10,20,30, 50,0,10, 0,10,50, ...] + + + Rotations Array + [0,45,0, 90,0,0, 0,180,0, ...] + + + Scales Array + [1,1,1, 2,2,2, 1,3,1, ...] + + + + Transform System + + + Physics System + + + + + + + + Objects with mixed data types + Contiguous arrays of same data type + diff --git a/assets/images/depth_correct.png b/assets/images/depth_correct.png new file mode 100644 index 0000000..d9dce89 Binary files /dev/null and b/assets/images/depth_correct.png differ diff --git a/assets/images/depth_issues.png b/assets/images/depth_issues.png new file mode 100644 index 0000000..ad486c9 Binary files /dev/null and b/assets/images/depth_issues.png differ diff --git a/assets/images/drawing_model.png b/assets/images/drawing_model.png new file mode 100644 index 0000000..c327281 Binary files /dev/null and b/assets/images/drawing_model.png differ diff --git a/assets/images/extra_square.svg b/assets/images/extra_square.svg new file mode 100644 index 0000000..3fa067f --- /dev/null +++ b/assets/images/extra_square.svg @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + + + + + + + + + + diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000..f90ef25 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/images/glfw_directory.png b/assets/images/glfw_directory.png new file mode 100644 index 0000000..afd450b Binary files /dev/null and b/assets/images/glfw_directory.png differ diff --git a/assets/images/highmipmaps.png b/assets/images/highmipmaps.png new file mode 100644 index 0000000..bb6c6dd Binary files /dev/null and b/assets/images/highmipmaps.png differ diff --git a/assets/images/include_dirs_stb.png b/assets/images/include_dirs_stb.png new file mode 100644 index 0000000..9035e76 Binary files /dev/null and b/assets/images/include_dirs_stb.png differ diff --git a/assets/images/include_dirs_tinyobjloader.png b/assets/images/include_dirs_tinyobjloader.png new file mode 100644 index 0000000..4e2fbe3 Binary files /dev/null and b/assets/images/include_dirs_tinyobjloader.png differ diff --git a/assets/images/indexed_rectangle.png b/assets/images/indexed_rectangle.png new file mode 100644 index 0000000..2c7498d Binary files /dev/null and b/assets/images/indexed_rectangle.png differ diff --git a/assets/images/inverted_texture_coordinates.png b/assets/images/inverted_texture_coordinates.png new file mode 100644 index 0000000..4d37371 Binary files /dev/null and b/assets/images/inverted_texture_coordinates.png differ diff --git a/assets/images/layered_architecture_diagram.png b/assets/images/layered_architecture_diagram.png new file mode 100644 index 0000000..c5b0d48 Binary files /dev/null and b/assets/images/layered_architecture_diagram.png differ diff --git a/assets/images/library_directory.png b/assets/images/library_directory.png new file mode 100644 index 0000000..692349e Binary files /dev/null and b/assets/images/library_directory.png differ diff --git a/assets/images/matrix-order-comparison.svg b/assets/images/matrix-order-comparison.svg new file mode 100644 index 0000000..68c26ef --- /dev/null +++ b/assets/images/matrix-order-comparison.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + Matrix Order Comparison: T × R × S vs R × T × S + + + CORRECT ORDER: T × R × S (Applied right-to-left: Scale → Rotate → Translate) + + + + Step 1: Scale + + Original cube + at origin + + + + + + + + Step 2: Rotate + + Rotated in place + around its center + + + + + + + + Step 3: Translate + + Moved to new position + (final desired result) + + + Origin + + + + + + + INCORRECT ORDER: R × T × S (Applied right-to-left: Scale → Translate → Rotate) + + + + Step 1: Scale + + Original cube + at origin + + + + + + + + Step 2: Translate + + Moved away from + origin first + + + Origin + + + + + + + + Step 3: Rotate + + Rotated around world origin + (cube orbits, not desired!) + + + Origin + + + + + + + + Key Insight: + In the incorrect order, the cube is moved away from the origin first, then rotated around the world origin, + causing it to orbit in a circle rather than rotate in place and then move to the desired position. + + diff --git a/assets/images/mipmaps.png b/assets/images/mipmaps.png new file mode 100644 index 0000000..c48bea7 Binary files /dev/null and b/assets/images/mipmaps.png differ diff --git a/assets/images/mipmaps_comparison.png b/assets/images/mipmaps_comparison.png new file mode 100644 index 0000000..405edfd Binary files /dev/null and b/assets/images/mipmaps_comparison.png differ diff --git a/assets/images/mipmaps_example.jpg b/assets/images/mipmaps_example.jpg new file mode 100644 index 0000000..c44c9ee Binary files /dev/null and b/assets/images/mipmaps_example.jpg differ diff --git a/assets/images/multisampling.png b/assets/images/multisampling.png new file mode 100644 index 0000000..3066ea5 Binary files /dev/null and b/assets/images/multisampling.png differ diff --git a/assets/images/multisampling_comparison.png b/assets/images/multisampling_comparison.png new file mode 100644 index 0000000..221f4dc Binary files /dev/null and b/assets/images/multisampling_comparison.png differ diff --git a/assets/images/multisampling_comparison2.png b/assets/images/multisampling_comparison2.png new file mode 100644 index 0000000..048ffc1 Binary files /dev/null and b/assets/images/multisampling_comparison2.png differ diff --git a/assets/images/normalized_device_coordinates.svg b/assets/images/normalized_device_coordinates.svg new file mode 100644 index 0000000..970c9f4 --- /dev/null +++ b/assets/images/normalized_device_coordinates.svg @@ -0,0 +1,219 @@ + + + + + + + + + + image/svg+xml + + + + + + + + Framebuffer coordinates + (0, 0) + (1920, 0) + (0, 1080) + (1920, 1080) + + (960, 540) + + Normalized device coordinates + (-1, -1) + (1, -1) + (-1, 1) + (1, 1) + + (0, 0) + + diff --git a/assets/images/rendering_pipeline_flowchart.png b/assets/images/rendering_pipeline_flowchart.png new file mode 100644 index 0000000..8a7614e Binary files /dev/null and b/assets/images/rendering_pipeline_flowchart.png differ diff --git a/assets/images/sample_shading.png b/assets/images/sample_shading.png new file mode 100644 index 0000000..7a71576 Binary files /dev/null and b/assets/images/sample_shading.png differ diff --git a/assets/images/sdk_install_mac.png b/assets/images/sdk_install_mac.png new file mode 100644 index 0000000..93aa7b6 Binary files /dev/null and b/assets/images/sdk_install_mac.png differ diff --git a/assets/images/select_develop_branch.png b/assets/images/select_develop_branch.png new file mode 100644 index 0000000..1312dcd Binary files /dev/null and b/assets/images/select_develop_branch.png differ diff --git a/assets/images/semaphore_in_use.png b/assets/images/semaphore_in_use.png new file mode 100644 index 0000000..13dd9a4 Binary files /dev/null and b/assets/images/semaphore_in_use.png differ diff --git a/assets/images/service_locator_pattern_diagram.svg b/assets/images/service_locator_pattern_diagram.svg new file mode 100644 index 0000000..29ced46 --- /dev/null +++ b/assets/images/service_locator_pattern_diagram.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + Service Locator Pattern + + + + IAudioService + + PlaySound(soundName) + + + + OpenALAudioService + implements IAudioService + + + NullAudioService + implements IAudioService + + + ... + Other services + + + + ServiceLocator + + GetAudioService() + + ProvideAudioService(service) + + + + Client Code + + + + + + + + + + + + + + + + + + + Concrete Implementations + Service Interface + Global Access Point + Uses services via locator + diff --git a/assets/images/spinning_quad.png b/assets/images/spinning_quad.png new file mode 100644 index 0000000..6c779d8 Binary files /dev/null and b/assets/images/spinning_quad.png differ diff --git a/assets/images/steam_layers_env.png b/assets/images/steam_layers_env.png new file mode 100644 index 0000000..c812e01 Binary files /dev/null and b/assets/images/steam_layers_env.png differ diff --git a/assets/images/swap_chain_validation_layer.png b/assets/images/swap_chain_validation_layer.png new file mode 100644 index 0000000..a0002cd Binary files /dev/null and b/assets/images/swap_chain_validation_layer.png differ diff --git a/assets/images/texcoord_visualization.png b/assets/images/texcoord_visualization.png new file mode 100644 index 0000000..c57b7f3 Binary files /dev/null and b/assets/images/texcoord_visualization.png differ diff --git a/assets/images/texture.jpg b/assets/images/texture.jpg new file mode 100644 index 0000000..975e338 Binary files /dev/null and b/assets/images/texture.jpg differ diff --git a/assets/images/texture_addressing.png b/assets/images/texture_addressing.png new file mode 100644 index 0000000..c148ff3 Binary files /dev/null and b/assets/images/texture_addressing.png differ diff --git a/assets/images/texture_filtering.png b/assets/images/texture_filtering.png new file mode 100644 index 0000000..c10b247 Binary files /dev/null and b/assets/images/texture_filtering.png differ diff --git a/assets/images/texture_on_square.png b/assets/images/texture_on_square.png new file mode 100644 index 0000000..41627be Binary files /dev/null and b/assets/images/texture_on_square.png differ diff --git a/assets/images/texture_on_square_colorized.png b/assets/images/texture_on_square_colorized.png new file mode 100644 index 0000000..98d7980 Binary files /dev/null and b/assets/images/texture_on_square_colorized.png differ diff --git a/assets/images/texture_on_square_repeated.png b/assets/images/texture_on_square_repeated.png new file mode 100644 index 0000000..589d2b2 Binary files /dev/null and b/assets/images/texture_on_square_repeated.png differ diff --git a/assets/images/triangle.png b/assets/images/triangle.png new file mode 100644 index 0000000..0d4dc5e Binary files /dev/null and b/assets/images/triangle.png differ diff --git a/assets/images/triangle_coordinates.svg b/assets/images/triangle_coordinates.svg new file mode 100644 index 0000000..5a40d46 --- /dev/null +++ b/assets/images/triangle_coordinates.svg @@ -0,0 +1,107 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + v0 + v1 + v2 + + diff --git a/assets/images/triangle_coordinates_colors.png b/assets/images/triangle_coordinates_colors.png new file mode 100644 index 0000000..233f451 Binary files /dev/null and b/assets/images/triangle_coordinates_colors.png differ diff --git a/assets/images/triangle_white.png b/assets/images/triangle_white.png new file mode 100644 index 0000000..61104df Binary files /dev/null and b/assets/images/triangle_white.png differ diff --git a/assets/images/validation_layer_anisotropy.png b/assets/images/validation_layer_anisotropy.png new file mode 100644 index 0000000..2979e6b Binary files /dev/null and b/assets/images/validation_layer_anisotropy.png differ diff --git a/assets/images/validation_layer_test.png b/assets/images/validation_layer_test.png new file mode 100644 index 0000000..9b733d2 Binary files /dev/null and b/assets/images/validation_layer_test.png differ diff --git a/assets/images/vertex_vs_index.svg b/assets/images/vertex_vs_index.svg new file mode 100644 index 0000000..188e84c --- /dev/null +++ b/assets/images/vertex_vs_index.svg @@ -0,0 +1,243 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + Vertex buffer only + Vertex + index buffer + + + v0 + v1 + v2 + v3 + v4 + v5 + v0 + v1 + v2 + v3 + + Indices{0, 1, 2, 2, 3, 0} + + diff --git a/assets/images/viewports_scissors.png b/assets/images/viewports_scissors.png new file mode 100644 index 0000000..6e6b8a2 Binary files /dev/null and b/assets/images/viewports_scissors.png differ diff --git a/assets/images/vs_all_configs.png b/assets/images/vs_all_configs.png new file mode 100644 index 0000000..628c6ba Binary files /dev/null and b/assets/images/vs_all_configs.png differ diff --git a/assets/images/vs_application_settings.png b/assets/images/vs_application_settings.png new file mode 100644 index 0000000..695e1e1 Binary files /dev/null and b/assets/images/vs_application_settings.png differ diff --git a/assets/images/vs_build_mode.png b/assets/images/vs_build_mode.png new file mode 100644 index 0000000..f9f367a Binary files /dev/null and b/assets/images/vs_build_mode.png differ diff --git a/assets/images/vs_cpp17.png b/assets/images/vs_cpp17.png new file mode 100644 index 0000000..401a2de Binary files /dev/null and b/assets/images/vs_cpp17.png differ diff --git a/assets/images/vs_cpp_general.png b/assets/images/vs_cpp_general.png new file mode 100644 index 0000000..f5937a5 Binary files /dev/null and b/assets/images/vs_cpp_general.png differ diff --git a/assets/images/vs_dependencies.png b/assets/images/vs_dependencies.png new file mode 100644 index 0000000..5bf85b1 Binary files /dev/null and b/assets/images/vs_dependencies.png differ diff --git a/assets/images/vs_export_template.png b/assets/images/vs_export_template.png new file mode 100644 index 0000000..2b35913 Binary files /dev/null and b/assets/images/vs_export_template.png differ diff --git a/assets/images/vs_include_dirs.png b/assets/images/vs_include_dirs.png new file mode 100644 index 0000000..def6bf6 Binary files /dev/null and b/assets/images/vs_include_dirs.png differ diff --git a/assets/images/vs_link_dirs.png b/assets/images/vs_link_dirs.png new file mode 100644 index 0000000..e0c76f7 Binary files /dev/null and b/assets/images/vs_link_dirs.png differ diff --git a/assets/images/vs_link_input.png b/assets/images/vs_link_input.png new file mode 100644 index 0000000..bcbde43 Binary files /dev/null and b/assets/images/vs_link_input.png differ diff --git a/assets/images/vs_link_settings.png b/assets/images/vs_link_settings.png new file mode 100644 index 0000000..ca187c5 Binary files /dev/null and b/assets/images/vs_link_settings.png differ diff --git a/assets/images/vs_new_cpp_project.png b/assets/images/vs_new_cpp_project.png new file mode 100644 index 0000000..338cd32 Binary files /dev/null and b/assets/images/vs_new_cpp_project.png differ diff --git a/assets/images/vs_new_item.png b/assets/images/vs_new_item.png new file mode 100644 index 0000000..75b4a42 Binary files /dev/null and b/assets/images/vs_new_item.png differ diff --git a/assets/images/vs_new_source_file.png b/assets/images/vs_new_source_file.png new file mode 100644 index 0000000..4371726 Binary files /dev/null and b/assets/images/vs_new_source_file.png differ diff --git a/assets/images/vs_open_project_properties.png b/assets/images/vs_open_project_properties.png new file mode 100644 index 0000000..552d659 Binary files /dev/null and b/assets/images/vs_open_project_properties.png differ diff --git a/assets/images/vs_template.png b/assets/images/vs_template.png new file mode 100644 index 0000000..9960153 Binary files /dev/null and b/assets/images/vs_template.png differ diff --git a/assets/images/vs_test_window.png b/assets/images/vs_test_window.png new file mode 100644 index 0000000..ded64e1 Binary files /dev/null and b/assets/images/vs_test_window.png differ diff --git a/assets/images/vulkan_pipeline_block_diagram.png b/assets/images/vulkan_pipeline_block_diagram.png new file mode 100644 index 0000000..10955e1 Binary files /dev/null and b/assets/images/vulkan_pipeline_block_diagram.png differ diff --git a/assets/images/vulkan_simplified_pipeline.svg b/assets/images/vulkan_simplified_pipeline.svg new file mode 100644 index 0000000..ac26767 --- /dev/null +++ b/assets/images/vulkan_simplified_pipeline.svg @@ -0,0 +1,1566 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Input assembler + + + Input assembler + + + + + + + + + + Vertex shader + + + Vertex shader + + + + + + + + + + Tessellation + + + Tessellation + + + + + + + + + + Geometry shader + + + Geometry shader + + + + + + + + + + Rasterization + + + Rasterization + + + + + + + + + + Fragment shader + + + Fragment shader + + + + + + + + + + Color blending + + + Color blending + + + + + + + + + Vertex/index buffer + + + Vertex/index buffer + + + + + + + Framebuffer + + + Framebuffer + + + + + + + + + + + 0 + 1 + 2 + + + + + 0 + 1 + 2 + + + + + + + + 0 + 1 + 2 + + + + + + + + + + + 0 + 1 + 2 + + + + + + + + + + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/xcode_frameworks.png b/assets/images/xcode_frameworks.png new file mode 100644 index 0000000..1c05b28 Binary files /dev/null and b/assets/images/xcode_frameworks.png differ diff --git a/assets/images/xcode_new_project.png b/assets/images/xcode_new_project.png new file mode 100644 index 0000000..6566e2e Binary files /dev/null and b/assets/images/xcode_new_project.png differ diff --git a/assets/images/xcode_new_project_2.png b/assets/images/xcode_new_project_2.png new file mode 100644 index 0000000..417addb Binary files /dev/null and b/assets/images/xcode_new_project_2.png differ diff --git a/assets/images/xcode_output.png b/assets/images/xcode_output.png new file mode 100644 index 0000000..520a51d Binary files /dev/null and b/assets/images/xcode_output.png differ diff --git a/assets/images/xcode_paths.png b/assets/images/xcode_paths.png new file mode 100644 index 0000000..fb29a7b Binary files /dev/null and b/assets/images/xcode_paths.png differ diff --git a/assets/images/xcode_variables.png b/assets/images/xcode_variables.png new file mode 100644 index 0000000..3d57b7d Binary files /dev/null and b/assets/images/xcode_variables.png differ