mesa/src/nouveau/nil/image.rs
Faith Ekstrand e029d2b45a nvk,nil: Stop panicing in image creation
If an image gets created with unsupported parameters (which is a pretty
complex thing to check), it's probably better to just return an error
rather than panic, especially since Rust panics happen even in release
builds.

Reviewed-by: Mel Henning <mhenning@darkrefraction.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33990>
2025-03-10 23:42:19 +00:00

990 lines
31 KiB
Rust

// Copyright © 2024 Collabora, Ltd.
// SPDX-License-Identifier: MIT
use crate::extent::{units, Extent4D};
use crate::format::Format;
use crate::modifiers::*;
use crate::tiling::Tiling;
use crate::Minify;
use nil_rs_bindings::*;
use nvidia_headers::classes::{cl9097, clc597};
use std::panic;
pub const MAX_LEVELS: usize = 16;
pub type ImageUsageFlags = u8;
pub const IMAGE_USAGE_2D_VIEW_BIT: ImageUsageFlags = 1 << 0;
pub const IMAGE_USAGE_LINEAR_BIT: ImageUsageFlags = 1 << 1;
pub const IMAGE_USAGE_SPARSE_RESIDENCY_BIT: ImageUsageFlags = 1 << 2;
pub const IMAGE_USAGE_VIDEO_BIT: ImageUsageFlags = 1 << 3;
#[derive(Clone, Debug, Copy, PartialEq, Default)]
#[repr(u8)]
pub enum ImageDim {
#[default]
_1D = 1,
_2D = 2,
_3D = 3,
}
#[derive(Clone, Debug, Copy, PartialEq, Default)]
#[repr(u8)]
pub enum SampleLayout {
_1x1,
_2x1,
_2x1D3d,
_2x2,
_4x2,
_4x2D3d,
_4x4,
#[default]
Invalid,
}
#[repr(C)]
pub struct SampleOffset {
pub x: u8,
pub y: u8,
}
impl SampleLayout {
#[no_mangle]
pub extern "C" fn nil_choose_sample_layout(samples: u32) -> SampleLayout {
Self::choose_sample_layout(samples)
}
pub fn choose_sample_layout(samples: u32) -> SampleLayout {
match samples {
1 => SampleLayout::_1x1,
2 => SampleLayout::_2x1D3d,
4 => SampleLayout::_2x2,
8 => SampleLayout::_4x2D3d,
16 => SampleLayout::_4x4,
_ => SampleLayout::Invalid,
}
}
pub fn samples(&self) -> u32 {
match self {
SampleLayout::_1x1 => 1,
SampleLayout::_2x1 => 2,
SampleLayout::_2x1D3d => 2,
SampleLayout::_2x2 => 4,
SampleLayout::_4x2 => 8,
SampleLayout::_4x2D3d => 8,
SampleLayout::_4x4 => 16,
SampleLayout::Invalid => panic!("Invalid sample layout"),
}
}
#[no_mangle]
pub extern "C" fn nil_sample_layout_samples(self) -> u32 {
self.samples()
}
pub fn px_extent_sa(&self) -> Extent4D<units::Samples> {
match self {
SampleLayout::_1x1 => Extent4D::new(1, 1, 1, 1),
SampleLayout::_2x1 => Extent4D::new(2, 1, 1, 1),
SampleLayout::_2x1D3d => Extent4D::new(2, 1, 1, 1),
SampleLayout::_2x2 => Extent4D::new(2, 2, 1, 1),
SampleLayout::_4x2 => Extent4D::new(4, 2, 1, 1),
SampleLayout::_4x2D3d => Extent4D::new(4, 2, 1, 1),
SampleLayout::_4x4 => Extent4D::new(4, 4, 1, 1),
SampleLayout::Invalid => panic!("Invalid sample layout"),
}
}
#[no_mangle]
pub extern "C" fn nil_px_extent_sa(self) -> Extent4D<units::Samples> {
self.px_extent_sa()
}
pub fn sa_offset(&self, s: u8) -> SampleOffset {
let (x, y) = match self {
SampleLayout::_1x1 => (0, 0),
SampleLayout::_2x1 => {
debug_assert!(s < 2);
(s, 0)
}
SampleLayout::_2x1D3d => {
debug_assert!(s < 2);
(1 - s, 0)
}
SampleLayout::_2x2 => {
debug_assert!(s < 4);
(s & 1, s >> 1)
}
SampleLayout::_4x2 => {
debug_assert!(s < 8);
(s & 3, s >> 2)
}
SampleLayout::_4x2D3d => match s {
0 => (2, 0),
1 => (1, 1),
2 => (3, 1),
3 => (1, 0),
4 => (0, 1),
5 => (0, 0),
6 => (2, 1),
7 => (3, 0),
_ => panic!("Invalid sample"),
},
SampleLayout::_4x4 => todo!("Figure out the layout of 4x4"),
SampleLayout::Invalid => panic!("Invalid sample layout"),
};
SampleOffset { x, y }
}
#[no_mangle]
pub extern "C" fn nil_sample_offset(self, s: u8) -> SampleOffset {
self.sa_offset(s)
}
}
#[derive(Clone, Debug, Copy, PartialEq)]
#[repr(C)]
pub struct ImageInitInfo {
pub dim: ImageDim,
pub format: Format,
pub extent_px: Extent4D<units::Pixels>,
pub levels: u32,
pub samples: u32,
pub usage: ImageUsageFlags,
pub modifier: u64,
pub explicit_row_stride_B: u32,
pub max_alignment_B: u32,
}
/// Represents the data layout of a single slice (level + lod) of an image.
#[repr(C)]
#[derive(Clone, Debug, Copy, PartialEq, Default)]
pub struct ImageLevel {
pub offset_B: u64,
pub tiling: Tiling,
pub row_stride_B: u32,
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub struct Image {
pub dim: ImageDim,
pub format: Format,
pub extent_px: Extent4D<units::Pixels>,
pub sample_layout: SampleLayout,
pub num_levels: u32,
pub mip_tail_first_lod: u32,
pub levels: [ImageLevel; MAX_LEVELS],
pub array_stride_B: u64,
pub align_B: u32,
pub size_B: u64,
pub compressed: bool,
pub tile_mode: u16,
pub pte_kind: u8,
}
impl Image {
#[no_mangle]
pub extern "C" fn nil_image_init(
dev: &nil_rs_bindings::nv_device_info,
image_out: *mut Self,
info: &ImageInitInfo,
) -> bool {
panic::catch_unwind(|| {
let image = Self::new(dev, std::slice::from_ref(info), 0);
unsafe {
assert!(!image_out.is_null());
image_out.write(image);
}
})
.is_ok()
}
#[no_mangle]
pub extern "C" fn nil_image_init_planar(
dev: &nil_rs_bindings::nv_device_info,
image_out: *mut Self,
info: *const ImageInitInfo,
plane: usize,
plane_count: usize,
) -> bool {
panic::catch_unwind(|| {
assert!(plane < plane_count);
let infos =
unsafe { std::slice::from_raw_parts(info, plane_count) };
let image = Self::new(dev, infos, plane);
unsafe {
assert!(!image_out.is_null());
image_out.write(image);
}
})
.is_ok()
}
fn new_linear(
_dev: &nil_rs_bindings::nv_device_info,
info: &ImageInitInfo,
) -> Self {
// Linear images need to be 2D
assert!(info.dim == ImageDim::_2D);
// Linear images can't be arrays
assert!(info.extent_px.array_len == 1);
// NVIDIA can't do linear and mipmapping
assert!(info.levels == 1);
// NVIDIA can't do linear and multisampling
assert!(info.samples == 1);
let sample_layout = SampleLayout::_1x1;
let align_B = if info.explicit_row_stride_B > 0 {
// If we're importing an image, allow smaller stride and offset
// alignments. The texture headers can handle as low as 32B-aligned
// and NVK has workarounds for rendering if needed.
assert!(info.modifier == DRM_FORMAT_MOD_LINEAR);
debug_assert!(info.explicit_row_stride_B % 32 == 0);
32
} else {
// If we get to pick the alignment, require 128B so that we can
// render to the image without workarounds.
128
};
let extent_B = info.extent_px.to_B(info.format, sample_layout);
let row_stride_B = if info.explicit_row_stride_B > 0 {
debug_assert!(info.explicit_row_stride_B % align_B == 0);
info.explicit_row_stride_B
} else {
extent_B.width.next_multiple_of(128)
};
let level0 = ImageLevel {
offset_B: 0,
tiling: Tiling::default(),
row_stride_B,
};
let size_B = u64::from(row_stride_B) * u64::from(extent_B.height);
let mut image = Self {
dim: info.dim,
format: info.format,
extent_px: info.extent_px,
sample_layout,
num_levels: info.levels,
levels: [ImageLevel::default(); MAX_LEVELS as usize],
array_stride_B: 0,
align_B,
size_B,
compressed: false,
tile_mode: 0,
pte_kind: 0,
mip_tail_first_lod: 0,
};
image.levels[0] = level0;
image
}
fn new_tiled(
dev: &nil_rs_bindings::nv_device_info,
infos: &[ImageInitInfo],
plane: usize,
) -> Self {
let info = &infos[plane];
let sample_layout = SampleLayout::choose_sample_layout(info.samples);
let tiling = if info.modifier != DRM_FORMAT_MOD_INVALID {
assert!((info.usage & IMAGE_USAGE_SPARSE_RESIDENCY_BIT) == 0);
assert!(info.dim == ImageDim::_2D);
assert!(sample_layout == SampleLayout::_1x1);
// This should be handled by new_linear()
assert!(info.modifier != DRM_FORMAT_MOD_LINEAR);
let bl_mod = BlockLinearModifier::try_from(info.modifier).unwrap();
// We don't support compression yet
assert!(bl_mod.compression_type() == CompressionType::None);
bl_mod
.tiling()
.clamp(info.extent_px.to_B(info.format, sample_layout))
} else if (info.usage & IMAGE_USAGE_SPARSE_RESIDENCY_BIT) != 0 {
assert!((info.usage & IMAGE_USAGE_VIDEO_BIT) == 0);
Tiling::sparse(info.format, info.dim)
} else if (info.usage & IMAGE_USAGE_VIDEO_BIT) != 0 {
assert!((info.usage & IMAGE_USAGE_SPARSE_RESIDENCY_BIT) == 0);
let mut min_tiling = Tiling::choose(
info.extent_px,
info.format,
sample_layout,
info.usage,
info.max_alignment_B,
);
for p in 0..infos.len() {
let plane_tiling = Tiling::choose(
infos[p].extent_px,
infos[p].format,
sample_layout,
infos[p].usage,
info.max_alignment_B,
);
min_tiling.x_log2 =
std::cmp::min(min_tiling.x_log2, plane_tiling.x_log2);
min_tiling.y_log2 =
std::cmp::min(min_tiling.y_log2, plane_tiling.y_log2);
min_tiling.z_log2 =
std::cmp::min(min_tiling.z_log2, plane_tiling.z_log2);
}
min_tiling
} else {
Tiling::choose(
info.extent_px,
info.format,
sample_layout,
info.usage,
info.max_alignment_B,
)
};
let mut image = Self {
dim: info.dim,
format: info.format,
extent_px: info.extent_px,
sample_layout,
num_levels: info.levels,
levels: [ImageLevel::default(); MAX_LEVELS as usize],
array_stride_B: 0,
align_B: 0,
size_B: 0,
compressed: false,
tile_mode: 0,
pte_kind: 0,
mip_tail_first_lod: 0,
};
if (info.usage & IMAGE_USAGE_SPARSE_RESIDENCY_BIT) != 0 {
image.mip_tail_first_lod = info.levels;
}
let mut layer_size_B = 0;
for level in 0..info.levels {
let mut lvl_ext_B = image.level_extent_B(level);
// NVIDIA images are layed out as an array of 1/2/3D images, each of
// which may have multiple miplevels. For the purposes of computing
// the size of a miplevel, we don't care about arrays.
lvl_ext_B.array_len = 1;
let lvl_tiling = tiling.clamp(lvl_ext_B);
if tiling != lvl_tiling {
image.mip_tail_first_lod =
std::cmp::min(image.mip_tail_first_lod, level);
}
// Align the size to tiles
let lvl_tiling_ext_B = lvl_tiling.extent_B();
lvl_ext_B = lvl_ext_B.align(&lvl_tiling_ext_B);
assert!(
info.explicit_row_stride_B == 0
|| info.explicit_row_stride_B == lvl_ext_B.width
);
image.levels[level as usize] = ImageLevel {
offset_B: layer_size_B,
tiling: lvl_tiling,
row_stride_B: lvl_ext_B.width,
};
layer_size_B += lvl_ext_B.size_B();
}
// We use the tiling for level 0 instead of the tiling selected above
// because, in the case of sparse residency with small images, level 0 may
// have a smaller tiling than what we tried to use. However, the level 0
// tiling is the one we program in the hardware so that's the one we need
// to use for array stride calculations and the like.
let lvl0_tiling_size_B = image.levels[0].tiling.size_B();
// The array stride has to be aligned to the size of a level 0 tile
image.array_stride_B =
layer_size_B.next_multiple_of(lvl0_tiling_size_B.into());
image.size_B =
image.array_stride_B * u64::from(image.extent_px.array_len);
image.align_B = lvl0_tiling_size_B;
// If the client requested sparse residency, we need a 64K alignment
// or else sparse binding may fail. This is true regardless of
// whether or not we actually select a 64K tile format.
if (info.usage & IMAGE_USAGE_SPARSE_RESIDENCY_BIT) != 0 {
image.align_B = std::cmp::max(image.align_B, 1 << 16);
}
image.pte_kind = Self::choose_pte_kind(
dev,
info.format,
info.samples,
image.compressed,
);
if info.modifier != DRM_FORMAT_MOD_INVALID {
let bl_mod = BlockLinearModifier::try_from(info.modifier).unwrap();
assert!(bl_mod.pte_kind() == image.pte_kind);
}
image.tile_mode = u16::from(image.levels[0].tiling.y_log2) << 4
| u16::from(image.levels[0].tiling.z_log2) << 8;
image.align_B = std::cmp::max(image.align_B, 4096);
if image.pte_kind >= 0xb && image.pte_kind <= 0xe {
image.align_B = std::cmp::max(image.align_B, 1 << 16);
}
image.size_B = image.size_B.next_multiple_of(image.align_B.into());
image
}
pub fn new(
dev: &nil_rs_bindings::nv_device_info,
infos: &[ImageInitInfo],
plane: usize,
) -> Self {
let info = &infos[plane];
match info.dim {
ImageDim::_1D => {
assert!(info.extent_px.height == 1);
assert!(info.extent_px.depth == 1);
assert!(info.samples == 1);
}
ImageDim::_2D => {
assert!(info.extent_px.depth == 1);
}
ImageDim::_3D => {
assert!(info.extent_px.array_len == 1);
assert!(info.samples == 1);
}
}
if (info.usage & IMAGE_USAGE_LINEAR_BIT) != 0
|| info.modifier == DRM_FORMAT_MOD_LINEAR
{
Self::new_linear(dev, info)
} else {
Self::new_tiled(dev, infos, plane)
}
}
/// The size in bytes of an extent at a given level.
fn level_extent_B(&self, level: u32) -> Extent4D<units::Bytes> {
self.level_extent_px(level)
.to_B(self.format, self.sample_layout)
}
#[no_mangle]
pub extern "C" fn nil_image_level_extent_px(
&self,
level: u32,
) -> Extent4D<units::Pixels> {
self.level_extent_px(level)
}
pub fn level_extent_px(&self, level: u32) -> Extent4D<units::Pixels> {
assert!(level == 0 || self.sample_layout == SampleLayout::_1x1);
self.extent_px.minify(level)
}
#[no_mangle]
pub extern "C" fn nil_image_level_layer_offset_B(
&self,
level: u32,
layer: u32,
) -> u64 {
self.level_layer_offset_B(level, layer)
}
pub fn level_layer_offset_B(&self, level: u32, layer: u32) -> u64 {
assert!(level < self.num_levels);
assert!(layer < self.extent_px.array_len);
self.levels[level as usize].offset_B
+ u64::from(layer) * self.array_stride_B
}
#[no_mangle]
pub extern "C" fn nil_image_mip_tail_offset_B(&self) -> u64 {
self.mip_tail_offset_B()
}
pub fn mip_tail_offset_B(&self) -> u64 {
assert!(self.mip_tail_first_lod > 0);
self.levels[self.mip_tail_first_lod as usize].offset_B
}
#[no_mangle]
pub extern "C" fn nil_image_mip_tail_size_B(&self) -> u32 {
self.mip_tail_size_B()
}
pub fn mip_tail_size_B(&self) -> u32 {
(self.array_stride_B - self.mip_tail_offset_B())
.try_into()
.unwrap()
}
#[no_mangle]
pub extern "C" fn nil_image_level_extent_sa(
&self,
level: u32,
) -> Extent4D<units::Samples> {
self.level_extent_sa(level)
}
pub fn level_extent_sa(&self, level: u32) -> Extent4D<units::Samples> {
self.level_extent_px(level).to_sa(self.sample_layout)
}
#[no_mangle]
pub extern "C" fn nil_image_level_layer_size_B(&self, level: u32) -> u64 {
self.level_layer_size_B(level)
}
pub fn level_layer_size_B(&self, level: u32) -> u64 {
assert!(level < self.num_levels);
let mut lvl_ext_B = self.level_extent_B(level);
// We only care about a single array layer here
lvl_ext_B.array_len = 1;
let level = &self.levels[level as usize];
if level.tiling.is_tiled() {
lvl_ext_B.align(&level.tiling.extent_B()).size_B()
} else {
assert!(lvl_ext_B.depth == 1);
assert!(lvl_ext_B.array_len == 1);
u64::from(level.row_stride_B) * u64::from(lvl_ext_B.height - 1)
+ u64::from(lvl_ext_B.width)
}
}
#[no_mangle]
pub extern "C" fn nil_image_level_size_B(&self, level: u32) -> u64 {
self.level_size_B(level)
}
pub fn level_size_B(&self, level: u32) -> u64 {
let lvl_ext_B = self.level_extent_B(level);
let lvl = &self.levels[level as usize];
if lvl.tiling.is_tiled() {
let lvl_layer_size_B = self.level_layer_size_B(level);
self.array_stride_B * u64::from(lvl_ext_B.array_len - 1)
+ lvl_layer_size_B
} else {
assert!(self.extent_px.array_len == 1);
self.level_layer_size_B(level)
}
}
#[no_mangle]
pub extern "C" fn nil_image_level_depth_stride_B(&self, level: u32) -> u64 {
self.level_depth_stride_B(level)
}
pub fn level_depth_stride_B(&self, level: u32) -> u64 {
assert!(level < self.num_levels);
let lvl_ext_B = self.level_extent_B(level);
let level = &self.levels[level as usize];
let lvl_tiling_ext_B = level.tiling.extent_B();
let lvl_ext_B = lvl_ext_B.align(&lvl_tiling_ext_B);
(lvl_ext_B.width * lvl_ext_B.height).into()
}
#[no_mangle]
pub extern "C" fn nil_image_for_level(
&self,
level: u32,
offset_in_bytes_out: &mut u64,
) -> Self {
self.image_for_level(level, offset_in_bytes_out)
}
pub fn image_for_level(
&self,
level: u32,
offset_in_bytes_out: &mut u64,
) -> Self {
assert!(level < self.num_levels);
let lvl_extent_px = self.level_extent_px(level);
let lvl = self.levels[level as usize];
let align_B = lvl.tiling.size_B();
let mut size_B = self.size_B - lvl.offset_B;
if (level + 1) < self.num_levels {
// This assumes levels are sequential, tightly packed and that each
// level has a higher alignment than the next one. All of this is
// currently true.
let next_lvl_offset_in_bytes =
self.levels[level as usize + 1].offset_B;
assert!(next_lvl_offset_in_bytes > lvl.offset_B);
size_B -= next_lvl_offset_in_bytes - lvl.offset_B;
}
let mut levels: [ImageLevel; MAX_LEVELS as usize] = Default::default();
levels[0] = lvl;
*offset_in_bytes_out = lvl.offset_B;
levels[0].offset_B = 0;
Self {
extent_px: lvl_extent_px,
num_levels: 1,
levels,
align_B,
size_B,
mip_tail_first_lod: if level < self.mip_tail_first_lod {
1
} else {
0
},
..*self
}
}
#[no_mangle]
pub extern "C" fn nil_image_level_as_uncompressed(
&self,
level: u32,
offset_in_bytes_out: &mut u64,
) -> Self {
self.level_as_uncompressed(level, offset_in_bytes_out)
}
pub fn level_as_uncompressed(
&self,
level: u32,
offset_in_bytes_out: &mut u64,
) -> Self {
assert!(self.sample_layout == SampleLayout::_1x1);
// Format is arbitrary. Pick one that has the right number of bits.
let uc_format = match self.format.el_size_B() {
4 => PIPE_FORMAT_R32_UINT,
8 => PIPE_FORMAT_R32G32_UINT,
16 => PIPE_FORMAT_R32G32B32A32_UINT,
_ => panic!("No compressed PIPE_FORMAT with this size"),
};
let lvl_image = self.image_for_level(level, offset_in_bytes_out);
let mut image_out = lvl_image.clone();
image_out.format = uc_format.try_into().unwrap();
image_out.extent_px = lvl_image
.extent_px
.to_el(lvl_image.format, lvl_image.sample_layout)
.cast_units();
image_out
}
#[no_mangle]
pub extern "C" fn nil_image_3d_level_as_2d_array(
&self,
level: u32,
offset_in_bytes_out: &mut u64,
) -> Self {
self._3d_level_as_2d_array(level, offset_in_bytes_out)
}
pub fn _3d_level_as_2d_array(
&self,
level: u32,
offset_in_bytes_out: &mut u64,
) -> Self {
assert!(self.dim == ImageDim::_3D);
assert!(self.extent_px.array_len == 1);
assert!(self.sample_layout == SampleLayout::_1x1);
let mut image_2d_out = self.image_for_level(level, offset_in_bytes_out);
let lvl0 = &image_2d_out.levels[0];
assert!(image_2d_out.num_levels == 1);
assert!(!lvl0.tiling.is_tiled() || lvl0.tiling.z_log2 == 0);
let lvl_tiling_ext_B = lvl0.tiling.extent_B();
let lvl_ext_B = image_2d_out.level_extent_B(0);
let lvl_ext_B = lvl_ext_B.align(&lvl_tiling_ext_B);
let z_stride = u64::from(lvl_ext_B.width * lvl_ext_B.height);
image_2d_out.dim = ImageDim::_2D;
image_2d_out.extent_px.array_len = image_2d_out.extent_px.depth;
image_2d_out.extent_px.depth = 1;
image_2d_out.array_stride_B = z_stride;
image_2d_out
}
pub fn choose_pte_kind(
dev: &nil_rs_bindings::nv_device_info,
format: Format,
samples: u32,
compressed: bool,
) -> u8 {
if dev.cls_eng3d >= clc597::TURING_A {
Self::tu102_choose_pte_kind(format, compressed)
} else if dev.cls_eng3d >= cl9097::FERMI_A {
Self::nvc0_choose_pte_kind(format, samples, compressed)
} else {
panic!("Unsupported 3d engine class")
}
}
fn tu102_choose_pte_kind(format: Format, compressed: bool) -> u8 {
use nvidia_headers::hwref::tu102::mmu::*;
match pipe_format::from(format) {
PIPE_FORMAT_Z16_UNORM => {
if compressed {
NV_MMU_PTE_KIND_Z16_COMPRESSIBLE_DISABLE_PLC
} else {
NV_MMU_PTE_KIND_Z16
}
}
PIPE_FORMAT_X8Z24_UNORM
| PIPE_FORMAT_S8X24_UINT
| PIPE_FORMAT_S8_UINT_Z24_UNORM => {
if compressed {
NV_MMU_PTE_KIND_Z24S8_COMPRESSIBLE_DISABLE_PLC
} else {
NV_MMU_PTE_KIND_Z24S8
}
}
PIPE_FORMAT_X24S8_UINT
| PIPE_FORMAT_Z24X8_UNORM
| PIPE_FORMAT_Z24_UNORM_S8_UINT => {
if compressed {
NV_MMU_PTE_KIND_S8Z24_COMPRESSIBLE_DISABLE_PLC
} else {
NV_MMU_PTE_KIND_S8Z24
}
}
PIPE_FORMAT_X32_S8X24_UINT | PIPE_FORMAT_Z32_FLOAT_S8X24_UINT => {
if compressed {
NV_MMU_PTE_KIND_ZF32_X24S8_COMPRESSIBLE_DISABLE_PLC
} else {
NV_MMU_PTE_KIND_ZF32_X24S8
}
}
PIPE_FORMAT_Z32_FLOAT => NV_MMU_PTE_KIND_GENERIC_MEMORY,
PIPE_FORMAT_S8_UINT => {
if compressed {
NV_MMU_PTE_KIND_S8_COMPRESSIBLE_DISABLE_PLC
} else {
NV_MMU_PTE_KIND_S8
}
}
_ => NV_MMU_PTE_KIND_GENERIC_MEMORY,
}
.try_into()
.unwrap()
}
fn nvc0_choose_pte_kind(
format: Format,
samples: u32,
compressed: bool,
) -> u8 {
use nvidia_headers::hwref::gp100::mmu::*;
let ms = samples.ilog2();
match pipe_format::from(format) {
PIPE_FORMAT_Z16_UNORM => {
if compressed {
NV_MMU_PTE_KIND_Z16_2C + ms
} else {
NV_MMU_PTE_KIND_Z16
}
}
PIPE_FORMAT_X8Z24_UNORM
| PIPE_FORMAT_S8X24_UINT
| PIPE_FORMAT_S8_UINT_Z24_UNORM => {
if compressed {
NV_MMU_PTE_KIND_Z24S8_2CZ + ms
} else {
NV_MMU_PTE_KIND_Z24S8
}
}
PIPE_FORMAT_X24S8_UINT
| PIPE_FORMAT_Z24X8_UNORM
| PIPE_FORMAT_Z24_UNORM_S8_UINT => {
if compressed {
NV_MMU_PTE_KIND_S8Z24_2CZ + ms
} else {
NV_MMU_PTE_KIND_S8Z24
}
}
PIPE_FORMAT_Z32_FLOAT => {
if compressed {
NV_MMU_PTE_KIND_ZF32_2CZ + ms
} else {
NV_MMU_PTE_KIND_ZF32
}
}
PIPE_FORMAT_X32_S8X24_UINT | PIPE_FORMAT_Z32_FLOAT_S8X24_UINT => {
if compressed {
NV_MMU_PTE_KIND_ZF32_X24S8_2CSZV + ms
} else {
NV_MMU_PTE_KIND_ZF32_X24S8
}
}
PIPE_FORMAT_S8_UINT => NV_MMU_PTE_KIND_S8,
_ => {
let blocksize_bits = format.el_size_B() * 8;
match blocksize_bits {
128 => {
if compressed {
match samples {
1 => NV_MMU_PTE_KIND_C128_2C,
2 => NV_MMU_PTE_KIND_C128_MS2_2C,
4 => NV_MMU_PTE_KIND_C128_MS4_2C,
8 | 16 => NV_MMU_PTE_KIND_C128_MS8_MS16_2C,
_ => panic!("Unsupported sample count"),
}
} else {
NV_MMU_PTE_KIND_GENERIC_16BX2
}
}
64 => {
if compressed {
match samples {
1 => NV_MMU_PTE_KIND_C64_2C,
2 => NV_MMU_PTE_KIND_C64_MS2_2C,
4 => NV_MMU_PTE_KIND_C64_MS4_2C,
8 | 16 => NV_MMU_PTE_KIND_C64_MS8_MS16_2C,
_ => panic!("Unsupported sample count"),
}
} else {
NV_MMU_PTE_KIND_GENERIC_16BX2
}
}
32 => {
if compressed {
match samples {
1 => NV_MMU_PTE_KIND_C32_2C,
2 => NV_MMU_PTE_KIND_C32_MS2_2C,
4 => NV_MMU_PTE_KIND_C32_MS4_2C,
8 | 16 => NV_MMU_PTE_KIND_C32_MS8_MS16_2C,
_ => panic!("Unsupported sample count"),
}
} else {
NV_MMU_PTE_KIND_GENERIC_16BX2
}
}
16 | 8 => NV_MMU_PTE_KIND_GENERIC_16BX2,
_ => NV_MMU_PTE_KIND_PITCH,
}
}
}
.try_into()
.unwrap()
}
#[no_mangle]
pub extern "C" fn nil_msaa_image_as_sa(&self) -> Self {
self.msaa_as_samples()
}
/// For a multisampled image, returns an image of samples
///
/// The resulting image is supersampled with each pixel in the original
/// consuming some number pixels in the supersampled images according to the
/// original image's sample layout
pub fn msaa_as_samples(&self) -> Self {
assert!(self.dim == ImageDim::_2D);
assert!(self.num_levels == 1);
let extent_sa = self.extent_px.to_sa(self.sample_layout);
let mut out = self.clone();
out.extent_px = extent_sa.cast_units();
out.sample_layout = SampleLayout::_1x1;
out
}
#[no_mangle]
pub extern "C" fn nil_image_level_z_offset_B(
&self,
level: u32,
z: u32,
) -> u64 {
self.level_z_offset_B(level, z)
}
pub fn level_z_offset_B(&self, level: u32, z: u32) -> u64 {
assert!(level < self.num_levels);
let lvl_extent_px = self.level_extent_px(level);
assert!(z < lvl_extent_px.depth);
let lvl_tiling = &self.levels[level as usize].tiling;
let z_tl = z >> lvl_tiling.z_log2;
let z_gob = z & ((1 << lvl_tiling.z_log2) - 1);
let lvl_extent_tl =
lvl_extent_px.to_tl(lvl_tiling, self.format, self.sample_layout);
let offset_B = u64::from(
lvl_extent_tl.width
* lvl_extent_tl.height
* z_tl
* lvl_tiling.size_B(),
);
let tiling_extent_B = lvl_tiling.extent_B();
let offset_B = offset_B
+ u64::from(tiling_extent_B.width * tiling_extent_B.height * z_gob);
offset_B
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, Copy, PartialEq)]
#[repr(u8)]
pub enum ViewType {
_1D,
_2D,
_3D,
_3DSliced,
Cube,
_1DArray,
_2DArray,
CubeArray,
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct View {
pub view_type: ViewType,
/// The format to use in the view
///
/// This may differ from the format of the actual isl_surf but must have the
/// same block size.
pub format: Format,
pub base_level: u32,
pub num_levels: u32,
/// Base array layer
///
/// For cube maps, both base_array_layer and array_len should be specified in
/// terms of 2-D layers and must be a multiple of 6.
pub base_array_layer: u32,
/// Array Length
///
/// Indicates the number of array elements starting at Base Array Layer.
pub array_len: u32,
pub swizzle: [nil_rs_bindings::pipe_swizzle; 4],
// VK_EXT_image_view_min_lod
pub min_lod_clamp: f32,
}