Blender如何优雅的使用OpenGL之VertArrayObject

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_vaosdynamic_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的绑定配置时,按照以下逻辑 

  1. 首先,通过调用glBindVertexArray函数绑定指定的VAO。

  2. 然后,通过遍历顶点缓冲对象数组(batch->verts_)来绑定每个VBO。对于每个VBO,调用vbo->bind()函数将其绑定到OpenGL上下文中。然后,调用vbo_bind函数来绑定VBO的属性,并更新attr_mask以排除已经绑定的属性。

  3. 接下来,通过遍历实例化顶点缓冲对象数组(batch->inst_)来绑定每个实例化VBO。与上一步类似,先将VBO绑定到上下文中,然后调用vbo_bind函数来绑定VBO的属性,并更新attr_mask

  4. 如果存在资源ID缓冲对象(batch->resource_id_buf),则根据接口中的属性信息进行绑定。根据属性的类型和名称,选择合适的绑定方式,并更新attr_mask

  5. 如果attr_mask不为0且支持顶点属性绑定(GLContext::vertex_attrib_binding_support为真),则通过遍历attr_mask的每一位来绑定剩余的属性。对于每个需要绑定的属性,调用OpenGL的相关函数来绑定默认的属性VBO,并设置属性的格式和绑定

  6. 最后,如果存在索引缓冲对象(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();
  }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值