diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 366ee81520..60a5cfdb29 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -435,6 +435,13 @@ target_sources(rpcs3_emu PRIVATE if(TARGET 3rdparty_vulkan) target_sources(rpcs3_emu PRIVATE + RSX/VK/helpers/chip_class.cpp + RSX/VK/helpers/fence.cpp + RSX/VK/helpers/mem_allocator.cpp + RSX/VK/helpers/memory_block.cpp + RSX/VK/helpers/physical_device.cpp + RSX/VK/helpers/sampler.cpp + RSX/VK/helpers/shared.cpp RSX/VK/VKCommandStream.cpp RSX/VK/VKCommonDecompiler.cpp RSX/VK/VKDMA.cpp diff --git a/rpcs3/Emu/RSX/RSXOffload.h b/rpcs3/Emu/RSX/RSXOffload.h index de233cecf4..54ec115eb6 100644 --- a/rpcs3/Emu/RSX/RSXOffload.h +++ b/rpcs3/Emu/RSX/RSXOffload.h @@ -2,6 +2,7 @@ #include "util/types.hpp" #include "Utilities/address_range.h" +#include "gcm_enums.h" #include diff --git a/rpcs3/Emu/RSX/VK/VKCommandStream.cpp b/rpcs3/Emu/RSX/VK/VKCommandStream.cpp index bb053e222a..231f7371f9 100644 --- a/rpcs3/Emu/RSX/VK/VKCommandStream.cpp +++ b/rpcs3/Emu/RSX/VK/VKCommandStream.cpp @@ -1,6 +1,8 @@ #include "stdafx.h" #include "VKCommandStream.h" +#include "helpers/fence.h" #include "Emu/IdManager.h" +#include "Emu/system_config.h" #include "Emu/RSX/RSXOffload.h" namespace vk diff --git a/rpcs3/Emu/RSX/VK/VKCommandStream.h b/rpcs3/Emu/RSX/VK/VKCommandStream.h index 68918f0628..3d6098bf6e 100644 --- a/rpcs3/Emu/RSX/VK/VKCommandStream.h +++ b/rpcs3/Emu/RSX/VK/VKCommandStream.h @@ -1,9 +1,16 @@ #pragma once -#include "VKHelpers.h" +#include "VulkanAPI.h" namespace vk { + struct fence; + + enum // callback commands + { + rctrl_queue_submit = 0x80000000 + }; + struct submit_packet { // Core components diff --git a/rpcs3/Emu/RSX/VK/VKDMA.h b/rpcs3/Emu/RSX/VK/VKDMA.h index 14ec5bbe36..5a40d19dee 100644 --- a/rpcs3/Emu/RSX/VK/VKDMA.h +++ b/rpcs3/Emu/RSX/VK/VKDMA.h @@ -1,8 +1,16 @@ #pragma once -#include "VKHelpers.h" + +namespace utils +{ + class address_range; +} namespace vk { + struct buffer; + class command_buffer; + class render_device; + std::pair map_dma(command_buffer& cmd, u32 local_address, u32 length); void load_dma(u32 local_address, u32 length); void flush_dma(u32 local_address, u32 length); diff --git a/rpcs3/Emu/RSX/VK/VKFormats.cpp b/rpcs3/Emu/RSX/VK/VKFormats.cpp index aff474f62e..b819161b4e 100644 --- a/rpcs3/Emu/RSX/VK/VKFormats.cpp +++ b/rpcs3/Emu/RSX/VK/VKFormats.cpp @@ -1,47 +1,9 @@ #include "stdafx.h" #include "VKFormats.h" +#include "VKHelpers.h" namespace vk { - gpu_formats_support get_optimal_tiling_supported_formats(const physical_device& dev) - { - gpu_formats_support result = {}; - - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_D24_UNORM_S8_UINT, &props); - - result.d24_unorm_s8 = !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) - && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) - && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) - && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); - - vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_D32_SFLOAT_S8_UINT, &props); - result.d32_sfloat_s8 = !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) - && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) - && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); - - // Hide d24_s8 if force high precision z buffer is enabled - if (g_cfg.video.force_high_precision_z_buffer && result.d32_sfloat_s8) - result.d24_unorm_s8 = false; - - // Checks if BGRA8 images can be used for blitting - vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_B8G8R8A8_UNORM, &props); - result.bgra8_linear = !!(props.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); - - // Check if device supports RGBA8 format - vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_R8G8B8A8_UNORM, &props); - if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) || - !(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) || - !(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) - { - // Non-fatal. Most games use BGRA layout due to legacy reasons as old GPUs typically supported BGRA and RGBA was emulated. - rsx_log.error("Your GPU and/or driver does not support RGBA8 format. This can cause problems in some rare games that use this memory layout."); - } - - result.argb8_linear = !!(props.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); - return result; - } - VkFormat get_compatible_depth_surface_format(const gpu_formats_support &support, rsx::surface_depth_format2 format) { switch (format) diff --git a/rpcs3/Emu/RSX/VK/VKFormats.h b/rpcs3/Emu/RSX/VK/VKFormats.h index f5f81d9553..5bf21971a6 100644 --- a/rpcs3/Emu/RSX/VK/VKFormats.h +++ b/rpcs3/Emu/RSX/VK/VKFormats.h @@ -1,9 +1,13 @@ #pragma once -#include "VKHelpers.h" +#include "VulkanAPI.h" +#include "../gcm_enums.h" #include namespace vk { + class image; + struct gpu_formats_support; + struct minification_filter { VkFilter filter; diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.h b/rpcs3/Emu/RSX/VK/VKFragmentProgram.h index 881034ba8f..3ac31a8607 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.h +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.h @@ -3,7 +3,8 @@ #include "../Common/GLSLTypes.h" #include "Emu/RSX/RSXFragmentProgram.h" #include "VulkanAPI.h" -#include "VKHelpers.h" +#include "VKProgramPipeline.h" +#include "helpers/pipeline_binding_table.h" namespace vk { diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index e68157318a..cb2a719985 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -17,55 +17,7 @@ namespace vk { - static chip_family_table s_AMD_family_tree = []() - { - chip_family_table table; - table.default_ = chip_class::AMD_gcn_generic; - - // AMD cards. See https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c - table.add(0x67C0, 0x67FF, chip_class::AMD_polaris); - table.add(0x6FDF, chip_class::AMD_polaris); // RX580 2048SP - table.add(0x6980, 0x699F, chip_class::AMD_polaris); // Polaris12 - table.add(0x694C, 0x694F, chip_class::AMD_vega); // VegaM - table.add(0x6860, 0x686F, chip_class::AMD_vega); // VegaPro - table.add(0x687F, chip_class::AMD_vega); // Vega56/64 - table.add(0x69A0, 0x69AF, chip_class::AMD_vega); // Vega12 - table.add(0x66A0, 0x66AF, chip_class::AMD_vega); // Vega20 - table.add(0x15DD, chip_class::AMD_vega); // Raven Ridge - table.add(0x15D8, chip_class::AMD_vega); // Raven Ridge - table.add(0x7310, 0x731F, chip_class::AMD_navi1x); // Navi10 - table.add(0x7340, 0x734F, chip_class::AMD_navi1x); // Navi14 - table.add(0x73A0, 0x73BF, chip_class::AMD_navi2x); // Sienna cichlid - - return table; - }(); - - static chip_family_table s_NV_family_tree = []() - { - chip_family_table table; - table.default_ = chip_class::NV_generic; - - // NV cards. See https://envytools.readthedocs.io/en/latest/hw/pciid.html - // NOTE: Since NV device IDs are linearly incremented per generation, there is no need to carefully check all the ranges - table.add(0x1180, 0x11fa, chip_class::NV_kepler); // GK104, 106 - table.add(0x0FC0, 0x0FFF, chip_class::NV_kepler); // GK107 - table.add(0x1003, 0x1028, chip_class::NV_kepler); // GK110 - table.add(0x1280, 0x12BA, chip_class::NV_kepler); // GK208 - table.add(0x1381, 0x13B0, chip_class::NV_maxwell); // GM107 - table.add(0x1340, 0x134D, chip_class::NV_maxwell); // GM108 - table.add(0x13C0, 0x13D9, chip_class::NV_maxwell); // GM204 - table.add(0x1401, 0x1427, chip_class::NV_maxwell); // GM206 - table.add(0x15F7, 0x15F9, chip_class::NV_pascal); // GP100 (Tesla P100) - table.add(0x1B00, 0x1D80, chip_class::NV_pascal); - table.add(0x1D81, 0x1DBA, chip_class::NV_volta); - table.add(0x1E02, 0x1F54, chip_class::NV_turing); // TU102, TU104, TU106, TU106M, TU106GL (RTX 20 series) - table.add(0x1F82, 0x1FB9, chip_class::NV_turing); // TU117, TU117M, TU117GL - table.add(0x2182, 0x21D1, chip_class::NV_turing); // TU116, TU116M, TU116GL - table.add(0x20B0, 0x20BE, chip_class::NV_ampere); // GA100 - table.add(0x2204, 0x25AF, chip_class::NV_ampere); // GA10x (RTX 30 series) - - return table; - }(); + extern chip_class g_chip_class; const context* g_current_vulkan_ctx = nullptr; const render_device* g_current_renderer; @@ -87,7 +39,6 @@ namespace vk // Driver compatibility workarounds VkFlags g_heap_compatible_buffer_types = 0; driver_vendor g_driver_vendor = driver_vendor::unknown; - chip_class g_chip_class = chip_class::unknown; bool g_drv_no_primitive_restart = false; bool g_drv_sanitize_fp_values = false; bool g_drv_disable_fence_reset = false; @@ -179,81 +130,6 @@ namespace vk return true; } - memory_type_mapping get_memory_mapping(const vk::physical_device& dev) - { - VkPhysicalDevice pdev = dev; - VkPhysicalDeviceMemoryProperties memory_properties; - vkGetPhysicalDeviceMemoryProperties(pdev, &memory_properties); - - memory_type_mapping result; - result.device_local = VK_MAX_MEMORY_TYPES; - result.host_visible_coherent = VK_MAX_MEMORY_TYPES; - - bool host_visible_cached = false; - VkDeviceSize host_visible_vram_size = 0; - VkDeviceSize device_local_vram_size = 0; - - for (u32 i = 0; i < memory_properties.memoryTypeCount; i++) - { - VkMemoryHeap &heap = memory_properties.memoryHeaps[memory_properties.memoryTypes[i].heapIndex]; - - bool is_device_local = !!(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - if (is_device_local) - { - if (device_local_vram_size < heap.size) - { - result.device_local = i; - device_local_vram_size = heap.size; - } - } - - bool is_host_visible = !!(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - bool is_host_coherent = !!(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - bool is_cached = !!(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT); - - if (is_host_coherent && is_host_visible) - { - if ((is_cached && !host_visible_cached) || - (host_visible_vram_size < heap.size)) - { - result.host_visible_coherent = i; - host_visible_vram_size = heap.size; - host_visible_cached = is_cached; - } - } - } - - if (result.device_local == VK_MAX_MEMORY_TYPES) fmt::throw_exception("GPU doesn't support device local memory"); - if (result.host_visible_coherent == VK_MAX_MEMORY_TYPES) fmt::throw_exception("GPU doesn't support host coherent device local memory"); - return result; - } - - pipeline_binding_table get_pipeline_binding_table(const vk::physical_device& dev) - { - pipeline_binding_table result{}; - - // Need to check how many samplers are supported by the driver - const auto usable_samplers = std::min(dev.get_limits().maxPerStageDescriptorSampledImages, 32u); - result.vertex_textures_first_bind_slot = result.textures_first_bind_slot + usable_samplers; - result.total_descriptor_bindings = result.vertex_textures_first_bind_slot + 4; - return result; - } - - chip_class get_chip_family(u32 vendor_id, u32 device_id) - { - if (vendor_id == 0x10DE) - { - return s_NV_family_tree.find(device_id); - } - - if (vendor_id == 0x1002) - { - return s_AMD_family_tree.find(device_id); - } - - return chip_class::unknown; - } - VkAllocationCallbacks default_callbacks() { VkAllocationCallbacks callbacks; @@ -469,12 +345,6 @@ namespace vk g_overlay_passes.clear(); } - vk::mem_allocator_base* get_current_mem_allocator() - { - ensure(g_current_renderer); - return g_current_renderer->get_allocator(); - } - void set_current_thread_ctx(const vk::context &ctx) { g_current_vulkan_ctx = &ctx; @@ -577,11 +447,6 @@ namespace vk return g_driver_vendor; } - chip_class get_chip_family() - { - return g_chip_class; - } - bool emulate_primitive_restart(rsx::primitive_type type) { if (g_drv_no_primitive_restart) @@ -1023,134 +888,6 @@ namespace vk renderer->emergency_query_cleanup(&cmd); } - void die_with_error(VkResult error_code, - const char* file, - const char* func, - u32 line, - u32 col) - { - std::string error_message; - int severity = 0; //0 - die, 1 - warn, 2 - nothing - - switch (error_code) - { - case VK_SUCCESS: - case VK_EVENT_SET: - case VK_EVENT_RESET: - case VK_INCOMPLETE: - return; - case VK_SUBOPTIMAL_KHR: - error_message = "Present surface is suboptimal (VK_SUBOPTIMAL_KHR)"; - severity = 1; - break; - case VK_NOT_READY: - error_message = "Device or resource busy (VK_NOT_READY)"; - break; - case VK_TIMEOUT: - error_message = "Timeout event (VK_TIMEOUT)"; - break; - case VK_ERROR_OUT_OF_HOST_MEMORY: - error_message = "Out of host memory (system RAM) (VK_ERROR_OUT_OF_HOST_MEMORY)"; - break; - case VK_ERROR_OUT_OF_DEVICE_MEMORY: - error_message = "Out of video memory (VRAM) (VK_ERROR_OUT_OF_DEVICE_MEMORY)"; - break; - case VK_ERROR_INITIALIZATION_FAILED: - error_message = "Initialization failed (VK_ERROR_INITIALIZATION_FAILED)"; - break; - case VK_ERROR_DEVICE_LOST: - error_message = "Device lost (Driver crashed with unspecified error or stopped responding and recovered) (VK_ERROR_DEVICE_LOST)"; - break; - case VK_ERROR_MEMORY_MAP_FAILED: - error_message = "Memory map failed (VK_ERROR_MEMORY_MAP_FAILED)"; - break; - case VK_ERROR_LAYER_NOT_PRESENT: - error_message = "Requested layer is not available (Try disabling debug output or install vulkan SDK) (VK_ERROR_LAYER_NOT_PRESENT)"; - break; - case VK_ERROR_EXTENSION_NOT_PRESENT: - error_message = "Requested extension not available (VK_ERROR_EXTENSION_NOT_PRESENT)"; - break; - case VK_ERROR_FEATURE_NOT_PRESENT: - error_message = "Requested feature not available (VK_ERROR_FEATURE_NOT_PRESENT)"; - break; - case VK_ERROR_INCOMPATIBLE_DRIVER: - error_message = "Incompatible driver (VK_ERROR_INCOMPATIBLE_DRIVER)"; - break; - case VK_ERROR_TOO_MANY_OBJECTS: - error_message = "Too many objects created (Out of handles) (VK_ERROR_TOO_MANY_OBJECTS)"; - break; - case VK_ERROR_FORMAT_NOT_SUPPORTED: - error_message = "Format not supported (VK_ERROR_FORMAT_NOT_SUPPORTED)"; - break; - case VK_ERROR_FRAGMENTED_POOL: - error_message = "Fragmented pool (VK_ERROR_FRAGMENTED_POOL)"; - break; - case VK_ERROR_SURFACE_LOST_KHR: - error_message = "Surface lost (VK_ERROR_SURFACE_LOST)"; - break; - case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: - error_message = "Native window in use (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)"; - break; - case VK_ERROR_OUT_OF_DATE_KHR: - error_message = "Present surface is out of date (VK_ERROR_OUT_OF_DATE_KHR)"; - severity = 1; - break; - case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: - error_message = "Incompatible display (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)"; - break; - case VK_ERROR_VALIDATION_FAILED_EXT: - error_message = "Validation failed (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)"; - break; - case VK_ERROR_INVALID_SHADER_NV: - error_message = "Invalid shader code (VK_ERROR_INVALID_SHADER_NV)"; - break; - case VK_ERROR_OUT_OF_POOL_MEMORY_KHR: - error_message = "Out of pool memory (VK_ERROR_OUT_OF_POOL_MEMORY_KHR)"; - break; - case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR: - error_message = "Invalid external handle (VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR)"; - break; - default: - error_message = fmt::format("Unknown Code (%Xh, %d)%s", static_cast(error_code), static_cast(error_code), src_loc{line, col, file, func}); - break; - } - - switch (severity) - { - default: - case 0: - fmt::throw_exception("Assertion Failed! Vulkan API call failed with unrecoverable error: %s%s", error_message, src_loc{line, col, file, func}); - case 1: - rsx_log.error("Vulkan API call has failed with an error but will continue: %s%s", error_message, src_loc{line, col, file, func}); - break; - case 2: - break; - } - } - - VKAPI_ATTR VkBool32 VKAPI_CALL dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, - u64 srcObject, usz location, s32 msgCode, - const char *pLayerPrefix, const char *pMsg, void *pUserData) - { - if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) - { - if (strstr(pMsg, "IMAGE_VIEW_TYPE_1D")) return false; - - rsx_log.error("ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, pMsg); - } - else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) - { - rsx_log.warning("WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode, pMsg); - } - else - { - return false; - } - - //Let the app crash.. - return false; - } - VkBool32 BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, u64 srcObject, usz location, s32 msgCode, const char *pLayerPrefix, const char *pMsg, void *pUserData) diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index 84adf3bb13..00ca2e4927 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -21,12 +21,14 @@ #include "../Common/TextureUtils.h" #include "../display.h" #include "../rsx_utils.h" - -#include "3rdparty/GPUOpen/include/vk_mem_alloc.h" - -#ifdef _MSC_VER -extern "C" void _mm_pause(); -#endif +#include "helpers/chip_class.h" +#include "helpers/fence.h" +#include "helpers/mem_allocator.h" +#include "helpers/memory_block.h" +#include "helpers/physical_device.h" +#include "helpers/pipeline_binding_table.h" +#include "helpers/shared.h" +#include "helpers/supported_extensions.h" #ifdef __APPLE__ #define VK_DISABLE_COMPONENT_SWIZZLE 1 @@ -50,16 +52,10 @@ namespace rsx namespace vk { -#define CHECK_RESULT(expr) { VkResult _res = (expr); if (_res != VK_SUCCESS) vk::die_with_error(_res); } - VKAPI_ATTR void *VKAPI_CALL mem_realloc(void *pUserData, void *pOriginal, usz size, usz alignment, VkSystemAllocationScope allocationScope); VKAPI_ATTR void *VKAPI_CALL mem_alloc(void *pUserData, usz size, usz alignment, VkSystemAllocationScope allocationScope); VKAPI_ATTR void VKAPI_CALL mem_free(void *pUserData, void *pMemory); - VKAPI_ATTR VkBool32 VKAPI_CALL dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, - u64 srcObject, usz location, s32 msgCode, - const char *pLayerPrefix, const char *pMsg, void *pUserData); - VkBool32 BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, u64 srcObject, usz location, s32 msgCode, const char *pLayerPrefix, const char *pMsg, @@ -73,58 +69,20 @@ namespace vk heap_changed = 3 }; - enum class driver_vendor - { - unknown, - AMD, - NVIDIA, - RADV, - INTEL - }; - - // Chip classes grouped by vendor in order of release - enum class chip_class - { - unknown, - AMD_gcn_generic, - AMD_polaris, - AMD_vega, - AMD_navi1x, - AMD_navi2x, - NV_generic, - NV_kepler, - NV_maxwell, - NV_pascal, - NV_volta, - NV_turing, - NV_ampere - }; - enum : u32// special remap_encoding enums { VK_REMAP_IDENTITY = 0xCAFEBABE, // Special view encoding to return an identity image view VK_REMAP_VIEW_MULTISAMPLED = 0xDEADBEEF // Special encoding for multisampled images; returns a multisampled image view }; - enum // callback commands - { - rctrl_queue_submit = 0x80000000 - }; - class context; class render_device; class swap_chain_image; - class physical_device; class command_buffer; class image; struct image_view; struct buffer; class data_heap; - class mem_allocator_base; - struct memory_type_mapping; - struct gpu_formats_support; - struct fence; - struct pipeline_binding_table; class event; const vk::context *get_current_thread_ctx(); @@ -133,8 +91,6 @@ namespace vk const vk::render_device *get_current_renderer(); void set_current_renderer(const vk::render_device &device); - mem_allocator_base *get_current_mem_allocator(); - //Compatibility workarounds bool emulate_primitive_restart(rsx::primitive_type type); bool sanitize_fp_values(); @@ -142,8 +98,6 @@ namespace vk bool emulate_conditional_rendering(); VkFlags get_heap_compatible_buffer_types(); driver_vendor get_driver_vendor(); - chip_class get_chip_family(u32 vendor_id, u32 device_id); - chip_class get_chip_family(); VkComponentMapping default_component_map(); VkComponentMapping apply_swizzle_remap(const std::array& base_remap, const std::pair, std::array>& remap_vector); @@ -157,10 +111,6 @@ namespace vk buffer* get_scratch_buffer(u32 min_required_size = 0); data_heap* get_upload_heap(); - memory_type_mapping get_memory_mapping(const physical_device& dev); - gpu_formats_support get_optimal_tiling_supported_formats(const physical_device& dev); - pipeline_binding_table get_pipeline_binding_table(const physical_device& dev); - // Sync helpers around vkQueueSubmit void acquire_global_submit_lock(); void release_global_submit_lock(); @@ -176,12 +126,6 @@ namespace vk void destroy_global_resources(); void reset_global_resources(); - void vmm_notify_memory_allocated(void* handle, u32 memory_type, u64 memory_size); - void vmm_notify_memory_freed(void* handle); - void vmm_reset(); - void vmm_check_memory_usage(); - bool vmm_handle_memory_pressure(rsx::problem_severity severity); - /** * Allocate enough space in upload_buffer and write all mipmap/layer data into the subbuffer. * Then copy all layers into dst_image. @@ -248,636 +192,6 @@ namespace vk // TODO: Move queries out of the renderer! void do_query_cleanup(vk::command_buffer& cmd); - void die_with_error(VkResult error_code, - const char* file = __builtin_FILE(), - const char* func = __builtin_FUNCTION(), - u32 line = __builtin_LINE(), - u32 col = __builtin_COLUMN()); - - struct pipeline_binding_table - { - u8 vertex_params_bind_slot = 0; - u8 vertex_constant_buffers_bind_slot = 1; - u8 fragment_constant_buffers_bind_slot = 2; - u8 fragment_state_bind_slot = 3; - u8 fragment_texture_params_bind_slot = 4; - u8 vertex_buffers_first_bind_slot = 5; - u8 conditional_render_predicate_slot = 8; - u8 rasterizer_env_bind_slot = 9; - u8 textures_first_bind_slot = 10; - u8 vertex_textures_first_bind_slot = 10; // Invalid, has to be initialized properly - u8 total_descriptor_bindings = vertex_textures_first_bind_slot; // Invalid, has to be initialized properly - }; - - struct memory_type_mapping - { - u32 host_visible_coherent; - u32 device_local; - }; - - struct gpu_formats_support - { - bool d24_unorm_s8; - bool d32_sfloat_s8; - bool bgra8_linear; - bool argb8_linear; - }; - - struct gpu_shader_types_support - { - bool allow_float64; - bool allow_float16; - bool allow_int8; - }; - - struct chip_family_table - { - chip_class default_ = chip_class::unknown; - std::unordered_map lut; - - void add(u32 first, u32 last, chip_class family) - { - for (auto i = first; i <= last; ++i) - { - lut[i] = family; - } - } - - void add(u32 id, chip_class family) - { - lut[id] = family; - } - - chip_class find(u32 device_id) - { - if (auto found = lut.find(device_id); found != lut.end()) - { - return found->second; - } - - rsx_log.warning("Unknown chip with device ID 0x%x", device_id); - return default_; - } - }; - - // Memory Allocator - base class - - class mem_allocator_base - { - public: - using mem_handle_t = void *; - - mem_allocator_base(VkDevice dev, VkPhysicalDevice /*pdev*/) : m_device(dev) {} - virtual ~mem_allocator_base() = default; - - virtual void destroy() = 0; - - virtual mem_handle_t alloc(u64 block_sz, u64 alignment, u32 memory_type_index) = 0; - virtual void free(mem_handle_t mem_handle) = 0; - virtual void *map(mem_handle_t mem_handle, u64 offset, u64 size) = 0; - virtual void unmap(mem_handle_t mem_handle) = 0; - virtual VkDeviceMemory get_vk_device_memory(mem_handle_t mem_handle) = 0; - virtual u64 get_vk_device_memory_offset(mem_handle_t mem_handle) = 0; - virtual f32 get_memory_usage() = 0; - - protected: - VkDevice m_device; - private: - }; - - // Memory Allocator - Vulkan Memory Allocator - // https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator - - class mem_allocator_vma : public mem_allocator_base - { - public: - mem_allocator_vma(VkDevice dev, VkPhysicalDevice pdev) : mem_allocator_base(dev, pdev) - { - // Initialize stats pool - std::fill(stats.begin(), stats.end(), VmaBudget{}); - - VmaAllocatorCreateInfo allocatorInfo = {}; - allocatorInfo.physicalDevice = pdev; - allocatorInfo.device = dev; - - vmaCreateAllocator(&allocatorInfo, &m_allocator); - } - - ~mem_allocator_vma() override = default; - - void destroy() override - { - vmaDestroyAllocator(m_allocator); - } - - mem_handle_t alloc(u64 block_sz, u64 alignment, u32 memory_type_index) override - { - VmaAllocation vma_alloc; - VkMemoryRequirements mem_req = {}; - VmaAllocationCreateInfo create_info = {}; - - mem_req.memoryTypeBits = 1u << memory_type_index; - mem_req.size = block_sz; - mem_req.alignment = alignment; - create_info.memoryTypeBits = 1u << memory_type_index; - - if (VkResult result = vmaAllocateMemory(m_allocator, &mem_req, &create_info, &vma_alloc, nullptr); - result != VK_SUCCESS) - { - if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY && - vmm_handle_memory_pressure(rsx::problem_severity::fatal)) - { - // If we just ran out of VRAM, attempt to release resources and try again - result = vmaAllocateMemory(m_allocator, &mem_req, &create_info, &vma_alloc, nullptr); - } - - if (result != VK_SUCCESS) - { - die_with_error(result); - } - else - { - rsx_log.warning("Renderer ran out of video memory but successfully recovered."); - } - } - - vmm_notify_memory_allocated(vma_alloc, memory_type_index, block_sz); - return vma_alloc; - } - - void free(mem_handle_t mem_handle) override - { - vmm_notify_memory_freed(mem_handle); - vmaFreeMemory(m_allocator, static_cast(mem_handle)); - } - - void *map(mem_handle_t mem_handle, u64 offset, u64 /*size*/) override - { - void *data = nullptr; - - CHECK_RESULT(vmaMapMemory(m_allocator, static_cast(mem_handle), &data)); - - // Add offset - data = static_cast(data) + offset; - return data; - } - - void unmap(mem_handle_t mem_handle) override - { - vmaUnmapMemory(m_allocator, static_cast(mem_handle)); - } - - VkDeviceMemory get_vk_device_memory(mem_handle_t mem_handle) override - { - VmaAllocationInfo alloc_info; - - vmaGetAllocationInfo(m_allocator, static_cast(mem_handle), &alloc_info); - return alloc_info.deviceMemory; - } - - u64 get_vk_device_memory_offset(mem_handle_t mem_handle) override - { - VmaAllocationInfo alloc_info; - - vmaGetAllocationInfo(m_allocator, static_cast(mem_handle), &alloc_info); - return alloc_info.offset; - } - - f32 get_memory_usage() override - { - vmaGetBudget(m_allocator, stats.data()); - - float max_usage = 0.f; - for (const auto& info : stats) - { - if (!info.budget) - { - break; - } - - const float this_usage = (info.usage * 100.f) / info.budget; - max_usage = std::max(max_usage, this_usage); - } - - return max_usage; - } - - private: - VmaAllocator m_allocator; - std::array stats; - }; - - // Memory Allocator - built-in Vulkan device memory allocate/free - - class mem_allocator_vk : public mem_allocator_base - { - public: - mem_allocator_vk(VkDevice dev, VkPhysicalDevice pdev) : mem_allocator_base(dev, pdev) {} - ~mem_allocator_vk() override = default; - - void destroy() override {} - - mem_handle_t alloc(u64 block_sz, u64 /*alignment*/, u32 memory_type_index) override - { - VkDeviceMemory memory; - VkMemoryAllocateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - info.allocationSize = block_sz; - info.memoryTypeIndex = memory_type_index; - - if (VkResult result = vkAllocateMemory(m_device, &info, nullptr, &memory); - result != VK_SUCCESS) - { - if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY && - vmm_handle_memory_pressure(rsx::problem_severity::fatal)) - { - // If we just ran out of VRAM, attempt to release resources and try again - result = vkAllocateMemory(m_device, &info, nullptr, &memory); - } - - if (result != VK_SUCCESS) - { - die_with_error(result); - } - else - { - rsx_log.warning("Renderer ran out of video memory but successfully recovered."); - } - } - - vmm_notify_memory_allocated(memory, memory_type_index, block_sz); - return memory; - } - - void free(mem_handle_t mem_handle) override - { - vmm_notify_memory_freed(mem_handle); - vkFreeMemory(m_device, static_cast(mem_handle), nullptr); - } - - void *map(mem_handle_t mem_handle, u64 offset, u64 size) override - { - void *data = nullptr; - CHECK_RESULT(vkMapMemory(m_device, static_cast(mem_handle), offset, std::max(size, 1u), 0, &data)); - return data; - } - - void unmap(mem_handle_t mem_handle) override - { - vkUnmapMemory(m_device, static_cast(mem_handle)); - } - - VkDeviceMemory get_vk_device_memory(mem_handle_t mem_handle) override - { - return static_cast(mem_handle); - } - - u64 get_vk_device_memory_offset(mem_handle_t /*mem_handle*/) override - { - return 0; - } - - f32 get_memory_usage() override - { - return 0.f; - } - - private: - }; - - struct memory_block - { - memory_block(VkDevice dev, u64 block_sz, u64 alignment, u32 memory_type_index) : m_device(dev) - { - m_mem_allocator = get_current_mem_allocator(); - m_mem_handle = m_mem_allocator->alloc(block_sz, alignment, memory_type_index); - } - - ~memory_block() - { - m_mem_allocator->free(m_mem_handle); - } - - VkDeviceMemory get_vk_device_memory() - { - return m_mem_allocator->get_vk_device_memory(m_mem_handle); - } - - u64 get_vk_device_memory_offset() - { - return m_mem_allocator->get_vk_device_memory_offset(m_mem_handle); - } - - void *map(u64 offset, u64 size) - { - return m_mem_allocator->map(m_mem_handle, offset, size); - } - - void unmap() - { - m_mem_allocator->unmap(m_mem_handle); - } - - memory_block(const memory_block&) = delete; - memory_block(memory_block&&) = delete; - - private: - VkDevice m_device; - vk::mem_allocator_base* m_mem_allocator; - mem_allocator_base::mem_handle_t m_mem_handle; - }; - - class supported_extensions - { - private: - std::vector m_vk_exts; - - public: - enum enumeration_class - { - instance = 0, - device = 1 - }; - - supported_extensions(enumeration_class _class, const char* layer_name = nullptr, VkPhysicalDevice pdev = VK_NULL_HANDLE) - { - u32 count; - if (_class == enumeration_class::instance) - { - if (vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr) != VK_SUCCESS) - return; - } - else - { - ensure(pdev); - if (vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, nullptr) != VK_SUCCESS) - return; - } - - m_vk_exts.resize(count); - if (_class == enumeration_class::instance) - { - vkEnumerateInstanceExtensionProperties(layer_name, &count, m_vk_exts.data()); - } - else - { - vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, m_vk_exts.data()); - } - } - - bool is_supported(const char *ext) - { - return std::any_of(m_vk_exts.cbegin(), m_vk_exts.cend(), - [&](const VkExtensionProperties& p) { return std::strcmp(p.extensionName, ext) == 0; }); - } - }; - - class physical_device - { - VkInstance parent = VK_NULL_HANDLE; - VkPhysicalDevice dev = VK_NULL_HANDLE; - VkPhysicalDeviceProperties props; - VkPhysicalDeviceFeatures features; - VkPhysicalDeviceMemoryProperties memory_properties; - std::vector queue_props; - - std::unordered_map format_properties; - gpu_shader_types_support shader_types_support{}; - VkPhysicalDeviceDriverPropertiesKHR driver_properties{}; - - bool stencil_export_support = false; - bool conditional_render_support = false; - bool unrestricted_depth_range_support = false; - - friend class render_device; -private: - void get_physical_device_features(bool allow_extensions) - { - if (!allow_extensions) - { - vkGetPhysicalDeviceFeatures(dev, &features); - return; - } - - supported_extensions instance_extensions(supported_extensions::instance); - supported_extensions device_extensions(supported_extensions::device, nullptr, dev); - - if (!instance_extensions.is_supported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) - { - vkGetPhysicalDeviceFeatures(dev, &features); - } - else - { - VkPhysicalDeviceFeatures2KHR features2; - features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - features2.pNext = nullptr; - - VkPhysicalDeviceFloat16Int8FeaturesKHR shader_support_info{}; - - if (device_extensions.is_supported(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) - { - shader_support_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR; - features2.pNext = &shader_support_info; - } - - if (device_extensions.is_supported(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) - { - driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; - driver_properties.pNext = features2.pNext; - features2.pNext = &driver_properties; - } - - auto getPhysicalDeviceFeatures2KHR = reinterpret_cast(vkGetInstanceProcAddr(parent, "vkGetPhysicalDeviceFeatures2KHR")); - ensure(getPhysicalDeviceFeatures2KHR); // "vkGetInstanceProcAddress failed to find entry point!" - getPhysicalDeviceFeatures2KHR(dev, &features2); - - shader_types_support.allow_float64 = !!features2.features.shaderFloat64; - shader_types_support.allow_float16 = !!shader_support_info.shaderFloat16; - shader_types_support.allow_int8 = !!shader_support_info.shaderInt8; - features = features2.features; - } - - stencil_export_support = device_extensions.is_supported(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME); - conditional_render_support = device_extensions.is_supported(VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME); - unrestricted_depth_range_support = device_extensions.is_supported(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); - } - - public: - - physical_device() = default; - ~physical_device() = default; - - void create(VkInstance context, VkPhysicalDevice pdev, bool allow_extensions) - { - dev = pdev; - parent = context; - vkGetPhysicalDeviceProperties(pdev, &props); - vkGetPhysicalDeviceMemoryProperties(pdev, &memory_properties); - get_physical_device_features(allow_extensions); - - rsx_log.always("Found vulkan-compatible GPU: '%s' running on driver %s", get_name(), get_driver_version()); - - if (get_driver_vendor() == driver_vendor::RADV && - get_name().find("LLVM 8.0.0") != umax) - { - // Serious driver bug causing black screens - // See https://bugs.freedesktop.org/show_bug.cgi?id=110970 - rsx_log.fatal("RADV drivers have a major driver bug with LLVM 8.0.0 resulting in no visual output. Upgrade to LLVM version 8.0.1 or greater to avoid this issue."); - } - else if (get_driver_vendor() == driver_vendor::NVIDIA) - { -#ifdef _WIN32 - // SPIRV bugs were fixed in 452.28 for windows - const u32 threshold_version = (452u >> 22) | (28 >> 14); -#else - // SPIRV bugs were fixed in 450.56 for linux/BSD - const u32 threshold_version = (450u >> 22) | (56 >> 14); -#endif - const auto current_version = props.driverVersion & ~0x3fffu; // Clear patch and revision fields - if (current_version < threshold_version) - { - rsx_log.error("Your current NVIDIA graphics driver version %s has known issues and is unsupported. Update to the latest NVIDIA driver.", - get_driver_version()); - } - } - - if (get_chip_class() == chip_class::AMD_vega) - { - // Disable fp16 if driver uses LLVM emitter. It does fine with AMD proprietary drivers though. - shader_types_support.allow_float16 = (driver_properties.driverID == VK_DRIVER_ID_AMD_PROPRIETARY_KHR); - } - } - - std::string get_name() const - { - return props.deviceName; - } - - driver_vendor get_driver_vendor() const - { - if (!driver_properties.driverID) - { - const auto gpu_name = get_name(); - if (gpu_name.find("Radeon") != umax) - { - return driver_vendor::AMD; - } - - if (gpu_name.find("NVIDIA") != umax || gpu_name.find("GeForce") != umax || gpu_name.find("Quadro") != umax) - { - return driver_vendor::NVIDIA; - } - - if (gpu_name.find("RADV") != umax) - { - return driver_vendor::RADV; - } - - if (gpu_name.find("Intel") != umax) - { - return driver_vendor::INTEL; - } - - return driver_vendor::unknown; - } - else - { - switch (driver_properties.driverID) - { - case VK_DRIVER_ID_AMD_PROPRIETARY_KHR: - case VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR: - return driver_vendor::AMD; - case VK_DRIVER_ID_MESA_RADV_KHR: - return driver_vendor::RADV; - case VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR: - return driver_vendor::NVIDIA; - case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR: - case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR: - return driver_vendor::INTEL; - default: - // Mobile - return driver_vendor::unknown; - } - } - } - - std::string get_driver_version() const - { - switch (get_driver_vendor()) - { - case driver_vendor::NVIDIA: - { - // 10 + 8 + 8 + 6 - const auto major_version = props.driverVersion >> 22; - const auto minor_version = (props.driverVersion >> 14) & 0xff; - const auto patch = (props.driverVersion >> 6) & 0xff; - const auto revision = (props.driverVersion & 0x3f); - - return fmt::format("%u.%u.%u.%u", major_version, minor_version, patch, revision); - } - default: - { - // 10 + 10 + 12 (standard vulkan encoding created with VK_MAKE_VERSION) - return fmt::format("%u.%u.%u", - (props.driverVersion >> 22), - (props.driverVersion >> 12) & 0x3ff, - (props.driverVersion) & 0x3ff); - } - } - } - - chip_class get_chip_class() const - { - return get_chip_family(props.vendorID, props.deviceID); - } - - u32 get_queue_count() const - { - if (!queue_props.empty()) - return ::size32(queue_props); - - u32 count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(dev, &count, nullptr); - - return count; - } - - VkQueueFamilyProperties get_queue_properties(u32 queue) - { - if (queue_props.empty()) - { - u32 count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(dev, &count, nullptr); - - queue_props.resize(count); - vkGetPhysicalDeviceQueueFamilyProperties(dev, &count, queue_props.data()); - } - - if (queue >= queue_props.size()) fmt::throw_exception("Bad queue index passed to get_queue_properties (%u)", queue); - return queue_props[queue]; - } - - VkPhysicalDeviceMemoryProperties get_memory_properties() const - { - return memory_properties; - } - - VkPhysicalDeviceLimits get_limits() const - { - return props.limits; - } - - operator VkPhysicalDevice() const - { - return dev; - } - - operator VkInstance() const - { - return parent; - } - }; - class render_device { physical_device *pgpu = nullptr; @@ -1197,58 +511,6 @@ private: } }; - struct fence - { - atomic_t flushed = false; - VkFence handle = VK_NULL_HANDLE; - VkDevice owner = VK_NULL_HANDLE; - - fence(VkDevice dev) - { - owner = dev; - VkFenceCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - CHECK_RESULT(vkCreateFence(dev, &info, nullptr, &handle)); - } - - ~fence() - { - if (handle) - { - vkDestroyFence(owner, handle, nullptr); - handle = VK_NULL_HANDLE; - } - } - - void reset() - { - vkResetFences(owner, 1, &handle); - flushed.release(false); - } - - void signal_flushed() - { - flushed.release(true); - } - - void wait_flush() - { - while (!flushed) - { -#ifdef _MSC_VER - _mm_pause(); -#else - __builtin_ia32_pause(); -#endif - } - } - - operator bool() const - { - return (handle != VK_NULL_HANDLE); - } - }; - class command_buffer { private: @@ -1990,64 +1252,6 @@ private: } }; - struct sampler - { - VkSampler value; - VkSamplerCreateInfo info = {}; - - sampler(VkDevice dev, VkSamplerAddressMode clamp_u, VkSamplerAddressMode clamp_v, VkSamplerAddressMode clamp_w, - VkBool32 unnormalized_coordinates, float mipLodBias, float max_anisotropy, float min_lod, float max_lod, - VkFilter min_filter, VkFilter mag_filter, VkSamplerMipmapMode mipmap_mode, VkBorderColor border_color, - VkBool32 depth_compare = false, VkCompareOp depth_compare_mode = VK_COMPARE_OP_NEVER) - : m_device(dev) - { - info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - info.addressModeU = clamp_u; - info.addressModeV = clamp_v; - info.addressModeW = clamp_w; - info.anisotropyEnable = VK_TRUE; - info.compareEnable = depth_compare; - info.unnormalizedCoordinates = unnormalized_coordinates; - info.mipLodBias = mipLodBias; - info.maxAnisotropy = max_anisotropy; - info.maxLod = max_lod; - info.minLod = min_lod; - info.magFilter = mag_filter; - info.minFilter = min_filter; - info.mipmapMode = mipmap_mode; - info.compareOp = depth_compare_mode; - info.borderColor = border_color; - - CHECK_RESULT(vkCreateSampler(m_device, &info, nullptr, &value)); - } - - ~sampler() - { - vkDestroySampler(m_device, value, nullptr); - } - - bool matches(VkSamplerAddressMode clamp_u, VkSamplerAddressMode clamp_v, VkSamplerAddressMode clamp_w, - VkBool32 unnormalized_coordinates, float mipLodBias, float max_anisotropy, float min_lod, float max_lod, - VkFilter min_filter, VkFilter mag_filter, VkSamplerMipmapMode mipmap_mode, VkBorderColor border_color, - VkBool32 depth_compare = false, VkCompareOp depth_compare_mode = VK_COMPARE_OP_NEVER) - { - if (info.magFilter != mag_filter || info.minFilter != min_filter || info.mipmapMode != mipmap_mode || - info.addressModeU != clamp_u || info.addressModeV != clamp_v || info.addressModeW != clamp_w || - info.compareEnable != depth_compare || info.unnormalizedCoordinates != unnormalized_coordinates || - !rsx::fcmp(info.maxLod, max_lod) || !rsx::fcmp(info.mipLodBias, mipLodBias) || !rsx::fcmp(info.minLod, min_lod) || - !rsx::fcmp(info.maxAnisotropy, max_anisotropy) || - info.compareOp != depth_compare_mode || info.borderColor != border_color) - return false; - - return true; - } - - sampler(const sampler&) = delete; - sampler(sampler&&) = delete; - private: - VkDevice m_device; - }; - struct framebuffer { VkFramebuffer value; @@ -3287,399 +2491,6 @@ public: } }; - class query_pool : public rsx::ref_counted - { - VkQueryPool m_query_pool; - VkDevice m_device; - - public: - query_pool(VkDevice dev, VkQueryType type, u32 size) - : m_query_pool(VK_NULL_HANDLE), m_device(dev) - { - VkQueryPoolCreateInfo info{}; - info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; - info.queryType = type; - info.queryCount = size; - vkCreateQueryPool(dev, &info, nullptr, &m_query_pool); - - // Take 'size' references on this object - ref_count.release(static_cast(size)); - } - - ~query_pool() - { - vkDestroyQueryPool(m_device, m_query_pool, nullptr); - } - - operator VkQueryPool() - { - return m_query_pool; - } - }; - - class graphics_pipeline_state - { - public: - VkPipelineInputAssemblyStateCreateInfo ia; - VkPipelineDepthStencilStateCreateInfo ds; - VkPipelineColorBlendAttachmentState att_state[4]; - VkPipelineColorBlendStateCreateInfo cs; - VkPipelineRasterizationStateCreateInfo rs; - VkPipelineMultisampleStateCreateInfo ms; - - struct extra_parameters - { - VkSampleMask msaa_sample_mask; - } - temp_storage; - - graphics_pipeline_state() - { - // NOTE: Vk** structs have padding bytes - memset(this, 0, sizeof(graphics_pipeline_state)); - - ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - cs.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - - rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rs.polygonMode = VK_POLYGON_MODE_FILL; - rs.cullMode = VK_CULL_MODE_NONE; - rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rs.lineWidth = 1.f; - - ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - temp_storage.msaa_sample_mask = 0xFFFFFFFF; - } - - graphics_pipeline_state(const graphics_pipeline_state& other) - { - // NOTE: Vk** structs have padding bytes - memcpy(this, &other, sizeof(graphics_pipeline_state)); - - if (other.cs.pAttachments == other.att_state) - { - // Rebase pointer - cs.pAttachments = att_state; - } - } - - ~graphics_pipeline_state() = default; - - graphics_pipeline_state& operator = (const graphics_pipeline_state& other) - { - if (this != &other) - { - // NOTE: Vk** structs have padding bytes - memcpy(this, &other, sizeof(graphics_pipeline_state)); - - if (other.cs.pAttachments == other.att_state) - { - // Rebase pointer - cs.pAttachments = att_state; - } - } - - return *this; - } - - void set_primitive_type(VkPrimitiveTopology type) - { - ia.topology = type; - } - - void enable_primitive_restart(bool enable = true) - { - ia.primitiveRestartEnable = enable? VK_TRUE : VK_FALSE; - } - - void set_color_mask(int index, bool r, bool g, bool b, bool a) - { - VkColorComponentFlags mask = 0; - if (a) mask |= VK_COLOR_COMPONENT_A_BIT; - if (b) mask |= VK_COLOR_COMPONENT_B_BIT; - if (g) mask |= VK_COLOR_COMPONENT_G_BIT; - if (r) mask |= VK_COLOR_COMPONENT_R_BIT; - - att_state[index].colorWriteMask = mask; - } - - void set_depth_mask(bool enable) - { - ds.depthWriteEnable = enable ? VK_TRUE : VK_FALSE; - } - - void set_stencil_mask(u32 mask) - { - ds.front.writeMask = mask; - ds.back.writeMask = mask; - } - - void set_stencil_mask_separate(int face, u32 mask) - { - if (!face) - ds.front.writeMask = mask; - else - ds.back.writeMask = mask; - } - - void enable_depth_test(VkCompareOp op) - { - ds.depthTestEnable = VK_TRUE; - ds.depthCompareOp = op; - } - - void enable_depth_clamp(bool enable = true) - { - rs.depthClampEnable = enable ? VK_TRUE : VK_FALSE; - } - - void enable_depth_bias(bool enable = true) - { - rs.depthBiasEnable = enable ? VK_TRUE : VK_FALSE; - } - - void enable_depth_bounds_test(bool enable = true) - { - ds.depthBoundsTestEnable = enable? VK_TRUE : VK_FALSE; - } - - void enable_blend(int mrt_index, VkBlendFactor src_factor_rgb, VkBlendFactor src_factor_a, - VkBlendFactor dst_factor_rgb, VkBlendFactor dst_factor_a, - VkBlendOp equation_rgb, VkBlendOp equation_a) - { - att_state[mrt_index].srcColorBlendFactor = src_factor_rgb; - att_state[mrt_index].srcAlphaBlendFactor = src_factor_a; - att_state[mrt_index].dstColorBlendFactor = dst_factor_rgb; - att_state[mrt_index].dstAlphaBlendFactor = dst_factor_a; - att_state[mrt_index].colorBlendOp = equation_rgb; - att_state[mrt_index].alphaBlendOp = equation_a; - att_state[mrt_index].blendEnable = VK_TRUE; - } - - void enable_stencil_test(VkStencilOp fail, VkStencilOp zfail, VkStencilOp pass, - VkCompareOp func, u32 func_mask, u32 ref) - { - ds.front.failOp = fail; - ds.front.passOp = pass; - ds.front.depthFailOp = zfail; - ds.front.compareOp = func; - ds.front.compareMask = func_mask; - ds.front.reference = ref; - ds.back = ds.front; - - ds.stencilTestEnable = VK_TRUE; - } - - void enable_stencil_test_separate(int face, VkStencilOp fail, VkStencilOp zfail, VkStencilOp pass, - VkCompareOp func, u32 func_mask, u32 ref) - { - auto& face_props = (face ? ds.back : ds.front); - face_props.failOp = fail; - face_props.passOp = pass; - face_props.depthFailOp = zfail; - face_props.compareOp = func; - face_props.compareMask = func_mask; - face_props.reference = ref; - - ds.stencilTestEnable = VK_TRUE; - } - - void enable_logic_op(VkLogicOp op) - { - cs.logicOpEnable = VK_TRUE; - cs.logicOp = op; - } - - void enable_cull_face(VkCullModeFlags cull_mode) - { - rs.cullMode = cull_mode; - } - - void set_front_face(VkFrontFace face) - { - rs.frontFace = face; - } - - void set_attachment_count(u32 count) - { - cs.attachmentCount = count; - cs.pAttachments = att_state; - } - - void set_multisample_state(u8 sample_count, u32 sample_mask, bool msaa_enabled, bool alpha_to_coverage, bool alpha_to_one) - { - temp_storage.msaa_sample_mask = sample_mask; - - ms.rasterizationSamples = static_cast(sample_count); - ms.alphaToCoverageEnable = alpha_to_coverage; - ms.alphaToOneEnable = alpha_to_one; - - if (!msaa_enabled) - { - // This register is likely glMinSampleShading but in reverse; probably sets max sample shading rate of 1 - // I (kd-11) suspect its what the control panel setting affects when MSAA is set to disabled - } - } - - void set_multisample_shading_rate(float shading_rate) - { - ms.sampleShadingEnable = VK_TRUE; - ms.minSampleShading = shading_rate; - } - }; - - namespace glsl - { - enum program_input_type : u32 - { - input_type_uniform_buffer = 0, - input_type_texel_buffer = 1, - input_type_texture = 2, - input_type_storage_buffer = 3, - - input_type_max_enum = 4 - }; - - struct bound_sampler - { - VkFormat format; - VkImage image; - VkComponentMapping mapping; - }; - - struct bound_buffer - { - VkFormat format = VK_FORMAT_UNDEFINED; - VkBuffer buffer = nullptr; - u64 offset = 0; - u64 size = 0; - }; - - struct program_input - { - ::glsl::program_domain domain; - program_input_type type; - - bound_buffer as_buffer; - bound_sampler as_sampler; - - u32 location; - std::string name; - }; - - class shader - { - ::glsl::program_domain type = ::glsl::program_domain::glsl_vertex_program; - VkShaderModule m_handle = VK_NULL_HANDLE; - std::string m_source; - std::vector m_compiled; - - public: - shader() = default; - ~shader() = default; - - void create(::glsl::program_domain domain, const std::string& source) - { - type = domain; - m_source = source; - } - - VkShaderModule compile() - { - ensure(m_handle == VK_NULL_HANDLE); - - if (!vk::compile_glsl_to_spv(m_source, type, m_compiled)) - { - std::string shader_type = type == ::glsl::program_domain::glsl_vertex_program ? "vertex" : - type == ::glsl::program_domain::glsl_fragment_program ? "fragment" : "compute"; - - rsx_log.notice("%s", m_source); - fmt::throw_exception("Failed to compile %s shader", shader_type); - } - - VkShaderModuleCreateInfo vs_info; - vs_info.codeSize = m_compiled.size() * sizeof(u32); - vs_info.pNext = nullptr; - vs_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - vs_info.pCode = m_compiled.data(); - vs_info.flags = 0; - - VkDevice dev = *vk::get_current_renderer(); - vkCreateShaderModule(dev, &vs_info, nullptr, &m_handle); - - return m_handle; - } - - void destroy() - { - m_source.clear(); - m_compiled.clear(); - - if (m_handle) - { - VkDevice dev = *vk::get_current_renderer(); - vkDestroyShaderModule(dev, m_handle, nullptr); - m_handle = nullptr; - } - } - - const std::string& get_source() const - { - return m_source; - } - - const std::vector get_compiled() const - { - return m_compiled; - } - - VkShaderModule get_handle() const - { - return m_handle; - } - }; - - class program - { - std::array, input_type_max_enum> uniforms; - VkDevice m_device; - - std::array fs_texture_bindings; - std::array fs_texture_mirror_bindings; - std::array vs_texture_bindings; - bool linked; - - void create_impl(); - - public: - VkPipeline pipeline; - VkPipelineLayout pipeline_layout; - u64 attribute_location_mask; - u64 vertex_attributes_mask; - - program(VkDevice dev, VkPipeline p, VkPipelineLayout layout, const std::vector &vertex_input, const std::vector& fragment_inputs); - program(VkDevice dev, VkPipeline p, VkPipelineLayout layout); - program(const program&) = delete; - program(program&& other) = delete; - ~program(); - - program& load_uniforms(const std::vector& inputs); - program& link(); - - bool has_uniform(program_input_type type, const std::string &uniform_name); - void bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string &uniform_name, VkDescriptorType type, VkDescriptorSet &descriptor_set); - void bind_uniform(const VkDescriptorImageInfo &image_descriptor, int texture_unit, ::glsl::program_domain domain, VkDescriptorSet &descriptor_set, bool is_stencil_mirror = false); - void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorSet &descriptor_set); - void bind_uniform(const VkBufferView &buffer_view, u32 binding_point, VkDescriptorSet &descriptor_set); - void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, VkDescriptorSet &descriptor_set); - - void bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type, VkDescriptorSet &descriptor_set); - void bind_descriptor_set(const VkCommandBuffer cmd, VkDescriptorSet descriptor_set); - }; - } - class data_heap : public ::data_heap { private: diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index 19e4fb09fa..e3bcaa6b0d 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -7,6 +7,7 @@ #include "VKResourceManager.h" #include "VKRenderPass.h" #include "VKPipelineCompiler.h" +#include "helpers/sampler.h" #include "../Overlays/overlays.h" diff --git a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp index d1b8f5664f..880b434949 100644 --- a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp +++ b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "VKPipelineCompiler.h" #include "VKRenderPass.h" +#include "VKHelpers.h" #include "Utilities/Thread.h" #include diff --git a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h index 3479eb60c9..3365a93c9e 100644 --- a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h +++ b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h @@ -1,11 +1,14 @@ #pragma once -#include "VKHelpers.h" #include "../rsx_utils.h" #include "Utilities/hash.h" #include "Utilities/lockless.h" +#include "VKProgramPipeline.h" +#include "helpers/graphics_pipeline_state.h" namespace vk { + class render_device; + struct pipeline_props { graphics_pipeline_state state; diff --git a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h index 040139860f..c173adf18a 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h +++ b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h @@ -3,7 +3,6 @@ #include "VKFragmentProgram.h" #include "../Common/ProgramStateCache.h" #include "Utilities/hash.h" -#include "VKHelpers.h" #include "VKRenderPass.h" #include "VKPipelineCompiler.h" diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp b/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp index 175c155890..67266ef256 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include "VKProgramPipeline.h" #include "VKHelpers.h" #include @@ -9,6 +10,66 @@ namespace vk { using namespace ::glsl; + void shader::create(::glsl::program_domain domain, const std::string& source) + { + type = domain; + m_source = source; + } + + VkShaderModule shader::compile() + { + ensure(m_handle == VK_NULL_HANDLE); + + if (!vk::compile_glsl_to_spv(m_source, type, m_compiled)) + { + const std::string shader_type = type == ::glsl::program_domain::glsl_vertex_program ? "vertex" : + type == ::glsl::program_domain::glsl_fragment_program ? "fragment" : "compute"; + + rsx_log.notice("%s", m_source); + fmt::throw_exception("Failed to compile %s shader", shader_type); + } + + VkShaderModuleCreateInfo vs_info; + vs_info.codeSize = m_compiled.size() * sizeof(u32); + vs_info.pNext = nullptr; + vs_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vs_info.pCode = m_compiled.data(); + vs_info.flags = 0; + + VkDevice dev = *vk::get_current_renderer(); + vkCreateShaderModule(dev, &vs_info, nullptr, &m_handle); + + return m_handle; + } + + void shader::destroy() + { + m_source.clear(); + m_compiled.clear(); + + if (m_handle) + { + VkDevice dev = *vk::get_current_renderer(); + vkDestroyShaderModule(dev, m_handle, nullptr); + m_handle = nullptr; + } + } + + const std::string& shader::get_source() const + { + return m_source; + } + + const std::vector shader::get_compiled() const + { + return m_compiled; + } + + VkShaderModule shader::get_handle() const + { + return m_handle; + } + void program::create_impl() { linked = false; diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h new file mode 100644 index 0000000000..81e36800ef --- /dev/null +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h @@ -0,0 +1,112 @@ +#pragma once + +#include "VulkanAPI.h" +#include "VKCommonDecompiler.h" +#include "../Common/GLSLTypes.h" + +#include +#include + +namespace vk +{ + namespace glsl + { + enum program_input_type : u32 + { + input_type_uniform_buffer = 0, + input_type_texel_buffer = 1, + input_type_texture = 2, + input_type_storage_buffer = 3, + + input_type_max_enum = 4 + }; + + struct bound_sampler + { + VkFormat format; + VkImage image; + VkComponentMapping mapping; + }; + + struct bound_buffer + { + VkFormat format = VK_FORMAT_UNDEFINED; + VkBuffer buffer = nullptr; + u64 offset = 0; + u64 size = 0; + }; + + struct program_input + { + ::glsl::program_domain domain; + program_input_type type; + + bound_buffer as_buffer; + bound_sampler as_sampler; + + u32 location; + std::string name; + }; + + class shader + { + ::glsl::program_domain type = ::glsl::program_domain::glsl_vertex_program; + VkShaderModule m_handle = VK_NULL_HANDLE; + std::string m_source; + std::vector m_compiled; + + public: + shader() = default; + ~shader() = default; + + void create(::glsl::program_domain domain, const std::string& source); + + VkShaderModule compile(); + + void destroy(); + + const std::string& get_source() const; + const std::vector get_compiled() const; + + VkShaderModule get_handle() const; + }; + + class program + { + std::array, input_type_max_enum> uniforms; + VkDevice m_device; + + std::array fs_texture_bindings; + std::array fs_texture_mirror_bindings; + std::array vs_texture_bindings; + bool linked; + + void create_impl(); + + public: + VkPipeline pipeline; + VkPipelineLayout pipeline_layout; + u64 attribute_location_mask; + u64 vertex_attributes_mask; + + program(VkDevice dev, VkPipeline p, VkPipelineLayout layout, const std::vector &vertex_input, const std::vector& fragment_inputs); + program(VkDevice dev, VkPipeline p, VkPipelineLayout layout); + program(const program&) = delete; + program(program&& other) = delete; + ~program(); + + program& load_uniforms(const std::vector& inputs); + program& link(); + + bool has_uniform(program_input_type type, const std::string &uniform_name); + void bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string &uniform_name, VkDescriptorType type, VkDescriptorSet &descriptor_set); + void bind_uniform(const VkDescriptorImageInfo &image_descriptor, int texture_unit, ::glsl::program_domain domain, VkDescriptorSet &descriptor_set, bool is_stencil_mirror = false); + void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorSet &descriptor_set); + void bind_uniform(const VkBufferView &buffer_view, u32 binding_point, VkDescriptorSet &descriptor_set); + void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, VkDescriptorSet &descriptor_set); + + void bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type, VkDescriptorSet &descriptor_set); + void bind_descriptor_set(const VkCommandBuffer cmd, VkDescriptorSet descriptor_set); + }; + } +} diff --git a/rpcs3/Emu/RSX/VK/VKQueryPool.h b/rpcs3/Emu/RSX/VK/VKQueryPool.h index 9785695c93..65b8838486 100644 --- a/rpcs3/Emu/RSX/VK/VKQueryPool.h +++ b/rpcs3/Emu/RSX/VK/VKQueryPool.h @@ -1,8 +1,13 @@ #pragma once -#include "VKHelpers.h" +#include "VulkanAPI.h" +#include namespace vk { + class command_buffer; + class query_pool; + class render_device; + class query_pool_manager { struct query_slot_info diff --git a/rpcs3/Emu/RSX/VK/VKRenderPass.cpp b/rpcs3/Emu/RSX/VK/VKRenderPass.cpp index 355ce58eca..c7c11716ee 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderPass.cpp +++ b/rpcs3/Emu/RSX/VK/VKRenderPass.cpp @@ -2,6 +2,7 @@ #include "Utilities/mutex.h" #include "VKRenderPass.h" +#include "VKHelpers.h" namespace vk { diff --git a/rpcs3/Emu/RSX/VK/VKRenderPass.h b/rpcs3/Emu/RSX/VK/VKRenderPass.h index 8f9750a42d..e6268c92ff 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderPass.h +++ b/rpcs3/Emu/RSX/VK/VKRenderPass.h @@ -1,9 +1,12 @@ #pragma once -#include "VKHelpers.h" +#include "VulkanAPI.h" +#include "Utilities/geometry.h" namespace vk { + class image; + u64 get_renderpass_key(const std::vector& images); u64 get_renderpass_key(const std::vector& images, u64 previous_key); u64 get_renderpass_key(VkFormat surface_format); diff --git a/rpcs3/Emu/RSX/VK/VKResourceManager.h b/rpcs3/Emu/RSX/VK/VKResourceManager.h index 3110cd6803..21aad9624d 100644 --- a/rpcs3/Emu/RSX/VK/VKResourceManager.h +++ b/rpcs3/Emu/RSX/VK/VKResourceManager.h @@ -1,5 +1,7 @@ #pragma once #include "VKHelpers.h" +#include "helpers/query_pool.h" +#include "helpers/sampler.h" #include #include diff --git a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h index f06daab348..31df4d9412 100644 --- a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h +++ b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h @@ -1,5 +1,6 @@ #pragma once #include "VKProgramBuffer.h" +#include "VKHelpers.h" #include namespace vk diff --git a/rpcs3/Emu/RSX/VK/VKVertexProgram.h b/rpcs3/Emu/RSX/VK/VKVertexProgram.h index b9d92d03c6..00acb955b6 100644 --- a/rpcs3/Emu/RSX/VK/VKVertexProgram.h +++ b/rpcs3/Emu/RSX/VK/VKVertexProgram.h @@ -3,7 +3,8 @@ #include "Emu/RSX/RSXVertexProgram.h" #include "Utilities/Thread.h" #include "VulkanAPI.h" -#include "../VK/VKHelpers.h" +#include "VKProgramPipeline.h" +#include "helpers/pipeline_binding_table.h" namespace vk { diff --git a/rpcs3/Emu/RSX/VK/helpers/chip_class.cpp b/rpcs3/Emu/RSX/VK/helpers/chip_class.cpp new file mode 100644 index 0000000000..134151df20 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/chip_class.cpp @@ -0,0 +1,51 @@ +#include "chip_class.h" +#include "util/logs.hpp" + +namespace vk +{ + chip_class g_chip_class = chip_class::unknown; + + chip_class get_chip_family() + { + return g_chip_class; + } + + chip_class get_chip_family(u32 vendor_id, u32 device_id) + { + if (vendor_id == 0x10DE) + { + return s_NV_family_tree.find(device_id); + } + + if (vendor_id == 0x1002) + { + return s_AMD_family_tree.find(device_id); + } + + return chip_class::unknown; + } + + void chip_family_table::add(u32 first, u32 last, chip_class family) + { + for (auto i = first; i <= last; ++i) + { + lut[i] = family; + } + } + + void chip_family_table::add(u32 id, chip_class family) + { + lut[id] = family; + } + + chip_class chip_family_table::find(u32 device_id) + { + if (auto found = lut.find(device_id); found != lut.end()) + { + return found->second; + } + + rsx_log.warning("Unknown chip with device ID 0x%x", device_id); + return default_; + } +} diff --git a/rpcs3/Emu/RSX/VK/helpers/chip_class.h b/rpcs3/Emu/RSX/VK/helpers/chip_class.h new file mode 100644 index 0000000000..178bf963c0 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/chip_class.h @@ -0,0 +1,98 @@ +#pragma once + +#include "util/types.hpp" +#include + +namespace vk +{ + // Chip classes grouped by vendor in order of release + enum class chip_class + { + unknown, + AMD_gcn_generic, + AMD_polaris, + AMD_vega, + AMD_navi1x, + AMD_navi2x, + NV_generic, + NV_kepler, + NV_maxwell, + NV_pascal, + NV_volta, + NV_turing, + NV_ampere + }; + + enum class driver_vendor + { + unknown, + AMD, + NVIDIA, + RADV, + INTEL + }; + + struct chip_family_table + { + chip_class default_ = chip_class::unknown; + std::unordered_map lut; + + void add(u32 first, u32 last, chip_class family); + void add(u32 id, chip_class family); + + chip_class find(u32 device_id); + }; + + static chip_family_table s_AMD_family_tree = []() + { + chip_family_table table; + table.default_ = chip_class::AMD_gcn_generic; + + // AMD cards. See https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c + table.add(0x67C0, 0x67FF, chip_class::AMD_polaris); + table.add(0x6FDF, chip_class::AMD_polaris); // RX580 2048SP + table.add(0x6980, 0x699F, chip_class::AMD_polaris); // Polaris12 + table.add(0x694C, 0x694F, chip_class::AMD_vega); // VegaM + table.add(0x6860, 0x686F, chip_class::AMD_vega); // VegaPro + table.add(0x687F, chip_class::AMD_vega); // Vega56/64 + table.add(0x69A0, 0x69AF, chip_class::AMD_vega); // Vega12 + table.add(0x66A0, 0x66AF, chip_class::AMD_vega); // Vega20 + table.add(0x15DD, chip_class::AMD_vega); // Raven Ridge + table.add(0x15D8, chip_class::AMD_vega); // Raven Ridge + table.add(0x7310, 0x731F, chip_class::AMD_navi1x); // Navi10 + table.add(0x7340, 0x734F, chip_class::AMD_navi1x); // Navi14 + table.add(0x73A0, 0x73BF, chip_class::AMD_navi2x); // Sienna cichlid + + return table; + }(); + + static chip_family_table s_NV_family_tree = []() + { + chip_family_table table; + table.default_ = chip_class::NV_generic; + + // NV cards. See https://envytools.readthedocs.io/en/latest/hw/pciid.html + // NOTE: Since NV device IDs are linearly incremented per generation, there is no need to carefully check all the ranges + table.add(0x1180, 0x11fa, chip_class::NV_kepler); // GK104, 106 + table.add(0x0FC0, 0x0FFF, chip_class::NV_kepler); // GK107 + table.add(0x1003, 0x1028, chip_class::NV_kepler); // GK110 + table.add(0x1280, 0x12BA, chip_class::NV_kepler); // GK208 + table.add(0x1381, 0x13B0, chip_class::NV_maxwell); // GM107 + table.add(0x1340, 0x134D, chip_class::NV_maxwell); // GM108 + table.add(0x13C0, 0x13D9, chip_class::NV_maxwell); // GM204 + table.add(0x1401, 0x1427, chip_class::NV_maxwell); // GM206 + table.add(0x15F7, 0x15F9, chip_class::NV_pascal); // GP100 (Tesla P100) + table.add(0x1B00, 0x1D80, chip_class::NV_pascal); + table.add(0x1D81, 0x1DBA, chip_class::NV_volta); + table.add(0x1E02, 0x1F54, chip_class::NV_turing); // TU102, TU104, TU106, TU106M, TU106GL (RTX 20 series) + table.add(0x1F82, 0x1FB9, chip_class::NV_turing); // TU117, TU117M, TU117GL + table.add(0x2182, 0x21D1, chip_class::NV_turing); // TU116, TU116M, TU116GL + table.add(0x20B0, 0x20BE, chip_class::NV_ampere); // GA100 + table.add(0x2204, 0x25AF, chip_class::NV_ampere); // GA10x (RTX 30 series) + + return table; + }(); + + chip_class get_chip_family(); + chip_class get_chip_family(u32 vendor_id, u32 device_id); +} diff --git a/rpcs3/Emu/RSX/VK/helpers/fence.cpp b/rpcs3/Emu/RSX/VK/helpers/fence.cpp new file mode 100644 index 0000000000..31d54b3f6f --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/fence.cpp @@ -0,0 +1,56 @@ +#pragma once + +#include "fence.h" +#include "shared.h" + +namespace vk +{ +#ifdef _MSC_VER + extern "C" void _mm_pause(); +#endif + + fence::fence(VkDevice dev) + { + owner = dev; + VkFenceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + CHECK_RESULT(vkCreateFence(dev, &info, nullptr, &handle)); + } + + fence::~fence() + { + if (handle) + { + vkDestroyFence(owner, handle, nullptr); + handle = VK_NULL_HANDLE; + } + } + + void fence::reset() + { + vkResetFences(owner, 1, &handle); + flushed.release(false); + } + + void fence::signal_flushed() + { + flushed.release(true); + } + + void fence::wait_flush() + { + while (!flushed) + { +#ifdef _MSC_VER + _mm_pause(); +#else + __builtin_ia32_pause(); +#endif + } + } + + fence::operator bool() const + { + return (handle != VK_NULL_HANDLE); + } +} diff --git a/rpcs3/Emu/RSX/VK/helpers/fence.h b/rpcs3/Emu/RSX/VK/helpers/fence.h new file mode 100644 index 0000000000..42717bfb72 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/fence.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../VulkanAPI.h" +#include "util/atomic.hpp" + +namespace vk +{ + struct fence + { + atomic_t flushed = false; + VkFence handle = VK_NULL_HANDLE; + VkDevice owner = VK_NULL_HANDLE; + + fence(VkDevice dev); + ~fence(); + + void reset(); + void signal_flushed(); + void wait_flush(); + + operator bool() const; + }; +} diff --git a/rpcs3/Emu/RSX/VK/helpers/graphics_pipeline_state.h b/rpcs3/Emu/RSX/VK/helpers/graphics_pipeline_state.h new file mode 100644 index 0000000000..ceae859d2c --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/graphics_pipeline_state.h @@ -0,0 +1,219 @@ +#pragma once + +#include "../VulkanAPI.h" + +namespace vk +{ + class graphics_pipeline_state + { + public: + VkPipelineInputAssemblyStateCreateInfo ia; + VkPipelineDepthStencilStateCreateInfo ds; + VkPipelineColorBlendAttachmentState att_state[4]; + VkPipelineColorBlendStateCreateInfo cs; + VkPipelineRasterizationStateCreateInfo rs; + VkPipelineMultisampleStateCreateInfo ms; + + struct extra_parameters + { + VkSampleMask msaa_sample_mask; + } + temp_storage; + + graphics_pipeline_state() + { + // NOTE: Vk** structs have padding bytes + memset(this, 0, sizeof(graphics_pipeline_state)); + + ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + cs.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + + rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rs.polygonMode = VK_POLYGON_MODE_FILL; + rs.cullMode = VK_CULL_MODE_NONE; + rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rs.lineWidth = 1.f; + + ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + temp_storage.msaa_sample_mask = 0xFFFFFFFF; + } + + graphics_pipeline_state(const graphics_pipeline_state& other) + { + // NOTE: Vk** structs have padding bytes + memcpy(this, &other, sizeof(graphics_pipeline_state)); + + if (other.cs.pAttachments == other.att_state) + { + // Rebase pointer + cs.pAttachments = att_state; + } + } + + ~graphics_pipeline_state() = default; + + graphics_pipeline_state& operator = (const graphics_pipeline_state& other) + { + if (this != &other) + { + // NOTE: Vk** structs have padding bytes + memcpy(this, &other, sizeof(graphics_pipeline_state)); + + if (other.cs.pAttachments == other.att_state) + { + // Rebase pointer + cs.pAttachments = att_state; + } + } + + return *this; + } + + void set_primitive_type(VkPrimitiveTopology type) + { + ia.topology = type; + } + + void enable_primitive_restart(bool enable = true) + { + ia.primitiveRestartEnable = enable? VK_TRUE : VK_FALSE; + } + + void set_color_mask(int index, bool r, bool g, bool b, bool a) + { + VkColorComponentFlags mask = 0; + if (a) mask |= VK_COLOR_COMPONENT_A_BIT; + if (b) mask |= VK_COLOR_COMPONENT_B_BIT; + if (g) mask |= VK_COLOR_COMPONENT_G_BIT; + if (r) mask |= VK_COLOR_COMPONENT_R_BIT; + + att_state[index].colorWriteMask = mask; + } + + void set_depth_mask(bool enable) + { + ds.depthWriteEnable = enable ? VK_TRUE : VK_FALSE; + } + + void set_stencil_mask(u32 mask) + { + ds.front.writeMask = mask; + ds.back.writeMask = mask; + } + + void set_stencil_mask_separate(int face, u32 mask) + { + if (!face) + ds.front.writeMask = mask; + else + ds.back.writeMask = mask; + } + + void enable_depth_test(VkCompareOp op) + { + ds.depthTestEnable = VK_TRUE; + ds.depthCompareOp = op; + } + + void enable_depth_clamp(bool enable = true) + { + rs.depthClampEnable = enable ? VK_TRUE : VK_FALSE; + } + + void enable_depth_bias(bool enable = true) + { + rs.depthBiasEnable = enable ? VK_TRUE : VK_FALSE; + } + + void enable_depth_bounds_test(bool enable = true) + { + ds.depthBoundsTestEnable = enable? VK_TRUE : VK_FALSE; + } + + void enable_blend(int mrt_index, VkBlendFactor src_factor_rgb, VkBlendFactor src_factor_a, + VkBlendFactor dst_factor_rgb, VkBlendFactor dst_factor_a, + VkBlendOp equation_rgb, VkBlendOp equation_a) + { + att_state[mrt_index].srcColorBlendFactor = src_factor_rgb; + att_state[mrt_index].srcAlphaBlendFactor = src_factor_a; + att_state[mrt_index].dstColorBlendFactor = dst_factor_rgb; + att_state[mrt_index].dstAlphaBlendFactor = dst_factor_a; + att_state[mrt_index].colorBlendOp = equation_rgb; + att_state[mrt_index].alphaBlendOp = equation_a; + att_state[mrt_index].blendEnable = VK_TRUE; + } + + void enable_stencil_test(VkStencilOp fail, VkStencilOp zfail, VkStencilOp pass, + VkCompareOp func, u32 func_mask, u32 ref) + { + ds.front.failOp = fail; + ds.front.passOp = pass; + ds.front.depthFailOp = zfail; + ds.front.compareOp = func; + ds.front.compareMask = func_mask; + ds.front.reference = ref; + ds.back = ds.front; + + ds.stencilTestEnable = VK_TRUE; + } + + void enable_stencil_test_separate(int face, VkStencilOp fail, VkStencilOp zfail, VkStencilOp pass, + VkCompareOp func, u32 func_mask, u32 ref) + { + auto& face_props = (face ? ds.back : ds.front); + face_props.failOp = fail; + face_props.passOp = pass; + face_props.depthFailOp = zfail; + face_props.compareOp = func; + face_props.compareMask = func_mask; + face_props.reference = ref; + + ds.stencilTestEnable = VK_TRUE; + } + + void enable_logic_op(VkLogicOp op) + { + cs.logicOpEnable = VK_TRUE; + cs.logicOp = op; + } + + void enable_cull_face(VkCullModeFlags cull_mode) + { + rs.cullMode = cull_mode; + } + + void set_front_face(VkFrontFace face) + { + rs.frontFace = face; + } + + void set_attachment_count(u32 count) + { + cs.attachmentCount = count; + cs.pAttachments = att_state; + } + + void set_multisample_state(u8 sample_count, u32 sample_mask, bool msaa_enabled, bool alpha_to_coverage, bool alpha_to_one) + { + temp_storage.msaa_sample_mask = sample_mask; + + ms.rasterizationSamples = static_cast(sample_count); + ms.alphaToCoverageEnable = alpha_to_coverage; + ms.alphaToOneEnable = alpha_to_one; + + if (!msaa_enabled) + { + // This register is likely glMinSampleShading but in reverse; probably sets max sample shading rate of 1 + // I (kd-11) suspect its what the control panel setting affects when MSAA is set to disabled + } + } + + void set_multisample_shading_rate(float shading_rate) + { + ms.sampleShadingEnable = VK_TRUE; + ms.minSampleShading = shading_rate; + } + }; +} diff --git a/rpcs3/Emu/RSX/VK/helpers/mem_allocator.cpp b/rpcs3/Emu/RSX/VK/helpers/mem_allocator.cpp new file mode 100644 index 0000000000..932a92c464 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/mem_allocator.cpp @@ -0,0 +1,186 @@ +#include "mem_allocator.h" +#include "util/logs.hpp" +#include "../VKHelpers.h" + +namespace vk +{ + extern const render_device* g_current_renderer; + + mem_allocator_vma::mem_allocator_vma(VkDevice dev, VkPhysicalDevice pdev) : mem_allocator_base(dev, pdev) + { + // Initialize stats pool + std::fill(stats.begin(), stats.end(), VmaBudget{}); + + VmaAllocatorCreateInfo allocatorInfo = {}; + allocatorInfo.physicalDevice = pdev; + allocatorInfo.device = dev; + + vmaCreateAllocator(&allocatorInfo, &m_allocator); + } + + void mem_allocator_vma::destroy() + { + vmaDestroyAllocator(m_allocator); + } + + mem_allocator_vk::mem_handle_t mem_allocator_vma::alloc(u64 block_sz, u64 alignment, u32 memory_type_index) + { + VmaAllocation vma_alloc; + VkMemoryRequirements mem_req = {}; + VmaAllocationCreateInfo create_info = {}; + + mem_req.memoryTypeBits = 1u << memory_type_index; + mem_req.size = block_sz; + mem_req.alignment = alignment; + create_info.memoryTypeBits = 1u << memory_type_index; + + if (VkResult result = vmaAllocateMemory(m_allocator, &mem_req, &create_info, &vma_alloc, nullptr); + result != VK_SUCCESS) + { + if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY && + vmm_handle_memory_pressure(rsx::problem_severity::fatal)) + { + // If we just ran out of VRAM, attempt to release resources and try again + result = vmaAllocateMemory(m_allocator, &mem_req, &create_info, &vma_alloc, nullptr); + } + + if (result != VK_SUCCESS) + { + die_with_error(result); + } + else + { + rsx_log.warning("Renderer ran out of video memory but successfully recovered."); + } + } + + vmm_notify_memory_allocated(vma_alloc, memory_type_index, block_sz); + return vma_alloc; + } + + void mem_allocator_vma::free(mem_handle_t mem_handle) + { + vmm_notify_memory_freed(mem_handle); + vmaFreeMemory(m_allocator, static_cast(mem_handle)); + } + + void* mem_allocator_vma::map(mem_handle_t mem_handle, u64 offset, u64 /*size*/) + { + void *data = nullptr; + + CHECK_RESULT(vmaMapMemory(m_allocator, static_cast(mem_handle), &data)); + + // Add offset + data = static_cast(data) + offset; + return data; + } + + void mem_allocator_vma::unmap(mem_handle_t mem_handle) + { + vmaUnmapMemory(m_allocator, static_cast(mem_handle)); + } + + VkDeviceMemory mem_allocator_vma::get_vk_device_memory(mem_handle_t mem_handle) + { + VmaAllocationInfo alloc_info; + + vmaGetAllocationInfo(m_allocator, static_cast(mem_handle), &alloc_info); + return alloc_info.deviceMemory; + } + + u64 mem_allocator_vma::get_vk_device_memory_offset(mem_handle_t mem_handle) + { + VmaAllocationInfo alloc_info; + + vmaGetAllocationInfo(m_allocator, static_cast(mem_handle), &alloc_info); + return alloc_info.offset; + } + + f32 mem_allocator_vma::get_memory_usage() + { + vmaGetBudget(m_allocator, stats.data()); + + float max_usage = 0.f; + for (const auto& info : stats) + { + if (!info.budget) + { + break; + } + + const float this_usage = (info.usage * 100.f) / info.budget; + max_usage = std::max(max_usage, this_usage); + } + + return max_usage; + } + + mem_allocator_vk::mem_handle_t mem_allocator_vk::alloc(u64 block_sz, u64 /*alignment*/, u32 memory_type_index) + { + VkDeviceMemory memory; + VkMemoryAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + info.allocationSize = block_sz; + info.memoryTypeIndex = memory_type_index; + + if (VkResult result = vkAllocateMemory(m_device, &info, nullptr, &memory); result != VK_SUCCESS) + { + if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY && vmm_handle_memory_pressure(rsx::problem_severity::fatal)) + { + // If we just ran out of VRAM, attempt to release resources and try again + result = vkAllocateMemory(m_device, &info, nullptr, &memory); + } + + if (result != VK_SUCCESS) + { + die_with_error(result); + } + else + { + rsx_log.warning("Renderer ran out of video memory but successfully recovered."); + } + } + + vmm_notify_memory_allocated(memory, memory_type_index, block_sz); + return memory; + } + + void mem_allocator_vk::free(mem_handle_t mem_handle) + { + vmm_notify_memory_freed(mem_handle); + vkFreeMemory(m_device, static_cast(mem_handle), nullptr); + } + + void* mem_allocator_vk::map(mem_handle_t mem_handle, u64 offset, u64 size) + { + void* data = nullptr; + CHECK_RESULT(vkMapMemory(m_device, static_cast(mem_handle), offset, std::max(size, 1u), 0, &data)); + return data; + } + + void mem_allocator_vk::unmap(mem_handle_t mem_handle) + { + vkUnmapMemory(m_device, static_cast(mem_handle)); + } + + VkDeviceMemory mem_allocator_vk::get_vk_device_memory(mem_handle_t mem_handle) + { + return static_cast(mem_handle); + } + + u64 mem_allocator_vk::get_vk_device_memory_offset(mem_handle_t /*mem_handle*/) + { + return 0; + } + + f32 mem_allocator_vk::get_memory_usage() + { + return 0.f; + } + + mem_allocator_base* get_current_mem_allocator() + { + ensure(g_current_renderer); + return g_current_renderer->get_allocator(); + } +} diff --git a/rpcs3/Emu/RSX/VK/helpers/mem_allocator.h b/rpcs3/Emu/RSX/VK/helpers/mem_allocator.h new file mode 100644 index 0000000000..d288e33197 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/mem_allocator.h @@ -0,0 +1,91 @@ +#pragma once + +#include "../VulkanAPI.h" +#include "../../rsx_utils.h" +#include "shared.h" + +#include "3rdparty/GPUOpen/include/vk_mem_alloc.h" + +namespace vk +{ + // Memory Allocator - base class + + class mem_allocator_base + { + public: + using mem_handle_t = void *; + + mem_allocator_base(VkDevice dev, VkPhysicalDevice /*pdev*/) : m_device(dev) {} + virtual ~mem_allocator_base() = default; + + virtual void destroy() = 0; + + virtual mem_handle_t alloc(u64 block_sz, u64 alignment, u32 memory_type_index) = 0; + virtual void free(mem_handle_t mem_handle) = 0; + virtual void *map(mem_handle_t mem_handle, u64 offset, u64 size) = 0; + virtual void unmap(mem_handle_t mem_handle) = 0; + virtual VkDeviceMemory get_vk_device_memory(mem_handle_t mem_handle) = 0; + virtual u64 get_vk_device_memory_offset(mem_handle_t mem_handle) = 0; + virtual f32 get_memory_usage() = 0; + + protected: + VkDevice m_device; + }; + + + // Memory Allocator - Vulkan Memory Allocator + // https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator + + class mem_allocator_vma : public mem_allocator_base + { + public: + mem_allocator_vma(VkDevice dev, VkPhysicalDevice pdev); + ~mem_allocator_vma() override = default; + + void destroy() override; + + mem_handle_t alloc(u64 block_sz, u64 alignment, u32 memory_type_index) override; + + void free(mem_handle_t mem_handle) override; + void* map(mem_handle_t mem_handle, u64 offset, u64 /*size*/) override; + void unmap(mem_handle_t mem_handle) override; + + VkDeviceMemory get_vk_device_memory(mem_handle_t mem_handle) override; + u64 get_vk_device_memory_offset(mem_handle_t mem_handle) override; + f32 get_memory_usage() override; + + private: + VmaAllocator m_allocator; + std::array stats; + }; + + + // Memory Allocator - built-in Vulkan device memory allocate/free + + class mem_allocator_vk : public mem_allocator_base + { + public: + mem_allocator_vk(VkDevice dev, VkPhysicalDevice pdev) : mem_allocator_base(dev, pdev) {} + ~mem_allocator_vk() override = default; + + void destroy() override {} + + mem_handle_t alloc(u64 block_sz, u64 /*alignment*/, u32 memory_type_index) override; + + void free(mem_handle_t mem_handle) override; + void* map(mem_handle_t mem_handle, u64 offset, u64 size) override; + void unmap(mem_handle_t mem_handle) override; + + VkDeviceMemory get_vk_device_memory(mem_handle_t mem_handle) override; + u64 get_vk_device_memory_offset(mem_handle_t /*mem_handle*/) override; + f32 get_memory_usage() override; + }; + + void vmm_notify_memory_allocated(void* handle, u32 memory_type, u64 memory_size); + void vmm_notify_memory_freed(void* handle); + void vmm_reset(); + void vmm_check_memory_usage(); + bool vmm_handle_memory_pressure(rsx::problem_severity severity); + + mem_allocator_base* get_current_mem_allocator(); +} diff --git a/rpcs3/Emu/RSX/VK/helpers/memory_block.cpp b/rpcs3/Emu/RSX/VK/helpers/memory_block.cpp new file mode 100644 index 0000000000..242d464ecc --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/memory_block.cpp @@ -0,0 +1,36 @@ +#include "memory_block.h" + +namespace vk +{ + memory_block::memory_block(VkDevice dev, u64 block_sz, u64 alignment, u32 memory_type_index) + : m_device(dev) + { + m_mem_allocator = get_current_mem_allocator(); + m_mem_handle = m_mem_allocator->alloc(block_sz, alignment, memory_type_index); + } + + memory_block::~memory_block() + { + m_mem_allocator->free(m_mem_handle); + } + + VkDeviceMemory memory_block::get_vk_device_memory() + { + return m_mem_allocator->get_vk_device_memory(m_mem_handle); + } + + u64 memory_block::get_vk_device_memory_offset() + { + return m_mem_allocator->get_vk_device_memory_offset(m_mem_handle); + } + + void* memory_block::map(u64 offset, u64 size) + { + return m_mem_allocator->map(m_mem_handle, offset, size); + } + + void memory_block::unmap() + { + m_mem_allocator->unmap(m_mem_handle); + } +} diff --git a/rpcs3/Emu/RSX/VK/helpers/memory_block.h b/rpcs3/Emu/RSX/VK/helpers/memory_block.h new file mode 100644 index 0000000000..98e0899245 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/memory_block.h @@ -0,0 +1,27 @@ +#pragma once + +#include "../VulkanAPI.h" +#include "mem_allocator.h" + +namespace vk +{ + struct memory_block + { + memory_block(VkDevice dev, u64 block_sz, u64 alignment, u32 memory_type_index); + ~memory_block(); + + VkDeviceMemory get_vk_device_memory(); + u64 get_vk_device_memory_offset(); + + void* map(u64 offset, u64 size); + void unmap(); + + memory_block(const memory_block&) = delete; + memory_block(memory_block&&) = delete; + + private: + VkDevice m_device; + vk::mem_allocator_base* m_mem_allocator; + mem_allocator_base::mem_handle_t m_mem_handle; + }; +} diff --git a/rpcs3/Emu/RSX/VK/helpers/physical_device.cpp b/rpcs3/Emu/RSX/VK/helpers/physical_device.cpp new file mode 100644 index 0000000000..6f21c2175d --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/physical_device.cpp @@ -0,0 +1,320 @@ +#include "physical_device.h" +#include "supported_extensions.h" +#include "util/logs.hpp" +#include "Emu/system_config.h" + +namespace vk +{ + void physical_device::get_physical_device_features(bool allow_extensions) + { + if (!allow_extensions) + { + vkGetPhysicalDeviceFeatures(dev, &features); + return; + } + + supported_extensions instance_extensions(supported_extensions::instance); + supported_extensions device_extensions(supported_extensions::device, nullptr, dev); + + if (!instance_extensions.is_supported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + { + vkGetPhysicalDeviceFeatures(dev, &features); + } + else + { + VkPhysicalDeviceFeatures2KHR features2; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + features2.pNext = nullptr; + + VkPhysicalDeviceFloat16Int8FeaturesKHR shader_support_info{}; + + if (device_extensions.is_supported(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) + { + shader_support_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR; + features2.pNext = &shader_support_info; + } + + if (device_extensions.is_supported(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) + { + driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; + driver_properties.pNext = features2.pNext; + features2.pNext = &driver_properties; + } + + auto getPhysicalDeviceFeatures2KHR = reinterpret_cast(vkGetInstanceProcAddr(parent, "vkGetPhysicalDeviceFeatures2KHR")); + ensure(getPhysicalDeviceFeatures2KHR); // "vkGetInstanceProcAddress failed to find entry point!" + getPhysicalDeviceFeatures2KHR(dev, &features2); + + shader_types_support.allow_float64 = !!features2.features.shaderFloat64; + shader_types_support.allow_float16 = !!shader_support_info.shaderFloat16; + shader_types_support.allow_int8 = !!shader_support_info.shaderInt8; + features = features2.features; + } + + stencil_export_support = device_extensions.is_supported(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME); + conditional_render_support = device_extensions.is_supported(VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME); + unrestricted_depth_range_support = device_extensions.is_supported(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); + } + + void physical_device::create(VkInstance context, VkPhysicalDevice pdev, bool allow_extensions) + { + dev = pdev; + parent = context; + vkGetPhysicalDeviceProperties(pdev, &props); + vkGetPhysicalDeviceMemoryProperties(pdev, &memory_properties); + get_physical_device_features(allow_extensions); + + rsx_log.always("Found vulkan-compatible GPU: '%s' running on driver %s", get_name(), get_driver_version()); + + if (get_driver_vendor() == driver_vendor::RADV && get_name().find("LLVM 8.0.0") != umax) + { + // Serious driver bug causing black screens + // See https://bugs.freedesktop.org/show_bug.cgi?id=110970 + rsx_log.fatal("RADV drivers have a major driver bug with LLVM 8.0.0 resulting in no visual output. Upgrade to LLVM version 8.0.1 or greater to avoid this issue."); + } + else if (get_driver_vendor() == driver_vendor::NVIDIA) + { +#ifdef _WIN32 + // SPIRV bugs were fixed in 452.28 for windows + const u32 threshold_version = (452u >> 22) | (28 >> 14); +#else + // SPIRV bugs were fixed in 450.56 for linux/BSD + const u32 threshold_version = (450u >> 22) | (56 >> 14); +#endif + const auto current_version = props.driverVersion & ~0x3fffu; // Clear patch and revision fields + if (current_version < threshold_version) + { + rsx_log.error("Your current NVIDIA graphics driver version %s has known issues and is unsupported. Update to the latest NVIDIA driver.", get_driver_version()); + } + } + + if (get_chip_class() == chip_class::AMD_vega) + { + // Disable fp16 if driver uses LLVM emitter. It does fine with AMD proprietary drivers though. + shader_types_support.allow_float16 = (driver_properties.driverID == VK_DRIVER_ID_AMD_PROPRIETARY_KHR); + } + } + + std::string physical_device::get_name() const + { + return props.deviceName; + } + + driver_vendor physical_device::get_driver_vendor() const + { + if (!driver_properties.driverID) + { + const auto gpu_name = get_name(); + if (gpu_name.find("Radeon") != umax) + { + return driver_vendor::AMD; + } + + if (gpu_name.find("NVIDIA") != umax || gpu_name.find("GeForce") != umax || gpu_name.find("Quadro") != umax) + { + return driver_vendor::NVIDIA; + } + + if (gpu_name.find("RADV") != umax) + { + return driver_vendor::RADV; + } + + if (gpu_name.find("Intel") != umax) + { + return driver_vendor::INTEL; + } + + return driver_vendor::unknown; + } + else + { + switch (driver_properties.driverID) + { + case VK_DRIVER_ID_AMD_PROPRIETARY_KHR: + case VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR: + return driver_vendor::AMD; + case VK_DRIVER_ID_MESA_RADV_KHR: + return driver_vendor::RADV; + case VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR: + return driver_vendor::NVIDIA; + case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR: + case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR: + return driver_vendor::INTEL; + default: + // Mobile + return driver_vendor::unknown; + } + } + } + + std::string physical_device::get_driver_version() const + { + switch (get_driver_vendor()) + { + case driver_vendor::NVIDIA: + { + // 10 + 8 + 8 + 6 + const auto major_version = props.driverVersion >> 22; + const auto minor_version = (props.driverVersion >> 14) & 0xff; + const auto patch = (props.driverVersion >> 6) & 0xff; + const auto revision = (props.driverVersion & 0x3f); + + return fmt::format("%u.%u.%u.%u", major_version, minor_version, patch, revision); + } + default: + { + // 10 + 10 + 12 (standard vulkan encoding created with VK_MAKE_VERSION) + return fmt::format("%u.%u.%u", (props.driverVersion >> 22), (props.driverVersion >> 12) & 0x3ff, (props.driverVersion) & 0x3ff); + } + } + } + + chip_class physical_device::get_chip_class() const + { + return get_chip_family(props.vendorID, props.deviceID); + } + + u32 physical_device::get_queue_count() const + { + if (!queue_props.empty()) + return ::size32(queue_props); + + u32 count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(dev, &count, nullptr); + + return count; + } + + VkQueueFamilyProperties physical_device::get_queue_properties(u32 queue) + { + if (queue_props.empty()) + { + u32 count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(dev, &count, nullptr); + + queue_props.resize(count); + vkGetPhysicalDeviceQueueFamilyProperties(dev, &count, queue_props.data()); + } + + if (queue >= queue_props.size()) + fmt::throw_exception("Bad queue index passed to get_queue_properties (%u)", queue); + return queue_props[queue]; + } + + VkPhysicalDeviceMemoryProperties physical_device::get_memory_properties() const + { + return memory_properties; + } + + VkPhysicalDeviceLimits physical_device::get_limits() const + { + return props.limits; + } + + physical_device::operator VkPhysicalDevice() const + { + return dev; + } + + physical_device::operator VkInstance() const + { + return parent; + } + + memory_type_mapping get_memory_mapping(const vk::physical_device& dev) + { + VkPhysicalDevice pdev = dev; + VkPhysicalDeviceMemoryProperties memory_properties; + vkGetPhysicalDeviceMemoryProperties(pdev, &memory_properties); + + memory_type_mapping result; + result.device_local = VK_MAX_MEMORY_TYPES; + result.host_visible_coherent = VK_MAX_MEMORY_TYPES; + + bool host_visible_cached = false; + VkDeviceSize host_visible_vram_size = 0; + VkDeviceSize device_local_vram_size = 0; + + for (u32 i = 0; i < memory_properties.memoryTypeCount; i++) + { + VkMemoryHeap& heap = memory_properties.memoryHeaps[memory_properties.memoryTypes[i].heapIndex]; + + bool is_device_local = !!(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (is_device_local) + { + if (device_local_vram_size < heap.size) + { + result.device_local = i; + device_local_vram_size = heap.size; + } + } + + bool is_host_visible = !!(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + bool is_host_coherent = !!(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + bool is_cached = !!(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT); + + if (is_host_coherent && is_host_visible) + { + if ((is_cached && !host_visible_cached) || (host_visible_vram_size < heap.size)) + { + result.host_visible_coherent = i; + host_visible_vram_size = heap.size; + host_visible_cached = is_cached; + } + } + } + + if (result.device_local == VK_MAX_MEMORY_TYPES) + fmt::throw_exception("GPU doesn't support device local memory"); + if (result.host_visible_coherent == VK_MAX_MEMORY_TYPES) + fmt::throw_exception("GPU doesn't support host coherent device local memory"); + return result; + } + + gpu_formats_support get_optimal_tiling_supported_formats(const physical_device& dev) + { + gpu_formats_support result = {}; + + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_D24_UNORM_S8_UINT, &props); + + result.d24_unorm_s8 = !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) && + !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); + + vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_D32_SFLOAT_S8_UINT, &props); + result.d32_sfloat_s8 = !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) && + !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); + + // Hide d24_s8 if force high precision z buffer is enabled + if (g_cfg.video.force_high_precision_z_buffer && result.d32_sfloat_s8) + result.d24_unorm_s8 = false; + + // Checks if BGRA8 images can be used for blitting + vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_B8G8R8A8_UNORM, &props); + result.bgra8_linear = !!(props.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); + + // Check if device supports RGBA8 format + vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_R8G8B8A8_UNORM, &props); + if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) || !(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) || + !(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) + { + // Non-fatal. Most games use BGRA layout due to legacy reasons as old GPUs typically supported BGRA and RGBA was emulated. + rsx_log.error("Your GPU and/or driver does not support RGBA8 format. This can cause problems in some rare games that use this memory layout."); + } + + result.argb8_linear = !!(props.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); + return result; + } + + pipeline_binding_table get_pipeline_binding_table(const vk::physical_device& dev) + { + pipeline_binding_table result{}; + + // Need to check how many samplers are supported by the driver + const auto usable_samplers = std::min(dev.get_limits().maxPerStageDescriptorSampledImages, 32u); + result.vertex_textures_first_bind_slot = result.textures_first_bind_slot + usable_samplers; + result.total_descriptor_bindings = result.vertex_textures_first_bind_slot + 4; + return result; + } +} diff --git a/rpcs3/Emu/RSX/VK/helpers/physical_device.h b/rpcs3/Emu/RSX/VK/helpers/physical_device.h new file mode 100644 index 0000000000..6885006cc5 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/physical_device.h @@ -0,0 +1,80 @@ +#pragma once + +#include "../VulkanAPI.h" +#include "chip_class.h" +#include "pipeline_binding_table.h" + +#include +#include + +namespace vk +{ + struct gpu_formats_support + { + bool d24_unorm_s8; + bool d32_sfloat_s8; + bool bgra8_linear; + bool argb8_linear; + }; + + struct gpu_shader_types_support + { + bool allow_float64; + bool allow_float16; + bool allow_int8; + }; + + struct memory_type_mapping + { + u32 host_visible_coherent; + u32 device_local; + }; + + class physical_device + { + VkInstance parent = VK_NULL_HANDLE; + VkPhysicalDevice dev = VK_NULL_HANDLE; + VkPhysicalDeviceProperties props; + VkPhysicalDeviceFeatures features; + VkPhysicalDeviceMemoryProperties memory_properties; + std::vector queue_props; + + std::unordered_map format_properties; + gpu_shader_types_support shader_types_support{}; + VkPhysicalDeviceDriverPropertiesKHR driver_properties{}; + + bool stencil_export_support = false; + bool conditional_render_support = false; + bool unrestricted_depth_range_support = false; + + friend class render_device; + private: + void get_physical_device_features(bool allow_extensions); + + public: + + physical_device() = default; + ~physical_device() = default; + + void create(VkInstance context, VkPhysicalDevice pdev, bool allow_extensions); + + std::string get_name() const; + + driver_vendor get_driver_vendor() const; + std::string get_driver_version() const; + chip_class get_chip_class() const; + + u32 get_queue_count() const; + + VkQueueFamilyProperties get_queue_properties(u32 queue); + VkPhysicalDeviceMemoryProperties get_memory_properties() const; + VkPhysicalDeviceLimits get_limits() const; + + operator VkPhysicalDevice() const; + operator VkInstance() const; + }; + + memory_type_mapping get_memory_mapping(const physical_device& dev); + gpu_formats_support get_optimal_tiling_supported_formats(const physical_device& dev); + pipeline_binding_table get_pipeline_binding_table(const physical_device& dev); +} diff --git a/rpcs3/Emu/RSX/VK/helpers/pipeline_binding_table.h b/rpcs3/Emu/RSX/VK/helpers/pipeline_binding_table.h new file mode 100644 index 0000000000..e2682a503e --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/pipeline_binding_table.h @@ -0,0 +1,21 @@ +#pragma once + +#include "util/types.hpp" + +namespace vk +{ + struct pipeline_binding_table + { + u8 vertex_params_bind_slot = 0; + u8 vertex_constant_buffers_bind_slot = 1; + u8 fragment_constant_buffers_bind_slot = 2; + u8 fragment_state_bind_slot = 3; + u8 fragment_texture_params_bind_slot = 4; + u8 vertex_buffers_first_bind_slot = 5; + u8 conditional_render_predicate_slot = 8; + u8 rasterizer_env_bind_slot = 9; + u8 textures_first_bind_slot = 10; + u8 vertex_textures_first_bind_slot = 10; // Invalid, has to be initialized properly + u8 total_descriptor_bindings = vertex_textures_first_bind_slot; // Invalid, has to be initialized properly + }; +} diff --git a/rpcs3/Emu/RSX/VK/helpers/query_pool.h b/rpcs3/Emu/RSX/VK/helpers/query_pool.h new file mode 100644 index 0000000000..90d4df9057 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/query_pool.h @@ -0,0 +1,38 @@ +#pragma once + +#include "../VulkanAPI.h" +#include "../../rsx_utils.h" + +namespace vk +{ + class query_pool : public rsx::ref_counted + { + VkQueryPool m_query_pool; + VkDevice m_device; + + public: + query_pool(VkDevice dev, VkQueryType type, u32 size) + : m_query_pool(VK_NULL_HANDLE) + , m_device(dev) + { + VkQueryPoolCreateInfo info{}; + info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + info.queryType = type; + info.queryCount = size; + vkCreateQueryPool(dev, &info, nullptr, &m_query_pool); + + // Take 'size' references on this object + ref_count.release(static_cast(size)); + } + + ~query_pool() + { + vkDestroyQueryPool(m_device, m_query_pool, nullptr); + } + + operator VkQueryPool() + { + return m_query_pool; + } + }; +} diff --git a/rpcs3/Emu/RSX/VK/helpers/sampler.cpp b/rpcs3/Emu/RSX/VK/helpers/sampler.cpp new file mode 100644 index 0000000000..fdaa69d960 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/sampler.cpp @@ -0,0 +1,52 @@ +#include "sampler.h" +#include "../../rsx_utils.h" + +namespace vk +{ + sampler::sampler(VkDevice dev, VkSamplerAddressMode clamp_u, VkSamplerAddressMode clamp_v, VkSamplerAddressMode clamp_w, + VkBool32 unnormalized_coordinates, float mipLodBias, float max_anisotropy, float min_lod, float max_lod, + VkFilter min_filter, VkFilter mag_filter, VkSamplerMipmapMode mipmap_mode, VkBorderColor border_color, + VkBool32 depth_compare, VkCompareOp depth_compare_mode) + : m_device(dev) + { + info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + info.addressModeU = clamp_u; + info.addressModeV = clamp_v; + info.addressModeW = clamp_w; + info.anisotropyEnable = VK_TRUE; + info.compareEnable = depth_compare; + info.unnormalizedCoordinates = unnormalized_coordinates; + info.mipLodBias = mipLodBias; + info.maxAnisotropy = max_anisotropy; + info.maxLod = max_lod; + info.minLod = min_lod; + info.magFilter = mag_filter; + info.minFilter = min_filter; + info.mipmapMode = mipmap_mode; + info.compareOp = depth_compare_mode; + info.borderColor = border_color; + + CHECK_RESULT(vkCreateSampler(m_device, &info, nullptr, &value)); + } + + sampler::~sampler() + { + vkDestroySampler(m_device, value, nullptr); + } + + bool sampler::matches(VkSamplerAddressMode clamp_u, VkSamplerAddressMode clamp_v, VkSamplerAddressMode clamp_w, + VkBool32 unnormalized_coordinates, float mipLodBias, float max_anisotropy, float min_lod, float max_lod, + VkFilter min_filter, VkFilter mag_filter, VkSamplerMipmapMode mipmap_mode, VkBorderColor border_color, + VkBool32 depth_compare, VkCompareOp depth_compare_mode) + { + if (info.magFilter != mag_filter || info.minFilter != min_filter || info.mipmapMode != mipmap_mode || + info.addressModeU != clamp_u || info.addressModeV != clamp_v || info.addressModeW != clamp_w || + info.compareEnable != depth_compare || info.unnormalizedCoordinates != unnormalized_coordinates || + !rsx::fcmp(info.maxLod, max_lod) || !rsx::fcmp(info.mipLodBias, mipLodBias) || !rsx::fcmp(info.minLod, min_lod) || + !rsx::fcmp(info.maxAnisotropy, max_anisotropy) || + info.compareOp != depth_compare_mode || info.borderColor != border_color) + return false; + + return true; + } +} diff --git a/rpcs3/Emu/RSX/VK/helpers/sampler.h b/rpcs3/Emu/RSX/VK/helpers/sampler.h new file mode 100644 index 0000000000..f55cea07fd --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/sampler.h @@ -0,0 +1,29 @@ +#pragma once + +#include "shared.h" + +namespace vk +{ + struct sampler + { + VkSampler value; + VkSamplerCreateInfo info = {}; + + sampler(VkDevice dev, VkSamplerAddressMode clamp_u, VkSamplerAddressMode clamp_v, VkSamplerAddressMode clamp_w, + VkBool32 unnormalized_coordinates, float mipLodBias, float max_anisotropy, float min_lod, float max_lod, + VkFilter min_filter, VkFilter mag_filter, VkSamplerMipmapMode mipmap_mode, VkBorderColor border_color, + VkBool32 depth_compare = false, VkCompareOp depth_compare_mode = VK_COMPARE_OP_NEVER); + + ~sampler(); + + bool matches(VkSamplerAddressMode clamp_u, VkSamplerAddressMode clamp_v, VkSamplerAddressMode clamp_w, + VkBool32 unnormalized_coordinates, float mipLodBias, float max_anisotropy, float min_lod, float max_lod, + VkFilter min_filter, VkFilter mag_filter, VkSamplerMipmapMode mipmap_mode, VkBorderColor border_color, + VkBool32 depth_compare = false, VkCompareOp depth_compare_mode = VK_COMPARE_OP_NEVER); + + sampler(const sampler&) = delete; + sampler(sampler&&) = delete; + private: + VkDevice m_device; + }; +} diff --git a/rpcs3/Emu/RSX/VK/helpers/shared.cpp b/rpcs3/Emu/RSX/VK/helpers/shared.cpp new file mode 100644 index 0000000000..fee0bf3d32 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/shared.cpp @@ -0,0 +1,134 @@ +#include "shared.h" +#include "util/logs.hpp" + +namespace vk +{ + void die_with_error(VkResult error_code, + const char* file, + const char* func, + u32 line, + u32 col) + { + std::string error_message; + int severity = 0; // 0 - die, 1 - warn, 2 - nothing + + switch (error_code) + { + case VK_SUCCESS: + case VK_EVENT_SET: + case VK_EVENT_RESET: + case VK_INCOMPLETE: + return; + case VK_SUBOPTIMAL_KHR: + error_message = "Present surface is suboptimal (VK_SUBOPTIMAL_KHR)"; + severity = 1; + break; + case VK_NOT_READY: + error_message = "Device or resource busy (VK_NOT_READY)"; + break; + case VK_TIMEOUT: + error_message = "Timeout event (VK_TIMEOUT)"; + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + error_message = "Out of host memory (system RAM) (VK_ERROR_OUT_OF_HOST_MEMORY)"; + break; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + error_message = "Out of video memory (VRAM) (VK_ERROR_OUT_OF_DEVICE_MEMORY)"; + break; + case VK_ERROR_INITIALIZATION_FAILED: + error_message = "Initialization failed (VK_ERROR_INITIALIZATION_FAILED)"; + break; + case VK_ERROR_DEVICE_LOST: + error_message = "Device lost (Driver crashed with unspecified error or stopped responding and recovered) (VK_ERROR_DEVICE_LOST)"; + break; + case VK_ERROR_MEMORY_MAP_FAILED: + error_message = "Memory map failed (VK_ERROR_MEMORY_MAP_FAILED)"; + break; + case VK_ERROR_LAYER_NOT_PRESENT: + error_message = "Requested layer is not available (Try disabling debug output or install vulkan SDK) (VK_ERROR_LAYER_NOT_PRESENT)"; + break; + case VK_ERROR_EXTENSION_NOT_PRESENT: + error_message = "Requested extension not available (VK_ERROR_EXTENSION_NOT_PRESENT)"; + break; + case VK_ERROR_FEATURE_NOT_PRESENT: + error_message = "Requested feature not available (VK_ERROR_FEATURE_NOT_PRESENT)"; + break; + case VK_ERROR_INCOMPATIBLE_DRIVER: + error_message = "Incompatible driver (VK_ERROR_INCOMPATIBLE_DRIVER)"; + break; + case VK_ERROR_TOO_MANY_OBJECTS: + error_message = "Too many objects created (Out of handles) (VK_ERROR_TOO_MANY_OBJECTS)"; + break; + case VK_ERROR_FORMAT_NOT_SUPPORTED: + error_message = "Format not supported (VK_ERROR_FORMAT_NOT_SUPPORTED)"; + break; + case VK_ERROR_FRAGMENTED_POOL: + error_message = "Fragmented pool (VK_ERROR_FRAGMENTED_POOL)"; + break; + case VK_ERROR_SURFACE_LOST_KHR: + error_message = "Surface lost (VK_ERROR_SURFACE_LOST)"; + break; + case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + error_message = "Native window in use (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)"; + break; + case VK_ERROR_OUT_OF_DATE_KHR: + error_message = "Present surface is out of date (VK_ERROR_OUT_OF_DATE_KHR)"; + severity = 1; + break; + case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: + error_message = "Incompatible display (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)"; + break; + case VK_ERROR_VALIDATION_FAILED_EXT: + error_message = "Validation failed (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)"; + break; + case VK_ERROR_INVALID_SHADER_NV: + error_message = "Invalid shader code (VK_ERROR_INVALID_SHADER_NV)"; + break; + case VK_ERROR_OUT_OF_POOL_MEMORY_KHR: + error_message = "Out of pool memory (VK_ERROR_OUT_OF_POOL_MEMORY_KHR)"; + break; + case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR: + error_message = "Invalid external handle (VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR)"; + break; + default: + error_message = fmt::format("Unknown Code (%Xh, %d)%s", static_cast(error_code), static_cast(error_code), src_loc{line, col, file, func}); + break; + } + + switch (severity) + { + default: + case 0: + fmt::throw_exception("Assertion Failed! Vulkan API call failed with unrecoverable error: %s%s", error_message, src_loc{line, col, file, func}); + case 1: + rsx_log.error("Vulkan API call has failed with an error but will continue: %s%s", error_message, src_loc{line, col, file, func}); + break; + case 2: + break; + } + } + + VKAPI_ATTR VkBool32 VKAPI_CALL dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, + u64 srcObject, usz location, s32 msgCode, + const char* pLayerPrefix, const char* pMsg, void* pUserData) + { + if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + { + if (strstr(pMsg, "IMAGE_VIEW_TYPE_1D")) + return false; + + rsx_log.error("ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, pMsg); + } + else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) + { + rsx_log.warning("WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode, pMsg); + } + else + { + return false; + } + + // Let the app crash.. + return false; + } +} diff --git a/rpcs3/Emu/RSX/VK/helpers/shared.h b/rpcs3/Emu/RSX/VK/helpers/shared.h new file mode 100644 index 0000000000..c3edc0bfa5 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/shared.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../VulkanAPI.h" + +namespace vk +{ +#define CHECK_RESULT(expr) { VkResult _res = (expr); if (_res != VK_SUCCESS) vk::die_with_error(_res); } + + void die_with_error(VkResult error_code, + const char* file = __builtin_FILE(), + const char* func = __builtin_FUNCTION(), + u32 line = __builtin_LINE(), + u32 col = __builtin_COLUMN()); + + VKAPI_ATTR VkBool32 VKAPI_CALL dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, + u64 srcObject, usz location, s32 msgCode, + const char *pLayerPrefix, const char *pMsg, void *pUserData); +} diff --git a/rpcs3/Emu/RSX/VK/helpers/supported_extensions.h b/rpcs3/Emu/RSX/VK/helpers/supported_extensions.h new file mode 100644 index 0000000000..065ae6244b --- /dev/null +++ b/rpcs3/Emu/RSX/VK/helpers/supported_extensions.h @@ -0,0 +1,52 @@ +#pragma once + +#include "../VulkanAPI.h" +#include +#include + +namespace vk +{ + class supported_extensions + { + private: + std::vector m_vk_exts; + + public: + enum enumeration_class + { + instance = 0, + device = 1 + }; + + supported_extensions(enumeration_class _class, const char* layer_name = nullptr, VkPhysicalDevice pdev = VK_NULL_HANDLE) + { + u32 count; + if (_class == enumeration_class::instance) + { + if (vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr) != VK_SUCCESS) + return; + } + else + { + ensure(pdev); + if (vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, nullptr) != VK_SUCCESS) + return; + } + + m_vk_exts.resize(count); + if (_class == enumeration_class::instance) + { + vkEnumerateInstanceExtensionProperties(layer_name, &count, m_vk_exts.data()); + } + else + { + vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, m_vk_exts.data()); + } + } + + bool is_supported(const char* ext) + { + return std::any_of(m_vk_exts.cbegin(), m_vk_exts.cend(), [&](const VkExtensionProperties& p) { return std::strcmp(p.extensionName, ext) == 0; }); + } + }; +} diff --git a/rpcs3/VKGSRender.vcxproj b/rpcs3/VKGSRender.vcxproj index 74c4a14543..f2fe8a21e7 100644 --- a/rpcs3/VKGSRender.vcxproj +++ b/rpcs3/VKGSRender.vcxproj @@ -19,6 +19,17 @@ + + + + + + + + + + + @@ -31,6 +42,7 @@ + @@ -43,6 +55,13 @@ + + + + + + + diff --git a/rpcs3/VKGSRender.vcxproj.filters b/rpcs3/VKGSRender.vcxproj.filters index 8ce096bb50..0ebc0e962d 100644 --- a/rpcs3/VKGSRender.vcxproj.filters +++ b/rpcs3/VKGSRender.vcxproj.filters @@ -23,6 +23,27 @@ + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + @@ -47,5 +68,44 @@ + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + helpers + + + + + {2c6cb5a5-ed99-44fe-a0b6-7ba1949c8b29} + \ No newline at end of file