@ -186,13 +186,22 @@ bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) {
scissor . max_y < regs . zeta_height ;
}
template < std : : size_t N >
std : : array < VkDeviceSize , N > ExpandStrides ( const std : : array < u16 , N > & strides ) {
std : : array < VkDeviceSize , N > expanded ;
std : : copy ( strides . begin ( ) , strides . end ( ) , expanded . begin ( ) ) ;
return expanded ;
}
} // Anonymous namespace
class BufferBindings final {
public :
void AddVertexBinding ( VkBuffer buffer , VkDeviceSize offset ) {
void AddVertexBinding ( VkBuffer buffer , VkDeviceSize offset , VkDeviceSize size , u32 stride ) {
vertex . buffers [ vertex . num_buffers ] = buffer ;
vertex . offsets [ vertex . num_buffers ] = offset ;
vertex . sizes [ vertex . num_buffers ] = size ;
vertex . strides [ vertex . num_buffers ] = static_cast < u16 > ( stride ) ;
+ + vertex . num_buffers ;
}
@ -202,76 +211,76 @@ public:
index . type = type ;
}
void Bind ( VKScheduler & scheduler ) const {
void Bind ( const VKDevice & device , VKScheduler & scheduler ) const {
// Use this large switch case to avoid dispatching more memory in the record lambda than
// what we need. It looks horrible, but it's the best we can do on standard C++.
switch ( vertex . num_buffers ) {
case 0 :
return BindStatic < 0 > ( scheduler) ;
return BindStatic < 0 > ( device, scheduler) ;
case 1 :
return BindStatic < 1 > ( scheduler) ;
return BindStatic < 1 > ( device, scheduler) ;
case 2 :
return BindStatic < 2 > ( scheduler) ;
return BindStatic < 2 > ( device, scheduler) ;
case 3 :
return BindStatic < 3 > ( scheduler) ;
return BindStatic < 3 > ( device, scheduler) ;
case 4 :
return BindStatic < 4 > ( scheduler) ;
return BindStatic < 4 > ( device, scheduler) ;
case 5 :
return BindStatic < 5 > ( scheduler) ;
return BindStatic < 5 > ( device, scheduler) ;
case 6 :
return BindStatic < 6 > ( scheduler) ;
return BindStatic < 6 > ( device, scheduler) ;
case 7 :
return BindStatic < 7 > ( scheduler) ;
return BindStatic < 7 > ( device, scheduler) ;
case 8 :
return BindStatic < 8 > ( scheduler) ;
return BindStatic < 8 > ( device, scheduler) ;
case 9 :
return BindStatic < 9 > ( scheduler) ;
return BindStatic < 9 > ( device, scheduler) ;
case 10 :
return BindStatic < 10 > ( scheduler) ;
return BindStatic < 10 > ( device, scheduler) ;
case 11 :
return BindStatic < 11 > ( scheduler) ;
return BindStatic < 11 > ( device, scheduler) ;
case 12 :
return BindStatic < 12 > ( scheduler) ;
return BindStatic < 12 > ( device, scheduler) ;
case 13 :
return BindStatic < 13 > ( scheduler) ;
return BindStatic < 13 > ( device, scheduler) ;
case 14 :
return BindStatic < 14 > ( scheduler) ;
return BindStatic < 14 > ( device, scheduler) ;
case 15 :
return BindStatic < 15 > ( scheduler) ;
return BindStatic < 15 > ( device, scheduler) ;
case 16 :
return BindStatic < 16 > ( scheduler) ;
return BindStatic < 16 > ( device, scheduler) ;
case 17 :
return BindStatic < 17 > ( scheduler) ;
return BindStatic < 17 > ( device, scheduler) ;
case 18 :
return BindStatic < 18 > ( scheduler) ;
return BindStatic < 18 > ( device, scheduler) ;
case 19 :
return BindStatic < 19 > ( scheduler) ;
return BindStatic < 19 > ( device, scheduler) ;
case 20 :
return BindStatic < 20 > ( scheduler) ;
return BindStatic < 20 > ( device, scheduler) ;
case 21 :
return BindStatic < 21 > ( scheduler) ;
return BindStatic < 21 > ( device, scheduler) ;
case 22 :
return BindStatic < 22 > ( scheduler) ;
return BindStatic < 22 > ( device, scheduler) ;
case 23 :
return BindStatic < 23 > ( scheduler) ;
return BindStatic < 23 > ( device, scheduler) ;
case 24 :
return BindStatic < 24 > ( scheduler) ;
return BindStatic < 24 > ( device, scheduler) ;
case 25 :
return BindStatic < 25 > ( scheduler) ;
return BindStatic < 25 > ( device, scheduler) ;
case 26 :
return BindStatic < 26 > ( scheduler) ;
return BindStatic < 26 > ( device, scheduler) ;
case 27 :
return BindStatic < 27 > ( scheduler) ;
return BindStatic < 27 > ( device, scheduler) ;
case 28 :
return BindStatic < 28 > ( scheduler) ;
return BindStatic < 28 > ( device, scheduler) ;
case 29 :
return BindStatic < 29 > ( scheduler) ;
return BindStatic < 29 > ( device, scheduler) ;
case 30 :
return BindStatic < 30 > ( scheduler) ;
return BindStatic < 30 > ( device, scheduler) ;
case 31 :
return BindStatic < 31 > ( scheduler) ;
return BindStatic < 31 > ( device, scheduler) ;
case 32 :
return BindStatic < 32 > ( scheduler) ;
return BindStatic < 32 > ( device, scheduler) ;
}
UNREACHABLE ( ) ;
}
@ -282,6 +291,8 @@ private:
std : : size_t num_buffers = 0 ;
std : : array < VkBuffer , Maxwell : : NumVertexArrays > buffers ;
std : : array < VkDeviceSize , Maxwell : : NumVertexArrays > offsets ;
std : : array < VkDeviceSize , Maxwell : : NumVertexArrays > sizes ;
std : : array < u16 , Maxwell : : NumVertexArrays > strides ;
} vertex ;
struct {
@ -291,15 +302,23 @@ private:
} index ;
template < std : : size_t N >
void BindStatic ( VKScheduler & scheduler ) const {
if ( index . buffer ) {
BindStatic < N , true > ( scheduler ) ;
void BindStatic ( const VKDevice & device , VKScheduler & scheduler ) const {
if ( device . IsExtExtendedDynamicStateSupported ( ) ) {
if ( index . buffer ) {
BindStatic < N , true , true > ( scheduler ) ;
} else {
BindStatic < N , false , true > ( scheduler ) ;
}
} else {
BindStatic < N , false > ( scheduler ) ;
if ( index . buffer ) {
BindStatic < N , true , false > ( scheduler ) ;
} else {
BindStatic < N , false , false > ( scheduler ) ;
}
}
}
template < std : : size_t N , bool is_indexed >
template < std : : size_t N , bool is_indexed , bool has_extended_dynamic_state >
void BindStatic ( VKScheduler & scheduler ) const {
static_assert ( N < = Maxwell : : NumVertexArrays ) ;
if constexpr ( N = = 0 ) {
@ -311,6 +330,31 @@ private:
std : : copy ( vertex . buffers . begin ( ) , vertex . buffers . begin ( ) + N , buffers . begin ( ) ) ;
std : : copy ( vertex . offsets . begin ( ) , vertex . offsets . begin ( ) + N , offsets . begin ( ) ) ;
if constexpr ( has_extended_dynamic_state ) {
// With extended dynamic states we can specify the length and stride of a vertex buffer
// std::array<VkDeviceSize, N> sizes;
std : : array < u16 , N > strides ;
// std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
std : : copy ( vertex . strides . begin ( ) , vertex . strides . begin ( ) + N , strides . begin ( ) ) ;
if constexpr ( is_indexed ) {
scheduler . Record (
[ buffers , offsets , strides , index = index ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . BindIndexBuffer ( index . buffer , index . offset , index . type ) ;
cmdbuf . BindVertexBuffers2EXT ( 0 , static_cast < u32 > ( N ) , buffers . data ( ) ,
offsets . data ( ) , nullptr ,
ExpandStrides ( strides ) . data ( ) ) ;
} ) ;
} else {
scheduler . Record ( [ buffers , offsets , strides ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . BindVertexBuffers2EXT ( 0 , static_cast < u32 > ( N ) , buffers . data ( ) ,
offsets . data ( ) , nullptr ,
ExpandStrides ( strides ) . data ( ) ) ;
} ) ;
}
return ;
}
if constexpr ( is_indexed ) {
// Indexed draw
scheduler . Record ( [ buffers , offsets , index = index ] ( vk : : CommandBuffer cmdbuf ) {
@ -369,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
const auto & gpu = system . GPU ( ) . Maxwell3D ( ) ;
GraphicsPipelineCacheKey key ;
key . fixed_state . Fill ( gpu . regs );
key . fixed_state . Fill ( gpu . regs , device . IsExtExtendedDynamicStateSupported ( ) );
buffer_cache . Map ( CalculateGraphicsStreamBufferSize ( is_indexed ) ) ;
@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
UpdateDynamicStates ( ) ;
buffer_bindings . Bind ( scheduler) ;
buffer_bindings . Bind ( device, scheduler) ;
BeginTransformFeedback ( ) ;
@ -822,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
const auto & gpu = system . GPU ( ) . Maxwell3D ( ) ;
const auto & regs = gpu . regs ;
SetupVertexArrays ( fixed_state. vertex_input , buffer_bindings) ;
SetupVertexArrays ( buffer_bindings) ;
const u32 base_instance = regs . vb_base_instance ;
const u32 num_instances = is_instanced ? gpu . mme_draw . instance_count : 1 ;
@ -893,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateBlendConstants ( regs ) ;
UpdateDepthBounds ( regs ) ;
UpdateStencilFaces ( regs ) ;
if ( device . IsExtExtendedDynamicStateSupported ( ) ) {
UpdateCullMode ( regs ) ;
UpdateDepthBoundsTestEnable ( regs ) ;
UpdateDepthTestEnable ( regs ) ;
UpdateDepthWriteEnable ( regs ) ;
UpdateDepthCompareOp ( regs ) ;
UpdateFrontFace ( regs ) ;
UpdatePrimitiveTopology ( regs ) ;
UpdateStencilOp ( regs ) ;
UpdateStencilTestEnable ( regs ) ;
}
}
void RasterizerVulkan : : BeginTransformFeedback ( ) {
@ -940,41 +995,25 @@ void RasterizerVulkan::EndTransformFeedback() {
[ ] ( vk : : CommandBuffer cmdbuf ) { cmdbuf . EndTransformFeedbackEXT ( 0 , 0 , nullptr , nullptr ) ; } ) ;
}
void RasterizerVulkan : : SetupVertexArrays ( FixedPipelineState : : VertexInput & vertex_input ,
BufferBindings & buffer_bindings ) {
void RasterizerVulkan : : SetupVertexArrays ( BufferBindings & buffer_bindings ) {
const auto & regs = system . GPU ( ) . Maxwell3D ( ) . regs ;
for ( std : : size_t index = 0 ; index < Maxwell : : NumVertexAttributes ; + + index ) {
const auto & attrib = regs . vertex_attrib_format [ index ] ;
if ( attrib . IsConstant ( ) ) {
vertex_input . SetAttribute ( index , false , 0 , 0 , { } , { } ) ;
continue ;
}
vertex_input . SetAttribute ( index , true , attrib . buffer , attrib . offset , attrib . type . Value ( ) ,
attrib . size . Value ( ) ) ;
}
for ( std : : size_t index = 0 ; index < Maxwell : : NumVertexArrays ; + + index ) {
const auto & vertex_array = regs . vertex_array [ index ] ;
if ( ! vertex_array . IsEnabled ( ) ) {
vertex_input . SetBinding ( index , false , 0 , 0 ) ;
continue ;
}
vertex_input . SetBinding (
index , true , vertex_array . stride ,
regs . instanced_arrays . IsInstancingEnabled ( index ) ? vertex_array . divisor : 0 ) ;
const GPUVAddr start { vertex_array . StartAddress ( ) } ;
const GPUVAddr end { regs . vertex_array_limit [ index ] . LimitAddress ( ) } ;
ASSERT ( end > = start ) ;
const std : : size_t size { end - start } ;
const std : : size_t size = end - start ;
if ( size = = 0 ) {
buffer_bindings . AddVertexBinding ( DefaultBuffer ( ) , 0 );
buffer_bindings . AddVertexBinding ( DefaultBuffer ( ) , 0 , DEFAULT_BUFFER_SIZE , 0 );
continue ;
}
const auto info = buffer_cache . UploadMemory ( start , size ) ;
buffer_bindings . AddVertexBinding ( info . handle , info . offset );
buffer_bindings . AddVertexBinding ( info . handle , info . offset , size , vertex_array . stride );
}
}
@ -1326,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
}
}
void RasterizerVulkan : : UpdateCullMode ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchCullMode ( ) ) {
return ;
}
scheduler . Record (
[ enabled = regs . cull_test_enabled , cull_face = regs . cull_face ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetCullModeEXT ( enabled ? MaxwellToVK : : CullFace ( cull_face ) : VK_CULL_MODE_NONE ) ;
} ) ;
}
void RasterizerVulkan : : UpdateDepthBoundsTestEnable ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchDepthBoundsTestEnable ( ) ) {
return ;
}
scheduler . Record ( [ enable = regs . depth_bounds_enable ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetDepthBoundsTestEnableEXT ( enable ) ;
} ) ;
}
void RasterizerVulkan : : UpdateDepthTestEnable ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchDepthTestEnable ( ) ) {
return ;
}
scheduler . Record ( [ enable = regs . depth_test_enable ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetDepthTestEnableEXT ( enable ) ;
} ) ;
}
void RasterizerVulkan : : UpdateDepthWriteEnable ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchDepthWriteEnable ( ) ) {
return ;
}
scheduler . Record ( [ enable = regs . depth_write_enabled ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetDepthWriteEnableEXT ( enable ) ;
} ) ;
}
void RasterizerVulkan : : UpdateDepthCompareOp ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchDepthCompareOp ( ) ) {
return ;
}
scheduler . Record ( [ func = regs . depth_test_func ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetDepthCompareOpEXT ( MaxwellToVK : : ComparisonOp ( func ) ) ;
} ) ;
}
void RasterizerVulkan : : UpdateFrontFace ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchFrontFace ( ) ) {
return ;
}
VkFrontFace front_face = MaxwellToVK : : FrontFace ( regs . front_face ) ;
if ( regs . screen_y_control . triangle_rast_flip ! = 0 ) {
front_face = front_face = = VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
: VK_FRONT_FACE_CLOCKWISE ;
}
scheduler . Record (
[ front_face ] ( vk : : CommandBuffer cmdbuf ) { cmdbuf . SetFrontFaceEXT ( front_face ) ; } ) ;
}
void RasterizerVulkan : : UpdatePrimitiveTopology ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchPrimitiveTopology ( ) ) {
return ;
}
const Maxwell : : PrimitiveTopology primitive_topology = regs . draw . topology . Value ( ) ;
scheduler . Record ( [ this , primitive_topology ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetPrimitiveTopologyEXT ( MaxwellToVK : : PrimitiveTopology ( device , primitive_topology ) ) ;
} ) ;
}
void RasterizerVulkan : : UpdateStencilOp ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchStencilOp ( ) ) {
return ;
}
const Maxwell : : StencilOp fail = regs . stencil_front_op_fail ;
const Maxwell : : StencilOp zfail = regs . stencil_front_op_zfail ;
const Maxwell : : StencilOp zpass = regs . stencil_front_op_zpass ;
const Maxwell : : ComparisonOp compare = regs . stencil_front_func_func ;
if ( regs . stencil_two_side_enable ) {
scheduler . Record ( [ fail , zfail , zpass , compare ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetStencilOpEXT ( VK_STENCIL_FACE_FRONT_AND_BACK , MaxwellToVK : : StencilOp ( fail ) ,
MaxwellToVK : : StencilOp ( zpass ) , MaxwellToVK : : StencilOp ( zfail ) ,
MaxwellToVK : : ComparisonOp ( compare ) ) ;
} ) ;
} else {
const Maxwell : : StencilOp back_fail = regs . stencil_back_op_fail ;
const Maxwell : : StencilOp back_zfail = regs . stencil_back_op_zfail ;
const Maxwell : : StencilOp back_zpass = regs . stencil_back_op_zpass ;
const Maxwell : : ComparisonOp back_compare = regs . stencil_back_func_func ;
scheduler . Record ( [ fail , zfail , zpass , compare , back_fail , back_zfail , back_zpass ,
back_compare ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetStencilOpEXT ( VK_STENCIL_FACE_FRONT_BIT , MaxwellToVK : : StencilOp ( fail ) ,
MaxwellToVK : : StencilOp ( zpass ) , MaxwellToVK : : StencilOp ( zfail ) ,
MaxwellToVK : : ComparisonOp ( compare ) ) ;
cmdbuf . SetStencilOpEXT ( VK_STENCIL_FACE_BACK_BIT , MaxwellToVK : : StencilOp ( back_fail ) ,
MaxwellToVK : : StencilOp ( back_zpass ) ,
MaxwellToVK : : StencilOp ( back_zfail ) ,
MaxwellToVK : : ComparisonOp ( back_compare ) ) ;
} ) ;
}
}
void RasterizerVulkan : : UpdateStencilTestEnable ( Tegra : : Engines : : Maxwell3D : : Regs & regs ) {
if ( ! state_tracker . TouchStencilTestEnable ( ) ) {
return ;
}
scheduler . Record ( [ enable = regs . stencil_enable ] ( vk : : CommandBuffer cmdbuf ) {
cmdbuf . SetStencilTestEnableEXT ( enable ) ;
} ) ;
}
std : : size_t RasterizerVulkan : : CalculateGraphicsStreamBufferSize ( bool is_indexed ) const {
std : : size_t size = CalculateVertexArraysSize ( ) ;
if ( is_indexed ) {