work on windows platform layer

This commit is contained in:
Matthew 2025-03-04 23:29:35 +11:00
parent 48c7e3f86d
commit ddcdf479a2
12 changed files with 317 additions and 91 deletions

View File

@ -2,10 +2,10 @@
setlocal enabledelayedexpansion setlocal enabledelayedexpansion
set vulkan_include=C:\VulkanSDK\1.4.304.1\Include set vulkan_include=C:\VulkanSDK\1.4.304.1\Include
set linker_flags=-incremental:no -opt:ref User32.lib kernel32.lib
mkdir build mkdir build
pushd build pushd build
cl /c /I ..\external\vma /I %vulkan_include% ..\external\vma\vma.cpp cl /c /I ..\external\vma /I %vulkan_include% ..\external\vma\vma.cpp
cl /I ..\external ..\src\main.c cl /DEBUG /I ..\external /I %vulkan_include% -DBUILD_DEBUG -DSTG_VULKAN_RENDERER ..\src\main.c /link %linker_flags%
popd popd

View File

@ -1,7 +1,7 @@
static Arena *CreateArena(rawptr buffer, isize length) static Arena *CreateArena(rawptr buffer, isize length)
{ {
Arena *arena = (Arena *)buffer; Arena *arena = (Arena *)buffer;
buffer += ARENA_HEADER_SIZE; buffer = PtrAdd(buffer, ARENA_HEADER_SIZE);
arena->buffer = buffer; arena->buffer = buffer;
arena->length = length; arena->length = length;

View File

@ -10,7 +10,7 @@ static void Run()
rawptr renderer_mem = ArenaAlloc(arena, renderer_mem_size); rawptr renderer_mem = ArenaAlloc(arena, renderer_mem_size);
Arena *renderer_arena = CreateArenaDebug(renderer_mem, MB(8), __LINE__); Arena *renderer_arena = CreateArenaDebug(renderer_mem, MB(8), __LINE__);
Assert(CreateWindow(), "Failed to initialize the window"); Assert(CreateSystemWindow(), "Failed to initialize the window");
Assert(InitRenderer(renderer_arena), "Failed to initialize the renderer"); Assert(InitRenderer(renderer_arena), "Failed to initialize the renderer");
b32 quit = false; b32 quit = false;

View File

@ -82,12 +82,12 @@ i32 EPrintf(const char *fmt, ...)
return result; return result;
} }
b32 CreateWindow() b32 CreatePlatformWindow()
{ {
return _CreateWindow(); return _CreateWindow();
} }
Window *GetWindow() Window *GetWindowPtr()
{ {
return _GetWindow(); return _GetWindow();
} }

View File

@ -61,12 +61,11 @@ i32 Printfln(const char *fmt, ...);
i32 EPrintf(const char *fmt, ...); i32 EPrintf(const char *fmt, ...);
// Window Functions // Window Functions
b32 CreateWindow(); b32 CreateSystemWindow();
Window *GetWindow();
b32 GetWindowEvent(WindowEvent *event); b32 GetWindowEvent(WindowEvent *event);
void WaitForWindowEvent(WindowEvent *event); void WaitForWindowEvent(WindowEvent *event);
WindowSize GetWindowSize(); WindowSize GetWindowSize();
// Directory Functions // Directory Functions
b32 ChangeWorkingDir(const char *); b32 ChangeWorkingDir(const char *);
u8 *OpenFile(const char *); u8 *OSOpenFile(const char *);

View File

@ -347,7 +347,7 @@ b32 ChangeWorkingDir(const char *)
return success; return success;
} }
u8 *OpenFile(const char *) u8 *OSOpenFile(const char *)
{ {
u8 *bytes = NULL; u8 *bytes = NULL;

View File

@ -1,3 +1,7 @@
HINSTANCE win32_instance = {};
Window win32_window = {};
LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param, LPARAM l_param) LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
{ {
LRESULT result = 0; LRESULT result = 0;
@ -31,12 +35,176 @@ LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param, LPARAM l_
int CALLBACK WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line, int show_code) int CALLBACK WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line, int show_code)
{ {
win32_instance = instance;
WNDCLASS window_class = { WNDCLASS window_class = {
.style = CS_OWNDC|CS_HREDREAW|CS_VREDRAW, .style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW,
.lpfnWndProc = , .lpfnWndProc = WindowProc,
.hInstance = instance, .hInstance = instance,
.lpszClassName = "GearsWindowClass", .lpszClassName = "GearsWindowClass",
}; };
if (RegisterClass(&window_class))
{
HWND window_handle = CreateWindowEx(
0,
window_class.lpszClassName,
"Video Game",
WS_OVERLAPPEDWINDOW|WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
instance,
0
);
if (window_handle)
{
while (true)
{
MSG message;
BOOL message_result = GetMessage(&message, 0, 0, 0);
if (message_result > 0)
{
TranslateMessage(&message);
DispatchMessage(&message);
}
else
{
break;
}
}
}
}
} }
b32 _LoadLib(const char *name, Library *out_lib)
{
b32 success = true;
out_lib->module = LoadLibraryA("vulkan-1.dll");
if (!out_lib->module)
success = false;
return success;
}
b32 _LoadFn(const char *name, Library *lib, Function *out_fn)
{
b32 success = true;
out_fn->fn = GetProcAddress(lib->module, name);
if (!out_fn->fn)
success = false;
return success;
}
b32 _InitPlatform()
{
b32 success = true;
return success;
}
rawptr _MemAlloc(isize size)
{
return NULL;
}
rawptr _MemAllocZeroed(isize size)
{
return NULL;
}
isize _GetPageSize()
{
return 0;
}
i32 _EPrint(void const *str)
{
return 0;
}
i32 _Printf(const char *fmt, ...)
{
return 0;
}
i32 _Printfln(const char *fmt, ...)
{
return 0;
}
i32 _EPrintf(const char *fmt, ...)
{
return 0;
}
b32 _CreateWindow()
{
b32 success = true;
WNDCLASS window_class = {
.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW,
.lpfnWndProc = WindowProc,
.hInstance = win32_instance,
.lpszClassName = WINDOW_CLASS_NAME,
};
ATOM r_class_atom = RegisterClass(&window_class);
if (!r_class_atom)
success = false;
if (success)
{
HWND window_handle = CreateWindowEx(
0,
window_class.lpszClassName,
"Video Game",
WS_OVERLAPPEDWINDOW|WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
win32_instance,
0
);
if (!window_handle)
success = false;
else
win32_window.handle = window_handle;
}
return success;
}
Window *_GetWindow()
{
return NULL;
}
b32 _GetWindowEvent(WindowEvent *event)
{
b32 success = true;
return success;
}
void _WaitForWindowEvent(WindowEvent *event)
{
}
WindowSize _GetWindowSize()
{
return (WindowSize){ .w = 0, .h = 0 };
}

View File

@ -4,6 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#define WINDOW_CLASS_NAME "GearsWindowClass"
typedef int8_t i8; typedef int8_t i8;
typedef int16_t i16; typedef int16_t i16;
typedef int32_t i32; typedef int32_t i32;
@ -35,17 +37,18 @@ typedef void * rawptr;
typedef struct typedef struct
{ {
i32 lib; HMODULE module;
} Library; } Library;
typedef struct typedef struct
{ {
i32 win; HINSTANCE instance;
HWND handle;
} Window; } Window;
typedef struct typedef struct
{ {
i32 fn; FARPROC fn;
} Function; } Function;
b32 _LoadLib(const char *name, Library *out_lib); b32 _LoadLib(const char *name, Library *out_lib);

View File

@ -745,7 +745,7 @@ static b32 InitVkDeviceFunctions() {
#ifdef __linux__ #ifdef __linux__
static b32 CreateSurface() static b32 CreateSurface()
{ {
Window *window = GetWindow(); Window *window = GetWindowPtr();
VkXcbSurfaceCreateInfoKHR surface_info = { VkXcbSurfaceCreateInfoKHR surface_info = {
.sType = STYPE(XCB_SURFACE_CREATE_INFO_KHR), .sType = STYPE(XCB_SURFACE_CREATE_INFO_KHR),
.connection = window->connection, .connection = window->connection,
@ -759,6 +759,15 @@ static b32 CreateSurface()
return result == VK_SUCCESS; return result == VK_SUCCESS;
} }
#elif _WIN32
static b32 CreateSurface()
{
Window *window = GetWindowPtr();
VkWin32SurfaceCreateInfoKHR surface_info = {
.sType = STYPE(WIN_32_SURFACE_CREATE_INFO_KHR),
.hinstance =
};
}
#endif #endif
static b32 LoadVulkanLib() static b32 LoadVulkanLib()
@ -770,7 +779,7 @@ static b32 LoadVulkanLib()
lib_found = LoadLib(vulkan_libs[i], lib); lib_found = LoadLib(vulkan_libs[i], lib);
if (lib_found) { if (lib_found) {
lib_found = LoadFn("vkGetInstanceProcAddr", lib, &fn); lib_found = LoadFn("vkGetInstanceProcAddr", lib, &fn);
vkGetInstanceProcAddr = fn.fn; vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)fn.fn;
break; break;
} }
} }

View File

@ -1,6 +1,15 @@
#pragma once #pragma once
#if __linux__
#define VK_USE_PLATFORM_XCB_KHR #define VK_USE_PLATFORM_XCB_KHR
#elif _WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#define VK_NO_PROTOTYPES #define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
@ -323,7 +332,9 @@ static char *vulkan_libs[] = {
#endif #endif
#if _WIN32 #if _WIN32
#error Not yet implemented static char *vulkan_libs[] = {
"vulkan-1.dll",
};
#endif #endif
#if __APPLE__ || __MACH__ #if __APPLE__ || __MACH__

View File

@ -17,6 +17,7 @@
#define BitEq(var, bits) (((var) & (bits)) == bits) #define BitEq(var, bits) (((var) & (bits)) == bits)
#define AlignPow2(x, b) (((x) + (b) - 1) & (~((b) - 1))) #define AlignPow2(x, b) (((x) + (b) - 1) & (~((b) - 1)))
#define IsPow2(x) ((x) != 0 && ((x) &((x) - 1)) == 0) #define IsPow2(x) ((x) != 0 && ((x) &((x) - 1)) == 0)
#define PtrAdd(ptr, add) (((char *)ptr) + add)
// Types // Types

View File

@ -24,10 +24,19 @@ static const char *instance_layers[] = {
static const char *instance_extensions[] = { static const char *instance_extensions[] = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_EXTENSION_NAME,
#ifdef __linux__
VK_KHR_XCB_SURFACE_EXTENSION_NAME, VK_KHR_XCB_SURFACE_EXTENSION_NAME,
#endif
#ifdef _WIN32
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
#endif
#ifdef BUILD_DEBUG #ifdef BUILD_DEBUG
VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
#endif #endif
}; };
static VkInstanceCreateInfo inst_info = { static VkInstanceCreateInfo inst_info = {
@ -86,10 +95,15 @@ static const VkPhysicalDeviceFeatures vk_features = {
.shaderStorageImageArrayDynamicIndexing = VK_TRUE .shaderStorageImageArrayDynamicIndexing = VK_TRUE
}; };
static VkPhysicalDeviceFeatures2 vk_features_2 = { static const VkPhysicalDeviceFeatures2 vk_features_2 = {
.sType = STYPE(PHYSICAL_DEVICE_FEATURES_2), .sType = STYPE(PHYSICAL_DEVICE_FEATURES_2),
.pNext = &vk_11_features, .pNext = &vk_11_features,
.features = vk_features .features = {
.shaderUniformBufferArrayDynamicIndexing = VK_TRUE,
.shaderSampledImageArrayDynamicIndexing = VK_TRUE,
.shaderStorageBufferArrayDynamicIndexing = VK_TRUE,
.shaderStorageImageArrayDynamicIndexing = VK_TRUE
},
}; };
static VkDeviceCreateInfo device_info = { static VkDeviceCreateInfo device_info = {
@ -293,88 +307,109 @@ static VkPipelineLayoutCreateInfo pipeline_layout_create_info = {
// PIPELINES // PIPELINES
// //
static VkDynamicState pipeline_dynamic_state[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; #define PIPELINE_DYNAMIC_STATE { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }
static const VkPipelineDynamicStateCreateInfo pipeline_default_dyn_state_info = { static VkDynamicState pipeline_dynamic_state[] = PIPELINE_DYNAMIC_STATE;
.sType = STYPE(PIPELINE_DYNAMIC_STATE_CREATE_INFO),
.pDynamicStates = pipeline_dynamic_state,
.dynamicStateCount = Len(pipeline_dynamic_state),
};
const static VkPipelineInputAssemblyStateCreateInfo pipeline_default_assembly_info = { #define PIPELINE_DEFAULT_DYN_STATE_INFO { \
.sType = STYPE(PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO), .sType = STYPE(PIPELINE_DYNAMIC_STATE_CREATE_INFO), \
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .pDynamicStates = pipeline_dynamic_state, \
.primitiveRestartEnable = VK_FALSE, .dynamicStateCount = Len(pipeline_dynamic_state), \
}; }
const static VkPipelineRasterizationStateCreateInfo pipeline_default_rasterization_info = { static VkPipelineDynamicStateCreateInfo pipeline_default_dyn_state_info = PIPELINE_DEFAULT_DYN_STATE_INFO;
.sType = STYPE(PIPELINE_RASTERIZATION_STATE_CREATE_INFO),
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
};
const static VkPipelineMultisampleStateCreateInfo pipeline_default_multisample_info = { #define PIPELINE_DEFAULT_ASSEMBLY_INFO { \
.sType = STYPE(PIPELINE_MULTISAMPLE_STATE_CREATE_INFO), .sType = STYPE(PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO), \
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, \
.minSampleShading = 1.0, .primitiveRestartEnable = VK_FALSE, \
.alphaToCoverageEnable = VK_FALSE, }
.alphaToOneEnable = VK_FALSE,
};
const static VkPipelineDepthStencilStateCreateInfo pipeline_default_depth_info = { static VkPipelineInputAssemblyStateCreateInfo pipeline_default_assembly_info = PIPELINE_DEFAULT_ASSEMBLY_INFO;
.sType = STYPE(PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO),
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL,
.depthBoundsTestEnable = VK_FALSE,
.stencilTestEnable = VK_FALSE,
.minDepthBounds = 0.0,
.maxDepthBounds = 1.0,
};
const static VkPipelineRenderingCreateInfo pipeline_default_rendering_info = { #define PIPELINE_DEFAULT_RASTERIZATION_INFO { \
.sType = STYPE(PIPELINE_RENDERING_CREATE_INFO), .sType = STYPE(PIPELINE_RASTERIZATION_STATE_CREATE_INFO), \
.colorAttachmentCount = 1, .polygonMode = VK_POLYGON_MODE_FILL, \
//.pColorAttachmentFormats = FORMAT .lineWidth = 1.0, \
//.depthAttachmentFormat = DEPTH_FORMAT .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, \
// Do not forget these whatever you do }
};
const static VkPipelineColorBlendAttachmentState pipeline_default_blend_attach_state = { static VkPipelineRasterizationStateCreateInfo pipeline_default_rasterization_info = PIPELINE_DEFAULT_RASTERIZATION_INFO;
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_FALSE,
};
const static VkPipelineColorBlendStateCreateInfo pipeline_default_blend_info = { #define PIPELINE_DEFAULT_MULTISAMPLE_INFO { \
.sType = STYPE(PIPELINE_COLOR_BLEND_STATE_CREATE_INFO), .sType = STYPE(PIPELINE_MULTISAMPLE_STATE_CREATE_INFO), \
.logicOpEnable = VK_FALSE, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, \
.logicOp = VK_LOGIC_OP_COPY, .minSampleShading = 1.0, \
.attachmentCount = 1, .alphaToCoverageEnable = VK_FALSE, \
.pAttachments = &pipeline_default_blend_attach_state, .alphaToOneEnable = VK_FALSE, \
}; }
const static VkPipelineVertexInputStateCreateInfo pipeline_default_vertex_info = { static VkPipelineMultisampleStateCreateInfo pipeline_default_multisample_info = PIPELINE_DEFAULT_MULTISAMPLE_INFO;
.sType = STYPE(PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO),
}; #define PIPELINE_DEFAULT_DEPTH_INFO { \
.sType = STYPE(PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO), \
.depthTestEnable = VK_TRUE, \
.depthWriteEnable = VK_TRUE, \
.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL, \
.depthBoundsTestEnable = VK_FALSE, \
.stencilTestEnable = VK_FALSE, \
.minDepthBounds = 0.0, \
.maxDepthBounds = 1.0, \
}
static VkPipelineDepthStencilStateCreateInfo pipeline_default_depth_info = PIPELINE_DEFAULT_DEPTH_INFO;
#define PIPELINE_DEFAULT_RENDERING_INFO { \
.sType = STYPE(PIPELINE_RENDERING_CREATE_INFO), \
.colorAttachmentCount = 1, \
}
static VkPipelineRenderingCreateInfo pipeline_default_rendering_info = PIPELINE_DEFAULT_RENDERING_INFO;
#define PIPELINE_DEFAULT_BLEND_ATTACH_STATE { \
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, \
.blendEnable = VK_FALSE, \
}
static VkPipelineColorBlendAttachmentState pipeline_default_blend_attach_state = PIPELINE_DEFAULT_BLEND_ATTACH_STATE;
#define PIPELINE_DEFAULT_BLEND_INFO { \
.sType = STYPE(PIPELINE_COLOR_BLEND_STATE_CREATE_INFO), \
.logicOpEnable = VK_FALSE, \
.logicOp = VK_LOGIC_OP_COPY, \
.attachmentCount = 1, \
.pAttachments = &pipeline_default_blend_attach_state, \
}
static VkPipelineColorBlendStateCreateInfo pipeline_default_blend_info = PIPELINE_DEFAULT_BLEND_INFO;
#define PIPELINE_DEFAULT_VERTEX_INFO { \
.sType = STYPE(PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO), \
}
static VkPipelineVertexInputStateCreateInfo pipeline_default_vertex_info = PIPELINE_DEFAULT_VERTEX_INFO;
#define PIPELINE_DEFAULT_VIEWPORT_INFO { \
.sType = STYPE(PIPELINE_VIEWPORT_STATE_CREATE_INFO), \
.viewportCount = 1, \
.scissorCount = 1, \
}
const static VkPipelineViewportStateCreateInfo pipeline_default_viewport_info = PIPELINE_DEFAULT_VIEWPORT_INFO;
const static VkPipelineViewportStateCreateInfo pipeline_default_viewport_info = {
.sType = STYPE(PIPELINE_VIEWPORT_STATE_CREATE_INFO),
.viewportCount = 1,
.scissorCount = 1,
};
#define INIT_PIPELINE_DEFAULTS(pipeline) \ #define INIT_PIPELINE_DEFAULTS(pipeline) \
static VkPipelineDynamicStateCreateInfo pipeline##_dynamic_info = pipeline_default_dyn_state_info; \ VkPipelineDynamicStateCreateInfo pipeline##_dynamic_info = PIPELINE_DEFAULT_DYN_STATE_INFO; \
static VkPipelineInputAssemblyStateCreateInfo pipeline##_assembly_info = pipeline_default_assembly_info; \ VkPipelineInputAssemblyStateCreateInfo pipeline##_assembly_info = PIPELINE_DEFAULT_ASSEMBLY_INFO; \
static VkPipelineRasterizationStateCreateInfo pipeline##_rasterization_info = pipeline_default_rasterization_info; \ VkPipelineRasterizationStateCreateInfo pipeline##_rasterization_info = PIPELINE_DEFAULT_RASTERIZATION_INFO; \
static VkPipelineMultisampleStateCreateInfo pipeline##_multisample_info = pipeline_default_multisample_info; \ VkPipelineMultisampleStateCreateInfo pipeline##_multisample_info = PIPELINE_DEFAULT_MULTISAMPLE_INFO; \
static VkPipelineDepthStencilStateCreateInfo pipeline##_depth_info = pipeline_default_depth_info; \ VkPipelineDepthStencilStateCreateInfo pipeline##_depth_info = PIPELINE_DEFAULT_DEPTH_INFO; \
static VkPipelineRenderingCreateInfo pipeline##_rendering_info = pipeline_default_rendering_info; \ VkPipelineRenderingCreateInfo pipeline##_rendering_info = PIPELINE_DEFAULT_RENDERING_INFO; \
static VkPipelineColorBlendStateCreateInfo pipeline##_blend_info = pipeline_default_blend_info; \ VkPipelineColorBlendStateCreateInfo pipeline##_blend_info = PIPELINE_DEFAULT_BLEND_INFO; \
static VkPipelineVertexInputStateCreateInfo pipeline##_vertex_info = pipeline_default_vertex_info; \ VkPipelineVertexInputStateCreateInfo pipeline##_vertex_info = PIPELINE_DEFAULT_VERTEX_INFO; \
static VkPipelineViewportStateCreateInfo pipeline##_viewport_info = pipeline_default_viewport_info; \ VkPipelineViewportStateCreateInfo pipeline##_viewport_info = PIPELINE_DEFAULT_VIEWPORT_INFO; \
static VkGraphicsPipelineCreateInfo pipeline##_create_info = { \ VkGraphicsPipelineCreateInfo pipeline##_create_info = { \
.sType = STYPE(GRAPHICS_PIPELINE_CREATE_INFO), \ .sType = STYPE(GRAPHICS_PIPELINE_CREATE_INFO), \
.pNext = &pipeline##_rendering_info, \ .pNext = &pipeline##_rendering_info, \
.pVertexInputState = &pipeline##_vertex_info, \ .pVertexInputState = &pipeline##_vertex_info, \