परिचय
HTML5 कैनवस, Apple के एक प्रयोग के तौर पर शुरू हुआ था. यह वेब पर 2D इमीडिएट मोड ग्राफ़िक्स के लिए सबसे ज़्यादा इस्तेमाल किया जाने वाला स्टैंडर्ड है. अब कई डेवलपर, अलग-अलग तरह के मल्टीमीडिया प्रोजेक्ट, विज़ुअलाइज़ेशन, और गेम के लिए इस पर भरोसा करते हैं. हालांकि, जैसे-जैसे हम ज़्यादा जटिल ऐप्लिकेशन बनाते हैं, वैसे-वैसे डेवलपर को अनजाने में परफ़ॉर्मेंस से जुड़ी समस्याओं का सामना करना पड़ता है. कैनवस की परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के बारे में, कई तरह की जानकारी मौजूद है. इस लेख का मकसद, इस जानकारी को एक जगह इकट्ठा करना है, ताकि डेवलपर इसे आसानी से समझ सकें. इस लेख में, सभी कंप्यूटर ग्राफ़िक्स एनवायरमेंट पर लागू होने वाले बुनियादी ऑप्टिमाइज़ेशन के बारे में बताया गया है. साथ ही, कैनवस से जुड़ी ऐसी तकनीकों के बारे में बताया गया है जिनमें कैनवस के लागू होने के तरीके में सुधार होने पर बदलाव हो सकता है. खास तौर पर, ब्राउज़र वेंडर कैनवस जीपीयू ऐक्सेलिरेशन लागू करते हैं. इसलिए, परफ़ॉर्मेंस को बेहतर बनाने के लिए बताई गई कुछ तकनीकों का असर कम हो सकता है. इसकी जानकारी, ज़रूरत के हिसाब से दी जाएगी. ध्यान दें कि इस लेख में, HTML5 कैनवस के इस्तेमाल के बारे में नहीं बताया गया है. इसके लिए, HTML5Rocks पर कैनवस से जुड़े ये लेख, Dive into HTML5 साइट पर यह चैप्टर या MDN Canvas ट्यूटोरियल देखें.
परफ़ॉर्मेंस की जांच
HTML5 कैनवस की तेज़ी से बदलती दुनिया के साथ तालमेल बनाए रखने के लिए, JSPerf (jsperf.com) टेस्ट से यह पुष्टि की जाती है कि सुझाया गया हर ऑप्टिमाइज़ेशन अब भी काम कर रहा है. JSPerf एक वेब ऐप्लिकेशन है. इसकी मदद से, डेवलपर JavaScript की परफ़ॉर्मेंस की जांच करने के लिए कोड लिख सकते हैं. हर टेस्ट, उस नतीजे पर फ़ोकस करता है जिसे आपको हासिल करना है. जैसे, कैनवस को खाली करना. साथ ही, इसमें एक ही नतीजा हासिल करने के कई तरीके शामिल होते हैं. JSPerf, हर तरीके को कम समयावधि में ज़्यादा से ज़्यादा बार चलाता है. साथ ही, हर सेकंड के हिसाब से, आंकड़ों के हिसाब से काम के इटरेशन की संख्या देता है. ज़्यादा स्कोर हमेशा बेहतर होता है! JSPerf परफ़ॉर्मेंस टेस्ट पेज पर आने वाले लोग, अपने ब्राउज़र पर टेस्ट चला सकते हैं. साथ ही, JSPerf को Browserscope (browserscope.org) पर टेस्ट के सामान्य नतीजे सेव करने की अनुमति दे सकते हैं. इस लेख में ऑप्टिमाइज़ेशन की जो तकनीकें बताई गई हैं वे JSPerf के नतीजे पर आधारित हैं. इसलिए, आपको अप-टू-डेट जानकारी मिल सकती है कि वह तकनीक अब भी लागू होती है या नहीं. मैंने एक छोटा हेल्पर ऐप्लिकेशन बनाया है, जो इन नतीजों को ग्राफ़ के तौर पर रेंडर करता है. ये ग्राफ़ इस लेख में एम्बेड किए गए हैं.
इस लेख में परफ़ॉर्मेंस के सभी नतीजे, ब्राउज़र के वर्शन पर आधारित हैं. यह एक सीमा है, क्योंकि हमें नहीं पता कि ब्राउज़र किस ओएस पर चल रहा था. इससे भी ज़्यादा ज़रूरी बात यह है कि परफ़ॉर्मेंस टेस्ट के दौरान, HTML5 कैनवस को हार्डवेयर से तेज़ी लाने की सुविधा मिली थी या नहीं. पता बार में about:gpu पर जाकर, यह पता लगाया जा सकता है कि Chrome के HTML5 कैनवस में हार्डवेयर से तेज़ी लाने की सुविधा चालू है या नहीं.
ऑफ़-स्क्रीन कैनवस पर प्री-रेंडर करना
अगर आपको कई फ़्रेम में स्क्रीन पर एक जैसे प्रिमिटिव फिर से ड्रॉ करने हैं, तो सीन के बड़े हिस्सों को पहले से रेंडर करके, परफ़ॉर्मेंस को बेहतर बनाया जा सकता है. गेम लिखते समय अक्सर ऐसा होता है. प्री-रेंडरिंग का मतलब है, किसी अलग ऑफ़-स्क्रीन कैनवस (या कैनवस) का इस्तेमाल करके, कुछ समय के लिए इमेज रेंडर करना. इसके बाद, ऑफ़-स्क्रीन कैनवस को वापस दिखने वाले कैनवस पर रेंडर करना. उदाहरण के लिए, मान लें कि आपको मारियो को 60 फ़्रेम प्रति सेकंड की रफ़्तार से दौड़ते हुए फिर से ड्रॉ करना है. इसके लिए, हर फ़्रेम में मारियो की टोपी, मूंछ, और “M” को फिर से ड्रॉ किया जा सकता है या ऐनिमेशन चलाने से पहले मारियो को रेंडर किया जा सकता है. कोई प्री-रेंडरिंग नहीं:
// canvas, context are defined
function render() {
drawMario(context);
requestAnimationFrame(render);
}
प्री-रेंडरिंग:
var m_canvas = document.createElement('canvas');
m_canvas.width = 64;
m_canvas.height = 64;
var m_context = m_canvas.getContext('2d');
drawMario(m_context);
function render() {
context.drawImage(m_canvas, 0, 0);
requestAnimationFrame(render);
}
ध्यान दें कि requestAnimationFrame का इस्तेमाल कैसे किया गया है. इसके बारे में ज़्यादा जानकारी, आगे के सेक्शन में दी गई है.
यह तकनीक खास तौर पर तब असरदार होती है, जब रेंडरिंग ऑपरेशन (ऊपर दिए गए उदाहरण में drawMario) महंगा हो. इसका एक अच्छा उदाहरण है टेक्स्ट रेंडरिंग, जो कि एक बहुत ही महंगा ऑपरेशन है.
हालांकि, “पहले से रेंडर किए गए लूज़” टेस्ट केस की परफ़ॉर्मेंस खराब रही. प्री-रेंडरिंग करते समय, यह पक्का करना ज़रूरी है कि आपका अस्थायी कैनवस, ड्रॉ की जा रही इमेज के आस-पास फ़िट हो. ऐसा न होने पर, ऑफ़-स्क्रीन रेंडरिंग की परफ़ॉर्मेंस में होने वाली बढ़ोतरी, एक बड़े कैनवस को दूसरे कैनवस पर कॉपी करने की वजह से होने वाली परफ़ॉर्मेंस की कमी से कम हो जाती है. यह कमी, सोर्स टारगेट के साइज़ के हिसाब से अलग-अलग होती है. ऊपर दिए गए टेस्ट में, स्नग कैनवस का साइज़ छोटा है:
can2.width = 100;
can2.height = 40;
इसकी तुलना में, ढीले-ढाले तरीके से बनाए गए विज्ञापन की परफ़ॉर्मेंस खराब होती है:
can3.width = 300;
can3.height = 100;
एक साथ कई कैनवस कॉल करना
ड्रॉइंग एक महंगा ऑपरेशन है. इसलिए, ड्रॉइंग स्टेट मशीन को कमांड के लंबे सेट के साथ लोड करना और फिर उन सभी को वीडियो बफ़र में डंप करना ज़्यादा असरदार होता है.
उदाहरण के लिए, कई लाइनें ड्रॉ करते समय, सभी लाइनों के साथ एक पाथ बनाना और उसे एक ही ड्रॉ कॉल से ड्रॉ करना ज़्यादा बेहतर होता है. दूसरे शब्दों में, अलग-अलग लाइनें बनाने के बजाय:
for (var i = 0; i < points.length - 1; i++) {
var p1 = points[i];
var p2 = points[i+1];
context.beginPath();
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
context.stroke();
}
एक पॉलीलाइन बनाने से हमें बेहतर परफ़ॉर्मेंस मिलती है:
context.beginPath();
for (var i = 0; i < points.length - 1; i++) {
var p1 = points[i];
var p2 = points[i+1];
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
}
context.stroke();
यह बात HTML5 कैनवस पर भी लागू होती है. उदाहरण के लिए, कोई मुश्किल पाथ बनाते समय, सेगमेंट को अलग-अलग रेंडर करने के बजाय, सभी पॉइंट को पाथ में डालना बेहतर होता है (jsperf).
हालांकि, ध्यान दें कि Canvas के साथ इस नियम का एक अहम अपवाद है: अगर मनचाहे ऑब्जेक्ट को ड्रॉ करने में शामिल प्रिमिटिव में छोटे बाउंडिंग बॉक्स (उदाहरण के लिए, हॉरिज़ॉन्टल और वर्टिकल लाइन) हैं, तो उन्हें अलग-अलग रेंडर करना ज़्यादा असरदार हो सकता है (jsperf).
कैनवस की स्थिति में बेवजह बदलाव करने से बचें
HTML5 कैनवस एलिमेंट को एक स्टेट मशीन के ऊपर लागू किया जाता है. यह मशीन, फ़िल और स्ट्रोक स्टाइल के साथ-साथ उन पिछले पॉइंट को भी ट्रैक करती है जिनसे मौजूदा पाथ बनता है. ग्राफ़िक्स की परफ़ॉर्मेंस को ऑप्टिमाइज़ करते समय, सिर्फ़ ग्राफ़िक्स रेंडरिंग पर फ़ोकस करना आसान होता है. हालांकि, स्टेट मशीन में बदलाव करने से, परफ़ॉर्मेंस पर भी असर पड़ सकता है. उदाहरण के लिए, अगर किसी सीन को रेंडर करने के लिए कई रंगों का इस्तेमाल किया जाता है, तो कैनवस पर प्लेसमेंट के बजाय रंग के हिसाब से रेंडर करना ज़्यादा किफ़ायती होता है. पिनस्ट्राइप पैटर्न रेंडर करने के लिए, एक स्ट्राइप रेंडर करें, रंग बदलें, अगली स्ट्राइप रेंडर करें वगैरह:
for (var i = 0; i < STRIPES; i++) {
context.fillStyle = (i % 2 ? COLOR1 : COLOR2);
context.fillRect(i * GAP, 0, GAP, 480);
}
इसके अलावा, सभी ऑड स्ट्राइप और फिर सभी इवन स्ट्राइप रेंडर करें:
context.fillStyle = COLOR1;
for (var i = 0; i < STRIPES/2; i++) {
context.fillRect((i*2) * GAP, 0, GAP, 480);
}
context.fillStyle = COLOR2;
for (var i = 0; i < STRIPES/2; i++) {
context.fillRect((i*2+1) * GAP, 0, GAP, 480);
}
जैसा कि उम्मीद थी, इंटरलेस्ड तरीका धीमा है, क्योंकि स्टेट मशीन को बदलना महंगा है.
सिर्फ़ स्क्रीन के अंतर को रेंडर करें, पूरी नई स्थिति को नहीं
जैसा कि उम्मीद की जा सकती है, स्क्रीन पर ज़्यादा रेंडर करने के मुकाबले, कम रेंडर करना ज़्यादा सस्ता होता है. अगर फिर से ड्रॉ करने के बीच सिर्फ़ थोड़ा अंतर है, तो सिर्फ़ अंतर को ड्रॉ करके परफ़ॉर्मेंस को काफ़ी बेहतर बनाया जा सकता है. दूसरे शब्दों में, ड्रॉ करने से पहले पूरी स्क्रीन को साफ़ करने के बजाय:
context.fillRect(0, 0, canvas.width, canvas.height);
ड्रॉ किए गए बाउंडिंग बॉक्स को ट्रैक करें और सिर्फ़ उसे मिटाएं.
context.fillRect(last.x, last.y, last.width, last.height);
अगर आपको कंप्यूटर ग्राफ़िक्स के बारे में जानकारी है, तो आपको इस तकनीक के बारे में भी पता होगा. इसे “रीड्रॉ रीजन” भी कहा जाता है. इसमें, पहले से रेंडर किए गए बाउंडिंग बॉक्स को सेव किया जाता है और फिर हर रेंडरिंग पर उसे मिटा दिया जाता है. यह तकनीक, पिक्सल पर आधारित रेंडरिंग कॉन्टेक्स्ट पर भी लागू होती है. इस JavaScript Nintendo एम्युलेटर टॉक में इसके बारे में बताया गया है.
जटिल सीन के लिए, कई लेयर वाले कैनवस का इस्तेमाल करना
जैसा कि पहले बताया गया है, बड़ी इमेज ड्रॉ करने में ज़्यादा समय लगता है. इसलिए, अगर हो सके, तो ऐसा करने से बचें. प्री-रेंडरिंग सेक्शन में बताए गए तरीके के मुताबिक, ऑफ़ स्क्रीन रेंडरिंग के लिए किसी दूसरे कैनवस का इस्तेमाल करने के अलावा, एक-दूसरे के ऊपर लेयर किए गए कैनवस का भी इस्तेमाल किया जा सकता है. फ़ोरग्राउंड कैनवस में पारदर्शिता का इस्तेमाल करके, हम रेंडर होने के समय ऐल्फ़ा को एक साथ कंपोज़िट करने के लिए, जीपीयू पर भरोसा कर सकते हैं. इसे इस तरह सेट अप किया जा सकता है. इसमें दो कैनवस को एक के ऊपर एक रखा जाता है.
<canvas id="bg" width="640" height="480" style="position: absolute; z-index: 0">
</canvas>
<canvas id="fg" width="640" height="480" style="position: absolute; z-index: 1">
</canvas>
यहां सिर्फ़ एक कैनवस होने का फ़ायदा यह है कि जब हम फ़ोरग्राउंड कैनवस को ड्रॉ या क्लियर करते हैं, तो हम बैकग्राउंड में कभी भी बदलाव नहीं करते. अगर आपके गेम या मल्टीमीडिया ऐप्लिकेशन को फ़ोरग्राउंड और बैकग्राउंड में बांटा जा सकता है, तो इन्हें अलग-अलग कैनवस पर रेंडर करें. इससे परफ़ॉर्मेंस में काफ़ी सुधार होगा.
अक्सर, इंसान की नज़र की कमियों का फ़ायदा उठाया जा सकता है. इसके लिए, बैकग्राउंड को सिर्फ़ एक बार या फ़ोरग्राउंड की तुलना में कम स्पीड से रेंडर करें. फ़ोरग्राउंड पर ही, आपके उपयोगकर्ता का ज़्यादा ध्यान होता है. उदाहरण के लिए, हर बार रेंडर करते समय फ़ोरग्राउंड को रेंडर किया जा सकता है, लेकिन बैकग्राउंड को सिर्फ़ हर Nवें फ़्रेम पर रेंडर किया जा सकता है. यह भी ध्यान रखें कि अगर आपका ऐप्लिकेशन इस तरह के स्ट्रक्चर के साथ बेहतर तरीके से काम करता है, तो यह तरीका किसी भी संख्या में कंपोज़िट कैनवस के लिए बेहतर तरीके से काम करता है.
shadowBlur से बचें
अन्य ग्राफ़िक एनवायरमेंट की तरह, HTML5 कैनवस की मदद से डेवलपर प्रिमिटिव को धुंधला कर सकते हैं. हालांकि, यह प्रोसेस काफ़ी महंगी हो सकती है:
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = 'rgba(255, 0, 0, 0.5)';
context.fillRect(20, 20, 150, 100);
कैनवस को खाली करने के अलग-अलग तरीके जानना
HTML5 कैनवस, इमीडिएट मोड ड्रॉइंग पैराडाइम है. इसलिए, हर फ़्रेम पर सीन को फिर से ड्रॉ करना ज़रूरी है. इस वजह से, HTML5 Canvas ऐप्लिकेशन और गेम के लिए, कैनवस को मिटाना एक अहम काम है.
कैनवस की स्थिति में बदलाव करने से बचें सेक्शन में बताया गया है कि पूरे कैनवस को मिटाना अक्सर सही नहीं होता. हालांकि, अगर आपको ऐसा करना ही है, तो आपके पास दो विकल्प हैं: context.clearRect(0, 0, width, height) को कॉल करना या कैनवस के लिए खास हैक का इस्तेमाल करना: canvas.width = canvas.width;.यह लेख लिखे जाने के समय, clearRect आम तौर पर चौड़ाई रीसेट करने वाले वर्शन से बेहतर परफ़ॉर्म करता है. हालांकि, कुछ मामलों में canvas.width रीसेट करने वाले हैक का इस्तेमाल करना, Chrome 14 में काफ़ी तेज़ होता है
इस सलाह का इस्तेमाल सावधानी से करें, क्योंकि यह कैनवस के लागू होने के तरीके पर निर्भर करती है. साथ ही, इसमें बदलाव भी हो सकता है. ज़्यादा जानकारी के लिए, कैनवस को मिटाने के बारे में साइमन सैरिस का लेख पढ़ें.
फ़्लोटिंग पॉइंट कोऑर्डिनेट का इस्तेमाल करने से बचें
HTML5 कैनवस, सब-पिक्सल रेंडरिंग के साथ काम करता है. इसे बंद करने का कोई तरीका नहीं है. अगर आपने ऐसे निर्देशांकों के साथ ड्रॉ किया है जो पूर्णांक नहीं हैं, तो यह लाइनों को स्मूद बनाने के लिए, अपने-आप एंटी-एलियासिंग का इस्तेमाल करता है. यहां विज़ुअल इफ़ेक्ट दिया गया है. इसे सेब ली-डेलिसल के सब-पिक्सल कैनवस की परफ़ॉर्मेंस वाले इस लेख से लिया गया है:
अगर आपको स्मूद स्प्राइट इफ़ेक्ट नहीं चाहिए, तो Math.floor या Math.round (jsperf) का इस्तेमाल करके, निर्देशांकों को पूर्णांकों में तेज़ी से बदला जा सकता है:
अपने फ़्लोटिंग पॉइंट कोऑर्डिनेट को पूर्णांकों में बदलने के लिए, कई बेहतरीन तकनीकों का इस्तेमाल किया जा सकता है. इनमें से सबसे बेहतर तकनीक में, टारगेट नंबर में आधा जोड़ना और फिर फ़्रैक्शनल पार्ट को हटाने के लिए, नतीजे पर बिटवाइज़ ऑपरेशन करना शामिल है.
// With a bitwise or.
rounded = (0.5 + somenum) | 0;
// A double bitwise not.
rounded = ~~ (0.5 + somenum);
// Finally, a left bitwise shift.
rounded = (0.5 + somenum) << 0;
परफ़ॉर्मेंस की पूरी जानकारी यहां दी गई है (jsperf).
ध्यान दें कि कैनवस लागू करने के बाद, इस तरह के ऑप्टिमाइज़ेशन की ज़रूरत नहीं होगी. ऐसा इसलिए, क्योंकि जीपीयू की मदद से, नॉन-इंटिजर कोऑर्डिनेट को तेज़ी से रेंडर किया जा सकेगा.
requestAnimationFrame की मदद से अपने ऐनिमेशन को ऑप्टिमाइज़ करना
ब्राउज़र में इंटरैक्टिव ऐप्लिकेशन लागू करने के लिए, नए requestAnimationFrame एपीआई का इस्तेमाल करने का सुझाव दिया जाता है. ब्राउज़र को किसी खास टिक रेट पर रेंडर करने का निर्देश देने के बजाय, ब्राउज़र से अपने रेंडरिंग रूटीन को कॉल करने के लिए कहें. साथ ही, ब्राउज़र उपलब्ध होने पर कॉल करें. इसका एक और फ़ायदा यह है कि अगर पेज फ़ोरग्राउंड में नहीं है, तो ब्राउज़र उसे रेंडर नहीं करता.
requestAnimationFrame कॉलबैक का मकसद, 60 FPS (फ़्रेम प्रति सेकंड) की कॉलबैक दर हासिल करना है. हालांकि, इसकी कोई गारंटी नहीं है. इसलिए, आपको यह ट्रैक करना होगा कि आखिरी रेंडर के बाद से कितना समय बीत चुका है. यह कुछ ऐसा दिख सकता है:
var x = 100;
var y = 100;
var lastRender = Date.now();
function render() {
var delta = Date.now() - lastRender;
x += delta;
y += delta;
context.fillRect(x, y, W, H);
requestAnimationFrame(render);
}
render();
ध्यान दें कि requestAnimationFrame का यह इस्तेमाल, कैनवस के साथ-साथ रेंडरिंग की अन्य टेक्नोलॉजी पर भी लागू होता है, जैसे कि WebGL.
फ़िलहाल, यह एपीआई सिर्फ़ Chrome, Safari, और Firefox में उपलब्ध है. इसलिए, आपको इस शिम का इस्तेमाल करना चाहिए.
ज़्यादातर मोबाइल कैनवस को लागू करने की प्रोसेस धीमी होती है
चलिए, मोबाइल के बारे में बात करते हैं. माफ़ करें, इस लेख को लिखे जाने के समय, सिर्फ़ iOS 5.0 बीटा वर्शन पर चलने वाले Safari 5.1 में जीपीयू की मदद से मोबाइल कैनवस को तेज़ी से लोड करने की सुविधा उपलब्ध है. जीपीयू ऐक्सेलरेटर के बिना, मोबाइल ब्राउज़र में आम तौर पर इतने पावरफ़ुल सीपीयू नहीं होते कि वे कैनवस पर आधारित आधुनिक ऐप्लिकेशन चला सकें. ऊपर बताए गए कई JSPerf टेस्ट, डेस्कटॉप की तुलना में मोबाइल पर काफ़ी खराब परफ़ॉर्म करते हैं. इससे, अलग-अलग डिवाइसों पर काम करने वाले ऐप्लिकेशन को सही तरीके से चलाने में काफ़ी रुकावट आती है.
नतीजा
इस लेख में, ऑप्टिमाइज़ेशन की कई काम की तकनीकों के बारे में बताया गया है. इनकी मदद से, HTML5 कैनवस पर आधारित बेहतर परफ़ॉर्मेंस वाले प्रोजेक्ट बनाए जा सकते हैं. अब जब आपने यहां कुछ नया सीखा है, तो आगे बढ़ें और अपने शानदार वीडियो को ऑप्टिमाइज़ करें. अगर आपके पास ऑप्टिमाइज़ करने के लिए कोई गेम या ऐप्लिकेशन नहीं है, तो प्रेरणा लेने के लिए Chrome Experiments और Creative JS देखें.
रेफ़रंस
- Immediate मोड बनाम retained मोड.
- HTML5Rocks के अन्य कैनवस लेख.
- Dive into HTML5 का Canvas सेक्शन.
- JSPerf की मदद से, डेवलपर JS की परफ़ॉर्मेंस की जांच कर सकते हैं.
- Browserscope, ब्राउज़र की परफ़ॉर्मेंस का डेटा सेव करता है.
- JSPerfView, जो JSPerf टेस्ट को चार्ट के तौर पर रेंडर करता है.
- कैनवस को खाली करने के बारे में साइमन की ब्लॉग पोस्ट और उनकी किताब, HTML5 Unleashed. इस किताब में कैनवस की परफ़ॉर्मेंस के बारे में चैप्टर शामिल हैं.
- सब-पिक्सल रेंडरिंग की परफ़ॉर्मेंस के बारे में सेबेस्टियन की ब्लॉग पोस्ट.
- जेएस एनईएस एम्युलेटर को ऑप्टिमाइज़ करने के बारे में बेन की बातचीत.
- Chrome DevTools में नया कैनवस प्रोफ़ाइलर.