मैं GLSL शेड कैसे डिबग कर सकता हूं?


45

नॉन-ट्रिवियल शेड्स लिखते समय (जिस तरह नॉन-ट्रिवियल कोड का कोई अन्य टुकड़ा लिखते समय), लोग गलती करते हैं। [उद्धरण वांछित] हालाँकि, मैं इसे किसी भी अन्य कोड की तरह डिबग नहीं कर सकता - आप केवल gdb या विज़ुअल स्टूडियो डीबगर को सब के बाद संलग्न नहीं कर सकते। आप प्रिंटफ डीबगिंग भी नहीं कर सकते, क्योंकि कंसोल आउटपुट का कोई रूप नहीं है। आम तौर पर मैं जो डेटा प्रस्तुत करना चाहता हूं, उसे रंग के रूप में देखना चाहता हूं, लेकिन यह एक बहुत ही अल्पविकसित और शौकिया समाधान है। मुझे यकीन है कि लोग बेहतर समाधान के साथ आए हैं।

तो मैं वास्तव में एक shader कैसे डिबग कर सकता हूं? वहाँ एक shader के माध्यम से कदम का एक तरीका है? क्या मैं एक विशिष्ट शीर्ष / आदिम / टुकड़े पर shader के निष्पादन को देख सकता हूं?

(यह प्रश्न विशेष रूप से शेडर कोड के डिबग करने के तरीके के बारे में है कि कोई "सामान्य" कोड को कैसे डिबग करेगा, न कि राज्य परिवर्तन जैसी चीजों को डीबग करने के बारे में।)


क्या आपने gDEBugger में देखा है? साइट का हवाला देते हुए: "gDEBugger एक उन्नत OpenGL और OpenCL डीबगर, प्रोफाइलर और मेमोरी एनालाइज़र है। GDEBugger वह करता है जो कोई अन्य टूल नहीं कर सकता है - आपको ओपनजीएल और ओपनवेयर APIs के शीर्ष पर एप्लिकेशन गतिविधि का पता लगाने देता है और देखता है कि सिस्टम कार्यान्वयन के भीतर क्या हो रहा है। " दी, कोई वी.एस. स्टाइल डिबगिंग / स्टेपिंग कोड के माध्यम से नहीं करता है, लेकिन यह आपको कुछ जानकारी दे सकता है कि आपका शेडर क्या करता है (या क्या करना चाहिए)। क्रायटेक ने डायरेक्ट शेडर "डीबगिंग" के लिए रेंडरडोक (फ्री, लेकिन एचएलएसएल शेड्स के लिए कड़ाई से, इसलिए शायद आपके लिए प्रासंगिक नहीं) के लिए एक समान उपकरण जारी किया।
बर्ट

@ बर्ट ह्म हाँ, मुझे लगता है कि gDEBugger OpenGL के WebGL- इंस्पेक्टर के बराबर है? मैंने बाद का इस्तेमाल किया है। यह बेहद उपयोगी है, लेकिन यह निश्चित रूप से OpenGL कॉल और shader निष्पादन की तुलना में राज्य परिवर्तनों को डीबग करना अधिक है।
मार्टिन एंडर

1
मैंने कोई भी WebGL प्रोग्रामिंग नहीं की है और इसलिए मैं WebGL- इंस्पेक्टर से परिचित नहीं हूं। GDEBugger के साथ आप कम से कम अपनी shader पाइपलाइन की पूरी स्थिति का निरीक्षण कर सकते हैं जिसमें बनावट मेमोरी, वर्टेक्स डेटा आदि शामिल हैं। फिर भी, कोड afaik के माध्यम से कोई वास्तविक कदम नहीं है।
बर्ट

gDEBugger बहुत पुराना है और कुछ समय से समर्थित नहीं है। यदि आप फ्रेम और GPU राज्य विश्लेषण से देख रहे हैं, तो यह एक अन्य प्रश्न है जो दृढ़ता से संबंधित है: computergraphics.stackexchange.com/questions/23/…
cifz

: यहाँ एक डिबगिंग विधि मैं एक संबंधित सवाल करने का सुझाव दिया है stackoverflow.com/a/29816231/758666
WIP

जवाबों:


26

जहाँ तक मुझे पता है कि कोई भी उपकरण नहीं है जो आपको एक shader में कोड के माध्यम से कदम रखने की अनुमति देता है (इसके अलावा, उस स्थिति में आपको सिर्फ एक पिक्सेल / शीर्ष का चयन करने में सक्षम होना चाहिए जिसे आप "डीबग" करना चाहते हैं, निष्पादन की संभावना है उस पर निर्भर करता है)।

जो मैं व्यक्तिगत रूप से करता हूं वह बहुत ही हैकिंग वाला "रंगीन डिबगिंग" है। तो मैं #if DEBUG / #endifगार्ड के साथ गतिशील शाखाओं का एक गुच्छा छिड़कता हूं जो मूल रूप से कहते हैं

#if DEBUG
if( condition ) 
    outDebugColour = aColorSignal;
#endif

.. rest of code .. 

// Last line of the pixel shader
#if DEBUG
OutColor = outDebugColour;
#endif

तो आप इस तरह से डिबग जानकारी का "अवलोकन" कर सकते हैं। मैं आमतौर पर विभिन्न अधिक जटिल घटनाओं या गैर-द्विआधारी सामान का परीक्षण करने के लिए विभिन्न "रंग कोड" के बीच लेरिंग या सम्मिश्रण जैसी कई चालें करता हूं।

इस "फ्रेमवर्क" में मुझे सामान्य मामलों के लिए निश्चित सम्मेलनों का एक सेट होना भी उपयोगी लगता है ताकि अगर मुझे लगातार वापस नहीं जाना पड़े और मैं किस रंग से जुड़ा हुआ हूं, इसकी जांच कर सकूं। महत्वपूर्ण चीज़ों में shader कोड के हॉट-रीलोडिंग के लिए एक अच्छा समर्थन है, इसलिए आप अपने ट्रैक किए गए डेटा / ईवेंट को लगभग अंतःक्रियात्मक रूप से बदल सकते हैं और डीबग विज़ुअलाइज़ेशन पर आसानी से / बंद कर सकते हैं।

यदि आपको किसी ऐसी चीज़ को डिबग करने की आवश्यकता है जिसे आप स्क्रीन पर आसानी से प्रदर्शित नहीं कर सकते हैं, तो आप हमेशा ऐसा ही कर सकते हैं और अपने परिणामों का निरीक्षण करने के लिए एक फ्रेम विश्लेषक उपकरण का उपयोग कर सकते हैं। मैंने उनमें से एक जोड़े को इस अन्य प्रश्न के उत्तर के रूप में सूचीबद्ध किया है

Obv, यह कहे बिना जाता है कि अगर मैं पिक्सेल शेडर या कंप्यूट शेडर "डीबगिंग" नहीं कर रहा हूँ, तो मैं इसे "डीबगचलर" जानकारी पाइप लाइन भर में इंटरपोल किए बिना ( flat कीवर्ड के साथ GLSL में )

फिर से, यह बहुत हैकिंग है और उचित डिबगिंग से बहुत दूर है, लेकिन मैं वह है जो किसी भी उचित विकल्प को नहीं जानने के साथ अटका हुआ है।


जब वे उपलब्ध हों तो आप SSBOs का उपयोग अधिक लचीले आउटपुट स्वरूप प्राप्त करने के लिए कर सकते हैं जहाँ आपको रंगों में एनकोड करने की आवश्यकता नहीं है। हालाँकि, इस दृष्टिकोण की बड़ी खामी यह है कि यह कोड को बदल देता है जो बग को छिपा सकता है / बदल सकता है, खासकर जब यूबी शामिल हो। +1 फिर भी, इसके लिए सबसे सीधा तरीका उपलब्ध है।
कोई नहीं

9

जीएलएसएल-डीबगर भी है । यह एक डिबगर है जिसे "जीएलएसएल डेविल" के रूप में जाना जाता है।

डिबगर खुद GLSL कोड के लिए ही नहीं, बल्कि OpenGL के लिए भी बहुत काम का है। आपके पास Shader स्विच पर ड्रा कॉल और ब्रेक के बीच कूदने की क्षमता है। यह आपको OpenGL द्वारा संचारित त्रुटि संदेश भी दिखाता है जो अनुप्रयोग में वापस आता है।


2
ध्यान दें कि 2018-08-07 के अनुसार, यह GLSL 1.2 से अधिक कुछ भी समर्थन नहीं करता है, और यह सक्रिय रूप से बनाए नहीं रखा गया है।
रुस्लान

उस टिप्पणी ने मुझे उदास कर दिया :(
rdelfin

परियोजना खुला स्रोत है और वास्तव में इसे आधुनिक बनाने में मदद करना पसंद करेगा। कोई अन्य उपकरण नहीं है जो यह करता है।
ज़ेनोनोफ्राक्टिकस

7

GPU विक्रेताओं द्वारा AMD के CodeXL या NVIDIA के nSight / Linux GFX डीबगर जैसे कई प्रसाद हैं, जो शेड्स के माध्यम से कदम रखने की अनुमति देते हैं, लेकिन संबंधित विक्रेता के हार्डवेयर से बंधे हैं।

मुझे ध्यान दें कि, हालांकि वे लिनक्स के तहत उपलब्ध हैं, मुझे हमेशा उनका उपयोग करने के साथ बहुत कम सफलता मिली। मैं विंडोज के तहत स्थिति पर टिप्पणी नहीं कर सकता।

जो विकल्प मैं हाल ही में उपयोग करने के लिए आया हूं, वह है कि मैं अपने shader कोड को संशोधित करूं#includes और GLSL और C ++ & glm के सामान्य उपसमूह में सम्मिलित कोड को प्रतिबंधित करूं

जब मैं किसी समस्या से टकराता हूं तो मैं इसे दूसरे डिवाइस पर पुन: पेश करने की कोशिश करता हूं ताकि यह देखा जा सके कि समस्या वही है जो तर्क त्रुटि (ड्राइवर समस्या / अपरिभाषित व्यवहार के बजाय) पर संकेत देती है। GPU (जैसे गलत तरीके से बंधे बफ़र्स आदि) से गलत डेटा पास करने की भी संभावना है, जो मैं आमतौर पर या तो आउटपुट डिबगिंग जैसे cifz उत्तर में या फिर एपिट्रेस के माध्यम से डेटा का निरीक्षण करके निकालता हूं

जब यह एक तर्क त्रुटि होती है तो मैं सीपीयू पर सीपीयू से स्थिति को फिर से उसी डेटा के साथ सीपीयू पर कॉल करके स्थिति को फिर से बनाने की कोशिश करता हूं। फिर मैं सीपीयू पर इसके माध्यम से कदम बढ़ा सकता हूं।

कोड की प्रतिरूपकता के आधार पर आप इसके लिए एकमत लिखने की कोशिश कर सकते हैं और GPU रन और CPU रन के बीच परिणामों की तुलना कर सकते हैं। हालांकि, आपको यह जानना होगा कि ऐसे कोने मामले हैं जहां सी ++ जीएलएसएल की तुलना में अलग व्यवहार कर सकता है, इस प्रकार आपको इन तुलनाओं में झूठी सकारात्मकता प्रदान करता है।

अंत में, जब आप किसी अन्य डिवाइस पर समस्या को पुन: उत्पन्न नहीं कर सकते, तो आप केवल नीचे खोदना शुरू कर सकते हैं जहां से अंतर आता है। यूनीटैस्ट आपको ऐसा करने में मदद कर सकता है जहां ऐसा होता है, लेकिन अंत में आपको शायद शेफ से अतिरिक्त डिबग जानकारी लिखने की आवश्यकता होगी जैसे कि सिफज़ जवाब में

और आपको यहाँ एक सिंहावलोकन देने के लिए मेरी डिबगिंग प्रक्रिया का फ़्लोचार्ट है: पाठ में वर्णित प्रक्रिया का प्रवाह चार्ट

इसे बंद करने के लिए यहां पर यादृच्छिक पेशेवरों और विपक्षों की सूची दी गई है:

समर्थक

  • सामान्य डिबगर के माध्यम से कदम
  • अतिरिक्त (अक्सर बेहतर) संकलक निदान

चोर


यह एक महान विचार है, और शायद निकटतम आप एकल-कदम वाले shader कोड प्राप्त कर सकते हैं। मुझे आश्चर्य है कि अगर एक सॉफ्टवेयर रेंडरर (मेसा?) के माध्यम से चल रहा है तो समान लाभ होगा?

@racarate: मैंने इसके बारे में भी सोचा था लेकिन अभी तक कोशिश करने का समय नहीं था। मैं मीसा का कोई विशेषज्ञ नहीं हूं, लेकिन मुझे लगता है कि शेडर को डिबग करना कठिन हो सकता है क्योंकि शेडर डिबग की जानकारी किसी तरह डिबगर तक पहुंच जाती है। तब फिर से, शायद मेसा के लोगों के पास पहले से ही मीसा को डिबग करने के लिए एक इंटरफ़ेस है :)
कोई भी

5

हालांकि यह वास्तव में OpenGL shader के माध्यम से कदम उठाने के लिए संभव नहीं लगता है, संकलन परिणाम प्राप्त करना संभव है।
निम्नलिखित एंड्रॉइड कार्डबोर्ड नमूने से लिया गया है ।

while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
    Log.e(TAG, label + ": glError " + error);
    throw new RuntimeException(label + ": glError " + error);

यदि आपका कोड ठीक से संकलित है, तो आपके पास कार्यक्रम की स्थिति को संप्रेषित करने के एक अलग तरीके को आज़माने के लिए बहुत कम विकल्प हैं। आप संकेत कर सकते हैं कि कोड का एक हिस्सा पहुंच गया था, उदाहरण के लिए, एक शीर्ष का रंग बदलना या एक अलग बनावट का उपयोग करना। जो अजीब है, लेकिन अभी के लिए एकमात्र तरीका लगता है।

संपादित करें: WebGL के लिए, मैं इस परियोजना को देख रहा हूं , लेकिन मैंने केवल इसे पाया है ... इसके लिए वाउच नहीं कर सकता।


3
हम्म, मुझे पता है कि मैं संकलक त्रुटियाँ प्राप्त कर सकता हूँ। मैं बेहतर रनिंग डिबगिंग की उम्मीद कर रहा था। मैंने पहले भी WebGL इंस्पेक्टर का उपयोग किया है, लेकिन मेरा मानना ​​है कि यह केवल आपको राज्य परिवर्तन दिखाता है, लेकिन आप एक shader मंगलाचरण में नहीं देख सकते। मुझे लगता है कि यह प्रश्न में स्पष्ट हो सकता था।
मार्टिन एंडर

2

यह StackOverflow में उसी प्रश्न के मेरे उत्तर की कॉपी-पेस्ट है


इस उत्तर के निचले भाग में GLSL कोड का एक उदाहरण है जो floatIEEE 754 को एन्कोडिंग के रूप में पूर्ण मूल्य को रंग के रूप में आउटपुट करने की अनुमति देता है binary32। मैं इसे इस प्रकार उपयोग करता हूं (यह स्निपेट yyमॉडलव्यू मैट्रिक्स का घटक देता है ):

vec4 xAsColor=toColor(gl_ModelViewMatrix[1][1]);
if(bool(1)) // put 0 here to get lowest byte instead of three highest
    gl_FrontColor=vec4(xAsColor.rgb,1);
else
    gl_FrontColor=vec4(xAsColor.a,0,0,1);

स्क्रीन पर इसे प्राप्त करने के बाद, आप बस किसी भी रंग बीनने वाले को ले जा सकते हैं, रंग को HTML के रूप में प्रारूपित कर सकते हैं ( यदि आपको उच्च परिशुद्धता की आवश्यकता नहीं है, तो मूल्य 00को जोड़कर rgb, और यदि आप करते हैं तो निम्न बाइट प्राप्त करने के लिए दूसरा पास कर सकते हैं), और आपको floatIEEE 754 के रूप में हेक्साडेसिमल प्रतिनिधित्व मिलता है binary32

यहाँ वास्तविक कार्यान्वयन है toColor():

const int emax=127;
// Input: x>=0
// Output: base 2 exponent of x if (x!=0 && !isnan(x) && !isinf(x))
//         -emax if x==0
//         emax+1 otherwise
int floorLog2(float x)
{
    if(x==0.) return -emax;
    // NOTE: there exist values of x, for which floor(log2(x)) will give wrong
    // (off by one) result as compared to the one calculated with infinite precision.
    // Thus we do it in a brute-force way.
    for(int e=emax;e>=1-emax;--e)
        if(x>=exp2(float(e))) return e;
    // If we are here, x must be infinity or NaN
    return emax+1;
}

// Input: any x
// Output: IEEE 754 biased exponent with bias=emax
int biasedExp(float x) { return emax+floorLog2(abs(x)); }

// Input: any x such that (!isnan(x) && !isinf(x))
// Output: significand AKA mantissa of x if !isnan(x) && !isinf(x)
//         undefined otherwise
float significand(float x)
{
    // converting int to float so that exp2(genType) gets correctly-typed value
    float expo=float(floorLog2(abs(x)));
    return abs(x)/exp2(expo);
}

// Input: x\in[0,1)
//        N>=0
// Output: Nth byte as counted from the highest byte in the fraction
int part(float x,int N)
{
    // All comments about exactness here assume that underflow and overflow don't occur
    const float byteShift=256.;
    // Multiplication is exact since it's just an increase of exponent by 8
    for(int n=0;n<N;++n)
        x*=byteShift;

    // Cut higher bits away.
    // $q \in [0,1) \cap \mathbb Q'.$
    float q=fract(x);

    // Shift and cut lower bits away. Cutting lower bits prevents potentially unexpected
    // results of rounding by the GPU later in the pipeline when transforming to TrueColor
    // the resulting subpixel value.
    // $c \in [0,255] \cap \mathbb Z.$
    // Multiplication is exact since it's just and increase of exponent by 8
    float c=floor(byteShift*q);
    return int(c);
}

// Input: any x acceptable to significand()
// Output: significand of x split to (8,8,8)-bit data vector
ivec3 significandAsIVec3(float x)
{
    ivec3 result;
    float sig=significand(x)/2.; // shift all bits to fractional part
    result.x=part(sig,0);
    result.y=part(sig,1);
    result.z=part(sig,2);
    return result;
}

// Input: any x such that !isnan(x)
// Output: IEEE 754 defined binary32 number, packed as ivec4(byte3,byte2,byte1,byte0)
ivec4 packIEEE754binary32(float x)
{
    int e = biasedExp(x);
    // sign to bit 7
    int s = x<0. ? 128 : 0;

    ivec4 binary32;
    binary32.yzw=significandAsIVec3(x);
    // clear the implicit integer bit of significand
    if(binary32.y>=128) binary32.y-=128;
    // put lowest bit of exponent into its position, replacing just cleared integer bit
    binary32.y+=128*int(mod(float(e),2.));
    // prepare high bits of exponent for fitting into their positions
    e/=2;
    // pack highest byte
    binary32.x=e+s;

    return binary32;
}

vec4 toColor(float x)
{
    ivec4 binary32=packIEEE754binary32(x);
    // Transform color components to [0,1] range.
    // Division is inexact, but works reliably for all integers from 0 to 255 if
    // the transformation to TrueColor by GPU uses rounding to nearest or upwards.
    // The result will be multiplied by 255 back when transformed
    // to TrueColor subpixel value by OpenGL.
    return vec4(binary32)/255.;
}

1

मेरे लिए जो समाधान काम किया गया है, वह C ++ में shader कोड का संकलन है - जैसा कि किसी ने भी उल्लेख नहीं किया है। यह एक जटिल कोड पर काम करते समय बहुत कुशल था, भले ही इसके लिए सेटअप की आवश्यकता हो।

मैं ज्यादातर एचएलएसएल कम्प्यूट शेड्स के साथ काम कर रहा हूं जिसके लिए मैंने यहां एक प्रूफ-ऑफ-कॉन्सेप्ट लाइब्रेरी विकसित की है:

https://github.com/cezbloch/shaderator

यह डायरेक्टएक्स एसडीके सैम्पल के एक कंपाउंडर शेडर पर प्रदर्शित करता है कि एचएलएसएल डीबगिंग जैसे सी ++ को कैसे सक्षम किया जाए और टेस्ट टेस्ट कैसे सेटअप किया जाए।

सीएल + से जीएलएसएल कम्प्यूट शेडर का संकलन एचएलएसएल की तुलना में आसान लगता है। मुख्य रूप से एचएलएसएल में वाक्य रचना के कारण। मैंने GLSL रे ट्रेसर कम्प्यूटर शेडर पर निष्पादन योग्य यूनिट टेस्ट का एक तुच्छ उदाहरण जोड़ा है जो आप ऊपर लिंक के तहत Shaderator प्रोजेक्ट के स्रोतों में भी पा सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.