This commit is contained in:
Zack Buhman 2026-04-04 23:50:52 -05:00
commit a978c8a9e6
13 changed files with 29294 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.gdb_history
*.spv
*.o
main

86
Makefile Normal file
View File

@ -0,0 +1,86 @@
CC=$(PREFIX)gcc
CXX=$(PREFIX)g++
OBJCOPY=$(PREFIX)objcopy
OBJARCH = elf64-x86-64
OPT = -O0 -march=x86-64-v3
DEBUG = -g
CSTD = -std=gnu23
CXXSTD = -std=gnu++23
CFLAGS += -Wall -Werror
CFLAGS += -Wfatal-errors
CFLAGS += -Wno-error=unused-variable -Wno-error=unused-but-set-variable
CFLAGS += -Wno-error=unused-function
CFLAGS += -Wno-error=array-bounds
CFLAGS += -Wno-unknown-pragmas
CFLAGS += -fno-strict-aliasing
CFLAGS += -I./include
CFLAGS += -I../SDL3-dist/include
CFLAGS += -I../volk
CFLAGS += -fpic
FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address
LDFLAGS += -lm
ifeq ($(shell uname),Linux)
LDFLAGS += -Wl,-z noexecstack
endif
OBJS = \
src/main.o
BINS = \
index.idx.o \
position_normal_texture.vtx.o
SHADERS = \
shader/triangle.vs.spv.o \
shader/triangle.ps.spv.o
LIBS = \
../SDL3-dist/lib64/libSDL3.a \
../volk/volk.o
all: main
define BUILD_BINARY_O
$(OBJCOPY) -I binary -O $(OBJARCH) $< $@
endef
%.vtx.o: %.vtx
$(BUILD_BINARY_O)
%.idx.o: %.idx
$(BUILD_BINARY_O)
%.spv.o: %.spv
$(BUILD_BINARY_O)
%.o: %.c
$(CC) $(ARCH) $(CSTD) $(CFLAGS) $(FLAGS) $(OPT) $(DEBUG) -c $< -o $@
%.o: %.cpp
$(CXX) $(ARCH) $(CXXSTD) $(CFLAGS) $(FLAGS) $(OPT) $(DEBUG) -c $< -o $@
main: $(OBJS) $(LIBS) $(BINS) $(SHADERS)
$(CC) $(ARCH) $(LDFLAGS) $(FLAGS) $(OPT) $(DEBUG) $^ -o $@
%.vs.spv: %.hlsl
../dxc/bin/dxc -spirv -T vs_6_1 -E VSMain -fspv-target-env=vulkan1.3 $< -Fo $@
%.ps.spv: %.hlsl
../dxc/bin/dxc -spirv -T ps_6_1 -E PSMain -fspv-target-env=vulkan1.3 $< -Fo $@
.SUFFIXES:
.INTERMEDIATE:
.SECONDARY:
.PHONY: all clean phony dist
%: RCS/%,v
%: RCS/%
%: %,v
%: s.%
%: SCCS/s.%

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

42
include/new.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#ifdef __aligned_alloc
#undef __aligned_alloc
#endif
#ifdef __alloca
#undef __alloca
#endif
#ifdef _WIN32
#include <stdlib.h>
#include <malloc.h>
#define __aligned_alloc(alignment, size) _aligned_malloc(size, alignment)
#define __alloca(size) _malloca(size)
#else
#include <stdlib.h>
#define __aligned_alloc(alignment, size) aligned_alloc(alignment, size)
#define __alloca(size) alloca(size)
#endif
template <typename T>
T * New(int elements)
{
return (T *)__aligned_alloc(16, (sizeof (T)) * elements);
}
template <typename T>
T * NewM(int elements)
{
return (T *)malloc((sizeof (T)) * elements);
}
/*
template <typename T>
T * NewA(int elements)
{
return (T *)__alloca((sizeof (T)) * elements);
}
*/
#undef __alloca
#undef __aligned_alloc

2941
include/sal.h Normal file

File diff suppressed because it is too large Load Diff

BIN
index.idx Normal file

Binary file not shown.

BIN
position_normal_texture.vtx Normal file

Binary file not shown.

24
shader/triangle.hlsl Normal file
View File

@ -0,0 +1,24 @@
static const float2 positions[3] = {
float2(0.0, -0.5),
float2(0.5, 0.5),
float2(-0.5, 0.5)
};
struct VSOutput
{
float4 Pos : SV_POSITION;
};
[shader("vertex")]
VSOutput VSMain(uint VertexIndex: SV_VertexID)
{
VSOutput output = (VSOutput)0;
output.Pos = float4(positions[VertexIndex], 0.0, 1.0);
return output;
}
[shader("pixel")]
float4 PSMain() : SV_TARGET
{
return float4(1.0, 0.0, 0.0, 1.0);
}

850
src/main.cpp Normal file
View File

@ -0,0 +1,850 @@
#include <stdio.h>
#include <stdlib.h>
#include "volk.h"
#include "vulkan/vk_enum_string_helper.h"
#include "SDL3/SDL.h"
#include "SDL3/SDL_vulkan.h"
#include "directxmath/directxmath.h"
#include "new.h"
extern "C" {
extern uint8_t const _binary_position_normal_texture_vtx_start[];
extern void * const _binary_position_normal_texture_vtx_size;
extern uint8_t const _binary_index_idx_start[];
extern void * const _binary_index_idx_size;
extern uint8_t const _binary_shader_triangle_vs_spv_start[];
extern void * const _binary_shader_triangle_vs_spv_size;
extern uint8_t const _binary_shader_triangle_ps_spv_start[];
extern void * const _binary_shader_triangle_ps_spv_size;
}
#define vtx_start _binary_position_normal_texture_vtx_start
#define vtx_size (size_t)(&_binary_position_normal_texture_vtx_size)
#define idx_start _binary_index_idx_start
#define idx_size (size_t)(&_binary_index_idx_size)
#define vs_start _binary_shader_triangle_vs_spv_start
#define vs_size (size_t)(&_binary_shader_triangle_vs_spv_size)
#define ps_start _binary_shader_triangle_ps_spv_start
#define ps_size (size_t)(&_binary_shader_triangle_ps_spv_size)
#define SDL_CHECK(f) \
{ \
bool result = (f); \
if (result != true) { \
fprintf(stderr, "SDL: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, SDL_GetError()); \
exit(EXIT_FAILURE); \
} \
}
#define SDL_CHECK_NONNULL(f) \
{ \
void * ptr = (void *)(f); \
if (ptr == nullptr) { \
fprintf(stderr, "SDL: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, SDL_GetError()); \
exit(EXIT_FAILURE); \
} \
}
#define VK_CHECK(f) \
{ \
VkResult result = (f); \
if (result != VK_SUCCESS) { \
fprintf(stderr, "VK: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, string_VkResult(result)); \
exit(EXIT_FAILURE); \
} \
}
#define VK_CHECK_SWAPCHAIN(f) \
{ \
VkResult result = (f); \
if (result == VK_ERROR_OUT_OF_DATE_KHR) { \
updateSwapchain = true; \
} else if (result != VK_SUCCESS) { \
fprintf(stderr, "VK: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, string_VkResult(result)); \
exit(EXIT_FAILURE); \
} \
}
#define ASSERT(expr, msg) \
{ \
bool result = (expr); \
if (result != true) { \
fprintf(stderr, "%s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, msg); \
exit(EXIT_FAILURE); \
} \
}
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
#define UNREACHABLE() __assume(false);
#else // GCC, Clang
#define UNREACHABLE() __builtin_unreachable();
#endif
constexpr uint32_t maxFramesInFlight{ 2 };
VkInstance instance{ VK_NULL_HANDLE };
VkDevice device{ VK_NULL_HANDLE };
VkQueue queue{ VK_NULL_HANDLE };
VkSurfaceKHR surface{ VK_NULL_HANDLE };
VkSwapchainKHR swapchain{ VK_NULL_HANDLE };
VkImage * swapchainImages{ nullptr };
VkImageView * swapchainImageViews{ nullptr };
VkImage depthImage{ VK_NULL_HANDLE };
VkImageView depthImageView{ VK_NULL_HANDLE };
VkBuffer vertexIndexBuffer{ VK_NULL_HANDLE };
VkFence fences[maxFramesInFlight];
VkSemaphore presentSemaphores[maxFramesInFlight];
VkSemaphore * renderSemaphores{ nullptr };
VkCommandPool commandPool{ VK_NULL_HANDLE };
VkCommandBuffer commandBuffers[maxFramesInFlight];
VkPipeline pipeline{ VK_NULL_HANDLE };
VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
XMINT2 windowSize{};
struct ShaderData {
XMFLOAT4X4 projection;
XMFLOAT4X4 view;
XMFLOAT4X4 model[3];
XMFLOAT4 lightPos{ 0.0f, -10.0f, 10.0f, 0.0f };
uint32_t selected{ 1 };
} shaderData{};
struct ShaderDataBuffer {
VkBuffer buffer{ VK_NULL_HANDLE };
VkDeviceAddress deviceAddress{};
};
ShaderDataBuffer shaderDataBuffers[maxFramesInFlight];
static void print_memoryPropertyFlags(VkMemoryPropertyFlags propertyFlags)
{
int index = 0;
while (propertyFlags) {
if (propertyFlags & 1) {
if (index != 0)
printf("|");
printf(string_VkMemoryPropertyFlagBits((VkMemoryPropertyFlagBits)(1u << index)));
}
propertyFlags >>= 1;
index += 1;
};
printf("\n");
}
uint32_t findMemoryTypeIndex(VkPhysicalDeviceMemoryProperties const * memoryProperties, uint32_t memoryTypeBits, VkMemoryPropertyFlags propertyFlags)
{
for (uint32_t i = 0; i < memoryProperties->memoryTypeCount; i++) {
if (!(memoryTypeBits & (1u << i)))
continue;
if (memoryProperties->memoryTypes[i].propertyFlags == propertyFlags) {
return i;
}
}
ASSERT(false, "no memory type index matching memoryTypeBits and propertyFlags");
UNREACHABLE();
}
int main()
{
SDL_CHECK(SDL_Init(SDL_INIT_VIDEO));
SDL_CHECK(SDL_Vulkan_LoadLibrary(NULL));
volkInitialize();
VkApplicationInfo appInfo{
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "Vulkan",
.apiVersion = VK_API_VERSION_1_3
};
uint32_t instanceExtensionsCount{ 0 };
char const * const * instanceExtensions{ SDL_Vulkan_GetInstanceExtensions(&instanceExtensionsCount) };
VkInstanceCreateInfo instanceCreateInfo{
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
.enabledExtensionCount = instanceExtensionsCount,
.ppEnabledExtensionNames = instanceExtensions,
};
VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance));
volkLoadInstance(instance);
//////////////////////////////////////////////////////////////////////
// physical device and queue family index
//////////////////////////////////////////////////////////////////////
uint32_t physicalDeviceCount{ 0 };
VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr));
VkPhysicalDevice * physicalDevices = NewM<VkPhysicalDevice>(physicalDeviceCount);
VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices));
uint32_t physicalDeviceIndex{ 0 };
printf("physicalDeviceCount %d\n", physicalDeviceCount);
for (uint32_t i = 0; i < physicalDeviceCount; i++) {
VkPhysicalDeviceProperties2 properties{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
vkGetPhysicalDeviceProperties2(physicalDevices[i], &properties);
printf("devices[%d] name: %s%s\n", i, properties.properties.deviceName, (i == physicalDeviceIndex) ? " [selected]" : "");
if (i == physicalDeviceIndex) {
printf("limits:\n");
printf(" maxImageDimension1D %u\n", properties.properties.limits.maxImageDimension1D);
printf(" maxImageDimension2D %u\n", properties.properties.limits.maxImageDimension2D);
printf(" maxMemoryAllocationCount %u\n", properties.properties.limits.maxMemoryAllocationCount);
printf(" maxSamplerAllocationCount %u\n", properties.properties.limits.maxSamplerAllocationCount);
}
}
VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
free(physicalDevices);
uint32_t queueFamilyCount{ 0 };
vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, nullptr);
VkQueueFamilyProperties2 * queueFamilyProperties = NewM<VkQueueFamilyProperties2>(queueFamilyCount);
for (uint32_t i = 0; i < queueFamilyCount; i++) {
queueFamilyProperties[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
queueFamilyProperties[i].pNext = nullptr;
}
vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, queueFamilyProperties);
uint32_t queueFamilyIndex{ ~0u };
for (uint32_t i = 0; i < queueFamilyCount; i++) {
VkQueueFlags queueFlags = queueFamilyProperties[i].queueFamilyProperties.queueFlags;
if ((queueFlags & VK_QUEUE_GRAPHICS_BIT) && (queueFlags & VK_QUEUE_COMPUTE_BIT)) {
queueFamilyIndex = i;
break;
}
}
ASSERT(queueFamilyIndex != ~0u, "no queue with VK_QUEUE_GRAPHICS_BIT && VK_QUEUE_COMPUTE_BIT");
free(queueFamilyProperties);
SDL_CHECK(SDL_Vulkan_GetPresentationSupport(instance, physicalDevice, queueFamilyIndex));
//////////////////////////////////////////////////////////////////////
// memory
//////////////////////////////////////////////////////////////////////
VkPhysicalDeviceMemoryProperties2 memoryProperties{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2,
.pNext = nullptr
};
vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &memoryProperties);
/*
for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryTypeCount; i++) {
printf("memoryTypes[%u].propertyFlags: ", i);
print_memoryPropertyFlags(memoryProperties.memoryProperties.memoryTypes[i].propertyFlags);
printf("memoryTypes[%u].heapIndex: %u\n", i, memoryProperties.memoryProperties.memoryTypes[i].heapIndex);
}
for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryHeapCount; i++) {
printf("memoryHeaps[%u].size %lu\n", i, memoryProperties.memoryProperties.memoryHeaps[i].size);
printf("memoryHeaps[%u].flags %08x\n", i, memoryProperties.memoryProperties.memoryHeaps[i].flags);
}
*/
//////////////////////////////////////////////////////////////////////
// device and queue
//////////////////////////////////////////////////////////////////////
const float queuePriorities{ 1.0f };
VkDeviceQueueCreateInfo queueCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = queueFamilyIndex,
.queueCount = 1,
.pQueuePriorities = &queuePriorities
};
VkPhysicalDeviceVulkan12Features enabledVulkan12Features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.descriptorIndexing = true,
.shaderSampledImageArrayNonUniformIndexing = true,
.descriptorBindingVariableDescriptorCount = true,
.runtimeDescriptorArray = true,
.bufferDeviceAddress = true
};
VkPhysicalDeviceVulkan13Features enabledVulkan13Features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.pNext = &enabledVulkan12Features,
.synchronization2 = true,
.dynamicRendering = true,
};
VkPhysicalDeviceFeatures enabledFeatures{
.samplerAnisotropy = VK_TRUE
};
constexpr uint32_t enabledExtensionCount = 1;
char const * enabledExtensionNames[enabledExtensionCount]{ VK_KHR_SWAPCHAIN_EXTENSION_NAME };
VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &enabledVulkan13Features,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueCreateInfo,
.enabledExtensionCount = enabledExtensionCount,
.ppEnabledExtensionNames = enabledExtensionNames,
.pEnabledFeatures = &enabledFeatures
};
VK_CHECK(vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device));
vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue);
//////////////////////////////////////////////////////////////////////
// window and surface
//////////////////////////////////////////////////////////////////////
SDL_Window * window = SDL_CreateWindow("Vulkan", 1024, 1024, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
SDL_CHECK_NONNULL(window);
SDL_CHECK(SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface));
SDL_CHECK(SDL_GetWindowSize(window, &windowSize.x, &windowSize.y));
VkSurfaceCapabilitiesKHR surfaceCapabilities{};
VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities));
printf("surfaceCapabilities currentExtent %d %d\n", surfaceCapabilities.currentExtent.width, surfaceCapabilities.currentExtent.height);
uint32_t surfaceFormatCount{ 0 };
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, nullptr));
VkSurfaceFormatKHR * surfaceFormats = NewM<VkSurfaceFormatKHR>(surfaceFormatCount);
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, surfaceFormats));
uint32_t surfaceFormatIndex{ 0 };
printf("surfaceFormatCount %d\n", surfaceFormatCount);
for (uint32_t i = 0; i < surfaceFormatCount; i++) {
printf("surfaceFormat[%d] %s %s%s\n", i, string_VkFormat(surfaceFormats[i].format), string_VkColorSpaceKHR(surfaceFormats[i].colorSpace), (i == surfaceFormatIndex) ? " [selected]" : "");
}
//////////////////////////////////////////////////////////////////////
// swapchain and images
//////////////////////////////////////////////////////////////////////
VkExtent2D imageExtent {
.width = surfaceCapabilities.currentExtent.width,
.height = surfaceCapabilities.currentExtent.height,
};
VkFormat imageFormat{ surfaceFormats[surfaceFormatIndex].format };
VkColorSpaceKHR imageColorSpace{ surfaceFormats[surfaceFormatIndex].colorSpace };
free(surfaceFormats);
VkSwapchainCreateInfoKHR swapchainCreateInfo{
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface,
.minImageCount = surfaceCapabilities.minImageCount,
.imageFormat = imageFormat,
.imageColorSpace = imageColorSpace,
.imageExtent = imageExtent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = VK_PRESENT_MODE_FIFO_KHR
};
VK_CHECK(vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain));
uint32_t swapchainImageCount{ 0 };
VK_CHECK(vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, nullptr));
swapchainImages = NewM<VkImage>(swapchainImageCount);
VK_CHECK(vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages));
swapchainImageViews = NewM<VkImageView>(swapchainImageCount);
for (uint32_t i = 0; i < swapchainImageCount; i++) {
VkImageViewCreateInfo imageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = swapchainImages[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = imageFormat,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
VK_CHECK(vkCreateImageView(device, &imageViewCreateInfo, nullptr, &swapchainImageViews[i]));
}
//////////////////////////////////////////////////////////////////////
// depth
//////////////////////////////////////////////////////////////////////
VkFormat depthFormat{ VK_FORMAT_UNDEFINED };
constexpr uint32_t depthFormatCount = 2;
VkFormat depthFormatList[depthFormatCount]{ VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT };
for (uint32_t i = 0; i < depthFormatCount; i++) {
VkFormatProperties2 formatProperties{ .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2 };
vkGetPhysicalDeviceFormatProperties2(physicalDevice, depthFormatList[i], &formatProperties);
if (formatProperties.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
depthFormat = depthFormatList[i];
break;
}
}
ASSERT(depthFormat != VK_FORMAT_UNDEFINED, "no depth format with VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT");
printf("depthFormat: %s\n", string_VkFormat(depthFormat));
VkImageCreateInfo depthImageCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = depthFormat,
.extent{
.width = imageExtent.width,
.height = imageExtent.height,
.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,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
VK_CHECK(vkCreateImage(device, &depthImageCreateInfo, nullptr, &depthImage));
VkMemoryRequirements depthImageMemoryRequirements;
vkGetImageMemoryRequirements(device, depthImage, &depthImageMemoryRequirements);
VkMemoryPropertyFlags depthImageMemoryPropertyFlags{
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
};
uint32_t depthImageMemoryTypeIndex = findMemoryTypeIndex(&memoryProperties.memoryProperties, depthImageMemoryRequirements.memoryTypeBits, depthImageMemoryPropertyFlags);
VkMemoryAllocateInfo depthImageAllocateInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = depthImageMemoryRequirements.size,
.memoryTypeIndex = depthImageMemoryTypeIndex,
};
VkDeviceMemory depthImageMemory;
VK_CHECK(vkAllocateMemory(device, &depthImageAllocateInfo, nullptr, &depthImageMemory));
VK_CHECK(vkBindImageMemory(device, depthImage, depthImageMemory, 0));
VkImageViewCreateInfo depthViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = depthImage,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = depthFormat,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.levelCount = 1,
.layerCount = 1
}
};
VK_CHECK(vkCreateImageView(device, &depthViewCreateInfo, nullptr, &depthImageView));
//////////////////////////////////////////////////////////////////////
// mesh
//////////////////////////////////////////////////////////////////////
VkDeviceSize vtxBufferSize{ vtx_size };
VkDeviceSize idxBufferSize{ idx_size };
VkBufferCreateInfo vertexIndexBufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = vtxBufferSize + idxBufferSize,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
VK_CHECK(vkCreateBuffer(device, &vertexIndexBufferCreateInfo, nullptr, &vertexIndexBuffer));
VkMemoryRequirements vertexIndexBufferMemoryRequirements;
vkGetBufferMemoryRequirements(device, vertexIndexBuffer, &vertexIndexBufferMemoryRequirements);
VkMemoryPropertyFlags vertexIndexBufferMemoryPropertyFlags{
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
};
uint32_t vertexIndexBufferMemoryTypeIndex = findMemoryTypeIndex(&memoryProperties.memoryProperties, vertexIndexBufferMemoryRequirements.memoryTypeBits, vertexIndexBufferMemoryPropertyFlags);
VkMemoryAllocateInfo vertexIndexBufferAllocateInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = vertexIndexBufferMemoryRequirements.size,
.memoryTypeIndex = vertexIndexBufferMemoryTypeIndex,
};
VkDeviceMemory vertexIndexBufferMemory;
VK_CHECK(vkAllocateMemory(device, &vertexIndexBufferAllocateInfo, nullptr, &vertexIndexBufferMemory));
VK_CHECK(vkBindBufferMemory(device, vertexIndexBuffer, vertexIndexBufferMemory, 0));
void * mappedData;
VK_CHECK(vkMapMemory(device, vertexIndexBufferMemory, 0, vertexIndexBufferCreateInfo.size, 0, &mappedData));
memcpy((void *)(((ptrdiff_t)mappedData) + 0), vtx_start, vtx_size);
memcpy((void *)(((ptrdiff_t)mappedData) + vtx_size), idx_start, idx_size);
vkUnmapMemory(device, vertexIndexBufferMemory);
//////////////////////////////////////////////////////////////////////
// shader buffers
//////////////////////////////////////////////////////////////////////
for (uint32_t i = 0; i < maxFramesInFlight; i++) {
VkBufferCreateInfo shaderBufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = sizeof(ShaderData),
.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
VK_CHECK(vkCreateBuffer(device, &shaderBufferCreateInfo, nullptr, &shaderDataBuffers[i].buffer));
VkMemoryRequirements shaderBufferMemoryRequirements;
vkGetBufferMemoryRequirements(device, shaderDataBuffers[i].buffer, &shaderBufferMemoryRequirements);
VkMemoryPropertyFlags shaderBufferMemoryPropertyFlags{
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
};
uint32_t shaderBufferMemoryTypeIndex = findMemoryTypeIndex(&memoryProperties.memoryProperties, depthImageMemoryRequirements.memoryTypeBits, shaderBufferMemoryPropertyFlags);
VkMemoryAllocateFlagsInfo shaderBufferAllocateFlagsInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT,
};
VkMemoryAllocateInfo shaderBufferAllocateInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &shaderBufferAllocateFlagsInfo,
.allocationSize = shaderBufferMemoryRequirements.size,
.memoryTypeIndex = shaderBufferMemoryTypeIndex,
};
VkDeviceMemory shaderBufferMemory;
VK_CHECK(vkAllocateMemory(device, &shaderBufferAllocateInfo, nullptr, &shaderBufferMemory));
VK_CHECK(vkBindBufferMemory(device, shaderDataBuffers[i].buffer, shaderBufferMemory, 0));
VkBufferDeviceAddressInfo shaderBufferDeviceAddressInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = shaderDataBuffers[i].buffer
};
shaderDataBuffers[i].deviceAddress = vkGetBufferDeviceAddress(device, &shaderBufferDeviceAddressInfo);
}
//////////////////////////////////////////////////////////////////////
// synchronization objects
//////////////////////////////////////////////////////////////////////
VkSemaphoreCreateInfo semaphoreCreateInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
};
VkFenceCreateInfo fenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT
};
for (uint32_t i = 0; i < maxFramesInFlight; i++) {
VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &fences[i]));
VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &presentSemaphores[i]));
}
renderSemaphores = NewM<VkSemaphore>(swapchainImageCount);
for (uint32_t i = 0; i < swapchainImageCount; i++) {
VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderSemaphores[i]));
}
//////////////////////////////////////////////////////////////////////
// command
//////////////////////////////////////////////////////////////////////
VkCommandPoolCreateInfo commandPoolCreateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = queueFamilyIndex
};
VK_CHECK(vkCreateCommandPool(device, &commandPoolCreateInfo, nullptr, &commandPool));
VkCommandBufferAllocateInfo commandBufferAllocateCreateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = commandPool,
.commandBufferCount = maxFramesInFlight
};
VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateCreateInfo, commandBuffers));
//////////////////////////////////////////////////////////////////////
// shaders
//////////////////////////////////////////////////////////////////////
VkShaderModuleCreateInfo vertexShaderModuleCreateInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = vs_size,
.pCode = (uint32_t *)vs_start
};
VkShaderModule vertexShaderModule{};
VK_CHECK(vkCreateShaderModule(device, &vertexShaderModuleCreateInfo, nullptr, &vertexShaderModule));
VkShaderModuleCreateInfo pixelShaderModuleCreateInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = ps_size,
.pCode = (uint32_t *)ps_start
};
VkShaderModule pixelShaderModule{};
VK_CHECK(vkCreateShaderModule(device, &pixelShaderModuleCreateInfo, nullptr, &pixelShaderModule));
//////////////////////////////////////////////////////////////////////
// pipeline
//////////////////////////////////////////////////////////////////////
VkPushConstantRange pushConstantRange{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.size = sizeof(VkDeviceAddress)
};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0,
.pSetLayouts = nullptr,
//.setLayoutCount = 1,
//.pSetLayouts = &descriptorSetLayoutTex,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange
};
VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
VkVertexInputBindingDescription vertexBindingDescriptions[1]{
{
.binding = 0,
.stride = (3 * (sizeof (float)) * 3),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
}
};
VkVertexInputAttributeDescription vertexAttributeDescriptions[3]{
{ .location = 0, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = 3 * 4 * 0 },
{ .location = 1, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = 3 * 4 * 1 },
{ .location = 2, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = 3 * 4 * 2 },
};
VkPipelineVertexInputStateCreateInfo vertexInputState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = vertexBindingDescriptions,
.vertexAttributeDescriptionCount = 3,
.pVertexAttributeDescriptions = vertexAttributeDescriptions,
};
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
};
VkPipelineShaderStageCreateInfo shaderStages[2]{
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vertexShaderModule, .pName = "VSMain"
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = pixelShaderModule, .pName = "PSMain"
}
};
VkPipelineViewportStateCreateInfo viewportState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1
};
VkDynamicState dynamicStates[2]{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 2,
.pDynamicStates = dynamicStates
};
VkPipelineDepthStencilStateCreateInfo depthStencilState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL
};
VkPipelineRenderingCreateInfo renderingCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &imageFormat,
.depthAttachmentFormat = depthFormat
};
VkPipelineColorBlendAttachmentState blendAttachment{
.colorWriteMask = 0xF
};
VkPipelineColorBlendStateCreateInfo colorBlendState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &blendAttachment
};
VkPipelineRasterizationStateCreateInfo rasterizationState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.lineWidth = 1.0f
};
VkPipelineMultisampleStateCreateInfo multisampleState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
VkGraphicsPipelineCreateInfo pipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &renderingCreateInfo,
.stageCount = 2,
.pStages = shaderStages,
.pVertexInputState = &vertexInputState,
.pInputAssemblyState = &inputAssemblyState,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizationState,
.pMultisampleState = &multisampleState,
.pDepthStencilState = &depthStencilState,
.pColorBlendState = &colorBlendState,
.pDynamicState = &dynamicState,
.layout = pipelineLayout
};
VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &pipeline));
//////////////////////////////////////////////////////////////////////
// loop
//////////////////////////////////////////////////////////////////////
bool updateSwapchain{ false };
uint32_t frameIndex{ 0 };
uint32_t imageIndex{ 0 };
bool quit{ false };
while (quit == false) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
quit = true;
}
}
// wait for fence
VK_CHECK(vkWaitForFences(device, 1, &fences[frameIndex], true, UINT64_MAX));
VK_CHECK(vkResetFences(device, 1, &fences[frameIndex]));
// acquire next image
VK_CHECK_SWAPCHAIN(vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, presentSemaphores[frameIndex], VK_NULL_HANDLE, &imageIndex));
// command buffer
VkCommandBuffer& commandBuffer = commandBuffers[frameIndex];
VK_CHECK(vkResetCommandBuffer(commandBuffer, 0));
VkCommandBufferBeginInfo commandBufferBeginInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
};
VK_CHECK(vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo));
VkImageMemoryBarrier2 outputBarriers[2]{
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = swapchainImages[imageIndex],
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
},
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = depthImage,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.levelCount = 1,
.layerCount = 1
}
}
};
VkDependencyInfo barrierDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 2,
.pImageMemoryBarriers = outputBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &barrierDependencyInfo);
VkRenderingAttachmentInfo colorRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = swapchainImageViews[imageIndex],
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue{ .color{ 0.0f, 0.0f, 0.2f, 1.0f } }
};
VkRenderingAttachmentInfo depthRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = depthImageView,
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.clearValue{ .depthStencil{ 1.0f, 0 } }
};
VkRenderingInfo renderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea{ .extent{ .width = (uint32_t)windowSize.x, .height = (uint32_t)windowSize.y } },
.layerCount = 1,
.colorAttachmentCount = 1,
.pColorAttachments = &colorRenderingAttachmentInfo,
.pDepthAttachment = &depthRenderingAttachmentInfo
};
vkCmdBeginRendering(commandBuffer, &renderingInfo);
VkViewport viewport{
.width = static_cast<float>(windowSize.x),
.height = static_cast<float>(windowSize.y),
.minDepth = 0.0f,
.maxDepth = 1.0f
};
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{ .extent{ .width = (uint32_t)windowSize.x, .height = (uint32_t)windowSize.y } };
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDeviceSize vertexOffset{ 0 };
//vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSetTex, 0, nullptr);
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexIndexBuffer, &vertexOffset);
VkDeviceSize indexOffset{ vtxBufferSize };
vkCmdBindIndexBuffer(commandBuffer, vertexIndexBuffer, indexOffset, VK_INDEX_TYPE_UINT32);
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VkDeviceAddress), &shaderDataBuffers[frameIndex].deviceAddress);
VkDeviceSize indexCount{ 3 };
vkCmdDrawIndexed(commandBuffer, indexCount, 3, 0, 0, 0);
vkCmdEndRendering(commandBuffer);
VkImageMemoryBarrier2 barrierPresent{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = 0,
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.image = swapchainImages[imageIndex],
.subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1 }
};
VkDependencyInfo barrierPresentDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &barrierPresent
};
vkCmdPipelineBarrier2(commandBuffer, &barrierPresentDependencyInfo);
VK_CHECK(vkEndCommandBuffer(commandBuffer));
// submit to graphics queue
VkPipelineStageFlags waitStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &presentSemaphores[frameIndex],
.pWaitDstStageMask = &waitStages,
.commandBufferCount = 1,
.pCommandBuffers = &commandBuffer,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &renderSemaphores[imageIndex],
};
VK_CHECK(vkQueueSubmit(queue, 1, &submitInfo, fences[frameIndex]));
frameIndex = (frameIndex + 1) % maxFramesInFlight;
// present
VkPresentInfoKHR presentInfo{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &renderSemaphores[imageIndex],
.swapchainCount = 1,
.pSwapchains = &swapchain,
.pImageIndices = &imageIndex
};
VK_CHECK_SWAPCHAIN(vkQueuePresentKHR(queue, &presentInfo));
assert(updateSwapchain == 0);
}
}