ทำไมเงื่อนไขนี้ในส่วนย่อยของฉันจึงช้า


19

ฉันได้ตั้งค่ารหัสการวัด FPS บางอย่างใน WebGL (ตามคำตอบ SO นี้ ) และได้ค้นพบความแปลกประหลาดบางอย่างกับประสิทธิภาพของชิ้นส่วนของฉัน รหัสนี้แสดงเพียงรูปสี่เหลี่ยมจัตุรัสเดียว (หรือมากกว่าสองรูปสามเหลี่ยม) บนผืนผ้าใบขนาด 1024x1024 ดังนั้นเวทมนตร์ทั้งหมดจึงเกิดขึ้นในส่วนย่อย

พิจารณา shader ง่าย ๆ นี้ (GLSL; vertex shader เป็นเพียงแค่ pass-through):

// some definitions

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

    // Nothing to see here...

    gl_FragColor = vec4(value, value, value, 1.0);
}

ดังนั้นนี่จึงเป็นผืนผ้าใบสีขาว มันมีค่าเฉลี่ยประมาณ 30 fps ในเครื่องของฉัน

ทีนี้เรามาดูตัวเลขที่คดเคี้ยวและคำนวณแต่ละส่วนตามเสียงรบกวนที่ขึ้นกับตำแหน่ง:

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

      float noise;
      for ( int j=0; j<10; ++j)
      {
        noise = 0.0;
        for ( int i=4; i>0; i-- )
        {
            float oct = pow(2.0,float(i));
            noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0;
        }
      }

      value = noise/2.0+0.5;

    gl_FragColor = vec4(value, value, value, 1.0);
}

หากคุณต้องการเรียกใช้รหัสข้างต้นฉันได้ใช้การดำเนินการsnoiseนี้

นี่ทำให้ fps ลดลงมาที่ 7 อย่างที่สมเหตุสมผล

ตอนนี้ส่วนที่แปลก ๆ ... ลองคำนวณเพียงเศษหนึ่งส่วนจาก 16 เสียงและปล่อยให้สีขาวเหลืออยู่เพียงเศษหนึ่งส่วนโดยการคำนวณการตัดเสียงในเงื่อนไขต่อไปนี้:

if (int(mod(x*512.0,4.0)) == 0 && int(mod(y*512.0,4.0)) == 0)) {
    // same noise computation
}

คุณคาดหวังว่าสิ่งนี้จะเร็วขึ้นมาก แต่ก็ยังคงเพียง 7 fps

สำหรับการทดสอบเพิ่มเติมอีกหนึ่งรายการลองกรองพิกเซลด้วยเงื่อนไขต่อไปนี้แทน

if (x > 0.5 && y > 0.5) {
    // same noise computation
}

สิ่งนี้จะให้จำนวนพิกเซลเสียงเท่าเดิมเหมือนเดิม แต่ตอนนี้เราสำรองข้อมูลได้สูงถึงเกือบ 30 fps

เกิดขึ้นที่นี่คืออะไร? ไม่ควรกรองสองวิธีที่ 16 ของพิกเซลให้จำนวนรอบที่แน่นอนหรือไม่ และทำไมพิกเซลที่ช้ากว่าจึงแสดงผลพิกเซลทั้งหมดเป็นเสียงรบกวน

คำถามโบนัส: ฉันจะทำอย่างไรกับเรื่องนี้? มีวิธีการทำงานรอบผลการดำเนินงานที่น่ากลัวถ้าจริงผมไม่ต้องการที่จะจุดผ้าใบของฉันมีเพียงไม่กี่ชิ้นส่วนราคาแพง?

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

คำตอบ:


22

พิกเซลได้รับการแบ่งออกเป็นสี่เหลี่ยมเล็ก ๆ (วิธีการใหญ่ขึ้นอยู่กับฮาร์ดแวร์) และคำนวณด้วยกันในหนึ่งเดียวSIMDท่อ (โครงสร้างของอาร์เรย์ประเภทของ SIMD)

ไปป์ไลน์นี้ (ซึ่งมีชื่อแตกต่างกันหลายชื่อขึ้นอยู่กับผู้ขาย: วาร์ป, wavefronts) จะดำเนินการสำหรับแต่ละพิกเซล / แฟรกเมนต์ใน lockstep ซึ่งหมายความว่าหาก 1 พิกเซลต้องมีการคำนวณพิกเซลทั้งหมดจะคำนวณและพิกเซลที่ไม่ต้องการผลลัพธ์ก็จะถูกลบทิ้ง

หากแฟรกเมนต์ทั้งหมดปฏิบัติตามเส้นทางเดียวกันผ่าน shader ดังนั้นกิ่งอื่น ๆ จะไม่ถูกดำเนินการ

ซึ่งหมายความว่าวิธีแรกของคุณในการคำนวณทุกพิกเซลที่ 16 จะเป็นการแยกเคสที่แย่ที่สุด

หากคุณต้องการลดขนาดภาพของคุณให้ปรับขนาดพื้นผิวให้เล็กลงแล้วยกระดับให้สูงขึ้น


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