diff --git a/.gitattributes b/.gitattributes index 615ae978..ee9b721b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,23 +3,28 @@ ############################## #- Source files -*.c linguist-language=C -*.h linguist-language=C -*.g linguist-language=HLSL -*.gh linguist-language=HLSL -*.cg linguist-language=C -*.cgh linguist-language=C -*.lay linguist-generated +*.c linguist-language=C +*.h linguist-language=C +*.g linguist-language=HLSL +*.gh linguist-language=HLSL +*.cg linguist-language=C +*.cgh linguist-language=C +*.lay linguist-language=C ############################## #- Binary files -*.exe filter=lfs diff=lfs merge=lfs -text -*.dll filter=lfs diff=lfs merge=lfs -text -*.lib filter=lfs diff=lfs merge=lfs -text -*.zip filter=lfs diff=lfs merge=lfs -text -*.tga filter=lfs diff=lfs merge=lfs -text -*.ase filter=lfs diff=lfs merge=lfs -text -*.ttf filter=lfs diff=lfs merge=lfs -text -*.mp3 filter=lfs diff=lfs merge=lfs -text -*.dat filter=lfs diff=lfs merge=lfs -text +*.exe filter=lfs diff=lfs merge=lfs -text +*.dll filter=lfs diff=lfs merge=lfs -text +*.lib filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.tga filter=lfs diff=lfs merge=lfs -text +*.ase filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.mp3 filter=lfs diff=lfs merge=lfs -text +*.dat filter=lfs diff=lfs merge=lfs -text + +############################## +#- Libraries + +# vulkan/* filter=lfs diff=lfs merge=lfs -text diff --git a/src/base/base.cgh b/src/base/base.cgh index 5e0c3e37..31ebdf3f 100644 --- a/src/base/base.cgh +++ b/src/base/base.cgh @@ -794,6 +794,7 @@ Inline u64 MixU64s(u64 seed_a, u64 seed_b) #if IsCpu StringList GetRawCommandline(void); + String GetAppName(void); String GetAppDirectory(void); void Echo(String msg); b32 Panic(String msg); diff --git a/src/base/base_win32/base_win32.c b/src/base/base_win32/base_win32.c index 1b0bd529..861c8a38 100644 --- a/src/base/base_win32/base_win32.c +++ b/src/base/base_win32/base_win32.c @@ -43,6 +43,11 @@ StringList GetRawCommandline(void) return W32.raw_command_line; } +String GetAppName(void) +{ + return W32.app_name; +} + void Echo(String msg) { HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); @@ -65,16 +70,22 @@ b32 Panic(String msg) LogPanic(msg); char msg_cstr[4096]; CstrFromStringToBuff(StringFromFixedArray(msg_cstr), msg); + b32 is_debug = IsRunningInDebugger(); + i32 mb_result = 0; { u32 mb_flags = MB_SETFOREGROUND | MB_ICONERROR; - MessageBoxExA(0, msg_cstr, "Fatal error", mb_flags, 0); + if (is_debug) + { + mb_flags |= MB_CANCELTRYCONTINUE; + } + mb_result = MessageBoxExA(0, msg_cstr, "Fatal error", mb_flags, 0); } HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE); if (console_handle != INVALID_HANDLE_VALUE) { WriteFile(console_handle, msg.text, msg.len, 0, 0); } - if (IsRunningInDebugger()) + if (is_debug && mb_result != IDCANCEL) { Assert(0); } @@ -508,18 +519,19 @@ i32 W32_Main(void) { String appdir_path = Zi; CommandlineArg appdir_arg = CommandlineArgFromName(Lit("appdir")); - CommandlineArg appname_arg = CommandlineArgFromName(Lit("appname")); + CommandlineArg app_name_arg = CommandlineArgFromName(Lit("app_name")); if (appdir_arg.exists && appdir_arg.value.len > 0) { appdir_path = appdir_arg.value; } else { - String appname = Lit(Stringize(DefaultAppName)); - if (appname_arg.exists && appname_arg.value.len > 0) + String app_name = Lit(Stringize(DefaultAppName)); + if (app_name_arg.exists && app_name_arg.value.len > 0) { - appname = appname_arg.value; + app_name = app_name_arg.value; } + W32.app_name = app_name; wchar_t *path_wstr = 0; HRESULT hr = SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, 0, &path_wstr); @@ -529,7 +541,7 @@ i32 W32_Main(void) } appdir_path = StringFromWstrNoLimit(perm, path_wstr); CoTaskMemFree(path_wstr); - appdir_path = PathFromString(perm, StringF(perm, "%F\\Cabin\\%F\\", FmtString(appdir_path), FmtString(appname)), '/'); + appdir_path = PathFromString(perm, StringF(perm, "%F\\Cabin\\%F\\", FmtString(appdir_path), FmtString(app_name)), '/'); } // Create app dir { diff --git a/src/base/base_win32/base_win32.h b/src/base/base_win32/base_win32.h index a7042056..6ee8538e 100644 --- a/src/base/base_win32/base_win32.h +++ b/src/base/base_win32/base_win32.h @@ -95,6 +95,7 @@ Struct(W32_Ctx) i64 ns_per_qpc; StringList raw_command_line; + String app_name; String appdir_path; //- Application control flow diff --git a/src/gpu/gpu.lay b/src/gpu/gpu.lay index 2b895d04..54213fe1 100644 --- a/src/gpu/gpu.lay +++ b/src/gpu/gpu.lay @@ -28,4 +28,4 @@ @IncludeC gpu_common.c // @DefaultDownstream Win32 gpu_dx12 -@DefaultDownstream Any gpu_vk +@DefaultDownstream Win32 gpu_vk diff --git a/src/gpu/gpu_vk/gpu_vk_core.c b/src/gpu/gpu_vk/gpu_vk_core.c index 45620a17..a462f85f 100644 --- a/src/gpu/gpu_vk/gpu_vk_core.c +++ b/src/gpu/gpu_vk/gpu_vk_core.c @@ -1,11 +1,588 @@ G_VK_Ctx G_VK = Zi; ThreadLocal G_VK_ThreadLocalCtx G_VK_tl = Zi; +PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = 0; + +// Define global procs +#define X(name, ...) CAT(PFN_, name) name = 0; + G_VK_ProcXList(X) +#undef X + //////////////////////////////////////////////////////////// //~ @hookimpl Bootstrap void G_Bootstrap(void) { + Arena *perm = PermArena(); + TempArena scratch = BeginScratchNoConflict(); + + Struct(VKProcDesc) + { + char *name_cstr; + void **dst; + G_VK_ProcFlag flags; + }; + VKProcDesc proc_descs[] = { + #define X(_name, _flags) { .name_cstr = #_name, .dst = (void **)&_name, .flags = _flags }, + G_VK_ProcXList(X) + #undef X + }; + + ////////////////////////////// + //- Load vulkan + + G_VK.lib = LoadLibraryW(L"vulkan-1.dll"); + if (!G_VK.lib) + { + Panic(Lit("Vulkan-1.dll not found. Ensure drivers are up to date.")); + } + + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress(G_VK.lib, "vkGetInstanceProcAddr"); + if (!vkGetInstanceProcAddr) + { + Panic(Lit("Failed to initialize Vulkan - proc 'vkGetInstanceProcAddr' not found")); + } + + ////////////////////////////// + //- Load global procs + + for (u32 proc_idx = 0; proc_idx < countof(proc_descs); ++proc_idx) + { + VKProcDesc desc = proc_descs[proc_idx]; + if (AnyBit(desc.flags, G_VK_ProcFlag_Global)) + { + void *proc = (void *)vkGetInstanceProcAddr(0, desc.name_cstr); + *desc.dst = proc; + if (!proc && !AnyBit(desc.flags, G_VK_ProcFlag_NoForce)) + { + String name = StringFromCstrNoLimit(desc.name_cstr); + Panic(StringF(PermArena(), "Failed to initialize Vulkan - Unable to locate procedure '%F'", FmtString(name))); + } + } + } + + u32 instance_version = 0; + G_VK_ForceCall(vkEnumerateInstanceVersion, &instance_version); + LogInfoF("Vulkan instance version: %F", FmtUint(instance_version)); + + ////////////////////////////// + //- Fetch extensions + + b32 validation_enabled = CommandlineArgFromName(Lit("gpu-debug-validation")).exists; + b32 debug_enabled = validation_enabled || CommandlineArgFromName(Lit("gpu-debug")).exists; + + u32 extensions_count = 0; + String *extensions = 0; + char **extension_cstrs = 0; + { + u32 available_extensions_count = 0; + VkExtensionProperties *available_extensions = 0; + { + vkEnumerateInstanceExtensionProperties(0, &available_extensions_count, 0); + available_extensions = PushStructsNoZero(scratch.arena, VkExtensionProperties, available_extensions_count); + vkEnumerateInstanceExtensionProperties(0, &available_extensions_count, available_extensions); + } + + { + // Build required extensions list + StringList extensions_list = Zi; + { + PushStringToList(scratch.arena, &extensions_list, Lit("VK_KHR_surface")); + PushStringToList(scratch.arena, &extensions_list, Lit("VK_KHR_win32_surface")); + if (debug_enabled) + { + PushStringToList(scratch.arena, &extensions_list, Lit("VK_EXT_debug_report")); + } + } + // Create extension arrays + { + extensions_count = extensions_list.count; + extensions = PushStructsNoZero(scratch.arena, String, extensions_count); + extension_cstrs = PushStructsNoZero(scratch.arena, char *, extensions_count); + { + u32 extension_idx = 0; + for (StringListNode *n = extensions_list.first; n; n = n->next) + { + extension_cstrs[extension_idx] = CstrFromString(scratch.arena, n->s); + extensions[extension_idx] = n->s; + ++extension_idx; + } + } + } + } + + // Find missing extensions + StringList missing_extensions = Zi; + for (u32 extension_idx = 0; extension_idx < extensions_count; ++extension_idx) + { + String extension_name = extensions[extension_idx]; + b32 found = 0; + for (u32 available_extension_idx = 0; available_extension_idx < available_extensions_count; ++available_extension_idx) + { + String available_extension_name = StringFromCstrNoLimit(available_extensions[available_extension_idx].extensionName); + if (MatchString(available_extension_name, extension_name)) + { + found = 1; + break; + } + } + if (!found) + { + PushStringToList(scratch.arena, &missing_extensions, StringF(scratch.arena, " - %F", FmtString(extension_name))); + } + } + + // Notify user if required extensions are missing + if (missing_extensions.count > 0) + { + String items = StringFromList(scratch.arena, missing_extensions, Lit("\n")); + String msg = StringF( + scratch.arena, + "Vulkan failed to initialize because the following instance extensions were not detected:\n\n" + "%F\n\n" + "Ensure that drivers are up to date.", + FmtString(items) + ); + Panic(msg); + } + } + + ////////////////////////////// + //- Create instance + + VkInstance instance = Zi; + { + VkApplicationInfo app_info = Zi; + { + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = CstrFromString(perm, GetAppName()); + app_info.applicationVersion = 0; + app_info.pEngineName = "Engine"; + app_info.engineVersion = 0; + app_info.apiVersion = VK_API_VERSION_1_3; + } + + VkInstanceCreateInfo create_info = Zi; + { + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + create_info.enabledLayerCount = 0; + create_info.ppEnabledLayerNames = 0; + create_info.enabledExtensionCount = extensions_count; + create_info.ppEnabledExtensionNames = extension_cstrs; + } + + VkResult r = vkCreateInstance(&create_info, 0, &G_VK.instance); + if (r != VK_SUCCESS) + { + String result_str = G_VK_StringFromVkResult(r); + String err = StringF(perm, "Failed to initialize Vulkan - vkCreateInstance failed with '%F'. Ensure that drivers are up to date.", FmtString(result_str)); + Panic(err); + } + } + + ////////////////////////////// + //- Load instance procs + + for (u32 proc_idx = 0; proc_idx < countof(proc_descs); ++proc_idx) + { + VKProcDesc desc = proc_descs[proc_idx]; + if (!AnyBit(desc.flags, G_VK_ProcFlag_Global)) + { + void *proc = (void *)vkGetInstanceProcAddr(G_VK.instance, desc.name_cstr); + *desc.dst = proc; + if (!proc && !AnyBit(desc.flags, G_VK_ProcFlag_NoForce)) + { + String name = StringFromCstrNoLimit(desc.name_cstr); + Panic(StringF(PermArena(), "Failed to initialize Vulkan - Unable to locate procedure '%F' on instance 0x%F", FmtString(name), FmtHex((u64)G_VK.instance, .z = 16))); + } + } + } + + ////////////////////////////// + //- Register debug callback + + if (debug_enabled && vkCreateDebugReportCallbackEXT) + { + VkDebugReportCallbackCreateInfoEXT desc = Zi; + { + desc.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + desc.pfnCallback = G_VK_DebugCallback; + desc.flags |= VK_DEBUG_REPORT_INFORMATION_BIT_EXT; + desc.flags |= VK_DEBUG_REPORT_WARNING_BIT_EXT; + desc.flags |= VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + desc.flags |= VK_DEBUG_REPORT_ERROR_BIT_EXT; + desc.flags |= VK_DEBUG_REPORT_DEBUG_BIT_EXT; + } + VkDebugReportCallbackEXT *debug_callback = 0; + G_VK_ForceCall(vkCreateDebugReportCallbackEXT, G_VK.instance, &desc, 0, (void *)&debug_callback); + } + + ////////////////////////////// + //- Fetch physical device + + VkPhysicalDevice physical_device = Zi; + VkPhysicalDeviceProperties physical_device_props = Zi; + VkPhysicalDeviceFeatures physical_device_features = Zi; + String physical_device_name = Zi; + { + // TODO: Command-line option to force lower-score GPUs + u32 target_gpu_score_idx = 0; + + u32 gpus_count = 0; + VkPhysicalDevice *gpus = 0; + { + vkEnumeratePhysicalDevices(G_VK.instance, &gpus_count, 0); + gpus = PushStructsNoZero(scratch.arena, VkPhysicalDevice, gpus_count); + vkEnumeratePhysicalDevices(G_VK.instance, &gpus_count, gpus); + } + + Struct(ScoreEntry) + { + u64 score; + VkPhysicalDevice gpu; + VkPhysicalDeviceProperties props; + VkPhysicalDeviceFeatures features; + String name; + }; + ScoreEntry *entries = PushStructs(scratch.arena, ScoreEntry, gpus_count); + + u64 highest_score = 0; + u64 highest_score_idx = 0; + for (u32 gpu_idx = 0; gpu_idx < gpus_count; ++gpu_idx) + { + VkPhysicalDevice gpu = gpus[gpu_idx]; + VkPhysicalDeviceProperties props = Zi; + VkPhysicalDeviceFeatures features = Zi; + u64 score = 0; + String name = Zi; + { + vkGetPhysicalDeviceProperties(gpu, &props); + vkGetPhysicalDeviceFeatures(gpu, &features); + } + { + name = StringFromCstrNoLimit(props.deviceName); + String props_str = G_VK_StringFromVkPhysicalDeviceProperties(scratch.arena, props); + String features_str = G_VK_StringFromVkPhysicalDeviceFeatures(scratch.arena, features); + if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + { + score += Tebi(256); + } + else if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) + { + score += Tebi(128); + } + // TODO: Score based on available video memory + LogInfoF( + "Enumerating physical Vulkan device #%F '%F', - Score: %F, Properties: %F, Features: %F", + FmtUint(gpu_idx), + FmtString(name), + FmtUint(score), + FmtString(props_str), + FmtString(features_str) + ); + if (score > highest_score) + { + highest_score = score; + highest_score_idx = gpu_idx; + } + } + ScoreEntry *entry = &entries[gpu_idx]; + entry->gpu = gpu; + entry->props = props; + entry->features = features; + entry->name = name; + entry->score = score; + } + + if (highest_score_idx < gpus_count) + { + physical_device = entries[highest_score_idx].gpu; + physical_device_props = entries[highest_score_idx].props; + physical_device_features = entries[highest_score_idx].features; + physical_device_name = entries[highest_score_idx].name; + } + + if (!physical_device) + { + Panic(Lit("Failed to initialize Vulkan - No devices found")); + } + } + + ////////////////////////////// + //- Enforce device capability + + // TODO + + ////////////////////////////// + //- Create device + + { + VkDeviceCreateInfo device_desc = Zi; + + // vkCreateDevice( + } + + ////////////////////////////// + //- Finish initialization + + G_VK.initialized = 1; + EndScratch(scratch); +} + +//////////////////////////////////////////////////////////// +//~ Helpers + +String G_VK_StringFromVkResult(VkResult v) +{ + switch (v) + { + default: return Lit("Unknown error"); + #define X(name) case name: return Lit(#name); + X(VK_SUCCESS) + X(VK_NOT_READY) + X(VK_TIMEOUT) + X(VK_EVENT_SET) + X(VK_EVENT_RESET) + X(VK_INCOMPLETE) + X(VK_ERROR_OUT_OF_HOST_MEMORY) + X(VK_ERROR_OUT_OF_DEVICE_MEMORY) + X(VK_ERROR_INITIALIZATION_FAILED) + X(VK_ERROR_DEVICE_LOST) + X(VK_ERROR_MEMORY_MAP_FAILED) + X(VK_ERROR_LAYER_NOT_PRESENT) + X(VK_ERROR_EXTENSION_NOT_PRESENT) + X(VK_ERROR_FEATURE_NOT_PRESENT) + X(VK_ERROR_INCOMPATIBLE_DRIVER) + X(VK_ERROR_TOO_MANY_OBJECTS) + X(VK_ERROR_FORMAT_NOT_SUPPORTED) + X(VK_ERROR_FRAGMENTED_POOL) + X(VK_ERROR_UNKNOWN) + X(VK_ERROR_VALIDATION_FAILED) + X(VK_ERROR_OUT_OF_POOL_MEMORY) + X(VK_ERROR_INVALID_EXTERNAL_HANDLE) + X(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS) + X(VK_ERROR_FRAGMENTATION) + X(VK_PIPELINE_COMPILE_REQUIRED) + X(VK_ERROR_NOT_PERMITTED) + X(VK_ERROR_SURFACE_LOST_KHR) + X(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR) + X(VK_SUBOPTIMAL_KHR) + X(VK_ERROR_OUT_OF_DATE_KHR) + X(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR) + X(VK_ERROR_INVALID_SHADER_NV) + X(VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR) + X(VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR) + X(VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR) + X(VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR) + X(VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR) + X(VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR) + X(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT) + X(VK_ERROR_PRESENT_TIMING_QUEUE_FULL_EXT) + X(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) + X(VK_THREAD_IDLE_KHR) + X(VK_THREAD_DONE_KHR) + X(VK_OPERATION_DEFERRED_KHR) + X(VK_OPERATION_NOT_DEFERRED_KHR) + X(VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR) + X(VK_ERROR_COMPRESSION_EXHAUSTED_EXT) + X(VK_INCOMPATIBLE_SHADER_BINARY_EXT) + X(VK_PIPELINE_BINARY_MISSING_KHR) + X(VK_ERROR_NOT_ENOUGH_SPACE_KHR) + #undef X + } +} + +String G_VK_StringFromVkPhysicalDeviceProperties(Arena *arena, VkPhysicalDeviceProperties props) +{ + String device_type_str = ( + props.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ? Lit("VK_PHYSICAL_DEVICE_TYPE_OTHER") : + props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU ? Lit("VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU") : + props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU ? Lit("VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU") : + props.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU ? Lit("VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU") : + props.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU ? Lit("VK_PHYSICAL_DEVICE_TYPE_CPU") : + Lit("Unknown") + ); + + String result = StringF( + arena, + "apiVersion: %F, " + "driverVersion: %F, " + "vendorID: %F, " + "deviceID: %F, " + "deviceType: %F, " + "deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]: %F, " + "pipelineCacheUUID[VK_UUID_SIZE]: %F, " + "limits: %F, " + "sparseProperties: %F", + FmtUint(props.apiVersion), + FmtUint(props.driverVersion), + FmtUint(props.vendorID), + FmtUint(props.deviceID), + FmtString(device_type_str), + FmtString(StringFromCstrNoLimit(props.deviceName)), + FmtString(Lit("...")), + FmtString(Lit("...")), + FmtString(Lit("...")) + ); + return result; +} + +String G_VK_StringFromVkPhysicalDeviceFeatures(Arena *arena, VkPhysicalDeviceFeatures features) +{ + String result = StringF( + arena, + "robustBufferAccess: %F, " + "fullDrawIndexUint32: %F, " + "imageCubeArray: %F, " + "independentBlend: %F, " + "geometryShader: %F, " + "tessellationShader: %F, " + "sampleRateShading: %F, " + "dualSrcBlend: %F, " + "logicOp: %F, " + "multiDrawIndirect: %F, " + "drawIndirectFirstInstance: %F, " + "depthClamp: %F, " + "depthBiasClamp: %F, " + "fillModeNonSolid: %F, " + "depthBounds: %F, " + "wideLines: %F, " + "largePoints: %F, " + "alphaToOne: %F, " + "multiViewport: %F, " + "samplerAnisotropy: %F, " + "textureCompressionETC2: %F, " + "textureCompressionASTC_LDR: %F, " + "textureCompressionBC: %F, " + "occlusionQueryPrecise: %F, " + "pipelineStatisticsQuery: %F, " + "vertexPipelineStoresAndAtomics: %F, " + "fragmentStoresAndAtomics: %F, " + "shaderTessellationAndGeometryPointSize: %F, " + "shaderImageGatherExtended: %F, " + "shaderStorageImageExtendedFormats: %F, " + "shaderStorageImageMultisample: %F, " + "shaderStorageImageReadWithoutFormat: %F, " + "shaderStorageImageWriteWithoutFormat: %F, " + "shaderUniformBufferArrayDynamicIndexing: %F, " + "shaderSampledImageArrayDynamicIndexing: %F, " + "shaderStorageBufferArrayDynamicIndexing: %F, " + "shaderStorageImageArrayDynamicIndexing: %F, " + "shaderClipDistance: %F, " + "shaderCullDistance: %F, " + "shaderFloat64: %F, " + "shaderInt64: %F, " + "shaderInt16: %F, " + "shaderResourceResidency: %F, " + "shaderResourceMinLod: %F, " + "sparseBinding: %F, " + "sparseResidencyBuffer: %F, " + "sparseResidencyImage2D: %F, " + "sparseResidencyImage3D: %F, " + "sparseResidency2Samples: %F, " + "sparseResidency4Samples: %F, " + "sparseResidency8Samples: %F, " + "sparseResidency16Samples: %F, " + "sparseResidencyAliased: %F, " + "variableMultisampleRate: %F, " + "inheritedQueries: %F", + FmtUint(features.robustBufferAccess), + FmtUint(features.fullDrawIndexUint32), + FmtUint(features.imageCubeArray), + FmtUint(features.independentBlend), + FmtUint(features.geometryShader), + FmtUint(features.tessellationShader), + FmtUint(features.sampleRateShading), + FmtUint(features.dualSrcBlend), + FmtUint(features.logicOp), + FmtUint(features.multiDrawIndirect), + FmtUint(features.drawIndirectFirstInstance), + FmtUint(features.depthClamp), + FmtUint(features.depthBiasClamp), + FmtUint(features.fillModeNonSolid), + FmtUint(features.depthBounds), + FmtUint(features.wideLines), + FmtUint(features.largePoints), + FmtUint(features.alphaToOne), + FmtUint(features.multiViewport), + FmtUint(features.samplerAnisotropy), + FmtUint(features.textureCompressionETC2), + FmtUint(features.textureCompressionASTC_LDR), + FmtUint(features.textureCompressionBC), + FmtUint(features.occlusionQueryPrecise), + FmtUint(features.pipelineStatisticsQuery), + FmtUint(features.vertexPipelineStoresAndAtomics), + FmtUint(features.fragmentStoresAndAtomics), + FmtUint(features.shaderTessellationAndGeometryPointSize), + FmtUint(features.shaderImageGatherExtended), + FmtUint(features.shaderStorageImageExtendedFormats), + FmtUint(features.shaderStorageImageMultisample), + FmtUint(features.shaderStorageImageReadWithoutFormat), + FmtUint(features.shaderStorageImageWriteWithoutFormat), + FmtUint(features.shaderUniformBufferArrayDynamicIndexing), + FmtUint(features.shaderSampledImageArrayDynamicIndexing), + FmtUint(features.shaderStorageBufferArrayDynamicIndexing), + FmtUint(features.shaderStorageImageArrayDynamicIndexing), + FmtUint(features.shaderClipDistance), + FmtUint(features.shaderCullDistance), + FmtUint(features.shaderFloat64), + FmtUint(features.shaderInt64), + FmtUint(features.shaderInt16), + FmtUint(features.shaderResourceResidency), + FmtUint(features.shaderResourceMinLod), + FmtUint(features.sparseBinding), + FmtUint(features.sparseResidencyBuffer), + FmtUint(features.sparseResidencyImage2D), + FmtUint(features.sparseResidencyImage3D), + FmtUint(features.sparseResidency2Samples), + FmtUint(features.sparseResidency4Samples), + FmtUint(features.sparseResidency8Samples), + FmtUint(features.sparseResidency16Samples), + FmtUint(features.sparseResidencyAliased), + FmtUint(features.variableMultisampleRate), + FmtUint(features.inheritedQueries) + ); + return result; +} + +VkResult G_VK_ForceSuccess(VkResult result, String func_name) +{ + if (result != VK_SUCCESS) + { + Arena *perm = PermArena(); + String result_str = G_VK_StringFromVkResult(result); + String msg = Zi; + if (!G_VK.initialized) + { + msg = StringF(perm, "Failed to initialize Vulkan - %F returned '%F'", FmtString(func_name), FmtString(result_str)); + } + else + { + msg = StringF(perm, "Vulkan error - Call to %F failed with '%F'", FmtString(func_name), FmtString(result_str)); + } + Panic(msg); + } + return result; +} + +//////////////////////////////////////////////////////////// +//~ Debug + +VkBool32 G_VK_DebugCallback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, + void* pUserData +) +{ + DEBUGBREAKABLE; + Panic(Lit("Testing")); + return 0; } //////////////////////////////////////////////////////////// diff --git a/src/gpu/gpu_vk/gpu_vk_core.h b/src/gpu/gpu_vk/gpu_vk_core.h index 01702e05..1a29eaf8 100644 --- a/src/gpu/gpu_vk/gpu_vk_core.h +++ b/src/gpu/gpu_vk/gpu_vk_core.h @@ -1,9 +1,43 @@ +//////////////////////////////////////////////////////////// +//~ Vulkan API types + +#define VK_NO_PROTOTYPES +#include "../../../vulkan/include/vulkan.h" + +#define G_VK_ProcXList(X) \ + X(vkEnumerateInstanceVersion, G_VK_ProcFlag_Global) \ + X(vkEnumerateInstanceExtensionProperties, G_VK_ProcFlag_Global) \ + X(vkEnumerateInstanceLayerProperties, G_VK_ProcFlag_Global) \ + X(vkCreateInstance, G_VK_ProcFlag_Global) \ + X(vkEnumeratePhysicalDevices, G_VK_ProcFlag_None) \ + X(vkCreateDevice, G_VK_ProcFlag_None) \ + X(vkCreateDebugReportCallbackEXT, G_VK_ProcFlag_NoForce) \ + X(vkGetPhysicalDeviceProperties, G_VK_ProcFlag_None) \ + X(vkGetPhysicalDeviceFeatures, G_VK_ProcFlag_None) \ +/* ----------------------------------------------------------------------------------- */ + +Enum(G_VK_ProcFlag) +{ + G_VK_ProcFlag_None = 0, + G_VK_ProcFlag_Global = (1 << 0), + G_VK_ProcFlag_NoForce = (1 << 1), +}; + +PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; +#define X(name, ...) extern CAT(PFN_, name) name; + G_VK_ProcXList(X) +#undef X + //////////////////////////////////////////////////////////// //~ State types Struct(G_VK_Ctx) { - i32 _; + HMODULE lib; + b32 initialized; + + VkInstance instance; + VkDevice device; }; Struct(G_VK_ThreadLocalCtx) @@ -13,3 +47,27 @@ Struct(G_VK_ThreadLocalCtx) extern G_VK_Ctx G_Vk; extern ThreadLocal G_VK_ThreadLocalCtx G_VK_tl; + +//////////////////////////////////////////////////////////// +//~ Helpers + +String G_VK_StringFromVkResult(VkResult v); +String G_VK_StringFromVkPhysicalDeviceProperties(Arena *arena, VkPhysicalDeviceProperties props); +String G_VK_StringFromVkPhysicalDeviceFeatures(Arena *arena, VkPhysicalDeviceFeatures features); +VkResult G_VK_ForceSuccess(VkResult result, String func_name); + +#define G_VK_ForceCall(func, ...) G_VK_ForceSuccess(func(__VA_ARGS__), Lit(#func)) + +//////////////////////////////////////////////////////////// +//~ Debug + +VkBool32 G_VK_DebugCallback( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, + void* pUserData +); diff --git a/src/meta/meta.c b/src/meta/meta.c index 87e23221..e8fa9660 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -484,6 +484,8 @@ void M_BuildEntryPoint(WaveLaneCtx *lane) PushStringToList(perm, &cp.warnings_msvc, Lit("-wd4702")); // unreachable code PushStringToList(perm, &cp.warnings_msvc, Lit("-wd4305")); // 'initializing': truncation from 'double' to 'f32' + // PushStringToList(perm, &cp.warnings_msvc, Lit("-wd4152")); // nonstandard extension, function/data pointer conversion in expression + // PushStringToList(perm, &cp.warnings_msvc, Lit("-wd4127")); // conditional expression is constant // PushStringToList(perm, &cp.warnings_msvc, Lit("-wd4820")); // bytes padding added after data member // PushStringToList(perm, &cp.warnings_msvc, Lit("-wd4464")); // relative include path contains '..'