整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          磁控濺射汽車膜生產工藝-太陽膜的最尖端工藝?

          磁控濺射汽車膜生產工藝-太陽膜的最尖端工藝?

          所周知,威固膜作為客戶認可度最高的太陽膜品牌,采用汽車貼膜生產中的先進工藝生產,擁有超高的隔熱率,隔熱效果冠絕群雄。那么磁控濺射汽車膜生產工藝就是太陽膜的最尖端工藝?

          我們先來一起認識一下,磁控濺射為“神馬”?

          磁控濺射工藝是為了在低氣壓下進行高速濺射,必須有效地提高氣體的離化率。通過在靶陰極表面引入磁場,利用磁場對帶電粒子的約束來提高等離子體密度以增加濺射率的方法。

          說的這些關于磁控濺射的原理似乎還是不能跟汽車貼膜結合起來,以及磁控濺射生產工的汽車貼膜與傳統汽車貼膜有何不同?

          我們接下來再詳細認識一下磁控濺射工藝的汽車貼膜!

          磁控濺射是目前車膜制造中的尖端技術,與早期或現在一些劣質車膜生產商仍在采用的染色與鍍鋁復合方式來生產窗膜的工藝有著本質的不同。采用真空磁控濺射工藝生產的車膜,可以做到10年原廠質量保證——這個10年承諾,既包括不褪色,也包括與玻璃接觸的膜紙四周的邊緣不翹起來。過去,一些車膜出現膜紙四周與車玻璃分離、車膜小于玻璃面積四周露白邊和車膜有氣泡等,都責怪是操作工安裝水平問題,實際上是膜的質量不過關造成的問題。

          傳統的汽車膜生產工藝是染色與鍍鋁復合方式。染色大家都明白,不必多述。鍍鋁方式就是制作鏡子的方式,既把一些熔點低的金屬物溶化之后,通過熱蒸發涂布在某種聚脂膜上。這種汽車膜生產工藝成本低,但最大的問題是顏料與鋁層對光線的相互干涉而產生大量散失光線,容易造成司機視覺疲勞,導致司機頭暈目眩、視覺模糊而引發駕車安全性下降等諸多問題。而磁控濺射汽車膜生產工藝則不同,磁控濺射工藝汽車貼膜解決了染色-熱蒸發工藝生產的窗膜透光低、高反光、隔熱功能差、視覺模糊、易褪色、耐腐蝕性差等諸多缺陷,不僅可以制作各種純金屬化窗膜,而且因為沒有添加任何顏料,所以它可以杜絕偏色、變色,做到永不褪色,保證純正的中性色,與任何車輛的顏色都能完美匹配,并保證不分層、不剝落與開裂。更重要的是,它在不同的光照度下,視覺顏色恒定不變,可以保證車內人員的視線清晰。

          目前汽車貼膜領域,磁控濺射汽車膜生產工藝無疑是汽車太陽膜領域最尖端的工藝。(威固隔熱膜進入中國19年,作為客戶認可度最高的太陽膜品牌,威固隔熱膜就是采用了先進的磁控濺射汽車膜生產工藝!前所未有的智能光譜選擇技術,先進磁控濺射科技,顛覆了數千年來人們對光和熱密不可分的傳統認知,威固隔熱膜無疑是高端隔熱膜的代表!)

          本文由汽車貼膜http://www.haiqiaoshiji.com/整理

          出自:http://www.haiqiaoshiji.com/changshi/qichetiemo/20151112742.html

          愛普生9880C回頂部

          PConline 海選導購】對于CAD專業制圖用戶而言,一張工程圖紙上凝聚著自己很長時間工作的心血,因此是否能將圖紙上的畫面以高分辨率、精準色彩等最好的形態展現出來,就顯得尤為重要,而且對后期的工程實施有著巨大的影響。眾所知周,在大幅面繪圖領域,打印機或者繪圖儀的好壞,直接關系著圖紙質量的好壞,是絕對不能疏忽的事情。今天,小編要為CAD專業制圖用戶推薦幾款專業級大幅面打印機,這些打印機不僅可以盡可能真實的還原圖紙中的細節,而且分辨率極高,有需要的CAD制圖用戶可以關注一下。

          CAD制圖首選 專業大幅面打印機選購指南

          推薦產品:愛普生9880C

          參考售價:37000

          愛普生STYLUS PRO 9880C是愛普生公司于2007年秋季推出的多款大幅面打印機中的一款。它使用8色顏料墨,可支持至44英寸幅面,還能夠帶給用戶超快速度的震撼體驗。在競爭激烈的印刷行業中,其性價比優勢相當明顯。

          愛普生 STYLUS PRO 9880C圖片評測論壇報價網購實價

          愛普生STYLUS PRO 9880C的外觀采用黑白混搭的色調設計,簡約大方。機身控制面板上,它的功能按鍵的設置便捷實用,加之具有背光照明系統的液晶屏幕,優雅美觀的同時也十分便于用戶監測打印機工作狀態。

          愛普生STYLUS PRO 9880C能夠帶給用戶震撼速度的體驗,其打印速度約為愛普生7600/9600的三倍,能夠輕松完成持續、大批量的打印任務。此外,愛普生9880C采用了“世紀虹彩K3 VM”墨水,在紅色和藍色色域展現了更強的表現力,色彩還原真實而準確,確保打印質量。值得一提的是,愛普生9880C除了在機身背后標配USB2.0接口,還提供了100Mbps的以太網接口,引入的網絡打印單元輕松實現用戶多點打印的需求,盡顯設計的人性化。

          耗材方面,愛普生STYLUS PRO 9880C采用均為220毫升的C13T604180、C13T604280、C13T604380、C13T604480、C13T604580、C13T604680、C13T604780、C13T604980、C13T612800墨盒,市場售價在400左右。

          品牌愛普生
          型號STYLUS PRO 9880C
          產品鏈接http://product.pconline.com.cn/largeformat_printers/epson/199748.html
          IT商城網購實價
          圖片 報價參數比較網友點評評測·行情

          STYLUS PRO 9880C兼容不同規格的紙張,從A4到44英寸幅面寬,不論是單頁紙還是卷紙都可以方便的打印輸出,對介質的厚度也有極大的寬容性,不論是在厚度僅為0.08毫米介質上還是較厚的介質上,甚至在厚度為1.5毫米厚的展板上都可以實現高精度的打印輸出。標配的自動切紙系統可以精確而快速的對介質進行裁切,PRO 9880C的用戶還可以選擇選配的手動裁紙單元,可以輕松的裁切堅韌或較厚的介質,在打印機機身上設置了極易讀取的介質安裝位置標記,便于用戶輕松對齊紙張。

          愛普生STYLUS PRO 9880C打印機的大尺寸液晶屏幕裝配了背光照明系統,不論是查詢打印機狀態和各種信息,還是進行脫機操作,都更加方便快捷。它具有的無邊距打印功能可以讓用戶直接打印照片等影像時,無需裁切白邊,提高工作效率。

          編輯點評:愛普生STYLUS PRO 9880C大幅面打印機采用愛普生微壓電打印技術,與愛普生“世紀虹彩K3 VM”顏料墨水以及新型圖像處理技術相配合,實現了精確的色彩還原和對細節的更精細的展現。愛普生通過獨有的微壓電打印技術控制壓電晶體形變,精確地控制墨滴的大小,使得愛普生STYLUS PRO 9880C成為收藏級照片輸出設備之一。愛普生STYLUS PRO 9880C的輸出效率高,打印品質好,價格也十分實惠,是一款性價比超高的大幅面打印機。加之其人性化的設計、強大的功能,愛普生9880C成為專業辦公的好助手不在話下。

          2佳能iPF815回頂部

          推薦產品:佳能iPF815

          參考售價:47000

          佳能 imagePROGRAF iPF815是一款5色的大幅面打印機,適合建筑機械CAD、地理信息、國土資源、衛星遙感、軍事等領域用戶在描圖紙、涂料紙和相紙等介質上進行輸出的需要。

          佳能 imagePROGRAF iPF815圖片評測論壇報價網購實價

          外觀方面,佳能 imagePROGRAF iPF815大幅面打印機采用黑白為主色調,商務大氣,機身尺寸為1893×1291×1144mm(安裝有支架時,安裝大容量收紙籃后),機器重量為約141kg(安裝有支架時),相比同樣產品,機器的身材算苗條。

          性能方面,最大打印幅面為1118mm(44inch/B0+),打印長度為18m,打印寬度為44英寸,支持普通紙、厚銅版紙、相光面紙等打印介質,打印分辨率為2400×1200dpi,標配的接口類型為USB2.0 Hi-Speed,10Base-T/100Base-TX,IEEE1394(選配)。

          佳能 imagePROGRAF iPF815大幅面打印機標配32GB內存,大容量內存提供了更大尺寸的數據交換空間,有效提升數據處理速度和能力,避免打印大尺寸圖像時出現的內存溢出現象。同時,其160GB的硬盤,有利于進行打印文件的存儲管理。

          品牌佳能
          型號imagePROGRAF iPF815
          產品鏈接http://product.pconline.com.cn/largeformat_printers/canon/444403.html
          IT商城網購實價
          圖片 報價參數比較網友點評評測·行情

          佳能 imagePROGRAF iPF815大幅面打印機標配大容量的硬盤的好處在于,當需要重復打印作業時,可以直接從打印機硬盤中調用解釋好的打印作業,有效減少用戶的等待時間,從而提升了打印工作的整體效率。

          耗材方面,佳能 imagePROGRAF iPF815大幅面打印機為每種顏色墨水都提供單獨副墨艙。在副墨艙中始終保留預設的墨水量,用戶無須停止打印即可更換墨水盒,即使主墨水盒意外用盡,用戶也可使用副墨艙繼續打印,充分利用墨水,避免浪費。噴頭配置為MBK5120個噴嘴,BK/C/M/Y×每色各2560個噴嘴,共計15360個噴嘴

          編輯點評:佳能 imagePROGRAF iPF815大幅面打印機,打印分辨率為2400×1200dpi,共有手送、卷筒紙和雙卷筒供紙三種方式。采用多種數據處理方式,可以根據需要徹底清除硬盤數據,有效防止泄密事件發生,能夠滿足很多行業用戶的需要。

          3愛普生Stylus PRO 9710回頂部

          推薦產品:愛普生Stylus PRO 9710

          參考售價:32000

          愛普生大幅面打印機Stylus PRO 9710,通過雙五色的墨水系統設計實現了速度的大幅提升,為CAD/GIS、廣告圖文輸出和版式/報業打樣等高速打印行業樹立了全新的標準。

          愛普生 Stylus PRO 9710圖片評測論壇報價網購實價

          愛普生Stylus PRO 9710大幅面打印機使用了愛普生最新一代打印頭技術——TFP微壓電打印頭,輸出速度顯著提升。與以往機型不同的是,這兩款新品采用雙五色、即五色十通道打印頭設計,工作時每種顏色的墨水可通過雙通道、720個噴嘴精確輸出,打印速度雙倍提高,使其在輸出線條圖時,最高機械打印速度可達36秒/A1,海報輸出的速度則高達46平米/小時,這樣的生產能力將為圖文輸出、CAD/GIS等高速打印行業帶來福音。

          品牌愛普生
          型號Stylus PRO 9710
          產品鏈接http://product.pconline.com.cn/largeformat_printers/epson/394005.html
          IT商城網購實價
          圖片 報價參數比較網友點評評測·行情

          耗材方面,愛普生Stylus PRO 9710大幅面打印機配備700ml大容量墨盒,可以通過降低更換墨盒的頻率進一步提高生產效率。而新旋轉型裁紙刀的應用則將包括布料和油畫布等介質的裁切時間縮至4秒,可謂全方位多角度為用戶提供高速應用體驗。

          編輯點評:愛普生 9710采用愛普生“活的色彩VM”顏料墨水,通過新增加鮮洋紅色墨水,其藍色至紅色區域的色域得到擴展,打印色彩更加鮮艷,同時,照片黑墨水的引入也提高了其在高光類介質上的打印輸出質量,使這款產品可適用的介質種類進一步增加。

          4惠普111回頂部

          推薦產品:惠普111

          參考價格:12000

          惠普111是惠普旗下的一款精致小巧的大幅面打印機產品,這款打印機一改其他大幅面打印機笨重的形象,采用攜帶式的設計,但是可以提供每90秒1份A1/D幅面的打印速度,保證了用戶的打印效率,同時提供不錯的打印質量,此外1.2萬元的價格也是十分親民,適合打印大幅面辦公文檔,建筑和機械設計及圖像。

          惠普 Designjet 111(CQ533A)(帶紙盒) 圖片評測論壇報價網購實價

          外觀方面,這款惠普111大幅面打印機采用深、淺灰色的色彩搭配,整體外觀給人感覺十分簡潔典雅。機身體積十分小巧便攜,尺寸為1042×495×220mm,僅重23kg,用戶可以任意擺放辦公室里。 同時打印機為用戶提供了滾筒與紙盒兩種出圖方式,用戶可以根據打印的幅面靈活選擇十分方便。

          性能方面,這款惠普111是一款24英寸熱噴墨大幅面打印機,雖然體積小,但是在性能上卻不含糊,作為一款四色的大幅面打印機,惠普111可以輸出純正、生動的色彩以及清晰、精確的線條圖、海報和展示材料,打印機標配有64MB標準緩存,可以為打印機數據處理能力提供保證。

          品牌惠普
          型號Designjet 111(CQ533A)
          產品鏈接http://product.pconline.com.cn/largeformat_printers/hp/485374.html
          IT商城網購實價
          更多詳細資料圖片 報價 參數 比較 網友點評 評測·行情

          在打印速度方面,打印機打印1份A1/D幅面只需90秒,標準、D 光面紙為14.5分鐘/頁,標準、D 涂料紙為5分鐘/頁。同時打印機提供高達1200×600dpi的最佳分辨率不但可以輸出高精度、無毛刺、無暈染的工程線條,還可以打印色彩鮮艷、色塊均勻的插圖或展示材料,高質量地為用戶再現設計藍圖。同時打印機還支持多種介質,均能提供專業級的CAD技術圖打印效果。

          耗材方面,這款惠普111打印機配置了4色(青色,品紅,黃色,黑色)墨盒,型號分別為黑色CH565A,青色C4836A,品紅色C4837A,黃色C4838A。容量分別為黑色69ml,彩色28ml ,為用戶提供連續輸出的能力,打印機最小墨滴僅為4pl, 也可以保證高質量的輸出效果。同時獨立的墨盒和打印頭色劑還可減少日常維護成本,方便維護,為用戶降低使用成本。

          編輯點評:這款惠普111噴墨大幅面打印機是專為中小型CAD/GIS工作組設計的產品,具有超強輸出、支持多種介質、體積小巧等特點,保證了打印的效率。同時打印機具有很高的性價比,非常適合打印大幅面辦公文檔,建筑和機械設計及圖像。

          5佳能iPF710回頂部

          推薦產品:佳能iPF710

          參考售價:39500

          佳能iPF710是一款針對CAD/GIS和海報輸出用戶的大幅面打印機。無論是產品設計、輸出幅面還是色彩的應用,佳能iPF710都體現出其在工業制圖方面的專業的水平。

          佳能 imagePROGRAF iPF710圖片評測論壇報價網購實價

          佳能iPF710的外觀采用經典的黑白搭配設計,并且裝載有大型圖解LCD面板,華麗美觀,便于操作。1507×871×1094mm的機身尺寸是整體結構簡約、緊湊,放置于辦公室內,雖然占地面積不大,卻能增添一股商務氣息。

          性能方面,佳能iPF710可以在56秒內輸出A0尺寸的CAD圖像,令人眼前一亮。它裝載了總計15,360個噴嘴(2,560個噴嘴×C、M、Y、MBK、MBK、BK)的高密度1英寸寬打印頭。此外,它還擁有5色墨水6個墨盒,而且5色墨水為防滲透染顏料混合墨水,能夠有效地防止在黑色與彩色邊緣產生滲透洇墨的現象。該款大幅面打印機采用的大尺寸單色液晶顯示屏,能夠實時顯示紙張種類和墨水剩余量等信息,便于監測與操作。同時,它還可以動畫顯示紙張更換順序,處處彰顯人性化理念。

          品牌佳能
          型號imagePROGRAF iPF710
          產品鏈接http://product.pconline.com.cn/largeformat_printers/canon/235561.html
          IT商城網購實價
          圖片 報價參數比較網友點評評測·行情

          耗材方面,佳能iPF710采用5色墨水6個墨盒。其中,PFI-102MBK粗面黑色墨水,市場售價約為520元;PFI-102BK黑色墨水、PFI-102M品紅墨水、PFI-102Y黃色墨水和PFI-102C青色墨水的市場售價均約為550元。打印頭采用Print Head PF-03,市場售價約為6000元。

          編輯點評:佳能iPF710是一款擁有超大幅面打印能力的五色打印機,應對高標準的工業制圖不在話下。它的價格也很適中,操作起來十分便捷,性價比高,是一款值得擁有的大幅面打印機。

          6惠普510回頂部

          推薦產品:惠普510

          參考價格:26500

          惠普510 42英寸大幅面打印機是惠普旗下的一款打印機產品,這款打印機主要針對CAD領域用戶,滿足用戶對工程圖紙的專業打印要求,以出色打印品質與超值價格組合,為用戶提供方便快捷的使用體驗,是設計院和制造行業用戶繪制成功藍圖的首選產品。

          惠普 Designjet 510 1067毫米(CH337A) 圖片評測論壇報價網購實價

          外觀方面,這款惠普510 整體采用白色的色調設計,搭配下面的墨綠色機架顯得十分的充滿生機,整個機身由上部的打印機與下面的支架構成,同時還包括一個出紙口。因為是大幅面打印機,所以在設計上與傳統的打印機相比要多一個出紙的支架。整個機身的尺寸相對而言還是比較輕便的,整機尺寸為1690×674×1100mm,比較方便用戶的放置。

          性能方面,這款全新設計的惠普510打印機標配內存160MB,可順利處理各種復雜文件。打印機的最大處理幅面為42寸,最大打印寬度為1067mm。速度方面,惠普510(42英寸)可以提供多種介質的打印服務,當進行彩色圖像ISO N5打印時標準D光面紙速度為7分鐘/頁,草稿D涂料紙速度為2.5分鐘/頁,標準D涂料紙為7分鐘/頁。而采用彩色、黑白線條圖打印速度為38頁/小時。這款惠普510還為用戶提供了出色的打印品質,打印機擁有高達2400×1200dpi的最佳分辨率不但可以輸出高精度、無毛刺、無暈染的工程線條,還可以打印色彩鮮艷、色塊均勻的插圖或展示材料。

          品牌惠普
          型號Designjet 510 1067毫米(CH337A)
          產品鏈接http://product.pconline.com.cn/largeformat_printers/hp/396097.html
          IT商城網購實價
          更多詳細資料圖片 報價 參數 比較 網友點評 評測·行情

          惠普510打印機系列打印誤差僅為千分之二,能打印0.04毫米寬的線條,在多種介質上均能為您打印出精確的細節,這一點對于CAD打印十分實用,為用戶提供專業級的CAD技術圖打印效果。打印機擁有直觀的控制面板和人性化的設計,兼容各類應用和操作系統,用戶使用起來十分方便。利用惠普510用戶能夠以經濟的價格、低廉的成本,輕松獲得快捷高效、高品質的CAD技術圖。

          此外,這款惠普510(42英寸)大幅面打印機采用了增強的HP-GL/2專業打印語言,可以輕松實現專業級CAD圖稿輸出。對比使用Raster(光柵語言)語言的打印機,HP-GL/2為用戶提供更加精確的線條定位和曲線形狀輸出,同時大幅提高圖稿修改之后再出圖速度,特別適合CAD領域經常需要進行修改的打印工程圖紙。

          耗材方面,這款惠普510(42寸)采用HP 82號墨盒,獨立的墨盒和打印頭設計可以減少日常維護成本,并提高維護效率,同時降低企業運營成本。打印機可以使用證券紙和涂料紙、技術用紙、膠片、相紙、校樣紙、背膠、橫幅和標記、畫布介質進行打印,用戶擁有豐富的選擇空間。

          編輯點評:HP 510(42英寸)大幅面打印機是惠普的新一代大幅面打印機產品,打印機專為工程圖紙打印設計,可讓設計院等行業用戶盡享出色的優秀品質和可靠性。同時獨立的墨盒和打印頭設計幫助企業以更低的成本實現業務目標,對設計院和制造行業用戶來說十分適合。

          ebGPU 是即將推出的 Web API,提供了一組訪問 GPU的低級通用API。

          我對圖形學不是很有經驗。我通過閱讀有關如何使用 OpenGL 構建游戲引擎的教程來學習 WebGL 的知識點,并通過觀看Inigo Quilez在ShaderToy上僅使用著色器(不使用任何 3D 網格或模型)做令人驚奇的事情來了解有關著色器的更多信息。這讓我可以在PROXX中構建背景動畫之類的東西,但我對 WebGL 并不滿意,我將很快解釋原因。

          當 WebGPU 出現在我的視野中時,我想深入了解它,但很多人警告我,WebGPU 的樣板比 WebGL 還要多。雖然沒有被嚇倒,但我預見到最壞的情況,教程和規范并不多,因為還處于 WebGPU 的早期階段。深入以后,我發現我沒有發現 WebGPU 實際上是一個我更熟悉的 API。

          所以在這里。我想分享在研究 GPU 和 WebGPU 時學到的東西。這篇博文的目標是讓 Web 開發人員可以訪問 WebGPU。但這里有一個提醒:我不會使用 WebGPU 來生成圖形。相反,我將使用 WebGPU 來訪問 GPU 提供的原始計算能力。也許我會寫一篇后續博客文章如何使用 WebGPU 渲染到你的屏幕上,但是已經有相當多的內容了。我將盡可能深入地理解 WebGPU,并希望能讓你有效地使用它——雖然不一定是有效的。

          1、WebGL

          WebGL于 2011 年問世,到目前為止,它是唯一可以從 Web 訪問 GPU 的低級 API。WebGL 的 API 實際上只是 OpenGL ES 2.0,帶有一些瘦包裝器和輔助工具以使其與 Web 兼容。WebGL 和 OpenGL 都由Khronos Group標準化,這基本上是 3D 圖形的 W3C。

          OpenGL 的 API 本身可以追溯到更遠,按照今天的標準,它并不是一個很好的 API。該設計以內部的全局狀態對象為中心。從這樣的角度來看,這種設計是有意義的,因為它可以最大限度地減少任何給定調用需要傳入和傳出 GPU 的數據量。但是,它也引入了很多精神負擔。

          WebGL 內部全局狀態對象的可視化。摘自WebGL Fundamentals。

          內部狀態對象基本上是指針的集合。你的 API 調用會影響狀態對象指向對象,但也會影響狀態對象本身。因此,API 調用的順序非常重要,我總覺得這使得構建抽象和庫變得困難。你必須非常細致地清理所有可能干擾將要進行的 API 調用的指針和狀態項,還要將指針和值恢復到它們之前的值,以便你的抽象正確組合。我經常發現自己盯著一個黑色的畫布(因為這幾乎是你在 WebGL 中報告錯誤的全部內容)并且暴力破解當前沒有指向正確方向的指針。老實說,我不知道ThreeJS如何設法變得如此強大,但它確實以某種方式管理。我認為這是大多數人直接使用 ThreeJS 而不是 WebGL 的主要原因之一。

          不是你,是我:需要明確的是,我無法內化 WebGL 可能是我自己的一個缺點。比我聰明的人已經能夠使用 WebGL(以及 Web 之外的 OpenGL)構建出令人驚嘆的東西,但它從來沒有真正讓我滿意。

          隨著機器學習、神經網絡以及加密貨幣的出現,GPU 已經表明它們不僅僅可以用于在屏幕上繪制三角形。使用 GPU 進行任何類型的計算通常被稱為通用 GPU 或 GPGPU,而 WebGL 1 在這方面并不出色。如果你想在 GPU 上處理任意數據,則必須將其編碼為紋理,在著色器中對其進行解碼,進行計算,然后將結果重新編碼為紋理。WebGL 2 使用Transform Feedback讓這件事變得更容易,但直到 2021 年 9 月,Safari 才支持 WebGL2(而大多數其他瀏覽器自 2017 年 1 月起支持 WebGL2),所以它不是一個真正的選擇。即便如此,WebGL2 的某些限制仍然讓人感覺有些笨拙。

          2、WebGPU

          在 Web 之外,新一代的圖形 API 已經建立起來,它們向圖形卡公開了一個更底層的接口。這些新的 API 適應了設計 OpenGL 時不存在的新用例和約束。一方面,GPU 現在幾乎無處不在。甚至我們的移動設備也內置了功能強大的 GPU。因此,兩者都現代圖形編程(3D 渲染和光線追蹤)和 GPGPU 用例越來越普遍。另一方面,我們的大多數設備都有多核處理器,因此能夠從多個線程與 GPU 交互可能是一個重要的優化向量。當 WebGPU 人員參與其中時,他們還重新審視了之前的一些設計決策,并預先加載了 GPU 必須完成的大量驗證工作,從而使開發人員能夠從他們的 GPU 中榨取更多性能。

          最受歡迎的下一代 GPU API 是Khronos Group的Vulkan 、 Apple的Metal和微軟的DirectX 12。為了將這些新功能帶入網絡,WebGPU 應運而生。雖然 WebGL 只是 OpenGL 的一個薄包裝層,但 WebGPU 選擇了不同的方法。它引入了自己的抽象,并且不直接反映任何這些原生 API。這部分是因為沒有單一的 API 在所有系統上都可用,還因為許多概念(例如極低級別的內存管理)對于面向 Web 的 API 來說并不慣用。取而代之的是,WebGPU 被設計成既能讓人感覺“webby”,又能舒適地坐在任何原生圖形 API 之上,同時抽象它們的特性。它在 W3C 中被標準化,所有主要的瀏覽器供應商都擁有一席之地。由于其相對低級的性質和強大的功能,WebGPU 有一些學習曲線并且在設置上相對繁重,

          3、WebGPU適配器和設備

          我們接觸到的 WebGPU 的第一個抽象是適配器和(邏輯)設備

          抽象層,從物理 GPU 到邏輯設備

          物理設備是 GPU 本身,通常區分為內置 GPU 和獨立 GPU。通常,任何給定設備都只有一個 GPU,但也可能有兩個或更多 GPU。例如,微軟的 SurfaceBook 以低功耗的集成 GPU 和高性能的獨立 GPU 著稱,操作系統將根據需要在它們之間切換。

          由 GPU 制造商提供的驅動程序將以操作系統理解和期望的方式向操作系統公開 GPU 的功能。反過來,操作系統可以使用操作系統提供的圖形 API(如 Vulkan 或 Metal)將其暴露給應用程序。

          GPU 是共享資源。它不僅被許多應用程序同時使用,而且還控制你在顯示器上看到的內容。需要有一些東西可以讓多個進程同時使用 GPU,這樣每個應用程序都可以將自己的 UI 放在屏幕上,而不會干擾其他應用程序,甚至不會惡意讀取其他應用程序的數據。對于每個進程,看起來他們對物理 GPU 擁有唯一的控制權,但顯然情況并非如此。這種多路復用主要由驅動程序和操作系統完成。

          反過來,適配器是從操作系統的本機圖形 API 到 WebGPU 的轉換層。由于瀏覽器是可以運行多個 Web 應用程序的單一 OS 級應用程序,因此再次需要多路復用,以便每個 Web 應用程序感覺就像它擁有對 GPU 的唯一控制權。這是在 WebGPU 中使用邏輯設備的概念建模的。

          要訪問適配器,請調用navigator.gpu.requestAdapter(). 在撰寫本文時,requestAdapter()選擇很少。這些選項允許你請求高性能或低能耗適配器。

          軟件渲染:一些實現還為沒有 GPU 或 GPU 能力不足的系統提供“后備適配器”。后備適配器實際上是一種純軟件實現,它不會很快,但可以保持你的應用程序正常運行。

          如果成功,即返回的適配器不是null,就可以檢查適配器的功能并使用 adapter.requestDevice()向適配器請求邏輯設備。

          if (!navigator.gpu) throw Error("WebGPU not supported.");
          
          const adapter=await navigator.gpu.requestAdapter();
          if (!adapter) throw Error("Couldn’t request WebGPU adapter.");
          
          const device=await adapter.requestDevice();
          if (!device) throw Error("Couldn’t request WebGPU logical device.");

          如果沒有任何選項,requestDevice()將返回一個不一定與物理設備的功能相匹配的設備,而是 WebGPU 團隊認為合理的、所有 GPU 的最低公分母的設備。詳細信息在 WebGPU 標準中指定。例如,即使我的 GPU 能夠輕松處理最大 4GiB 的數據緩沖區,device返回的數據緩沖區也只會允許最大 1GiB 的數據緩沖區,并且會拒絕任何更大的數據緩沖區。這可能看起來有限制,但實際上很有幫助:如果你的 WebGPU 應用程序使用默認設備運行,它將在絕大多數設備上運行。如有必要,你可以利用adapter.limits檢查物理 GPU 的實際限制并通過將選項對象傳遞給requestDevice() 來請求一個 device 。

          4、WebGPU著色器

          如果您曾經使用過 WebGL,那么可能熟悉頂點著色器和片段著色器。無需過多深入,傳統設置的工作原理如下:你將數據緩沖區上傳到 GPU,并告訴它如何將該數據解釋為一系列三角形。每個頂點占據該數據緩沖區的一塊,描述該頂點在 3D 空間中的位置,但可能還包括顏色、紋理 ID、法線和其他內容等輔助數據。列表中的每個頂點都由 GPU 在頂點階段處理,在每個頂點上運行頂點著色器,這將應用平移、旋轉或透視變形。

          著色器: “著色器”這個詞曾經讓我感到困惑,因為你可以做的不僅僅是著色。但在過去(即 1980 年代后期!),這個術語是恰當的:它是在 GPU 上運行的一小段代碼,用于決定每個像素應該是什么顏色,這樣你就可以正在渲染的對象進行著色,實現燈光和陰影的錯覺。如今,著色器泛指在 GPU 上運行的任何程序。

          GPU 現在對三角形進行光柵化,這意味著 GPU 會計算出每個三角形在屏幕上覆蓋的像素。然后每個像素由片段著色器處理,它可以訪問像素坐標,也可以訪問輔助數據來決定該像素應該是哪種顏色。如果使用得當,可以使用此過程創建令人驚嘆的 3D 圖形。

          這種將數據傳遞到頂點著色器,然后到片段著色器,然后將其直接輸出到屏幕上的系統稱為管道,在 WebGPU 中,你必須明確定義管道。

          5、WebGPU管道

          目前,WebGPU 允許你創建兩種類型的管道:渲染管道和計算管道。顧名思義,渲染管道渲染某些東西,這意味著它創建了一個 2D 圖像。該圖像不必在屏幕上,而可以只渲染到內存(稱為幀緩沖區)。計算更通用,因為它返回一個緩沖區,該緩沖區可以包含任何類型的數據。在這篇博文的其余部分,我將專注于計算管道,因為我喜歡將渲染管道視為計算管道的專業化/優化。現在,這既是歷史上的倒退——計算管道是作為專門構建的渲染管道的泛化而構建的——而且還大大低估了這些管道在你的 GPU 中是物理上不同的電路。然而,就 API 而言,我發現這個心智模型很有幫助。在未來,似乎更多類型的管道——也許是光線追蹤管道——將被添加到 WebGPU 中。

          使用 WebGPU,管道由一個(或多個)可編程階段組成,其中每個階段由一個著色器和一個入口點定義。計算管道只有一個compute階段,而渲染管道將有一個vertex和一個fragment階段:

          const module=device.createShaderModule({
            code: `
              @stage(compute) @workgroup_size(64)
              fn main() {
                // Pointless!
              }
            `,
          });
          
          const pipeline=device.createComputePipeline({
            compute: {
              module,
              entryPoint: "main",
            },
          });

          這是WebGPU著色語言 WGSL(發音為“wig-sal”)第一次出現。WGSL 對我來說就像是 Rust 和 GLSL 的交叉。它有很多 Rust-y 語法,帶有 GLSL 的全局函數(如dot(), norm(), len(), ...),類型(如vec2, mat4x4, ...)和swizzling符號(如some_vec.xxy, ...)。瀏覽器會將你的 WGSL 編譯為底層系統所期望的。這可能是 DirectX 12 的 HLSL、Metal 的 MSL 和Vulkan的 SPIR-V。

          SPIR-V:SPIR-V很有趣,因為它是由 Khronos Group 標準化的開放、二進制、中間格式。你可以將 SPIR-V 視為并行編程語言編譯器的 LLVM,并且支持將多種語言編譯SPIR-V 以及將 SPIR-V 編譯為多種其他語言。

          在上面的著色器模塊中,我們只是創建了一個名為main的函數,并使用@stage(compute)屬性將其標記為計算階段的入口點。你可以將多個函數標記為著色器模塊中的入口點,因為可以為多個管道重用相同的著色器模塊,并通過entryPoint選項選擇不同的函數來調用。但是那個@workgroup_size(64)屬性是什么?

          6、WebGPU的并行性

          GPU 以延遲為代價針對吞吐量進行了優化。要理解這一點,我們必須看一下 GPU 的架構。我不想(老實說,不能)完整地解釋它。我會盡可能深入。如果你想了解更多信息,Fabian Giesen的這個由13 部分組成的博客文章系列非常棒。

          眾所周知,GPU 具有大量內核,可以進行大規模并行工作。但是,這些內核并不像你在為多核 CPU 編程時所習慣的那樣獨立。首先,GPU 核心是分層分組的。層次結構中不同層的術語在供應商和 API 之間并不一致。英特爾有一個很好的文檔,提供了對其架構的高級概述,我被告知可以安全地假設其他 GPU 至少可以類似地工作,盡管 GPU 的確切架構是受 NDA 保護的機密。

          在英特爾的情況下,層次結構中的最低級別是“執行單元”(EU),它有多個(在本例中為七個)SIMT內核。這意味著它有七個以鎖步方式運行并始終執行相同指令的內核。但是,每個內核都有自己的一組寄存器和堆棧指針。因此,雖然他們必須執行相同的操作,但他們可以在不同的數據上執行它。這也是 GPU 性能專家避免分支(如if/else或循環)的原因:如果 EU 遇到if/ else,所有內核都必須同時執行分支,除非所有核心碰巧采用相同的分支。可以告訴每個核心忽略它正在輸入的指令,但這顯然浪費了可以用于計算的寶貴周期。這同樣適用于循環!如果一個核心提前完成了他們的循環,它將不得不假裝執行循環體,直到所有核心都完成了循環。

          盡管內核的頻率很高,但從內存(或紋理中的像素)獲取數據仍然需要相對較長的時間——Fabian 說這需要幾百個時鐘周期。這幾百個周期可以用于計算。為了利用這些原本空閑的周期,每個歐盟的工作都嚴重超額認購。每當 EU 最終空閑(例如,等待內存中的值)時,它就會切換到另一個工作項,并且只有在新的工作項需要等待某事時才會切換回來。這是 GPU 如何以延遲為代價優化吞吐量的關鍵技巧:單個工作項將花費更長的時間,因為切換到另一個工作項可能會停止執行的時間超過必要的時間,但總體利用率更高并導致更高的吞吐量。

          英特爾 Iris Xe 圖形芯片的架構

          不過,EU 只是層次結構中的最低級別。多個 EU 被分組為英特爾所謂的“SubSlice”。SubSlice 中的所有 EU 都可以訪問少量共享本地內存 (SLM),在英特爾的情況下約為 64KiB。如果要運行的程序有任何同步命令,則必須在同一個 SubSlice 內執行,因為只有它們有共享內存進行同步。

          在最后一層,多個 SubSlice 被組合成一個 Slice,形成 GPU。對于集成的 Intel GPU,最終總共有 170-700 個內核。離散 GPU 可以輕松擁有 1500 個或更多內核。同樣,這里的命名取自英特爾,其他供應商可能使用不同的名稱,但每個 GPU 的總體架構都是相似的。

          為了充分利用這種架構的好處,需要專門為這種架構設置程序,以便純程序化的 GPU 調度程序可以最大限度地利用。因此,圖形 API 公開了一個線程模型,自然允許以這種方式剖析工作。在 WebGPU 中,這里重要的原語是“工作組”。

          7、WebGPU工作組

          在傳統設置中,頂點著色器會為每個頂點調用一次,片段著色器會為每個像素調用一次(我知道我在這里略過一些細節)。在 GPGPU 設置中,將為你安排的每個工作項調用一次計算著色器。由你來定義工作項是什么。

          所有工作項的集合(我將其稱為“工作負載”)被分解為工作組。工作組中的所有工作項都計劃一起運行。在 WebGPU 中,工作負載被建模為一個 3 維網格,其中每個“立方體”是一個工作項,工作項被分組為更大的立方體以形成一個工作組。

          這是一個工作負載。白邊立方體是一個工作項。紅邊長方體是一個工作組

          最后,我們有足夠的信息來討論這個@workgroup_size(x, y, z)屬性,在這一點上它甚至可能是不言自明的:這個屬性允許你告訴 GPU 這個著色器的工作組的大小應該是多少。或者用上圖的語言,@workgroup_size屬性定義了紅邊立方體的大小。 x×y×z 表示每個工作組的工作項數。任何跳過的參數都假定為 1,因此@workgroup_size(64)等價于@workgroup_size(64, 1, 1)。

          當然,實際的 EU 并不排列在芯片上的 3D 網格中。在 3D 網格中建模工作項的目的是增加局部性。假設相鄰的工作組很可能會訪問內存中的相似區域,因此當順序運行相鄰的工作組時,緩存中已經有值的機會更高,無需從緩存中抓取它們,從而節省了幾百個周期記憶。然而,大多數硬件似乎只是以串行順序運行工作組,因為運行著色器@workgroup_size(64)與@workgroup_size(8, 8)之間的差異可以忽略不計。所以這個概念被認為有點遺留。

          但是,工作組受到多種方式的限制:device.limits有許多值得了解的屬性:

          // device.limits
          {
            // ...
            maxComputeInvocationsPerWorkgroup: 256,
            maxComputeWorkgroupSizeX: 256,
            maxComputeWorkgroupSizeY: 256,
            maxComputeWorkgroupSizeZ: 64,
            maxComputeWorkgroupsPerDimension: 65535,
            // ...
          }

          工作組大小的每個維度的大小都受到限制,但即使 x、y 和 z 分別在限制范圍內,它們的乘積 (=x×y×z )可能不是,因為有其自己的限制,你只能使用這么多工作組。

          專業提示:不要產生最大數量的線程。盡管 GPU 由操作系統和底層調度程序管理,但你可能會使用長時間運行的 GPU 程序凍結整個系統。

          那么合適的工作組規模是多少?這實際上取決于你分配工作項坐標的語義。我確實意識到這并不是一個真正有用的答案,所以我想給你如下建議:“使用 [a workgroup size of] 64,除非你知道你的目標是什么 GPU 或者你的工作負載需要不同的東西。” 這似乎是一個安全的數字,可以在許多 GPU 上運行良好,并允許 GPU 調度程序讓盡可能多的 EU 保持忙碌。

          8、WebGPU命令執行

          我們已經編寫了著色器并設置了管道。剩下要做的就是調用 GPU 來執行它。由于 GPU可以是具有自己的內存芯片的完全獨立的卡,因此你可以通過所謂的命令緩沖區或命令隊列來控制它。命令隊列是一塊內存,其中包含供 GPU 執行的編碼命令。編碼高度特定于 GPU,由驅動程序負責。WebGPU 公開了一個CommandEncoder以利用該功能。

          const commandEncoder=device.createCommandEncoder();
          const passEncoder=commandEncoder.beginComputePass();
          passEncoder.setPipeline(pipeline);
          passEncoder.dispatch(1);
          passEncoder.end();
          const commands=commandEncoder.finish();
          device.queue.submit([commands]);

          commandEncoder有多種方法可讓你將數據從一個 GPU 緩沖區復制到另一個 GPU 緩沖區并操作紋理。它還允許你創建PassEncoder,它對管道的設置和調用進行編碼。在這種情況下,我們有一個計算管道,因此我們必須創建一個計算通道,將其設置為使用我們預先聲明的管道,最后調用dispatch(w_x, w_y, w_z)以告訴 GPU 沿每個維度創建多少個工作組。換句話說,我們的計算著色器將被調用的次數等于 wx×wywzx×y×z 。 順便說一句,pass 編碼器是 WebGPU 的抽象,用于避免我在這篇博文開始時描述的內部全局狀態對象。運行 GPU 管道所需的所有數據和狀態都通過 pass 編碼器顯式傳遞。

          抽象:命令緩沖區也是驅動程序或操作系統的掛鉤,讓多個應用程序使用 GPU 而不會相互干擾。當你將命令排隊時,下面的抽象層將向隊列中注入額外的命令,以保存先前程序的狀態并恢復您的程序狀態,這樣就感覺沒有其他人在使用 GPU。

          運行這段代碼,我們實際上在 GPU 上生成了 64 個線程,它們完全沒有任何作用。但它有效,所以這很酷。讓我們談談我們如何給 GPU 一些數據來工作。

          9、WebGPU交換數據

          正如所承諾的,我不會直接將 WebGPU 用于圖形,因此我認為在 GPU 上運行物理模擬并使用 Canvas2D 將其可視化會很有趣。也許我自稱是“物理模擬”——我正在做的是生成一大堆圓圈,讓它們在平面上以隨機方向滾動并讓它們發生碰撞。

          為此,我們需要將一些模擬參數和初始狀態推送到GPU,在 GPU 上運行模擬并從GPU 讀取模擬結果。這可以說是 WebGPU 最令人毛骨悚然的部分,因為有一堆數據雜技(不是說看似毫無意義的復制),但這就是讓 WebGPU 成為以最高性能水平運行的與設備無關的 API 的原因。

          10、WebGPU綁定組布局

          為了與 GPU 交換數據,我們需要使用綁定組布局擴展我們的管道定義。綁定組是在管道執行期間可訪問的 GPU 實體(內存緩沖區、紋理、采樣器等)的集合。綁定組布局預先定義了這些 GPU 實體的類型、用途和用途,這使 GPU 可以提前弄清楚如何最有效地運行管道。讓我們在這個初始步驟中保持簡單,并讓我們的管道訪問單個內存緩沖區:

          const bindGroupLayout=device.createBindGroupLayout({
              entries: [{
                binding: 1,
                visibility: GPUShaderStage.COMPUTE,
                buffer: {
                  type: "storage",
                },
              }],
            });
          
          const pipeline=device.createComputePipeline({
            layout: device.createPipelineLayout({
              bindGroupLayouts: [bindGroupLayout],
            }),
            compute: {
              module,
              entryPoint: "main",
            },
          });

          該binding數字可以自由選擇,并用于將 WGSL 代碼中的變量與綁定組布局的此槽中的緩沖區內容聯系起來。我們bindGroupLayout還定義了每個緩沖區的用途,在本例中為"storage". 另一個選項是"read-only-storage",它是只讀的,并允許 GPU 在永遠不會寫入此緩沖區并且因此不需要同步的基礎上進行進一步的優化。緩沖區類型的最后一個可能值是"uniform",在計算管道的上下文中,它在功能上主要等同于存儲緩沖區。

          綁定組布局就位。現在我們可以創建綁定組本身,其中包含綁定組布局所期望的 GPU 實體的實際實例。一旦帶有緩沖區的綁定組就位,計算著色器可以用數據填充它,我們可以從 GPU 讀取它。但是有一個障礙:暫存緩沖區。

          11、WebGPU暫存區

          我再說一遍:GPU 以延遲為代價對吞吐量進行了高度優化。GPU 需要能夠以令人難以置信的高速率向內核提供數據,以維持該吞吐量。Fabian 從 2011 年開始在他的博客文章系列中做了一些背后的數學運算,得出的結論是 GPU 需要維持 3.3GB/s 的速度,僅用于以 1280x720 分辨率運行的著色器的紋理樣本。為了適應當今的圖形需求,GPU 需要更快地挖掘數據。這只有在 GPU 的內存與內核緊密集成的情況下才能實現。這種緊密的集成使得很難將相同的內存也暴露給主機進行讀寫。

          相反,GPU 有額外的內存庫,主機和 GPU 都可以訪問,但集成度不高,無法快速提供數據。暫存緩沖區是在此中間內存領域中分配的緩沖區,可以映射到主機系統進行讀寫。為了從 GPU 讀取數據,我們將數據從內部高性能緩沖區復制到暫存緩沖區,然后將暫存緩沖區映射到主機,以便我們可以將數據讀回主內存。對于寫作,這個過程是相同的,但相反。

          回到我們的代碼:我們將創建一個可寫緩沖區并將其添加到綁定組,以便計算著色器可以寫入它。我們還將創建一個大小相同的第二個緩沖區,作為暫存緩沖區。每個緩沖區都使用usage位掩碼創建,你可以在其中聲明您打算如何使用該緩沖區。然后,GPU 將確定緩沖區的位置以實現所有這些用例,或者如果標志組合無法實現,則拋出錯誤。

          const BUFFER_SIZE=1000;
          
          const output=device.createBuffer({
            size: BUFFER_SIZE,
            usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
          });
          
          const stagingBuffer=device.createBuffer({
            size: BUFFER_SIZE,
            usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
          });
          
          const bindGroup=device.createBindGroup({
            layout: bindGroupLayout,
            entries: [{
              binding: 1,
              resource: {
                buffer: output,
              },
            }],
          });

          請注意,createBuffer()返回一個 GPUBuffer,而不是ArrayBuffer. 暫時無法讀取或寫入它們。為此,它們需要被映射,這是一個單獨的 API 調用,并且只會對具有GPUBufferUsage.MAP_READ或GPUBufferUsage.MAP_WRITE的緩沖區成功。

          TypeScript:我發現 TypeScript 在探索新 API 時非常有用。幸運的是,Chrome 的 WebGPU 團隊維護@webgpu/types,因此你可以享受準確的自動完成。

          現在我們不僅有了綁定組布局,甚至還有實際的綁定組本身,我們需要更新我們的調度代碼以使用這個綁定組。之后,我們映射暫存緩沖區以將結果讀回 JavaScript。

          const commandEncoder=device.createCommandEncoder();
          const passEncoder=commandEncoder.beginComputePass();
          passEncoder.setPipeline(pipeline);
          passEncoder.setBindGroup(0, bindGroup);
          passEncoder.dispatch(1);
          passEncoder.dispatch(Math.ceil(BUFFER_SIZE / 64));
          passEncoder.end();
          commandEncoder.copyBufferToBuffer(
            output,
            0, // Source offset
            stagingBuffer,
            0, // Destination offset
            BUFFER_SIZE
          );
          const commands=commandEncoder.finish();
          device.queue.submit([commands]);
          
          await stagingBuffer.mapAsync(
            GPUMapMode.READ,
            0, // Offset
            BUFFER_SIZE // Length
           );
          const copyArrayBuffer=stagingBuffer.getMappedRange(0, BUFFER_SIZE);
          const data=copyArrayBuffer.slice();
          stagingBuffer.unmap();
          console.log(new Float32Array(data));

          由于我們向管道添加了綁定組布局,因此任何不提供綁定組的調用現在都會失敗。在我們定義“pass”之后,我們通過命令編碼器添加一個額外的命令,將數據從輸出緩沖區復制到暫存緩沖區,并將我們的命令緩沖區提交到隊列。GPU 將開始通過命令隊列工作。我們不知道 GPU 何時會準確完成,但我們已經可以提交我們的請求stagingBuffer以進行映射。這個函數是異步的,因為它需要等待命令隊列被完全處理。當返回的 promise 解析時,緩沖區被映射,但還沒有暴露給 JavaScript。stagingBuffer.getMappedRange()讓我們請求一個小節(或整個緩沖區)作為一個好的 ol' 暴露給 JavaScriptArrayBuffer. 這是真實的、映射的 GPU 內存,這意味著數據將在stagingBuffer未映射時消??失(ArrayBuffer將“分離”),因此我在使用slice()創建 JavaScript 擁有的副本。

          不是很令人興奮,但我們從 GPU 的內存中復制了這些零

          零以外的東西可能會更有說服力。在我們開始在我們的 GPU 上進行任何高級計算之前,讓我們將一些手工挑選的數據放入我們的緩沖區中,以證明我們的管道確實按預期工作。這是我們新的計算著色器代碼,為了清晰起見,有額外的間距。

          @group(0) @binding(1)
          var<storage, write> output: array<f32>;
          
          @stage(compute) @workgroup_size(64)
          fn main(
          
            @builtin(global_invocation_id)
            global_id : vec3<u32>,
          
            @builtin(local_invocation_id)
            local_id : vec3<u32>,
          
          ) {
            output[global_id.x]=f32(global_id.x) * 1000. + f32(local_id.x);
          }
          

          前兩行聲明了一個名為 的模塊范圍變量output,它是一個動態大小的數組f32。屬性聲明數據的來源:從我們第一個(第 0 個)綁定組中的緩沖區,binding值為 1 的條目。數組的長度將自動反映底層緩沖區的長度(向下舍入)。

          變量: WGSL 與 Rust 的不同之處在于聲明的變量let是不可變的。如果你希望一個變量是可變的,應該使用關鍵字是var。

          我們main()函數的簽名增加了兩個參數:global_id和local_id。我可以選擇任何名稱——它們的值由與它們關聯的屬性決定: Theglobal_invocation_id是一個內置值,對應于工作負載中此著色器調用的全局 x/y/z 坐標。local_invocation_id是工作組中此著色器的 x/y/z坐標。

          工作量中標記的三個工作項 a、b 和 c 的示例

          此圖顯示了工作負載@workgroup_size(4, 4, 4)坐標系的一種可能解釋。可以為你的用例定義坐標系。如果我們在上面繪制的軸上達成一致,我們將看到main() 的以下a、b 和 c參數:

          a:

          • local_id=(x=0, y=0, z=0)
          • global_id=(x=0, y=0, z=0)

          b:

          • local_id=(x=0, y=0, z=0)
          • global_id=(x=4, y=0, z=0)

          c:

          • local_id=(x=1, y=1, z=0)
          • global_id=(x=5, y=5, z=0)


          在我們的著色器中,我們有@workgroup_size(64, 1, 1),因此local_id.x范圍從 0 到 63。為了能夠檢查這兩個值,我將它們“編碼”為一個數字。請注意,WGSL 是嚴格類型的: local_id和global_id都是 vec3<u32>,因此我們必須顯式地將它們的值轉換f32為能夠將它們分配給我們的f32輸出緩沖區。

          GPU 填寫的實際值

          這證明了我們的計算著色器確實為輸出內存中的每個值調用并用唯一值填充它。我們不知道這些數據是按什么順序填寫的,因為這是故意未指定的,并留給 GPU 的調度程序。

          12、WebGPU過調度

          精明的觀察者可能已經注意到,著色器調用的總數 ( Math.ceil(BUFFER_SIZE / 64) * 64) 將導致global_id.x大于我們數組的長度,因為每個f32占用 4 個字節。幸運的是,訪問數組受到隱式鉗位的保護,因此每次超出數組末尾的寫入最終都會寫入數組的最后一個元素。這樣可以避免內存訪問錯誤,但仍可能生成不可用的數據。事實上,如果你檢查返回緩沖區的最后 3 個元素,會發現數字 247055、248056 和 608032。我們有責任通過提前退出來防止在著色器代碼中發生這種情況:

          fn main( /* ... */) {
            if(global_id.x >=arrayLength(&output)) {
              return;
            }
            output[global_id.x]=f32(global_id.x) * 100. + f32(local_id.x);
          }

          如果需要,您可以運行此演示并檢查完整源代碼。

          13、WebGPU結構對齊

          我們的目標是讓整個樂塔球在 2D 空間中移動并進行愉快的小碰撞。為此,每個球都需要有一個半徑、一個位置和一個速度矢量。我們可以繼續處理array<f32>,并說第一個浮點數是第一個球的 x 位置,第二個浮點數是第一個球的 y 位置,依此類推。這不是我所說的人體工程學。幸運的是,WGSL 允許我們定義自己的結構,將多條數據捆綁在一個整潔的包中。

          舊消息:如果你知道什么是內存對齊,則可以跳過本節(盡管請看一下代碼示例)。如果你還不知道它是什么,我不會真正解釋原因,而是向你展示它是如何體現的以及如何使用它。

          因此,用所有這些組件定義一個struct Balla 并將我們的array<f32>轉換為array<Ball>是有意義的。所有這一切的缺點:我們必須談論對齊。

          struct Ball {
            radius: f32;
            position: vec2<f32>;
            velocity: vec2<f32>;
          }
          
          @group(0) @binding(1)
          var<storage, write> output: array<f32>;
          var<storage, write> output: array<Ball>;
          
          @stage(compute) @workgroup_size(64)
          fn main(
            @builtin(global_invocation_id) global_id : vec3<u32>,
            @builtin(local_invocation_id) local_id : vec3<u32>,
          ) {
            let num_balls=arrayLength(&output);
            if(global_id.x >=num_balls) {
              return;
            }
          
            output[global_id.x].radius=999.;
            output[global_id.x].position=vec2<f32>(global_id.xy);
            output[global_id.x].velocity=vec2<f32>(local_id.xy);
          }

          如果運行此演示,你將在控制臺中看到:

          由于對齊約束,該結構在其內存布局中有一個填充

          我放置999到結構的第一個字段,以便于查看結構在緩沖區中的開始位置。在我們到達下一個之前總共有 6 個數字999,這有點令人驚訝,因為該結構實際??上只有 5 個數字要存儲:radius、position.x、position.y 、velocity.x和velocity.y。仔細一看,很明顯radius后面的數字總是0。這是對齊的原因。

          每種 WGSL 數據類型都有明確定義的對齊要求。如果數據類型的對齊方為N,這意味著該數據類型的值只能存儲在一個內存地址是N的倍數. f32對齊為 4,而vec2<f32>對齊為 8。如果我們假設我們的結構從地址 0 開始,則該radius字段可以存儲在地址 0,因為 0 是 4 的倍數。結構中的下一個字段是vec2<f32>,對齊為 8。但是,之后的第一個空閑地址radius是 4,它不是8 的倍數。為了解決這個問題,編譯器添加了 4 個字節的填充以到達下一個 8 的倍數的地址。這解釋了我們在 DevTools 控制臺中看到一個值為 0 的未使用字段。

          WGSL 規范中的(縮短的)對齊表

          現在知道了結構在內存中是如何布局的,我們可以從 JavaScript 填充它以生成我們的初始狀態球,并將其讀回以可視化它。

          14、WebGPU輸入輸出

          我們已經成功地從 GPU 讀取數據,將其帶到 JavaScript 并“解碼”它。現在是處理另一個方向的時候了。我們需要在 JavaScript 中生成所有球的初始狀態并將其提供給 GPU,以便它可以在其上運行計算著色器。生成初始狀態相當簡單:

          let inputBalls=new Float32Array(new ArrayBuffer(BUFFER_SIZE));
          for (let i=0; i < NUM_BALLS; i++) {
            inputBalls[i * 6 + 0]=randomBetween(2, 10); // radius
            inputBalls[i * 6 + 1]=0; // padding
            inputBalls[i * 6 + 2]=randomBetween(0, ctx.canvas.width); // position.x
            inputBalls[i * 6 + 3]=randomBetween(0, ctx.canvas.height); // position.y
            inputBalls[i * 6 + 4]=randomBetween(-100, 100); // velocity.x
            inputBalls[i * 6 + 5]=randomBetween(-100, 100); // velocity.y
          }

          Buffer-backed-object:對于更復雜的數據結構,從 JavaScript 中操作數據會變得相當乏味。雖然最初是為工人用例編寫的,但我的庫buffer-backed-object在這里可以派上用場!

          我們也已經知道如何將緩沖區暴露給我們的著色器。只需要調整我們的管道綁定組布局以期望另一個緩沖區:

          const bindGroupLayout=device.createBindGroupLayout({
            entries: [
              {
                binding: 0,
                visibility: GPUShaderStage.COMPUTE,
                buffer: {
                  type: "read-only-storage",
                },
              },
              {
                binding: 1,
                visibility: GPUShaderStage.COMPUTE,
                buffer: {
                  type: "storage",
                },
              },
            ],
          });

          ...并創建一個我們可以使用綁定組綁定的 GPU 緩沖區:

          const input=device.createBuffer({
            size: BUFFER_SIZE,
            usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
          });
          
          const bindGroup=device.createBindGroup({
            layout: bindGroupLayout,
            entries: [
              {
                binding: 0,
                resource: {
                  buffer: input,
                },
              },
              {
                binding: 1,
                resource: {
                  buffer: output,
                },
              },
            ],
          });

          現在是新的部分:向 GPU 發送數據。就像讀取數據一樣,我們在技術上必須創建一個可以映射的暫存緩沖區,將我們的數據復制到暫存緩沖區,然后發出命令將我們的數據從暫存緩沖區復制到存儲緩沖區。然而,WebGPU 提供了一個方便的功能,它可以為我們選擇最有效的方式將我們的數據放入存儲緩沖區,即使這涉及動態創建臨時暫存緩沖區:

          device.queue.writeBuffer(input, 0, inputBalls);

          這樣就行了?是的!我們甚至不需要命令編碼器。我們可以直接把這個命令放到命令隊列中。device.queue還為紋理提供了一些其他類似的便利功能。

          現在我們需要將這個新緩沖區綁定到 WGSL 中的一個變量并對其進行處理:

          struct Ball {
            radius: f32;
            position: vec2<f32>;
            velocity: vec2<f32>;
          }
          
          @group(0) @binding(0)
          var<storage, read> input: array<Ball>;
          
          @group(0) @binding(1)
          var<storage, write> output: array<Ball>;
          
          let TIME_STEP: f32=0.016;
          
          @stage(compute) @workgroup_size(64)
          fn main(
            @builtin(global_invocation_id)
            global_id : vec3<u32>,
          ) {
            let num_balls=arrayLength(&output);
            if(global_id.x >=num_balls) {
              return;
            }
            output[global_id.x].position=input[global_id.x].position +
              input[global_id.x].velocity * TIME_STEP;
          }

          我希望這個著色器代碼的絕大部分在這一點上不會讓你感到意外。

          每一幀更新球的位置并使用 Canvas2D 繪制到屏幕上

          最后,我們需要做的就是將output緩沖區讀回 JavaScript,編寫一些 Canvas2D 代碼來可視化緩沖區的內容并將其全部放入requestAnimationFrame()循環中。你可以看到這個演示的結果。

          15、WebGPU性能

          上一個演示只是沿著它們的速度矢量移動每個球。不完全令人興奮或計算復雜。在我們查看我們的創作的性能特征之前,讓我在著色器中放入一些適當的物理計算。我不會在這里解釋它們——博客文章已經足夠長了——但我會說我采取了最幼稚的方法:每個球都檢查與其他球的碰撞。如果你好奇,你可以看看最終演示的源代碼,其中還包含指向我用來編寫物理-y 位的資源的鏈接。

          ...現在有彈性墻壁和彈性球!

          我不想對這個實驗進行任何精確的測量,因為我沒有優化物理算法,也沒有優化我對 WebGPU 的使用。然而,即使是這種幼稚的實現也表現得非常好(在我的 M1 MacBook Air 上)這一事實給我留下了深刻的印象。在我們降到 60fps 以下之前,我可以去大約 2500 個球。但是,查看軌跡,很明顯,在 2500 個球時,瓶頸是 Canvas2D 嘗試繪制場景,而不是 WebGPU 計算。

          在 14000 個球上,原始 GPU 計算時間在 M1 MBA 上達到約 16 毫秒

          為了看看這到底有多快,我禁用了渲染,而是performance.measure()在用盡 16 毫秒的幀預算之前查看我可以模擬多少個球。這發生在我的機器上大約 14000 個球。這種未經優化的快速運行真的讓我陶醉于 WebGPU 讓我訪問的計算能力。

          16、WebGPU穩定性和可用性

          WebGPU 已經開發了一段時間,我認為標準組渴望將 API 聲明為穩定的。話雖如此,該 API 僅在 Chrome 和 Firefox 中可用。我對 Safari 提供此 API 持樂觀態度,但在撰寫本文時,Safari TP 中還沒有什么可看的。

          在穩定性方面,即使在我為本文進行研究時,也發生了一些變化。例如,屬性的語法從 更改[[stage(compute), workgroup_size(64)]]為@stage(compute) @workgroup_size(64)。在撰寫本文時,Firefox 仍在使用舊語法。passEncoder.end()曾經是passEncoder.endPass()。規范中還有一些內容尚未在任何瀏覽器中實現,例如著色器常量或移動設備上可用的 API。

          基本上我想說的是:當瀏覽器和標準人員處于這個 API 的 ?stable? 之旅的最后階段時,預計會發生更多的重大變化。

          17、結束語

          擁有一個現代 API 來與 Web 上的 GPU 對話將非常有趣。在投入時間克服最初的學習曲線之后,我真的覺得自己有能力使用 JavaScript 在 GPU 上運行大規模并行工作負載。還有wgpu,它在 Rust 中實現了 WebGPU API,允許你在瀏覽器之外使用 API。wgpu 還支持將 WebAssembly 作為編譯目標,因此可以在瀏覽器外部和通過 WebAssembly 在瀏覽器內部本地運行 WebGPU 程序。有趣的事實:Deno是第一個開箱即用也支持 WebGPU 的運行時(感謝 wgpu)。


          原文鏈接:http://www.bimant.com/blog/webgpu-deep-dive/


          主站蜘蛛池模板: 国产精品av一区二区三区不卡蜜| 国产精品无码一区二区三区不卡| 国产在线视频一区| 91福利一区二区| 伊人色综合一区二区三区 | 亚洲无删减国产精品一区| 国产在线一区二区视频| 久久久久无码国产精品一区| 一区二区视频免费观看| 成人一区二区三区视频在线观看| 色偷偷久久一区二区三区| 精品一区二区高清在线观看| 亚洲a∨无码一区二区| 久久精品国产一区二区三区肥胖| 一区二区三区无码高清| 视频一区视频二区制服丝袜| 亚洲性无码一区二区三区| 中文字幕一区一区三区| 色偷偷一区二区无码视频| 久久高清一区二区三区| 久久99热狠狠色精品一区| 国内精品无码一区二区三区| 亚洲av无码天堂一区二区三区 | 精品一区二区91| 天天躁日日躁狠狠躁一区| 精品国产一区二区麻豆| 色综合久久一区二区三区| 精品一区二区三区免费毛片爱 | 亚洲一区二区三区无码影院| 白丝爆浆18禁一区二区三区| 国产拳头交一区二区| 蜜桃视频一区二区三区| 一区二区视频在线免费观看| 动漫精品专区一区二区三区不卡| 波多野结衣中文一区| 国产99精品一区二区三区免费| 无码人妻AⅤ一区二区三区水密桃| 国产一区二区三区日韩精品| 无码人妻精品一区二区蜜桃百度| 亚洲一区二区三区在线视频 | 99精品一区二区三区无码吞精|