nak: Add helpers for filling QMDs

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/28756>
This commit is contained in:
Faith Ekstrand 2024-04-10 16:08:57 -05:00 committed by Marge Bot
parent 64f02ac16d
commit b9c0e3c1ab
5 changed files with 455 additions and 1 deletions

View File

@ -31,6 +31,12 @@ dep_syn = dependency('syn',
required : true,
)
dep_paste = dependency('paste',
version : '>= 1.0.14',
fallback : ['paste', 'dep_paste'],
required : true,
)
libnak_c_files = files(
'nak.h',
'nak_nir.c',
@ -116,6 +122,10 @@ _libnak_rs = static_library(
gnu_symbol_visibility : 'hidden',
rust_abi : 'c',
rust_args : nak_rust_args,
dependencies : [
dep_paste,
idep_nvidia_headers_rs,
],
link_with: [_libbitview_rs, libnak_bindings_gen, _libnak_ir_proc_rs],
)

View File

@ -102,13 +102,16 @@ struct nak_xfb_info {
struct nak_shader_info {
gl_shader_stage stage;
/** Shader model */
uint8_t sm;
/** Number of GPRs used */
uint8_t num_gprs;
/** Number of barriers used */
uint8_t num_barriers;
uint16_t _pad0;
uint8_t _pad0;
/** Size of shader local (scratch) memory */
uint32_t slm_size;
@ -177,6 +180,31 @@ nak_compile_shader(nir_shader *nir, bool dump_asm,
nir_variable_mode robust2_modes,
const struct nak_fs_key *fs_key);
struct nak_qmd_cbuf {
uint32_t index;
uint32_t size;
uint64_t addr;
};
struct nak_qmd_info {
uint64_t addr;
uint16_t smem_size;
uint16_t smem_max;
uint32_t global_size[3];
uint32_t num_cbufs;
struct nak_qmd_cbuf cbufs[8];
};
void nak_fill_qmd(const struct nv_device_info *dev,
const struct nak_shader_info *info,
const struct nak_qmd_info *qmd_info,
void *qmd_out, size_t qmd_size);
uint32_t nak_qmd_dispatch_size_offset(const struct nv_device_info *dev);
#ifdef __cplusplus
}
#endif

View File

@ -312,6 +312,7 @@ pub extern "C" fn nak_compile_shader(
let info = nak_shader_info {
stage: nir.info.stage(),
sm: s.info.sm,
num_gprs: if s.info.sm >= 70 {
max(4, s.info.num_gprs + 2)
} else {

View File

@ -23,6 +23,7 @@ mod opt_dce;
mod opt_jump_thread;
mod opt_lop;
mod opt_out;
mod qmd;
mod repair_ssa;
mod sph;
mod spill_values;

View File

@ -0,0 +1,414 @@
// Copyright © 2024 Collabora, Ltd.
// SPDX-License-Identifier: MIT
extern crate bitview;
extern crate nvidia_headers;
extern crate paste;
use nak_bindings::*;
use nvidia_headers::classes::{cla0c0, clc0c0, clc3c0, clc6c0};
use bitview::*;
use paste::paste;
type QMDBitView<'a> = BitMutView<'a, [u32]>;
trait QMD {
const GLOBAL_SIZE_OFFSET: usize;
fn new() -> Self;
fn set_barrier_count(&mut self, barrier_count: u8);
fn set_cbuf(&mut self, idx: u8, addr: u64, size: u32);
fn set_global_size(&mut self, width: u32, height: u32, depth: u32);
fn set_local_size(&mut self, width: u16, height: u16, depth: u16);
fn set_prog_addr(&mut self, addr: u64);
fn set_register_count(&mut self, register_count: u8);
fn set_slm_size(&mut self, slm_size: u32);
fn set_smem_size(&mut self, smem_size: u32, smem_max: u32);
}
macro_rules! set_enum {
($bv:expr, $cls:ident, $strct:ident, $field:ident, $enum:ident) => {
$bv.set_field(
paste! {$cls::[<$strct _ $field>]},
paste! {$cls::[<$strct _ $field _ $enum>]},
)
};
}
macro_rules! set_field {
($bv:expr, $cls:ident, $strct:ident, $field:ident, $val:expr) => {
$bv.set_field(paste! {$cls::[<$strct _ $field>]}, $val)
};
}
macro_rules! set_array {
($bv:expr, $cls:ident, $strct:ident, $f:ident, $i:expr, $x:expr) => {
$bv.set_field(paste! {$cls::[<$strct _ $f>]}($i), $x)
};
}
macro_rules! qmd_init {
($bv: expr, $c:ident, $s:ident, $mjv:expr, $mnv:expr) => {
set_field!($bv, $c, $s, QMD_MAJOR_VERSION, $mjv);
set_field!($bv, $c, $s, QMD_VERSION, $mnv);
set_enum!($bv, $c, $s, API_VISIBLE_CALL_LIMIT, NO_CHECK);
set_enum!($bv, $c, $s, SAMPLER_INDEX, INDEPENDENTLY);
};
}
macro_rules! qmd_impl_common {
($c:ident, $s:ident) => {
fn set_barrier_count(&mut self, barrier_count: u8) {
let mut bv = QMDBitView::new(&mut self.qmd);
set_field!(bv, $c, $s, BARRIER_COUNT, barrier_count);
}
const GLOBAL_SIZE_OFFSET: usize = {
let w = paste! {$c::[<$s _CTA_RASTER_WIDTH>]};
assert!(w.end == w.start + 32);
w.start / 32
};
fn set_global_size(&mut self, width: u32, height: u32, depth: u32) {
let mut bv = QMDBitView::new(&mut self.qmd);
set_field!(bv, $c, $s, CTA_RASTER_WIDTH, width);
set_field!(bv, $c, $s, CTA_RASTER_HEIGHT, height);
set_field!(bv, $c, $s, CTA_RASTER_DEPTH, depth);
}
fn set_local_size(&mut self, width: u16, height: u16, depth: u16) {
let mut bv = QMDBitView::new(&mut self.qmd);
set_field!(bv, $c, $s, CTA_THREAD_DIMENSION0, width);
set_field!(bv, $c, $s, CTA_THREAD_DIMENSION1, height);
set_field!(bv, $c, $s, CTA_THREAD_DIMENSION2, depth);
}
fn set_slm_size(&mut self, slm_size: u32) {
let mut bv = QMDBitView::new(&mut self.qmd);
let slm_size = slm_size.next_multiple_of(0x10);
set_field!(bv, $c, $s, SHADER_LOCAL_MEMORY_HIGH_SIZE, 0);
set_field!(bv, $c, $s, SHADER_LOCAL_MEMORY_LOW_SIZE, slm_size);
}
};
}
const SIZE_SHIFT: u8 = 0;
const SIZE_SHIFTED4_SHIFT: u8 = 4;
macro_rules! qmd_impl_set_cbuf {
($c:ident, $s:ident, $size_field:ident) => {
fn set_cbuf(&mut self, idx: u8, addr: u64, size: u32) {
let mut bv = QMDBitView::new(&mut self.qmd);
let idx = idx.into();
let addr_lo = addr as u32;
let addr_hi = (addr >> 32) as u32;
set_array!(bv, $c, $s, CONSTANT_BUFFER_ADDR_LOWER, idx, addr_lo);
set_array!(bv, $c, $s, CONSTANT_BUFFER_ADDR_UPPER, idx, addr_hi);
paste! {
let shift = [<$size_field _SHIFT>];
let range = $c::[<$s _CONSTANT_BUFFER_ $size_field>](idx);
assert!(((size >> shift) << shift) == size);
bv.set_field(range, size >> shift);
}
set_array!(bv, $c, $s, CONSTANT_BUFFER_VALID, idx, true);
}
};
}
macro_rules! qmd_impl_set_prog_addr_32 {
($c:ident, $s:ident) => {
fn set_prog_addr(&mut self, addr: u64) {
let mut bv = QMDBitView::new(&mut self.qmd);
set_field!(bv, $c, $s, PROGRAM_OFFSET, addr);
}
};
}
macro_rules! qmd_impl_set_prog_addr_64 {
($c:ident, $s:ident) => {
fn set_prog_addr(&mut self, addr: u64) {
let mut bv = QMDBitView::new(&mut self.qmd);
let addr_lo = addr as u32;
let addr_hi = (addr >> 32) as u32;
set_field!(bv, $c, $s, PROGRAM_ADDRESS_LOWER, addr_lo);
set_field!(bv, $c, $s, PROGRAM_ADDRESS_UPPER, addr_hi);
}
};
}
macro_rules! qmd_impl_set_register_count {
($c:ident, $s:ident, $field:ident) => {
fn set_register_count(&mut self, register_count: u8) {
let mut bv = QMDBitView::new(&mut self.qmd);
set_field!(bv, $c, $s, $field, register_count);
}
};
}
mod qmd_0_6 {
use crate::qmd::*;
use nvidia_headers::classes::cla0c0::qmd as cla0c0;
#[repr(transparent)]
pub struct Qmd0_6 {
qmd: [u32; 64],
}
impl QMD for Qmd0_6 {
fn new() -> Self {
let mut qmd = [0; 64];
let mut bv = QMDBitView::new(&mut qmd);
qmd_init!(bv, cla0c0, QMDV00_06, 0, 6);
set_field!(bv, cla0c0, QMDV00_06, SASS_VERSION, 0x30);
Self { qmd }
}
qmd_impl_common!(cla0c0, QMDV00_06);
qmd_impl_set_cbuf!(cla0c0, QMDV00_06, SIZE);
qmd_impl_set_prog_addr_32!(cla0c0, QMDV00_06);
qmd_impl_set_register_count!(cla0c0, QMDV00_06, REGISTER_COUNT);
fn set_smem_size(&mut self, smem_size: u32, _smem_max: u32) {
let mut bv = QMDBitView::new(&mut self.qmd);
let smem_size = smem_size.next_multiple_of(0x100);
set_field!(bv, cla0c0, QMDV00_06, SHARED_MEMORY_SIZE, smem_size);
let l1_config = if smem_size <= (16 << 10) {
cla0c0::QMDV00_06_L1_CONFIGURATION_DIRECTLY_ADDRESSABLE_MEMORY_SIZE_16KB
} else if smem_size <= (32 << 10) {
cla0c0::QMDV00_06_L1_CONFIGURATION_DIRECTLY_ADDRESSABLE_MEMORY_SIZE_32KB
} else if smem_size <= (48 << 10) {
cla0c0::QMDV00_06_L1_CONFIGURATION_DIRECTLY_ADDRESSABLE_MEMORY_SIZE_48KB
} else {
panic!("Iclalid shared memory size");
};
set_field!(bv, cla0c0, QMDV00_06, L1_CONFIGURATION, l1_config);
}
}
}
use qmd_0_6::Qmd0_6;
mod qmd_2_1 {
use crate::qmd::*;
use nvidia_headers::classes::clc0c0::qmd as clc0c0;
#[repr(transparent)]
pub struct Qmd2_1 {
qmd: [u32; 64],
}
impl QMD for Qmd2_1 {
fn new() -> Self {
let mut qmd = [0; 64];
let mut bv = QMDBitView::new(&mut qmd);
qmd_init!(bv, clc0c0, QMDV02_01, 2, 1);
set_field!(bv, clc0c0, QMDV02_01, SM_GLOBAL_CACHING_ENABLE, true);
Self { qmd }
}
qmd_impl_common!(clc0c0, QMDV02_01);
qmd_impl_set_cbuf!(clc0c0, QMDV02_01, SIZE_SHIFTED4);
qmd_impl_set_prog_addr_32!(clc0c0, QMDV02_01);
qmd_impl_set_register_count!(clc0c0, QMDV02_01, REGISTER_COUNT);
fn set_smem_size(&mut self, smem_size: u32, _smem_max: u32) {
let mut bv = QMDBitView::new(&mut self.qmd);
let smem_size = smem_size.next_multiple_of(0x100);
set_field!(bv, clc0c0, QMDV02_01, SHARED_MEMORY_SIZE, smem_size);
}
}
}
use qmd_2_1::Qmd2_1;
fn gv100_sm_config_smem_size(size: u32) -> u32 {
let size = if size > 64 * 1024 {
96 * 1024
} else if size > 32 * 1024 {
64 * 1024
} else if size > 16 * 1024 {
32 * 1024
} else if size > 8 * 1024 {
16 * 1024
} else {
8 * 1024
};
size / 4096 + 1
}
macro_rules! qmd_impl_set_smem_size_bounded {
($c:ident, $s:ident) => {
fn set_smem_size(&mut self, smem_size: u32, smem_max: u32) {
let mut bv = QMDBitView::new(&mut self.qmd);
let smem_size = smem_size.next_multiple_of(0x100);
set_field!(bv, $c, $s, SHARED_MEMORY_SIZE, smem_size);
let max = gv100_sm_config_smem_size(smem_max);
let min = gv100_sm_config_smem_size(smem_size.into());
let target = gv100_sm_config_smem_size(smem_size.into());
set_field!(bv, $c, $s, MIN_SM_CONFIG_SHARED_MEM_SIZE, min);
set_field!(bv, $c, $s, MAX_SM_CONFIG_SHARED_MEM_SIZE, max);
set_field!(bv, $c, $s, TARGET_SM_CONFIG_SHARED_MEM_SIZE, target);
}
};
}
mod qmd_2_2 {
use crate::qmd::*;
use nvidia_headers::classes::clc3c0::qmd as clc3c0;
#[repr(transparent)]
pub struct Qmd2_2 {
qmd: [u32; 64],
}
impl QMD for Qmd2_2 {
fn new() -> Self {
let mut qmd = [0; 64];
let mut bv = QMDBitView::new(&mut qmd);
qmd_init!(bv, clc3c0, QMDV02_02, 2, 2);
set_field!(bv, clc3c0, QMDV02_02, SM_GLOBAL_CACHING_ENABLE, true);
Self { qmd }
}
qmd_impl_common!(clc3c0, QMDV02_02);
qmd_impl_set_cbuf!(clc3c0, QMDV02_02, SIZE_SHIFTED4);
qmd_impl_set_prog_addr_64!(clc3c0, QMDV02_02);
qmd_impl_set_register_count!(clc3c0, QMDV02_02, REGISTER_COUNT_V);
qmd_impl_set_smem_size_bounded!(clc3c0, QMDV02_02);
}
}
use qmd_2_2::Qmd2_2;
mod qmd_3_0 {
use crate::qmd::*;
use nvidia_headers::classes::clc6c0::qmd as clc6c0;
#[repr(transparent)]
pub struct Qmd3_0 {
qmd: [u32; 64],
}
impl QMD for Qmd3_0 {
fn new() -> Self {
let mut qmd = [0; 64];
let mut bv = QMDBitView::new(&mut qmd);
qmd_init!(bv, clc6c0, QMDV03_00, 3, 0);
set_field!(bv, clc6c0, QMDV03_00, SM_GLOBAL_CACHING_ENABLE, true);
Self { qmd }
}
qmd_impl_common!(clc6c0, QMDV03_00);
qmd_impl_set_cbuf!(clc6c0, QMDV03_00, SIZE_SHIFTED4);
qmd_impl_set_prog_addr_64!(clc6c0, QMDV03_00);
qmd_impl_set_register_count!(clc6c0, QMDV03_00, REGISTER_COUNT_V);
qmd_impl_set_smem_size_bounded!(clc6c0, QMDV03_00);
}
}
use qmd_3_0::Qmd3_0;
fn fill_qmd<Q: QMD>(info: &nak_shader_info, qmd_info: &nak_qmd_info) -> Q {
let cs_info = unsafe {
assert!(info.stage == MESA_SHADER_COMPUTE);
&info.__bindgen_anon_1.cs
};
let mut qmd = Q::new();
qmd.set_barrier_count(info.num_barriers);
qmd.set_global_size(
qmd_info.global_size[0],
qmd_info.global_size[1],
qmd_info.global_size[2],
);
qmd.set_local_size(
cs_info.local_size[0],
cs_info.local_size[1],
cs_info.local_size[2],
);
qmd.set_prog_addr(qmd_info.addr);
qmd.set_register_count(info.num_gprs);
qmd.set_slm_size(info.slm_size);
assert!(qmd_info.smem_size >= cs_info.smem_size);
assert!(qmd_info.smem_size <= qmd_info.smem_max);
qmd.set_smem_size(qmd_info.smem_size.into(), qmd_info.smem_max.into());
for i in 0..qmd_info.num_cbufs {
let cb = &qmd_info.cbufs[usize::try_from(i).unwrap()];
if cb.size > 0 {
qmd.set_cbuf(cb.index.try_into().unwrap(), cb.addr, cb.size);
}
}
qmd
}
#[no_mangle]
pub extern "C" fn nak_fill_qmd(
dev: *const nv_device_info,
info: *const nak_shader_info,
qmd_info: *const nak_qmd_info,
qmd_out: *mut ::std::os::raw::c_void,
qmd_size: usize,
) {
assert!(!dev.is_null());
let dev = unsafe { &*dev };
assert!(!info.is_null());
let info = unsafe { &*info };
assert!(!qmd_info.is_null());
let qmd_info = unsafe { &*qmd_info };
unsafe {
if dev.cls_compute >= clc6c0::AMPERE_COMPUTE_A {
let qmd_out = qmd_out as *mut Qmd3_0;
assert!(qmd_size == std::mem::size_of_val(&*qmd_out));
qmd_out.write(fill_qmd(info, qmd_info));
} else if dev.cls_compute >= clc3c0::VOLTA_COMPUTE_A {
let qmd_out = qmd_out as *mut Qmd2_2;
assert!(qmd_size == std::mem::size_of_val(&*qmd_out));
qmd_out.write(fill_qmd(info, qmd_info));
} else if dev.cls_compute >= clc0c0::PASCAL_COMPUTE_A {
let qmd_out = qmd_out as *mut Qmd2_1;
assert!(qmd_size == std::mem::size_of_val(&*qmd_out));
qmd_out.write(fill_qmd(info, qmd_info));
} else if dev.cls_compute >= cla0c0::KEPLER_COMPUTE_A {
let qmd_out = qmd_out as *mut Qmd0_6;
assert!(qmd_size == std::mem::size_of_val(&*qmd_out));
qmd_out.write(fill_qmd(info, qmd_info));
} else {
panic!("Unknown shader model");
}
}
}
#[no_mangle]
pub extern "C" fn nak_qmd_dispatch_size_offset(
dev: *const nv_device_info,
) -> u32 {
assert!(!dev.is_null());
let dev = unsafe { &*dev };
if dev.cls_compute >= clc6c0::AMPERE_COMPUTE_A {
Qmd3_0::GLOBAL_SIZE_OFFSET.try_into().unwrap()
} else if dev.cls_compute >= clc3c0::VOLTA_COMPUTE_A {
Qmd2_2::GLOBAL_SIZE_OFFSET.try_into().unwrap()
} else if dev.cls_compute >= clc0c0::PASCAL_COMPUTE_A {
Qmd2_1::GLOBAL_SIZE_OFFSET.try_into().unwrap()
} else if dev.cls_compute >= cla0c0::KEPLER_COMPUTE_A {
Qmd0_6::GLOBAL_SIZE_OFFSET.try_into().unwrap()
} else {
panic!("Unsupported shader model");
}
}