VK: refactoring part 1

This commit is contained in:
Megamouse 2020-12-24 11:49:08 +01:00 committed by Ivan
parent f8589de476
commit d9eb31000d
42 changed files with 1849 additions and 1509 deletions

View File

@ -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

View File

@ -2,6 +2,7 @@
#include "util/types.hpp"
#include "Utilities/address_range.h"
#include "gcm_enums.h"
#include <vector>

View File

@ -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

View File

@ -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

View File

@ -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<u32, vk::buffer*> 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);

View File

@ -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)

View File

@ -1,9 +1,13 @@
#pragma once
#include "VKHelpers.h"
#include "VulkanAPI.h"
#include "../gcm_enums.h"
#include <tuple>
namespace vk
{
class image;
struct gpu_formats_support;
struct minification_filter
{
VkFilter filter;

View File

@ -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
{

View File

@ -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<s32>(error_code), static_cast<s32>(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)

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
#include "VKResourceManager.h"
#include "VKRenderPass.h"
#include "VKPipelineCompiler.h"
#include "helpers/sampler.h"
#include "../Overlays/overlays.h"

View File

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "VKPipelineCompiler.h"
#include "VKRenderPass.h"
#include "VKHelpers.h"
#include "Utilities/Thread.h"
#include <thread>

View File

@ -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;

View File

@ -3,7 +3,6 @@
#include "VKFragmentProgram.h"
#include "../Common/ProgramStateCache.h"
#include "Utilities/hash.h"
#include "VKHelpers.h"
#include "VKRenderPass.h"
#include "VKPipelineCompiler.h"

View File

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "VKProgramPipeline.h"
#include "VKHelpers.h"
#include <string>
@ -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<u32> shader::get_compiled() const
{
return m_compiled;
}
VkShaderModule shader::get_handle() const
{
return m_handle;
}
void program::create_impl()
{
linked = false;

View File

@ -0,0 +1,112 @@
#pragma once
#include "VulkanAPI.h"
#include "VKCommonDecompiler.h"
#include "../Common/GLSLTypes.h"
#include <string>
#include <vector>
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<u32> 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<u32> get_compiled() const;
VkShaderModule get_handle() const;
};
class program
{
std::array<std::vector<program_input>, input_type_max_enum> uniforms;
VkDevice m_device;
std::array<u32, 16> fs_texture_bindings;
std::array<u32, 16> fs_texture_mirror_bindings;
std::array<u32, 4> 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<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs);
program(VkDevice dev, VkPipeline p, VkPipelineLayout layout);
program(const program&) = delete;
program(program&& other) = delete;
~program();
program& load_uniforms(const std::vector<program_input>& 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);
};
}
}

View File

@ -1,8 +1,13 @@
#pragma once
#include "VKHelpers.h"
#include "VulkanAPI.h"
#include <deque>
namespace vk
{
class command_buffer;
class query_pool;
class render_device;
class query_pool_manager
{
struct query_slot_info

View File

@ -2,6 +2,7 @@
#include "Utilities/mutex.h"
#include "VKRenderPass.h"
#include "VKHelpers.h"
namespace vk
{

View File

@ -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<vk::image*>& images);
u64 get_renderpass_key(const std::vector<vk::image*>& images, u64 previous_key);
u64 get_renderpass_key(VkFormat surface_format);

View File

@ -1,5 +1,7 @@
#pragma once
#include "VKHelpers.h"
#include "helpers/query_pool.h"
#include "helpers/sampler.h"
#include <unordered_map>
#include <deque>

View File

@ -1,5 +1,6 @@
#pragma once
#include "VKProgramBuffer.h"
#include "VKHelpers.h"
#include <unordered_map>
namespace vk

View File

@ -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
{

View File

@ -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_;
}
}

View File

@ -0,0 +1,98 @@
#pragma once
#include "util/types.hpp"
#include <unordered_map>
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<u32, chip_class> 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);
}

View File

@ -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);
}
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "../VulkanAPI.h"
#include "util/atomic.hpp"
namespace vk
{
struct fence
{
atomic_t<bool> 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;
};
}

View File

@ -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<VkSampleCountFlagBits>(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;
}
};
}

View File

@ -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<VmaAllocation>(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<VmaAllocation>(mem_handle), &data));
// Add offset
data = static_cast<u8 *>(data) + offset;
return data;
}
void mem_allocator_vma::unmap(mem_handle_t mem_handle)
{
vmaUnmapMemory(m_allocator, static_cast<VmaAllocation>(mem_handle));
}
VkDeviceMemory mem_allocator_vma::get_vk_device_memory(mem_handle_t mem_handle)
{
VmaAllocationInfo alloc_info;
vmaGetAllocationInfo(m_allocator, static_cast<VmaAllocation>(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<VmaAllocation>(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<VkDeviceMemory>(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<VkDeviceMemory>(mem_handle), offset, std::max<u64>(size, 1u), 0, &data));
return data;
}
void mem_allocator_vk::unmap(mem_handle_t mem_handle)
{
vkUnmapMemory(m_device, static_cast<VkDeviceMemory>(mem_handle));
}
VkDeviceMemory mem_allocator_vk::get_vk_device_memory(mem_handle_t mem_handle)
{
return static_cast<VkDeviceMemory>(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();
}
}

View File

@ -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<VmaBudget, VK_MAX_MEMORY_HEAPS> 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();
}

View File

@ -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);
}
}

View File

@ -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;
};
}

View File

@ -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<PFN_vkGetPhysicalDeviceFeatures2KHR>(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;
}
}

View File

@ -0,0 +1,80 @@
#pragma once
#include "../VulkanAPI.h"
#include "chip_class.h"
#include "pipeline_binding_table.h"
#include <vector>
#include <unordered_map>
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<VkQueueFamilyProperties> queue_props;
std::unordered_map<VkFormat, VkFormatProperties> 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);
}

View File

@ -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
};
}

View File

@ -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<s32>(size));
}
~query_pool()
{
vkDestroyQueryPool(m_device, m_query_pool, nullptr);
}
operator VkQueryPool()
{
return m_query_pool;
}
};
}

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -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<s32>(error_code), static_cast<s32>(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;
}
}

View File

@ -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);
}

View File

@ -0,0 +1,52 @@
#pragma once
#include "../VulkanAPI.h"
#include <algorithm>
#include <vector>
namespace vk
{
class supported_extensions
{
private:
std::vector<VkExtensionProperties> 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; });
}
};
}

View File

@ -19,6 +19,17 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\VK\helpers\chip_class.h" />
<ClInclude Include="Emu\RSX\VK\helpers\fence.h" />
<ClInclude Include="Emu\RSX\VK\helpers\graphics_pipeline_state.h" />
<ClInclude Include="Emu\RSX\VK\helpers\memory_block.h" />
<ClInclude Include="Emu\RSX\VK\helpers\mem_allocator.h" />
<ClInclude Include="Emu\RSX\VK\helpers\physical_device.h" />
<ClInclude Include="Emu\RSX\VK\helpers\pipeline_binding_table.h" />
<ClInclude Include="Emu\RSX\VK\helpers\query_pool.h" />
<ClInclude Include="Emu\RSX\VK\helpers\sampler.h" />
<ClInclude Include="Emu\RSX\VK\helpers\shared.h" />
<ClInclude Include="Emu\RSX\VK\helpers\supported_extensions.h" />
<ClInclude Include="Emu\RSX\VK\VKCommandStream.h" />
<ClInclude Include="Emu\RSX\VK\VKCommonDecompiler.h" />
<ClInclude Include="Emu\RSX\VK\VKCompute.h" />
@ -31,6 +42,7 @@
<ClInclude Include="Emu\RSX\VK\VKOverlays.h" />
<ClInclude Include="Emu\RSX\VK\VKPipelineCompiler.h" />
<ClInclude Include="Emu\RSX\VK\VKProgramBuffer.h" />
<ClInclude Include="Emu\RSX\VK\VKProgramPipeline.h" />
<ClInclude Include="Emu\RSX\VK\VKQueryPool.h" />
<ClInclude Include="Emu\RSX\VK\VKRenderPass.h" />
<ClInclude Include="Emu\RSX\VK\VKRenderTargets.h" />
@ -43,6 +55,13 @@
<ClInclude Include="Emu\RSX\VK\VulkanAPI.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Emu\RSX\VK\helpers\chip_class.cpp" />
<ClCompile Include="Emu\RSX\VK\helpers\fence.cpp" />
<ClCompile Include="Emu\RSX\VK\helpers\memory_block.cpp" />
<ClCompile Include="Emu\RSX\VK\helpers\mem_allocator.cpp" />
<ClCompile Include="Emu\RSX\VK\helpers\physical_device.cpp" />
<ClCompile Include="Emu\RSX\VK\helpers\sampler.cpp" />
<ClCompile Include="Emu\RSX\VK\helpers\shared.cpp" />
<ClCompile Include="Emu\RSX\VK\VKCommandStream.cpp" />
<ClCompile Include="Emu\RSX\VK\VKCommonDecompiler.cpp" />
<ClCompile Include="Emu\RSX\VK\VKDMA.cpp" />

View File

@ -23,6 +23,27 @@
<ClCompile Include="Emu\RSX\VK\VKCommandStream.cpp" />
<ClCompile Include="Emu\RSX\VK\VKQueryPool.cpp" />
<ClCompile Include="Emu\RSX\VK\VKPipelineCompiler.cpp" />
<ClCompile Include="Emu\RSX\VK\helpers\chip_class.cpp">
<Filter>helpers</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\helpers\fence.cpp">
<Filter>helpers</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\helpers\mem_allocator.cpp">
<Filter>helpers</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\helpers\memory_block.cpp">
<Filter>helpers</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\helpers\physical_device.cpp">
<Filter>helpers</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\helpers\sampler.cpp">
<Filter>helpers</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\helpers\shared.cpp">
<Filter>helpers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\VK\VKCommonDecompiler.h" />
@ -47,5 +68,44 @@
<ClInclude Include="Emu\RSX\VK\VKCommandStream.h" />
<ClInclude Include="Emu\RSX\VK\VKQueryPool.h" />
<ClInclude Include="Emu\RSX\VK\VKPipelineCompiler.h" />
<ClInclude Include="Emu\RSX\VK\VKProgramPipeline.h" />
<ClInclude Include="Emu\RSX\VK\helpers\chip_class.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\fence.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\graphics_pipeline_state.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\mem_allocator.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\memory_block.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\physical_device.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\pipeline_binding_table.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\query_pool.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\sampler.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\shared.h">
<Filter>helpers</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\helpers\supported_extensions.h">
<Filter>helpers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="helpers">
<UniqueIdentifier>{2c6cb5a5-ed99-44fe-a0b6-7ba1949c8b29}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>