diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 06cbd9e6da..6cfe53cd7c 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -105,22 +105,24 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe if (maxwell3d.dirty.flags[Dirty::VertexInput]) { if (features.has_dynamic_vertex_input) { // Dirty flag will be reset by the command buffer update - static constexpr std::array LUT{ - 0u, // Invalid - 1u, // SignedNorm - 1u, // UnsignedNorm - 2u, // SignedInt - 3u, // UnsignedInt - 1u, // UnsignedScaled - 1u, // SignedScaled - 1u, // Float - }; + // 0u, // Invalid + // 1u, // SignedNorm + // 2u, // UnsignedNorm + // 3u, // SignedInt + // 4u, // UnsignedInt + // 5u, // UnsignedScaled + // 6u, // SignedScaled + // 7u, // Float + // We sparsely store the bits for each of them, so if they clash we don't deal + // with the fixed pipeline taking in invalid vertices! :) const auto& attrs = regs.vertex_attrib_format; - attribute_types = 0; + attribute_types[0] = attribute_types[1] = attribute_types[2] = attribute_types[3] = 0; for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { - const u32 mask = attrs[i].constant != 0 ? 0 : 3; - const u32 type = LUT[static_cast(attrs[i].type.Value())]; - attribute_types |= static_cast(type & mask) << (i * 2); + u32 const mask = attrs[i].constant != 0 ? 0 : 0x07; // non-constant equates invalid + u32 const type = size_t(attrs[i].type.Value()); + attribute_types[0] |= u64((type >> 0) & 1) << i; + attribute_types[1] |= u64((type >> 1) & 1) << i; + attribute_types[2] |= u64((type >> 2) & 1) << i; } } else { maxwell3d.dirty.flags[Dirty::VertexInput] = false; diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index c5bc14f448..c5c84764e0 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -226,7 +226,7 @@ struct FixedPipelineState { std::array viewport_swizzles; union { - u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state + u64 attribute_types[3]; // Used with VK_EXT_vertex_input_dynamic_state u64 enabled_divisors; }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 60b899a811..0d47295cc1 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1758,61 +1758,60 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& } void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { - auto& dirty{maxwell3d->dirty.flags}; - if (!dirty[Dirty::VertexInput]) { - return; - } - dirty[Dirty::VertexInput] = false; - - boost::container::static_vector bindings; - boost::container::static_vector attributes; - - // There seems to be a bug on Nvidia's driver where updating only higher attributes ends up - // generating dirty state. Track the highest dirty attribute and update all attributes until - // that one. - size_t highest_dirty_attr{}; - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - if (dirty[Dirty::VertexAttribute0 + index]) { - highest_dirty_attr = index; + auto& dirty = maxwell3d->dirty.flags; + if (dirty[Dirty::VertexInput]) { + dirty[Dirty::VertexInput] = false; + boost::container::static_vector bindings; + boost::container::static_vector attributes; + // There seems to be a bug on Nvidia's driver where updating only higher attributes ends up + // generating dirty state. Track the highest dirty attribute and update all attributes until + // that one. + size_t highest_dirty_attr = 0; + if (device.IsNvidia()) { + for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) + if (dirty[Dirty::VertexAttribute0 + i]) + highest_dirty_attr = i; + } else { + highest_dirty_attr = Maxwell::NumVertexAttributes; } - } - for (size_t index = 0; index < highest_dirty_attr; ++index) { - const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; - const u32 binding{attribute.buffer}; - dirty[Dirty::VertexAttribute0 + index] = false; - dirty[Dirty::VertexBinding0 + static_cast(binding)] = true; - if (!attribute.constant) { - attributes.push_back({ - .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, - .pNext = nullptr, - .location = static_cast(index), - .binding = binding, - .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), - .offset = attribute.offset, - }); + for (size_t i = 0; i < highest_dirty_attr; ++i) { + if (dirty[Dirty::VertexAttribute0 + i]) { + dirty[Dirty::VertexAttribute0 + i] = false; + Maxwell::VertexAttribute const attribute = regs.vertex_attrib_format[i]; + u32 const binding = attribute.buffer; + dirty[Dirty::VertexBinding0 + size_t(binding)] = true; + if (!attribute.constant) { + attributes.push_back({ + .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, + .pNext = nullptr, + .location = u32(i), + .binding = binding, + .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), + .offset = attribute.offset, + }); + } + } } - } - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - if (!dirty[Dirty::VertexBinding0 + index]) { - continue; + for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { + if (dirty[Dirty::VertexBinding0 + i]) { + dirty[Dirty::VertexBinding0 + i] = false; + const u32 binding = u32(i); + const auto& input_binding = regs.vertex_streams[binding]; + const bool is_instanced = regs.vertex_stream_instances.IsInstancingEnabled(binding); + 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, + .divisor = is_instanced ? input_binding.frequency : 1, + }); + } } - dirty[Dirty::VertexBinding0 + index] = false; - - const u32 binding{static_cast(index)}; - const auto& input_binding{regs.vertex_streams[binding]}; - const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; - 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, - .divisor = is_instanced ? input_binding.frequency : 1, + scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { + cmdbuf.SetVertexInputEXT(bindings, attributes); }); } - scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { - cmdbuf.SetVertexInputEXT(bindings, attributes); - }); } void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) {