blob: a61dc84b754b53a55ed73928ac0a52e39e549a21 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
carnelian::Point,
euclid::Transform2D,
fidl::endpoints::{ClientEnd, ServerEnd},
fidl_fuchsia_sysmem::{
AllocatorMarker, BufferCollectionConstraints, BufferCollectionSynchronousProxy,
BufferCollectionTokenMarker, BufferMemoryConstraints, BufferUsage, ColorSpace,
ColorSpaceType, FormatModifier, HeapType, ImageFormatConstraints,
PixelFormat as SysmemPixelFormat, PixelFormatType, CPU_USAGE_WRITE_OFTEN,
FORMAT_MODIFIER_LINEAR,
},
fuchsia_component::client::connect_to_service,
fuchsia_zircon::{self as zx, prelude::*},
spinel_rs_sys::{SpnCommand, SpnResult::*, *},
std::{
cell::RefCell,
collections::{BTreeMap, HashSet},
convert::TryFrom,
f32,
ffi::CStr,
fs::File,
io::Read,
mem,
os::raw::{c_char, c_void},
ptr,
rc::Rc,
sync::Arc,
},
vk_sys as vk,
};
// Required spinel libraries.
#[link(name = "spinel", kind = "static")]
#[link(name = "compute_common", kind = "static")]
#[link(name = "compute_common_vk", kind = "static")]
#[link(name = "hotsort_vk", kind = "static")]
#[link(name = "hotsort_vk_hs_nvidia_sm35_u64", kind = "static")]
#[link(name = "hotsort_vk_hs_intel_gen8_u64", kind = "static")]
#[link(name = "hotsort_vk_hs_amd_gcn3_u64", kind = "static")]
#[link(name = "spinel_vk", kind = "static")]
#[link(name = "spinel_vk_find_target", kind = "static")]
#[link(name = "spinel_vk_spn_nvidia_sm50", kind = "static")]
#[link(name = "spinel_vk_spn_intel_gen8", kind = "static")]
#[link(name = "spinel_vk_spn_amd_gcn3", kind = "static")]
extern "C" {}
pub enum Path {
Spinel(SpinelPath),
Mold(mold::Path),
}
pub trait PathBuilder {
fn begin(&mut self);
fn end(&mut self) -> Path;
fn move_to(&mut self, p: &Point);
fn line_to(&mut self, p: &Point);
}
pub enum Raster {
Spinel(SpinelRaster),
Mold(mold::Raster),
}
pub enum GroupId {
Spinel(SpnGroupId),
Mold(u32),
}
pub trait RasterBuilder {
fn begin(&mut self);
fn end(&mut self) -> Raster;
fn add(&mut self, path: &Path, transform: &Transform2D<f32>, clip: &[f32; 4]);
}
pub trait Styling {
fn seal(&mut self);
fn unseal(&mut self);
fn reset(&mut self);
fn alloc_group(&mut self, range_lo: u32, range_hi: u32, background_color: &[f32; 4])
-> GroupId;
fn group_layer(&mut self, group_id: &GroupId, layer_id: u32, color: &[f32; 4]);
}
pub trait Composition {
fn seal(&mut self);
fn unseal(&mut self);
fn reset(&mut self);
fn set_clip(&mut self, clip: &[u32; 4]);
fn place(&mut self, raster: &Raster, layer_id: u32);
}
pub trait Image {
fn flush(&mut self);
}
pub struct CopyRegion {
pub src_offset: [u32; 2],
pub dst_offset: [u32; 2],
pub extent: [u32; 2],
}
#[allow(dead_code)]
pub enum RenderExt {
// Clear color.
PreClear([f32; 4]),
// Source image followed by region to copy. Source can be the same as
// render image.
PreCopy((u32, CopyRegion)),
// Destination image followed by border color, exposure distance and
// region to copy. Destination image must be different from render image.
PostCopy((u32, [f32; 4], [i32; 2], CopyRegion)),
}
pub trait Context {
fn get_pixel_format(&self) -> fuchsia_framebuffer::PixelFormat;
fn styling(&mut self) -> &mut dyn Styling;
fn path_builder(&mut self) -> &mut dyn PathBuilder;
fn raster_builder(&mut self) -> &mut dyn RasterBuilder;
fn composition(&mut self, id: u32) -> &mut dyn Composition;
fn image(&mut self, id: u32, size: &[u32; 2]) -> &mut dyn Image;
fn image_from_index(&mut self, id: u32, index: u32) -> &mut dyn Image;
fn render(&mut self, image_id: u32, composition_id: u32, clip: &[u32; 4], exts: &[RenderExt]);
}
//
// Spinel Implementation
//
pub struct SpinelPath {
context: Rc<RefCell<SpnContext>>,
path: SpnPath,
}
impl SpinelPath {
fn new(context: Rc<RefCell<SpnContext>>, path: SpnPath) -> Self {
Self { context, path }
}
}
impl Drop for SpinelPath {
fn drop(&mut self) {
unsafe {
spn_path_release(*self.context.borrow(), &self.path as *const _, 1);
}
}
}
pub struct SpinelRaster {
context: Rc<RefCell<SpnContext>>,
raster: SpnRaster,
}
impl SpinelRaster {
fn new(context: Rc<RefCell<SpnContext>>, raster: SpnRaster) -> Self {
Self { context, raster }
}
}
impl Drop for SpinelRaster {
fn drop(&mut self) {
unsafe {
spn_raster_release(*self.context.borrow(), &self.raster as *const _, 1);
}
}
}
struct SpinelPathBuilder {
context: Rc<RefCell<SpnContext>>,
path_builder: SpnPathBuilder,
}
impl SpinelPathBuilder {
fn new(context: Rc<RefCell<SpnContext>>) -> Self {
let path_builder = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_path_builder_create(*context.borrow(), output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
output.assume_init()
};
Self { context, path_builder }
}
}
impl PathBuilder for SpinelPathBuilder {
fn begin(&mut self) {
unsafe {
let status = spn_path_builder_begin(self.path_builder);
assert_eq!(status, SpnSuccess);
}
}
fn end(&mut self) -> Path {
let path = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_path_builder_end(self.path_builder, output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
output.assume_init()
};
Path::Spinel(SpinelPath::new(Rc::clone(&self.context), path))
}
fn move_to(&mut self, p: &Point) {
unsafe {
let status = spn_path_builder_move_to(self.path_builder, p.x, p.y);
assert_eq!(status, SpnSuccess);
}
}
fn line_to(&mut self, p: &Point) {
unsafe {
let status = spn_path_builder_line_to(self.path_builder, p.x, p.y);
assert_eq!(status, SpnSuccess);
}
}
}
impl Drop for SpinelPathBuilder {
fn drop(&mut self) {
unsafe {
spn_path_builder_release(self.path_builder);
}
}
}
struct SpinelRasterBuilder {
context: Rc<RefCell<SpnContext>>,
raster_builder: SpnRasterBuilder,
}
impl SpinelRasterBuilder {
fn new(context: Rc<RefCell<SpnContext>>) -> Self {
let raster_builder = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_raster_builder_create(*context.borrow(), output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
output.assume_init()
};
Self { context, raster_builder }
}
}
impl RasterBuilder for SpinelRasterBuilder {
fn begin(&mut self) {
unsafe {
let status = spn_raster_builder_begin(self.raster_builder);
assert_eq!(status, SpnSuccess);
}
}
fn end(&mut self) -> Raster {
let raster = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_raster_builder_end(self.raster_builder, output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
output.assume_init()
};
Raster::Spinel(SpinelRaster::new(Rc::clone(&self.context), raster))
}
fn add(&mut self, path: &Path, transform: &Transform2D<f32>, clip: &[f32; 4]) {
let transform = SpnTransform {
sx: transform.m11 * 32.0,
shx: transform.m21 * 32.0,
tx: transform.m31 * 32.0,
shy: transform.m12 * 32.0,
sy: transform.m22 * 32.0,
ty: transform.m32 * 32.0,
w0: 0.0,
w1: 0.0,
};
let clip = SpnClip { x0: clip[0], y0: clip[1], x1: clip[2], y1: clip[3] };
match path {
Path::Spinel(path) => unsafe {
let status = spn_raster_builder_add(
self.raster_builder,
&path.path,
ptr::null_mut(),
&transform,
ptr::null_mut(),
&clip,
1,
);
assert_eq!(status, SpnSuccess);
},
_ => {
panic!("bad path");
}
}
}
}
impl Drop for SpinelRasterBuilder {
fn drop(&mut self) {
unsafe {
spn_raster_builder_release(self.raster_builder);
}
}
}
struct SpinelStyling {
styling: SpnStyling,
}
impl SpinelStyling {
fn new(context: Rc<RefCell<SpnContext>>, layers_count: u32, cmds_count: u32) -> Self {
let styling = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_styling_create(
*context.borrow(),
output.as_mut_ptr(),
layers_count,
cmds_count,
);
assert_eq!(status, SpnSuccess);
output.assume_init()
};
Self { styling }
}
}
impl Styling for SpinelStyling {
fn seal(&mut self) {
unsafe {
let status = spn_styling_seal(self.styling);
assert_eq!(status, SpnSuccess);
}
}
fn unseal(&mut self) {
unsafe {
let status = spn_styling_unseal(self.styling);
assert_eq!(status, SpnSuccess);
}
}
fn reset(&mut self) {
unsafe {
let status = spn_styling_reset(self.styling);
assert_eq!(status, SpnSuccess);
}
}
fn alloc_group(
&mut self,
range_lo: u32,
range_hi: u32,
background_color: &[f32; 4],
) -> GroupId {
let group_id = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_styling_group_alloc(self.styling, output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
output.assume_init()
};
unsafe {
let status = spn_styling_group_parents(self.styling, group_id, 0, ptr::null_mut());
assert_eq!(status, SpnSuccess);
spn_styling_group_range_lo(self.styling, group_id, range_lo);
spn_styling_group_range_hi(self.styling, group_id, range_hi);
}
let cmds_enter = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_styling_group_enter(self.styling, group_id, 1, output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
std::slice::from_raw_parts_mut(output.assume_init(), 1)
};
cmds_enter[0] = SpnCommand::SpnStylingOpcodeColorAccZero;
let cmds_leave = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_styling_group_leave(self.styling, group_id, 4, output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
std::slice::from_raw_parts_mut(output.assume_init(), 4)
};
unsafe {
spn_styling_background_over_encoder(&mut cmds_leave[0], background_color.as_ptr());
}
cmds_leave[3] = SpnCommand::SpnStylingOpcodeColorAccStoreToSurface;
GroupId::Spinel(group_id)
}
fn group_layer(&mut self, group_id: &GroupId, layer_id: u32, color: &[f32; 4]) {
let group_id = match group_id {
GroupId::Spinel(id) => *id,
_ => panic!("invalid group id"),
};
let cmds = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status =
spn_styling_group_layer(self.styling, group_id, layer_id, 6, output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
std::slice::from_raw_parts_mut(output.assume_init(), 6)
};
cmds[0] = SpnCommand::SpnStylingOpcodeCoverWipZero;
cmds[1] = SpnCommand::SpnStylingOpcodeCoverNonzero;
unsafe {
spn_styling_layer_fill_rgba_encoder(&mut cmds[2], color.as_ptr());
}
cmds[5] = SpnCommand::SpnStylingOpcodeBlendOver;
}
}
impl Drop for SpinelStyling {
fn drop(&mut self) {
unsafe {
spn_styling_release(self.styling);
}
}
}
struct SpinelComposition {
composition: SpnComposition,
}
impl SpinelComposition {
fn new(context: Rc<RefCell<SpnContext>>) -> Self {
let composition = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status = spn_composition_create(*context.borrow(), output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
output.assume_init()
};
Self { composition }
}
}
impl Composition for SpinelComposition {
fn seal(&mut self) {
unsafe {
let status = spn_composition_seal(self.composition);
assert_eq!(status, SpnSuccess);
}
}
fn unseal(&mut self) {
unsafe {
let status = spn_composition_unseal(self.composition);
assert_eq!(status, SpnSuccess);
}
}
fn reset(&mut self) {
unsafe {
let status = spn_composition_reset(self.composition);
assert_eq!(status, SpnSuccess);
}
}
fn set_clip(&mut self, clip: &[u32; 4]) {
unsafe {
let status = spn_composition_set_clip(self.composition, clip.as_ptr());
assert_eq!(status, SpnSuccess);
}
}
fn place(&mut self, raster: &Raster, layer_id: u32) {
match raster {
Raster::Spinel(raster) => unsafe {
let status = spn_composition_place(
self.composition,
&raster.raster,
&layer_id,
ptr::null(),
1,
);
assert_eq!(status, SpnSuccess);
},
_ => {
panic!("bad raster");
}
}
}
}
impl Drop for SpinelComposition {
fn drop(&mut self) {
unsafe {
spn_composition_release(self.composition);
}
}
}
// TODO: Remove buffer collection bindings when they are upstream.
type BufferCollectionFUCHSIA = u64;
const STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA: u32 = 1001004000;
const STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA: u32 = 1001004004;
const STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA: u32 = 1001004005;
const STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA: u32 = 1001004006;
#[repr(C)]
#[allow(non_snake_case)]
struct BufferCollectionCreateInfoFUCHSIA {
sType: vk::StructureType,
pNext: *const ::std::os::raw::c_void,
collectionToken: u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct ImportMemoryBufferCollectionFUCHSIA {
sType: vk::StructureType,
pNext: *const ::std::os::raw::c_void,
collection: BufferCollectionFUCHSIA,
index: u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct BufferCollectionImageCreateInfoFUCHSIA {
sType: vk::StructureType,
pNext: *const ::std::os::raw::c_void,
collection: BufferCollectionFUCHSIA,
index: u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct BufferCollectionPropertiesFUCHSIA {
sType: vk::StructureType,
pNext: *const ::std::os::raw::c_void,
memoryTypeBits: u32,
count: u32,
}
macro_rules! ptrs {(
$struct_name:ident,
{ $($name:ident => ($($param_n:ident: $param_ty:ty),*) -> $ret:ty,)+ }) => (
#[allow(non_snake_case)]
struct $struct_name {
$(
pub $name: extern "system" fn($($param_ty),*) -> $ret,
)+
}
impl $struct_name {
fn load<F>(mut f: F) -> $struct_name
where F: FnMut(&CStr) -> *const ::std::os::raw::c_void
{
#[allow(non_snake_case)]
$struct_name {
$(
$name: unsafe {
extern "system" fn $name($(_: $param_ty),*) {
panic!("function pointer `{}` not loaded", stringify!($name))
}
let name = CStr::from_bytes_with_nul_unchecked(
concat!("vk", stringify!($name), "\0").as_bytes());
let val = f(name);
if val.is_null() {
mem::transmute($name as *const ())
} else {
mem::transmute(val)
}
},
)+
}
}
$(
#[inline]
#[allow(non_snake_case)]
unsafe fn $name(&self $(, $param_n: $param_ty)*) -> $ret {
let ptr = self.$name;
ptr($($param_n),*)
}
)+
}
)
}
ptrs!(FuchsiaExtensionPointers, {
CreateBufferCollectionFUCHSIA => (
device: vk::Device,
pImportInfo: *const BufferCollectionCreateInfoFUCHSIA,
pAllocator: *const vk::AllocationCallbacks,
pCollection: *mut BufferCollectionFUCHSIA) -> vk::Result,
SetBufferCollectionConstraintsFUCHSIA => (
device: vk::Device,
collection: BufferCollectionFUCHSIA,
pImageInfo: *const vk::ImageCreateInfo) -> vk::Result,
DestroyBufferCollectionFUCHSIA => (
device: vk::Device,
collection: BufferCollectionFUCHSIA,
pAllocator: *const vk::AllocationCallbacks) -> (),
GetBufferCollectionPropertiesFUCHSIA => (
device: vk::Device,
collection: BufferCollectionFUCHSIA,
pProperties: *mut BufferCollectionPropertiesFUCHSIA) -> vk::Result,
});
#[link(name = "vulkan")]
extern "C" {
fn vkGetInstanceProcAddr(
instance: vk::Instance,
pName: *const ::std::os::raw::c_char,
) -> vk::PFN_vkVoidFunction;
}
const STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: u32 = 1000059000;
const STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT: u32 = 1000261000;
const STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR: u32 = 1000269000;
const STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT: u32 = 1000221000;
const STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR: u32 = 1000082000;
const STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT: u32 = 1000225002;
#[repr(C)]
#[allow(non_snake_case)]
struct PhysicalDeviceHostQueryResetFeaturesEXT {
sType: vk::StructureType,
pNext: *const c_void,
hostQueryReset: vk::Bool32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct PhysicalDevicePipelineExecutablePropertiesFeaturesKHR {
sType: vk::StructureType,
pNext: *const c_void,
pipelineExecutableInfo: vk::Bool32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct PhysicalDeviceScalarBlockLayoutFeaturesEXT {
sType: vk::StructureType,
pNext: *const c_void,
scalarBlockLayout: vk::Bool32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct VkPhysicalDeviceShaderFloat16Int8FeaturesKHR {
sType: vk::StructureType,
pNext: *const c_void,
shaderFloat16: vk::Bool32,
shaderInt8: vk::Bool32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct VkPhysicalDeviceSubgroupSizeControlFeaturesEXT {
sType: vk::StructureType,
pNext: *const c_void,
subgroupSizeControl: vk::Bool32,
computeFullSubgroups: vk::Bool32,
}
struct VulkanImage {
device: vk::Device,
image: vk::Image,
sampler: vk::Sampler,
view: vk::ImageView,
memory: vk::DeviceMemory,
vk: vk::DevicePointers,
width: u32,
height: u32,
layout: vk::ImageLayout,
stage: vk::PipelineStageFlagBits,
access_mask: vk::AccessFlags,
pool: Option<vk::CommandPool>,
cb: Option<vk::CommandBuffer>,
queue: Option<vk::Queue>,
fence: Option<vk::Fence>,
}
impl VulkanImage {
fn get_image_create_info(
width: u32,
height: u32,
format: vk::Format,
p_next: *const c_void,
) -> vk::ImageCreateInfo {
vk::ImageCreateInfo {
sType: vk::STRUCTURE_TYPE_IMAGE_CREATE_INFO,
pNext: p_next,
flags: 0,
imageType: vk::IMAGE_TYPE_2D,
format,
extent: vk::Extent3D { width, height, depth: 1 },
mipLevels: 1,
arrayLayers: 1,
samples: vk::SAMPLE_COUNT_1_BIT,
tiling: vk::IMAGE_TILING_OPTIMAL,
usage: vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| vk::IMAGE_USAGE_STORAGE_BIT
| vk::IMAGE_USAGE_TRANSFER_SRC_BIT
| vk::IMAGE_USAGE_TRANSFER_DST_BIT,
sharingMode: vk::SHARING_MODE_EXCLUSIVE,
queueFamilyIndexCount: 0,
pQueueFamilyIndices: ptr::null(),
initialLayout: vk::IMAGE_LAYOUT_UNDEFINED,
}
}
fn new(
device: vk::Device,
vk_i: &vk::InstancePointers,
width: u32,
height: u32,
format: vk::Format,
) -> Self {
let vk = vk::DevicePointers::load(|name| unsafe {
vk_i.GetDeviceProcAddr(device, name.as_ptr()) as *const _
});
let image = unsafe {
let info = Self::get_image_create_info(width, height, format, ptr::null());
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreateImage(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let mem_reqs = unsafe {
let mut output = mem::MaybeUninit::uninit();
vk.GetImageMemoryRequirements(device, image, output.as_mut_ptr());
output.assume_init()
};
let mem_type_bits = mem_reqs.memoryTypeBits;
assert_ne!(mem_type_bits, 0);
let mem_type_index = {
let mut bits = mem_type_bits;
let mut index = 0;
while (bits & 1) == 0 {
index += 1;
bits >>= 1;
}
index
};
let memory = unsafe {
let alloc_info = vk::MemoryAllocateInfo {
sType: vk::STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
pNext: ptr::null(),
allocationSize: mem_reqs.size as u64,
memoryTypeIndex: mem_type_index,
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.AllocateMemory(device, &alloc_info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
unsafe {
let result = vk.BindImageMemory(device, image, memory, 0);
assert_eq!(result, vk::SUCCESS);
}
let sampler = unsafe {
let info = vk::SamplerCreateInfo {
sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
magFilter: vk::FILTER_NEAREST,
minFilter: vk::FILTER_NEAREST,
mipmapMode: vk::SAMPLER_MIPMAP_MODE_NEAREST,
addressModeU: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
addressModeV: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
addressModeW: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
mipLodBias: 0.0,
anisotropyEnable: vk::FALSE,
maxAnisotropy: 1.0,
compareEnable: vk::FALSE,
compareOp: vk::COMPARE_OP_NEVER,
minLod: 0.0,
maxLod: 0.0,
borderColor: vk::BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
unnormalizedCoordinates: vk::TRUE,
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreateSampler(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let view = unsafe {
let info = vk::ImageViewCreateInfo {
sType: vk::STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
image: image,
viewType: vk::IMAGE_VIEW_TYPE_2D,
format,
components: vk::ComponentMapping {
r: vk::COMPONENT_SWIZZLE_R,
g: vk::COMPONENT_SWIZZLE_G,
b: vk::COMPONENT_SWIZZLE_B,
a: vk::COMPONENT_SWIZZLE_A,
},
subresourceRange: vk::ImageSubresourceRange {
aspectMask: vk::IMAGE_ASPECT_COLOR_BIT,
baseMipLevel: 0,
levelCount: 1,
baseArrayLayer: 0,
layerCount: 1,
},
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreateImageView(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
VulkanImage {
device,
image,
sampler,
view,
memory,
vk,
width,
height,
layout: vk::IMAGE_LAYOUT_UNDEFINED,
stage: vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT,
access_mask: 0,
pool: None,
cb: None,
queue: None,
fence: None,
}
}
fn from_buffer_collection(
device: vk::Device,
vk_i: &vk::InstancePointers,
vk_ext: &FuchsiaExtensionPointers,
width: u32,
height: u32,
format: vk::Format,
buffer_collection: BufferCollectionFUCHSIA,
index: u32,
) -> Self {
let vk = vk::DevicePointers::load(|name| unsafe {
vk_i.GetDeviceProcAddr(device, name.as_ptr()) as *const _
});
let image = unsafe {
let buffer_collection_info = BufferCollectionImageCreateInfoFUCHSIA {
sType: STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA,
pNext: ptr::null(),
collection: buffer_collection,
index: index,
};
let info = Self::get_image_create_info(
width,
height,
format,
&buffer_collection_info as *const _ as *const c_void,
);
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreateImage(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let mem_reqs = unsafe {
let mut output = mem::MaybeUninit::uninit();
vk.GetImageMemoryRequirements(device, image, output.as_mut_ptr());
output.assume_init()
};
let mut mem_props = unsafe {
let output = mem::MaybeUninit::<BufferCollectionPropertiesFUCHSIA>::zeroed();
output.assume_init()
};
mem_props.sType = STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA;
unsafe {
let result = vk_ext.GetBufferCollectionPropertiesFUCHSIA(
device,
buffer_collection,
&mut mem_props,
);
assert_eq!(result, vk::SUCCESS);
}
let mem_type_bits = mem_reqs.memoryTypeBits & mem_props.memoryTypeBits;
assert_ne!(mem_type_bits, 0);
let mem_type_index = {
let mut bits = mem_type_bits;
let mut index = 0;
while (bits & 1) == 0 {
index += 1;
bits >>= 1;
}
index
};
let memory = unsafe {
let import_info = ImportMemoryBufferCollectionFUCHSIA {
sType: STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA,
pNext: ptr::null(),
collection: buffer_collection,
index: index,
};
let alloc_info = vk::MemoryAllocateInfo {
sType: vk::STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
pNext: &import_info as *const _ as *const c_void,
allocationSize: mem_reqs.size as u64,
memoryTypeIndex: mem_type_index,
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.AllocateMemory(device, &alloc_info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
unsafe {
let result = vk.BindImageMemory(device, image, memory, 0);
assert_eq!(result, vk::SUCCESS);
}
let sampler = unsafe {
let info = vk::SamplerCreateInfo {
sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
magFilter: vk::FILTER_NEAREST,
minFilter: vk::FILTER_NEAREST,
mipmapMode: vk::SAMPLER_MIPMAP_MODE_NEAREST,
addressModeU: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
addressModeV: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
addressModeW: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
mipLodBias: 0.0,
anisotropyEnable: vk::FALSE,
maxAnisotropy: 1.0,
compareEnable: vk::FALSE,
compareOp: vk::COMPARE_OP_NEVER,
minLod: 0.0,
maxLod: 0.0,
borderColor: vk::BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
unnormalizedCoordinates: vk::TRUE,
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreateSampler(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let view = unsafe {
let info = vk::ImageViewCreateInfo {
sType: vk::STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
image: image,
viewType: vk::IMAGE_VIEW_TYPE_2D,
format,
components: vk::ComponentMapping {
r: vk::COMPONENT_SWIZZLE_R,
g: vk::COMPONENT_SWIZZLE_G,
b: vk::COMPONENT_SWIZZLE_B,
a: vk::COMPONENT_SWIZZLE_A,
},
subresourceRange: vk::ImageSubresourceRange {
aspectMask: vk::IMAGE_ASPECT_COLOR_BIT,
baseMipLevel: 0,
levelCount: 1,
baseArrayLayer: 0,
layerCount: 1,
},
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreateImageView(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
VulkanImage {
device,
image,
sampler,
view,
memory,
vk,
width,
height,
layout: vk::IMAGE_LAYOUT_UNDEFINED,
stage: vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT,
access_mask: 0,
pool: None,
cb: None,
queue: None,
fence: None,
}
}
fn layout_transition(
&mut self,
new_layout: vk::ImageLayout,
stage: vk::PipelineStageFlagBits,
access_mask: vk::AccessFlagBits,
) {
let pool = self.pool.take().unwrap_or_else(|| unsafe {
let info = vk::CommandPoolCreateInfo {
sType: vk::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
pNext: ptr::null(),
flags: vk::COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
queueFamilyIndex: 0,
};
let mut output = mem::MaybeUninit::uninit();
let result =
self.vk.CreateCommandPool(self.device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
});
let cb = self.cb.take().unwrap_or_else(|| unsafe {
let info = vk::CommandBufferAllocateInfo {
sType: vk::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
pNext: ptr::null(),
commandPool: pool,
level: vk::COMMAND_BUFFER_LEVEL_PRIMARY,
commandBufferCount: 1,
};
let mut output = mem::MaybeUninit::uninit();
let result = self.vk.AllocateCommandBuffers(self.device, &info, output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
});
let queue = self.queue.take().unwrap_or_else(|| unsafe {
let mut output = mem::MaybeUninit::uninit();
self.vk.GetDeviceQueue(self.device, 0, 0, output.as_mut_ptr());
output.assume_init()
});
let fence = self.fence.take().unwrap_or_else(|| unsafe {
let info = vk::FenceCreateInfo {
sType: vk::STRUCTURE_TYPE_FENCE_CREATE_INFO,
pNext: ptr::null(),
flags: vk::FENCE_CREATE_SIGNALED_BIT,
};
let mut output = mem::MaybeUninit::uninit();
let result = self.vk.CreateFence(self.device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
});
unsafe {
const TIMEOUT_NS: u64 = 10_000_000_000; // 10 seconds
let result = self.vk.WaitForFences(self.device, 1, &fence, vk::TRUE, TIMEOUT_NS);
assert_eq!(result, vk::SUCCESS);
let result = self.vk.ResetFences(self.device, 1, &fence);
assert_eq!(result, vk::SUCCESS);
let info = vk::CommandBufferBeginInfo {
sType: vk::STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
pNext: ptr::null(),
flags: vk::COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
pInheritanceInfo: ptr::null(),
};
let result = self.vk.BeginCommandBuffer(cb, &info);
assert_eq!(result, vk::SUCCESS);
let barrier = vk::ImageMemoryBarrier {
sType: vk::STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
pNext: ptr::null(),
srcAccessMask: self.access_mask,
dstAccessMask: access_mask,
oldLayout: self.layout,
newLayout: new_layout,
srcQueueFamilyIndex: 0,
dstQueueFamilyIndex: 0,
image: self.image,
subresourceRange: vk::ImageSubresourceRange {
aspectMask: vk::IMAGE_ASPECT_COLOR_BIT,
baseMipLevel: 0,
levelCount: 1,
baseArrayLayer: 0,
layerCount: 1,
},
};
self.vk.CmdPipelineBarrier(
cb,
self.stage,
stage,
0,
0,
ptr::null(),
0,
ptr::null(),
1,
&barrier,
);
let result = self.vk.EndCommandBuffer(cb);
assert_eq!(result, vk::SUCCESS);
let info = vk::SubmitInfo {
sType: vk::STRUCTURE_TYPE_SUBMIT_INFO,
pNext: ptr::null(),
waitSemaphoreCount: 0,
pWaitSemaphores: ptr::null(),
pWaitDstStageMask: ptr::null(),
commandBufferCount: 1,
pCommandBuffers: &cb,
signalSemaphoreCount: 0,
pSignalSemaphores: ptr::null(),
};
let result = self.vk.QueueSubmit(queue, 1, &info, fence);
assert_eq!(result, vk::SUCCESS);
}
self.layout = new_layout;
self.stage = stage;
self.access_mask = access_mask;
// Cache pool, cb, queue and fence.
self.pool = Some(pool);
self.cb = Some(cb);
self.queue = Some(queue);
self.fence = Some(fence);
}
}
impl Drop for VulkanImage {
fn drop(&mut self) {
unsafe {
self.vk.DestroyImageView(self.device, self.view, ptr::null());
self.vk.DestroySampler(self.device, self.sampler, ptr::null());
self.vk.DestroyImage(self.device, self.image, ptr::null());
self.vk.FreeMemory(self.device, self.memory, ptr::null());
}
if let Some(pool) = self.pool {
unsafe {
self.vk.DestroyCommandPool(self.device, pool, ptr::null());
}
}
if let Some(fence) = self.fence {
unsafe {
self.vk.DestroyFence(self.device, fence, ptr::null());
}
}
}
}
impl Image for VulkanImage {
fn flush(&mut self) {}
}
const EXTERNAL_MEMORY_EXTENSION_NAME: &'static [u8; 27usize] = b"VK_FUCHSIA_external_memory\0";
const BUFFER_COLLECTION_EXTENSION_NAME: &'static [u8; 29usize] = b"VK_FUCHSIA_buffer_collection\0";
#[cfg(debug_assertions)]
const VALIDATION_LAYER_NAME: &'static [u8; 28usize] = b"VK_LAYER_KHRONOS_validation\0";
extern "C" fn image_dispatch_submitter(
queue: vk::Queue,
fence: vk::Fence,
cb: vk::CommandBuffer,
data: *const ::std::os::raw::c_void,
) {
unsafe {
// TODO: Add acquire and release semaphores.
let info = vk::SubmitInfo {
sType: vk::STRUCTURE_TYPE_SUBMIT_INFO,
pNext: ptr::null(),
waitSemaphoreCount: 0,
pWaitSemaphores: ptr::null(),
pWaitDstStageMask: ptr::null(),
commandBufferCount: 1,
pCommandBuffers: &cb,
signalSemaphoreCount: 0,
pSignalSemaphores: ptr::null(),
};
// Safe as images outlive the spinel context.
let image_ptr = data as *const VulkanImage;
let result = (*image_ptr).vk.QueueSubmit(queue, 1, &info, fence);
assert_eq!(result, vk::SUCCESS);
}
}
// Matches struct in copy.comp. See shader source for field descriptions.
#[repr(C)]
struct CopyParams {
src_offset: [i32; 2], // ivec2 at 0 (8 byte aligned)
dst_offset: [i32; 2], // ivec2 at 8 (8 byte aligned)
dxdy: [i32; 2], // ivec2 at 16 (8 byte aligned)
extent: u32, // uint at 20 (4 byte aligned)
}
// Matches struct in motioncopy.comp. See shader source for field descriptions.
#[repr(C)]
struct MotionCopyParams {
src_offset: [i32; 2], // ivec2 at 0 (8 byte aligned)
src_dims: [i32; 2], // ivec2 at 8 (8 byte aligned)
dst_offset: [i32; 2], // ivec2 at 16 (8 byte aligned)
dxdy: [i32; 2], // ivec2 at 24 (8 byte aligned)
border: [f32; 4], // vec4 at 32 (16 byte aligned)
exposure: u32, // uint at 48 (4 byte aligned)
extent: u32, // uint at 52 (4 byte aligned)
}
struct VulkanShader {
pipeline: vk::Pipeline,
pipeline_layout: vk::PipelineLayout,
}
const DESCRIPTOR_SET_PRE_PROCESS: usize = 0;
const DESCRIPTOR_SET_POST_PROCESS: usize = 1;
const DESCRIPTOR_SET_COUNT: usize = 2;
struct VulkanContext {
device: vk::Device,
vk_i: vk::InstancePointers,
vk: vk::DevicePointers,
vk_ext: FuchsiaExtensionPointers,
buffer_collection: BufferCollectionFUCHSIA,
width: u32,
height: u32,
format: vk::Format,
descriptor_pool: vk::DescriptorPool,
descriptor_set_layout: vk::DescriptorSetLayout,
descriptor_sets: Vec<vk::DescriptorSet>,
copy_shader: Option<VulkanShader>,
motioncopy_shader: Option<VulkanShader>,
}
pub struct SpinelContext {
vulkan: VulkanContext,
images: BTreeMap<u32, Box<VulkanImage>>,
_environment: Box<SpnVkEnvironment>,
context: Rc<RefCell<SpnContext>>,
compositions: BTreeMap<u32, SpinelComposition>,
styling: SpinelStyling,
path_builder: SpinelPathBuilder,
raster_builder: SpinelRasterBuilder,
image_post_copy_regions: Vec<vk::ImageCopy>,
}
impl SpinelContext {
fn create_shader(
vk: &vk::DevicePointers,
device: vk::Device,
descriptor_set_layout: vk::DescriptorSetLayout,
name: &str,
constant_range_size: usize,
) -> VulkanShader {
let pipeline_layout = unsafe {
let constant_range = vk::PushConstantRange {
stageFlags: vk::SHADER_STAGE_COMPUTE_BIT,
offset: 0,
size: constant_range_size as u32,
};
let info = vk::PipelineLayoutCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
setLayoutCount: 1,
pSetLayouts: &descriptor_set_layout,
pushConstantRangeCount: 1,
pPushConstantRanges: &constant_range,
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreatePipelineLayout(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
// glslangValidator -V shaders/name.comp -o shaders/name-noopt.comp.spv
// spirv-opt -O shaders/name-noopt.comp.spv -o shaders/name.comp.spv
let shader = {
let file_name = format!("/pkg/data/shaders/{}.comp.spv", name);
let error_str = format!("Can't open file /pkg/data/shaders/{}.comp.spv", name);
let mut file = File::open(file_name).expect(&error_str);
let mut data = vec![];
file.read_to_end(&mut data).unwrap();
data
};
let shader_module = unsafe {
let info = vk::ShaderModuleCreateInfo {
sType: vk::STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
codeSize: shader.len(),
pCode: shader.as_ptr() as *const u32,
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreateShaderModule(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let pipeline = unsafe {
let info = vk::ComputePipelineCreateInfo {
sType: vk::STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
stage: vk::PipelineShaderStageCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
stage: vk::SHADER_STAGE_COMPUTE_BIT,
module: shader_module,
pName: b"main\0".as_ptr() as *const c_char,
pSpecializationInfo: ptr::null(),
},
layout: pipeline_layout,
basePipelineHandle: 0,
basePipelineIndex: 0,
};
let mut output = mem::MaybeUninit::uninit();
let result =
vk.CreateComputePipelines(device, 0, 1, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
unsafe {
vk.DestroyShaderModule(device, shader_module, ptr::null());
}
VulkanShader { pipeline, pipeline_layout }
}
fn update_descriptor_set(
vk: &vk::DevicePointers,
device: vk::Device,
src_image: &VulkanImage,
dst_image: &VulkanImage,
descriptor_set: vk::DescriptorSet,
) {
let src_image_info = vk::DescriptorImageInfo {
sampler: src_image.sampler,
imageView: src_image.view,
imageLayout: vk::IMAGE_LAYOUT_GENERAL,
};
let dst_image_info = vk::DescriptorImageInfo {
sampler: dst_image.sampler,
imageView: dst_image.view,
imageLayout: vk::IMAGE_LAYOUT_GENERAL,
};
let descriptor_set = [
vk::WriteDescriptorSet {
sType: vk::STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
pNext: ptr::null(),
dstSet: descriptor_set,
dstBinding: 0,
dstArrayElement: 0,
descriptorCount: 1,
descriptorType: vk::DESCRIPTOR_TYPE_STORAGE_IMAGE,
pImageInfo: &src_image_info,
pBufferInfo: ptr::null(),
pTexelBufferView: ptr::null(),
},
vk::WriteDescriptorSet {
sType: vk::STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
pNext: ptr::null(),
dstSet: descriptor_set,
dstBinding: 1,
dstArrayElement: 0,
descriptorCount: 1,
descriptorType: vk::DESCRIPTOR_TYPE_STORAGE_IMAGE,
pImageInfo: &dst_image_info,
pBufferInfo: ptr::null(),
pTexelBufferView: ptr::null(),
},
];
unsafe {
vk.UpdateDescriptorSets(
device,
descriptor_set.len() as u32,
descriptor_set.as_ptr(),
0,
ptr::null(),
);
}
}
pub fn new(
token: ClientEnd<BufferCollectionTokenMarker>,
config: &fuchsia_framebuffer::Config,
app_name: *const u8,
block_pool_size: u64,
handle_count: u32,
layers_count: u32,
cmds_count: u32,
) -> Self {
let entry_points = {
vk::EntryPoints::load(|name| unsafe {
mem::transmute(vkGetInstanceProcAddr(0, name.as_ptr()))
})
};
macro_rules! vulkan_version {
( $major:expr, $minor:expr, $patch:expr ) => {
($major as u32) << 22 | ($minor as u32) << 12 | ($patch as u32)
};
}
let app_info = vk::ApplicationInfo {
sType: vk::STRUCTURE_TYPE_APPLICATION_INFO,
pNext: ptr::null(),
pApplicationName: app_name as *const c_char,
applicationVersion: 0,
pEngineName: ptr::null(),
engineVersion: 0,
apiVersion: vulkan_version!(1, 1, 0),
};
#[cfg(debug_assertions)]
let layers = vec![VALIDATION_LAYER_NAME.as_ptr() as *const c_char];
#[cfg(not(debug_assertions))]
let layers = Vec::new();
let instance = unsafe {
let mut output = mem::MaybeUninit::uninit();
let create_info = vk::InstanceCreateInfo {
sType: vk::STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
pApplicationInfo: &app_info,
enabledLayerCount: layers.len() as u32,
ppEnabledLayerNames: layers.as_ptr(),
enabledExtensionCount: 0,
ppEnabledExtensionNames: ptr::null(),
};
let result =
entry_points.CreateInstance(&create_info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let vk_i = {
vk::InstancePointers::load(|name| unsafe {
mem::transmute(vkGetInstanceProcAddr(instance, name.as_ptr()))
})
};
let physical_devices: Vec<vk::PhysicalDevice> = unsafe {
let mut num = 0;
let result = vk_i.EnumeratePhysicalDevices(instance, &mut num, ptr::null_mut());
assert_eq!(result, vk::SUCCESS);
let mut output = Vec::with_capacity(num as usize);
let result = vk_i.EnumeratePhysicalDevices(instance, &mut num, output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.set_len(num as usize);
output
};
assert_ne!(physical_devices.len(), 0);
// TODO: Ability to select physical device.
let physical_device = physical_devices[0];
let pdmp = unsafe {
let mut output = mem::MaybeUninit::uninit();
vk_i.GetPhysicalDeviceMemoryProperties(physical_device, output.as_mut_ptr());
output.assume_init()
};
let pdp = unsafe {
let mut output = mem::MaybeUninit::uninit();
vk_i.GetPhysicalDeviceProperties(physical_device, output.as_mut_ptr());
output.assume_init()
};
println!(
"{:X} : {:X} : {:?}",
pdp.vendorID,
pdp.deviceID,
unsafe { CStr::from_ptr(pdp.deviceName.as_ptr()) }.to_str().unwrap()
);
let format = {
macro_rules! format {
($var:tt) => {
(vk::$var, stringify!($var))
};
}
static FORMATS: &'static [(vk::Format, &str)] =
&[format!(FORMAT_B8G8R8A8_UNORM), format!(FORMAT_R8G8B8A8_UNORM)];
let mut preferred_format = vk::FORMAT_UNDEFINED;
let mut preferred_format_features = 0;
for format in FORMATS {
let properties = unsafe {
let mut output = mem::MaybeUninit::uninit();
vk_i.GetPhysicalDeviceFormatProperties(
physical_device,
format.0,
output.as_mut_ptr(),
);
output.assume_init()
};
// Storage is required.
if properties.optimalTilingFeatures & vk::FORMAT_FEATURE_STORAGE_IMAGE_BIT == 0 {
continue;
}
if preferred_format == vk::FORMAT_UNDEFINED {
preferred_format = format.0;
preferred_format_features = properties.optimalTilingFeatures;
}
// Prefer formats that support sampling.
if (properties.optimalTilingFeatures & vk::FORMAT_FEATURE_SAMPLED_IMAGE_BIT != 0)
&& (preferred_format_features & vk::FORMAT_FEATURE_SAMPLED_IMAGE_BIT == 0)
{
preferred_format = format.0;
preferred_format_features = properties.optimalTilingFeatures;
}
println!("{}{}", format.1, if preferred_format == format.0 { " *" } else { "" });
}
preferred_format
};
assert_ne!(format, vk::FORMAT_UNDEFINED);
let (spn_target, hs_target) = unsafe {
let mut spn_output = mem::MaybeUninit::uninit();
let mut hs_output = mem::MaybeUninit::uninit();
let status = spn_vk_find_target(
pdp.vendorID,
pdp.deviceID,
spn_output.as_mut_ptr(),
hs_output.as_mut_ptr(),
ptr::null_mut(),
0,
);
assert_eq!(status, true);
(spn_output.assume_init(), hs_output.assume_init() as *mut _)
};
let mut spn_tr = unsafe {
let mut output = mem::MaybeUninit::zeroed();
let status = spn_vk_target_get_requirements(spn_target, output.as_mut_ptr());
assert_eq!(status, SpnErrorPartialTargetRequirements);
output.assume_init()
};
let mut hs_tr = unsafe {
let mut output = mem::MaybeUninit::zeroed();
let status = hotsort_vk_target_get_requirements(hs_target, output.as_mut_ptr());
assert_eq!(status, false);
output.assume_init()
};
let mut qcis = unsafe {
let num = spn_tr.qci_count as usize;
let mut output: Vec<vk::DeviceQueueCreateInfo> = Vec::with_capacity(num);
output.set_len(num as usize);
output
};
let mut ext_names = unsafe {
let num = 2 + spn_tr.ext_name_count as usize + hs_tr.ext_name_count as usize;
let mut output: Vec<*const c_char> = Vec::with_capacity(num);
output.set_len(num as usize);
output
};
ext_names[0] = EXTERNAL_MEMORY_EXTENSION_NAME.as_ptr() as *const c_char;
ext_names[1] = BUFFER_COLLECTION_EXTENSION_NAME.as_ptr() as *const c_char;
let mut feature1 = unsafe {
let output = mem::MaybeUninit::<PhysicalDeviceHostQueryResetFeaturesEXT>::zeroed();
output.assume_init()
};
feature1.sType = STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT;
let mut feature2 = unsafe {
let output =
mem::MaybeUninit::<PhysicalDevicePipelineExecutablePropertiesFeaturesKHR>::zeroed();
output.assume_init()
};
feature2.sType = STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR;
let mut feature3 = unsafe {
let output = mem::MaybeUninit::<PhysicalDeviceScalarBlockLayoutFeaturesEXT>::zeroed();
output.assume_init()
};
feature3.sType = STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT;
let mut feature4 = unsafe {
let output = mem::MaybeUninit::<VkPhysicalDeviceShaderFloat16Int8FeaturesKHR>::zeroed();
output.assume_init()
};
feature4.sType = STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
let mut feature5 = unsafe {
let output =
mem::MaybeUninit::<VkPhysicalDeviceSubgroupSizeControlFeaturesEXT>::zeroed();
output.assume_init()
};
feature5.sType = STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
feature1.pNext = &mut feature2 as *mut _ as *mut c_void;
feature2.pNext = &mut feature3 as *mut _ as *mut c_void;
feature3.pNext = &mut feature4 as *mut _ as *mut c_void;
feature4.pNext = &mut feature5 as *mut _ as *mut c_void;
let mut pdf2 = unsafe {
let output = mem::MaybeUninit::<PhysicalDeviceFeatures2>::zeroed();
output.assume_init()
};
pdf2.sType = STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
pdf2.pNext = &mut feature1 as *mut _ as *mut c_void;
spn_tr.qcis = qcis.as_mut_ptr();
spn_tr.ext_names =
if spn_tr.ext_name_count > 0 { &mut ext_names[2] } else { ptr::null_mut() };
spn_tr.pdf2 = &mut pdf2;
unsafe {
let status = spn_vk_target_get_requirements(spn_target, &mut spn_tr);
assert_eq!(status, SpnSuccess);
};
hs_tr.ext_names = if hs_tr.ext_name_count > 0 {
&mut ext_names[2 + spn_tr.ext_name_count as usize]
} else {
ptr::null_mut()
};
hs_tr.pdf = &mut pdf2.features;
unsafe {
let status = hotsort_vk_target_get_requirements(hs_target, &mut hs_tr);
assert_eq!(status, true);
};
let device = unsafe {
let info = vk::DeviceCreateInfo {
sType: vk::STRUCTURE_TYPE_DEVICE_CREATE_INFO,
pNext: pdf2.pNext,
flags: 0,
queueCreateInfoCount: qcis.len() as u32,
pQueueCreateInfos: qcis.as_ptr(),
enabledLayerCount: 0,
ppEnabledLayerNames: ptr::null(),
enabledExtensionCount: ext_names.len() as u32,
ppEnabledExtensionNames: ext_names.as_ptr(),
pEnabledFeatures: &pdf2.features,
};
let mut output = mem::MaybeUninit::uninit();
let result =
vk_i.CreateDevice(physical_device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let vk = vk::DevicePointers::load(|name| unsafe {
vk_i.GetDeviceProcAddr(device, name.as_ptr()) as *const _
});
let vk_ext = FuchsiaExtensionPointers::load(|name| unsafe {
vk_i.GetDeviceProcAddr(device, name.as_ptr()) as *const _
});
let buffer_collection = unsafe {
let info = BufferCollectionCreateInfoFUCHSIA {
sType: STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA,
pNext: ptr::null(),
collectionToken: token.into_channel().into_raw(),
};
let mut output = mem::MaybeUninit::uninit();
let result = vk_ext.CreateBufferCollectionFUCHSIA(
device,
&info,
ptr::null(),
output.as_mut_ptr(),
);
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
unsafe {
let info = VulkanImage::get_image_create_info(
config.width,
config.height,
format,
ptr::null(),
);
let result =
vk_ext.SetBufferCollectionConstraintsFUCHSIA(device, buffer_collection, &info);
assert_eq!(result, vk::SUCCESS);
};
let descriptor_pool = unsafe {
let pool_size = vk::DescriptorPoolSize {
ty: vk::DESCRIPTOR_TYPE_STORAGE_IMAGE,
descriptorCount: 2 * DESCRIPTOR_SET_COUNT as u32,
};
let info = vk::DescriptorPoolCreateInfo {
sType: vk::STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
maxSets: DESCRIPTOR_SET_COUNT as u32,
poolSizeCount: 1,
pPoolSizes: &pool_size,
};
let mut output = mem::MaybeUninit::uninit();
let result = vk.CreateDescriptorPool(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let descriptor_set_layout = unsafe {
let bindings = [
vk::DescriptorSetLayoutBinding {
binding: 0,
descriptorType: vk::DESCRIPTOR_TYPE_STORAGE_IMAGE,
descriptorCount: 1,
stageFlags: vk::SHADER_STAGE_COMPUTE_BIT,
pImmutableSamplers: ptr::null(),
},
vk::DescriptorSetLayoutBinding {
binding: 1,
descriptorType: vk::DESCRIPTOR_TYPE_STORAGE_IMAGE,
descriptorCount: 1,
stageFlags: vk::SHADER_STAGE_COMPUTE_BIT,
pImmutableSamplers: ptr::null(),
},
];
let info = vk::DescriptorSetLayoutCreateInfo {
sType: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
bindingCount: bindings.len() as u32,
pBindings: bindings.as_ptr(),
};
let mut output = mem::MaybeUninit::uninit();
let result =
vk.CreateDescriptorSetLayout(device, &info, ptr::null(), output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.assume_init()
};
let descriptor_sets = unsafe {
let layouts = [descriptor_set_layout, descriptor_set_layout];
let info = vk::DescriptorSetAllocateInfo {
sType: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
pNext: ptr::null(),
descriptorPool: descriptor_pool,
descriptorSetCount: DESCRIPTOR_SET_COUNT as u32,
pSetLayouts: layouts.as_ptr(),
};
let mut output = Vec::with_capacity(DESCRIPTOR_SET_COUNT);
let result = vk.AllocateDescriptorSets(device, &info, output.as_mut_ptr());
assert_eq!(result, vk::SUCCESS);
output.set_len(DESCRIPTOR_SET_COUNT as usize);
output
};
let mut environment = Box::new(SpnVkEnvironment {
d: device,
ac: ptr::null(),
pc: 0,
pd: physical_device,
qfi: 0,
pdmp: pdmp,
});
let create_info = SpnVkContextCreateInfo {
block_pool_size,
handle_count,
spinel: spn_target,
hotsort: hs_target,
};
let context = unsafe {
let mut output = mem::MaybeUninit::uninit();
let status =
spn_vk_context_create(&mut *environment, &create_info, output.as_mut_ptr());
assert_eq!(status, SpnSuccess);
output.assume_init()
};
let context = Rc::new(RefCell::new(context));
let styling = SpinelStyling::new(Rc::clone(&context), layers_count, cmds_count);
let path_builder = SpinelPathBuilder::new(Rc::clone(&context));
let raster_builder = SpinelRasterBuilder::new(Rc::clone(&context));
Self {
vulkan: VulkanContext {
device,
vk_i,
vk,
vk_ext,
buffer_collection,
width: config.width,
height: config.height,
format,
descriptor_pool,
descriptor_set_layout,
descriptor_sets,
copy_shader: None,
motioncopy_shader: None,
},
images: BTreeMap::new(),
_environment: environment,
context,
compositions: BTreeMap::new(),
styling,
path_builder,
raster_builder,
image_post_copy_regions: Vec::new(),
}
}
}
impl Drop for SpinelContext {
fn drop(&mut self) {
self.images.clear();
unsafe {
self.vulkan.vk_ext.DestroyBufferCollectionFUCHSIA(
self.vulkan.device,
self.vulkan.buffer_collection,
ptr::null(),
);
self.vulkan.vk.FreeDescriptorSets(
self.vulkan.device,
self.vulkan.descriptor_pool,
2,
self.vulkan.descriptor_sets.as_ptr(),
);
self.vulkan.vk.DestroyDescriptorSetLayout(
self.vulkan.device,
self.vulkan.descriptor_set_layout,
ptr::null(),
);
if let Some(shader) = self.vulkan.copy_shader.take() {
self.vulkan.vk.DestroyPipeline(self.vulkan.device, shader.pipeline, ptr::null());
self.vulkan.vk.DestroyPipelineLayout(
self.vulkan.device,
shader.pipeline_layout,
ptr::null(),
);
}
if let Some(shader) = self.vulkan.motioncopy_shader.take() {
self.vulkan.vk.DestroyPipeline(self.vulkan.device, shader.pipeline, ptr::null());
self.vulkan.vk.DestroyPipelineLayout(
self.vulkan.device,
shader.pipeline_layout,
ptr::null(),
);
}
self.vulkan.vk.DestroyDescriptorPool(
self.vulkan.device,
self.vulkan.descriptor_pool,
ptr::null(),
);
self.vulkan.vk.DestroyDevice(self.vulkan.device, ptr::null());
}
}
}
impl Context for SpinelContext {
fn get_pixel_format(&self) -> fuchsia_framebuffer::PixelFormat {
match self.vulkan.format {
vk::FORMAT_R8G8B8A8_UNORM => fuchsia_framebuffer::PixelFormat::BgrX888,
_ => fuchsia_framebuffer::PixelFormat::RgbX888,
}
}
fn styling(&mut self) -> &mut dyn Styling {
&mut self.styling
}
fn path_builder(&mut self) -> &mut dyn PathBuilder {
&mut self.path_builder
}
fn raster_builder(&mut self) -> &mut dyn RasterBuilder {
&mut self.raster_builder
}
fn composition(&mut self, id: u32) -> &mut dyn Composition {
let context = &self.context;
self.compositions.entry(id).or_insert_with(|| SpinelComposition::new(Rc::clone(context)))
}
fn image(&mut self, id: u32, size: &[u32; 2]) -> &mut dyn Image {
let vulkan = &self.vulkan;
self.images
.entry(id)
.or_insert_with(|| {
Box::new(VulkanImage::new(
vulkan.device,
&vulkan.vk_i,
size[0],
size[1],
vulkan.format,
))
})
.as_mut()
}
fn image_from_index(&mut self, id: u32, index: u32) -> &mut dyn Image {
let vulkan = &self.vulkan;
self.images
.entry(id)
.or_insert_with(|| {
Box::new(VulkanImage::from_buffer_collection(
vulkan.device,
&vulkan.vk_i,
&vulkan.vk_ext,
vulkan.width,
vulkan.height,
vulkan.format,
vulkan.buffer_collection,
index,
))
})
.as_mut()
}
fn render(&mut self, image_id: u32, composition_id: u32, clip: &[u32; 4], exts: &[RenderExt]) {
let mut pre_clear = None;
let mut pre_copy = None;
let mut post_copy = None;
for ext in exts {
match ext {
RenderExt::PreClear(_) => {
pre_clear = Some(ext);
}
RenderExt::PreCopy(_) => {
pre_copy = Some(ext);
}
RenderExt::PostCopy(_) => {
post_copy = Some(ext);
}
}
}
let mut rs_image_pre_barrier = SpnVkRenderSubmitExtImagePreBarrier {
ext: ptr::null_mut(),
type_: SpnVkRenderSubmitExtType::SpnVkRenderSubmitExtTypeImagePreBarrier,
old_layout: vk::IMAGE_LAYOUT_UNDEFINED,
src_qfi: vk::QUEUE_FAMILY_IGNORED,
};
let mut rs_clear_color = vk::ClearColorValue { float32: [0.0; 4] };
let mut rs_image_clear = SpnVkRenderSubmitExtImagePreClear {
ext: ptr::null_mut(),
type_: SpnVkRenderSubmitExtType::SpnVkRenderSubmitExtTypeImagePreClear,
color: &rs_clear_color,
};
let mut rs_image_pre_process_params =
CopyParams { src_offset: [0; 2], dst_offset: [0; 2], dxdy: [0; 2], extent: 0 };
let mut rs_image_pre_process = SpnVkRenderSubmitExtImageProcess {
ext: ptr::null_mut(),
type_: SpnVkRenderSubmitExtType::SpnVkRenderSubmitExtTypeImagePreProcess,
access_mask: vk::ACCESS_SHADER_READ_BIT | vk::ACCESS_SHADER_WRITE_BIT,
pipeline: 0,
pipeline_layout: 0,
descriptor_set_count: 0,
descriptor_sets: ptr::null(),
push_offset: 0,
push_size: mem::size_of::<CopyParams>() as u32,
push_values: &rs_image_pre_process_params as *const _ as *const c_void,
group_count_x: 1,
group_count_y: 1,
group_count_z: 1,
};
let mut rs_image_post_process_params = MotionCopyParams {
src_offset: [0; 2],
src_dims: [0; 2],
dst_offset: [0; 2],
dxdy: [0; 2],
border: [0.0; 4],
exposure: 0,
extent: 0,
};
let mut rs_image_post_process = SpnVkRenderSubmitExtImageProcess {
ext: ptr::null_mut(),
type_: SpnVkRenderSubmitExtType::SpnVkRenderSubmitExtTypeImagePostProcess,
access_mask: vk::ACCESS_SHADER_READ_BIT,
pipeline: 0,
pipeline_layout: 0,
descriptor_set_count: 0,
descriptor_sets: ptr::null(),
push_offset: 0,
push_size: mem::size_of::<MotionCopyParams>() as u32,
push_values: &rs_image_post_process_params as *const _ as *const c_void,
group_count_x: 1,
group_count_y: 1,
group_count_z: 1,
};
let mut rs_image_post_copy = SpnVkRenderSubmitExtImagePostCopyToImage {
ext: ptr::null_mut(),
type_: SpnVkRenderSubmitExtType::SpnVkRenderSubmitExtTypeImagePostCopyToImage,
dst: 0,
dst_layout: vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
region_count: 0,
regions: ptr::null(),
};
let mut rs_image_post_barrier = SpnVkRenderSubmitExtImagePostBarrier {
ext: ptr::null_mut(),
type_: SpnVkRenderSubmitExtType::SpnVkRenderSubmitExtTypeImagePostBarrier,
new_layout: vk::IMAGE_LAYOUT_UNDEFINED,
dst_qfi: vk::QUEUE_FAMILY_IGNORED,
};
let mut rs_image_ext = ptr::null_mut();
let mut post_image_layout = vk::IMAGE_LAYOUT_PRESENT_SRC_KHR;
let image = self.images.get(&image_id).expect("invalid image");
if image.layout != vk::IMAGE_LAYOUT_GENERAL {
rs_image_pre_barrier.old_layout = image.layout;
rs_image_pre_barrier.ext = rs_image_ext;
rs_image_ext = &mut rs_image_pre_barrier as *mut _ as *mut c_void;
}
if let Some(RenderExt::PreClear(color)) = pre_clear {
rs_clear_color.float32 = *color;
rs_image_clear.ext = rs_image_ext;
rs_image_ext = &mut rs_image_clear as *mut _ as *mut c_void;
}
if let Some(RenderExt::PreCopy((src, region))) = pre_copy {
let vk = &self.vulkan.vk;
let device = self.vulkan.device;
let descriptor_set_layout = self.vulkan.descriptor_set_layout;
let copy_shader = self.vulkan.copy_shader.get_or_insert_with(|| {
Self::create_shader(
vk,
device,
descriptor_set_layout,
"copy",
mem::size_of::<CopyParams>(),
)
});
rs_image_pre_process.pipeline = copy_shader.pipeline;
rs_image_pre_process.pipeline_layout = copy_shader.pipeline_layout;
let src_image = self.images.get(&src).expect("invalid image");
let dst_image = self.images.get(&image_id).expect("invalid image");
Self::update_descriptor_set(
vk,
device,
src_image,
dst_image,
self.vulkan.descriptor_sets[DESCRIPTOR_SET_PRE_PROCESS],
);
rs_image_pre_process.descriptor_set_count = 1;
rs_image_pre_process.descriptor_sets =
&self.vulkan.descriptor_sets[DESCRIPTOR_SET_PRE_PROCESS];
// TODO: Combined X+Y offset support.
if region.dst_offset[0] != region.src_offset[0]
&& region.dst_offset[1] != region.src_offset[1]
{
panic!("dst/src X and dst/src Y offset cannot both change");
}
let mut extent = None;
let mut groups = 0;
for i in 0..2 {
match region.dst_offset[i] as i32 - region.src_offset[i] as i32 {
std::i32::MIN..=-1 => {
// Copy forward.
rs_image_pre_process_params.src_offset[i] = region.src_offset[i] as i32;
rs_image_pre_process_params.dst_offset[i] = region.dst_offset[i] as i32;
rs_image_pre_process_params.dxdy[i] = 1;
extent = Some(region.extent[i]);
}
0 => {
// Copy direction is not important.
rs_image_pre_process_params.src_offset[i] = region.src_offset[i] as i32;
rs_image_pre_process_params.dst_offset[i] = region.dst_offset[i] as i32;
rs_image_pre_process_params.dxdy[i] = 0;
groups = region.extent[i];
}
1..=std::i32::MAX => {
// Copy backwards.
rs_image_pre_process_params.src_offset[i] =
(region.src_offset[i] + region.extent[i]) as i32 - 1;
rs_image_pre_process_params.dst_offset[i] =
(region.dst_offset[i] + region.extent[i]) as i32 - 1;
rs_image_pre_process_params.dxdy[i] = -1;
extent = Some(region.extent[i]);
}
}
}
let extent = extent.unwrap_or_else(|| {
// Copy rows forward if direction is not important for either dimension.
rs_image_pre_process_params.dxdy[0] = 1;
groups = region.extent[1];
region.extent[0]
});
rs_image_pre_process_params.extent = extent;
const LOCAL_SIZE_X: u32 = 48;
// TODO: Clip output to extent instead of rounding up.
rs_image_pre_process.group_count_x = (groups + LOCAL_SIZE_X - 1) / LOCAL_SIZE_X;
rs_image_pre_process.ext = rs_image_ext;
rs_image_ext = &mut rs_image_pre_process as *mut _ as *mut c_void;
}
if let Some(RenderExt::PostCopy((dst, border, exposure, region))) = post_copy {
if *dst == image_id {
panic!("dst image must be different from the render image")
}
// Use motion copy shader if exposure amount is larger than 1.
if exposure[0].abs() > 1 || exposure[1].abs() > 1 {
let vk = &self.vulkan.vk;
let device = self.vulkan.device;
let descriptor_set_layout = self.vulkan.descriptor_set_layout;
let motioncopy_shader = self.vulkan.motioncopy_shader.get_or_insert_with(|| {
Self::create_shader(
vk,
device,
descriptor_set_layout,
"motioncopy",
mem::size_of::<MotionCopyParams>(),
)
});
rs_image_post_process.pipeline = motioncopy_shader.pipeline;
rs_image_post_process.pipeline_layout = motioncopy_shader.pipeline_layout;
let dst_image = self.images.get_mut(&dst).expect("invalid image");
if dst_image.layout != vk::IMAGE_LAYOUT_GENERAL {
dst_image.layout_transition(
vk::IMAGE_LAYOUT_GENERAL,
vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT,
vk::ACCESS_SHADER_WRITE_BIT,
);
}
let src_image = self.images.get(&image_id).expect("invalid image");
let dst_image = self.images.get(&dst).expect("invalid image");
Self::update_descriptor_set(
vk,
device,
src_image,
dst_image,
self.vulkan.descriptor_sets[DESCRIPTOR_SET_POST_PROCESS],
);
rs_image_post_process.descriptor_set_count = 1;
rs_image_post_process.descriptor_sets =
&self.vulkan.descriptor_sets[DESCRIPTOR_SET_POST_PROCESS];
// TODO: Combined X+Y exposure support.
if exposure[0] != 0 && exposure[1] != 0 {
panic!("X and Y exposure cannot be used at the same time");
}
let mut extent = None;
let mut groups = 0;
for i in 0..2 {
match exposure[i] {
std::i32::MIN..=-1 => {
// Copy forward.
rs_image_post_process_params.src_offset[i] =
region.src_offset[i] as i32;
rs_image_post_process_params.dst_offset[i] =
region.dst_offset[i] as i32;
rs_image_post_process_params.dxdy[i] = 1;
extent = Some(region.extent[i]);
}
0 => {
// Copy direction is not important.
rs_image_post_process_params.src_offset[i] =
region.src_offset[i] as i32;
rs_image_post_process_params.dst_offset[i] =
region.dst_offset[i] as i32;
rs_image_post_process_params.dxdy[i] = 0;
groups = region.extent[i];
}
1..=std::i32::MAX => {
// Copy backwards.
rs_image_post_process_params.src_offset[i] =
(region.src_offset[i] + region.extent[i]) as i32 - 1;
rs_image_post_process_params.dst_offset[i] =
(region.dst_offset[i] + region.extent[i]) as i32 - 1;
rs_image_post_process_params.dxdy[i] = -1;
extent = Some(region.extent[i]);
}
}
}
let extent = extent.unwrap_or_else(|| {
// Copy rows forward if direction is not important for either dimension.
rs_image_post_process_params.dxdy[0] = 1;
groups = region.extent[1];
region.extent[0]
});
let exposure_amount = extent.min((exposure[0].abs() + exposure[1].abs()) as u32);
rs_image_post_process_params.exposure = exposure_amount;
rs_image_post_process_params.extent = extent - exposure_amount;
rs_image_post_process_params.src_dims =
[src_image.width as i32, src_image.height as i32];
rs_image_post_process_params.border = *border;
const LOCAL_SIZE_X: u32 = 48;
// TODO: Clip output to extent instead of rounding up.
rs_image_post_process.group_count_x = (groups + LOCAL_SIZE_X - 1) / LOCAL_SIZE_X;
rs_image_post_process.ext = rs_image_ext;
rs_image_ext = &mut rs_image_post_process as *mut _ as *mut c_void;
} else {
let src_image = self.images.get(&image_id).expect("invalid image");
let mut src_y = region.src_offset[1];
let mut dst_y = region.dst_offset[1];
let mut height = region.extent[1];
self.image_post_copy_regions.clear();
while height > 0 {
let rows = height.min(src_image.height - src_y);
let mut width = region.extent[0];
let mut src_x = region.src_offset[0];
let mut dst_x = region.dst_offset[0];
while width > 0 {
let columns = width.min(src_image.width - src_x);
self.image_post_copy_regions.push(vk::ImageCopy {
srcSubresource: vk::ImageSubresourceLayers {
aspectMask: vk::IMAGE_ASPECT_COLOR_BIT,
mipLevel: 0,
baseArrayLayer: 0,
layerCount: 1,
},
srcOffset: vk::Offset3D { x: src_x as i32, y: src_y as i32, z: 0 },
dstSubresource: vk::ImageSubresourceLayers {
aspectMask: vk::IMAGE_ASPECT_COLOR_BIT,
mipLevel: 0,
baseArrayLayer: 0,
layerCount: 1,
},
dstOffset: vk::Offset3D { x: dst_x as i32, y: dst_y as i32, z: 0 },
extent: vk::Extent3D { width: columns, height: rows, depth: 1 },
});
width -= columns;
dst_x += columns;
src_x = 0;
}
height -= rows;
dst_y += rows;
src_y = 0;
}
let dst_image = self.images.get_mut(&dst).expect("invalid image");
if dst_image.layout != vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL {
dst_image.layout_transition(
vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
vk::PIPELINE_STAGE_TRANSFER_BIT,
vk::ACCESS_TRANSFER_WRITE_BIT,
);
}
rs_image_post_copy.region_count = self.image_post_copy_regions.len() as u32;
rs_image_post_copy.regions = self.image_post_copy_regions.as_ptr();
rs_image_post_copy.dst = dst_image.image;
rs_image_post_copy.ext = rs_image_ext;
rs_image_ext = &mut rs_image_post_copy as *mut _ as *mut c_void;
}
post_image_layout = vk::IMAGE_LAYOUT_GENERAL;
}
let image = self.images.get_mut(&image_id).expect("invalid image");
if post_image_layout != vk::IMAGE_LAYOUT_GENERAL {
rs_image_post_barrier.new_layout = post_image_layout;
rs_image_post_barrier.ext = rs_image_ext;
rs_image_ext = &mut rs_image_post_barrier as *mut _ as *mut c_void;
}
image.layout = post_image_layout;
let mut rs_image = SpnVkRenderSubmitExtImageRender {
ext: rs_image_ext,
type_: SpnVkRenderSubmitExtType::SpnVkRenderSubmitExtTypeImageRender,
image: image.image,
image_info: vk::DescriptorImageInfo {
sampler: image.sampler,
imageView: image.view,
imageLayout: vk::IMAGE_LAYOUT_GENERAL,
},
submitter_pfn: image_dispatch_submitter,
// Safe as image outlive context.
submitter_data: image as &mut VulkanImage as *mut _ as *mut c_void,
};
let composition = self.compositions.get(&composition_id).expect("invalid composition");
let rs = SpnRenderSubmit {
ext: &mut rs_image as *mut _ as *mut c_void,
styling: self.styling.styling,
composition: composition.composition,
clip: *clip,
};
let status = unsafe { spn_render(*self.context.borrow(), &rs) };
assert_eq!(status, SpnSuccess);
// Unsealing the composition will block until rendering has completed.
// TODO(reveman): Use fence instead of blocking.
unsafe {
let status = spn_composition_unseal(composition.composition);
assert_eq!(status, SpnSuccess);
}
}
}
//
// Mold Implementation
//
struct MoldPathBuilder {
path_builder: spinel_mold::PathBuilder,
}
impl MoldPathBuilder {
fn new() -> Self {
Self { path_builder: spinel_mold::PathBuilder::new() }
}
}
impl PathBuilder for MoldPathBuilder {
fn begin(&mut self) {}
fn end(&mut self) -> Path {
Path::Mold(self.path_builder.build())
}
fn move_to(&mut self, p: &Point) {
self.path_builder.move_to(p.x, p.y);
}
fn line_to(&mut self, p: &Point) {
self.path_builder.line_to(p.x, p.y);
}
}
struct MoldRasterBuilder {
raster_builder: spinel_mold::RasterBuilder,
}
impl MoldRasterBuilder {
fn new() -> Self {
Self { raster_builder: spinel_mold::RasterBuilder::new() }
}
}
impl RasterBuilder for MoldRasterBuilder {
fn begin(&mut self) {}
fn end(&mut self) -> Raster {
Raster::Mold(self.raster_builder.build())
}
fn add(&mut self, path: &Path, transform: &Transform2D<f32>, _clip: &[f32; 4]) {
let transform: [f32; 9] = [
transform.m11,
transform.m21,
transform.m31,
transform.m12,
transform.m22,
transform.m32,
0.0,
0.0,
1.0,
];
match path {
Path::Mold(path) => self.raster_builder.push_path(path.clone(), &transform),
_ => {
panic!("bad path");
}
}
}
}
struct MoldStyling {
styling: spinel_mold::Styling,
}
impl MoldStyling {
fn new() -> Self {
Self { styling: spinel_mold::Styling::new() }
}
}
impl Styling for MoldStyling {
fn seal(&mut self) {}
fn unseal(&mut self) {}
fn reset(&mut self) {
self.styling.reset();
}
fn alloc_group(
&mut self,
range_lo: u32,
range_hi: u32,
background_color: &[f32; 4],
) -> GroupId {
let group_id = self.styling.group_alloc();
self.styling.group_range_lo(group_id, range_lo);
self.styling.group_range_hi(group_id, range_hi);
let cmds_enter = self.styling.group_enter(group_id, 1);
unsafe {
cmds_enter.write(spinel_mold::SPN_STYLING_OPCODE_COLOR_ACC_ZERO);
}
let mut cmds_leave = self.styling.group_leave(group_id, 4);
let mut bytes = [0u8; 4];
for i in 0..4 {
bytes[i] = u8::try_from((background_color[i] * 255.0).round() as u32)
.expect("RGBA colors must be between 0.0 and 1.0");
}
unsafe {
cmds_leave.write(spinel_mold::SPN_STYLING_OPCODE_COLOR_ACC_OVER_BACKGROUND);
cmds_leave = cmds_leave.add(1);
cmds_leave.write(u32::from_be_bytes(bytes));
cmds_leave = cmds_leave.add(1);
cmds_leave.write(0);
cmds_leave = cmds_leave.add(1);
cmds_leave.write(spinel_mold::SPN_STYLING_OPCODE_COLOR_ACC_STORE_TO_SURFACE);
}
GroupId::Mold(group_id)
}
fn group_layer(&mut self, group_id: &GroupId, layer_id: u32, color: &[f32; 4]) {
let group_id = match group_id {
GroupId::Mold(id) => *id,
_ => panic!("invalid group id"),
};
let mut cmds = self.styling.layer(group_id, layer_id, 6);
unsafe {
cmds.write(spinel_mold::SPN_STYLING_OPCODE_COVER_WIP_ZERO);
cmds = cmds.add(1);
cmds.write(spinel_mold::SPN_STYLING_OPCODE_COVER_NONZERO);
cmds = cmds.add(1);
}
let mut bytes = [0u8; 4];
for i in 0..4 {
bytes[i] = u8::try_from((color[i] * 255.0).round() as u32)
.expect("RGBA colors must be between 0.0 and 1.0");
}
unsafe {
cmds.write(spinel_mold::SPN_STYLING_OPCODE_COLOR_FILL_SOLID);
cmds = cmds.add(1);
cmds.write(u32::from_be_bytes(bytes));
cmds = cmds.add(1);
cmds.write(0);
cmds = cmds.add(1);
cmds.write(spinel_mold::SPN_STYLING_OPCODE_BLEND_OVER);
}
}
}
struct MoldComposition {
composition: spinel_mold::Composition,
}
impl MoldComposition {
fn new() -> Self {
Self { composition: spinel_mold::Composition::new() }
}
}
impl Composition for MoldComposition {
fn seal(&mut self) {}
fn unseal(&mut self) {}
fn reset(&mut self) {
self.composition.reset();
}
fn set_clip(&mut self, _clip: &[u32; 4]) {}
fn place(&mut self, raster: &Raster, layer_id: u32) {
match raster {
Raster::Mold(raster) => {
self.composition.place(layer_id, raster.clone());
}
_ => {
panic!("bad raster");
}
}
}
}
#[derive(Clone)]
struct MoldColorBuffer {
mapping: Arc<mapped_vmo::Mapping>,
stride: usize,
}
impl mold::ColorBuffer for MoldColorBuffer {
fn pixel_format(&self) -> mold::PixelFormat {
mold::PixelFormat::BGRA8888
}
fn stride(&self) -> usize {
self.stride
}
unsafe fn write_at(&mut self, offset: usize, src: *const u8, len: usize) {
self.mapping.write_at(offset, std::slice::from_raw_parts(src, len));
}
}
struct MoldImage {
vmo: zx::Vmo,
width: u32,
height: u32,
size_bytes: u64,
color_buffer: MoldColorBuffer,
map: Option<mold::tile::Map>,
old_prints: HashSet<u32>,
new_prints: HashSet<u32>,
}
impl MoldImage {
fn new(width: u32, height: u32) -> Self {
let size_bytes = width * height * 4;
let (mapping, vmo) =
mapped_vmo::Mapping::allocate(size_bytes as usize).expect("Mapping::allocate");
MoldImage {
vmo,
width,
height,
size_bytes: size_bytes as u64,
color_buffer: MoldColorBuffer { mapping: Arc::new(mapping), stride: width as usize },
map: None,
old_prints: HashSet::new(),
new_prints: HashSet::new(),
}
}
fn from_buffer_collection(
buffer_collection: &mut BufferCollectionSynchronousProxy,
index: u32,
width: u32,
height: u32,
) -> Self {
let (status, buffers) = buffer_collection
.wait_for_buffers_allocated(zx::Time::after(10.second()))
.expect("wait_for_buffers_allocated");
assert_eq!(status, zx::sys::ZX_OK);
let vmo_buffer = &buffers.buffers[index as usize];
let vmo = vmo_buffer
.vmo
.as_ref()
.expect("vmo_buffer")
.duplicate_handle(zx::Rights::SAME_RIGHTS)
.expect("duplicating buffer vmo");
let size_bytes = buffers.settings.buffer_settings.size_bytes;
let mapping = Arc::new(
mapped_vmo::Mapping::create_from_vmo(
&vmo,
size_bytes as usize,
zx::VmarFlags::PERM_READ
| zx::VmarFlags::PERM_WRITE
| zx::VmarFlags::MAP_RANGE
| zx::VmarFlags::REQUIRE_NON_RESIZABLE,
)
.expect("Mapping::create_from_vmo"),
);
assert_eq!(buffers.settings.has_image_format_constraints, true);
let bytes_per_row = buffers.settings.image_format_constraints.min_bytes_per_row;
let divisor = buffers.settings.image_format_constraints.bytes_per_row_divisor;
let bytes_per_row = ((bytes_per_row + divisor - 1) / divisor) * divisor;
MoldImage {
vmo,
width,
height,
size_bytes: size_bytes as u64,
color_buffer: MoldColorBuffer { mapping, stride: (bytes_per_row / 4) as usize },
map: None,
old_prints: HashSet::new(),
new_prints: HashSet::new(),
}
}
}
impl Image for MoldImage {
fn flush(&mut self) {
self.vmo
.op_range(zx::VmoOp::CACHE_CLEAN_INVALIDATE, 0, self.size_bytes)
.expect("cache clean");
}
}
pub struct MoldContext {
buffer_collection: BufferCollectionSynchronousProxy,
size: [u32; 2],
images: BTreeMap<u32, MoldImage>,
compositions: BTreeMap<u32, MoldComposition>,
styling: MoldStyling,
path_builder: MoldPathBuilder,
raster_builder: MoldRasterBuilder,
}
impl MoldContext {
fn linear_image_format_constraints(width: u32, height: u32) -> ImageFormatConstraints {
ImageFormatConstraints {
pixel_format: SysmemPixelFormat {
type_: PixelFormatType::Bgra32,
has_format_modifier: true,
format_modifier: FormatModifier { value: FORMAT_MODIFIER_LINEAR },
},
color_spaces_count: 1,
color_space: [ColorSpace { type_: ColorSpaceType::Srgb }; 32],
min_coded_width: width,
max_coded_width: std::u32::MAX,
min_coded_height: height,
max_coded_height: std::u32::MAX,
min_bytes_per_row: width * 4,
max_bytes_per_row: std::u32::MAX,
max_coded_width_times_coded_height: std::u32::MAX,
layers: 1,
coded_width_divisor: 1,
coded_height_divisor: 1,
bytes_per_row_divisor: 4,
start_offset_divisor: 1,
display_width_divisor: 1,
display_height_divisor: 1,
required_min_coded_width: 0,
required_max_coded_width: 0,
required_min_coded_height: 0,
required_max_coded_height: 0,
required_min_bytes_per_row: 0,
required_max_bytes_per_row: 0,
}
}
fn buffer_memory_constraints(width: u32, height: u32) -> BufferMemoryConstraints {
BufferMemoryConstraints {
min_size_bytes: width * height * 4,
max_size_bytes: std::u32::MAX,
physically_contiguous_required: false,
secure_required: false,
ram_domain_supported: true,
cpu_domain_supported: true,
inaccessible_domain_supported: false,
heap_permitted_count: 1,
heap_permitted: [HeapType::SystemRam; 32],
}
}
fn buffer_collection_constraints(width: u32, height: u32) -> BufferCollectionConstraints {
BufferCollectionConstraints {
usage: BufferUsage {
none: 0,
cpu: CPU_USAGE_WRITE_OFTEN,
vulkan: 0,
display: 0,
video: 0,
},
min_buffer_count_for_camping: 0,
min_buffer_count_for_dedicated_slack: 0,
min_buffer_count_for_shared_slack: 0,
min_buffer_count: 1,
max_buffer_count: std::u32::MAX,
has_buffer_memory_constraints: true,
buffer_memory_constraints: Self::buffer_memory_constraints(width, height),
image_format_constraints_count: 1,
image_format_constraints: [Self::linear_image_format_constraints(width, height); 32],
}
}
pub fn new(
token: ClientEnd<BufferCollectionTokenMarker>,
config: &fuchsia_framebuffer::Config,
) -> Self {
let sysmem = connect_to_service::<AllocatorMarker>().unwrap();
let (collection_client, collection_request) = zx::Channel::create().unwrap();
sysmem
.bind_shared_collection(
ClientEnd::new(token.into_channel()),
ServerEnd::new(collection_request),
)
.unwrap();
let mut buffer_collection = BufferCollectionSynchronousProxy::new(collection_client);
let mut constraints = Self::buffer_collection_constraints(config.width, config.height);
buffer_collection
.set_constraints(true, &mut constraints)
.expect("Sending buffer constraints to sysmem");
Self {
buffer_collection,
size: [config.width, config.height],
images: BTreeMap::new(),
compositions: BTreeMap::new(),
styling: MoldStyling::new(),
path_builder: MoldPathBuilder::new(),
raster_builder: MoldRasterBuilder::new(),
}
}
fn copy_region(
src_ptr: *mut u8,
src_width: usize,
src_height: usize,
src_bytes_per_row: usize,
dst_ptr: *mut u8,
dst_len: usize,
dst_bytes_per_row: usize,
region: &CopyRegion,
) {
let (mut y, dy) = if region.dst_offset[1] < region.src_offset[1] {
// Copy forward.
(0, 1)
} else {
// Copy backwards.
(region.extent[1] as i32 - 1, -1)
};
let mut extent = region.extent[1];
while extent > 0 {
let src_y = (region.src_offset[1] + y as u32) % src_height as u32;
let dst_y = region.dst_offset[1] + y as u32;
let mut src_x = region.src_offset[0] as usize;
let mut dst_x = region.dst_offset[0] as usize;
let mut width = region.extent[0] as usize;
while width > 0 {
let columns = width.min(src_width - src_x);
let src_offset = src_y as usize * src_bytes_per_row + src_x * 4;
let dst_offset = dst_y as usize * dst_bytes_per_row + dst_x * 4;
assert!((dst_offset + dst_bytes_per_row) <= dst_len);
let src = (src_ptr as usize).checked_add(src_offset).unwrap() as *mut u8;
let dst = (dst_ptr as usize).checked_add(dst_offset).unwrap() as *mut u8;
unsafe {
ptr::copy(src, dst, (columns * 4) as usize);
}
width -= columns;
dst_x += columns;
src_x = 0;
}
y += dy;
extent -= 1;
}
}
}
impl Context for MoldContext {
fn get_pixel_format(&self) -> fuchsia_framebuffer::PixelFormat {
fuchsia_framebuffer::PixelFormat::RgbX888
}
fn styling(&mut self) -> &mut dyn Styling {
&mut self.styling
}
fn path_builder(&mut self) -> &mut dyn PathBuilder {
&mut self.path_builder
}
fn raster_builder(&mut self) -> &mut dyn RasterBuilder {
&mut self.raster_builder
}
fn composition(&mut self, id: u32) -> &mut dyn Composition {
self.compositions.entry(id).or_insert_with(|| MoldComposition::new())
}
fn image(&mut self, id: u32, size: &[u32; 2]) -> &mut dyn Image {
self.images.entry(id).or_insert_with(|| MoldImage::new(size[0], size[1]))
}
fn image_from_index(&mut self, id: u32, index: u32) -> &mut dyn Image {
let buffer_collection = &mut self.buffer_collection;
let width = self.size[0];
let height = self.size[1];
self.images.entry(id).or_insert_with(|| {
MoldImage::from_buffer_collection(buffer_collection, index, width, height)
})
}
fn render(&mut self, image_id: u32, composition_id: u32, clip: &[u32; 4], exts: &[RenderExt]) {
let mut pre_clear = None;
let mut pre_copy = None;
let mut post_copy = None;
for ext in exts {
match ext {
RenderExt::PreClear(_) => {
pre_clear = Some(ext);
}
RenderExt::PreCopy(_) => {
pre_copy = Some(ext);
}
RenderExt::PostCopy(_) => {
post_copy = Some(ext);
}
}
}
let width = self.size[0] as usize;
let height = self.size[1] as usize;
let image = self.images.get_mut(&image_id).expect("invalid image");
let mut map = image
.map
.take()
.filter(|map| map.width() == width && map.height() == height)
.unwrap_or_else(|| mold::tile::Map::new(width, height));
if let Some(RenderExt::PreClear(color)) = pre_clear {
let (ptr, _) = Arc::get_mut(&mut image.color_buffer.mapping).unwrap().as_ptr_len();
let bgra = [color[2], color[1], color[0], color[3]];
let mut bytes = [0u8; 4];
for i in 0..4 {
bytes[i] = u8::try_from((bgra[i] * 255.0).round() as u32)
.expect("RGBA colors must be between 0.0 and 1.0");
}
for i in (0..image.size_bytes).step_by(4) {
let dst = (ptr as usize).checked_add(i as usize).unwrap() as *mut u8;
unsafe {
ptr::copy_nonoverlapping(bytes.as_mut_ptr(), dst, 4);
}
}
for id in image.old_prints.drain() {
map.remove(id);
}
}
if let Some(RenderExt::PreCopy((src, region))) = pre_copy {
let src_image = self.images.get_mut(src).expect("invalid image");
let (src_ptr, _) =
Arc::get_mut(&mut src_image.color_buffer.mapping).unwrap().as_ptr_len();
let src_bytes_per_row = src_image.color_buffer.stride * 4;
let src_width = src_image.width as usize;
let src_height = src_image.height as usize;
let dst_image = self.images.get_mut(&image_id).expect("invalid image");
let (dst_ptr, dst_len) =
Arc::get_mut(&mut dst_image.color_buffer.mapping).unwrap().as_ptr_len();
let dst_bytes_per_row = dst_image.color_buffer.stride * 4;
Self::copy_region(
src_ptr,
src_width,
src_height,
src_bytes_per_row,
dst_ptr,
dst_len as usize,
dst_bytes_per_row,
region,
);
}
map.set_clip(Some(mold::Clip {
x: clip[0] as usize,
y: clip[1] as usize,
width: (clip[2] - clip[0]) as usize,
height: (clip[3] - clip[1]) as usize,
}));
let image = self.images.get_mut(&image_id).expect("invalid image");
let composition = self.compositions.get_mut(&composition_id).expect("invalid composition");
self.styling.styling.prints(&mut composition.composition, &mut map, &mut image.new_prints);
for &id in image.old_prints.difference(&image.new_prints) {
map.remove(id);
}
image.old_prints.clear();
mem::swap(&mut image.old_prints, &mut image.new_prints);
map.render(image.color_buffer.clone());
image.map = Some(map);
// TODO: Motion blur support.
if let Some(RenderExt::PostCopy((dst, _, _, region))) = post_copy {
let (src_ptr, _) = Arc::get_mut(&mut image.color_buffer.mapping).unwrap().as_ptr_len();
let src_bytes_per_row = image.color_buffer.stride * 4;
let dst_image = self.images.get_mut(dst).expect("invalid image");
let (dst_ptr, dst_len) =
Arc::get_mut(&mut dst_image.color_buffer.mapping).unwrap().as_ptr_len();
let dst_bytes_per_row = dst_image.color_buffer.stride * 4;
Self::copy_region(
src_ptr,
width,
height,
src_bytes_per_row,
dst_ptr,
dst_len as usize,
dst_bytes_per_row,
region,
);
}
}
}