1.GPUBatch
2.Batch
/**
* Base class which is then specialized for each implementation (GL, VK, ...).
*
* \note Extends #GPUBatch as we still needs to expose some of the internals to the outside C code.
*/
class Batch : public GPUBatch {
public:
virtual ~Batch() = default;
virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0;
virtual void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) = 0;
virtual void multi_draw_indirect(GPUStorageBuf *indirect_buf,
int count,
intptr_t offset,
intptr_t stride) = 0;
/* Convenience casts. */
IndexBuf *elem_() const
{
return unwrap(elem);
}
VertBuf *verts_(const int index) const
{
return unwrap(verts[index]);
}
VertBuf *inst_(const int index) const
{
return unwrap(inst[index]);
}
};
} // namespace gpu
} // namespace blender
3.GLBatch
Blender中,使用GLVaoCache
类,它与GLBatch类的方法相关,它管理VAO的状态,并为每个着色器接口(GLShaderInterface
)记住几何状态(顶点属性绑定和元素缓冲区)。它有以下成员变量和方法:
context_
:指向GLContext的指针,表示生成vao_cache_的上下文。interface_
:指向GLShaderInterface的指针,表示上一次使用的接口。vao_id_
:缓存的上一次接口的VAO ID。vao_base_instance_
:当不支持arb_base_instance
时使用的基础实例。base_instance_
:基础实例。is_dynamic_vao_count
:标志是否使用动态VAO计数。static_vaos
和dynamic_vaos
:静态和动态VAO的结构体,包含接口和VAO ID的数组。vao_get
:获取批次的VAO ID。base_instance_vao_get
:获取基础实例的VAO ID。lookup
:在缓存中查找接口对应的VAO ID。insert
:创建一个新的VAO对象并存储在缓存中。remove
:从缓存中移除接口对应的VAO。clear
:清空缓存。init
:初始化方法。context_check
:检查上下文是否匹配的方法。
接下来是GLBatch
类,它是批处理的子类,包含了与GPUShaderInterface对应的所有VAO缓存。它有以下成员变量和方法:
vao_cache_
:VAO缓存对象。draw
:绘制方法,用于绘制批次中的几何体。draw_indirect
:间接绘制方法,使用间接缓冲区进行绘制。multi_draw_indirect
:多次间接绘制方法,使用间接缓冲区进行多次绘制。bind
:绑定方法,用于绑定批次的索引缓冲区。elem_
、verts_
、inst_
:方便获取器,用于获取索引缓冲区、顶点缓冲区和实例缓冲区。
namespace blender {
namespace gpu {
class GLContext;
class GLShaderInterface;
#define GPU_VAO_STATIC_LEN 3
/**
* VAO management: remembers all geometry state (vertex attribute bindings & element buffer)
* for each shader interface. Start with a static number of VAO's and fallback to dynamic count
* if necessary. Once a batch goes dynamic it does not go back.
*/
class GLVaoCache {
private:
/** Context for which the vao_cache_ was generated. */
GLContext *context_ = nullptr;
/** Last interface this batch was drawn with. */
GLShaderInterface *interface_ = nullptr;
/** Cached VAO for the last interface. */
GLuint vao_id_ = 0;
/** Used when arb_base_instance is not supported. */
GLuint vao_base_instance_ = 0;
int base_instance_ = 0;
bool is_dynamic_vao_count = false;
union {
/** Static handle count */
struct {
const GLShaderInterface *interfaces[GPU_VAO_STATIC_LEN];
GLuint vao_ids[GPU_VAO_STATIC_LEN];
} static_vaos;
/** Dynamic handle count */
struct {
uint count;
const GLShaderInterface **interfaces;
GLuint *vao_ids;
} dynamic_vaos;
};
public:
GLVaoCache();
~GLVaoCache();
GLuint vao_get(GPUBatch *batch);
GLuint base_instance_vao_get(GPUBatch *batch, int i_first);
/**
* Return 0 on cache miss (invalid VAO).
*/
GLuint lookup(const GLShaderInterface *interface);
/**
* Create a new VAO object and store it in the cache.
*/
void insert(const GLShaderInterface *interface, GLuint vao_id);
void remove(const GLShaderInterface *interface);
void clear();
private:
void init();
/**
* The #GLVaoCache object is only valid for one #GLContext.
* Reset the cache if trying to draw in another context;.
*/
void context_check();
};
class GLBatch : public Batch {
public:
/** All vaos corresponding to all the GPUShaderInterface this batch was drawn with. */
GLVaoCache vao_cache_;
public:
void draw(int v_first, int v_count, int i_first, int i_count) override;
void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override;
void multi_draw_indirect(GPUStorageBuf *indirect_buf,
int count,
intptr_t offset,
intptr_t stride) override;
void bind(int i_first);
/* Convenience getters. */
GLIndexBuf *elem_() const
{
return static_cast<GLIndexBuf *>(unwrap(elem));
}
GLVertBuf *verts_(const int index) const
{
return static_cast<GLVertBuf *>(unwrap(verts[index]));
}
GLVertBuf *inst_(const int index) const
{
return static_cast<GLVertBuf *>(unwrap(inst[index]));
}
MEM_CXX_CLASS_ALLOC_FUNCS("GLBatch");
};
} // namespace gpu
}
4.更新VAO
更新一个VAO的绑定配置时,按照以下逻辑
-
首先,通过调用
glBindVertexArray
函数绑定指定的VAO。 -
然后,通过遍历顶点缓冲对象数组(
batch->verts_
)来绑定每个VBO。对于每个VBO,调用vbo->bind()
函数将其绑定到OpenGL上下文中。然后,调用vbo_bind
函数来绑定VBO的属性,并更新attr_mask
以排除已经绑定的属性。 -
接下来,通过遍历实例化顶点缓冲对象数组(
batch->inst_
)来绑定每个实例化VBO。与上一步类似,先将VBO绑定到上下文中,然后调用vbo_bind
函数来绑定VBO的属性,并更新attr_mask
。 -
如果存在资源ID缓冲对象(
batch->resource_id_buf
),则根据接口中的属性信息进行绑定。根据属性的类型和名称,选择合适的绑定方式,并更新attr_mask
。 -
如果
attr_mask
不为0且支持顶点属性绑定(GLContext::vertex_attrib_binding_support
为真),则通过遍历attr_mask
的每一位来绑定剩余的属性。对于每个需要绑定的属性,调用OpenGL的相关函数来绑定默认的属性VBO,并设置属性的格式和绑定 -
最后,如果存在索引缓冲对象(
batch->elem
),则将其绑定到上下文中。
void GLVertArray::update_bindings(const GLuint vao,
const GPUBatch *batch_, /* Should be GLBatch. */
const ShaderInterface *interface,
const int base_instance)
{
const GLBatch *batch = static_cast<const GLBatch *>(batch_);
uint16_t attr_mask = interface->enabled_attr_mask_;
glBindVertexArray(vao);
/* Reverse order so first VBO'S have more prevalence (in term of attribute override). */
for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) {
GLVertBuf *vbo = batch->verts_(v);
if (vbo) {
vbo->bind();
attr_mask &= ~vbo_bind(interface, &vbo->format, 0, vbo->vertex_len, false);
}
}
for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) {
GLVertBuf *vbo = batch->inst_(v);
if (vbo) {
vbo->bind();
attr_mask &= ~vbo_bind(interface, &vbo->format, base_instance, vbo->vertex_len, true);
}
}
if (batch->resource_id_buf) {
const ShaderInput *input = interface->attr_get("drw_ResourceID");
int component_len = 1;
if (input == nullptr) {
/* Uses Custom IDs */
input = interface->attr_get("vertex_in_drw_ResourceID");
component_len = 2;
}
if (input) {
dynamic_cast<GLStorageBuf *>(unwrap(batch->resource_id_buf))->bind_as(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(input->location);
glVertexAttribDivisor(input->location, 1);
glVertexAttribIPointer(
input->location, component_len, to_gl(GPU_COMP_I32), 0, (GLvoid *)nullptr);
attr_mask &= ~(1 << input->location);
}
}
if (attr_mask != 0 && GLContext::vertex_attrib_binding_support) {
for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) {
if (attr_mask & mask) {
GLContext *ctx = GLContext::get();
/* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style.
* Fix issues for some drivers (see #75069). */
glBindVertexBuffer(a, ctx->default_attr_vbo_, intptr_t(0), intptr_t(0));
glEnableVertexAttribArray(a);
glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0);
glVertexAttribBinding(a, a);
}
}
}
if (batch->elem) {
/* Binds the index buffer. This state is also saved in the VAO. */
static_cast<GLIndexBuf *>(unwrap(batch->elem))->bind();
}
}