2016-02-16 20:23:17 +01:00
# include "stdafx.h"
2016-02-21 18:50:49 +03:00
# include "Emu/Memory/Memory.h"
# include "Emu/System.h"
2016-02-16 20:23:17 +01:00
# include "VKGSRender.h"
2016-02-21 18:50:49 +03:00
# include "../rsx_methods.h"
2017-01-08 10:34:10 +03:00
# include "../rsx_utils.h"
2016-02-21 18:50:49 +03:00
# include "../Common/BufferUtils.h"
2016-03-12 01:13:30 +01:00
# include "VKFormats.h"
2017-06-15 02:33:50 +03:00
# include "VKCommonDecompiler.h"
2016-02-16 20:23:17 +01:00
2016-02-21 18:50:49 +03:00
namespace
2016-02-16 20:23:17 +01:00
{
2016-02-21 18:50:49 +03:00
u32 get_max_depth_value ( rsx : : surface_depth_format format )
{
switch ( format )
{
case rsx : : surface_depth_format : : z16 : return 0xFFFF ;
case rsx : : surface_depth_format : : z24s8 : return 0xFFFFFF ;
}
2016-08-08 19:01:06 +03:00
fmt : : throw_exception ( " Unknown depth format " HERE ) ;
2016-02-21 18:50:49 +03:00
}
2016-02-16 20:23:17 +01:00
2016-02-21 18:50:49 +03:00
u8 get_pixel_size ( rsx : : surface_depth_format format )
{
switch ( format )
{
case rsx : : surface_depth_format : : z16 : return 2 ;
case rsx : : surface_depth_format : : z24s8 : return 4 ;
}
2016-08-08 19:01:06 +03:00
fmt : : throw_exception ( " Unknown depth format " HERE ) ;
2016-02-21 18:50:49 +03:00
}
2016-02-16 20:23:17 +01:00
}
2016-02-21 18:50:49 +03:00
namespace vk
{
2017-06-12 12:42:30 +03:00
VkCompareOp get_compare_func ( rsx : : comparison_function op , bool reverse_direction = false )
2016-02-21 18:50:49 +03:00
{
2016-06-26 23:37:02 +02:00
switch ( op )
2016-02-21 18:50:49 +03:00
{
2016-07-21 13:35:06 +08:00
case rsx : : comparison_function : : never : return VK_COMPARE_OP_NEVER ;
2017-06-12 12:42:30 +03:00
case rsx : : comparison_function : : greater : return reverse_direction ? VK_COMPARE_OP_LESS : VK_COMPARE_OP_GREATER ;
case rsx : : comparison_function : : less : return reverse_direction ? VK_COMPARE_OP_GREATER : VK_COMPARE_OP_LESS ;
case rsx : : comparison_function : : less_or_equal : return reverse_direction ? VK_COMPARE_OP_GREATER_OR_EQUAL : VK_COMPARE_OP_LESS_OR_EQUAL ;
case rsx : : comparison_function : : greater_or_equal : return reverse_direction ? VK_COMPARE_OP_LESS_OR_EQUAL : VK_COMPARE_OP_GREATER_OR_EQUAL ;
2016-07-21 13:35:06 +08:00
case rsx : : comparison_function : : equal : return VK_COMPARE_OP_EQUAL ;
case rsx : : comparison_function : : not_equal : return VK_COMPARE_OP_NOT_EQUAL ;
case rsx : : comparison_function : : always : return VK_COMPARE_OP_ALWAYS ;
2016-02-21 18:50:49 +03:00
default :
2016-08-08 19:01:06 +03:00
fmt : : throw_exception ( " Unknown compare op: 0x%x " HERE , ( u32 ) op ) ;
2016-02-21 18:50:49 +03:00
}
}
2016-05-30 17:28:16 +03:00
std : : pair < VkFormat , VkComponentMapping > get_compatible_surface_format ( rsx : : surface_color_format color_format )
2016-02-21 18:50:49 +03:00
{
switch ( color_format )
{
case rsx : : surface_color_format : : r5g6b5 :
2016-05-30 17:28:16 +03:00
return std : : make_pair ( VK_FORMAT_R5G6B5_UNORM_PACK16 , vk : : default_component_map ( ) ) ;
2016-02-21 18:50:49 +03:00
case rsx : : surface_color_format : : a8r8g8b8 :
2017-04-21 16:35:13 +03:00
case rsx : : surface_color_format : : a8b8g8r8 :
2016-05-30 17:28:16 +03:00
return std : : make_pair ( VK_FORMAT_B8G8R8A8_UNORM , vk : : default_component_map ( ) ) ;
2016-02-21 18:50:49 +03:00
2016-05-30 17:28:16 +03:00
case rsx : : surface_color_format : : x8b8g8r8_o8b8g8r8 :
case rsx : : surface_color_format : : x8b8g8r8_z8b8g8r8 :
case rsx : : surface_color_format : : x8r8g8b8_z8r8g8b8 :
2016-02-21 18:50:49 +03:00
case rsx : : surface_color_format : : x8r8g8b8_o8r8g8b8 :
2016-05-30 17:28:16 +03:00
{
VkComponentMapping no_alpha = { VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_G , VK_COMPONENT_SWIZZLE_B , VK_COMPONENT_SWIZZLE_ONE } ;
return std : : make_pair ( VK_FORMAT_B8G8R8A8_UNORM , no_alpha ) ;
}
2016-02-21 18:50:49 +03:00
case rsx : : surface_color_format : : w16z16y16x16 :
2016-05-30 17:28:16 +03:00
return std : : make_pair ( VK_FORMAT_R16G16B16A16_SFLOAT , vk : : default_component_map ( ) ) ;
2016-02-21 18:50:49 +03:00
case rsx : : surface_color_format : : w32z32y32x32 :
2016-05-30 17:28:16 +03:00
return std : : make_pair ( VK_FORMAT_R32G32B32A32_SFLOAT , vk : : default_component_map ( ) ) ;
2016-02-21 18:50:49 +03:00
case rsx : : surface_color_format : : x1r5g5b5_o1r5g5b5 :
case rsx : : surface_color_format : : x1r5g5b5_z1r5g5b5 :
2016-05-30 17:28:16 +03:00
{
VkComponentMapping no_alpha = { VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_G , VK_COMPONENT_SWIZZLE_B , VK_COMPONENT_SWIZZLE_ONE } ;
return std : : make_pair ( VK_FORMAT_B8G8R8A8_UNORM , no_alpha ) ;
}
case rsx : : surface_color_format : : b8 :
return std : : make_pair ( VK_FORMAT_R8_UNORM , vk : : default_component_map ( ) ) ;
2016-02-21 18:50:49 +03:00
case rsx : : surface_color_format : : g8b8 :
2016-05-30 17:28:16 +03:00
return std : : make_pair ( VK_FORMAT_R8G8_UNORM , vk : : default_component_map ( ) ) ;
2016-02-21 18:50:49 +03:00
case rsx : : surface_color_format : : x32 :
2016-05-30 17:28:16 +03:00
return std : : make_pair ( VK_FORMAT_R32_SFLOAT , vk : : default_component_map ( ) ) ;
2016-02-21 18:50:49 +03:00
default :
2016-08-03 23:51:05 +03:00
LOG_ERROR ( RSX , " Surface color buffer: Unsupported surface color format (0x%x) " , ( u32 ) color_format ) ;
2016-05-30 17:28:16 +03:00
return std : : make_pair ( VK_FORMAT_B8G8R8A8_UNORM , vk : : default_component_map ( ) ) ;
2016-02-21 18:50:49 +03:00
}
}
2016-03-17 21:31:34 +01:00
/** Maps color_format, depth_stencil_format and color count to an int as below :
* idx = color_count + 5 * depth_stencil_idx + 15 * color_format_idx
* This should perform a 1 : 1 mapping
*/
size_t get_render_pass_location ( VkFormat color_format , VkFormat depth_stencil_format , u8 color_count )
{
size_t color_format_idx = 0 ;
size_t depth_format_idx = 0 ;
2016-08-15 16:29:38 +03:00
verify ( HERE ) , color_count < 5 ;
2016-03-17 21:31:34 +01:00
switch ( color_format )
{
case VK_FORMAT_R5G6B5_UNORM_PACK16 :
color_format_idx = 0 ;
break ;
case VK_FORMAT_B8G8R8A8_UNORM :
color_format_idx = 1 ;
break ;
case VK_FORMAT_R16G16B16A16_SFLOAT :
color_format_idx = 2 ;
break ;
case VK_FORMAT_R32G32B32A32_SFLOAT :
color_format_idx = 3 ;
break ;
2017-06-14 01:38:39 +03:00
case VK_FORMAT_R8_UNORM :
2016-03-17 21:31:34 +01:00
color_format_idx = 4 ;
break ;
2017-06-14 01:38:39 +03:00
case VK_FORMAT_R8G8_UNORM :
2016-03-17 21:31:34 +01:00
color_format_idx = 5 ;
break ;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16 :
color_format_idx = 6 ;
break ;
case VK_FORMAT_R32_SFLOAT :
color_format_idx = 7 ;
break ;
}
switch ( depth_stencil_format )
{
case VK_FORMAT_D16_UNORM :
depth_format_idx = 0 ;
break ;
case VK_FORMAT_D24_UNORM_S8_UINT :
case VK_FORMAT_D32_SFLOAT_S8_UINT :
depth_format_idx = 1 ;
break ;
case VK_FORMAT_UNDEFINED :
depth_format_idx = 2 ;
break ;
}
return color_count + 5 * depth_format_idx + 5 * 3 * color_format_idx ;
}
2016-02-21 18:50:49 +03:00
std : : vector < u8 > get_draw_buffers ( rsx : : surface_target fmt )
{
switch ( fmt )
{
case rsx : : surface_target : : none :
return { } ;
case rsx : : surface_target : : surface_a :
return { 0 } ;
case rsx : : surface_target : : surface_b :
return { 1 } ;
case rsx : : surface_target : : surfaces_a_b :
return { 0 , 1 } ;
case rsx : : surface_target : : surfaces_a_b_c :
return { 0 , 1 , 2 } ;
case rsx : : surface_target : : surfaces_a_b_c_d :
return { 0 , 1 , 2 , 3 } ;
default :
2016-08-03 23:51:05 +03:00
LOG_ERROR ( RSX , " Bad surface color target: %d " , ( u32 ) fmt ) ;
2016-02-21 18:50:49 +03:00
return { } ;
}
}
2016-06-26 23:37:02 +02:00
VkLogicOp get_logic_op ( rsx : : logic_op op )
2016-06-13 19:10:59 +08:00
{
switch ( op )
{
2016-06-26 23:37:02 +02:00
case rsx : : logic_op : : logic_clear : return VK_LOGIC_OP_CLEAR ;
case rsx : : logic_op : : logic_and : return VK_LOGIC_OP_AND ;
case rsx : : logic_op : : logic_and_reverse : return VK_LOGIC_OP_AND_REVERSE ;
case rsx : : logic_op : : logic_copy : return VK_LOGIC_OP_COPY ;
case rsx : : logic_op : : logic_and_inverted : return VK_LOGIC_OP_AND_INVERTED ;
case rsx : : logic_op : : logic_noop : return VK_LOGIC_OP_NO_OP ;
case rsx : : logic_op : : logic_xor : return VK_LOGIC_OP_XOR ;
case rsx : : logic_op : : logic_or : return VK_LOGIC_OP_OR ;
case rsx : : logic_op : : logic_nor : return VK_LOGIC_OP_NOR ;
case rsx : : logic_op : : logic_equiv : return VK_LOGIC_OP_EQUIVALENT ;
case rsx : : logic_op : : logic_invert : return VK_LOGIC_OP_INVERT ;
case rsx : : logic_op : : logic_or_reverse : return VK_LOGIC_OP_OR_REVERSE ;
case rsx : : logic_op : : logic_copy_inverted : return VK_LOGIC_OP_COPY_INVERTED ;
case rsx : : logic_op : : logic_or_inverted : return VK_LOGIC_OP_OR_INVERTED ;
case rsx : : logic_op : : logic_nand : return VK_LOGIC_OP_NAND ;
2017-01-27 16:52:43 -06:00
case rsx : : logic_op : : logic_set : return VK_LOGIC_OP_SET ;
2016-06-13 19:10:59 +08:00
default :
2016-08-08 19:01:06 +03:00
fmt : : throw_exception ( " Unknown logic op 0x%x " HERE , ( u32 ) op ) ;
2016-06-13 19:10:59 +08:00
}
}
2016-06-26 23:37:02 +02:00
VkBlendFactor get_blend_factor ( rsx : : blend_factor factor )
2016-02-21 18:50:49 +03:00
{
switch ( factor )
{
2016-06-26 23:37:02 +02:00
case rsx : : blend_factor : : one : return VK_BLEND_FACTOR_ONE ;
case rsx : : blend_factor : : zero : return VK_BLEND_FACTOR_ZERO ;
case rsx : : blend_factor : : src_alpha : return VK_BLEND_FACTOR_SRC_ALPHA ;
case rsx : : blend_factor : : dst_alpha : return VK_BLEND_FACTOR_DST_ALPHA ;
case rsx : : blend_factor : : src_color : return VK_BLEND_FACTOR_SRC_COLOR ;
case rsx : : blend_factor : : dst_color : return VK_BLEND_FACTOR_DST_COLOR ;
case rsx : : blend_factor : : constant_color : return VK_BLEND_FACTOR_CONSTANT_COLOR ;
case rsx : : blend_factor : : constant_alpha : return VK_BLEND_FACTOR_CONSTANT_ALPHA ;
case rsx : : blend_factor : : one_minus_src_color : return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR ;
case rsx : : blend_factor : : one_minus_dst_color : return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR ;
case rsx : : blend_factor : : one_minus_src_alpha : return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA ;
case rsx : : blend_factor : : one_minus_dst_alpha : return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA ;
case rsx : : blend_factor : : one_minus_constant_alpha : return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA ;
case rsx : : blend_factor : : one_minus_constant_color : return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR ;
2017-04-22 22:35:36 +08:00
case rsx : : blend_factor : : src_alpha_saturate : return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE ;
2016-02-21 18:50:49 +03:00
default :
2016-08-08 19:01:06 +03:00
fmt : : throw_exception ( " Unknown blend factor 0x%x " HERE , ( u32 ) factor ) ;
2016-02-21 18:50:49 +03:00
}
} ;
2016-06-26 23:37:02 +02:00
VkBlendOp get_blend_op ( rsx : : blend_equation op )
2016-02-21 18:50:49 +03:00
{
switch ( op )
{
2017-06-30 04:06:02 +02:00
case rsx : : blend_equation : : add :
case rsx : : blend_equation : : add_signed : return VK_BLEND_OP_ADD ;
2016-06-26 23:37:02 +02:00
case rsx : : blend_equation : : substract : return VK_BLEND_OP_SUBTRACT ;
case rsx : : blend_equation : : reverse_substract : return VK_BLEND_OP_REVERSE_SUBTRACT ;
case rsx : : blend_equation : : min : return VK_BLEND_OP_MIN ;
case rsx : : blend_equation : : max : return VK_BLEND_OP_MAX ;
2016-02-21 18:50:49 +03:00
default :
2016-08-08 19:01:06 +03:00
fmt : : throw_exception ( " Unknown blend op: 0x%x " HERE , ( u32 ) op ) ;
2016-02-21 18:50:49 +03:00
}
}
2016-06-13 19:10:59 +08:00
2016-06-26 23:37:02 +02:00
VkStencilOp get_stencil_op ( rsx : : stencil_op op )
2016-06-13 19:10:59 +08:00
{
switch ( op )
{
2016-06-26 23:37:02 +02:00
case rsx : : stencil_op : : keep : return VK_STENCIL_OP_KEEP ;
case rsx : : stencil_op : : zero : return VK_STENCIL_OP_ZERO ;
case rsx : : stencil_op : : replace : return VK_STENCIL_OP_REPLACE ;
case rsx : : stencil_op : : incr : return VK_STENCIL_OP_INCREMENT_AND_CLAMP ;
case rsx : : stencil_op : : decr : return VK_STENCIL_OP_DECREMENT_AND_CLAMP ;
case rsx : : stencil_op : : invert : return VK_STENCIL_OP_INVERT ;
case rsx : : stencil_op : : incr_wrap : return VK_STENCIL_OP_INCREMENT_AND_WRAP ;
case rsx : : stencil_op : : decr_wrap : return VK_STENCIL_OP_DECREMENT_AND_WRAP ;
2016-06-13 19:10:59 +08:00
default :
2016-08-08 19:01:06 +03:00
fmt : : throw_exception ( " Unknown stencil op: 0x%x " HERE , ( u32 ) op ) ;
2016-06-13 19:10:59 +08:00
}
}
2016-06-26 23:37:02 +02:00
2016-07-17 19:57:50 +03:00
VkFrontFace get_front_face ( rsx : : front_face ffv )
2016-06-19 09:53:49 +08:00
{
switch ( ffv )
2016-06-30 12:46:25 +08:00
{
2016-07-17 22:55:59 +03:00
case rsx : : front_face : : cw : return VK_FRONT_FACE_CLOCKWISE ;
case rsx : : front_face : : ccw : return VK_FRONT_FACE_COUNTER_CLOCKWISE ;
2016-06-26 23:37:02 +02:00
default :
2016-08-08 19:01:06 +03:00
fmt : : throw_exception ( " Unknown front face value: 0x%x " HERE , ( u32 ) ffv ) ;
2016-06-19 09:53:49 +08:00
}
}
2016-07-03 10:35:51 +08:00
2016-09-06 09:43:27 +08:00
VkCullModeFlags get_cull_face ( rsx : : cull_face cfv )
2016-06-26 23:37:02 +02:00
{
switch ( cfv )
{
2016-09-06 09:43:27 +08:00
case rsx : : cull_face : : back : return VK_CULL_MODE_BACK_BIT ;
case rsx : : cull_face : : front : return VK_CULL_MODE_FRONT_BIT ;
case rsx : : cull_face : : front_and_back : return VK_CULL_MODE_FRONT_AND_BACK ;
default :
fmt : : throw_exception ( " Unknown cull face value: 0x%x " HERE , ( u32 ) cfv ) ;
2016-06-26 23:37:02 +02:00
}
2016-07-03 10:35:51 +08:00
}
2016-02-21 18:50:49 +03:00
}
2016-03-17 21:31:34 +01:00
namespace
{
VkRenderPass precompute_render_pass ( VkDevice dev , VkFormat color_format , u8 number_of_color_surface , VkFormat depth_format )
{
2016-03-18 23:30:58 +01:00
// Some driver crashes when using empty render pass
if ( number_of_color_surface = = 0 & & depth_format = = VK_FORMAT_UNDEFINED )
return nullptr ;
2016-03-17 21:31:34 +01:00
/* Describe a render pass and framebuffer attachments */
std : : vector < VkAttachmentDescription > attachments = { } ;
std : : vector < VkAttachmentReference > attachment_references ;
VkAttachmentDescription color_attachement_description = { } ;
color_attachement_description . format = color_format ;
color_attachement_description . samples = VK_SAMPLE_COUNT_1_BIT ;
color_attachement_description . loadOp = VK_ATTACHMENT_LOAD_OP_LOAD ;
color_attachement_description . storeOp = VK_ATTACHMENT_STORE_OP_STORE ;
color_attachement_description . stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE ;
color_attachement_description . stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE ;
color_attachement_description . initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ;
color_attachement_description . finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ;
2016-07-17 19:57:50 +03:00
2016-03-17 21:31:34 +01:00
for ( u32 i = 0 ; i < number_of_color_surface ; + + i )
{
attachments . push_back ( color_attachement_description ) ;
attachment_references . push_back ( { i , VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL } ) ;
}
if ( depth_format ! = VK_FORMAT_UNDEFINED )
{
VkAttachmentDescription depth_attachement_description = { } ;
depth_attachement_description . format = depth_format ;
depth_attachement_description . samples = VK_SAMPLE_COUNT_1_BIT ;
depth_attachement_description . loadOp = VK_ATTACHMENT_LOAD_OP_LOAD ;
depth_attachement_description . storeOp = VK_ATTACHMENT_STORE_OP_STORE ;
depth_attachement_description . stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD ;
depth_attachement_description . stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE ;
depth_attachement_description . initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ;
depth_attachement_description . finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ;
attachments . push_back ( depth_attachement_description ) ;
attachment_references . push_back ( { number_of_color_surface , VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL } ) ;
}
VkSubpassDescription subpass = { } ;
subpass . pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS ;
subpass . colorAttachmentCount = number_of_color_surface ;
subpass . pColorAttachments = number_of_color_surface > 0 ? attachment_references . data ( ) : nullptr ;
subpass . pDepthStencilAttachment = depth_format ! = VK_FORMAT_UNDEFINED ? & attachment_references . back ( ) : nullptr ;
2016-09-28 17:12:52 +03:00
VkSubpassDependency dependency = { } ;
dependency . srcSubpass = VK_SUBPASS_EXTERNAL ;
dependency . srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ;
dependency . srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
dependency . dstSubpass = 0 ;
dependency . dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ;
dependency . dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
dependency . dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT ;
2016-03-17 21:31:34 +01:00
VkRenderPassCreateInfo rp_info = { } ;
rp_info . sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO ;
2016-03-30 20:23:40 +02:00
rp_info . attachmentCount = static_cast < uint32_t > ( attachments . size ( ) ) ;
2016-03-17 21:31:34 +01:00
rp_info . pAttachments = attachments . data ( ) ;
rp_info . subpassCount = 1 ;
rp_info . pSubpasses = & subpass ;
2016-09-28 17:12:52 +03:00
rp_info . pDependencies = & dependency ;
rp_info . dependencyCount = 1 ;
2016-03-17 21:31:34 +01:00
VkRenderPass result ;
CHECK_RESULT ( vkCreateRenderPass ( dev , & rp_info , NULL , & result ) ) ;
return result ;
}
std : : array < VkRenderPass , 120 > get_precomputed_render_passes ( VkDevice dev , const vk : : gpu_formats_support & gpu_format_support )
{
std : : array < VkRenderPass , 120 > result = { } ;
const std : : array < VkFormat , 3 > depth_format_list = { VK_FORMAT_UNDEFINED , VK_FORMAT_D16_UNORM , gpu_format_support . d24_unorm_s8 ? VK_FORMAT_D24_UNORM_S8_UINT : VK_FORMAT_D32_SFLOAT_S8_UINT } ;
2017-06-14 01:38:39 +03:00
const std : : array < VkFormat , 8 > color_format_list = { VK_FORMAT_R5G6B5_UNORM_PACK16 , VK_FORMAT_B8G8R8A8_UNORM , VK_FORMAT_R16G16B16A16_SFLOAT , VK_FORMAT_R32G32B32A32_SFLOAT , VK_FORMAT_R8_UNORM , VK_FORMAT_R8G8_UNORM , VK_FORMAT_A1R5G5B5_UNORM_PACK16 , VK_FORMAT_R32_SFLOAT } ;
2016-03-17 21:31:34 +01:00
for ( const VkFormat & color_format : color_format_list )
{
for ( const VkFormat & depth_stencil_format : depth_format_list )
{
for ( u8 number_of_draw_buffer = 0 ; number_of_draw_buffer < = 4 ; number_of_draw_buffer + + )
{
size_t idx = vk : : get_render_pass_location ( color_format , depth_stencil_format , number_of_draw_buffer ) ;
result [ idx ] = precompute_render_pass ( dev , color_format , number_of_draw_buffer , depth_stencil_format ) ;
}
}
}
return result ;
}
2016-03-17 23:57:53 +01:00
std : : tuple < VkPipelineLayout , VkDescriptorSetLayout > get_shared_pipeline_layout ( VkDevice dev )
{
2016-09-20 17:23:56 +03:00
std : : array < VkDescriptorSetLayoutBinding , 39 > bindings = { } ;
2016-03-17 23:57:53 +01:00
size_t idx = 0 ;
// Vertex buffer
for ( int i = 0 ; i < 16 ; i + + )
{
bindings [ idx ] . descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ;
bindings [ idx ] . descriptorCount = 1 ;
bindings [ idx ] . stageFlags = VK_SHADER_STAGE_VERTEX_BIT ;
bindings [ idx ] . binding = VERTEX_BUFFERS_FIRST_BIND_SLOT + i ;
idx + + ;
}
bindings [ idx ] . descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ;
bindings [ idx ] . descriptorCount = 1 ;
bindings [ idx ] . stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT ;
bindings [ idx ] . binding = FRAGMENT_CONSTANT_BUFFERS_BIND_SLOT ;
idx + + ;
bindings [ idx ] . descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ;
bindings [ idx ] . descriptorCount = 1 ;
bindings [ idx ] . stageFlags = VK_SHADER_STAGE_VERTEX_BIT ;
bindings [ idx ] . binding = VERTEX_CONSTANT_BUFFERS_BIND_SLOT ;
idx + + ;
for ( int i = 0 ; i < 16 ; i + + )
{
bindings [ idx ] . descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ;
bindings [ idx ] . descriptorCount = 1 ;
bindings [ idx ] . stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT ;
bindings [ idx ] . binding = TEXTURES_FIRST_BIND_SLOT + i ;
idx + + ;
}
2016-09-20 17:23:56 +03:00
for ( int i = 0 ; i < 4 ; i + + )
{
bindings [ idx ] . descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ;
bindings [ idx ] . descriptorCount = 1 ;
bindings [ idx ] . stageFlags = VK_SHADER_STAGE_VERTEX_BIT ;
bindings [ idx ] . binding = VERTEX_TEXTURES_FIRST_BIND_SLOT + i ;
idx + + ;
}
2016-03-17 23:57:53 +01:00
bindings [ idx ] . descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ;
bindings [ idx ] . descriptorCount = 1 ;
bindings [ idx ] . stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS ;
bindings [ idx ] . binding = SCALE_OFFSET_BIND_SLOT ;
VkDescriptorSetLayoutCreateInfo infos = { } ;
infos . sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO ;
infos . pBindings = bindings . data ( ) ;
2016-03-30 20:23:40 +02:00
infos . bindingCount = static_cast < uint32_t > ( bindings . size ( ) ) ;
2016-03-17 23:57:53 +01:00
VkDescriptorSetLayout set_layout ;
CHECK_RESULT ( vkCreateDescriptorSetLayout ( dev , & infos , nullptr , & set_layout ) ) ;
VkPipelineLayoutCreateInfo layout_info = { } ;
layout_info . sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO ;
layout_info . setLayoutCount = 1 ;
layout_info . pSetLayouts = & set_layout ;
VkPipelineLayout result ;
CHECK_RESULT ( vkCreatePipelineLayout ( dev , & layout_info , nullptr , & result ) ) ;
return std : : make_tuple ( result , set_layout ) ;
}
2016-03-17 21:31:34 +01:00
}
2017-05-20 14:45:02 +03:00
VKGSRender : : VKGSRender ( ) : GSRender ( )
2016-02-21 18:50:49 +03:00
{
2017-05-29 19:24:09 +03:00
//shaders_cache.load(rsx::old_shaders_cache::shader_language::glsl);
2016-02-21 18:50:49 +03:00
2017-05-26 17:10:40 +03:00
u32 instance_handle = m_thread_context . createInstance ( " RPCS3 " ) ;
2017-06-06 19:58:06 +03:00
2017-05-26 17:10:40 +03:00
if ( instance_handle > 0 )
{
m_thread_context . makeCurrentInstance ( instance_handle ) ;
m_thread_context . enable_debugging ( ) ;
}
else
{
LOG_FATAL ( RSX , " Could not find a vulkan compatible GPU driver. Your GPU(s) may not support Vulkan, or you need to install the vulkan runtime and drivers " ) ;
m_device = VK_NULL_HANDLE ;
return ;
}
2016-02-21 18:50:49 +03:00
2016-03-07 11:38:00 +03:00
# ifdef _WIN32
HINSTANCE hInstance = NULL ;
HWND hWnd = ( HWND ) m_frame - > handle ( ) ;
2017-04-11 12:15:57 +03:00
std : : vector < vk : : physical_device > & gpus = m_thread_context . enumerateDevices ( ) ;
//Actually confirm that the loader found at least one compatible device
2017-05-26 17:10:40 +03:00
//This should not happen unless something is wrong with the driver setup on the target system
2017-04-11 12:15:57 +03:00
if ( gpus . size ( ) = = 0 )
{
//We can't throw in Emulator::Load, so we show error and return
2017-05-26 17:10:40 +03:00
LOG_FATAL ( RSX , " No compatible GPU devices found " ) ;
2017-04-11 12:15:57 +03:00
m_device = VK_NULL_HANDLE ;
return ;
}
2017-05-25 11:46:28 -04:00
bool gpu_found = false ;
std : : string adapter_name = g_cfg . video . vk . adapter ;
for ( auto & gpu : gpus )
{
if ( gpu . name ( ) = = adapter_name )
{
m_swap_chain = m_thread_context . createSwapChain ( hInstance , hWnd , gpu ) ;
gpu_found = true ;
break ;
}
}
if ( ! gpu_found | | adapter_name . empty ( ) )
{
m_swap_chain = m_thread_context . createSwapChain ( hInstance , hWnd , gpus [ 0 ] ) ;
}
2017-05-29 19:24:09 +03:00
# elif __linux__
Window window = ( Window ) m_frame - > handle ( ) ;
Display * display = XOpenDisplay ( 0 ) ;
std : : vector < vk : : physical_device > & gpus = m_thread_context . enumerateDevices ( ) ;
//Actually confirm that the loader found at least one compatible device
//This should not happen unless something is wrong with the driver setup on the target system
if ( gpus . size ( ) = = 0 )
{
//We can't throw in Emulator::Load, so we show error and return
LOG_FATAL ( RSX , " No compatible GPU devices found " ) ;
m_device = VK_NULL_HANDLE ;
return ;
}
XFlush ( display ) ;
bool gpu_found = false ;
std : : string adapter_name = g_cfg . video . vk . adapter ;
for ( auto & gpu : gpus )
{
if ( gpu . name ( ) = = adapter_name )
{
m_swap_chain = m_thread_context . createSwapChain ( display , window , gpu ) ;
gpu_found = true ;
break ;
}
}
if ( ! gpu_found | | adapter_name . empty ( ) )
{
m_swap_chain = m_thread_context . createSwapChain ( display , window , gpus [ 0 ] ) ;
}
m_display_handle = display ;
2017-05-25 11:46:28 -04:00
2016-03-07 11:38:00 +03:00
# endif
2016-02-21 18:50:49 +03:00
m_device = ( vk : : render_device * ) ( & m_swap_chain - > get_device ( ) ) ;
2016-03-12 01:13:30 +01:00
2016-03-14 19:52:00 +01:00
m_memory_type_mapping = get_memory_mapping ( m_device - > gpu ( ) ) ;
2016-03-12 01:13:30 +01:00
m_optimal_tiling_supported_formats = vk : : get_optimal_tiling_supported_formats ( m_device - > gpu ( ) ) ;
2016-02-21 18:50:49 +03:00
vk : : set_current_thread_ctx ( m_thread_context ) ;
vk : : set_current_renderer ( m_swap_chain - > get_device ( ) ) ;
2016-06-16 18:51:56 +03:00
m_client_width = m_frame - > client_width ( ) ;
m_client_height = m_frame - > client_height ( ) ;
m_swap_chain - > init_swapchain ( m_client_width , m_client_height ) ;
2016-02-21 18:50:49 +03:00
//create command buffer...
m_command_buffer_pool . create ( ( * m_device ) ) ;
2017-04-22 21:30:16 +03:00
for ( auto & cb : m_primary_cb_list )
{
cb . create ( m_command_buffer_pool ) ;
cb . init_fence ( * m_device ) ;
}
m_current_command_buffer = & m_primary_cb_list [ 0 ] ;
2017-04-21 22:55:05 +03:00
//Create secondar command_buffer for parallel operations
m_secondary_command_buffer_pool . create ( ( * m_device ) ) ;
m_secondary_command_buffer . create ( m_secondary_command_buffer_pool ) ;
2016-03-23 23:22:33 +01:00
open_command_buffer ( ) ;
2016-02-21 18:50:49 +03:00
for ( u32 i = 0 ; i < m_swap_chain - > get_swap_image_count ( ) ; + + i )
{
2017-04-22 21:30:16 +03:00
vk : : change_image_layout ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( i ) ,
2016-03-15 18:58:16 +01:00
VK_IMAGE_LAYOUT_UNDEFINED , VK_IMAGE_LAYOUT_GENERAL ,
2016-03-25 17:44:03 +01:00
vk : : get_image_subresource_range ( 0 , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ) ;
2016-03-15 18:58:16 +01:00
VkClearColorValue clear_color { } ;
2016-03-25 17:44:03 +01:00
auto range = vk : : get_image_subresource_range ( 0 , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ;
2017-04-22 21:30:16 +03:00
vkCmdClearColorImage ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( i ) , VK_IMAGE_LAYOUT_GENERAL , & clear_color , 1 , & range ) ;
vk : : change_image_layout ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( i ) ,
2016-03-15 18:58:16 +01:00
VK_IMAGE_LAYOUT_GENERAL , VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ,
2016-03-25 17:44:03 +01:00
vk : : get_image_subresource_range ( 0 , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ) ;
2016-03-15 18:58:16 +01:00
2016-02-21 18:50:49 +03:00
}
2017-06-10 23:32:17 +03:00
m_attrib_ring_info . init ( VK_ATTRIB_RING_BUFFER_SIZE_M * 0x100000 ) ;
m_attrib_ring_info . heap . reset ( new vk : : buffer ( * m_device , VK_ATTRIB_RING_BUFFER_SIZE_M * 0x100000 , m_memory_type_mapping . host_visible_coherent , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT , VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT , 0 ) ) ;
m_uniform_buffer_ring_info . init ( VK_UBO_RING_BUFFER_SIZE_M * 0x100000 ) ;
m_uniform_buffer_ring_info . heap . reset ( new vk : : buffer ( * m_device , VK_UBO_RING_BUFFER_SIZE_M * 0x100000 , m_memory_type_mapping . host_visible_coherent , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT , VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT , 0 ) ) ;
m_index_buffer_ring_info . init ( VK_INDEX_RING_BUFFER_SIZE_M * 0x100000 ) ;
m_index_buffer_ring_info . heap . reset ( new vk : : buffer ( * m_device , VK_INDEX_RING_BUFFER_SIZE_M * 0x100000 , m_memory_type_mapping . host_visible_coherent , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT , VK_BUFFER_USAGE_INDEX_BUFFER_BIT , 0 ) ) ;
m_texture_upload_buffer_ring_info . init ( VK_TEXTURE_UPLOAD_RING_BUFFER_SIZE_M * 0x100000 ) ;
m_texture_upload_buffer_ring_info . heap . reset ( new vk : : buffer ( * m_device , VK_TEXTURE_UPLOAD_RING_BUFFER_SIZE_M * 0x100000 , m_memory_type_mapping . host_visible_coherent , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT , VK_BUFFER_USAGE_TRANSFER_SRC_BIT , 0 ) ) ;
2016-03-17 21:31:34 +01:00
m_render_passes = get_precomputed_render_passes ( * m_device , m_optimal_tiling_supported_formats ) ;
2016-03-17 23:57:53 +01:00
std : : tie ( pipeline_layout , descriptor_layouts ) = get_shared_pipeline_layout ( * m_device ) ;
2016-06-11 16:24:27 +03:00
VkDescriptorPoolSize uniform_buffer_pool = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 3 * DESCRIPTOR_MAX_DRAW_CALLS } ;
VkDescriptorPoolSize uniform_texel_pool = { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 16 * DESCRIPTOR_MAX_DRAW_CALLS } ;
2016-09-22 11:06:57 +03:00
VkDescriptorPoolSize texture_pool = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 20 * DESCRIPTOR_MAX_DRAW_CALLS } ;
2016-03-17 23:57:53 +01:00
std : : vector < VkDescriptorPoolSize > sizes { uniform_buffer_pool , uniform_texel_pool , texture_pool } ;
2016-03-30 20:23:40 +02:00
descriptor_pool . create ( * m_device , sizes . data ( ) , static_cast < uint32_t > ( sizes . size ( ) ) ) ;
2016-03-17 23:57:53 +01:00
2016-03-16 00:34:36 +01:00
2016-07-24 19:28:49 +03:00
null_buffer = std : : make_unique < vk : : buffer > ( * m_device , 32 , m_memory_type_mapping . host_visible_coherent , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT , VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT , 0 ) ;
2016-03-16 00:34:36 +01:00
null_buffer_view = std : : make_unique < vk : : buffer_view > ( * m_device , null_buffer - > value , VK_FORMAT_R32_SFLOAT , 0 , 32 ) ;
2016-03-23 23:22:33 +01:00
VkFenceCreateInfo fence_info = { } ;
fence_info . sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO ;
VkSemaphoreCreateInfo semaphore_info = { } ;
semaphore_info . sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO ;
vkCreateSemaphore ( ( * m_device ) , & semaphore_info , nullptr , & m_present_semaphore ) ;
2016-10-11 03:55:42 +03:00
2017-06-22 20:25:32 +03:00
vk : : initialize_compiler_context ( ) ;
2017-05-20 14:45:02 +03:00
if ( g_cfg . video . overlay )
2016-10-11 03:55:42 +03:00
{
size_t idx = vk : : get_render_pass_location ( m_swap_chain - > get_surface_format ( ) , VK_FORMAT_UNDEFINED , 1 ) ;
m_text_writer . reset ( new vk : : text_writer ( ) ) ;
m_text_writer - > init ( * m_device , m_memory_type_mapping , m_render_passes [ idx ] ) ;
}
2016-02-21 18:50:49 +03:00
}
VKGSRender : : ~ VKGSRender ( )
{
2017-04-11 12:15:57 +03:00
if ( m_device = = VK_NULL_HANDLE )
{
//Initialization failed
return ;
}
2017-06-16 16:01:42 +03:00
//Close recording and wait for all to finish
2017-06-25 23:14:56 +03:00
close_render_pass ( ) ;
2017-06-16 16:01:42 +03:00
CHECK_RESULT ( vkEndCommandBuffer ( * m_current_command_buffer ) ) ;
for ( auto & cb : m_primary_cb_list )
if ( cb . pending ) cb . wait ( ) ;
2017-04-23 15:00:38 +03:00
2017-06-07 22:45:02 +03:00
//Wait for device to finish up with resources
vkDeviceWaitIdle ( * m_device ) ;
2016-02-21 18:50:49 +03:00
2016-08-24 03:50:07 +03:00
//Sync objects
2016-02-21 18:50:49 +03:00
if ( m_present_semaphore )
{
vkDestroySemaphore ( ( * m_device ) , m_present_semaphore , nullptr ) ;
m_present_semaphore = nullptr ;
}
2017-06-22 20:25:32 +03:00
//Texture cache
m_texture_cache . destroy ( ) ;
2016-08-24 03:50:07 +03:00
//Shaders
2017-06-22 20:25:32 +03:00
vk : : finalize_compiler_context ( ) ;
2016-02-21 18:50:49 +03:00
m_prog_buffer . clear ( ) ;
2016-08-24 03:50:07 +03:00
//Global resources
vk : : destroy_global_resources ( ) ;
//Data heaps/buffers
m_index_buffer_ring_info . heap . reset ( ) ;
m_uniform_buffer_ring_info . heap . reset ( ) ;
m_attrib_ring_info . heap . reset ( ) ;
m_texture_upload_buffer_ring_info . heap . reset ( ) ;
//Fallback bindables
null_buffer . reset ( ) ;
null_buffer_view . reset ( ) ;
//Temporary objects
2016-03-16 00:34:36 +01:00
m_buffer_view_to_clean . clear ( ) ;
2016-03-16 00:42:40 +01:00
m_sampler_to_clean . clear ( ) ;
2016-03-21 18:20:30 +01:00
m_framebuffer_to_clean . clear ( ) ;
2016-03-18 23:33:23 +01:00
2016-08-24 03:50:07 +03:00
//Render passes
2016-03-17 21:31:34 +01:00
for ( auto & render_pass : m_render_passes )
if ( render_pass )
vkDestroyRenderPass ( * m_device , render_pass , nullptr ) ;
2016-02-21 18:50:49 +03:00
2016-08-24 03:50:07 +03:00
//Textures
2016-03-16 00:42:40 +01:00
m_rtts . destroy ( ) ;
2016-08-24 03:50:07 +03:00
m_texture_cache . destroy ( ) ;
2016-03-16 00:42:40 +01:00
2016-10-11 03:55:42 +03:00
m_text_writer . reset ( ) ;
2016-08-24 03:50:07 +03:00
//Pipeline descriptors
2016-03-17 23:57:53 +01:00
vkDestroyPipelineLayout ( * m_device , pipeline_layout , nullptr ) ;
vkDestroyDescriptorSetLayout ( * m_device , descriptor_layouts , nullptr ) ;
descriptor_pool . destroy ( ) ;
2016-08-24 03:50:07 +03:00
//Command buffer
2017-04-22 21:30:16 +03:00
for ( auto & cb : m_primary_cb_list )
cb . destroy ( ) ;
2016-02-21 18:50:49 +03:00
m_command_buffer_pool . destroy ( ) ;
2017-04-21 22:55:05 +03:00
m_secondary_command_buffer . destroy ( ) ;
m_secondary_command_buffer_pool . destroy ( ) ;
2016-08-24 03:50:07 +03:00
//Device handles/contexts
2016-02-21 18:50:49 +03:00
m_swap_chain - > destroy ( ) ;
m_thread_context . close ( ) ;
2016-08-24 03:50:07 +03:00
2016-02-21 18:50:49 +03:00
delete m_swap_chain ;
2017-05-29 19:24:09 +03:00
# ifdef __linux__
if ( m_display_handle )
XCloseDisplay ( m_display_handle ) ;
# endif
2016-02-21 18:50:49 +03:00
}
bool VKGSRender : : on_access_violation ( u32 address , bool is_writing )
{
if ( is_writing )
return m_texture_cache . invalidate_address ( address ) ;
2017-04-21 16:35:13 +03:00
else
2017-04-21 22:55:05 +03:00
{
2017-05-20 14:45:02 +03:00
if ( g_cfg . video . write_color_buffers | | g_cfg . video . write_depth_buffer )
2017-04-22 21:30:16 +03:00
{
2017-04-23 19:20:22 +03:00
bool flushable , synchronized ;
std : : tie ( flushable , synchronized ) = m_texture_cache . address_is_flushable ( address ) ;
if ( ! flushable )
return false ;
2017-04-22 21:30:16 +03:00
2017-04-23 19:20:22 +03:00
if ( synchronized )
2017-04-23 15:00:38 +03:00
{
2017-04-23 19:20:22 +03:00
if ( m_last_flushable_cb > = 0 )
{
if ( m_primary_cb_list [ m_last_flushable_cb ] . pending )
m_primary_cb_list [ m_last_flushable_cb ] . wait ( ) ;
}
2017-04-21 22:55:05 +03:00
2017-04-23 19:20:22 +03:00
m_last_flushable_cb = - 1 ;
2017-04-23 15:00:38 +03:00
}
else
{
2017-04-23 19:20:22 +03:00
//This region is buffered, but no previous sync point has been put in place to start sync efforts
//Just stall and get what we have at this point
if ( std : : this_thread : : get_id ( ) ! = rsx_thread )
{
2017-05-21 19:53:21 +03:00
{
std : : lock_guard < std : : mutex > lock ( m_flush_queue_mutex ) ;
m_flush_commands = true ;
m_queued_threads + + ;
}
2017-04-23 19:20:22 +03:00
//This is awful!
2017-06-25 23:14:56 +03:00
while ( m_flush_commands )
{
_mm_lfence ( ) ;
_mm_pause ( ) ;
}
2017-04-23 19:20:22 +03:00
std : : lock_guard < std : : mutex > lock ( m_secondary_cb_guard ) ;
bool status = m_texture_cache . flush_address ( address , * m_device , m_secondary_command_buffer , m_memory_type_mapping , m_swap_chain - > get_present_queue ( ) ) ;
m_queued_threads - - ;
2017-06-25 23:14:56 +03:00
_mm_sfence ( ) ;
2017-04-23 19:20:22 +03:00
return status ;
}
else
{
//NOTE: If the rsx::thread is trampling its own data, we have an operation that should be moved to the GPU
//We should never interrupt our own cb recording since some operations are not interruptible
if ( ! vk : : is_uninterruptible ( ) )
//TODO: Investigate driver behaviour to determine if we need a hard sync or a soft flush
flush_command_queue ( ) ;
}
2017-04-23 15:00:38 +03:00
}
2017-04-21 22:55:05 +03:00
}
2017-04-23 19:20:22 +03:00
else
{
//If we aren't managing buffer sync, dont bother checking the cache
return false ;
}
2017-04-21 22:55:05 +03:00
std : : lock_guard < std : : mutex > lock ( m_secondary_cb_guard ) ;
return m_texture_cache . flush_address ( address , * m_device , m_secondary_command_buffer , m_memory_type_mapping , m_swap_chain - > get_present_queue ( ) ) ;
}
2016-02-21 18:50:49 +03:00
return false ;
}
void VKGSRender : : begin ( )
{
rsx : : thread : : begin ( ) ;
2017-06-30 01:20:23 +03:00
if ( skip_frame )
return ;
2017-06-10 23:32:17 +03:00
//Ease resource pressure if the number of draw calls becomes too high or we are running low on memory resources
if ( m_used_descriptors > = DESCRIPTOR_MAX_DRAW_CALLS | |
m_attrib_ring_info . is_critical ( ) | |
m_texture_upload_buffer_ring_info . is_critical ( ) | |
m_uniform_buffer_ring_info . is_critical ( ) | |
m_index_buffer_ring_info . is_critical ( ) )
2016-06-11 16:24:27 +03:00
{
2017-01-28 17:00:49 -06:00
std : : chrono : : time_point < steady_clock > submit_start = steady_clock : : now ( ) ;
2016-10-11 03:55:42 +03:00
2017-04-22 21:30:16 +03:00
flush_command_queue ( true ) ;
2016-06-11 16:24:27 +03:00
2017-04-22 21:30:16 +03:00
CHECK_RESULT ( vkResetDescriptorPool ( * m_device , descriptor_pool , 0 ) ) ;
2017-06-25 23:14:56 +03:00
m_last_descriptor_set = VK_NULL_HANDLE ;
2016-06-11 16:24:27 +03:00
m_used_descriptors = 0 ;
2017-04-22 21:30:16 +03:00
2017-06-10 23:32:17 +03:00
m_uniform_buffer_ring_info . reset_allocation_stats ( ) ;
m_index_buffer_ring_info . reset_allocation_stats ( ) ;
m_attrib_ring_info . reset_allocation_stats ( ) ;
m_texture_upload_buffer_ring_info . reset_allocation_stats ( ) ;
2016-10-11 03:55:42 +03:00
2017-01-28 17:00:49 -06:00
std : : chrono : : time_point < steady_clock > submit_end = steady_clock : : now ( ) ;
2016-10-11 03:55:42 +03:00
m_flip_time + = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( submit_end - submit_start ) . count ( ) ;
2016-06-11 16:24:27 +03:00
}
2016-03-23 23:16:49 +01:00
VkDescriptorSetAllocateInfo alloc_info = { } ;
alloc_info . descriptorPool = descriptor_pool ;
alloc_info . descriptorSetCount = 1 ;
alloc_info . pSetLayouts = & descriptor_layouts ;
alloc_info . sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO ;
VkDescriptorSet new_descriptor_set ;
CHECK_RESULT ( vkAllocateDescriptorSets ( * m_device , & alloc_info , & new_descriptor_set ) ) ;
descriptor_sets = new_descriptor_set ;
2017-06-25 23:14:56 +03:00
m_used_descriptors + + ;
std : : chrono : : time_point < steady_clock > start = steady_clock : : now ( ) ;
2016-02-21 18:50:49 +03:00
init_buffers ( ) ;
2017-07-05 01:16:59 +03:00
if ( ! framebuffer_status_valid )
return ;
2016-06-26 23:37:02 +02:00
float actual_line_width = rsx : : method_registers . line_width ( ) ;
2016-03-20 02:26:51 +01:00
2017-04-22 21:30:16 +03:00
vkCmdSetLineWidth ( * m_current_command_buffer , actual_line_width ) ;
2016-02-21 18:50:49 +03:00
//TODO: Set up other render-state parameters into the program pipeline
2017-01-28 17:00:49 -06:00
std : : chrono : : time_point < steady_clock > stop = steady_clock : : now ( ) ;
2016-10-11 03:55:42 +03:00
m_setup_time + = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( stop - start ) . count ( ) ;
2017-06-25 23:14:56 +03:00
}
2016-10-11 03:55:42 +03:00
2017-06-25 23:14:56 +03:00
void VKGSRender : : emit_geometry_instance ( u32 /* instance_count*/ )
{
begin_render_pass ( ) ;
m_instanced_draws + + ;
//Repeat last command
if ( ! m_last_draw_indexed )
vkCmdDraw ( * m_current_command_buffer , m_last_vertex_count , 1 , 0 , 0 ) ;
else
{
vkCmdBindIndexBuffer ( * m_current_command_buffer , m_index_buffer_ring_info . heap - > value , m_last_ib_offset , m_last_ib_type ) ;
vkCmdDrawIndexed ( * m_current_command_buffer , m_last_vertex_count , 1 , 0 , 0 , 0 ) ;
}
2016-02-21 18:50:49 +03:00
}
2017-06-25 23:14:56 +03:00
void VKGSRender : : begin_render_pass ( )
2016-03-09 13:23:25 +03:00
{
2017-06-25 23:14:56 +03:00
if ( render_pass_open )
return ;
2016-03-17 21:31:34 +01:00
size_t idx = vk : : get_render_pass_location (
2016-06-26 23:37:02 +02:00
vk : : get_compatible_surface_format ( rsx : : method_registers . surface_color ( ) ) . first ,
vk : : get_compatible_depth_surface_format ( m_optimal_tiling_supported_formats , rsx : : method_registers . surface_depth_fmt ( ) ) ,
( u8 ) vk : : get_draw_buffers ( rsx : : method_registers . surface_color_target ( ) ) . size ( ) ) ;
2016-03-17 21:31:34 +01:00
VkRenderPass current_render_pass = m_render_passes [ idx ] ;
2017-06-25 23:14:56 +03:00
VkRenderPassBeginInfo rp_begin = { } ;
rp_begin . sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO ;
rp_begin . renderPass = current_render_pass ;
rp_begin . framebuffer = m_framebuffer_to_clean . back ( ) - > value ;
rp_begin . renderArea . offset . x = 0 ;
rp_begin . renderArea . offset . y = 0 ;
rp_begin . renderArea . extent . width = m_framebuffer_to_clean . back ( ) - > width ( ) ;
rp_begin . renderArea . extent . height = m_framebuffer_to_clean . back ( ) - > height ( ) ;
vkCmdBeginRenderPass ( * m_current_command_buffer , & rp_begin , VK_SUBPASS_CONTENTS_INLINE ) ;
render_pass_open = true ;
}
void VKGSRender : : close_render_pass ( )
{
if ( ! render_pass_open )
return ;
vkCmdEndRenderPass ( * m_current_command_buffer ) ;
render_pass_open = false ;
}
void VKGSRender : : end ( )
{
2017-07-05 01:16:59 +03:00
if ( skip_frame | | ! framebuffer_status_valid )
2017-06-30 01:20:23 +03:00
{
rsx : : thread : : end ( ) ;
return ;
}
2017-03-26 00:59:57 +03:00
std : : chrono : : time_point < steady_clock > program_start = steady_clock : : now ( ) ;
2017-06-25 23:14:56 +03:00
const bool is_instanced = is_probable_instanced_draw ( ) & & m_last_descriptor_set ! = VK_NULL_HANDLE & & m_program ! = nullptr ;
if ( is_instanced )
{
//Copy descriptor set
VkCopyDescriptorSet copy_info [ 39 ] ;
u8 descriptors_count = 0 ;
for ( u8 i = 0 ; i < 39 ; + + i )
{
if ( ( m_program - > attribute_location_mask & ( 1ull < < i ) ) = = 0 )
continue ;
const u8 n = descriptors_count ;
copy_info [ n ] = { } ;
copy_info [ n ] . sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET ;
copy_info [ n ] . srcSet = m_last_descriptor_set ;
copy_info [ n ] . dstSet = descriptor_sets ;
copy_info [ n ] . srcBinding = i ;
copy_info [ n ] . dstBinding = i ;
copy_info [ n ] . srcArrayElement = 0 ;
copy_info [ n ] . dstArrayElement = 0 ;
copy_info [ n ] . descriptorCount = 1 ;
descriptors_count + + ;
}
vkUpdateDescriptorSets ( * m_device , 0 , nullptr , descriptors_count , ( const VkCopyDescriptorSet * ) & copy_info ) ;
}
2017-03-26 00:59:57 +03:00
//Load program here since it is dependent on vertex state
2017-06-25 23:14:56 +03:00
load_program ( is_instanced ) ;
2017-03-26 00:59:57 +03:00
std : : chrono : : time_point < steady_clock > program_stop = steady_clock : : now ( ) ;
m_setup_time + = ( u32 ) std : : chrono : : duration_cast < std : : chrono : : microseconds > ( program_stop - program_start ) . count ( ) ;
2017-06-25 23:14:56 +03:00
if ( is_instanced )
{
//Only the program constants descriptors should have changed
vkCmdBindPipeline ( * m_current_command_buffer , VK_PIPELINE_BIND_POINT_GRAPHICS , m_program - > pipeline ) ;
vkCmdBindDescriptorSets ( * m_current_command_buffer , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline_layout , 0 , 1 , & descriptor_sets , 0 , nullptr ) ;
emit_geometry_instance ( 1 ) ;
m_last_instanced_cb_index = m_current_cb_index ;
rsx : : thread : : end ( ) ;
return ;
}
2017-07-01 00:24:41 +03:00
close_render_pass ( ) ; //Texture upload stuff conflicts active RPs
if ( g_cfg . video . strict_rendering_mode )
{
auto copy_rtt_contents = [ & ] ( vk : : render_target * surface )
{
const VkImageAspectFlags aspect = surface - > attachment_aspect_flag ;
const u16 parent_w = surface - > old_contents - > width ( ) ;
const u16 parent_h = surface - > old_contents - > height ( ) ;
u16 copy_w , copy_h ;
std : : tie ( std : : ignore , std : : ignore , copy_w , copy_h ) = rsx : : clip_region < u16 > ( parent_w , parent_h , 0 , 0 , surface - > width ( ) , surface - > height ( ) , true ) ;
VkImageSubresourceRange subresource_range = { aspect , 0 , 1 , 0 , 1 } ;
VkImageLayout old_layout = surface - > current_layout ;
vk : : change_image_layout ( * m_current_command_buffer , surface , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , subresource_range ) ;
vk : : change_image_layout ( * m_current_command_buffer , surface - > old_contents , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , subresource_range ) ;
VkImageCopy copy_rgn ;
copy_rgn . srcOffset = { 0 , 0 , 0 } ;
copy_rgn . dstOffset = { 0 , 0 , 0 } ;
copy_rgn . dstSubresource = { aspect , 0 , 0 , 1 } ;
copy_rgn . srcSubresource = { aspect , 0 , 0 , 1 } ;
copy_rgn . extent = { copy_w , copy_h , 1 } ;
vkCmdCopyImage ( * m_current_command_buffer , surface - > old_contents - > value , surface - > old_contents - > current_layout , surface - > value , surface - > current_layout , 1 , & copy_rgn ) ;
vk : : change_image_layout ( * m_current_command_buffer , surface , old_layout , subresource_range ) ;
surface - > dirty = false ;
surface - > old_contents = nullptr ;
} ;
//Prepare surfaces if needed
for ( auto & rtt : m_rtts . m_bound_render_targets )
{
if ( std : : get < 0 > ( rtt ) ! = 0 )
{
auto surface = std : : get < 1 > ( rtt ) ;
if ( surface - > dirty & & surface - > old_contents ! = nullptr )
copy_rtt_contents ( surface ) ;
}
}
if ( auto ds = std : : get < 1 > ( m_rtts . m_bound_depth_stencil ) )
{
if ( ds - > dirty & & ds - > old_contents ! = nullptr )
copy_rtt_contents ( ds ) ;
}
}
2017-06-25 23:14:56 +03:00
std : : chrono : : time_point < steady_clock > vertex_start0 = steady_clock : : now ( ) ;
auto upload_info = upload_vertex_data ( ) ;
std : : chrono : : time_point < steady_clock > vertex_end0 = steady_clock : : now ( ) ;
m_vertex_upload_time + = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( vertex_end0 - vertex_start0 ) . count ( ) ;
2017-01-28 17:00:49 -06:00
std : : chrono : : time_point < steady_clock > textures_start = steady_clock : : now ( ) ;
2016-10-11 03:55:42 +03:00
2016-07-19 22:50:40 +08:00
for ( int i = 0 ; i < rsx : : limits : : fragment_textures_count ; + + i )
2016-02-21 18:50:49 +03:00
{
2016-03-16 19:06:55 +01:00
if ( m_program - > has_uniform ( " tex " + std : : to_string ( i ) ) )
2016-02-21 18:50:49 +03:00
{
2016-06-26 23:37:02 +02:00
if ( ! rsx : : method_registers . fragment_textures [ i ] . enabled ( ) )
2016-02-21 18:50:49 +03:00
{
2016-03-16 00:42:40 +01:00
m_program - > bind_uniform ( { vk : : null_sampler ( ) , vk : : null_image_view ( ) , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } , " tex " + std : : to_string ( i ) , descriptor_sets ) ;
2016-02-21 18:50:49 +03:00
continue ;
}
2016-08-03 22:33:52 +03:00
2017-04-22 21:30:16 +03:00
vk : : image_view * texture0 = m_texture_cache . upload_texture ( * m_current_command_buffer , rsx : : method_registers . fragment_textures [ i ] , m_rtts , m_memory_type_mapping , m_texture_upload_buffer_ring_info , m_texture_upload_buffer_ring_info . heap . get ( ) ) ;
2016-08-03 22:33:52 +03:00
if ( ! texture0 )
{
LOG_ERROR ( RSX , " Texture upload failed to texture index %d. Binding null sampler. " , i ) ;
m_program - > bind_uniform ( { vk : : null_sampler ( ) , vk : : null_image_view ( ) , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } , " tex " + std : : to_string ( i ) , descriptor_sets ) ;
continue ;
}
2016-02-21 18:50:49 +03:00
2017-06-12 12:42:30 +03:00
const u32 texture_format = rsx : : method_registers . fragment_textures [ i ] . format ( ) & ~ ( CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN ) ;
VkBool32 is_depth_texture = ( texture_format = = CELL_GCM_TEXTURE_DEPTH16 | | texture_format = = CELL_GCM_TEXTURE_DEPTH24_D8 ) ;
VkCompareOp depth_compare = is_depth_texture ? vk : : get_compare_func ( ( rsx : : comparison_function ) rsx : : method_registers . fragment_textures [ i ] . zfunc ( ) , true ) : VK_COMPARE_OP_NEVER ;
2016-03-16 00:42:40 +01:00
VkFilter min_filter ;
VkSamplerMipmapMode mip_mode ;
2016-10-04 16:51:40 +03:00
float min_lod = 0.f , max_lod = 0.f ;
float lod_bias = 0.f ;
2016-06-26 23:37:02 +02:00
std : : tie ( min_filter , mip_mode ) = vk : : get_min_filter_and_mip ( rsx : : method_registers . fragment_textures [ i ] . min_filter ( ) ) ;
2016-08-03 22:33:52 +03:00
2016-10-04 16:51:40 +03:00
if ( rsx : : method_registers . fragment_textures [ i ] . get_exact_mipmap_count ( ) > 1 )
2016-09-20 17:23:56 +03:00
{
2016-10-04 16:51:40 +03:00
min_lod = ( float ) ( rsx : : method_registers . fragment_textures [ i ] . min_lod ( ) > > 8 ) ;
max_lod = ( float ) ( rsx : : method_registers . fragment_textures [ i ] . max_lod ( ) > > 8 ) ;
lod_bias = rsx : : method_registers . fragment_textures [ i ] . bias ( ) ;
2016-09-20 17:23:56 +03:00
}
2016-10-04 16:51:40 +03:00
else
2016-09-20 17:23:56 +03:00
{
2016-10-04 16:51:40 +03:00
mip_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST ;
2016-09-20 17:23:56 +03:00
}
2016-10-04 16:51:40 +03:00
2016-09-20 17:23:56 +03:00
m_sampler_to_clean . push_back ( std : : make_unique < vk : : sampler > (
* m_device ,
vk : : vk_wrap_mode ( rsx : : method_registers . fragment_textures [ i ] . wrap_s ( ) ) , vk : : vk_wrap_mode ( rsx : : method_registers . fragment_textures [ i ] . wrap_t ( ) ) , vk : : vk_wrap_mode ( rsx : : method_registers . fragment_textures [ i ] . wrap_r ( ) ) ,
! ! ( rsx : : method_registers . fragment_textures [ i ] . format ( ) & CELL_GCM_TEXTURE_UN ) ,
2016-10-04 16:51:40 +03:00
lod_bias , vk : : max_aniso ( rsx : : method_registers . fragment_textures [ i ] . max_aniso ( ) ) , min_lod , max_lod ,
2017-06-12 12:42:30 +03:00
min_filter , vk : : get_mag_filter ( rsx : : method_registers . fragment_textures [ i ] . mag_filter ( ) ) , mip_mode , vk : : get_border_color ( rsx : : method_registers . fragment_textures [ i ] . border_color ( ) ) ,
is_depth_texture , depth_compare ) ) ;
2016-09-20 17:23:56 +03:00
m_program - > bind_uniform ( { m_sampler_to_clean . back ( ) - > value , texture0 - > value , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } , " tex " + std : : to_string ( i ) , descriptor_sets ) ;
}
}
for ( int i = 0 ; i < rsx : : limits : : vertex_textures_count ; + + i )
{
if ( m_program - > has_uniform ( " vtex " + std : : to_string ( i ) ) )
{
if ( ! rsx : : method_registers . vertex_textures [ i ] . enabled ( ) )
{
m_program - > bind_uniform ( { vk : : null_sampler ( ) , vk : : null_image_view ( ) , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } , " vtex " + std : : to_string ( i ) , descriptor_sets ) ;
continue ;
}
2017-04-22 21:30:16 +03:00
vk : : image_view * texture0 = m_texture_cache . upload_texture ( * m_current_command_buffer , rsx : : method_registers . vertex_textures [ i ] , m_rtts , m_memory_type_mapping , m_texture_upload_buffer_ring_info , m_texture_upload_buffer_ring_info . heap . get ( ) ) ;
2016-09-20 17:23:56 +03:00
if ( ! texture0 )
{
LOG_ERROR ( RSX , " Texture upload failed to vtexture index %d. Binding null sampler. " , i ) ;
m_program - > bind_uniform ( { vk : : null_sampler ( ) , vk : : null_image_view ( ) , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } , " vtex " + std : : to_string ( i ) , descriptor_sets ) ;
continue ;
}
m_sampler_to_clean . push_back ( std : : make_unique < vk : : sampler > (
* m_device ,
VK_SAMPLER_ADDRESS_MODE_REPEAT , VK_SAMPLER_ADDRESS_MODE_REPEAT , VK_SAMPLER_ADDRESS_MODE_REPEAT ,
! ! ( rsx : : method_registers . vertex_textures [ i ] . format ( ) & CELL_GCM_TEXTURE_UN ) ,
2017-06-18 17:53:02 +03:00
0.f , 1.f , ( f32 ) rsx : : method_registers . vertex_textures [ i ] . min_lod ( ) , ( f32 ) rsx : : method_registers . vertex_textures [ i ] . max_lod ( ) ,
2016-09-20 17:23:56 +03:00
VK_FILTER_NEAREST , VK_FILTER_NEAREST , VK_SAMPLER_MIPMAP_MODE_NEAREST , vk : : get_border_color ( rsx : : method_registers . vertex_textures [ i ] . border_color ( ) )
) ) ;
m_program - > bind_uniform ( { m_sampler_to_clean . back ( ) - > value , texture0 - > value , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } , " vtex " + std : : to_string ( i ) , descriptor_sets ) ;
}
}
2017-01-28 17:00:49 -06:00
std : : chrono : : time_point < steady_clock > textures_end = steady_clock : : now ( ) ;
2016-10-11 03:55:42 +03:00
m_textures_upload_time + = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( textures_end - textures_start ) . count ( ) ;
2017-04-23 17:17:05 +03:00
//While vertex upload is an interruptible process, if we made it this far, there's no need to sync anything that occurs past this point
//Only textures are synchronized tightly with the GPU and they have been read back above
vk : : enter_uninterruptible ( ) ;
2017-06-25 23:14:56 +03:00
begin_render_pass ( ) ;
2017-04-22 21:30:16 +03:00
vkCmdBindPipeline ( * m_current_command_buffer , VK_PIPELINE_BIND_POINT_GRAPHICS , m_program - > pipeline ) ;
vkCmdBindDescriptorSets ( * m_current_command_buffer , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline_layout , 0 , 1 , & descriptor_sets , 0 , nullptr ) ;
2016-03-17 23:57:53 +01:00
2017-06-19 13:47:38 +03:00
if ( auto ds = std : : get < 1 > ( m_rtts . m_bound_depth_stencil ) )
{
if ( ds - > dirty )
{
//Clear this surface before drawing on it
VkClearValue depth_clear_value ;
depth_clear_value . depthStencil . depth = 1.f ;
depth_clear_value . depthStencil . stencil = 255 ;
VkClearRect clear_rect = { 0 , 0 , m_framebuffer_to_clean . back ( ) - > width ( ) , m_framebuffer_to_clean . back ( ) - > height ( ) , 0 , 1 } ;
VkClearAttachment clear_desc = { ds - > attachment_aspect_flag , 0 , depth_clear_value } ;
vkCmdClearAttachments ( * m_current_command_buffer , 1 , & clear_desc , 1 , & clear_rect ) ;
2017-07-01 00:24:41 +03:00
2017-06-19 13:47:38 +03:00
ds - > dirty = false ;
}
}
2016-08-04 23:21:45 +02:00
std : : optional < std : : tuple < VkDeviceSize , VkIndexType > > index_info = std : : get < 2 > ( upload_info ) ;
2017-06-25 23:14:56 +03:00
if ( m_attrib_ring_info . mapped )
{
wait_for_vertex_upload_task ( ) ;
m_attrib_ring_info . unmap ( ) ;
}
std : : chrono : : time_point < steady_clock > vertex_end = steady_clock : : now ( ) ;
m_vertex_upload_time + = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( vertex_end - textures_end ) . count ( ) ;
2016-08-04 23:21:45 +02:00
if ( ! index_info )
2017-06-25 23:14:56 +03:00
{
2017-04-22 21:30:16 +03:00
vkCmdDraw ( * m_current_command_buffer , std : : get < 1 > ( upload_info ) , 1 , 0 , 0 ) ;
2017-06-25 23:14:56 +03:00
m_last_draw_indexed = false ;
}
2016-02-21 18:50:49 +03:00
else
{
2016-03-14 22:29:18 +01:00
VkIndexType index_type ;
2016-08-04 23:21:45 +02:00
u32 index_count = std : : get < 1 > ( upload_info ) ;
2016-03-14 22:29:18 +01:00
VkDeviceSize offset ;
2016-08-04 23:21:45 +02:00
std : : tie ( offset , index_type ) = index_info . value ( ) ;
2016-02-21 18:50:49 +03:00
2017-04-22 21:30:16 +03:00
vkCmdBindIndexBuffer ( * m_current_command_buffer , m_index_buffer_ring_info . heap - > value , offset , index_type ) ;
vkCmdDrawIndexed ( * m_current_command_buffer , index_count , 1 , 0 , 0 , 0 ) ;
2017-06-25 23:14:56 +03:00
m_last_draw_indexed = false ;
m_last_ib_type = index_type ;
m_last_ib_offset = offset ;
m_last_vertex_count = index_count ;
2016-02-21 18:50:49 +03:00
}
2017-06-25 23:14:56 +03:00
m_last_instanced_cb_index = ~ 0 ;
m_last_descriptor_set = descriptor_sets ;
2016-02-21 18:50:49 +03:00
2017-04-23 17:17:05 +03:00
vk : : leave_uninterruptible ( ) ;
2017-01-28 17:00:49 -06:00
std : : chrono : : time_point < steady_clock > draw_end = steady_clock : : now ( ) ;
2016-10-11 03:55:42 +03:00
m_draw_time + = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( draw_end - vertex_end ) . count ( ) ;
2016-02-21 18:50:49 +03:00
2017-04-21 16:35:13 +03:00
copy_render_targets_to_dma_location ( ) ;
2017-04-23 15:00:38 +03:00
m_draw_calls + + ;
2017-04-21 22:55:05 +03:00
rsx : : thread : : end ( ) ;
2016-02-21 18:50:49 +03:00
}
void VKGSRender : : set_viewport ( )
{
2016-06-26 23:37:02 +02:00
u16 scissor_x = rsx : : method_registers . scissor_origin_x ( ) ;
u16 scissor_w = rsx : : method_registers . scissor_width ( ) ;
u16 scissor_y = rsx : : method_registers . scissor_origin_y ( ) ;
u16 scissor_h = rsx : : method_registers . scissor_height ( ) ;
2016-02-21 18:50:49 +03:00
2016-09-26 15:21:17 +03:00
//NOTE: The scale_offset matrix already has viewport matrix factored in
2016-03-12 22:22:28 +01:00
VkViewport viewport = { } ;
2016-09-26 15:21:17 +03:00
viewport . x = 0 ;
viewport . y = 0 ;
viewport . width = rsx : : method_registers . surface_clip_width ( ) ;
viewport . height = rsx : : method_registers . surface_clip_height ( ) ;
2016-02-21 18:50:49 +03:00
viewport . minDepth = 0.f ;
viewport . maxDepth = 1.f ;
2017-04-22 21:30:16 +03:00
vkCmdSetViewport ( * m_current_command_buffer , 0 , 1 , & viewport ) ;
2016-02-21 18:50:49 +03:00
2016-03-12 22:22:28 +01:00
VkRect2D scissor = { } ;
2016-02-21 18:50:49 +03:00
scissor . extent . height = scissor_h ;
scissor . extent . width = scissor_w ;
scissor . offset . x = scissor_x ;
scissor . offset . y = scissor_y ;
2017-04-22 21:30:16 +03:00
vkCmdSetScissor ( * m_current_command_buffer , 0 , 1 , & scissor ) ;
2017-07-05 01:16:59 +03:00
if ( scissor_x > = viewport . width | | scissor_y > = viewport . height | | scissor_w = = 0 | | scissor_h = = 0 )
{
if ( ! g_cfg . video . strict_rendering_mode )
{
framebuffer_status_valid = false ;
return ;
}
}
2016-02-21 18:50:49 +03:00
}
void VKGSRender : : on_init_thread ( )
{
2017-04-11 12:15:57 +03:00
if ( m_device = = VK_NULL_HANDLE )
{
fmt : : throw_exception ( " No vulkan device was created " ) ;
}
2016-02-21 18:50:49 +03:00
GSRender : : on_init_thread ( ) ;
2017-04-21 22:55:05 +03:00
rsx_thread = std : : this_thread : : get_id ( ) ;
2016-02-21 18:50:49 +03:00
}
void VKGSRender : : on_exit ( )
{
2016-07-20 15:16:19 +03:00
return GSRender : : on_exit ( ) ;
2016-02-21 18:50:49 +03:00
}
void VKGSRender : : clear_surface ( u32 mask )
{
2017-06-30 01:20:23 +03:00
if ( skip_frame ) return ;
2016-08-21 18:18:26 +08:00
// Ignore clear if surface target is set to CELL_GCM_SURFACE_TARGET_NONE
if ( rsx : : method_registers . surface_color_target ( ) = = rsx : : surface_target : : none ) return ;
2016-09-21 16:41:29 +03:00
2016-02-21 18:50:49 +03:00
if ( ! ( mask & 0xF3 ) ) return ;
2016-06-26 23:37:02 +02:00
if ( m_current_present_image = = 0xFFFF ) return ;
2016-02-21 18:50:49 +03:00
init_buffers ( ) ;
2017-07-05 01:16:59 +03:00
if ( ! framebuffer_status_valid ) return ;
2017-04-23 15:00:38 +03:00
copy_render_targets_to_dma_location ( ) ;
2016-02-21 18:50:49 +03:00
float depth_clear = 1.f ;
2016-03-07 17:55:02 +03:00
u32 stencil_clear = 0 ;
2016-09-21 16:41:29 +03:00
u32 depth_stencil_mask = 0 ;
std : : vector < VkClearAttachment > clear_descriptors ;
std : : vector < VkClearRect > clear_regions ;
2016-02-21 18:50:49 +03:00
VkClearValue depth_stencil_clear_values , color_clear_values ;
2016-09-21 16:41:29 +03:00
u16 scissor_x = rsx : : method_registers . scissor_origin_x ( ) ;
u16 scissor_w = rsx : : method_registers . scissor_width ( ) ;
u16 scissor_y = rsx : : method_registers . scissor_origin_y ( ) ;
u16 scissor_h = rsx : : method_registers . scissor_height ( ) ;
2017-06-07 22:45:02 +03:00
const u32 fb_width = m_framebuffer_to_clean . back ( ) - > width ( ) ;
const u32 fb_height = m_framebuffer_to_clean . back ( ) - > height ( ) ;
//clip region
2017-06-10 23:32:17 +03:00
std : : tie ( scissor_x , scissor_y , scissor_w , scissor_h ) = rsx : : clip_region < u16 > ( fb_width , fb_height , scissor_x , scissor_y , scissor_w , scissor_h , true ) ;
2016-09-21 16:41:29 +03:00
VkClearRect region = { { { scissor_x , scissor_y } , { scissor_w , scissor_h } } , 0 , 1 } ;
auto targets = vk : : get_draw_buffers ( rsx : : method_registers . surface_color_target ( ) ) ;
auto surface_depth_format = rsx : : method_registers . surface_depth_fmt ( ) ;
2016-03-28 20:46:23 +02:00
2016-02-21 18:50:49 +03:00
if ( mask & 0x1 )
{
u32 max_depth_value = get_max_depth_value ( surface_depth_format ) ;
2017-03-26 14:51:25 +03:00
u32 clear_depth = rsx : : method_registers . z_clear_value ( surface_depth_format = = rsx : : surface_depth_format : : z24s8 ) ;
2016-02-21 18:50:49 +03:00
float depth_clear = ( float ) clear_depth / max_depth_value ;
depth_stencil_clear_values . depthStencil . depth = depth_clear ;
depth_stencil_clear_values . depthStencil . stencil = stencil_clear ;
2016-09-21 16:41:29 +03:00
depth_stencil_mask | = VK_IMAGE_ASPECT_DEPTH_BIT ;
2016-02-21 18:50:49 +03:00
}
2016-03-28 20:46:23 +02:00
if ( mask & 0x2 )
2016-02-21 18:50:49 +03:00
{
2016-09-21 16:41:29 +03:00
if ( surface_depth_format = = rsx : : surface_depth_format : : z24s8 )
{
u8 clear_stencil = rsx : : method_registers . stencil_clear_value ( ) ;
2017-03-24 12:29:18 +03:00
depth_stencil_clear_values . depthStencil . stencil = clear_stencil ;
2016-02-21 18:50:49 +03:00
2016-09-21 16:41:29 +03:00
depth_stencil_mask | = VK_IMAGE_ASPECT_STENCIL_BIT ;
}
2016-03-28 20:46:23 +02:00
}
2016-02-21 18:50:49 +03:00
if ( mask & 0xF0 )
{
2016-06-26 23:37:02 +02:00
u8 clear_a = rsx : : method_registers . clear_color_a ( ) ;
u8 clear_r = rsx : : method_registers . clear_color_r ( ) ;
u8 clear_g = rsx : : method_registers . clear_color_g ( ) ;
u8 clear_b = rsx : : method_registers . clear_color_b ( ) ;
2016-02-21 18:50:49 +03:00
color_clear_values . color . float32 [ 0 ] = ( float ) clear_r / 255 ;
color_clear_values . color . float32 [ 1 ] = ( float ) clear_g / 255 ;
color_clear_values . color . float32 [ 2 ] = ( float ) clear_b / 255 ;
color_clear_values . color . float32 [ 3 ] = ( float ) clear_a / 255 ;
2016-09-21 16:41:29 +03:00
for ( int index = 0 ; index < targets . size ( ) ; + + index )
2016-02-21 18:50:49 +03:00
{
2016-09-21 16:41:29 +03:00
clear_descriptors . push_back ( { VK_IMAGE_ASPECT_COLOR_BIT , ( uint32_t ) index , color_clear_values } ) ;
clear_regions . push_back ( region ) ;
2016-02-21 18:50:49 +03:00
}
2017-07-01 00:24:41 +03:00
for ( auto & rtt : m_rtts . m_bound_render_targets )
{
if ( std : : get < 0 > ( rtt ) ! = 0 )
{
std : : get < 1 > ( rtt ) - > dirty = false ;
std : : get < 1 > ( rtt ) - > old_contents = nullptr ;
}
}
2016-02-21 18:50:49 +03:00
}
if ( mask & 0x3 )
2016-03-09 13:23:25 +03:00
{
2016-09-21 16:41:29 +03:00
clear_descriptors . push_back ( { ( VkImageAspectFlags ) depth_stencil_mask , 0 , depth_stencil_clear_values } ) ;
clear_regions . push_back ( region ) ;
2016-03-09 13:23:25 +03:00
}
2016-02-21 18:50:49 +03:00
2017-06-25 23:14:56 +03:00
begin_render_pass ( ) ;
2017-06-18 17:53:02 +03:00
vkCmdClearAttachments ( * m_current_command_buffer , ( u32 ) clear_descriptors . size ( ) , clear_descriptors . data ( ) , ( u32 ) clear_regions . size ( ) , clear_regions . data ( ) ) ;
2016-09-21 16:41:29 +03:00
2017-06-19 13:47:38 +03:00
if ( mask & 0x3 )
{
if ( std : : get < 0 > ( m_rtts . m_bound_depth_stencil ) ! = 0 )
2017-07-01 00:24:41 +03:00
{
2017-06-19 13:47:38 +03:00
std : : get < 1 > ( m_rtts . m_bound_depth_stencil ) - > dirty = false ;
2017-07-01 00:24:41 +03:00
std : : get < 1 > ( m_rtts . m_bound_depth_stencil ) - > old_contents = nullptr ;
}
2017-06-19 13:47:38 +03:00
}
2016-03-23 23:22:33 +01:00
}
2016-02-21 18:50:49 +03:00
2016-03-23 23:22:33 +01:00
void VKGSRender : : sync_at_semaphore_release ( )
{
2017-04-21 16:35:13 +03:00
m_flush_draw_buffers = true ;
}
void VKGSRender : : copy_render_targets_to_dma_location ( )
{
if ( ! m_flush_draw_buffers )
return ;
2017-05-20 14:45:02 +03:00
if ( ! g_cfg . video . write_color_buffers & & ! g_cfg . video . write_depth_buffer )
2017-04-21 22:55:05 +03:00
return ;
//TODO: Make this asynchronous. Should be similar to a glFlush() but in this case its similar to glFinish
//This is due to all the hard waits for fences
//TODO: Use a command buffer array to allow explicit draw command tracking
2017-04-23 17:17:05 +03:00
vk : : enter_uninterruptible ( ) ;
2017-05-20 14:45:02 +03:00
if ( g_cfg . video . write_color_buffers )
2017-04-21 16:35:13 +03:00
{
for ( u8 index = 0 ; index < rsx : : limits : : color_buffers_count ; index + + )
{
if ( ! m_surface_info [ index ] . pitch )
continue ;
m_texture_cache . flush_memory_to_cache ( m_surface_info [ index ] . address , m_surface_info [ index ] . pitch * m_surface_info [ index ] . height ,
2017-04-22 21:30:16 +03:00
* m_current_command_buffer , m_memory_type_mapping , m_swap_chain - > get_present_queue ( ) ) ;
2017-04-21 16:35:13 +03:00
}
}
2017-05-20 14:45:02 +03:00
if ( g_cfg . video . write_depth_buffer )
2017-04-21 16:35:13 +03:00
{
if ( m_depth_surface_info . pitch )
{
m_texture_cache . flush_memory_to_cache ( m_depth_surface_info . address , m_depth_surface_info . pitch * m_depth_surface_info . height ,
2017-04-22 21:30:16 +03:00
* m_current_command_buffer , m_memory_type_mapping , m_swap_chain - > get_present_queue ( ) ) ;
2017-04-21 16:35:13 +03:00
}
}
2017-04-23 17:17:05 +03:00
vk : : leave_uninterruptible ( ) ;
2017-04-22 21:30:16 +03:00
m_last_flushable_cb = m_current_cb_index ;
flush_command_queue ( ) ;
m_flush_draw_buffers = false ;
}
void VKGSRender : : flush_command_queue ( bool hard_sync )
{
2017-06-25 23:14:56 +03:00
close_render_pass ( ) ;
2017-04-22 21:30:16 +03:00
close_and_submit_command_buffer ( { } , m_current_command_buffer - > submit_fence ) ;
if ( hard_sync )
{
//swap handler checks the pending flag, so call it here
process_swap_request ( ) ;
2017-04-23 15:00:38 +03:00
//wait for the latest intruction to execute
m_current_command_buffer - > pending = true ;
m_current_command_buffer - > wait ( ) ;
2017-04-22 21:30:16 +03:00
//Clear all command buffer statuses
for ( auto & cb : m_primary_cb_list )
cb . poke ( ) ;
2017-04-23 15:00:38 +03:00
m_last_flushable_cb = - 1 ;
m_flush_commands = false ;
2017-04-22 21:30:16 +03:00
}
else
{
//Mark this queue as pending
m_current_command_buffer - > pending = true ;
//Grab next cb in line and make it usable
m_current_cb_index = ( m_current_cb_index + 1 ) % VK_MAX_ASYNC_CB_COUNT ;
m_current_command_buffer = & m_primary_cb_list [ m_current_cb_index ] ;
m_current_command_buffer - > reset ( ) ;
}
2017-04-21 22:55:05 +03:00
open_command_buffer ( ) ;
}
2017-04-22 21:30:16 +03:00
void VKGSRender : : queue_swap_request ( )
{
//buffer the swap request and return
if ( m_swap_command_buffer & & m_swap_command_buffer - > pending )
{
//Its probable that no actual drawing took place
process_swap_request ( ) ;
}
m_swap_command_buffer = m_current_command_buffer ;
close_and_submit_command_buffer ( { m_present_semaphore } , m_current_command_buffer - > submit_fence ) ;
//Grab next cb in line and make it usable
m_current_cb_index = ( m_current_cb_index + 1 ) % VK_MAX_ASYNC_CB_COUNT ;
m_current_command_buffer = & m_primary_cb_list [ m_current_cb_index ] ;
m_current_command_buffer - > reset ( ) ;
m_swap_command_buffer - > pending = true ;
open_command_buffer ( ) ;
}
void VKGSRender : : process_swap_request ( )
{
if ( ! m_swap_command_buffer )
return ;
if ( m_swap_command_buffer - > pending )
{
//Perform hard swap here
m_swap_command_buffer - > wait ( ) ;
VkSwapchainKHR swap_chain = ( VkSwapchainKHR ) ( * m_swap_chain ) ;
VkPresentInfoKHR present = { } ;
present . sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR ;
present . pNext = nullptr ;
present . swapchainCount = 1 ;
present . pSwapchains = & swap_chain ;
present . pImageIndices = & m_current_present_image ;
CHECK_RESULT ( m_swap_chain - > queuePresentKHR ( m_swap_chain - > get_present_queue ( ) , & present ) ) ;
}
//Clean up all the resources from the last frame
//Feed back damaged resources to the main texture cache for management...
//m_texture_cache.merge_dirty_textures(m_rtts.invalidated_resources);
m_rtts . invalidated_resources . clear ( ) ;
m_texture_cache . flush ( ) ;
2017-06-19 02:00:32 +03:00
if ( g_cfg . video . invalidate_surface_cache_every_frame )
m_rtts . invalidate_surface_cache_data ( & * m_current_command_buffer ) ;
2017-04-22 21:30:16 +03:00
m_buffer_view_to_clean . clear ( ) ;
m_sampler_to_clean . clear ( ) ;
m_framebuffer_to_clean . clear ( ) ;
2017-05-20 14:45:02 +03:00
if ( g_cfg . video . overlay )
2017-04-22 21:30:16 +03:00
{
m_text_writer - > reset_descriptors ( ) ;
}
m_swap_command_buffer = nullptr ;
}
2017-04-21 22:55:05 +03:00
void VKGSRender : : do_local_task ( )
{
if ( m_flush_commands )
{
2017-05-21 19:53:21 +03:00
std : : lock_guard < std : : mutex > lock ( m_flush_queue_mutex ) ;
2017-04-23 15:00:38 +03:00
//TODO: Determine if a hard sync is necessary
//Pipeline barriers later may do a better job synchronizing than wholly stalling the pipeline
flush_command_queue ( ) ;
2017-04-21 22:55:05 +03:00
m_flush_commands = false ;
2017-06-25 23:14:56 +03:00
while ( m_queued_threads )
{
_mm_lfence ( ) ;
_mm_pause ( ) ;
}
2017-04-21 22:55:05 +03:00
}
2016-02-21 18:50:49 +03:00
}
bool VKGSRender : : do_method ( u32 cmd , u32 arg )
{
switch ( cmd )
{
case NV4097_CLEAR_SURFACE :
clear_surface ( arg ) ;
return true ;
2016-03-23 23:22:33 +01:00
case NV4097_TEXTURE_READ_SEMAPHORE_RELEASE :
case NV4097_BACK_END_WRITE_SEMAPHORE_RELEASE :
sync_at_semaphore_release ( ) ;
return false ; //call rsx::thread method implementation
2016-02-21 18:50:49 +03:00
default :
return false ;
}
}
2017-06-25 23:14:56 +03:00
bool VKGSRender : : load_program ( bool fast_update )
2016-02-21 18:50:49 +03:00
{
2017-06-25 23:14:56 +03:00
RSXVertexProgram vertex_program ;
RSXFragmentProgram fragment_program ;
if ( ! fast_update )
2016-09-29 09:54:32 +03:00
{
2017-06-25 23:14:56 +03:00
auto rtt_lookup_func = [ this ] ( u32 texaddr , rsx : : fragment_texture & , bool is_depth ) - > std : : tuple < bool , u16 >
{
vk : : render_target * surface = nullptr ;
if ( ! is_depth )
surface = m_rtts . get_texture_from_render_target_if_applicable ( texaddr ) ;
else
surface = m_rtts . get_texture_from_depth_stencil_if_applicable ( texaddr ) ;
2016-10-04 16:51:40 +03:00
2017-06-25 23:14:56 +03:00
if ( ! surface ) return std : : make_tuple ( false , 0 ) ;
2017-02-10 12:08:46 +03:00
2017-06-25 23:14:56 +03:00
return std : : make_tuple ( true , surface - > native_pitch ) ;
} ;
2017-02-10 12:08:46 +03:00
2017-06-25 23:14:56 +03:00
vertex_program = get_current_vertex_program ( ) ;
fragment_program = get_current_fragment_program ( rtt_lookup_func ) ;
2016-09-29 09:54:32 +03:00
2017-06-25 23:14:56 +03:00
vk : : pipeline_props properties = { } ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
properties . ia . sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO ;
bool unused ;
properties . ia . topology = vk : : get_appropriate_topology ( rsx : : method_registers . current_draw_clause . primitive , unused ) ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
if ( rsx : : method_registers . restart_index_enabled ( ) )
{
properties . ia . primitiveRestartEnable = VK_TRUE ;
}
else
properties . ia . primitiveRestartEnable = VK_FALSE ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
for ( int i = 0 ; i < 4 ; + + i )
{
properties . att_state [ i ] . colorWriteMask = 0xf ;
properties . att_state [ i ] . blendEnable = VK_FALSE ;
}
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
VkColorComponentFlags mask = 0 ;
if ( rsx : : method_registers . color_mask_a ( ) ) mask | = VK_COLOR_COMPONENT_A_BIT ;
if ( rsx : : method_registers . color_mask_b ( ) ) mask | = VK_COLOR_COMPONENT_B_BIT ;
if ( rsx : : method_registers . color_mask_g ( ) ) mask | = VK_COLOR_COMPONENT_G_BIT ;
if ( rsx : : method_registers . color_mask_r ( ) ) mask | = VK_COLOR_COMPONENT_R_BIT ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
VkColorComponentFlags color_masks [ 4 ] = { mask } ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
u8 render_targets [ ] = { 0 , 1 , 2 , 3 } ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
for ( u8 idx = 0 ; idx < m_draw_buffers_count ; + + idx )
{
properties . att_state [ render_targets [ idx ] ] . colorWriteMask = mask ;
}
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
if ( rsx : : method_registers . blend_enabled ( ) )
{
VkBlendFactor sfactor_rgb = vk : : get_blend_factor ( rsx : : method_registers . blend_func_sfactor_rgb ( ) ) ;
VkBlendFactor sfactor_a = vk : : get_blend_factor ( rsx : : method_registers . blend_func_sfactor_a ( ) ) ;
VkBlendFactor dfactor_rgb = vk : : get_blend_factor ( rsx : : method_registers . blend_func_dfactor_rgb ( ) ) ;
VkBlendFactor dfactor_a = vk : : get_blend_factor ( rsx : : method_registers . blend_func_dfactor_a ( ) ) ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
VkBlendOp equation_rgb = vk : : get_blend_op ( rsx : : method_registers . blend_equation_rgb ( ) ) ;
VkBlendOp equation_a = vk : : get_blend_op ( rsx : : method_registers . blend_equation_a ( ) ) ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
for ( u8 idx = 0 ; idx < m_draw_buffers_count ; + + idx )
{
properties . att_state [ render_targets [ idx ] ] . blendEnable = VK_TRUE ;
properties . att_state [ render_targets [ idx ] ] . srcColorBlendFactor = sfactor_rgb ;
properties . att_state [ render_targets [ idx ] ] . dstColorBlendFactor = dfactor_rgb ;
properties . att_state [ render_targets [ idx ] ] . srcAlphaBlendFactor = sfactor_a ;
properties . att_state [ render_targets [ idx ] ] . dstAlphaBlendFactor = dfactor_a ;
properties . att_state [ render_targets [ idx ] ] . colorBlendOp = equation_rgb ;
properties . att_state [ render_targets [ idx ] ] . alphaBlendOp = equation_a ;
}
auto blend_colors = rsx : : get_constant_blend_colors ( ) ;
properties . cs . blendConstants [ 0 ] = blend_colors [ 0 ] ;
properties . cs . blendConstants [ 1 ] = blend_colors [ 1 ] ;
properties . cs . blendConstants [ 2 ] = blend_colors [ 2 ] ;
properties . cs . blendConstants [ 3 ] = blend_colors [ 3 ] ;
}
else
2016-03-20 02:26:51 +01:00
{
2017-06-25 23:14:56 +03:00
for ( u8 idx = 0 ; idx < m_draw_buffers_count ; + + idx )
{
properties . att_state [ render_targets [ idx ] ] . blendEnable = VK_FALSE ;
}
2016-03-20 02:26:51 +01:00
}
2017-06-25 23:14:56 +03:00
properties . cs . sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO ;
properties . cs . attachmentCount = m_draw_buffers_count ;
properties . cs . pAttachments = properties . att_state ;
if ( rsx : : method_registers . logic_op_enabled ( ) )
2016-03-20 02:26:51 +01:00
{
2017-06-25 23:14:56 +03:00
properties . cs . logicOpEnable = true ;
properties . cs . logicOp = vk : : get_logic_op ( rsx : : method_registers . logic_operation ( ) ) ;
2016-03-20 02:26:51 +01:00
}
2017-06-25 23:14:56 +03:00
properties . ds . sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO ;
properties . ds . depthWriteEnable = rsx : : method_registers . depth_write_enabled ( ) ? VK_TRUE : VK_FALSE ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
if ( rsx : : method_registers . depth_bounds_test_enabled ( ) )
{
properties . ds . depthBoundsTestEnable = VK_TRUE ;
properties . ds . minDepthBounds = rsx : : method_registers . depth_bounds_min ( ) ;
properties . ds . maxDepthBounds = rsx : : method_registers . depth_bounds_max ( ) ;
}
else
properties . ds . depthBoundsTestEnable = VK_FALSE ;
2016-06-13 19:10:59 +08:00
2017-06-25 23:14:56 +03:00
if ( rsx : : method_registers . stencil_test_enabled ( ) )
{
properties . ds . stencilTestEnable = VK_TRUE ;
properties . ds . front . writeMask = rsx : : method_registers . stencil_mask ( ) ;
properties . ds . front . compareMask = rsx : : method_registers . stencil_func_mask ( ) ;
properties . ds . front . reference = rsx : : method_registers . stencil_func_ref ( ) ;
properties . ds . front . failOp = vk : : get_stencil_op ( rsx : : method_registers . stencil_op_fail ( ) ) ;
properties . ds . front . passOp = vk : : get_stencil_op ( rsx : : method_registers . stencil_op_zpass ( ) ) ;
properties . ds . front . depthFailOp = vk : : get_stencil_op ( rsx : : method_registers . stencil_op_zfail ( ) ) ;
properties . ds . front . compareOp = vk : : get_compare_func ( rsx : : method_registers . stencil_func ( ) ) ;
if ( rsx : : method_registers . two_sided_stencil_test_enabled ( ) )
{
properties . ds . back . writeMask = rsx : : method_registers . back_stencil_mask ( ) ;
properties . ds . back . compareMask = rsx : : method_registers . back_stencil_func_mask ( ) ;
properties . ds . back . reference = rsx : : method_registers . back_stencil_func_ref ( ) ;
properties . ds . back . failOp = vk : : get_stencil_op ( rsx : : method_registers . back_stencil_op_fail ( ) ) ;
properties . ds . back . passOp = vk : : get_stencil_op ( rsx : : method_registers . back_stencil_op_zpass ( ) ) ;
properties . ds . back . depthFailOp = vk : : get_stencil_op ( rsx : : method_registers . back_stencil_op_zfail ( ) ) ;
properties . ds . back . compareOp = vk : : get_compare_func ( rsx : : method_registers . back_stencil_func ( ) ) ;
}
else
properties . ds . back = properties . ds . front ;
}
else
properties . ds . stencilTestEnable = VK_FALSE ;
2016-06-26 23:37:02 +02:00
2017-06-25 23:14:56 +03:00
if ( rsx : : method_registers . depth_test_enabled ( ) )
2016-06-13 19:10:59 +08:00
{
2017-06-25 23:14:56 +03:00
properties . ds . depthTestEnable = VK_TRUE ;
properties . ds . depthCompareOp = vk : : get_compare_func ( rsx : : method_registers . depth_func ( ) ) ;
2016-06-13 19:10:59 +08:00
}
else
2017-06-25 23:14:56 +03:00
properties . ds . depthTestEnable = VK_FALSE ;
2016-06-26 23:37:02 +02:00
2017-06-25 23:14:56 +03:00
properties . rs . sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO ;
properties . rs . polygonMode = VK_POLYGON_MODE_FILL ;
properties . rs . depthClampEnable = VK_FALSE ;
properties . rs . rasterizerDiscardEnable = VK_FALSE ;
properties . rs . depthBiasEnable = VK_FALSE ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
if ( rsx : : method_registers . cull_face_enabled ( ) )
properties . rs . cullMode = vk : : get_cull_face ( rsx : : method_registers . cull_face_mode ( ) ) ;
else
properties . rs . cullMode = VK_CULL_MODE_NONE ;
2016-07-17 19:57:50 +03:00
2017-06-25 23:14:56 +03:00
properties . rs . frontFace = vk : : get_front_face ( rsx : : method_registers . front_face_mode ( ) ) ;
2016-06-26 23:37:02 +02:00
2017-06-25 23:14:56 +03:00
size_t idx = vk : : get_render_pass_location (
vk : : get_compatible_surface_format ( rsx : : method_registers . surface_color ( ) ) . first ,
vk : : get_compatible_depth_surface_format ( m_optimal_tiling_supported_formats , rsx : : method_registers . surface_depth_fmt ( ) ) ,
( u8 ) vk : : get_draw_buffers ( rsx : : method_registers . surface_color_target ( ) ) . size ( ) ) ;
2016-06-26 23:37:02 +02:00
2017-06-25 23:14:56 +03:00
properties . render_pass = m_render_passes [ idx ] ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
properties . num_targets = m_draw_buffers_count ;
2016-03-20 02:26:51 +01:00
2017-06-25 23:14:56 +03:00
vk : : enter_uninterruptible ( ) ;
2017-04-23 17:17:05 +03:00
2017-06-25 23:14:56 +03:00
//Load current program from buffer
m_program = m_prog_buffer . getGraphicPipelineState ( vertex_program , fragment_program , properties , * m_device , pipeline_layout ) . get ( ) ;
}
else
vk : : enter_uninterruptible ( ) ;
2016-02-21 18:50:49 +03:00
//TODO: Update constant buffers..
//1. Update scale-offset matrix
//2. Update vertex constants
//3. Update fragment constants
2016-03-14 22:29:18 +01:00
const size_t scale_offset_offset = m_uniform_buffer_ring_info . alloc < 256 > ( 256 ) ;
2016-04-02 00:18:20 +02:00
u8 * buf = ( u8 * ) m_uniform_buffer_ring_info . map ( scale_offset_offset , 256 ) ;
2016-02-21 18:50:49 +03:00
/**
2016-06-26 23:37:02 +02:00
* NOTE : While VK ' s coord system resembles GLs , the clip volume is no longer symetrical in z
* Its like D3D without the flip in y ( depending on how you build the spir - v )
*/
2017-06-14 18:47:01 +03:00
fill_scale_offset_data ( buf , false ) ;
2017-04-25 05:32:39 -05:00
fill_user_clip_data ( buf + 64 ) ;
2016-02-21 18:50:49 +03:00
2016-04-02 00:18:20 +02:00
m_uniform_buffer_ring_info . unmap ( ) ;
2016-02-21 18:50:49 +03:00
2017-06-25 23:14:56 +03:00
m_program - > bind_uniform ( { m_uniform_buffer_ring_info . heap - > value , scale_offset_offset , 256 } , SCALE_OFFSET_BIND_SLOT , descriptor_sets ) ;
2016-02-21 18:50:49 +03:00
2017-06-25 23:14:56 +03:00
if ( ! fast_update | | m_transform_constants_dirty )
{
const size_t vertex_constants_offset = m_uniform_buffer_ring_info . alloc < 256 > ( 512 * 4 * sizeof ( float ) ) ;
buf = ( u8 * ) m_uniform_buffer_ring_info . map ( vertex_constants_offset , 512 * 4 * sizeof ( float ) ) ;
fill_vertex_program_constants_data ( buf ) ;
* ( reinterpret_cast < u32 * > ( buf + ( 468 * 4 * sizeof ( float ) ) ) ) = rsx : : method_registers . transform_branch_bits ( ) ;
m_uniform_buffer_ring_info . unmap ( ) ;
2016-07-24 19:28:49 +03:00
2017-06-25 23:14:56 +03:00
m_program - > bind_uniform ( { m_uniform_buffer_ring_info . heap - > value , vertex_constants_offset , 512 * 4 * sizeof ( float ) } , VERTEX_CONSTANT_BUFFERS_BIND_SLOT , descriptor_sets ) ;
m_transform_constants_dirty = false ;
}
2017-02-10 12:08:46 +03:00
2017-06-25 23:14:56 +03:00
if ( ! fast_update )
{
const size_t fragment_constants_sz = m_prog_buffer . get_fragment_constants_buffer_size ( fragment_program ) ;
const size_t fragment_buffer_sz = fragment_constants_sz + ( 17 * 4 * sizeof ( float ) ) ;
const size_t fragment_constants_offset = m_uniform_buffer_ring_info . alloc < 256 > ( fragment_buffer_sz ) ;
2016-02-21 18:50:49 +03:00
2017-06-25 23:14:56 +03:00
buf = ( u8 * ) m_uniform_buffer_ring_info . map ( fragment_constants_offset , fragment_buffer_sz ) ;
if ( fragment_constants_sz )
m_prog_buffer . fill_fragment_constants_buffer ( { reinterpret_cast < float * > ( buf ) , : : narrow < int > ( fragment_constants_sz ) } , fragment_program ) ;
fill_fragment_state_buffer ( buf + fragment_constants_sz , fragment_program ) ;
m_uniform_buffer_ring_info . unmap ( ) ;
m_program - > bind_uniform ( { m_uniform_buffer_ring_info . heap - > value , fragment_constants_offset , fragment_buffer_sz } , FRAGMENT_CONSTANT_BUFFERS_BIND_SLOT , descriptor_sets ) ;
}
2016-02-21 18:50:49 +03:00
2017-04-23 17:17:05 +03:00
vk : : leave_uninterruptible ( ) ;
2016-02-21 18:50:49 +03:00
return true ;
}
static const u32 mr_color_offset [ rsx : : limits : : color_buffers_count ] =
{
NV4097_SET_SURFACE_COLOR_AOFFSET ,
NV4097_SET_SURFACE_COLOR_BOFFSET ,
NV4097_SET_SURFACE_COLOR_COFFSET ,
NV4097_SET_SURFACE_COLOR_DOFFSET
} ;
static const u32 mr_color_dma [ rsx : : limits : : color_buffers_count ] =
{
NV4097_SET_CONTEXT_DMA_COLOR_A ,
NV4097_SET_CONTEXT_DMA_COLOR_B ,
NV4097_SET_CONTEXT_DMA_COLOR_C ,
NV4097_SET_CONTEXT_DMA_COLOR_D
} ;
static const u32 mr_color_pitch [ rsx : : limits : : color_buffers_count ] =
{
NV4097_SET_SURFACE_PITCH_A ,
NV4097_SET_SURFACE_PITCH_B ,
NV4097_SET_SURFACE_PITCH_C ,
NV4097_SET_SURFACE_PITCH_D
} ;
void VKGSRender : : init_buffers ( bool skip_reading )
{
2017-04-22 21:30:16 +03:00
//Clear any pending swap requests
process_swap_request ( ) ;
2016-02-21 18:50:49 +03:00
prepare_rtts ( ) ;
if ( ! skip_reading )
{
read_buffers ( ) ;
}
set_viewport ( ) ;
}
void VKGSRender : : read_buffers ( )
{
}
void VKGSRender : : write_buffers ( )
{
}
2017-04-21 22:55:05 +03:00
void VKGSRender : : close_and_submit_command_buffer ( const std : : vector < VkSemaphore > & semaphores , VkFence fence , VkPipelineStageFlags pipeline_stage_flags )
2016-03-23 23:22:33 +01:00
{
2017-04-22 21:30:16 +03:00
CHECK_RESULT ( vkEndCommandBuffer ( * m_current_command_buffer ) ) ;
2016-03-23 23:22:33 +01:00
2017-04-22 21:30:16 +03:00
VkCommandBuffer cmd = * m_current_command_buffer ;
2016-03-23 23:22:33 +01:00
VkSubmitInfo infos = { } ;
infos . commandBufferCount = 1 ;
infos . pCommandBuffers = & cmd ;
2017-04-21 22:55:05 +03:00
infos . pWaitDstStageMask = & pipeline_stage_flags ;
2016-03-23 23:22:33 +01:00
infos . pWaitSemaphores = semaphores . data ( ) ;
2016-03-30 20:23:40 +02:00
infos . waitSemaphoreCount = static_cast < uint32_t > ( semaphores . size ( ) ) ;
2016-03-23 23:22:33 +01:00
infos . sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ;
CHECK_RESULT ( vkQueueSubmit ( m_swap_chain - > get_present_queue ( ) , 1 , & infos , fence ) ) ;
}
void VKGSRender : : open_command_buffer ( )
2016-02-21 18:50:49 +03:00
{
2016-03-12 22:22:28 +01:00
VkCommandBufferInheritanceInfo inheritance_info = { } ;
2016-02-21 18:50:49 +03:00
inheritance_info . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO ;
2016-03-12 22:22:28 +01:00
VkCommandBufferBeginInfo begin_infos = { } ;
2016-02-21 18:50:49 +03:00
begin_infos . pInheritanceInfo = & inheritance_info ;
begin_infos . sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO ;
2016-03-23 23:22:33 +01:00
begin_infos . flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT ;
2017-04-22 21:30:16 +03:00
CHECK_RESULT ( vkBeginCommandBuffer ( * m_current_command_buffer , & begin_infos ) ) ;
2016-02-21 18:50:49 +03:00
}
void VKGSRender : : prepare_rtts ( )
{
if ( ! m_rtts_dirty )
return ;
2017-06-25 23:14:56 +03:00
close_render_pass ( ) ;
2017-04-21 16:35:13 +03:00
copy_render_targets_to_dma_location ( ) ;
2016-02-21 18:50:49 +03:00
m_rtts_dirty = false ;
2016-06-26 23:37:02 +02:00
u32 clip_width = rsx : : method_registers . surface_clip_width ( ) ;
u32 clip_height = rsx : : method_registers . surface_clip_height ( ) ;
u32 clip_x = rsx : : method_registers . surface_clip_origin_x ( ) ;
u32 clip_y = rsx : : method_registers . surface_clip_origin_y ( ) ;
2016-02-21 18:50:49 +03:00
2017-07-05 01:16:59 +03:00
if ( clip_width = = 0 | | clip_height = = 0 )
{
LOG_ERROR ( RSX , " Invalid framebuffer setup, w=%d, h=%d " , clip_width , clip_height ) ;
framebuffer_status_valid = false ;
}
framebuffer_status_valid = true ;
2017-04-21 16:35:13 +03:00
auto surface_addresses = get_color_surface_addresses ( ) ;
auto zeta_address = get_zeta_surface_address ( ) ;
const u32 surface_pitchs [ ] = { rsx : : method_registers . surface_a_pitch ( ) , rsx : : method_registers . surface_b_pitch ( ) ,
rsx : : method_registers . surface_c_pitch ( ) , rsx : : method_registers . surface_d_pitch ( ) } ;
2017-04-22 21:30:16 +03:00
m_rtts . prepare_render_target ( & * m_current_command_buffer ,
2016-06-26 23:37:02 +02:00
rsx : : method_registers . surface_color ( ) , rsx : : method_registers . surface_depth_fmt ( ) ,
2017-04-21 16:35:13 +03:00
clip_width , clip_height ,
2016-06-26 23:37:02 +02:00
rsx : : method_registers . surface_color_target ( ) ,
2017-04-21 16:35:13 +03:00
surface_addresses , zeta_address ,
2017-04-22 21:30:16 +03:00
( * m_device ) , & * m_current_command_buffer , m_optimal_tiling_supported_formats , m_memory_type_mapping ) ;
2016-02-21 18:50:49 +03:00
2017-04-21 16:35:13 +03:00
//Reset framebuffer information
for ( u8 i = 0 ; i < rsx : : limits : : color_buffers_count ; + + i )
{
m_surface_info [ i ] . address = m_surface_info [ i ] . pitch = 0 ;
m_surface_info [ i ] . width = clip_width ;
m_surface_info [ i ] . height = clip_height ;
m_surface_info [ i ] . color_format = rsx : : method_registers . surface_color ( ) ;
}
m_depth_surface_info . address = m_depth_surface_info . pitch = 0 ;
m_depth_surface_info . width = clip_width ;
m_depth_surface_info . height = clip_height ;
m_depth_surface_info . depth_format = rsx : : method_registers . surface_depth_fmt ( ) ;
2016-02-21 18:50:49 +03:00
//Bind created rtts as current fbo...
2016-06-26 23:37:02 +02:00
std : : vector < u8 > draw_buffers = vk : : get_draw_buffers ( rsx : : method_registers . surface_color_target ( ) ) ;
2016-03-16 00:42:40 +01:00
std : : vector < std : : unique_ptr < vk : : image_view > > fbo_images ;
2016-02-21 18:50:49 +03:00
2016-06-26 23:37:02 +02:00
for ( u8 index : draw_buffers )
2016-02-21 18:50:49 +03:00
{
2016-03-16 00:42:40 +01:00
vk : : image * raw = std : : get < 1 > ( m_rtts . m_bound_render_targets [ index ] ) ;
2016-03-21 18:20:30 +01:00
VkImageSubresourceRange subres = { } ;
subres . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
subres . baseArrayLayer = 0 ;
subres . baseMipLevel = 0 ;
subres . layerCount = 1 ;
subres . levelCount = 1 ;
2016-03-16 00:42:40 +01:00
fbo_images . push_back ( std : : make_unique < vk : : image_view > ( * m_device , raw - > value , VK_IMAGE_VIEW_TYPE_2D , raw - > info . format , vk : : default_component_map ( ) , subres ) ) ;
2017-04-21 16:35:13 +03:00
m_surface_info [ index ] . address = surface_addresses [ index ] ;
m_surface_info [ index ] . pitch = surface_pitchs [ index ] ;
if ( surface_pitchs [ index ] < = 64 )
{
if ( clip_width > surface_pitchs [ index ] )
//Ignore this buffer (usually set to 64)
m_surface_info [ index ] . pitch = 0 ;
}
2016-02-21 18:50:49 +03:00
}
2016-03-30 20:23:40 +02:00
m_draw_buffers_count = static_cast < u32 > ( fbo_images . size ( ) ) ;
2016-03-21 18:20:30 +01:00
2016-02-21 18:50:49 +03:00
if ( std : : get < 1 > ( m_rtts . m_bound_depth_stencil ) ! = nullptr )
{
2016-03-16 00:42:40 +01:00
vk : : image * raw = ( std : : get < 1 > ( m_rtts . m_bound_depth_stencil ) ) ;
2016-03-21 18:20:30 +01:00
VkImageSubresourceRange subres = { } ;
2016-06-26 23:37:02 +02:00
subres . aspectMask = ( rsx : : method_registers . surface_depth_fmt ( ) = = rsx : : surface_depth_format : : z24s8 ) ? ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) : VK_IMAGE_ASPECT_DEPTH_BIT ;
2016-03-21 18:20:30 +01:00
subres . baseArrayLayer = 0 ;
subres . baseMipLevel = 0 ;
subres . layerCount = 1 ;
subres . levelCount = 1 ;
2016-03-16 00:42:40 +01:00
fbo_images . push_back ( std : : make_unique < vk : : image_view > ( * m_device , raw - > value , VK_IMAGE_VIEW_TYPE_2D , raw - > info . format , vk : : default_component_map ( ) , subres ) ) ;
2017-04-21 16:35:13 +03:00
m_depth_surface_info . address = zeta_address ;
m_depth_surface_info . pitch = rsx : : method_registers . surface_z_pitch ( ) ;
if ( m_depth_surface_info . pitch < = 64 & & clip_width > m_depth_surface_info . pitch )
m_depth_surface_info . pitch = 0 ;
}
2017-05-20 14:45:02 +03:00
if ( g_cfg . video . write_color_buffers )
2017-04-21 16:35:13 +03:00
{
for ( u8 index : draw_buffers )
{
if ( ! m_surface_info [ index ] . address | | ! m_surface_info [ index ] . pitch ) continue ;
const u32 range = m_surface_info [ index ] . pitch * m_surface_info [ index ] . height ;
m_texture_cache . lock_memory_region ( std : : get < 1 > ( m_rtts . m_bound_render_targets [ index ] ) , m_surface_info [ index ] . address , range ,
m_surface_info [ index ] . width , m_surface_info [ index ] . height ) ;
}
}
2017-05-20 14:45:02 +03:00
if ( g_cfg . video . write_depth_buffer )
2017-04-21 16:35:13 +03:00
{
if ( m_depth_surface_info . address & & m_depth_surface_info . pitch )
{
u32 pitch = m_depth_surface_info . width * 2 ;
if ( m_depth_surface_info . depth_format ! = rsx : : surface_depth_format : : z16 ) pitch * = 2 ;
const u32 range = pitch * m_depth_surface_info . height ;
m_texture_cache . lock_memory_region ( std : : get < 1 > ( m_rtts . m_bound_depth_stencil ) , m_depth_surface_info . address , range ,
m_depth_surface_info . width , m_depth_surface_info . height ) ;
}
2016-02-21 18:50:49 +03:00
}
2016-06-26 23:37:02 +02:00
size_t idx = vk : : get_render_pass_location ( vk : : get_compatible_surface_format ( rsx : : method_registers . surface_color ( ) ) . first , vk : : get_compatible_depth_surface_format ( m_optimal_tiling_supported_formats , rsx : : method_registers . surface_depth_fmt ( ) ) , ( u8 ) draw_buffers . size ( ) ) ;
2016-03-17 21:31:34 +01:00
VkRenderPass current_render_pass = m_render_passes [ idx ] ;
2016-02-21 18:50:49 +03:00
2016-03-21 18:20:30 +01:00
m_framebuffer_to_clean . push_back ( std : : make_unique < vk : : framebuffer > ( * m_device , current_render_pass , clip_width , clip_height , std : : move ( fbo_images ) ) ) ;
2016-02-21 18:50:49 +03:00
}
void VKGSRender : : flip ( int buffer )
{
2017-06-30 01:20:23 +03:00
if ( skip_frame )
{
m_frame - > flip ( m_context ) ;
rsx : : thread : : flip ( buffer ) ;
if ( ! skip_frame )
{
m_draw_calls = 0 ;
m_instanced_draws = 0 ;
m_draw_time = 0 ;
m_setup_time = 0 ;
m_vertex_upload_time = 0 ;
m_textures_upload_time = 0 ;
}
return ;
}
2016-06-16 18:51:56 +03:00
bool resize_screen = false ;
2016-02-21 18:50:49 +03:00
2016-07-02 17:27:53 +03:00
if ( m_client_height ! = m_frame - > client_height ( ) | |
m_client_width ! = m_frame - > client_width ( ) )
{
if ( ! ! m_frame - > client_height ( ) & & ! ! m_frame - > client_width ( ) )
resize_screen = true ;
2016-06-16 18:51:56 +03:00
}
2016-02-21 18:50:49 +03:00
2017-01-28 17:00:49 -06:00
std : : chrono : : time_point < steady_clock > flip_start = steady_clock : : now ( ) ;
2016-10-11 03:55:42 +03:00
2017-06-25 23:14:56 +03:00
close_render_pass ( ) ;
2017-04-22 21:30:16 +03:00
process_swap_request ( ) ;
2016-06-16 18:51:56 +03:00
if ( ! resize_screen )
2016-02-21 18:50:49 +03:00
{
2016-06-16 18:51:56 +03:00
u32 buffer_width = gcm_buffers [ buffer ] . width ;
u32 buffer_height = gcm_buffers [ buffer ] . height ;
u32 buffer_pitch = gcm_buffers [ buffer ] . pitch ;
areai screen_area = coordi ( { } , { ( int ) buffer_width , ( int ) buffer_height } ) ;
coordi aspect_ratio ;
2016-04-27 01:27:24 +03:00
sizei csize = { m_frame - > client_width ( ) , m_frame - > client_height ( ) } ;
2016-02-21 18:50:49 +03:00
sizei new_size = csize ;
2017-07-08 21:06:11 +08:00
if ( ! g_cfg . video . stretch_to_display_area )
2016-02-21 18:50:49 +03:00
{
2017-07-08 21:06:11 +08:00
const double aq = ( double ) buffer_width / buffer_height ;
const double rq = ( double ) new_size . width / new_size . height ;
const double q = aq / rq ;
if ( q > 1.0 )
{
new_size . height = int ( new_size . height / q ) ;
aspect_ratio . y = ( csize . height - new_size . height ) / 2 ;
}
else if ( q < 1.0 )
{
new_size . width = int ( new_size . width * q ) ;
aspect_ratio . x = ( csize . width - new_size . width ) / 2 ;
}
2016-02-21 18:50:49 +03:00
}
aspect_ratio . size = new_size ;
2016-06-16 18:51:56 +03:00
//Prepare surface for new frame
CHECK_RESULT ( vkAcquireNextImageKHR ( ( * m_device ) , ( * m_swap_chain ) , 0 , m_present_semaphore , VK_NULL_HANDLE , & m_current_present_image ) ) ;
2016-02-21 18:50:49 +03:00
2016-06-16 18:51:56 +03:00
//Blit contents to screen..
VkImage image_to_flip = nullptr ;
2016-03-17 21:31:34 +01:00
2016-06-16 18:51:56 +03:00
if ( std : : get < 1 > ( m_rtts . m_bound_render_targets [ 0 ] ) ! = nullptr )
image_to_flip = std : : get < 1 > ( m_rtts . m_bound_render_targets [ 0 ] ) - > value ;
else if ( std : : get < 1 > ( m_rtts . m_bound_render_targets [ 1 ] ) ! = nullptr )
image_to_flip = std : : get < 1 > ( m_rtts . m_bound_render_targets [ 1 ] ) - > value ;
2016-02-21 18:50:49 +03:00
2016-06-16 18:51:56 +03:00
VkImage target_image = m_swap_chain - > get_swap_chain_image ( m_current_present_image ) ;
if ( image_to_flip )
{
2017-04-22 21:30:16 +03:00
vk : : copy_scaled_image ( * m_current_command_buffer , image_to_flip , target_image , VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL , VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ,
2016-07-30 05:07:39 +03:00
0 , 0 , buffer_width , buffer_height , aspect_ratio . x , aspect_ratio . y , aspect_ratio . width , aspect_ratio . height , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ;
2016-06-16 18:51:56 +03:00
}
else
{
//No draw call was issued!
VkImageSubresourceRange range = vk : : get_image_subresource_range ( 0 , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ;
VkClearColorValue clear_black = { 0 } ;
2017-04-22 21:30:16 +03:00
vk : : change_image_layout ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( m_current_present_image ) , VK_IMAGE_LAYOUT_PRESENT_SRC_KHR , VK_IMAGE_LAYOUT_GENERAL , range ) ;
vkCmdClearColorImage ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( m_current_present_image ) , VK_IMAGE_LAYOUT_GENERAL , & clear_black , 1 , & range ) ;
vk : : change_image_layout ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( m_current_present_image ) , VK_IMAGE_LAYOUT_GENERAL , VK_IMAGE_LAYOUT_PRESENT_SRC_KHR , range ) ;
2016-06-16 18:51:56 +03:00
}
2016-02-21 18:50:49 +03:00
2016-10-11 03:55:42 +03:00
std : : unique_ptr < vk : : framebuffer > direct_fbo ;
std : : vector < std : : unique_ptr < vk : : image_view > > swap_image_view ;
2017-05-20 14:45:02 +03:00
if ( g_cfg . video . overlay )
2016-10-11 03:55:42 +03:00
{
//Change the image layout whilst setting up a dependency on waiting for the blit op to finish before we start writing
auto subres = vk : : get_image_subresource_range ( 0 , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ;
VkImageMemoryBarrier barrier = { } ;
barrier . sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER ;
barrier . newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ;
barrier . oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ;
barrier . image = target_image ;
barrier . srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT ;
barrier . dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT ;
barrier . dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED ;
barrier . srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED ;
barrier . subresourceRange = subres ;
2017-04-22 21:30:16 +03:00
vkCmdPipelineBarrier ( * m_current_command_buffer , VK_PIPELINE_STAGE_TRANSFER_BIT , VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , VK_DEPENDENCY_BY_REGION_BIT , 0 , nullptr , 0 , nullptr , 1 , & barrier ) ;
2016-10-11 03:55:42 +03:00
size_t idx = vk : : get_render_pass_location ( m_swap_chain - > get_surface_format ( ) , VK_FORMAT_UNDEFINED , 1 ) ;
VkRenderPass single_target_pass = m_render_passes [ idx ] ;
swap_image_view . push_back ( std : : make_unique < vk : : image_view > ( * m_device , target_image , VK_IMAGE_VIEW_TYPE_2D , m_swap_chain - > get_surface_format ( ) , vk : : default_component_map ( ) , subres ) ) ;
direct_fbo . reset ( new vk : : framebuffer ( * m_device , single_target_pass , m_client_width , m_client_height , std : : move ( swap_image_view ) ) ) ;
2017-06-25 23:14:56 +03:00
m_text_writer - > print_text ( * m_current_command_buffer , * direct_fbo , 0 , 0 , direct_fbo - > width ( ) , direct_fbo - > height ( ) , " draw calls: " + std : : to_string ( m_draw_calls ) + " , instanced repeats: " + std : : to_string ( m_instanced_draws ) ) ;
2017-04-22 21:30:16 +03:00
m_text_writer - > print_text ( * m_current_command_buffer , * direct_fbo , 0 , 18 , direct_fbo - > width ( ) , direct_fbo - > height ( ) , " draw call setup: " + std : : to_string ( m_setup_time ) + " us " ) ;
m_text_writer - > print_text ( * m_current_command_buffer , * direct_fbo , 0 , 36 , direct_fbo - > width ( ) , direct_fbo - > height ( ) , " vertex upload time: " + std : : to_string ( m_vertex_upload_time ) + " us " ) ;
m_text_writer - > print_text ( * m_current_command_buffer , * direct_fbo , 0 , 54 , direct_fbo - > width ( ) , direct_fbo - > height ( ) , " texture upload time: " + std : : to_string ( m_textures_upload_time ) + " us " ) ;
m_text_writer - > print_text ( * m_current_command_buffer , * direct_fbo , 0 , 72 , direct_fbo - > width ( ) , direct_fbo - > height ( ) , " draw call execution: " + std : : to_string ( m_draw_time ) + " us " ) ;
m_text_writer - > print_text ( * m_current_command_buffer , * direct_fbo , 0 , 90 , direct_fbo - > width ( ) , direct_fbo - > height ( ) , " submit and flip: " + std : : to_string ( m_flip_time ) + " us " ) ;
2016-10-11 03:55:42 +03:00
2017-04-22 21:30:16 +03:00
vk : : change_image_layout ( * m_current_command_buffer , target_image , VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL , VK_IMAGE_LAYOUT_PRESENT_SRC_KHR , subres ) ;
2016-10-11 03:55:42 +03:00
}
2017-06-07 22:45:02 +03:00
m_framebuffer_to_clean . push_back ( std : : move ( direct_fbo ) ) ;
2017-04-22 21:30:16 +03:00
queue_swap_request ( ) ;
2016-03-17 21:31:34 +01:00
}
else
{
2016-06-16 18:51:56 +03:00
/**
* Since we are about to destroy the old swapchain and its images , we just discard the commandbuffer .
* Waiting for the commands to process does not work reliably as the fence can be signaled before swap images are released
* and there are no explicit methods to ensure that the presentation engine is not using the images at all .
*/
2017-04-22 21:30:16 +03:00
//NOTE: This operation will create a hard sync point
CHECK_RESULT ( vkEndCommandBuffer ( * m_current_command_buffer ) ) ;
2016-07-02 17:27:53 +03:00
//Will have to block until rendering is completed
VkFence resize_fence = VK_NULL_HANDLE ;
VkFenceCreateInfo infos = { } ;
infos . sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO ;
vkQueueWaitIdle ( m_swap_chain - > get_present_queue ( ) ) ;
vkDeviceWaitIdle ( * m_device ) ;
vkCreateFence ( ( * m_device ) , & infos , nullptr , & resize_fence ) ;
//Wait for all grpahics tasks to complete
VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT ;
VkSubmitInfo submit_infos = { } ;
submit_infos . commandBufferCount = 0 ;
submit_infos . pCommandBuffers = nullptr ;
submit_infos . pWaitDstStageMask = & pipe_stage_flags ;
submit_infos . pWaitSemaphores = nullptr ;
submit_infos . waitSemaphoreCount = 0 ;
submit_infos . sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ;
CHECK_RESULT ( vkQueueSubmit ( m_swap_chain - > get_present_queue ( ) , 1 , & submit_infos , resize_fence ) ) ;
vkWaitForFences ( ( * m_device ) , 1 , & resize_fence , VK_TRUE , UINT64_MAX ) ;
vkResetFences ( ( * m_device ) , 1 , & resize_fence ) ;
vkDeviceWaitIdle ( * m_device ) ;
//Rebuild swapchain. Old swapchain destruction is handled by the init_swapchain call
m_client_width = m_frame - > client_width ( ) ;
m_client_height = m_frame - > client_height ( ) ;
m_swap_chain - > init_swapchain ( m_client_width , m_client_height ) ;
//Prepare new swapchain images for use
2017-04-22 21:30:16 +03:00
m_current_command_buffer - > reset ( ) ;
2016-07-02 17:27:53 +03:00
open_command_buffer ( ) ;
for ( u32 i = 0 ; i < m_swap_chain - > get_swap_image_count ( ) ; + + i )
{
2017-04-22 21:30:16 +03:00
vk : : change_image_layout ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( i ) ,
2016-07-02 17:27:53 +03:00
VK_IMAGE_LAYOUT_UNDEFINED , VK_IMAGE_LAYOUT_GENERAL ,
vk : : get_image_subresource_range ( 0 , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ) ;
VkClearColorValue clear_color { } ;
auto range = vk : : get_image_subresource_range ( 0 , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ;
2017-04-22 21:30:16 +03:00
vkCmdClearColorImage ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( i ) , VK_IMAGE_LAYOUT_GENERAL , & clear_color , 1 , & range ) ;
vk : : change_image_layout ( * m_current_command_buffer , m_swap_chain - > get_swap_chain_image ( i ) ,
2016-07-02 17:27:53 +03:00
VK_IMAGE_LAYOUT_GENERAL , VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ,
vk : : get_image_subresource_range ( 0 , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ) ) ;
}
//Flush the command buffer
close_and_submit_command_buffer ( { } , resize_fence ) ;
CHECK_RESULT ( vkWaitForFences ( ( * m_device ) , 1 , & resize_fence , VK_TRUE , UINT64_MAX ) ) ;
2016-06-16 18:51:56 +03:00
vkDestroyFence ( ( * m_device ) , resize_fence , nullptr ) ;
2017-04-22 21:30:16 +03:00
m_current_command_buffer - > reset ( ) ;
open_command_buffer ( ) ;
//Do cleanup
m_swap_command_buffer = m_current_command_buffer ;
process_swap_request ( ) ;
2016-03-17 21:31:34 +01:00
}
2016-02-21 18:50:49 +03:00
2017-01-28 17:00:49 -06:00
std : : chrono : : time_point < steady_clock > flip_end = steady_clock : : now ( ) ;
2016-10-11 03:55:42 +03:00
m_flip_time = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( flip_end - flip_start ) . count ( ) ;
2017-06-10 23:32:17 +03:00
m_uniform_buffer_ring_info . reset_allocation_stats ( ) ;
m_index_buffer_ring_info . reset_allocation_stats ( ) ;
m_attrib_ring_info . reset_allocation_stats ( ) ;
m_texture_upload_buffer_ring_info . reset_allocation_stats ( ) ;
2016-02-21 18:50:49 +03:00
2017-06-30 01:20:23 +03:00
//NOTE:Resource destruction is handled within the real swap handler
m_frame - > flip ( m_context ) ;
rsx : : thread : : flip ( buffer ) ;
//Do not reset perf counters if we are skipping the next frame
if ( skip_frame ) return ;
2016-03-23 23:16:49 +01:00
2016-02-21 18:50:49 +03:00
m_draw_calls = 0 ;
2017-06-25 23:14:56 +03:00
m_instanced_draws = 0 ;
2016-10-11 03:55:42 +03:00
m_draw_time = 0 ;
m_setup_time = 0 ;
m_vertex_upload_time = 0 ;
m_textures_upload_time = 0 ;
2016-02-21 18:50:49 +03:00
}