淺談OpenGL之DSA

DeepDream發表於2020-11-08
今天準備寫一篇文章簡單介紹一下OpenGL4.5引入的一個新的擴充套件ARB_direct_state_access,這個擴充套件為OpenGL引入了一個新的特性就是Direct State Acess,下文統稱為DSA。
 
那麼什麼是DSA,又為什麼要引入DSA呢?
 
瞭解OpenGL的都知道,它的設計是一個基於狀態機的API,你的每一次資源繫結,紋理繫結,修改渲染狀態等等都會改變一個全域性的狀態機。這種設計的最大問題就是容易造成狀態洩露,你可能只是想為紋理設定一個引數,但是卻要改變狀態機的紋理繫結,而這個狀態你如果不修改,會一直洩露到其他的DrawCall中。這就給基於OpenGL的渲染引擎設計帶來很多的問題,大多數引擎都會通過自己的方式來確保一個DrawCall的狀態不會洩露到另一個DrawCall,從而導致一些非常難以除錯的BUG。
 
那麼為了從一定程度上緩解這個問題,OpenGL就引入了DSA。DSA可以讓你直接訪問一個object的狀態而不需要繫結到全域性狀態機上 。比如沒有DSA時,你需要先繫結一個buffer的object,才能為它分配儲存空間,上傳資料等,而有了DSA,你就可以直接訪問object,為它分配儲存,上傳資料,避免將其繫結到狀態機上。
 
但是我為什麼說是一定程度上緩解呢,因為DSA不能完全避免繫結,當你將buffer或者texture真正用到渲染程式上時,你還是要將它們繫結到上下文裡。比如前面你通過DSA的方式分配了一個buffer object,也為它上傳了資料,現在你想將其用到某次DrawCall中作為uniform資料,你就需要呼叫glBindBufferRange(GL_UNIFORM_BUFFER,object,...)。將object繫結到指定Target上。
 
雖然如此,有了DSA還是可以為程式設計帶來很多的方便。你僅僅需要在真正繪製的時候繫結object,而不是在各種初始化時就要繫結它,從一定程度上減少了狀態機切換的次數。
 
下面我就簡單列舉幾個DSA引入的新的API和舊的API之間的對比
 
Program
Without DSA:
glUseProgram(progId);
glUniform1f(loc, x);

 

With DSA:
glProgramUniform1fEXT(progId, loc, x);

 

Texture
Without DSA:
glGenTextures(1, &name); 
glBindTexture(GL_TEXTURE_2D, name); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); 
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

 

With DSA:
glCreateTextures(GL_TEXTURE_2D, 1, &name);
glTextureParameteri(name, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTextureParameteri(name, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTextureParameteri(name, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(name, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureStorage2D(name, 1, GL_RGBA8, width, height);
glTextureSubImage2D(name, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
 
Framebuffer
Without DSA:
glGenFramebuffers(1, &fbo); 
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tid, 0);

 

With DSA:
glCreateFramebuffers(1, &fbo); 
glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, tex, 0);
glNamedFramebufferTexture(fbo, GL_DEPTH_ATTACHMENT, depthTex, 0);
 
Buffer
Without DSA
struct vertex_t { vec3 pos, nrm; vec2 tex; }; glBindVertexArray(vao); 
glBindVertexBuffer(0, vbo, 0, sizeof(vertex_t));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, offsetof(vertex_t, pos));
glVertexAttribFormat(1, 3, GL_FLOAT, GL_FALSE, offsetof(vertex_t, nrm));
glVertexAttribFormat(2, 2, GL_FLOAT, GL_FALSE, offsetof(vertex_t, tex));
glVertexAttribBinding(0, 0);
glVertexAttribBinding(1, 0);
glVertexAttribBinding(2, 0);

 

With DSA
glVertexArrayVertexBuffer(vao, 0, data->vbo, 0, sizeof(vertex_t)); 
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glEnableVertexArrayAttrib(vao, 2);
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, offsetof(vertex_t, pos));
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, offsetof(vertex_t, nrm));
glVertexArrayAttribFormat(vao, 2, 2, GL_FLOAT, GL_FALSE, offsetof(vertex_t, tex));
glVertexArrayAttribBinding(vao, 0, 0);
glVertexArrayAttribBinding(vao, 1, 0);
glVertexArrayAttribBinding(vao, 2, 0);

 

相關文章