Vulkan学习笔记

  1. 顺序很重要#define 必须在 #include <GLFW/glfw3.h> 之前出现,否则不起作用。

  2. 作用 :当 GLFW 的头文件看到这个宏被定义后,它就会知道你需要 Vulkan 支持,并自动执行 #include <vulkan/vulkan.h>,你就不需要再重复包含了。

问题:只是为了添加#include <vulkan/vulkan.h>我直接写不也差不多?

答:当你定义了 GLFW_INCLUDE_VULKAN,GLFW 不只是帮你自动 #include 了 Vulkan 头文件,它实际上接管了 Vulkan 头文件的包含策略

GLFW 内部会根据你使用的平台(Windows, Linux, macOS),自动选择一个合适的 Vulkan 头文件包含方式。它可能会做一些细微的处理,来确保 GLFW 自身的 Vulkan 函数声明和 Vulkan SDK 的头文件完美兼容,避免某些平台下的头文件包含顺序问题或宏冲突。


glfwWindowHint的作用:

glfwWindowHint 函数的作用是在创建窗口之前 ,向 GLFW 预设一系列窗口属性。你可以把它理解成填写一份"订单",告诉系统你想要一个什么样的窗口,然后 glfwCreateWindow 会按照这份订单来"生产"窗口。

在你调用 glfwCreateWindow 之前,明确告诉 GLFW:这个窗口将来是为 Vulkan、OpenGL 还是 OpenGL ES 准备的。根据你的设定,GLFW 会在创建窗口实例时,加载对应的上下文或做好相关准备。

具体用法如下,可选的参数值有三个:

  1. GLFW_NO_API

    这是你用 Vulkan 时必须设置的。它告诉 GLFW 这个窗口不建立任何 OpenGL/ES 上下文。

    复制代码
    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);

    因为 Vulkan 完全自己管理上下文,不需要 GLFW 帮忙,所以用这个值来明确"不用 OpenGL"。

  2. GLFW_OPENGL_API

    创建标准桌面 OpenGL 窗口。

  3. GLFW_OPENGL_ES_API

    创建 OpenGL ES 窗口,常用于移动平台或轻量级嵌入式设备。

在用 Vulkan 时,如果你忘了设成 GLFW_NO_API,GLFW 可能会默认尝试去创建 OpenGL 上下文,导致不必要的资源浪费甚至潜在的兼容问题。

**问题:**glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE)是什么意思?

glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE) 的意思是:在创建窗口时,禁止用户通过拖拽窗口边缘来调整窗口的大小

这行代码具体做了两件事:

  1. glfwWindowHint:这是你之前了解过的函数,用于在创建窗口前预设属性。

  2. GLFW_RESIZABLE, GLFW_FALSE :这是一个属性键值对。GLFW_RESIZABLE 表示要设置"是否可调整大小"这个属性,GLFW_FALSE 则明确指定为"不允许"。


glfwCreateWindow

复制代码
GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);
  1. widthheight:窗口的宽和高,单位是像素。

  2. title :窗口标题,一个字符串。比如 "My Vulkan App"

  3. monitor:用于全屏模式。

    • NULL:创建窗口模式

    • 传一个 GLFWmonitor*:创建全屏独占模式,窗口会铺满你指定的那个显示器,并自动采用它的分辨率和刷新率。

  4. share :用于共享资源。传另一个窗口的指针,可以让新窗口和它共享 OpenGL 的纹理、缓冲等资源。在 Vulkan 中很少用,一般传 NULL

    GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
    if (!primaryMonitor) {
    fprintf(stderr, "Failed to find primary monitor\n");
    glfwTerminate();
    return -1;
    }

    复制代码
     //    注意:monitor 参数传了 primaryMonitor,窗口会自动铺满整个屏幕
     GLFWwindow* window = glfwCreateWindow(
         mode->width,           // 使用显示器的原生宽度
         mode->height,          // 使用显示器的原生高度
         "Vulkan Fullscreen",   // 标题(全屏下通常看不到)
         primaryMonitor,        // 这就是全屏的关键参数!
         NULL                   // 不共享资源
     );

glfwPollEvents 是你窗口事件循环的核心动力。没有它,你的窗口就会卡死,像个没有反应的照片。

它的主要作用就一个:强制 GLFW 去处理所有排队等候的事件,然后立即返回。

事件包含操作系统发给窗口的各种通知:

  • 用户输入:键盘按下、鼠标点击、鼠标移动、手柄摇杆。

  • 窗口状态:窗口被拖动、大小被改变、被最小化、被关闭(点那个 X 按钮)。

  • 其他:显示器连接变化、文件拖放等。


绿色部分为不可编程模块,紫色为可编程模块


layout.frag 文件里的作用,主要是用来硬性指定输出的去向 (每个颜色值分别要写到哪里)和声明资源的内存布局,确保数据在不同的处理器之间能被精确无误地读取。

复制代码
// 顶点着色器中:顶点的位置数据来自第 0 号插槽,颜色数据来自第 1 号插槽
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;

// 片元着色器中:将第一个输出颜色写入第 0 号颜色附件,第二个写入第 1 号
layout(location = 0) out vec4 outColor;   // 一般最终颜色
layout(location = 1) out vec4 outNormal;  // 例如延迟渲染的 G-Buffer 法线

问题:

顶点着色器layout out的变量location为0,我可以在片元着色器中用layout in locaiont = 0 把顶点着色器的值带到片元着色器?

答:完全正确,你的理解非常精准。


lfwInit 是使用 GLFW 库时的第一个必须调用的函数,作用是初始化整个 GLFW 库,为后续所有窗口和输入操作做准备。

函数原型:

c

复制代码
int glfwInit(void);

返回值:

  • 返回 GLFW_TRUE(非零):初始化成功。

  • 返回 GLFW_FALSE(零):初始化失败,后续所有 GLFW 函数都不能使用。


它内部做了什么?

简单说,它负责向操作系统"打招呼",建立起 GLFW 与系统底层之间的联系。具体包括:

  1. 初始化后端:根据编译时的平台(Windows、Linux、macOS),加载对应的系统服务。

  2. 设置输入处理:准备好键盘、鼠标、手柄等输入设备的监听机制。

  3. 配置图形 API:检测系统支持的 OpenGL / Vulkan 能力,但不会创建任何窗口或上下文。

  4. 分配内部数据结构:为后续的窗口管理、事件队列等分配必要的内存。


它们之间有一条隐形的线连着 。这个连接点就是 GLFW_CLIENT_API 这个选项。

我们回想一下,在创建窗口前,你明确设定了:

c

复制代码
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);

这行代码就是关键。它告诉 GLFW:"我要创建的窗口,不需要你现在就为我建立图形上下文,因为稍后我会自己用一个图形 API(Vulkan)来接管它。"

如果把这个行为拆开来看,过程是这样的:

  1. 创建窗口 (GLFW 的工作)
    glfwCreateWindow 执行后,GLFW 向操作系统申请了一个原生的空白窗口。此时,这个窗口与 Vulkan 还没有发生任何关系,只是一个普通的系统窗口。

  2. 建立连接 (你的工作)
    为了让 Vulkan 能在这个窗口上画画 ,你需要调用另一个函数 glfwCreateWindowSurface就是在这里,VkInstance 被用上了


VKAPI_ATTR ------ 函数属性宏

它控制函数的可见性调用约定(属性)。

  • 在 Windows 上 :它通常定义为 __stdcall,这决定了函数调用时参数入栈和清理的方式。Vulkan 运行时是用 __stdcall 编译的,你的程序也必须用相同约定来调用,否则会堆栈错误。

  • 在 Linux / Android 等平台上:它通常定义为空,因为这些平台有统一的默认调用约定。

  • 对于动态库导出 :当编译 Vulkan 加载器这类库时,它还会包含 __declspec(dllexport)__attribute__((visibility("default"))),用来将函数导出。

简单来说,VKAPI_ATTR 就是告诉编译器"这个函数需要按 Vulkan 规定的方式被找到和调用"。

VKAPI_CALL ------ 函数名修饰宏

它主要处理 C++ 和 C 之间的名称问题。

  • 核心作用 :定义为 extern "C"(C++ 环境下)。这要求编译器按 C 语言的规则处理函数名,而不是 C++ 那种带有参数类型信息的复杂修饰名。

  • 必要性 :正是因为 VKAPI_CALL,你才能用 GetProcAddress 通过名字 "vkCreateInstance" 找到这个函数,否则函数名会变成类似 ?vkCreateInstance@@YA... 这样的乱码。

相关推荐
Hammer_Hans1 小时前
DFT笔记46
笔记
声网2 小时前
OpenAI Realtime API 重磅更新:锚定语音模型「深度推理+自主执行」演进路径|Voice Agent 学习笔记
笔记·学习
前端摸鱼匠2 小时前
【AI大模型春招面试题31】什么是“零样本学习(Zero-Shot)”“少样本学习(Few-Shot)”?大模型实现这类能力的核心原因?
人工智能·学习·面试·大模型·求职招聘
星光技术人3 小时前
投机采样 Speculative Decoding 核心笔记
人工智能·笔记·深度学习·计算机视觉·语言模型·自动驾驶
码途漫谈3 小时前
Easy-Vibe高级开发篇阅读笔记(二十)——多平台开发之个人网页与博客开发
人工智能·笔记·ai·开源·ai编程
码途漫谈4 小时前
Easy-Vibe高级开发篇阅读笔记(二十一)——AI能力强化之RAG 与企业级智能客服
人工智能·笔记·ai·开源·ai编程
薛定猫AI4 小时前
【深度解析】Hermes Agent:持久记忆、自学习闭环与桌面化 Autonomous AI 工作流实践
人工智能·学习
谙弆悕博士4 小时前
快速学C语言—— 第0章:C语言简介
c语言·开发语言·经验分享·笔记·程序人生·课程设计·学习方法
老虎06274 小时前
黑马程序员苍穹外卖--学习笔记(苍穹外卖万字总结—重点知识,面试常见问题)超全
笔记·学习·面试