4
社区成员
发帖
与我相关
我的任务
分享┌─────────────────────────────────────────────────────────────────────────────┐ │ CPU vs GPU 执行分布 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ CPU 执行 (应用程序代码) GPU 执行 (硬件处理) │ │ ════════════════════════════════ ════════════════════════════════ │ │ │ │ ① createInstance() CPU (仅创建软件对象,GPU无感知) │ │ ② setupDebugMessenger() CPU (仅设置回调,GPU无感知) │ │ ③ createSurface() CPU (操作系统层面,GPU无感知) │ │ ④ pickPhysicalDevice() CPU (查询GPU信息,GPU响应查询) │ │ ⑤ createLogicalDevice() CPU (创建软件接口,GPU记录状态) │ │ ⑥ createSwapChain() CPU (准备显示资源,GPU分配内存) │ │ ⑦ createImageViews() CPU (创建视图对象,GPU无感知) │ │ ⑧ createRenderPass() CPU (定义渲染流程,GPU无感知) │ │ ⑨ createGraphicsPipeline() CPU (编译着色器,GPU准备硬件状态) │ │ ⑩ createFramebuffers() CPU (绑定资源,GPU无感知) │ │ ⑪ createCommandPool() CPU (分配内存,GPU无感知) │ │ ⑫ createCommandBuffers() CPU (分配内存,GPU无感知) │ │ ⑬ createSyncObjects() CPU (创建同步对象,GPU无感知) │ │ │ │ 主循环: drawFrame() CPU+GPU (CPU提交命令,GPU执行渲染) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
让我用更直观的方式展示每一步在计算机系统中发生了什么:
┌─────────────────────────────────────────────────────────────────────────────────┐ │ 完整的硬件执行流程 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────────────────────┐ │ │ │ CPU 端 (主程序) │ │ │ ├────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ ① createInstance() │ │ │ │ ├─► CPU执行: 创建VkInstance对象 │ │ │ │ ├─► CPU执行: 调用Vulkan驱动(DLL/so)加载 │ │ │ │ ├─► CPU执行: 验证层配置在CPU内存 │ │ │ │ └─► GPU状态: 无 (只是建立通信通道) │ │ │ │ │ │ │ │ ② setupDebugMessenger() │ │ │ │ ├─► CPU执行: 注册回调函数到CPU内存 │ │ │ │ └─► GPU状态: 无 │ │ │ │ │ │ │ │ ③ createSurface() │ │ │ │ ├─► CPU执行: 调用操作系统API (Win32/XCB/Wayland) │ │ │ │ ├─► CPU执行: 操作系统创建窗口缓冲区 │ │ │ │ └─► GPU状态: 无 │ │ │ │ │ │ │ │ ④ pickPhysicalDevice() │ │ │ │ ├─► CPU执行: vkEnumeratePhysicalDevices() │ │ │ │ ├─► CPU→GPU: 查询GPU信息 (PCIe总线通信) │ │ │ │ ├─► GPU→CPU: 返回设备信息 (设备ID、特性等) │ │ │ │ └─► CPU执行: 选择合适GPU并记录在CPU内存 │ │ │ │ │ │ │ │ ⑤ createLogicalDevice() │ │ │ │ ├─► CPU执行: 创建VkDevice对象 (CPU内存) │ │ │ │ ├─► CPU→GPU: 通知GPU创建设备上下文 (通过驱动) │ │ │ │ ├─► CPU→GPU: 分配队列资源 (GPU调度器) │ │ │ │ └─► GPU执行: 记录设备状态、准备队列 │ │ │ │ │ │ │ │ ⑥ createSwapChain() │ │ │ │ ├─► CPU执行: 调用Vulkan交换链创建 │ │ │ │ ├─► CPU→GPU: 请求分配显示缓冲区 (GPU显存) │ │ │ │ ├─► GPU执行: 在显存中分配2-3个图像缓冲区 │ │ │ │ ├─► CPU→GPU: 建立显示控制器连接 │ │ │ │ └─► GPU执行: 准备显示管道 │ │ │ │ │ │ │ │ ⑦ createImageViews() │ │ │ │ ├─► CPU执行: 创建VkImageView对象 (CPU内存) │ │ │ │ ├─► CPU→GPU: 告诉GPU如何解释图像数据 │ │ │ │ └─► GPU执行: 记录视图描述符 │ │ │ │ │ │ │ │ ⑧ createRenderPass() │ │ │ │ ├─► CPU执行: 创建VkRenderPass对象 (CPU内存) │ │ │ │ ├─► CPU执行: 定义渲染流程 (加载/存储/布局转换) │ │ │ │ └─► GPU状态: 无 (只是元数据定义) │ │ │ │ │ │ │ │ ⑨ createGraphicsPipeline() │ │ │ │ ├─► CPU执行: 从硬盘读取SPIR-V着色器 (磁盘→内存) │ │ │ │ ├─► CPU执行: 创建VkShaderModule (CPU内存) │ │ │ │ ├─► CPU→GPU: 上传着色器代码到GPU (PCIe传输) │ │ │ │ ├─► GPU执行: 编译着色器为GPU原生指令 (驱动编译) │ │ │ │ ├─► GPU执行: 配置硬件状态 (光栅化器、混合器、深度测试等) │ │ │ │ └─► GPU执行: 将管线配置写入GPU寄存器 │ │ │ │ │ │ │ │ ⑩ createFramebuffers() │ │ │ │ ├─► CPU执行: 创建VkFramebuffer对象 (CPU内存) │ │ │ │ ├─► CPU→GPU: 绑定图像视图到渲染通道 │ │ │ │ └─► GPU执行: 记录帧缓冲配置 │ │ │ │ │ │ │ │ ⑪ createCommandPool() │ │ │ │ ├─► CPU执行: 在CPU内存分配命令池管理结构 │ │ │ │ ├─► CPU→GPU: 请求GPU端命令缓冲区内存 (显存或系统内存) │ │ │ │ └─► GPU执行: 准备命令缓冲区存储区域 │ │ │ │ │ │ │ │ ⑫ createCommandBuffers() │ │ │ │ ├─► CPU执行: 分配VkCommandBuffer对象 │ │ │ │ ├─► CPU→GPU: 分配GPU可访问的命令缓冲区内存 │ │ │ │ └─► GPU状态: 准备好接收命令 │ │ │ │ │ │ │ │ ⑬ createSyncObjects() │ │ │ │ ├─► CPU执行: 创建信号量和栅栏对象 (CPU内存) │ │ │ │ ├─► CPU→GPU: 创建GPU端同步对象 (GPU调度器) │ │ │ │ └─► GPU执行: 准备同步机制 │ │ │ │ │ │ │ └────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────────────────┐ │ │ │ 主循环: drawFrame() (每一帧) │ │ │ ├────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ 1. vkWaitForFences() │ │ │ │ ├─► CPU执行: 检查栅栏状态 (CPU轮询或等待) │ │ │ │ ├─► GPU→CPU: 通知栅栏已触发 (上一帧完成) │ │ │ │ └─► CPU继续: 确认可以开始新帧 │ │ │ │ │ │ │ │ 2. vkAcquireNextImageKHR() │ │ │ │ ├─► CPU→GPU: 请求获取下一个交换链图像 │ │ │ │ ├─► GPU执行: 检查显示控制器状态 │ │ │ │ ├─► GPU执行: 选择可用图像 │ │ │ │ ├─► GPU→CPU: 返回图像索引 │ │ │ │ └─► CPU执行: 获取到图像索引 │ │ │ │ │ │ │ │ 3. vkResetCommandBuffer() + recordCommandBuffer() │ │ │ │ ├─► CPU执行: 重置命令缓冲区 │ │ │ │ ├─► CPU执行: 录制命令到CPU内存 │ │ │ │ │ ├─► vkCmdBeginRenderPass() (CPU写入命令) │ │ │ │ │ ├─► vkCmdBindPipeline() (CPU写入命令) │ │ │ │ │ ├─► vkCmdSetViewport() (CPU写入命令) │ │ │ │ │ ├─► vkCmdDraw() (CPU写入命令) │ │ │ │ │ └─► vkCmdEndRenderPass() (CPU写入命令) │ │ │ │ ├─► CPU→GPU: 将命令缓冲区上传到GPU可访问内存 │ │ │ │ └─► GPU执行: 命令缓冲区就绪 (等待提交) │ │ │ │ │ │ │ │ 4. vkQueueSubmit() │ │ │ │ ├─► CPU执行: 构建提交信息 │ │ │ │ ├─► CPU→GPU: 将命令缓冲区提交到图形队列 │ │ │ │ ├─► GPU执行: 调度器接收命令 │ │ │ │ ├─► GPU执行: 等待信号量 (图像可用) │ │ │ │ ├─► GPU执行: 开始执行命令 │ │ │ │ │ ├─► 顶点着色器执行 (GPU核心) │ │ │ │ │ ├─► 光栅化 (GPU硬件) │ │ │ │ │ ├─► 片段着色器执行 (GPU核心) │ │ │ │ │ └─► 颜色混合/写入帧缓冲 (GPU硬件) │ │ │ │ ├─► GPU执行: 完成渲染后触发信号量 │ │ │ │ └─► GPU执行: 完成后触发栅栏 │ │ │ │ │ │ │ │ 5. vkQueuePresentKHR() │ │ │ │ ├─► CPU执行: 提交呈现请求 │ │ │ │ ├─► CPU→GPU: 通知显示控制器 │ │ │ │ ├─► GPU执行: 等待渲染完成信号量 │ │ │ │ ├─► GPU执行: 将图像交给显示控制器 │ │ │ │ ├─► 显示控制器: 在下一个VBlank读取图像并显示到屏幕 │ │ │ │ └─► GPU→CPU: 返回呈现结果 │ │ │ │ │ │ │ └────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ │ 完整的系统层次结构 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 用户应用程序 (你的代码) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ HelloTriangleApplication::run() │ │ │ │ ├─► initWindow() (CPU执行) │ │ │ │ ├─► initVulkan() (CPU执行, 主要配置) │ │ │ │ ├─► mainLoop() (CPU执行提交, GPU执行渲染) │ │ │ │ └─► cleanup() (CPU执行) │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ Vulkan API (驱动层) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Vulkan Loader (vulkan-1.dll / libvulkan.so) │ │ │ │ ├─► 验证层 (Validation Layers) - CPU执行 │ │ │ │ ├─► API调用分发 │ │ │ │ └─► 驱动特定实现 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ GPU驱动 (NVIDIA/AMD/Intel) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ ├─► 命令缓冲区管理 (CPU→GPU传输) │ │ │ │ ├─► 着色器编译 (CPU执行: SPIR-V→GPU原生指令) │ │ │ │ ├─► 内存管理 (CPU执行: 分配GPU显存) │ │ │ │ └─► 硬件调度 (CPU执行: 提交命令到GPU) │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ GPU硬件 │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ GPU Core (流处理器/SM/CU) │ │ │ │ ├─► 顶点处理单元 │ │ │ │ ├─► 光栅化单元 │ │ │ │ ├─► 片段处理单元 │ │ │ │ └─► 纹理单元 │ │ │ │ │ │ │ │ GPU显存 (VRAM) │ │ │ │ ├─► 交换链图像 │ │ │ │ ├─► 着色器代码 │ │ │ │ ├─► 顶点/索引缓冲 │ │ │ │ └─► 纹理数据 │ │ │ │ │ │ │ │ 显示控制器 │ │ │ │ ├─► 从交换链读取图像 │ │ │ │ ├─► 信号处理 (HDMI/DP) │ │ │ │ └─► 发送到显示器 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ 显示器 │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 显示像素 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ │ 数据流动路径 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 硬盘 (磁盘) │ │ ┌──────────────────────┐ │ │ │ 着色器 .spv文件 │ │ │ └──────────┬───────────┘ │ │ │ fstream::read() (CPU执行) │ │ ▼ │ │ 系统内存 (RAM) │ │ ┌──────────────────────┐ │ │ │ 着色器字节数组 │ ← createShaderModule() 读取到系统内存 │ │ │ SPIR-V代码 │ │ │ └──────────┬───────────┘ │ │ │ vkCreateShaderModule() (CPU执行) │ │ │ PCIe总线传输 (CPU→GPU) │ │ ▼ │ │ GPU显存 (VRAM) │ │ ┌──────────────────────┐ │ │ │ 编译后的着色器 │ ← GPU驱动编译为原生指令 │ │ │ GPU原生指令 │ │ │ └──────────┬───────────┘ │ │ │ GPU执行 │ │ ▼ │ │ GPU核心执行 │ │ ┌──────────────────────┐ │ │ │ 着色器核心执行 │ │ │ │ 光栅化、混合等 │ │ │ └──────────┬───────────┘ │ │ │ 写入帧缓冲 │ │ ▼ │ │ GPU显存中的交换链图像 │ │ ┌──────────────────────┐ │ │ │ 渲染结果 (像素数据) │ │ │ └──────────┬───────────┘ │ │ │ 显示控制器读取 │ │ ▼ │ │ 显示信号 (HDMI/DP) │ │ ┌──────────────────────┐ │ │ │ 视频信号输出 │ │ │ └──────────┬───────────┘ │ │ │ │ │ ▼ │ │ 显示器屏幕 │ │ ┌──────────────────────┐ │ │ │ 显示三角形 │ │ │ └──────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ │ CPU和GPU并行执行时间线 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 时间 → │ │ │ │ CPU线程: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 初始化阶段 (主要CPU) │ │ │ │ ├─► createInstance() ████ │ │ │ │ ├─► createSurface() ███ │ │ │ │ ├─► pickPhysicalDevice() ██████ (查询GPU) │ │ │ │ ├─► createDevice() ████ │ │ │ │ ├─► createSwapChain() ████ (分配GPU内存) │ │ │ │ ├─► createPipeline() ████████ (上传着色器到GPU) │ │ │ │ └─► ... │ │ │ │ │ │ │ │ 渲染循环 (每帧) │ │ │ │ ├─► vkAcquireImage() ██ (等待GPU) │ │ │ │ ├─► recordCommands() ████ (CPU录制) │ │ │ │ ├─► vkQueueSubmit() ██ (提交到GPU) │ │ │ │ ├─► vkQueuePresent() ██ (等待GPU) │ │ │ │ └─► 返回 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 提交命令 │ │ ▼ │ │ GPU执行: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 等待命令... (GPU空闲) │ │ │ │ │ │ │ │ 收到命令开始执行 │ │ │ │ ├─► 等待图像可用 ██ │ │ │ │ ├─► 执行顶点着色器 ████████ │ │ │ │ ├─► 光栅化 ██████ │ │ │ │ ├─► 执行片段着色器 ████████ │ │ │ │ ├─► 混合/写入 ████ │ │ │ │ ├─► 呈现到显示 ████ │ │ │ │ └─► 触发栅栏 █ │ │ │ │ │ │ │ │ 继续下一帧... │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 注意: │ │ 1. CPU提交命令后可以立即处理其他事情 (异步) │ │ 2. GPU执行时CPU可以同时录制下一帧的命令 │ │ 3. 通过信号量和栅栏实现CPU-GPU同步 │ │ 4. 双重/三重缓冲允许CPU和GPU并行工作 │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐ │ 关键概念总结 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. 哪些在CPU执行? │ │ ├─► 所有Vulkan API调用 (vkCreate*, vkAllocate*, vkGet*) │ │ ├─► 创建和管理对象 (VkInstance, VkDevice, VkPipeline等) │ │ ├─► 录制命令缓冲区 (vkCmd*) │ │ ├─► 提交命令到GPU (vkQueueSubmit) │ │ ├─► 读取文件 (着色器、纹理、模型数据) │ │ └─► 窗口管理 (GLFW、操作系统API) │ │ │ │ 2. 哪些在GPU执行? │ │ ├─► 顶点着色器执行 │ │ ├─► 细分和几何着色器执行 │ │ ├─► 光栅化 │ │ ├─► 片段着色器执行 │ │ ├─► 深度/模板测试 │ │ ├─► 颜色混合 │ │ ├─► 计算着色器执行 │ │ └─► 呈现到显示 │ │ │ │ 3. 哪些涉及CPU→GPU通信? │ │ ├─► 创建交换链 (分配GPU显存) │ │ ├─► 创建着色器模块 (上传着色器代码) │ │ ├─► 创建缓冲区/图像 (分配GPU资源) │ │ ├─► vkQueueSubmit (传输命令缓冲区) │ │ ├─► vkQueuePresent (通知显示控制器) │ │ └─► 同步操作 (信号量/栅栏状态查询) │ │ │ │ 4. 哪些数据在GPU显存中? │ │ ├─► 交换链图像 (2-3个帧缓冲) │ │ ├─► 编译后的着色器代码 │ │ ├─► 顶点/索引缓冲区 │ │ ├─► 纹理图像 │ │ ├─► Uniform缓冲区 (常量数据) │ │ └─► 命令缓冲区 │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘