淺談OpenGL之DSA

今天準備寫一篇文章簡單介紹一下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);

 

Tags: