Skip to content

Commit

Permalink
AMF support, improved result file for permutations, and option to all…
Browse files Browse the repository at this point in the history
…ow duplicate scoring (#7)
  • Loading branch information
Proryanator authored Mar 3, 2023
1 parent b5b4cb7 commit 7d5e883
Show file tree
Hide file tree
Showing 22 changed files with 343 additions and 44 deletions.
10 changes: 9 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ members = [
"permutation",
"permutor-cli",
"cli",
"gpus"
"gpus",
"codecs"
]
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ PCI bottlenecking for GPU's not in the primary slot.

- do make sure your SSD drive in Windows does _not_ have drive compression enabled. This can severely affect your
sequential read speeds, which is very important for reading high resolution/fps input files
- the tool does _not_ support multiple Nvidia GPU's in your system, so it's suggested to just have 1 in your system
- the tool supports multiple Nvidia GPU's in your system for both the benchmark & permutor-cli tool, so you can feel
free to have more than 1 for your testing (although the benchmark would only run against one)
- the tool does _not_ support multiple AMD GPU's for the benchmark tool, but you are able to still specify _-gpu_ with
the permutor-cli tool

---

Expand Down
3 changes: 2 additions & 1 deletion benchmark/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ permutation = { path = "../permutation" }
engine = { path = "../engine" }
ffmpeg = { path = "../ffmpeg" }
cli = { path = "../cli" }
gpus = { path = "../gpus" }
gpus = { path = "../gpus" }
codecs = { path = "../codecs" }
45 changes: 32 additions & 13 deletions benchmark/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ use text_io::read;

use cli::cli_util::{is_dev, pause};
use cli::supported::{get_supported_encoders, get_supported_inputs};
use codecs::amf::Amf;
use codecs::get_vendor_for_codec;
use codecs::nvenc::Nvenc;
use codecs::permute::Permute;
use codecs::vendor::Vendor;
use engine::benchmark_engine::BenchmarkEngine;
use engine::h264_hevc_nvenc::Nvenc;
use engine::permute::Permute;
use ffmpeg::metadata::MetaData;
use gpus::get_gpus;
use permutation::permutation::Permutation;
Expand Down Expand Up @@ -41,15 +44,13 @@ fn main() {

cli.validate();

let input_files = get_input_files(cli.source_file);
let input_files = get_input_files(cli.source_file.clone());
let mut engine = BenchmarkEngine::new();
let nvenc = Nvenc::new(cli.encoder == "hevc_nvenc", cli.gpu);

// prepare permutations for the engine to run over
for input in input_files {
let mut permutation = Permutation::new(input, cli.encoder.clone());
let settings = get_settings_for(&nvenc);
let bitrate = get_bitrate_for(&permutation.get_metadata());
let settings = get_benchmark_settings_for(&cli);
let bitrate = get_bitrate_for(&permutation.get_metadata(), cli.encoder.clone());

permutation.bitrate = bitrate;
permutation.encoder_settings = settings;
Expand Down Expand Up @@ -151,14 +152,32 @@ fn print_options(input_vec: Vec<&str>) {
}
}

fn get_settings_for(nvenc: &Nvenc) -> String {
// need to support other encoders here
return nvenc.get_benchmark_settings();
fn get_benchmark_settings_for(cli: &BenchmarkCli) -> String {
let vendor = get_vendor_for_codec(&cli.encoder);

return match vendor {
Vendor::Nvidia => {
let nvenc = Nvenc::new(cli.encoder == "hevc_nvenc", cli.gpu);
nvenc.get_benchmark_settings()
}

Vendor::AMD => {
let amf = Amf::new(cli.encoder == "hevc_nvenc", cli.gpu);
amf.get_benchmark_settings()
}
Vendor::Unknown => {
// nothing to do here
String::from("")
}
};
}

fn get_bitrate_for(metadata: &MetaData) -> u32 {
// need to support other encoders here
return *Nvenc::get_resolution_to_bitrate_map(metadata.fps).get(&metadata.get_res()).unwrap();
fn get_bitrate_for(metadata: &MetaData, string: String) -> u32 {
if string.contains("nvenc") {
return *Nvenc::get_resolution_to_bitrate_map(metadata.fps).get(&metadata.get_res()).unwrap();
} else {
return *Amf::get_resolution_to_bitrate_map(metadata.fps).get(&metadata.get_res()).unwrap();
}
}

fn get_input_files(source_file: String) -> Vec<String> {
Expand Down
4 changes: 2 additions & 2 deletions cli/src/supported.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const SUPPORTED_ENCODERS: [&'static str; 2] = ["h264_nvenc", "hevc_nvenc"];
const SUPPORTED_ENCODERS: [&'static str; 4] = ["h264_nvenc", "hevc_nvenc", "h264_amf", "hevc_amf"];
const DOWNLOAD_URL: &str = "https://www.dropbox.com/sh/x08pkk47lc1v5ex/AADGaoOjOcA0-uPo7I0NaxL-a?dl=0";
const ENCODE_FILES: [&'static str; 8] = ["720-60.y4m", "720-120.y4m", "1080-60.y4m", "1080-120.y4m", "2k-60.y4m", "2k-120.y4m", "4k-60.y4m", "4k-120.y4m"];

pub fn is_encoder_supported(potential_encoder: &String) -> bool {
return SUPPORTED_ENCODERS.contains(&potential_encoder.as_str());
}

pub fn get_supported_encoders() -> [&'static str; 2] {
pub fn get_supported_encoders() -> [&'static str; 4] {
return SUPPORTED_ENCODERS;
}

Expand Down
9 changes: 9 additions & 0 deletions codecs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "codecs"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
itertools = "0.10.5"
184 changes: 184 additions & 0 deletions codecs/src/amf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
use std::collections::HashMap;

use itertools::Itertools;

use crate::permute::Permute;
use crate::resolutions::map_res_to_bitrate;

pub struct Amf {
usages: Vec<&'static str>,
qualities: Vec<&'static str>,
profiles: Vec<&'static str>,
profile_tiers: Vec<&'static str>,
rate_controls: Vec<&'static str>,
// might be able to make this the size we're expecting
permutations: Vec<String>,
index: i32,
gpu: u8,
}

impl Amf {
pub fn new(is_hevc: bool, gpu: u8) -> Self {
Self {
usages: get_amf_usages(),
qualities: get_amf_quality(),
// this is the only difference between hevc & h264
profiles: if is_hevc { vec!["main"] } else { vec!["main", "high", "constrained_baseline", "constrained_high"] },
// leaving out vbr rate controls as these are not ideal for game streaming
profile_tiers: get_amf_profile_tiers(is_hevc),
rate_controls: vec!["cbr"],
permutations: Vec::new(),
// starts at -1, so that first next() will return the first element
index: -1,
gpu,
}
}

pub fn get_benchmark_settings(&self) -> String {
// both hevc and h264 perform best at main (kinda, h264 it doesn't matter much)
// hevc and h264 both share the same high fps with the same settings, even without profile_tier for hevc
let profile = "main";
return format!("-usage ultralowlatency -quality speed -profile:v {} -rc cbr -cbr true -gpu {}", profile, self.gpu);
}

fn has_next(&self) -> bool {
return self.index != (self.permutations.len() - 1) as i32;
}
}

fn get_amf_profile_tiers(hevc: bool) -> Vec<&'static str> {
if hevc {
return vec!["main", "high"];
}

// there are no amf profile tiers for h264
return vec![];
}

fn get_amf_usages() -> Vec<&'static str> {
return vec!["transcoding", "ultralowlatency", "lowlatency", "webcam"];
}

fn get_amf_quality() -> Vec<&'static str> {
return vec!["balanced", "speed", "quality"];
}

#[derive(Copy, Clone)]
struct AmfSettings {
usage: &'static str,
quality: &'static str,
profile: &'static str,
profile_tier: &'static str,
rate_control: &'static str,
gpu: u8,
}

impl AmfSettings {
fn to_string(&self) -> String {
let mut args = String::new();
args.push_str("-usage ");
args.push_str(self.usage);
args.push_str(" -quality ");
args.push_str(self.quality);
args.push_str(" -profile:v ");
args.push_str(self.profile);

if !self.profile_tier.is_empty() {
args.push_str(" -profile_tier ");
args.push_str(self.profile_tier);
}

args.push_str(" -rc ");
args.push_str(self.rate_control);
// always set this to constant bit rate to ensure reliable stream
args.push_str(" -cbr true");
args.push_str(" -gpu ");
args.push_str(self.gpu.to_string().as_str());

return args;
}
}

impl Iterator for Amf {
type Item = (usize, String);

// maybe we can pull this code out
fn next(&mut self) -> Option<Self::Item> {
if !self.has_next() {
return None;
}

self.index += 1;

let usize_index = self.index as usize;
return Option::from((usize_index as usize, self.permutations.get(usize_index).unwrap().to_string()));
}
}

impl Permute for Amf {
fn init(&mut self) -> &Vec<String> {
// reset index, otherwise we won't be able to iterate at all
self.index = -1;

// clear the vectors if there were entries before
self.permutations.clear();

let mut permutations = if self.profile_tiers.is_empty() { vec![&self.usages, &self.qualities, &self.profiles, &self.rate_controls] } else {
vec![&self.usages, &self.qualities, &self.profiles, &self.profile_tiers, &self.rate_controls]
}
.into_iter().multi_cartesian_product();

loop {
let perm = permutations.next();
if perm.is_none() {
break;
}

let unwrapped_perm = perm.unwrap();
let profile_tier = if !self.profile_tiers.is_empty() { unwrapped_perm.get(3).unwrap() } else { "" };
let rc_index = if !self.profile_tiers.is_empty() { 4 } else { 3 };
let settings = AmfSettings {
usage: unwrapped_perm.get(0).unwrap(),
quality: unwrapped_perm.get(1).unwrap(),
profile: unwrapped_perm.get(2).unwrap(),
profile_tier,
rate_control: unwrapped_perm.get(rc_index).unwrap(),
gpu: self.gpu,
};

self.permutations.push(settings.to_string());
}

return &self.permutations;
}

fn run_standard_only(&mut self) -> &Vec<String> {
// reset index, otherwise we won't be able to iterate at all
self.index = -1;

// clear the vectors if there were entries before
self.permutations.clear();

// note: this only works when hevc/h264 both use just 1 profile, if we add more this will break
self.permutations.push(String::from(self.get_benchmark_settings()));
return &self.permutations;
}


fn get_resolution_to_bitrate_map(fps: u32) -> HashMap<String, u32> {
let mut map: HashMap<String, u32> = HashMap::new();

// bitrates are within 5Mb/s of each other, using higher one
// note: these are the 60fps bitrate values
let mut bitrates: [u32; 4] = [20, 35, 50, 85];

// 120 fps is effectively double the bitrate
if fps == 120 {
bitrates.iter_mut().for_each(|b| *b = *b * 2);
}

map_res_to_bitrate(&mut map, bitrates);

return map;
}
}
18 changes: 18 additions & 0 deletions codecs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::vendor::Vendor;

pub mod nvenc;
pub mod amf;
pub mod permute;
mod resolutions;
pub mod vendor;


pub fn get_vendor_for_codec(codec: &String) -> Vendor {
if codec.contains("nvenc") {
return Vendor::Nvidia;
} else if codec.contains("amf") {
return Vendor::AMD;
}

return Vendor::Unknown;
}
Loading

0 comments on commit 7d5e883

Please sign in to comment.