From 4a3026b16b0be26d80f5a7429755e19ee8d494a4 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Sun, 22 Dec 2019 22:48:43 -0300 Subject: fixed_pipeline_state: Define structure and loaders The intention behind this hasheable structure is to describe the state of fixed function pipeline state that gets compiled to a single graphics pipeline state object. This is all dynamic state in OpenGL but Vulkan wants it in an immutable state, even if hardware can edit it freely. In this commit the structure is defined in an optimized state (it uses booleans, has paddings and many data entries that can be packed to single integers). This is intentional as an initial implementation that is easier to debug, implement and review. It will be optimized in later stages, or it might change if Vulkan gets more dynamic states. --- .../renderer_vulkan/fixed_pipeline_state.cpp | 295 +++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 src/video_core/renderer_vulkan/fixed_pipeline_state.cpp (limited to 'src/video_core/renderer_vulkan/fixed_pipeline_state.cpp') diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp new file mode 100644 index 000000000..f75c348fe --- /dev/null +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -0,0 +1,295 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include + +#include "common/common_types.h" +#include "video_core/renderer_vulkan/fixed_pipeline_state.h" + +namespace Vulkan { + +namespace { + +constexpr FixedPipelineState::DepthStencil GetDepthStencilState(const Maxwell& regs) { + const FixedPipelineState::StencilFace front_stencil( + regs.stencil_front_op_fail, regs.stencil_front_op_zfail, regs.stencil_front_op_zpass, + regs.stencil_front_func_func); + const FixedPipelineState::StencilFace back_stencil = + regs.stencil_two_side_enable + ? FixedPipelineState::StencilFace(regs.stencil_back_op_fail, regs.stencil_back_op_zfail, + regs.stencil_back_op_zpass, + regs.stencil_back_func_func) + : front_stencil; + return FixedPipelineState::DepthStencil( + regs.depth_test_enable == 1, regs.depth_write_enabled == 1, regs.depth_bounds_enable == 1, + regs.stencil_enable == 1, regs.depth_test_func, front_stencil, back_stencil); +} + +constexpr FixedPipelineState::InputAssembly GetInputAssemblyState(const Maxwell& regs) { + return FixedPipelineState::InputAssembly( + regs.draw.topology, regs.primitive_restart.enabled, + regs.draw.topology == Maxwell::PrimitiveTopology::Points ? regs.point_size : 0.0f); +} + +constexpr FixedPipelineState::BlendingAttachment GetBlendingAttachmentState( + const Maxwell& regs, std::size_t render_target) { + const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : render_target]; + const std::array components = {mask.R != 0, mask.G != 0, mask.B != 0, mask.A != 0}; + + const FixedPipelineState::BlendingAttachment default_blending( + false, Maxwell::Blend::Equation::Add, Maxwell::Blend::Factor::One, + Maxwell::Blend::Factor::Zero, Maxwell::Blend::Equation::Add, Maxwell::Blend::Factor::One, + Maxwell::Blend::Factor::Zero, components); + if (render_target >= regs.rt_control.count) { + return default_blending; + } + + if (!regs.independent_blend_enable) { + const auto& src = regs.blend; + if (!src.enable[render_target]) { + return default_blending; + } + return FixedPipelineState::BlendingAttachment( + true, src.equation_rgb, src.factor_source_rgb, src.factor_dest_rgb, src.equation_a, + src.factor_source_a, src.factor_dest_a, components); + } + + if (!regs.blend.enable[render_target]) { + return default_blending; + } + const auto& src = regs.independent_blend[render_target]; + return FixedPipelineState::BlendingAttachment( + true, src.equation_rgb, src.factor_source_rgb, src.factor_dest_rgb, src.equation_a, + src.factor_source_a, src.factor_dest_a, components); +} + +constexpr FixedPipelineState::ColorBlending GetColorBlendingState(const Maxwell& regs) { + return FixedPipelineState::ColorBlending( + {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b, regs.blend_color.a}, + regs.rt_control.count, + {GetBlendingAttachmentState(regs, 0), GetBlendingAttachmentState(regs, 1), + GetBlendingAttachmentState(regs, 2), GetBlendingAttachmentState(regs, 3), + GetBlendingAttachmentState(regs, 4), GetBlendingAttachmentState(regs, 5), + GetBlendingAttachmentState(regs, 6), GetBlendingAttachmentState(regs, 7)}); +} + +constexpr FixedPipelineState::Tessellation GetTessellationState(const Maxwell& regs) { + return FixedPipelineState::Tessellation(regs.patch_vertices, regs.tess_mode.prim, + regs.tess_mode.spacing, regs.tess_mode.cw != 0); +} + +constexpr std::size_t Point = 0; +constexpr std::size_t Line = 1; +constexpr std::size_t Polygon = 2; +constexpr std::array PolygonOffsetEnableLUT = { + Point, // Points + Line, // Lines + Line, // LineLoop + Line, // LineStrip + Polygon, // Triangles + Polygon, // TriangleStrip + Polygon, // TriangleFan + Polygon, // Quads + Polygon, // QuadStrip + Polygon, // Polygon + Line, // LinesAdjacency + Line, // LineStripAdjacency + Polygon, // TrianglesAdjacency + Polygon, // TriangleStripAdjacency + Polygon, // Patches +}; + +constexpr FixedPipelineState::Rasterizer GetRasterizerState(const Maxwell& regs) { + const std::array enabled_lut = {regs.polygon_offset_point_enable, + regs.polygon_offset_line_enable, + regs.polygon_offset_fill_enable}; + const auto topology = static_cast(regs.draw.topology.Value()); + const bool depth_bias_enabled = enabled_lut[PolygonOffsetEnableLUT[topology]]; + + Maxwell::Cull::FrontFace front_face = regs.cull.front_face; + if (regs.screen_y_control.triangle_rast_flip != 0 && + regs.viewport_transform[0].scale_y > 0.0f) { + if (front_face == Maxwell::Cull::FrontFace::CounterClockWise) + front_face = Maxwell::Cull::FrontFace::ClockWise; + else if (front_face == Maxwell::Cull::FrontFace::ClockWise) + front_face = Maxwell::Cull::FrontFace::CounterClockWise; + } + + const bool gl_ndc = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne; + return FixedPipelineState::Rasterizer(regs.cull.enabled, depth_bias_enabled, gl_ndc, + regs.cull.cull_face, front_face); +} + +} // Anonymous namespace + +std::size_t FixedPipelineState::VertexBinding::Hash() const { + return (index << stride) ^ divisor; +} + +bool FixedPipelineState::VertexBinding::operator==(const VertexBinding& rhs) const { + return std::tie(index, stride, divisor) == std::tie(rhs.index, rhs.stride, rhs.divisor); +} + +std::size_t FixedPipelineState::VertexAttribute::Hash() const { + return static_cast(index) ^ (static_cast(buffer) << 13) ^ + (static_cast(type) << 22) ^ (static_cast(size) << 31) ^ + (static_cast(offset) << 36); +} + +bool FixedPipelineState::VertexAttribute::operator==(const VertexAttribute& rhs) const { + return std::tie(index, buffer, type, size, offset) == + std::tie(rhs.index, rhs.buffer, rhs.type, rhs.size, rhs.offset); +} + +std::size_t FixedPipelineState::StencilFace::Hash() const { + return static_cast(action_stencil_fail) ^ + (static_cast(action_depth_fail) << 4) ^ + (static_cast(action_depth_fail) << 20) ^ + (static_cast(action_depth_pass) << 36); +} + +bool FixedPipelineState::StencilFace::operator==(const StencilFace& rhs) const { + return std::tie(action_stencil_fail, action_depth_fail, action_depth_pass, test_func) == + std::tie(rhs.action_stencil_fail, rhs.action_depth_fail, rhs.action_depth_pass, + rhs.test_func); +} + +std::size_t FixedPipelineState::BlendingAttachment::Hash() const { + return static_cast(enable) ^ (static_cast(rgb_equation) << 5) ^ + (static_cast(src_rgb_func) << 10) ^ + (static_cast(dst_rgb_func) << 15) ^ + (static_cast(a_equation) << 20) ^ + (static_cast(src_a_func) << 25) ^ + (static_cast(dst_a_func) << 30) ^ + (static_cast(components[0]) << 35) ^ + (static_cast(components[1]) << 36) ^ + (static_cast(components[2]) << 37) ^ + (static_cast(components[3]) << 38); +} + +bool FixedPipelineState::BlendingAttachment::operator==(const BlendingAttachment& rhs) const { + return std::tie(enable, rgb_equation, src_rgb_func, dst_rgb_func, a_equation, src_a_func, + dst_a_func, components) == + std::tie(rhs.enable, rhs.rgb_equation, rhs.src_rgb_func, rhs.dst_rgb_func, + rhs.a_equation, rhs.src_a_func, rhs.dst_a_func, rhs.components); +} + +std::size_t FixedPipelineState::VertexInput::Hash() const { + std::size_t hash = num_bindings ^ (num_attributes << 32); + for (std::size_t i = 0; i < num_bindings; ++i) { + boost::hash_combine(hash, bindings[i].Hash()); + } + for (std::size_t i = 0; i < num_attributes; ++i) { + boost::hash_combine(hash, attributes[i].Hash()); + } + return hash; +} + +bool FixedPipelineState::VertexInput::operator==(const VertexInput& rhs) const { + return std::equal(bindings.begin(), bindings.begin() + num_bindings, rhs.bindings.begin(), + rhs.bindings.begin() + rhs.num_bindings) && + std::equal(attributes.begin(), attributes.begin() + num_attributes, + rhs.attributes.begin(), rhs.attributes.begin() + rhs.num_attributes); +} + +std::size_t FixedPipelineState::InputAssembly::Hash() const { + std::size_t point_size_int = 0; + std::memcpy(&point_size_int, &point_size, sizeof(point_size)); + return (static_cast(topology) << 24) ^ (point_size_int << 32) ^ + static_cast(primitive_restart_enable); +} + +bool FixedPipelineState::InputAssembly::operator==(const InputAssembly& rhs) const { + return std::tie(topology, primitive_restart_enable, point_size) == + std::tie(rhs.topology, rhs.primitive_restart_enable, rhs.point_size); +} + +std::size_t FixedPipelineState::Tessellation::Hash() const { + return static_cast(patch_control_points) ^ + (static_cast(primitive) << 6) ^ (static_cast(spacing) << 8) ^ + (static_cast(clockwise) << 10); +} + +bool FixedPipelineState::Tessellation::operator==(const Tessellation& rhs) const { + return std::tie(patch_control_points, primitive, spacing, clockwise) == + std::tie(rhs.patch_control_points, rhs.primitive, rhs.spacing, rhs.clockwise); +} + +std::size_t FixedPipelineState::Rasterizer::Hash() const { + return static_cast(cull_enable) ^ + (static_cast(depth_bias_enable) << 1) ^ + (static_cast(ndc_minus_one_to_one) << 2) ^ + (static_cast(cull_face) << 24) ^ + (static_cast(front_face) << 48); +} + +bool FixedPipelineState::Rasterizer::operator==(const Rasterizer& rhs) const { + return std::tie(cull_enable, depth_bias_enable, ndc_minus_one_to_one, cull_face, front_face) == + std::tie(rhs.cull_enable, rhs.depth_bias_enable, rhs.ndc_minus_one_to_one, rhs.cull_face, + rhs.front_face); +} + +std::size_t FixedPipelineState::DepthStencil::Hash() const { + std::size_t hash = static_cast(depth_test_enable) ^ + (static_cast(depth_write_enable) << 1) ^ + (static_cast(depth_bounds_enable) << 2) ^ + (static_cast(stencil_enable) << 3) ^ + (static_cast(depth_test_function) << 4); + boost::hash_combine(hash, front_stencil.Hash()); + boost::hash_combine(hash, back_stencil.Hash()); + return hash; +} + +bool FixedPipelineState::DepthStencil::operator==(const DepthStencil& rhs) const { + return std::tie(depth_test_enable, depth_write_enable, depth_bounds_enable, depth_test_function, + stencil_enable, front_stencil, back_stencil) == + std::tie(rhs.depth_test_enable, rhs.depth_write_enable, rhs.depth_bounds_enable, + rhs.depth_test_function, rhs.stencil_enable, rhs.front_stencil, + rhs.back_stencil); +} + +std::size_t FixedPipelineState::ColorBlending::Hash() const { + std::size_t hash = attachments_count << 13; + for (std::size_t rt = 0; rt < static_cast(attachments_count); ++rt) { + boost::hash_combine(hash, attachments[rt].Hash()); + } + return hash; +} + +bool FixedPipelineState::ColorBlending::operator==(const ColorBlending& rhs) const { + return std::equal(attachments.begin(), attachments.begin() + attachments_count, + rhs.attachments.begin(), rhs.attachments.begin() + rhs.attachments_count); +} + +std::size_t FixedPipelineState::Hash() const noexcept { + std::size_t hash = 0; + boost::hash_combine(hash, vertex_input.Hash()); + boost::hash_combine(hash, input_assembly.Hash()); + boost::hash_combine(hash, tessellation.Hash()); + boost::hash_combine(hash, rasterizer.Hash()); + boost::hash_combine(hash, depth_stencil.Hash()); + boost::hash_combine(hash, color_blending.Hash()); + return hash; +} + +bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { + return std::tie(vertex_input, input_assembly, tessellation, rasterizer, depth_stencil, + color_blending) == std::tie(rhs.vertex_input, rhs.input_assembly, + rhs.tessellation, rhs.rasterizer, rhs.depth_stencil, + rhs.color_blending); +} + +FixedPipelineState GetFixedPipelineState(const Maxwell& regs) { + FixedPipelineState fixed_state; + fixed_state.input_assembly = GetInputAssemblyState(regs); + fixed_state.tessellation = GetTessellationState(regs); + fixed_state.rasterizer = GetRasterizerState(regs); + fixed_state.depth_stencil = GetDepthStencilState(regs); + fixed_state.color_blending = GetColorBlendingState(regs); + return fixed_state; +} + +} // namespace Vulkan -- cgit v1.2.3