Metal日記:使用步驟指南

本文參考資料:

juejin.im/post/5b1e8f…

xiaozhuanlan.com/topic/04598…

developer.apple.com/videos/play…

github.com/quinn0809/G…

cloud.tencent.com/developer/a…

devstreaming-cdn.apple.com/videos/wwdc…

Metal處理邏輯

無論是CoreImage、GPUImage框架,還是Metal、OpenGL框架,處理邏輯類似:

輸入(資源+邏輯 )-> 黑盒 -> 輸出

CoreImage 可以選擇GPU處理->Metal->CoreImage,也可以選擇CPU處理

GPUImage 有OpenGL ES版,也有Metal版本(Metal 版本極為簡陋)

Metal使用大致分為:

  • build :shader
  • initialize :device and Queues Render Objects
  • Render:commandBuffer、ResourceUpdate、renderEncoder、Display

Metal 為控制GPU的編程語言 其實從代碼來講,大部分時間都是在CPU完成組件的創建,包括shader,pipline,encoder。

build :shader

主要完成shader的編譯,涉及到vertex 、fragment

Metal中的shader是MSL語言,SIMD的存在支持MSL與原生代碼共享數據結構。

一個簡單的vertexShader :

vertex ThreeInputVertexIO threeInputVertex(device packed_float2 *position [[buffer(0)]],device packed_float2 *texturecoord [[buffer(1)]],device packed_float2 *texturecoord2 [[buffer(2)]],uint vid [[vertex_id]])
{ThreeInputVertexIO outputVertices;outputVertices.position = float4(position[vid], 0, 1.0);outputVertices.textureCoordinate = texturecoord[vid];outputVertices.textureCoordinate2 = texturecoord2[vid];return outputVertices;
}
復制代碼

outputVertices.position = float4(position[vid], 0, 1.0); position[vid] 是float2 SIMD 是 Apple 提供的一款方便原生程序與著色器程序共享數據結構的庫。

開發者可以基于SIMD框架在Objective-C頭文件中定義一系列數據結構,在原生代碼和著色器程序中通過#include包含這個頭文件,兩者就都有了這個結構的定義。

ThreeInputVertexIO 聲明如下:

struct ThreeInputVertexIO
{float4 position [[position]];float2 textureCoordinate [[user(texturecoord)]];float2 textureCoordinate [[user(texturecoord2)]];};
復制代碼

device packed_float2 *position [[buffer(0)]]

device packed_float2 *texturecoord [[buffer(1)]]

packed_float2是類型 positiontexturecoord是變量名

device是內存修飾符,Metal種的內存訪問主要有兩種方式:Device模式和Constant模式,由代碼中顯式指定。

Device模式是比較通用的訪問模式,使用限制比較少,而Constant模式是為了多次讀取而設計的快速訪問只讀模式,通過Constant內存模式訪問的參數的數據的字節數量是固定的,特點總結為: Device支持讀寫,并且沒有size的限制; Constant是只讀,并且限定大小; 如何選擇Device和Constant模式? 先看數據size是否會變化,再看訪問的頻率高低,只有那些固定size且經常訪問的部分適合使用constant模式,其他的均用Device。

[[buffer(0)]][[buffer(1)]]是句柄,在MSL中不同的類型用不同的buffer表示,與renderCommandEncoder時相對應:

    //buffer renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)renderEncoder.setVertexBuffer(textureBuffer1, offset: 0, index: 1)renderEncoder.setVertexBuffer(textureBuffer2, offset: 0, index: 2)······//samper[renderEncoder setFragmentSampler:sampler atIndex:0];[renderEncoder setFragmentSampler:sampler1 atIndex:0];······//texturerenderEncoder.setFragmentTexture(texture, index: 0)renderEncoder.setFragmentTexture(texture1, index: 1)······
復制代碼

index 與 [[buffer(0)]]相對應,如,此時上文MSL的vertexShader中

  • [[buffer(0)]] 為vertex數據
  • [[buffer(1)]]為第一個紋理坐標數據
  • [[buffer(2)]]為第二個紋理坐標數據

index與shader中聲明的[[buffer(x)]]嚴格對應,否則在Metal Validation Layer中極可能會報錯(通常是內存讀取越界),或者繪制出不符合預期的結果。 vertexShader的執行次數與頂點數量有關,即vid為索引數。

一個簡單的fragmentShader :

fragment half4 lookupSplitFragment(TwoInputVertexIO fragmentInput [[stage_in]],texture2d<half> inputTexture [[texture(0)]],texture2d<half> inputTexture2 [[texture(1)]],texture2d<half> inputTexture3 [[texture(2)]],constant SplitUniform& uniform [[ buffer(1) ]])
{}
復制代碼

同上文的renderCommandEncoder時,

  • inputTexture 為第一個紋理
  • inputTexture2 為第二個紋理
  • inputTexture3 為第三個紋理

SplitUniform 為自定義的參數,在此shader中的意義為split 的外界值。 SplitUniform的定義如下: 在metal文件中:

typedef struct
{float intensity;float progress;} SplitUniform;
復制代碼

『intensity』filter的濃度

『progress』filtersplit 進度

shader 在xcode building 的 時候就會被 編譯到 metal library中 至此,本次目標渲染的shader 已經完成,下面開始初始化工作,將shader通過渲染管線聯系起來。

初始化工作

  • devide
  • commandQueue
  • buffer
  • texture
  • pipline

初始化Device

devidemetal 控制的GPU 入口,是一個一次創建最好永久使用的對象,用來創建buffercommandtexture;在Metal最佳實踐之南中,指出開發者應該長期持有一個device對象(device 對象創建比較昂貴)

OC:

id<MTLDevice> device = MTLCreateSystemDefaultDevice();
復制代碼

Swift:

guard let device = MTLCreateSystemDefaultDevice() else {fatalError("Could not create Metal Device")
}
復制代碼

創建 CommandQueue 命令隊列

Metal 最佳實踐指南中,指出大部分情況下,開發者要重復使用一個命令隊列 通過Device -> commandQueue

/// device 創建命令隊列guard let commandQueue = self.device.makeCommandQueue() else {fatalError("Could not create command queue")}
復制代碼

創建 Buffer 數據

Metal 中,所有無結構的數據都使用 Buffer 來管理。與 OpenGL 類似的,頂點、索引等數據都通過 Buffer 管理。 比如:vertexBuffer、textureCoordBuffer

/// 紋理坐標buffer
let coordinateBuffer = device.makeBuffer(bytes: inputTextureCoordinates,length: inputTextureCoordinates.count * MemoryLayout<Float>.size,options: [])!
///頂點數據buffer
let vertexBuffer = device.makeBuffer(bytes: imageVertices,length: imageVertices.count * MemoryLayout<Float>.size,options: [])!
復制代碼

這些Buffer在renderCommandEncoder中 進行編碼然后提交到GPU

創建 Texture

texture 可以理解為被加工的對象,設計者為它增加了一個描述對象MTLTextureDescriptor

在Metal中,有一個抽象對象,專門由于描述 teture 的詳情(fromat,width,height,storageMode)

storageMode為 控制CPU、GPU的內存管理方式。Apple 推薦在 iOS 中使用 shared mode,而在 macOS 中使用 managed mode。

Shared Storage:CPU 和 GPU 均可讀寫這塊內存。
Private Storage: 僅 GPU 可讀寫這塊內存,可以通過 Blit 命令等進行拷貝。
Managed Storage: 僅在 macOS 中允許。僅 GPU 可讀寫這塊內存,但 Metal 會創建一塊鏡像內存供 CPU 使用
復制代碼

//紋理描述 器
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: pixelFormat,width: width,height: height,mipmapped: mipmapped)
//通過 devide創建簡單紋理(比如單色紋理)
guard let newTexture = device.makeTexture(descriptor: textureDescriptor) else {fatalError("Could not create texture of size: (\(width), \(height))")}// 通過 圖片創建 (MetalKit)
var textureLoader = MTKTextureLoader(device: self.device)
let imageTexture = try textureLoader.newTexture(cgImage: img, options: [MTKTextureLoader.Option.SRGB : false])復制代碼

MTKTextureLoader 也建議重復使用

創建 pipline 渲染管線

pipline:最為復雜的東西,也是最簡單的東西,說他復雜是因為,他的成員變量多;說簡單,是因為pipline只是一個所有資源的描述者

在Metal中,有一個抽象對象,專門由于描述 pipline 的 詳情的對象Descriptor,包含了(頂點著色器,片段著色器,顏色格式,深度等)

colorAttachments,用于寫入顏色數據
depthAttachment,用于寫入深度信息
stencilAttachment,允許我們基于一些條件丟棄指定片段MTLRenderPassDescriptor 里面的 colorAttachments,支持多達 4 個 用來存儲顏色像素數據的 attachment,在 2D 圖像處理時,我們一般只會關聯一個。
即 colorAttachments[0]。
復制代碼
let descriptor = MTLRenderPipelineDescriptor()descriptor.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unormdescriptor.vertexFunction = vertexFunctiondescriptor.fragmentFunction = fragmentFunction
復制代碼

關于shader 函數 的創建:

guard let vertexFunction = defaultLibrary.makeFunction(name: vertexFunctionName) else {fatalError("Could not compile vertex function \(vertexFunctionName)")
}guard let fragmentFunction = defaultLibrary.makeFunction(name: fragmentFunctionName) else {fatalError("Could not compile fragment function \(fragmentFunctionName)")
}
復制代碼

defaultLibrary 為通過device 創建 的 函數庫,上文我們在編譯的時候已經編譯好了頂點著色器以及片段著色器,這是通過

do {let frameworkBundle = Bundle(for: Context.self)let metalLibraryPath = frameworkBundle.path(forResource: "default", ofType: "metallib")!self.defaultLibrary = try device.makeLibrary(filepath:metalLibraryPath)} catch {fatalError("Could not load library")}復制代碼

可以獲取到 defaultLibrary,這是有Metal 提供的方法

到目前為止,我們已經完成了渲染所需的子控件的構造,初始化,下面將介紹 命令編碼,提交,渲染

Render:commandBuffer、ResourceUpdate、renderEncoder、Display

renderEncoder

上文我們創建了渲染管線狀態,這里我們需要根據RenderPassDescriptor生成一個 RenderCommandEncoder,在encoder中鏈接shader GPU 渲染圖像的步驟大致可以分為:加載、渲染、存儲。開發者可以指定這三個步驟具體做什么事。

MTLRenderPassDescriptor * desc = [MTLRenderPassDescriptor new];
desc.colorAttachment[0].texture = myColorTexture;// 指定三個步驟的行為
desc.colorAttachment[0].loadAction = MTLLoadActionClear;
desc.colorAttachment[0].clearColor = MTLClearColorMake(0.39f, 0.34f, 0.53f, 1.0f);
desc.colorAttachment[0].storeAction = MTLStoreActionStore;
復制代碼

myColorTexture 可以理解為容器,用于安置渲染的結果。

上文有提到編碼:

    //buffer renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)renderEncoder.setVertexBuffer(textureBuffer1, offset: 0, index: 1)renderEncoder.setVertexBuffer(textureBuffer2, offset: 0, index: 2)······//samper[renderEncoder setFragmentSampler:sampler atIndex:0];[renderEncoder setFragmentSampler:sampler1 atIndex:0];······//texturerenderEncoder.setFragmentTexture(texture, index: 0)renderEncoder.setFragmentTexture(texture1, index: 1)······
復制代碼

編碼所需代碼大致如下:

        let commandBuffer = commonQueue.makeCommandBuffer()!let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescripor)!commandEncoder.setRenderPipelineState(pipelineState)commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)commandEncoder.setFragmentTexture(texture, index: 0)commandEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)commandEncoder.endEncoding()
復制代碼

提交渲染

        commandBuffer.present(drawable)commandBuffer.commit()
復制代碼

渲染時的三幀緩存: 創建三幀的資源緩沖區來形成一個緩沖池。CPU 將每一幀的數據按順序寫入緩沖區供 GPU 使用。

提交時,分為同步提交(阻塞),異步提交(非阻塞) 阻塞:

id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];// 編碼命令...[commandBuffer commit];[commandBuffer waitUntilCompleted];
復制代碼

非阻塞:

id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];// 編碼命令...commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> commandBuffer) {// 回調 CPU...
}[commandBuffer commit];
復制代碼

重申:本文參考資料:

juejin.im/post/5b1e8f…

xiaozhuanlan.com/topic/04598…

developer.apple.com/videos/play…

github.com/quinn0809/G…

cloud.tencent.com/developer/a…

devstreaming-cdn.apple.com/videos/wwdc…

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/449677.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/449677.shtml
英文地址,請注明出處:http://en.pswp.cn/news/449677.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

還駕馭不了4核? 別人已模擬出百萬核心上的并行

摘要&#xff1a;不管是臺式機還是筆記本&#xff0c;四核雙核都已經不是新鮮的事了。計算機領域的你可能已經認識到了給電腦選配4核的處理器完全是一種浪費&#xff0c;因為大多數的程序都不支持多核心的并行處理。然而斯坦福的計算機科學家最近公布&#xff0c;他們已經模擬出…

docker安裝并運行ubuntu

拉取鏡像 docker pull dorowu/ubuntu-desktop-lxde-vnc 運行容器&#xff1a; docker run -p 6080:80 dorowu/ubuntu-desktop-lxde-vnc 之后就可以http://localhost:6080/

Django內置權限擴展案例

當Django的內置權限無法滿足需求的時候就自己擴展吧~ 背景介紹 overmind項目使用了Django內置的權限系統&#xff0c;Django內置權限系統基于model層做控制&#xff0c;新的model創建后會默認新建三個權限&#xff0c;分別為&#xff1a;add、change、delete&#xff0c;如果給…

Java 從入門到高級學習路線

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Java 從入門到高級學習路線《一》1.Jvm 部分Jvm 內存模型、Jvm 內存結構、Jvm 參數調優、Java 垃圾回收《二》Java 基礎部分1.必須會使用…

Flutter Mac iOS 環境配置

官方文檔&#xff1a;flutter.io/docs/get-st… 1.需要的命令行工具 bash curl git 2.x mkdir rm unzip which 2.SDK下載地址 flutter_macos_v1.0.0-stable.zip storage.googleapis.com/flutter_inf… 3.解壓Flutter SDK cd ~/Flutter/SDK $ unzip ~/Downloads/flutter_macos_v…

多線程研究1

單線程&#xff1a; from urllib.request import urlretrieve import time import random starttime.time() fopen(E:\Python\py\web\hh.txt,r)#打開存放URL的文件 af.readlines() f.close() for i in a:brandom.randint(0,30)urlretrieve(i,%d.png%b) endtime.time() print(…

android viewpage預加載和懶加載問題

1、本人理解懶加載和預加載問題某種情況下可以歸結為一類問題&#xff0c;下面我就說一下我遇到的預加載問題和懶加載問題及解決的相應方法&#xff1a; - [1 ] 預加載問題 描述&#xff1a;我用到了三個fragment、viewpage及tablayout實現點擊切換、滑動切換。 …

大數據,且行且思

“大數據”概念于20世紀90年代被提出&#xff0c;最初只是對一些在一定時間內無法用傳統方法進行抓取、管理和處理的數據的統稱。隨著時間的推移和科技的發展以及物聯網、移動互聯網、SNS的興起&#xff0c;每年產生的數據量都以幾何級數增長&#xff0c;《IDC Digital Univers…

IntelliJ IDEA中新建JAVA WEB項目、maven項目

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 在IntelliJ IDEA 中新建一個Web應用項目。 1、 在主界面頂部菜單欄依次“File”-"New"-"Project..." 2、在對話框中…

S/4HANA業務角色概覽之訂單到收款篇

2019獨角獸企業重金招聘Python工程師標準>>> 大家好我叫Sean Zhang&#xff0c;中文名張正永。目前在S/4HANA產品研發部門任職產品經理&#xff0c;而這一階段要從2017年算起&#xff0c;而在那之前接觸更多還是技術類的&#xff0c;比如做過iOS、HANA、ABAP、UI5等…

掘金量化的一個代碼,對本人寫策略避免入坑有重要意義

# codingutf-8from __future__ import print_function, absolute_import, unicode_literalsfrom gm.api import *import numpy as npdef init(context):# 選擇的兩個合約context.symbol [DCE.j1901, DCE.jm1901]# 訂閱歷史數據subscribe(symbolscontext.symbol,frequency1d,co…

C++ STL學習筆記

C STL學習筆記一 為何要學習STL&#xff1a; 數據結構與算法是編程的核心&#xff0c;STL中包含各種數據結構和優秀的算法&#xff0c;確實值得深入學習&#xff0c;本文中雖然著重使用&#xff0c;但希望有心的朋友能多看看相關數據結構的實現&#xff0c;對于C語言確實會有較…

ItelliJ IDEA開發工具使用—創建一個web項目

轉自&#xff1a;https://blog.csdn.net/wangyang1354/article/details/50452806概念需要明確一下IDEA中的項目&#xff08;project&#xff09;與eclipse中的項目&#xff08;project&#xff09;是不同的概念&#xff0c;IDEA的project 相當于之前eclipse的workspace,IDEA的M…

AKOJ-2037-出行方案

鏈接&#xff1a;https://oj.ahstu.cc/JudgeOnline/problem.php?id2037 題意&#xff1a; 安科的夏天真是不一般的熱&#xff0c;避免炎熱&#xff0c;伍學長因此想為自己規劃一個校園出行方案&#xff0c;使得從宿舍出發到校園的各個地方距離花費時間最短。我們已知校園一共有…

akshare 布林通道策略

import datetime import pandas as pd import backtrader as bt import matplotlib.pyplot as plt from datetime import datetime import matplotlib import akshare as ak %matplotlib inline class Boll_strategy(bt.Strategy):#自定義參數&#xff0c;每次買入1800手param…

一些資源網站..

github上各種免費編程書籍~~~ : https://github.com/EbookFoundation/free-programming-books/blob/master/free-programming-books-zh.md正則表達式學習 :https://web.archive.org/web/20161119141236/http://deerchao.net:80/tutorials/regex/regex.htmtorch&#xff1a;http…

極客無極限 一行HTML5代碼引發的創意大爆炸

摘要&#xff1a;一行HTML5代碼能做什么&#xff1f;國外開發者Jose Jesus Perez Aguinaga寫了一行HTML5代碼的文本編輯器。這件事在分享到Code Wall、Hacker News之后&#xff0c;引起了眾多開發者的注意&#xff0c;紛紛發表了自己的創意。 這是最初的HTML5代碼&#xff0c;它…

c# 寫文件注意問題及用例展示

以txt寫string舉例&#xff0c;正確代碼如下&#xff1a; private void xie(){FileStream fs new FileStream("1.txt", FileMode.Create);StreamWriter sw new StreamWriter(fs, Encoding.Default);sw.Write("123");sw.Flush();sw.Close();//fs.Flush();…

akshare sma策略

import datetimeimport pandas as pdimport backtrader as bt from datetime import datetime import matplotlib import akshare as ak %matplotlib inlineclass SmaCross(bt.Strategy):# 全局設定交易策略的參數params ((pfast, 5), (pslow, 20),)def __init__(self):sma1 …

DOCKER windows 7 詳細安裝教程

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 DOCKER windows安裝 DOCKER windows安裝 1.下載程序包2. 設置環境變量3. 啟動DOCKERT4. 分析start.sh5. 利用SSH工具管理6. 下載鏡像 6.1…