ചോക്ക് ഉപകരണത്തിന്റെ പ്രകടനം: Full-Screen Pass-ുകളിൽ നിന്ന് Scissor Rectangles-ലേക്ക്
സാന്ദ്രമായ കൈയെഴുത്ത് പ്രദേശങ്ങളിൽ Lulucat Notes-ന്റെ ചോക്ക് ഉപകരണം മന്ദഗതിയിലായി. പ്രധാന തടസ്സം 3,571 input samples ആയിരുന്നില്ല — ഓരോ ഫ്രെയിമിലും നടന്ന 70 full-screen scratch passes ആയിരുന്നു. നിരസിച്ച കുറഞ്ഞ റെസല്യൂഷൻ cache-വും ഓരോ stroke-നുമുള്ള scissor rectangle-വും ബാക്കി കഥ പറയുന്നു.

അന്തിമ പരിശോധനയ്ക്കായി ഉപയോഗിച്ച device build-ിലെ ചുവപ്പും നീലയും ചോക്ക്, 255% സൂമിൽ 155 strokes.
Lulucat Notes-ലെ ചോക്ക് ഉപകരണത്തിന് ഒരു പ്രത്യേക പ്രകടന പ്രശ്നമുണ്ടായിരുന്നു: ശൂന്യ പ്രദേശത്ത് എഴുതുന്നത് സുഗമമായിരുന്നു, പക്ഷേ ഇതിനകം ചോക്ക് strokes നിറഞ്ഞ പ്രദേശത്തേക്ക് നീങ്ങുമ്പോൾ pen tip പിന്നിലാകാൻ തുടങ്ങി. അതേ പ്രദേശത്ത് എഴുതുന്നത് തുടർന്നപ്പോൾ canvas പാനിങ്ങും ക്രമേണ മന്ദഗതിയിലായി.
സാധാരണ കൈയെഴുത്തിന്റെ ഒരു പേജ് തന്നെ ഇത് ആരംഭിക്കാൻ മതിയായിരുന്നു: 300% സൂം, പ്രാദേശിക പ്രദേശത്ത് കാണുന്ന 70 chalk strokes, ആകെ 3,571 input sample points. ശൂന്യ പ്രദേശങ്ങൾ സുഗമമായി തുടർന്നു; strokes കേന്ദ്രീകരിച്ചിരുന്ന പ്രദേശം മാത്രം മന്ദമായി.
പരിഹാരത്തിന് ശേഷം അതേ പേജിൽ 255% സൂമിൽ പുതിയ എഴുത്ത് തുടരാം; pen-down സമയത്തും പാനിങ്ങിനിടയിലും നിലവിലുള്ള strokes പൂർണ്ണമായ വ്യക്തത നിലനിർത്തും.

അന്തിമ പരിശോധനയ്ക്കായി ഉപയോഗിച്ച device-ന്റെ സ്ക്രീൻഷോട്ട്, ആകെ 155 strokes. ഈ സൂം നിലയിൽ pen-down-ഓ പാനിങ്ങോ നിലവിലുള്ള strokes-ന്റെ വ്യക്തത താൽക്കാലികമായി മാറ്റുന്നില്ല.
ചോക്കിന് scratch texture എന്തുകൊണ്ട് വേണം
ഒരു സാധാരണ pen-ന് ഓരോ circular stamp-ും source-over blending ഉപയോഗിച്ച് ink texture-ലേക്ക് നേരിട്ട് composite ചെയ്യാം. ചോക്ക് ഒരു grain-gating layer ചേർക്കുന്നു: renderer ആദ്യം ഒരു മുഴുവൻ stroke-ന്റെ body coverage-യും depth-ഉം accumulate ചെയ്യുന്നു, തുടർന്ന് fixed grain texture ഉപയോഗിച്ച് ഏത് positions-ലാണ് ചോക്ക് പൊടി ലഭിക്കേണ്ടതെന്ന് തീരുമാനിക്കുന്നു, ഒടുവിൽ result existing ink-ലേക്ക് composite ചെയ്യുന്നു.
ഈ scratch texture ഒരു ചോക്ക് stroke-നെ വേർതിരിക്കുന്നു. ഒരേ stroke-ലുള്ള stamps ശക്തമായി overlap ചെയ്യുന്നതിനാൽ ഈ വേർതിരിക്കൽ പ്രധാനമാണ്; ഓരോ stamp-നും വേർതിരിച്ച് grain gate പ്രയോഗിച്ചാൽ stroke centerline-ൽ repeated color അടിഞ്ഞുകൂടുകയും sampling density-യോടൊപ്പം ചോക്കിന്റെ pores മാറുകയും ചെയ്യും.
ഉയർന്ന സൂം നിലകളിൽ Lulucat Notes നിലവിലെ viewport-ൽ കാണുന്ന vector strokes വീണ്ടും വരയ്ക്കുന്നു. പഴയ നടപ്പാക്കൽ കാണുന്ന ഓരോ chalk stroke-നും താഴെയുള്ള ഘട്ടങ്ങൾ നടത്തി:
- main render encoder അവസാനിപ്പിക്കുക;
- scratch texture clear ചെയ്യുക;
- ഈ chalk stroke മാത്രം scratch-ലേക്ക് draw ചെയ്യുക;
- main render encoder വീണ്ടും തുറക്കുക;
- full-screen triangle ഉപയോഗിച്ച് scratch-നെ drawable-ലേക്ക് വീണ്ടും composite ചെയ്യുക.
ഒരു stroke-ന്റെ അർത്ഥം ശരിയായിരുന്നെങ്കിലും പ്രവർത്തന പരിധി വളരെ വലുതായിരുന്നു. iPad-ന്റെ drawable 2732×2048 — ഏകദേശം 5.6 മില്യൺ പിക്സലുകൾ. ഓരോ chalk stroke-ും ഒരു scratch pass-ും ഒരു full-screen composite-ും ഉണ്ടാക്കി. 70 chalk strokes എന്നാൽ ഏകദേശം 141 render encoders-ും 70 full-screen composites-ും.
കാണുന്ന chalk strokes-ന്റെ എണ്ണം
ഓരോ chalk stroke-നും സ്ഥിരമായ render-pass overhead ഉണ്ടായിരുന്നു; അതിനാൽ ആ ചെലവും
iPadOS 18.6.2 പ്രവർത്തിക്കുന്ന 12.9-inch iPad Pro (5th generation, M1)-ൽ അളവുകൾ എടുത്തു. ഈ പ്രത്യേക iPad-ിലെ അതേ Debug device build-ൽ command-buffer timestamps ഉപയോഗിച്ച് മാറ്റത്തിന് മുമ്പും ശേഷവും ഒരേ viewport-ന്റെ GPU timestamps താരതമ്യം ചെയ്തു. താഴെയുള്ള പരിധികൾ ഒന്നിലധികം ഫ്രെയിം log-കളിലെ സാധാരണ വ്യതിയാനങ്ങളാണ്; വിതരണം ചെയ്യാനിരിക്കുന്ന പതിപ്പിന്റെ frame-rate വാഗ്ദാനങ്ങളല്ല. 70 visible chalk strokes ഉള്ളപ്പോൾ ഒരു frame-ന് സാധാരണയായി 52–60 ms GPU time ആവശ്യമായി; ഏകദേശം 120 strokes ഉള്ള area-യിൽ GPU time 77–80 ms ആയി ഉയർന്നു.
Scratch, composite passes-ന്റെ full-screen rectangle area അടിസ്ഥാനമാക്കി കണക്കാക്കിയാൽ, ഓരോ ഫ്രെയിമിലെയും സൈദ്ധാന്തിക പ്രവർത്തന പരിധി ഏകദേശം 783 മില്യൺ പിക്സലിൽ നിന്ന് 1.34 ബില്യൺ പിക്സലായി വളർന്നു. ഈ കണക്ക് rectangle areas-ന്റെ ആകെത്തുകയാണ്; fragment invocation counts, video-memory read/write bytes, അല്ലെങ്കിൽ GPU hardware counters എന്നിവയ്ക്ക് ഇത് തുല്യമല്ല. Metal-ന്റെ fast clear, attachment load/store, pass switching എന്നിവ GPU-യും driver-ഉം നിയന്ത്രിക്കുന്നതിലാണ് തുടരുന്നത്.
ശൂന്യ പ്രദേശങ്ങൾ സുഗമമായി തുടർന്നതും ഇതു വിശദീകരിക്കുന്നു. Visibility culling viewport-ന് പുറത്തുള്ള strokes ഒഴിവാക്കുന്നു; ശൂന്യ പ്രദേശത്ത്
0.85 ms-ൽ ലഭിച്ച തെറ്റായ ഉത്തരം
ആപ്പിൽ ഇതിനകം ഓരോ point-നും രണ്ട് പിക്സലിൽ baked ചെയ്ത മുഴുവൻ പേജിന്റെ ink texture ഉണ്ടായിരുന്നു. എഴുത്ത്, പാൻ, സൂം സമയത്ത് ഈ texture നേരിട്ട് കാണിക്കുകയും നിലവിലെ Apple Pencil stroke മാത്രം live vector ആയി നിലനിർത്തുകയും ചെയ്യുന്ന രീതിയാണ് ഞങ്ങൾ പരീക്ഷിച്ചത്; interaction കഴിഞ്ഞാൽ ഒരു അധിക ഫ്രെയിം ഉയർന്ന റെസല്യൂഷനിലുള്ള vector ഫലം വീണ്ടും render ചെയ്യും.
ഈ സമീപനം വളരെ നന്നായി പ്രവർത്തിച്ചു. അതേ സാന്ദ്ര പ്രദേശത്ത് 300% സൂമിൽ GPU time 0.84–0.85 ms ആയി കുറഞ്ഞു; നിലവിലുള്ള chalk strokes-ന്റെ എണ്ണത്തോടൊപ്പം ഇനി വളർന്നില്ല.
യഥാർത്ഥ device-ിലെ പ്രശ്നവും അത്ര തന്നെ വ്യക്തമായിരുന്നു. 300% zoom-ൽ ഓരോ point-നും ഏകദേശം ആറ് screen pixels ആവശ്യമായിരുന്നു, പക്ഷേ cache നൽകിയത് രണ്ട് മാത്രം. Apple Pencil touch-down ചെയ്ത നിമിഷം നിലവിലുള്ള strokes എല്ലാം മൃദുവായ low-resolution image ആയി മാറി; Pencil ഉയർത്തുമ്പോൾ അവ പൂർണ്ണ വ്യക്തതയിലേക്ക് മടങ്ങി.
പരിശോധകൻ പറഞ്ഞു: “ഞാൻ എഴുതുമ്പോൾ മുഴുവൻ canvas-ും blurry ആകുന്നു. കൈ വിട്ടാൽ അത് വീണ്ടും clear ആകുന്നു.”
ഈ optimization നീക്കം ചെയ്തു. 0.85 ms അളന്നതിൽ ഏറ്റവും കുറഞ്ഞ ഫലമായിരുന്നു, പക്ഷേ അത് സ്വീകരിക്കാവുന്ന ചോക്ക് ഉപകരണമായിരുന്നില്ല. നിലവിലുള്ള strokes എഴുത്തിനിടയിലെ feedback-ന്റെ ഭാഗമാണ്; pen-down-ൽ അവയുടെ വ്യക്തത മാറരുത്.
ഓരോ chalk stroke-നെയും സ്വന്തം rectangle-ലേക്ക് പരിമിതപ്പെടുത്തുക
അന്തിമ പരിഹാരം ഓരോ stroke-ന്റെയും scratch-ും compositing-ും നിലനിർത്തി, pixel പ്രവർത്തന പരിധി മാത്രം കുറച്ചു. ഓരോ stroke-നും അതിലെ എല്ലാ stamp radii-കളുടെ union-ൽ നിന്ന് ലഭിച്ച canvas bounding box ഇതിനകം ഉണ്ടായിരുന്നു. Renderer ഈ bounding box നിലവിലെ viewport-ന്റെ drawable coordinates-ലേക്ക് transform ചെയ്ത് antialiasing margin-നായി two pixels pad ചെയ്യുന്നു.
Scratch clear ചെയ്യാനും stroke draw ചെയ്യാനും result main surface-ലേക്ക് വീണ്ടും composite ചെയ്യാനും ഒരേ scissor rectangle ഉപയോഗിക്കുന്നു.
let rect = displayScissorRect(for: stroke.bounds, viewport: viewport)
scratchEncoder.setScissorRect(rect)
clearScratchExplicitly()
drawStrokeIntoScratch(stroke)
mainEncoder.setScissorRect(rect)
compositeChalkFromScratch(stroke)
mainEncoder.setScissorRect(fullDrawable)
4096² ink texture-ലുള്ള baking, partial replay എന്നിവയ്ക്കും ഇതേ logic ഉപയോഗിക്കുന്നു; അതിനാൽ high-zoom display-യും സ്ഥിരമായ ink layer-ഉം രണ്ട് വ്യത്യസ്ത ചോക്ക് പെരുമാറ്റങ്ങൾ ഉണ്ടാക്കുന്നില്ല.
ഇവിടെ രണ്ട് details എളുപ്പത്തിൽ വിട്ടുപോകാം.
ഒന്നാമത്, ഒരു render pass-ന്റെ loadAction = .clear attachment load stage-ൽ നടക്കുന്നു; rasterization scissor അതിനെ നിയന്ത്രിക്കുന്നില്ല. അത് ഉപയോഗിക്കുന്നത് തുടരുകയാണെങ്കിൽ scratch texture മുഴുവൻ clear ചെയ്യും. സ്ഥിരമായ pass .dontCare ഉപയോഗിക്കുകയും തുടർന്ന് scissor-ന്റെ അകത്ത് clear_fragment വരയ്ക്കുകയും ചെയ്യുന്നു. ഈ rectangle പിന്നീട് പൂർണ്ണമായി എഴുതപ്പെടുന്നു, composite അതേ rectangle മാത്രം വായിക്കുന്നു; അതിനാൽ പഴയ attachment contents load ചെയ്യേണ്ടതില്ല.
രണ്ടാമത്, ഓരോ chalk stroke-ന്റെയും composite കഴിഞ്ഞാൽ outer scissor restore ചെയ്യണം. ഈ state restoration line ഒഴിവാക്കിയാൽ തുടർന്നുള്ള pens, images, selections എന്നിവ മുൻ chalk stroke-ന്റെ bounds കൊണ്ട് clip ചെയ്യപ്പെടും; strokes-ഉം images-ഉം കാണാതായതുപോലെ തോന്നും.
Chalk grain rectangle-നുള്ളിലെ local UV-കളിൽ നിന്ന് അല്ല, absolute canvas coordinates-ൽ നിന്നാണ് sample ചെയ്യുന്നത്. Scissor മാറ്റുന്നത് GPU process ചെയ്യുന്ന pixels മാത്രം മാറ്റുന്നു; ഓരോ pixel വായിക്കുന്ന grain texture location മാറുന്നില്ല. അതിനാൽ adjacent rectangles-ൽ texture seams ഉണ്ടാകില്ല; canvas drag ചെയ്യുമ്പോൾ grain drift ചെയ്യുകയും ഇല്ല.
പിക്സൽ ജോലിയുടെ അളവ് മാത്രം പരിഗണിച്ചാൽ പുതിയ പ്രവർത്തന പരിധി ഏകദേശം ഇങ്ങനെയാണ്:
ഇവിടെ
ഒരേ നിറമുള്ള chalk strokes കൂട്ടമായി പ്രോസസ്സ് ചെയ്യാതിരുന്നതെന്തുകൊണ്ട്
പേജിലെ ഭൂരിഭാഗം chalk strokes-ക്കും ഒരേ നിറവും സാന്ദ്രതയും ആണ്, അതിനാൽ ഡസൻ കണക്കിന് strokes scratch-ലേക്ക് ഒരുമിച്ച് വരച്ച് ഒരിക്കൽ മാത്രം composite ചെയ്യുന്നത് ആകർഷകമാണ്. ഇത് render passes ഇനിയും കുറയ്ക്കും, പക്ഷേ overlap ചെയ്യുന്ന പ്രദേശങ്ങളിലെ നിറത്തിന്റെയും grain-ന്റെയും അർത്ഥം മാറ്റും.
ഉദ്ദേശപൂർവ്വം ലളിതമാക്കിയ ഒരു സാഹചര്യം പരിഗണിക്കാം: ഒരു pixel-ൽ രണ്ട് strokes ഒരേ grain-gate value
എന്നാൽ bodies ആദ്യം merge ചെയ്ത് ഒരു gate മാത്രം പ്രയോഗിച്ചാൽ ലഭിക്കുന്നത്
വ്യത്യാസം
കൃത്യമായ batching-ന് stroke pixels പരസ്പരം വേർപെട്ടവയാണെന്ന് തെളിയിക്കണം, അല്ലെങ്കിൽ ഓരോ stroke-നും സ്വതന്ത്ര atlas region allocate ചെയ്ത് യഥാർത്ഥ ക്രമത്തിൽ composite ചെയ്യണം. അന്തിമ device acceptance scissor സമീപനം നിലനിർത്തി; അതിനാൽ ഈ ഘട്ടത്തിൽ atlas-ഓ അതിന്റെ പരിപാലനത്തിലെ സങ്കീർണ്ണതയോ കൊണ്ടുവന്നില്ല.
ഒരു billion pixels-ൽ നിന്ന് വീണ്ടും കുറച്ച് million pixels-ലേക്ക്
അന്തിമ device അളവുകൾ:
| സാഹചര്യം | പരിഹാരത്തിന് മുമ്പ് | കൃത്യമായ scissor |
|---|---|---|
| കാണുന്ന 70 chalk strokes, 300% zoom-ൽ എഴുതുമ്പോൾ | GPU 52–60 ms | ≈ 9–10 ms |
| ≈ 121 കാണുന്ന chalk strokes, 300% zoom | GPU 77–80 ms | 13.7–15.6 ms |
| ഓരോ frame-ലെയും സൈദ്ധാന്തിക rectangle scope (scratch + composite) | 783 M–1.34 B പിക്സലുകൾ | ≈ 1.7 M–3 M പിക്സലുകൾ |
ഒരു device document-ൽ നിന്നുള്ള 3,452 strokes, 202,710 sample points ഉപയോഗിച്ച് clipping bounds-ും പരിശോധിച്ചു. 0.5×, 1×, 2×, 3×, 5×, 8× zoom-ുകളിൽ 186,408 viewport cases സൃഷ്ടിച്ചു; non-zero coverage ഉണ്ടാക്കാൻ കഴിയുന്ന ഓരോ point sprite-ും കണക്കാക്കിയ scissor-ന്റെ അകത്ത് വന്നു. Canvas edges, viewport edges, വിവിധ offset combinations എന്നിവ ഈ check ഉൾപ്പെടുത്തി.
അന്തിമ code interaction state അനുസരിച്ച് low-resolution LOD-ലേക്ക് മാറുന്നില്ല. കുറഞ്ഞ zoom നിലകളിൽ മുഴുവൻ പേജിന്റെ ink texture തന്നെയാണ് കാണിക്കുന്നത്; ഉയർന്ന zoom നിലകളിൽ കാണുന്ന strokes vectors ആയി വീണ്ടും വരയ്ക്കുന്നു. Threshold-ന്റെ അതേ വശത്ത് pen-down-ഉം panning-ഉം നിലവിലുള്ള strokes-നെ വേറൊരു വ്യക്തതാ നിലയിലേക്ക് മാറ്റുന്നില്ല. High-zoom vector redraw സമയത്ത് ഓരോ chalk stroke-ന്റെയും scratch clear, composite എന്നിവ അതിന്റെ screen bounding box മാത്രം cover ചെയ്യുന്നു.
GPU time clarity capture ചെയ്തില്ല
GPU പ്രകടന പ്രശ്നങ്ങൾ data structure-ൽ ഏറ്റവും ശ്രദ്ധയിൽപ്പെടുന്ന അളവിനൊപ്പം തന്നെ വർധിക്കണമെന്നില്ല. ഇവിടെ 3,571 input points സംശയിക്കാൻ എളുപ്പമുള്ളവയായിരുന്നു; frame time നിർണ്ണയിച്ചത് 70 chalk strokes ഓരോന്നും സൃഷ്ടിച്ച full-screen ജോലിയും അവയുടെ render-pass switching-ഉം ആയിരുന്നു.
ദൃശ്യ അർത്ഥവും ലഭ്യമായ optimizations-നെ പരിമിതപ്പെടുത്തി. ഓരോ stroke-ന്റെയും scratch, യഥാർത്ഥ compositing order, absolute canvas grain coordinates എന്നിവ ഇഷ്ടം പോലെ നീക്കം ചെയ്യാനായില്ല. ഒരേ നിറവും ഒരേ സാന്ദ്രതയും എന്നത് parameters പൊരുത്തപ്പെടുന്നു എന്നേയുള്ളൂ — overlap ചെയ്യുന്ന ഫലങ്ങൾ merge ചെയ്യാമെന്ന് അത് തെളിയിക്കുന്നില്ല.
യഥാർത്ഥ device-ൽ നിന്നുള്ള പ്രതികരണം ഏറ്റവും കുറഞ്ഞ GPU time ഉള്ള പതിപ്പിനെ നിരസിച്ചു. “മുഴുവൻ canvas blurry ആകുന്നു” എന്ന പരാമർശം measurement മാത്രം പ്രകടിപ്പിക്കാത്ത product constraint വ്യക്തമാക്കുകയായിരുന്നു: Apple Pencil touch-down ചെയ്യുമ്പോൾ ഉപയോക്താക്കൾ നിലവിലുള്ള strokes-ുകളും കാണുകയാണ്.
അന്തിമ പതിപ്പ് പുതിയ interaction cache layer ചേർക്കുന്നില്ല, വ്യക്തത കുറയ്ക്കുകയും ചെയ്യുന്നില്ല. High-zoom vector redraw ഓരോ chalk stroke-ന്റെയും ജോലി അതിന്റെ screen bounding box-ലേക്ക് മാത്രം പരിമിതപ്പെടുത്തുന്നു. LucasPad-ലേക്ക് വീണ്ടും load ചെയ്തശേഷം feedback ഇങ്ങനെയായിരുന്നു: “വളരെ നന്നായി തോന്നുന്നു.”