hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/src/platform/vulkan
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/vulkan')
-rw-r--r--src/platform/vulkan/vulkan_common.h5
-rw-r--r--src/platform/vulkan/vulkan_cubemap.cpp19
-rw-r--r--src/platform/vulkan/vulkan_device.cpp36
-rw-r--r--src/platform/vulkan/vulkan_device.h2
-rw-r--r--src/platform/vulkan/vulkan_resources.cpp3
-rw-r--r--src/platform/vulkan/vulkan_swapchain.cpp64
6 files changed, 103 insertions, 26 deletions
diff --git a/src/platform/vulkan/vulkan_common.h b/src/platform/vulkan/vulkan_common.h
index 4f93fdc..2c778c1 100644
--- a/src/platform/vulkan/vulkan_common.h
+++ b/src/platform/vulkan/vulkan_common.h
@@ -171,6 +171,7 @@ namespace Donut::RHI
auto bind_pipeline(Pipeline* p) -> void override
{
m_pipe = static_cast<VkPipelineR*>(p);
+ if (!m_pipe || m_pipe->m_pipeline == VK_NULL_HANDLE) { m_pipe = nullptr; return; }
vkCmdBindPipeline(m_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipe->m_pipeline);
}
auto set_viewport(int x, int y, int w, int h, bool flip_y) -> void override
@@ -200,9 +201,9 @@ namespace Donut::RHI
auto bind_index_buffer(Buffer* ib) -> void override
{ vkCmdBindIndexBuffer(m_cmd, static_cast<VkBufferR*>(ib)->m_buf, 0, VK_INDEX_TYPE_UINT32); }
auto draw(uint32_t vertex_count) -> void override
- { flush_descriptors(); vkCmdDraw(m_cmd, vertex_count, 1, 0, 0); }
+ { if (!m_pipe) return; flush_descriptors(); vkCmdDraw(m_cmd, vertex_count, 1, 0, 0); }
auto draw_indexed(uint32_t index_count) -> void override
- { flush_descriptors(); vkCmdDrawIndexed(m_cmd, index_count, 1, 0, 0, 0); }
+ { if (!m_pipe) return; flush_descriptors(); vkCmdDrawIndexed(m_cmd, index_count, 1, 0, 0, 0); }
auto flush_descriptors() -> void
{
diff --git a/src/platform/vulkan/vulkan_cubemap.cpp b/src/platform/vulkan/vulkan_cubemap.cpp
index 3ffc496..f687bcc 100644
--- a/src/platform/vulkan/vulkan_cubemap.cpp
+++ b/src/platform/vulkan/vulkan_cubemap.cpp
@@ -2,6 +2,7 @@
#include "stb_image.h"
#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/packing.hpp>
namespace Donut::RHI
{
@@ -36,10 +37,16 @@ namespace Donut::RHI
csm.addressModeU = csm.addressModeV = csm.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
vkCreateSampler(m_device, &csm, nullptr, &tex->m_sampler);
+ VkShaderModule vmod = VK_NULL_HANDLE, fmod = VK_NULL_HANDLE;
+ const bool shaders_ok = create_shader_module("assets/shaders/generated/equirect_to_cubemap.vertexMain.spv", vmod)
+ && create_shader_module("assets/shaders/generated/equirect_to_cubemap.fragmentMain.spv", fmod);
int w = 0, h = 0, ch = 0;
- float* pixels = stbi_loadf(path.c_str(), &w, &h, &ch, 4);
+ stbi_set_flip_vertically_on_load(true);
+ float* pixels = shaders_ok ? stbi_loadf(path.c_str(), &w, &h, &ch, 4) : nullptr;
if (!pixels)
{
+ if (vmod) vkDestroyShaderModule(m_device, vmod, nullptr);
+ if (fmod) vkDestroyShaderModule(m_device, fmod, nullptr);
DONUT_WARN("Vulkan RHI: HDRI '{}' could not be loaded; using a dark background", path);
VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
cbai.commandPool = m_command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
@@ -65,13 +72,13 @@ namespace Donut::RHI
}
const VkFormat eq_fmt = VK_FORMAT_R16G16B16A16_SFLOAT;
- size_t texel_count = (size_t)w * h * 4;
+ size_t texel_count = (size_t)w * (size_t)h * 4;
VkDeviceSize eq_size = (VkDeviceSize)texel_count * sizeof(uint16_t);
VkBuffer eq_staging; VkDeviceMemory eq_staging_mem;
create_buffer_raw(eq_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, host_vis, eq_staging, eq_staging_mem);
void* mp = nullptr; vkMapMemory(m_device, eq_staging_mem, 0, eq_size, 0, &mp);
uint16_t* dst = (uint16_t*)mp;
- for (size_t i = 0; i < texel_count; ++i) { __fp16 hf = (__fp16)pixels[i]; std::memcpy(&dst[i], &hf, sizeof(uint16_t)); }
+ for (size_t i = 0; i < texel_count; ++i) dst[i] = glm::packHalf1x16(pixels[i]);
vkUnmapMemory(m_device, eq_staging_mem);
stbi_image_free(pixels);
@@ -141,9 +148,6 @@ namespace Donut::RHI
VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dpci.maxSets = 6; dpci.poolSizeCount = 2; dpci.pPoolSizes = psizes;
vkCreateDescriptorPool(m_device, &dpci, nullptr, &pool);
- VkShaderModule vmod, fmod;
- create_shader_module("assets/shaders/generated/equirect_to_cubemap.vertexMain.spv", vmod);
- create_shader_module("assets/shaders/generated/equirect_to_cubemap.fragmentMain.spv", fmod);
VkPipelineLayout playout;
VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; plci.setLayoutCount = 1; plci.pSetLayouts = &set_layout;
vkCreatePipelineLayout(m_device, &plci, nullptr, &playout);
@@ -181,7 +185,6 @@ namespace Donut::RHI
vkMapMemory(m_device, cube_vb_mem, 0, sizeof(cube_verts), 0, &mp); std::memcpy(mp, cube_verts, sizeof(cube_verts)); vkUnmapMemory(m_device, cube_vb_mem);
glm::mat4 proj = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
- proj[1][1] *= -1.0f;
glm::mat4 views[6] = {
glm::lookAt(glm::vec3(0), glm::vec3( 1, 0, 0), glm::vec3(0, -1, 0)),
glm::lookAt(glm::vec3(0), glm::vec3(-1, 0, 0), glm::vec3(0, -1, 0)),
@@ -194,7 +197,7 @@ namespace Donut::RHI
for (uint32_t i = 0; i < 6; ++i)
{
create_buffer_raw(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, ubo[i], ubo_mem[i]);
- glm::mat4 mats[2] = { glm::transpose(proj), glm::transpose(views[i]) };
+ glm::mat4 mats[2] = { proj, views[i] };
vkMapMemory(m_device, ubo_mem[i], 0, 128, 0, &mp); std::memcpy(mp, mats, 128); vkUnmapMemory(m_device, ubo_mem[i]);
VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &set_layout;
vkAllocateDescriptorSets(m_device, &dsai, &sets[i]);
diff --git a/src/platform/vulkan/vulkan_device.cpp b/src/platform/vulkan/vulkan_device.cpp
index b4c2630..f5d7662 100644
--- a/src/platform/vulkan/vulkan_device.cpp
+++ b/src/platform/vulkan/vulkan_device.cpp
@@ -1,5 +1,15 @@
#include "vulkan_common.h"
+#include <algorithm>
+#include <filesystem>
+
+#if defined(_WIN32)
+ #ifndef NOMINMAX
+ #define NOMINMAX
+ #endif
+ #include <windows.h>
+#endif
+
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_vulkan.h>
@@ -44,7 +54,14 @@ namespace Donut::RHI
auto VulkanDevice::create_shader_module(const std::string& path, VkShaderModule& out) const -> bool
{
auto spv = load_spirv(path);
- if (spv.empty()) { DONUT_ERROR("Vulkan RHI: failed to load SPIR-V {}", path); return false; }
+ if (spv.empty())
+ {
+ std::error_code ec;
+ const auto cwd = std::filesystem::current_path(ec).u8string();
+ DONUT_ERROR("Vulkan RHI: failed to load SPIR-V {} (cwd {}; shaders live in assets/shaders/generated, see tools/compile-shaders.sh)",
+ path, std::string(cwd.begin(), cwd.end()));
+ return false;
+ }
VkShaderModuleCreateInfo ci{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
ci.codeSize = spv.size() * 4; ci.pCode = spv.data();
return vkCreateShaderModule(m_device, &ci, nullptr, &out) == VK_SUCCESS;
@@ -184,6 +201,10 @@ namespace Donut::RHI
void* mp = nullptr; vkMapMemory(m_device, mem, 0, sz, 0, &mp);
std::memcpy(out.data(), mp, sz);
vkUnmapMemory(m_device, mem);
+ const size_t row = (size_t)w * 4;
+ for (uint32_t y = 0; y < h / 2; ++y)
+ std::swap_ranges(out.begin() + (std::ptrdiff_t)(y * row), out.begin() + (std::ptrdiff_t)((y + 1) * row),
+ out.begin() + (std::ptrdiff_t)((h - 1 - y) * row));
vkFreeCommandBuffers(m_device, m_command_pool, 1, &cmd);
vkDestroyBuffer(m_device, buf, nullptr); vkFreeMemory(m_device, mem, nullptr);
}
@@ -221,6 +242,10 @@ namespace Donut::RHI
void* mp = nullptr; vkMapMemory(m_device, mem, 0, sz, 0, &mp);
std::memcpy(out.data(), mp, sz);
vkUnmapMemory(m_device, mem);
+ const size_t row = (size_t)w * 4;
+ for (uint32_t y = 0; y < h / 2; ++y)
+ std::swap_ranges(out.begin() + (std::ptrdiff_t)(y * row), out.begin() + (std::ptrdiff_t)((y + 1) * row),
+ out.begin() + (std::ptrdiff_t)((h - 1 - y) * row));
vkFreeCommandBuffers(m_device, m_command_pool, 1, &cmd);
vkDestroyBuffer(m_device, buf, nullptr); vkFreeMemory(m_device, mem, nullptr);
}
@@ -291,6 +316,15 @@ namespace Donut::RHI
auto create_vulkan_device() -> Scope<Device> { return create_scope<VulkanDevice>(); }
+ auto vulkan_runtime_available() -> bool
+ {
+#if defined(_WIN32)
+ return LoadLibraryExW(L"vulkan-1.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) != nullptr;
+#else
+ return true;
+#endif
+ }
+
auto vulkan_prepare_glfw() -> void
{
#ifdef __APPLE__
diff --git a/src/platform/vulkan/vulkan_device.h b/src/platform/vulkan/vulkan_device.h
index 8c0069f..8a4a664 100644
--- a/src/platform/vulkan/vulkan_device.h
+++ b/src/platform/vulkan/vulkan_device.h
@@ -7,4 +7,6 @@ namespace Donut::RHI
auto create_vulkan_device() -> Scope<Device>;
auto vulkan_prepare_glfw() -> void;
+
+ auto vulkan_runtime_available() -> bool;
}
diff --git a/src/platform/vulkan/vulkan_resources.cpp b/src/platform/vulkan/vulkan_resources.cpp
index 69225ab..ff14ce1 100644
--- a/src/platform/vulkan/vulkan_resources.cpp
+++ b/src/platform/vulkan/vulkan_resources.cpp
@@ -77,7 +77,8 @@ namespace Donut::RHI
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.imageType = VK_IMAGE_TYPE_2D; ici.format = cfmt; ici.extent = { (uint32_t)w, (uint32_t)h, 1 };
ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT;
- ici.tiling = VK_IMAGE_TILING_OPTIMAL; ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+ ici.tiling = VK_IMAGE_TILING_OPTIMAL;
+ ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vkCreateImage(m_device, &ici, nullptr, &rt->m_image);
VkMemoryRequirements req{}; vkGetImageMemoryRequirements(m_device, rt->m_image, &req);
VkMemoryAllocateInfo ai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
diff --git a/src/platform/vulkan/vulkan_swapchain.cpp b/src/platform/vulkan/vulkan_swapchain.cpp
index c6157b0..fa2b0b8 100644
--- a/src/platform/vulkan/vulkan_swapchain.cpp
+++ b/src/platform/vulkan/vulkan_swapchain.cpp
@@ -11,19 +11,38 @@ namespace Donut::RHI
const char** glfwExts = glfwGetRequiredInstanceExtensions(&glfwExtCount);
if (!glfwExts) { DONUT_ERROR("Vulkan RHI: GLFW reports no surface support"); return false; }
std::vector<const char*> exts(glfwExts, glfwExts + glfwExtCount);
- exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
- exts.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
+
+ uint32_t iec = 0; vkEnumerateInstanceExtensionProperties(nullptr, &iec, nullptr);
+ std::vector<VkExtensionProperties> iexts(iec);
+ vkEnumerateInstanceExtensionProperties(nullptr, &iec, iexts.data());
+ auto has_ext = [&](const char* name)
+ {
+ for (const auto& e : iexts) if (std::strcmp(e.extensionName, name) == 0) return true;
+ return false;
+ };
+ VkInstanceCreateFlags flags = 0;
+#ifdef VK_KHR_portability_enumeration
+ if (has_ext(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME))
+ {
+ exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
+ flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
+ }
+#endif
+ if (has_ext(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
+ exts.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
std::vector<const char*> layers;
+#ifdef DONUT_DEBUG
uint32_t layer_count = 0; vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
std::vector<VkLayerProperties> avail(layer_count);
vkEnumerateInstanceLayerProperties(&layer_count, avail.data());
for (const auto& l : avail)
if (std::strcmp(l.layerName, "VK_LAYER_KHRONOS_validation") == 0)
layers.push_back("VK_LAYER_KHRONOS_validation");
+#endif
VkInstanceCreateInfo ici{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
- ici.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
+ ici.flags = flags;
ici.pApplicationInfo = &app;
ici.enabledExtensionCount = (uint32_t)exts.size(); ici.ppEnabledExtensionNames = exts.data();
ici.enabledLayerCount = (uint32_t)layers.size(); ici.ppEnabledLayerNames = layers.data();
@@ -31,6 +50,7 @@ namespace Donut::RHI
if (r != VK_SUCCESS && !layers.empty())
{
DONUT_WARN("Vulkan RHI: validation layer unavailable, continuing without it");
+ layers.clear();
ici.enabledLayerCount = 0; ici.ppEnabledLayerNames = nullptr;
r = vkCreateInstance(&ici, nullptr, &m_instance);
}
@@ -46,19 +66,27 @@ namespace Donut::RHI
if (count == 0) { DONUT_ERROR("Vulkan RHI: no physical devices"); return false; }
std::vector<VkPhysicalDevice> devices(count);
vkEnumeratePhysicalDevices(m_instance, &count, devices.data());
- m_physical = devices[0];
- uint32_t q = 0; vkGetPhysicalDeviceQueueFamilyProperties(m_physical, &q, nullptr);
- std::vector<VkQueueFamilyProperties> qfams(q);
- vkGetPhysicalDeviceQueueFamilyProperties(m_physical, &q, qfams.data());
- bool fg = false, fp = false;
- for (uint32_t i = 0; i < q; ++i)
+ int best = -1;
+ for (VkPhysicalDevice pd : devices)
{
- if (!fg && (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) { m_graphics_family = i; fg = true; }
- VkBool32 present = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(m_physical, i, m_surface, &present);
- if (!fp && present) { m_present_family = i; fp = true; }
+ uint32_t q = 0; vkGetPhysicalDeviceQueueFamilyProperties(pd, &q, nullptr);
+ std::vector<VkQueueFamilyProperties> qfams(q);
+ vkGetPhysicalDeviceQueueFamilyProperties(pd, &q, qfams.data());
+ uint32_t gfam = UINT32_MAX, pfam = UINT32_MAX;
+ for (uint32_t i = 0; i < q; ++i)
+ {
+ if (gfam == UINT32_MAX && (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) gfam = i;
+ VkBool32 present = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(pd, i, m_surface, &present);
+ if (pfam == UINT32_MAX && present) pfam = i;
+ }
+ if (gfam == UINT32_MAX || pfam == UINT32_MAX) continue;
+ VkPhysicalDeviceProperties pp{}; vkGetPhysicalDeviceProperties(pd, &pp);
+ const int score = pp.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU ? 3
+ : pp.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU ? 2 : 1;
+ if (score > best) { best = score; m_physical = pd; m_graphics_family = gfam; m_present_family = pfam; }
}
- if (!fg || !fp) { DONUT_ERROR("Vulkan RHI: no graphics/present queue"); return false; }
+ if (best < 0) { DONUT_ERROR("Vulkan RHI: no gpu with both a graphics and a present queue"); return false; }
std::vector<const char*> dev_exts = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
uint32_t dec = 0; vkEnumerateDeviceExtensionProperties(m_physical, nullptr, &dec, nullptr);
@@ -289,13 +317,21 @@ namespace Donut::RHI
auto VulkanDevice::recreate_swapchain() -> bool
{
int w = 0, h = 0; glfwGetFramebufferSize(m_window, &w, &h);
- while (w == 0 || h == 0) { glfwGetFramebufferSize(m_window, &w, &h); glfwWaitEvents(); }
+ while ((w == 0 || h == 0) && !glfwWindowShouldClose(m_window)) { glfwWaitEvents(); glfwGetFramebufferSize(m_window, &w, &h); }
+ if (w == 0 || h == 0) return false;
m_width = w; m_height = h;
vkDeviceWaitIdle(m_device);
cleanup_swapchain();
if (!create_swapchain()) return false;
if (!create_image_views()) return false;
if (!create_depth_and_framebuffers())return false;
+ if (m_render_finished.size() != m_images.size())
+ {
+ for (auto s : m_render_finished) vkDestroySemaphore(m_device, s, nullptr);
+ m_render_finished.assign(m_images.size(), VK_NULL_HANDLE);
+ VkSemaphoreCreateInfo sci{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
+ for (auto& s : m_render_finished) VKD_CHECK(vkCreateSemaphore(m_device, &sci, nullptr, &s));
+ }
m_images_in_flight.assign(m_images.size(), VK_NULL_HANDLE);
return true;
}