Fixed various errors in swapchain creation and created depth image

This commit is contained in:
Conál 2024-01-19 22:23:00 -05:00
parent cc49f1b704
commit 758c577161
21 changed files with 233 additions and 63 deletions

7
.gitignore vendored
View File

@ -9,10 +9,12 @@
*.user *.user
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
.vscode/
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs
# Mono auto generated files # Mono auto generated files
mono_crash.* mono_crash.*
@ -34,6 +36,9 @@ bld/
[Ll]ogs/ [Ll]ogs/
[Ll]ib/ [Ll]ib/
# Cmake directory
[Bb]uild/
# Visual Studio 2015/2017 cache/options directory # Visual Studio 2015/2017 cache/options directory
.vs/ .vs/
# Uncomment if you have tasks that create the project's static files in wwwroot # Uncomment if you have tasks that create the project's static files in wwwroot
@ -361,4 +366,4 @@ MigrationBackup/
.ionide/ .ionide/
# Fody - auto-generated XML schema # Fody - auto-generated XML schema
FodyWeavers.xsd FodyWeavers.xsd

View File

@ -3,16 +3,21 @@
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "-DDEBUG")
set(CMAKE_CXX_FLAGS "-fsanitize=address -g -DDEBUG -D_DEBUG")
project(Pleascach) project(Pleascach)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") if(WIN32)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
endif()
file(GLOB SOURCES file(GLOB SOURCES
pléascach.cpp pléascach.cpp
*/*.cpp */*.cpp
) )
add_executable (Pleascach ${SOURCES}) add_executable (pleascach ${SOURCES})
find_package(glfw3 3.3 REQUIRED) find_package(glfw3 3.3 REQUIRED)
find_package(Vulkan REQUIRED) find_package(Vulkan REQUIRED)
@ -21,7 +26,8 @@ if(UNIX)
set(GLFW3_LIBRARY glfw) set(GLFW3_LIBRARY glfw)
endif() endif()
include_directories(${CMAKE_CURRENT_LIST_DIR} include ${GLFW3_INCLUDE_DIR}) include_directories(${CMAKE_CURRENT_LIST_DIR} include ${GLFW3_INCLUDE_DIR})
target_link_libraries(Pleascach ${GLFW3_LIBRARY} Vulkan::Vulkan) target_link_libraries(pleascach ${GLFW3_LIBRARY} Vulkan::Vulkan)

View File

@ -1,7 +1,7 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#define INPUT_PTR GLFWwindow* #define INPUT_PTR GLFWwindow*
#include <input/input.hpp> #include <Input/Input.hpp>
#include <util/log.hpp> #include <util/log.hpp>
@ -62,4 +62,4 @@ Input::Input(INPUT_PTR in) : in(in) {
} }
}); });
}); });
} }

View File

@ -32,6 +32,7 @@ constexpr u32 operator ~ (InputModifierBit a) {
struct InputEvent { struct InputEvent {
enum Tag { enum Tag {
EXIT,
RESIZE, RESIZE,
CURSOR, CURSOR,
KEY, KEY,

View File

@ -1,15 +1,19 @@
#include <memory/memory.hpp> #include <Memory/Memory.hpp>
#include "Memory.hpp"
namespace mem { namespace mem {
u32 choose_heap(vk::PhysicalDevice& phys_dev, const vk::MemoryRequirements& requirements, vk::MemoryPropertyFlags req_flags, vk::MemoryPropertyFlags pref_flags) { u32 choose_heap(vk::PhysicalDevice& phys_dev, const vk::MemoryRequirements& requirements, vk::MemoryPropertyFlags req_flags, vk::MemoryPropertyFlags pref_flags) {
auto dev_props = phys_dev.getMemoryProperties(); auto dev_props = phys_dev.getMemoryProperties();
/* try to find an exact match for preferred flags first */ if(static_cast<int>(pref_flags.operator VkImageCreateFlags()) != -1) {
for (u32 memory_type = 0; memory_type < 32; memory_type++) {
if (requirements.memoryTypeBits & (1 << memory_type)) { /* try to find an exact match for preferred flags first */
const auto& type = dev_props.memoryTypes[memory_type]; for (u32 memory_type = 0; memory_type < 32; memory_type++) {
if ((type.propertyFlags & pref_flags) == pref_flags) { if (requirements.memoryTypeBits & (1 << memory_type)) {
return type.heapIndex; const auto& type = dev_props.memoryTypes[memory_type];
if ((type.propertyFlags & pref_flags) == pref_flags) {
return type.heapIndex;
}
} }
} }
} }
@ -22,5 +26,7 @@ namespace mem {
} }
} }
} }
return -1;
} }
} }

View File

@ -5,5 +5,5 @@
#include <util/int.hpp> #include <util/int.hpp>
namespace mem { namespace mem {
u32 choose_heap(vk::PhysicalDevice& phys_dev, const vk::MemoryRequirements& requirements, vk::MemoryPropertyFlags req_flags, vk::MemoryPropertyFlags pref_flags); u32 choose_heap(vk::PhysicalDevice& phys_dev, const vk::MemoryRequirements& requirements, vk::MemoryPropertyFlags req_flags, vk::MemoryPropertyFlags pref_flags = vk::MemoryPropertyFlagBits(-1));
} }

View File

@ -2,4 +2,5 @@
## Long Term Improvements ## Long Term Improvements
- Properly query surface to find supported formats for surfaces
- Add pipeline caching - Add pipeline caching

View File

@ -1,4 +1,6 @@
#include <renderer/command_buffer.hpp> #include <Renderer/CommandBuffer.hpp>
#include <Renderer/Pipeline.hpp>
CommandBuffer::CommandBuffer(vk::Device dev, u32 queue_family) { CommandBuffer::CommandBuffer(vk::Device dev, u32 queue_family) {
/* (For now) allow command buffers to be individually recycled */ /* (For now) allow command buffers to be individually recycled */
@ -17,7 +19,8 @@ CommandBuffer::CommandBuffer(vk::Device dev, u32 queue_family) {
.commandBufferCount = 1, .commandBufferCount = 1,
}; };
dev.allocateCommandBuffers(alloc_info);
command_buffer = dev.allocateCommandBuffers(alloc_info)[0];
} }
void CommandBuffer::begin() { void CommandBuffer::begin() {
@ -33,6 +36,14 @@ void CommandBuffer::copy(vk::Buffer in, vk::Buffer out, vk::ArrayProxy<const vk:
command_buffer.copyBuffer(in, out, regions); command_buffer.copyBuffer(in, out, regions);
} }
void CommandBuffer::bind(const GraphicsPipeline& pipeline) {
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.pipeline);
}
void CommandBuffer::bind(const ComputePipeline& pipeline) {
command_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
}
void CommandBuffer::end() { void CommandBuffer::end() {
command_buffer.end(); command_buffer.end();
} }
@ -42,6 +53,5 @@ void CommandBuffer::recycle() {
} }
void CommandBuffer::cleanup(vk::Device dev) { void CommandBuffer::cleanup(vk::Device dev) {
dev.freeCommandBuffers(command_pool, command_buffer);
dev.destroyCommandPool(command_pool); dev.destroyCommandPool(command_pool);
} }

View File

@ -5,6 +5,9 @@
#include <util/int.hpp> #include <util/int.hpp>
struct GraphicsPipeline;
struct ComputePipeline;
struct CommandBuffer { struct CommandBuffer {
CommandBuffer(vk::Device dev, u32 queue_family); CommandBuffer(vk::Device dev, u32 queue_family);
@ -16,6 +19,9 @@ struct CommandBuffer {
/* copy between buffer */ /* copy between buffer */
void copy(vk::Buffer in, vk::Buffer out, vk::ArrayProxy<const vk::BufferCopy> regions); void copy(vk::Buffer in, vk::Buffer out, vk::ArrayProxy<const vk::BufferCopy> regions);
void bind(const GraphicsPipeline& pipeline);
void bind(const ComputePipeline& pipeline);
/* stop recording commands */ /* stop recording commands */
void end(); void end();
@ -23,4 +29,4 @@ struct CommandBuffer {
vk::CommandBuffer command_buffer; vk::CommandBuffer command_buffer;
vk::CommandPool command_pool; vk::CommandPool command_pool;
}; };

19
Renderer/Pipeline.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <Renderer/Pipeline.hpp>
#include <Renderer/Shader.hpp>
ComputePipeline::ComputePipeline(vk::Device dev, const Shader& shader) {
auto shader_info = vk::PipelineShaderStageCreateInfo {
.stage = vk::ShaderStageFlagBits::eCompute,
.module = shader,
.pName = "main",
};
auto layout = vk::PipelineLayoutCreateInfo {
};
auto create_info = vk::ComputePipelineCreateInfo {
.stage = shader_info,
};
}

27
Renderer/Pipeline.hpp Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
struct Shader;
struct ComputePipeline {
ComputePipeline(vk::Device dev, const Shader& shader);
vk::Pipeline pipeline;
inline operator vk::Pipeline&() {
return pipeline;
}
};
struct GraphicsPipeline {
GraphicsPipeline(vk::Device dev);
vk::Pipeline pipeline;
inline operator vk::Pipeline&() {
return pipeline;
}
};

View File

@ -1,18 +1,20 @@
#include <renderer/renderer.hpp> #include <Renderer/Renderer.hpp>
#include <window/window.hpp> #include <Window/Window.hpp>
#include <util/log.hpp> #include <util/log.hpp>
#include <Memory/Memory.hpp>
using namespace std::string_literals; using namespace std::string_literals;
Renderer::Renderer(Window& win) : win(win) { Renderer::Renderer(Window& win) : win(win) {
/* Create Instance object */ /* Create Instance object */
auto app_info = vk::ApplicationInfo { auto app_info = vk::ApplicationInfo {
.pApplicationName = "Pléascach Demo", .pApplicationName = "Pl<EFBFBD>ascach Demo",
.applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0),
.pEngineName = "Pléascach", .pEngineName = "Pl<EFBFBD>ascach",
.engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), .engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0),
.apiVersion = VK_API_VERSION_1_0, .apiVersion = VK_API_VERSION_1_1,
}; };
const auto req_extensions = win.requiredExtensions(); const auto req_extensions = win.requiredExtensions();
@ -29,7 +31,7 @@ Renderer::Renderer(Window& win) : win(win) {
} }
/* query and enable available layers if in DEBUG mode */ /* query and enable available layers if in DEBUG mode */
#ifdef _DEBUG #ifdef DEBUG
auto layers = vk::enumerateInstanceLayerProperties(); auto layers = vk::enumerateInstanceLayerProperties();
Log::info("%zu available instance layers\n", layers.size()); Log::info("%zu available instance layers\n", layers.size());
@ -46,8 +48,8 @@ Renderer::Renderer(Window& win) : win(win) {
.pApplicationInfo = &app_info, .pApplicationInfo = &app_info,
.enabledLayerCount = std::size(my_layers), .enabledLayerCount = std::size(my_layers),
.ppEnabledLayerNames = my_layers, .ppEnabledLayerNames = my_layers,
.enabledExtensionCount = static_cast<u32>(extensions.size()), .enabledExtensionCount = static_cast<u32>(req_extensions.size()),
.ppEnabledExtensionNames = extension_names.data(), .ppEnabledExtensionNames = req_extensions.data(),
}; };
#else #else
@ -55,8 +57,8 @@ Renderer::Renderer(Window& win) : win(win) {
.pApplicationInfo = &app_info, .pApplicationInfo = &app_info,
.enabledLayerCount = 0, .enabledLayerCount = 0,
.ppEnabledLayerNames = nullptr, .ppEnabledLayerNames = nullptr,
.enabledExtensionCount = static_cast<u32>(extensions.size()), .enabledExtensionCount = static_cast<u32>(req_extensions.size()),
.ppEnabledExtensionNames = extensions.data(), .ppEnabledExtensionNames = req_extensions.data(),
}; };
#endif #endif
@ -108,11 +110,11 @@ Renderer::Renderer(Window& win) : win(win) {
/* enumerate available device features */ /* enumerate available device features */
std::vector<const char*> required_extensions; std::vector<const char*> required_extensions;
required_extensions.push_back("VK_KHR_swapchain");
auto dev_extentions = phys_dev.enumerateDeviceExtensionProperties(); auto dev_extentions = phys_dev.enumerateDeviceExtensionProperties();
Log::info("%zu available device extensions\n", dev_extentions.size()); Log::info("%zu available device extensions\n", dev_extentions.size());
for (const auto& ext : dev_extentions) { for (const auto& ext : dev_extentions) {
Log::info("\t\"%s\"\n", ext.extensionName.data()); Log::info("\t\"%s\"\n", ext.extensionName.data());
required_extensions.push_back(ext.extensionName);
} }
auto dev_layers = phys_dev.enumerateDeviceLayerProperties(); auto dev_layers = phys_dev.enumerateDeviceLayerProperties();
@ -124,7 +126,6 @@ Renderer::Renderer(Window& win) : win(win) {
"VK_LAYER_KHRONOS_validation" "VK_LAYER_KHRONOS_validation"
}; };
#pragma message("TODO: MAKE THIS NO LONGER EVERY SINGLE EXTENTION NAME")
auto dev_info = vk::DeviceCreateInfo{ auto dev_info = vk::DeviceCreateInfo{
.flags = vk::DeviceCreateFlagBits(0), .flags = vk::DeviceCreateFlagBits(0),
.queueCreateInfoCount = 1, .queueCreateInfoCount = 1,
@ -147,6 +148,66 @@ Renderer::Renderer(Window& win) : win(win) {
swapchain = std::make_unique<Swapchain>(dev, surface, win.getDimensions()); swapchain = std::make_unique<Swapchain>(dev, surface, win.getDimensions());
queue = dev.getQueue(queue_family, 0); queue = dev.getQueue(queue_family, 0);
/* depth image */
/* 1. create an Image to be the buffer
* 2. find memory req's
* 3. allocate
* 4. set layout
* 5. create attachment view
*/
auto extent = win.getDimensions();
auto depth_image_info = vk::ImageCreateInfo {
.imageType = vk::ImageType::e2D,
.format = vk::Format::eD16Unorm,
.extent = {
.width = extent.width,
.height = extent.height,
.depth = 1,
},
.mipLevels = 1,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment,
.sharingMode = vk::SharingMode::eExclusive,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = NULL,
.initialLayout = vk::ImageLayout::eUndefined,
};
depth_image = dev.createImage(depth_image_info);
auto depth_mem_reqs = dev.getImageMemoryRequirements(depth_image);
auto depth_alloc_info = vk::MemoryAllocateInfo {
.allocationSize = depth_mem_reqs.size,
.memoryTypeIndex = mem::choose_heap(phys_dev, depth_mem_reqs, vk::MemoryPropertyFlagBits::eDeviceLocal),
};
auto depth_alloc = dev.allocateMemory(depth_alloc_info);
dev.bindImageMemory(depth_image, depth_alloc, 0);
auto depth_view_info = vk::ImageViewCreateInfo {
.image = depth_image,
.viewType = vk::ImageViewType::e2D,
.format = vk::Format::eD16Unorm,
.components = {
.r = vk::ComponentSwizzle::eR,
.g = vk::ComponentSwizzle::eG,
.b = vk::ComponentSwizzle::eB,
.a = vk::ComponentSwizzle::eA,
},
.subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
depth_image_view = dev.createImageView(depth_view_info);
} }
void Renderer::draw() { void Renderer::draw() {
@ -161,6 +222,8 @@ void Renderer::draw() {
command_buffer->recycle(); command_buffer->recycle();
command_buffer->begin(); command_buffer->begin();
command_buffer->end(); command_buffer->end();
} }
@ -185,7 +248,9 @@ void Renderer::present() {
} }
Renderer::~Renderer() { Renderer::~Renderer() {
command_buffer->cleanup(dev); dev.destroyImage(depth_image);
dev.destroyImageView(depth_image_view);
swapchain.reset(); swapchain.reset();
dev.waitIdle(); dev.waitIdle();
dev.destroy(); dev.destroy();

View File

@ -5,8 +5,8 @@
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS #define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#include <renderer/swapchain.hpp> #include <Renderer/Swapchain.hpp>
#include <renderer/command_buffer.hpp> #include <Renderer/CommandBuffer.hpp>
struct Window; struct Window;
@ -19,12 +19,19 @@ struct Renderer {
void present(); void present();
Window& win; Window& win;
vk::Instance instance; vk::Instance instance;
vk::Device dev; vk::Device dev;
vk::SurfaceKHR surface; vk::SurfaceKHR surface;
std::unique_ptr<Swapchain> swapchain; std::unique_ptr<Swapchain> swapchain;
int queue_family; int queue_family;
vk::Queue queue; vk::Queue queue;
std::unique_ptr<CommandBuffer> command_buffer; std::unique_ptr<CommandBuffer> command_buffer;
uint32_t current_image_idx; uint32_t current_image_idx;
};
vk::Image depth_image;
vk::ImageView depth_image_view;
};

View File

@ -1,4 +1,4 @@
#include <renderer/shader.hpp> #include <Renderer/Shader.hpp>
#include <util/log.hpp> #include <util/log.hpp>

View File

@ -9,7 +9,9 @@ struct Shader {
Shader(vk::Device dev, const std::string& fname); Shader(vk::Device dev, const std::string& fname);
void cleanup(vk::Device dev); void cleanup(vk::Device dev);
inline operator vk::ShaderModule() const {
return module;
}
inline operator vk::ShaderModule& () { inline operator vk::ShaderModule& () {
return module; return module;
@ -17,4 +19,4 @@ struct Shader {
std::string fname; std::string fname;
}; };

View File

@ -1,4 +1,4 @@
#include <renderer/swapchain.hpp> #include <Renderer/Swapchain.hpp>
Swapchain::Swapchain(vk::Device& dev, const vk::SurfaceKHR& surface, const vk::Extent2D& extent) : dev(dev), surface(surface) { Swapchain::Swapchain(vk::Device& dev, const vk::SurfaceKHR& surface, const vk::Extent2D& extent) : dev(dev), surface(surface) {
create(extent); create(extent);
@ -6,11 +6,13 @@ Swapchain::Swapchain(vk::Device& dev, const vk::SurfaceKHR& surface, const vk::E
void Swapchain::create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchain) { void Swapchain::create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchain) {
auto format = vk::Format::eB8G8R8A8Unorm;
auto swap_info = vk::SwapchainCreateInfoKHR{ auto swap_info = vk::SwapchainCreateInfoKHR{
.surface = surface, .surface = surface,
/* at least double-buffered */ /* at least double-buffered */
.minImageCount = 3, .minImageCount = 3,
.imageFormat = vk::Format::eR8G8B8A8Unorm, .imageFormat = format,
.imageColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear, .imageColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear,
.imageExtent = extent, .imageExtent = extent,
.imageArrayLayers = 1, .imageArrayLayers = 1,
@ -30,6 +32,29 @@ void Swapchain::create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchai
swapchain = dev.createSwapchainKHR(swap_info); swapchain = dev.createSwapchainKHR(swap_info);
images = dev.getSwapchainImagesKHR(swapchain); images = dev.getSwapchainImagesKHR(swapchain);
views.resize(images.size());
for(size_t i = 0; i < views.size(); i++) {
auto color_image_info = vk::ImageViewCreateInfo {
.image = images[i],
.viewType = vk::ImageViewType::e2D,
.format = format,
.components = {
.r = vk::ComponentSwizzle::eR,
.g = vk::ComponentSwizzle::eG,
.b = vk::ComponentSwizzle::eB,
.a = vk::ComponentSwizzle::eA,
},
.subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
}
};
views[i] = dev.createImageView(color_image_info);
}
} }
@ -45,4 +70,4 @@ void Swapchain::cleanup() {
Swapchain::~Swapchain() { Swapchain::~Swapchain() {
cleanup(); cleanup();
} }

View File

@ -13,6 +13,7 @@ struct Swapchain {
vk::Device& dev; vk::Device& dev;
vk::SurfaceKHR surface; vk::SurfaceKHR surface;
std::vector<vk::Image> images; std::vector<vk::Image> images;
std::vector<vk::ImageView> views;
void create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchain = nullptr); void create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchain = nullptr);
void recreate(const vk::Extent2D& extent); void recreate(const vk::Extent2D& extent);

View File

@ -4,8 +4,8 @@
#define WINDOW_PTR GLFWwindow* #define WINDOW_PTR GLFWwindow*
#define INPUT_PTR GLFWwindow* #define INPUT_PTR GLFWwindow*
#include <window/window.hpp> #include <Window/Window.hpp>
#include <input/input.hpp> #include <Input/Input.hpp>
#include <util/log.hpp> #include <util/log.hpp>
@ -84,4 +84,4 @@ Window::~Window() {
glfwTerminate(); glfwTerminate();
Log::info("Terminated GLFW\n"); Log::info("Terminated GLFW\n");
} }

View File

@ -5,7 +5,9 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <input/input.hpp> #include <memory>
#include <Input/Input.hpp>
#ifndef WINDOW_PTR #ifndef WINDOW_PTR
#define WINDOW_PTR void* #define WINDOW_PTR void*

View File

@ -1,6 +1,6 @@
#include <window/window.hpp> #include <Window/Window.hpp>
#include <input/input.hpp> #include <Input/Input.hpp>
#include <renderer/renderer.hpp> #include <Renderer/Renderer.hpp>
#include <util/log.hpp> #include <util/log.hpp>
@ -30,6 +30,8 @@ int main() {
break; break;
case InputEvent::Tag::KEY: case InputEvent::Tag::KEY:
Log::info("Event Processed: Button 0x%x %d\n", event.key.key, event.key.state); Log::info("Event Processed: Button 0x%x %d\n", event.key.key, event.key.state);
break;
case InputEvent::Tag::EXIT:
win.close(); win.close();
break; break;
} }
@ -38,5 +40,5 @@ int main() {
} catch (const std::string& e) { } catch (const std::string& e) {
std::cerr << "Exception: " << e << std::endl; std::cerr << "Exception: " << e << std::endl;
} }
} }

View File

@ -1,15 +0,0 @@
#pragma once
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
struct ComputePipeline {
ComputePipeline(vk::Device dev) {
}
vk::Pipeline pipeline;
inline operator vk::Pipeline&() {
return pipeline;
}
};