Compare commits

..

4 Commits

Author SHA1 Message Date
717a8f1869 Texture sampling 2025-12-20 23:45:54 +00:00
893a0e5452 Create texture image 2025-12-20 20:49:11 +00:00
284a7a818a Rename transitionImageLayout to transitionSwapChainImageLayout 2025-12-20 20:48:41 +00:00
7dbb43f9d1 Uniform buffer 2025-12-20 18:07:54 +00:00
119 changed files with 7983 additions and 48 deletions

View File

@@ -393,7 +393,7 @@ class HelloTriangleApplication {
commandBuffer.begin({});
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal,
@@ -438,7 +438,7 @@ class HelloTriangleApplication {
commandBuffer.endRendering();
// After rendering, transition the swapchain image to PRESENT_SRC
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR,
@@ -451,7 +451,7 @@ class HelloTriangleApplication {
// Finish recording the command buffer
commandBuffer.end();
}
void transitionImageLayout(
void transitionSwapChainImageLayout(
uint32_t imageIndex,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout,

View File

@@ -446,7 +446,7 @@ class HelloTriangleApplication {
commandBuffer.begin({});
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal,
@@ -491,7 +491,7 @@ class HelloTriangleApplication {
commandBuffer.endRendering();
// After rendering, transition the swapchain image to PRESENT_SRC
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR,
@@ -504,7 +504,7 @@ class HelloTriangleApplication {
// Finish recording the command buffer
commandBuffer.end();
}
void transitionImageLayout(
void transitionSwapChainImageLayout(
uint32_t imageIndex,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout,

View File

@@ -456,7 +456,7 @@ class HelloTriangleApplication {
commandBuffer.begin({});
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal,
@@ -501,7 +501,7 @@ class HelloTriangleApplication {
commandBuffer.endRendering();
// After rendering, transition the swapchain image to PRESENT_SRC
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR,
@@ -514,7 +514,7 @@ class HelloTriangleApplication {
// Finish recording the command buffer
commandBuffer.end();
}
void transitionImageLayout(
void transitionSwapChainImageLayout(
uint32_t imageIndex,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout,

View File

@@ -489,7 +489,7 @@ class HelloTriangleApplication {
commandBuffer.begin({});
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal,
@@ -534,7 +534,7 @@ class HelloTriangleApplication {
commandBuffer.endRendering();
// After rendering, transition the swapchain image to PRESENT_SRC
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR,
@@ -547,7 +547,7 @@ class HelloTriangleApplication {
// Finish recording the command buffer
commandBuffer.end();
}
void transitionImageLayout(
void transitionSwapChainImageLayout(
uint32_t imageIndex,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout,

View File

@@ -534,7 +534,7 @@ class HelloTriangleApplication {
commandBuffer.begin({});
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal,
@@ -579,7 +579,7 @@ class HelloTriangleApplication {
commandBuffer.endRendering();
// After rendering, transition the swapchain image to PRESENT_SRC
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR,
@@ -592,7 +592,7 @@ class HelloTriangleApplication {
// Finish recording the command buffer
commandBuffer.end();
}
void transitionImageLayout(
void transitionSwapChainImageLayout(
uint32_t imageIndex,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout,

View File

@@ -558,7 +558,7 @@ class HelloTriangleApplication {
commandBuffer.begin({});
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal,
@@ -604,7 +604,7 @@ class HelloTriangleApplication {
commandBuffer.endRendering();
// After rendering, transition the swapchain image to PRESENT_SRC
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR,
@@ -627,7 +627,7 @@ class HelloTriangleApplication {
throw std::runtime_error("failed to find suitable memory type!");
}
void transitionImageLayout(
void transitionSwapChainImageLayout(
uint32_t imageIndex,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout,

View File

@@ -562,7 +562,7 @@ class HelloTriangleApplication {
commandBuffer.begin({});
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal,
@@ -608,7 +608,7 @@ class HelloTriangleApplication {
commandBuffer.endRendering();
// After rendering, transition the swapchain image to PRESENT_SRC
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR,
@@ -676,7 +676,7 @@ class HelloTriangleApplication {
throw std::runtime_error("failed to find suitable memory type!");
}
void transitionImageLayout(
void transitionSwapChainImageLayout(
uint32_t imageIndex,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout,

View File

@@ -592,7 +592,7 @@ class HelloTriangleApplication {
commandBuffer.begin({});
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal,
@@ -639,7 +639,7 @@ class HelloTriangleApplication {
commandBuffer.endRendering();
// After rendering, transition the swapchain image to PRESENT_SRC
transitionImageLayout(
transitionSwapChainImageLayout(
imageIndex,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR,
@@ -707,7 +707,7 @@ class HelloTriangleApplication {
throw std::runtime_error("failed to find suitable memory type!");
}
void transitionImageLayout(
void transitionSwapChainImageLayout(
uint32_t imageIndex,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout,

921
22_descriptor_layout.cpp Normal file
View File

@@ -0,0 +1,921 @@
#include <cassert>
#include <cstring>
#include <glm/trigonometric.hpp>
#include <tuple>
#if defined(__INTELLISENSE__) || !defined(USE_CPP20_MODULES)
#include "vulkan/vulkan.hpp"
#include <vulkan/vulkan_raii.hpp>
#include <vulkan/vulkan_core.h>
#else
import vulkan_hpp;
#endif
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <chrono>
#include <ios>
#include <iostream>
#include <fstream>
#include <array>
#include <vector>
#include <string>
#include <stdexcept>
#include <cstdlib>
#include <cstdint>
#include <limits>
#include <algorithm>
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<vk::VertexInputAttributeDescription, 2> 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 {
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
};
const std::vector<Vertex> 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<uint16_t> indices = { 0, 1, 2, 2, 3, 0 };
const std::vector<char const *> validationLayers = {
"VK_LAYER_KHRONOS_validation"
};
#ifdef NDEBUG
constexpr bool enableValidationLayers = false;
#else
constexpr bool enableValidationLayers = true;
#endif
static std::vector<char> 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<char> buffer(file.tellg());
file.seekg(0, std::ios::beg);
file.read(buffer.data(), static_cast<std::streamsize>(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<HelloTriangleApplication*>(glfwGetWindowUserPointer(window));
app->framebufferResized = true;
}
void initVulkan() {
createInstance();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createSwapChain();
createImageViews();
createDescriptorSetLayout();
createGraphicsPipeline();
createCommandPool();
createVertexBuffer();
createIndexBuffer();
createUniformBuffers();
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<int>(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<int>(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<char const*> 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<uint32_t>(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<const char*> 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<vk::QueueFlags>(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<vk::QueueFamilyProperties> 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<vk::PhysicalDeviceFeatures2,
vk::PhysicalDeviceVulkan13Features,
vk::PhysicalDeviceVulkan11Features,
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> 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<const char*> deviceExtensions = {
vk::KHRSwapchainExtensionName,
vk::KHRSpirv14ExtensionName,
vk::KHRSynchronization2ExtensionName,
vk::KHRCreateRenderpass2ExtensionName
};
vk::DeviceCreateInfo deviceCreateInfo {
.pNext = &featureChain.get<vk::PhysicalDeviceFeatures2>(),
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &deviceQueueCreateInfo,
.enabledExtensionCount = static_cast<uint32_t>(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<vk::DynamicState> dynamicStates = {
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
};
vk::PipelineDynamicStateCreateInfo dynamicState = {
.dynamicStateCount = static_cast<uint32_t>(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::eClockwise,
.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 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 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<float, std::chrono::seconds::period>(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<float>(swapChainExtent.width) / static_cast<float>(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<float>(swapChainExtent.width),
.height = static_cast<float>(swapChainExtent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
commandBuffer.setViewport(0, viewport);
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(0, 0), swapChainExtent));
// 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::CommandBufferAllocateInfo allocInfo{
.commandPool = commandPool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1
};
vk::raii::CommandBuffer commandCopyBuffer = std::move(device.allocateCommandBuffers(allocInfo).front());
commandCopyBuffer.begin(vk::CommandBufferBeginInfo{
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
});
commandCopyBuffer.copyBuffer(*srcBuffer, *dstBuffer, vk::BufferCopy(0, 0, size));
commandCopyBuffer.end();
graphicsQueue.submit(
vk::SubmitInfo{
.commandBufferCount = 1,
.pCommandBuffers = &*commandCopyBuffer
},
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 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<char> &code) const {
vk::ShaderModuleCreateInfo createInfo {
.codeSize = code.size() * sizeof(char),
.pCode = reinterpret_cast<const uint32_t *>(code.data()),
};
return vk::raii::ShaderModule{device, createInfo};
}
vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& 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<vk::PresentModeKHR>& 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<uint32_t>::max()) {
return capabilities.currentExtent;
}
int width, height;
glfwGetFramebufferSize(window, &width, &height);
return {
std::clamp<uint32_t>(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width),
std::clamp<uint32_t>(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<vk::QueueFamilyProperties> 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<vk::Image> swapChainImages;
std::vector<vk::raii::ImageView> swapChainImageViews;
vk::raii::DescriptorSetLayout descriptorSetLayout = nullptr;
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;
std::vector<vk::raii::Buffer> uniformBuffers;
std::vector<vk::raii::DeviceMemory> uniformBuffersMemory;
std::vector<void *> unifromBuffersMapped;
std::vector<vk::raii::CommandBuffer> commandBuffers;
std::vector<vk::raii::Semaphore> presentCompleteSemaphores;
std::vector<vk::raii::Semaphore> renderFinishedSemaphores;
std::vector<vk::raii::Fence> 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;
}

966
23_descriptor_sets.cpp Normal file
View File

@@ -0,0 +1,966 @@
#include <cassert>
#include <cstring>
#include <glm/trigonometric.hpp>
#include <tuple>
#if defined(__INTELLISENSE__) || !defined(USE_CPP20_MODULES)
#include "vulkan/vulkan.hpp"
#include <vulkan/vulkan_raii.hpp>
#include <vulkan/vulkan_core.h>
#else
import vulkan_hpp;
#endif
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <chrono>
#include <ios>
#include <iostream>
#include <fstream>
#include <array>
#include <vector>
#include <string>
#include <stdexcept>
#include <cstdlib>
#include <cstdint>
#include <limits>
#include <algorithm>
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<vk::VertexInputAttributeDescription, 2> 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<Vertex> 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<uint16_t> indices = { 0, 1, 2, 2, 3, 0 };
const std::vector<char const *> validationLayers = {
"VK_LAYER_KHRONOS_validation"
};
#ifdef NDEBUG
constexpr bool enableValidationLayers = false;
#else
constexpr bool enableValidationLayers = true;
#endif
static std::vector<char> 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<char> buffer(file.tellg());
file.seekg(0, std::ios::beg);
file.read(buffer.data(), static_cast<std::streamsize>(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<HelloTriangleApplication*>(glfwGetWindowUserPointer(window));
app->framebufferResized = true;
}
void initVulkan() {
createInstance();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createSwapChain();
createImageViews();
createDescriptorSetLayout();
createGraphicsPipeline();
createCommandPool();
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<int>(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<int>(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<char const*> 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<uint32_t>(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<const char*> 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<vk::QueueFlags>(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<vk::QueueFamilyProperties> 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<vk::PhysicalDeviceFeatures2,
vk::PhysicalDeviceVulkan13Features,
vk::PhysicalDeviceVulkan11Features,
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> 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<const char*> deviceExtensions = {
vk::KHRSwapchainExtensionName,
vk::KHRSpirv14ExtensionName,
vk::KHRSynchronization2ExtensionName,
vk::KHRCreateRenderpass2ExtensionName
};
vk::DeviceCreateInfo deviceCreateInfo {
.pNext = &featureChain.get<vk::PhysicalDeviceFeatures2>(),
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &deviceQueueCreateInfo,
.enabledExtensionCount = static_cast<uint32_t>(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<vk::DynamicState> dynamicStates = {
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
};
vk::PipelineDynamicStateCreateInfo dynamicState = {
.dynamicStateCount = static_cast<uint32_t>(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 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<vk::DescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, *descriptorSetLayout);
vk::DescriptorSetAllocateInfo allocInfo {
.descriptorPool = descriptorPool,
.descriptorSetCount = static_cast<uint32_t>(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<float, std::chrono::seconds::period>(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<float>(swapChainExtent.width) / static_cast<float>(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<float>(swapChainExtent.width),
.height = static_cast<float>(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::CommandBufferAllocateInfo allocInfo{
.commandPool = commandPool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1
};
vk::raii::CommandBuffer commandCopyBuffer = std::move(device.allocateCommandBuffers(allocInfo).front());
commandCopyBuffer.begin(vk::CommandBufferBeginInfo{
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
});
commandCopyBuffer.copyBuffer(*srcBuffer, *dstBuffer, vk::BufferCopy(0, 0, size));
commandCopyBuffer.end();
graphicsQueue.submit(
vk::SubmitInfo{
.commandBufferCount = 1,
.pCommandBuffers = &*commandCopyBuffer
},
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 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<char> &code) const {
vk::ShaderModuleCreateInfo createInfo {
.codeSize = code.size() * sizeof(char),
.pCode = reinterpret_cast<const uint32_t *>(code.data()),
};
return vk::raii::ShaderModule{device, createInfo};
}
vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& 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<vk::PresentModeKHR>& 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<uint32_t>::max()) {
return capabilities.currentExtent;
}
int width, height;
glfwGetFramebufferSize(window, &width, &height);
return {
std::clamp<uint32_t>(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width),
std::clamp<uint32_t>(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<vk::QueueFamilyProperties> 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<vk::Image> swapChainImages;
std::vector<vk::raii::ImageView> swapChainImageViews;
vk::raii::DescriptorSetLayout descriptorSetLayout = nullptr;
vk::raii::DescriptorPool descriptorPool = nullptr;
std::vector<vk::raii::DescriptorSet> 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;
std::vector<vk::raii::Buffer> uniformBuffers;
std::vector<vk::raii::DeviceMemory> uniformBuffersMemory;
std::vector<void *> unifromBuffersMapped;
std::vector<vk::raii::CommandBuffer> commandBuffers;
std::vector<vk::raii::Semaphore> presentCompleteSemaphores;
std::vector<vk::raii::Semaphore> renderFinishedSemaphores;
std::vector<vk::raii::Fence> 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;
}

1079
24_texture_image.cpp Normal file

File diff suppressed because it is too large Load Diff

1113
25_sampler.cpp Normal file

File diff suppressed because it is too large Load Diff

1142
26_texture_mapping.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -229,32 +229,32 @@ add_chapter (21_index_buffer
SHADER 18_shader_vertexbuffer
LIBS glm::glm)
# add_chapter (22_descriptor_layout
# SHADER 22_shader_ubo
# LIBS glm::glm)
#
# add_chapter (23_descriptor_sets
# SHADER 22_shader_ubo
# LIBS glm::glm)
#
# add_chapter (24_texture_image
# SHADER 22_shader_ubo
# TEXTURES ../../images/texture.jpg
# LIBS glm::glm)
#
# add_chapter (25_sampler
# SHADER 22_shader_ubo
# TEXTURES ../../images/texture.jpg
# LIBS glm::glm)
#
# add_chapter (26_texture_mapping
# SHADER 26_shader_textures
# TEXTURES ../../images/texture.jpg
# LIBS glm::glm)
#
add_chapter (22_descriptor_layout
SHADER 22_shader_ubo
LIBS glm::glm)
add_chapter (23_descriptor_sets
SHADER 22_shader_ubo
LIBS glm::glm)
add_chapter (24_texture_image
SHADER 22_shader_ubo
TEXTURES images/texture.jpg
LIBS glm::glm)
add_chapter (25_sampler
SHADER 22_shader_ubo
TEXTURES images/texture.jpg
LIBS glm::glm)
add_chapter (26_texture_mapping
SHADER 26_shader_textures
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
assets/images/aliasing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
assets/images/bistro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 70 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/images/cube_demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="600" height="450" xmlns="http://www.w3.org/2000/svg">
<!-- Data-Oriented Design Diagram -->
<style>
.data-array { fill: #e6f2ff; stroke: #0066cc; stroke-width: 2; }
.system { fill: #e6ffe6; stroke: #339933; stroke-width: 2; }
.oop-class { fill: #ffe6e6; stroke: #cc0000; stroke-width: 2; }
.text { font-family: Arial; font-size: 14px; text-anchor: middle; }
.small-text { font-family: Arial; font-size: 12px; text-anchor: middle; }
.title-text { font-family: Arial; font-size: 16px; font-weight: bold; text-anchor: middle; }
.main-title { font-family: Arial; font-size: 20px; font-weight: bold; text-anchor: middle; }
.arrow { stroke: #555; stroke-width: 2; marker-end: url(#arrowhead); }
.vs-text { font-family: Arial; font-size: 18px; font-weight: bold; text-anchor: middle; }
</style>
<!-- Arrow marker definition -->
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#555" />
</marker>
</defs>
<text x="300" y="30" class="main-title">Data-Oriented Design</text>
<!-- Left side: Object-Oriented Approach -->
<text x="150" y="60" class="title-text">Object-Oriented Approach</text>
<!-- OOP Classes -->
<rect x="50" y="80" width="200" height="100" rx="5" class="oop-class" />
<text x="150" y="100" class="title-text">GameObject 1</text>
<text x="150" y="125" class="small-text">position: (10, 20, 30)</text>
<text x="150" y="145" class="small-text">rotation: (0, 45, 0)</text>
<text x="150" y="165" class="small-text">scale: (1, 1, 1)</text>
<rect x="50" y="190" width="200" height="100" rx="5" class="oop-class" />
<text x="150" y="210" class="title-text">GameObject 2</text>
<text x="150" y="235" class="small-text">position: (50, 0, 10)</text>
<text x="150" y="255" class="small-text">rotation: (90, 0, 0)</text>
<text x="150" y="275" class="small-text">scale: (2, 2, 2)</text>
<rect x="50" y="300" width="200" height="100" rx="5" class="oop-class" />
<text x="150" y="320" class="title-text">GameObject 3</text>
<text x="150" y="345" class="small-text">position: (0, 10, 50)</text>
<text x="150" y="365" class="small-text">rotation: (0, 180, 0)</text>
<text x="150" y="385" class="small-text">scale: (1, 3, 1)</text>
<!-- VS text -->
<text x="300" y="225" class="vs-text">VS</text>
<!-- Right side: Data-Oriented Approach -->
<text x="450" y="60" class="title-text">Data-Oriented Approach</text>
<!-- Data Arrays -->
<rect x="350" y="80" width="200" height="60" rx="5" class="data-array" />
<text x="450" y="100" class="title-text">Positions Array</text>
<text x="450" y="125" class="small-text">[10,20,30, 50,0,10, 0,10,50, ...]</text>
<rect x="350" y="150" width="200" height="60" rx="5" class="data-array" />
<text x="450" y="170" class="title-text">Rotations Array</text>
<text x="450" y="195" class="small-text">[0,45,0, 90,0,0, 0,180,0, ...]</text>
<rect x="350" y="220" width="200" height="60" rx="5" class="data-array" />
<text x="450" y="240" class="title-text">Scales Array</text>
<text x="450" y="265" class="small-text">[1,1,1, 2,2,2, 1,3,1, ...]</text>
<!-- Systems -->
<rect x="350" y="300" width="200" height="40" rx="5" class="system" />
<text x="450" y="325" class="title-text">Transform System</text>
<rect x="350" y="350" width="200" height="40" rx="5" class="system" />
<text x="450" y="375" class="title-text">Physics System</text>
<!-- Arrows from data to systems -->
<line x1="450" y1="140" x2="450" y2="300" class="arrow" />
<line x1="450" y1="210" x2="450" y2="300" class="arrow" />
<line x1="450" y1="280" x2="450" y2="300" class="arrow" />
<!-- Explanation -->
<text x="150" y="420" class="text">Objects with mixed data types</text>
<text x="450" y="420" class="text">Contiguous arrays of same data type</text>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

View File

@@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="89.14743mm"
height="58.706608mm"
viewBox="0 0 315.87672 208.01553"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="3d_geometry.svg">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="3.4283239 : 623.25581 : 1"
inkscape:vp_y="0 : 741.72186 : 0"
inkscape:vp_z="744.19986 : 656.59754 : 1"
inkscape:persp3d-origin="373.56148 : 401.7537 : 1"
id="perspective4136" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="3.1710559 : 602.23322 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="743.73008 : 647.18501 : 1"
inkscape:persp3d-origin="373.19803 : 303.60092 : 1"
id="perspective4136-1" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="4.6862849 : 603.37958 : 1"
inkscape:vp_y="0 : 839.48686 : 0"
inkscape:vp_z="745.24531 : 641.11602 : 1"
inkscape:persp3d-origin="374.71326 : 352.68169 : 1"
id="perspective4136-4" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="214.75667"
inkscape:cy="186.60972"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-left="10"
fit-margin-top="10"
fit-margin-bottom="10"
fit-margin-right="10"
inkscape:window-width="2560"
inkscape:window-height="1377"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Layer 3"
transform="translate(-219.62754,-456.27778)">
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 273.21429,533.61218 99.64285,-29.10714 108.57143,22.14286 -99.82143,49.46428 z"
id="path4377"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 274.46429,567.54075 99.82143,-32.85714 108.03571,24.64286 -99.46429,56.42857 z"
id="path4379"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2"
transform="translate(-219.62754,-456.27778)" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-219.62754,-456.27778)">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="254.81097"
y="537.43695"
id="text4239"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4241"
x="254.81097"
y="537.43695">v0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="366.31015"
y="500.82339"
id="text4239-8"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4241-0"
x="366.31015"
y="500.82339">v1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="484.49802"
y="528.8551"
id="text4239-85"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4241-6"
x="484.49802"
y="528.8551">v2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="374.64392"
y="569.7663"
id="text4239-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4241-7"
x="374.64392"
y="569.7663">v3</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="254.68829"
y="571.53406"
id="text4239-7"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4241-8"
x="254.68829"
y="571.53406">v4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="367.32031"
y="531.63306"
id="text4239-87"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4241-2"
x="367.32031"
y="531.63306">v5</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="485.50818"
y="562.69519"
id="text4239-71"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4241-60"
x="485.50818"
y="562.69519">v6</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="376.15915"
y="628.86023"
id="text4239-37"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4241-5"
x="376.15915"
y="628.86023">v7</tspan></text>
<circle
style="fill:#ff0000;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path4321"
cx="274.44583"
cy="567.61523"
r="1.9571706" />
<circle
style="fill:#ff0000;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path4321-8"
cx="273.05685"
cy="533.64886"
r="1.9571706" />
<circle
style="fill:#ff0000;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path4321-88"
cx="373.06195"
cy="504.48071"
r="1.9571706" />
<circle
style="fill:#ff0000;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path4321-0"
cx="481.68588"
cy="526.53003"
r="1.9571706" />
<circle
style="fill:#ff0000;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path4321-5"
cx="482.41071"
cy="559.1925"
r="1.9571706" />
<circle
style="fill:#ff0000;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path4321-2"
cx="381.77451"
cy="575.94904"
r="1.9571706" />
<circle
style="fill:#ff0000;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path4321-59"
cx="374.28571"
cy="534.50507"
r="1.9571706" />
<circle
style="fill:#ff0000;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path4321-84"
cx="382.85715"
cy="615.57648"
r="1.9571706" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
assets/images/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
.title { font-family: Arial, sans-serif; font-size: 16px; font-weight: bold; }
.subtitle { font-family: Arial, sans-serif; font-size: 14px; font-weight: bold; }
.label { font-family: Arial, sans-serif; font-size: 12px; }
.step-label { font-family: Arial, sans-serif; font-size: 11px; }
.correct-path { stroke: #2E8B57; fill: #2E8B57; }
.incorrect-path { stroke: #DC143C; fill: #DC143C; }
.cube-correct { fill: #4CAF50; stroke: #2E7D32; stroke-width: 2; }
.cube-incorrect { fill: #F44336; stroke: #C62828; stroke-width: 2; }
.arrow { stroke-width: 2; marker-end: url(#arrowhead); }
.origin { fill: #666; stroke: #333; stroke-width: 1; }
</style>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#333" />
</marker>
</defs>
<!-- Title -->
<text x="400" y="25" text-anchor="middle" class="title">Matrix Order Comparison: T × R × S vs R × T × S</text>
<!-- Correct Order Section -->
<text x="400" y="50" text-anchor="middle" class="subtitle correct-path">CORRECT ORDER: T × R × S (Applied right-to-left: Scale → Rotate → Translate)</text>
<!-- Step 1: Scale -->
<g transform="translate(50, 80)">
<text x="50" y="15" text-anchor="middle" class="step-label">Step 1: Scale</text>
<rect x="40" y="25" width="20" height="20" class="cube-correct" />
<text x="50" y="55" text-anchor="middle" class="label">Original cube</text>
<text x="50" y="70" text-anchor="middle" class="label">at origin</text>
</g>
<!-- Arrow 1 -->
<line x1="120" y1="115" x2="180" y2="115" class="arrow correct-path" />
<!-- Step 2: Rotate -->
<g transform="translate(200, 80)">
<text x="50" y="15" text-anchor="middle" class="step-label">Step 2: Rotate</text>
<polygon points="40,25 60,35 60,55 40,45" class="cube-correct" />
<text x="50" y="70" text-anchor="middle" class="label">Rotated in place</text>
<text x="50" y="85" text-anchor="middle" class="label">around its center</text>
</g>
<!-- Arrow 2 -->
<line x1="270" y1="115" x2="330" y2="115" class="arrow correct-path" />
<!-- Step 3: Translate -->
<g transform="translate(350, 80)">
<text x="50" y="15" text-anchor="middle" class="step-label">Step 3: Translate</text>
<polygon points="60,25 80,35 80,55 60,45" class="cube-correct" />
<text x="70" y="70" text-anchor="middle" class="label">Moved to new position</text>
<text x="70" y="85" text-anchor="middle" class="label">(final desired result)</text>
<!-- Origin point -->
<circle cx="30" cy="50" r="3" class="origin" />
<text x="30" y="65" text-anchor="middle" class="step-label">Origin</text>
</g>
<!-- Separator Line -->
<line x1="50" y1="200" x2="750" y2="200" stroke="#ccc" stroke-width="1" />
<!-- Incorrect Order Section -->
<text x="400" y="230" text-anchor="middle" class="subtitle incorrect-path">INCORRECT ORDER: R × T × S (Applied right-to-left: Scale → Translate → Rotate)</text>
<!-- Step 1: Scale -->
<g transform="translate(50, 260)">
<text x="50" y="15" text-anchor="middle" class="step-label">Step 1: Scale</text>
<rect x="40" y="25" width="20" height="20" class="cube-incorrect" />
<text x="50" y="55" text-anchor="middle" class="label">Original cube</text>
<text x="50" y="70" text-anchor="middle" class="label">at origin</text>
</g>
<!-- Arrow 1 -->
<line x1="120" y1="295" x2="180" y2="295" class="arrow incorrect-path" />
<!-- Step 2: Translate -->
<g transform="translate(200, 260)">
<text x="50" y="15" text-anchor="middle" class="step-label">Step 2: Translate</text>
<rect x="60" y="45" width="20" height="20" class="cube-incorrect" />
<text x="70" y="80" text-anchor="middle" class="label">Moved away from</text>
<text x="70" y="95" text-anchor="middle" class="label">origin first</text>
<!-- Origin point -->
<circle cx="30" cy="50" r="3" class="origin" />
<text x="30" y="65" text-anchor="middle" class="step-label">Origin</text>
</g>
<!-- Arrow 2 -->
<line x1="270" y1="295" x2="330" y2="295" class="arrow incorrect-path" />
<!-- Step 3: Rotate -->
<g transform="translate(350, 260)">
<text x="50" y="15" text-anchor="middle" class="step-label">Step 3: Rotate</text>
<rect x="30" y="65" width="20" height="20" class="cube-incorrect" />
<text x="70" y="50" text-anchor="middle" class="label">Rotated around world origin</text>
<text x="70" y="65" text-anchor="middle" class="label">(cube orbits, not desired!)</text>
<!-- Origin point -->
<circle cx="70" cy="30" r="3" class="origin" />
<text x="70" y="20" text-anchor="middle" class="step-label">Origin</text>
<!-- Orbital path indication -->
<path d="M 70,30 Q 40,50 40,85 Q 70,105 100,85 Q 130,50 100,30"
fill="none" stroke="#DC143C" stroke-width="1" stroke-dasharray="3,3" opacity="0.7" />
</g>
<!-- Key Insight Box -->
<g transform="translate(50, 380)">
<rect x="0" y="0" width="700" height="80" fill="#f8f9fa" stroke="#dee2e6" stroke-width="1" rx="5" />
<text x="20" y="25" class="subtitle">Key Insight:</text>
<text x="20" y="45" class="label">In the incorrect order, the cube is moved away from the origin first, then rotated around the world origin,</text>
<text x="20" y="60" class="label">causing it to orbit in a circle rather than rotate in place and then move to the desired position.</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
assets/images/mipmaps.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

View File

@@ -0,0 +1,219 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="168.23553mm"
height="76.127022mm"
viewBox="0 0 596.11016 269.74141"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="clip_coordinates.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="140.75091"
inkscape:cy="-3.0732866"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-68.169789,-67.73013)">
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.37949777;stroke-opacity:1"
id="rect4136"
width="185.26089"
height="129.17273"
x="127.66544"
y="152.46893" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="142.5"
y="114.50506"
id="text4153"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4155"
x="142.5"
y="114.50506">Framebuffer coordinates</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="108.08633"
y="144.23506"
id="text4157"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159"
x="108.08633"
y="144.23506">(0, 0)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="289.4823"
y="143.68567"
id="text4157-1"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-7"
x="289.4823"
y="143.68567">(1920, 0)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="102.49812"
y="299.52383"
id="text4157-0"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-3"
x="102.49812"
y="299.52383">(0, 1080)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="277.83316"
y="298.46939"
id="text4157-1-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-7-2"
x="277.83316"
y="298.46939">(1920, 1080)</tspan></text>
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
id="path4229"
cx="220.46579"
cy="218.48128"
r="1.767767" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="187.29964"
y="232.99626"
id="text4157-1-3-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-7-2-3"
x="187.29964"
y="232.99626">(960, 540)</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.37949777;stroke-opacity:1"
id="rect4136-0"
width="185.26089"
height="129.17273"
x="426.228"
y="150.62413" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="465.34827"
y="112.66027"
id="text4153-2"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4155-2"
x="435.34827"
y="112.66027">Normalized device coordinates</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="406.6489"
y="142.39026"
id="text4157-9"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-0"
x="406.6489"
y="142.39026">(-1, -1)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="588.04486"
y="141.84087"
id="text4157-1-4"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-7-21"
x="588.04486"
y="141.84087">(1, -1)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="401.0607"
y="297.67902"
id="text4157-0-6"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-3-5"
x="401.0607"
y="297.67902">(-1, 1)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="592.82428"
y="296.62457"
id="text4157-1-3-7"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-7-2-6"
x="592.82428"
y="296.62457">(1, 1)</tspan></text>
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
id="path4229-5"
cx="519.02832"
cy="216.63647"
r="1.767767" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="500.14792"
y="231.15146"
id="text4157-1-3-3-8"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4159-7-2-3-0"
x="500.14792"
y="231.15146">(0, 0)</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="600" height="450" xmlns="http://www.w3.org/2000/svg">
<!-- Service Locator Pattern Diagram -->
<style>
.interface { fill: #e6f2ff; stroke: #0066cc; stroke-width: 2; }
.implementation { fill: #fff2e6; stroke: #ff8c1a; stroke-width: 2; }
.locator { fill: #e6ffe6; stroke: #339933; stroke-width: 2; }
.client { fill: #f2e6ff; stroke: #8c1aff; stroke-width: 2; }
.text { font-family: Arial; font-size: 14px; text-anchor: middle; }
.small-text { font-family: Arial; font-size: 12px; text-anchor: middle; }
.title-text { font-family: Arial; font-size: 16px; font-weight: bold; text-anchor: middle; }
.main-title { font-family: Arial; font-size: 20px; font-weight: bold; text-anchor: middle; }
.arrow { stroke: #555; stroke-width: 2; marker-end: url(#arrowhead); }
.dashed { stroke-dasharray: 5,5; }
</style>
<!-- Arrow marker definition -->
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#555" />
</marker>
</defs>
<text x="300" y="30" class="main-title">Service Locator Pattern</text>
<!-- Service Interface -->
<rect x="200" y="60" width="200" height="60" rx="5" class="interface" />
<text x="300" y="85" class="title-text">IAudioService</text>
<text x="300" y="105" class="small-text">+ PlaySound(soundName)</text>
<!-- Service Implementations -->
<rect x="50" y="170" width="180" height="60" rx="5" class="implementation" />
<text x="140" y="195" class="title-text">OpenALAudioService</text>
<text x="140" y="215" class="small-text">implements IAudioService</text>
<rect x="250" y="170" width="180" height="60" rx="5" class="implementation" />
<text x="340" y="195" class="title-text">NullAudioService</text>
<text x="340" y="215" class="small-text">implements IAudioService</text>
<rect x="450" y="170" width="100" height="60" rx="5" class="implementation" />
<text x="500" y="195" class="title-text">...</text>
<text x="500" y="215" class="small-text">Other services</text>
<!-- Service Locator -->
<rect x="200" y="280" width="200" height="80" rx="5" class="locator" />
<text x="300" y="305" class="title-text">ServiceLocator</text>
<text x="300" y="325" class="small-text">+ GetAudioService()</text>
<text x="300" y="345" class="small-text">+ ProvideAudioService(service)</text>
<!-- Client -->
<rect x="200" y="390" width="200" height="50" rx="5" class="client" />
<text x="300" y="420" class="title-text">Client Code</text>
<!-- Implementation to Interface arrows -->
<line x1="140" y1="170" x2="250" y2="120" class="arrow" />
<line x1="340" y1="170" x2="300" y2="120" class="arrow" />
<line x1="500" y1="170" x2="350" y2="120" class="arrow" />
<!-- Service Locator to Interface arrow -->
<line x1="300" y1="280" x2="300" y2="120" class="arrow" />
<!-- Service Locator to Implementations arrows -->
<line x1="250" y1="280" x2="140" y2="230" class="arrow dashed" />
<line x1="300" y1="280" x2="340" y2="230" class="arrow dashed" />
<line x1="350" y1="280" x2="500" y2="230" class="arrow dashed" />
<!-- Client to Service Locator arrow -->
<line x1="300" y1="390" x2="300" y2="360" class="arrow" />
<!-- Explanation -->
<text x="140" y="150" class="small-text">Concrete Implementations</text>
<text x="300" y="50" class="small-text">Service Interface</text>
<text x="300" y="270" class="small-text">Global Access Point</text>
<text x="300" y="380" class="small-text">Uses services via locator</text>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
assets/images/texture.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
assets/images/triangle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="72.956146mm"
height="57.12682mm"
viewBox="0 0 258.50603 202.41786"
id="svg4303"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="triangle_coordinates.svg">
<defs
id="defs4305" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.7"
inkscape:cx="430.81355"
inkscape:cy="-18.504758"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1600"
inkscape:window-height="837"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10" />
<metadata
id="metadata4308">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-173.60413,-102.58184)">
<rect
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.37900019;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4136-0"
width="185.26089"
height="129.17273"
x="210.2267"
y="139.20441" />
<path
style="fill:#ff0000;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 300,165.93363 -53.57143,72.14286 107.85714,0 z"
id="path4866"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="292.14285"
y="160.21935"
id="text4870"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4872"
x="292.14285"
y="160.21935">v0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="357.85715"
y="244.50507"
id="text4874"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4876"
x="357.85715"
y="244.50507">v1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="227.85713"
y="245.21935"
id="text4878"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4880"
x="227.85713"
y="245.21935">v2</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,243 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="137.68689mm"
height="79.406677mm"
viewBox="0 0 487.86693 281.36224"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="vertex_vs_index.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="-25.484848"
inkscape:cy="297.31319"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="10"
fit-margin-bottom="10"
fit-margin-right="10"
fit-margin-left="10"
inkscape:window-width="2560"
inkscape:window-height="1377"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-130.77675,-64.431342)">
<rect
style="fill:none;stroke:#000000;stroke-width:1.77554488;stroke-miterlimit:4;stroke-dasharray:none"
id="rect4136"
width="152.18956"
height="103.36208"
x="184.03664"
y="148.54825" />
<path
style="fill:none;stroke:#000000;stroke-width:1.77554488;stroke-miterlimit:4;stroke-dasharray:none"
d="M 184.03661,148.54824 336.22618,251.43473"
id="path4138"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.7554493px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="179.28067"
y="113.35439"
id="text4140"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4142"
x="179.28067"
y="113.35439">Vertex buffer only</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.7554493px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="392.70364"
y="113.81391"
id="text4140-2"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4142-7"
x="392.70364"
y="113.81391">Vertex + index buffer</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:1.77554488;stroke-miterlimit:4;stroke-dasharray:none"
id="rect4136-9"
width="152.18956"
height="103.36208"
x="412.06735"
y="148.23116" />
<path
style="fill:none;stroke:#000000;stroke-width:1.77554488;stroke-miterlimit:4;stroke-dasharray:none"
d="M 412.06731,148.23118 564.25688,251.11767"
id="path4138-5"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="180.86603"
y="144.7435"
id="text4180"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182"
x="180.86603"
y="144.7435">v0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="329.09991"
y="144.52441"
id="text4180-1"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-8"
x="329.09991"
y="144.52441">v1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="339.56296"
y="253.91068"
id="text4180-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-1"
x="339.56296"
y="253.91068">v2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="326.56339"
y="267.86139"
id="text4180-8"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-12"
x="326.56339"
y="267.86139">v3</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="172.78851"
y="267.54434"
id="text4180-38"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-88"
x="172.78851"
y="267.54434">v4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="165.81319"
y="157.20691"
id="text4180-7"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-10"
x="165.81319"
y="157.20691">v5</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="401.21762"
y="143.88382"
id="text4180-4"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-17"
x="401.21762"
y="143.88382">v0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="559.92078"
y="144.20735"
id="text4180-1-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-8-8"
x="559.92078"
y="144.20735">v1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="561.24097"
y="266.04681"
id="text4180-3-5"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-1-8"
x="561.24097"
y="266.04681">v2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="398.45514"
y="266.90369"
id="text4180-8-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4182-12-6"
x="398.45514"
y="266.90369">v3</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:7.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="408.75"
y="245.21935"
id="text4306"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4308"
x="408.75"
y="245.21935" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:13.31658649px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="492.57816"
y="291.54303"
id="text4310"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4312"
x="492.57816"
y="291.54303">Indices</tspan><tspan
sodipodi:role="line"
x="492.57816"
y="308.18875"
id="tspan4314">{0, 1, 2, 2, 3, 0}</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/images/vs_cpp17.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Some files were not shown because too many files have changed in this diff Show More