论坛的首页
勇芳的软件
教程和帮助
VisualFreeBasic编程文档
勇芳系列软件帮助说明教程
留言或交流
登录
搜索
登录
搜索
勇芳
累计撰写
333
篇文章
累计收到
0
条评论
首页
栏目
论坛的首页
勇芳的软件
教程和帮助
VisualFreeBasic编程文档
勇芳系列软件帮助说明教程
留言或交流
登录
自定义幻灯片
推荐
联系勇芳软件
最新文章
2026-3-6
SDL3_API分类参考_编译器内联函数(CategoryIntrinsics)
编译器内联函数子系统(CategoryIntrinsics) SDL 会通过预处理指令的灵活处理,判断编译器是否支持特定 CPU 架构的内联函数(intrinsics)——这一判断过程并不简单,往往依赖于系统特性、编译工具版本及其他外部因素。 包含 SDL 头文件的应用程序,可通过统一的预处理宏定义判断是否能安全使用特定 CPU 架构的编译器内联函数。注意:这些宏仅表示编译器具备使用对应内联函数的能力;运行时仍需通过 CPU 信息函数(如 SDL_HasSSE()、SDL_HasNEON())检查当前系统是否支持该指令集,否则程序可能因执行不支持的 CPU 指令而崩溃。 SDL 仅在编译器支持对应内联函数时才定义相关预处理宏,因此应用程序应使用 #ifdef(而非 #if)进行检查。 此外,SDL 会自动包含指令集对应的系统头文件:例如若 SDL 定义了 SDL_SSE2_INTRINSICS,则会自动 #include <emmintrin.h>。 函数 (无) 数据类型 (无) 结构体 (无) 枚举 (无) 宏 SDL_ALTIVEC_INTRINSICS:编译期宏,定义则表示编译器支持 PowerPC 架构的 AltiVec 内联函数,且已包含对应头文件 SDL_AVX2_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 AVX2 内联函数,且已包含对应头文件 SDL_AVX512F_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 AVX-512F 内联函数,且已包含对应头文件 SDL_AVX_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 AVX 内联函数,且已包含对应头文件 SDL_HAS_TARGET_ATTRIBS:编译期宏,定义则表示编译器支持目标架构属性(如 GCC 的 attribute((target))) SDL_LASX_INTRINSICS:编译期宏,定义则表示编译器支持龙芯架构的 LASX 内联函数,且已包含对应头文件 SDL_LSX_INTRINSICS:编译期宏,定义则表示编译器支持龙芯架构的 LSX 内联函数,且已包含对应头文件 SDL_MMX_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 MMX 内联函数,且已包含对应头文件 SDL_NEON_INTRINSICS:编译期宏,定义则表示编译器支持 ARM 架构的 NEON 内联函数,且已包含对应头文件 SDL_SSE2_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 SSE2 内联函数,且已包含对应头文件(emmintrin.h) SDL_SSE3_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 SSE3 内联函数,且已包含对应头文件(pmmintrin.h) SDL_SSE4_1_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 SSE4.1 内联函数,且已包含对应头文件(smmintrin.h) SDL_SSE4_2_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 SSE4.2 内联函数,且已包含对应头文件(nmmintrin.h) SDL_SSE_INTRINSICS:编译期宏,定义则表示编译器支持 x86 架构的 SSE 内联函数,且已包含对应头文件(xmmintrin.h) SDL_TARGETING:编译期宏,用于标记当前编译目标架构的内联函数支持状态 FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充 CPU 信息函数声明(用于运行时检测) Declare Function SDL_HasSSE2 CDecl () As Integer Declare Function SDL_HasAVX2 CDecl () As Integer Declare Function SDL_HasNEON CDecl () As Integer ' 编译期:判断编译器是否支持特定内联函数 #If Defined(SDL_SSE2_INTRINSICS) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【编译期】编译器支持 SSE2 内联函数") ' SDL 已自动包含 emmintrin.h,可直接使用 SSE2 内联函数 #Define SUPPORT_SSE2_COMPILE 1 #Else SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【编译期】编译器不支持 SSE2 内联函数") #Define SUPPORT_SSE2_COMPILE 0 #EndIf #If Defined(SDL_AVX2_INTRINSICS) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【编译期】编译器支持 AVX2 内联函数") #Define SUPPORT_AVX2_COMPILE 1 #Else SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【编译期】编译器不支持 AVX2 内联函数") #Define SUPPORT_AVX2_COMPILE 0 #EndIf #If Defined(SDL_NEON_INTRINSICS) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【编译期】编译器支持 NEON 内联函数") #Define SUPPORT_NEON_COMPILE 1 #Else SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【编译期】编译器不支持 NEON 内联函数") #Define SUPPORT_NEON_COMPILE 0 #EndIf ' SIMD 数据加法示例(SSE2 实现) Sub SIMDAdd_SSE2(ByVal a As Single Ptr, ByVal b As Single Ptr, ByVal c As Single Ptr, ByVal count As Integer) ' 仅当编译期支持 SSE2 且运行期 CPU 支持时执行 If (SUPPORT_SSE2_COMPILE = 0 Or SDL_HasSSE2() = 0) Then SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "无法执行 SSE2 优化:编译/运行期不支持") Exit Sub End If SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "使用 SSE2 内联函数执行 SIMD 加法") ' 注意:FreeBASIC 对 x86 内联函数的支持需结合编译器特性,此处为逻辑示例 Dim As Integer i = 0 ' SSE2 每次处理 4 个单精度浮点数(128 位) While (i + 3 < count) ' 示例:使用 _mm_load_ps/_mm_add_ps/_mm_store_ps 等 SSE2 内联函数 ' __m128 vecA = _mm_load_ps(a + i); ' __m128 vecB = _mm_load_ps(b + i); ' __m128 vecC = _mm_add_ps(vecA, vecB); ' _mm_store_ps(c + i, vecC); ' 模拟 SSE2 并行加法(实际需替换为真实内联函数) c[i] = a[i] + b[i] c[i+1] = a[i+1] + b[i+1] c[i+2] = a[i+2] + b[i+2] c[i+3] = a[i+3] + b[i+3] i += 4 Wend ' 处理剩余数据 While (i < count) c[i] = a[i] + b[i] i += 1 Wend End Sub ' 通用数据加法(无 SIMD 优化) Sub SIMDAdd_Generic(ByVal a As Single Ptr, ByVal b As Single Ptr, ByVal c As Single Ptr, ByVal count As Integer) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "使用通用指令执行加法") For i As Integer = 0 To count - 1 c[i] = a[i] + b[i] Next 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 ' 运行期:检测 CPU 是否支持对应指令集 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【运行期】CPU 指令集支持情况") SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SSE2 支持:%s", IIf(SDL_HasSSE2(), "是", "否")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "AVX2 支持:%s", IIf(SDL_HasAVX2(), "是", "否")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "NEON 支持:%s", IIf(SDL_HasNEON(), "是", "否")) ' 测试数据 Const DATA_COUNT As Integer = 100 Dim As Single a(DATA_COUNT-1), b(DATA_COUNT-1), c(DATA_COUNT-1) For i As Integer = 0 To DATA_COUNT-1 a[i] = i * 1.0 b[i] = i * 2.0 Next ' 优先使用 SSE2 优化,否则使用通用实现 If (SUPPORT_SSE2_COMPILE = 1 And SDL_HasSSE2() = 1) Then SIMDAdd_SSE2(@a[0], @b[0], @c[0], DATA_COUNT) Else SIMDAdd_Generic(@a[0], @b[0], @c[0], DATA_COUNT) End If ' 打印结果(前 5 个) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "加法结果(前5个):") For i As Integer = 0 To 4 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "c[%d] = %.1f + %.1f = %.1f", i, a[i], b[i], c[i]) Next ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 编译期 vs 运行期检测的区别: 编译期宏(SDL_XXX_INTRINSICS):仅判断「编译器是否能识别对应内联函数」,不代表运行时 CPU 支持; 运行期函数(SDL_HasXXX()):判断「当前运行的 CPU 是否支持该指令集」,是执行内联函数的最终依据; 必须同时满足「编译期支持 + 运行期支持」,才能安全使用内联函数。 头文件自动包含规则: SDL 会根据宏定义自动包含对应指令集的系统头文件,无需手动 #include: 宏 自动包含的头文件 SDL_SSE_INTRINSICS xmmintrin.h SDL_SSE2_INTRINSICS emmintrin.h SDL_AVX2_INTRINSICS avx2intrin.h SDL_NEON_INTRINSICS arm_neon.h 宏检查的正确方式: 必须使用 #ifdef SDL_XXX_INTRINSICS(而非 #if SDL_XXX_INTRINSICS); SDL 仅在支持时定义宏,不支持时宏不存在——#if 会因宏未定义导致编译错误,#ifdef 则安全。 目标架构属性(SDL_HAS_TARGET_ATTRIBS): 定义该宏时,编译器支持「按函数粒度指定目标架构」(如 GCC 的 __attribute__((target("sse4.2")))); 可用于为不同函数编译不同指令集版本,运行时动态选择。 总结 核心优势: 统一的编译期宏定义,避免开发者手动适配不同编译器/平台的内联函数头文件和检测逻辑; 自动包含指令集头文件,减少手动引入错误; 与 SDL CPUInfo 子系统配合,实现「编译期验证 + 运行期检测」的双层安全校验; 覆盖主流架构的内联函数(x86/ARM/龙芯/PowerPC),满足多平台优化需求。 使用建议: 编译期用 #ifdef SDL_XXX_INTRINSICS 判断编译器是否支持内联函数,隔离优化代码; 运行期用 SDL_HasXXX() 检测 CPU 支持,决定是否执行优化逻辑; 始终提供通用实现作为降级方案,避免无指令集支持时程序崩溃; 对性能关键路径(如图形渲染、数据处理)使用内联函数优化,非关键路径使用通用实现。 关键点回顾: SDL_XXX_INTRINSICS 宏仅表示编译器支持内联函数,运行时仍需 CPUInfo 检测; 宏检查必须用 #ifdef,避免未定义宏导致的编译错误; SDL 自动包含指令集头文件,无需手动引入; 优化代码需提供降级方案,保证程序兼容性。
2026年-3月-6日
293 阅读
0 评论
VisualFreeBasic编程文档
2026-3-6
SDL3_API分类参考_CPU信息(CategoryCPUInfo)
CPU信息子系统(CategoryCPUInfo) SDL 提供了一套 CPU 特性检测接口,主要用于判断系统是否支持各类 SIMD(单指令多数据)指令集,同时也能获取系统内存大小、逻辑 CPU 核心数等关键硬件信息。 CPU 指令集检测函数(如 SDL_HasSSE()、SDL_HasNEON())在所有平台上均可用——即使某些指令集对当前架构无意义(例如 ARM 处理器永远不支持 SSE,x86 处理器永远不支持 NEON),这些函数仍会存在,只是返回 false。 函数 SDL_GetCPUCacheLineSize:获取 CPU 缓存行大小(字节),优化内存访问对齐时使用 SDL_GetNumLogicalCPUCores:获取系统逻辑 CPU 核心数(超线程会被计入,如 8 核 16 线程返回 16) SDL_GetSIMDAlignment:获取 SIMD 指令集要求的内存对齐大小(字节),分配 SIMD 数据缓冲区时需满足该对齐 SDL_GetSystemPageSize:获取系统内存页大小(字节),内存分配/映射时的基础单位 SDL_GetSystemRAM:获取系统总物理内存大小(MB),用于判断硬件性能等级 SDL_HasAltiVec:检测 CPU 是否支持 PowerPC 架构的 AltiVec 指令集(返回非 0 表示支持) SDL_HasARMSIMD:检测 CPU 是否支持 ARM 架构的基础 SIMD 指令集(返回非 0 表示支持) SDL_HasAVX:检测 CPU 是否支持 x86 架构的 AVX 指令集(返回非 0 表示支持) SDL_HasAVX2:检测 CPU 是否支持 x86 架构的 AVX2 指令集(返回非 0 表示支持) SDL_HasAVX512F:检测 CPU 是否支持 x86 架构的 AVX-512F 基础指令集(返回非 0 表示支持) SDL_HasLASX:检测 CPU 是否支持龙芯架构的 LASX 指令集(返回非 0 表示支持) SDL_HasLSX:检测 CPU 是否支持龙芯架构的 LSX 指令集(返回非 0 表示支持) SDL_HasMMX:检测 CPU 是否支持 x86 架构的 MMX 指令集(返回非 0 表示支持) SDL_HasNEON:检测 CPU 是否支持 ARM 架构的 NEON 高级 SIMD 指令集(返回非 0 表示支持) SDL_HasSSE:检测 CPU 是否支持 x86 架构的 SSE 指令集(返回非 0 表示支持) SDL_HasSSE2:检测 CPU 是否支持 x86 架构的 SSE2 指令集(返回非 0 表示支持) SDL_HasSSE3:检测 CPU 是否支持 x86 架构的 SSE3 指令集(返回非 0 表示支持) SDL_HasSSE41:检测 CPU 是否支持 x86 架构的 SSE4.1 指令集(返回非 0 表示支持) SDL_HasSSE42:检测 CPU 是否支持 x86 架构的 SSE4.2 指令集(返回非 0 表示支持) 数据类型 (无) 结构体 (无) 枚举 (无) 宏 SDL_CACHELINE_SIZE:编译期宏,定义默认的 CPU 缓存行大小(通常为 64 字节,实际硬件可能不同,建议运行时用 SDL_GetCPUCacheLineSize 获取) FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充函数声明(FreeBASIC 绑定可能缺失) Declare Function SDL_GetCPUCacheLineSize CDecl () As Integer Declare Function SDL_GetNumLogicalCPUCores CDecl () As Integer Declare Function SDL_GetSIMDAlignment CDecl () As Integer Declare Function SDL_GetSystemPageSize CDecl () As Integer Declare Function SDL_GetSystemRAM CDecl () As Integer Declare Function SDL_HasAltiVec CDecl () As Integer Declare Function SDL_HasARMSIMD CDecl () As Integer Declare Function SDL_HasAVX CDecl () As Integer Declare Function SDL_HasAVX2 CDecl () As Integer Declare Function SDL_HasAVX512F CDecl () As Integer Declare Function SDL_HasLASX CDecl () As Integer Declare Function SDL_HasLSX CDecl () As Integer Declare Function SDL_HasMMX CDecl () As Integer Declare Function SDL_HasNEON CDecl () As Integer Declare Function SDL_HasSSE CDecl () As Integer Declare Function SDL_HasSSE2 CDecl () As Integer Declare Function SDL_HasSSE3 CDecl () As Integer Declare Function SDL_HasSSE41 CDecl () As Integer Declare Function SDL_HasSSE42 CDecl () As Integer ' 打印SIMD指令集支持情况 Sub PrintSIMDSupport() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【SIMD 指令集支持情况】") ' x86 架构指令集 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "MMX: %s", IIf(SDL_HasMMX(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SSE: %s", IIf(SDL_HasSSE(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SSE2: %s", IIf(SDL_HasSSE2(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SSE3: %s", IIf(SDL_HasSSE3(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SSE4.1: %s", IIf(SDL_HasSSE41(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SSE4.2: %s", IIf(SDL_HasSSE42(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "AVX: %s", IIf(SDL_HasAVX(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "AVX2: %s", IIf(SDL_HasAVX2(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "AVX512F:%s", IIf(SDL_HasAVX512F(), "支持", "不支持")) ' ARM 架构指令集 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "ARM SIMD: %s", IIf(SDL_HasARMSIMD(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "NEON: %s", IIf(SDL_HasNEON(), "支持", "不支持")) ' 其他架构指令集 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "AltiVec: %s", IIf(SDL_HasAltiVec(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "LSX: %s", IIf(SDL_HasLSX(), "支持", "不支持")) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "LASX: %s", IIf(SDL_HasLASX(), "支持", "不支持")) End Sub ' 打印系统/CPU硬件信息 Sub PrintSystemInfo() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【系统/CPU 硬件信息】") ' CPU 核心数 Dim As Integer coreCount = SDL_GetNumLogicalCPUCores() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "逻辑CPU核心数:%d", coreCount) ' 系统内存 Dim As Integer ramSize = SDL_GetSystemRAM() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "总物理内存:%d MB (%.2f GB)", ramSize, ramSize / 1024.0) ' 缓存行大小 Dim As Integer cacheLineSize = SDL_GetCPUCacheLineSize() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CPU缓存行大小:%d 字节 (编译期默认:%d 字节)", cacheLineSize, SDL_CACHELINE_SIZE) ' SIMD对齐要求 Dim As Integer simdAlign = SDL_GetSIMDAlignment() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SIMD内存对齐要求:%d 字节", simdAlign) ' 系统页大小 Dim As Integer pageSize = SDL_GetSystemPageSize() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "系统内存页大小:%d 字节", pageSize) End Sub ' 根据硬件特性调整程序配置 Sub AdjustAppConfig() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【根据硬件特性调整配置】") ' 根据内存大小设置纹理质量 Dim As Integer ramSize = SDL_GetSystemRAM() If (ramSize >= 16384) Then ' 16GB 及以上 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "内存充足,启用4K纹理质量") ElseIf (ramSize >= 8192) Then ' 8GB 及以上 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "内存中等,启用2K纹理质量") Else SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "内存较少,启用1080P纹理质量") End If ' 根据CPU核心数设置线程池大小 Dim As Integer coreCount = SDL_GetNumLogicalCPUCores() Dim As Integer threadCount = IIf(coreCount > 4, coreCount - 1, coreCount) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "设置线程池大小:%d 线程", threadCount) ' 根据SIMD指令集启用优化 If (SDL_HasAVX2() Or SDL_HasNEON()) Then SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "启用高级SIMD优化(图形渲染/数据处理)") ElseIf (SDL_HasSSE42()) Then SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "启用基础SIMD优化(数据处理)") Else SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "使用通用CPU指令集(无SIMD优化)") End If 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 ' 打印硬件信息 PrintSystemInfo() PrintSIMDSupport() ' 根据硬件调整配置 AdjustAppConfig() ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 SIMD 指令集检测规则: 所有指令集检测函数返回 非 0 表示支持,0 表示不支持; 跨架构检测时(如 x86 上检测 NEON),函数仍会返回 0,不会报错; 建议运行时动态检测,而非编译期判断——同一编译产物可能运行在不同指令集的 CPU 上。 内存对齐关键要点: SDL_GetSIMDAlignment() 返回的是当前 CPU SIMD 指令集要求的最小对齐大小(如 AVX2 要求 32 字节,NEON 要求 16 字节); 分配 SIMD 数据缓冲区时,需满足该对齐要求,否则可能导致程序崩溃或性能下降; SDL 提供 SDL_aligned_alloc/SDL_aligned_free 函数,可便捷分配对齐内存。 硬件信息的应用场景: 函数 典型应用场景 SDL_GetNumLogicalCPUCores 设置线程池大小、任务并行数 SDL_GetSystemRAM 调整内存占用、纹理/贴图质量等级 SDL_GetCPUCacheLineSize 优化内存访问模式(避免缓存行颠簸) SDL_GetSystemPageSize 内存映射(mmap)、大内存块分配 SDL_CACHELINE_SIZE 宏说明: 编译期默认值通常为 64 字节(主流 CPU 的缓存行大小); 实际硬件的缓存行大小可能不同(如部分嵌入式 CPU 为 32 字节),建议运行时用 SDL_GetCPUCacheLineSize 获取真实值。 总结 核心优势: 统一的跨平台硬件检测接口,无需适配不同架构的原生检测方法; 完整覆盖主流 SIMD 指令集(x86/ARM/龙芯/PowerPC),满足多架构优化需求; 硬件信息获取函数返回值直观,无需解析复杂的系统信息结构体; 所有函数均为纯查询操作,无副作用,调用成本极低。 使用建议: 游戏/高性能应用中,根据 SIMD 指令集动态启用优化代码路径(如 AVX2 优化的图形渲染、NEON 优化的音频处理); 根据 CPU 核心数动态调整线程池大小,避免过度并行导致的线程竞争; 根据系统内存大小分级调整资源加载策略(如大内存加载高清纹理,小内存加载压缩纹理); 分配 SIMD 数据缓冲区时,务必遵循 SDL_GetSIMDAlignment() 返回的对齐要求。 关键点回顾: SIMD 指令集检测函数跨平台可用,非目标架构返回 false,无需额外判空; SDL_GetSIMDAlignment() 是 SIMD 内存分配的关键,需满足对齐要求; 硬件信息可用于动态调整程序配置,实现「低配能跑、高配优化」的自适应效果。
2026年-3月-6日
219 阅读
0 评论
VisualFreeBasic编程文档
2026-3-6
SDL3_API分类参考_平台(CategoryPlatform)
平台子系统(CategoryPlatform) SDL 提供了在编译期和运行期识别应用程序运行平台的方法,帮助开发者实现跨平台的差异化逻辑处理。 函数 SDL_GetPlatform:运行时获取当前平台名称(返回字符串指针,如 "Windows"、"Linux"、"macOS" 等,无需手动释放) 数据类型 (无) 结构体 (无) 枚举 (无) 宏 SDL_PLATFORM_3DS:编译期宏,标识任天堂 3DS 平台 SDL_PLATFORM_AIX:编译期宏,标识 IBM AIX 系统 SDL_PLATFORM_ANDROID:编译期宏,标识 Android 平台 SDL_PLATFORM_APPLE:编译期宏,标识苹果系平台(包含 macOS/iOS/tvOS 等) SDL_PLATFORM_BSDI:编译期宏,标识 BSDI 系统 SDL_PLATFORM_CYGWIN:编译期宏,标识 Cygwin 环境(Windows 下的类 Unix 层) SDL_PLATFORM_EMSCRIPTEN:编译期宏,标识 Emscripten 编译的 WASM/HTML5 平台 SDL_PLATFORM_FREEBSD:编译期宏,标识 FreeBSD 系统 SDL_PLATFORM_GDK:编译期宏,标识 Xbox GDK 开发套件平台 SDL_PLATFORM_HAIKU:编译期宏,标识 Haiku 操作系统 SDL_PLATFORM_HPUX:编译期宏,标识 HP-UX 系统 SDL_PLATFORM_HURD:编译期宏,标识 GNU Hurd 系统 SDL_PLATFORM_IOS:编译期宏,标识 iOS 平台 SDL_PLATFORM_IRIX:编译期宏,标识 SGI IRIX 系统 SDL_PLATFORM_LINUX:编译期宏,标识 Linux 系统 SDL_PLATFORM_MACOS:编译期宏,标识 macOS 系统 SDL_PLATFORM_NETBSD:编译期宏,标识 NetBSD 系统 SDL_PLATFORM_NGAGE:编译期宏,标识诺基亚 N-Gage 平台 SDL_PLATFORM_OPENBSD:编译期宏,标识 OpenBSD 系统 SDL_PLATFORM_OS2:编译期宏,标识 OS/2 系统 SDL_PLATFORM_OSF:编译期宏,标识 OSF/1 系统 SDL_PLATFORM_PS2:编译期宏,标识索尼 PS2 平台 SDL_PLATFORM_PSP:编译期宏,标识索尼 PSP 平台 SDL_PLATFORM_QNXNTO:编译期宏,标识 QNX Neutrino 系统 SDL_PLATFORM_RISCOS:编译期宏,标识 RISC OS 系统 SDL_PLATFORM_SOLARIS:编译期宏,标识 Solaris 系统 SDL_PLATFORM_TVOS:编译期宏,标识 tvOS 平台(苹果电视) SDL_PLATFORM_UNIX:编译期宏,标识通用 Unix 类系统 SDL_PLATFORM_VISIONOS:编译期宏,标识 visionOS 平台(苹果 Vision Pro) SDL_PLATFORM_VITA:编译期宏,标识索尼 PS Vita 平台 SDL_PLATFORM_WIN32:编译期宏,标识 32/64 位 Windows 平台(兼容 Win32/Win64) SDL_PLATFORM_WINDOWS:编译期宏,标识 Windows 平台(与 SDL_PLATFORM_WIN32 等价) SDL_PLATFORM_WINGDK:编译期宏,标识 Windows GDK 平台 SDL_PLATFORM_XBOXONE:编译期宏,标识 Xbox One 平台 SDL_PLATFORM_XBOXSERIES:编译期宏,标识 Xbox Series X/S 平台 SDL_WINAPI_FAMILY_PHONE:编译期宏,标识 Windows Phone 平台 FreeBASIC 示例代码 ' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库) #Include "SDL.bi" ' 补充函数声明(FreeBASIC 绑定可能缺失) Declare Function SDL_GetPlatform CDecl () As ZString Ptr ' 编译期平台判断示例(预处理指令) #If Defined(SDL_PLATFORM_WINDOWS) #Define PLATFORM_NAME "Windows" #Elif Defined(SDL_PLATFORM_LINUX) #Define PLATFORM_NAME "Linux" #Elif Defined(SDL_PLATFORM_MACOS) #Define PLATFORM_NAME "macOS" #Elif Defined(SDL_PLATFORM_ANDROID) #Define PLATFORM_NAME "Android" #Elif Defined(SDL_PLATFORM_IOS) #Define PLATFORM_NAME "iOS" #Elif Defined(SDL_PLATFORM_EMSCRIPTEN) #Define PLATFORM_NAME "Emscripten (WASM/HTML5)" #Else #Define PLATFORM_NAME "Unknown Platform" #EndIf ' 运行期平台处理逻辑 Sub HandlePlatformSpecificLogic() Dim As ZString Ptr runtimePlatform = SDL_GetPlatform() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "运行期检测到平台:%s", *runtimePlatform) ' 根据运行期平台执行差异化逻辑 Select Case *runtimePlatform Case "Windows" SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "执行 Windows 专属逻辑:适配注册表、路径分隔符为 \") ' 示例:Windows 下的文件路径处理、注册表访问等 Case "Linux" SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "执行 Linux 专属逻辑:适配 /home 目录、权限管理") ' 示例:Linux 下的系统配置文件读取、权限检查等 Case "Mac OS X", "macOS" SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "执行 macOS 专属逻辑:适配 ~/Library 目录、沙盒权限") ' 示例:macOS 下的应用沙盒、Finder 集成等 Case "Android" SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "执行 Android 专属逻辑:适配 APK 资源、JNI 调用") ' 示例:Android 下的权限申请、JNI 交互等 Case "iOS" SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "执行 iOS 专属逻辑:适配沙盒、应用内购买") ' 示例:iOS 下的沙盒文件访问、IAP 处理等 Case Else SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "未适配的平台:%s,使用通用逻辑", *runtimePlatform) End Select 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 ' 打印编译期平台信息 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "编译期检测到平台:%s", PLATFORM_NAME) ' 执行运行期平台逻辑 HandlePlatformSpecificLogic() ' 清理 SDL SDL_Quit() SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出") End Sub ' 运行主程序 Main() 核心知识点补充 编译期 vs 运行期平台检测: *编译期宏(SDLPLATFORM)**:在代码编译时确定平台,用于条件编译(如不同平台的代码分支、头文件引入); 运行期函数(SDL_GetPlatform):程序运行时获取实际平台名称,适用于「同一编译产物运行在多平台」的场景(如跨平台打包的应用、Emscripten 编译的 WASM)。 SDL_GetPlatform 返回值规范: 平台 返回字符串 Windows(32/64位) "Windows" Linux "Linux" macOS "Mac OS X" / "macOS" Android "Android" iOS "iOS" Emscripten "Emscripten" FreeBSD "FreeBSD" Xbox "Xbox One" / "Xbox Series X/S" 跨平台开发最佳实践: 编译期宏用于「不可变的平台差异」(如头文件、API 声明); 运行期检测用于「可变的运行环境」(如同一二进制包运行在不同 Linux 发行版); 优先使用 SDL 提供的跨平台 API 封装,减少直接的平台判断; 平台判断逻辑集中管理,避免散落在代码各处。 总结 核心优势: 同时支持编译期和运行期平台检测,覆盖不同跨平台开发场景; 统一的平台标识体系,避免开发者记忆不同平台的原生宏定义; SDL_GetPlatform 返回的字符串格式稳定,便于运行期逻辑判断; 编译期宏覆盖主流平台(PC、移动端、游戏主机、嵌入式),满足多平台开发需求。 使用建议: 编译期:用 #If Defined(SDL_PLATFORM_XXX) 做条件编译,隔离平台专属代码; 运行期:用 SDL_GetPlatform() 获取平台名称,执行差异化的运行逻辑; 避免过度依赖平台判断,优先使用 SDL 封装的跨平台接口(如文件路径、输入输出); 对 SDL 未覆盖的小众平台,可结合原生宏(如 __linux__、_WIN32)补充判断。 关键点回顾: SDL_GetPlatform() 是运行期平台检测的核心函数,返回值无需手动释放; 编译期宏(SDLPLATFORM*)用于条件编译,区分不同平台的编译逻辑; 跨平台开发应优先使用 SDL 封装的 API,仅在必要时做平台差异化处理。
2026年-3月-6日
217 阅读
0 评论
VisualFreeBasic编程文档
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日
308 阅读
0 评论
VisualFreeBasic编程文档
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日
233 阅读
0 评论
VisualFreeBasic编程文档
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日
220 阅读
0 评论
VisualFreeBasic编程文档
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日
255 阅读
0 评论
VisualFreeBasic编程文档
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日
260 阅读
0 评论
VisualFreeBasic编程文档
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日
222 阅读
0 评论
VisualFreeBasic编程文档
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日
207 阅读
0 评论
VisualFreeBasic编程文档
1
2
3
4
5