ฉันจะแก้ไขข้อบกพร่อง GLSL ได้อย่างไร


45

เมื่อเขียนเฉดที่ไม่สำคัญ (เช่นเดียวกับเมื่อเขียนโค้ดอื่นที่ไม่ใช่เรื่องไร้สาระ) ผู้คนทำผิดพลาด [อ้างอิงที่จำเป็น]อย่างไรก็ตามฉันไม่สามารถแก้จุดบกพร่องเช่นเดียวกับรหัสอื่น ๆ - คุณไม่สามารถแนบ gdb หรือดีบักเกอร์ Visual Studio หลังจากทั้งหมด คุณไม่สามารถทำการดีบัก printf ได้เนื่องจากไม่มีรูปแบบเอาต์พุตคอนโซล สิ่งที่ฉันมักจะทำคือการแสดงข้อมูลที่ฉันต้องการดูเป็นสี แต่นั่นเป็นวิธีแก้ปัญหาขั้นพื้นฐานและชำนาญ ฉันแน่ใจว่าผู้คนจะคิดวิธีแก้ปัญหาที่ดีกว่า

ดังนั้นฉันจะแก้ไขข้อบกพร่องของ shader ได้อย่างไร มีวิธีก้าวผ่านตัว Shader หรือไม่? ฉันสามารถดูการดำเนินการของ shader บนจุดสุดยอด / ดั้งเดิม / ชิ้นส่วนที่เฉพาะเจาะจงได้หรือไม่

(คำถามนี้เกี่ยวกับวิธีการดีบักโค้ด shader โดยเฉพาะคล้ายกับวิธีที่จะดีบั๊กโค้ด "ปกติ" ไม่ใช่เกี่ยวกับการดีบักสิ่งต่าง ๆ เช่นการเปลี่ยนแปลงสถานะ)


คุณดูเป็น gDEBugger หรือไม่? การอ้างถึงไซต์: "gDEBugger เป็น OpenGL ขั้นสูงและ OpenCL Debugger, Profiler และ Memory Analyzer gDEBugger ทำในสิ่งที่ไม่มีเครื่องมืออื่นสามารถทำได้ - ช่วยให้คุณสามารถติดตามกิจกรรมของแอปพลิเคชันด้านบนของ OpenGL และ OpenCL API และดูว่าเกิดอะไรขึ้น " ได้รับไม่มีการดีบักสไตล์ VS / การก้าวผ่านโค้ด แต่อาจให้ข้อมูลเชิงลึกเกี่ยวกับสิ่งที่ shader ของคุณทำ (หรือควรทำ) Crytec เปิดตัวเครื่องมือที่คล้ายกันสำหรับ "ดีบั๊ก" Direct shader ที่เรียกว่า RenderDoc (ฟรี แต่อย่างเคร่งครัดสำหรับ HLSL shaders ดังนั้นอาจไม่เกี่ยวข้องกับคุณ)
Bert

@Bert อืมใช่ฉันเดาว่า gDEBugger นั้นเทียบเท่ากับ OpenGL กับ WebGL-Inspector หรือเปล่า ฉันใช้หลัง มันมีประโยชน์อย่างมาก แต่แน่นอนว่าการดีบักการโทร OpenGL และการเปลี่ยนแปลงสถานะมากกว่าการประมวลผล shader
Martin Ender

1
ฉันไม่เคยเขียนโปรแกรม WebGL เลยด้วยเหตุนี้ฉันจึงไม่คุ้นเคยกับ WebGL- Inspector ด้วย gDEBugger อย่างน้อยคุณสามารถตรวจสอบสถานะทั้งหมดของไปป์ไลน์ shader ของคุณได้รวมถึงหน่วยความจำพื้นผิวข้อมูลจุดสุดยอด ฯลฯ ยังไม่มีการก้าวผ่านรหัส afaik
Bert

gDEBugger นั้นเก่ามากและไม่รองรับมาระยะหนึ่ง หากคุณกำลังมองหาการวิเคราะห์สถานะเฟรมและ GPU คุณเป็นคำถามอื่นที่เกี่ยวข้องอย่างยิ่ง: computergraphics.stackexchange.com/questions/23/…
cifz

ต่อไปนี้เป็นวิธีการแก้ไขข้อบกพร่องที่ฉันแนะนำสำหรับคำถามที่เกี่ยวข้อง: stackoverflow.com/a/29816231/758666
wip

คำตอบ:


26

เท่าที่ฉันรู้ว่าไม่มีเครื่องมือที่อนุญาตให้คุณทำตามขั้นตอนในรหัสใน shader (เช่นในกรณีนี้คุณจะต้องเลือกพิกเซล / จุดยอดที่คุณต้องการจะ "debug" การดำเนินการมีแนวโน้มที่จะ แตกต่างกันไปขึ้นอยู่กับว่า)

สิ่งที่ฉันทำเองก็คือ "การแก้ไขข้อบกพร่องสีสัน" ที่แฮ็คมาก ดังนั้นฉันจึงโรยพวงกิ่งก้านสาขาด้วย#if DEBUG / #endifยามที่พูด

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

.. rest of code .. 

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

ดังนั้นคุณสามารถ "สังเกต" ข้อมูลการดีบักด้วยวิธีนี้ ฉันมักจะใช้เทคนิคต่าง ๆ เช่นการ lerping หรือการผสมระหว่าง "รหัสสี" ที่หลากหลายเพื่อทดสอบเหตุการณ์ที่ซับซ้อนมากขึ้นหรือสิ่งที่ไม่ใช่ไบนารี

ในกรอบนี้ฉันยังพบว่ามีประโยชน์ที่จะมีชุดของการประชุมคงที่สำหรับกรณีทั่วไปดังนั้นถ้าฉันไม่จำเป็นต้องย้อนกลับไปและตรวจสอบสีที่เกี่ยวข้องกับสิ่งที่ฉัน สิ่งสำคัญคือมีการรองรับการโหลดโค้ด shader ที่ดีดังนั้นคุณสามารถเปลี่ยนข้อมูล / เหตุการณ์ที่ติดตามได้และสามารถเปิด / ปิดการสร้างภาพดีบักได้อย่างง่ายดาย

หากต้องการแก้ไขข้อบกพร่องบางอย่างที่คุณไม่สามารถแสดงบนหน้าจอได้อย่างง่ายดายคุณสามารถทำสิ่งเดียวกันได้ตลอดเวลาและใช้เครื่องมือตัววิเคราะห์เฟรมเดียวเพื่อตรวจสอบผลลัพธ์ของคุณ ฉันแสดงรายการคู่ของพวกเขาเป็นคำตอบของคำถามอื่นนี้

Obv, มันจะไปโดยไม่บอกว่าถ้าฉันไม่ใช่ "debugging" pixel shader หรือ shader คำนวณ, ฉันผ่านข้อมูล "debugColor" นี้ไปทั่วไปป์ไลน์โดยไม่ต้องแก้ไขมัน (ใน GLSL ด้วย flat คีย์เวิร์ด)

อีกครั้งนี้เป็นแฮ็คและไกลจากการดีบักที่เหมาะสม แต่เป็นสิ่งที่ฉันติดอยู่กับไม่รู้ทางเลือกที่เหมาะสม


เมื่อพร้อมใช้งานคุณสามารถใช้ SSBOs เพื่อรับรูปแบบเอาต์พุตที่ยืดหยุ่นมากขึ้นโดยที่คุณไม่จำเป็นต้องเข้ารหัสในสี อย่างไรก็ตามข้อเสียเปรียบครั้งใหญ่ของวิธีนี้คือการเปลี่ยนแปลงรหัสซึ่งสามารถซ่อน / แก้ไขข้อบกพร่องโดยเฉพาะอย่างยิ่งเมื่อเกี่ยวข้องกับ UB +1 อย่างไรก็ตามเนื่องจากเป็นวิธีที่ตรงที่สุดที่มีอยู่
ไม่มีใคร

9

นอกจากนี้ยังมีGLSL-ดีบักเกอร์ มันเป็นตัวดีบักที่เคยรู้จักกันในนาม "GLSL Devil"

ตัวดีบักนั้นมีประโยชน์ไม่เพียง แต่สำหรับรหัส GLSL เท่านั้น แต่สำหรับ OpenGL เองเช่นกัน คุณมีความสามารถในการข้ามไปมาระหว่างการโทรออกและหยุดพักสวิตช์ Shader นอกจากนี้ยังแสดงข้อความแสดงข้อผิดพลาดที่คุณสื่อสารโดย OpenGL กลับไปที่แอปพลิเคชันเอง


2
โปรดทราบว่าตั้งแต่วันที่ 2018-08-07 มันไม่รองรับอะไรที่สูงกว่า GLSL 1.2 และไม่ได้รับการบำรุงรักษาอย่างแข็งขัน
Ruslan

ความคิดเห็นนั้นทำให้ฉันเศร้าอย่างถูกต้อง :(
rdelfin

โครงการเป็นโอเพนซอร์ซและรักที่จะช่วยปรับปรุงมันให้ทันสมัย ไม่มีเครื่องมืออื่นที่สามารถทำได้
XenonofArcticus

7

มีหลายข้อเสนอโดยผู้ขายเช่น GPU ของ AMD มีCodeXLหรือ NVIDIA ของnSight / ลินุกซ์ GFX ดีบักเกอร์ซึ่งจะช่วยให้ก้าวผ่าน shaders แต่จะมีการเชื่อมโยงกับฮาร์ดแวร์ของผู้ขายที่เกี่ยวข้องของ

แจ้งให้ฉันทราบว่าถึงแม้ว่าพวกเขาจะพร้อมใช้งานภายใต้ Linux แต่ฉันก็ประสบความสำเร็จเพียงเล็กน้อยเมื่อใช้ที่นั่น ฉันไม่สามารถแสดงความคิดเห็นกับสถานการณ์ภายใต้ Windows

ตัวเลือกที่ฉันได้มาใช้เมื่อเร็ว ๆ นี้คือการ modularize รหัส Shader ของฉันผ่าน#includesและ จำกัด รหัสรวมถึงการเซตย่อยทั่วไปของ GLSL และ C ++ และGLM

เมื่อฉันประสบปัญหาฉันพยายามสร้างมันบนอุปกรณ์อื่นเพื่อดูว่าปัญหานั้นเหมือนเดิมซึ่งบอกเป็นนัยถึงข้อผิดพลาดทางตรรกะ (แทนที่จะเป็นปัญหาไดรเวอร์ / พฤติกรรมที่ไม่ได้กำหนด) นอกจากนี้ยังมีโอกาสในการส่งผ่านข้อมูลที่ไม่ถูกต้องกับ GPU (เช่นโดยบัฟเฟอร์ที่ถูกผูกไว้ไม่ถูกต้อง ฯลฯ ) ซึ่งผมมักจะออกกฎทั้งโดยการส่งออกการแก้จุดบกพร่องเช่นในคำตอบ cifzหรือโดยการตรวจสอบข้อมูลผ่านapitrace

เมื่อเป็นข้อผิดพลาดทางตรรกะฉันพยายามสร้างสถานการณ์ใหม่จาก GPU บน CPU โดยเรียกรหัสที่รวมอยู่ใน CPU ด้วยข้อมูลเดียวกัน จากนั้นฉันสามารถก้าวผ่านมันบน CPU

สร้างขึ้นบนความเป็นโมดูลของโค้ดคุณยังสามารถลองเขียนสิ่งที่ไม่เหมาะสมที่สุดและเปรียบเทียบผลลัพธ์ระหว่างการรัน GPU และการทำงานของ CPU อย่างไรก็ตามคุณต้องระวังว่ามีมุมกรณีที่ C ++ อาจทำงานแตกต่างจาก GLSL ดังนั้นจึงให้ผลบวกผิด ๆ ในการเปรียบเทียบเหล่านี้

ในที่สุดเมื่อคุณไม่สามารถสร้างปัญหาขึ้นมาบนอุปกรณ์อื่นได้คุณสามารถเริ่มขุดลงในที่ซึ่งความแตกต่างนั้นมาจาก unittests อาจช่วยให้คุณให้แคบลงที่เกิดขึ้นที่ แต่ในท้ายที่สุดคุณอาจจะต้องเขียนออกข้อมูลการแก้ปัญหาเพิ่มเติมจาก Shader เช่นในคำตอบ cifz

และเพื่อให้ภาพรวมที่นี่เป็นผังของกระบวนการแก้ไขข้อบกพร่องของฉัน: แผนภูมิการไหลของขั้นตอนที่อธิบายไว้ในข้อความ

ในการปัดเศษนี่เป็นรายการของข้อดีและข้อเสียแบบสุ่ม:

มือโปร

  • ผ่านขั้นตอนการดีบักเกอร์ปกติ
  • การวิเคราะห์คอมไพเลอร์เพิ่มเติม (มักจะดีกว่า)

แย้ง


นี่เป็นความคิดที่ดีและอาจใกล้เคียงที่สุดที่คุณสามารถเข้าถึงรหัส shader แบบก้าวเดียว ฉันสงสัยว่าทำงานผ่านตัวแสดงซอฟต์แวร์ (Mesa?) จะมีประโยชน์เหมือนกันหรือไม่

@ คาราเต้: ฉันคิดเกี่ยวกับสิ่งนั้นเช่นกัน แต่ยังไม่มีเวลาลอง ฉันไม่มีความเชี่ยวชาญใน mesa แต่ฉันคิดว่ามันอาจเป็นเรื่องยากที่จะดีบัก shader เนื่องจากข้อมูลการดีบัก shader นั้นจะต้องไปถึงตัวดีบัก จากนั้นอีกครั้งบางทีคนที่ mesa มีอินเทอร์เฟซสำหรับที่จะแก้ปัญหา mesa เอง :)
ไม่มีใคร

5

แม้ว่ามันจะเป็นไปไม่ได้ที่จะก้าวผ่าน shader ของ OpenGL แต่ก็เป็นไปได้ที่จะได้รับผลการรวบรวม
ต่อไปนี้จะถูกนำมาจากตัวอย่างของ Android กระดาษแข็ง

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

หากรหัสของคุณคอมไพล์อย่างถูกต้องแสดงว่าคุณมีทางเลือกน้อย แต่ลองใช้วิธีการสื่อสารสถานะต่าง ๆ ของโปรแกรมกับคุณ คุณสามารถส่งสัญญาณว่าถึงบางส่วนของรหัสโดยการเปลี่ยนสีของจุดสุดยอดหรือการใช้พื้นผิวที่แตกต่างกัน ซึ่งน่าอึดอัดใจ แต่ดูเหมือนจะเป็นวิธีเดียวในตอนนี้

แก้ไข: สำหรับ WebGL ฉันกำลังดูโครงการนี้แต่ฉันเพิ่งพบมัน ... ไม่สามารถรับรองได้


3
หืมมมฉันรู้ว่าฉันสามารถรับข้อผิดพลาดของคอมไพเลอร์ได้ ฉันหวังว่าการดีบักรันไทม์ที่ดีขึ้น ฉันเคยใช้ตัวตรวจสอบ WebGL ในอดีต แต่ฉันเชื่อว่ามันจะแสดงเฉพาะการเปลี่ยนแปลงของคุณ แต่คุณไม่สามารถตรวจสอบการขอ shader ฉันเดาว่าคำถามนี้อาจชัดเจนกว่านี้
Martin Ender

2

นี่คือการคัดลอกวางของคำตอบของฉันเดียวกันคำถามที่ StackOverflow


ที่ด้านล่างของคำตอบนี้เป็นตัวอย่างของรหัส GLSL ซึ่งจะช่วยให้การส่งออกเต็มรูปแบบfloatคุ้มค่าเป็นสีเข้ารหัส IEEE binary32754 ฉันใช้มันเหมือนดังต่อไปนี้ (ตัวอย่างนี้ให้yyส่วนประกอบของ modelview matrix):

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ค่าถ้าคุณไม่จำเป็นต้องมีความแม่นยำที่สูงขึ้นและการทำผ่านที่สองที่จะได้รับไบต์ต่ำกว่าถ้าคุณทำ) และ คุณจะได้รับการแสดงเลขฐานสิบหกของfloatว่า IEEE binary32754

นี่คือการใช้งานจริงของ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

วิธีแก้ปัญหาที่ใช้งานได้สำหรับฉันคือการรวบรวมรหัส shader ไปยัง C ++ - ดังที่ Nobody กล่าวถึง มันมีประสิทธิภาพมากเมื่อทำงานกับรหัสที่ซับซ้อนแม้ว่าจะต้องมีการตั้งค่าเล็กน้อย

ฉันทำงานกับ HLSL Compute Shaders เป็นส่วนใหญ่ซึ่งฉันได้พัฒนาไลบรารี่ที่พิสูจน์แล้วของแนวคิดที่มีให้ที่นี่:

https://github.com/cezbloch/shaderator

มันแสดงให้เห็นถึงการ Compute Shader จาก DirectX SDK ตัวอย่างวิธีการเปิดใช้งาน C ++ เช่นการดีบัก HLSL และวิธีการตั้งค่าการทดสอบหน่วย

การคอมไพล์ของ GLSL compute shader ไปยัง C ++ ดูง่ายกว่า HLSL สาเหตุหลักมาจากการสร้างไวยากรณ์ใน HLSL ฉันได้เพิ่มตัวอย่างเล็ก ๆ น้อย ๆ ของปฏิบัติการทดสอบหน่วยบน GLSL ray Tracer Compute Shader ซึ่งคุณสามารถหาได้ในแหล่งที่มาของโครงการ Shaderator ภายใต้ลิงก์ด้านบน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.