Split vulkan utility, use positional arguments in pipeline utility for extendable arguments
This commit is contained in:
parent
0c0ee087c9
commit
91ff840a78
7 changed files with 1249 additions and 1131 deletions
277
positional_utl.h
Normal file
277
positional_utl.h
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright (c) 2024 mittorn
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef POSITIONAL_UTL_H
|
||||
#define POSITIONAL_UTL_H
|
||||
// wrapper for chains with pNext
|
||||
template <typename T, typename... Ts>
|
||||
struct StructureChain : T, StructureChain<Ts...>
|
||||
{
|
||||
StructureChain(const T &t, const Ts&... a) : T(t), StructureChain<Ts...>(a...)
|
||||
{
|
||||
((T*)this)->pNext = (StructureChain<Ts...>*)this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct StructureChain<T> : T
|
||||
{
|
||||
StructureChain(const T &t) : T(t){}
|
||||
};
|
||||
|
||||
// Positional arguments? Im my C++? It's more likely than you think!
|
||||
enum SetterOperation
|
||||
{
|
||||
AssignVal,
|
||||
AssignArrayPtrWithLength,
|
||||
UpdateObject,
|
||||
AssignObject,
|
||||
AppendObject,
|
||||
AssignArrayByIndex,
|
||||
AssignArrayCopy,
|
||||
AssignArrayCopyWithLength,
|
||||
// CallWith
|
||||
CallMethod,
|
||||
SetTrue
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Setter;
|
||||
template<typename T, typename V, SetterOperation o = AssignVal>
|
||||
struct SetterVal // keeps val reference and SetterFunc wrapper
|
||||
{
|
||||
constexpr static SetterOperation op = o;
|
||||
const Setter<T> &member;
|
||||
const V& val;
|
||||
SetterVal(const Setter<T> &m, const V &v) : member(m), val(v){}
|
||||
};
|
||||
template<typename T, typename V>
|
||||
struct SetterValPtr // keeps val reference and SetterFunc wrapper
|
||||
{
|
||||
constexpr static SetterOperation op = AssignVal;
|
||||
const Setter<T> &member;
|
||||
const V *val;
|
||||
SetterValPtr(const Setter<T> &m, const V *v) : member(m), val(v){}
|
||||
};
|
||||
/* Delayed function call (maybe useless)
|
||||
* struct aaa{
|
||||
void Func(int a, int b, int c){printf("%d %d %d\n",a,b,c);}
|
||||
} s;
|
||||
$F(s, $(Func) (10, 20, 30));
|
||||
*/
|
||||
template <typename T, typename... Ts>
|
||||
struct ArgumentList : ArgumentList<Ts...>
|
||||
{
|
||||
const T &v;
|
||||
ArgumentList(const T &t, const Ts&... a) : v(t), ArgumentList<Ts...>(a...){}
|
||||
template<typename Func, typename I, typename... Ts2> void CallMember(Func f, I &i, const Ts2&... ts) const
|
||||
{
|
||||
ArgumentList<Ts...>::CallMember(f, i,ts..., v);
|
||||
}
|
||||
template<typename Func, typename I, typename... Ts2> void CallCtx(Func f, I &i, const Ts2&... ts) const
|
||||
{
|
||||
ArgumentList<Ts...>::CallCtx(f, i,ts..., v);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArgumentList<T>
|
||||
{
|
||||
const T &v;
|
||||
ArgumentList(const T &t) : v(t){}
|
||||
template<typename Func, typename I, typename... Ts2> void CallMember(Func f, I &i, const Ts2&... ts) const
|
||||
{
|
||||
(i.*f)(ts..., v);
|
||||
}
|
||||
template<typename Func, typename I, typename... Ts2> void CallCtx(Func f, I &i, const Ts2&... ts) const
|
||||
{
|
||||
f(i,ts...,v);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename... Vs>
|
||||
struct SetterFunc // keeps val reference and SetterFunc wrapper
|
||||
{
|
||||
constexpr static SetterOperation op = CallMethod;
|
||||
const Setter<T> &func;
|
||||
ArgumentList<Vs... > vals;
|
||||
SetterFunc(const Setter<T> &f, const Vs&... vs) : func(f), vals(vs...) {}
|
||||
};
|
||||
|
||||
// container for updating vals
|
||||
template <typename T>
|
||||
struct ObjectUpdater:T
|
||||
{
|
||||
ObjectUpdater(const T& t): T(t){}
|
||||
};
|
||||
|
||||
#define Vals(...) ObjectUpdater([&](auto &s){ $F(s,__VA_ARGS__);})
|
||||
|
||||
template<typename T, typename T1>
|
||||
struct SetterArray;
|
||||
|
||||
template<typename T, typename T1, typename V, SetterOperation O = AssignArrayPtrWithLength>
|
||||
struct SetterArrayVal // keeps val reference and SetterFunc wrapper
|
||||
{
|
||||
constexpr static SetterOperation op = O;
|
||||
const SetterArray<T,T1> &arr;
|
||||
const V &val;
|
||||
SetterArrayVal(const SetterArray<T, T1> &f, const V &v) : arr(f), val(v){}
|
||||
};
|
||||
|
||||
template<typename T, typename T1>
|
||||
struct SetterArray // keeps val reference and SetterFunc wrapper
|
||||
{
|
||||
const Setter<T> &member;
|
||||
const Setter<T1> &count;
|
||||
template <typename V>
|
||||
SetterArrayVal<T,T1,V> operator= (const V &v) {return SetterArrayVal<T,T1,V>(*this,v);}
|
||||
SetterArray(const Setter<T> &m, const Setter<T1> &c) : member(m), count(c) {}
|
||||
};
|
||||
template<typename T>
|
||||
struct SetterArrayIndex;
|
||||
template<typename T, typename V>
|
||||
struct SetterArrayIndexVal // keeps val reference and SetterFunc wrapper
|
||||
{
|
||||
constexpr static SetterOperation op = AssignArrayByIndex;
|
||||
const SetterArrayIndex<T> &arr;
|
||||
const V &val;
|
||||
SetterArrayIndexVal(const SetterArrayIndex<T> &f, const V &v) : arr(f), val(v){}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SetterArrayIndex // keeps val reference and SetterFunc wrapper
|
||||
{
|
||||
const Setter<T> &member;
|
||||
const int idx;
|
||||
template <typename V>
|
||||
SetterArrayIndexVal<T,V> operator= (const V &v) {return SetterArrayIndexVal<T,V>(*this,v);}
|
||||
SetterArrayIndex(const Setter<T> &m, const int i) : member(m), idx(i) {}
|
||||
};
|
||||
// todo: sync with other utilities...
|
||||
template <typename T, uint32_t cnt>
|
||||
struct Array
|
||||
{
|
||||
T array[cnt];
|
||||
constexpr static uint32_t count = cnt;
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct ArrayInitializer
|
||||
{
|
||||
T array[sizeof... (Args)];
|
||||
constexpr static uint32_t count = sizeof... (Args);
|
||||
};
|
||||
#define ARRAY_WRAPPER(x,y) template <typename... Args> Array<x, sizeof... (Args)> y(const Args&... arguments) { return {arguments...};}
|
||||
template <typename T0, typename... Ts>
|
||||
Array<T0, sizeof...(Ts)+1> Arr(T0 arg0, Ts... arguments)
|
||||
{
|
||||
return {arg0, arguments...};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct Setter // keeps member pointer getter, wrapper for user-provided lambda
|
||||
{
|
||||
constexpr static SetterOperation op = SetTrue;
|
||||
const T &func;
|
||||
Setter(const T &data) :func(data){}
|
||||
template <typename T1>
|
||||
SetterVal<T,T1,AssignObject> operator = (const ObjectUpdater<T1>& v) {return SetterVal<T,T1,AssignObject>(*this,v);}
|
||||
template <typename T1>
|
||||
SetterVal<T,T1,UpdateObject> operator += (const ObjectUpdater<T1>& v) {return SetterVal<T,T1,UpdateObject>(*this,v);}
|
||||
template <typename T1>
|
||||
SetterVal<T,T1,AppendObject> operator += (const T1& v) {return SetterVal<T,T1,AppendObject>(*this,v);}
|
||||
template <typename T1, uint32_t cnt>
|
||||
SetterVal<T,Array<T1, cnt>,AssignArrayCopy> operator = (const Array<T1, cnt>& v) {return SetterVal<T,Array<T1, cnt>,AssignArrayCopy>(*this,v);}
|
||||
template <typename V>
|
||||
SetterValPtr<T,V> operator &= (const V &v) {return SetterValPtr<T,V>(*this,&v);}
|
||||
template <typename V>
|
||||
SetterVal<T,V> operator= (const V &v) {return SetterVal<T,V>(*this,v);}
|
||||
template <typename... Vs>
|
||||
SetterFunc<T,Vs...> operator() (const Vs&... v) {return SetterFunc<T,Vs...>(*this,v...);}
|
||||
template <typename T1>
|
||||
SetterArray<T,T1> ptrWithLength (const Setter<T1> &v) {return SetterArray<T,T1>(*this,v);}
|
||||
template <typename T1>
|
||||
SetterArray<T,T1> copyWidthength (const Setter<T1> &v) {return SetterArray<T,T1>(*this,v);}
|
||||
SetterArrayIndex<T> operator [](int v) {return SetterArrayIndex<T>(*this,v);}
|
||||
};
|
||||
|
||||
|
||||
// macro wrapper for building positional argument setters
|
||||
template <typename T> struct Twrap{using type = T;};
|
||||
#define $(k) Setter([](const auto a) constexpr -> decltype(&decltype(a)::type::k) {return &decltype(a)::type::k;})
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
void $F(T &t, const Ts&... ts) // fills all SetterVal's member pointers with it's values
|
||||
{
|
||||
auto filler = [](T &t, const auto &arg){
|
||||
if constexpr(arg.op == AssignArrayPtrWithLength)
|
||||
{
|
||||
t.*arg.arr.member.func(Twrap<T>{}) = arg.val.array;
|
||||
t.*arg.arr.count.func(Twrap<T>{}) = arg.val.count;
|
||||
}
|
||||
else if constexpr(arg.op == AssignArrayCopyWithLength)
|
||||
{
|
||||
for(int i = 0; i < arg.val.count; i++)
|
||||
(t.*arg.member.func(Twrap<T>{}))[i] = arg.val.array[i];
|
||||
t.*arg.arr.count.func(Twrap<T>{}) = arg.val.count;
|
||||
}
|
||||
else if constexpr(arg.op == AssignArrayByIndex)
|
||||
(t.*arg.arr.member.func(Twrap<T>{}))[arg.arr.idx] = arg.val;
|
||||
else if constexpr(arg.op == AssignArrayCopy)
|
||||
for(int i = 0; i < arg.val.count; i++)
|
||||
(t.*arg.member.func(Twrap<T>{}))[i] = arg.val.array[i];
|
||||
else if constexpr(arg.op == CallMethod)
|
||||
arg.vals.CallMember(arg.func.func(Twrap<T>{}), t);
|
||||
else if constexpr(arg.op == UpdateObject)
|
||||
arg.val(t.*arg.member.func(Twrap<T>{}));
|
||||
else if constexpr(arg.op == AssignObject)
|
||||
{
|
||||
t.*arg.member.func(Twrap<T>{}) = {};
|
||||
arg.val(t.*arg.member.func(Twrap<T>{}));
|
||||
}
|
||||
else if constexpr(arg.op == AppendObject)
|
||||
t.*arg.member.func(Twrap<T>{}) += arg.val;
|
||||
else if constexpr(arg.op == SetTrue)
|
||||
{
|
||||
auto v = t.*arg.func(Twrap<T>{});
|
||||
t.*arg.func(Twrap<T>{}) = (decltype(v))1;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto v = t.*arg.member.func(Twrap<T>{});
|
||||
t.*arg.member.func(Twrap<T>{}) = (decltype(v))(arg.val);
|
||||
}
|
||||
};
|
||||
(filler(t,ts),...);
|
||||
}
|
||||
|
||||
// wrapper for inline constructors with FillStructure
|
||||
template <typename T, typename... Ts>
|
||||
T $M(T t, const Ts&... ts)
|
||||
{
|
||||
$F(t, ts...);
|
||||
return t;
|
||||
}
|
||||
|
||||
#endif // POSITIONAL_UTL_H
|
|
@ -22,8 +22,9 @@
|
|||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "vulkan_pipeline_utl.h"
|
||||
#include "vulkan_utl.h"
|
||||
#include "vulkan_texture_utl.h"
|
||||
#include <string.h>
|
||||
|
||||
#include <math.h>
|
||||
|
@ -251,7 +252,12 @@ struct ComputeApplication {
|
|||
}
|
||||
template <typename Codec>
|
||||
void run(bool p010, const char *filename) {
|
||||
context.Create("streamingengine", "vulkan-playground");
|
||||
#ifndef NDEBUG
|
||||
#define ENABLE_VALIDATION_LAYERS 1
|
||||
#else
|
||||
#define ENABLE_VALIDATION_LAYERS 0
|
||||
#endif
|
||||
context.Create("streamingengine", "vulkan-playground", ENABLE_VALIDATION_LAYERS);
|
||||
dev.Create(context.FindPhysicalDevice(), VK_QUEUE_COMPUTE_BIT);
|
||||
dev.CreateDevice(context);
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
#include "vulkan_pipeline_utl.h"
|
||||
#include "vulkan_utl.h"
|
||||
#include "vulkan_framebuffer_utl.h"
|
||||
#include "vulkan_texture_utl.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <vulkan/vulkan_xlib.h>
|
||||
// use glfw for temporary swapchain window
|
||||
|
@ -255,7 +259,12 @@ struct GraphicsApplication
|
|||
|
||||
void Run()
|
||||
{
|
||||
context.Create("streamingengine", "vulkan-playground-server");
|
||||
#ifndef NDEBUG
|
||||
#define ENABLE_VALIDATION_LAYERS 1
|
||||
#else
|
||||
#define ENABLE_VALIDATION_LAYERS 0
|
||||
#endif
|
||||
context.Create("streamingengine", "vulkan-playground-server", ENABLE_VALIDATION_LAYERS);
|
||||
dev.Create(context.FindPhysicalDevice(), VK_QUEUE_GRAPHICS_BIT);
|
||||
dev.CreateDevice(context);
|
||||
int width = 800;
|
||||
|
|
283
vulkan_framebuffer_utl.h
Normal file
283
vulkan_framebuffer_utl.h
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2019 Collabora Ltd.
|
||||
* Copyright (c) 2024 mittorn
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef VULKAN_FRAMEBUFFER_UTL_H
|
||||
#define VULKAN_FRAMEBUFFER_UTL_H
|
||||
#include "vulkan_utl.h"
|
||||
struct VulkanFramebuffer
|
||||
{
|
||||
VkDevice device = NULL;
|
||||
struct
|
||||
{
|
||||
VkImage image;
|
||||
VkDeviceMemory mem;
|
||||
VkImageView view;
|
||||
VkFormat format;
|
||||
bool allocated = false;
|
||||
} depth = {};
|
||||
|
||||
|
||||
VkImageView color_view = NULL;
|
||||
|
||||
uint32_t width, height;
|
||||
VkFramebuffer frame_buffer = NULL;
|
||||
VkRenderPass render_pass = NULL;
|
||||
void Create(VkDevice d)
|
||||
{
|
||||
device = d;
|
||||
}
|
||||
void Destroy()
|
||||
{
|
||||
if(!device)
|
||||
return;
|
||||
if(color_view)
|
||||
vkDestroyImageView(device, color_view, NULL);
|
||||
color_view = NULL;
|
||||
if(depth.view)
|
||||
vkDestroyImageView(device, depth.view, NULL);
|
||||
depth.view = NULL;
|
||||
if(depth.allocated)
|
||||
{
|
||||
if(depth.image)
|
||||
vkDestroyImage(device, depth.image, NULL);
|
||||
depth.image = NULL;
|
||||
vkFreeMemory(device, depth.mem, NULL);
|
||||
depth.mem = NULL;
|
||||
depth.allocated = false;
|
||||
}
|
||||
|
||||
if(frame_buffer)
|
||||
vkDestroyFramebuffer(device, frame_buffer, NULL);
|
||||
frame_buffer = NULL;
|
||||
|
||||
if(render_pass)
|
||||
vkDestroyRenderPass(device, render_pass, NULL);
|
||||
render_pass = NULL;
|
||||
}
|
||||
void ImportDepthAttachment(VkImage image, VkFormat format)
|
||||
{
|
||||
depth.image = image;
|
||||
VkImageViewCreateInfo image_view = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = depth.image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
|
||||
VK_CHECK_RESULT(vkCreateImageView(device, &image_view, NULL, &depth.view));
|
||||
}
|
||||
|
||||
void CreateDepthAttachment(VulkanDevice &dev, VkFormat format, uint32_t w, uint32_t h)
|
||||
{
|
||||
depth.format = format;
|
||||
|
||||
VkImageCreateInfo image = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = { .width = w, .height = h, .depth = 1 },
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
};
|
||||
VkImage dimage;
|
||||
VK_CHECK_RESULT(vkCreateImage(device, &image, NULL, &dimage));
|
||||
|
||||
VkMemoryRequirements mem_reqs;
|
||||
vkGetImageMemoryRequirements(device, dimage, &mem_reqs);
|
||||
|
||||
VkMemoryAllocateInfo mem_alloc = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = mem_reqs.size,
|
||||
};
|
||||
|
||||
if (!dev.GetMemoryType(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
&mem_alloc.memoryTypeIndex))
|
||||
printf("Could not find memory type.\n");
|
||||
|
||||
VK_CHECK_RESULT(vkAllocateMemory(device, &mem_alloc, NULL, &depth.mem));
|
||||
VK_CHECK_RESULT(vkBindImageMemory(device, dimage, depth.mem, 0));
|
||||
ImportDepthAttachment(dimage, format);
|
||||
depth.allocated = true;
|
||||
}
|
||||
|
||||
|
||||
void Init(VkImage color_image, VkFormat color_format, uint32_t w, uint32_t h)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
|
||||
// Set up separate renderpass with references to the color and depth
|
||||
// attachments
|
||||
VkAttachmentDescription attachmentDescs[2];
|
||||
|
||||
// Init attachment properties
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
attachmentDescs[i].flags = 0;
|
||||
attachmentDescs[i].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachmentDescs[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attachmentDescs[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachmentDescs[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachmentDescs[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
if (i == 1) {
|
||||
attachmentDescs[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attachmentDescs[i].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
} else {
|
||||
attachmentDescs[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attachmentDescs[i].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Formats
|
||||
attachmentDescs[0].format = color_format;
|
||||
attachmentDescs[1].format = depth.format;
|
||||
|
||||
VkAttachmentReference colorReferences[1] = {
|
||||
{ .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }
|
||||
};
|
||||
|
||||
VkAttachmentReference depthReference = {
|
||||
.attachment = 1,
|
||||
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||
};
|
||||
|
||||
VkSubpassDescription subpass = {
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = ARRAY_SIZE(colorReferences),
|
||||
.pColorAttachments = colorReferences,
|
||||
.pDepthStencilAttachment = &depthReference,
|
||||
};
|
||||
|
||||
// Use subpass dependencies for attachment layput transitions
|
||||
VkSubpassDependency dependencies[2] = {
|
||||
VkSubpassDependency{
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT,
|
||||
},
|
||||
VkSubpassDependency{
|
||||
.srcSubpass = 0,
|
||||
.dstSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||||
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT,
|
||||
}
|
||||
};
|
||||
|
||||
VkRenderPassCreateInfo renderPassInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = 2,
|
||||
.pAttachments = attachmentDescs,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 2,
|
||||
.pDependencies = dependencies,
|
||||
};
|
||||
|
||||
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, NULL, &render_pass));
|
||||
|
||||
VkImageViewCreateInfo imageView = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = color_image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = color_format,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
|
||||
VK_CHECK_RESULT(vkCreateImageView(device, &imageView, NULL, &color_view));
|
||||
|
||||
VkImageView attachments[2] = { color_view, depth.view };
|
||||
|
||||
VkFramebufferCreateInfo fbufCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.renderPass = render_pass,
|
||||
.attachmentCount = ARRAY_SIZE(attachments),
|
||||
.pAttachments = attachments,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.layers = 1,
|
||||
};
|
||||
|
||||
VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, NULL, &frame_buffer));
|
||||
}
|
||||
|
||||
void BeginRenderPass(VkCommandBuffer cmdBuffer)
|
||||
{
|
||||
VkClearValue clearValues[2] = { { .color = { { 0.0f, 0.0f, 0.0f, 0.0f } } },
|
||||
{ .depthStencil = { 1.0f, 0 } } };
|
||||
|
||||
VkRenderPassBeginInfo renderPassBeginInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = render_pass,
|
||||
.framebuffer = frame_buffer,
|
||||
.renderArea = { .extent = { .width = width,
|
||||
.height = height } },
|
||||
.clearValueCount = ARRAY_SIZE(clearValues),
|
||||
.pClearValues = clearValues,
|
||||
};
|
||||
|
||||
vkCmdBeginRenderPass(cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
}
|
||||
void SetViewportAndScissor(VkCommandBuffer cmdBuffer)
|
||||
{
|
||||
VkViewport viewport = { .width = (float)width,
|
||||
.height = (float)height,
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f };
|
||||
vkCmdSetViewport(cmdBuffer, 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor = { .offset = { .x = 0, .y = 0 },
|
||||
.extent = { .width = width,
|
||||
.height = height } };
|
||||
vkCmdSetScissor(cmdBuffer, 0, 1, &scissor);
|
||||
}
|
||||
|
||||
~VulkanFramebuffer()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
};
|
||||
#endif // VULKAN_FRAMEBUFFER_UTL_H
|
354
vulkan_pipeline_utl.h
Normal file
354
vulkan_pipeline_utl.h
Normal file
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* Copyright (c) 2024 mittorn
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef VULKAN_PIPELINE_UTL_H
|
||||
#define VULKAN_PIPELINE_UTL_H
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#ifndef VK_CHECK_RESULT
|
||||
#define VK_CHECK_RESULT(f) \
|
||||
{ \
|
||||
VkResult res = (f); \
|
||||
if (res != VK_SUCCESS) \
|
||||
{ \
|
||||
printf("Fatal : VkResult is %d in %s at line %d\n", res, __FILE__, __LINE__); \
|
||||
assert(res == VK_SUCCESS); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include "positional_utl.h"
|
||||
|
||||
template<class A, class B>
|
||||
struct CompareTypes_w {
|
||||
constexpr static bool res = false;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct CompareTypes_w<T, T>{
|
||||
constexpr static bool res = true;
|
||||
};
|
||||
#define CompareTypes(A,B) (CompareTypes_w<A,B>::res)
|
||||
|
||||
template <typename A>
|
||||
void BadStaticAssert(const A &arg)
|
||||
{
|
||||
static_assert(!CompareTypes(A,A), "BadStaticAssert");
|
||||
}
|
||||
|
||||
struct BaseVulkanPipeline
|
||||
{
|
||||
VkDevice device = NULL;
|
||||
VkPipeline pipeline = NULL;
|
||||
VkPipelineLayout pipelineLayout = NULL;
|
||||
|
||||
VkDescriptorPool descriptorPool = NULL;
|
||||
VkDescriptorSetLayout descriptorSetLayout = NULL;
|
||||
|
||||
template <typename... Args>
|
||||
void CreateDescriptorSetLayout(const Args&... arguments)
|
||||
{
|
||||
const VkDescriptorSetLayoutBinding descriptorSetLayoutBinding[sizeof... (Args)] = {arguments...};
|
||||
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
|
||||
descriptorSetLayoutCreateInfo.bindingCount = sizeof... (Args);
|
||||
descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBinding;
|
||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCreateInfo, NULL, &descriptorSetLayout));
|
||||
}
|
||||
|
||||
// todo: calculate based on layout?
|
||||
// todo: no way to pass flags!
|
||||
template <typename... Args>
|
||||
void CreatePool(size_t maxSets, const Args&... arguments)
|
||||
{
|
||||
const VkDescriptorPoolSize descriptorPoolSize[sizeof... (Args)] = {arguments...};
|
||||
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
|
||||
descriptorPoolCreateInfo.maxSets = maxSets; // we only need to allocate one descriptor set from the pool.
|
||||
descriptorPoolCreateInfo.poolSizeCount = sizeof... (Args);
|
||||
descriptorPoolCreateInfo.pPoolSizes = descriptorPoolSize;
|
||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, NULL, &descriptorPool));
|
||||
}
|
||||
template <typename... Args>
|
||||
void WriteDescriptors(const Args&... arguments)
|
||||
{
|
||||
const VkWriteDescriptorSet writeDescriptorSet[sizeof... (Args)] = {arguments...};
|
||||
vkUpdateDescriptorSets(device, sizeof... (Args), writeDescriptorSet, 0, NULL);
|
||||
}
|
||||
|
||||
VkDescriptorSet AllocateSingleDescriptorSet(VkDescriptorSetLayout layout = NULL)
|
||||
{
|
||||
VkDescriptorSet ret;
|
||||
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
|
||||
if(!layout)
|
||||
layout = descriptorSetLayout;
|
||||
descriptorSetAllocateInfo.descriptorPool = descriptorPool; // pool to allocate from.
|
||||
descriptorSetAllocateInfo.descriptorSetCount = 1; // allocate a single descriptor set.
|
||||
descriptorSetAllocateInfo.pSetLayouts = &layout;
|
||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
VkPipelineShaderStageCreateInfo ShaderFromFile(VkShaderModule &outShaderModule, const char *filename, VkShaderStageFlagBits stage, const char *entrypoint = "main")
|
||||
{
|
||||
// todo: rewrite this in safer way
|
||||
FILE* fp = fopen(filename, "rb");
|
||||
if (fp == NULL) {
|
||||
printf("Could not find or open file: %s\n", filename);
|
||||
}
|
||||
|
||||
// get file size.
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long filesize = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
size_t filesizepadded = long(ceil(filesize / 4.0)) * 4;
|
||||
|
||||
uint32_t contents[filesizepadded/4];
|
||||
char *str = (char*) contents;
|
||||
fread(contents, filesize, sizeof(char), fp);
|
||||
fclose(fp);
|
||||
|
||||
// data padding.
|
||||
for (int i = filesize; i < filesizepadded; i++) {
|
||||
str[i] = 0;
|
||||
}
|
||||
|
||||
VkShaderModuleCreateInfo info = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
||||
info.codeSize = filesizepadded;
|
||||
info.pCode = contents;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateShaderModule(device, &info, NULL, &outShaderModule));
|
||||
|
||||
return $M(VkPipelineShaderStageCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO},
|
||||
$(stage) = stage,
|
||||
$(module) = outShaderModule,
|
||||
$(pName) = entrypoint);
|
||||
}
|
||||
static VkPipelineInputAssemblyStateCreateInfo AssemblyTopology(VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, bool restart = false, VkPipelineInputAssemblyStateCreateFlags flags = 0)
|
||||
{
|
||||
return {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = topology,
|
||||
.primitiveRestartEnable = restart
|
||||
};
|
||||
}
|
||||
template <typename... Args>
|
||||
static VkPipelineRasterizationStateCreateInfo RasterMode(VkFrontFace frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL, VkCullModeFlags cull = VK_CULL_MODE_BACK_BIT, float lineWidth = 1.0f, bool depthClampEnable = false, const Args&... arguments)
|
||||
{
|
||||
return $M(VkPipelineRasterizationStateCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO},
|
||||
$(depthClampEnable) = depthClampEnable, $(polygonMode) = polygonMode,
|
||||
$(cullMode) = cull, $(frontFace) = frontFace, $(lineWidth) = lineWidth,
|
||||
arguments...);
|
||||
}
|
||||
|
||||
struct BlendOp
|
||||
{
|
||||
VkBlendOp op;
|
||||
VkBlendFactor srcFactor;
|
||||
VkBlendFactor dstFactor;
|
||||
};
|
||||
|
||||
static VkPipelineColorBlendAttachmentState BlendAttachment(bool enable = false, const BlendOp &color = BlendOp{}, const BlendOp& alpha = BlendOp{}, VkColorComponentFlags colorWriteMask = 0xf)
|
||||
{
|
||||
return {
|
||||
.blendEnable = enable,
|
||||
.srcColorBlendFactor = color.srcFactor,
|
||||
.dstColorBlendFactor = color.dstFactor,
|
||||
.colorBlendOp = color.op,
|
||||
.srcAlphaBlendFactor = alpha.srcFactor,
|
||||
.dstAlphaBlendFactor = alpha.dstFactor,
|
||||
.alphaBlendOp = alpha.op,
|
||||
.colorWriteMask = 0xf
|
||||
};
|
||||
}
|
||||
|
||||
// todo: array initializer?
|
||||
template <typename... Args>
|
||||
static VkPipelineColorBlendStateCreateInfo ColorBlend(const VkPipelineColorBlendAttachmentState& attachment = BlendAttachment(), const Args&... arguments)
|
||||
{
|
||||
return $M(VkPipelineColorBlendStateCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO},
|
||||
$(attachmentCount), $(pAttachments) = &attachment,arguments...);
|
||||
}
|
||||
|
||||
// todo: check this defaults
|
||||
template <typename... Args>
|
||||
static VkPipelineDepthStencilStateCreateInfo DepthStencil(bool test = false, bool write = false,VkCompareOp compare = VK_COMPARE_OP_LESS_OR_EQUAL, VkCompareOp stencil = VK_COMPARE_OP_ALWAYS, const Args&... arguments )
|
||||
{
|
||||
return $M(VkPipelineDepthStencilStateCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO},
|
||||
$(depthTestEnable) = test, $(depthWriteEnable) = write, $(depthCompareOp) = compare,
|
||||
$(front) = Vals($(compareOp) = stencil), $(back) = Vals($(compareOp) = stencil),
|
||||
arguments...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static VkPipelineViewportStateCreateInfo ViewportState(const Args&... arguments)
|
||||
{
|
||||
return $M(VkPipelineViewportStateCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO},
|
||||
$(viewportCount), $(scissorCount), arguments...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static VkPipelineMultisampleStateCreateInfo MultisampleState( VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT, const Args&... arguments)
|
||||
{
|
||||
return $M(VkPipelineMultisampleStateCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO},
|
||||
$(rasterizationSamples) = samples, arguments...);
|
||||
}
|
||||
ARRAY_WRAPPER(VkDynamicState, DynamicStates);
|
||||
ARRAY_WRAPPER(VkPipelineShaderStageCreateInfo,Stages);
|
||||
ARRAY_WRAPPER(VkVertexInputBindingDescription, VertexBindings);
|
||||
ARRAY_WRAPPER(VkVertexInputAttributeDescription,VertexAttributes);
|
||||
|
||||
VkVertexInputBindingDescription VertexBinding(uint32_t binding, uint32_t stride, VkVertexInputRate inputRate = VK_VERTEX_INPUT_RATE_VERTEX )
|
||||
{
|
||||
return {binding, stride, inputRate};
|
||||
}
|
||||
VkVertexInputAttributeDescription VertAttrib(uint32_t location, uint32_t binding, uint32_t offset, VkFormat format = VK_FORMAT_R32G32B32_SFLOAT)
|
||||
{
|
||||
return {location, binding, format, offset};
|
||||
}
|
||||
|
||||
template <typename Stages, typename VertexBindings, typename VertexAttributes, typename DynamicStates, typename... Ts>
|
||||
void CreateGraphicsPipeline(VkRenderPass renderPass, const Stages &stages,
|
||||
const VertexBindings &vertexBindings,
|
||||
const VertexAttributes &vertexAttributes,
|
||||
const DynamicStates &dynamicStates,const Ts&... args)
|
||||
{
|
||||
|
||||
const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = $M(
|
||||
VkPipelineLayoutCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO},
|
||||
$(setLayoutCount), $(pSetLayouts) &= descriptorSetLayout);
|
||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, NULL, &pipelineLayout));
|
||||
|
||||
VkGraphicsPipelineCreateInfo info = {VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
|
||||
|
||||
const VkPipelineDynamicStateCreateInfo dynamic_state = $M(
|
||||
VkPipelineDynamicStateCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO},
|
||||
$(pDynamicStates).ptrWithLength($(dynamicStateCount)) = dynamicStates);
|
||||
const VkPipelineVertexInputStateCreateInfo vertex_input_state = $M(
|
||||
VkPipelineVertexInputStateCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO},
|
||||
$(pVertexBindingDescriptions).ptrWithLength($(vertexBindingDescriptionCount)) = vertexBindings,
|
||||
$(pVertexAttributeDescriptions).ptrWithLength($(vertexAttributeDescriptionCount)) = vertexAttributes);
|
||||
const VkPipelineInputAssemblyStateCreateInfo asmInfo= AssemblyTopology();
|
||||
info.pInputAssemblyState = &asmInfo;
|
||||
const VkPipelineViewportStateCreateInfo viewportInfo = ViewportState();
|
||||
info.pViewportState = &viewportInfo;
|
||||
const VkPipelineMultisampleStateCreateInfo multisample = MultisampleState();
|
||||
info.pMultisampleState = &multisample;
|
||||
const VkPipelineRasterizationStateCreateInfo rasterMode = RasterMode();
|
||||
info.pRasterizationState = &rasterMode;
|
||||
const VkPipelineDepthStencilStateCreateInfo depthStencil = DepthStencil();
|
||||
info.pDepthStencilState = &depthStencil;
|
||||
const VkPipelineColorBlendAttachmentState blendAttachment = BlendAttachment();
|
||||
const VkPipelineColorBlendStateCreateInfo blendState = ColorBlend(blendAttachment);
|
||||
info.pColorBlendState = &blendState;
|
||||
|
||||
auto filler = [](VkGraphicsPipelineCreateInfo &info, const auto &arg){
|
||||
if constexpr(CompareTypes(decltype(arg),const VkPipelineInputAssemblyStateCreateInfo &))info.pInputAssemblyState = &arg;
|
||||
else if constexpr(CompareTypes(decltype(arg),const VkPipelineViewportStateCreateInfo&))info.pViewportState = &arg;
|
||||
else if constexpr(CompareTypes(decltype(arg),const VkPipelineMultisampleStateCreateInfo&))info.pMultisampleState = &arg;
|
||||
else if constexpr(CompareTypes(decltype(arg),const VkPipelineRasterizationStateCreateInfo&))info.pRasterizationState = &arg;
|
||||
else if constexpr(CompareTypes(decltype(arg),const VkPipelineDepthStencilStateCreateInfo&))info.pDepthStencilState = &arg;
|
||||
else if constexpr(CompareTypes(decltype(arg),const VkPipelineColorBlendStateCreateInfo&))info.pColorBlendState = &arg;
|
||||
else $F(info,arg);
|
||||
};
|
||||
|
||||
$F(info,
|
||||
$(pStages).ptrWithLength($(stageCount)) = stages,
|
||||
$(pVertexInputState) = &vertex_input_state,
|
||||
$(pDynamicState) = &dynamic_state,
|
||||
$(renderPass) = renderPass,
|
||||
$(layout) = pipelineLayout
|
||||
);
|
||||
(filler(info,args),...);
|
||||
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &info, NULL, &pipeline);
|
||||
}
|
||||
|
||||
void CreateComputePipeline(const VkPipelineShaderStageCreateInfo &shaderStageCreateInfo)
|
||||
{
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||
pipelineLayoutCreateInfo.setLayoutCount = 1;
|
||||
pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayout;
|
||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, NULL, &pipelineLayout));
|
||||
VkComputePipelineCreateInfo pipelineCreateInfo = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
|
||||
pipelineCreateInfo.stage = shaderStageCreateInfo;
|
||||
pipelineCreateInfo.layout = pipelineLayout;
|
||||
VK_CHECK_RESULT(vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, NULL, &pipeline));
|
||||
|
||||
}
|
||||
static VkDescriptorSetLayoutBinding BasicBinding(uint32_t binding, VkDescriptorType descriptorType, uint32_t descriptorCount = 1, VkShaderStageFlags stageFlags = 0, const VkSampler* pImmutableSamplers = NULL )
|
||||
{
|
||||
return {binding, descriptorType, descriptorCount, stageFlags, pImmutableSamplers};
|
||||
}
|
||||
static VkDescriptorPoolSize BasicPoolSize(VkDescriptorType type, uint32_t descriptorCount)
|
||||
{
|
||||
return {type, descriptorCount};
|
||||
}
|
||||
VkDescriptorImageInfo ImageDescriptor(VkImageView imageView, VkImageLayout imageLayout, VkSampler sampler = 0)
|
||||
{
|
||||
return {sampler, imageView, imageLayout};
|
||||
}
|
||||
// todo: different types
|
||||
VkWriteDescriptorSet ImageWrite(VkDescriptorSet dstSet, uint32_t binding, const VkDescriptorImageInfo &info)
|
||||
{
|
||||
VkWriteDescriptorSet wr = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
||||
wr.dstSet = dstSet;
|
||||
wr.dstBinding = binding;
|
||||
wr.descriptorCount = 1;
|
||||
wr.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
wr.pImageInfo = &info;
|
||||
return wr;
|
||||
}
|
||||
|
||||
VkWriteDescriptorSet BufferWrite(VkDescriptorSet dstSet, uint32_t binding, const VkDescriptorBufferInfo &info)
|
||||
{
|
||||
VkWriteDescriptorSet wr = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
||||
wr.dstSet = dstSet;
|
||||
wr.dstBinding = binding;
|
||||
wr.descriptorCount = 1;
|
||||
wr.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
wr.pBufferInfo = &info;
|
||||
return wr;
|
||||
}
|
||||
void Destroy()
|
||||
{
|
||||
if(!device)
|
||||
return;
|
||||
if(descriptorPool)
|
||||
vkDestroyDescriptorPool(device, descriptorPool, NULL);
|
||||
descriptorPool = NULL;
|
||||
if(descriptorSetLayout)
|
||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, NULL);
|
||||
descriptorSetLayout = NULL;
|
||||
if(pipelineLayout)
|
||||
vkDestroyPipelineLayout(device, pipelineLayout, NULL);
|
||||
pipelineLayout = NULL;
|
||||
if(pipeline)
|
||||
vkDestroyPipeline(device, pipeline, NULL);
|
||||
pipeline = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // VULKAN_PIPELINE_UTL_H
|
296
vulkan_texture_utl.h
Normal file
296
vulkan_texture_utl.h
Normal file
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2019 Collabora Ltd.
|
||||
* Copyright (c) 2024 mittorn
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef VULKAN_TEXTURE_UTL_H
|
||||
#define VULKAN_TEXTURE_UTL_H
|
||||
#include "vulkan_utl.h"
|
||||
struct VulkanTexture
|
||||
{
|
||||
// keep device for destroy
|
||||
VkDevice owning_device = NULL;
|
||||
VkImage image = NULL;
|
||||
VkImageLayout image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkDeviceMemory device_memory = NULL;
|
||||
VkSampler sampler = NULL;
|
||||
VkImageView view = NULL;
|
||||
|
||||
uint32_t width = 0, height = 0;
|
||||
uint32_t mip_levels = 0;
|
||||
uint32_t layer_count = 0;
|
||||
|
||||
bool created_from_image = false;
|
||||
static VkAccessFlags GetAccessFlags(VkImageLayout layout)
|
||||
{
|
||||
switch (layout) {
|
||||
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
|
||||
case VK_IMAGE_LAYOUT_GENERAL: return 0;
|
||||
case VK_IMAGE_LAYOUT_PREINITIALIZED: return VK_ACCESS_HOST_WRITE_BIT;
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: return VK_ACCESS_TRANSFER_READ_BIT;
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
return VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
return VK_ACCESS_SHADER_READ_BIT;
|
||||
default: printf("Unhandled access mask case for layout %d.\n", layout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SetImageLayout(VkCommandBuffer cmd_buffer, VkImage image, VkImageLayout src_layout, VkImageLayout dst_layout, VkImageSubresourceRange subresource_range)
|
||||
{
|
||||
VkImageMemoryBarrier imageMemoryBarrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = GetAccessFlags(src_layout),
|
||||
.dstAccessMask = GetAccessFlags(dst_layout),
|
||||
.oldLayout = src_layout,
|
||||
.newLayout = dst_layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = image,
|
||||
.subresourceRange = subresource_range,
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier(cmd_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, NULL, 0, NULL,
|
||||
1, &imageMemoryBarrier);
|
||||
}
|
||||
|
||||
VkDescriptorImageInfo GetDescriptor()
|
||||
{
|
||||
VkDescriptorImageInfo descriptor = {
|
||||
.sampler = sampler,
|
||||
.imageView = view,
|
||||
.imageLayout = image_layout,
|
||||
};
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
void Destroy()
|
||||
{
|
||||
if (view)
|
||||
vkDestroyImageView(owning_device, view, NULL);
|
||||
view = NULL;
|
||||
if (!created_from_image && image)
|
||||
vkDestroyImage(owning_device, image, NULL);
|
||||
image = NULL;
|
||||
if (sampler)
|
||||
vkDestroySampler(owning_device, sampler, NULL);
|
||||
sampler = NULL;
|
||||
if (device_memory)
|
||||
vkFreeMemory(owning_device, device_memory, NULL);
|
||||
device_memory = 0;
|
||||
}
|
||||
|
||||
void CreateImage(VkDevice device, VkFormat format)
|
||||
{
|
||||
VkImageCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = { .width = width, .height = height, .depth = 1 },
|
||||
.mipLevels = mip_levels,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
|
||||
};
|
||||
owning_device = device;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateImage(device, &info, NULL, &image));
|
||||
|
||||
}
|
||||
|
||||
static void AllocateImageMemory(VkImage image,
|
||||
VulkanDevice dev,
|
||||
VkMemoryRequirements *mem_reqs,
|
||||
VkMemoryAllocateInfo *mem_info,
|
||||
VkDeviceMemory *out_memory)
|
||||
{
|
||||
vkGetImageMemoryRequirements(dev.device, image, mem_reqs);
|
||||
|
||||
mem_info->allocationSize = mem_reqs->size;
|
||||
|
||||
dev.GetMemoryType(mem_reqs->memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_info->memoryTypeIndex);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(dev.device, mem_info, NULL, out_memory));
|
||||
VK_CHECK_RESULT(vkBindImageMemory(dev.device, image, *out_memory, 0));
|
||||
}
|
||||
|
||||
void TransferImage(VulkanDevice &dev, VkQueue copy_queue, VkBuffer staging_buffer, VkImageLayout dest_layout, VkBufferImageCopy *buffer_image_copies)
|
||||
{
|
||||
VkImageSubresourceRange subresource_range = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = mip_levels,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
VkCommandBuffer copy_cmd = dev.CreateCommandBuffer();
|
||||
SetImageLayout(copy_cmd, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresource_range);
|
||||
|
||||
vkCmdCopyBufferToImage(copy_cmd, staging_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
mip_levels, buffer_image_copies);
|
||||
|
||||
image_layout = dest_layout;
|
||||
SetImageLayout(copy_cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dest_layout, subresource_range);
|
||||
|
||||
dev.FlushCommandBuffer(copy_cmd, copy_queue);
|
||||
}
|
||||
|
||||
uint8_t *BeginUpload(VulkanDevice &dev, VkMemoryRequirements &mem_reqs, VkMemoryAllocateInfo &mem_info, VkBuffer &staging_buffer, VkDeviceMemory &staging_memory, VkDeviceSize size)
|
||||
{
|
||||
VkBufferCreateInfo bufferCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = size,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
||||
};
|
||||
VK_CHECK_RESULT(vkCreateBuffer(dev.device, &bufferCreateInfo, NULL, &staging_buffer));
|
||||
|
||||
vkGetBufferMemoryRequirements(dev.device, staging_buffer, &mem_reqs);
|
||||
|
||||
uint32_t type_index;
|
||||
dev.GetMemoryType(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &type_index);
|
||||
|
||||
mem_info = VkMemoryAllocateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = mem_reqs.size,
|
||||
.memoryTypeIndex = type_index,
|
||||
};
|
||||
|
||||
VK_CHECK_RESULT(vkAllocateMemory(dev.device, &mem_info, NULL, &staging_memory));
|
||||
VK_CHECK_RESULT(vkBindBufferMemory(dev.device, staging_buffer, staging_memory, 0));
|
||||
|
||||
uint8_t *data;
|
||||
VK_CHECK_RESULT(vkMapMemory(dev.device, staging_memory, 0, mem_reqs.size, 0, (void **)&data));
|
||||
return data;
|
||||
}
|
||||
|
||||
void EndUpload(VulkanDevice &dev, VkQueue copy_queue, bool alloc_mem, VkImageLayout dest_layout, VkMemoryRequirements &mem_reqs, VkMemoryAllocateInfo &mem_info, VkBuffer &staging_buffer, VkDeviceMemory &staging_memory, unsigned int depth, unsigned int *mip_offsets)
|
||||
{
|
||||
|
||||
VkBufferImageCopy buffer_image_copies[mip_levels];
|
||||
vkUnmapMemory(dev.device, staging_memory);
|
||||
unsigned int offset = 0;
|
||||
if(!mip_offsets)
|
||||
mip_offsets = &offset;
|
||||
for (uint32_t i = 0; i < mip_levels; i++) {
|
||||
buffer_image_copies[i] = VkBufferImageCopy {
|
||||
.bufferOffset = mip_offsets[i],
|
||||
.imageSubresource =
|
||||
{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = i,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.imageExtent = {
|
||||
.width = width >> i,
|
||||
.height = height >> i,
|
||||
.depth = depth >> i,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (alloc_mem)
|
||||
AllocateImageMemory(image, dev, &mem_reqs, &mem_info, &device_memory);
|
||||
TransferImage(dev, copy_queue, staging_buffer, dest_layout, buffer_image_copies);
|
||||
vkFreeMemory(dev.device, staging_memory, NULL);
|
||||
vkDestroyBuffer(dev.device, staging_buffer, NULL);
|
||||
}
|
||||
|
||||
void FromBuffer(VulkanDevice &dev, int w, int h, int mips, VkFormat format, VkQueue copy_queue, VkImageLayout dest_layout, uint8_t *buffer, VkDeviceSize size)
|
||||
{
|
||||
VkMemoryRequirements mem_reqs;
|
||||
VkMemoryAllocateInfo mem_info;
|
||||
VkBuffer staging_buffer;
|
||||
VkDeviceMemory staging_memory;
|
||||
width = w;
|
||||
height = h;
|
||||
mip_levels = mips;
|
||||
CreateImage(dev.device, format);
|
||||
uint8_t *data = BeginUpload(dev, mem_reqs, mem_info,staging_buffer, staging_memory, size);
|
||||
memcpy(data, buffer, size);
|
||||
EndUpload(dev, copy_queue, true, dest_layout, mem_reqs, mem_info, staging_buffer, staging_memory, 1, NULL);
|
||||
CreateSampler(dev.device);
|
||||
CreateImageView(dev.device, format);
|
||||
created_from_image = false;
|
||||
}
|
||||
|
||||
void CreateSampler(VkDevice device)
|
||||
{
|
||||
VkSamplerCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = VK_FILTER_LINEAR,
|
||||
.minFilter = VK_FILTER_LINEAR,
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
||||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
.mipLodBias = 0.0f,
|
||||
.anisotropyEnable = VK_TRUE,
|
||||
.maxAnisotropy = 8,
|
||||
.compareOp = VK_COMPARE_OP_NEVER,
|
||||
.minLod = 0.0f,
|
||||
.maxLod = (float)mip_levels,
|
||||
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
|
||||
};
|
||||
owning_device = device;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateSampler(device, &info, NULL, &sampler));
|
||||
|
||||
}
|
||||
void CreateImageView(VkDevice device, VkFormat format)
|
||||
{
|
||||
VkImageViewCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
.components = {
|
||||
.r = VK_COMPONENT_SWIZZLE_R,
|
||||
.g = VK_COMPONENT_SWIZZLE_G,
|
||||
.b = VK_COMPONENT_SWIZZLE_B,
|
||||
.a = VK_COMPONENT_SWIZZLE_A,
|
||||
},
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = mip_levels,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
owning_device = device;
|
||||
VK_CHECK_RESULT(vkCreateImageView(device, &info, NULL, &view));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // VULKAN_TEXTURE_UTL_H
|
1149
vulkan_utl.h
1149
vulkan_utl.h
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue