Hata Ayıklama

Grafik programlama oldukça eğlenceli olabilir; ancak bir şey doğru şekilde çizilmediğinde veya hiç görünmediğinde, bu büyük bir hayal kırıklığına dönüşebilir. Çoğu zaman pikselleri manipüle ettiğimiz için, bir şeyin neden düzgün çalışmadığını anlamak zor olabilir. Bu tür görsel hataların ayıklanması (ing. debugging), CPU tarafındaki hata ayıklamadan farklıdır. Konsola çıktı yazdıracak bir sistemimiz yoktur, GLSL kodu için breakpoint koyamayız ve GPU'nun yürütme durumunu kolayca kontrol edemeyiz.

Bu bölümde, OpenGL programınızda hata ayıklamak için kullanabileceğiniz çeşitli teknikleri ve ipuçlarını inceleyeceğiz. OpenGL'de hata ayıklamak aslında çok zor değildir ve bu teknikleri öğrenmek uzun vadede size büyük fayda sağlayacaktır.

glGetError()

OpenGL’i yanlış şekilde kullandığınız anda (örneğin, herhangi bir arabellek bağlamadan önce onu yapılandırmaya çalışırsanız), bunu fark eder ve arka planda bir veya daha fazla kullanıcı kaynaklı hata bayrağı (error flag) oluşturur. Bu hata bayraklarını, glGetError\red{glGetError} adlı fonksiyonu kullanarak sorgulayabiliriz. Bu fonksiyon, ayarlanmış hata bayraklarını kontrol eder ve OpenGL yanlış kullanıldıysa bir hata değeri döndürür:

GLenum glGetError(); 

glGetError\red{glGetError} çağrıldığı anda, ya bir hata bayrağı döndürür veya hiç hata vermez.glGetError\red{glGetError} öğesinin döndüğü hata kodları aşağıda listelenmiştir:

Bayrak

Hata Kodu

Tanım

GL_NO_ERROR

0

glGetError' a son çağrıdan bu yana kullanıcı hatası bildirilmediğini ifade eder.

GL_INVALID_ENUM

1280

Bir numaralandırma (ing. enumeration) parametresi kuralına uygun olmadığında bu hata kodu döndürülür.

GL_INVALID_VALUE

1281

Bir değer (ing. value) parametresi kuralına uygun olmadığında bu hata kodu döndürülür.

GL_INVALID_OPERATION

1282

Bir komutun durumunu, verilen parametreler için kuralına uygun olmadığında bu hata kodu döndürülür.

GL_STACK_OVERFLOW

1283

Yığına ekleme işlemi bir yığın taşmasına neden olduğunda bu hata kodu döndürülür.

GL_STACK_UNDERFLOW

1284

Yığın en düşük noktasındayken, yığın patlama işlemi gerçekleştiğinde bu hata kodu döndürülür.

GL_OUT_OF_MEMORY

1285

Bellek ayırma işlemi sırasında yeterli bellek ayrılamadığında bu hata kodu döndürülür.

GL_INVALID_FRAMEBUFFER_OPERATION

1286

Tamamlanmamış bir framebuffer okurken veya yazarken bu hata kodu döndürülür.

OpenGL fonksiyonlarının dokümantasyonlarında, bir fonksiyon yanlış kullanıldığında hangi hata kodlarını üretebileceğini her zaman bulabilirsiniz. Örneğin, glBindTexture fonksiyonunun dokümantasyonuna bakarsanız, oluşturabileceği tüm kullanıcı hatası kodlarını Errors (Hatalar) bölümünde görebilirsiniz.

Bir hata bayrağı ayarlandığı anda, başka hata bayrakları raporlanmaz. Ayrıca, glGetError\red{glGetError} çağrıldığı anda tüm hata bayraklarını temizler (veya dağıtılmış bir sistemde yalnızca bir tanesini, aşağıdaki nota bakın). Bu da demektir ki, eğer glGetError\red{glGetError} fonksiyonunu her kare sonunda bir kez çağırırsanız ve bu fonksiyon bir hata döndürürse, bu hatanın tek başına oluştuğu sonucuna varamazsınız ve hatanın kaynağı çerçevenin herhangi bir yerinde gerçekleşmiş olabilir.


glBindTexture(GL_TEXTURE_2D, tex);
std::cout << glGetError() << std::endl; // returns 0 (no error)
  
glTexImage2D(GL_TEXTURE_3D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
std::cout << glGetError() << std::endl; // returns 1280 (invalid enum)
  
glGenTextures(-5, textures);
std::cout << glGetError() << std::endl; // returns 1281 (invalid value)
  
std::cout << glGetError() << std::endl; // returns 0 (no error)

glGetError\red{glGetError} fonksiyonunun güzel yanı, herhangi bir hatanın nerede oluştuğunu belirlemeyi ve OpenGL’in doğru kullanılıp kullanılmadığını doğrulamayı nispeten kolay hale getirmesidir. Diyelim ki ekranda sadece siyah bir görüntü var ve nedenini bilmiyorsunuz: framebuffer düzgün ayarlanmadı mı? Bir texture’ı bağlamayı mı unuttum? Kodunuzun farklı yerlerinde glGetError\red{glGetError} çağırarak, bir OpenGL hatasının ilk kez nerede ortaya çıktığını hızlıca tespit edebilirsiniz.

Varsayılan olarak glGetError\red{glGetError} , yalnızca hata numaralarını döndürür; bu da, hata kodlarını ezbere bilmiyorsanız pek anlaşılır değildir. Bu yüzden genellikle, hata kontrol fonksiyonunun nerede çağrıldığını ve hangi hata mesajının döndüğünü kolayca yazdıran küçük bir yardımcı fonksiyon yazmak mantıklı olur.

GLenum glCheckError_(const char *file, int line)
{
    GLenum errorCode;
    while ((errorCode = glGetError()) != GL_NO_ERROR)
    {
        std::string error;
        switch (errorCode)
        {
            case GL_INVALID_ENUM:                  error = "INVALID_ENUM"; break;
            case GL_INVALID_VALUE:                 error = "INVALID_VALUE"; break;
            case GL_INVALID_OPERATION:             error = "INVALID_OPERATION"; break;
            case GL_STACK_OVERFLOW:                error = "STACK_OVERFLOW"; break;
            case GL_STACK_UNDERFLOW:               error = "STACK_UNDERFLOW"; break;
            case GL_OUT_OF_MEMORY:                 error = "OUT_OF_MEMORY"; break;
            case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break;
        }
        std::cout << error << " | " << file << " (" << line << ")" << std::endl;
    }
    return errorCode;
}
#define glCheckError() glCheckError_(__FILE__, __LINE__) 

Eğer __FILE__ ve __LINE__ adlı önişlemci yönergelerinin (preprocessor directives) ne olduğunu bilmiyorsanız: bu değişkenler derleme sırasında bulundukları dosya adı ve satır numarasıyla değiştirilir. Kod tabanımıza çok sayıda glCheckError\red{glCheckError} fonksiyonu çağrısı eklemeye karar verirsek, hangi glCheckError\red{glCheckError} çağrısının hatayı döndürdüğünü daha kesin şekilde bilmek oldukça faydalı olur.


glBindBuffer(GL_VERTEX_ARRAY, vbo);
glCheckError(); 

Bu bize aşağıdaki çıktıyı verecektir:

glGetError\red{glGetError} size çok fazla bilgi sunmaz çünkü döndürdüğü bilgiler oldukça basittir; ancak genellikle yazım hatalarını yakalamanıza veya kodunuzda sorunun nerede başladığını hızlıca belirlemenize yardımcı olur. Basit ama hata ayıklama araç kutunuzda etkili bir araçtır.

Hata Ayıklama Çıktısı (ing. Debug Output)

glCheckError\red{glCheckError} fonksiyonundan daha az yaygın ama çok daha kullanışlı bir araç, OpenGL 4.3 sürümünden itibaren çekirdek özelliği haline gelen bir uzantı olan debug output (hata ayıklama çıktısı) özelliğidir. Bu uzantı sayesinde OpenGL, glCheckError\red{glCheckError} ' kıyasla çok daha fazla ayrıntı içeren hata veya uyarı mesajlarını doğrudan kullanıcıya iletebilir. Yalnızca daha fazla bilgi sunmakla kalmaz, aynı zamanda bir hata oluştuğu anda bunu tam yerinde yakalamanıza da yardımcı olur; özellikle bir hata ayıklayıcı (debugger) ile akıllıca kullanıldığında son derece etkilidir.

Debug output kullanmaya başlamak için, OpenGL'den başlatma sürecinde bir debug context talep etmemiz gerekir. Bu süreç, kullandığınız pencereleme sistemine bağlı olarak değişir. Bu bölümde, GLFW üzerinde nasıl ayarlanacağını ele alacağız; ancak diğer sistemler hakkında bilgiyi bölüm sonundaki ek kaynaklarda bulabilirsiniz.

GLFW'de Debug output

GLFW' de bir debug context talep etmek şaşırtıcı derecede kolaydır; yapmamız gereken tek şey, debug output context istediğimizi belirten bir yapılandırma ayarını (hint) GLFW’ye iletmektir. Bu işlemi, glfwCreateWindow\red{glfwCreateWindow} fonksiyonunu çağırmadan önce yapmamız gerekir:

glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);  

GLFW’yi başlattıktan sonra, eğer OpenGL 4.3 veya daha yüksek bir sürüm kullanıyorsak, artık bir debug context’e sahip olmamız gerekir. Eğer bu sürümden daha düşük bir versiyon kullanıyorsak, sistemin yine de bir debug context oluşturabilmesini umut etmek zorundayız. Aksi halde, debug output özelliğini OpenGL uzantıları aracılığıyla manuel olarak etkinleştirmemiz gerekir.

Bir debug context’i başarıyla başlatıp başlatmadığımızı kontrol etmek için OpenGL’den bunu sorgulayabiliriz:

int flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
{
    // initialize debug output 
}

Debug output’un çalışma mantığı, OpenGL’e bir hata logu callback fonksiyonu vermemiz üzerine kuruludur (GLFW’nin input callback’lerine benzer). Bu callback fonksiyonu içinde, OpenGL’den gelen hata verilerini dilediğimiz şekilde işleyebiliriz; bizim örneğimizde, bu verileri konsola yazdıracağız. Aşağıda, OpenGL’in debug output için beklediği callback fonksiyon prototipi yer almaktadır:

void APIENTRY glDebugOutput(GLenum source, GLenum type, unsigned int id, GLenum severity, 
                            GLsizei length, const char *message, const void *userParam);

Elimizde oldukça fazla veri bulunduğundan, aşağıdaki gibi kullanışlı bir hata yazdırma aracı oluşturabiliriz:

void APIENTRY glDebugOutput(GLenum source, 
                            GLenum type, 
                            unsigned int id, 
                            GLenum severity, 
                            GLsizei length, 
                            const char *message, 
                            const void *userParam)
{
    // ignore non-significant error/warning codes
    if(id == 131169 || id == 131185 || id == 131218 || id == 131204) return; 

    std::cout << "---------------" << std::endl;
    std::cout << "Debug message (" << id << "): " <<  message << std::endl;

    switch (source)
    {
        case GL_DEBUG_SOURCE_API:             std::cout << "Source: API"; break;
        case GL_DEBUG_SOURCE_WINDOW_SYSTEM:   std::cout << "Source: Window System"; break;
        case GL_DEBUG_SOURCE_SHADER_COMPILER: std::cout << "Source: Shader Compiler"; break;
        case GL_DEBUG_SOURCE_THIRD_PARTY:     std::cout << "Source: Third Party"; break;
        case GL_DEBUG_SOURCE_APPLICATION:     std::cout << "Source: Application"; break;
        case GL_DEBUG_SOURCE_OTHER:           std::cout << "Source: Other"; break;
    } std::cout << std::endl;

    switch (type)
    {
        case GL_DEBUG_TYPE_ERROR:               std::cout << "Type: Error"; break;
        case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: std::cout << "Type: Deprecated Behaviour"; break;
        case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:  std::cout << "Type: Undefined Behaviour"; break; 
        case GL_DEBUG_TYPE_PORTABILITY:         std::cout << "Type: Portability"; break;
        case GL_DEBUG_TYPE_PERFORMANCE:         std::cout << "Type: Performance"; break;
        case GL_DEBUG_TYPE_MARKER:              std::cout << "Type: Marker"; break;
        case GL_DEBUG_TYPE_PUSH_GROUP:          std::cout << "Type: Push Group"; break;
        case GL_DEBUG_TYPE_POP_GROUP:           std::cout << "Type: Pop Group"; break;
        case GL_DEBUG_TYPE_OTHER:               std::cout << "Type: Other"; break;
    } std::cout << std::endl;
    
    switch (severity)
    {
        case GL_DEBUG_SEVERITY_HIGH:         std::cout << "Severity: high"; break;
        case GL_DEBUG_SEVERITY_MEDIUM:       std::cout << "Severity: medium"; break;
        case GL_DEBUG_SEVERITY_LOW:          std::cout << "Severity: low"; break;
        case GL_DEBUG_SEVERITY_NOTIFICATION: std::cout << "Severity: notification"; break;
    } std::cout << std::endl;
    std::cout << std::endl;
}

Debug output bir OpenGL hatası algıladığında, bu callback fonksiyonunu çağırır ve biz de OpenGL hatasına dair oldukça fazla bilgiyi yazdırabiliriz. Ancak bazı hata kodlarını (örneğin NVidia sürücülerinde buffer’ın başarıyla oluşturulduğunu belirten 131185 gibi) genellikle işe yarar bilgi içermedikleri için göz ardı ediyoruz.

Artık callback fonksiyonumuz hazır olduğuna göre, şimdi debug output’u başlatma zamanı geldi:

if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
{
    glEnable(GL_DEBUG_OUTPUT);
    glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 
    glDebugMessageCallback(glDebugOutput, nullptr);
    glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
} 

Burada OpenGL’e debug output özelliğini etkinleştirmesini söylüyoruz. glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS) çağrısı ise, bir hata oluştuğu anda OpenGL’in callback fonksiyonunu hemen çağırmasını sağlar.

Debug output'u filtreleme

glDebugMessageControl\red{glDebugMessageControl} fonksiyonu ile, hangi türdeki hata mesajlarını almak istediğinizi filtreleyebilirsiniz. Bizim örneğimizde, kaynak, tür veya önem derecesi açısından herhangi bir filtre uygulamıyoruz — yani tüm mesajları alıyoruz. Ancak yalnızca OpenGL API’den gelen, hata türündeki ve yüksek önem derecesine sahip mesajları göstermek isteseydik, bu fonksiyonu aşağıdaki gibi yapılandırırdık:

glDebugMessageControl(GL_DEBUG_SOURCE_API, 
                      GL_DEBUG_TYPE_ERROR, 
                      GL_DEBUG_SEVERITY_HIGH,
                      0, nullptr, GL_TRUE); 

Yaptığımız yapılandırmaya ve debug output destekleyen bir context kullandığınızı varsayarsak, artık her hatalı OpenGL komutu, çok sayıda faydalı bilgiyi içeren kapsamlı bir çıktı verecektir:

Debug hatasının izini sürmek

Debug output ile ilgili bir diğer harika yöntem ise, bir hatanın oluştuğu satırı veya fonksiyon çağrısını nispeten kolay bir şekilde tespit edebilmenizdir. DebugOutput\red{DebugOutput} fonksiyonunda belirli bir hata türüne (veya ayrıntıya takılmıyorsanız doğrudan fonksiyonun en üstüne) breakpoint koyarak, hata oluştuğunda debugger bu hatayı yakalayacaktır. Ardından, çağrı yığını (call stack) üzerinden geriye giderek, mesajın gönderilmesine neden olan fonksiyona ulaşabilirsiniz.

Biraz el ile müdahale gerektirir, ancak ne aradığınıza dair kabaca bir fikriniz varsa, hangi fonksiyon çağrısının hataya neden olduğunu hızlıca belirlemek için son derece kullanışlıdır.

Özelleştirilmiş hata çıktısı

Sadece mesajları okumakla kalmaz, aynı zamanda glDebugMessageInsert\red{glDebugMessageInsert } fonksiyonu ile debug output sistemine kendi mesajlarımızı da ekleyebiliriz.

glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0,                       
                     GL_DEBUG_SEVERITY_MEDIUM, -1, "error message here"); 

Bu özellik özellikle, başka bir uygulamaya ya da debug output context kullanan bir OpenGL koduna entegre oluyorsanız oldukça faydalıdır. Diğer geliştiriciler, sizin özel OpenGL kodunuzda oluşan hataları bu sayede kolayca fark edebilir ve çözebilir.

Özetle, debug output (eğer kullanabiliyorsanız), hataları hızlı bir şekilde yakalamak için son derece kullanışlıdır ve kurulumu zahmete değecek kadar büyük bir zaman kazancı sağlar. Hem glGetError\red{glGetError} hem de debug output context yapılandırılmış bir kaynak kod örneğini burada bulabilirsiniz; bakalım tüm hataları düzeltebilecek misiniz.

Shader Çıktısında Hata Ayıklama

GLSL söz konusu olduğunda, maalesef ne glGetError\red{glGetError} gibi bir fonksiyona ne de shader kodunun adım adım çalışmasını izleme yeteneğine sahibiz. Ekranda simsiyah bir görüntü ya da tamamen hatalı görsellerle karşılaştığınızda, sorunun shader kodunda olup olmadığını anlamak genellikle zordur. Evet, derleme sırasında sözdizimi (syntax) hatalarını bildiren hata raporları alıyoruz, ancak semantik hataları yakalamak çok daha zordur.

Shader’da neyin yanlış gittiğini anlamak için sıkça kullanılan yöntemlerden biri, ilgili tüm değişkenleri doğrudan fragment shader’ın çıktı kanalına göndermektir. Shader değişkenlerini doğrudan renk çıkış kanallarına yazarak, görsel çıktılara bakarak önemli bilgiler elde edebiliriz. Örneğin, bir modelin normal vektörlerinin doğru olup olmadığını kontrol etmek istediğimizi düşünelim. Bu normal vektörleri (ister dönüştürülmüş, ister ham haliyle) vertex shader’dan fragment shader’a aktarabilir ve ardından bunları şöyle bir çıktı ile ekrana yansıtabiliriz:

#version 330 core
out vec4 FragColor;
in vec3 Normal;
[...]
  
void main()
{
    [...]
    FragColor.rgb = Normal;
    FragColor.a = 1.0f;
}

By outputting a (non-color) variable to the output color channel like this we can quickly inspect if the variable is, as far as you can tell, displaying correct values. If, for instance, the visual result is completely black it is clear the normal vectors aren't correctly passed to the shaders; and when they are displayed it's relatively easy to check if they're (sort of) correct or not:

Görsel sonuçlara baktığımızda, dünya uzayındaki normal vektörlerinin doğru göründüğünü söyleyebiliriz; çünkü sırt çantası modelinin sağ tarafı ağırlıklı olarak kırmızı renkte (bu da normal vektörlerin yaklaşık olarak pozitif x eksenine doğru yöneldiğini gösterir). Benzer şekilde, çantanın ön yüzü ağırlıklı olarak maviye boyanmış, yani normal vektörler pozitif z eksenine doğru yöneliyor demektir.

Bu yaklaşım, test etmek istediğiniz her türlü değişkene kolayca genişletilebilir. Bir noktada takılırsanız ve shader’larınızda bir sorun olduğunu düşünüyorsanız, birden fazla değişkeni ve/veya ara hesaplama sonuçlarını ekrana yansıtarak algoritmanın hangi kısmında bir eksiklik ya da yanlışlık olduğunu tespit etmeye çalışın.

OpenGL GLSL referans derleyicisi

Her sürücünün kendine özgü tuhaflıkları ve küçük farklılıkları vardır; örneğin, NVIDIA sürücüleri daha esnektir ve OpenGL spesifikasyonundaki bazı kısıtlamaları görmezden gelme eğilimindedir. Buna karşılık, ATI/AMD sürücüleri genellikle OpenGL spesifikasyonunu daha sıkı uygular (ki bu bana göre daha doğru bir yaklaşımdır). Bunun sonucu olarak, bir makinede çalışan bir shader, sürücü farklılıkları nedeniyle başka bir makinede çalışmayabilir.

Yıllar içinde GPU üreticileri arasındaki küçük farkları öğrenmeye başlarsınız; ancak shader kodunuzun her tür makinede çalışacağından emin olmak istiyorsanız, shader kodunuzu doğrudan resmi spesifikasyona göre OpenGL’in GLSL referans derleyicisi ile kontrol edebilirsiniz. Bu amaçla kullanılan GLSL lang validator adlı ikili dosyaları buradan indirebilir veya tam kaynak koduna buradan erişebilirsiniz.

GLSL lang validator ikili dosyasını aldıktan sonra, shader kodunuzu sadece ilk argüman olarak vererek kolayca kontrol edebilirsiniz. Unutmayın, GLSL lang validator shader türünü aşağıdaki sabit dosya uzantılarıyla belirler:

  • .vert: vertex shader

  • .frag: fragment shader

  • .geom: geometry shader

  • .tesc: tessellation control shader

  • .tese: tessellation evaluation shader

  • .comp: compute shader

GLSL referans derleyicisini çalıştırmak ise bu kadar basittir:

glsllangvalidator shaderFile.vert  

Eğer derleyici herhangi bir hata algılamazsa, herhangi bir çıktı vermez, bunu unutmayın. Bozuk bir vertex shader üzerinde GLSL referans derleyicisini test ettiğimizde ise aşağıdaki çıktıyı alırız:

GLSL referans derleyicisi, AMD, NVIDIA veya Intel’in GLSL derleyicileri arasındaki ince farkları size göstermez; ayrıca shader’larınızı tamamen hatasız hale getirmenize de yardımcı olmaz. Ancak en azından shader kodlarınızı doğrudan GLSL spesifikasyonuna göre kontrol etmenizi sağlar.

Framebuffer çıktısı

Hata ayıklama araç kutunuz için bir diğer faydalı yöntem, bir framebuffer’ın içeriğini ekranınızın önceden belirlenmiş bir bölgesinde görüntülemektir. Framebuffer’ları oldukça sık kullanacaksınız ve işlemlerinin çoğu perde arkasında gerçekleştiği için, neler olup bittiğini anlamak zaman zaman zor olabilir. Bu yüzden, framebuffer içeriğini ekrana yansıtmak, her şeyin doğru görünüp görünmediğini hızlıca kontrol etmek için oldukça kullanışlı bir yöntemdir.

// vertex shader
#version 330 core
layout (location = 0) in vec2 position;
layout (location = 1) in vec2 texCoords;

out vec2 TexCoords;

void main()
{
    gl_Position = vec4(position, 0.0f, 1.0f);
    TexCoords = texCoords;
}
  
// fragment shader
#version 330 core
out vec4 FragColor;
in  vec2 TexCoords;
  
uniform sampler2D fboAttachment;
  
void main()
{
    FragColor = texture(fboAttachment, TexCoords);
} 
void DisplayFramebufferTexture(unsigned int textureID)
{
    if (!notInitialized)
    {
        // initialize shader and vao w/ NDC vertex coordinates at top-right of the screen
        [...]
    }
  
    glActiveTexture(GL_TEXTURE0);  	
    glUseProgram(shaderDisplayFBOOutput);
        glBindTexture(GL_TEXTURE_2D, textureID);
        glBindVertexArray(vaoDebugTexturedRect);
            glDrawArrays(GL_TRIANGLES, 0, 6);
        glBindVertexArray(0);
    glUseProgram(0);
}
  
int main()
{
    [...]
    while (!glfwWindowShouldClose(window))
    {
        [...]
        DisplayFramebufferTexture(fboAttachment0);
        
        glfwSwapBuffers(window);
    }
}  

Bu yöntem, ekranınızın köşesinde framebuffer çıktısını hata ayıklamak için kullanabileceğiniz küçük bir pencere oluşturur. Örneğin, deferred rendering tekniğinde geometry pass sırasında normal vektörlerinin doğru görünüp görünmediğini kontrol etmek için oldukça kullanışlıdır.

Elbette böyle bir yardımcı fonksiyonu daha da geliştirerek birden fazla texture’ı görüntüleyecek şekilde genişletebilirsiniz. Bu yöntem, framebuffer(lar)ınızda neler olup bittiğine dair sürekli geri bildirim almanızı sağlayan hızlı ve pratik bir çözümdür.

Harici Hata Ayıklama Yazılımları

Tüm diğer yöntemler işe yaramadığında, hata ayıklama sürecinde size yardımcı olabilecek üçüncü parti araçları kullanma seçeneğiniz hâlâ vardır. Bu uygulamalar genellikle OpenGL sürücülerine kendilerini enjekte eder ve çeşitli OpenGL çağrılarını yakalayarak size oldukça fazla sayıda ilginç veri sağlar. Bu araçlar OpenGL fonksiyonlarının kullanımını profillemek, darboğazları bulmak, tampon belleği incelemek, texture’ları ve framebuffer eklerini görüntülemek gibi çeşitli şekillerde size yardımcı olabilir. Büyük ve üretim düzeyindeki projelerde çalışırken, bu tür araçlar geliştirme sürecinizde çok değerli hâle gelebilir.

Burada en popüler hata ayıklama araçlarından bazılarını listeledim; hangisinin ihtiyaçlarınıza en uygun olduğunu görmek için birkaç tanesini denemenizde fayda var.

RenderDoc

RenderDoc, tamamen açık kaynaklı ve bağımsız çalışan harika bir hata ayıklama aracıdır. Bir kareyi yakalamaya başlamak için, çalıştırmak istediğiniz uygulamanın yürütülebilir dosyasını (executable) ve çalışma dizinini belirtirsiniz. Uygulama normal şekilde çalışmaya devam eder ve belirli bir kareyi incelemek istediğinizde, RenderDoc o anki yürütme durumunda bir veya daha fazla kareyi yakalar.

Yakalanan kare(ler) içinde; pipeline durumunu, tüm OpenGL komutlarını, tampon (buffer) belleğini ve kullanılan texture’ları ayrıntılı şekilde görüntüleyebilirsiniz.

CodeXL

CodeXL, hem bağımsız bir uygulama hem de Visual Studio eklentisi olarak sunulan bir GPU hata ayıklama aracıdır. Grafik uygulamalarını profillemek için oldukça kullanışlıdır ve kapsamlı bilgiler sunar. CodeXL, NVidia ve Intel ekran kartlarında da çalışır; ancak bu kartlarda OpenCL hata ayıklama desteği bulunmaz.

Ben şahsen CodeXL ile çok fazla deneyime sahip değilim çünkü RenderDoc’u kullanması bana daha kolay geldi. Ancak yine de bu aracı listeye dahil ettim, çünkü oldukça sağlam bir araç gibi görünüyor ve büyük GPU üreticilerinden biri tarafından geliştiriliyor.

NVIDIA Nsight

NVIDIA’nın popüler GPU hata ayıklama aracı Nsight, başta bağımsız bir uygulama olmayan; Visual Studio ya da Eclipse IDE için bir eklenti (plugin) olarak sunulan bir araçtır (NVIDIA artık bağımsız bir sürümünü de sunmaktadır). Nsight, grafik geliştiricileri için son derece faydalı bir araçtır çünkü GPU kullanımı ve kare kare GPU durumu hakkında çok sayıda çalışma zamanı (run-time) istatistiği sağlar.

Uygulamanızı Visual Studio (veya Eclipse) içinden, Nsight’ın hata ayıklama veya profil çıkarma komutlarıyla çalıştırdığınız anda, Nsight doğrudan uygulamanın içinde çalışır. Nsight’ın en güzel yanı, uygulamanızın içine bir katmanlı grafik arayüz (overlay GUI) yerleştirmesi ve bu arayüzü kullanarak hem çalışma zamanında hem de kare bazlı analizlerde uygulamanız hakkında birçok ilginç bilgi toplayabilmenizdir.

Nsight, son derece kullanışlı bir araçtır; ancak önemli bir dezavantajı vardır: yalnızca NVIDIA ekran kartlarında çalışır. Eğer NVIDIA kartı kullanıyorsanız (ve Visual Studio ile çalışıyorsanız), kesinlikle denemeye değer bir araçtır.

Eminim burada bahsetmeyi unuttuğum daha birçok hata ayıklama aracı vardır (aklıma gelenlerden bazıları Valve’ın VOGL aracı ve APItrace), ancak bu liste size deneyebileceğiniz yeterince araç sunuyor.

Ek Kaynaklar

  • Why is your code producing a black window: Reto Koradi tarafından hazırlanan ve ekranınızda neden görüntü çıkmadığına dair genel nedenleri listeleyen bir yazı.

  • Debug Output in OpenGL: Vallentin tarafından yazılmış, farklı pencereleme sistemlerinde debug context kurulumu da dâhil olmak üzere OpenGL debug output üzerine kapsamlı bir rehber.

Orijinal Kaynak: Debugging

Çeviri: Nezihe Sözen

Last updated

Was this helpful?