内容简介:文档列表见:Texture的使用已在
文档列表见: Rust 移动端跨平台复杂图形渲染项目开发系列总结(目录)
Texture的使用已在 以OpenGL/ES视角介绍gfx-hal(Vulkan) Texture接口使用 介绍,本文档分析Vulkan接口如何映射到Metal,内容安排完全参考前一个文档,建议结合阅读。源码总路径为src/backend/metal/src。
创建纹理背后的故事
Metal如何创建Image
fn create_image
根据传递的Texture信息配置MTLTextureDescriptor,按照Metal开发套路,之后通过这个描述符向MTLDevice申请具体的资源,细节见文件:device.rs。
fn create_image( &self, kind: image::Kind, mip_levels: image::Level, format: format::Format, tiling: image::Tiling, usage: image::Usage, flags: image::StorageFlags, ) -> Result<n::UnboundImage, image::CreationError> { debug!("create_image {:?} with {} mips of {:?} {:?} and usage {:?}", kind, mip_levels, format, tiling, usage); let is_cube = flags.contains(image::StorageFlags::CUBE_VIEW); let mtl_format = self.private_caps .map_format(format) .ok_or(image::CreationError::Format(format))?; let descriptor = metal::TextureDescriptor::new(); let (mtl_type, num_layers) = match kind { image::Kind::D1(_, 1) => { assert!(!is_cube); (MTLTextureType::D1, None) } image::Kind::D1(_, layers) => { assert!(!is_cube); (MTLTextureType::D1Array, Some(layers)) } image::Kind::D2(_, _, layers, 1) => { if is_cube && layers > 6 { assert_eq!(layers % 6, 0); (MTLTextureType::CubeArray, Some(layers / 6)) } else if is_cube { assert_eq!(layers, 6); (MTLTextureType::Cube, None) } else if layers > 1 { (MTLTextureType::D2Array, Some(layers)) } else { (MTLTextureType::D2, None) } } image::Kind::D2(_, _, 1, samples) if !is_cube => { descriptor.set_sample_count(samples as u64); (MTLTextureType::D2Multisample, None) } image::Kind::D2(..) => { error!("Multi-sampled array textures or cubes are not supported: {:?}", kind); return Err(image::CreationError::Kind) } image::Kind::D3(..) => { assert!(!is_cube); (MTLTextureType::D3, None) } }; descriptor.set_texture_type(mtl_type); if let Some(count) = num_layers { descriptor.set_array_length(count as u64); } let extent = kind.extent(); descriptor.set_width(extent.width as u64); descriptor.set_height(extent.height as u64); descriptor.set_depth(extent.depth as u64); descriptor.set_mipmap_level_count(mip_levels as u64); descriptor.set_pixel_format(mtl_format); descriptor.set_usage(conv::map_texture_usage(usage, tiling)); let format_desc = format.surface_desc(); let mip_sizes = (0 .. mip_levels) .map(|level| { let pitches = n::Image::pitches_impl(extent.at_level(level), format_desc); num_layers.unwrap_or(1) as buffer::Offset * pitches[3] }) .collect(); let host_usage = image::Usage::TRANSFER_SRC | image::Usage::TRANSFER_DST; let host_visible = mtl_type == MTLTextureType::D2 && mip_levels == 1 && num_layers.is_none() && format_desc.aspects.contains(format::Aspects::COLOR) && tiling == image::Tiling::Linear && host_usage.contains(usage); Ok(n::UnboundImage { texture_desc: descriptor, format, kind, mip_sizes, host_visible, }) } 复制代码
获取Image的存储要求
fn get_image_requirements
根据前面创建的MTLTextureDescriptor属性及设备能力创建合适的 Requirements
,它包含大小、对齐、内存类型信息,这些属性决定了内存的访问速度。
/// Memory requirements for a certain resource (buffer/image). #[derive(Clone, Copy, Debug)] pub struct Requirements { /// Size in the memory. pub size: u64, /// Memory alignment. pub alignment: u64, /// Supported memory types. pub type_mask: u64, } fn get_image_requirements(&self, image: &n::UnboundImage) -> memory::Requirements { if self.private_caps.resource_heaps { // We don't know what memory type the user will try to allocate the image with, so we test them // all get the most stringent ones. Note we don't check Shared because heaps can't use it let mut max_size = 0; let mut max_alignment = 0; let types = if image.host_visible { MemoryTypes::all() } else { MemoryTypes::PRIVATE }; for (i, _) in self.memory_types.iter().enumerate() { if !types.contains(MemoryTypes::from_bits(1 << i).unwrap()) { continue } let (storage, cache_mode) = MemoryTypes::describe(i); image.texture_desc.set_storage_mode(storage); image.texture_desc.set_cpu_cache_mode(cache_mode); let requirements = self.shared.device .lock() .heap_texture_size_and_align(&image.texture_desc); max_size = cmp::max(max_size, requirements.size); max_alignment = cmp::max(max_alignment, requirements.align); } memory::Requirements { size: max_size, alignment: max_alignment, type_mask: types.bits(), } } else if image.host_visible { assert_eq!(image.mip_sizes.len(), 1); let mask = self.private_caps.buffer_alignment - 1; memory::Requirements { size: (image.mip_sizes[0] + mask) & !mask, alignment: self.private_caps.buffer_alignment, type_mask: MemoryTypes::all().bits(), } } else { memory::Requirements { size: image.mip_sizes.iter().sum(), alignment: 4, type_mask: MemoryTypes::PRIVATE.bits(), } } } 复制代码
分配Texture的支持存储空间
fn allocate_memory
根据前面的存储模式MTLStorageMode要求决定申请Metal设备分配Buffer还是MTLHeap(gfx目前有bug,执行不到这个分支),如果StorageMode为 MTLStorageMode::Private
则在后面的 fn bind_image_memory
分配MTLTexture对象。
fn allocate_memory(&self, memory_type: hal::MemoryTypeId, size: u64) -> Result<n::Memory, OutOfMemory> { let (storage, cache) = MemoryTypes::describe(memory_type.0); let device = self.shared.device.lock(); debug!("allocate_memory type {:?} of size {}", memory_type, size); // Heaps cannot be used for CPU coherent resources //TEMP: MacOS supports Private only, iOS and tvOS can do private/shared let heap = if self.private_caps.resource_heaps && storage != MTLStorageMode::Shared && false { let descriptor = metal::HeapDescriptor::new(); descriptor.set_storage_mode(storage); descriptor.set_cpu_cache_mode(cache); descriptor.set_size(size); let heap_raw = device.new_heap(&descriptor); n::MemoryHeap::Native(heap_raw) } else if storage == MTLStorageMode::Private { n::MemoryHeap::Private } else { let options = conv::resource_options_from_storage_and_cache(storage, cache); let cpu_buffer = device.new_buffer(size, options); debug!("\tbacked by cpu buffer {:?}", cpu_buffer.as_ptr()); n::MemoryHeap::Public(memory_type, cpu_buffer) }; Ok(n::Memory::new(heap, size)) } 复制代码
分配实际的Texture对象
fn bind_image_memory
根据MemoryHeap类型( Native
、 Public
、 Private
)决定是从MTLDevice直接分配MTLTexture对象还是从MTLHeap中分配。 从目前来看MTLHeap似乎会多占一些内存,有待进一步确认。
fn bind_image_memory( &self, memory: &n::Memory, offset: u64, image: n::UnboundImage ) -> Result<n::Image, BindError> { let base = image.format.base_format(); let format_desc = base.0.desc(); let like = match memory.heap { n::MemoryHeap::Native(ref heap) => { let resource_options = conv::resource_options_from_storage_and_cache( heap.storage_mode(), heap.cpu_cache_mode()); image.texture_desc.set_resource_options(resource_options); n::ImageLike::Texture( heap.new_texture(&image.texture_desc) .unwrap_or_else(|| { // TODO: disable hazard tracking? self.shared.device .lock() .new_texture(&image.texture_desc) }) ) }, n::MemoryHeap::Public(_memory_type, ref cpu_buffer) => { assert_eq!(image.mip_sizes.len(), 1); n::ImageLike::Buffer(n::Buffer { raw: cpu_buffer.clone(), range: offset .. offset + image.mip_sizes[0] as u64, options: MTLResourceOptions::StorageModeShared, }) } n::MemoryHeap::Private => { image.texture_desc.set_storage_mode(MTLStorageMode::Private); n::ImageLike::Texture( self.shared.device .lock() .new_texture(&image.texture_desc) ) } }; Ok(n::Image { like, kind: image.kind, format_desc, shader_channel: base.1.into(), mtl_format: match self.private_caps.map_format(image.format) { Some(format) => format, None => { error!("failed to find corresponding Metal format for {:?}", image.format); return Err(BindError::OutOfBounds); }, }, mtl_type: image.texture_desc.texture_type(), }) } 复制代码
上传数据到纹理背后的故事
创建Staging Buffer
此部分参考Buffer的相关操作。
创建Fence
Metal目前没提供类似Vulkan的Fence数据结构,gfx在此利用MTLCommandBuffer模拟这一行为。
fn create_fence(&self, signaled: bool) -> n::Fence { n::Fence(RefCell::new(n::FenceInner::Idle { signaled })) } 复制代码
创建用于数据拷贝的Submmit
创建带类型的Command Pool
let mut staging_pool = device.borrow().device.create_command_pool_typed( &device.borrow().queues, pool::CommandPoolCreateFlags::empty(), 16, ); 复制代码
/// Create a new command pool for a given queue family. /// /// *Note*: the family has to be associated by one as the `Gpu::queue_groups`. fn create_command_pool(&self, family: QueueFamilyId, create_flags: CommandPoolCreateFlags) -> B::CommandPool; /// Create a strongly typed command pool wrapper. fn create_command_pool_typed<C>( &self, group: &QueueGroup<B, C>, flags: CommandPoolCreateFlags, max_buffers: usize, ) -> CommandPool<B, C> { let raw = self.create_command_pool(group.family(), flags); let mut pool = unsafe { CommandPool::new(raw) }; pool.reserve(max_buffers); pool } 复制代码
fn create_command_pool( &self, _family: QueueFamilyId, _flags: CommandPoolCreateFlags ) -> command::CommandPool { command::CommandPool::new(&self.shared, self.online_recording.clone()) } 复制代码
pub struct CommandPool { shared: Arc<Shared>, allocated: Vec<CommandBufferInnerPtr>, pool_shared: PoolSharedPtr, } impl CommandPool { pub(crate) fn new( shared: &Arc<Shared>, online_recording: OnlineRecording, ) -> Self { let pool_shared = PoolShared { #[cfg(feature = "dispatch")] dispatch_queue: match online_recording { OnlineRecording::Immediate | OnlineRecording::Deferred => None, OnlineRecording::Remote(priority) => Some(dispatch::Queue::global(priority.clone())), }, online_recording, }; CommandPool { shared: Arc::clone(shared), allocated: Vec::new(), pool_shared: Arc::new(RefCell::new(pool_shared)), } } } 复制代码
创建Command Buffer
/// Get a primary command buffer for recording. /// /// You can only record to one command buffer per pool at the same time. /// If more command buffers are requested than allocated, new buffers will be reserved. /// The command buffer will be returned in 'recording' state. pub fn acquire_command_buffer<S: Shot>( &mut self, allow_pending_resubmit: bool ) -> CommandBuffer<B, C, S> { self.reserve(1); let buffer = &mut self.buffers[self.next_buffer]; let mut flags = S::FLAGS; if allow_pending_resubmit { flags |= CommandBufferFlags::SIMULTANEOUS_USE; } buffer.begin(flags, CommandBufferInheritanceInfo::default()); self.next_buffer += 1; unsafe { CommandBuffer::new(buffer) } } 复制代码
创建Barrier
复制代码
向Command Buffer提交Barrier
复制代码
向Command Buffer提交Copy Buffer to Image命令
/// Identical to the `RawCommandBuffer` method of the same name. pub fn copy_buffer_to_image<T>( &mut self, src: &B::Buffer, dst: &B::Image, dst_layout: image::Layout, regions: T, ) where T: IntoIterator, T::Item: Borrow<BufferImageCopy>, { self.raw.copy_buffer_to_image(src, dst, dst_layout, regions) } 复制代码
fn copy_buffer_to_image<T>( &mut self, src: &native::Buffer, dst: &native::Image, _dst_layout: Layout, regions: T, ) where T: IntoIterator, T::Item: Borrow<com::BufferImageCopy>, { match dst.like { native::ImageLike::Texture(ref dst_raw) => { let commands = regions.into_iter().filter_map(|region| { let r = region.borrow(); if r.image_extent.is_empty() { None } else { Some(soft::BlitCommand::CopyBufferToImage { src: AsNative::from(src.raw.as_ref()), dst: AsNative::from(dst_raw.as_ref()), dst_desc: dst.format_desc, region: com::BufferImageCopy { buffer_offset: r.buffer_offset + src.range.start, .. r.clone() }, }) } }); self.inner .borrow_mut() .sink() .blit_commands(commands); } native::ImageLike::Buffer(ref dst_buffer) => { self.copy_buffer(src, dst_buffer, regions.into_iter().map(|region| { let r = region.borrow(); com::BufferCopy { src: r.buffer_offset, dst: dst.byte_offset(r.image_offset), size: dst.byte_extent(r.image_extent), } })) } } } 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Zepto源码学习Event模块
- NodeJS Cluster模块源码学习
- NodeJS Events模块源码学习
- 试读angular源码第四章:angular模块及JIT编译模块
- 对公司内部某个模块某个源码审计
- 比特币源码分析:txdb 模块(三)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
新媒体革命——在线时代的媒体、公关与传播
仇勇 / 电子工业出版社 / 2016-2-1 / CNY 50.00
这既是传统媒体的大裂变年代,也是在线媒体开启的新闻业的黄金时代。 信息流动的新法则不仅改变了媒体业,也在重塑公关、传播和商业的面貌。总之,这个世界的连接方式不仅不再相同,而且这一改变不可逆转。在这个全新重启的在线时代里,无论是信息的获取还是商业本身,信任都变得比以往更重要。 从告别传统媒体的那一刻起,我就有着两个小小的“野心”:一是探寻适合在线时代的媒体生产方式;二是让优质内容有权获得......一起来看看 《新媒体革命——在线时代的媒体、公关与传播》 这本书的介绍吧!