نقاط المزامنة في أنابيب التجزئة في Vulkan

من ويكي
اذهب إلى: تصفح، ابحث
تفاصيل أنبوب تجزئة الرسم والمعالجة في Vulkan، هذا مجرد رسم بياني وغالباً لايعكس التصميم الداخلي للجهاز
نقاط المزامنة في أنابيب تجزئة المعالجة في Vulkan

عندما تحدثنا عن VkSubmitInfo::pWaitDstStageMask قلنا أن VkPipelineStageFlagBits يحدد المرحلة التي سينتظر عندها السيمافور، سنتحدث في هذا المقال عن الأنابيب في Vulkan حيث سنوضح الخطوات التي تمر بها البيانات إلى أن تعطي النتائج النهائية لفهم VkPipelineStageFlagBits ولأن مفهوم الأنابيب مهم عند الحديث مستقبلاً عن كائنات أنابيب تجزئة المعالجة compute pipeline وأنابيب تجزئة الرسم graphics pipeline، وسنوضح كذلك نقاط المزامنة في هذه المراحل لأنها مهمة عند الحديث عن الحواجز barriers ومراحل التصيير renderpasses.

فكرة أنابيب التجزئة pipelining

لو كانت لدينا عملية برمجية تأخذ 200 نانو ثانية حتى تنتهي، وأردنا تنفيذ 4 عمليات (P0, P1, P2, P3)، فإننا سنجد أن تنفيذ العمليات الأربعة تلك سيأخذ في المجمل 800 نانو ثانية، بعض أنواع العمليات الكبيرة يمكن تجزئتها إلى عمليات فرعية بحيث يصبح تنفيذ العملية يتم على عدة مراحل صغيرة بدلاً من مرحلة واحدة طويلة، لنفترض أننا قمنا بتقسيم العملية السابقة التي تأخذ 200 نانو ثانية إلى 4 مراحل فرعية كل مرحلة تأخذ 50 نانو ثانية، سنجد أن المرحلة الفرعية الأولى ستصبح متاحة بعد انتقال العملية P0 من المرحلة الفرعية الأولى للمرحلة الفرعية الثانية، ويمكننا أن نبدأ فوراً ونجعل المرحلة الفرعية الأولى تبدأ بتنفيذ العملية P1، هكذا سنجد أن الوقت الازم لإنهاء تنفيذ العمليات الأربعة تلك قد قل إلى 400 نانو ثانية:

P0
P1 P0
P2 P1 P0
P3 P2 P1 P0
P3 P2 P1 (انتهت P0)
P3 P2 (انتهت P1)
P3 (انتهت P2)
(انتهت P3)

فكرة أنابيب تجزئة المعالجة pipelined processing شائعة في معظم المعالجات الحديثة من معالجات مركزية ومعالجات رسومية، حيث استوحي الاسم من خطوط الأنابيب المخصصة للنقل وخطوط الإنتاج في المصانع، حيث تقسم العملية الطويلة والمعقدة إلى أجزاء صغيرة وتتم المعالجة على مراحل فرعية، وتنتقل العملية من مرحلة فرعية إلى مرحلة فرعية تليها، ونشغل المرحلة الفرعية الشاغرة بمهمة كانت في الإنتظار.

معظم المعالجات الرسومية تستخدم أنابيب التجزئة بطريقة مشابهة، حيث تجزأ المهام الرسومية إلى عدة مراحل، تختلف تلك المعالجات في تفاصيل عمل مراحل الأنابيب وطريقة إعدادها إلا أن التصميم العام مشابه، كون Vulkan تستهدف أنواع متعددة من الأجهزة المختلفة فقد وفرت أنبوب تجزئة تجريدي، مصممين الأجهزة عليهم مسؤلية عزل التفاصيل الداخلية باستخدام مشغلات Vulkan التي يوفرونها، فمثلاً معظم المعالجات الرسومية تستخدم تعليمات آلة خاصة فيها لتنفيذ المظللات، توفر Vulkan تعليمات SPIR-V والتي تعزل هذه التفاصيل، سيقوم المشغل داخلياً بتحويل تلك التعليمات لما يقابها في تعليمات الجهاز، أي اختلافات في تفاصيل هذه المراحل وكيفية عملها سيعمل المشغل على إخفاء هذه الاختلافات عنك كي يبدو وكما لو أن الجهاز يستخدم المراحل الموصوفة في Vulkan.

هناك نوعين من أنابيب التجزئة في Vulkan وهما أنبوب الرسوميات وأنبوب الحوسبة يمثلهما الكائن VkPipeline، كلاً منهما يتكون من عدة مراحل فرعية، أنبوب الرسوميات أكثر مراحل فرعية من أنبوب الحوسبة، وهناك أيضاً النقل والمستخدم في القراءة والكتابة من ذاكرة الجهاز عبر طوابير النقل، وكذل عمليات المضيف وهي مرحلة مجازية تعبر عن عمليات القراءة والكتابة من وإلى ذاكرة المضيف، لكنهما ليسا أنابيب تجزئة بالمعنى الذي وصفناه سابقاً، بل يتكونان من مرحلة واحدة فقط.

أنبوب تجزئة الحوسبة Compute Pipeline

تستخدم الحوسبة للتنفيذ للمعالجة المتوازية للاستفادة من قدرات المعالجات الرسومية على تنفيذ آلاف الحسابات المتوازية خصوصاً تلك التي يمكن إجراءها على كل عنصر من عناصر البيانات باستقلالية عن باقي العناصر مثل معالجة بكسلات الصور، أو محاكاة حركة الجزيئات مثل الغبار، فلست بحاجة لإجراء تلك الحسابات على المعالج الرئيسي ثم ترسمها، بل يمكنك إجراء تلك الحسابات مباشرة على المعالج الرسومي ورسمها مباشرة وهي على ذاكرة الجهاز، أنبوب تجزئة الحوسوبة مثل مسار مختصر وسريع لإجراء الحسابات المتوازية، ويمكنه مشاركة البيانات مع أنبوب تجزئة الرسوميات.

يبدأ أنبوب الحوسبة مع تنفيذ أوامر إرسال أوامر الحوسبة vkCmdDispatch*() حيث vkCmdDispatch() أو vkCmdDispatchIndirect() ثم يجهز الجهاز مدخلات مظللات المظلل الحسابي لينفذه على مجموعة بيانات أحادية أو ثنائية أو ثلاثية الأبعاد تحدد أبعادها مباشرة في معاملات vkCmdDispatch() أو تمررها عن طريق كائن ذاكرة وسيطة VkBuffer أنشأته باستخدام الراية VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT إلى الدالة vkCmdDispatchIndirect().

عند إنشاء كائن أنبوب التجزئة VkPipeline الخاص بأنبوب تجزئة الحوسبة باستخدم vkCreateComputePipelines() فإننا سنحدد معلومات الموارد التي يقرأها أو يكتب لها المظلل كالذواكر الوسيطة والصور باستخدام واصفات الموارد resource descriptors، سنتحدث عن هذه التفاصيل لاحقاً عن حديثنا عن أنبوب التجزئة.

أنبوب تجزئة الرسوميات Graphics Pipeline

يستخدم أنبوب تجزئة معالجة الصور للرسوميات، حيث يتكون من العديد من المراحل كما ترى في العمود الأيسر من الصور الأولى، لا يشترط أن يطابق هذا الرسم بنية العتاد الداخلية، فمثلاً معظم المعالجات الرسومية تستخدم نموذج المظلل الموحد unified shader model بدلاً من أنوية ووحدات معالجة مخصصة بنوع معين من المظللات، حيث تنفذ المظللات على أنوية عامة الاستخدام يمكنها تنفيذ مظللات نقاط و مظللات بكسلات وغيرها من أنواع المظللات ، وبعض المعالجات الرسومية خصوصاً تلك المستخدمة في الجوالات لاتصيير المشهد مرة واحدة، بل تستخدم طريقة البلاطات في التصيير tiled rendering بحيث تقسم الشاشة لعدة أجزاء وتبدأ بتصيير الأجزاء هذه باستقلال عن بعض وعلى عدة مراحل، وذلك لتقليل كمية البيانات المنقولة ومن ثم تقليل الطاقة، لكن في كل الحالات Vulkan تعزلك عن هذه التفاصيل الداخلية.

يتكون أنبوب الرسوميات من عدة مراحل بعضها قابلة للبرمجة باستخدام مظللات والبعض الآخر ثابته ليست قابلة للبرمجة لكن يمكن التحكم فيها ببعض الإعداد التي ستمررها عند إنشاء الكائن VkPipeline الخاص بأنبوب الرسم باستخدام الدالة vkCreateGraphicsPipelines().

يبدأ أنبوب الرسوميات بتنفيذ أوامر الرسم vkCmdDraw*() وهي vkCmdDraw() و vkCmdDrawIndexed() المستخدمة للرسم المباشر، أو vkCmdDrawIndirect() و vkCmdDrawIndexedIndirect() للرسم غير المباشر التي تأخذ معاملات الدالة من ذاكرة وسيطة VkBuffer أنشأتها باستخدام الراية VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT، تتم بعدها قراءة النقاط وتركيبها في مجمّع النقاط input assembler والذي يقرأ من ذاكرتين خاصة وهي ذاكرة النقاط VK_BUFFER_USAGE_VERTEX_BUFFER_BIT وكذلك ذاكرة مؤشرات النقاط VK_BUFFER_USAGE_INDEX_BUFFER_BIT في حال استخدمت vkCmdDrawIndexed() أو vkCmdDrawIndexedIndirect()، يعالج مجمّع النقاط معلومات الشكل الهندسي ويمرر النقاط مفردة لمظلل النقاط الذي يجري عليها التحويلات مثل التحريك والإسقاط، يمكن لمظلل النقاط أن يقرأ ويكتب لأي ذاكرة أو صورة محددة في واصفات الموارد.

بعد هذه المرحلة تأتي مرحلة الفسيفساء وهي مرحلة اختيارية ولاتدعمها كل الأجهزة خصوصاً المعالجات الرسومية الخاصة بالجولات، تمكنك هذه المرحلة من إعادة تقسيم الشكل الهندسي وزيادة تفاصيله، طبعاً زيادة التقسم لن تضيف شيء، لكن باستخدام خرائط الإزاحة displacement maps لعمل نتوئات مثل نتوئات سطوح الصخور، استخدام الفسيفساء أكثر كفاءة وسرعة مقارنة بإرسال أجسام عالية التفاصيل، بالإضافة لأنها تمكنك من التحكم في مستوى التفاصيل برمجياً، في حال كان الجهاز يدعم مظلل الفسيفسياء وقمت بتفيعله فإنه يستدعى مظلل التحكم في الفسيفساء tessellation control shader الذي يمرر له مجمّع الفسيفساء دفعة نقاط تحددها حجمها مسبقاً مثل 3 نقاط، ثم يقوم مظلل التحكم بإعداد التقسيمات الفرعية التي سيولدها مُولد الفسيفساء، بعدها سيمرر مُولد الفسيفساء إلى مظلل تنفيذ الفسيفساء tessellation evaluation shader الذي يستخدم لمعالجة النقاط الناتجة مثل الإزاحة.

بعدها يأتي دور مرحلة المظلل الهندسي، وهي مرحلة اختيارية ولا تدعمها كل الأجهزة، حيث يسمح المظلل الهندسي geometry shader بحذف و توليد أشكال هندسية باستخدام معلومات شكل هندسي أساسي ممرر من المرحلة السابقة، فكرة المظلل الهندسي ظهرت قبل ظهور مظلل الفسيفساء، حالياً المظلل الهندسي شبه مهجور لكونه محدود الاستخدام ويستعاض عنه بالفسيفساء.

بعدها تبدأ مرحلة البكسلة rasterization، حيث يولد المُبكسل معلومات البكسلات التي ستنتج من الشكل الهندسي كي يمررها لاحقاً لمظلل البكسلات لتلوينها في حال تجاوزت بعض الاختبارات مثل اختبارات العمق، هذه المرحلة ثابتة غير قابلة للبرمجة لكنها قابلة للإعداد.

بعدها ستجرى الاختبارات التي فعلتها ماقبل مظلل البكسلات، مثل اختبار العمق لتحديد أي البكسلات فعلياً ظاهر وأيها يغطيه بكسل آخر، واختبار الشبلونة الذي يسمح لك باستخدام صورة كقناع يظهر أو يخفي أجزاء من الشكل، بعد نجاح هذه الاختبارات فسينفذ مظلل البكسلات لينتج اللون النهائي للبكسل، بعض الأجهزة قد تجري تلك الاختبارات بعد تنفيذ مظلل البكسلات، لكن الأغلبية تجريها قبله كي لاينفذ مظلل البكسلات على بكسلات لن تظهر.

في النهاية تجرى عملية الخلط في حال تفعيلها لخلط الصور الشفافة مع الصور الموجودة في ذاكرة الألوان حسب اعدادات الخلط التي تحددها، بعدها سيكتب اللون النهائي للبكسل في ذاكرة الألوان وهي الصورة التي تكتب فيها البكسلات النهائية.

نقاط المزامنة VkPipelineStageFlagBits

لو نظرت للرسم الأول ستجد أن معظم المراحل في الأنابيب تقرأ وبعضها تكتب للعمود الذي في المنتصف والذي يمثل الموارد سواءً ذواكر وسيطة أو صور، منها ماهو مورد مستقل مثل ذاكرة الرسم المباشر وذاكرة النقاط وذاكرة مؤشرات النقاط ومنها موارد في واصفات الموارد ومنها ماهو في ذاكرة الإطار، يمثل VkPipelineStageFlagBits والمفصّل في الصورة الثانية المراحل في أنابيب المعالجة المختلفة التي تقرأ أو تكتب لأحد الموارد، مثلاً تعني VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT مرحلة الرسم -وكذلك الحوسبة- والتي يمكن لها أن تقرأ من ذاكرة الرسم -والحوسبة- غير المباشرة، ويمثل VK_PIPELINE_STAGE_VERTEX_SHADER_BIT مرحلة مظلل النقاط والذي يقرأ من الموارد المجموعة في واصفات الموارد.

تمثل قيم VkPipelineStageFlagBits المراحل الفرعية في أنابيب المعالجة، بعض القيم التي لاتنتمي لأنابيب المعالجة:

  • VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT تمثل مرحلة أولية قبل بداية تنفيذ أي أنبوب معالجة سواءً رسوميات أو حوسبة أو نقل أو عمليات مضيف
  • VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT تمثل مرحلة متأخرة بعد تنفيذ كل الأوامر ونهاية أنبوب المعالجة
  • VK_PIPELINE_STAGE_TRANSFER_BIT تشير لعمليات النقل، وهي أي نسخ vkCmdCopy()*() وتنظيف vkCmdClear*()، يستثنى من هذا الأمرين vkCmdClearAttachments() و vkCmdCopyQueryPoolResults()
  • VK_PIPELINE_STAGE_HOST_BIT تشير لعمليات القراءة والكتابة من وإلى المضيف
  • VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT اختصار يجمع جميع مراحل أنبوب الرسوميات بدأً من VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT وانتهاءً VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
  • VK_PIPELINE_STAGE_ALL_COMMANDS_BIT اختصار يجمع جميع مراحل كل الأنابيب

عندما تريد المزامنة يفترض أن تحدد بدقة أي المراحل بحاجة للمزامنة، مثلاً لو كان لديك ذاكرة VkBuffer استخدامها VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT بحيث تكتب لها من مظلل حوسبة وتقرأها من مظلل بكسلات، هنا قد تريد المزامنة بين مظلل الحوسبة VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT وبين مظلل البكسلات VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT فقط، هذا سيسمح للمراحل السابقة بالتنفيذ، وفي حال لو كنت تستخدم طابور واحد واستخدمت الحواجز vkCmdPipelineBarrier() فإنه يمكنك تحديد الذاكرة كي تسمح لمن لايستخدمها بالتنفيذ كما سنرى لاحقاً.