1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Generates multiple fragments per framebuffer pixel when rasterizing. This can be used for
//! anti-aliasing.

use crate::device::Device;
use crate::image::SampleCount;
use crate::pipeline::graphics::GraphicsPipelineCreationError;
use crate::pipeline::DynamicState;
use crate::render_pass::Subpass;
use fnv::FnvHashMap;
use std::ptr;

// TODO: handle some weird behaviors with non-floating-point targets

/// State of the multisampling.
#[derive(Copy, Clone, Debug)]
pub struct MultisampleState {
    /*
        TODO: enable
        /// The number of rasterization samples to take per pixel. The GPU will pick this many different
        /// locations within each pixel and assign to each of these locations a different depth value.
        /// The depth and stencil test will then be run for each sample.
        pub rasterization_samples: SampleCount,
    */
    /// Controls the proportion (between 0.0 and 1.0) of the samples that will be run through the
    /// fragment shader.
    ///
    /// If the value is 1.0, then all sub-pixel samples will run
    /// through the shader and get a different value. If the value is 0.5, about half of the samples
    /// will run through the shader and the other half will get their values from the ones which
    /// went through the shader.
    ///
    /// If set to `Some`, the [`sample_rate_shading`](crate::device::Features::sample_rate_shading)
    /// feature must be enabled on the device.
    pub sample_shading: Option<f32>,

    /*
        TODO: enable
        pub sample_mask: Option<[u32; 2]>, // 64 bits for needed for 64 SampleCount
    */
    /// Controls whether the alpha value of the fragment will be used in an implementation-defined
    /// way to determine which samples get disabled or not. For example if the alpha value is 0.5,
    /// then about half of the samples will be discarded. If you render to a multisample image, this
    /// means that the color will end up being mixed with whatever color was underneath, which gives
    /// the same effect as alpha blending.
    pub alpha_to_coverage_enable: bool,

    /// Controls whether the alpha value of all the samples will be forced to 1.0 (or the
    /// maximum possible value) after the effects of `alpha_to_coverage` have been applied.
    ///
    /// If set to `true`, the [`alpha_to_one`](crate::device::Features::alpha_to_one)
    /// feature must be enabled on the device.
    pub alpha_to_one_enable: bool,
}

impl MultisampleState {
    /// Creates a `MultisampleState` with multisampling disabled.
    #[inline]
    pub fn new() -> MultisampleState {
        MultisampleState {
            //rasterization_samples: 1,
            sample_shading: None,
            //sample_mask: [0xffffffff; 4],
            alpha_to_coverage_enable: false,
            alpha_to_one_enable: false,
        }
    }

    pub(crate) fn to_vulkan(
        &self,
        device: &Device,
        dynamic_state_modes: &mut FnvHashMap<DynamicState, bool>,
        subpass: &Subpass,
    ) -> Result<ash::vk::PipelineMultisampleStateCreateInfo, GraphicsPipelineCreationError> {
        let rasterization_samples = subpass.num_samples().unwrap_or(SampleCount::Sample1).into();

        let (sample_shading_enable, min_sample_shading) =
            if let Some(min_sample_shading) = self.sample_shading {
                if !device.enabled_features().sample_rate_shading {
                    return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
                        feature: "sample_rate_shading",
                        reason: "MultisampleState::sample_shading was Some",
                    });
                }
                assert!(min_sample_shading >= 0.0 && min_sample_shading <= 1.0); // TODO: return error?
                (ash::vk::TRUE, min_sample_shading)
            } else {
                (ash::vk::FALSE, 0.0)
            };

        let alpha_to_one_enable = {
            if self.alpha_to_one_enable {
                if !device.enabled_features().alpha_to_one {
                    return Err(GraphicsPipelineCreationError::FeatureNotEnabled {
                        feature: "alpha_to_one",
                        reason: "MultisampleState::alpha_to_one was true",
                    });
                }
            }
            self.alpha_to_one_enable as ash::vk::Bool32
        };

        Ok(ash::vk::PipelineMultisampleStateCreateInfo {
            flags: ash::vk::PipelineMultisampleStateCreateFlags::empty(),
            rasterization_samples,
            sample_shading_enable,
            min_sample_shading,
            p_sample_mask: ptr::null(),
            alpha_to_coverage_enable: self.alpha_to_coverage_enable as ash::vk::Bool32,
            alpha_to_one_enable,
            ..Default::default()
        })
    }
}

impl Default for MultisampleState {
    /// Returns [`MultisampleState::new()`].
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}