1458 lines
36 KiB
D
1458 lines
36 KiB
D
import vulkan_funcs;
|
|
import vulkan_logging;
|
|
import aliases;
|
|
import std.stdio;
|
|
import std.algorithm.comparison;
|
|
import core.stdc.string : strcmp;
|
|
import std.format : sformat;
|
|
import u = util : HashTable, Result, Logf, Log;
|
|
import a = alloc;
|
|
import p = platform;
|
|
import renderer;
|
|
|
|
bool g_VLAYER_SUPPORT = false;
|
|
|
|
const FRAME_OVERLAP = 2;
|
|
|
|
version(linux)
|
|
{
|
|
const string[] VULKAN_LIBS = [
|
|
"libvulkan.so.1",
|
|
"libvulkan.so",
|
|
];
|
|
}
|
|
|
|
version(Windows)
|
|
{
|
|
const string[] VULKAN_LIBS = [
|
|
"vulkan-1.dll",
|
|
];
|
|
}
|
|
|
|
const char*[] VK_INSTANCE_LAYERS = [];
|
|
const char*[] VK_INSTANCE_LAYERS_DEBUG = [ "VK_LAYER_KHRONOS_validation" ];
|
|
|
|
version(linux)
|
|
{
|
|
const char*[] VK_INSTANCE_EXT = [ cast(char*)VK_KHR_SURFACE_EXTENSION_NAME, cast(char*)VK_KHR_XCB_SURFACE_EXTENSION_NAME ];
|
|
const char*[] VK_INSTANCE_EXT_DEBUG = VK_INSTANCE_EXT ~ [ cast(char*)VK_EXT_DEBUG_UTILS_EXTENSION_NAME ];
|
|
}
|
|
|
|
version(Windows)
|
|
{
|
|
const char*[] VK_INSTANCE_EXT = [ cast(char*)VK_KHR_SURFACE_EXTENSION_NAME, cast(char*)VK_KHR_WIN32_SURFACE_EXTENSION_NAME ];
|
|
const char*[] VK_INSTANCE_EXT_DEBUG = VK_INSTANCE_EXT ~ [ cast(char*)VK_EXT_DEBUG_UTILS_EXTENSION_NAME ];
|
|
}
|
|
|
|
const char*[] VK_DEVICE_EXTENSIONS = [
|
|
cast(char*)VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
cast(char*)VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME,
|
|
];
|
|
|
|
const VkFormat[] VK_IMAGE_FORMATS = [
|
|
VK_FORMAT_R16G16B16A16_UNORM,
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
];
|
|
|
|
const VkImageUsageFlags VK_DRAW_IMAGE_USAGE_FLAGS = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
|
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
|
VK_IMAGE_USAGE_STORAGE_BIT |
|
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
|
|
enum StepInitialized : u32
|
|
{
|
|
Renderer = 1,
|
|
Instance,
|
|
Debug,
|
|
Surface,
|
|
Device,
|
|
Vma,
|
|
MappedBuffers, // TODO
|
|
FrameStructures,
|
|
Swapchain,
|
|
DrawImages,
|
|
Descriptors,
|
|
}
|
|
|
|
alias SI = StepInitialized;
|
|
|
|
enum DescType : u32
|
|
{
|
|
Shared = 0,
|
|
SampledImage,
|
|
Material,
|
|
Mesh,
|
|
|
|
Max,
|
|
}
|
|
|
|
alias DT = DescType;
|
|
|
|
struct PushConst
|
|
{
|
|
u32 texture;
|
|
}
|
|
|
|
struct Image
|
|
{
|
|
VkImage image;
|
|
VmaAllocation alloc;
|
|
VkFormat format;
|
|
VkImageLayout layout;
|
|
}
|
|
|
|
struct ImageView
|
|
{
|
|
Image base;
|
|
VkImageView view;
|
|
}
|
|
|
|
struct DescBindings
|
|
{
|
|
u32[] free;
|
|
u64 count;
|
|
HashTable!(string, u32) lookup_table;
|
|
}
|
|
|
|
struct Vulkan
|
|
{
|
|
a.Arena arena;
|
|
a.Arena[FRAME_OVERLAP] frame_arenas;
|
|
|
|
u.SLList!(SI) cleanup_list;
|
|
|
|
p.Window* window;
|
|
|
|
VkDebugUtilsMessengerEXT dbg_msg;
|
|
VkInstance instance;
|
|
VkSurfaceKHR surface;
|
|
VkPhysicalDevice physical_device;
|
|
VkDevice device;
|
|
VmaAllocator vma;
|
|
VkSwapchainKHR swapchain;
|
|
|
|
VkSurfaceFormatKHR surface_format;
|
|
VkPresentModeKHR present_mode;
|
|
VkExtent3D swapchain_extent;
|
|
|
|
ImageView[] present_images;
|
|
ImageView draw_image;
|
|
ImageView depth_image;
|
|
|
|
VkCommandPool[FRAME_OVERLAP] cmd_pools;
|
|
VkCommandBuffer[FRAME_OVERLAP] cmds;
|
|
|
|
VkSemaphore[] swapchain_sems;
|
|
VkSemaphore[FRAME_OVERLAP] render_sems;
|
|
VkFence[FRAME_OVERLAP] render_fences;
|
|
|
|
VkCommandPool imm_pool;
|
|
VkCommandBuffer imm_cmd;
|
|
VkFence imm_fence;
|
|
|
|
VkDescriptorPool desc_pool;
|
|
VkDescriptorSet[DT.max] desc_sets;
|
|
VkDescriptorSetLayout[DT.max] desc_layouts;
|
|
DescBindings[DT.max] desc_bindings;
|
|
|
|
VkSampler nearest_sampler;
|
|
|
|
VkPipelineLayout pipeline_layout;
|
|
|
|
QueueInfo queues;
|
|
}
|
|
|
|
struct QueueInfo
|
|
{
|
|
i32 gfx_index, tfer_index;
|
|
VkQueue gfx_queue, tfer_queue;
|
|
bool single_queue;
|
|
};
|
|
|
|
u.Result!(Vulkan)
|
|
Init(p.Window* window, u64 permanent_mem, u64 frame_mem)
|
|
{
|
|
bool success = true;
|
|
|
|
Vulkan vk = {
|
|
arena: a.CreateArena(permanent_mem),
|
|
frame_arenas: [
|
|
a.CreateArena(frame_mem),
|
|
a.CreateArena(frame_mem),
|
|
],
|
|
window: window,
|
|
};
|
|
|
|
Push(&vk, SI.Renderer);
|
|
|
|
success = LoadGlobalFunctions();
|
|
|
|
if (success) success = InitInstance(&vk);
|
|
|
|
if (success)
|
|
{
|
|
LoadInstanceFunctions(&vk);
|
|
EnableVLayers(&vk);
|
|
}
|
|
|
|
if (success) success = InitSurface(&vk);
|
|
if (success) success = InitDevice(&vk);
|
|
if (success) success = InitVMA(&vk);
|
|
if (success) success = CreateSwapchain(&vk);
|
|
if (success) success = CreateDrawImages(&vk);
|
|
if (success) success = InitFrameStructures(&vk);
|
|
if (success) success = InitDescriptors(&vk);
|
|
|
|
u.Result!(Vulkan) result = {
|
|
ok: success,
|
|
value: vk,
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
Result!(Shader)
|
|
BuildShader(Vulkan* vk, u8[] bytes)
|
|
{
|
|
Result!(Shader) shader;
|
|
|
|
VkShaderModuleCreateInfo shader_info = {
|
|
sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
codeSize: bytes.length,
|
|
pCode: cast(uint*)bytes.ptr,
|
|
};
|
|
|
|
VkResult result = vkCreateShaderModule(vk.device, &shader_info, null, &shader.value);
|
|
shader.ok = VkCheck("vkCreateShaderModule failure", result);
|
|
|
|
return shader;
|
|
}
|
|
|
|
Result!(VkPipeline)
|
|
CreateComputePipeline(Vulkan* vk, string shader)
|
|
{
|
|
Result!(VkPipeline) pipeline;
|
|
|
|
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
void
|
|
Destroy(Vulkan* vk)
|
|
{
|
|
alias N = u.Node!(SI);
|
|
assert(vk.cleanup_list.first != null, "node null");
|
|
for(N* node = vk.cleanup_list.first; node != null; node = node.next)
|
|
{
|
|
switch (node.value)
|
|
{
|
|
case SI.Renderer:
|
|
DestroyRenderer(vk);
|
|
break;
|
|
case SI.Instance:
|
|
Destroy(vk.instance);
|
|
break;
|
|
case SI.Debug:
|
|
Destroy(vk.dbg_msg, vk.instance);
|
|
break;
|
|
case SI.Surface:
|
|
Destroy(vk.surface, vk.instance);
|
|
break;
|
|
case SI.Device:
|
|
Destroy(vk.device);
|
|
break;
|
|
case SI.Vma:
|
|
Destroy(vk.vma);
|
|
break;
|
|
case SI.FrameStructures:
|
|
DestroyFS(vk);
|
|
break;
|
|
case SI.Swapchain:
|
|
Destroy(vk.swapchain, vk.present_images, vk.device);
|
|
break;
|
|
case SI.DrawImages:
|
|
Destroy(&vk.draw_image, &vk.depth_image, vk.device, vk.vma);
|
|
break;
|
|
case SI.Descriptors:
|
|
Destroy(vk.desc_pool, vk.desc_layouts, vk.pipeline_layout, vk.nearest_sampler, vk.device);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
InitDescriptors(Vulkan* vk)
|
|
{
|
|
Push(vk, SI.Descriptors);
|
|
|
|
bool success = true;
|
|
|
|
VkDescriptorPoolSize[] pool_sizes = [
|
|
{ type: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, descriptorCount: 4096 },
|
|
{ type: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 4096 },
|
|
{ type: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, descriptorCount: 4096 },
|
|
{ type: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount: 4096 },
|
|
{ type: VK_DESCRIPTOR_TYPE_SAMPLER, descriptorCount: 4096 },
|
|
];
|
|
|
|
VkDescriptorPoolCreateInfo pool_info = {
|
|
sType: VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
|
poolSizeCount: cast(u32)pool_sizes.length,
|
|
pPoolSizes: pool_sizes.ptr,
|
|
maxSets: 12,
|
|
flags: VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
|
|
};
|
|
|
|
VkResult result = vkCreateDescriptorPool(vk.device, &pool_info, null, &vk.desc_pool);
|
|
success = VkCheck("vkCreateDescriptorPool failure", result);
|
|
|
|
if (success)
|
|
{
|
|
VkDescriptorBindingFlags[4] shared_binding_flags;
|
|
shared_binding_flags[] = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT;
|
|
|
|
VkDescriptorSetLayoutBindingFlagsCreateInfo shared_flag_info = {
|
|
sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
|
bindingCount: cast(u32)shared_binding_flags.length,
|
|
pBindingFlags: shared_binding_flags.ptr,
|
|
};
|
|
|
|
VkDescriptorSetLayoutBinding[] shared_bindings = [
|
|
{ binding: 0, descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
|
|
{ binding: 1, descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
|
|
{ binding: 2, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
|
|
{ binding: 3, descriptorType: VK_DESCRIPTOR_TYPE_SAMPLER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
|
|
];
|
|
|
|
VkDescriptorSetLayoutCreateInfo shared_set_info = {
|
|
sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
|
pNext: &shared_flag_info,
|
|
bindingCount: cast(u32)shared_bindings.length,
|
|
pBindings: shared_bindings.ptr,
|
|
flags: VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
|
};
|
|
|
|
result = vkCreateDescriptorSetLayout(vk.device, &shared_set_info, null, &vk.desc_layouts[DT.Shared]);
|
|
success = VkCheck("vkCreateDescriptorSetLayout failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkDescriptorType[DT.max] type_lookup = [
|
|
DT.SampledImage: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
|
DT.Material: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
DT.Mesh: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
];
|
|
|
|
VkDescriptorBindingFlags bindless_flag = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT;
|
|
|
|
VkDescriptorSetLayoutBindingFlagsCreateInfo bindless_flag_info = {
|
|
sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
|
bindingCount: 1,
|
|
pBindingFlags: &bindless_flag,
|
|
};
|
|
|
|
VkDescriptorSetLayoutBinding binding = {
|
|
binding: 0,
|
|
descriptorCount: 1024,
|
|
stageFlags: VK_SHADER_STAGE_ALL,
|
|
};
|
|
|
|
VkDescriptorSetLayoutCreateInfo bindless_set_info = {
|
|
sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
|
pNext: &bindless_flag_info,
|
|
bindingCount: 1,
|
|
pBindings: &binding,
|
|
flags: VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
|
};
|
|
|
|
foreach(i; cast(u64)(DT.Shared+1) .. DT.max)
|
|
{
|
|
binding.descriptorType = type_lookup[i];
|
|
|
|
if (success)
|
|
{
|
|
result = vkCreateDescriptorSetLayout(vk.device, &bindless_set_info, null, vk.desc_layouts.ptr + i);
|
|
success = VkCheck("vkCreateDescriptorSetLayout failure", result);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkDescriptorSetAllocateInfo alloc_info = {
|
|
sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
descriptorSetCount: cast(u32)DT.max,
|
|
pSetLayouts: vk.desc_layouts.ptr,
|
|
descriptorPool: vk.desc_pool,
|
|
};
|
|
|
|
result = vkAllocateDescriptorSets(vk.device, &alloc_info, vk.desc_sets.ptr);
|
|
success = VkCheck("vkAllocateDescriptorSets failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkPushConstantRange const_range = {
|
|
offset: 0,
|
|
size: cast(VkDeviceSize)PushConst.sizeof,
|
|
stageFlags: VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT,
|
|
};
|
|
|
|
VkPipelineLayoutCreateInfo layout_info = {
|
|
sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
setLayoutCount: cast(u32)DT.max,
|
|
pushConstantRangeCount: 1,
|
|
pPushConstantRanges: &const_range,
|
|
pSetLayouts: vk.desc_layouts.ptr,
|
|
};
|
|
|
|
result = vkCreatePipelineLayout(vk.device, &layout_info, null, &vk.pipeline_layout);
|
|
success = VkCheck("vkCreatePipelineLayout failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
foreach(i; cast(u64)DT.min .. cast(u64)DT.max)
|
|
{
|
|
vk.desc_bindings[i].lookup_table = u.CreateHashTable!(string, u32)(8);
|
|
|
|
u32 DESC_MAX_BINDINGS = 512;
|
|
vk.desc_bindings[i].free = a.AllocArray!(u32)(&vk.arena, DESC_MAX_BINDINGS);
|
|
|
|
u32 free_count = 0;
|
|
for(i32 j = DESC_MAX_BINDINGS-1; j >= 0; j -= 1)
|
|
{
|
|
vk.desc_bindings[i].free[j] = cast(u32)free_count;
|
|
free_count += 1;
|
|
}
|
|
|
|
vk.desc_bindings[i].count = free_count;
|
|
}
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkPhysicalDeviceProperties props;
|
|
vkGetPhysicalDeviceProperties(vk.physical_device, &props);
|
|
|
|
VkSamplerCreateInfo sampler_info = {
|
|
sType: VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
|
magFilter: VK_FILTER_NEAREST,
|
|
minFilter: VK_FILTER_NEAREST,
|
|
addressModeU: VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
addressModeV: VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
addressModeW: VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
anisotropyEnable: VK_TRUE,
|
|
maxAnisotropy: props.limits.maxSamplerAnisotropy,
|
|
borderColor: VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
|
compareOp: VK_COMPARE_OP_ALWAYS,
|
|
mipmapMode: VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
|
};
|
|
|
|
result = vkCreateSampler(vk.device, &sampler_info, null, &vk.nearest_sampler);
|
|
success = VkCheck("vkCreateSampler failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkDescriptorImageInfo sampler_info = {
|
|
sampler: vk.nearest_sampler,
|
|
};
|
|
|
|
VkWriteDescriptorSet write = {
|
|
sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
dstSet: vk.desc_sets[DT.Shared],
|
|
dstBinding: 3,
|
|
descriptorCount: 1,
|
|
descriptorType: VK_DESCRIPTOR_TYPE_SAMPLER,
|
|
pImageInfo: &sampler_info,
|
|
};
|
|
|
|
vkUpdateDescriptorSets(vk.device, 1, &write, 0, null);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
VkCheck(string message, VkResult result)
|
|
{
|
|
bool success = true;
|
|
|
|
if (result != VK_SUCCESS)
|
|
{
|
|
success = false;
|
|
char[512] buf;
|
|
buf.sformat("%s: %s", message, VkResultStr(result));
|
|
Log(buf.ptr);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
InitFrameStructures(Vulkan* vk)
|
|
{
|
|
Push(vk, SI.FrameStructures);
|
|
|
|
bool success = true;
|
|
a.Arena* arena = &vk.frame_arenas[0];
|
|
|
|
VkSemaphoreCreateInfo sem_info = { sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
|
|
|
VkCommandPoolCreateInfo pool_info = {
|
|
sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
flags: VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
|
};
|
|
|
|
VkFenceCreateInfo fence_info = {
|
|
sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
flags: VK_FENCE_CREATE_SIGNALED_BIT,
|
|
};
|
|
|
|
VkCommandBufferAllocateInfo cmd_info = {
|
|
sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
commandBufferCount: 1,
|
|
level: VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
};
|
|
|
|
vk.swapchain_sems = a.AllocArray!(VkSemaphore)(arena, vk.present_images.length);
|
|
|
|
foreach(i; 0 .. vk.swapchain_sems.length)
|
|
{
|
|
VkResult result = vkCreateSemaphore(vk.device, &sem_info, null, vk.swapchain_sems.ptr + i);
|
|
success = VkCheck("vkCreateSemaphore failure", result);
|
|
}
|
|
|
|
foreach(i; 0 .. FRAME_OVERLAP)
|
|
{
|
|
VkResult result;
|
|
|
|
if (success)
|
|
{
|
|
result = vkCreateCommandPool(vk.device, &pool_info, null, vk.cmd_pools.ptr + i);
|
|
success = VkCheck("vkCreateCommandPool failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
cmd_info.commandPool = vk.cmd_pools[i];
|
|
result = vkAllocateCommandBuffers(vk.device, &cmd_info, vk.cmds.ptr + i);
|
|
success = VkCheck("vkAllocateCommandBuffers failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
result = vkCreateFence(vk.device, &fence_info, null, vk.render_fences.ptr + i);
|
|
success = VkCheck("vkCreateFence failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
result = vkCreateSemaphore(vk.device, &sem_info, null, vk.render_sems.ptr + i);
|
|
success = VkCheck("vkCreateSemaphore failure", result);
|
|
}
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkResult result = vkCreateCommandPool(vk.device, &pool_info, null, &vk.imm_pool);
|
|
success = VkCheck("vkCreateCommandPool failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
cmd_info.commandPool = vk.imm_pool;
|
|
VkResult result = vkAllocateCommandBuffers(vk.device, &cmd_info, &vk.imm_cmd);
|
|
success = VkCheck("vkAllocateCommandBuffers failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkResult result = vkCreateFence(vk.device, &fence_info, null, &vk.imm_fence);
|
|
success = VkCheck("vkCreateFence failure", result);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
VkFormat
|
|
GetDrawImageFormat(Vulkan* vk)
|
|
{
|
|
VkFormat selected_format;
|
|
foreach(format; VK_IMAGE_FORMATS)
|
|
{
|
|
VkImageFormatProperties props;
|
|
VkResult result = vkGetPhysicalDeviceImageFormatProperties(
|
|
vk.physical_device,
|
|
format,
|
|
VK_IMAGE_TYPE_2D,
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
VK_DRAW_IMAGE_USAGE_FLAGS,
|
|
0,
|
|
&props
|
|
);
|
|
if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (result == VK_SUCCESS)
|
|
{
|
|
selected_format = format;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return selected_format;
|
|
}
|
|
|
|
bool
|
|
CreateDrawImages(Vulkan* vk)
|
|
{
|
|
Push(vk, SI.DrawImages);
|
|
|
|
bool success = true;
|
|
|
|
VkFormat draw_format = GetDrawImageFormat(vk);
|
|
VkFormat depth_format = VK_FORMAT_D32_SFLOAT;
|
|
|
|
VmaAllocationCreateInfo alloc_info = {
|
|
usage: VMA_MEMORY_USAGE_GPU_ONLY,
|
|
requiredFlags: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
};
|
|
|
|
VkImageCreateInfo image_info = {
|
|
sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
imageType: VK_IMAGE_TYPE_2D,
|
|
mipLevels: 1,
|
|
arrayLayers: 1,
|
|
samples: VK_SAMPLE_COUNT_1_BIT,
|
|
tiling: VK_IMAGE_TILING_OPTIMAL,
|
|
usage: VK_DRAW_IMAGE_USAGE_FLAGS,
|
|
extent: vk.swapchain_extent,
|
|
format: draw_format,
|
|
};
|
|
|
|
VkResult result = vmaCreateImage(vk.vma, &image_info, &alloc_info, &vk.draw_image.base.image, &vk.draw_image.base.alloc, null);
|
|
success = VkCheck("vmaCreateImage failure", result);
|
|
|
|
if (success)
|
|
{
|
|
VkImageViewCreateInfo view_info = {
|
|
sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
image: vk.draw_image.base.image,
|
|
format: draw_format,
|
|
viewType: VK_IMAGE_VIEW_TYPE_2D,
|
|
subresourceRange: {
|
|
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT,
|
|
baseMipLevel: 0,
|
|
levelCount: 1,
|
|
baseArrayLayer: 0,
|
|
layerCount: 1,
|
|
},
|
|
};
|
|
|
|
result = vkCreateImageView(vk.device, &view_info, null, &vk.draw_image.view);
|
|
success = VkCheck("vkCreateImageView failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkImageCreateInfo depth_image_info = {
|
|
sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
imageType: VK_IMAGE_TYPE_2D,
|
|
mipLevels: 1,
|
|
arrayLayers: 1,
|
|
samples: VK_SAMPLE_COUNT_1_BIT,
|
|
tiling: VK_IMAGE_TILING_OPTIMAL,
|
|
format: depth_format,
|
|
usage: VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
|
extent: vk.swapchain_extent,
|
|
};
|
|
|
|
result = vmaCreateImage(vk.vma, &depth_image_info, &alloc_info, &vk.depth_image.base.image, &vk.depth_image.base.alloc, null);
|
|
success = VkCheck("vmaCreateImage failure", result);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
VkImageViewCreateInfo depth_view_info = {
|
|
sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
image: vk.depth_image.base.image,
|
|
viewType: VK_IMAGE_VIEW_TYPE_2D,
|
|
format: depth_format,
|
|
subresourceRange: {
|
|
aspectMask: VK_IMAGE_ASPECT_DEPTH_BIT,
|
|
baseMipLevel: 0,
|
|
levelCount: 1,
|
|
baseArrayLayer: 0,
|
|
layerCount: 1,
|
|
},
|
|
};
|
|
|
|
result = vkCreateImageView(vk.device, &depth_view_info, null, &vk.depth_image.view);
|
|
success = VkCheck("vmaCreateImageView failure", result);
|
|
}
|
|
|
|
vk.draw_image.base.format = draw_format;
|
|
vk.draw_image.base.layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
vk.depth_image.base.format = depth_format;
|
|
vk.depth_image.base.layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
return success;
|
|
}
|
|
|
|
void
|
|
SelectSwapchainFormats(Vulkan* vk)
|
|
{
|
|
a.Arena* arena = &vk.frame_arenas[0];
|
|
|
|
u32 format_count;
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(vk.physical_device, vk.surface, &format_count, null);
|
|
VkSurfaceFormatKHR[] formats = a.AllocArray!(VkSurfaceFormatKHR)(arena, format_count);
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(vk.physical_device, vk.surface, &format_count, formats.ptr);
|
|
|
|
u32 mode_count;
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(vk.physical_device, vk.surface, &mode_count, null);
|
|
VkPresentModeKHR[] modes = a.AllocArray!(VkPresentModeKHR)(arena, mode_count);
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(vk.physical_device, vk.surface, &mode_count, modes.ptr);
|
|
|
|
VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
|
foreach(mode; modes)
|
|
{
|
|
if (mode == VK_PRESENT_MODE_MAILBOX_KHR)
|
|
{
|
|
present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
vk.surface_format = formats[0];
|
|
vk.present_mode = present_mode;
|
|
}
|
|
|
|
bool
|
|
CreateSwapchain(Vulkan* vk)
|
|
{
|
|
Push(vk, SI.Swapchain);
|
|
|
|
bool success = true;
|
|
a.Arena* arena = &vk.frame_arenas[0];
|
|
|
|
VkSurfaceCapabilitiesKHR cap;
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.physical_device, vk.surface, &cap);
|
|
|
|
static bool initialized = false;
|
|
if (!initialized)
|
|
{
|
|
SelectSwapchainFormats(vk);
|
|
}
|
|
|
|
VkSwapchainCreateInfoKHR info = {
|
|
sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
|
imageArrayLayers: 1,
|
|
imageUsage: VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
clipped: VK_TRUE,
|
|
imageSharingMode: VK_SHARING_MODE_EXCLUSIVE,
|
|
minImageCount: cap.minImageCount + 1,
|
|
surface: vk.surface,
|
|
imageFormat: vk.surface_format.format,
|
|
imageColorSpace: vk.surface_format.colorSpace,
|
|
imageExtent: {
|
|
width: clamp(cast(u32)vk.window.w, cap.minImageExtent.width, cap.maxImageExtent.width),
|
|
height: clamp(cast(u32)vk.window.h, cap.minImageExtent.height, cap.maxImageExtent.height),
|
|
},
|
|
preTransform: cap.currentTransform,
|
|
presentMode: vk.present_mode,
|
|
};
|
|
|
|
VkResult result = vkCreateSwapchainKHR(vk.device, &info, null, &vk.swapchain);
|
|
success = VkCheck("vkCreateSwapchainKHR failure", result);
|
|
|
|
u32 count;
|
|
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &count, null);
|
|
VkImage[] images = a.AllocArray!(VkImage)(arena, count);
|
|
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &count, images.ptr);
|
|
VkImageView[] views = a.AllocArray!(VkImageView)(arena, count);
|
|
|
|
vk.present_images = a.AllocArray!(ImageView)(&vk.arena, count);
|
|
|
|
VkImageViewCreateInfo view_info = {
|
|
sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
viewType: VK_IMAGE_VIEW_TYPE_2D,
|
|
components: {
|
|
r: VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
g: VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
b: VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
a: VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
},
|
|
subresourceRange: {
|
|
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT,
|
|
baseMipLevel: 0,
|
|
levelCount: 1,
|
|
baseArrayLayer: 0,
|
|
layerCount: 1,
|
|
},
|
|
};
|
|
|
|
foreach(i, image; vk.present_images)
|
|
{
|
|
vk.present_images[i].base.image = images[i];
|
|
view_info.image = images[i];
|
|
view_info.format = vk.surface_format.format;
|
|
|
|
result = vkCreateImageView(vk.device, &view_info, null, &vk.present_images[i].view);
|
|
success = VkCheck("vkCreateImageView failure", result);
|
|
}
|
|
|
|
vk.swapchain_extent.width = info.imageExtent.width;
|
|
vk.swapchain_extent.height = info.imageExtent.height;
|
|
vk.swapchain_extent.depth = 1;
|
|
|
|
if (!initialized && success)
|
|
{
|
|
initialized = true;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
InitVMA(Vulkan* vk)
|
|
{
|
|
Push(vk, SI.Vma);
|
|
|
|
bool success = true;
|
|
|
|
VmaVulkanFunctions vk_functions = {
|
|
vkGetInstanceProcAddr: vkGetInstanceProcAddr,
|
|
vkGetDeviceProcAddr: vkGetDeviceProcAddr,
|
|
};
|
|
|
|
VmaAllocatorCreateInfo info = {
|
|
flags: VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
|
|
vulkanApiVersion: VK_MAKE_API_VERSION(0, 1, 3, 0),
|
|
pVulkanFunctions: &vk_functions,
|
|
physicalDevice: vk.physical_device,
|
|
device: vk.device,
|
|
instance: vk.instance,
|
|
};
|
|
|
|
VkResult result = vmaCreateAllocator(&info, &vk.vma);
|
|
success = VkCheck("vmaCreateAllocator failure", result);
|
|
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
InitDevice(Vulkan* vk)
|
|
{
|
|
Push(vk, SI.Device);
|
|
|
|
bool success = false;
|
|
a.Arena* arena = &vk.frame_arenas[0];
|
|
|
|
u32 count;
|
|
vkEnumeratePhysicalDevices(vk.instance, &count, null);
|
|
VkPhysicalDevice[] devices = a.AllocArray!(VkPhysicalDevice)(arena, count);
|
|
vkEnumeratePhysicalDevices(vk.instance, &count, devices.ptr);
|
|
|
|
VkPhysicalDevice physical_device = null;
|
|
bool discrete_candidate = false;
|
|
QueueInfo candidate = {
|
|
gfx_index: -1,
|
|
tfer_index: -1,
|
|
single_queue: false,
|
|
};
|
|
|
|
foreach(dev; devices)
|
|
{
|
|
QueueInfo current = CheckQueueProperties(arena, dev, vk.surface);
|
|
b32 discrete = false;
|
|
|
|
if (current.gfx_index < 0)
|
|
continue;
|
|
if (!CheckDeviceProperties(arena, dev, vk.surface, &discrete))
|
|
continue;
|
|
if (discrete_candidate && !discrete)
|
|
continue;
|
|
if (!CheckDeviceFeatures(dev))
|
|
continue;
|
|
|
|
discrete_candidate = cast(bool)discrete;
|
|
candidate = current;
|
|
physical_device = dev;
|
|
|
|
if (discrete_candidate && !candidate.single_queue)
|
|
continue;
|
|
}
|
|
|
|
if (physical_device)
|
|
{
|
|
VkDeviceQueueCreateInfo[2] queue_info;
|
|
f32 priority = 1.0f;
|
|
count = 1;
|
|
|
|
queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queue_info[0].queueFamilyIndex = candidate.gfx_index;
|
|
queue_info[0].queueCount = 1;
|
|
queue_info[0].pQueuePriorities = &priority;
|
|
queue_info[0].flags = 0;
|
|
|
|
if (!candidate.single_queue)
|
|
{
|
|
queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queue_info[1].queueFamilyIndex = candidate.tfer_index;
|
|
queue_info[1].queueCount = 1;
|
|
queue_info[1].pQueuePriorities = &priority;
|
|
queue_info[1].flags = 0;
|
|
|
|
count += 1;
|
|
}
|
|
|
|
VkPhysicalDeviceVulkan13Features features_13 = {
|
|
sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
|
|
synchronization2: VK_TRUE,
|
|
dynamicRendering: VK_TRUE,
|
|
};
|
|
|
|
VkPhysicalDeviceVulkan12Features features_12 = {
|
|
sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
|
pNext: &features_13,
|
|
descriptorIndexing: VK_TRUE,
|
|
bufferDeviceAddress: VK_TRUE,
|
|
descriptorBindingUniformBufferUpdateAfterBind: VK_TRUE,
|
|
descriptorBindingSampledImageUpdateAfterBind: VK_TRUE,
|
|
descriptorBindingStorageImageUpdateAfterBind: VK_TRUE,
|
|
descriptorBindingStorageBufferUpdateAfterBind: VK_TRUE,
|
|
descriptorBindingPartiallyBound: VK_TRUE,
|
|
shaderSampledImageArrayNonUniformIndexing: VK_TRUE,
|
|
shaderUniformBufferArrayNonUniformIndexing: VK_TRUE,
|
|
runtimeDescriptorArray: VK_TRUE,
|
|
};
|
|
|
|
VkPhysicalDeviceVulkan11Features features_11 = {
|
|
sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
|
pNext: &features_12,
|
|
};
|
|
|
|
VkPhysicalDeviceFeatures2 features = {
|
|
sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
|
pNext: &features_11,
|
|
features: {
|
|
shaderUniformBufferArrayDynamicIndexing: VK_TRUE,
|
|
shaderSampledImageArrayDynamicIndexing: VK_TRUE,
|
|
shaderStorageBufferArrayDynamicIndexing: VK_TRUE,
|
|
shaderStorageImageArrayDynamicIndexing: VK_TRUE,
|
|
samplerAnisotropy: VK_TRUE,
|
|
},
|
|
};
|
|
|
|
VkDeviceCreateInfo device_info = {
|
|
sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
|
pNext: &features,
|
|
ppEnabledExtensionNames: VK_DEVICE_EXTENSIONS.ptr,
|
|
enabledExtensionCount: cast(u32)VK_DEVICE_EXTENSIONS.length,
|
|
queueCreateInfoCount: count,
|
|
pQueueCreateInfos: queue_info.ptr,
|
|
pEnabledFeatures: null,
|
|
};
|
|
|
|
VkResult result = vkCreateDevice(physical_device, &device_info, null, &vk.device);
|
|
if (result != VK_SUCCESS)
|
|
{
|
|
Logf("vkCreateDevices failure: %s", VkResultStr(result));
|
|
}
|
|
else
|
|
{
|
|
LoadDeviceFunctions(vk);
|
|
|
|
vkGetDeviceQueue(
|
|
vk.device,
|
|
candidate.gfx_index,
|
|
0,
|
|
&candidate.gfx_queue
|
|
);
|
|
|
|
if (!candidate.single_queue)
|
|
{
|
|
vkGetDeviceQueue(
|
|
vk.device,
|
|
candidate.tfer_index,
|
|
candidate.tfer_index == candidate.gfx_index ? 1 : 0,
|
|
&candidate.tfer_queue
|
|
);
|
|
}
|
|
else
|
|
{
|
|
candidate.tfer_queue = candidate.gfx_queue;
|
|
candidate.tfer_index = candidate.gfx_index;
|
|
}
|
|
|
|
vk.physical_device = physical_device;
|
|
vk.queues = candidate;
|
|
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
CheckDeviceFeatures(VkPhysicalDevice device)
|
|
{
|
|
VkPhysicalDeviceFeatures2 features2 = { sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
|
|
VkPhysicalDeviceVulkan12Features features_12 = { sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES };
|
|
VkPhysicalDeviceVulkan13Features features_13 = { sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES };
|
|
|
|
features2.pNext = &features_12;
|
|
vkGetPhysicalDeviceFeatures2(device, &features2);
|
|
features2.pNext = &features_13;
|
|
vkGetPhysicalDeviceFeatures2(device, &features2);
|
|
|
|
VkPhysicalDeviceFeatures features = features2.features;
|
|
bool result = true;
|
|
|
|
result &= cast(bool)features.shaderUniformBufferArrayDynamicIndexing;
|
|
result &= cast(bool)features.shaderSampledImageArrayDynamicIndexing;
|
|
result &= cast(bool)features.shaderStorageBufferArrayDynamicIndexing;
|
|
result &= cast(bool)features.shaderStorageImageArrayDynamicIndexing;
|
|
result &= cast(bool)features.samplerAnisotropy;
|
|
|
|
result &= cast(bool)features_12.descriptorIndexing;
|
|
result &= cast(bool)features_12.bufferDeviceAddress;
|
|
result &= cast(bool)features_12.descriptorBindingUniformBufferUpdateAfterBind;
|
|
result &= cast(bool)features_12.descriptorBindingSampledImageUpdateAfterBind;
|
|
result &= cast(bool)features_12.descriptorBindingStorageImageUpdateAfterBind;
|
|
result &= cast(bool)features_12.descriptorBindingStorageBufferUpdateAfterBind;
|
|
result &= cast(bool)features_12.descriptorBindingPartiallyBound;
|
|
result &= cast(bool)features_12.runtimeDescriptorArray;
|
|
result &= cast(bool)features_12.shaderSampledImageArrayNonUniformIndexing;
|
|
result &= cast(bool)features_12.shaderUniformBufferArrayNonUniformIndexing;
|
|
result &= cast(bool)features_12.timelineSemaphore;
|
|
|
|
result &= cast(bool)features_13.synchronization2;
|
|
result &= cast(bool)features_13.dynamicRendering;
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
CheckDeviceProperties(a.Arena *arena, VkPhysicalDevice device, VkSurfaceKHR surface, b32* discrete)
|
|
{
|
|
bool success = false;
|
|
|
|
VkPhysicalDeviceProperties props;
|
|
vkGetPhysicalDeviceProperties(device, &props);
|
|
|
|
if (VK_API_VERSION_MINOR(props.apiVersion) >= 3)
|
|
{
|
|
u32 ext_count;
|
|
vkEnumerateDeviceExtensionProperties(device, null, &ext_count, null);
|
|
VkExtensionProperties[] ext_props = a.AllocArray!(VkExtensionProperties)(arena, ext_count);
|
|
vkEnumerateDeviceExtensionProperties(device, null, &ext_count, ext_props.ptr);
|
|
|
|
i32 matched = 0;
|
|
foreach(prop; ext_props)
|
|
{
|
|
foreach(ext; VK_DEVICE_EXTENSIONS)
|
|
{
|
|
if (strcmp(cast(char*)prop.extensionName, ext) == 0)
|
|
{
|
|
matched += 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (matched == VK_DEVICE_EXTENSIONS.length)
|
|
{
|
|
u32 fmt_count, present_count;
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &fmt_count, null);
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, null);
|
|
|
|
*discrete = props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
|
|
success = fmt_count && present_count;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
QueueInfo
|
|
CheckQueueProperties(a.Arena *arena, VkPhysicalDevice device, VkSurfaceKHR surface)
|
|
{
|
|
const u32 T_BIT = VK_QUEUE_TRANSFER_BIT;
|
|
const u32 C_BIT = VK_QUEUE_COMPUTE_BIT;
|
|
const u32 G_BIT = VK_QUEUE_GRAPHICS_BIT;
|
|
const u32 S_BIT = VK_QUEUE_SPARSE_BINDING_BIT;
|
|
|
|
QueueInfo current = {
|
|
gfx_index: -1,
|
|
tfer_index: -1,
|
|
single_queue: false,
|
|
};
|
|
|
|
u32 count;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &count, null);
|
|
VkQueueFamilyProperties[] properties = a.AllocArray!(VkQueueFamilyProperties)(arena, count);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &count, properties.ptr);
|
|
|
|
if (count == 1 && properties[0].queueCount == 1 && u.BitEq(properties[0].queueFlags, T_BIT | C_BIT | G_BIT))
|
|
{
|
|
current.gfx_index = current.tfer_index = 0;
|
|
current.single_queue = true;
|
|
}
|
|
else
|
|
{
|
|
bool sparse = false, tfer_only = false;
|
|
|
|
foreach(i, prop; properties)
|
|
{
|
|
b32 surface_support;
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, cast(u32)i, surface, &surface_support);
|
|
|
|
if (current.gfx_index < 0 && surface_support && u.BitEq(prop.queueFlags, G_BIT))
|
|
{
|
|
current.gfx_index = cast(i32)i;
|
|
continue;
|
|
}
|
|
|
|
if (u.BitEq(prop.queueFlags, T_BIT | S_BIT) && !u.BitEq(prop.queueFlags, G_BIT | C_BIT))
|
|
{
|
|
sparse = true;
|
|
tfer_only = true;
|
|
current.tfer_index = cast(i32)i;
|
|
continue;
|
|
}
|
|
|
|
if (!(sparse && tfer_only) && u.BitEq(prop.queueFlags, T_BIT | S_BIT))
|
|
{
|
|
sparse = true;
|
|
current.tfer_index = cast(i32)i;
|
|
continue;
|
|
}
|
|
|
|
if (!sparse && !u.BitEq(prop.queueFlags, T_BIT) && u.BitEq(prop.queueFlags, C_BIT))
|
|
{
|
|
tfer_only = true;
|
|
current.tfer_index = cast(i32)i;
|
|
continue;
|
|
}
|
|
|
|
if (!sparse && !tfer_only && u.BitEq(prop.queueFlags, C_BIT))
|
|
{
|
|
current.tfer_index = cast(i32)i;
|
|
}
|
|
}
|
|
|
|
if (current.tfer_index < 0)
|
|
{
|
|
current.tfer_index = current.gfx_index;
|
|
}
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
pragma(inline): void
|
|
Push(Vulkan* vk, StepInitialized step)
|
|
{
|
|
u.Node!(SI)* node = a.Alloc!(u.Node!(SI));
|
|
node.value = step;
|
|
u.PushFront(&vk.cleanup_list, node, null);
|
|
}
|
|
|
|
void
|
|
DestroyRenderer(Vulkan* vk)
|
|
{
|
|
foreach(i, arena; vk.frame_arenas)
|
|
{
|
|
a.Free(vk.frame_arenas.ptr + i);
|
|
}
|
|
|
|
a.Free(&vk.arena);
|
|
}
|
|
|
|
void
|
|
Destroy(VkInstance instance)
|
|
{
|
|
if (instance)
|
|
{
|
|
vkDestroyInstance(instance, null);
|
|
}
|
|
}
|
|
|
|
void
|
|
Destroy(VkDebugUtilsMessengerEXT dbg, VkInstance instance)
|
|
{
|
|
if (dbg)
|
|
{
|
|
vkDestroyDebugUtilsMessengerEXT(instance, dbg, null);
|
|
}
|
|
}
|
|
|
|
void
|
|
Destroy(VkSurfaceKHR surface, VkInstance instance)
|
|
{
|
|
if (surface)
|
|
{
|
|
vkDestroySurfaceKHR(instance, surface, null);
|
|
}
|
|
}
|
|
|
|
void
|
|
Destroy(VkDevice device)
|
|
{
|
|
if (device)
|
|
{
|
|
vkDestroyDevice(device, null);
|
|
}
|
|
}
|
|
|
|
void
|
|
Destroy(VmaAllocator vma)
|
|
{
|
|
if (vma)
|
|
{
|
|
vmaDestroyAllocator(vma);
|
|
}
|
|
}
|
|
|
|
void
|
|
Destroy(VkSwapchainKHR swapchain, ImageView[] views, VkDevice device)
|
|
{
|
|
foreach(view; views)
|
|
{
|
|
if (view.view)
|
|
{
|
|
vkDestroyImageView(device, view.view, null);
|
|
}
|
|
}
|
|
|
|
if (swapchain)
|
|
{
|
|
vkDestroySwapchainKHR(device, swapchain, null);
|
|
}
|
|
}
|
|
|
|
void
|
|
Destroy(ImageView* view, VkDevice device, VmaAllocator vma)
|
|
{
|
|
if (view.view)
|
|
{
|
|
vkDestroyImageView(device, view.view, null);
|
|
}
|
|
|
|
if (view.base.image)
|
|
{
|
|
vmaDestroyImage(vma, view.base.image, view.base.alloc);
|
|
}
|
|
}
|
|
|
|
void
|
|
Destroy(VkDescriptorPool pool, VkDescriptorSetLayout[] layouts, VkPipelineLayout pipeline_layout, VkSampler sampler, VkDevice device)
|
|
{
|
|
if (sampler)
|
|
{
|
|
vkDestroySampler(device, sampler, null);
|
|
}
|
|
|
|
if (pipeline_layout)
|
|
{
|
|
vkDestroyPipelineLayout(device, pipeline_layout, null);
|
|
}
|
|
|
|
foreach(layout; layouts)
|
|
{
|
|
if (layout)
|
|
{
|
|
vkDestroyDescriptorSetLayout(device, layout, null);
|
|
}
|
|
}
|
|
|
|
if (pool)
|
|
{
|
|
vkDestroyDescriptorPool(device, pool, null);
|
|
}
|
|
}
|
|
|
|
void
|
|
Destroy(ImageView* draw, ImageView* depth, VkDevice device, VmaAllocator vma)
|
|
{
|
|
Destroy(draw, device, vma);
|
|
Destroy(depth, device, vma);
|
|
}
|
|
|
|
void
|
|
DestroyFS(Vulkan* vk)
|
|
{
|
|
if (vk.imm_fence)
|
|
{
|
|
vkDestroyFence(vk.device, vk.imm_fence, null);
|
|
}
|
|
|
|
if (vk.imm_cmd)
|
|
{
|
|
vkFreeCommandBuffers(vk.device, vk.imm_pool, 1, &vk.imm_cmd);
|
|
}
|
|
|
|
if (vk.imm_pool)
|
|
{
|
|
vkDestroyCommandPool(vk.device, vk.imm_pool, null);
|
|
}
|
|
|
|
foreach(i, sem; vk.swapchain_sems)
|
|
{
|
|
if (sem)
|
|
{
|
|
vkDestroySemaphore(vk.device, sem, null);
|
|
}
|
|
}
|
|
|
|
foreach(i; 0 .. FRAME_OVERLAP)
|
|
{
|
|
if (vk.render_sems[i])
|
|
{
|
|
vkDestroySemaphore(vk.device, vk.render_sems[i], null);
|
|
}
|
|
|
|
if (vk.render_fences[i])
|
|
{
|
|
vkDestroyFence(vk.device, vk.render_fences[i], null);
|
|
}
|
|
|
|
if (vk.cmd_pools[i])
|
|
{
|
|
vkFreeCommandBuffers(vk.device, vk.cmd_pools[i], 1, &vk.cmds[i]);
|
|
}
|
|
|
|
if (vk.cmd_pools[i])
|
|
{
|
|
vkDestroyCommandPool(vk.device, vk.cmd_pools[i], null);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
InitInstance(Vulkan* vk)
|
|
{
|
|
Push(vk, SI.Instance);
|
|
|
|
bool success = true;
|
|
a.Arena* arena = &vk.frame_arenas[0];
|
|
|
|
u32 count;
|
|
vkEnumerateInstanceLayerProperties(&count, null);
|
|
VkLayerProperties[] layers = a.AllocArray!(VkLayerProperties)(arena, count);
|
|
vkEnumerateInstanceLayerProperties(&count, layers.ptr);
|
|
|
|
foreach(i, layer; layers)
|
|
{
|
|
if (strcmp(cast(char*)&layer.layerName, "VK_LAYER_KHRONOS_validation") == 0)
|
|
{
|
|
g_VLAYER_SUPPORT = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char*[] instance_layers = g_VLAYER_SUPPORT && BUILD_DEBUG ? VK_INSTANCE_LAYERS_DEBUG : VK_INSTANCE_LAYERS;
|
|
const char*[] instance_ext = g_VLAYER_SUPPORT && BUILD_DEBUG ? VK_INSTANCE_EXT_DEBUG : VK_INSTANCE_EXT;
|
|
|
|
VkApplicationInfo app_info = {
|
|
sType: VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
pApplicationName: "Video Game",
|
|
applicationVersion: VK_MAKE_API_VERSION(0, 0, 0, 1),
|
|
pEngineName: "Gears",
|
|
engineVersion: VK_MAKE_API_VERSION(0, 0, 0, 1),
|
|
apiVersion: VK_MAKE_API_VERSION(0, 1, 3, 0),
|
|
};
|
|
|
|
VkInstanceCreateInfo instance_info = {
|
|
sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
pApplicationInfo: &app_info,
|
|
enabledLayerCount: cast(u32)instance_layers.length,
|
|
ppEnabledLayerNames: instance_layers.ptr,
|
|
enabledExtensionCount: cast(u32)instance_ext.length,
|
|
ppEnabledExtensionNames: instance_ext.ptr,
|
|
};
|
|
|
|
VkResult result = vkCreateInstance(&instance_info, null, &vk.instance);
|
|
success = VkCheck("vkCreateInstance failure", result);
|
|
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
InitSurface(Vulkan* vk)
|
|
{
|
|
Push(vk, SI.Surface);
|
|
|
|
version(linux)
|
|
{
|
|
VkXcbSurfaceCreateInfoKHR surface_info = {
|
|
sType: VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
|
|
connection: vk.window.conn,
|
|
window: vk.window.window,
|
|
};
|
|
|
|
VkResult result = vkCreateXcbSurfaceKHR(vk.instance, &surface_info, null, &vk.surface);
|
|
}
|
|
|
|
version(Windows)
|
|
{
|
|
VkWin32SurfaceCreateInfoKHR surface_info = {
|
|
sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
|
|
hinstance: vk.window.instance,
|
|
hwnd: vk.window.handle,
|
|
};
|
|
|
|
VkResult result = vkCreateWin32SurfaceKHR(vk.instance, &surface_info, null, &vk.surface);
|
|
}
|
|
|
|
bool success = VkCheck("InitSurface failure", result);
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
void
|
|
EnableVLayers(Vulkan* vk)
|
|
{
|
|
debug
|
|
{
|
|
Push(vk, SI.Debug);
|
|
|
|
if (g_VLAYER_SUPPORT)
|
|
{
|
|
VkDebugUtilsMessengerCreateInfoEXT info = {
|
|
sType: VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
|
messageSeverity: VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
|
messageType: VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
|
|
pfnUserCallback: cast(PFN_vkDebugUtilsMessengerCallbackEXT)&DebugCallback,
|
|
};
|
|
|
|
VkResult result = vkCreateDebugUtilsMessengerEXT(vk.instance, &info, null, &vk.dbg_msg);
|
|
if (result != VK_SUCCESS)
|
|
{
|
|
Logf("EnableVLayers failed to initialize, will continue without validation: %s", VkResultStr(result));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Logf("EnableVLayers warning: Not supported on current device, continuing without");
|
|
}
|
|
}
|
|
}
|
|
|