إنّ التصميم الجيد لرأس البيانات وضغطه يُعد جزءًا لا يتجزأ من أداء أي تطبيق رسومي، سواء كان التطبيق يتكون من واجهات مستخدم ثنائية الأبعاد أو عبارة عن لعبة عالم مفتوح ثلاثية الأبعاد كبيرة. يشير الاختبار الداخلي باستخدام أداة تحليل الإطارات من Android GPU Inspector على عشرات من أفضل ألعاب Android إلى إمكانية بذل الكثير من الجهد لتحسين إدارة بيانات رأس الصفحة. لقد لاحظنا أنّه من الشائع أن تستخدم بيانات رأس الصفحة دقة كاملة، وقيمًا عائمة بمقدار 32 بت لجميع سمات رأس الصفحة، بالإضافة إلى تخطيط المخزن المؤقت الرأسي الذي يستخدم مصفوفة من البنى المنسَّقة بسمات متداخلة بالكامل.
تناقش هذه المقالة كيفية تحسين أداء الرسومات لتطبيق Android باستخدام الأساليب التالية:
- ضغط Vertex
- تقسيم بث Vertex
يمكن أن يؤدي تنفيذ هذه الأساليب إلى تحسين استخدام معدل نقل بيانات الذاكرة في رأس الصفحة بنسبة تصل إلى 50%، وتقليل التضارب في ناقل الذاكرة مع وحدة المعالجة المركزية (CPU)، والحدّ من حالات التعطّل في ذاكرة النظام، وتحسين عمر البطارية. وكلها مكسب لكل من المطورين والمستخدمين!
إنّ جميع البيانات المعروضة مأخوذة من مثال لمشهد ثابت يحتوي على حوالي 19,000,000 رأس يتم تشغيله على هاتف Pixel 4:
الشكل 1: مشهد نموذجي من 6 حلقات ورؤوس يبلغ ارتفاعها 19 متر
ضغط Vertex
ضغط Vertex هو مصطلح شامل لتقنيات الضغط مع فقدان البيانات التي استخدام التعبئة الفعّالة لتقليل حجم بيانات الرأس أثناء وقت التشغيل والتخزين. هناك فوائد عديدة لتقليل حجم الرؤوس، بما في ذلك تقليل معدل نقل بيانات الذاكرة على وحدة معالجة الرسومات (عن طريق مقايضة الحوسبة لمعدل نقل البيانات)، وتحسين استخدام ذاكرة التخزين المؤقت، وربما تقليل مخاطر تسرب السجلات.
تشمل الأساليب الشائعة لضغط Vertex ما يلي:
- تقليل الدقة الرقمية لسمات بيانات رأسية (مثل: عدد عائم 32 بت إلى عدد عائم 16 بت)
- تمثيل السمات بأشكال مختلفة
على سبيل المثال، إذا كان الرأس يستخدم أعدادًا عائمة كاملة 32 بت للموضع (vec3)، والعادي (vec3)، وإحداثيات الهيئة (vec2)، فإن استبدال كل هذه الأعداد بأشكال عائمة 16 بت سيؤدي إلى تقليل حجم الرأس بنسبة 50% (16 بايت في رأس رأس الصفحة هو 32 بايت في المتوسط).
مواضع الرأس
يمكن ضغط بيانات موضع Vertex من قيم النقاط العائمة بدقة 32 بت إلى قيم النقاط العائمة 16 بت بدقة بنصف في الغالبية العظمى من الشبكات، ويتم دعم نصف البيانات العائمة في الأجهزة على جميع الأجهزة الجوّالة تقريبًا. تظهر دالة التحويل التي تنتقل من float32 إلى float16 على النحو التالي (استنادًا إلى هذا الدليل):
uint16_t f32_to_f16(float f) {
uint32_t x = (uint32_t)f;
uint32_t sign = (unsigned short)(x >> 31);
uint32_t mantissa;
uint32_t exp;
uint16_t hf;
mantissa = x & ((1 << 23) - 1);
exp = x & (0xFF << 23);
if (exp >= 0x47800000) {
// check if the original number is a NaN
if (mantissa && (exp == (0xFF << 23))) {
// single precision NaN
mantissa = (1 << 23) - 1;
} else {
// half-float will be Inf
mantissa = 0;
}
hf = (((uint16_t)sign) << 15) | (uint16_t)((0x1F << 10)) |
(uint16_t)(mantissa >> 13);
}
// check if exponent is <= -15
else if (exp <= 0x38000000) {
hf = 0; // too small to be represented
} else {
hf = (((uint16_t)sign) << 15) | (uint16_t)((exp - 0x38000000) >> 13) |
(uint16_t)(mantissa >> 13);
}
return hf;
}
هناك قيد على هذا المنهج؛ تقل الدقة كلما ابتعد الرأس عن مكانه الأصلي، مما يجعله أقل ملاءمة للشبكات الكبيرة للغاية من الناحية المكانية (الرؤوس التي تحتوي على عناصر تتجاوز 1024). يمكنك معالجة ذلك عن طريق تقسيم شبكة إلى أجزاء أصغر، مع توسيط كل مقطع حول أصل النموذج، ثم التحجيم بحيث تتناسب جميع رؤوس كل مقطع مع النطاق [-1، 1]، الذي يحتوي على أعلى دقة لقيم النقاط العائمة. يبدو الكود الزائف للضغط كما يلي:
for each position p in Mesh:
p -= center_of_bounding_box // Moves Mesh back to the center of model space
p /= half_size_bounding_box // Fits the mesh into a [-1, 1] cube
vec3<float16> result = vec3(f32_to_f16(p.x), f32_to_f16(p.y), f32_to_f16(p.z));
وتقوم بخبز عامل المقياس وترجمته في مصفوفة النموذج لفك ضغط بيانات الرأس عند العرض. وتذكَّر أنّك لا تحتاج إلى استخدام مصفوفة النموذج نفسها لتحويل القيم العادية، لأنّه لم يتم تطبيق الضغط نفسه عليها. ستحتاج إلى مصفوفة لا تتضمّن عمليات تحويل الضغط هذه للقيم العادية، أو يمكنك استخدام مصفوفة النموذج الأساسي (التي يمكنك استخدامها للقيم العادية) ثم تطبيق عمليات تحويل الضغط الإضافية على مصفوفة النماذج ضمن أداة التظليل. مثال:
vec3 in in_pos;
void main() {
...
// bounding box data packed into uniform buffer
vec3 decompress_pos = in_pos * half_size_bounding_box + center_of_bounding_box;
gl_Position = proj * view * model * decompress_pos;
}
وتتضمّن الطريقة الأخرى استخدام القيم الصحيحة الموقَّعة (SNORM). تستخدم أنواع بيانات SNORM الأعداد الصحيحة بدلاً من النقطة العائمة لتمثيل القيم بين [-1، 1]. إنّ استخدام SNORM 16 بت للمواقف يوفر لك نفس توفير الذاكرة الذي يتم توفيره في صورة float16 بدون عيوب التوزيعات غير المنتظمة. تظهر عملية التنفيذ التي ننصح بها لاستخدام SNORM على النحو التالي:
const int BITS = 16
for each position p in Mesh:
p -= center_of_bounding_box // Moves Mesh back to the center of model space
p /= half_size_bounding_box // Fits the mesh into a [-1, 1] cube
// float to integer value conversion
p = clamp(p * (2^(BITS - 1) - 1), -2^(BITS - 1), 2^(BITS - 1) - 1)
التنسيق | حجم الملف | |
---|---|---|
قبل | vec4<float32 > |
16 بايت |
بعد | vec3<float16/SNORM16 > |
6 بايت |
قيم قمة الحدود ومساحة ظل الزاوية
وهناك حاجة إلى قيم عمودية عادية للإضاءة، ونحتاج إلى مساحة ظل الزاوية للتقنيات الأكثر تعقيدًا مثل رسم الخرائط العادية.
مسافة مماس
مساحة المماس هي نظام إحداثي حيث يتكون كل رأس من الخط المتجه الطبيعي وظل المماس وخط التمام. نظرًا لأن هذه المتجهات الثلاثة عادةً ما تكون متعامدة مع بعضها البعض، فإننا نحتاج فقط إلى تخزين اثنين منها ويمكننا حساب الخط الثالث بأخذ حاصل ضرب تقاطعي اثنين آخرين في تظليل الرأس.
يمكن تمثيل هذه المتجهات عادةً باستخدام أعداد عائمة 16 بت دون أي فقدان إدراكي في الدقة البصرية، لذا فهذا مكان جيد للبدء!
ويمكننا الضغط أكثر باستخدام تقنية تُعرَف باسم QTangents لتخزين مساحة ظل الزاوية بالكامل في رباعية واحدة. ونظرًا لأنه يمكن استخدام الأشكال الرباعية لتمثيل عمليات التدوير، فإنه من خلال التفكير في متجهات مساحة ظل الزاوية كمتجهات عمودية لمصفوفة 3×3 تمثل الدوران (في هذه الحالة من مساحة النموذج إلى مساحة المماس) يمكننا التحويل بينهما! يمكن التعامل مع الرسم الرباعي على أنّه بيانات vec4، وفي ما يلي التحويل من متّجهات مساحة ظل الزاوية إلى QTangent بناءً على البحث المرتبط أعلاه والتكيّف مع عملية التنفيذ هنا:
const int BITS = 16
quaternion tangent_space_to_quat(vec3 normal, vec3 tangent, vec3 bitangent) {
mat3 tbn = {normal, tangent, bitangent};
quaternion qTangent(tbn);
qTangent.normalize();
//Make sure QTangent is always positive
if (qTangent.w < 0)
qTangent = -qTangent;
const float bias = 1.0 / (2^(BITS - 1) - 1);
//Because '-0' sign information is lost when using integers,
//we need to apply a "bias"; while making sure the Quaternion
//stays normalized.
// ** Also our shaders assume qTangent.w is never 0. **
if (qTangent.w < bias) {
Real normFactor = Math::Sqrt( 1 - bias * bias );
qTangent.w = bias;
qTangent.x *= normFactor;
qTangent.y *= normFactor;
qTangent.z *= normFactor;
}
//If it's reflected, then make sure .w is negative.
vec3 naturalBinormal = cross_product(tangent, normal);
if (dot_product(naturalBinormal, binormal) <= 0)
qTangent = -qTangent;
return qTangent;
}
ستتم تسوية الدالة الرباعية، وستكون قادرًا على ضغطها باستخدام SNORMs. وتعطي مؤشرات SNORM 16 بت الدقة والتوفير في الذاكرة بشكل جيد. ويمكن أن توفّر SNORMs 8 بت المزيد من التوفير، ولكنها قد تتسبب في حدوث أخطاء على المواد عالية الدقة. يمكنك تجربة كليهما ومعرفة ما يناسب مواد العرض. يبدو ترميز اللوحة الرباعية على النحو التالي:
for each vertex v in mesh:
quaternion res = tangent_space_to_quat(v.normal, v.tangent, v.bitangent);
// Once we have the quaternion we can compress it
res = clamp(res * (2^(BITS - 1) - 1), -2^(BITS - 1), 2^(BITS - 1) - 1);
لفك ترميز الوحدة الرباعية في أداة تظليل رأس الصفحة (تم التكيّف من هنا):
vec3 xAxis( vec4 qQuat )
{
float fTy = 2.0 * qQuat.y;
float fTz = 2.0 * qQuat.z;
float fTwy = fTy * qQuat.w;
float fTwz = fTz * qQuat.w;
float fTxy = fTy * qQuat.x;
float fTxz = fTz * qQuat.x;
float fTyy = fTy * qQuat.y;
float fTzz = fTz * qQuat.z;
return vec3( 1.0-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy );
}
vec3 yAxis( vec4 qQuat )
{
float fTx = 2.0 * qQuat.x;
float fTy = 2.0 * qQuat.y;
float fTz = 2.0 * qQuat.z;
float fTwx = fTx * qQuat.w;
float fTwz = fTz * qQuat.w;
float fTxx = fTx * qQuat.x;
float fTxy = fTy * qQuat.x;
float fTyz = fTz * qQuat.y;
float fTzz = fTz * qQuat.z;
return vec3( fTxy-fTwz, 1.0-(fTxx+fTzz), fTyz+fTwx );
}
void main() {
vec4 qtangent = normalize(in_qtangent); //Needed because 16-bit quantization
vec3 normal = xAxis(qtangent);
vec3 tangent = yAxis(qtangent);
float biNormalReflection = sign(in_qtangent.w); //ensured qtangent.w != 0
vec3 binormal = cross(normal, tangent) * biNormalReflection;
...
}
التنسيق | حجم الملف | |
---|---|---|
قبل | vec3<float32 > + vec3<float32 > + vec3<float32 > |
36 بايت |
بعد | vec4<SNORM16 > |
8 بايت |
العاديون فقط
إذا كنت تحتاج فقط إلى تخزين المتجهات العادية، هناك طريقة مختلفة يمكن أن تؤدي إلى توفير المزيد من التوفير، وذلك باستخدام التعيين الثماني الأوجه لمتجهات الوحدة بدلاً من الإحداثيات الديكارتية لضغط الخط المتجه العادي. يعمل "رسم الخرائط الثمانية الأوجه" من خلال إسقاط وحدة الكرة على ثمانية أوجه، ثم إسقاطها في مستوى ثنائي الأبعاد. والنتيجة هي أنه يمكنك تمثيل أي متجه طبيعي باستخدام رقمين فقط. ويمكن اعتبار هذين الرقمين كإحداثيات زخرفة نستخدمها "لأخذ عينة" من المستوى الثنائي الأبعاد الذي توقعنا الكرة عليه، مما يتيح لنا استعادة الخط المتجه الأصلي. ويمكن بعد ذلك تخزين هذين الرقمين في SNORM8.
الشكل 2: رسم خرائط ثماني الأوجه (المصدر)
const int BITS = 8
// Assumes the vector is unit length
// sign() function should return positive for 0
for each normal n in mesh:
float invL1Norm = 1.0 / (abs(n.x) + abs(n.y) + abs(n.z));
vec2 res;
if (n.z < 0.0) {
res.x = (1.0 - abs(n.y * invL1Norm)) * sign(n.x);
res.y = (1.0 - abs(n.x * invL1Norm)) * sign(n.y);
} else {
res.x = n.x * invL1Norm;
res.y = n.y * invL1Norm;
}
res = clamp(res * (2^(BITS - 1) - 1), -2^(BITS - 1), 2^(BITS - 1) - 1)
ويكون إلغاء الضغط في أداة تظليل رأس الرأس (للتحويل إلى الإحداثيات الديكارتية مرة أخرى) غير مكلف؛ في معظم أجهزة الجوال الحديثة، لم نلاحظ أي انخفاض كبير في الأداء عند تطبيق هذا الأسلوب. عملية فك الضغط في أداة تظليل رأس الصفحة:
//Additional Optimization: twitter.com/Stubbesaurus/status/937994790553227264
vec3 oct_to_vec(vec2 e):
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return v;
ويمكن أيضًا استخدام هذه الطريقة لتخزين مساحة ظل الزاوية بالكامل، وذلك باستخدام هذه التقنية لتخزين الخط المتجه العادي وخط المماس باستخدام vec2<SNORM8
> ولكنك ستحتاج إلى إيجاد طريقة لتخزين اتجاه ظل التمام (المطلوب في السيناريو الشائع الذي تكون فيه إحداثيات الأشعة فوق البنفسجية معكوسة على أحد النماذج). لتنفيذ ذلك، يمكنك ربط أحد مكوّنات ترميز خطّ المماس لكي يكون موجبًا دائمًا، ثم قلب علامته إذا كنت بحاجة إلى قلب اتجاه التماس والبحث عن ذلك في أداة تظليل رأس المماس:
const int BITS = 8
const float bias = 1.0 / (2^(BITS - 1) - 1)
// Compressing
for each normal n in mesh:
//encode to octahedron, result in range [-1, 1]
vec2 res = vec_to_oct(n);
// map y to always be positive
res.y = res.y * 0.5 + 0.5;
// add a bias so that y is never 0 (sign in the vertex shader)
if (res.y < bias)
res.y = bias;
// Apply the sign of the binormal to y, which was computed elsewhere
if (binormal_sign < 0)
res.y *= -1;
res = clamp(res * (2^(BITS - 1) - 1), -2^(BITS - 1), 2^(BITS - 1) - 1)
// Vertex shader decompression
vec2 encode = vec2(tangent_encoded.x, abs(tangent_encoded.y) * 2.0 - 1.0));
vec3 tangent_real = oct_to_vec3(encode);
float binormal_sign = sign(tangent_encode.y);
التنسيق | حجم الملف | |
---|---|---|
قبل | vec3<float32 > |
12 بايت |
بعد | vec2<SNORM8 > |
2 بايت |
إحداثيات Vertex للأشعة فوق البنفسجية
يتم عادةً تخزين إحداثيات الأشعة فوق البنفسجية المستخدمة في تخطيط الهيئة (من بين أمور أخرى) باستخدام أعداد عائمة 32 بت. ويؤدي ضغطها باستخدام عدد عائم 16 بت إلى حدوث مشاكل في الدقة في ما يتعلق بالزخارف الأكبر من 1024×1024؛ تعني دقة النقطة العائمة بين [0.5، 1.0] أنّ القيم ستزيد بمقدار أكبر من 1 بكسل.
الأسلوب الأفضل هو استخدام الأعداد الصحيحة غير الموقعة والمواطَنة (UNORM)، وتحديدًا UNORM16؛ ويوفر ذلك توزيعًا موحدًا عبر نطاق إحداثيات الهيئة بالكامل، ويدعم الزخارف حتى 65536×65536! وهذا يفترض أن تقع إحداثيات الهيئة ضمن النطاق [0.0، 1.0] لكل عنصر، وهذا قد لا يكون هو الحال حسب الشبكة المتداخلة (على سبيل المثال، يمكن للجدران أن تستخدم إحداثيات التفاف الزخرفة التي تتجاوز 1.0)، لذا يُرجى وضع ذلك في الاعتبار عند البحث عن هذا الأسلوب. ستبدو دالة التحويل على النحو التالي:
const int BITS = 16
for each vertex_uv V in mesh:
V *= clamp(2^BITS - 1, 0, 2^BITS - 1); // float to integer value conversion
التنسيق | حجم الملف | |
---|---|---|
قبل | vec2<float32 > |
8 بايت |
بعد | vec2<UNORM16 > |
4 بايت |
نتائج ضغط Vertex
وقد أدت تقنيات ضغط رأس الصفحة هذه إلى انخفاض بنسبة 66% في مساحة تخزين ذاكرة رأس الصفحة، بالانتقال من 48 بايت إلى 16 بايت. وقد تبيَّن لنا ذلك على النحو التالي:
- معدّل نقل بيانات ذاكرة Vertex لقراءة:
- الربط: 27 غيغابايت/ثانية إلى 9 غيغابايت/ثانية
- العرض: 4.5 مليار/ثانية إلى 1.5 غيغا بايت/ثانية
- أكشاك إحضار Vertex:
- الربط: من 50% إلى 0%
- العرض: من 90% إلى 90%
- متوسط وحدات البايت/Vertex:
- الربط: من 48 مليار إلى 16 مليار
- العرض: من 52B إلى 18B
الشكل 3: عرض "أداة فحص وحدة معالجة الرسومات" في Android للرؤوس غير المضغوطة
الشكل 4: عرض "أداة فحص وحدة معالجة الرسومات" في Android للرؤوس المضغوطة
تقسيم بث Vertex
تساعد ميزة "تقسيم بث Vertex" على تحسين تنظيم البيانات في المخزن المؤقت للرأس. هذا تحسين لأداء ذاكرة التخزين المؤقت، وإحداث فرق في وحدات معالجة الرسومات (GPU) المستندة إلى مربّعات والتي تتوفّر عادةً في أجهزة Android، لا سيما في أثناء خطوة التجميع في عملية العرض.
تعمل وحدات معالجة الرسومات المستنِدة إلى المربّعات على إنشاء أداة تظليل تحتسب إحداثيات الجهاز التي تمت تسويتها استنادًا إلى أداة تظليل رأس الشاشة التي يتم توفيرها لتنفيذ عملية الربط. يتم تنفيذه أولاً على كل رأس في المشهد، سواء كان مرئيًا أم لا. وبالتالي، فإن إبقاء بيانات موضع الرأس متجاورة في الذاكرة يمثل ميزة إضافية كبيرة. أماكن أخرى يمكن أن يكون تخطيط البث الرأسي هذا مفيدًا بالنسبة لتمريرات الظل، حيث تحتاج عادةً فقط إلى بيانات الموضع لحسابات الظل، بالإضافة إلى تجاوزات العمق، وهي تقنية تُستخدم عادةً لعرض وحدة التحكم/سطح المكتب؛ يمكن أن يكون تخطيط البث الرأسي هذا مكسبًا لفئات متعددة من محرك العرض!
يتضمّن تقسيم البث إعداد المخزن المؤقت للرأس مع قسم متجاورة من بيانات موضع الرأس وقسم آخر يحتوي على سمات رأس متداخلة. عادةً ما تقوم معظم التطبيقات بإعداد مخازنها الاحتياطية التي تتداخل بالكامل مع جميع السمات. يوضح هذا العنصر المرئي الفرق:
Before:
|Position1/Normal1/Tangent1/UV1/Position2/Normal2/Tangent2/UV2......|
After:
|Position1/Position2...|Normal1/Tangent1/UV1/Normal2/Tangent2/UV2...|
إن النظر إلى كيفية استرجاع وحدة معالجة الرسومات للبيانات الرأسية يساعدنا في فهم فوائد تقسيم البث افتراض ما يلي من أجل الجدال:
- أسطر ذاكرة التخزين المؤقت 32 بايت (حجم شائع جدًا)
- يتكون تنسيق Vertex مما يلي:
- الموضع، vec3<float32> = 12 بايت
- vec3<float32> العادي = 12 بايت
- إحداثيات الأشعة فوق البنفسجية vec2<float32> = 8 بايت
- إجمالي الحجم = 32 بايت
عندما تجلب وحدة معالجة الرسومات البيانات من الذاكرة للتجميع، ستسحب سطر ذاكرة التخزين المؤقت بسعة 32 بايت لتشغيله. بدون تقسيم البث الرأسي، سيتم استخدام أول 12 بايت فقط من سطر ذاكرة التخزين المؤقت هذا للتجميع، وتجاهل الـ 20 بايت الأخرى لأنها تجلب رأس الصفحة التالي. ومن خلال تقسيم البث الرأسي، ستكون مواضع الرأس متجاورة في الذاكرة، لذلك عندما يتم سحب هذا المقطع الذي يبلغ حجمه 32 بايت إلى ذاكرة التخزين المؤقت، سيحتوي في الواقع على موضعَي رأس كاملَين لتشغيلهما قبل الحاجة إلى العودة إلى الذاكرة الرئيسية لجلب المزيد، وهو تحسين بمقدار مرتين.
والآن، إذا دمجنا تقسيم البث الرأسي مع ضغط الرأس، سنقلل حجم موضع رأس واحد إلى 6 بايت، وبالتالي فإن سطر ذاكرة التخزين المؤقت الفردي بسعة 32 بايت والذي يتم سحبه من ذاكرة النظام سيحتوي على 5 مواضع رأسية للعمل عليها، وهو تحسين بمقدار 5 أضعاف!
نتائج تقسيم بث Vertex
- معدّل نقل بيانات ذاكرة Vertex لقراءة:
- الربط: 27 غيغابايت/ثانية إلى 6.5 غيغابايت/ثانية
- العرض: 4.5 غيغابايت/ثانية إلى 4.5 غيغابايت/ثانية
- أكشاك إحضار Vertex:
- الربط: من 40% إلى 0%
- العرض: من 90% إلى 90%
- متوسط وحدات البايت/Vertex:
- الربط: من 48 مليار إلى 12 مليار
- العرض: 52B إلى 52B
الشكل 5: عرض "أداة فحص وحدة معالجة الرسومات" في Android لأحداث البث غير المقسّمة
الشكل 6: عرض "أداة فحص وحدة معالجة الرسومات في Android" لمصادر البيانات المقسّمة
النتائج المُركّبة
- معدّل نقل بيانات ذاكرة Vertex لقراءة:
- الربط: 25 غيغابايت/ثانية إلى 4.5 غيغابايت/ثانية
- العرض: 4.5 غيغابايت/ثانية إلى 1.7 غيغابايت/ثانية
- أكشاك إحضار Vertex:
- الربط: من 41% إلى 0%
- العرض: من 90% إلى 90%
- متوسط وحدات البايت/Vertex:
- الربط: من 48 مليار إلى 8 مليار
- العرض: من 52B إلى 19B
الشكل 7: عرض "أداة فحص وحدة معالجة الرسومات" في Android لأحداث البث غير المضغوطة وغير المضغوطة
الشكل 8: طريقة عرض "أداة فحص وحدة معالجة الرسومات" في Android لأحداث البث المباشر المضغوطة والمنقسمة
اعتبارات إضافية
بيانات المخزن المؤقت للفهرس 16 مقابل 32 بت
- قم دائمًا بتقسيم/قطع الشبكات كي تتناسب مع المخزن المؤقت بفهرس 16 بت (بحد أقصى 65536 رأسًا فريدًا). سيساعد ذلك في العرض المفهرَس على الأجهزة الجوّالة، لأنّ الحصول على بيانات رأس الصفحة هو بتكلفة أقل، وسيؤدي إلى استهلاك قدر أقل من الطاقة.
تنسيقات سمة المخزن المؤقت Vertex غير متاحة
- لا تتوفّر تنسيقات الرأس المخفّضة على نطاق واسع على الأجهزة الجوّالة، وعند استخدامها، قد يؤدي استخدامها إلى خفض مستوى الأداء مقارنةً ببرامج التشغيل التي تحاول محاكاتها إذا لم تكن متوافقة مع الأجهزة. ابحث دائمًا عن SNORM وادفع تكلفة ALU ضئيلة لفك ضغطها.