Lulucat

Prestasi Alat Kapur: Daripada Pass Skrin Penuh kepada Segi Empat Tepat Scissor

Gaoge ZhangGaoge Zhang

Alat kapur dalam Lulucat Notes menjadi perlahan di kawasan tulisan tangan yang padat. Kekangan prestasi utamanya bukan 3,571 sampel input, tetapi 70 scratch pass skrin penuh bagi setiap bingkai. Cache resolusi rendah yang ditolak dan segi empat tepat scissor bagi setiap sapuan menceritakan selebihnya.

Paparan potongan Lulucat Notes pada iPad dengan zum 255%, menunjukkan tulisan tangan kapur merah dan biru bagi petikan klasik Cina, dengan sebahagian bar alat aplikasi kelihatan.

Kapur merah dan biru pada binaan peranti untuk pengesahan akhir, 155 sapuan pada zum 255%.

Alat kapur dalam Lulucat Notes mempunyai masalah prestasi yang khusus: menulis di kawasan kosong terasa lancar, tetapi apabila masuk ke kawasan yang sudah dipenuhi sapuan kapur, hujung pen mula ketinggalan. Terus menulis di kawasan yang sama juga memperlahankan panning kanvas secara beransur-ansur.

Satu halaman tulisan tangan biasa sudah cukup untuk mencetuskannya: zum 300%, 70 sapuan kapur yang kelihatan di kawasan setempat, berjumlah 3,571 titik sampel input. Kawasan kosong kekal lancar; hanya kawasan tempat sapuan tertumpu menjadi perlahan.

Selepas pembaikan, halaman yang sama boleh terus menerima tulisan baharu pada zum 255%, dan sapuan sedia ada mengekalkan kejelasan penuh semasa pen-down dan panning.

Lulucat Notes pada iPad dengan zum 255%, memaparkan tulisan tangan kapur merah dan biru. Teksnya berbunyi "天行健,君子以自强不息;地势坤,君子以厚德载物" — petikan klasik Cina. Maskot Lulucat biru berada di bahagian atas kanan. Bar alat bawah menunjukkan kiraan sapuan 155, Save, Clear dan pelungsur zum 255%.

Tangkapan skrin peranti untuk pengesahan akhir, 155 sapuan semuanya. Pada tahap zum ini, pen-down dan panning tidak menukar kejelasan sapuan sedia ada buat sementara waktu.

Mengapa kapur memerlukan tekstur scratch

Pen biasa boleh mengomposit setiap cop bulat terus ke tekstur dakwat dengan pengadunan source-over. Kapur menambah lapisan grain-gating: perender mula-mula mengumpulkan body coverage dan depth bagi seluruh sapuan, kemudian menggunakan tekstur grain tetap untuk menentukan kedudukan yang menerima habuk kapur, dan akhirnya mengompositkan hasilnya ke atas dakwat sedia ada.

Tekstur scratch ini mengasingkan satu sapuan kapur. Pengasingan penting kerana cop dalam sapuan yang sama banyak bertindih; jika setiap cop diberi grain gate secara berasingan, garis tengah sapuan akan mengumpulkan warna berulang dan liang kapur akan beralih mengikut ketumpatan pensampelan.

Pada tahap zum tinggi, Lulucat Notes melukis semula sapuan vektor yang kelihatan dalam viewport semasa. Pelaksanaan lama melakukan langkah berikut bagi setiap sapuan kapur yang kelihatan:

  1. Menamatkan main render encoder;
  2. Mengosongkan tekstur scratch;
  3. Melukis satu sapuan kapur ini ke dalam scratch;
  4. Membuka semula main render encoder;
  5. Mengompositkan scratch kembali ke drawable dengan segi tiga skrin penuh.

Semantik satu sapuan adalah betul, tetapi skop kerja jauh lebih besar. Drawable iPad berukuran 2732×2048 — kira-kira 5.6 juta piksel. Setiap sapuan kapur mencetuskan satu scratch pass dan satu komposit skrin penuh. Tujuh puluh sapuan kapur bermaksud kira-kira 141 render encoder dan 70 komposit skrin penuh.

Biarkan bilangan sapuan kapur yang kelihatan ialah dan bilangan piksel drawable ialah . Jika hanya kerja yang berskala dengan liputan piksel dipertimbangkan, pelaksanaan lama hampir kepada

Setiap sapuan kapur juga membawa overhed render-pass tetap, jadi kos itu turut meningkat secara linear dengan . 3,571 titik input hanya menyumbang kos sekunder. Yang berskala dengan bilangan sapuan setempat ialah skop kerja skrin penuh yang dicetuskan oleh setiap sapuan.

Pengukuran dibuat pada iPad Pro 12.9 inci (generasi ke-5, M1) yang menjalankan iPadOS 18.6.2. Kami membandingkan timestamp GPU daripada viewport yang sama sebelum dan selepas perubahan, menggunakan timestamp command-buffer dalam binaan peranti Debug yang sama pada iPad ini — selepas ini dipanggil LucasPad. Julat di bawah ialah turun naik biasa daripada log berbilang bingkai, bukan janji kadar bingkai untuk versi yang akan diedarkan. Dengan 70 sapuan kapur yang kelihatan, satu bingkai biasanya memerlukan 52–60 ms masa GPU; di kawasan dengan kira-kira 120 sapuan, masa GPU meningkat kepada 77–80 ms.

Dianggarkan berdasarkan luas segi empat tepat skrin penuh bagi pass scratch dan komposit, skop kerja teori setiap bingkai meningkat daripada kira-kira 783 juta piksel kepada 1.34 bilion piksel. Angka ini ialah jumlah luas segi empat tepat dan tidak bersamaan dengan bilangan seruan fragment, bait bacaan/tulisan memori video atau pembilang perkakasan GPU. Fast clear Metal, attachment load/store dan pertukaran pass kekal di bawah kawalan GPU dan pemacu.

Ini juga menerangkan mengapa kawasan kosong kekal lancar. Visibility culling melangkau sapuan di luar viewport; di kawasan kosong hampir kepada sifar, manakala di kawasan padat terus meningkat.

Jawapan yang salah pada 0.85 ms

Aplikasi sudah mempunyai tekstur dakwat seluruh halaman yang dibakar pada dua piksel setiap titik. Kami cuba memaparkan tekstur ini secara langsung semasa menulis, panning dan menzum, dengan hanya sapuan Apple Pencil semasa dikekalkan sebagai vektor langsung; selepas interaksi tamat, satu bingkai tambahan akan melukis semula hasil vektor resolusi tinggi.

Pendekatan ini berprestasi sangat baik. Di kawasan padat yang sama pada zum 300%, masa GPU turun kepada 0.84–0.85 ms dan tidak lagi meningkat mengikut bilangan sapuan kapur sedia ada.

Masalah pada peranti sebenar juga sama jelas. Pada zum 300%, kira-kira enam piksel skrin bagi setiap titik diperlukan, tetapi cache hanya menyediakan dua. Sebaik sahaja Apple Pencil menyentuh skrin, semua sapuan sedia ada bertukar menjadi imej lembut beresolusi rendah; apabila Pencil diangkat, ia kembali kepada kejelasan penuh.

Penguji berkata: “Apabila saya menulis, seluruh kanvas menjadi kabur. Ia menjadi jelas semula apabila saya melepaskannya.”

Pengoptimuman itu dibuang. 0.85 ms ialah hasil terendah yang diukur, tetapi ia bukan alat kapur yang boleh diterima. Sapuan sedia ada ialah sebahagian daripada maklum balas penulisan; kejelasannya tidak boleh berubah apabila pen-down.

Mengehadkan setiap sapuan kapur kepada segi empat tepatnya sendiri

Pembaikan akhir mengekalkan scratch setiap sapuan dan pengompositan setiap sapuan, lalu hanya mengurangkan skop kerja pikselnya. Setiap sapuan sudah mempunyai bounding box kanvas yang terhasil daripada gabungan semua jejari copnya. Perender menukar bounding box ini kepada koordinat drawable viewport semasa dan menambah padding dua piksel untuk margin antialiasing.

Segi empat tepat scissor yang sama digunakan untuk tiga perkara: mengosongkan scratch, melukis sapuan dan mengompositkan hasilnya kembali ke permukaan utama.

let rect = displayScissorRect(for: stroke.bounds, viewport: viewport)

scratchEncoder.setScissorRect(rect)
clearScratchExplicitly()
drawStrokeIntoScratch(stroke)

mainEncoder.setScissorRect(rect)
compositeChalkFromScratch(stroke)
mainEncoder.setScissorRect(fullDrawable)

Logik yang sama juga digunakan untuk baking dan partial replay pada tekstur dakwat 4096², supaya paparan zum tinggi dan lapisan dakwat yang telah dimuktamadkan tidak menghasilkan dua tingkah laku kapur yang berbeza.

Dua perincian mudah terlepas di sini.

Pertama, loadAction = .clear bagi render pass berlaku semasa peringkat attachment load dan tidak dihadkan oleh rasterization scissor. Jika terus digunakan, ia masih akan mengosongkan seluruh tekstur scratch. Pass tetap menggunakan .dontCare, kemudian melukis clear_fragment dalam scissor. Segi empat tepat ini kemudiannya ditulis sepenuhnya, dan komposit hanya membaca segi empat tepat yang sama, jadi kandungan attachment lama tidak perlu dimuatkan.

Kedua, selepas komposit setiap sapuan kapur selesai, scissor luar mesti dipulihkan. Jika baris pemulihan state ini ditinggalkan, pen, imej atau pilihan seterusnya akan terus dipotong oleh sempadan sapuan kapur sebelumnya, lalu kelihatan seperti sapuan atau imej yang hilang.

Grain kapur masih mengambil sampel daripada koordinat kanvas mutlak, bukan UV setempat dalam segi empat tepat. Mengalihkan scissor hanya mengubah piksel yang diproses GPU; ia tidak mengubah lokasi tekstur grain yang dibaca oleh setiap piksel. Oleh itu, segi empat tepat bersebelahan tidak menghasilkan jahitan tekstur, dan menyeret kanvas tidak menyebabkan grain hanyut.

Jika hanya beban kerja piksel dipertimbangkan, skop kerja baharu hampir kepada

di mana ialah luas axis-aligned bounding-box bagi sapuan kapur ke- pada skrin semasa. Bilangan render encoder tidak berkurang, tetapi setiap clear dan komposit kini dihadkan oleh bounding box skrin sapuan tersebut.

Mengapa sapuan kapur warna sama tidak dibatch

Kebanyakan sapuan kapur pada halaman berkongsi warna dan ketumpatan yang sama, dan memang menggoda untuk melukis berpuluh-puluh sapuan ke dalam scratch sekali gus lalu mengomposit hanya sekali. Ini akan mengurangkan render pass dengan lebih lanjut, tetapi mengubah semantik warna dan grain di kawasan yang bertindih.

Pertimbangkan kes yang sengaja dipermudahkan: dua sapuan berkongsi nilai grain-gate yang sama pada piksel tertentu, dengan body coverages dan . Dalam shader sebenar, gate juga bergantung pada pressure depth setiap sapuan; kes yang lebih ringkas ini sudah memadai untuk menunjukkan bahawa batching secara umum tidak setara. Pengompositan setiap sapuan semasa menghasilkan

manakala menggabungkan body dahulu lalu menggunakan satu gate menghasilkan

Perbezaannya ialah . Setiap kali dua sapuan bertindih dan grain gate bukan sifar tulen atau satu tulen, hasilnya berbeza. Batch secara terus akan mengubah cara habuk kapur jatuh di persilangan.

Batch yang tepat memerlukan bukti bahawa piksel sapuan adalah saling tidak bersilang, atau memperuntukkan kawasan atlas bebas untuk setiap sapuan dan mengomposit dalam susunan asal. Penerimaan akhir pada peranti mengekalkan pendekatan scissor, jadi pusingan ini tidak memperkenalkan atlas atau kerumitan pengurusannya.

Daripada satu bilion piksel kembali kepada beberapa juta

Pengukuran peranti akhir:

SenarioSebelum pembaikanScissor tepat
70 sapuan kapur kelihatan, zum 300%, menulisGPU 52–60 ms≈ 9–10 ms
≈ 121 sapuan kapur kelihatan, zum 300%GPU 77–80 ms13.7–15.6 ms
Skop segi empat tepat teori setiap bingkai (scratch + komposit)783 M–1.34 B piksel≈ 1.7 M–3 M piksel

Kami turut menyemak sempadan clipping menggunakan 3,452 sapuan dan 202,710 titik sampel daripada dokumen peranti. Pada zum 0.5×, 1×, 2×, 3×, 5× dan 8×, 186,408 kes viewport dihasilkan; setiap point sprite yang boleh menghasilkan coverage non-zero berada dalam scissor yang dikira. Semakan ini merangkumi tepi kanvas, tepi viewport dan pelbagai gabungan offset.

Kod akhir tidak bertukar kepada LOD beresolusi rendah berdasarkan keadaan interaksi. Tahap zum rendah masih memaparkan tekstur dakwat seluruh halaman; tahap zum tinggi masih melukis semula sapuan yang kelihatan sebagai vektor. Pada sisi ambang yang sama, pen-down dan panning tidak menggantikan sapuan sedia ada dengan tahap kejelasan yang berbeza. Semasa lukisan semula vektor zum tinggi, clear scratch dan komposit setiap sapuan kapur hanya meliputi bounding box skrin sendiri.

Masa GPU tidak menangkap kejelasan

Masalah prestasi GPU tidak semestinya berskala dengan kuantiti yang paling jelas dalam struktur data. Dalam kes ini, 3,571 titik input ialah suspek yang mudah; yang menentukan masa bingkai ialah kerja skrin penuh yang dicetuskan oleh setiap 70 sapuan kapur, bersama pertukaran render-passnya.

Semantik visual turut mengehadkan pengoptimuman yang tersedia. Scratch setiap sapuan, susunan pengompositan asal dan koordinat grain kanvas mutlak tidak boleh dibuang sesuka hati. Warna sama dan ketumpatan sama hanya bermakna parameternya sepadan — ia tidak membuktikan bahawa hasil yang bertindih boleh digabungkan.

Maklum balas peranti sebenar menolak versi dengan masa GPU terendah. Kata-kata “seluruh kanvas menjadi kabur” memberikan kekangan produk yang tidak dinyatakan oleh pengukuran semata-mata: apabila Apple Pencil menyentuh skrin, pengguna juga sedang melihat sapuan sedia ada.

Versi akhir tidak memperkenalkan lapisan cache interaksi baharu dan tidak mengurangkan kejelasan. Lukisan semula vektor zum tinggi hanya mengehadkan kerja setiap sapuan kapur kepada bounding box skrinnya sendiri. Selepas dimuatkan semula ke LucasPad, maklum balasnya ialah: “Nampak hebat.”