diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 488fd639aa..77e33d1354 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -649,11 +649,17 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset return; } if (device.IsExtExtendedDynamicStateSupported()) { - scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { + const bool dynamic_stride = !device.IsExtVertexInputDynamicStateSupported(); + scheduler.Record([index, buffer, offset, size, stride, + dynamic_stride](vk::CommandBuffer cmdbuf) { const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE; const VkDeviceSize vk_stride = stride; - cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); + const VkDeviceSize* strides = nullptr; + if (dynamic_stride) { + strides = &vk_stride; + } + cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, strides); }); } else { if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) { @@ -688,10 +694,14 @@ void BufferCacheRuntime::RecordVertexBuffers(const VideoCommon::HostBindings& infos); bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; } + u32 VertexAttributeMask() const noexcept { + return vertex_attribute_mask; + } bool SupportsAlphaToCoverage() const noexcept { return fragment_has_color0_output; } @@ -168,6 +171,7 @@ private: size_t num_image_elements{}; u32 num_textures{}; bool fragment_has_color0_output{}; + u32 vertex_attribute_mask{}; vk::DescriptorSetLayout descriptor_set_layout; DescriptorAllocator descriptor_allocator; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 18d715f058..30ae972dfc 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -264,7 +264,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, bool skip_empty, Func&& draw if (!pipeline->Configure(is_indexed)) return; - UpdateDynamicStates(pipeline->HasDynamicVertexInput()); + UpdateDynamicStates(*pipeline); query_cache.NotifySegment(true); HandleTransformFeedback(); @@ -383,13 +383,6 @@ void RasterizerVulkan::DrawTexture() { texture_cache.SynchronizeDescriptors(false); texture_cache.UpdateRenderTargets(false); - bool dynamic_vertex_input = false; - if (device.IsExtVertexInputDynamicStateSupported()) { - GraphicsPipeline* const gp = pipeline_cache.CurrentGraphicsPipeline(); - dynamic_vertex_input = gp && gp->HasDynamicVertexInput(); - } - UpdateDynamicStates(dynamic_vertex_input); - const auto& draw_texture_state = maxwell3d->draw_manager.draw_texture_state; const auto& sampler = texture_cache.GetSampler(draw_texture_state.src_sampler, false); const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); @@ -1078,7 +1071,7 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info, return DmaBufferImageCopy(copy_info, buffer_operand, image_operand); } -void RasterizerVulkan::UpdateDynamicStates(bool dynamic_vertex_input) { +void RasterizerVulkan::UpdateDynamicStates(const GraphicsPipeline& pipeline) { auto& regs = maxwell3d->regs; auto& flags = maxwell3d->dirty.flags; const auto topology = maxwell3d->draw_manager.draw_state.topology; @@ -1126,27 +1119,13 @@ void RasterizerVulkan::UpdateDynamicStates(bool dynamic_vertex_input) { } if (device.IsExtExtendedDynamicState3EnablesSupported()) { - using namespace Tegra::Engines; - // AMD Workaround: LogicOp incompatible with float render targets - if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || - device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) { - const auto has_float = std::any_of( - regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), - [](const auto& attrib) { - return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float; - } - ); - if (regs.logic_op.enable) { - regs.logic_op.enable = static_cast(!has_float); - } - } UpdateLogicOpEnable(regs); UpdateDepthClampEnable(regs); UpdateLineRasterizationMode(regs); UpdateLineStippleEnable(regs); UpdateConservativeRasterizationMode(regs); - UpdateAlphaToCoverageEnable(regs); - UpdateAlphaToOneEnable(regs); + UpdateAlphaToCoverageEnable(regs, pipeline); + UpdateAlphaToOneEnable(regs, pipeline); } if (device.IsExtExtendedDynamicState3BlendingSupported()) { @@ -1155,8 +1134,8 @@ void RasterizerVulkan::UpdateDynamicStates(bool dynamic_vertex_input) { UpdateColorWriteEnable(regs); } - if (dynamic_vertex_input) { - UpdateVertexInput(regs); + if (pipeline.HasDynamicVertexInput()) { + UpdateVertexInput(regs, pipeline.VertexAttributeMask()); } } @@ -1690,36 +1669,33 @@ void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& r [is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); }); } -void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchAlphaToCoverageEnable()) { - return; - } +void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs, + const GraphicsPipeline& pipeline) { if (!device.SupportsDynamicState3AlphaToCoverageEnable()) { return; } - GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); - const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToCoverage() && - regs.anti_alias_alpha_control.alpha_to_coverage != 0; + const bool enable = + pipeline.SupportsAlphaToCoverage() && regs.anti_alias_alpha_control.alpha_to_coverage != 0; + if (!state_tracker.TouchAlphaToCoverageEnable() && enable == alpha_to_coverage_enabled) { + return; + } + alpha_to_coverage_enabled = enable; scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetAlphaToCoverageEnableEXT(enable ? VK_TRUE : VK_FALSE); }); } -void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchAlphaToOneEnable()) { - return; - } +void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs, + const GraphicsPipeline& pipeline) { if (!device.SupportsDynamicState3AlphaToOneEnable()) { - static std::once_flag warn_alpha_to_one; - std::call_once(warn_alpha_to_one, [] { - LOG_WARNING(Render_Vulkan, - "Alpha-to-one is not supported on this device; forcing it disabled"); - }); return; } - GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); - const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToOne() && - regs.anti_alias_alpha_control.alpha_to_one != 0; + const bool enable = + pipeline.SupportsAlphaToOne() && regs.anti_alias_alpha_control.alpha_to_one != 0; + if (!state_tracker.TouchAlphaToOneEnable() && enable == alpha_to_one_enabled) { + return; + } + alpha_to_one_enabled = enable; scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetAlphaToOneEnableEXT(enable ? VK_TRUE : VK_FALSE); }); @@ -1793,7 +1769,17 @@ void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) { } void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchBlending()) { + u8 integer_targets = 0; + for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { + const auto format = regs.rt[index].format; + if (format != Tegra::RenderTargetFormat::NONE && + IsPixelFormatInteger(VideoCore::Surface::PixelFormatFromRenderTargetFormat(format))) { + integer_targets |= static_cast(1u << index); + } + } + const bool targets_changed = + std::exchange(blend_integer_targets, integer_targets) != integer_targets; + if (!state_tracker.TouchBlending() && !targets_changed) { return; } @@ -1820,17 +1806,12 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { }); } - if (state_tracker.TouchBlendEnable()) { + if (state_tracker.TouchBlendEnable() || targets_changed) { std::array setup_enables{}; for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - bool is_integer = false; - if (regs.rt[index].format != Tegra::RenderTargetFormat::NONE) { - const auto format = - VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[index].format); - is_integer = IsPixelFormatInteger(format); - } + const bool is_integer = ((integer_targets >> index) & 1) != 0; setup_enables[index] = - (!is_integer && regs.blend.enable[index] != 0) ? VK_TRUE : VK_FALSE; + static_cast(!is_integer && regs.blend.enable[index] != 0); } scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { cmdbuf.SetColorBlendEnableEXT(0, setup_enables); @@ -1903,18 +1884,9 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& }); } -void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { +void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, + u32 attribute_mask) { auto& dirty{maxwell3d->dirty.flags}; - const bool vertex_input_dirty = dirty[Dirty::VertexInput]; - const bool vertex_buffers_dirty = dirty[VideoCommon::Dirty::VertexBuffers]; - if (!vertex_input_dirty && !vertex_buffers_dirty) { - return; - } - dirty[Dirty::VertexInput] = false; - - boost::container::static_vector bindings; - boost::container::static_vector attributes; - const u32 max_attributes = static_cast(std::min(Maxwell::NumVertexAttributes, device.GetMaxVertexInputAttributes())); @@ -1922,11 +1894,25 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) static_cast(std::min(Maxwell::NumVertexArrays, device.GetMaxVertexInputBindings())); + bool changed = dirty[Dirty::VertexInput] || vertex_input_attributes != attribute_mask; + dirty[Dirty::VertexInput] = false; + vertex_input_attributes = attribute_mask; + for (u32 binding = 0; binding < max_bindings; ++binding) { + const u32 stride = regs.vertex_streams[binding].stride; + changed |= vertex_input_strides[binding] != stride; + vertex_input_strides[binding] = stride; + } + if (!changed) { + return; + } + + boost::container::static_vector bindings; + boost::container::static_vector attributes; for (u32 index = 0; index < max_attributes; ++index) { const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; const u32 binding{attribute.buffer}; - if (attribute.constant || binding >= max_bindings) { + if (((attribute_mask >> index) & 1) == 0 || attribute.constant || binding >= max_bindings) { continue; } attributes.push_back({ @@ -1941,17 +1927,19 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) for (u32 binding = 0; binding < max_bindings; ++binding) { const auto& input_binding{regs.vertex_streams[binding]}; - const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; + VkVertexInputRate input_rate = VK_VERTEX_INPUT_RATE_VERTEX; u32 divisor = 1; - if (is_instanced) { + if (regs.vertex_stream_instances.IsInstancingEnabled(binding) && + input_binding.frequency != 0) { + input_rate = VK_VERTEX_INPUT_RATE_INSTANCE; divisor = (std::min)(input_binding.frequency, device.GetMaxVertexAttribDivisor()); } bindings.push_back({ .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT, .pNext = nullptr, .binding = binding, - .stride = input_binding.stride, - .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, + .stride = vertex_input_strides[binding], + .inputRate = input_rate, .divisor = divisor, }); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index e1f14cdbf3..b05d12e2f1 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -158,7 +158,7 @@ private: void FlushWork(); - void UpdateDynamicStates(bool dynamic_vertex_input); + void UpdateDynamicStates(const GraphicsPipeline& pipeline); void HandleTransformFeedback(); @@ -183,8 +183,10 @@ private: void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs, + const GraphicsPipeline& pipeline); + void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs, + const GraphicsPipeline& pipeline); void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); @@ -192,7 +194,7 @@ private: void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, u32 attribute_mask); Tegra::GPU& gpu; Tegra::MaxwellDeviceMemoryManager& device_memory; @@ -226,6 +228,11 @@ private: u32 draw_counter = 0; Tegra::DepthFormat depth_bias_zeta_format{}; + std::array vertex_input_strides{}; + u32 vertex_input_attributes{}; + u8 blend_integer_targets{}; + bool alpha_to_coverage_enabled{}; + bool alpha_to_one_enabled{}; }; } // namespace Vulkan diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 58218a91fb..b3969fd77d 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1127,6 +1127,9 @@ bool Device::GetSuitability(bool requires_swapchain) { properties.properties = properties2.properties; max_vertex_attrib_divisor = (std::max)(1U, properties.vertex_attribute_divisor.maxVertexAttribDivisor); + if (!features.vertex_attribute_divisor.vertexAttributeInstanceRateDivisor) { + max_vertex_attrib_divisor = 1; + } // Unload extensions if feature support is insufficient. RemoveUnsuitableExtensions(); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 88504926c8..fa230fa0f4 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -76,6 +76,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \ FEATURE(EXT, TexelBufferAlignment, TEXEL_BUFFER_ALIGNMENT, texel_buffer_alignment) \ FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \ + FEATURE(EXT, VertexAttributeDivisor, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \ FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \ FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ @@ -96,7 +97,6 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \ EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ EXTENSION(EXT, TOOLING_INFO, tooling_info) \ - EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ EXTENSION(KHR, COPY_COMMANDS_2, copy_commands2) \ EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \ EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \