Split vulkan utility, use positional arguments in pipeline utility for extendable arguments

This commit is contained in:
mittorn 2024-10-16 08:08:58 +03:00
parent 0c0ee087c9
commit 91ff840a78
7 changed files with 1249 additions and 1131 deletions

277
positional_utl.h Normal file
View 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

View file

@ -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);

View file

@ -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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff