mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	vk_rasterizer: Use VK_EXT_extended_dynamic_state
This commit is contained in:
		@@ -354,11 +354,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
 | 
			
		||||
    color_blend_ci.pAttachments = cb_attachments.data();
 | 
			
		||||
    std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
 | 
			
		||||
 | 
			
		||||
    static constexpr std::array dynamic_states = {
 | 
			
		||||
    std::vector dynamic_states = {
 | 
			
		||||
        VK_DYNAMIC_STATE_VIEWPORT,           VK_DYNAMIC_STATE_SCISSOR,
 | 
			
		||||
        VK_DYNAMIC_STATE_DEPTH_BIAS,         VK_DYNAMIC_STATE_BLEND_CONSTANTS,
 | 
			
		||||
        VK_DYNAMIC_STATE_DEPTH_BOUNDS,       VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
 | 
			
		||||
        VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE};
 | 
			
		||||
        VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
 | 
			
		||||
    };
 | 
			
		||||
    if (device.IsExtExtendedDynamicStateSupported()) {
 | 
			
		||||
        static constexpr std::array extended = {
 | 
			
		||||
            VK_DYNAMIC_STATE_CULL_MODE_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_FRONT_FACE_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
 | 
			
		||||
            VK_DYNAMIC_STATE_STENCIL_OP_EXT,
 | 
			
		||||
        };
 | 
			
		||||
        dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkPipelineDynamicStateCreateInfo dynamic_state_ci;
 | 
			
		||||
    dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
 | 
			
		||||
 
 | 
			
		||||
@@ -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, sizes, 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(), sizes.data(),
 | 
			
		||||
                                                     ExpandStrides(strides).data());
 | 
			
		||||
                    });
 | 
			
		||||
            } else {
 | 
			
		||||
                scheduler.Record([buffers, offsets, sizes, strides](vk::CommandBuffer cmdbuf) {
 | 
			
		||||
                    cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
 | 
			
		||||
                                                 offsets.data(), sizes.data(),
 | 
			
		||||
                                                 ExpandStrides(strides).data());
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if constexpr (is_indexed) {
 | 
			
		||||
            // Indexed draw
 | 
			
		||||
            scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) {
 | 
			
		||||
@@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
 | 
			
		||||
 | 
			
		||||
    UpdateDynamicStates();
 | 
			
		||||
 | 
			
		||||
    buffer_bindings.Bind(scheduler);
 | 
			
		||||
    buffer_bindings.Bind(device, scheduler);
 | 
			
		||||
 | 
			
		||||
    BeginTransformFeedback();
 | 
			
		||||
 | 
			
		||||
@@ -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() {
 | 
			
		||||
@@ -952,13 +1007,13 @@ void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1310,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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -245,6 +245,16 @@ private:
 | 
			
		||||
    void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
 | 
			
		||||
    void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
    void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
			
		||||
 | 
			
		||||
    std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
 | 
			
		||||
 | 
			
		||||
    std::size_t CalculateComputeStreamBufferSize() const;
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() {
 | 
			
		||||
    flags[BlendConstants] = true;
 | 
			
		||||
    flags[DepthBounds] = true;
 | 
			
		||||
    flags[StencilProperties] = true;
 | 
			
		||||
    flags[CullMode] = true;
 | 
			
		||||
    flags[DepthBoundsEnable] = true;
 | 
			
		||||
    flags[DepthTestEnable] = true;
 | 
			
		||||
    flags[DepthWriteEnable] = true;
 | 
			
		||||
    flags[DepthCompareOp] = true;
 | 
			
		||||
    flags[FrontFace] = true;
 | 
			
		||||
    flags[PrimitiveTopology] = true;
 | 
			
		||||
    flags[StencilOp] = true;
 | 
			
		||||
    flags[StencilTestEnable] = true;
 | 
			
		||||
    return flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) {
 | 
			
		||||
    table[OFF(stencil_back_func_mask)] = StencilProperties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyCullMode(Tables& tables) {
 | 
			
		||||
    auto& table = tables[0];
 | 
			
		||||
    table[OFF(cull_face)] = CullMode;
 | 
			
		||||
    table[OFF(cull_test_enabled)] = CullMode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyDepthBoundsEnable(Tables& tables) {
 | 
			
		||||
    tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyDepthTestEnable(Tables& tables) {
 | 
			
		||||
    tables[0][OFF(depth_test_enable)] = DepthTestEnable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyDepthWriteEnable(Tables& tables) {
 | 
			
		||||
    tables[0][OFF(depth_write_enabled)] = DepthWriteEnable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyDepthCompareOp(Tables& tables) {
 | 
			
		||||
    tables[0][OFF(depth_test_func)] = DepthCompareOp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyFrontFace(Tables& tables) {
 | 
			
		||||
    auto& table = tables[0];
 | 
			
		||||
    table[OFF(front_face)] = FrontFace;
 | 
			
		||||
    table[OFF(screen_y_control)] = FrontFace;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyPrimitiveTopology(Tables& tables) {
 | 
			
		||||
    tables[0][OFF(draw.topology)] = PrimitiveTopology;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyStencilOp(Tables& tables) {
 | 
			
		||||
    auto& table = tables[0];
 | 
			
		||||
    table[OFF(stencil_front_op_fail)] = StencilOp;
 | 
			
		||||
    table[OFF(stencil_front_op_zfail)] = StencilOp;
 | 
			
		||||
    table[OFF(stencil_front_op_zpass)] = StencilOp;
 | 
			
		||||
    table[OFF(stencil_front_func_func)] = StencilOp;
 | 
			
		||||
    table[OFF(stencil_back_op_fail)] = StencilOp;
 | 
			
		||||
    table[OFF(stencil_back_op_zfail)] = StencilOp;
 | 
			
		||||
    table[OFF(stencil_back_op_zpass)] = StencilOp;
 | 
			
		||||
    table[OFF(stencil_back_func_func)] = StencilOp;
 | 
			
		||||
 | 
			
		||||
    // Table 0 is used by StencilProperties
 | 
			
		||||
    tables[1][OFF(stencil_two_side_enable)] = StencilOp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupDirtyStencilTestEnable(Tables& tables) {
 | 
			
		||||
    tables[0][OFF(stencil_enable)] = StencilTestEnable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
StateTracker::StateTracker(Core::System& system)
 | 
			
		||||
@@ -90,6 +150,14 @@ void StateTracker::Initialize() {
 | 
			
		||||
    SetupDirtyBlendConstants(tables);
 | 
			
		||||
    SetupDirtyDepthBounds(tables);
 | 
			
		||||
    SetupDirtyStencilProperties(tables);
 | 
			
		||||
    SetupDirtyCullMode(tables);
 | 
			
		||||
    SetupDirtyDepthBoundsEnable(tables);
 | 
			
		||||
    SetupDirtyDepthTestEnable(tables);
 | 
			
		||||
    SetupDirtyDepthWriteEnable(tables);
 | 
			
		||||
    SetupDirtyDepthCompareOp(tables);
 | 
			
		||||
    SetupDirtyFrontFace(tables);
 | 
			
		||||
    SetupDirtyPrimitiveTopology(tables);
 | 
			
		||||
    SetupDirtyStencilOp(tables);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StateTracker::InvalidateCommandBufferState() {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,16 @@ enum : u8 {
 | 
			
		||||
    DepthBounds,
 | 
			
		||||
    StencilProperties,
 | 
			
		||||
 | 
			
		||||
    CullMode,
 | 
			
		||||
    DepthBoundsEnable,
 | 
			
		||||
    DepthTestEnable,
 | 
			
		||||
    DepthWriteEnable,
 | 
			
		||||
    DepthCompareOp,
 | 
			
		||||
    FrontFace,
 | 
			
		||||
    PrimitiveTopology,
 | 
			
		||||
    StencilOp,
 | 
			
		||||
    StencilTestEnable,
 | 
			
		||||
 | 
			
		||||
    Last
 | 
			
		||||
};
 | 
			
		||||
static_assert(Last <= std::numeric_limits<u8>::max());
 | 
			
		||||
@@ -64,6 +74,46 @@ public:
 | 
			
		||||
        return Exchange(Dirty::StencilProperties, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchCullMode() {
 | 
			
		||||
        return Exchange(Dirty::CullMode, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchDepthBoundsTestEnable() {
 | 
			
		||||
        return Exchange(Dirty::DepthBoundsEnable, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchDepthTestEnable() {
 | 
			
		||||
        return Exchange(Dirty::DepthTestEnable, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchDepthBoundsEnable() {
 | 
			
		||||
        return Exchange(Dirty::DepthBoundsEnable, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchDepthWriteEnable() {
 | 
			
		||||
        return Exchange(Dirty::DepthWriteEnable, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchDepthCompareOp() {
 | 
			
		||||
        return Exchange(Dirty::DepthCompareOp, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchFrontFace() {
 | 
			
		||||
        return Exchange(Dirty::FrontFace, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchPrimitiveTopology() {
 | 
			
		||||
        return Exchange(Dirty::PrimitiveTopology, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchStencilOp() {
 | 
			
		||||
        return Exchange(Dirty::StencilOp, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TouchStencilTestEnable() {
 | 
			
		||||
        return Exchange(Dirty::StencilTestEnable, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    bool Exchange(std::size_t id, bool new_value) const noexcept {
 | 
			
		||||
        auto& flags = system.GPU().Maxwell3D().dirty.flags;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user