mod blend_build;
mod blend_modes;
mod box_pipelines;
mod core_pipelines;
mod gradient_pipelines;
mod porter_duff;
mod span_pipelines;
mod sweep;
mod transform;
use cranelift_codegen::ir::instructions::BlockArg;
use cranelift_codegen::ir::types;
use cranelift_codegen::ir::{AbiParam, Endianness, InstBuilder, MemFlags, Value};
use cranelift_codegen::isa::OwnedTargetIsa;
use cranelift_codegen::settings;
use cranelift_codegen::settings::Configurable;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{Linkage, Module, default_libcall_names};
use super::cache::{
LinearGradientCovFn, PipelineBoxFn, PipelineCovFn, PipelineFn, PipelineSpanCovFn,
PipelineSpanFn, RadialGradientRowFn, SweepFn,
};
use super::key::PipelineKey;
use crate::api::style::{CompOp, FillRule};
use blend_build::*;
use box_pipelines::*;
use core_pipelines::*;
use gradient_pipelines::*;
use porter_duff::*;
use span_pipelines::*;
use sweep::build_sweep;
use transform::build_transform_edges;
fn build_flags() -> settings::Flags {
let mut flag_builder = settings::builder();
flag_builder.set("use_colocated_libcalls", "false").unwrap();
flag_builder.set("is_pic", "false").unwrap();
flag_builder.set("opt_level", "speed").unwrap();
settings::Flags::new(flag_builder)
}
fn build_isa(flags: &settings::Flags) -> OwnedTargetIsa {
let isa_builder = cranelift_native::builder().expect("host machine is not supported");
isa_builder.finish(flags.clone()).unwrap()
}
fn new_module(flags: &settings::Flags) -> JITModule {
let isa = build_isa(flags);
JITModule::new(JITBuilder::with_isa(isa, default_libcall_names()))
}
fn finalize_function(
module: &mut JITModule,
func_id: cranelift_module::FuncId,
ctx: &mut cranelift_codegen::Context,
func_name: &str,
) {
if std::env::var("RADEN_DUMP_PIPELINE").is_ok() {
ctx.set_disasm(true);
}
module.define_function(func_id, ctx).unwrap();
if let Some(disasm) = ctx.compiled_code().unwrap().vcode.as_ref() {
eprintln!("=== {} ===\n{}", func_name, disasm);
}
module.clear_context(ctx);
module.finalize_definitions().unwrap();
}
pub struct PipelineCompiler {
modules: Vec<JITModule>,
flags: settings::Flags,
}
impl PipelineCompiler {
pub fn new() -> Self {
Self {
modules: Vec::new(),
flags: build_flags(),
}
}
pub fn compile(&mut self, key: &PipelineKey, comp_op: CompOp) -> PipelineFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(types::I32)); sig.params.push(AbiParam::new(ptr_type));
let func_name = format!("pipeline_{:#x}", key.value());
let func_id = module
.declare_function(&func_name, Linkage::Local, &sig)
.unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
match comp_op {
CompOp::SrcOver => build_src_over(bcx, ptr_type),
CompOp::SrcCopy => build_src_copy(bcx, ptr_type),
CompOp::Clear => build_clear(bcx, ptr_type),
CompOp::DstCopy => build_dst_copy(bcx, ptr_type),
CompOp::Plus => build_plus(bcx, ptr_type),
CompOp::SrcIn => build_src_in(bcx, ptr_type),
CompOp::SrcOut => build_src_out(bcx, ptr_type),
CompOp::SrcAtop => build_src_atop(bcx, ptr_type),
CompOp::DstOver => build_dst_over(bcx, ptr_type),
CompOp::DstIn => build_dst_in(bcx, ptr_type),
CompOp::DstOut => build_dst_out(bcx, ptr_type),
CompOp::DstAtop => build_dst_atop(bcx, ptr_type),
CompOp::Xor => build_xor(bcx, ptr_type),
CompOp::Minus => build_minus(bcx, ptr_type),
CompOp::Modulate => build_modulate(bcx, ptr_type),
CompOp::Multiply => build_multiply(bcx, ptr_type),
CompOp::Screen => build_screen(bcx, ptr_type),
CompOp::Overlay => build_overlay(bcx, ptr_type),
CompOp::Darken => build_darken(bcx, ptr_type),
CompOp::Lighten => build_lighten(bcx, ptr_type),
CompOp::ColorDodge => build_color_dodge(bcx, ptr_type),
CompOp::ColorBurn => build_color_burn(bcx, ptr_type),
CompOp::LinearBurn => build_linear_burn(bcx, ptr_type),
CompOp::LinearLight => build_linear_light(bcx, ptr_type),
CompOp::PinLight => build_pin_light(bcx, ptr_type),
CompOp::HardLight => build_hard_light(bcx, ptr_type),
CompOp::SoftLight => build_soft_light(bcx, ptr_type),
CompOp::Difference => build_difference(bcx, ptr_type),
CompOp::Exclusion => build_exclusion(bcx, ptr_type),
}
}
finalize_function(&mut module, func_id, &mut ctx, &func_name);
let code = module.get_finalized_function(func_id);
let func: PipelineFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
pub fn compile_cov(&mut self, key: &PipelineKey, comp_op: CompOp) -> PipelineCovFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(types::I32)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type));
let func_name = format!("pipeline_cov_{:#x}", key.value());
let func_id = module
.declare_function(&func_name, Linkage::Local, &sig)
.unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
match comp_op {
CompOp::SrcOver => build_src_over_cov(bcx, ptr_type),
CompOp::SrcCopy => build_src_copy_cov(bcx, ptr_type),
CompOp::Clear => build_clear_cov(bcx, ptr_type),
CompOp::DstCopy => build_dst_copy_cov(bcx, ptr_type),
CompOp::Plus => build_plus_cov(bcx, ptr_type),
CompOp::SrcIn => build_src_in_cov(bcx, ptr_type),
CompOp::SrcOut => build_src_out_cov(bcx, ptr_type),
CompOp::SrcAtop => build_src_atop_cov(bcx, ptr_type),
CompOp::DstOver => build_dst_over_cov(bcx, ptr_type),
CompOp::DstIn => build_dst_in_cov(bcx, ptr_type),
CompOp::DstOut => build_dst_out_cov(bcx, ptr_type),
CompOp::DstAtop => build_dst_atop_cov(bcx, ptr_type),
CompOp::Xor => build_xor_cov(bcx, ptr_type),
CompOp::Minus => build_minus_cov(bcx, ptr_type),
CompOp::Modulate => build_modulate_cov(bcx, ptr_type),
CompOp::Multiply => build_multiply_cov(bcx, ptr_type),
CompOp::Screen => build_screen_cov(bcx, ptr_type),
CompOp::Overlay => build_overlay_cov(bcx, ptr_type),
CompOp::Darken => build_darken_cov(bcx, ptr_type),
CompOp::Lighten => build_lighten_cov(bcx, ptr_type),
CompOp::ColorDodge => build_color_dodge_cov(bcx, ptr_type),
CompOp::ColorBurn => build_color_burn_cov(bcx, ptr_type),
CompOp::LinearBurn => build_linear_burn_cov(bcx, ptr_type),
CompOp::LinearLight => build_linear_light_cov(bcx, ptr_type),
CompOp::PinLight => build_pin_light_cov(bcx, ptr_type),
CompOp::HardLight => build_hard_light_cov(bcx, ptr_type),
CompOp::SoftLight => build_soft_light_cov(bcx, ptr_type),
CompOp::Difference => build_difference_cov(bcx, ptr_type),
CompOp::Exclusion => build_exclusion_cov(bcx, ptr_type),
}
}
finalize_function(&mut module, func_id, &mut ctx, &func_name);
let code = module.get_finalized_function(func_id);
let func: PipelineCovFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
pub fn compile_box(&mut self, key: &PipelineKey, comp_op: CompOp) -> PipelineBoxFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(types::I32)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type));
let func_name = format!("pipeline_box_{:#x}", key.value());
let func_id = module
.declare_function(&func_name, Linkage::Local, &sig)
.unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
match comp_op {
CompOp::SrcOver => build_src_over_box(bcx, ptr_type),
CompOp::SrcCopy => build_src_copy_box(bcx, ptr_type),
_ => unreachable!("compile_box supports only SrcOver and SrcCopy"),
}
}
finalize_function(&mut module, func_id, &mut ctx, &func_name);
let code = module.get_finalized_function(func_id);
let func: PipelineBoxFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
pub fn compile_sweep(&mut self, fill_rule: FillRule) -> SweepFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type));
let name = match fill_rule {
FillRule::NonZero => "jit_sweep_non_zero",
FillRule::EvenOdd => "jit_sweep_even_odd",
};
let func_id = module.declare_function(name, Linkage::Local, &sig).unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
build_sweep(bcx, ptr_type, fill_rule);
}
finalize_function(&mut module, func_id, &mut ctx, name);
let code = module.get_finalized_function(func_id);
let func: SweepFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
pub fn compile_span(&mut self, key: &PipelineKey, comp_op: CompOp) -> PipelineSpanFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type));
let func_name = format!("pipeline_span_{:#x}", key.value());
let func_id = module
.declare_function(&func_name, Linkage::Local, &sig)
.unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
match comp_op {
CompOp::SrcOver => build_src_over_span(bcx, ptr_type),
_ => build_src_over_span(bcx, ptr_type),
}
}
finalize_function(&mut module, func_id, &mut ctx, &func_name);
let code = module.get_finalized_function(func_id);
let func: PipelineSpanFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
pub fn compile_span_cov(&mut self, key: &PipelineKey, comp_op: CompOp) -> PipelineSpanCovFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type));
let func_name = format!("pipeline_span_cov_{:#x}", key.value());
let func_id = module
.declare_function(&func_name, Linkage::Local, &sig)
.unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
match comp_op {
CompOp::SrcOver => build_src_over_span_cov(bcx, ptr_type),
_ => build_src_over_span_cov(bcx, ptr_type),
}
}
finalize_function(&mut module, func_id, &mut ctx, &func_name);
let code = module.get_finalized_function(func_id);
let func: PipelineSpanCovFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
pub fn compile_linear_gradient_cov(&mut self) -> LinearGradientCovFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64));
let func_name = "linear_gradient_cov";
let func_id = module
.declare_function(func_name, Linkage::Local, &sig)
.unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
build_linear_gradient_cov_opaque(bcx, ptr_type);
}
finalize_function(&mut module, func_id, &mut ctx, func_name);
let code = module.get_finalized_function(func_id);
let func: LinearGradientCovFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
pub fn compile_radial_row(&mut self) -> RadialGradientRowFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(types::F32)); sig.params.push(AbiParam::new(types::F32)); sig.params.push(AbiParam::new(types::F32)); sig.params.push(AbiParam::new(types::F32)); sig.params.push(AbiParam::new(types::F32)); sig.params.push(AbiParam::new(types::F32)); sig.params.push(AbiParam::new(types::F32)); sig.params.push(AbiParam::new(types::F32));
let func_name = "radial_gradient_row";
let func_id = module
.declare_function(func_name, Linkage::Local, &sig)
.unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
build_radial_row_opaque(bcx, ptr_type);
}
finalize_function(&mut module, func_id, &mut ctx, func_name);
let code = module.get_finalized_function(func_id);
let func: RadialGradientRowFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
pub fn compile_transform_edges(&mut self) -> super::cache::TransformEdgesFn {
let mut module = new_module(&self.flags);
let ptr_type = module.target_config().pointer_type();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(ptr_type)); sig.params.push(AbiParam::new(types::F64)); sig.params.push(AbiParam::new(types::F64)); sig.params.push(AbiParam::new(types::F64)); sig.params.push(AbiParam::new(types::F64)); sig.params.push(AbiParam::new(types::F64)); sig.params.push(AbiParam::new(types::F64));
let name = "jit_transform_edges";
let func_id = module.declare_function(name, Linkage::Local, &sig).unwrap();
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
ctx.func.signature = sig;
{
let bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
build_transform_edges(bcx, ptr_type);
}
finalize_function(&mut module, func_id, &mut ctx, name);
let code = module.get_finalized_function(func_id);
let func: super::cache::TransformEdgesFn = unsafe { std::mem::transmute(code) };
self.modules.push(module);
func
}
}
impl Default for PipelineCompiler {
fn default() -> Self {
Self::new()
}
}
pub(super) fn block_args(values: &[Value]) -> Vec<BlockArg> {
values.iter().map(|v| BlockArg::Value(*v)).collect()
}
pub(super) fn emit_extract_channels_simd(
bcx: &mut FunctionBuilder,
pixel_vec: Value,
mask_0xff_vec: Value,
) -> (Value, Value, Value, Value) {
let a = bcx.ins().ushr_imm(pixel_vec, 24);
let a = bcx.ins().band(a, mask_0xff_vec);
let r = bcx.ins().ushr_imm(pixel_vec, 16);
let r = bcx.ins().band(r, mask_0xff_vec);
let g = bcx.ins().ushr_imm(pixel_vec, 8);
let g = bcx.ins().band(g, mask_0xff_vec);
let b = bcx.ins().band(pixel_vec, mask_0xff_vec);
(a, r, g, b)
}
pub(super) fn emit_pack_channels_simd(
bcx: &mut FunctionBuilder,
a: Value,
r: Value,
g: Value,
b: Value,
) -> Value {
let result = bcx.ins().ishl_imm(a, 24);
let tmp = bcx.ins().ishl_imm(r, 16);
let result = bcx.ins().bor(result, tmp);
let tmp = bcx.ins().ishl_imm(g, 8);
let result = bcx.ins().bor(result, tmp);
bcx.ins().bor(result, b)
}
pub(super) fn emit_expand_packed_coverage_i32x4(
bcx: &mut FunctionBuilder,
packed_i32: Value,
) -> Value {
let vec = bcx.ins().scalar_to_vector(types::I32X4, packed_i32);
let le_flags = MemFlags::new().with_endianness(Endianness::Little);
let vec_i8 = bcx.ins().bitcast(types::I8X16, le_flags, vec);
let vec_i16 = bcx.ins().uwiden_low(vec_i8);
bcx.ins().uwiden_low(vec_i16)
}