Kimi K3తో Lulucat Notes రెండరింగ్ పైప్లైన్ను పునర్నిర్మించడం
మా Core Graphics టైల్ పైప్లైన్ ఆరు చిన్న దశల్లో Metal point-sprite పైప్లైన్గా మారింది; ప్రతి దశను నిజమైన iPadపై ధృవీకరించాం. ఈ కోడ్ను Fireworksపై Kimi K3తో జంటగా ప్రోగ్రామ్ చేశాం.
గత వారం, Lulucat Notesలో lasso selectionను లాగితే కనిపించే అల ఏర్పడేది: అదే frameలో కొన్ని screen tiles selectionను కొత్త స్థానంలో చూపిస్తే, మరికొన్ని ఇంకా పాత స్థానాన్నే చూపించేవి. మొత్తం rendering pipelineను — Core Graphics bitmapతో పాటు CATiledLayerను — Metalతో భర్తీ చేశాం; ఈ మార్పును ఆరు చిన్న దశల్లో చేశాం, తదుపరి దశ మొదలయ్యే ముందు ప్రతి దశను నిజమైన iPadపై ధృవీకరించాం.
Fireworksపై నడిచే Moonshot open model అయిన Kimi K3తో ఈ కోడ్ను pair-program చేశాం. మనిషి నడిపాడు, నిర్ణయాలు తీసుకున్నాడు, పరీక్షించాడు; model దాదాపు ప్రతి lineను రాసింది.
పైప్లైన్
పాత pipelineలో క్రమంగా ఒకదానికొకటి దూరమైన రెండు drawing విధానాలు ఉండేవి: తక్కువ ఖర్చుతో display చేయడానికి strokesను bitmapలో bake చేసేవారు; tilesకు మరింత detail అవసరమైనప్పుడల్లా వాటిని vectorsగా మళ్లీ గీసేవారు. కొత్త pipelineలో ఖచ్చితంగా ఒకే ఆలోచన ఉంది: inkలాంటిదంతా ఒక point sprite. ఒక pen stroke, ఒక highlighter sweep, ఒక eraser dab — ఇవన్నీ ఒకే 32-byte vertex — position, diameter, color —; stroke యొక్క arc length వెంట ఒక point దూరంలో అమర్చిన GPU-rasterized circlesగా ఒకే shader pairతో గీయబడతాయి.
![]()
ఒక point అంతరం వద్ద వృత్తాల గొలుసు గణితపరంగా పరిపూర్ణమైన capsule నుంచి సుమారు 0.075 points మేరకు తప్పుతుంది — మా canvas density వద్ద ఒక pixelలో ఐదవ వంతు. దానికి బదులుగా, మూడు tools ఒకే code pathగా కలిసిపోతాయి, GPU తనకు బాగా వచ్చిన పని చేస్తుంది.
ఆ ఆలోచన చుట్టూ architecture సరళంగా ఉంటుంది. Committed ink ఒకే 4096² textureలో ఉంటుంది. UIScrollView మిగిలి ఉంటుంది, కానీ pure gesture engineగా మాత్రమే తగ్గించబడుతుంది: దాని contentOffset మరియు zoomScale ప్రతి frameలో ఒక viewport uniformకు feed అవుతాయి, కాబట్టి panning, zooming ఏమీ రాయవు. ప్రతి frameలో ఐదు draws ఉంటాయి:
flowchart TB
subgraph frame["Every frame: five draws"]
direction TB
paper["1 · paper blit"] --> ink["2 · committed ink"]
ink --> live["3 · live stroke"]
live --> sel["4 · selection"]
sel --> dash["5 · lasso dashes"]
end
commit["stroke commit<br/>append stamps"] --> tex[("ink texture<br/>4096² render target")]
replay["regional replay<br/>erase · delete · move · undo"] --> tex
tex -. "sampled or re-drawn" .-> ink
Edits textureలోకి నేరుగా రాస్తాయి. ఒక strokeను commit చేయడం దాని stampsను append చేస్తుంది. Erasing, deleting, moving, undoing ఒక scissor rectangle వెనుక ప్రభావిత regionను replay చేస్తాయి: ఆ regionను clear చేయండి, దానిని intersect చేసే strokesను మళ్లీ గీయండి, పూర్తయింది. Partial erasure మునుపటి పోస్ట్లోని ownership semanticsను ఉంచుతుంది — ఒక erasure అది inkను తొలగించే strokeకే చెందుతుంది — ప్రతి erased strokeను scratch textureలో గీసి, దాని స్వంత erasure pathsను destination-out blendingతో (
ఇవన్నీ మొదలైన selection ఇప్పుడు point spritesగానూ గీయబడుతుంది. దాన్ని లాగడం ఒక uniform offsetను మాత్రమే update చేస్తుంది. Zero texture writes, zero tile invalidations — ఆ అల నిర్మాణపరంగా పోయింది, కేవలం తగ్గించబడలేదు.
గేట్కీపర్: ఒక pixel diff
మేము Core Graphics rendererను తొలగించలేదు. దాన్ని offline reference implementationగా తగ్గించాం; deviceపై capture చేసిన నిజమైన stroke dataపై ప్రతి Metal మార్పు దానితో చేసే pixel comparisonలో పాస్ కావాలి. Acceptance criterion “identical pixels” కాదు — రెండు సరైన rasterizers anti-aliased edges వెంట కొన్ని gray levelsలో న్యాయంగా భిన్నంగా ఉండవచ్చు. ఈ gate నిర్మాణాత్మకమైనది: missing ink లేదు, offset లేదు, color drift లేదు, inkకు దూరంగా ఎక్కడా పెద్ద తేడా లేదు.
![]()
Offline rendererను నిర్మించేటప్పుడు ఎదురైన ఐదు bugsలో నాలుగింటిని ఈ harness పట్టుకుంది; outputను pixel levelకు crop చేసి అన్నింటినీ నిర్ధారించాం: Swift/Metal struct stride mismatch (28 bytes versus 32, ఎందుకంటే Metal float4ను 16కు align చేస్తుంది — screen మొత్తం color blocksతో నిండిపోయింది), [[point_size]]ను fragment shaderలో varyingగా చదవలేకపోవడం (ప్రతి stamp squareగా వచ్చింది), ఒకే command bufferపై రెండు render encoders కలిసి ఉండటం (అంతా black), మరియు వేగమైన strokes మొదటి millimeter కనిపించకుండా చేసిన missing start dab.
ఆరు దశలు, ఒకే rewrite కాదు
Migration plan ఆరు independently shippable దశలను కలిగి ఉంది: pixel diff పాస్ చేసే offline renderer; zero visual changeతో display shell; GPUపై live stroke; GPUపై selection; textureలోకి నేరుగా రాసే mutations; high-zoom vector re-drawing (“zero visual change” దాన్ని కోరినందున రెండో దశలో కలిపారు). ప్రతి దశ డెస్క్పై ఉన్న iPad Proలో ఒక మనిషి రాయడం, తుడవడం, zoom చేయడం, drag చేయడం ద్వారా పరీక్షించడంతో ముగిసింది — simulator కాదు, screenshot diff కాదు.
ప్రతి automated check తప్పించిన మూడు bugsను device పట్టుకుంది. 100% zoomకు మించి strokes రెండుసార్లు గీయబడ్డాయి — కింద soft texture, పైన sharp sprites — మనిషి కొన్ని secondsలో గుర్తించిన మసక blurలా కనిపించాయి. ఒక strokeను commit చేస్తే ఒక frame పాటు flicker అయ్యేది, ఎందుకంటే పాత overlay texture updateతో sync లేకుండా cross-fade అయి బయటకు వెళ్లేది. 170% zoomకు మించి ప్రతి note మాయమైంది: visibility-culling rectangle తన scaled coordinate spaceలో contentOffsetను ఉపయోగించింది, కాబట్టి zoom చేస్తూ ఉంటే strokes నుంచి దూరంగా జారిపోయేది. ఈ మూడింటికీ ఒక line నుంచి ఒక function వరకు ఉండే fixes చాలు; మేము ముందుగా రాయగలిగే ఏ testలోనూ అవి లేవు, ఎందుకంటే వాటి కోసం చూడాలని మాకు తెలియదు. UI-first consumer appలో మనిషిని loopలో ఉంచడానికి కారణం ఇదే.
Kimi K3తో పనిచేయడం ఎలా ఉంటుంది
మొదటిది, వేగం. “discuss, write, build, install, look” loop నిమిషాల్లో నడిచింది; త్వరగా సమాధానం చెప్పే model ఒక రోజులో మీరు భరించగల loopల సంఖ్యను మార్చుతుంది.
రెండవది, అది over-engineer చేయదు. ఈ codebase స్పష్టమైన house rulesపై నడుస్తుంది — launchకు ముందు backward-compatibility scaffolding లేదు, ఏ device అవసరమని నిరూపించినప్పుడే complexity — మరియు K3 గుర్తు చేయకుండానే వాటిని పాటిస్తుంది. అది “తర్వాత కోసం” spatial indexes జోడించలేదు, ప్రతి callను defensive checksలో చుట్టలేదు, speculativeగా abstract చేయలేదు. దానికి prompt చేయడం, house rules చదివి వాటిని నిజంగా నమ్మే సమర్థుడైన సహోద్యోగితో పని చేయడం లాంటిది.
మూడవది, tools ఇస్తే అవి ఉత్సాహంగా ఉపయోగిస్తుంది. మేము image utilitiesను కలిపాం — view, pixel regionకు crop, resize — మరియు పై ఐదు harness bugsను diagnose చేయడానికి model తన renderer outputను proactiveగా crop చేయడం ప్రారంభించింది. Tool ఉండటం దానికి చూడాలని గుర్తు చేసింది.
మరో వైపు: notesను మాయం చేసిన coordinate-space bugతో సహా, ఈ కథలోని ఎక్కువ bugsను K3 రాసింది. దాని పరిమితులు నిజమైనవి. పనిని సురక్షితంగా చేసింది model ఎప్పుడూ సరైనదిగా ఉండటం కాదు; harness rendering driftను పట్టుకోవడం, మనిషి feelను పట్టుకోవడం. అయినా రోజువారీగా మేము ఉపయోగించే frontier closed models — Opus-class systems — నుంచి దాన్ని నమ్మకంగా వేరు చేయలేకపోయాను. కొన్ని అంశాల్లో అది స్పష్టంగా మెరుగ్గా ఉంది: వేగంగా, అలాగే codebaseను defensive designతో నింపే మొగ్గు చాలా తక్కువగా.
ఇక నుంచి మేము ఎలా నిర్మించాలనుకుంటున్నాము
మేము big-spec agentic developmentను ముగించాం — modelకు పెద్ద specification ఇచ్చి, దాని నుంచి వచ్చినదాన్ని ఏదైనా అంగీకరించే శైలి. Failure mode చెడు code కాదు; ఎవరూ అర్థం చేసుకోలేని code.
ఇక్కడ పనిచేసింది, మేము కొనసాగించేది: చిన్న దశలు; ప్రతి దశ ప్రారంభానికి ముందు చర్చించబడుతుంది, build చేయడానికి ముందు మనిషికి అర్థమవుతుంది, అది ఉండబోయే deviceపై ధృవీకరించబడుతుంది. Model పని వేగంగా, ఖచ్చితంగా ఉండటం మరియు uncertainty గురించి నిజాయితీగా ఉండటం. మనిషి పని judgment, taste, e2e verification — ముఖ్యంగా UI-first consumer software కోసం, ఎందుకంటే “సరైనది” ఎలా అనిపిస్తుందో specification వివరించలేడు. Fireworksపై Kimi K3 ఈ loopకు సరిగ్గా సరిపోయేలా ఉంది: loopను బిగుతుగా ఉంచేంత వేగంగా, దశలను చిన్నగా, శుభ్రంగా ఉంచేంత తెలివిగా.
ఆ అల పోయింది, pipeline రెండు ఆలోచనలకు బదులుగా ఒకే ఆలోచనగా మారింది, మమ్మల్ని అక్కడికి తీసుకువచ్చిన process అలాగే కొనసాగుతుంది.