Initial commit for vulkan tutorial
Following along the first 9 chapters of the Vulkan HelloTriangle tutorial.
This commit is contained in:
2
.clangd
Normal file
2
.clangd
Normal file
@@ -0,0 +1,2 @@
|
||||
CompileFlags:
|
||||
Remove: [-fmodules-ts, -fmodule-mapper=*, -fdeps-format=*]
|
||||
57
00_base_code.cpp
Normal file
57
00_base_code.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#if defined(__INTELLISENSE__) || !defined(USE_CPP20_MODULES)
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
#else
|
||||
import vulkan_hpp;
|
||||
#endif
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
94
01_instance_creation.cpp
Normal file
94
01_instance_creation.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#if defined(__INTELLISENSE__) || !defined(USE_CPP20_MODULES)
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#else
|
||||
import vulkan_hpp;
|
||||
#endif
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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 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,
|
||||
.enabledExtensionCount = glfwExtensionCount,
|
||||
.ppEnabledExtensionNames = glfwExtensions,
|
||||
};
|
||||
|
||||
instance = vk::raii::Instance(context, createInfo);
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
125
02_validation_layers.cpp
Normal file
125
02_validation_layers.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#if defined(__INTELLISENSE__) || !defined(USE_CPP20_MODULES)
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#else
|
||||
import vulkan_hpp;
|
||||
#endif
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
const std::vector<char const *> validationLayers = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr bool enableValidationLayers = false;
|
||||
#else
|
||||
constexpr bool enableValidationLayers = true;
|
||||
#endif
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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);
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
185
03_physical_device_selection.cpp
Normal file
185
03_physical_device_selection.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#if defined(__INTELLISENSE__) || !defined(USE_CPP20_MODULES)
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#else
|
||||
import vulkan_hpp;
|
||||
#endif
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
const std::vector<char const *> validationLayers = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr bool enableValidationLayers = false;
|
||||
#else
|
||||
constexpr bool enableValidationLayers = true;
|
||||
#endif
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
pickPhysicalDevice();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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 pickPhysicalDevice() {
|
||||
std::vector<const char*> deviceExtensions = {
|
||||
vk::KHRSwapchainExtensionName,
|
||||
vk::KHRSpirv14ExtensionName,
|
||||
vk::KHRSynchronization2ExtensionName,
|
||||
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");
|
||||
}
|
||||
}
|
||||
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 supports graphics
|
||||
auto graphicsQueueFamilyProperty =
|
||||
std::find_if( queueFamilyProperties.begin(),
|
||||
queueFamilyProperties.end(),
|
||||
[]( vk::QueueFamilyProperties const & qfp ) { return qfp.queueFlags & vk::QueueFlagBits::eGraphics; } );
|
||||
|
||||
return static_cast<uint32_t>( std::distance( queueFamilyProperties.begin(), graphicsQueueFamilyProperty ) );
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
vk::raii::PhysicalDevice physicalDevice = nullptr;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
226
04_logical_device.cpp
Normal file
226
04_logical_device.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "vulkan/vulkan.hpp"
|
||||
#if defined(__INTELLISENSE__) || !defined(USE_CPP20_MODULES)
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#else
|
||||
import vulkan_hpp;
|
||||
#endif
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
const std::vector<char const *> validationLayers = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr bool enableValidationLayers = false;
|
||||
#else
|
||||
constexpr bool enableValidationLayers = true;
|
||||
#endif
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
pickPhysicalDevice();
|
||||
createLogicalDevice();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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 pickPhysicalDevice() {
|
||||
std::vector<const char*> deviceExtensions = {
|
||||
vk::KHRSwapchainExtensionName,
|
||||
vk::KHRSpirv14ExtensionName,
|
||||
vk::KHRSynchronization2ExtensionName,
|
||||
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();
|
||||
uint32_t graphicsIndex = findQueueFamilies(physicalDevice);
|
||||
float queuePriority = 0.5f;
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfo {
|
||||
.queueFamilyIndex = graphicsIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority,
|
||||
};
|
||||
|
||||
// Create a chain of feature structures
|
||||
vk::StructureChain<vk::PhysicalDeviceFeatures2,
|
||||
vk::PhysicalDeviceVulkan13Features,
|
||||
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
|
||||
{}, // vk::PhysicalDeviceFeatures2 (empty for now)
|
||||
{.dynamicRendering = true }, // Enable dynamic rendering from Vulkan 1.3
|
||||
{.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, graphicsIndex, 0);
|
||||
}
|
||||
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 supports graphics
|
||||
auto graphicsQueueFamilyProperty =
|
||||
std::find_if( queueFamilyProperties.begin(),
|
||||
queueFamilyProperties.end(),
|
||||
[]( vk::QueueFamilyProperties const & qfp ) { return qfp.queueFlags & vk::QueueFlagBits::eGraphics; } );
|
||||
|
||||
return static_cast<uint32_t>( std::distance( queueFamilyProperties.begin(), graphicsQueueFamilyProperty ) );
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
vk::raii::PhysicalDevice physicalDevice = nullptr;
|
||||
vk::raii::Device device = nullptr;
|
||||
vk::raii::Queue graphicsQueue = nullptr;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
244
05_window_surface.cpp
Normal file
244
05_window_surface.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#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 <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
const std::vector<char const *> validationLayers = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr bool enableValidationLayers = false;
|
||||
#else
|
||||
constexpr bool enableValidationLayers = true;
|
||||
#endif
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
createSurface();
|
||||
pickPhysicalDevice();
|
||||
createLogicalDevice();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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::KHRSynchronization2ExtensionName,
|
||||
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();
|
||||
uint32_t graphicsIndex = findQueueFamilies(physicalDevice);
|
||||
float queuePriority = 0.5f;
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfo {
|
||||
.queueFamilyIndex = graphicsIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority,
|
||||
};
|
||||
|
||||
// Create a chain of feature structures
|
||||
vk::StructureChain<vk::PhysicalDeviceFeatures2,
|
||||
vk::PhysicalDeviceVulkan13Features,
|
||||
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
|
||||
{}, // vk::PhysicalDeviceFeatures2 (empty for now)
|
||||
{.dynamicRendering = true }, // Enable dynamic rendering from Vulkan 1.3
|
||||
{.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, graphicsIndex, 0);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
vk::raii::PhysicalDevice physicalDevice = nullptr;
|
||||
vk::raii::Device device = nullptr;
|
||||
vk::raii::Queue graphicsQueue = nullptr;
|
||||
vk::raii::SurfaceKHR surface = nullptr;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
313
06_swap_chain_creation.cpp
Normal file
313
06_swap_chain_creation.cpp
Normal file
@@ -0,0 +1,313 @@
|
||||
#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 <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
const std::vector<char const *> validationLayers = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr bool enableValidationLayers = false;
|
||||
#else
|
||||
constexpr bool enableValidationLayers = true;
|
||||
#endif
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
createSurface();
|
||||
pickPhysicalDevice();
|
||||
createLogicalDevice();
|
||||
createSwapChain();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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::KHRSynchronization2ExtensionName,
|
||||
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();
|
||||
uint32_t graphicsIndex = findQueueFamilies(physicalDevice);
|
||||
float queuePriority = 0.5f;
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfo {
|
||||
.queueFamilyIndex = graphicsIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority,
|
||||
};
|
||||
|
||||
// Create a chain of feature structures
|
||||
vk::StructureChain<vk::PhysicalDeviceFeatures2,
|
||||
vk::PhysicalDeviceVulkan13Features,
|
||||
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
|
||||
{}, // vk::PhysicalDeviceFeatures2 (empty for now)
|
||||
{.dynamicRendering = true }, // Enable dynamic rendering from Vulkan 1.3
|
||||
{.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, graphicsIndex, 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();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
vk::raii::PhysicalDevice physicalDevice = nullptr;
|
||||
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;
|
||||
std::vector<vk::Image> swapChainImages;
|
||||
std::vector<vk::raii::ImageView> swapChainImageViews;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
330
07_image_views.cpp
Normal file
330
07_image_views.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
#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 <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
const std::vector<char const *> validationLayers = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr bool enableValidationLayers = false;
|
||||
#else
|
||||
constexpr bool enableValidationLayers = true;
|
||||
#endif
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
createSurface();
|
||||
pickPhysicalDevice();
|
||||
createLogicalDevice();
|
||||
createSwapChain();
|
||||
createImageViews();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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::KHRSynchronization2ExtensionName,
|
||||
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();
|
||||
uint32_t graphicsIndex = findQueueFamilies(physicalDevice);
|
||||
float queuePriority = 0.5f;
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfo {
|
||||
.queueFamilyIndex = graphicsIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority,
|
||||
};
|
||||
|
||||
// Create a chain of feature structures
|
||||
vk::StructureChain<vk::PhysicalDeviceFeatures2,
|
||||
vk::PhysicalDeviceVulkan13Features,
|
||||
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
|
||||
{}, // vk::PhysicalDeviceFeatures2 (empty for now)
|
||||
{.dynamicRendering = true }, // Enable dynamic rendering from Vulkan 1.3
|
||||
{.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, graphicsIndex, 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));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
vk::raii::PhysicalDevice physicalDevice = nullptr;
|
||||
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;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
332
08_graphics_pipeline.cpp
Normal file
332
08_graphics_pipeline.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
#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 <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
const std::vector<char const *> validationLayers = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr bool enableValidationLayers = false;
|
||||
#else
|
||||
constexpr bool enableValidationLayers = true;
|
||||
#endif
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
createSurface();
|
||||
pickPhysicalDevice();
|
||||
createLogicalDevice();
|
||||
createSwapChain();
|
||||
createImageViews();
|
||||
createGraphicsPipeline();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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::KHRSynchronization2ExtensionName,
|
||||
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();
|
||||
uint32_t graphicsIndex = findQueueFamilies(physicalDevice);
|
||||
float queuePriority = 0.5f;
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfo {
|
||||
.queueFamilyIndex = graphicsIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority,
|
||||
};
|
||||
|
||||
// Create a chain of feature structures
|
||||
vk::StructureChain<vk::PhysicalDeviceFeatures2,
|
||||
vk::PhysicalDeviceVulkan13Features,
|
||||
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
|
||||
{}, // vk::PhysicalDeviceFeatures2 (empty for now)
|
||||
{.dynamicRendering = true }, // Enable dynamic rendering from Vulkan 1.3
|
||||
{.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, graphicsIndex, 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 createGraphicsPipeline() {}
|
||||
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;
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
vk::raii::PhysicalDevice physicalDevice = nullptr;
|
||||
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;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
374
09_shader_modules.cpp
Normal file
374
09_shader_modules.cpp
Normal file
@@ -0,0 +1,374 @@
|
||||
#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 <ios>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
constexpr uint32_t WIDTH = 800;
|
||||
constexpr uint32_t HEIGHT = 600;
|
||||
|
||||
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_FALSE);
|
||||
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
|
||||
}
|
||||
void initVulkan() {
|
||||
createInstance();
|
||||
createSurface();
|
||||
pickPhysicalDevice();
|
||||
createLogicalDevice();
|
||||
createSwapChain();
|
||||
createImageViews();
|
||||
createGraphicsPipeline();
|
||||
}
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
void cleanup() {
|
||||
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::KHRSynchronization2ExtensionName,
|
||||
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();
|
||||
uint32_t graphicsIndex = findQueueFamilies(physicalDevice);
|
||||
float queuePriority = 0.5f;
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfo {
|
||||
.queueFamilyIndex = graphicsIndex,
|
||||
.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)
|
||||
{.dynamicRendering = true }, // Enable dynamic rendering 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, graphicsIndex, 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 createGraphicsPipeline() {
|
||||
vk::raii::ShaderModule shaderModule = createShaderModule(readFile("shaders/09_shader_base.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};
|
||||
}
|
||||
[[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;
|
||||
}
|
||||
|
||||
GLFWwindow *window;
|
||||
vk::raii::Context context;
|
||||
vk::raii::Instance instance = nullptr;
|
||||
vk::raii::PhysicalDevice physicalDevice = nullptr;
|
||||
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;
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloTriangleApplication app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
106
CMake/FindKTX.cmake
Normal file
106
CMake/FindKTX.cmake
Normal file
@@ -0,0 +1,106 @@
|
||||
# FindKTX.cmake
|
||||
#
|
||||
# Finds the KTX library
|
||||
#
|
||||
# This will define the following variables
|
||||
#
|
||||
# KTX_FOUND
|
||||
# KTX_INCLUDE_DIRS
|
||||
# KTX_LIBRARIES
|
||||
#
|
||||
# and the following imported targets
|
||||
#
|
||||
# KTX::ktx
|
||||
#
|
||||
|
||||
# Check if we're on Linux - if so, we'll skip the search and directly use FetchContent
|
||||
if(UNIX AND NOT APPLE)
|
||||
# On Linux, we assume KTX is not installed and proceed directly to fetching it
|
||||
set(KTX_FOUND FALSE)
|
||||
else()
|
||||
# On non-Linux platforms, try to find KTX using pkg-config first
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_KTX QUIET ktx libktx ktx2 libktx2)
|
||||
endif()
|
||||
|
||||
# Try to find KTX using standard find_package
|
||||
find_path(KTX_INCLUDE_DIR
|
||||
NAMES ktx.h
|
||||
PATH_SUFFIXES include ktx KTX ktx2 KTX2
|
||||
HINTS
|
||||
${PC_KTX_INCLUDEDIR}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{KTX_DIR}/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${CMAKE_SOURCE_DIR}/external/ktx/include
|
||||
)
|
||||
|
||||
find_library(KTX_LIBRARY
|
||||
NAMES ktx ktx2 libktx libktx2
|
||||
PATH_SUFFIXES lib lib64
|
||||
HINTS
|
||||
${PC_KTX_LIBDIR}
|
||||
/usr/lib
|
||||
/usr/lib64
|
||||
/usr/local/lib
|
||||
/usr/local/lib64
|
||||
$ENV{KTX_DIR}/lib
|
||||
$ENV{VULKAN_SDK}/lib
|
||||
${CMAKE_SOURCE_DIR}/external/ktx/lib
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(KTX
|
||||
REQUIRED_VARS KTX_INCLUDE_DIR KTX_LIBRARY
|
||||
FAIL_MESSAGE "" # Suppress the error message to allow our fallback
|
||||
)
|
||||
|
||||
# Debug output if KTX is not found (only on non-Linux platforms)
|
||||
if(NOT KTX_FOUND)
|
||||
message(STATUS "KTX include directory search paths: ${PC_KTX_INCLUDEDIR}, /usr/include, /usr/local/include, $ENV{KTX_DIR}/include, $ENV{VULKAN_SDK}/include, ${CMAKE_SOURCE_DIR}/external/ktx/include")
|
||||
message(STATUS "KTX library search paths: ${PC_KTX_LIBDIR}, /usr/lib, /usr/lib64, /usr/local/lib, /usr/local/lib64, $ENV{KTX_DIR}/lib, $ENV{VULKAN_SDK}/lib, ${CMAKE_SOURCE_DIR}/external/ktx/lib")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(KTX_FOUND)
|
||||
set(KTX_INCLUDE_DIRS ${KTX_INCLUDE_DIR})
|
||||
set(KTX_LIBRARIES ${KTX_LIBRARY})
|
||||
|
||||
if(NOT TARGET KTX::ktx)
|
||||
add_library(KTX::ktx UNKNOWN IMPORTED)
|
||||
set_target_properties(KTX::ktx PROPERTIES
|
||||
IMPORTED_LOCATION "${KTX_LIBRARIES}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${KTX_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
# If not found, use FetchContent to download and build
|
||||
include(FetchContent)
|
||||
|
||||
# Only show the message on non-Linux platforms
|
||||
if(NOT (UNIX AND NOT APPLE))
|
||||
message(STATUS "KTX not found, fetching from GitHub...")
|
||||
endif()
|
||||
|
||||
FetchContent_Declare(
|
||||
ktx
|
||||
GIT_REPOSITORY https://github.com/KhronosGroup/KTX-Software.git
|
||||
GIT_TAG v4.3.1 # Use a specific tag for stability
|
||||
)
|
||||
|
||||
# Set options to minimize build time and dependencies
|
||||
set(KTX_FEATURE_TOOLS OFF CACHE BOOL "Build KTX tools" FORCE)
|
||||
set(KTX_FEATURE_DOC OFF CACHE BOOL "Build KTX documentation" FORCE)
|
||||
set(KTX_FEATURE_TESTS OFF CACHE BOOL "Build KTX tests" FORCE)
|
||||
|
||||
FetchContent_MakeAvailable(ktx)
|
||||
|
||||
# Create an alias to match the expected target name
|
||||
if(NOT TARGET KTX::ktx)
|
||||
add_library(KTX::ktx ALIAS ktx)
|
||||
endif()
|
||||
|
||||
set(KTX_FOUND TRUE)
|
||||
endif()
|
||||
937
CMake/FindVulkan.cmake
Normal file
937
CMake/FindVulkan.cmake
Normal file
@@ -0,0 +1,937 @@
|
||||
# Updates for iOS Copyright (c) 2024, Holochip Inc.
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindVulkan
|
||||
----------
|
||||
|
||||
.. versionadded:: 3.7
|
||||
|
||||
Find Vulkan, which is a low-overhead, cross-platform 3D graphics
|
||||
and computing API.
|
||||
|
||||
Optional COMPONENTS
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 3.24
|
||||
|
||||
This module respects several optional COMPONENTS.
|
||||
There are corresponding imported targets for each of these.
|
||||
|
||||
``glslc``
|
||||
The SPIR-V compiler.
|
||||
|
||||
``glslangValidator``
|
||||
The ``glslangValidator`` tool.
|
||||
|
||||
``glslang``
|
||||
The SPIR-V generator library.
|
||||
|
||||
``shaderc_combined``
|
||||
The static library for Vulkan shader compilation.
|
||||
|
||||
``SPIRV-Tools``
|
||||
Tools to process SPIR-V modules.
|
||||
|
||||
``MoltenVK``
|
||||
On macOS, an additional component ``MoltenVK`` is available.
|
||||
|
||||
``dxc``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
The DirectX Shader Compiler.
|
||||
|
||||
The ``glslc`` and ``glslangValidator`` components are provided even
|
||||
if not explicitly requested (for backward compatibility).
|
||||
|
||||
IMPORTED Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines :prop_tgt:`IMPORTED` targets if Vulkan has been found:
|
||||
|
||||
``Vulkan::Vulkan``
|
||||
The main Vulkan library.
|
||||
|
||||
``Vulkan::glslc``
|
||||
.. versionadded:: 3.19
|
||||
|
||||
The GLSLC SPIR-V compiler, if it has been found.
|
||||
|
||||
``Vulkan::Headers``
|
||||
.. versionadded:: 3.21
|
||||
|
||||
Provides just Vulkan headers include paths, if found. No library is
|
||||
included in this target. This can be useful for applications that
|
||||
load Vulkan library dynamically.
|
||||
|
||||
``Vulkan::glslangValidator``
|
||||
.. versionadded:: 3.21
|
||||
|
||||
The glslangValidator tool, if found. It is used to compile GLSL and
|
||||
HLSL shaders into SPIR-V.
|
||||
|
||||
``Vulkan::glslang``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Defined if SDK has the Khronos-reference front-end shader parser and SPIR-V
|
||||
generator library (glslang).
|
||||
|
||||
``Vulkan::shaderc_combined``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Defined if SDK has the Google static library for Vulkan shader compilation
|
||||
(shaderc_combined).
|
||||
|
||||
``Vulkan::SPIRV-Tools``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Defined if SDK has the Khronos library to process SPIR-V modules
|
||||
(SPIRV-Tools).
|
||||
|
||||
``Vulkan::MoltenVK``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Defined if SDK has the Khronos library which implement a subset of Vulkan API
|
||||
over Apple Metal graphics framework. (MoltenVK).
|
||||
|
||||
``Vulkan::volk``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
Defined if SDK has the Vulkan meta-loader (volk).
|
||||
|
||||
``Vulkan::dxc_lib``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
Defined if SDK has the DirectX shader compiler library.
|
||||
|
||||
``Vulkan::dxc_exe``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
Defined if SDK has the DirectX shader compiler CLI tool.
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines the following variables:
|
||||
|
||||
``Vulkan_FOUND``
|
||||
set to true if Vulkan was found
|
||||
``Vulkan_INCLUDE_DIRS``
|
||||
include directories for Vulkan
|
||||
``Vulkan_LIBRARIES``
|
||||
link against this library to use Vulkan
|
||||
``Vulkan_VERSION``
|
||||
.. versionadded:: 3.23
|
||||
|
||||
value from ``vulkan/vulkan_core.h``
|
||||
``Vulkan_glslc_FOUND``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
True, if the SDK has the glslc executable.
|
||||
``Vulkan_glslangValidator_FOUND``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
True, if the SDK has the glslangValidator executable.
|
||||
``Vulkan_glslang_FOUND``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
True, if the SDK has the glslang library.
|
||||
``Vulkan_shaderc_combined_FOUND``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
True, if the SDK has the shaderc_combined library.
|
||||
``Vulkan_SPIRV-Tools_FOUND``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
True, if the SDK has the SPIRV-Tools library.
|
||||
``Vulkan_MoltenVK_FOUND``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
True, if the SDK has the MoltenVK library.
|
||||
``Vulkan_volk_FOUND``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
True, if the SDK has the volk library.
|
||||
|
||||
``Vulkan_dxc_lib_FOUND``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
True, if the SDK has the DirectX shader compiler library.
|
||||
|
||||
``Vulkan_dxc_exe_FOUND``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
True, if the SDK has the DirectX shader compiler CLI tool.
|
||||
|
||||
|
||||
The module will also defines these cache variables:
|
||||
|
||||
``Vulkan_INCLUDE_DIR``
|
||||
the Vulkan include directory
|
||||
``Vulkan_LIBRARY``
|
||||
the path to the Vulkan library
|
||||
``Vulkan_GLSLC_EXECUTABLE``
|
||||
the path to the GLSL SPIR-V compiler
|
||||
``Vulkan_GLSLANG_VALIDATOR_EXECUTABLE``
|
||||
the path to the glslangValidator tool
|
||||
``Vulkan_glslang_LIBRARY``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Path to the glslang library.
|
||||
``Vulkan_shaderc_combined_LIBRARY``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Path to the shaderc_combined library.
|
||||
``Vulkan_SPIRV-Tools_LIBRARY``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Path to the SPIRV-Tools library.
|
||||
``Vulkan_MoltenVK_LIBRARY``
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Path to the MoltenVK library.
|
||||
|
||||
``Vulkan_volk_LIBRARY``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
Path to the volk library.
|
||||
|
||||
``Vulkan_dxc_LIBRARY``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
Path to the DirectX shader compiler library.
|
||||
|
||||
``Vulkan_dxc_EXECUTABLE``
|
||||
.. versionadded:: 3.25
|
||||
|
||||
Path to the DirectX shader compiler CLI tool.
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
.. versionadded:: 3.18
|
||||
|
||||
The ``VULKAN_SDK`` environment variable optionally specifies the
|
||||
location of the Vulkan SDK root directory for the given
|
||||
architecture. It is typically set by sourcing the toplevel
|
||||
``setup-env.sh`` script of the Vulkan SDK directory into the shell
|
||||
environment.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(SET CMP0057 NEW)
|
||||
|
||||
# Provide compatibility with a common invalid component request that
|
||||
# was silently ignored prior to CMake 3.24.
|
||||
if("FATAL_ERROR" IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
message(AUTHOR_WARNING
|
||||
"Ignoring unknown component 'FATAL_ERROR'.\n"
|
||||
"The find_package() command documents no such argument."
|
||||
)
|
||||
list(REMOVE_ITEM Vulkan_FIND_COMPONENTS "FATAL_ERROR")
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
get_filename_component(Vulkan_Target_SDK "$ENV{VULKAN_SDK}/.." REALPATH)
|
||||
list(APPEND CMAKE_FRAMEWORK_PATH "${Vulkan_Target_SDK}/iOS/lib")
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
|
||||
endif()
|
||||
|
||||
# For backward compatibility as `FindVulkan` in previous CMake versions allow to retrieve `glslc`
|
||||
# and `glslangValidator` without requesting the corresponding component.
|
||||
if(NOT glslc IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
list(APPEND Vulkan_FIND_COMPONENTS glslc)
|
||||
endif()
|
||||
if(NOT glslangValidator IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
list(APPEND Vulkan_FIND_COMPONENTS glslangValidator)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(_Vulkan_library_name vulkan-1)
|
||||
set(_Vulkan_hint_include_search_paths
|
||||
"$ENV{VULKAN_SDK}/include"
|
||||
)
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_Vulkan_hint_executable_search_paths
|
||||
"$ENV{VULKAN_SDK}/bin"
|
||||
)
|
||||
set(_Vulkan_hint_library_search_paths
|
||||
"$ENV{VULKAN_SDK}/lib"
|
||||
"$ENV{VULKAN_SDK}/bin"
|
||||
)
|
||||
else()
|
||||
set(_Vulkan_hint_executable_search_paths
|
||||
"$ENV{VULKAN_SDK}/bin32"
|
||||
"$ENV{VULKAN_SDK}/bin"
|
||||
)
|
||||
set(_Vulkan_hint_library_search_paths
|
||||
"$ENV{VULKAN_SDK}/lib32"
|
||||
"$ENV{VULKAN_SDK}/bin32"
|
||||
"$ENV{VULKAN_SDK}/lib"
|
||||
"$ENV{VULKAN_SDK}/bin"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
set(_Vulkan_library_name vulkan)
|
||||
set(_Vulkan_hint_include_search_paths
|
||||
"$ENV{VULKAN_SDK}/include"
|
||||
)
|
||||
set(_Vulkan_hint_executable_search_paths
|
||||
"$ENV{VULKAN_SDK}/bin"
|
||||
)
|
||||
set(_Vulkan_hint_library_search_paths
|
||||
"$ENV{VULKAN_SDK}/lib"
|
||||
)
|
||||
endif()
|
||||
if(APPLE AND DEFINED ENV{VULKAN_SDK})
|
||||
list(APPEND _Vulkan_hint_include_search_paths
|
||||
"${Vulkan_Target_SDK}/macOS/include"
|
||||
)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
list(APPEND _Vulkan_hint_library_search_paths
|
||||
"${Vulkan_Target_SDK}/iOS/lib"
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "tvOS")
|
||||
list(APPEND _Vulkan_hint_library_search_paths
|
||||
"${Vulkan_Target_SDK}/tvOS/lib"
|
||||
)
|
||||
else()
|
||||
list(APPEND _Vulkan_hint_library_search_paths
|
||||
"${Vulkan_Target_SDK}}/lib"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_path(Vulkan_INCLUDE_DIR
|
||||
NAMES vulkan/vulkan.h
|
||||
HINTS
|
||||
${_Vulkan_hint_include_search_paths}
|
||||
)
|
||||
message(STATUS "vulkan_include_dir ${Vulkan_INCLUDE_DIR} search paths ${_Vulkan_hint_include_search_paths}")
|
||||
mark_as_advanced(Vulkan_INCLUDE_DIR)
|
||||
|
||||
find_library(Vulkan_LIBRARY
|
||||
NAMES ${_Vulkan_library_name}
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
message(STATUS "vulkan_library ${Vulkan_LIBRARY} search paths ${_Vulkan_hint_library_search_paths}")
|
||||
mark_as_advanced(Vulkan_LIBRARY)
|
||||
|
||||
find_library(Vulkan_Layer_API_DUMP
|
||||
NAMES VkLayer_api_dump
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_Layer_API_DUMP)
|
||||
|
||||
find_library(Vulkan_Layer_SHADER_OBJECT
|
||||
NAMES VkLayer_khronos_shader_object
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(VkLayer_khronos_shader_object)
|
||||
|
||||
find_library(Vulkan_Layer_SYNC2
|
||||
NAMES VkLayer_khronos_synchronization2
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_Layer_SYNC2)
|
||||
|
||||
find_library(Vulkan_Layer_VALIDATION
|
||||
NAMES VkLayer_khronos_validation
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_Layer_VALIDATION)
|
||||
|
||||
if(glslc IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
find_program(Vulkan_GLSLC_EXECUTABLE
|
||||
NAMES glslc
|
||||
HINTS
|
||||
${_Vulkan_hint_executable_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_GLSLC_EXECUTABLE)
|
||||
endif()
|
||||
if(glslangValidator IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
find_program(Vulkan_GLSLANG_VALIDATOR_EXECUTABLE
|
||||
NAMES glslangValidator
|
||||
HINTS
|
||||
${_Vulkan_hint_executable_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_GLSLANG_VALIDATOR_EXECUTABLE)
|
||||
endif()
|
||||
if(glslang IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
find_library(Vulkan_glslang-spirv_LIBRARY
|
||||
NAMES SPIRV
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-spirv_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-spirv_DEBUG_LIBRARY
|
||||
NAMES SPIRVd
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-spirv_DEBUG_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-oglcompiler_LIBRARY
|
||||
NAMES OGLCompiler
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-oglcompiler_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-oglcompiler_DEBUG_LIBRARY
|
||||
NAMES OGLCompilerd
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-oglcompiler_DEBUG_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-osdependent_LIBRARY
|
||||
NAMES OSDependent
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-osdependent_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-osdependent_DEBUG_LIBRARY
|
||||
NAMES OSDependentd
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-osdependent_DEBUG_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-machineindependent_LIBRARY
|
||||
NAMES MachineIndependent
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-machineindependent_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-machineindependent_DEBUG_LIBRARY
|
||||
NAMES MachineIndependentd
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-machineindependent_DEBUG_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-genericcodegen_LIBRARY
|
||||
NAMES GenericCodeGen
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-genericcodegen_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang-genericcodegen_DEBUG_LIBRARY
|
||||
NAMES GenericCodeGend
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang-genericcodegen_DEBUG_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang_LIBRARY
|
||||
NAMES glslang
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang_LIBRARY)
|
||||
|
||||
find_library(Vulkan_glslang_DEBUG_LIBRARY
|
||||
NAMES glslangd
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_glslang_DEBUG_LIBRARY)
|
||||
endif()
|
||||
if(shaderc_combined IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
find_library(Vulkan_shaderc_combined_LIBRARY
|
||||
NAMES shaderc_combined
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths})
|
||||
mark_as_advanced(Vulkan_shaderc_combined_LIBRARY)
|
||||
|
||||
find_library(Vulkan_shaderc_combined_DEBUG_LIBRARY
|
||||
NAMES shaderc_combinedd
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths})
|
||||
mark_as_advanced(Vulkan_shaderc_combined_DEBUG_LIBRARY)
|
||||
endif()
|
||||
if(SPIRV-Tools IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
find_library(Vulkan_SPIRV-Tools_LIBRARY
|
||||
NAMES SPIRV-Tools
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths})
|
||||
mark_as_advanced(Vulkan_SPIRV-Tools_LIBRARY)
|
||||
|
||||
find_library(Vulkan_SPIRV-Tools_DEBUG_LIBRARY
|
||||
NAMES SPIRV-Toolsd
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths})
|
||||
mark_as_advanced(Vulkan_SPIRV-Tools_DEBUG_LIBRARY)
|
||||
endif()
|
||||
if(MoltenVK IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
# CMake has a bug in 3.28 that doesn't handle xcframeworks. Do it by hand for now.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
if(CMAKE_VERSION VERSION_LESS 3.29)
|
||||
set( _Vulkan_hint_library_search_paths ${Vulkan_Target_SDK}/ios/lib/MoltenVK.xcframework/ios-arm64)
|
||||
else ()
|
||||
set( _Vulkan_hint_library_search_paths ${Vulkan_Target_SDK}/ios/lib/)
|
||||
endif ()
|
||||
endif ()
|
||||
find_library(Vulkan_MoltenVK_LIBRARY
|
||||
NAMES MoltenVK
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_MoltenVK_LIBRARY)
|
||||
|
||||
find_path(Vulkan_MoltenVK_INCLUDE_DIR
|
||||
NAMES MoltenVK/mvk_vulkan.h
|
||||
HINTS
|
||||
${_Vulkan_hint_include_search_paths}
|
||||
)
|
||||
mark_as_advanced(Vulkan_MoltenVK_INCLUDE_DIR)
|
||||
endif()
|
||||
if(volk IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
find_library(Vulkan_volk_LIBRARY
|
||||
NAMES volk
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths})
|
||||
mark_as_advanced(Vulkan_Volk_LIBRARY)
|
||||
endif()
|
||||
|
||||
if (dxc IN_LIST Vulkan_FIND_COMPONENTS)
|
||||
find_library(Vulkan_dxc_LIBRARY
|
||||
NAMES dxcompiler
|
||||
HINTS
|
||||
${_Vulkan_hint_library_search_paths})
|
||||
mark_as_advanced(Vulkan_dxc_LIBRARY)
|
||||
|
||||
find_program(Vulkan_dxc_EXECUTABLE
|
||||
NAMES dxc
|
||||
HINTS
|
||||
${_Vulkan_hint_executable_search_paths})
|
||||
mark_as_advanced(Vulkan_dxc_EXECUTABLE)
|
||||
endif()
|
||||
|
||||
if(Vulkan_GLSLC_EXECUTABLE)
|
||||
set(Vulkan_glslc_FOUND TRUE)
|
||||
else()
|
||||
set(Vulkan_glslc_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
if(Vulkan_GLSLANG_VALIDATOR_EXECUTABLE)
|
||||
set(Vulkan_glslangValidator_FOUND TRUE)
|
||||
else()
|
||||
set(Vulkan_glslangValidator_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
if (Vulkan_dxc_EXECUTABLE)
|
||||
set(Vulkan_dxc_exe_FOUND TRUE)
|
||||
else()
|
||||
set(Vulkan_dxc_exe_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
function(_Vulkan_set_library_component_found component)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 _ARG
|
||||
"NO_WARNING"
|
||||
""
|
||||
"DEPENDENT_COMPONENTS")
|
||||
|
||||
set(all_dependent_component_found TRUE)
|
||||
foreach(dependent_component IN LISTS _ARG_DEPENDENT_COMPONENTS)
|
||||
if(NOT Vulkan_${dependent_component}_FOUND)
|
||||
set(all_dependent_component_found FALSE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(all_dependent_component_found AND (Vulkan_${component}_LIBRARY OR Vulkan_${component}_DEBUG_LIBRARY))
|
||||
set(Vulkan_${component}_FOUND TRUE PARENT_SCOPE)
|
||||
|
||||
# For Windows Vulkan SDK, third party tools binaries are provided with different MSVC ABI:
|
||||
# - Release binaries uses a runtime library
|
||||
# - Debug binaries uses a debug runtime library
|
||||
# This lead to incompatibilities in linking for some configuration types due to CMake-default or project-configured selected MSVC ABI.
|
||||
if(WIN32 AND NOT _ARG_NO_WARNING)
|
||||
if(NOT Vulkan_${component}_LIBRARY)
|
||||
message(WARNING
|
||||
"Library ${component} for Release configuration is missing, imported target Vulkan::${component} may not be able to link when targeting this build configuration due to incompatible MSVC ABI.")
|
||||
endif()
|
||||
if(NOT Vulkan_${component}_DEBUG_LIBRARY)
|
||||
message(WARNING
|
||||
"Library ${component} for Debug configuration is missing, imported target Vulkan::${component} may not be able to link when targeting this build configuration due to incompatible MSVC ABI. Consider re-installing the Vulkan SDK and request debug libraries to fix this warning.")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(Vulkan_${component}_FOUND FALSE PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
_Vulkan_set_library_component_found(glslang-spirv NO_WARNING)
|
||||
_Vulkan_set_library_component_found(glslang-oglcompiler NO_WARNING)
|
||||
_Vulkan_set_library_component_found(glslang-osdependent NO_WARNING)
|
||||
_Vulkan_set_library_component_found(glslang-machineindependent NO_WARNING)
|
||||
_Vulkan_set_library_component_found(glslang-genericcodegen NO_WARNING)
|
||||
_Vulkan_set_library_component_found(glslang
|
||||
DEPENDENT_COMPONENTS
|
||||
glslang-spirv
|
||||
glslang-oglcompiler
|
||||
glslang-osdependent
|
||||
glslang-machineindependent
|
||||
glslang-genericcodegen)
|
||||
_Vulkan_set_library_component_found(shaderc_combined)
|
||||
_Vulkan_set_library_component_found(SPIRV-Tools)
|
||||
_Vulkan_set_library_component_found(volk)
|
||||
_Vulkan_set_library_component_found(dxc)
|
||||
|
||||
if(Vulkan_MoltenVK_INCLUDE_DIR AND Vulkan_MoltenVK_LIBRARY)
|
||||
set(Vulkan_MoltenVK_FOUND TRUE)
|
||||
else()
|
||||
set(Vulkan_MoltenVK_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
set(Vulkan_LIBRARIES ${Vulkan_LIBRARY})
|
||||
set(Vulkan_INCLUDE_DIRS ${Vulkan_INCLUDE_DIR})
|
||||
|
||||
# detect version e.g 1.2.189
|
||||
set(Vulkan_VERSION "")
|
||||
if(Vulkan_INCLUDE_DIR)
|
||||
set(VULKAN_CORE_H ${Vulkan_INCLUDE_DIR}/vulkan/vulkan_core.h)
|
||||
if(EXISTS ${VULKAN_CORE_H})
|
||||
file(STRINGS ${VULKAN_CORE_H} VulkanHeaderVersionLine REGEX "^#define VK_HEADER_VERSION ")
|
||||
string(REGEX MATCHALL "[0-9]+" VulkanHeaderVersion "${VulkanHeaderVersionLine}")
|
||||
file(STRINGS ${VULKAN_CORE_H} VulkanHeaderVersionLine2 REGEX "^#define VK_HEADER_VERSION_COMPLETE ")
|
||||
string(REGEX MATCHALL "[0-9]+" VulkanHeaderVersion2 "${VulkanHeaderVersionLine2}")
|
||||
list(LENGTH VulkanHeaderVersion2 _len)
|
||||
# versions >= 1.2.175 have an additional numbers in front of e.g. '0, 1, 2' instead of '1, 2'
|
||||
if(_len EQUAL 3)
|
||||
list(REMOVE_AT VulkanHeaderVersion2 0)
|
||||
endif()
|
||||
list(APPEND VulkanHeaderVersion2 ${VulkanHeaderVersion})
|
||||
list(JOIN VulkanHeaderVersion2 "." Vulkan_VERSION)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(Vulkan_MoltenVK_FOUND)
|
||||
set(Vulkan_MoltenVK_VERSION "")
|
||||
if(Vulkan_MoltenVK_INCLUDE_DIR)
|
||||
set(VK_MVK_MOLTENVK_H ${Vulkan_MoltenVK_INCLUDE_DIR}/MoltenVK/vk_mvk_moltenvk.h)
|
||||
if(EXISTS ${VK_MVK_MOLTENVK_H})
|
||||
file(STRINGS ${VK_MVK_MOLTENVK_H} _Vulkan_MoltenVK_VERSION_MAJOR REGEX "^#define MVK_VERSION_MAJOR ")
|
||||
string(REGEX MATCHALL "[0-9]+" _Vulkan_MoltenVK_VERSION_MAJOR "${_Vulkan_MoltenVK_VERSION_MAJOR}")
|
||||
file(STRINGS ${VK_MVK_MOLTENVK_H} _Vulkan_MoltenVK_VERSION_MINOR REGEX "^#define MVK_VERSION_MINOR ")
|
||||
string(REGEX MATCHALL "[0-9]+" _Vulkan_MoltenVK_VERSION_MINOR "${_Vulkan_MoltenVK_VERSION_MINOR}")
|
||||
file(STRINGS ${VK_MVK_MOLTENVK_H} _Vulkan_MoltenVK_VERSION_PATCH REGEX "^#define MVK_VERSION_PATCH ")
|
||||
string(REGEX MATCHALL "[0-9]+" _Vulkan_MoltenVK_VERSION_PATCH "${_Vulkan_MoltenVK_VERSION_PATCH}")
|
||||
set(Vulkan_MoltenVK_VERSION "${_Vulkan_MoltenVK_VERSION_MAJOR}.${_Vulkan_MoltenVK_VERSION_MINOR}.${_Vulkan_MoltenVK_VERSION_PATCH}")
|
||||
unset(_Vulkan_MoltenVK_VERSION_MAJOR)
|
||||
unset(_Vulkan_MoltenVK_VERSION_MINOR)
|
||||
unset(_Vulkan_MoltenVK_VERSION_PATCH)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Vulkan
|
||||
REQUIRED_VARS
|
||||
Vulkan_LIBRARY
|
||||
Vulkan_INCLUDE_DIR
|
||||
VERSION_VAR
|
||||
Vulkan_VERSION
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
|
||||
if(Vulkan_FOUND AND NOT TARGET Vulkan::Vulkan)
|
||||
add_library(Vulkan::Vulkan UNKNOWN IMPORTED)
|
||||
set_target_properties(Vulkan::Vulkan PROPERTIES
|
||||
IMPORTED_LOCATION "${Vulkan_LIBRARIES}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
endif()
|
||||
|
||||
if(Vulkan_FOUND AND NOT TARGET Vulkan::Headers)
|
||||
add_library(Vulkan::Headers INTERFACE IMPORTED)
|
||||
set_target_properties(Vulkan::Headers PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
endif()
|
||||
|
||||
if(Vulkan_FOUND AND Vulkan_GLSLC_EXECUTABLE AND NOT TARGET Vulkan::glslc)
|
||||
add_executable(Vulkan::glslc IMPORTED)
|
||||
set_property(TARGET Vulkan::glslc PROPERTY IMPORTED_LOCATION "${Vulkan_GLSLC_EXECUTABLE}")
|
||||
endif()
|
||||
|
||||
if(Vulkan_FOUND AND Vulkan_GLSLANG_VALIDATOR_EXECUTABLE AND NOT TARGET Vulkan::glslangValidator)
|
||||
add_executable(Vulkan::glslangValidator IMPORTED)
|
||||
set_property(TARGET Vulkan::glslangValidator PROPERTY IMPORTED_LOCATION "${Vulkan_GLSLANG_VALIDATOR_EXECUTABLE}")
|
||||
endif()
|
||||
|
||||
if(Vulkan_FOUND)
|
||||
if((Vulkan_glslang-spirv_LIBRARY OR Vulkan_glslang-spirv_DEBUG_LIBRARY) AND NOT TARGET Vulkan::glslang-spirv)
|
||||
add_library(Vulkan::glslang-spirv STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::glslang-spirv
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
if(Vulkan_glslang-spirv_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-spirv APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::glslang-spirv
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_glslang-spirv_LIBRARY}")
|
||||
endif()
|
||||
if(Vulkan_glslang-spirv_DEBUG_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-spirv APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Debug)
|
||||
set_property(TARGET Vulkan::glslang-spirv
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_DEBUG "${Vulkan_glslang-spirv_DEBUG_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if((Vulkan_glslang-oglcompiler_LIBRARY OR Vulkan_glslang-oglcompiler_DEBUG_LIBRARY) AND NOT TARGET Vulkan::glslang-oglcompiler)
|
||||
add_library(Vulkan::glslang-oglcompiler STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::glslang-oglcompiler
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
if(Vulkan_glslang-oglcompiler_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-oglcompiler APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::glslang-oglcompiler
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_glslang-oglcompiler_LIBRARY}")
|
||||
endif()
|
||||
if(Vulkan_glslang-oglcompiler_DEBUG_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-oglcompiler APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Debug)
|
||||
set_property(TARGET Vulkan::glslang-oglcompiler
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_DEBUG "${Vulkan_glslang-oglcompiler_DEBUG_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if((Vulkan_glslang-osdependent_LIBRARY OR Vulkan_glslang-osdependent_DEBUG_LIBRARY) AND NOT TARGET Vulkan::glslang-osdependent)
|
||||
add_library(Vulkan::glslang-osdependent STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::glslang-osdependent
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
if(Vulkan_glslang-osdependent_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-osdependent APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::glslang-osdependent
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_glslang-osdependent_LIBRARY}")
|
||||
endif()
|
||||
if(Vulkan_glslang-osdependent_DEBUG_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-osdependent APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Debug)
|
||||
set_property(TARGET Vulkan::glslang-osdependent
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_DEBUG "${Vulkan_glslang-osdependent_DEBUG_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if((Vulkan_glslang-machineindependent_LIBRARY OR Vulkan_glslang-machineindependent_DEBUG_LIBRARY) AND NOT TARGET Vulkan::glslang-machineindependent)
|
||||
add_library(Vulkan::glslang-machineindependent STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::glslang-machineindependent
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
if(Vulkan_glslang-machineindependent_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-machineindependent APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::glslang-machineindependent
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_glslang-machineindependent_LIBRARY}")
|
||||
endif()
|
||||
if(Vulkan_glslang-machineindependent_DEBUG_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-machineindependent APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Debug)
|
||||
set_property(TARGET Vulkan::glslang-machineindependent
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_DEBUG "${Vulkan_glslang-machineindependent_DEBUG_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if((Vulkan_glslang-genericcodegen_LIBRARY OR Vulkan_glslang-genericcodegen_DEBUG_LIBRARY) AND NOT TARGET Vulkan::glslang-genericcodegen)
|
||||
add_library(Vulkan::glslang-genericcodegen STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::glslang-genericcodegen
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
if(Vulkan_glslang-genericcodegen_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-genericcodegen APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::glslang-genericcodegen
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_glslang-genericcodegen_LIBRARY}")
|
||||
endif()
|
||||
if(Vulkan_glslang-genericcodegen_DEBUG_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang-genericcodegen APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Debug)
|
||||
set_property(TARGET Vulkan::glslang-genericcodegen
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_DEBUG "${Vulkan_glslang-genericcodegen_DEBUG_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if((Vulkan_glslang_LIBRARY OR Vulkan_glslang_DEBUG_LIBRARY)
|
||||
AND TARGET Vulkan::glslang-spirv
|
||||
AND TARGET Vulkan::glslang-oglcompiler
|
||||
AND TARGET Vulkan::glslang-osdependent
|
||||
AND TARGET Vulkan::glslang-machineindependent
|
||||
AND TARGET Vulkan::glslang-genericcodegen
|
||||
AND NOT TARGET Vulkan::glslang)
|
||||
add_library(Vulkan::glslang STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::glslang
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
if(Vulkan_glslang_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::glslang
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_glslang_LIBRARY}")
|
||||
endif()
|
||||
if(Vulkan_glslang_DEBUG_LIBRARY)
|
||||
set_property(TARGET Vulkan::glslang APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Debug)
|
||||
set_property(TARGET Vulkan::glslang
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_DEBUG "${Vulkan_glslang_DEBUG_LIBRARY}")
|
||||
endif()
|
||||
target_link_libraries(Vulkan::glslang
|
||||
INTERFACE
|
||||
Vulkan::glslang-spirv
|
||||
Vulkan::glslang-oglcompiler
|
||||
Vulkan::glslang-osdependent
|
||||
Vulkan::glslang-machineindependent
|
||||
Vulkan::glslang-genericcodegen
|
||||
)
|
||||
endif()
|
||||
|
||||
if((Vulkan_shaderc_combined_LIBRARY OR Vulkan_shaderc_combined_DEBUG_LIBRARY) AND NOT TARGET Vulkan::shaderc_combined)
|
||||
add_library(Vulkan::shaderc_combined STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::shaderc_combined
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
if(Vulkan_shaderc_combined_LIBRARY)
|
||||
set_property(TARGET Vulkan::shaderc_combined APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::shaderc_combined
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_shaderc_combined_LIBRARY}")
|
||||
endif()
|
||||
if(Vulkan_shaderc_combined_DEBUG_LIBRARY)
|
||||
set_property(TARGET Vulkan::shaderc_combined APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Debug)
|
||||
set_property(TARGET Vulkan::shaderc_combined
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_DEBUG "${Vulkan_shaderc_combined_DEBUG_LIBRARY}")
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(Vulkan::shaderc_combined
|
||||
INTERFACE
|
||||
Threads::Threads)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if((Vulkan_SPIRV-Tools_LIBRARY OR Vulkan_SPIRV-Tools_DEBUG_LIBRARY) AND NOT TARGET Vulkan::SPIRV-Tools)
|
||||
add_library(Vulkan::SPIRV-Tools STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::SPIRV-Tools
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
if(Vulkan_SPIRV-Tools_LIBRARY)
|
||||
set_property(TARGET Vulkan::SPIRV-Tools APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::SPIRV-Tools
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_SPIRV-Tools_LIBRARY}")
|
||||
endif()
|
||||
if(Vulkan_SPIRV-Tools_DEBUG_LIBRARY)
|
||||
set_property(TARGET Vulkan::SPIRV-Tools APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Debug)
|
||||
set_property(TARGET Vulkan::SPIRV-Tools
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_DEBUG "${Vulkan_SPIRV-Tools_DEBUG_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(Vulkan_volk_LIBRARY AND NOT TARGET Vulkan::volk)
|
||||
add_library(Vulkan::volk STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::volk
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
set_property(TARGET Vulkan::volk APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::volk APPEND
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_volk_LIBRARY}")
|
||||
|
||||
if (NOT WIN32)
|
||||
set_property(TARGET Vulkan::volk APPEND
|
||||
PROPERTY
|
||||
IMPORTED_LINK_INTERFACE_LIBRARIES dl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (Vulkan_dxc_LIBRARY AND NOT TARGET Vulkan::dxc_lib)
|
||||
add_library(Vulkan::dxc_lib STATIC IMPORTED)
|
||||
set_property(TARGET Vulkan::dxc_lib
|
||||
PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
|
||||
set_property(TARGET Vulkan::dxc_lib APPEND
|
||||
PROPERTY
|
||||
IMPORTED_CONFIGURATIONS Release)
|
||||
set_property(TARGET Vulkan::dxc_lib APPEND
|
||||
PROPERTY
|
||||
IMPORTED_LOCATION_RELEASE "${Vulkan_dxc_LIBRARY}")
|
||||
endif()
|
||||
|
||||
if(Vulkan_dxc_EXECUTABLE AND NOT TARGET Vulkan::dxc_exe)
|
||||
add_executable(Vulkan::dxc_exe IMPORTED)
|
||||
set_property(TARGET Vulkan::dxc_exe PROPERTY IMPORTED_LOCATION "${Vulkan_dxc_EXECUTABLE}")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if(Vulkan_MoltenVK_FOUND)
|
||||
if(Vulkan_MoltenVK_LIBRARY AND NOT TARGET Vulkan::MoltenVK)
|
||||
add_library(Vulkan::MoltenVK SHARED IMPORTED)
|
||||
set_target_properties(Vulkan::MoltenVK
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_MoltenVK_INCLUDE_DIR}"
|
||||
IMPORTED_LOCATION "${Vulkan_MoltenVK_LIBRARY}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
unset(_Vulkan_library_name)
|
||||
unset(_Vulkan_hint_include_search_paths)
|
||||
unset(_Vulkan_hint_executable_search_paths)
|
||||
unset(_Vulkan_hint_library_search_paths)
|
||||
|
||||
cmake_policy(POP)
|
||||
426
CMake/FindVulkanHpp.cmake
Normal file
426
CMake/FindVulkanHpp.cmake
Normal file
@@ -0,0 +1,426 @@
|
||||
# FindVulkanHpp.cmake
|
||||
#
|
||||
# Finds or downloads the Vulkan-Hpp headers and Vulkan Profiles headers
|
||||
#
|
||||
# This will define the following variables
|
||||
#
|
||||
# VulkanHpp_FOUND
|
||||
# VulkanHpp_INCLUDE_DIRS
|
||||
#
|
||||
# and the following imported targets
|
||||
#
|
||||
# VulkanHpp::VulkanHpp
|
||||
#
|
||||
|
||||
# Try to find the package using standard find_path
|
||||
find_path(VulkanHpp_INCLUDE_DIR
|
||||
NAMES vulkan/vulkan.hpp
|
||||
PATHS
|
||||
${Vulkan_INCLUDE_DIR}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../include
|
||||
)
|
||||
|
||||
# Also try to find vulkan.cppm
|
||||
find_path(VulkanHpp_CPPM_DIR
|
||||
NAMES vulkan/vulkan.cppm
|
||||
PATHS
|
||||
${Vulkan_INCLUDE_DIR}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../include
|
||||
)
|
||||
|
||||
# Try to find vulkan_profiles.hpp
|
||||
find_path(VulkanProfiles_INCLUDE_DIR
|
||||
NAMES vulkan/vulkan_profiles.hpp
|
||||
PATHS
|
||||
${Vulkan_INCLUDE_DIR}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../include
|
||||
)
|
||||
|
||||
# Function to extract Vulkan version from vulkan_core.h
|
||||
function(extract_vulkan_version VULKAN_CORE_H_PATH OUTPUT_VERSION_TAG)
|
||||
# Extract the version information from vulkan_core.h
|
||||
file(STRINGS ${VULKAN_CORE_H_PATH} VULKAN_VERSION_MAJOR_LINE REGEX "^#define VK_VERSION_MAJOR")
|
||||
file(STRINGS ${VULKAN_CORE_H_PATH} VULKAN_VERSION_MINOR_LINE REGEX "^#define VK_VERSION_MINOR")
|
||||
file(STRINGS ${VULKAN_CORE_H_PATH} VULKAN_HEADER_VERSION_LINE REGEX "^#define VK_HEADER_VERSION")
|
||||
|
||||
set(VERSION_TAG "v1.3.275") # Default fallback
|
||||
|
||||
if(VULKAN_VERSION_MAJOR_LINE AND VULKAN_VERSION_MINOR_LINE AND VULKAN_HEADER_VERSION_LINE)
|
||||
string(REGEX REPLACE "^#define VK_VERSION_MAJOR[ \t]+([0-9]+).*$" "\\1" VULKAN_VERSION_MAJOR "${VULKAN_VERSION_MAJOR_LINE}")
|
||||
string(REGEX REPLACE "^#define VK_VERSION_MINOR[ \t]+([0-9]+).*$" "\\1" VULKAN_VERSION_MINOR "${VULKAN_VERSION_MINOR_LINE}")
|
||||
string(REGEX REPLACE "^#define VK_HEADER_VERSION[ \t]+([0-9]+).*$" "\\1" VULKAN_HEADER_VERSION "${VULKAN_HEADER_VERSION_LINE}")
|
||||
|
||||
# Construct the version tag
|
||||
set(VERSION_TAG "v${VULKAN_VERSION_MAJOR}.${VULKAN_VERSION_MINOR}.${VULKAN_HEADER_VERSION}")
|
||||
else()
|
||||
# Alternative approach: look for VK_HEADER_VERSION_COMPLETE
|
||||
file(STRINGS ${VULKAN_CORE_H_PATH} VULKAN_HEADER_VERSION_COMPLETE_LINE REGEX "^#define VK_HEADER_VERSION_COMPLETE")
|
||||
file(STRINGS ${VULKAN_CORE_H_PATH} VULKAN_HEADER_VERSION_LINE REGEX "^#define VK_HEADER_VERSION")
|
||||
|
||||
if(VULKAN_HEADER_VERSION_COMPLETE_LINE AND VULKAN_HEADER_VERSION_LINE)
|
||||
# Extract the header version
|
||||
string(REGEX REPLACE "^#define VK_HEADER_VERSION[ \t]+([0-9]+).*$" "\\1" VULKAN_HEADER_VERSION "${VULKAN_HEADER_VERSION_LINE}")
|
||||
|
||||
# Check if the complete version line contains the major and minor versions
|
||||
if(VULKAN_HEADER_VERSION_COMPLETE_LINE MATCHES "VK_MAKE_API_VERSION\\(.*,[ \t]*([0-9]+),[ \t]*([0-9]+),[ \t]*VK_HEADER_VERSION\\)")
|
||||
set(VULKAN_VERSION_MAJOR "${CMAKE_MATCH_1}")
|
||||
set(VULKAN_VERSION_MINOR "${CMAKE_MATCH_2}")
|
||||
set(VERSION_TAG "v${VULKAN_VERSION_MAJOR}.${VULKAN_VERSION_MINOR}.${VULKAN_HEADER_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Return the version tag
|
||||
set(${OUTPUT_VERSION_TAG} ${VERSION_TAG} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Determine the Vulkan version to use for Vulkan-Hpp and Vulkan-Profiles
|
||||
set(VULKAN_VERSION_TAG "v1.3.275") # Default version
|
||||
|
||||
# Try to detect the Vulkan version
|
||||
set(VULKAN_CORE_H "")
|
||||
|
||||
# If we're building for Android, try to detect the NDK's Vulkan version
|
||||
if(DEFINED ANDROID_NDK)
|
||||
# Find the vulkan_core.h file in the NDK
|
||||
find_file(VULKAN_CORE_H vulkan_core.h
|
||||
PATHS
|
||||
${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/vulkan
|
||||
${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/vulkan
|
||||
${ANDROID_NDK}/toolchains/llvm/prebuilt/windows-x86_64/sysroot/usr/include/vulkan
|
||||
${ANDROID_NDK}/toolchains/llvm/prebuilt/windows/sysroot/usr/include/vulkan
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
if(VULKAN_CORE_H)
|
||||
extract_vulkan_version(${VULKAN_CORE_H} VULKAN_VERSION_TAG)
|
||||
message(STATUS "Detected NDK Vulkan version: ${VULKAN_VERSION_TAG}")
|
||||
else()
|
||||
message(STATUS "Could not find vulkan_core.h in NDK, using default version: ${VULKAN_VERSION_TAG}")
|
||||
endif()
|
||||
# For desktop builds, try to detect the Vulkan SDK version
|
||||
elseif(DEFINED ENV{VULKAN_SDK})
|
||||
# Find the vulkan_core.h file in the Vulkan SDK
|
||||
find_file(VULKAN_CORE_H vulkan_core.h
|
||||
PATHS
|
||||
$ENV{VULKAN_SDK}/include/vulkan
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
if(VULKAN_CORE_H)
|
||||
extract_vulkan_version(${VULKAN_CORE_H} VULKAN_VERSION_TAG)
|
||||
message(STATUS "Detected Vulkan SDK version: ${VULKAN_VERSION_TAG}")
|
||||
else()
|
||||
message(STATUS "Could not find vulkan_core.h in Vulkan SDK, using default version: ${VULKAN_VERSION_TAG}")
|
||||
endif()
|
||||
# If Vulkan package was already found, try to use its include directory
|
||||
elseif(DEFINED Vulkan_INCLUDE_DIR)
|
||||
# Find the vulkan_core.h file in the Vulkan include directory
|
||||
find_file(VULKAN_CORE_H vulkan_core.h
|
||||
PATHS
|
||||
${Vulkan_INCLUDE_DIR}/vulkan
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
if(VULKAN_CORE_H)
|
||||
extract_vulkan_version(${VULKAN_CORE_H} VULKAN_VERSION_TAG)
|
||||
message(STATUS "Detected Vulkan version from include directory: ${VULKAN_VERSION_TAG}")
|
||||
else()
|
||||
message(STATUS "Could not find vulkan_core.h in Vulkan include directory, using default version: ${VULKAN_VERSION_TAG}")
|
||||
endif()
|
||||
else()
|
||||
# Try to find vulkan_core.h in system paths
|
||||
find_file(VULKAN_CORE_H vulkan_core.h
|
||||
PATHS
|
||||
/usr/include/vulkan
|
||||
/usr/local/include/vulkan
|
||||
)
|
||||
|
||||
if(VULKAN_CORE_H)
|
||||
extract_vulkan_version(${VULKAN_CORE_H} VULKAN_VERSION_TAG)
|
||||
message(STATUS "Detected system Vulkan version: ${VULKAN_VERSION_TAG}")
|
||||
else()
|
||||
message(STATUS "Could not find vulkan_core.h in system paths, using default version: ${VULKAN_VERSION_TAG}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# If the include directory wasn't found, use FetchContent to download and build
|
||||
if(NOT VulkanHpp_INCLUDE_DIR OR NOT VulkanHpp_CPPM_DIR)
|
||||
# If not found, use FetchContent to download
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "Vulkan-Hpp not found, fetching from GitHub with version ${VULKAN_VERSION_TAG}...")
|
||||
FetchContent_Declare(
|
||||
VulkanHpp
|
||||
GIT_REPOSITORY https://github.com/KhronosGroup/Vulkan-Hpp.git
|
||||
GIT_TAG ${VULKAN_VERSION_TAG} # Use the detected or default version
|
||||
)
|
||||
|
||||
# Set policy to suppress the deprecation warning
|
||||
if(POLICY CMP0169)
|
||||
cmake_policy(SET CMP0169 OLD)
|
||||
endif()
|
||||
|
||||
# Make sure FetchContent is available
|
||||
include(FetchContent)
|
||||
|
||||
# Populate the content
|
||||
FetchContent_GetProperties(VulkanHpp SOURCE_DIR VulkanHpp_SOURCE_DIR)
|
||||
if(NOT VulkanHpp_POPULATED)
|
||||
FetchContent_Populate(VulkanHpp)
|
||||
# Get the source directory after populating
|
||||
FetchContent_GetProperties(VulkanHpp SOURCE_DIR VulkanHpp_SOURCE_DIR)
|
||||
endif()
|
||||
|
||||
# Set the include directory to the source directory
|
||||
set(VulkanHpp_INCLUDE_DIR ${VulkanHpp_SOURCE_DIR})
|
||||
message(STATUS "VulkanHpp_SOURCE_DIR: ${VulkanHpp_SOURCE_DIR}")
|
||||
message(STATUS "VulkanHpp_INCLUDE_DIR: ${VulkanHpp_INCLUDE_DIR}")
|
||||
|
||||
# Check if vulkan.cppm exists in the downloaded repository
|
||||
if(EXISTS "${VulkanHpp_SOURCE_DIR}/vulkan/vulkan.cppm")
|
||||
set(VulkanHpp_CPPM_DIR ${VulkanHpp_SOURCE_DIR})
|
||||
else()
|
||||
# If vulkan.cppm doesn't exist, we need to create it
|
||||
set(VulkanHpp_CPPM_DIR ${CMAKE_CURRENT_BINARY_DIR}/VulkanHpp)
|
||||
file(MAKE_DIRECTORY ${VulkanHpp_CPPM_DIR}/vulkan)
|
||||
|
||||
# Create vulkan.cppm file
|
||||
file(WRITE "${VulkanHpp_CPPM_DIR}/vulkan/vulkan.cppm"
|
||||
"// Auto-generated vulkan.cppm file
|
||||
module;
|
||||
#include <vulkan/vulkan.hpp>
|
||||
export module vulkan;
|
||||
export namespace vk {
|
||||
using namespace VULKAN_HPP_NAMESPACE;
|
||||
}
|
||||
")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# If the Vulkan Profiles include directory wasn't found, use FetchContent to download
|
||||
if(NOT VulkanProfiles_INCLUDE_DIR)
|
||||
# If not found, use FetchContent to download
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "Vulkan-Profiles not found, fetching from GitHub main branch...")
|
||||
FetchContent_Declare(
|
||||
VulkanProfiles
|
||||
GIT_REPOSITORY https://github.com/KhronosGroup/Vulkan-Profiles.git
|
||||
GIT_TAG main # Use main branch instead of a specific tag
|
||||
)
|
||||
|
||||
# Set policy to suppress the deprecation warning
|
||||
if(POLICY CMP0169)
|
||||
cmake_policy(SET CMP0169 OLD)
|
||||
endif()
|
||||
|
||||
# Populate the content
|
||||
FetchContent_GetProperties(VulkanProfiles SOURCE_DIR VulkanProfiles_SOURCE_DIR)
|
||||
if(NOT VulkanProfiles_POPULATED)
|
||||
FetchContent_Populate(VulkanProfiles)
|
||||
# Get the source directory after populating
|
||||
FetchContent_GetProperties(VulkanProfiles SOURCE_DIR VulkanProfiles_SOURCE_DIR)
|
||||
endif()
|
||||
|
||||
# Create the include directory structure if it doesn't exist
|
||||
set(VulkanProfiles_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/VulkanProfiles/include)
|
||||
file(MAKE_DIRECTORY ${VulkanProfiles_INCLUDE_DIR}/vulkan)
|
||||
|
||||
# Create a stub vulkan_profiles.hpp file if it doesn't exist
|
||||
if(NOT EXISTS "${VulkanProfiles_INCLUDE_DIR}/vulkan/vulkan_profiles.hpp")
|
||||
file(WRITE "${VulkanProfiles_INCLUDE_DIR}/vulkan/vulkan_profiles.hpp"
|
||||
"// Auto-generated vulkan_profiles.hpp stub file
|
||||
#pragma once
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vp {
|
||||
// Stub implementation for Vulkan Profiles
|
||||
struct ProfileDesc {
|
||||
const char* name;
|
||||
uint32_t specVersion;
|
||||
};
|
||||
|
||||
inline bool GetProfileSupport(VkPhysicalDevice physicalDevice, const ProfileDesc* pProfile, VkBool32* pSupported) {
|
||||
*pSupported = VK_TRUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
")
|
||||
endif()
|
||||
|
||||
message(STATUS "VulkanProfiles_SOURCE_DIR: ${VulkanProfiles_SOURCE_DIR}")
|
||||
message(STATUS "VulkanProfiles_INCLUDE_DIR: ${VulkanProfiles_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
# Set the variables
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(VulkanHpp
|
||||
REQUIRED_VARS VulkanHpp_INCLUDE_DIR
|
||||
FAIL_MESSAGE "Could NOT find VulkanHpp. Install it or set VulkanHpp_INCLUDE_DIR to the directory containing vulkan/vulkan.hpp"
|
||||
)
|
||||
|
||||
# Debug output
|
||||
message(STATUS "VulkanHpp_FOUND: ${VulkanHpp_FOUND}")
|
||||
message(STATUS "VULKANHPP_FOUND: ${VULKANHPP_FOUND}")
|
||||
|
||||
if(VulkanHpp_FOUND)
|
||||
set(VulkanHpp_INCLUDE_DIRS ${VulkanHpp_INCLUDE_DIR})
|
||||
|
||||
# Make sure VulkanHpp_CPPM_DIR is set
|
||||
if(NOT DEFINED VulkanHpp_CPPM_DIR)
|
||||
# Check if vulkan.cppm exists in the include directory
|
||||
if(EXISTS "${VulkanHpp_INCLUDE_DIR}/vulkan/vulkan.cppm")
|
||||
set(VulkanHpp_CPPM_DIR ${VulkanHpp_INCLUDE_DIR})
|
||||
message(STATUS "Found vulkan.cppm in VulkanHpp_INCLUDE_DIR: ${VulkanHpp_CPPM_DIR}")
|
||||
elseif(DEFINED VulkanHpp_SOURCE_DIR AND EXISTS "${VulkanHpp_SOURCE_DIR}/vulkan/vulkan.cppm")
|
||||
set(VulkanHpp_CPPM_DIR ${VulkanHpp_SOURCE_DIR})
|
||||
message(STATUS "Found vulkan.cppm in VulkanHpp_SOURCE_DIR: ${VulkanHpp_CPPM_DIR}")
|
||||
elseif(DEFINED vulkanhpp_SOURCE_DIR AND EXISTS "${vulkanhpp_SOURCE_DIR}/vulkan/vulkan.cppm")
|
||||
set(VulkanHpp_CPPM_DIR ${vulkanhpp_SOURCE_DIR})
|
||||
message(STATUS "Found vulkan.cppm in vulkanhpp_SOURCE_DIR: ${VulkanHpp_CPPM_DIR}")
|
||||
else()
|
||||
# If vulkan.cppm doesn't exist, we need to create it
|
||||
set(VulkanHpp_CPPM_DIR ${CMAKE_CURRENT_BINARY_DIR}/VulkanHpp)
|
||||
file(MAKE_DIRECTORY ${VulkanHpp_CPPM_DIR}/vulkan)
|
||||
message(STATUS "Creating vulkan.cppm in ${VulkanHpp_CPPM_DIR}")
|
||||
|
||||
# Create vulkan.cppm file
|
||||
file(WRITE "${VulkanHpp_CPPM_DIR}/vulkan/vulkan.cppm"
|
||||
"// Auto-generated vulkan.cppm file
|
||||
module;
|
||||
#include <vulkan/vulkan.hpp>
|
||||
export module vulkan;
|
||||
export namespace vk {
|
||||
using namespace VULKAN_HPP_NAMESPACE;
|
||||
}
|
||||
")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Final VulkanHpp_CPPM_DIR: ${VulkanHpp_CPPM_DIR}")
|
||||
|
||||
# Add Vulkan Profiles include directory if found
|
||||
if(VulkanProfiles_INCLUDE_DIR AND EXISTS "${VulkanProfiles_INCLUDE_DIR}/vulkan/vulkan_profiles.hpp")
|
||||
list(APPEND VulkanHpp_INCLUDE_DIRS ${VulkanProfiles_INCLUDE_DIR})
|
||||
message(STATUS "Added Vulkan Profiles include directory: ${VulkanProfiles_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
# Create an imported target
|
||||
if(NOT TARGET VulkanHpp::VulkanHpp)
|
||||
add_library(VulkanHpp::VulkanHpp INTERFACE IMPORTED)
|
||||
set_target_properties(VulkanHpp::VulkanHpp PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${VulkanHpp_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
elseif(DEFINED VulkanHpp_SOURCE_DIR OR DEFINED vulkanhpp_SOURCE_DIR)
|
||||
# If find_package_handle_standard_args failed but we have a VulkanHpp source directory from FetchContent
|
||||
# Create an imported target
|
||||
if(NOT TARGET VulkanHpp::VulkanHpp)
|
||||
add_library(VulkanHpp::VulkanHpp INTERFACE IMPORTED)
|
||||
|
||||
# Determine the source directory
|
||||
if(DEFINED VulkanHpp_SOURCE_DIR)
|
||||
set(_vulkanhpp_source_dir ${VulkanHpp_SOURCE_DIR})
|
||||
elseif(DEFINED vulkanhpp_SOURCE_DIR)
|
||||
set(_vulkanhpp_source_dir ${vulkanhpp_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
message(STATUS "Using fallback VulkanHpp source directory: ${_vulkanhpp_source_dir}")
|
||||
|
||||
set_target_properties(VulkanHpp::VulkanHpp PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${_vulkanhpp_source_dir}"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Set variables to indicate that VulkanHpp was found
|
||||
set(VulkanHpp_FOUND TRUE)
|
||||
set(VULKANHPP_FOUND TRUE)
|
||||
|
||||
# Set include directories
|
||||
if(DEFINED _vulkanhpp_source_dir)
|
||||
set(VulkanHpp_INCLUDE_DIR ${_vulkanhpp_source_dir})
|
||||
elseif(DEFINED VulkanHpp_SOURCE_DIR)
|
||||
set(VulkanHpp_INCLUDE_DIR ${VulkanHpp_SOURCE_DIR})
|
||||
elseif(DEFINED vulkanhpp_SOURCE_DIR)
|
||||
set(VulkanHpp_INCLUDE_DIR ${vulkanhpp_SOURCE_DIR})
|
||||
endif()
|
||||
set(VulkanHpp_INCLUDE_DIRS ${VulkanHpp_INCLUDE_DIR})
|
||||
|
||||
# Add Vulkan Profiles include directory if found
|
||||
if(VulkanProfiles_INCLUDE_DIR AND EXISTS "${VulkanProfiles_INCLUDE_DIR}/vulkan/vulkan_profiles.hpp")
|
||||
list(APPEND VulkanHpp_INCLUDE_DIRS ${VulkanProfiles_INCLUDE_DIR})
|
||||
message(STATUS "Added Vulkan Profiles include directory to fallback: ${VulkanProfiles_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
# Make sure VulkanHpp_CPPM_DIR is set
|
||||
if(NOT DEFINED VulkanHpp_CPPM_DIR)
|
||||
# Check if vulkan.cppm exists in the downloaded repository
|
||||
if(DEFINED VulkanHpp_INCLUDE_DIR AND EXISTS "${VulkanHpp_INCLUDE_DIR}/vulkan/vulkan.cppm")
|
||||
set(VulkanHpp_CPPM_DIR ${VulkanHpp_INCLUDE_DIR})
|
||||
message(STATUS "Found vulkan.cppm in VulkanHpp_INCLUDE_DIR: ${VulkanHpp_CPPM_DIR}")
|
||||
elseif(DEFINED _vulkanhpp_source_dir AND EXISTS "${_vulkanhpp_source_dir}/vulkan/vulkan.cppm")
|
||||
set(VulkanHpp_CPPM_DIR ${_vulkanhpp_source_dir})
|
||||
message(STATUS "Found vulkan.cppm in _vulkanhpp_source_dir: ${VulkanHpp_CPPM_DIR}")
|
||||
elseif(DEFINED VulkanHpp_SOURCE_DIR AND EXISTS "${VulkanHpp_SOURCE_DIR}/vulkan/vulkan.cppm")
|
||||
set(VulkanHpp_CPPM_DIR ${VulkanHpp_SOURCE_DIR})
|
||||
message(STATUS "Found vulkan.cppm in VulkanHpp_SOURCE_DIR: ${VulkanHpp_CPPM_DIR}")
|
||||
elseif(DEFINED vulkanhpp_SOURCE_DIR AND EXISTS "${vulkanhpp_SOURCE_DIR}/vulkan/vulkan.cppm")
|
||||
set(VulkanHpp_CPPM_DIR ${vulkanhpp_SOURCE_DIR})
|
||||
message(STATUS "Found vulkan.cppm in vulkanhpp_SOURCE_DIR: ${VulkanHpp_CPPM_DIR}")
|
||||
else()
|
||||
# If vulkan.cppm doesn't exist, we need to create it
|
||||
set(VulkanHpp_CPPM_DIR ${CMAKE_CURRENT_BINARY_DIR}/VulkanHpp)
|
||||
file(MAKE_DIRECTORY ${VulkanHpp_CPPM_DIR}/vulkan)
|
||||
message(STATUS "Creating vulkan.cppm in ${VulkanHpp_CPPM_DIR}")
|
||||
|
||||
# Create vulkan.cppm file
|
||||
file(WRITE "${VulkanHpp_CPPM_DIR}/vulkan/vulkan.cppm"
|
||||
"// Auto-generated vulkan.cppm file
|
||||
module;
|
||||
#include <vulkan/vulkan.hpp>
|
||||
export module vulkan;
|
||||
export namespace vk {
|
||||
using namespace VULKAN_HPP_NAMESPACE;
|
||||
}
|
||||
")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Final VulkanHpp_CPPM_DIR: ${VulkanHpp_CPPM_DIR}")
|
||||
endif()
|
||||
|
||||
mark_as_advanced(VulkanHpp_INCLUDE_DIR VulkanHpp_CPPM_DIR)
|
||||
133
CMake/Findglm.cmake
Normal file
133
CMake/Findglm.cmake
Normal file
@@ -0,0 +1,133 @@
|
||||
# Findglm.cmake
|
||||
#
|
||||
# Finds the GLM library
|
||||
#
|
||||
# This will define the following variables
|
||||
#
|
||||
# glm_FOUND
|
||||
# glm_INCLUDE_DIRS
|
||||
#
|
||||
# and the following imported targets
|
||||
#
|
||||
# glm::glm
|
||||
#
|
||||
|
||||
# Try to find the package using pkg-config first
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_glm QUIET glm)
|
||||
endif()
|
||||
|
||||
# Find the include directory
|
||||
find_path(glm_INCLUDE_DIR
|
||||
NAMES glm/glm.hpp
|
||||
PATHS
|
||||
${PC_glm_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../include
|
||||
PATH_SUFFIXES glm
|
||||
)
|
||||
|
||||
# If the include directory wasn't found, use FetchContent to download and build
|
||||
if(NOT glm_INCLUDE_DIR)
|
||||
# If not found, use FetchContent to download and build
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "GLM not found, fetching from GitHub...")
|
||||
FetchContent_Declare(
|
||||
glm
|
||||
GIT_REPOSITORY https://github.com/g-truc/glm.git
|
||||
GIT_TAG 0.9.9.8 # Use a specific tag for stability
|
||||
)
|
||||
|
||||
# Define a function to update the CMake minimum required version
|
||||
function(update_glm_cmake_version)
|
||||
# Get the source directory
|
||||
FetchContent_GetProperties(glm SOURCE_DIR glm_SOURCE_DIR)
|
||||
|
||||
# Update the minimum required CMake version
|
||||
file(READ "${glm_SOURCE_DIR}/CMakeLists.txt" GLM_CMAKE_CONTENT)
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.2"
|
||||
"cmake_minimum_required(VERSION 3.5"
|
||||
GLM_CMAKE_CONTENT "${GLM_CMAKE_CONTENT}")
|
||||
file(WRITE "${glm_SOURCE_DIR}/CMakeLists.txt" "${GLM_CMAKE_CONTENT}")
|
||||
endfunction()
|
||||
|
||||
# Set policy to suppress the deprecation warning
|
||||
if(POLICY CMP0169)
|
||||
cmake_policy(SET CMP0169 OLD)
|
||||
endif()
|
||||
|
||||
# First, declare and populate the content
|
||||
FetchContent_GetProperties(glm)
|
||||
if(NOT glm_POPULATED)
|
||||
FetchContent_Populate(glm)
|
||||
# Update the CMake version before making it available
|
||||
update_glm_cmake_version()
|
||||
endif()
|
||||
|
||||
# Now make it available (this will process the CMakeLists.txt)
|
||||
FetchContent_MakeAvailable(glm)
|
||||
|
||||
# Get the include directory from the target
|
||||
if(TARGET glm)
|
||||
get_target_property(glm_INCLUDE_DIR glm INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(NOT glm_INCLUDE_DIR)
|
||||
# If we can't get the include directory from the target, use the source directory
|
||||
set(glm_INCLUDE_DIR ${glm_SOURCE_DIR})
|
||||
endif()
|
||||
else()
|
||||
# GLM might not create a target, so use the source directory
|
||||
set(glm_INCLUDE_DIR ${glm_SOURCE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set the variables
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(glm
|
||||
REQUIRED_VARS glm_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(glm_FOUND)
|
||||
set(glm_INCLUDE_DIRS ${glm_INCLUDE_DIR})
|
||||
|
||||
# Create an imported target
|
||||
if(NOT TARGET glm::glm)
|
||||
add_library(glm::glm INTERFACE IMPORTED)
|
||||
set_target_properties(glm::glm PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${glm_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
elseif(TARGET glm)
|
||||
# If find_package_handle_standard_args failed but we have a glm target from FetchContent
|
||||
# Create an alias for the glm target
|
||||
if(NOT TARGET glm::glm)
|
||||
add_library(glm::glm ALIAS glm)
|
||||
endif()
|
||||
|
||||
# Set variables to indicate that glm was found
|
||||
set(glm_FOUND TRUE)
|
||||
set(GLM_FOUND TRUE)
|
||||
|
||||
# Set include directories
|
||||
get_target_property(glm_INCLUDE_DIR glm INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(glm_INCLUDE_DIR)
|
||||
set(glm_INCLUDE_DIRS ${glm_INCLUDE_DIR})
|
||||
else()
|
||||
# If we can't get the include directory from the target, use the source directory
|
||||
set(glm_INCLUDE_DIR ${glm_SOURCE_DIR})
|
||||
set(glm_INCLUDE_DIRS ${glm_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(glm_INCLUDE_DIR)
|
||||
154
CMake/Findnlohmann_json.cmake
Normal file
154
CMake/Findnlohmann_json.cmake
Normal file
@@ -0,0 +1,154 @@
|
||||
# Findnlohmann_json.cmake
|
||||
#
|
||||
# Finds the nlohmann_json library
|
||||
#
|
||||
# This will define the following variables
|
||||
#
|
||||
# nlohmann_json_FOUND
|
||||
# nlohmann_json_INCLUDE_DIRS
|
||||
#
|
||||
# and the following imported targets
|
||||
#
|
||||
# nlohmann_json::nlohmann_json
|
||||
#
|
||||
|
||||
# Try to find the package using pkg-config first
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_nlohmann_json QUIET nlohmann_json)
|
||||
endif()
|
||||
|
||||
# Find the include directory
|
||||
find_path(nlohmann_json_INCLUDE_DIR
|
||||
NAMES nlohmann/json.hpp json.hpp
|
||||
PATHS
|
||||
${PC_nlohmann_json_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../include
|
||||
PATH_SUFFIXES nlohmann json
|
||||
)
|
||||
|
||||
# If the include directory wasn't found, use FetchContent to download and build
|
||||
if(NOT nlohmann_json_INCLUDE_DIR)
|
||||
# If not found, use FetchContent to download and build
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "nlohmann_json not found, fetching from GitHub...")
|
||||
FetchContent_Declare(
|
||||
nlohmann_json
|
||||
GIT_REPOSITORY https://github.com/nlohmann/json.git
|
||||
GIT_TAG v3.12.0 # Use a specific tag for stability
|
||||
)
|
||||
|
||||
# Set policy to suppress the deprecation warning
|
||||
if(POLICY CMP0169)
|
||||
cmake_policy(SET CMP0169 OLD)
|
||||
endif()
|
||||
|
||||
# Populate the content but don't configure it yet
|
||||
FetchContent_GetProperties(nlohmann_json)
|
||||
if(NOT nlohmann_json_POPULATED)
|
||||
FetchContent_Populate(nlohmann_json)
|
||||
|
||||
if(ANDROID)
|
||||
# Update the minimum required CMake version before including the CMakeLists.txt
|
||||
file(READ "${nlohmann_json_SOURCE_DIR}/CMakeLists.txt" NLOHMANN_JSON_CMAKE_CONTENT)
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.1"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.2"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.3"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.4"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.5"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.6"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.7"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.8"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.9"
|
||||
"cmake_minimum_required(VERSION 3.10"
|
||||
NLOHMANN_JSON_CMAKE_CONTENT "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
file(WRITE "${nlohmann_json_SOURCE_DIR}/CMakeLists.txt" "${NLOHMANN_JSON_CMAKE_CONTENT}")
|
||||
endif()
|
||||
|
||||
# Now add the subdirectory manually
|
||||
add_subdirectory(${nlohmann_json_SOURCE_DIR} ${nlohmann_json_BINARY_DIR})
|
||||
else()
|
||||
# If already populated, just make it available
|
||||
FetchContent_MakeAvailable(nlohmann_json)
|
||||
endif()
|
||||
|
||||
# Get the include directory from the target
|
||||
if(TARGET nlohmann_json)
|
||||
get_target_property(nlohmann_json_INCLUDE_DIR nlohmann_json INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(NOT nlohmann_json_INCLUDE_DIR)
|
||||
# If we can't get the include directory from the target, use the source directory
|
||||
set(nlohmann_json_INCLUDE_DIR ${nlohmann_json_SOURCE_DIR}/include)
|
||||
endif()
|
||||
else()
|
||||
# nlohmann_json might not create a target, so use the source directory
|
||||
set(nlohmann_json_INCLUDE_DIR ${nlohmann_json_SOURCE_DIR}/include)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set the variables
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(nlohmann_json
|
||||
REQUIRED_VARS nlohmann_json_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(nlohmann_json_FOUND)
|
||||
set(nlohmann_json_INCLUDE_DIRS ${nlohmann_json_INCLUDE_DIR})
|
||||
|
||||
# Create an imported target
|
||||
if(NOT TARGET nlohmann_json::nlohmann_json)
|
||||
add_library(nlohmann_json::nlohmann_json INTERFACE IMPORTED)
|
||||
set_target_properties(nlohmann_json::nlohmann_json PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${nlohmann_json_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
elseif(TARGET nlohmann_json)
|
||||
# If find_package_handle_standard_args failed but we have a nlohmann_json target from FetchContent
|
||||
# Create an alias for the nlohmann_json target
|
||||
if(NOT TARGET nlohmann_json::nlohmann_json)
|
||||
add_library(nlohmann_json::nlohmann_json ALIAS nlohmann_json)
|
||||
endif()
|
||||
|
||||
# Set variables to indicate that nlohmann_json was found
|
||||
set(nlohmann_json_FOUND TRUE)
|
||||
set(NLOHMANN_JSON_FOUND TRUE)
|
||||
|
||||
# Set include directories
|
||||
get_target_property(nlohmann_json_INCLUDE_DIR nlohmann_json INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(nlohmann_json_INCLUDE_DIR)
|
||||
set(nlohmann_json_INCLUDE_DIRS ${nlohmann_json_INCLUDE_DIR})
|
||||
else()
|
||||
# If we can't get the include directory from the target, use the source directory
|
||||
set(nlohmann_json_INCLUDE_DIR ${nlohmann_json_SOURCE_DIR}/include)
|
||||
set(nlohmann_json_INCLUDE_DIRS ${nlohmann_json_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(nlohmann_json_INCLUDE_DIR)
|
||||
86
CMake/Findstb.cmake
Normal file
86
CMake/Findstb.cmake
Normal file
@@ -0,0 +1,86 @@
|
||||
# Findstb.cmake
|
||||
#
|
||||
# Finds the stb library (specifically stb_image.h)
|
||||
#
|
||||
# This will define the following variables
|
||||
#
|
||||
# stb_FOUND
|
||||
# stb_INCLUDE_DIRS
|
||||
#
|
||||
# and the following imported targets
|
||||
#
|
||||
# stb::stb
|
||||
#
|
||||
|
||||
# Try to find the package using pkg-config first
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_stb QUIET stb)
|
||||
endif()
|
||||
|
||||
# Find the include directory
|
||||
find_path(stb_INCLUDE_DIR
|
||||
NAMES stb_image.h
|
||||
PATHS
|
||||
${PC_stb_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../include
|
||||
PATH_SUFFIXES stb
|
||||
)
|
||||
|
||||
# If the include directory wasn't found, use FetchContent to download and build
|
||||
if(NOT stb_INCLUDE_DIR)
|
||||
# If not found, use FetchContent to download and build
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "stb_image.h not found, fetching from GitHub...")
|
||||
FetchContent_Declare(
|
||||
stb
|
||||
GIT_REPOSITORY https://github.com/nothings/stb.git
|
||||
GIT_TAG master # stb doesn't use version tags, so we use master
|
||||
)
|
||||
|
||||
# Set policy to suppress the deprecation warning
|
||||
if(POLICY CMP0169)
|
||||
cmake_policy(SET CMP0169 OLD)
|
||||
endif()
|
||||
|
||||
# Populate the content
|
||||
FetchContent_GetProperties(stb)
|
||||
if(NOT stb_POPULATED)
|
||||
FetchContent_Populate(stb)
|
||||
endif()
|
||||
|
||||
# stb is a header-only library with no CMakeLists.txt, so we just need to set the include directory
|
||||
set(stb_INCLUDE_DIR ${stb_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Set the variables
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(stb
|
||||
REQUIRED_VARS stb_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(stb_FOUND)
|
||||
set(stb_INCLUDE_DIRS ${stb_INCLUDE_DIR})
|
||||
|
||||
# Create an imported target
|
||||
if(NOT TARGET stb::stb)
|
||||
add_library(stb::stb INTERFACE IMPORTED)
|
||||
set_target_properties(stb::stb PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${stb_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(stb_INCLUDE_DIR)
|
||||
162
CMake/Findtinygltf.cmake
Normal file
162
CMake/Findtinygltf.cmake
Normal file
@@ -0,0 +1,162 @@
|
||||
# Findtinygltf.cmake
|
||||
#
|
||||
# Finds the tinygltf library
|
||||
#
|
||||
# This will define the following variables
|
||||
#
|
||||
# tinygltf_FOUND
|
||||
# tinygltf_INCLUDE_DIRS
|
||||
#
|
||||
# and the following imported targets
|
||||
#
|
||||
# tinygltf::tinygltf
|
||||
#
|
||||
|
||||
# First, try to find nlohmann_json
|
||||
find_package(nlohmann_json QUIET)
|
||||
if(NOT nlohmann_json_FOUND)
|
||||
include(FetchContent)
|
||||
message(STATUS "nlohmann_json not found, fetching v3.12.0 from GitHub...")
|
||||
FetchContent_Declare(
|
||||
nlohmann_json
|
||||
GIT_REPOSITORY https://github.com/nlohmann/json.git
|
||||
GIT_TAG v3.12.0 # Use a specific tag for stability
|
||||
)
|
||||
FetchContent_MakeAvailable(nlohmann_json)
|
||||
endif()
|
||||
|
||||
# Try to find tinygltf using standard find_package
|
||||
find_path(tinygltf_INCLUDE_DIR
|
||||
NAMES tiny_gltf.h
|
||||
PATHS
|
||||
${PC_tinygltf_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../include
|
||||
PATH_SUFFIXES tinygltf include
|
||||
)
|
||||
|
||||
# If not found, use FetchContent to download and build
|
||||
if(NOT tinygltf_INCLUDE_DIR)
|
||||
# If not found, use FetchContent to download and build
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "tinygltf not found, fetching from GitHub...")
|
||||
FetchContent_Declare(
|
||||
tinygltf
|
||||
GIT_REPOSITORY https://github.com/syoyo/tinygltf.git
|
||||
GIT_TAG v2.8.18 # Use a specific tag for stability
|
||||
)
|
||||
|
||||
# Set policy to suppress the deprecation warning
|
||||
if(POLICY CMP0169)
|
||||
cmake_policy(SET CMP0169 OLD)
|
||||
endif()
|
||||
|
||||
# Populate the content but don't configure it yet
|
||||
FetchContent_GetProperties(tinygltf)
|
||||
if(NOT tinygltf_POPULATED)
|
||||
FetchContent_Populate(tinygltf)
|
||||
|
||||
# Update the minimum required CMake version to avoid deprecation warning
|
||||
file(READ "${tinygltf_SOURCE_DIR}/CMakeLists.txt" TINYGLTF_CMAKE_CONTENT)
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.6)"
|
||||
"cmake_minimum_required(VERSION 3.10)"
|
||||
TINYGLTF_CMAKE_CONTENT "${TINYGLTF_CMAKE_CONTENT}")
|
||||
file(WRITE "${tinygltf_SOURCE_DIR}/CMakeLists.txt" "${TINYGLTF_CMAKE_CONTENT}")
|
||||
|
||||
# Create a symbolic link to make nlohmann/json.hpp available
|
||||
if(EXISTS "${tinygltf_SOURCE_DIR}/json.hpp")
|
||||
file(MAKE_DIRECTORY "${tinygltf_SOURCE_DIR}/nlohmann")
|
||||
file(CREATE_LINK "${tinygltf_SOURCE_DIR}/json.hpp" "${tinygltf_SOURCE_DIR}/nlohmann/json.hpp" SYMBOLIC)
|
||||
endif()
|
||||
|
||||
# Set tinygltf to header-only mode
|
||||
set(TINYGLTF_HEADER_ONLY ON CACHE BOOL "Use header only version" FORCE)
|
||||
set(TINYGLTF_INSTALL OFF CACHE BOOL "Do not install tinygltf" FORCE)
|
||||
|
||||
# Add the subdirectory after modifying the CMakeLists.txt
|
||||
add_subdirectory(${tinygltf_SOURCE_DIR} ${tinygltf_BINARY_DIR})
|
||||
else()
|
||||
# If already populated, just make it available
|
||||
FetchContent_MakeAvailable(tinygltf)
|
||||
endif()
|
||||
|
||||
# Get the include directory from the target
|
||||
get_target_property(tinygltf_INCLUDE_DIR tinygltf INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(NOT tinygltf_INCLUDE_DIR)
|
||||
# If we can't get the include directory from the target, use the source directory
|
||||
FetchContent_GetProperties(tinygltf SOURCE_DIR tinygltf_SOURCE_DIR)
|
||||
set(tinygltf_INCLUDE_DIR ${tinygltf_SOURCE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(tinygltf
|
||||
REQUIRED_VARS tinygltf_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(tinygltf_FOUND)
|
||||
set(tinygltf_INCLUDE_DIRS ${tinygltf_INCLUDE_DIR})
|
||||
|
||||
if(NOT TARGET tinygltf::tinygltf)
|
||||
add_library(tinygltf::tinygltf INTERFACE IMPORTED)
|
||||
set_target_properties(tinygltf::tinygltf PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${tinygltf_INCLUDE_DIRS}"
|
||||
INTERFACE_COMPILE_DEFINITIONS "TINYGLTF_IMPLEMENTATION;TINYGLTF_NO_EXTERNAL_IMAGE;TINYGLTF_NO_STB_IMAGE;TINYGLTF_NO_STB_IMAGE_WRITE"
|
||||
)
|
||||
if(TARGET nlohmann_json::nlohmann_json)
|
||||
target_link_libraries(tinygltf::tinygltf INTERFACE nlohmann_json::nlohmann_json)
|
||||
endif()
|
||||
endif()
|
||||
elseif(TARGET tinygltf)
|
||||
# If find_package_handle_standard_args failed but we have a tinygltf target from FetchContent
|
||||
# Create an alias for the tinygltf target
|
||||
if(NOT TARGET tinygltf::tinygltf)
|
||||
add_library(tinygltf_wrapper INTERFACE)
|
||||
target_link_libraries(tinygltf_wrapper INTERFACE tinygltf)
|
||||
target_compile_definitions(tinygltf_wrapper INTERFACE
|
||||
TINYGLTF_IMPLEMENTATION
|
||||
TINYGLTF_NO_EXTERNAL_IMAGE
|
||||
TINYGLTF_NO_STB_IMAGE
|
||||
TINYGLTF_NO_STB_IMAGE_WRITE
|
||||
)
|
||||
if(TARGET nlohmann_json::nlohmann_json)
|
||||
target_link_libraries(tinygltf_wrapper INTERFACE nlohmann_json::nlohmann_json)
|
||||
endif()
|
||||
add_library(tinygltf::tinygltf ALIAS tinygltf_wrapper)
|
||||
endif()
|
||||
|
||||
# Set variables to indicate that tinygltf was found
|
||||
set(tinygltf_FOUND TRUE)
|
||||
set(TINYGLTF_FOUND TRUE)
|
||||
|
||||
# Set include directories
|
||||
get_target_property(tinygltf_INCLUDE_DIR tinygltf INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(tinygltf_INCLUDE_DIR)
|
||||
set(tinygltf_INCLUDE_DIRS ${tinygltf_INCLUDE_DIR})
|
||||
else()
|
||||
# If we can't get the include directory from the target, use the source directory
|
||||
FetchContent_GetProperties(tinygltf SOURCE_DIR tinygltf_SOURCE_DIR)
|
||||
set(tinygltf_INCLUDE_DIR ${tinygltf_SOURCE_DIR})
|
||||
set(tinygltf_INCLUDE_DIRS ${tinygltf_INCLUDE_DIR})
|
||||
|
||||
# Explicitly set the include directory on the target
|
||||
if(TARGET tinygltf)
|
||||
set_target_properties(tinygltf PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${tinygltf_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(tinygltf_INCLUDE_DIR)
|
||||
160
CMake/Findtinyobjloader.cmake
Normal file
160
CMake/Findtinyobjloader.cmake
Normal file
@@ -0,0 +1,160 @@
|
||||
# Findtinyobjloader.cmake
|
||||
# Find the tinyobjloader library
|
||||
#
|
||||
# This module defines the following variables:
|
||||
# tinyobjloader_FOUND - True if tinyobjloader was found
|
||||
# tinyobjloader_INCLUDE_DIRS - Include directories for tinyobjloader
|
||||
# tinyobjloader_LIBRARIES - Libraries to link against tinyobjloader
|
||||
#
|
||||
# It also defines the following targets:
|
||||
# tinyobjloader::tinyobjloader
|
||||
|
||||
# Try to find the package using pkg-config first
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_tinyobjloader QUIET tinyobjloader)
|
||||
endif()
|
||||
|
||||
# Find the include directory
|
||||
find_path(tinyobjloader_INCLUDE_DIR
|
||||
NAMES tiny_obj_loader.h
|
||||
PATHS
|
||||
${PC_tinyobjloader_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
$ENV{VULKAN_SDK}/include
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../include
|
||||
PATH_SUFFIXES tinyobjloader tiny_obj_loader
|
||||
)
|
||||
|
||||
# Find the library
|
||||
find_library(tinyobjloader_LIBRARY
|
||||
NAMES tinyobjloader
|
||||
PATHS
|
||||
${PC_tinyobjloader_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
$ENV{VULKAN_SDK}/lib
|
||||
${ANDROID_NDK}/sources/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../attachments/lib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../external
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../lib
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
|
||||
# If the include directory wasn't found, use FetchContent to download and build
|
||||
if(NOT tinyobjloader_INCLUDE_DIR)
|
||||
# If not found, use FetchContent to download and build
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "tinyobjloader not found, fetching from GitHub...")
|
||||
FetchContent_Declare(
|
||||
tinyobjloader
|
||||
GIT_REPOSITORY https://github.com/tinyobjloader/tinyobjloader.git
|
||||
GIT_TAG v2.0.0rc10 # Use a specific tag for stability
|
||||
)
|
||||
|
||||
# Set options before making tinyobjloader available
|
||||
set(TINYOBJLOADER_BUILD_TEST_LOADER OFF CACHE BOOL "Do not build test loader" FORCE)
|
||||
set(TINYOBJLOADER_BUILD_OBJ_STICHER OFF CACHE BOOL "Do not build obj sticher" FORCE)
|
||||
set(TINYOBJLOADER_INSTALL OFF CACHE BOOL "Do not install tinyobjloader" FORCE)
|
||||
|
||||
# Update CMake policy to suppress the deprecation warning
|
||||
if(POLICY CMP0169)
|
||||
cmake_policy(SET CMP0169 OLD)
|
||||
endif()
|
||||
|
||||
# Populate the content but don't configure it yet
|
||||
FetchContent_GetProperties(tinyobjloader)
|
||||
if(NOT tinyobjloader_POPULATED)
|
||||
FetchContent_Populate(tinyobjloader)
|
||||
|
||||
# Update the minimum required CMake version before including the CMakeLists.txt
|
||||
file(READ "${tinyobjloader_SOURCE_DIR}/CMakeLists.txt" TINYOBJLOADER_CMAKE_CONTENT)
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.2)"
|
||||
"cmake_minimum_required(VERSION 3.10)"
|
||||
TINYOBJLOADER_CMAKE_CONTENT "${TINYOBJLOADER_CMAKE_CONTENT}")
|
||||
string(REPLACE "cmake_minimum_required(VERSION 3.5)"
|
||||
"cmake_minimum_required(VERSION 3.10)"
|
||||
TINYOBJLOADER_CMAKE_CONTENT "${TINYOBJLOADER_CMAKE_CONTENT}")
|
||||
file(WRITE "${tinyobjloader_SOURCE_DIR}/CMakeLists.txt" "${TINYOBJLOADER_CMAKE_CONTENT}")
|
||||
|
||||
# Now add the subdirectory manually
|
||||
add_subdirectory(${tinyobjloader_SOURCE_DIR} ${tinyobjloader_BINARY_DIR})
|
||||
else()
|
||||
# If already populated, just make it available
|
||||
FetchContent_MakeAvailable(tinyobjloader)
|
||||
endif()
|
||||
|
||||
# Get the include directory from the target
|
||||
get_target_property(tinyobjloader_INCLUDE_DIR tinyobjloader INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(NOT tinyobjloader_INCLUDE_DIR)
|
||||
# If we can't get the include directory from the target, use the source directory
|
||||
set(tinyobjloader_INCLUDE_DIR ${tinyobjloader_SOURCE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set the variables
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(tinyobjloader
|
||||
REQUIRED_VARS tinyobjloader_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(tinyobjloader_FOUND)
|
||||
set(tinyobjloader_INCLUDE_DIRS ${tinyobjloader_INCLUDE_DIR})
|
||||
|
||||
if(tinyobjloader_LIBRARY)
|
||||
set(tinyobjloader_LIBRARIES ${tinyobjloader_LIBRARY})
|
||||
else()
|
||||
# tinyobjloader is a header-only library, so no library is needed
|
||||
set(tinyobjloader_LIBRARIES "")
|
||||
endif()
|
||||
|
||||
# Create an imported target
|
||||
if(NOT TARGET tinyobjloader::tinyobjloader)
|
||||
add_library(tinyobjloader::tinyobjloader INTERFACE IMPORTED)
|
||||
set_target_properties(tinyobjloader::tinyobjloader PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${tinyobjloader_INCLUDE_DIRS}"
|
||||
)
|
||||
if(tinyobjloader_LIBRARIES)
|
||||
set_target_properties(tinyobjloader::tinyobjloader PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "${tinyobjloader_LIBRARIES}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
elseif(TARGET tinyobjloader)
|
||||
# If find_package_handle_standard_args failed but we have a tinyobjloader target from FetchContent
|
||||
# Create an alias for the tinyobjloader target
|
||||
if(NOT TARGET tinyobjloader::tinyobjloader)
|
||||
add_library(tinyobjloader::tinyobjloader ALIAS tinyobjloader)
|
||||
endif()
|
||||
|
||||
# Set variables to indicate that tinyobjloader was found
|
||||
set(tinyobjloader_FOUND TRUE)
|
||||
set(TINYOBJLOADER_FOUND TRUE)
|
||||
|
||||
# Set include directories
|
||||
get_target_property(tinyobjloader_INCLUDE_DIR tinyobjloader INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(tinyobjloader_INCLUDE_DIR)
|
||||
set(tinyobjloader_INCLUDE_DIRS ${tinyobjloader_INCLUDE_DIR})
|
||||
else()
|
||||
# If we can't get the include directory from the target, use the source directory
|
||||
set(tinyobjloader_INCLUDE_DIR ${tinyobjloader_SOURCE_DIR})
|
||||
set(tinyobjloader_INCLUDE_DIRS ${tinyobjloader_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(tinyobjloader_INCLUDE_DIR tinyobjloader_LIBRARY)
|
||||
324
CMakeLists.txt
Normal file
324
CMakeLists.txt
Normal file
@@ -0,0 +1,324 @@
|
||||
cmake_minimum_required (VERSION 3.29)
|
||||
|
||||
project (VulkanTutorial)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
|
||||
|
||||
# Add option to enable/disable C++ 20 module
|
||||
option(ENABLE_CPP20_MODULE "Enable C++ 20 module support for Vulkan" OFF)
|
||||
|
||||
# Enable C++ module dependency scanning only if C++ 20 module is enabled
|
||||
if(ENABLE_CPP20_MODULE)
|
||||
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
|
||||
endif()
|
||||
|
||||
find_package (glfw3 REQUIRED)
|
||||
find_package (glm REQUIRED)
|
||||
find_package (Vulkan REQUIRED)
|
||||
find_package (tinyobjloader REQUIRED)
|
||||
find_package (tinygltf REQUIRED)
|
||||
find_package (KTX REQUIRED)
|
||||
|
||||
# set up Vulkan C++ module only if enabled
|
||||
if(ENABLE_CPP20_MODULE)
|
||||
add_library(VulkanCppModule)
|
||||
add_library(Vulkan::cppm ALIAS VulkanCppModule)
|
||||
|
||||
target_compile_definitions(VulkanCppModule
|
||||
PUBLIC VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 VULKAN_HPP_NO_STRUCT_CONSTRUCTORS=1
|
||||
)
|
||||
target_include_directories(VulkanCppModule
|
||||
PUBLIC
|
||||
"${Vulkan_INCLUDE_DIR}"
|
||||
)
|
||||
target_link_libraries(VulkanCppModule
|
||||
PUBLIC
|
||||
Vulkan::Vulkan
|
||||
)
|
||||
|
||||
set_target_properties(VulkanCppModule PROPERTIES CXX_STANDARD 20)
|
||||
|
||||
# Add MSVC-specific compiler options for proper C++ module support
|
||||
if(MSVC)
|
||||
target_compile_options(VulkanCppModule PRIVATE
|
||||
/std:c++latest # Use latest C++ standard for better module support
|
||||
/permissive- # Standards conformance mode
|
||||
/Zc:__cplusplus # Enable correct __cplusplus macro
|
||||
/EHsc # Enable C++ exception handling
|
||||
/Zc:preprocessor # Use conforming preprocessor
|
||||
/translateInclude # Automatically translate #include to import for standard library
|
||||
)
|
||||
endif()
|
||||
|
||||
target_sources(VulkanCppModule
|
||||
PUBLIC
|
||||
FILE_SET cxx_modules TYPE CXX_MODULES
|
||||
BASE_DIRS
|
||||
"${Vulkan_INCLUDE_DIR}"
|
||||
FILES
|
||||
"${Vulkan_INCLUDE_DIR}/vulkan/vulkan.cppm"
|
||||
)
|
||||
|
||||
|
||||
# Add the vulkan.cppm file directly as a source file
|
||||
target_sources(VulkanCppModule
|
||||
PRIVATE
|
||||
"${Vulkan_INCLUDE_DIR}/vulkan/vulkan.cppm"
|
||||
)
|
||||
else()
|
||||
# Create a dummy interface library when C++ 20 module is disabled
|
||||
add_library(VulkanCppModule INTERFACE)
|
||||
add_library(Vulkan::cppm ALIAS VulkanCppModule)
|
||||
target_link_libraries(VulkanCppModule INTERFACE Vulkan::Vulkan)
|
||||
target_compile_definitions(VulkanCppModule
|
||||
INTERFACE VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 VULKAN_HPP_NO_STRUCT_CONSTRUCTORS=1
|
||||
)
|
||||
endif()
|
||||
|
||||
find_package(stb REQUIRED)
|
||||
set(STB_INCLUDEDIR ${stb_INCLUDE_DIRS})
|
||||
|
||||
add_executable (glslang::validator IMPORTED)
|
||||
find_program (GLSLANG_VALIDATOR "glslangValidator" HINTS $ENV{VULKAN_SDK}/bin REQUIRED)
|
||||
set_property (TARGET glslang::validator PROPERTY IMPORTED_LOCATION "${GLSLANG_VALIDATOR}")
|
||||
find_program(SLANGC_EXECUTABLE slangc HINTS $ENV{VULKAN_SDK}/bin REQUIRED)
|
||||
|
||||
function (add_shaders_target TARGET)
|
||||
cmake_parse_arguments ("SHADER" "" "CHAPTER_NAME" "SOURCES" ${ARGN})
|
||||
set (SHADERS_DIR shaders)
|
||||
add_custom_command (
|
||||
OUTPUT ${SHADERS_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${SHADERS_DIR}
|
||||
)
|
||||
add_custom_command (
|
||||
OUTPUT ${SHADERS_DIR}/frag.spv ${SHADERS_DIR}/vert.spv
|
||||
COMMAND glslang::validator
|
||||
ARGS --target-env vulkan1.0 ${SHADER_SOURCES} --quiet
|
||||
WORKING_DIRECTORY ${SHADERS_DIR}
|
||||
DEPENDS ${SHADERS_DIR} ${SHADER_SOURCES}
|
||||
COMMENT "Compiling Shaders"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target (${TARGET} DEPENDS ${SHADERS_DIR}/frag.spv ${SHADERS_DIR}/vert.spv)
|
||||
endfunction ()
|
||||
|
||||
function (add_slang_shader_target TARGET)
|
||||
cmake_parse_arguments ("SHADER" "" "CHAPTER_NAME" "SOURCES" ${ARGN})
|
||||
set (SHADERS_DIR ${CHAPTER_NAME}/shaders)
|
||||
file(GLOB HAS_COMPUTE shaders/${CHAPTER_SHADER}.comp)
|
||||
set (ENTRY_POINTS -entry vertMain -entry fragMain)
|
||||
if(HAS_COMPUTE)
|
||||
list(APPEND ENTRY_POINTS -entry compMain)
|
||||
endif()
|
||||
add_custom_command (
|
||||
OUTPUT ${SHADERS_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${SHADERS_DIR}
|
||||
)
|
||||
add_custom_command (
|
||||
OUTPUT ${SHADERS_DIR}/${CHAPTER_SHADER}.spv
|
||||
COMMAND ${SLANGC_EXECUTABLE} ${SHADER_SOURCES} -target spirv -profile spirv_1_4+spvRayQueryKHR -emit-spirv-directly -fvk-use-entrypoint-name ${ENTRY_POINTS} -o ${CHAPTER_SHADER}.spv
|
||||
WORKING_DIRECTORY ${SHADERS_DIR}
|
||||
DEPENDS ${SHADERS_DIR} ${SHADER_SOURCES}
|
||||
COMMENT "Compiling Slang Shaders"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target (${TARGET} DEPENDS ${SHADERS_DIR}/${CHAPTER_SHADER}.spv)
|
||||
endfunction()
|
||||
|
||||
function (add_chapter CHAPTER_NAME)
|
||||
cmake_parse_arguments (CHAPTER "" "SHADER" "LIBS;TEXTURES;MODELS" ${ARGN})
|
||||
add_executable (${CHAPTER_NAME} ${CHAPTER_NAME}.cpp)
|
||||
set_target_properties (${CHAPTER_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CHAPTER_NAME})
|
||||
set_target_properties (${CHAPTER_NAME} PROPERTIES CXX_STANDARD 20)
|
||||
target_link_libraries (${CHAPTER_NAME} Vulkan::cppm glfw)
|
||||
target_include_directories (${CHAPTER_NAME} PRIVATE ${STB_INCLUDEDIR})
|
||||
|
||||
# Add compile definition if C++ 20 module is enabled
|
||||
if(ENABLE_CPP20_MODULE)
|
||||
target_compile_definitions(${CHAPTER_NAME} PRIVATE USE_CPP20_MODULES=1)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(${CMAKE_GENERATOR} MATCHES "Visual Studio.*")
|
||||
set_target_properties(${CHAPTER_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/${CHAPTER_NAME}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (DEFINED CHAPTER_SHADER)
|
||||
set (CHAPTER_SHADER_TARGET ${CHAPTER_NAME}_shader)
|
||||
file (GLOB SHADER_SOURCES shaders/${CHAPTER_SHADER}.frag shaders/${CHAPTER_SHADER}.vert shaders/${CHAPTER_SHADER}.comp)
|
||||
if(SHADER_SOURCES)
|
||||
add_shaders_target (${CHAPTER_SHADER_TARGET} CHAPTER_NAME ${CHAPTER_NAME} SOURCES ${SHADER_SOURCES})
|
||||
add_dependencies (${CHAPTER_NAME} ${CHAPTER_SHADER_TARGET})
|
||||
endif()
|
||||
|
||||
set (CHAPTER_SHADER_SLANG_TARGET ${CHAPTER_NAME}_slang_shader)
|
||||
file (GLOB SHADER_SLANG_SOURCES shaders/${CHAPTER_SHADER}.slang)
|
||||
if(SHADER_SLANG_SOURCES)
|
||||
add_slang_shader_target( ${CHAPTER_SHADER_SLANG_TARGET} CHAPTER_NAME ${CHAPTER_NAME} SOURCES ${SHADER_SLANG_SOURCES})
|
||||
add_dependencies(${CHAPTER_NAME} ${CHAPTER_SHADER_SLANG_TARGET})
|
||||
endif()
|
||||
endif ()
|
||||
if (DEFINED CHAPTER_LIBS)
|
||||
target_link_libraries (${CHAPTER_NAME} ${CHAPTER_LIBS})
|
||||
endif ()
|
||||
if (DEFINED CHAPTER_MODELS)
|
||||
list(TRANSFORM CHAPTER_MODELS PREPEND "${CMAKE_SOURCE_DIR}/assets/")
|
||||
file (COPY ${CHAPTER_MODELS} DESTINATION ${CMAKE_BINARY_DIR}/${CHAPTER_NAME}/models)
|
||||
endif ()
|
||||
if (DEFINED CHAPTER_TEXTURES)
|
||||
list(TRANSFORM CHAPTER_TEXTURES PREPEND "${CMAKE_SOURCE_DIR}/assets/")
|
||||
file (COPY ${CHAPTER_TEXTURES} DESTINATION ${CMAKE_BINARY_DIR}/${CHAPTER_NAME}/textures)
|
||||
endif ()
|
||||
endfunction ()
|
||||
|
||||
add_chapter (00_base_code)
|
||||
|
||||
add_chapter (01_instance_creation)
|
||||
|
||||
add_chapter (02_validation_layers)
|
||||
|
||||
add_chapter (03_physical_device_selection)
|
||||
|
||||
add_chapter (04_logical_device)
|
||||
|
||||
add_chapter (05_window_surface)
|
||||
|
||||
add_chapter (06_swap_chain_creation)
|
||||
|
||||
add_chapter (07_image_views)
|
||||
|
||||
add_chapter (08_graphics_pipeline)
|
||||
|
||||
add_chapter (09_shader_modules
|
||||
SHADER 09_shader_base)
|
||||
|
||||
# add_chapter (10_fixed_functions
|
||||
# SHADER 09_shader_base)
|
||||
#
|
||||
# add_chapter (12_graphics_pipeline_complete
|
||||
# SHADER 09_shader_base)
|
||||
#
|
||||
# add_chapter (14_command_buffers
|
||||
# SHADER 09_shader_base)
|
||||
#
|
||||
# add_chapter (15_hello_triangle
|
||||
# SHADER 09_shader_base)
|
||||
#
|
||||
# add_chapter (16_frames_in_flight
|
||||
# SHADER 09_shader_base)
|
||||
#
|
||||
# add_chapter (17_swap_chain_recreation
|
||||
# SHADER 09_shader_base)
|
||||
#
|
||||
# add_chapter (18_vertex_input
|
||||
# SHADER 18_shader_vertexbuffer
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (19_vertex_buffer
|
||||
# SHADER 18_shader_vertexbuffer
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (20_staging_buffer
|
||||
# SHADER 18_shader_vertexbuffer
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (21_index_buffer
|
||||
# SHADER 18_shader_vertexbuffer
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (22_descriptor_layout
|
||||
# SHADER 22_shader_ubo
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (23_descriptor_sets
|
||||
# SHADER 22_shader_ubo
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (24_texture_image
|
||||
# SHADER 22_shader_ubo
|
||||
# TEXTURES ../../images/texture.jpg
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (25_sampler
|
||||
# SHADER 22_shader_ubo
|
||||
# TEXTURES ../../images/texture.jpg
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (26_texture_mapping
|
||||
# SHADER 26_shader_textures
|
||||
# TEXTURES ../../images/texture.jpg
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (27_depth_buffering
|
||||
# SHADER 27_shader_depth
|
||||
# TEXTURES ../../images/texture.jpg
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (28_model_loading
|
||||
# SHADER 27_shader_depth
|
||||
# MODELS viking_room.obj
|
||||
# TEXTURES viking_room.png
|
||||
# LIBS glm::glm tinyobjloader::tinyobjloader)
|
||||
#
|
||||
# add_chapter (29_mipmapping
|
||||
# SHADER 27_shader_depth
|
||||
# MODELS viking_room.obj
|
||||
# TEXTURES viking_room.png
|
||||
# LIBS glm::glm tinyobjloader::tinyobjloader)
|
||||
#
|
||||
# add_chapter (30_multisampling
|
||||
# SHADER 27_shader_depth
|
||||
# MODELS viking_room.obj
|
||||
# TEXTURES viking_room.png
|
||||
# LIBS glm::glm tinyobjloader::tinyobjloader)
|
||||
#
|
||||
# add_chapter (31_compute_shader
|
||||
# SHADER 31_shader_compute
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (32_ecosystem_utilities
|
||||
# SHADER 27_shader_depth
|
||||
# MODELS viking_room.obj
|
||||
# TEXTURES viking_room.png
|
||||
# LIBS glm::glm tinyobjloader::tinyobjloader)
|
||||
#
|
||||
# add_chapter (33_vulkan_profiles
|
||||
# SHADER 27_shader_depth
|
||||
# MODELS viking_room.obj
|
||||
# TEXTURES viking_room.png
|
||||
# LIBS glm::glm tinyobjloader::tinyobjloader)
|
||||
#
|
||||
# add_chapter (34_android
|
||||
# SHADER 27_shader_depth
|
||||
# MODELS viking_room.obj
|
||||
# TEXTURES viking_room.png
|
||||
# LIBS glm::glm tinyobjloader::tinyobjloader)
|
||||
#
|
||||
# add_chapter (35_gltf_ktx
|
||||
# SHADER 27_shader_depth
|
||||
# MODELS viking_room.glb
|
||||
# TEXTURES viking_room.ktx2
|
||||
# LIBS glm::glm tinygltf::tinygltf KTX::ktx)
|
||||
#
|
||||
# add_chapter (36_multiple_objects
|
||||
# SHADER 27_shader_depth
|
||||
# MODELS viking_room.glb
|
||||
# TEXTURES viking_room.ktx2
|
||||
# LIBS glm::glm tinygltf::tinygltf KTX::ktx)
|
||||
#
|
||||
# add_chapter (37_multithreading
|
||||
# SHADER 37_shader_compute
|
||||
# LIBS glm::glm)
|
||||
#
|
||||
# add_chapter (38_ray_tracing
|
||||
# SHADER 38_ray_tracing
|
||||
# MODELS plant_on_table.obj
|
||||
# MODELS plant_on_table.mtl
|
||||
# TEXTURES plant_on_table_textures/nettle_plant_diff_4k.png
|
||||
# TEXTURES plant_on_table_textures/potted_plant_02_pot_diff_1k.png
|
||||
# TEXTURES plant_on_table_textures/wooden_picnic_table_bottom_diff_1k.png
|
||||
# TEXTURES plant_on_table_textures/wooden_picnic_table_top_diff_1k.png
|
||||
# LIBS glm::glm tinyobjloader::tinyobjloader)
|
||||
6
build
Executable file
6
build
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
CMAKE_BUILD_DIR=cmake-build
|
||||
|
||||
cmake -S . -B $CMAKE_BUILD_DIR -G Ninja
|
||||
cmake --build $CMAKE_BUILD_DIR
|
||||
31
incremental_patch.sh
Executable file
31
incremental_patch.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check if both a starting file and patch are provided
|
||||
if [ $# != 2 ]; then
|
||||
echo "usage: <first_file.cpp> <patch.txt>"
|
||||
echo "specified patch will be applied to first_file.cpp and every code file larger than it (from later chapters)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Iterate over code files in order of increasing size
|
||||
# i.e. in order of chapters (every chapter adds code)
|
||||
apply_patch=false
|
||||
|
||||
for f in `ls -Sr *.cpp`
|
||||
do
|
||||
# Apply patch on every code file including and after initial one
|
||||
if [ $f = $1 ] || [ $apply_patch = true ]; then
|
||||
apply_patch=true
|
||||
|
||||
patch -f $f < $2 | grep -q "FAILED" > /dev/null
|
||||
if [ $? = 0 ]; then
|
||||
echo "failed to apply patch to $f"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f *.orig
|
||||
fi
|
||||
done
|
||||
|
||||
echo "patch successfully applied to all files"
|
||||
exit 0
|
||||
5
run
Executable file
5
run
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
TARGET="$1"
|
||||
cd cmake-build/$TARGET
|
||||
./$TARGET
|
||||
29
shaders/09_shader_base.slang
Normal file
29
shaders/09_shader_base.slang
Normal file
@@ -0,0 +1,29 @@
|
||||
static float2 positions[3] = float2[](
|
||||
float2( 0.0, -0.5),
|
||||
float2( 0.5, 0.5),
|
||||
float2(-0.5, 0.5),
|
||||
);
|
||||
|
||||
static float3 colors[3] = float3[](
|
||||
float3(1.0, 0.0, 0.0),
|
||||
float3(0.0, 1.0, 0.0),
|
||||
float3(0.0, 0.0, 1.0),
|
||||
);
|
||||
|
||||
struct VertexOutput {
|
||||
float3 color;
|
||||
float4 sv_position : SV_Position;
|
||||
};
|
||||
|
||||
[shader("vertex")]
|
||||
VertexOutput vertMain(uint vid : SV_VertexID) {
|
||||
VertexOutput output;
|
||||
output.color = colors[vid];
|
||||
output.sv_position = float4(positions[vid], 0.0, 1.0);
|
||||
return output;
|
||||
}
|
||||
|
||||
[shader("fragment")]
|
||||
float4 fragMain(VertexOutput inVert) : SV_Target {
|
||||
return float4(inVert.color, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user