Compare commits
4 Commits
b0d28c747e
...
717a8f1869
| Author | SHA1 | Date | |
|---|---|---|---|
| 717a8f1869 | |||
| 893a0e5452 | |||
| 284a7a818a | |||
| 7dbb43f9d1 |
@@ -393,7 +393,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.begin({});
|
commandBuffer.begin({});
|
||||||
|
|
||||||
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eUndefined,
|
vk::ImageLayout::eUndefined,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
@@ -438,7 +438,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.endRendering();
|
commandBuffer.endRendering();
|
||||||
|
|
||||||
// After rendering, transition the swapchain image to PRESENT_SRC
|
// After rendering, transition the swapchain image to PRESENT_SRC
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::ImageLayout::ePresentSrcKHR,
|
vk::ImageLayout::ePresentSrcKHR,
|
||||||
@@ -451,7 +451,7 @@ class HelloTriangleApplication {
|
|||||||
// Finish recording the command buffer
|
// Finish recording the command buffer
|
||||||
commandBuffer.end();
|
commandBuffer.end();
|
||||||
}
|
}
|
||||||
void transitionImageLayout(
|
void transitionSwapChainImageLayout(
|
||||||
uint32_t imageIndex,
|
uint32_t imageIndex,
|
||||||
vk::ImageLayout oldLayout,
|
vk::ImageLayout oldLayout,
|
||||||
vk::ImageLayout newLayout,
|
vk::ImageLayout newLayout,
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.begin({});
|
commandBuffer.begin({});
|
||||||
|
|
||||||
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eUndefined,
|
vk::ImageLayout::eUndefined,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
@@ -491,7 +491,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.endRendering();
|
commandBuffer.endRendering();
|
||||||
|
|
||||||
// After rendering, transition the swapchain image to PRESENT_SRC
|
// After rendering, transition the swapchain image to PRESENT_SRC
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::ImageLayout::ePresentSrcKHR,
|
vk::ImageLayout::ePresentSrcKHR,
|
||||||
@@ -504,7 +504,7 @@ class HelloTriangleApplication {
|
|||||||
// Finish recording the command buffer
|
// Finish recording the command buffer
|
||||||
commandBuffer.end();
|
commandBuffer.end();
|
||||||
}
|
}
|
||||||
void transitionImageLayout(
|
void transitionSwapChainImageLayout(
|
||||||
uint32_t imageIndex,
|
uint32_t imageIndex,
|
||||||
vk::ImageLayout oldLayout,
|
vk::ImageLayout oldLayout,
|
||||||
vk::ImageLayout newLayout,
|
vk::ImageLayout newLayout,
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.begin({});
|
commandBuffer.begin({});
|
||||||
|
|
||||||
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eUndefined,
|
vk::ImageLayout::eUndefined,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
@@ -501,7 +501,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.endRendering();
|
commandBuffer.endRendering();
|
||||||
|
|
||||||
// After rendering, transition the swapchain image to PRESENT_SRC
|
// After rendering, transition the swapchain image to PRESENT_SRC
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::ImageLayout::ePresentSrcKHR,
|
vk::ImageLayout::ePresentSrcKHR,
|
||||||
@@ -514,7 +514,7 @@ class HelloTriangleApplication {
|
|||||||
// Finish recording the command buffer
|
// Finish recording the command buffer
|
||||||
commandBuffer.end();
|
commandBuffer.end();
|
||||||
}
|
}
|
||||||
void transitionImageLayout(
|
void transitionSwapChainImageLayout(
|
||||||
uint32_t imageIndex,
|
uint32_t imageIndex,
|
||||||
vk::ImageLayout oldLayout,
|
vk::ImageLayout oldLayout,
|
||||||
vk::ImageLayout newLayout,
|
vk::ImageLayout newLayout,
|
||||||
|
|||||||
@@ -489,7 +489,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.begin({});
|
commandBuffer.begin({});
|
||||||
|
|
||||||
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eUndefined,
|
vk::ImageLayout::eUndefined,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
@@ -534,7 +534,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.endRendering();
|
commandBuffer.endRendering();
|
||||||
|
|
||||||
// After rendering, transition the swapchain image to PRESENT_SRC
|
// After rendering, transition the swapchain image to PRESENT_SRC
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::ImageLayout::ePresentSrcKHR,
|
vk::ImageLayout::ePresentSrcKHR,
|
||||||
@@ -547,7 +547,7 @@ class HelloTriangleApplication {
|
|||||||
// Finish recording the command buffer
|
// Finish recording the command buffer
|
||||||
commandBuffer.end();
|
commandBuffer.end();
|
||||||
}
|
}
|
||||||
void transitionImageLayout(
|
void transitionSwapChainImageLayout(
|
||||||
uint32_t imageIndex,
|
uint32_t imageIndex,
|
||||||
vk::ImageLayout oldLayout,
|
vk::ImageLayout oldLayout,
|
||||||
vk::ImageLayout newLayout,
|
vk::ImageLayout newLayout,
|
||||||
|
|||||||
@@ -534,7 +534,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.begin({});
|
commandBuffer.begin({});
|
||||||
|
|
||||||
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eUndefined,
|
vk::ImageLayout::eUndefined,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
@@ -579,7 +579,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.endRendering();
|
commandBuffer.endRendering();
|
||||||
|
|
||||||
// After rendering, transition the swapchain image to PRESENT_SRC
|
// After rendering, transition the swapchain image to PRESENT_SRC
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::ImageLayout::ePresentSrcKHR,
|
vk::ImageLayout::ePresentSrcKHR,
|
||||||
@@ -592,7 +592,7 @@ class HelloTriangleApplication {
|
|||||||
// Finish recording the command buffer
|
// Finish recording the command buffer
|
||||||
commandBuffer.end();
|
commandBuffer.end();
|
||||||
}
|
}
|
||||||
void transitionImageLayout(
|
void transitionSwapChainImageLayout(
|
||||||
uint32_t imageIndex,
|
uint32_t imageIndex,
|
||||||
vk::ImageLayout oldLayout,
|
vk::ImageLayout oldLayout,
|
||||||
vk::ImageLayout newLayout,
|
vk::ImageLayout newLayout,
|
||||||
|
|||||||
@@ -558,7 +558,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.begin({});
|
commandBuffer.begin({});
|
||||||
|
|
||||||
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eUndefined,
|
vk::ImageLayout::eUndefined,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
@@ -604,7 +604,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.endRendering();
|
commandBuffer.endRendering();
|
||||||
|
|
||||||
// After rendering, transition the swapchain image to PRESENT_SRC
|
// After rendering, transition the swapchain image to PRESENT_SRC
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::ImageLayout::ePresentSrcKHR,
|
vk::ImageLayout::ePresentSrcKHR,
|
||||||
@@ -627,7 +627,7 @@ class HelloTriangleApplication {
|
|||||||
|
|
||||||
throw std::runtime_error("failed to find suitable memory type!");
|
throw std::runtime_error("failed to find suitable memory type!");
|
||||||
}
|
}
|
||||||
void transitionImageLayout(
|
void transitionSwapChainImageLayout(
|
||||||
uint32_t imageIndex,
|
uint32_t imageIndex,
|
||||||
vk::ImageLayout oldLayout,
|
vk::ImageLayout oldLayout,
|
||||||
vk::ImageLayout newLayout,
|
vk::ImageLayout newLayout,
|
||||||
|
|||||||
@@ -562,7 +562,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.begin({});
|
commandBuffer.begin({});
|
||||||
|
|
||||||
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eUndefined,
|
vk::ImageLayout::eUndefined,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
@@ -608,7 +608,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.endRendering();
|
commandBuffer.endRendering();
|
||||||
|
|
||||||
// After rendering, transition the swapchain image to PRESENT_SRC
|
// After rendering, transition the swapchain image to PRESENT_SRC
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::ImageLayout::ePresentSrcKHR,
|
vk::ImageLayout::ePresentSrcKHR,
|
||||||
@@ -676,7 +676,7 @@ class HelloTriangleApplication {
|
|||||||
|
|
||||||
throw std::runtime_error("failed to find suitable memory type!");
|
throw std::runtime_error("failed to find suitable memory type!");
|
||||||
}
|
}
|
||||||
void transitionImageLayout(
|
void transitionSwapChainImageLayout(
|
||||||
uint32_t imageIndex,
|
uint32_t imageIndex,
|
||||||
vk::ImageLayout oldLayout,
|
vk::ImageLayout oldLayout,
|
||||||
vk::ImageLayout newLayout,
|
vk::ImageLayout newLayout,
|
||||||
|
|||||||
@@ -592,7 +592,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.begin({});
|
commandBuffer.begin({});
|
||||||
|
|
||||||
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eUndefined,
|
vk::ImageLayout::eUndefined,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
@@ -639,7 +639,7 @@ class HelloTriangleApplication {
|
|||||||
commandBuffer.endRendering();
|
commandBuffer.endRendering();
|
||||||
|
|
||||||
// After rendering, transition the swapchain image to PRESENT_SRC
|
// After rendering, transition the swapchain image to PRESENT_SRC
|
||||||
transitionImageLayout(
|
transitionSwapChainImageLayout(
|
||||||
imageIndex,
|
imageIndex,
|
||||||
vk::ImageLayout::eColorAttachmentOptimal,
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::ImageLayout::ePresentSrcKHR,
|
vk::ImageLayout::ePresentSrcKHR,
|
||||||
@@ -707,7 +707,7 @@ class HelloTriangleApplication {
|
|||||||
|
|
||||||
throw std::runtime_error("failed to find suitable memory type!");
|
throw std::runtime_error("failed to find suitable memory type!");
|
||||||
}
|
}
|
||||||
void transitionImageLayout(
|
void transitionSwapChainImageLayout(
|
||||||
uint32_t imageIndex,
|
uint32_t imageIndex,
|
||||||
vk::ImageLayout oldLayout,
|
vk::ImageLayout oldLayout,
|
||||||
vk::ImageLayout newLayout,
|
vk::ImageLayout newLayout,
|
||||||
|
|||||||
921
22_descriptor_layout.cpp
Normal 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
@@ -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
1113
25_sampler.cpp
Normal file
1142
26_texture_mapping.cpp
Normal file
@@ -229,32 +229,32 @@ add_chapter (21_index_buffer
|
|||||||
SHADER 18_shader_vertexbuffer
|
SHADER 18_shader_vertexbuffer
|
||||||
LIBS glm::glm)
|
LIBS glm::glm)
|
||||||
|
|
||||||
# add_chapter (22_descriptor_layout
|
add_chapter (22_descriptor_layout
|
||||||
# SHADER 22_shader_ubo
|
SHADER 22_shader_ubo
|
||||||
# LIBS glm::glm)
|
LIBS glm::glm)
|
||||||
#
|
|
||||||
# add_chapter (23_descriptor_sets
|
add_chapter (23_descriptor_sets
|
||||||
# SHADER 22_shader_ubo
|
SHADER 22_shader_ubo
|
||||||
# LIBS glm::glm)
|
LIBS glm::glm)
|
||||||
#
|
|
||||||
# add_chapter (24_texture_image
|
add_chapter (24_texture_image
|
||||||
# SHADER 22_shader_ubo
|
SHADER 22_shader_ubo
|
||||||
# TEXTURES ../../images/texture.jpg
|
TEXTURES images/texture.jpg
|
||||||
# LIBS glm::glm)
|
LIBS glm::glm)
|
||||||
#
|
|
||||||
# add_chapter (25_sampler
|
add_chapter (25_sampler
|
||||||
# SHADER 22_shader_ubo
|
SHADER 22_shader_ubo
|
||||||
# TEXTURES ../../images/texture.jpg
|
TEXTURES images/texture.jpg
|
||||||
# LIBS glm::glm)
|
LIBS glm::glm)
|
||||||
#
|
|
||||||
# add_chapter (26_texture_mapping
|
add_chapter (26_texture_mapping
|
||||||
# SHADER 26_shader_textures
|
SHADER 26_shader_textures
|
||||||
# TEXTURES ../../images/texture.jpg
|
TEXTURES images/texture.jpg
|
||||||
# LIBS glm::glm)
|
LIBS glm::glm)
|
||||||
#
|
|
||||||
# add_chapter (27_depth_buffering
|
# add_chapter (27_depth_buffering
|
||||||
# SHADER 27_shader_depth
|
# SHADER 27_shader_depth
|
||||||
# TEXTURES ../../images/texture.jpg
|
# TEXTURES images/texture.jpg
|
||||||
# LIBS glm::glm)
|
# LIBS glm::glm)
|
||||||
#
|
#
|
||||||
# add_chapter (28_model_loading
|
# add_chapter (28_model_loading
|
||||||
|
|||||||
BIN
assets/images/38_TASK01_renderdoc_color.gif
Normal file
|
After Width: | Height: | Size: 4.4 MiB |
BIN
assets/images/38_TASK01_renderdoc_events.png
Normal file
|
After Width: | Height: | Size: 1017 KiB |
BIN
assets/images/38_TASK01_renderdoc_launch.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/images/38_TASK02_blas_build.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
assets/images/38_TASK02_blas_structures.png
Normal file
|
After Width: | Height: | Size: 405 KiB |
BIN
assets/images/38_TASK03_tlas_structures.png
Normal file
|
After Width: | Height: | Size: 409 KiB |
BIN
assets/images/38_TASK04_nsight_capture.png
Normal file
|
After Width: | Height: | Size: 4.5 MiB |
BIN
assets/images/38_TASK04_nsight_inspector.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/images/38_TASK04_nsight_launch.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/images/38_TASK04_nsight_main.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
assets/images/38_TASK06_concept_shadows.png
Normal file
|
After Width: | Height: | Size: 819 KiB |
BIN
assets/images/38_TASK06_shadows_static.gif
Normal file
|
After Width: | Height: | Size: 40 MiB |
BIN
assets/images/38_TASK07_shadows_dynamic.gif
Normal file
|
After Width: | Height: | Size: 42 MiB |
BIN
assets/images/38_TASK08_alphacut_after.png
Normal file
|
After Width: | Height: | Size: 655 KiB |
BIN
assets/images/38_TASK08_alphacut_before.png
Normal file
|
After Width: | Height: | Size: 654 KiB |
BIN
assets/images/38_TASK09_instance_custom_index.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/images/38_TASK09_instance_lut.png
Normal file
|
After Width: | Height: | Size: 899 KiB |
BIN
assets/images/38_TASK10_alphacut_shadows.png
Normal file
|
After Width: | Height: | Size: 880 KiB |
BIN
assets/images/38_TASK11_alphacut_reflections.png
Normal file
|
After Width: | Height: | Size: 899 KiB |
BIN
assets/images/38_TASK11_concept_reflections.png
Normal file
|
After Width: | Height: | Size: 812 KiB |
BIN
assets/images/38_bounding_boxes.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/images/aliasing.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/images/anisotropic_filtering.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
assets/images/antialiasing.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
assets/images/bistro.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
assets/images/component_based_architecture_diagram.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/images/compute_shader_particles.png
Normal file
|
After Width: | Height: | Size: 652 KiB |
4
assets/images/compute_space.svg
Normal file
|
After Width: | Height: | Size: 70 KiB |
4
assets/images/compute_ssbo_read_write.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/images/cube_demo.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
assets/images/cube_demo_mac.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
assets/images/cube_demo_nowindow.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
81
assets/images/data_oriented_design_diagram.svg
Normal 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 |
BIN
assets/images/depth_correct.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
assets/images/depth_issues.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
assets/images/drawing_model.png
Normal file
|
After Width: | Height: | Size: 287 KiB |
240
assets/images/extra_square.svg
Normal 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
|
After Width: | Height: | Size: 804 B |
BIN
assets/images/glfw_directory.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
assets/images/highmipmaps.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
assets/images/include_dirs_stb.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/images/include_dirs_tinyobjloader.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/images/indexed_rectangle.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
assets/images/inverted_texture_coordinates.png
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
assets/images/layered_architecture_diagram.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/library_directory.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
112
assets/images/matrix-order-comparison.svg
Normal 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
|
After Width: | Height: | Size: 276 KiB |
BIN
assets/images/mipmaps_comparison.png
Normal file
|
After Width: | Height: | Size: 372 KiB |
BIN
assets/images/mipmaps_example.jpg
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
assets/images/multisampling.png
Normal file
|
After Width: | Height: | Size: 284 KiB |
BIN
assets/images/multisampling_comparison.png
Normal file
|
After Width: | Height: | Size: 302 KiB |
BIN
assets/images/multisampling_comparison2.png
Normal file
|
After Width: | Height: | Size: 336 KiB |
219
assets/images/normalized_device_coordinates.svg
Normal 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 |
BIN
assets/images/rendering_pipeline_flowchart.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
assets/images/sample_shading.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/images/sdk_install_mac.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
assets/images/select_develop_branch.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
assets/images/semaphore_in_use.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
75
assets/images/service_locator_pattern_diagram.svg
Normal 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 |
BIN
assets/images/spinning_quad.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
assets/images/steam_layers_env.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
assets/images/swap_chain_validation_layer.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
assets/images/texcoord_visualization.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/images/texture.jpg
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
assets/images/texture_addressing.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
assets/images/texture_filtering.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
assets/images/texture_on_square.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
assets/images/texture_on_square_colorized.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
assets/images/texture_on_square_repeated.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
assets/images/triangle.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
107
assets/images/triangle_coordinates.svg
Normal 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 |
BIN
assets/images/triangle_coordinates_colors.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/images/triangle_white.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
assets/images/validation_layer_anisotropy.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/images/validation_layer_test.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
243
assets/images/vertex_vs_index.svg
Normal 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 |
BIN
assets/images/viewports_scissors.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
assets/images/vs_all_configs.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
assets/images/vs_application_settings.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
assets/images/vs_build_mode.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/images/vs_cpp17.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/vs_cpp_general.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/images/vs_dependencies.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/images/vs_export_template.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/images/vs_include_dirs.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |