移动端图形API之数据

在图形编程中,尤其是在使用现代图形 API(如 Vulkan、OpenGL ES、Metal)时,理解 CPU 侧的 API 编程部分和 GPU 侧的 Shader 编程部分是非常重要的。以下是详细解释和扩展:

1. 数据类型

1.1 简单数据结构

在 CPU 侧,API 使用的基本数据结构可以分为几类,主要包括简单数据结构、设备资源和提交相关的数据。

  • 简单数据结构

    • 由于这些 API 通常基于 C++,因此可以使用 C++ 中的所有基本数据类型。不同的图形 API 可能会定义一些额外的数据类型,以便于跨平台的兼容性。
    数据类型VulkanOpenGL ES
    布尔值VkBool32 / VK_TRUE / VK_FALSEGLboolean
    地址VkDeviceAddress-
    浮点数float / VkHalf / VkFixed16GLfloat / GLclampf
    整数int / uint32_tGLint / GLuint
1.2 设备(GPU 上的)资源

在现代图形 API 中,GPU 资源通常被视为句柄(handle),这使得资源管理更加灵活和高效。

  • OpenGL

    • 在 OpenGL 中,所有 GPU 资源(如纹理、缓冲区等)都使用 GLuint 类型的句柄来表示。
  • Vulkan

    • Vulkan 进一步将句柄封装为特定的类型,例如 VkBufferVkImage 等。这种类型安全性使得 API 更加清晰和易于使用。
  • Metal

    • 在 Metal 中,资源被封装为具体的对象,例如 MtlBufferMtlImage。这种面向对象的设计使得资源管理更加直观。
1.3 提交相关的数据

在现代图形 API 中,提交相关的数据结构是必不可少的。这些结构用于管理命令的提交和执行。

  • 图形设备

    • 代表图形硬件的接口,例如 VkDevice(Vulkan)和 MTLDevice(Metal)。
  • 提交队列

    • 用于管理命令的执行顺序,例如 VkQueue(Vulkan)和 MTLCommandQueue(Metal)。
  • 提交指令

    • 用于存储和管理待执行的命令,例如 VkCommandBuffer(Vulkan)和 MTLCommandBuffer(Metal)。

2. 线程安全性

大多数现代图形 API 的数据结构和资源管理并不是线程安全的。这意味着在多线程环境中,开发者需要小心管理对这些资源的访问,以避免数据竞争和不一致性的问题。通常,开发者需要使用锁或其他同步机制来确保在多线程环境中对 GPU 资源的安全访问。

总结

在现代图形编程中,理解 CPU 侧的 API 编程部分是至关重要的。不同的图形 API 提供了不同的数据类型和资源管理方式,开发者需要根据具体的 API 特性来设计和实现图形应用程序。同时,注意线程安全性也是确保应用程序稳定性和性能的重要因素。

1.2对象创建

1. OpenGL ES 风格的创建 API

在 OpenGL ES 中,资源的创建和管理相对简单,API 设计上隐藏了许多底层细节。以下是创建对象的基本流程:

创建对象的步骤
  1. 生成句柄

    • 使用 glGenBuffers 等函数生成一个或多个对象的句柄(handle)。
    GLuint id;
    glGenBuffers(1, &id); // 生成一个 buffer 的句柄
    
  2. 绑定对象

    • 使用 glBindBuffer 等函数将生成的句柄绑定到实际的对象上。这一过程会创建对象的实体,并将其与句柄关联。
    glBindBuffer(GL_ARRAY_BUFFER, id); // 创建 buffer 对象并绑定到句柄 id
    
  3. 操作对象

    • 之后可以使用该句柄来操作对象,例如填充数据、设置属性等。
  4. 删除对象

    • 使用 glDeleteBuffers 等函数释放对象,回收句柄。
    glDeleteBuffers(1, &id); // 释放 buffer 对象
    

2. 高级 API(如 Vulkan 和 Metal)

在现代图形 API 中,资源的创建通常更加复杂,提供了更多的灵活性和性能优化选项。

直接创建
  • Vulkan

    • 使用 vkCreateXXX 函数直接创建对象,通常需要提供一个结构体(如 VkBufferCreateInfo)来描述对象的属性。
    VkBuffer buffer;
    VkBufferCreateInfo bufferInfo = {};
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferInfo.size = bufferSize;
    bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
    bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    
    vkCreateBuffer(device, &bufferInfo, nullptr, &buffer); // 创建 buffer
    
  • Metal

    • 使用类似的方式,通过设备对象直接创建资源。
    id<MTLBuffer> buffer = [device newBufferWithLength:bufferSize options:MTLResourceStorageModeShared]; // 创建 buffer
    
从池创建
  • Vulkan

    • 先创建资源池(如 VkDescriptorPool),然后从池中分配资源。这种方式通常性能更高,适合频繁创建和销毁的资源。
    VkDescriptorPool pool;
    // 创建池的代码...
    
    VkDescriptorSetAllocateInfo allocInfo = {};
    allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    allocInfo.descriptorPool = pool;
    allocInfo.descriptorSetCount = 1;
    allocInfo.pSetLayouts = &setLayout;
    
    vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet); // 从池中分配资源
    
  • Metal

    • Metal 中的资源池称为 heap,首先需要创建一个 heap,然后从 heap 中分配资源。
    id<MTLHeap> heap = [device newHeapWithDescriptor:heapDescriptor]; // 创建 heap
    id<MTLBuffer> buffer = [heap newBufferWithLength:bufferSize options:MTLResourceStorageModePrivate]; // 从 heap 创建 buffer
    

总结

在 OpenGL ES 中,资源的创建过程相对简单,主要通过生成句柄和绑定对象来实现。而在 Vulkan 和 Metal 等现代图形 API 中,资源的创建提供了更多的灵活性和性能优化选项,包括直接创建和从池创建的方式。开发者需要根据具体的应用需求和性能考虑来选择合适的资源创建方式。

在图形编程中,数据查询是一个重要的功能,允许开发者获取当前上下文或特定对象的状态信息。不同的图形 API 提供了不同的查询机制。以下是对 OpenGL ES、Metal 和 Vulkan 中数据查询的详细说明。

1. 数据查询

1.1 Metal

在 Metal 中,所有对象都有明确的数据结构,因此可以直接从这些对象中获取所需的信息。这种设计使得查询操作更加直观和高效。

  • 示例

    • 获取缓冲区的大小:
    NSUInteger bufferSize = [buffer length]; // 直接从 MTLBuffer 对象获取大小
    
  • 获取其他属性

    • Metal 提供了丰富的 API 来查询对象的状态,例如获取纹理的宽度和高度、获取渲染管线的状态等。
1.2 OpenGL ES

在 OpenGL ES 中,查询操作通常依赖于全局 API。开发者可以使用一系列的 glGet* 函数来获取当前上下文的状态或特定对象的属性。

  • 全局上下文查询

    • 使用 glGetBooleanvglGetIntegervglGetFloatv 等函数查询当前上下文的状态。
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport); // 查询当前视口大小
    
  • 对象属性查询

    • OpenGL ES 提供了多种 GetXXX 类型的 API 来查询特定对象的属性,这些查询可能会显得有些杂乱。
    • 示例
      • 查询顶点属性:
      GLint vertexAttributeSize;
      glGetVertexAttribiv(attributeIndex, GL_VERTEX_ATTRIB_ARRAY_SIZE, &vertexAttributeSize);
      
      • 查询缓冲区参数:
      GLint bufferSize;
      glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufferSize);
      
      • 查询着色器状态:
      GLint shaderCompileStatus;
      glGetShaderiv(shader, GL_COMPILE_STATUS, &shaderCompileStatus);
      
1.3 Vulkan

Vulkan 的设计哲学与 OpenGL ES 和 Metal 有所不同。Vulkan 不提供类似的查询接口,因为它的设计目标是让开发者对状态和数据的管理有更高的控制权。所有的数据和状态都是由应用程序自己管理的,Vulkan 认为开发者有能力记住这些数据。

  • 状态管理

    • 在 Vulkan 中,开发者需要在创建资源时明确设置所有的状态信息,并在应用程序中维护这些状态。
    • 例如,创建缓冲区时,开发者需要手动记录缓冲区的大小和其他属性。
  • 示例

    • 在 Vulkan 中,开发者通常会在创建资源时将相关信息存储在自己的数据结构中,以便后续使用。

总结

数据查询在不同的图形 API 中有着不同的实现方式。Metal 提供了直接从对象中获取信息的方式,OpenGL ES 则依赖于全局 API 来查询状态,而 Vulkan 则强调开发者对状态的管理,避免了多余的查询接口。开发者需要根据所使用的 API 选择合适的查询方式,并在设计应用程序时考虑如何有效地管理和维护状态信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值