2 पॉइंट द्वारा GN⁺ 2024-02-15 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • M1 कई वर्षों तक OpenGL 4.1 पर अटका रहा, लेकिन Fedora Asahi Remix के नवीनतम M1/M2 ड्राइवर अब OpenGL 4.6 और OpenGL ES 3.2 को पूरी तरह सपोर्ट करते हैं
  • Asahi का open source Linux ड्राइवर vendor के non-conformant 4.1 ड्राइवर के विपरीत Khronos सूची में दर्ज conformant driver है, जो Blender जैसे आधुनिक OpenGL workloads के साथ compatibility बढ़ाता है
  • OpenGL 4.6 में robustness, SPIR-V, clip control, cull distance, compute shaders और बेहतर transform feedback की जरूरत होती है, लेकिन M1 hardware आधुनिक graphics standards से सीधे मेल नहीं खाता
  • hardware में मौजूद न होने वाले features को driver और compiler तकनीकों से पूरा किया गया: geometry shaders, tessellation, transform feedback को compute shaders से, और cull distance व clip control को shader transformations से संभाला गया
  • 100,000 से अधिक conformance tests पास करने के लिए buffer और image robustness को software में implement किया गया, और clamp, preamble optimization, तथा mipmap workaround से अतिरिक्त लागत घटाई गई

Fedora Asahi Remix में OpenGL 4.6/ES 3.2 उपलब्ध

  • M1 अब तक सिर्फ OpenGL 4.1 तक सपोर्ट करता था, लेकिन अब OpenGL 4.6 और OpenGL ES 3.2 को सपोर्ट करता है
  • नवीनतम M1/M2 series ड्राइवर Fedora Asahi Remix इंस्टॉल करके इस्तेमाल किए जा सकते हैं
  • जिन उपयोगकर्ताओं ने इसे पहले से इंस्टॉल किया है, वे इस कमांड से अपडेट करें
    • dnf upgrade --refresh
  • Asahi का open source Mesa driver vendor के non-conformant 4.1 ड्राइवर के विपरीत नवीनतम OpenGL versions के लिए conformance रखता है
  • conformant 4.6/3.2 ड्राइवर को correctness सुनिश्चित करने के लिए 100,000 से अधिक tests पास करने होते हैं
    • Khronos की आधिकारिक सूची में Asahi का OpenGL 4.6 और OpenGL ES 3.2 शामिल हैं
  • 6 महीने पहले OpenGL ES 3.1 ड्राइवर के साथ M1 के standard graphics API के लिए पहला conformant driver आया था, और अब OpenGL 4.6 भी पूरा हो गया है
  • Vulkan सपोर्ट पर भी काम जारी है

OpenGL 4.1 की बाधा पार करने के लिए feature implementation

  • OpenGL 4.6, 4.1 की तुलना में कई अनिवार्य features जोड़ता है
    • Robustness
    • SPIR-V
    • Clip control
    • Cull distance
    • Compute shaders
    • upgraded transform feedback
  • M1, OpenGL ES 3.1 के बाद के graphics standards के लिए बहुत उपयुक्त नहीं है
    • Vulkan कुछ features को optional बनाता है, लेकिन उसके ऊपर DirectX और OpenGL को layer करने के लिए missing features की जरूरत पड़ती है
    • M1 का मौजूदा समाधान OpenGL 4.1 feature set से आगे नहीं बढ़ पाता था
  • जिन नए features के लिए hardware support नहीं है, उन्हें driver techniques से implement किया गया
    • Geometry shaders, tessellation, और transform feedback को compute shaders से संभाला गया
    • Cull distance को transformed interpolants से संभाला गया
    • Clip control को vertex shader epilogue में implement किया गया

Buffer robustness और M1 का software correction

  • GPU पारंपरिक रूप से safety की बजाय performance को प्राथमिकता देता है, इसलिए shader अगर buffer सीमा के बाहर पढ़े तो गलत code में undefined behavior हो सकता है
  • web browser जैसे applications, जो untrusted shaders को संभालते हैं, उनके लिए यह समझौता उचित नहीं है
    • graphics API स्वयं security boundary नहीं है, इसलिए कुछ sanitization जरूरी है
    • API के undefined behavior को कम करना defense in depth में मदद करता है
  • Robustness चालू होने पर application कुछ performance छोड़कर out-of-bounds access के लिए defined behavior चुन सकता है
  • अलग-अलग APIs में out-of-bounds buffer load का परिणाम अलग होता है
    • Direct3D और Vulkan robustBufferAccess2: 0 लौटाते हैं
    • OpenGL और Vulkan robustBufferAccess: 0 या buffer के अंदर का कुछ data लौटाते हैं
    • OpenGL ES: मनमाना मान संभव है, लेकिन crash नहीं होना चाहिए
  • OpenGL की requirement यह है कि out-of-bounds access पर 0 या buffer के भीतर का data लौटे, इसलिए आखिरी valid index और access index का unsigned minimum निकालकर safe index से load किया जा सकता है
    • robustness के बिना uniform buffer load: load.i32 result, buffer, index
    • robustness लागू करने के बाद: umin idx, index, last के बाद load.i32 result, buffer, idx
  • M1 का preamble सभी threads में एक ही value बार-बार calculate करने के बजाय उसे एक बार calculate करके reuse करता है
    • uniform buffer size fixed होती है, इसलिए अतिरिक्त robustness arithmetic को भी preamble में ले जाया जा सकता है
    • robust storage buffer में भले ही load/store खुद move न हो सके, clamp calculation को preamble में ले जाया जा सकता है

Vertex buffer robustness implementation

  • graphics API में application vertex buffer का GPU base address और attribute layout सेट करता है
    • हर attribute का एक offset और format होता है
    • buffer में stride होता है, जो प्रति vertex bytes की संख्या बताता है
    • vertex shader vertex के आधार पर implicit indexing से attribute पढ़ता है
  • कुछ hardware robust vertex fetch को native रूप से implement करते हैं या bounds-checked buffer के जरिए software fetch को तेज करते हैं, लेकिन M1 में दोनों नहीं हैं
  • M1 GPU memory load 64-bit base address और element-unit offset लेता है, और imad integer multiply-add instruction भी देता है
    • 32-bit attribute load को imad idx, stride/4, vertex, offset/4 और load.i32 result, base, idx इन दो instructions से implement किया जा सकता है
    • 4 tightly packed 32-bit values वाला vector attribute load.v4i32 result, base, vertex << 2 एक instruction में load किया जा सकता है
  • Robustness के लिए clamp जरूरी है, लेकिन vertex buffer size bytes में होती है जबकि optimized load vertex-unit index का इस्तेमाल करता है
  • इस समस्या को एक buffer के भीतर कई attributes और offsets को per-attribute अलग base address की तरह reinterpret करके हल किया गया
    • shader में offset जोड़ने के बजाय per-attribute base pass किया जाता है
    • byte-unit buffer size को हर attribute के लिए vertex-unit size में बदला जा सकता है
    • offset की जगह vertex index को clamp किया जाता है
  • driver हर attribute format के size का उपयोग करके आखिरी valid vertex index पहले से calculate करता है और shader को देता है
  • अगर buffer इतना छोटा हो कि कुछ भी load न किया जा सके, तो clamp से समस्या हल नहीं होती; ऐसे में उस attribute के buffer को छोटे zero buffer से बदल दिया जाता है
    • per-attribute base address इस्तेमाल होने के कारण यह निर्णय भी per-attribute लिया जा सकता है
  • अंत में, थोड़ी driver-side calculation और एक umin की लागत पर robust vertex buffer implement किया गया

Image robustness और mipmap workaround

  • Buffer robustness के अलावा image robustness भी जरूरी है, और out-of-bounds image load को 0 लौटाना चाहिए
  • mipmapped image में कई levels of detail शामिल होते हैं
    • base level मूल image होता है
    • हर अगला level पिछले level का छोटा किया हुआ image होता है
    • rendering के दौरान hardware screen size के करीब वाले level को चुनकर efficiency और visual quality बेहतर करता है
  • specification के अनुसार robustness में image load को इन स्थितियों में 0 लौटाना चाहिए
    • जब X या Y coordinate सीमा से बाहर हो
    • जब level सीमा से बाहर हो
  • M1 GPU का image load behavior इन requirements से अलग है
    • X या Y coordinate सीमा से बाहर हो तो 0 लौटाता है
    • level सीमा से बाहर हो तो आखिरी level का मान लौटाता है
  • vendor ने hardware documentation सार्वजनिक नहीं की है, इसलिए यह behavior इरादतन है या hardware bug, यह स्पष्ट नहीं है; conformance पास करने के लिए workaround जरूरी था
  • सीधा workaround यह है कि level valid होने पर ही load किया जाए, नहीं तो 0 लौटाया जाए, लेकिन branch अप्रभावी है
  • बेहतर तरीका यह है कि out-of-bounds level पर भी load crash नहीं करता, इसका फायदा उठाकर पहले load किया जाए और फिर compare-select से 0 चुना जाए
    • लेकिन M1 GPU का instruction set scalar है, जबकि image load red/green/blue/alpha वाले 4-component vector लौटाता है
    • हर component के लिए ulesel चाहिए, जिससे assembly बड़ी हो जाती है
  • अंतिम workaround इस तथ्य का उपयोग करता है कि X या Y सीमा से बाहर होने पर hardware 0 लौटाता है
    • अधिकतम image width 16384px है, इसलिए X को 20000 जैसे मान पर बदलने से वह सीमा से बाहर हो जाता है
    • अगर level valid है तो मूल X इस्तेमाल किया जाता है, और अगर valid नहीं है तो X को 20000 बनाकर image load से 0 लौटवाया जाता है
  • यह तरीका पूरे vector को select करने के बजाय सिर्फ एक scalar बदलता है, इसलिए compact assembly में compile होता है
    • अगर constant को uniform register में पहले से लोड कर दिया जाए, तो workaround की लागत सिर्फ एक instruction रह जाती है
    • इसी तरीके से conformance पास किया गया

1 टिप्पणियां

 
GN⁺ 2024-02-15
Hacker News की राय
  • Alyssa Rosenzweig कम्युनिटी में लगातार योगदान देने वाला एक बड़ा तोहफा जैसी लगती हैं
    हर ब्लॉग पोस्ट में आधुनिक graphics hardware की अंदरूनी संरचना के बारे में कुछ न कुछ ऐसा सीखने को मिल ही जाता है जो पहले नहीं पता था

  • यह काम दिखाता है कि बातें नहीं, काबिलियत हर बार जीतती है
    सिर्फ ब्लॉग पढ़ने भर से दिमाग गरम हो जाए, इतना कुछ खोलकर समझने लायक होता है; और निष्कर्ष आखिरी वाक्य में नहीं बल्कि दूसरे वाक्य में होने के बावजूद, अंत में आप हर bit manipulation का पीछा करते हुए rabbit hole में उतर ही जाते हैं
    अगर प्रति पैराग्राफ सीख की संख्या जैसा कोई benchmark हो, तो Alyssa शायद हर जगह पहले नंबर पर होंगी

  • अगर किसी दिन Apple OpenGL 3.3 core को हटा दे, तो शायद अंततः बाकी सब भी उसे हटाने लगें
    आम तौर पर सुना है कि OpenGL, Vulkan से इस्तेमाल में आसान है, लेकिन बहुत जटिल API कम अनुभव वाले developers के लिए GPU का उपयोग कठिन बना सकती है और entry barrier बनकर indie game developers को बाहर धकेल सकती है
    आजकल सब Unity और Unreal इस्तेमाल करते हैं, इसलिए शून्य से बनाना या कोई और engine इस्तेमाल करना अजीब लगता है; Unity के और ज्यादा lock-in की कोशिश के बाद game development जगत को जागते देखना दिलचस्प है, लेकिन झुंझलाहट भी होती है
    game development में open source हमेशा तंग हालत में रहा है, और Godot मौजूद होने के बावजूद Unity और Unreal को गंभीर चुनौती देना मुश्किल दिखता है
    Godot पर्याप्त रूप से सक्षम हो भी, तो भी indie developers Unity और Unreal से ज्यादा परिचित हैं, इसलिए उनके वहीं बने रहने की संभावना ज्यादा है
    game development में open source की स्थिति कभी-कभी निराशाजनक लगती है, और अगली पीढ़ी के graphics API का आना चीजों को आसान नहीं बनाता

    • यह कहना काफी वाजिब है कि OpenGL, Vulkan से आसान है
      OpenGL triangle rendering example करीब 200 lines का है, और Vulkan triangle rendering example करीब 1000 lines का
    • निजी तौर पर मुझे Metal, Vulkan से इस्तेमाल में आसान लगता है
      Vulkan को बहुत flexible तरह से design किया गया है, लेकिन उसमें convenience features ज्यादा नहीं हैं
      किसी भी स्थिति में, OpenGL driver के direct API के रूप में expose करने के लिए बहुत high-level था; GPU hardware जिस तरह काम करता है, उसके हिसाब से Vulkan जैसी low-level API को base layer बनाकर उसके ऊपर OpenGL जैसी चीज रखना बेहतर मेल खाता है
      साथ ही ऐसा भी नहीं कि हर कोई Unity और Unreal ही इस्तेमाल करता है
      The Game Awards 2023 में Game of the Year के 6 nominee सभी अपने in-house engines से बने थे, और indie scene में भी Hades जैसे अपने engine बनाने वाले developers अब भी हैं
      हालांकि बहुसंख्यक लोग तैयार engines इस्तेमाल करते हैं, यह सही है
    • OpenGL खत्म नहीं किया गया है, बल्कि यह ज्यादा सरल है और जहां Vulkan जरूरत से ज्यादा भारी पड़ता है, वहां अब भी इस्तेमाल होता है
      अगर यह आपकी सभी जरूरी सुविधाएं पूरी करता है और आप state-based rendering pipeline स्वीकार कर सकते हैं, तो नए project में इसे इस्तेमाल करना भी अच्छा विकल्प है
    • macOS और iOS पर OpenGL कई सालों से deprecated स्थिति में है
      यह अभी भी चलता है और आजकल Metal के ऊपर एक layer के रूप में run होता है, लेकिन macOS या iOS के लिए GL code build करने पर deprecation warnings लगातार आते हैं
      define से इन्हें बंद किया जा सकता है
    • WGPU इस समस्या को Vulkan से आसान cross-platform API के रूप में हल करने की कोशिश के ज्यादा करीब है
      OpenGL की समस्या यह है कि यह GPU के काम करने के तरीके से बहुत दूर है, इसलिए अच्छा performance निकालना मुश्किल होता है
  • सोचता हूं कि इस काम का कितना हिस्सा M1 GPU code से बंधा है, और feature-on-feature implementation का कितना हिस्सा दूसरी जगहों पर भी reuse किया जा सकता है
    यह Zink के उस तरीके जैसा बहुत लगता है जिसमें वह अधिक primitive Vulkan के ऊपर जटिल OpenGL features चलाता है, लेकिन M1 के लिए अभी target करने लायक Vulkan backend नहीं है

    • अधिक सामान्य रूप से, जटिल OpenGL या Vulkan को CPU software rendering और hardware-specific native acceleration support के किसी भी arbitrary combination के ऊपर चलाया जा सकता है
      अंततः यह workload का सवाल है, और इसे अलग-अलग hardware पर reuse किया जा सकता है
      यह उन hardware के लिए भी मददगार हो सकता है जो पुराने हैं और अच्छी तरह समझे जा चुके हैं, लेकिन आधुनिक workloads के लिए अकेले इस्तेमाल करना मुश्किल है
  • इससे होने वाला performance impact कितना होगा, खासकर macOS पर सीधे Metal इस्तेमाल करने की तुलना में, यह जानने की बहुत उत्सुकता है
    जवाब निश्चित ही “स्थिति पर निर्भर करता है” होगा, लेकिन फिर भी जिज्ञासा है
    शायद लेख में इसका जवाब हो, लेकिन ज्यादातर बातें मेरी समझ में नहीं आईं

    • किसी feature को driver के compute code से implement करने और GPU hardware support से implement करने के बीच जरूरी नहीं कि बहुत बड़ा अंतर हो
      “hardware support” भी आम तौर पर GPU microcode में implement होता है और कई बार वही silicon path इस्तेमाल करता है
      कोई भी feature performance bottleneck बन सकता है, और वास्तव में करके देखे बिना यह जानना मुश्किल है कि bottleneck कहां आएगा
    • Alyssa की अभिव्यक्ति थोड़ी अजीब लगती है
      यह सही है कि Apple GPU geometry shaders को native तौर पर support नहीं करता, लेकिन geometry shaders का design अच्छा नहीं है और वे GPU hardware के साथ ठीक से मेल नहीं खाते
      जिन hardware में असल support बताया जाता है, उन पर भी वे धीमे माने जाते हैं, और Nvidia ने mesh shading design की, इसकी वजह है
      transform feedback का भी अक्सर जिक्र होता है, लेकिन Apple GPU किसी भी shader stage से arbitrary memory locations में लिख सकता है, इसलिए transform feedback वस्तुतः अनावश्यक है
      मुख्य बात यह है कि Apple ने एक साफ-सुथरा compute architecture implement करते हुए कई पुराने अवशेषों और ऐसी features को काट दिया जो ठीक से काम न करने के लिए जाने जाते हैं
      “M1 OpenGL 4.1 में फंस गया” कहना उचित नहीं लगता
      OpenGL को बहुत पहले से follow नहीं किया, इसलिए 4.1 के बाद कौन-सी features की बात है, यह नहीं जानता; लेकिन अगर OpenGL में कोई ऐसी feature है जो Metal में नहीं की जा सकती, तो मुझे बहुत आश्चर्य होगा
      उल्टा, Metal में बहुत सी चीजें संभव हैं जो OpenGL में बिल्कुल संभव नहीं हैं, और Metal shading language में full pointers होना ही इसका एक उदाहरण है
  • यह M1 पर Fedora के लिए है
    अगर यह macOS पर भी संभव हो जाए तो हैरानी होगी, लेकिन सोचता हूँ कि ऐसा बनाने के लिए क्या-क्या चाहिए होगा

    • आखिरकार संरचना यही है कि command buffer बनाए जाते हैं और GPU को भेजे जाते हैं, इसलिए macOS पर ऐसा करने का कोई तरीका चाहिए
      M1 GPU के लिए शुरुआती Mesa driver भी IOKit के जरिए macOS के AGX driver को command buffer भेजने के तरीके से bootstrap किया गया था
      https://rosenzweig.io/blog/asahi-gpu-part-2.html
      https://github.com/AsahiLinux/gpu/blob/main/demo/iokit.c
      इसलिए GPU के surface को macOS स्क्रीन पर composite किए जा सकने वाले target के रूप में पास करने के लिए Mesa की तरफ थोड़ा और glue code चाहिए
    • developers के अनुसार, Apple के पास स्थिर public kernel API नहीं है, इसलिए व्यवहार में यह मुश्किल है: https://social.treehouse.systems/@AsahiLinux/111930744188229065
    • शायद यह पहले से ही MoltenVK → Vulkan → Zink रास्ते से संभव हो सकता है
    • लगता है Apple third-party kernel drivers को प्रतिबंधित करता है
      मेरी समझ में, ठीक-ठाक Vulkan या OpenGL implementation बनाने के लिए GPU processing संभालने वाला kernel-side counterpart चाहिए होता है
      शायद इसलिए macOS के लिए native Vulkan implement करने की कोशिश करने वाला कोई नहीं दिखता
      हालांकि अगर Apple driver के ऊपर यह संभव हो तो, मुझे ठीक से नहीं पता
    • Metal के ऊपर OpenGL driver implement किया जा सकता है
      लेकिन एक suboptimal legacy API के लिए इतने resources लगाने की वजह है या नहीं, यह नहीं जानता
  • out-of-bounds access को trap से arbitrary data लौटाने में बदलने को robustness कहना काफी मजेदार है
    graphics programming वाकई अजीब है

    • graphics driver लिखने के नजरिए से यह समझ में आता है, और Postel’s law, यानी robustness principle से भी मेल खाता है
      GPU driver का मुख्य काम broken applications को चलाना या उन्हें तेज चलाना है
      defaults को strict बना देने से broken code रिलीज़ करने वाली video game industry की structural समस्या ठीक नहीं होगी, बस users दूर चले जाएंगे
      ऐसे hardware में जहाँ branching आम तौर पर बहुत महंगी होती है, system को edge cases को सबसे efficient तरीके से चुपचाप handle करने को बताने वाला flag उपयोगी लगता है
      ऐसे कई valid use cases भी होंगे जहाँ programmer reasonably भरोसा कर सकता है कि ये edge cases final rendered frame पर लगभग असर नहीं डालते
    • robustness checks न होने का मतलब यह नहीं कि out-of-bounds access जरूर trap होगा; यहाँ robustness का मतलब है कि ऐसे अजीब cases में भी known result दिया जाए
      इसे इस बात के साथ मिलाएँ कि GPU आम तौर पर traps को खास पसंद नहीं करते, तो बात समझ आती है
      Carmack ने भी कभी कहा था कि megatexture design करते समय manufacturers को virtual memory के idea को स्वीकार करवाना बहुत दर्दनाक था
    • इसलिए graphics, high-performance computing, high-energy physics और ultra-low-latency trading के क्षेत्रों में C और C++ का भविष्य अब भी उज्ज्वल है
      जहाँ “performance safety से ऊपर” वाली culture हावी है, वहाँ दूसरी programming languages की बात करना दीवार से बात करने जैसा है
  • यह निश्चित रूप से बहुत दिलचस्प काम है, लेकिन सोचता हूँ कि पहले Vulkan को target क्यों नहीं किया गया
    आजकल यह ज्यादा महत्वपूर्ण target लगता है, और इसके ऊपर पहले से OpenGL implementation भी है

    • Vulkan पर OpenGL compatibility layer कोई जादू नहीं है
      किसी खास OpenGL feature को support करने के लिए Vulkan driver को उसके बराबर feature support करना पड़ता है, और आम तौर पर extension चाहिए होता है
      यानी सिर्फ basic Vulkan driver implement करने से OGL 4.6 support मुफ्त में नहीं मिल जाता, और Mesa से OGL 4.6 को Vulkan में translate करवाने के लिए Vulkan driver में OGL 4.6 features सारे implement करने होंगे
      इसके अलावा Alyssa reverse engineering और OpenGL driver projects पहले कर चुकी हैं
      पूरी परिस्थितियाँ नहीं जानता, लेकिन familiar API के लिए driver बनाना unfamiliar API के driver से कहीं आसान और तेज होने की संभावना है
    • पहले पुराने OpenGL को target करके basic feature set को काम करने लायक बनाया गया
      वहाँ से नए OpenGL तक ले जाना शायद पूर्ण Vulkan implementation से कम काम रहा होगा, और Vulkan के लिए जरूरी चीजें भी काफी सीखी होंगी
    • मैंने भी ऐसा ही सोचा था, लेकिन OpenGL को Vulkan पर support करने के लिए वैसे भी ज्यादा high-version Vulkan चाहिए और काम भी बड़ा है
      इसलिए लगता है कि जल्दी कुछ working पाने के लिए पहले lower-version OpenGL को चुना गया
  • अगर 90 के दशक में John Carmack ने Quake II में OpenGL इस्तेमाल करने पर इतना ज़ोर न दिया होता, तो 3D गेम्स में OpenGL की मौजूदगी शायद बन ही न पाती—यह सोचकर काफ़ी हैरानी होती है

    • Quake शायद इतिहास का बस एक छोटा-सा हिस्सा ही है
      OpenGL को आखिरकार उस रूप में ढालने का श्रेय SGI और कई सिस्टम्स व आर्किटेक्चर्स पर compatible implementations बनाने की विशाल कोशिश को जाता है
    • मज़ेदार बात यह है कि सबसे पुरानी archive की गई OpenGL साइट पर animated Quake 1 graphics और दूसरे menu वाला एक बड़ा FAST GAMES GRAPHICS banner था :-P
      https://web.archive.org/web/19970707113513/http://www.opengl.org/
    • पता नहीं यह इकलौता कारण था या नहीं, लेकिन Carmack ने OpenGL को जो push दिया, उससे निश्चित रूप से मदद मिली
      3D games से जुड़ी बहुत-सी चीज़ों का श्रेय Doom और Quake को जाता है
    • कुछ साल बाद, 2011 में John Carmack ने कहा कि उस समय Direct3D बेहतर API था
      उनके मुताबिक Microsoft में API सुधारने के लिए बड़े incompatible changes करते रहने की हिम्मत थी, जबकि compatibility की चिंताओं ने OpenGL को पीछे रोक रखा था
      उन्होंने कहा कि Direct3D multithreading को बेहतर संभालता है, और नए versions में state management भी बेहतर है
      फिर भी उन्होंने कहा कि id Software inertia की वजह से OpenGL पर ही बना हुआ है, और फायदों के बावजूद Direct3D पर जाने की कोई योजना नहीं है
      स्रोत: https://www.bit-tech.net/news/gaming/pc/carmack-directx-better-opengl/1/
    • संदर्भ सामग्री: https://www.chrishecker.com/OpenGL/Press_Release