这里是勇芳编写的软件使用教程和使用帮助
论坛的首页 勇芳的软件
教程和帮助
  • VisualFreeBasic编程文档
  • 勇芳系列软件帮助说明教程
  • 留言或交流 登录
    登录
    侧边栏壁纸
    博主头像
    勇芳

    • 累计撰写 330 篇文章
    • 累计收到 0 条评论
    • 首页
    • 栏目
      • 论坛的首页
      • 勇芳的软件
      • 教程和帮助
        • VisualFreeBasic编程文档
        • 勇芳系列软件帮助说明教程
      • 留言或交流
      • 登录
    教程和帮助
    • SDL3_API分类参考_异步IO(CategoryAsyncIO) 2026-3-6
      SDL3_API分类参考_异步IO(CategoryAsyncIO) 异步IO子系统(CategoryAsyncIO) SDL 提供了异步执行 I/O 操作的方式,允许应用程序在读写文件时无需等待数据实际传输完成——发起 I/O 请求的函数在请求处理期间永远不会阻塞。 数据会在后台传输,应用程序可在合适的时机检查操作结果。 这种方式比同步读写文件更复杂,但能提升效率,避免因硬盘读写延迟导致帧率下降等问题。 异步IO通用使用流程 创建一个或多个 SDL_AsyncIOQueue(异步IO队列)对象; 通过 SDL_AsyncIOFromFile 打开文件,创建异步IO句柄; 调用 SDL_ReadAsyncIO 或 SDL_WriteAsyncIO 启动文件的IO任务,并将任务放入指定队列; 后续通过 SDL_GetAsyncIOResult 检查队列,非阻塞地获取已完成的任务结果(任务可能以任意顺序成功/失败完成); 所有任务完成后,调用 SDL_CloseAsyncIO 关闭文件(这也会生成一个任务,因为可能需要将缓存刷入磁盘)。 上述流程可在单线程中无阻塞运行,也可在后台线程中等待队列: 在一个或多个线程中调用 SDL_WaitAsyncIOResult,高效阻塞直到有新任务完成; 程序关闭时,调用 SDL_SignalAsyncIOQueue 唤醒所有休眠线程(即使无新任务完成)。 此外,为匹配同步的 SDL_LoadFile,SDL 提供了便捷函数 SDL_LoadFileAsync——自动分配缓冲区、读取整个文件数据并添加空终止符,你只需后续检查结果即可。 底层实现 SDL 会在支持的平台上使用高效的新API(如 Linux 的 io_uring、Windows 11 的 IoRing);若这些技术不可用,SDL 会将任务卸载到线程池,在不阻塞应用程序的前提下执行同步加载。 最佳实践 简单非阻塞IO:仅需在数据就绪时获取、避免磁盘等待导致帧率丢失的应用,可按需调用 SDL_ReadAsyncIO/SDL_LoadFileAsync,每帧调用 SDL_GetAsyncIOResult 检查完成的任务并处理数据; 队列管理:程序不同模块需独立IO时,可各自创建队列(避免误消费对方的任务结果);队列会占用少量资源,但无需为每个任务创建队列——多个任务放入单个队列更高效(任务按完成顺序返回,与提交顺序无关); 线程与队列匹配:若追求极致效率,建议「一线程一队列」,多线程并行处理,且任务的发起和结果消费由同一线程完成(现代平台可最大化数据传输效率,避免线程间队列访问竞争); 数据刷盘:即使关闭任务完成,写入的数据也不保证已写入物理介质——需将 SDL_CloseAsyncIO 的 flush 参数设为 true 强制刷盘(否则断电可能导致数据丢失);但刷盘耗时更长,可根据应用需求选择是否执行。 函数 SDL_AsyncIOFromFile:从文件创建异步IO句柄(指定文件路径和打开模式,返回 SDL_AsyncIO 句柄) SDL_CloseAsyncIO:关闭异步IO句柄(生成关闭任务,可指定是否刷盘,任务完成后释放文件资源) SDL_CreateAsyncIOQueue:创建异步IO队列(用于管理异步IO任务,返回队列句柄) SDL_DestroyAsyncIOQueue:销毁异步IO队列(释放队列资源,需确保队列中无未处理的任务) SDL_GetAsyncIOResult:非阻塞获取队列中已完成的任务结果(返回首个完成的任务,无完成任务则返回空) SDL_GetAsyncIOSize:获取异步IO句柄对应文件/流的总大小(字节),仅支持可定位的数据源 SDL_LoadFileAsync:异步加载整个文件到内存(自动分配缓冲区,添加到队列,完成后数据带空终止符) SDL_ReadAsyncIO:发起异步读任务(指定读取缓冲区、大小、偏移,任务加入队列后立即返回) SDL_SignalAsyncIOQueue:唤醒等待该队列的所有线程(即使无新任务完成,用于程序退出时清理) SDL_WaitAsyncIOResult:阻塞等待队列中的任务完成(直到有任务完成或被信号唤醒,返回完成的任务) SDL_WriteAsyncIO:发起异步写任务(指定写入缓冲区、大小、偏移,任务加入队列后立即返回) 数据类型 SDL_AsyncIO:异步IO句柄类型(标识一个打开的异步文件/流,所有异步IO操作基于此句柄) SDL_AsyncIOQueue:异步IO队列类型(管理异步IO任务的队列,任务按完成顺序存储结果) 结构体 SDL_AsyncIOOutcome:异步IO任务结果结构体(包含任务类型、句柄、操作字节数、错误状态等) 枚举 SDL_AsyncIOResult:异步IO结果枚举(成功、失败、队列空、被信号唤醒等) SDL_AsyncIOTaskType:异步IO任务类型枚举(读、写、关闭、加载文件等) 宏 (无) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充枚举/结构体/函数声明(FreeBASIC 绑定可能缺失) Enum SDL_AsyncIOResult SDL_ASYNC_IO_SUCCESS = 0 ' 任务成功 SDL_ASYNC_IO_FAILED = 1 ' 任务失败 SDL_ASYNC_IO_QUEUE_EMPTY = 2 ' 队列无完成任务 SDL_ASYNC_IO_SIGNALED = 3 ' 被信号唤醒(无任务) End Enum Enum SDL_AsyncIOTaskType SDL_ASYNC_IO_TASK_READ = 0 ' 读任务 SDL_ASYNC_IO_TASK_WRITE = 1 ' 写任务 SDL_ASYNC_IO_TASK_CLOSE = 2 ' 关闭任务 SDL_ASYNC_IO_TASK_LOAD_FILE = 3 ' 加载文件任务 End Enum Type SDL_AsyncIO As Any Ptr ' 异步IO句柄 Type SDL_AsyncIOQueue As Any Ptr ' 异步IO队列 Type SDL_AsyncIOOutcome task_type As SDL_AsyncIOTaskType ' 任务类型 aio As SDL_AsyncIO ' 关联的异步IO句柄 bytes_processed As ULLong ' 处理的字节数 result As SDL_AsyncIOResult ' 任务结果 userdata As Any Ptr ' 用户自定义数据 End Type ' 核心函数声明 Declare Function SDL_CreateAsyncIOQueue CDecl () As SDL_AsyncIOQueue Declare Sub SDL_DestroyAsyncIOQueue CDecl (ByVal queue As SDL_AsyncIOQueue) Declare Function SDL_AsyncIOFromFile CDecl (ByVal path As ZString Ptr, ByVal mode As ZString Ptr) As SDL_AsyncIO Declare Function SDL_ReadAsyncIO CDecl (ByVal queue As SDL_AsyncIOQueue, ByVal aio As SDL_AsyncIO, _ ByVal buffer As Any Ptr, ByVal size As ULLong, ByVal offset As LongInt, ByVal userdata As Any Ptr) As Integer Declare Function SDL_WriteAsyncIO CDecl (ByVal queue As SDL_AsyncIOQueue, ByVal aio As SDL_AsyncIO, _ ByVal buffer As Any Ptr, ByVal size As ULLong, ByVal offset As LongInt, ByVal userdata As Any Ptr) As Integer Declare Function SDL_CloseAsyncIO CDecl (ByVal queue As SDL_AsyncIOQueue, ByVal aio As SDL_AsyncIO, _ ByVal flush As Integer, ByVal userdata As Any Ptr) As Integer Declare Function SDL_GetAsyncIOResult CDecl (ByVal queue As SDL_AsyncIOQueue, ByVal outcome As SDL_AsyncIOOutcome Ptr) As Integer Declare Function SDL_WaitAsyncIOResult CDecl (ByVal queue As SDL_AsyncIOQueue, ByVal outcome As SDL_AsyncIOOutcome Ptr) As Integer Declare Sub SDL_SignalAsyncIOQueue CDecl (ByVal queue As SDL_AsyncIOQueue) Declare Function SDL_LoadFileAsync CDecl (ByVal queue As SDL_AsyncIOQueue, ByVal path As ZString Ptr, _ ByVal userdata As Any Ptr) As Integer Declare Sub SDL_free CDecl (ByVal ptr As Any Ptr) ' 打印异步IO任务结果 Sub PrintAsyncResult(ByVal outcome As SDL_AsyncIOOutcome Ptr) Dim As ZString * 20 taskTypeStr Select Case outcome->task_type Case SDL_ASYNC_IO_TASK_READ: taskTypeStr = "读任务" Case SDL_ASYNC_IO_TASK_WRITE: taskTypeStr = "写任务" Case SDL_ASYNC_IO_TASK_CLOSE: taskTypeStr = "关闭任务" Case SDL_ASYNC_IO_TASK_LOAD_FILE: taskTypeStr = "加载文件任务" Case Else: taskTypeStr = "未知任务" End Select Dim As ZString * 20 resultStr Select Case outcome->result Case SDL_ASYNC_IO_SUCCESS: resultStr = "成功" Case SDL_ASYNC_IO_FAILED: resultStr = "失败" Case SDL_ASYNC_IO_QUEUE_EMPTY: resultStr = "队列空" Case SDL_ASYNC_IO_SIGNALED: resultStr = "被信号唤醒" Case Else: resultStr = "未知结果" End Select SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "任务结果:") SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 任务类型:%s", taskTypeStr) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 处理字节数:%llu", outcome->bytes_processed) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 执行结果:%s", resultStr) End Sub ' 示例1:异步读写文件 Sub AsyncFileRWExample() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【示例1:异步读写文件】") ' 1. 创建异步IO队列 Dim As SDL_AsyncIOQueue Ptr queue = SDL_CreateAsyncIOQueue() If (queue = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "创建异步IO队列失败:%s", SDL_GetError()) Exit Sub End If ' 2. 打开异步文件句柄(可写模式) Dim As SDL_AsyncIO Ptr aio = SDL_AsyncIOFromFile(StrPtr("async_test.txt"), StrPtr("wb")) If (aio = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "打开异步文件失败:%s", SDL_GetError()) SDL_DestroyAsyncIOQueue(queue) Exit Sub End If ' 3. 准备写入数据并发起异步写任务 Dim As ZString * 100 writeData = "SDL 异步IO测试数据 - 写入内容" Dim As ULLong writeSize = Len(writeData) If (SDL_WriteAsyncIO(queue, aio, @writeData, writeSize, 0, NULL) = 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "发起异步写任务失败:%s", SDL_GetError()) SDL_DestroyAsyncIOQueue(queue) Exit Sub End If SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "发起异步写任务(大小:%llu 字节)", writeSize) ' 4. 等待写任务完成(阻塞方式) Dim As SDL_AsyncIOOutcome writeOutcome If (SDL_WaitAsyncIOResult(queue, @writeOutcome) = 1) Then PrintAsyncResult(@writeOutcome) End If ' 5. 重新打开文件(只读模式),发起异步读任务 SDL_CloseAsyncIO(queue, aio, 1, NULL) ' 先关闭写句柄(刷盘) aio = SDL_AsyncIOFromFile(StrPtr("async_test.txt"), StrPtr("rb")) If (aio = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "重新打开异步文件失败:%s", SDL_GetError()) SDL_DestroyAsyncIOQueue(queue) Exit Sub End If ' 分配读缓冲区 Dim As Any Ptr readBuffer = SDL_malloc(writeSize + 1) If (readBuffer = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "内存分配失败") SDL_DestroyAsyncIOQueue(queue) Exit Sub End If ' 发起读任务 If (SDL_ReadAsyncIO(queue, aio, readBuffer, writeSize, 0, NULL) = 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "发起异步读任务失败:%s", SDL_GetError()) SDL_free(readBuffer) SDL_DestroyAsyncIOQueue(queue) Exit Sub End If SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "发起异步读任务(大小:%llu 字节)", writeSize) ' 6. 非阻塞检查读任务结果(模拟每帧检查) Dim As Integer checkCount = 0 Dim As SDL_AsyncIOOutcome readOutcome Do checkCount += 1 If (SDL_GetAsyncIOResult(queue, @readOutcome) = 1) Then SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "第%d次检查:读任务完成", checkCount) PrintAsyncResult(@readOutcome) Exit Do End If SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "第%d次检查:读任务未完成,等待10ms...", checkCount) SDL_Delay(10) ' 模拟帧间隔 Loop Until checkCount >= 100 ' 7. 打印读取内容 If (readOutcome.result = SDL_ASYNC_IO_SUCCESS) Then Dim As ZString Ptr readStr = Cast(ZString Ptr, readBuffer) readStr[readOutcome.bytes_processed] = 0 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "读取内容:%s", *readStr) End If ' 8. 发起关闭任务(刷盘) Dim As SDL_AsyncIOOutcome closeOutcome SDL_CloseAsyncIO(queue, aio, 1, NULL) SDL_WaitAsyncIOResult(queue, @closeOutcome) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "关闭任务结果:%s", IIf(closeOutcome.result=SDL_ASYNC_IO_SUCCESS, "成功", "失败")) ' 9. 释放资源 SDL_free(readBuffer) SDL_SignalAsyncIOQueue(queue) ' 唤醒可能等待的线程 SDL_DestroyAsyncIOQueue(queue) End Sub ' 示例2:异步加载整个文件 Sub AsyncLoadFileExample() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【示例2:异步加载整个文件】") ' 1. 创建队列 Dim As SDL_AsyncIOQueue Ptr queue = SDL_CreateAsyncIOQueue() If (queue = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "创建异步IO队列失败:%s", SDL_GetError()) Exit Sub End If ' 2. 发起异步加载文件任务 Dim As Integer userdata = 123 ' 自定义用户数据 If (SDL_LoadFileAsync(queue, StrPtr("async_test.txt"), @userdata) = 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "发起异步加载文件任务失败:%s", SDL_GetError()) SDL_DestroyAsyncIOQueue(queue) Exit Sub End If ' 3. 等待任务完成 Dim As SDL_AsyncIOOutcome loadOutcome If (SDL_WaitAsyncIOResult(queue, @loadOutcome) = 1) Then PrintAsyncResult(@loadOutcome) ' 4. 处理加载结果(数据在 userdata 指向的缓冲区) If (loadOutcome.result = SDL_ASYNC_IO_SUCCESS AndAlso loadOutcome.userdata <> NULL) Then Dim As ZString Ptr fileData = Cast(ZString Ptr, loadOutcome.userdata) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "异步加载文件内容:%s", *fileData) SDL_free(fileData) ' 释放 SDL_LoadFileAsync 分配的内存 End If End If ' 5. 清理资源 SDL_DestroyAsyncIOQueue(queue) End Sub ' 主程序 Sub Main() ' 初始化 SDL If (SDL_Init(0) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' 运行示例 AsyncFileRWExample() AsyncLoadFileExample() ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 异步IO核心特性: 非阻塞:所有任务发起函数立即返回,IO操作在后台执行; 结果获取:支持两种方式——SDL_GetAsyncIOResult(非阻塞,适合帧循环)、SDL_WaitAsyncIOResult(阻塞,适合后台线程); 任务顺序:任务按完成顺序返回结果,与提交顺序无关; 跨平台优化:自动适配平台原生高效异步IO API,无原生支持时使用线程池兼容。 队列管理规则: 每个队列独立管理任务,不同队列的任务结果不会互相干扰; 程序退出前必须调用 SDL_SignalAsyncIOQueue 唤醒等待队列的线程,避免线程卡死; 销毁队列前需确保所有任务完成,或通过 SDL_SignalAsyncIOQueue 强制唤醒。 刷盘与数据安全: SDL_CloseAsyncIO 的 flush 参数设为 1 时,会强制将缓存写入物理介质(避免断电丢失); 刷盘操作会增加耗时,非关键数据可设为 0 提升效率; 即使任务返回「成功」,未刷盘的数据仍可能停留在系统缓存中。 SDL_LoadFileAsync 特性: 自动分配与文件大小匹配的缓冲区,无需手动计算; 数据末尾自动添加空终止符,可直接作为字符串使用; 完成后需通过 SDL_free 释放缓冲区,而非平台原生 free。 总结 核心优势: 非阻塞IO操作,避免主线程/帧循环因磁盘IO阻塞导致卡顿; 自动适配平台高效异步IO API,兼顾性能与兼容性; 提供队列管理机制,支持多任务/多线程安全处理; 便捷的 SDL_LoadFileAsync 函数,简化异步加载整个文件的流程。 使用建议: 游戏/实时应用中,优先使用非阻塞的 SDL_GetAsyncIOResult 每帧检查任务结果; 后台数据处理线程中,使用 SDL_WaitAsyncIOResult 高效等待任务; 关键数据写入后,关闭文件时务必开启 flush 确保数据持久化; 多个独立模块使用异步IO时,为每个模块创建独立队列,避免结果混淆。 关键接口: 队列管理:SDL_CreateAsyncIOQueue()/SDL_DestroyAsyncIOQueue()/SDL_SignalAsyncIOQueue(); 任务发起:SDL_AsyncIOFromFile()/SDL_ReadAsyncIO()/SDL_WriteAsyncIO()/SDL_LoadFileAsync(); 结果获取:SDL_GetAsyncIOResult()/SDL_WaitAsyncIOResult(); 资源清理:SDL_CloseAsyncIO()(务必调用,且按需刷盘)。
      • 2026年-3月-6日
      • 9 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_输入输出流(CategoryIOStream) 2026-3-6
      SDL3_API分类参考_输入输出流(CategoryIOStream) 输入输出流子系统(CategoryIOStream) SDL 提供了一套用于读写数据流的抽象接口,支持文件、内存等多种数据源的实现,同时应用程序也可以自定义实现该接口。 注意:SDL_IOStream 与 C++ 标准库中的 iostream 类无关联,仅二者都是用于读写数据的抽象接口这一点相似。 函数 SDL_CloseIO:关闭 IO 流(释放相关资源,关闭底层数据源) SDL_FlushIO:刷新 IO 流(将缓冲区数据写入底层数据源,确保数据持久化) SDL_GetIOProperties:获取 IO 流的属性(如是否可读写、是否支持定位等) SDL_GetIOSize:获取 IO 流的总大小(字节),仅支持可定位的流 SDL_GetIOStatus:获取 IO 流的当前状态(成功、EOF、错误等) SDL_IOFromConstMem:从只读内存块创建 IO 流(数据源为 const 内存,仅支持读操作) SDL_IOFromDynamicMem:从动态内存创建 IO 流(自动管理内存,支持读写,可扩容) SDL_IOFromFile:从文件创建 IO 流(指定文件路径和打开模式,如 "rb"、"wb" 等) SDL_IOFromMem:从内存块创建 IO 流(数据源为可读写内存,支持读写操作) SDL_IOprintf:格式化写入 IO 流(类似标准库 printf,输出到 IO 流) SDL_IOvprintf:可变参数格式化写入 IO 流(底层实现,通常不直接调用) SDL_LoadFile:加载整个文件到内存(自动分配内存,返回文件数据和大小) SDL_LoadFile_IO:从 IO 流加载全部数据到内存(自动分配内存,适配任意 IO 流) SDL_OpenIO:打开自定义 IO 流(基于 SDL_IOStreamInterface 实现自定义数据源) SDL_ReadIO:从 IO 流读取指定字节数的数据到缓冲区(返回实际读取的字节数) SDL_ReadS16BE:从 IO 流读取大端序 16 位有符号整数 SDL_ReadS16LE:从 IO 流读取小端序 16 位有符号整数 SDL_ReadS32BE:从 IO 流读取大端序 32 位有符号整数 SDL_ReadS32LE:从 IO 流读取小端序 32 位有符号整数 SDL_ReadS64BE:从 IO 流读取大端序 64 位有符号整数 SDL_ReadS64LE:从 IO 流读取小端序 64 位有符号整数 SDL_ReadS8:从 IO 流读取 8 位有符号整数(字节) SDL_ReadU16BE:从 IO 流读取大端序 16 位无符号整数 SDL_ReadU16LE:从 IO 流读取小端序 16 位无符号整数 SDL_ReadU32BE:从 IO 流读取大端序 32 位无符号整数 SDL_ReadU32LE:从 IO 流读取小端序 32 位无符号整数 SDL_ReadU64BE:从 IO 流读取大端序 64 位无符号整数 SDL_ReadU64LE:从 IO 流读取小端序 64 位无符号整数 SDL_ReadU8:从 IO 流读取 8 位无符号整数(字节) SDL_SaveFile:将内存数据保存到文件(指定路径和数据,自动创建/覆盖文件) SDL_SaveFile_IO:将内存数据保存到 IO 流(适配任意可写 IO 流) SDL_SeekIO:定位 IO 流的读写指针(支持相对当前位置、开头、末尾定位) SDL_TellIO:获取 IO 流当前读写指针位置(距离流开头的字节数) SDL_WriteIO:将缓冲区数据写入 IO 流(返回实际写入的字节数) SDL_WriteS16BE:向 IO 流写入大端序 16 位有符号整数 SDL_WriteS16LE:向 IO 流写入小端序 16 位有符号整数 SDL_WriteS32BE:向 IO 流写入大端序 32 位有符号整数 SDL_WriteS32LE:向 IO 流写入小端序 32 位有符号整数 SDL_WriteS64BE:向 IO 流写入大端序 64 位有符号整数 SDL_WriteS64LE:向 IO 流写入小端序 64 位有符号整数 SDL_WriteS8:向 IO 流写入 8 位有符号整数(字节) SDL_WriteU16BE:向 IO 流写入大端序 16 位无符号整数 SDL_WriteU16LE:向 IO 流写入小端序 16 位无符号整数 SDL_WriteU32BE:向 IO 流写入大端序 32 位无符号整数 SDL_WriteU32LE:向 IO 流写入小端序 32 位无符号整数 SDL_WriteU64BE:向 IO 流写入大端序 64 位无符号整数 SDL_WriteU64LE:向 IO 流写入小端序 64 位无符号整数 SDL_WriteU8:向 IO 流写入 8 位无符号整数(字节) 数据类型 SDL_IOStream:IO 流句柄类型(标识一个打开的 IO 流,所有操作均基于此句柄) 结构体 SDL_IOStreamInterface:IO 流接口结构体(自定义 IO 流时使用,包含读写、定位、关闭等函数指针) 枚举 SDL_IOStatus:IO 流状态枚举(成功、到达文件末尾、读错误、写错误、定位错误等) SDL_IOWhence:IO 流定位基准枚举(SDL_IO_SEEK_SET:开头,SDL_IO_SEEK_CUR:当前位置,SDL_IO_SEEK_END:末尾) 宏 (无) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充枚举/类型/函数声明(FreeBASIC 绑定可能缺失) Enum SDL_IOWhence SDL_IO_SEEK_SET = 0 ' 相对于流开头 SDL_IO_SEEK_CUR = 1 ' 相对于当前位置 SDL_IO_SEEK_END = 2 ' 相对于流末尾 End Enum Type SDL_IOStream As Any Ptr ' IO 流句柄 Declare Function SDL_IOFromFile CDecl (ByVal path As ZString Ptr, ByVal mode As ZString Ptr) As SDL_IOStream Declare Function SDL_ReadIO CDecl (ByVal stream As SDL_IOStream, ByVal ptr As Any Ptr, ByVal size As UInteger, ByVal maxnum As UInteger) As UInteger Declare Function SDL_WriteIO CDecl (ByVal stream As SDL_IOStream, ByVal ptr As Any Ptr, ByVal size As UInteger, ByVal num As UInteger) As UInteger Declare Function SDL_SeekIO CDecl (ByVal stream As SDL_IOStream, ByVal offset As LongInt, ByVal whence As SDL_IOWhence) As LongInt Declare Function SDL_TellIO CDecl (ByVal stream As SDL_IOStream) As LongInt Declare Function SDL_GetIOSize CDecl (ByVal stream As SDL_IOStream) As LongInt Declare Sub SDL_FlushIO CDecl (ByVal stream As SDL_IOStream) Declare Sub SDL_CloseIO CDecl (ByVal stream As SDL_IOStream) Declare Function SDL_IOprintf CDecl (ByVal stream As SDL_IOStream, ByVal fmt As ZString Ptr, ...) As Integer Declare Function SDL_LoadFile CDecl (ByVal path As ZString Ptr, ByVal size As LongInt Ptr) As Any Ptr Declare Sub SDL_free CDecl (ByVal ptr As Any Ptr) ' 补充大小端读写函数声明 Declare Function SDL_ReadU8 CDecl (ByVal stream As SDL_IOStream) As UByte Declare Function SDL_ReadU16LE CDecl (ByVal stream As SDL_IOStream) As UShort Declare Function SDL_ReadU32LE CDecl (ByVal stream As SDL_IOStream) As UInteger Declare Function SDL_WriteU8 CDecl (ByVal stream As SDL_IOStream, ByVal value As UByte) As Integer Declare Function SDL_WriteU16LE CDecl (ByVal stream As SDL_IOStream, ByVal value As UShort) As Integer Declare Function SDL_WriteU32LE CDecl (ByVal stream As SDL_IOStream, ByVal value As UInteger) As Integer ' 示例1:文件 IO 流读写 Sub FileIOExample() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【示例1:文件 IO 流读写】") ' 1. 创建/写入文件 Dim As SDL_IOStream Ptr fileStream = SDL_IOFromFile(StrPtr("test_io.txt"), StrPtr("wb")) If (fileStream = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "打开文件失败:%s", SDL_GetError()) Exit Sub End If ' 写入字符串 Dim As ZString * 100 writeData = "SDL IOStream 测试数据" Dim As UInteger writeCount = SDL_WriteIO(fileStream, @writeData, 1, Len(writeData)) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "写入字节数:%u(预期:%u)", writeCount, Len(writeData)) ' 格式化写入 SDL_IOprintf(fileStream, StrPtr(vbCrLf & "数字:%d,浮点数:%.2f"), 123, 3.14) ' 刷新并关闭 SDL_FlushIO(fileStream) SDL_CloseIO(fileStream) ' 2. 读取文件 fileStream = SDL_IOFromFile(StrPtr("test_io.txt"), StrPtr("rb")) If (fileStream = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "打开文件失败:%s", SDL_GetError()) Exit Sub End If ' 获取文件大小 Dim As LongInt fileSize = SDL_GetIOSize(fileStream) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "文件大小:%lld 字节", fileSize) ' 分配缓冲区并读取全部内容 Dim As Any Ptr readBuffer = SDL_malloc(fileSize + 1) If (readBuffer = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "内存分配失败") SDL_CloseIO(fileStream) Exit Sub End If Dim As UInteger readCount = SDL_ReadIO(fileStream, readBuffer, 1, fileSize) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "读取字节数:%u", readCount) ' 打印读取内容 Dim As ZString Ptr readStr = Cast(ZString Ptr, readBuffer) readStr[readCount] = 0 ' 确保字符串终止 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "文件内容:%s", *readStr) ' 释放资源 SDL_free(readBuffer) SDL_CloseIO(fileStream) End Sub ' 示例2:内存 IO 流操作 Sub MemoryIOExample() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【示例2:内存 IO 流操作】") ' 1. 准备测试数据(小端序数值) Dim As UByte byteVal = &HFF Dim As UShort shortVal = &H1234 Dim As UInteger intVal = &HABCD1234 ' 2. 创建内存缓冲区并写入数据 Dim As Byte Ptr memBuffer = Allocate(1024) ' 1KB 缓冲区 Dim As SDL_IOStream Ptr memStream = SDL_IOFromMem(memBuffer, 1024) If (memStream = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "创建内存流失败:%s", SDL_GetError()) Deallocate(memBuffer) Exit Sub End If ' 写入不同类型数据(小端序) SDL_WriteU8(memStream, byteVal) SDL_WriteU16LE(memStream, shortVal) SDL_WriteU32LE(memStream, intVal) ' 获取当前写入位置 Dim As LongInt writePos = SDL_TellIO(memStream) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "写入后位置:%lld 字节", writePos) ' 3. 定位到流开头,读取数据 SDL_SeekIO(memStream, 0, SDL_IO_SEEK_SET) Dim As UByte readByte = SDL_ReadU8(memStream) Dim As UShort readShort = SDL_ReadU16LE(memStream) Dim As UInteger readInt = SDL_ReadU32LE(memStream) ' 打印读取结果 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "读取数据:") SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 8位无符号整数:0x%02X(预期:0x%02X)", readByte, byteVal) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 16位无符号整数(小端):0x%04X(预期:0x%04X)", readShort, shortVal) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 32位无符号整数(小端):0x%08X(预期:0x%08X)", readInt, intVal) ' 4. 关闭流并释放内存 SDL_CloseIO(memStream) Deallocate(memBuffer) End Sub ' 示例3:加载整个文件到内存 Sub LoadFileExample() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【示例3:加载整个文件到内存】") Dim As LongInt fileSize = 0 Dim As Any Ptr fileData = SDL_LoadFile(StrPtr("test_io.txt"), @fileSize) If (fileData = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "加载文件失败:%s", SDL_GetError()) Exit Sub End If SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "加载文件大小:%lld 字节", fileSize) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "文件内容:%s", Cast(ZString Ptr, fileData)) ' 释放 SDL_LoadFile 分配的内存 SDL_free(fileData) End Sub ' 主程序 Sub Main() ' 初始化 SDL If (SDL_Init(0) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' 运行示例 FileIOExample() MemoryIOExample() LoadFileExample() ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 IO 流核心特性: 抽象性:统一的接口适配不同数据源(文件、内存、自定义流),切换数据源无需修改业务逻辑; 定位支持:可通过 SDL_SeekIO/SDL_TellIO 实现随机读写,仅部分流(如文件、内存)支持定位; 大小端适配:内置大端(BE)/小端(LE)数值读写函数,无需手动转换字节序,适配跨平台数据交换。 IO 流打开模式规则: 与标准 C 库 fopen 模式兼容: "r"/"rb":只读,文件不存在则失败; "w"/"wb":只写,创建/覆盖文件; "a"/"ab":追加,文件不存在则创建; "r+"/"rb+":读写,文件不存在则失败; 后缀 "b" 表示二进制模式,避免 Windows 平台的换行符转换。 读写函数返回值规则: SDL_ReadIO/SDL_WriteIO 的参数 size 是单个数据项的大小,maxnum/num 是数据项数量; 返回值为实际读写的数据项数量(非字节数),例如:SDL_ReadIO(stream, buf, 4, 10) 尝试读取 10 个 4 字节整数,返回 5 表示读取了 5 个(20 字节); 返回 0 可能表示到达流末尾(EOF)或操作失败,需通过 SDL_GetIOStatus 区分。 内存流类型差异: SDL_IOFromConstMem:只读内存流,数据源为 const 指针,不可写; SDL_IOFromMem:可读写内存流,数据源为普通指针,大小固定; SDL_IOFromDynamicMem:动态内存流,自动扩容,适合不确定数据大小的场景。 总结 核心优势: 统一抽象接口,适配多数据源,降低跨数据源切换成本; 内置字节序转换函数,简化跨平台数据读写; 提供 SDL_LoadFile/SDL_SaveFile 快捷函数,一键加载/保存文件; 兼容标准 C 库文件操作模式,学习成本低。 使用建议: 优先使用 SDL_IOFromFile 处理文件读写,替代标准 C 库 fopen/fread/fwrite; 读写数值数据时,优先使用大小端专用函数(如 SDL_ReadU32LE),避免字节序错误; 写入数据后调用 SDL_FlushIO 确保数据刷入底层存储,避免缓存丢失; SDL_LoadFile 分配的内存必须用 SDL_free 释放,而非 free/Deallocate。 关键接口: 流创建:SDL_IOFromFile()/SDL_IOFromMem()/SDL_IOFromDynamicMem(); 基础读写:SDL_ReadIO()/SDL_WriteIO()/SDL_IOprintf(); 定位操作:SDL_SeekIO()/SDL_TellIO()/SDL_GetIOSize(); 快捷操作:SDL_LoadFile()/SDL_SaveFile(); 资源管理:SDL_FlushIO()/SDL_CloseIO()。
      • 2026年-3月-6日
      • 9 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_存储(CategoryStorage) 2026-3-6
      SDL3_API分类参考_存储(CategoryStorage) 存储子系统(CategoryStorage) SDL 的存储 API 是一套高层级接口,旨在屏蔽底层文件操作的跨平台兼容性问题(在 SDL 架构中,该模块基于 Filesystem 和 IOStream 子系统实现)。与传统文件系统 API 相比,它的使用限制更多,主要原因如下: 访问对象限制:传统文件系统 API 通常假设所有存储是统一的整体,但许多平台(尤其是游戏主机)对存储类型的访问有严格区分。例如,游戏资源文件和用户数据往往存储在两个独立的存储设备中,具备完全不同的特性(甚至底层 API 都不同)。 访问方式限制:应用程序常错误地认为所有存储都可写入——但多数平台将游戏资源和用户数据分离,仅用户数据区可写入,游戏资源区为只读。 访问时机限制:文件系统访问最常见的兼容性问题是「时机」——不能假定存储设备始终可用,也不能假定对某设备的访问时长无限制。 反面示例(传统文件操作) 传统文件操作代码因上述假设,存在严重的跨平台兼容性问题: void ReadGameData(void) { extern char** fileNames; extern size_t numFiles; for (size_t i = 0; i < numFiles; i += 1) { FILE *data = fopen(fileNames[i], "rwb"); if (data == NULL) { /* 错误处理 */ } else { /* 读取逻辑 */ fclose(data); } } } void ReadSave(void) { FILE *save = fopen("saves/save0.sav", "rb"); if (save == NULL) { /* 错误处理 */ } else { /* 读取逻辑 */ fclose(save); } } void WriteSave(void) { FILE *save = fopen("saves/save0.sav", "wb"); if (save == NULL) { /* 错误处理 */ } else { /* 写入逻辑 */ fclose(save); } } 该代码的问题: 访问对象:假设游戏资源和存档都在当前工作目录(可能并非游戏安装目录); 访问方式:假设资源路径可写入,且存档与资源同目录也可写入; 访问时机:假设任何时候都能调用文件操作,存储设备始终可用。 这些问题会导致: 游戏安装在只读设备时,资源加载和存档操作均会失败/崩溃; 部分平台需显式挂载存储设备,未挂载时无法找到任何文件; I/O 未刷新/验证,程序异常可能导致存档丢失/损坏。 正面示例(SDL_Storage 实现) 使用 SDL_Storage 可避免上述问题: void ReadGameData(void) { extern char** fileNames; extern size_t numFiles; SDL_Storage *title = SDL_OpenTitleStorage(NULL, 0); if (title == NULL) { /* 错误处理 */ } while (!SDL_StorageReady(title)) { SDL_Delay(1); } for (size_t i = 0; i < numFiles; i += 1) { void* dst; Uint64 dstLen = 0; if (SDL_GetStorageFileSize(title, fileNames[i], &dstLen) && dstLen > 0) { dst = SDL_malloc(dstLen); if (SDL_ReadStorageFile(title, fileNames[i], dst, dstLen)) { /* 读取逻辑 */ } else { /* 错误处理 */ } SDL_free(dst); } else { /* 错误处理 */ } } SDL_CloseStorage(title); } // 读取/写入存档的实现类似,使用 SDL_OpenUserStorage 访问用户存储区 SDL_Storage 的改进点: 明确访问对象:根据场景显式访问游戏资源存储(TitleStorage)或用户存储(UserStorage); 明确访问方式:根据场景使用读/写函数,区分只读/可写存储区; 明确访问时机:使用时打开存储设备,用完关闭,且通过 SDL_StorageReady 检查设备可用性。 路径规则说明 存储 API 对路径有严格规范,确保跨平台兼容性: 所有路径使用 Unix 风格分隔符(/),不支持其他分隔符(如 \); 禁止使用相对目录(. 和 ..); 文件名支持合法 UTF-8 字符串(排除 NULL 终止符和 /),但底层实现可能限制特殊字符。 函数 SDL_CloseStorage:关闭已打开的存储设备(释放资源,刷新未完成的 I/O 操作) SDL_CopyStorageFile:在存储设备内复制文件(支持跨目录复制,保留文件属性) SDL_CreateStorageDirectory:在存储设备中创建目录(仅创建单层,失败返回具体错误) SDL_EnumerateStorageDirectory:枚举存储设备中指定目录的所有项(文件/子目录),通过回调处理 SDL_GetStorageFileSize:获取存储设备中指定文件的大小(字节),目录返回 0 SDL_GetStoragePathInfo:获取存储设备中指定路径的详细信息(类型、大小、修改时间等) SDL_GetStorageSpaceRemaining:获取存储设备的剩余可用空间(字节),用于检查存档写入空间 SDL_GlobStorageDirectory:按通配符(*、? 等)枚举存储设备中指定目录的文件 SDL_OpenFileStorage:打开基于本地文件系统的存储设备(适配传统文件路径) SDL_OpenStorage:通用存储设备打开函数(自定义存储接口时使用) SDL_OpenTitleStorage:打开游戏资源存储设备(只读,用于加载游戏资源) SDL_OpenUserStorage:打开用户数据存储设备(可写,用于保存存档/配置,需传入厂商/应用名) SDL_ReadStorageFile:从存储设备读取文件内容到内存(原子操作,确保数据完整性) SDL_RemoveStoragePath:删除存储设备中的文件/空目录(非空目录删除失败) SDL_RenameStoragePath:重命名/移动存储设备中的文件/目录(目标路径已存在则失败) SDL_StorageReady:检查存储设备是否就绪(返回非0表示可用,需循环等待直到就绪) SDL_WriteStorageFile:将内存数据写入存储设备的文件(原子操作,自动刷新确保数据持久化) 数据类型 SDL_Storage:存储设备句柄类型(标识已打开的存储设备,所有操作均基于此句柄) 结构体 SDL_StorageInterface:存储接口结构体(自定义存储实现时使用,包含各类操作的函数指针) 枚举 (无) 宏 (无) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充类型/函数声明(FreeBASIC 绑定可能缺失) Type SDL_Storage As Any Ptr ' 存储设备句柄 Declare Function SDL_OpenTitleStorage CDecl (ByVal path As ZString Ptr, ByVal flags As Integer) As SDL_Storage Declare Function SDL_OpenUserStorage CDecl (ByVal org As ZString Ptr, ByVal app As ZString Ptr, ByVal flags As Integer) As SDL_Storage Declare Function SDL_StorageReady CDecl (ByVal storage As SDL_Storage) As Integer Declare Function SDL_GetStorageFileSize CDecl (ByVal storage As SDL_Storage, ByVal path As ZString Ptr, ByVal size As ULLong Ptr) As Integer Declare Function SDL_ReadStorageFile CDecl (ByVal storage As SDL_Storage, ByVal path As ZString Ptr, ByVal buffer As Any Ptr, ByVal size As ULLong) As Integer Declare Function SDL_WriteStorageFile CDecl (ByVal storage As SDL_Storage, ByVal path As ZString Ptr, ByVal buffer As Any Ptr, ByVal size As ULLong) As Integer Declare Function SDL_CreateStorageDirectory CDecl (ByVal storage As SDL_Storage, ByVal path As ZString Ptr) As Integer Declare Function SDL_GetStorageSpaceRemaining CDecl (ByVal storage As SDL_Storage, ByVal remaining As ULLong Ptr) As Integer Declare Sub SDL_CloseStorage CDecl (ByVal storage As SDL_Storage) Declare Function SDL_RemoveStoragePath CDecl (ByVal storage As SDL_Storage, ByVal path As ZString Ptr) As Integer ' 读取游戏资源文件(只读,TitleStorage) Sub ReadGameResource(ByVal resourceName As ZString Ptr) ' 打开游戏资源存储设备 Dim As SDL_Storage titleStorage = SDL_OpenTitleStorage(NULL, 0) If (titleStorage = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_STORAGE, "打开游戏资源存储失败:%s", SDL_GetError()) Exit Sub End If ' 等待存储设备就绪 While (SDL_StorageReady(titleStorage) = 0) SDL_Delay(1) Wend ' 获取文件大小 Dim As ULLong fileSize = 0 If (SDL_GetStorageFileSize(titleStorage, resourceName, @fileSize) = 0) Then SDL_LogError(SDL_LOG_CATEGORY_STORAGE, "获取资源文件大小失败:%s", SDL_GetError()) SDL_CloseStorage(titleStorage) Exit Sub End If If (fileSize = 0) Then SDL_LogError(SDL_LOG_CATEGORY_STORAGE, "资源文件为空:%s", *resourceName) SDL_CloseStorage(titleStorage) Exit Sub End If ' 分配内存并读取文件 Dim As Any Ptr buffer = SDL_malloc(fileSize) If (buffer = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_STORAGE, "内存分配失败") SDL_CloseStorage(titleStorage) Exit Sub End If If (SDL_ReadStorageFile(titleStorage, resourceName, buffer, fileSize) = 0) Then SDL_LogError(SDL_LOG_CATEGORY_STORAGE, "读取资源文件失败:%s", SDL_GetError()) Else SDL_LogInfo(SDL_LOG_CATEGORY_STORAGE, "成功读取资源文件:%s(大小:%llu 字节)", *resourceName, fileSize) ' 此处可处理读取到的资源数据 End If ' 释放资源 SDL_free(buffer) SDL_CloseStorage(titleStorage) End Sub ' 保存用户存档(可写,UserStorage) Sub SaveUserData(ByVal saveName As ZString Ptr, ByVal data As ZString Ptr) ' 打开用户数据存储设备(厂商名/应用名) Dim As SDL_Storage userStorage = SDL_OpenUserStorage("SDL_Example", "StorageDemo", 0) If (userStorage = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_STORAGE, "打开用户存储失败:%s", SDL_GetError()) Exit Sub End If ' 等待存储设备就绪 While (SDL_StorageReady(userStorage) = 0) SDL_Delay(1) Wend ' 检查剩余空间 Dim As ULLong remainingSpace = 0 If (SDL_GetStorageSpaceRemaining(userStorage, @remainingSpace) = 0) Then SDL_LogWarn(SDL_LOG_CATEGORY_STORAGE, "无法获取剩余空间,继续操作...") Else Dim As ULLong dataSize = Len(*data) If (remainingSpace < dataSize) Then SDL_LogError(SDL_LOG_CATEGORY_STORAGE, "存储空间不足(需要:%llu 字节,剩余:%llu 字节)", dataSize, remainingSpace) SDL_CloseStorage(userStorage) Exit Sub End If End If ' 创建存档目录(如需) SDL_CreateStorageDirectory(userStorage, "saves") ' 拼接存档路径(必须使用 / 分隔符) Dim As String savePath = "saves/" & *saveName Dim As ULLong dataSize = Len(*data) ' 写入存档数据 If (SDL_WriteStorageFile(userStorage, StrPtr(savePath), data, dataSize) = 0) Then SDL_LogError(SDL_LOG_CATEGORY_STORAGE, "写入存档失败:%s", SDL_GetError()) Else SDL_LogInfo(SDL_LOG_CATEGORY_STORAGE, "成功写入存档:%s(大小:%llu 字节)", savePath, dataSize) End If ' 关闭存储设备 SDL_CloseStorage(userStorage) End Sub ' 主程序 Sub Main() ' 初始化 SDL If (SDL_Init(0) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' ========== 1. 读取游戏资源 ========== SDL_LogInfo(SDL_LOG_CATEGORY_STORAGE, "【1. 读取游戏资源】") ReadGameResource(StrPtr("assets/config.txt")) ' 假设存在该资源文件 ' ========== 2. 保存用户存档 ========== SDL_LogInfo(SDL_LOG_CATEGORY_STORAGE, vbCrLf & "【2. 保存用户存档】") Dim As ZString * 100 saveData = "玩家存档数据:等级=10,金币=9999,进度=50%" SaveUserData(StrPtr("save01.sav"), @saveData) ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 存储设备类型区分: TitleStorage:游戏资源存储区,只读,用于加载游戏内置资源(如贴图、音效、配置); UserStorage:用户数据存储区,可写,用于保存存档、用户配置等,需传入厂商名/应用名以区分不同应用; 两者完全隔离,避免误写游戏资源导致的兼容性问题。 设备就绪检查: 必须通过 SDL_StorageReady 循环等待存储设备就绪,尤其是游戏主机/移动平台,存储设备可能需要挂载时间; 就绪检查通过后,才能执行文件操作,避免「设备未就绪」导致的操作失败。 路径规范强制要求: 仅支持 / 分隔符,不支持 \(即使 Windows 平台); 禁止 ./.. 相对路径,所有路径必须是存储设备内的绝对路径; 示例:saves/save01.sav 合法,./saves/save01.sav 非法。 原子操作保障: SDL_ReadStorageFile/SDL_WriteStorageFile 是原子操作,确保数据读写的完整性; 写入操作会自动刷新缓存,避免程序异常导致的数据丢失(传统 fopen/fwrite 需手动 fflush)。 总结 核心优势: 严格区分只读/可写存储区,避免跨平台写入权限问题; 强制检查存储设备就绪状态,适配需挂载的平台(如游戏主机); 原子化读写操作,保障数据完整性,避免存档损坏; 统一路径规范,彻底解决跨平台路径分隔符/相对路径问题。 使用建议: 游戏资源加载使用 SDL_OpenTitleStorage,存档/配置保存使用 SDL_OpenUserStorage; 所有存储操作前必须调用 SDL_StorageReady 等待设备就绪; 写入文件前检查剩余空间(SDL_GetStorageSpaceRemaining),避免空间不足导致失败; 存储设备使用完毕后必须调用 SDL_CloseStorage,释放资源并刷新缓存。 关键接口: 存储设备管理:SDL_OpenTitleStorage()/SDL_OpenUserStorage()/SDL_StorageReady()/SDL_CloseStorage(); 文件操作:SDL_GetStorageFileSize()/SDL_ReadStorageFile()/SDL_WriteStorageFile(); 辅助操作:SDL_CreateStorageDirectory()/SDL_GetStorageSpaceRemaining()/SDL_RemoveStoragePath()。
      • 2026年-3月-6日
      • 6 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_文件系统(CategoryFilesystem) 2026-3-6
      SDL3_API分类参考_文件系统(CategoryFilesystem) 文件系统子系统(CategoryFilesystem) SDL 提供一套用于检查和操作系统文件系统的 API,涵盖了目录相关的大部分常用操作(实际的文件读写 I/O 功能由 CategoryIOStream 和 CategoryAsyncIO 模块提供)。 该模块可回答各类路径相关的核心问题: 应用程序的数据目录在哪里?SDL_GetBasePath() 哪里可以安全地写入文件?SDL_GetPrefPath() 系统预设文件夹(下载、桌面、音乐等)的路径是什么?SDL_GetUserFolder() 指定路径对应的文件/目录属性是什么?SDL_GetPathInfo() 某个文件夹下包含哪些文件/子目录?SDL_EnumerateDirectory() 按通配符匹配某个文件夹下的文件?SDL_GlobDirectory() 当前工作目录是什么?SDL_GetCurrentDirectory() 此外,SDL 还提供了操作目录树的函数,支持文件/目录的重命名、删除、复制等操作。 函数 SDL_CopyFile:复制文件(支持跨目录复制,保留文件属性) SDL_CreateDirectory:创建目录(支持创建单层目录,失败时返回具体错误) SDL_EnumerateDirectory:枚举目录下的所有项(文件/子目录),通过回调函数处理每个项 SDL_GetBasePath:获取应用程序的基础目录(二进制文件所在目录,末尾带路径分隔符) SDL_GetCurrentDirectory:获取当前工作目录(进程的当前工作路径) SDL_GetPathInfo:获取指定路径的详细信息(类型、大小、修改时间等,存储在 SDL_PathInfo 中) SDL_GetPrefPath:获取应用程序的偏好设置目录(可安全写入的目录,按厂商/应用名划分) SDL_GetUserFolder:获取系统预设的用户文件夹路径(桌面、下载、音乐等) SDL_GlobDirectory:按通配符匹配枚举目录下的文件(支持 *、? 等通配符) SDL_RemovePath:删除指定路径(文件/空目录,非空目录删除失败) SDL_RenamePath:重命名/移动文件/目录(支持跨目录移动,目标路径已存在则失败) 数据类型 SDL_EnumerateDirectoryCallback:目录枚举回调函数类型(处理每个枚举到的文件/目录项) SDL_GlobFlags:通配符枚举标志类型(控制 Glob 匹配的行为,如大小写敏感、递归等) 结构体 SDL_PathInfo:路径信息结构体(包含路径类型、大小、创建时间、修改时间等属性) 枚举 SDL_EnumerationResult:目录枚举结果枚举(控制是否继续枚举,如继续、停止、跳过当前项) SDL_Folder:系统预设文件夹类型枚举(桌面、下载、音乐、文档、图片等) SDL_PathType:路径类型枚举(文件、目录、符号链接、不存在等) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充枚举定义 Enum SDL_PathType SDL_PATH_TYPE_NONE = 0 ' 路径不存在 SDL_PATH_TYPE_FILE = 1 ' 普通文件 SDL_PATH_TYPE_DIRECTORY = 2 ' 目录 SDL_PATH_TYPE_SYMLINK = 3 ' 符号链接 SDL_PATH_TYPE_OTHER = 4 ' 其他类型 End Enum Enum SDL_Folder SDL_FOLDER_DESKTOP = 0 ' 桌面 SDL_FOLDER_DOWNLOADS = 1 ' 下载 SDL_FOLDER_DOCUMENTS = 2 ' 文档 SDL_FOLDER_MUSIC = 3 ' 音乐 SDL_FOLDER_PICTURES = 4 ' 图片 SDL_FOLDER_VIDEOS = 5 ' 视频 SDL_FOLDER_APPDATA = 6 ' 应用数据 End Enum Enum SDL_EnumerationResult SDL_ENUMERATION_CONTINUE = 0 ' 继续枚举 SDL_ENUMERATION_STOP = 1 ' 停止枚举 SDL_ENUMERATION_SKIP = 2 ' 跳过当前项 End Enum ' 补充结构体定义 Type SDL_PathInfo type_ As SDL_PathType ' 路径类型 size As ULLong ' 文件大小(字节),目录为0 created As SDL_Time ' 创建时间(纳秒时间戳) modified As SDL_Time ' 修改时间(纳秒时间戳) accessed As SDL_Time ' 访问时间(纳秒时间戳) End Type ' 补充函数指针类型定义 Type SDL_EnumerateDirectoryCallback As Function CDecl (ByVal path As ZString Ptr, ByVal info As SDL_PathInfo Ptr, ByVal userdata As Any Ptr) As SDL_EnumerationResult ' 补充函数声明(FreeBASIC 绑定可能缺失) Declare Function SDL_GetBasePath CDecl () As ZString Ptr Declare Function SDL_GetPrefPath CDecl (ByVal org As ZString Ptr, ByVal app As ZString Ptr) As ZString Ptr Declare Function SDL_GetUserFolder CDecl (ByVal folder As SDL_Folder) As ZString Ptr Declare Function SDL_GetCurrentDirectory CDecl () As ZString Ptr Declare Function SDL_GetPathInfo CDecl (ByVal path As ZString Ptr, ByVal info As SDL_PathInfo Ptr) As Integer Declare Function SDL_EnumerateDirectory CDecl (ByVal dir As ZString Ptr, ByVal callback As SDL_EnumerateDirectoryCallback, ByVal userdata As Any Ptr) As Integer Declare Function SDL_CreateDirectory CDecl (ByVal path As ZString Ptr) As Integer Declare Function SDL_CopyFile CDecl (ByVal src As ZString Ptr, ByVal dst As ZString Ptr) As Integer Declare Function SDL_RenamePath CDecl (ByVal oldpath As ZString Ptr, ByVal newpath As ZString Ptr) As Integer Declare Function SDL_RemovePath CDecl (ByVal path As ZString Ptr) As Integer ' 目录枚举回调函数:打印每个文件/目录信息 Function EnumDirCallback CDecl (ByVal path As ZString Ptr, ByVal info As SDL_PathInfo Ptr, ByVal userdata As Any Ptr) As SDL_EnumerationResult Dim As ZString * 20 typeStr Select Case info->type_ Case SDL_PATH_TYPE_FILE: typeStr = "文件" Case SDL_PATH_TYPE_DIRECTORY: typeStr = "目录" Case SDL_PATH_TYPE_SYMLINK: typeStr = "符号链接" Case Else: typeStr = "其他类型" End Select SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " %s - [%s] 大小:%llu 字节", *path, typeStr, info->size) ' 继续枚举所有项 Return SDL_ENUMERATION_CONTINUE End Function ' 打印路径信息 Sub PrintPathInfo(ByVal path As ZString Ptr, ByVal title As ZString Ptr) SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "=== %s ===", title) Dim As SDL_PathInfo info If (SDL_GetPathInfo(path, @info) = 0) Then Dim As ZString * 20 typeStr Select Case info.type_ Case SDL_PATH_TYPE_NONE: typeStr = "不存在" Case SDL_PATH_TYPE_FILE: typeStr = "普通文件" Case SDL_PATH_TYPE_DIRECTORY: typeStr = "目录" Case SDL_PATH_TYPE_SYMLINK: typeStr = "符号链接" Case SDL_PATH_TYPE_OTHER: typeStr = "其他类型" End Select SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 路径:%s", *path) SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 类型:%s", typeStr) SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 大小:%llu 字节", info.size) SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 创建时间:%llu ns", info.created) SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, " 修改时间:%llu ns", info.modified) Else SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "获取路径信息失败:%s", SDL_GetError()) End If End Sub ' 主程序 Sub Main() ' 初始化 SDL(Filesystem 模块无需显式初始化子系统) If (SDL_Init(0) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' ========== 1. 获取系统/应用路径 ========== SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "【1. 获取系统/应用路径】") ' 应用基础目录 Dim As ZString Ptr basePath = SDL_GetBasePath() If (basePath <> NULL) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "应用基础目录:%s", *basePath) SDL_free(basePath) ' 必须释放 SDL 分配的字符串 End If ' 应用偏好设置目录(厂商名/应用名) Dim As ZString Ptr prefPath = SDL_GetPrefPath("SDL_Example", "FilesystemDemo") If (prefPath <> NULL) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "应用偏好设置目录:%s", *prefPath) End If ' 当前工作目录 Dim As ZString Ptr cwd = SDL_GetCurrentDirectory() If (cwd <> NULL) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "当前工作目录:%s", *cwd) SDL_free(cwd) End If ' 系统预设用户文件夹 Dim As ZString * 20 folderNames(0 To 5) = { "桌面", "下载", "文档", "音乐", "图片", "视频" } For i As Integer = 0 To 5 Dim As ZString Ptr folderPath = SDL_GetUserFolder(i) If (folderPath <> NULL) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "%s 目录:%s", folderNames(i), *folderPath) SDL_free(folderPath) End If Next ' ========== 2. 创建测试目录和文件 ========== SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, vbCrLf & "【2. 创建测试目录和文件】") ' 拼接测试目录路径(偏好设置目录下的 test_dir) Dim As String testDirPath = *prefPath & "test_dir" If (SDL_CreateDirectory(StrPtr(testDirPath)) = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "创建测试目录成功:%s", testDirPath) Else SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "创建测试目录失败:%s", SDL_GetError()) End If ' 创建测试文件(FreeBASIC 原生文件操作) Dim As String testFilePath = testDirPath & "/test.txt" Dim As Integer fileHandle = FreeFile() If (Open(testFilePath For Output As #fileHandle) = 0) Then Print #fileHandle, "SDL Filesystem 测试文件" Close #fileHandle SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "创建测试文件成功:%s", testFilePath) End If ' ========== 3. 获取路径信息 ========== SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, vbCrLf & "【3. 获取路径信息】") PrintPathInfo(StrPtr(testFilePath), "测试文件信息") PrintPathInfo(StrPtr(testDirPath), "测试目录信息") ' ========== 4. 枚举目录内容 ========== SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, vbCrLf & "【4. 枚举测试目录内容】") If (SDL_EnumerateDirectory(StrPtr(testDirPath), @EnumDirCallback, NULL) = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "目录枚举完成") Else SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "目录枚举失败:%s", SDL_GetError()) End If ' ========== 5. 复制/重命名/删除文件 ========== SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, vbCrLf & "【5. 文件操作:复制/重命名/删除】") ' 复制文件 Dim As String copyFilePath = testDirPath & "/test_copy.txt" If (SDL_CopyFile(StrPtr(testFilePath), StrPtr(copyFilePath)) = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "复制文件成功:%s -> %s", testFilePath, copyFilePath) Else SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "复制文件失败:%s", SDL_GetError()) End If ' 重命名文件 Dim As String renameFilePath = testDirPath & "/test_renamed.txt" If (SDL_RenamePath(StrPtr(copyFilePath), StrPtr(renameFilePath)) = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "重命名文件成功:%s -> %s", copyFilePath, renameFilePath) Else SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "重命名文件失败:%s", SDL_GetError()) End If ' 删除文件 If (SDL_RemovePath(StrPtr(testFilePath)) = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "删除文件成功:%s", testFilePath) End If If (SDL_RemovePath(StrPtr(renameFilePath)) = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "删除文件成功:%s", renameFilePath) End If ' 删除目录(必须为空) If (SDL_RemovePath(StrPtr(testDirPath)) = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_FILESYSTEM, "删除测试目录成功:%s", testDirPath) Else SDL_LogError(SDL_LOG_CATEGORY_FILESYSTEM, "删除测试目录失败(可能非空):%s", SDL_GetError()) End If ' 释放内存 If (prefPath <> NULL) Then SDL_free(prefPath) End If ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 路径管理核心规则: SDL_GetBasePath() 返回的路径末尾自带路径分隔符(/ 或 \),拼接子路径时无需额外添加; SDL_GetPrefPath() 需要传入厂商名和应用名,返回的目录是系统推荐的可写入目录(跨平台兼容); SDL 分配的路径字符串(如 SDL_GetBasePath() 返回值)必须用 SDL_free() 释放,避免内存泄漏。 路径类型与操作限制: SDL_RemovePath() 只能删除文件或空目录,非空目录需先删除内部文件; SDL_CreateDirectory() 仅创建单层目录,创建多级目录需逐层调用; SDL_RenamePath() 支持跨目录移动文件,但目标路径不能已存在。 目录枚举回调函数: 返回 SDL_ENUMERATION_CONTINUE:继续枚举下一个项; 返回 SDL_ENUMERATION_STOP:立即停止枚举; 返回 SDL_ENUMERATION_SKIP:跳过当前项,继续枚举下一个; 回调函数中可通过 SDL_PathInfo 判断项的类型(文件/目录),实现差异化处理。 系统预设文件夹兼容性: SDL_GetUserFolder() 返回的路径因操作系统而异: Windows:C:\Users\<用户名>\Desktop/Downloads 等; macOS:/Users/<用户名>/Desktop/Downloads 等; Linux:/home/<用户名>/Desktop/Downloads 等; 部分平台可能不支持某些文件夹类型,返回 NULL,需做空指针判断。 总结 核心优势: 提供跨平台统一的文件系统操作接口,屏蔽不同系统的路径格式、目录结构差异; 自动适配系统的可写入目录、用户预设文件夹,避免手动拼接路径导致的兼容性问题; 内置路径信息查询、目录枚举、文件操作等完整功能,无需依赖平台原生 API; 内存管理清晰,SDL 分配的字符串需用 SDL_free() 释放,符合 SDL 内存管理规范。 使用建议: 优先使用 SDL_GetPrefPath() 存储应用配置文件,而非自定义路径,确保跨平台可写入; 枚举目录时通过回调函数处理每个项,避免一次性加载大量文件导致内存占用过高; 所有文件/目录操作后检查返回值,失败时通过 SDL_GetError() 获取具体错误信息; 拼接路径时注意路径分隔符,优先使用 SDL 返回的带分隔符路径,避免手动拼接出错。 关键接口: 路径获取:SDL_GetBasePath()/SDL_GetPrefPath()/SDL_GetUserFolder()/SDL_GetCurrentDirectory(); 路径信息:SDL_GetPathInfo(); 目录操作:SDL_CreateDirectory()/SDL_EnumerateDirectory(); 文件操作:SDL_CopyFile()/SDL_RenamePath()/SDL_RemovePath()。
      • 2026年-3月-6日
      • 6 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_日期时间(CategoryTime) 2026-3-6
      SDL3_API分类参考_日期时间(CategoryTime) 日期时间子系统(CategoryTime) SDL 提供实时时钟和日期/时间相关的函数,用于处理日历时间(区别于 Timer 模块的短时长计时)。 本模块核心使用两种数据类型: SDL_Time:表示从某个特定时间点("纪元/epoch")开始的纳秒数(时间戳); SDL_DateTime:将时间拆解为人类可读的组件(年、月、日、时、分、秒等)。 模块的大部分功能围绕这两种类型的相互转换,以及获取时间相关的辅助信息展开。 函数 SDL_DateTimeToTime:将 SDL_DateTime 结构体转换为 SDL_Time 时间戳(纳秒级) SDL_GetCurrentTime:获取当前系统时间的 SDL_Time 时间戳(基于 UTC 时间,纳秒级) SDL_GetDateTimeLocalePreferences:获取当前系统的日期/时间格式偏好(如日期格式、时间格式) SDL_GetDayOfWeek:根据 SDL_DateTime 计算该日期是一周中的第几天(0=周日,1=周一,…,6=周六) SDL_GetDayOfYear:根据 SDL_DateTime 计算该日期是一年中的第几天(1-366) SDL_GetDaysInMonth:获取指定年份和月份的天数(自动处理闰年) SDL_TimeFromWindows:将 Windows 系统的 FILETIME 时间转换为 SDL_Time 时间戳 SDL_TimeToDateTime:将 SDL_Time 时间戳转换为 SDL_DateTime 结构体(拆解为年月日时分秒) SDL_TimeToWindows:将 SDL_Time 时间戳转换为 Windows 系统的 FILETIME 时间格式 数据类型 (无) 结构体 SDL_DateTime:日期时间结构体(包含年、月、日、时、分、秒、毫秒、微秒、纳秒、时区偏移等字段) 枚举 SDL_DateFormat:日期格式枚举(如 MM/DD/YYYY、DD/MM/YYYY、YYYY/MM/DD 等) SDL_TimeFormat:时间格式枚举(如 12小时制、24小时制) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充 SDL_Time 类型定义(纳秒级时间戳,无符号64位整型) Type SDL_Time As ULLong ' 补充 SDL_DateTime 结构体定义 Type SDL_DateTime year As Integer ' 年份(如 2026) month As Integer ' 月份(1-12) day As Integer ' 日期(1-31) hour As Integer ' 小时(0-23) minute As Integer ' 分钟(0-59) second As Integer ' 秒(0-59) millisecond As Integer ' 毫秒(0-999) microsecond As Integer ' 微秒(0-999) nanosecond As Integer ' 纳秒(0-999) tz_offset_minutes As Integer ' 时区偏移(分钟,UTC+8 为 480) End Type ' 补充日期/时间格式枚举定义 Enum SDL_DateFormat SDL_DATE_FORMAT_MMDDYYYY = 0 ' 月/日/年(美式) SDL_DATE_FORMAT_DDMMYYYY = 1 ' 日/月/年(欧式) SDL_DATE_FORMAT_YYYYMMDD = 2 ' 年/月/日(ISO) End Enum Enum SDL_TimeFormat SDL_TIME_FORMAT_24HR = 0 ' 24小时制 SDL_TIME_FORMAT_12HR = 1 ' 12小时制 End Enum ' 补充函数声明(FreeBASIC 绑定可能缺失) Declare Function SDL_GetCurrentTime CDecl () As SDL_Time Declare Function SDL_TimeToDateTime CDecl (ByVal time As SDL_Time, ByVal dt As SDL_DateTime Ptr) As Integer Declare Function SDL_DateTimeToTime CDecl (ByVal dt As SDL_DateTime Ptr, ByVal time As SDL_Time Ptr) As Integer Declare Function SDL_GetDayOfWeek CDecl (ByVal dt As SDL_DateTime Ptr) As Integer Declare Function SDL_GetDayOfYear CDecl (ByVal dt As SDL_DateTime Ptr) As Integer Declare Function SDL_GetDaysInMonth CDecl (ByVal year As Integer, ByVal month As Integer) As Integer Declare Sub SDL_GetDateTimeLocalePreferences CDecl (ByVal date_format As SDL_DateFormat Ptr, ByVal time_format As SDL_TimeFormat Ptr) ' 打印 SDL_DateTime 结构体内容 Sub PrintDateTime(ByVal dt As SDL_DateTime Ptr, ByVal title As ZString Ptr) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "=== %s ===", title) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 日期:%04d年%02d月%02d日", dt->year, dt->month, dt->day) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 时间:%02d:%02d:%02d.%03d%03d%03d", dt->hour, dt->minute, dt->second, _ dt->millisecond, dt->microsecond, dt->nanosecond) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 时区:UTC%+d (偏移 %d 分钟)", dt->tz_offset_minutes / 60, dt->tz_offset_minutes) ' 计算星期几 Dim As Integer weekday = SDL_GetDayOfWeek(dt) Dim As ZString * 10 weekdayStr Select Case weekday Case 0: weekdayStr = "周日" Case 1: weekdayStr = "周一" Case 2: weekdayStr = "周二" Case 3: weekdayStr = "周三" Case 4: weekdayStr = "周四" Case 5: weekdayStr = "周五" Case 6: weekdayStr = "周六" End Select SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 星期:%s (一周的第%d天)", weekdayStr, weekday) ' 计算一年中的第几天 Dim As Integer dayOfYear = SDL_GetDayOfYear(dt) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 年内天数:第%d天", dayOfYear) ' 计算当月天数 Dim As Integer daysInMonth = SDL_GetDaysInMonth(dt->year, dt->month) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 当月天数:%d天", daysInMonth) End Sub ' 主程序 Sub Main() ' 初始化 SDL(Time 模块无需显式初始化子系统) If (SDL_Init(0) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' ========== 1. 获取当前系统时间 ========== SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【1. 获取当前系统时间】") Dim As SDL_Time currentTime = SDL_GetCurrentTime() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "当前时间戳(纳秒):%llu", currentTime) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "当前时间戳(秒):%.3f", currentTime / 1000000000.0) ' ========== 2. 时间戳转换为日期时间 ========== SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【2. 时间戳转日期时间】") Dim As SDL_DateTime dt If (SDL_TimeToDateTime(currentTime, @dt) = 0) Then PrintDateTime(@dt, "当前系统日期时间") Else SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "时间戳转日期时间失败:%s", SDL_GetError()) End If ' ========== 3. 日期时间转换为时间戳 ========== SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【3. 日期时间转时间戳】") ' 手动构造一个日期时间(2026年3月8日 12:34:56.123456789,UTC+8) Dim As SDL_DateTime customDt customDt.year = 2026 customDt.month = 3 customDt.day = 8 customDt.hour = 12 customDt.minute = 34 customDt.second = 56 customDt.millisecond = 123 customDt.microsecond = 456 customDt.nanosecond = 789 customDt.tz_offset_minutes = 8 * 60 ' UTC+8 PrintDateTime(@customDt, "自定义日期时间") ' 转换为时间戳 Dim As SDL_Time customTime If (SDL_DateTimeToTime(@customDt, @customTime) = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "自定义日期时间对应的时间戳:") SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 纳秒:%llu", customTime) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " 秒:%.9f", customTime / 1000000000.0) Else SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "日期时间转时间戳失败:%s", SDL_GetError()) End If ' ========== 4. 获取系统日期时间格式偏好 ========== SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【4. 获取系统日期时间格式偏好】") Dim As SDL_DateFormat dateFmt Dim As SDL_TimeFormat timeFmt SDL_GetDateTimeLocalePreferences(@dateFmt, @timeFmt) Dim As ZString * 20 dateFmtStr Select Case dateFmt Case SDL_DATE_FORMAT_MMDDYYYY: dateFmtStr = "MM/DD/YYYY(月/日/年)" Case SDL_DATE_FORMAT_DDMMYYYY: dateFmtStr = "DD/MM/YYYY(日/月/年)" Case SDL_DATE_FORMAT_YYYYMMDD: dateFmtStr = "YYYY/MM/DD(年/月/日)" End Select Dim As ZString * 20 timeFmtStr Select Case timeFmt Case SDL_TIME_FORMAT_24HR: timeFmtStr = "24小时制" Case SDL_TIME_FORMAT_12HR: timeFmtStr = "12小时制" End Select SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "系统日期格式:%s", dateFmtStr) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "系统时间格式:%s", timeFmtStr) ' ========== 5. 闰年测试 ========== SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【5. 闰年/月份天数测试】") Dim As Integer years(0 To 3) = {2024, 2025, 2026, 2000} Dim As Integer months(0 To 1) = {2, 4} ' 2月、4月 For i As Integer = 0 To 3 For j As Integer = 0 To 1 Dim As Integer days = SDL_GetDaysInMonth(years(i), months(j)) Dim As ZString * 10 leapStr = IIf(months(j)=2 And days=29, "(闰年)", "") SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%d年%d月的天数:%d天 %s", years(i), months(j), days, leapStr) Next Next ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 SDL_Time 核心特性: 本质是无符号64位整型(ULLong),表示从 "Unix 纪元"(1970-01-01 00:00:00 UTC)开始的纳秒数; 范围覆盖约 584 年(0 ~ 18446744073709551615 纳秒),足够满足常规日期时间需求; 与系统时钟同步,可精准表示当前系统时间。 SDL_DateTime 字段规则: 月份范围:1(1月)~ 12(12月),而非0开始; 日期范围:1 ~ 当月最大天数(由 SDL_GetDaysInMonth 计算); 时间字段:小时(0-23)、分钟(0-59)、秒(0-59),毫秒/微秒/纳秒均为 0-999; 时区偏移:以分钟为单位,UTC+8 对应 480 分钟,UTC-5 对应 -300 分钟。 闰年判断逻辑: SDL_GetDaysInMonth 会自动处理闰年: 能被400整除的年份是闰年; 能被4整除但不能被100整除的年份是闰年; 其他年份为平年; 闰年2月有29天,平年2月有28天。 本地化格式偏好: SDL_GetDateTimeLocalePreferences 获取系统默认的日期/时间显示格式; 常见日期格式: MM/DD/YYYY:美式(如 03/08/2026); DD/MM/YYYY:欧式(如 08/03/2026); YYYY/MM/DD:ISO标准(如 2026/03/08); 时间格式:12小时制(带AM/PM)、24小时制。 总结 核心优势: 提供跨平台的日历时间处理接口,屏蔽不同系统的时间API差异; 支持纳秒级高精度时间戳,兼顾精度与易用性; 内置日期时间拆解/组装、星期/年内天数计算、闰年判断等实用功能; 支持本地化格式偏好获取,适配不同地区的显示习惯。 使用建议: SDL_GetCurrentTime 是获取当前时间的首选方式(UTC 时间,无时区偏差); 转换时间戳/日期时间时,需检查函数返回值(0=成功,非0=失败); 处理时区时,注意 tz_offset_minutes 字段的单位是分钟,需转换为小时展示; 计算星期/年内天数前,确保 SDL_DateTime 结构体的年月日字段有效。 关键接口: 时间获取:SDL_GetCurrentTime(); 类型转换:SDL_TimeToDateTime()/SDL_DateTimeToTime(); 日期计算:SDL_GetDayOfWeek()/SDL_GetDayOfYear()/SDL_GetDaysInMonth(); 本地化:SDL_GetDateTimeLocalePreferences()。
      • 2026年-3月-6日
      • 8 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_定时器(CategoryTimer) 2026-3-6
      SDL3_API分类参考_定时器(CategoryTimer) 定时器子系统(CategoryTimer) SDL 提供时间管理相关功能,主要用于处理(通常是)短时长的时间操作。 请注意,这与「日历时间(calendar time)」管理不同——日历时间功能由 CategoryTime 模块提供。 本模块涵盖以下核心能力: 测量已流逝的时间(SDL_GetTicks()、SDL_GetPerformanceCounter()); 让线程休眠指定时长(SDL_Delay()、SDL_DelayNS()、SDL_DelayPrecise()); 在指定时长后触发回调函数(SDL_AddTimer() 等)。 此外,SDL 还提供了实用的宏,用于不同时间单位之间的转换(如 SDL_SECONDS_TO_NS() 等)。 函数 SDL_AddTimer:添加毫秒级定时器(指定触发间隔和回调函数,定时器线程中执行回调) SDL_AddTimerNS:添加纳秒级高精度定时器(以纳秒为单位指定触发间隔) SDL_Delay:让当前线程休眠指定毫秒数(基础休眠函数,精度受系统限制) SDL_DelayNS:让当前线程休眠指定纳秒数(纳秒级休眠,精度高于 SDL_Delay) SDL_DelayPrecise:高精度休眠(尽可能精准地休眠指定毫秒数,减少误差) SDL_GetPerformanceCounter:获取高性能计数器的当前值(用于高精度时间测量) SDL_GetPerformanceFrequency:获取高性能计数器的频率(每秒计数次数) SDL_GetTicks:获取 SDL 初始化后的毫秒数(从 SDL_Init 调用开始计时,溢出后循环) SDL_GetTicksNS:获取 SDL 初始化后的纳秒数(高精度版本的 SDL_GetTicks) SDL_RemoveTimer:移除指定的定时器(停止定时器触发,释放相关资源) 数据类型 SDL_NSTimerCallback:纳秒级定时器回调函数类型(定时器触发时执行的函数原型) SDL_TimerCallback:毫秒级定时器回调函数类型(返回值为下一次触发间隔,返回0则停止) SDL_TimerID:定时器唯一标识符(标识一个创建的定时器,用于移除操作) 结构体 (无) 枚举 (无) 宏 SDL_MS_PER_SECOND:每秒的毫秒数(值为 1000) SDL_MS_TO_NS:毫秒转换为纳秒(ms × 1000000) SDL_NS_PER_MS:每毫秒的纳秒数(值为 1000000) SDL_NS_PER_SECOND:每秒的纳秒数(值为 1000000000) SDL_NS_PER_US:每微秒的纳秒数(值为 1000) SDL_NS_TO_MS:纳秒转换为毫秒(ns ÷ 1000000) SDL_NS_TO_SECONDS:纳秒转换为秒(ns ÷ 1000000000) SDL_NS_TO_US:纳秒转换为微秒(ns ÷ 1000) SDL_SECONDS_TO_NS:秒转换为纳秒(s × 1000000000) SDL_US_PER_SECOND:每秒的微秒数(值为 1000000) SDL_US_TO_NS:微秒转换为纳秒(us × 1000) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充时间转换宏的定义(FreeBASIC 绑定可能缺失) #Define SDL_MS_PER_SECOND 1000 #Define SDL_NS_PER_SECOND 1000000000ULL #Define SDL_NS_PER_MS 1000000ULL #Define SDL_NS_PER_US 1000ULL #Define SDL_US_PER_SECOND 1000000ULL #Define SDL_MS_TO_NS(ms) ((ULLong)(ms) * SDL_NS_PER_MS) #Define SDL_NS_TO_MS(ns) ((ULLong)(ns) / SDL_NS_PER_MS) #Define SDL_NS_TO_SECONDS(ns) ((Double)(ns) / SDL_NS_PER_SECOND) #Define SDL_NS_TO_US(ns) ((ULLong)(ns) / SDL_NS_PER_US) #Define SDL_SECONDS_TO_NS(s) ((ULLong)(s) * SDL_NS_PER_SECOND) #Define SDL_US_TO_NS(us) ((ULLong)(us) * SDL_NS_PER_US) ' 全局变量:定时器控制标志 Dim Shared As Boolean g_quitTimer = False Dim Shared As SDL_TimerID g_timerId = 0 ' 定时器ID ' 毫秒级定时器回调函数 Function TimerCallback CDecl (ByVal interval As UInteger, ByVal param As Any Ptr) As UInteger Static As Integer counter = 0 counter += 1 Dim As ZString Ptr timerName = Cast(ZString Ptr, param) SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "%s - 定时器触发(第%d次),间隔:%dms", timerName, counter, interval) ' 返回下一次触发间隔(返回0则停止定时器) If (g_quitTimer Or counter >= 5) Then SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "%s - 定时器停止", timerName) Return 0 End If Return interval ' 保持原间隔继续触发 End Function ' 纳秒级定时器回调函数(FreeBASIC 函数原型适配) Function NSTimerCallback CDecl (ByVal interval As ULLong, ByVal param As Any Ptr) As ULLong Static As Integer counter = 0 counter += 1 Dim As ZString Ptr timerName = Cast(ZString Ptr, param) Dim As Double intervalSec = SDL_NS_TO_SECONDS(interval) SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "%s - 纳秒定时器触发(第%d次),间隔:%.3fs", timerName, counter, intervalSec) ' 返回下一次触发间隔(返回0则停止) If (counter >= 3) Then SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "%s - 纳秒定时器停止", timerName) Return 0 End If Return interval ' 保持原间隔 End Function ' 测量函数执行耗时(使用高性能计数器) Sub MeasureFunctionTime() ' 获取高性能计数器频率(每秒计数次数) Dim As ULLong freq = SDL_GetPerformanceFrequency() SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "高性能计数器频率:%llu Hz", freq) ' 记录开始时间 Dim As ULLong start = SDL_GetPerformanceCounter() ' 模拟耗时操作(循环1000万次) Dim As Integer i For i = 1 To 10000000 ' 空循环 Next ' 记录结束时间 Dim As ULLong end_ = SDL_GetPerformanceCounter() ' 计算耗时(秒) Dim As Double elapsedSec = (end_ - start) / (Double)freq Dim As ULLong elapsedNs = SDL_SECONDS_TO_NS(elapsedSec) SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "耗时操作完成:") SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 计数器差值:%llu", end_ - start) SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 耗时:%.6f 秒", elapsedSec) SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 耗时:%llu 纳秒", elapsedNs) End Sub ' 主程序 Sub Main() ' 初始化 SDL(必须初始化定时器子系统) If (SDL_Init(SDL_INIT_TIMER) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' ========== 1. 基础时间获取 ========== Dim As UInteger ticks = SDL_GetTicks() Dim As ULLong ticksNs = SDL_GetTicksNS() SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "SDL 初始化后时间:") SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 毫秒数:%u ms", ticks) SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 纳秒数:%llu ns", ticksNs) ' ========== 2. 测量函数耗时 ========== SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "开始测量函数耗时...") MeasureFunctionTime() ' ========== 3. 线程休眠测试 ========== SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "主线程休眠 1.5 秒(基础 Delay)...") Dim As UInteger sleepMs = 1500 Dim As ULLong sleepNs = SDL_MS_TO_NS(sleepMs) Dim As ULLong sleepStart = SDL_GetPerformanceCounter() SDL_Delay(sleepMs) ' 基础毫秒休眠 Dim As ULLong sleepEnd = SDL_GetPerformanceCounter() Dim As Double actualSleepSec = (sleepEnd - sleepStart) / (Double)SDL_GetPerformanceFrequency() SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "实际休眠时间:%.3f 秒(目标:%.3f 秒)", actualSleepSec, sleepMs / 1000.0) ' 高精度休眠测试 SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "主线程高精度休眠 500ms...") SDL_DelayPrecise(500) ' ========== 4. 毫秒级定时器 ========== Dim As String timerName = "毫秒定时器" g_timerId = SDL_AddTimer(1000, @TimerCallback, StrPtr(timerName)) ' 1秒触发一次 If (g_timerId = 0) Then SDL_LogError(SDL_LOG_CATEGORY_TIMER, "创建毫秒定时器失败:%s", SDL_GetError()) Else SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "创建毫秒定时器成功(ID:%d)", g_timerId) End If ' ========== 5. 纳秒级定时器 ========== Dim As String nsTimerName = "纳秒定时器" Dim As ULLong nsInterval = SDL_SECONDS_TO_NS(2.5) ' 2.5秒 Dim As SDL_TimerID nsTimerId = SDL_AddTimerNS(nsInterval, @NSTimerCallback, StrPtr(nsTimerName)) If (nsTimerId = 0) Then SDL_LogError(SDL_LOG_CATEGORY_TIMER, "创建纳秒定时器失败:%s", SDL_GetError()) Else SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "创建纳秒定时器成功(ID:%d,间隔:%.3fs)", nsTimerId, SDL_NS_TO_SECONDS(nsInterval)) End If ' 主线程等待定时器执行完成(7秒) SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "主线程等待 7 秒...") SDL_Delay(7000) ' 手动停止定时器(演示用,实际回调返回0会自动停止) If (g_timerId <> 0) Then SDL_RemoveTimer(g_timerId) SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "手动移除毫秒定时器(ID:%d)", g_timerId) End If ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出") End Sub ' 补充定时器函数声明(FreeBASIC 绑定可能缺失) Declare Function SDL_AddTimer CDecl (ByVal interval As UInteger, ByVal callback As SDL_TimerCallback, ByVal param As Any Ptr) As SDL_TimerID Declare Function SDL_AddTimerNS CDecl (ByVal interval As ULLong, ByVal callback As SDL_NSTimerCallback, ByVal param As Any Ptr) As SDL_TimerID Declare Function SDL_RemoveTimer CDecl (ByVal id As SDL_TimerID) As Integer Declare Function SDL_GetPerformanceCounter CDecl () As ULLong Declare Function SDL_GetPerformanceFrequency CDecl () As ULLong Declare Function SDL_GetTicksNS CDecl () As ULLong Declare Sub SDL_DelayPrecise CDecl (ByVal ms As UInteger) Declare Sub SDL_DelayNS CDecl (ByVal ns As ULLong) ' 运行主程序 Main() 核心知识点补充 时间测量精度分级: 基础精度(毫秒级):SDL_GetTicks() —— 适合普通计时,精度约 1-10ms,受系统时钟影响; 高精度(纳秒级):SDL_GetPerformanceCounter() —— 基于 CPU 高性能计数器,精度可达纳秒级,适合测量短耗时操作。 定时器核心规则: 定时器回调函数运行在独立的定时器线程中,不可执行阻塞操作(如 SDL_Delay),也不可调用 SDL 图形/事件相关函数; 毫秒定时器回调返回值为下一次触发间隔(ms),返回 0 则停止定时器; 纳秒定时器回调返回值为下一次触发间隔(ns),返回 0 则停止; 需通过 SDL_RemoveTimer() 手动移除未自动停止的定时器,避免资源泄漏。 休眠函数差异: 函数 时间单位 精度 适用场景 SDL_Delay 毫秒 较低(~1ms) 普通休眠,对精度无要求 SDL_DelayNS 纳秒 中等 纳秒级休眠需求 SDL_DelayPrecise 毫秒 较高 需精准控制休眠时长 时间单位转换注意事项: 所有转换宏使用无符号长整型(ULLong)避免溢出; 纳秒转秒/毫秒时建议用浮点型计算,避免整数截断丢失精度; 高性能计数器计算耗时公式:耗时(秒) = (结束值 - 开始值) / 计数器频率。 总结 核心优势: 提供从毫秒到纳秒的多精度时间管理能力,满足不同场景需求; 高性能计数器可实现纳秒级耗时测量,适合性能分析; 跨平台统一接口,屏蔽不同系统的定时器/休眠实现差异; 内置时间单位转换宏,简化单位换算逻辑。 使用建议: 初始化 SDL 时必须指定 SDL_INIT_TIMER 子系统,否则定时器函数会失败; 普通计时用 SDL_GetTicks(),高精度计时用 SDL_GetPerformanceCounter(); 定时器回调函数需保持轻量,避免阻塞; 休眠时长较短(<10ms)且要求精准时,优先使用 SDL_DelayPrecise()。 关键接口: 时间测量:SDL_GetTicks()/SDL_GetPerformanceCounter()/SDL_GetPerformanceFrequency(); 线程休眠:SDL_Delay()/SDL_DelayPrecise()/SDL_DelayNS(); 定时器:SDL_AddTimer()/SDL_AddTimerNS()/SDL_RemoveTimer(); 单位转换:SDL_MS_TO_NS()/SDL_NS_TO_SECONDS()/SDL_SECONDS_TO_NS()。
      • 2026年-3月-6日
      • 8 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_原子操作(CategoryAtomic) 2026-3-6
      SDL3_API分类参考_原子操作(CategoryAtomic) 原子操作子系统(CategoryAtomic) 原子操作相关功能。 重要提示:如果你并非无锁并发编程领域的专家,请勿使用本模块中的任何函数。你应当使用完整的互斥锁来保护数据结构,而非直接使用原子操作。 郑重提醒:此处暗藏风险! 你可以通过以下资源了解无锁编程及其可能引发的微妙问题: https://learn.microsoft.com/en-us/windows/win32/dxtecharts/lockless-programming 此外,这些资源也包含大量优质内容: https://www.1024cores.net/home/lock-free-algorithms https://preshing.com/ 这些原子操作不一定都通过处理器专属的原子指令实现: 条件允许时,会直接使用处理器原生原子指令; 条件不允许时,会基于底层原子操作实现的锁来模拟原子行为。 所有修改内存的原子操作均为全内存屏障(full memory barriers),可确保内存操作的顺序性和可见性。 函数 SDL_AddAtomicInt:原子化地为 SDL_AtomicInt 类型变量执行加法操作(返回操作前的原值) SDL_AddAtomicU32:原子化地为 SDL_AtomicU32 类型变量执行加法操作(返回操作前的原值) SDL_CompareAndSwapAtomicInt:对原子整型执行比较并交换(CAS)操作(值匹配时替换,返回操作前的值) SDL_CompareAndSwapAtomicPointer:对原子指针执行比较并交换(CAS)操作(指针匹配时替换,返回操作前的指针) SDL_CompareAndSwapAtomicU32:对原子32位无符号整型执行比较并交换(CAS)操作(值匹配时替换,返回操作前的值) SDL_GetAtomicInt:原子化地读取 SDL_AtomicInt 类型变量的值(确保读取到最新值) SDL_GetAtomicPointer:原子化地读取原子指针的值(确保读取到最新指针) SDL_GetAtomicU32:原子化地读取 SDL_AtomicU32 类型变量的值(确保读取到最新值) SDL_LockSpinlock:自旋锁加锁(忙等方式,不放弃CPU,适合短时间锁定场景) SDL_MemoryBarrierAcquireFunction:执行获取(Acquire)内存屏障(确保后续内存操作可见前面的写入) SDL_MemoryBarrierReleaseFunction:执行释放(Release)内存屏障(确保前面的内存操作对其他线程可见) SDL_SetAtomicInt:原子化地设置 SDL_AtomicInt 类型变量的值(确保写入立即对其他线程可见) SDL_SetAtomicPointer:原子化地设置原子指针的值(确保指针写入立即对其他线程可见) SDL_SetAtomicU32:原子化地设置 SDL_AtomicU32 类型变量的值(确保写入立即对其他线程可见) SDL_TryLockSpinlock:尝试获取自旋锁(非阻塞,获取失败直接返回,不忙等) SDL_UnlockSpinlock:自旋锁解锁(释放自旋锁,允许其他线程获取) 数据类型 SDL_SpinLock:自旋锁类型(基于原子操作实现的忙等锁,无内核态切换开销) 结构体 SDL_AtomicInt:原子整型结构体(封装int类型,支持原子操作) SDL_AtomicU32:原子32位无符号整型结构体(封装uint32_t类型,支持原子操作) 枚举 (无) 宏 SDL_AtomicDecRef:原子化地减少引用计数(基于SDL_AtomicInt实现,返回操作后的值) SDL_AtomicIncRef:原子化地增加引用计数(基于SDL_AtomicInt实现,返回操作后的值) SDL_CompilerBarrier:编译器屏障(阻止编译器重排指令,不影响CPU执行顺序) SDL_CPUPauseInstruction:CPU暂停指令(自旋锁中使用,减少忙等时的CPU占用) SDL_MemoryBarrierAcquire:获取(Acquire)内存屏障宏(封装SDL_MemoryBarrierAcquireFunction) SDL_MemoryBarrierRelease:释放(Release)内存屏障宏(封装SDL_MemoryBarrierReleaseFunction) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充原子操作结构体的定义(FreeBASIC 绑定可能缺失) Type SDL_AtomicInt value As Integer End Type Type SDL_AtomicU32 value As UInteger End Type ' 补充原子操作函数声明(FreeBASIC 绑定可能缺失) Declare Function SDL_AddAtomicInt CDecl (ByVal a As SDL_AtomicInt Ptr, ByVal v As Integer) As Integer Declare Function SDL_AddAtomicU32 CDecl (ByVal a As SDL_AtomicU32 Ptr, ByVal v As UInteger) As UInteger Declare Function SDL_CompareAndSwapAtomicInt CDecl (ByVal a As SDL_AtomicInt Ptr, ByVal oldval As Integer, ByVal newval As Integer) As Integer Declare Function SDL_CompareAndSwapAtomicU32 CDecl (ByVal a As SDL_AtomicU32 Ptr, ByVal oldval As UInteger, ByVal newval As UInteger) As UInteger Declare Function SDL_GetAtomicInt CDecl (ByVal a As SDL_AtomicInt Ptr) As Integer Declare Function SDL_GetAtomicU32 CDecl (ByVal a As SDL_AtomicU32 Ptr) As UInteger Declare Sub SDL_SetAtomicInt CDecl (ByVal a As SDL_AtomicInt Ptr, ByVal v As Integer) Declare Sub SDL_SetAtomicU32 CDecl (ByVal a As SDL_AtomicU32 Ptr, ByVal v As UInteger) Declare Sub SDL_LockSpinlock CDecl (ByVal lock As SDL_SpinLock Ptr) Declare Function SDL_TryLockSpinlock CDecl (ByVal lock As SDL_SpinLock Ptr) As Integer Declare Sub SDL_UnlockSpinlock CDecl (ByVal lock As SDL_SpinLock Ptr) ' 原子变量定义 Dim Shared As SDL_AtomicInt g_atomicCounter ' 原子整型计数器 Dim Shared As SDL_AtomicU32 g_atomicU32Value ' 原子32位无符号整型 Dim Shared As SDL_SpinLock g_spinLock = 0 ' 自旋锁(初始值0为未锁定) Dim Shared As Any Ptr g_atomicPointer = NULL ' 原子指针(示例) Dim Shared As Boolean g_quitThreads = False ' 线程退出标志 ' 线程1:原子加法操作 Function AtomicAddThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,执行原子加法操作", threadName, threadId) While (Not g_quitThreads) ' 原子化地给计数器加1(返回操作前的值) Dim As Integer oldValue = SDL_AddAtomicInt(@g_atomicCounter, 1) Dim As Integer currValue = SDL_GetAtomicInt(@g_atomicCounter) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 原子加法:旧值=%d,新值=%d", threadName, oldValue, currValue) ' 原子化地给无符号整型加5 Dim As UInteger oldU32 = SDL_AddAtomicU32(@g_atomicU32Value, 5) Dim As UInteger currU32 = SDL_GetAtomicU32(@g_atomicU32Value) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 原子U32加法:旧值=%u,新值=%u", threadName, oldU32, currU32) ' 模拟工作 SDL_Delay(300) Wend SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 线程2:CAS(比较并交换)操作 Function AtomicCASThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,执行CAS操作", threadName, threadId) While (Not g_quitThreads) ' CAS操作:只有当计数器值等于10时,才将其设置为0 Dim As Integer expected = 10 Dim As Integer desired = 0 Dim As Integer oldValue = SDL_CompareAndSwapAtomicInt(@g_atomicCounter, expected, desired) If (oldValue = expected) Then SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - CAS成功:计数器从%d重置为%d", threadName, expected, desired) Else SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - CAS失败:期望值=%d,实际值=%d", threadName, expected, oldValue) End If ' 模拟工作 SDL_Delay(800) Wend SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 线程3:自旋锁操作 Function SpinlockThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,使用自旋锁", threadName, threadId) While (Not g_quitThreads) ' 获取自旋锁(忙等,适合短时间锁定) SDL_LockSpinlock(@g_spinLock) ' 临界区(自旋锁保护,独占访问) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 获取自旋锁,执行临界区操作", threadName) SDL_Delay(100) ' 短时间操作(自旋锁适合短临界区) ' 释放自旋锁 SDL_UnlockSpinlock(@g_spinLock) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 释放自旋锁", threadName) ' 模拟工作 SDL_Delay(500) Wend SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 线程4:引用计数原子操作 Function RefCountThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() Static As SDL_AtomicInt refCount ' 静态引用计数(原子整型) ' 初始化引用计数 If (SDL_GetAtomicInt(@refCount) = 0) Then SDL_SetAtomicInt(@refCount, 1) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 初始化引用计数为1", threadName) End If SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,操作引用计数", threadName, threadId) ' 增加引用计数(使用宏) Dim As Integer newRef = SDL_AtomicIncRef(@refCount) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 增加引用计数:%d", threadName, newRef) ' 模拟使用资源 SDL_Delay(1000) ' 减少引用计数(使用宏) newRef = SDL_AtomicDecRef(@refCount) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 减少引用计数:%d", threadName, newRef) ' 引用计数为0时释放资源 If (newRef = 0) Then SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 引用计数为0,释放资源", threadName) End If SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 主程序 Sub Main() ' 初始化 SDL If (SDL_Init(SDL_INIT_THREADS) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' 初始化原子变量 SDL_SetAtomicInt(@g_atomicCounter, 0) SDL_SetAtomicU32(@g_atomicU32Value, 0) ' 创建线程 Dim As String t1Name = "原子加法线程" Dim As String t2Name = "CAS操作线程" Dim As String t3Name = "自旋锁线程" Dim As String t4Name = "引用计数线程1" Dim As String t5Name = "引用计数线程2" Dim As SDL_Thread Ptr t1 = SDL_CreateThread(@AtomicAddThread, t1Name, StrPtr(t1Name)) Dim As SDL_Thread Ptr t2 = SDL_CreateThread(@AtomicCASThread, t2Name, StrPtr(t2Name)) Dim As SDL_Thread Ptr t3 = SDL_CreateThread(@SpinlockThread, t3Name, StrPtr(t3Name)) Dim As SDL_Thread Ptr t4 = SDL_CreateThread(@RefCountThread, t4Name, StrPtr(t4Name)) Dim As SDL_Thread Ptr t5 = SDL_CreateThread(@RefCountThread, t5Name, StrPtr(t5Name)) If (t1 = NULL Or t2 = NULL Or t3 = NULL Or t4 = NULL Or t5 = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建线程失败:%s", SDL_GetError()) g_quitThreads = True SDL_Quit() Exit Sub End If ' 主线程运行 6 秒 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程运行 6 秒...") SDL_Delay(6000) ' 通知线程退出 g_quitThreads = True SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程:通知所有线程退出") ' 等待所有线程结束 Dim As Integer result SDL_WaitThread(t1, @result) SDL_WaitThread(t2, @result) SDL_WaitThread(t3, @result) SDL_WaitThread(t4, @result) SDL_WaitThread(t5, @result) ' 打印最终原子变量值 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "最终原子计数器值:%d", SDL_GetAtomicInt(@g_atomicCounter)) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "最终原子U32值:%u", SDL_GetAtomicU32(@g_atomicU32Value)) ' 退出 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出") End Sub ' 补充引用计数宏的实现(FreeBASIC 绑定可能缺失) #Define SDL_AtomicIncRef(a) (SDL_AddAtomicInt(a, 1) + 1) #Define SDL_AtomicDecRef(a) (SDL_AddAtomicInt(a, -1) - 1) ' 运行主程序 Main() 核心知识点补充 原子操作核心特性: 不可分割性:原子操作的执行过程无法被其他线程中断,要么完全执行,要么完全不执行; 内存可见性:修改原子变量的操作会立即对所有线程可见(全内存屏障); 无锁特性:无需互斥锁即可实现线程安全,避免内核态切换开销,但编程复杂度极高。 CAS 操作(Compare and Swap): 核心逻辑:如果变量值等于期望值,则替换为目标值,返回原值; 典型用法:实现无锁数据结构(如无锁队列、栈),需配合循环重试; 注意事项:可能引发 ABA 问题(变量值从 A→B→A,CAS 误判为未修改),需额外处理。 自旋锁适用场景: 临界区执行时间极短(微秒级),且线程数不超过 CPU 核心数; 避免在单核 CPU 或临界区较长的场景使用(会导致其他线程无法执行); SDL_CPUPauseInstruction 可减少自旋锁忙等时的 CPU 占用率。 内存屏障分类: Acquire(获取)屏障:确保屏障后的读操作能看到其他线程的写操作; Release(释放)屏障:确保屏障前的写操作对其他线程可见; 编译器屏障:仅阻止编译器指令重排,不影响 CPU 执行顺序。 原子操作 vs 互斥锁: 特性 原子操作 互斥锁 开销 极低(用户态,无切换) 较高(可能触发内核态) 适用场景 简单变量操作(计数、标志) 复杂数据结构/长临界区 编程复杂度 极高(易出隐蔽 bug) 较低(逻辑清晰) 阻塞方式 无阻塞(CAS 需循环重试) 阻塞(线程进入等待队列) 总结 核心优势: 提供跨平台的原子操作接口,屏蔽不同处理器的原子指令差异; 支持整型、指针、32位无符号整型的原子操作,覆盖基础无锁编程需求; 自旋锁基于原子操作实现,适合短临界区的高效同步。 使用建议: 非无锁编程专家,优先使用互斥锁而非原子操作; 原子操作仅用于简单变量(计数器、标志位),复杂逻辑仍需互斥锁; 自旋锁仅用于临界区极短的场景,避免滥用; CAS 操作需处理 ABA 问题,必要时使用版本号辅助判断。 关键接口: 基础原子操作:SDL_AddAtomicInt()/SDL_SetAtomicInt()/SDL_GetAtomicInt(); CAS 操作:SDL_CompareAndSwapAtomicInt()/SDL_CompareAndSwapAtomicPointer(); 自旋锁:SDL_LockSpinlock()/SDL_TryLockSpinlock()/SDL_UnlockSpinlock(); 内存屏障:SDL_MemoryBarrierAcquire()/SDL_MemoryBarrierRelease(); 引用计数:SDL_AtomicIncRef()/SDL_AtomicDecRef()。
      • 2026年-3月-6日
      • 4 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_互斥锁(CategoryMutex) 2026-3-6
      SDL3_API分类参考_互斥锁(CategoryMutex) 互斥锁子系统(CategoryMutex) SDL 提供了多种线程同步原语,用于保障多线程程序的线程安全。本文档无法全面覆盖线程安全这一复杂主题,但理解这些同步原语的用途、价值和正确用法,是编写正确且安全的多线程程序的关键。 SDL 提供的核心同步原语包括: 互斥锁(Mutexes):SDL_CreateMutex() —— 独占式资源访问控制 读写锁(Read/Write locks):SDL_CreateRWLock() —— 读共享、写独占的资源访问控制 信号量(Semaphores):SDL_CreateSemaphore() —— 控制并发访问数量的同步机制 条件变量(Condition variables):SDL_CreateCondition() —— 线程间的条件等待/通知机制 此外,SDL 还提供了 SDL_InitState 数据类型,用于确保当多个线程同时尝试首次使用某个资源时,只有一个线程负责该资源的初始化/反初始化操作。 函数 SDL_BroadcastCondition:广播唤醒所有等待该条件变量的线程 SDL_CreateCondition:创建条件变量(用于线程间条件等待/通知) SDL_CreateMutex:创建互斥锁(基础的独占式同步锁) SDL_CreateRWLock:创建读写锁(读共享、写独占的高级锁) SDL_CreateSemaphore:创建信号量(指定初始计数值) SDL_DestroyCondition:销毁条件变量(释放关联资源) SDL_DestroyMutex:销毁互斥锁(释放关联资源) SDL_DestroyRWLock:销毁读写锁(释放关联资源) SDL_DestroySemaphore:销毁信号量(释放关联资源) SDL_GetSemaphoreValue:获取信号量的当前计数值 SDL_LockMutex:加锁互斥锁(阻塞式,获取不到锁则等待) SDL_LockRWLockForReading:为读操作加读写锁(共享锁,允许多线程同时读) SDL_LockRWLockForWriting:为写操作加读写锁(独占锁,仅允许一个线程写) SDL_SetInitialized:设置资源的初始化状态(标记为已初始化/已反初始化) SDL_ShouldInit:检查资源是否需要初始化(防止多线程重复初始化) SDL_ShouldQuit:检查是否需要退出(线程退出条件检测) SDL_SignalCondition:唤醒一个等待该条件变量的线程(随机唤醒一个) SDL_SignalSemaphore:信号量计数加1(释放一个资源许可) SDL_TryLockMutex:尝试加锁互斥锁(非阻塞式,获取不到锁直接返回失败) SDL_TryLockRWLockForReading:尝试为读操作加读写锁(非阻塞式) SDL_TryLockRWLockForWriting:尝试为写操作加读写锁(非阻塞式) SDL_TryWaitSemaphore:尝试等待信号量(非阻塞式,计数不足则直接返回失败) SDL_UnlockMutex:解锁互斥锁(释放锁,允许其他线程获取) SDL_UnlockRWLock:解锁读写锁(释放读/写锁) SDL_WaitCondition:等待条件变量(需配合互斥锁使用,阻塞直到被唤醒) SDL_WaitConditionTimeout:带超时的条件变量等待(超时后自动返回) SDL_WaitSemaphore:等待信号量(阻塞式,计数减1,计数为0则等待) SDL_WaitSemaphoreTimeout:带超时的信号量等待(超时后自动返回) 数据类型 SDL_Condition:条件变量句柄类型(标识一个创建的条件变量) SDL_Mutex:互斥锁句柄类型(标识一个创建的互斥锁) SDL_RWLock:读写锁句柄类型(标识一个创建的读写锁) SDL_Semaphore:信号量句柄类型(标识一个创建的信号量) 结构体 SDL_InitState:资源初始化状态结构体(用于多线程安全的资源初始化控制) 枚举 SDL_InitStatus:初始化状态枚举(未初始化/初始化中/已初始化/反初始化中) 宏 SDL_ACQUIRE:标记获取独占锁的函数(线程安全分析用) SDL_ACQUIRE_SHARED:标记获取共享锁的函数(线程安全分析用) SDL_ACQUIRED_AFTER:标记锁的获取顺序(当前函数获取的锁在指定锁之后) SDL_ACQUIRED_BEFORE:标记锁的获取顺序(当前函数获取的锁在指定锁之前) SDL_ASSERT_CAPABILITY:断言当前线程持有指定的独占锁(调试用) SDL_ASSERT_SHARED_CAPABILITY:断言当前线程持有指定的共享锁(调试用) SDL_CAPABILITY:定义线程安全能力(锁)的类型别名(线程安全分析用) SDL_EXCLUDES:标记变量/函数排除指定锁(线程安全分析用) SDL_GUARDED_BY:标记变量受指定独占锁保护(线程安全分析用) SDL_NO_THREAD_SAFETY_ANALYSIS:禁用当前函数的线程安全分析 SDL_PT_GUARDED_BY:标记指针指向的变量受指定独占锁保护(线程安全分析用) SDL_RELEASE:标记释放独占锁的函数(线程安全分析用) SDL_RELEASE_GENERIC:标记释放通用锁的函数(线程安全分析用) SDL_RELEASE_SHARED:标记释放共享锁的函数(线程安全分析用) SDL_REQUIRES:标记函数要求调用线程持有指定独占锁(线程安全分析用) SDL_REQUIRES_SHARED:标记函数要求调用线程持有指定共享锁(线程安全分析用) SDL_RETURN_CAPABILITY:标记函数返回一个锁(线程安全分析用) SDL_SCOPED_CAPABILITY:标记作用域锁(自动释放锁,线程安全分析用) SDL_THREAD_ANNOTATION_ATTRIBUTE__:线程注解属性的基础宏(内部使用) SDL_TRY_ACQUIRE:标记尝试获取独占锁的函数(线程安全分析用) SDL_TRY_ACQUIRE_SHARED:标记尝试获取共享锁的函数(线程安全分析用) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 全局共享资源 Dim Shared As Integer g_sharedCounter = 0 ' 同步原语 Dim Shared As SDL_Mutex Ptr g_mutex = NULL ' 互斥锁 Dim Shared As SDL_RWLock Ptr g_rwLock = NULL ' 读写锁 Dim Shared As SDL_Semaphore Ptr g_semaphore = NULL ' 信号量 Dim Shared As SDL_Condition Ptr g_condition = NULL ' 条件变量 Dim Shared As SDL_Mutex Ptr g_condMutex = NULL ' 条件变量配套互斥锁 Dim Shared As SDL_InitState g_initState ' 初始化状态 ' 线程控制标志 Dim Shared As Boolean g_quitThreads = False ' 初始化状态的辅助函数声明(FreeBASIC 绑定可能缺失) Declare Function SDL_ShouldInit CDecl (ByVal state As SDL_InitState Ptr) As Integer Declare Sub SDL_SetInitialized CDecl (ByVal state As SDL_InitState Ptr, ByVal status As SDL_InitStatus) ' 线程1:使用互斥锁访问共享计数器 Function MutexThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动", threadName, threadId) While (Not g_quitThreads) ' 加锁互斥锁 SDL_LockMutex(g_mutex) ' 安全访问共享资源 g_sharedCounter += 1 SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 互斥锁:计数器 = %d", threadName, g_sharedCounter) ' 解锁互斥锁 SDL_UnlockMutex(g_mutex) ' 模拟工作 SDL_Delay(300) Wend SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 线程2:使用读写锁读取共享计数器(读线程) Function ReadRWLockThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动", threadName, threadId) While (Not g_quitThreads) ' 加读锁(共享锁,允许多线程同时读) SDL_LockRWLockForReading(g_rwLock) ' 安全读取共享资源 SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 读锁:计数器 = %d", threadName, g_sharedCounter) ' 解锁读写锁 SDL_UnlockRWLock(g_rwLock) ' 模拟工作 SDL_Delay(500) Wend SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 线程3:使用读写锁修改共享计数器(写线程) Function WriteRWLockThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动", threadName, threadId) While (Not g_quitThreads) ' 加写锁(独占锁,仅允许一个线程写) SDL_LockRWLockForWriting(g_rwLock) ' 安全修改共享资源 g_sharedCounter *= 2 SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 写锁:计数器 ×2 = %d", threadName, g_sharedCounter) ' 解锁读写锁 SDL_UnlockRWLock(g_rwLock) ' 模拟工作 SDL_Delay(2000) Wend SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 线程4:使用信号量控制并发 Function SemaphoreThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动", threadName, threadId) While (Not g_quitThreads) ' 等待信号量(获取资源许可,最多2个线程同时执行) SDL_WaitSemaphore(g_semaphore) ' 临界区(最多2个线程同时进入) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 信号量:获取许可,当前计数 = %d", threadName, SDL_GetSemaphoreValue(g_semaphore)) SDL_Delay(1000) ' 模拟耗时操作 ' 释放信号量(归还资源许可) SDL_SignalSemaphore(g_semaphore) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 信号量:释放许可,当前计数 = %d", threadName, SDL_GetSemaphoreValue(g_semaphore)) ' 模拟工作 SDL_Delay(500) Wend SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 线程5:使用条件变量等待通知 Function ConditionThread CDecl (ByVal data As Any Ptr) As Integer Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,等待条件变量通知...", threadName, threadId) ' 加锁条件变量配套的互斥锁 SDL_LockMutex(g_condMutex) ' 等待条件变量(自动释放锁,被唤醒后重新获取锁) SDL_WaitCondition(g_condition, g_condMutex) ' 被唤醒后执行 SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 收到通知,计数器 = %d", threadName, threadId, g_sharedCounter) ' 解锁互斥锁 SDL_UnlockMutex(g_condMutex) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 初始化共享资源(线程安全) Sub InitSharedResource() ' 确保只有一个线程执行初始化 If (SDL_ShouldInit(@g_initState)) Then SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "主线程:初始化共享资源...") ' 初始化同步原语 g_mutex = SDL_CreateMutex() g_rwLock = SDL_CreateRWLock() g_semaphore = SDL_CreateSemaphore(2) ' 初始计数2,最多2个线程并发 g_condition = SDL_CreateCondition() g_condMutex = SDL_CreateMutex() ' 标记初始化完成 SDL_SetInitialized(@g_initState, SDL_INIT_STATUS_INITIALIZED) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "主线程:共享资源初始化完成") End If End Sub ' 主程序 Sub Main() ' 初始化 SDL If (SDL_Init(SDL_INIT_THREADS) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' 初始化共享资源(线程安全) InitSharedResource() ' 检查初始化是否成功 If (g_mutex = NULL Or g_rwLock = NULL Or g_semaphore = NULL Or g_condition = NULL Or g_condMutex = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_THREAD, "同步原语创建失败:%s", SDL_GetError()) g_quitThreads = True SDL_Quit() Exit Sub End If ' 创建线程 Dim As String t1Name = "互斥锁线程" Dim As String t2Name = "读锁线程1" Dim As String t3Name = "写锁线程" Dim As String t4Name = "信号量线程1" Dim As String t5Name = "信号量线程2" Dim As String t6Name = "信号量线程3" Dim As String t7Name = "条件变量线程" Dim As SDL_Thread Ptr t1 = SDL_CreateThread(@MutexThread, t1Name, StrPtr(t1Name)) Dim As SDL_Thread Ptr t2 = SDL_CreateThread(@ReadRWLockThread, t2Name, StrPtr(t2Name)) Dim As SDL_Thread Ptr t3 = SDL_CreateThread(@WriteRWLockThread, t3Name, StrPtr(t3Name)) Dim As SDL_Thread Ptr t4 = SDL_CreateThread(@SemaphoreThread, t4Name, StrPtr(t4Name)) Dim As SDL_Thread Ptr t5 = SDL_CreateThread(@SemaphoreThread, t5Name, StrPtr(t5Name)) Dim As SDL_Thread Ptr t6 = SDL_CreateThread(@SemaphoreThread, t6Name, StrPtr(t6Name)) Dim As SDL_Thread Ptr t7 = SDL_CreateThread(@ConditionThread, t7Name, StrPtr(t7Name)) If (t1 = NULL Or t2 = NULL Or t3 = NULL Or t4 = NULL Or t5 = NULL Or t6 = NULL Or t7 = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建线程失败:%s", SDL_GetError()) g_quitThreads = True SDL_Quit() Exit Sub End If ' 主线程运行 5 秒 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程运行 5 秒...") SDL_Delay(5000) ' 发送条件变量通知 SDL_LockMutex(g_condMutex) SDL_SignalCondition(g_condition) ' 唤醒等待条件变量的线程 SDL_UnlockMutex(g_condMutex) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程:发送条件变量通知") ' 通知所有线程退出 g_quitThreads = True SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程:通知所有线程退出") ' 等待所有线程结束 Dim As Integer result SDL_WaitThread(t1, @result) SDL_WaitThread(t2, @result) SDL_WaitThread(t3, @result) SDL_WaitThread(t4, @result) SDL_WaitThread(t5, @result) SDL_WaitThread(t6, @result) SDL_WaitThread(t7, @result) ' 清理资源 SDL_DestroyMutex(g_mutex) SDL_DestroyRWLock(g_rwLock) SDL_DestroySemaphore(g_semaphore) SDL_DestroyCondition(g_condition) SDL_DestroyMutex(g_condMutex) ' 退出 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出,最终计数器值:%d", g_sharedCounter) End Sub ' 运行主程序 Main() 核心知识点补充 同步原语适用场景: 互斥锁(Mutex):最简单的独占锁,适用于所有需要独占访问的场景(如简单的计数器修改); 读写锁(RWLock):读多写少的场景(如配置文件读取/更新),读操作共享,写操作独占,提升并发效率; 信号量(Semaphore):控制并发数量的场景(如线程池、资源池),计数值表示可用资源数; 条件变量(Condition):线程间等待/通知的场景(如生产者-消费者模型),必须配合互斥锁使用。 关键使用规则: 互斥锁/读写锁:加锁和解锁必须成对出现,且在同一个线程中执行; 条件变量:SDL_WaitCondition() 会自动释放传入的互斥锁,被唤醒后重新获取锁; 信号量:SDL_WaitSemaphore() 使计数减1(获取资源),SDL_SignalSemaphore() 使计数加1(释放资源),计数为0时 Wait 会阻塞; SDL_InitState:SDL_ShouldInit() 确保只有一个线程执行初始化,避免多线程重复初始化资源。 非阻塞操作: SDL_TryLockMutex()/SDL_TryLockRWLockForReading()/SDL_TryLockRWLockForWriting():非阻塞加锁,获取不到锁时直接返回失败,适合不想阻塞的场景; SDL_TryWaitSemaphore():非阻塞等待信号量,计数不足时直接返回失败。 超时操作: SDL_WaitConditionTimeout():带超时的条件变量等待,超时时间单位为毫秒; SDL_WaitSemaphoreTimeout():带超时的信号量等待,避免线程永久阻塞。 总结 核心优势: 提供跨平台统一的线程同步接口,屏蔽不同系统的同步原语差异; 涵盖从基础互斥锁到高级条件变量的完整同步方案,满足不同场景需求; SDL_InitState 简化多线程安全的资源初始化逻辑。 使用建议: 优先选择最贴合场景的同步原语(读多写少用读写锁,并发控制用信号量); 加锁/解锁必须成对,避免死锁(如忘记解锁、解锁非当前线程持有的锁); 条件变量必须配合互斥锁使用,且等待前需加锁; 信号量计数值需合理设置,避免资源耗尽或过度并发。 关键接口: 互斥锁:SDL_CreateMutex()/SDL_LockMutex()/SDL_UnlockMutex(); 读写锁:SDL_CreateRWLock()/SDL_LockRWLockForReading()/SDL_LockRWLockForWriting()/SDL_UnlockRWLock(); 信号量:SDL_CreateSemaphore()/SDL_WaitSemaphore()/SDL_SignalSemaphore(); 条件变量:SDL_CreateCondition()/SDL_WaitCondition()/SDL_SignalCondition()/SDL_BroadcastCondition(); 初始化控制:SDL_ShouldInit()/SDL_SetInitialized()。
      • 2026年-3月-6日
      • 4 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_线程(CategoryThread) 2026-3-6
      SDL3_API分类参考_线程(CategoryThread) 线程子系统(CategoryThread) SDL 提供了跨平台的线程管理功能,主要用于线程的创建、优先级设置和终止处理。 此外,SDL 还支持线程本地存储(Thread Local Storage, TLS) —— 即每个线程拥有唯一的专属数据,但可通过同一个键值访问这些数据。 平台兼容性说明 在不支持线程的平台上(例如未启用 pthreads 的 Emscripten 环境),这些函数依然存在,但 SDL_CreateThread() 这类核心函数会直接返回失败,不会执行实际的线程创建操作。 线程安全注意事项 如果要使用线程功能,你必须充分理解线程安全相关的防护措施:线程的锁定和同步机制由 SDL_mutex.h 中的函数提供(可参考 CategoryMutex 文档)。 函数 SDL_CleanupTLS:清理指定的线程本地存储(TLS)键,释放关联的资源 SDL_CreateThread:创建一个新线程(指定线程函数、线程名称和传递参数) SDL_CreateThreadWithProperties:通过属性配置创建线程(更精细的线程控制) SDL_DetachThread:分离线程(线程结束后自动释放资源,无需等待) SDL_GetCurrentThreadID:获取当前执行线程的唯一标识符(ThreadID) SDL_GetThreadID:获取指定线程对象的唯一标识符(ThreadID) SDL_GetThreadName:获取指定线程的名称(创建线程时设置的名称) SDL_GetThreadState:获取指定线程的当前状态(运行/终止/等待等) SDL_GetTLS:从线程本地存储中,通过 TLS 键获取当前线程的专属数据 SDL_SetCurrentThreadPriority:设置当前执行线程的优先级 SDL_SetTLS:将指定数据关联到当前线程的 TLS 键(设置线程本地存储数据) SDL_WaitThread:等待指定线程执行完成(阻塞当前线程,直到目标线程结束) 数据类型 SDL_Thread:线程对象句柄类型(标识一个创建的线程) SDL_ThreadFunction:线程函数的函数指针类型(线程入口函数的标准格式) SDL_ThreadID:线程唯一标识符类型(用于区分不同线程) SDL_TLSDestructorCallback:TLS 析构函数回调类型(线程退出时清理 TLS 数据) SDL_TLSID:线程本地存储(TLS)的键类型(用于访问线程专属数据) 结构体 (无) 枚举 SDL_ThreadPriority:线程优先级枚举(低/正常/高/时间关键等) SDL_ThreadState:线程状态枚举(运行中/已终止/等待中/已分离等) 宏 (无) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 全局变量:用于线程间同步的互斥锁 Dim Shared As SDL_Mutex Ptr g_mutex = NULL ' 全局变量:线程退出标志 Dim Shared As Boolean g_quitThread = False ' TLS 键:存储每个线程的专属计数器 Dim Shared As SDL_TLSID g_tlsCounterKey = SDL_TLSID(-1) ' 线程函数:模拟耗时任务 Function ThreadTask CDecl (ByVal data As Any Ptr) As Integer ' 获取线程名称(通过传入参数传递) Dim As ZString Ptr threadName = Cast(ZString Ptr, data) Dim As Integer threadId = SDL_GetCurrentThreadID() ' 初始化当前线程的 TLS 计数器(每个线程独立) Dim As Integer Ptr counter = Allocate(SizeOf(Integer)) *counter = 0 SDL_SetTLS(g_tlsCounterKey, counter, @SDL_free) ' 设置析构函数自动释放 SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程 %s (ID: %d) 启动", threadName, threadId) ' 设置当前线程优先级为正常 SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_NORMAL) ' 线程主循环 While (Not g_quitThread) ' 加锁保护共享资源访问 SDL_LockMutex(g_mutex) ' 更新 TLS 计数器(线程专属,无需加锁) (*counter) += 1 SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程 %s 计数器:%d", threadName, *counter) ' 解锁 SDL_UnlockMutex(g_mutex) ' 模拟耗时操作 SDL_Delay(500) Wend ' 获取并打印最终的 TLS 计数器值 Dim As Integer Ptr finalCounter = Cast(Integer Ptr, SDL_GetTLS(g_tlsCounterKey)) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程 %s 结束,最终计数器:%d", threadName, *finalCounter) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程 %s (ID: %d) 退出", threadName, threadId) Return 0 End Function ' 主程序 Sub Main() ' 初始化 SDL If (SDL_Init(SDL_INIT_THREADS) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' 创建互斥锁(线程同步用) g_mutex = SDL_CreateMutex() If (g_mutex = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建互斥锁失败:%s", SDL_GetError()) SDL_Quit() Exit Sub End If ' 创建 TLS 键(用于存储线程专属计数器) g_tlsCounterKey = SDL_CreateTLS() If (g_tlsCounterKey = SDL_TLSID(-1)) Then SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建 TLS 键失败:%s", SDL_GetError()) SDL_DestroyMutex(g_mutex) SDL_Quit() Exit Sub End If ' 定义线程名称 Dim As String thread1Name = "线程1" Dim As String thread2Name = "线程2" ' 创建两个工作线程 Dim As SDL_Thread Ptr thread1 = SDL_CreateThread(@ThreadTask, thread1Name, StrPtr(thread1Name)) Dim As SDL_Thread Ptr thread2 = SDL_CreateThread(@ThreadTask, thread2Name, StrPtr(thread2Name)) If (thread1 = NULL Or thread2 = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建线程失败:%s", SDL_GetError()) g_quitThread = True SDL_DestroyMutex(g_mutex) SDL_CleanupTLS(g_tlsCounterKey) SDL_Quit() Exit Sub End If ' 打印线程信息 SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "主线程 ID:%d", SDL_GetCurrentThreadID()) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程1 ID:%d", SDL_GetThreadID(thread1)) SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程2 ID:%d", SDL_GetThreadID(thread2)) ' 主线程等待 5 秒后通知线程退出 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程等待 5 秒...") SDL_Delay(5000) g_quitThread = True ' 等待线程执行完成 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "等待线程退出...") Dim As Integer thread1Result, thread2Result SDL_WaitThread(thread1, @thread1Result) SDL_WaitThread(thread2, @thread2Result) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "线程1退出码:%d,线程2退出码:%d", thread1Result, thread2Result) ' 清理资源 SDL_CleanupTLS(g_tlsCounterKey) ' 清理 TLS 键 SDL_DestroyMutex(g_mutex) ' 销毁互斥锁 SDL_Quit() ' 退出 SDL SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出") End Sub ' 补充 SDL_CreateTLS 函数声明(FreeBASIC 绑定可能缺失) Declare Function SDL_CreateTLS CDecl () As SDL_TLSID ' 运行主程序 Main() 核心知识点补充 线程函数规范: SDL 线程函数必须是 CDecl 调用约定,返回值为 Integer,参数为 Any Ptr; 线程函数的参数可用于传递任意数据(如示例中的线程名称); 线程退出时返回的整数值可通过 SDL_WaitThread() 获取(退出码)。 线程本地存储(TLS): 每个线程通过同一个 TLS 键访问的数据是独立的,无需加锁; 设置 TLS 时可指定析构函数(如示例中的 SDL_free),线程退出时自动释放数据; 使用完 TLS 键后需调用 SDL_CleanupTLS() 清理全局资源。 线程管理关键操作: 等待线程:SDL_WaitThread() 会阻塞当前线程,直到目标线程结束,适合需要等待线程结果的场景; 分离线程:SDL_DetachThread() 后线程无需等待,结束后自动释放资源,适合无需关注结果的后台线程; 优先级:SDL_SetCurrentThreadPriority() 仅在当前线程内调用有效,优先级受系统限制(并非所有平台都支持)。 线程安全: 共享资源(如全局变量)必须通过互斥锁(SDL_Mutex)保护,避免数据竞争; TLS 数据是线程专属的,访问时无需加锁,适合存储线程私有状态。 总结 核心优势: 跨平台统一的线程管理接口,屏蔽不同系统的线程 API 差异; 支持线程本地存储(TLS),简化线程私有数据的管理; 兼容无线程支持的平台,函数调用不会崩溃(仅返回失败)。 使用建议: 线程函数必须遵循 CDecl 调用约定,否则会导致崩溃; 共享资源必须加锁保护,TLS 数据无需加锁; 线程创建后要么用 SDL_WaitThread() 等待,要么用 SDL_DetachThread() 分离,避免资源泄漏; 退出程序前需确保所有线程正常终止,避免僵尸线程。 关键接口: 线程创建:SDL_CreateThread(); 线程同步:SDL_WaitThread()/SDL_DetachThread(); TLS 管理:SDL_CreateTLS()/SDL_SetTLS()/SDL_GetTLS()/SDL_CleanupTLS(); 线程信息:SDL_GetCurrentThreadID()/SDL_GetThreadID()/SDL_GetThreadState()。
      • 2026年-3月-6日
      • 3 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • SDL3_API分类参考_GPU(CategoryGPU) 2026-3-6
      SDL3_API分类参考_GPU(CategoryGPU) GPU 子系统(CategoryGPU) SDL GPU API 提供了跨平台的现代图形硬件交互方式,支持 3D 图形渲染和计算功能,设计风格对标 Metal、Vulkan 和 Direct3D 12。 基础工作流程 设备初始化:应用通过 SDL_CreateGPUDevice() 创建 GPU 设备,并使用 SDL_ClaimWindowForGPUDevice() 将设备关联到窗口(也可完全离屏渲染,如仅做图像处理); 静态资源准备(创建一次,重复使用): 着色器(GPU 执行的程序):SDL_CreateGPUShader(); 顶点缓冲区(几何数据)及其他渲染数据:SDL_CreateGPUBuffer() + SDL_UploadToGPUBuffer(); 纹理(图像):SDL_CreateGPUTexture() + SDL_UploadToGPUTexture(); 采样器(纹理读取方式):SDL_CreateGPUSampler(); 渲染管线(预计算的渲染状态):SDL_CreateGPUGraphicsPipeline(); 命令缓冲区创建:通过 SDL_AcquireGPUCommandBuffer() 创建一个/多个命令缓冲区,用于批量收集渲染指令(复杂场景可多线程并行创建,按序提交); 渲染目标准备: 渲染到纹理(其他 API 称“渲染目标”); 渲染到交换链纹理(窗口内容的特殊纹理):SDL_WaitAndAcquireGPUSwapchainTexture(); 渲染通道编码: 开始渲染通道:SDL_BeginGPURenderPass(); 设置渲染状态(管线、视口、顶点缓冲区等); 执行绘制命令(SDL_DrawGPUPrimitives() 等); 结束渲染通道:SDL_EndGPURenderPass()(结束后重置所有渲染状态); 命令提交:SDL_SubmitGPUCommandBuffer() 将命令缓冲区提交给 GPU 执行; 数据回读(可选):通过 SDL_DownloadFromGPUTexture()/SDL_DownloadFromGPUBuffer() 读取 GPU 数据,配合 SDL_SubmitGPUCommandBufferAndAcquireFence() 获取围栏(Fence),等待 GPU 操作完成后安全读取数据。 计算功能支持 开始计算通道:SDL_BeginGPUComputePass()(指定可写入的纹理/缓冲区); 设置计算状态(计算管线、存储缓冲区/纹理等); 分发计算任务:SDL_DispatchGPUCompute(); 结束计算通道:SDL_EndGPUComputePass()。 着色器说明 不同底层后端(Vulkan/Metal/D3D12)需要不同的着色器格式: 创建 GPU 设备时,应用声明支持的着色器格式,SDL 自动选择适配的后端; 建议预编译着色器,也可使用 SDL_shadercross 实现运行时跨编译; 着色器资源布局需严格匹配 API 要求,否则会导致着色器失效。 适用场景 简单 2D 图形:优先使用 Render API(更易用,仍硬件加速); 高性能/复杂渲染:使用 GPU API(2D 应用也能获得显著性能提升); GPU 计算:如图像处理、物理模拟等并行计算场景。 性能优化建议 减少渲染通道数量(开始新通道开销较高); 最小化状态切换(如管线绑定); 尽早完成帧内数据上传; 缓存资源(创建/释放资源开销大); 大量数据使用存储缓冲区(而非统一缓冲区); 正确使用“循环(Cycling)”机制; 使用裁剪技术减少像素写入。 坐标系 GPU API 采用左手坐标系(对齐 D3D12/Metal 规范): 归一化设备坐标(NDC):左下角 (-1.0, -1.0),右上角 (1.0, 1.0),Z 值范围 [0.0, 1.0](0 为近裁剪面); 视口坐标:左上角 (0, 0),右下角 (viewportWidth, viewportHeight),+Y 向下; 纹理坐标:左上角 (0, 0),右下角 (1.0, 1.0),+Y 向下; SDL 自动适配 Vulkan 等不同坐标系的后端,无需在着色器中翻转坐标。 循环(Cycling)机制 命令缓冲区提交后操作并非立即执行,资源在使用期间处于“绑定”状态; 所有写入资源的函数/结构体均有 cycle 布尔参数,开启后自动切换到未绑定的内部资源(无可用资源时创建新资源); 避免手动跟踪资源状态和同步,防止数据依赖和覆盖; 循环后资源数据视为未定义,需重新写入后再读取。 调试工具 Windows/Linux:RenderDoc(可视化 GPU 帧、绘制调用、资源); macOS:Xcode 内置 Metal 调试器(GPU Frame Capture); 后端调试层: D3D12:安装“Graphics Tools”可选功能; Vulkan:安装 Vulkan SDK/验证层; Metal:Xcode 运行自动输出详细错误。 函数 SDL_AcquireGPUCommandBuffer:获取GPU命令缓冲区(用于编码渲染/计算指令) SDL_AcquireGPUSwapchainTexture:获取交换链纹理(非阻塞,可能返回NULL) SDL_BeginGPUComputePass:开始计算通道(设置可写入的存储纹理/缓冲区) SDL_BeginGPUCopyPass:开始拷贝通道(用于GPU内资源拷贝) SDL_BeginGPURenderPass:开始渲染通道(指定颜色/深度目标) SDL_BindGPUComputePipeline:绑定计算管线到当前计算通道 SDL_BindGPUComputeSamplers:绑定计算阶段使用的采样器 SDL_BindGPUComputeStorageBuffers:绑定计算阶段的存储缓冲区 SDL_BindGPUComputeStorageTextures:绑定计算阶段的存储纹理 SDL_BindGPUFragmentSamplers:绑定片段着色器阶段的采样器 SDL_BindGPUFragmentStorageBuffers:绑定片段着色器的存储缓冲区 SDL_BindGPUFragmentStorageTextures:绑定片段着色器的存储纹理 SDL_BindGPUGraphicsPipeline:绑定图形渲染管线到当前渲染通道 SDL_BindGPUIndexBuffer:绑定索引缓冲区 SDL_BindGPUVertexBuffers:绑定顶点缓冲区数组 SDL_BindGPUVertexSamplers:绑定顶点着色器阶段的采样器 SDL_BindGPUVertexStorageBuffers:绑定顶点着色器的存储缓冲区 SDL_BindGPUVertexStorageTextures:绑定顶点着色器的存储纹理 SDL_BlitGPUTexture:GPU纹理拷贝(二维矩形区域复制) SDL_CalculateGPUTextureFormatSize:计算指定格式/尺寸的纹理内存大小 SDL_CancelGPUCommandBuffer:取消未提交的命令缓冲区(释放资源) SDL_ClaimWindowForGPUDevice:将窗口关联到GPU设备(创建交换链) SDL_CopyGPUBufferToBuffer:GPU缓冲区间拷贝 SDL_CopyGPUTextureToTexture:GPU纹理间拷贝 SDL_CreateGPUBuffer:创建GPU缓冲区(顶点/索引/存储数据) SDL_CreateGPUComputePipeline:创建计算管线(关联计算着色器) SDL_CreateGPUDevice:创建GPU设备(指定驱动/配置) SDL_CreateGPUDeviceWithProperties:带属性创建GPU设备(精细控制) SDL_CreateGPUGraphicsPipeline:创建图形渲染管线(关联顶点/片段着色器) SDL_CreateGPUSampler:创建采样器(定义纹理采样方式) SDL_CreateGPUShader:创建着色器(顶点/片段/计算着色器) SDL_CreateGPUTexture:创建GPU纹理(2D/3D/立方体贴图等) SDL_CreateGPUTransferBuffer:创建传输缓冲区(CPU-GPU数据交换) SDL_DestroyGPUDevice:销毁GPU设备(释放所有关联资源) SDL_DispatchGPUCompute:分发计算任务(指定线程组数量) SDL_DispatchGPUComputeIndirect:间接分发计算任务(从缓冲区读取参数) SDL_DownloadFromGPUBuffer:从GPU缓冲区下载数据到CPU SDL_DownloadFromGPUTexture:从GPU纹理下载数据到CPU SDL_DrawGPUIndexedPrimitives:绘制索引化图元(三角形/线等) SDL_DrawGPUIndexedPrimitivesIndirect:间接绘制索引化图元 SDL_DrawGPUPrimitives:绘制非索引化图元 SDL_DrawGPUPrimitivesIndirect:间接绘制非索引化图元 SDL_EndGPUComputePass:结束计算通道 SDL_EndGPUCopyPass:结束拷贝通道 SDL_EndGPURenderPass:结束渲染通道(重置渲染状态) SDL_GDKResumeGPU:GDK平台恢复GPU(Xbox) SDL_GDKSuspendGPU:GDK平台暂停GPU(Xbox) SDL_GenerateMipmapsForGPUTexture:为GPU纹理生成Mipmap SDL_GetGPUDeviceDriver:获取GPU设备使用的驱动名称 SDL_GetGPUDeviceProperties:获取GPU设备属性(功能支持/限制) SDL_GetGPUDriver:获取指定索引的GPU驱动名称 SDL_GetGPUShaderFormats:获取GPU设备支持的着色器格式 SDL_GetGPUSwapchainTextureFormat:获取交换链纹理的格式 SDL_GetGPUTextureFormatFromPixelFormat:像素格式转GPU纹理格式 SDL_GetNumGPUDrivers:获取系统可用的GPU驱动数量 SDL_GetPixelFormatFromGPUTextureFormat:GPU纹理格式转像素格式 SDL_GPUSupportsProperties:检查GPU设备是否支持指定属性 SDL_GPUSupportsShaderFormats:检查GPU设备是否支持指定着色器格式 SDL_GPUTextureFormatTexelBlockSize:计算纹理格式的纹素块大小 SDL_GPUTextureSupportsFormat:检查GPU是否支持指定纹理格式 SDL_GPUTextureSupportsSampleCount:检查纹理是否支持指定采样数 SDL_InsertGPUDebugLabel:插入GPU调试标签(辅助调试工具定位) SDL_MapGPUTransferBuffer:映射传输缓冲区到CPU地址空间 SDL_PopGPUDebugGroup:弹出GPU调试分组 SDL_PushGPUComputeUniformData:推送计算着色器的统一数据 SDL_PushGPUDebugGroup:推送GPU调试分组(组织调试标签) SDL_PushGPUFragmentUniformData:推送片段着色器的统一数据 SDL_PushGPUVertexUniformData:推送顶点着色器的统一数据 SDL_QueryGPUFence:查询GPU围栏状态(完成/未完成) SDL_ReleaseGPUBuffer:释放GPU缓冲区 SDL_ReleaseGPUComputePipeline:释放计算管线 SDL_ReleaseGPUFence:释放GPU围栏 SDL_ReleaseGPUGraphicsPipeline:释放图形渲染管线 SDL_ReleaseGPUSampler:释放采样器 SDL_ReleaseGPUShader:释放着色器 SDL_ReleaseGPUTexture:释放GPU纹理 SDL_ReleaseGPUTransferBuffer:释放传输缓冲区 SDL_ReleaseWindowFromGPUDevice:解除窗口与GPU设备的关联 SDL_SetGPUAllowedFramesInFlight:设置允许的在飞帧数(并发帧数量) SDL_SetGPUBlendConstants:设置混合常量(用于颜色混合计算) SDL_SetGPUBufferName:设置GPU缓冲区名称(调试用) SDL_SetGPUScissor:设置裁剪矩形(限制像素绘制区域) SDL_SetGPUStencilReference:设置模板测试参考值 SDL_SetGPUSwapchainParameters:设置交换链参数(刷新率/呈现模式) SDL_SetGPUTextureName:设置GPU纹理名称(调试用) SDL_SetGPUViewport:设置视口(渲染输出区域) SDL_SubmitGPUCommandBuffer:提交命令缓冲区到GPU执行 SDL_SubmitGPUCommandBufferAndAcquireFence:提交命令缓冲区并获取围栏 SDL_UnmapGPUTransferBuffer:解除传输缓冲区的CPU映射 SDL_UploadToGPUBuffer:从CPU上传数据到GPU缓冲区 SDL_UploadToGPUTexture:从CPU上传数据到GPU纹理 SDL_WaitAndAcquireGPUSwapchainTexture:等待并获取交换链纹理(阻塞) SDL_WaitForGPUFences:等待多个GPU围栏完成 SDL_WaitForGPUIdle:等待GPU完成所有待处理命令 SDL_WaitForGPUSwapchain:等待交换链就绪 SDL_WindowSupportsGPUPresentMode:检查窗口是否支持指定呈现模式 SDL_WindowSupportsGPUSwapchainComposition:检查窗口是否支持交换链合成 数据类型 SDL_GPUBuffer:GPU缓冲区句柄类型(顶点/索引/存储数据) SDL_GPUBufferUsageFlags:GPU缓冲区使用标志(顶点/索引/存储等) SDL_GPUColorComponentFlags:颜色组件标志(R/G/B/A通道) SDL_GPUCommandBuffer:GPU命令缓冲区句柄(存储渲染/计算指令) SDL_GPUComputePass:计算通道句柄 SDL_GPUComputePipeline:计算管线句柄 SDL_GPUCopyPass:拷贝通道句柄 SDL_GPUDevice:GPU设备句柄(核心设备对象) SDL_GPUFence:GPU围栏句柄(同步CPU/GPU操作) SDL_GPUGraphicsPipeline:图形渲染管线句柄 SDL_GPURenderPass:渲染通道句柄 SDL_GPUSampler:采样器句柄(纹理采样配置) SDL_GPUShader:着色器句柄(顶点/片段/计算着色器) SDL_GPUShaderFormat:着色器格式枚举(SPIR-V/Metal/DXIL等) SDL_GPUTexture:GPU纹理句柄(2D/3D/立方体贴图) SDL_GPUTextureUsageFlags:GPU纹理使用标志(采样/渲染目标/存储等) SDL_GPUTransferBuffer:传输缓冲区句柄(CPU-GPU数据交换) 结构体 SDL_GPUBlitInfo:纹理拷贝信息(源/目标区域、过滤方式) SDL_GPUBlitRegion:纹理拷贝区域(坐标/尺寸) SDL_GPUBufferBinding:缓冲区绑定信息(缓冲区/偏移/范围) SDL_GPUBufferCreateInfo:GPU缓冲区创建参数(大小/使用标志) SDL_GPUBufferLocation:缓冲区位置(缓冲区/偏移量) SDL_GPUBufferRegion:缓冲区区域(偏移/大小) SDL_GPUColorTargetBlendState:颜色目标混合状态(混合因子/操作) SDL_GPUColorTargetDescription:颜色目标描述(格式/样本数) SDL_GPUColorTargetInfo:颜色目标信息(纹理/加载/存储操作) SDL_GPUComputePipelineCreateInfo:计算管线创建参数(着色器/布局) SDL_GPUDepthStencilState:深度模板状态(测试/操作) SDL_GPUDepthStencilTargetInfo:深度模板目标信息(纹理/操作) SDL_GPUGraphicsPipelineCreateInfo:图形管线创建参数(着色器/顶点状态等) SDL_GPUGraphicsPipelineTargetInfo:图形管线目标信息(颜色/深度格式) SDL_GPUIndexedIndirectDrawCommand:索引化间接绘制命令(参数) SDL_GPUIndirectDispatchCommand:间接计算分发命令(线程组数量) SDL_GPUIndirectDrawCommand:间接绘制命令(顶点数/实例数等) SDL_GPUMultisampleState:多重采样状态(采样数/质量) SDL_GPURasterizerState:光栅化状态(裁剪/填充/视口等) SDL_GPUSamplerCreateInfo:采样器创建参数(过滤/寻址模式) SDL_GPUShaderCreateInfo:着色器创建参数(类型/格式/资源布局) SDL_GPUStencilOpState:模板操作状态(失败/通过/深度失败操作) SDL_GPUStorageBufferReadWriteBinding:存储缓冲区读写绑定 SDL_GPUStorageTextureReadWriteBinding:存储纹理读写绑定 SDL_GPUTextureCreateInfo:纹理创建参数(格式/尺寸/使用标志) SDL_GPUTextureLocation:纹理位置(纹理/层级/切片) SDL_GPUTextureRegion:纹理区域(坐标/尺寸/层级) SDL_GPUTextureSamplerBinding:纹理采样器绑定(纹理/采样器) SDL_GPUTextureTransferInfo:纹理传输信息(格式/布局/行间距) SDL_GPUTransferBufferCreateInfo:传输缓冲区创建参数(大小/使用方式) SDL_GPUTransferBufferLocation:传输缓冲区位置(缓冲区/偏移) SDL_GPUVertexAttribute:顶点属性(格式/偏移/绑定点) SDL_GPUVertexBufferDescription:顶点缓冲区描述(格式/步长) SDL_GPUVertexInputState:顶点输入状态(属性/缓冲区描述) SDL_GPUViewport:视口参数(坐标/尺寸/深度范围) SDL_GPUVulkanOptions:Vulkan后端选项(实例/设备扩展) 枚举 SDL_GPUBlendFactor:混合因子(源/目标混合值计算方式) SDL_GPUBlendOp:混合操作(加法/减法/最小值等) SDL_GPUCompareOp:比较操作(小于/等于/大于等,用于深度/模板测试) SDL_GPUCubeMapFace:立方体贴图面(正/负X/Y/Z轴) SDL_GPUCullMode:裁剪模式(无/前/后向三角形裁剪) SDL_GPUFillMode:填充模式(线框/实心) SDL_GPUFilter:过滤模式(最近邻/线性/各向异性) SDL_GPUFrontFace:正面朝向(顺时针/逆时针) SDL_GPUIndexElementSize:索引元素大小(16/32位) SDL_GPULoadOp:加载操作(清除/加载/不操作,渲染目标初始状态) SDL_GPUPresentMode:呈现模式(立即/邮箱/双缓冲等) SDL_GPUPrimitiveType:图元类型(点/线/三角形/带/扇等) SDL_GPUSampleCount:采样数(1/2/4/8/16等,多重采样) SDL_GPUSamplerAddressMode:采样器寻址模式(重复/镜像/钳位等) SDL_GPUSamplerMipmapMode:采样器Mipmap模式(最近邻/线性) SDL_GPUShaderStage:着色器阶段(顶点/片段/计算) SDL_GPUStencilOp:模板操作(保持/替换/递增/递减等) SDL_GPUStoreOp:存储操作(存储/不操作,渲染目标最终状态) SDL_GPUSwapchainComposition:交换链合成模式(硬件/软件) SDL_GPUTextureFormat:GPU纹理格式(RGBA8/RGBA16F/深度模板等) SDL_GPUTextureType:纹理类型(2D/3D/立方体贴图/数组等) SDL_GPUTransferBufferUsage:传输缓冲区使用方式(上传/下载/双向) SDL_GPUVertexElementFormat:顶点元素格式(float2/float3/int4等) SDL_GPUVertexInputRate:顶点输入速率(顶点/实例) 宏 (无) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库,且启用 GPU 支持) #Include "SDL.bi" ' 全局变量 Dim Shared As SDL_Window Ptr g_window = NULL Dim Shared As SDL_GPUDevice Ptr g_gpuDevice = NULL Dim Shared As SDL_GPUTexture Ptr g_swapchainTexture = NULL Dim Shared As SDL_GPUCommandBuffer Ptr g_cmdBuffer = NULL ' 初始化 GPU 环境 Function InitGPU() As Boolean ' 1. 创建窗口 g_window = SDL_CreateWindow("SDL GPU Example", 800, 600, 0) If (g_window = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "创建窗口失败:%s", SDL_GetError()) Return False End If ' 2. 创建 GPU 设备(使用默认驱动) g_gpuDevice = SDL_CreateGPUDevice(NULL) If (g_gpuDevice = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_RENDER, "创建 GPU 设备失败:%s", SDL_GetError()) SDL_DestroyWindow(g_window) Return False End If ' 3. 将窗口关联到 GPU 设备 If (SDL_ClaimWindowForGPUDevice(g_gpuDevice, g_window) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_RENDER, "关联窗口失败:%s", SDL_GetError()) SDL_DestroyGPUDevice(g_gpuDevice) SDL_DestroyWindow(g_window) Return False End If ' 4. 获取命令缓冲区 g_cmdBuffer = SDL_AcquireGPUCommandBuffer(g_gpuDevice) If (g_cmdBuffer = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_RENDER, "获取命令缓冲区失败:%s", SDL_GetError()) SDL_ReleaseWindowFromGPUDevice(g_gpuDevice, g_window) SDL_DestroyGPUDevice(g_gpuDevice) SDL_DestroyWindow(g_window) Return False End If SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "GPU 环境初始化成功") Return True End Function ' 渲染一帧 Sub RenderFrame() ' 1. 等待并获取交换链纹理 g_swapchainTexture = SDL_WaitAndAcquireGPUSwapchainTexture(g_gpuDevice, g_window, True) If (g_swapchainTexture = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_RENDER, "获取交换链纹理失败:%s", SDL_GetError()) Return End If ' 2. 定义渲染目标(交换链纹理) Dim As SDL_GPUColorTargetInfo colorTarget SDL_memset(@colorTarget, 0, SizeOf(SDL_GPUColorTargetInfo)) colorTarget.texture = g_swapchainTexture colorTarget.load_op = SDL_GPU_LOAD_OP_CLEAR colorTarget.store_op = SDL_GPU_STORE_OP_STORE colorTarget.clear_color[0] = 0.2 ' R colorTarget.clear_color[1] = 0.3 ' G colorTarget.clear_color[2] = 0.4 ' B colorTarget.clear_color[3] = 1.0 ' A colorTarget.cycle = True ' 启用循环 ' 3. 开始渲染通道 Dim As SDL_GPURenderPass Ptr renderPass = SDL_BeginGPURenderPass( _ g_cmdBuffer, _ @colorTarget, 1, _ NULL, _ 0, 0, 0, 0 _ ) If (renderPass = NULL) Then SDL_LogError(SDL_LOG_CATEGORY_RENDER, "开始渲染通道失败:%s", SDL_GetError()) Return End If ' 4. 设置视口 Dim As SDL_GPUViewport viewport viewport.x = 0 viewport.y = 0 viewport.width = 800 viewport.height = 600 viewport.min_depth = 0.0 viewport.max_depth = 1.0 SDL_SetGPUViewport(g_cmdBuffer, @viewport) ' 5. 此处可添加绘制命令(三角形/纹理等) ' 6. 结束渲染通道 SDL_EndGPURenderPass(renderPass) ' 7. 提交命令缓冲区 If (SDL_SubmitGPUCommandBuffer(g_cmdBuffer) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_RENDER, "提交命令缓冲区失败:%s", SDL_GetError()) Return End If SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "帧渲染完成") End Sub ' 清理 GPU 资源 Sub CleanupGPU() If (g_cmdBuffer <> NULL) Then SDL_CancelGPUCommandBuffer(g_cmdBuffer) g_cmdBuffer = NULL End If If (g_gpuDevice <> NULL) Then SDL_ReleaseWindowFromGPUDevice(g_gpuDevice, g_window) SDL_DestroyGPUDevice(g_gpuDevice) g_gpuDevice = NULL End If If (g_window <> NULL) Then SDL_DestroyWindow(g_window) g_window = NULL End If SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "GPU 资源清理完成") End Sub ' 主程序 Sub Main() ' 初始化 SDL If (SDL_Init(SDL_INIT_VIDEO) < 0) Then SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError()) Exit Sub End If ' 初始化 GPU If (Not InitGPU()) Then SDL_Quit() Exit Sub End If ' 主循环 Dim As Boolean running = True Dim As SDL_Event evt SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "开始主循环(按ESC退出)") While (running) ' 处理事件 While (SDL_PollEvent(@evt)) Select Case evt.type Case SDL_EVENT_QUIT running = False Case SDL_EVENT_KEY_DOWN If (evt.key.keysym.sym = SDLK_ESCAPE) Then running = False End If End Select Wend ' 渲染帧 RenderFrame() ' 短暂延迟 SDL_Delay(16) Wend ' 清理资源 CleanupGPU() ' 退出 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 GPU 资源生命周期: 设备(Device):核心对象,所有资源关联到设备; 管线(Pipeline):预编译的渲染/计算状态,创建后可重复使用; 命令缓冲区(CommandBuffer):一次性使用,提交后需重新获取; 围栏(Fence):CPU/GPU 同步关键,用于等待 GPU 操作完成。 渲染通道关键参数: load_op:渲染前对目标的操作(CLEAR/LOAD/DONT_CARE); store_op:渲染后对目标的操作(STORE/DONT_CARE); cycle:启用后自动切换内部资源,避免帧间数据依赖。 跨平台注意事项: Vulkan:需 Vulkan 1.0+ 及指定扩展,Android 可通过属性降低要求; D3D12:仅支持 Win10+,Xbox GDK,Feature Level 11_0+; Metal:macOS 10.14+/iOS 13.0+,Apple Silicon/Intel GPU Family 2。 常见问题排查: 黑屏:检查交换链纹理获取、渲染通道创建、视口设置; 着色器失效:检查资源布局、统一数据推送、着色器格式; 性能差:减少渲染通道/状态切换,启用循环,优化数据上传。 总结 核心优势: 跨平台统一的现代 GPU 接口,对齐 Vulkan/Metal/D3D12 设计; 支持图形渲染和 GPU 计算,兼顾高性能和灵活性; 逻辑抽象(如循环机制)简化资源管理和同步; 自动适配不同坐标系和后端特性,降低跨平台开发成本。 使用建议: 简单 2D 场景优先使用 Render API,复杂场景/高性能需求使用 GPU API; 预创建并缓存管线、缓冲区、纹理等资源,避免运行时频繁创建; 启用 cycle 参数简化资源同步,避免手动状态跟踪; 利用调试工具(RenderDoc/Xcode)定位渲染问题,而非仅依赖日志。 关键接口: 设备管理:SDL_CreateGPUDevice()/SDL_DestroyGPUDevice(); 资源创建:SDL_CreateGPUBuffer()/SDL_CreateGPUTexture()/SDL_CreateGPUShader(); 命令编码:SDL_BeginGPURenderPass()/SDL_DrawGPUPrimitives()/SDL_EndGPURenderPass(); 命令提交:SDL_SubmitGPUCommandBuffer(); 同步:SDL_SubmitGPUCommandBufferAndAcquireFence()/SDL_WaitForGPUFences()。
      • 2026年-3月-6日
      • 3 阅读
      • 0 评论
      VisualFreeBasic编程文档
    • 1
    • 2
    • 3
    • 4
    • 5
    博主栏壁纸
    博主头像 勇芳

    330 文章数
    0 评论量
    • QQ游戏大厅多开版_旧版_2012到2025版
    • 使用Sandboxie沙盒多开QQ游戏大厅
    • Sandboxie沙盒(隔离软件)
    人生倒计时
    最新评论
    链接
    • 公益·寻亲,让爱回家
    • Visual Basic6 语言和控件手册
    • CWindow类库帮助FreeBasic版
    • FreeBASIC 帮助文档中文版
    • Windows GDI 编程手册
    • Windows GDI+ 编程手册
    • SQLite3数据库API手册
    • WebBrowser控件编程手册
    • Win32API参考手册
    • Windows 编程宝典
    • WinHttp参考资料
    • WMI编程手册
    • VisualFreeBasic编程文档
    舔狗日记
    载入天数...载入时分秒...

    © 2025 勇芳软件工作室 版权所有

    ICP备案图标 浙ICP备11006222号-1 | 公安备案图标 33100402331731号

    powered by emlog 浙ICP备11006222号-1