diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index f7a3c6a696..7178a7fd0d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -25,6 +26,7 @@ #include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/polygon_mode_utils.h" #include "video_core/shader_notify.h" +#include "video_core/texture_cache/samples_helper.h" #include "video_core/texture_cache/texture_cache.h" #include "video_core/vulkan_common/vulkan_device.h" @@ -779,11 +781,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { const bool supports_alpha_output = fragment_has_color0_output; const bool alpha_to_one_supported = device.SupportsAlphaToOne(); - const VkPipelineMultisampleStateCreateInfo multisample_ci{ + const auto msaa_mode = key.state.msaa_mode.Value(); + const VkSampleCountFlagBits vk_samples = MaxwellToVK::MsaaMode(msaa_mode); + VkPipelineMultisampleStateCreateInfo multisample_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, - .rasterizationSamples = MaxwellToVK::MsaaMode(key.state.msaa_mode), + .rasterizationSamples = vk_samples, .sampleShadingEnable = Settings::values.sample_shading.GetValue() ? VK_TRUE : VK_FALSE, .minSampleShading = static_cast(Settings::values.sample_shading_fraction.GetValue()) / 100.0f, .pSampleMask = nullptr, @@ -792,6 +796,27 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .alphaToOneEnable = supports_alpha_output && alpha_to_one_supported && key.state.alpha_to_one_enabled != 0 ? VK_TRUE : VK_FALSE, }; + + std::array default_sample_locations{}; + VkSampleLocationsInfoEXT sample_locations_info{ + .sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT, + .pNext = nullptr, + .sampleLocationsPerPixel = vk_samples, + .sampleLocationGridSize = {1u, 1u}, + .sampleLocationsCount = static_cast(VideoCommon::NumSamples(msaa_mode)), + .pSampleLocations = default_sample_locations.data(), + }; + VkPipelineSampleLocationsStateCreateInfoEXT sample_locations_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT, + .pNext = nullptr, + .sampleLocationsEnable = VK_FALSE, + .sampleLocationsInfo = sample_locations_info, + }; + if (device.IsExtSampleLocationsSupported() && sample_locations_info.sampleLocationsCount > 1 && + device.SupportsSampleLocationsFor(vk_samples)) { + sample_locations_ci.sampleLocationsEnable = VK_TRUE; + sample_locations_ci.pNext = std::exchange(multisample_ci.pNext, &sample_locations_ci); + } const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .pNext = nullptr, @@ -941,6 +966,10 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { } } + if (sample_locations_ci.sampleLocationsEnable) { + dynamic_states.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT); + } + const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 140d220307..29f959eef9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -37,6 +37,7 @@ #include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/shader_cache.h" #include "video_core/texture_cache/texture_cache_base.h" +#include "video_core/texture_cache/samples_helper.h" #include "video_core/polygon_mode_utils.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -1020,6 +1021,7 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateDepthBounds(regs); UpdateStencilFaces(regs); UpdateLineWidth(regs); + UpdateSampleLocations(regs); // EDS1: CullMode, DepthCompare, FrontFace, StencilOp, DepthBoundsTest, DepthTest, DepthWrite, StencilTest if (device.IsExtExtendedDynamicStateSupported()) { @@ -1375,6 +1377,58 @@ void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); }); } +void RasterizerVulkan::UpdateSampleLocations(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!device.IsExtSampleLocationsSupported()) { + state_tracker.TouchSampleLocations(); + return; + } + if (!state_tracker.TouchSampleLocations()) { + return; + } + + const auto msaa_mode = regs.anti_alias_samples_mode; + const u32 sample_count = static_cast(VideoCommon::NumSamples(msaa_mode)); + if (sample_count <= 1) { + return; + } + + const VkSampleCountFlagBits vk_samples = MaxwellToVK::MsaaMode(msaa_mode); + if (!device.SupportsSampleLocationsFor(vk_samples)) { + return; + } + + const auto& props = device.SampleLocationProperties(); + std::array locations{}; + constexpr float unit = 1.0f / 16.0f; + const auto clamp_coord = [&](float coord) { + return std::clamp(coord, props.sampleLocationCoordinateRange[0], + props.sampleLocationCoordinateRange[1]); + }; + + for (u32 sample_index = 0; sample_index < sample_count; ++sample_index) { + const auto& packed = regs.multisample_sample_locations[sample_index / 4]; + const auto [raw_x, raw_y] = packed.Location(sample_index % 4); + const float x = clamp_coord((static_cast(raw_x) - 8) * unit); + const float y = clamp_coord((static_cast(raw_y) - 8) * unit); + locations[sample_index] = VkSampleLocationEXT{.x = x, .y = y}; + } + + VkSampleLocationsInfoEXT info{ + .sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT, + .pNext = nullptr, + .sampleLocationsPerPixel = vk_samples, + .sampleLocationGridSize = {1u, 1u}, + .sampleLocationsCount = sample_count, + .pSampleLocations = locations.data(), + }; + + scheduler.Record([info, locations](vk::CommandBuffer cmdbuf) mutable { + auto info_copy = info; + info_copy.pSampleLocations = locations.data(); + cmdbuf.SetSampleLocationsEXT(info_copy); + }); +} + void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchCullMode()) { return; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b689c6b660..dc9a41e606 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -168,6 +168,7 @@ private: void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateSampleLocations(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 79967d540a..b73fb5a92a 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -40,6 +40,7 @@ Flags MakeInvalidationFlags() { StencilWriteMask, StencilCompare, LineWidth, + SampleLocations, CullMode, DepthBoundsEnable, DepthTestEnable, @@ -129,6 +130,12 @@ void SetupDirtyLineWidth(Tables& tables) { tables[0][OFF(line_width_aliased)] = LineWidth; } +void SetupDirtySampleLocations(Tables& tables) { + tables[0][OFF(anti_alias_samples_mode)] = SampleLocations; + FillBlock(tables[0], OFF(multisample_sample_locations), + NUM(multisample_sample_locations), SampleLocations); +} + void SetupDirtyCullMode(Tables& tables) { auto& table = tables[0]; table[OFF(gl_cull_face)] = CullMode; @@ -246,6 +253,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { SetupDirtyDepthBounds(tables); SetupDirtyStencilProperties(tables); SetupDirtyLineWidth(tables); + SetupDirtySampleLocations(tables); SetupDirtyCullMode(tables); SetupDirtyStateEnable(tables); SetupDirtyDepthCompareOp(tables); diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 74bae9e181..36fc818aa5 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -42,6 +42,7 @@ enum : u8 { StencilWriteMask, StencilCompare, LineWidth, + SampleLocations, CullMode, DepthBoundsEnable, @@ -185,6 +186,10 @@ public: return Exchange(Dirty::LineWidth, false); } + bool TouchSampleLocations() { + return Exchange(Dirty::SampleLocations, false); + } + bool TouchCullMode() { return Exchange(Dirty::CullMode, false); } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 62a7b1602c..af2eab9700 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1108,6 +1108,11 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; SetNext(next, properties.transform_feedback); } + if (extensions.sample_locations) { + properties.sample_locations.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT; + SetNext(next, properties.sample_locations); + } if (extensions.maintenance5) { properties.maintenance5.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR; @@ -1379,6 +1384,11 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); + // VK_EXT_sample_locations + extensions.sample_locations = features.sample_locations.sampleLocations; + RemoveExtensionFeatureIfUnsuitable(extensions.sample_locations, features.sample_locations, + VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME); + // VK_EXT_vertex_input_dynamic_state extensions.vertex_input_dynamic_state = features.vertex_input_dynamic_state.vertexInputDynamicState; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 7dd329c021..6357fbfb69 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -63,6 +63,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \ FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \ FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \ + FEATURE(EXT, SampleLocations, SAMPLE_LOCATIONS, sample_locations) \ FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \ FEATURE(EXT, SwapchainMaintenance1, SWAPCHAIN_MAINTENANCE_1, swapchain_maintenance1) \ FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \ @@ -342,6 +343,11 @@ public: return properties.float_controls; } + /// Returns sample location properties (VK_EXT_sample_locations). + const VkPhysicalDeviceSampleLocationsPropertiesEXT& SampleLocationProperties() const { + return properties.sample_locations; + } + /// Returns true if ASTC is natively supported. bool IsOptimalAstcSupported() const { return features.features.textureCompressionASTC_LDR; @@ -550,6 +556,17 @@ public: return extensions.transform_feedback; } + /// Returns true if the device supports VK_EXT_sample_locations. + bool IsExtSampleLocationsSupported() const { + return extensions.sample_locations; + } + + /// Returns true if the device supports custom sample locations for the given sample count. + bool SupportsSampleLocationsFor(VkSampleCountFlagBits samples) const { + return extensions.sample_locations && + (properties.sample_locations.sampleLocationSampleCounts & samples) != 0; + } + /// Returns true if the device supports VK_EXT_transform_feedback properly. bool AreTransformFeedbackGeometryStreamsSupported() const { return features.transform_feedback.geometryStreams; @@ -1017,6 +1034,7 @@ private: VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{}; VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; + VkPhysicalDeviceSampleLocationsPropertiesEXT sample_locations{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; VkPhysicalDeviceMultiDrawPropertiesEXT multi_draw{}; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 9ee93653d9..d0acc3ee2e 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -166,6 +166,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetColorWriteMaskEXT); X(vkCmdSetColorBlendEnableEXT); X(vkCmdSetColorBlendEquationEXT); + X(vkCmdSetSampleLocationsEXT); X(vkCmdResolveImage); X(vkCreateBuffer); X(vkCreateBufferView); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 39717affa1..ae013d5214 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -266,6 +266,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{}; PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{}; PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{}; + PFN_vkCmdSetSampleLocationsEXT vkCmdSetSampleLocationsEXT{}; PFN_vkCmdWaitEvents vkCmdWaitEvents{}; PFN_vkCreateBuffer vkCreateBuffer{}; PFN_vkCreateBufferView vkCreateBufferView{}; @@ -1525,6 +1526,10 @@ public: dld->vkCmdSetColorBlendEquationEXT(handle, first, equations.size(), equations.data()); } + void SetSampleLocationsEXT(const VkSampleLocationsInfoEXT& info) const noexcept { + dld->vkCmdSetSampleLocationsEXT(handle, &info); + } + void SetLineWidth(float line_width) const noexcept { dld->vkCmdSetLineWidth(handle, line_width); }