使用SDL开发PSP游戏时需要注意的地方

近在使用SDL2开发PSP游戏,遇到了一些奇奇怪怪的小问题,在这里分享一下。

首先是SDK相关的问题。我使用的是PSPDEV提供的一整套工具链

psp-pacman是一个好东西,我们可以直接安装一些编译起来非常麻烦的依赖项。不过它提供的pacman需要libgpgme.so.11,这在Arch Linux上早就已经不复存在了,所以我直接使用了系统提供的pacman

想使用setlocale?当然可以!不过第二个参数只能是"C""POSIX",这是因为newlib(PSPDEV使用的C库,通常用于嵌入式系统)默认只提供了最小化的实现。这也同时意味着类似mbtowc等依赖区域设置的函数用了相当于没用。

为了能够同时编译PSP应用,CMakeLists.txt需要修改一下。

加载依赖包的时候应该这样做:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
if(NOT PSP)
    find_package(SDL2 REQUIRED)
    find_package(SDL2_image REQUIRED)
    find_package(SDL2_mixer REQUIRED)
    find_package(SDL2_ttf REQUIRED)
else()
    include(FindPkgConfig)
    pkg_search_module(SDL2 REQUIRED sdl2) # 这里是小写
    pkg_search_module(SDL2_IMAGE REQUIRED SDL2_image)
    pkg_search_module(SDL2_MIXER REQUIRED SDL2_mixer)
    pkg_search_module(SDL2_TTF REQUIRED SDL2_ttf)
endif()

链接的时候应该这样做:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
if(NOT PSP)
    target_link_libraries(${PROJECT_NAME}
        SDL2::SDL2 SDL2_image::SDL2_image
        SDL2_mixer::SDL2_mixer SDL2_ttf::SDL2_ttf
        ...
    )
else()
    target_include_directories(${PROJECT_NAME} PRIVATE
        ${SDL2_INCLUDE_DIRS}
        ${SDL2_IMAGE_INCLUDE_DIRS}
        ${SDL2_MIXER_INCLUDE_DIRS}
        ${SDL2_TTF_INCLUDE_DIRS}
    )
    target_link_libraries(${PROJECT_NAME} PRIVATE
        ${SDL2_LIBRARIES}
        ${SDL2_IMAGE_LIBRARIES}
        ${SDL2_MIXER_LIBRARIES}
        ${SDL2_TTF_LIBRARIES}
        ...
    )
    create_pbp_file(
        TARGET ${PROJECT_NAME} # 只有这一项是必须的
        ICON_PATH NULL # 大小为 144x82 的 .png 文件
        BACKGROUND_PATH NULL # 大小为 480x272 的 .png 文件
        PREVIEW_PATH NULL # 大小为 480x272 的 .png 文件
        TITLE ${PROJECT_NAME}
        VERSION 1.0
    )
endif()

一定要记得是使用psp-cmake而不是cmake


接下来就是有关PSP的一系列问题,这些问题主要归功于其孱弱的硬件。

PSP最大只支持大小为512×512的贴图。这个限制可能在各种意想不到的地方坑你一下,比如使用TTF_Render*_Wrapped的时候,wrapLength参数必须小于512(屏幕宽度才480呢)。

PSP的CPU(MIPS32)不支持SIMD指令。好巧不巧的是SDL_ttf又需要SIMD指令来加速,导致绘制文字的时候非常慢。所以我给出了以下解决方案:

  • 从贴图加载并绘制文字
    • 优点:真的非常快;缺点:大概率只能绘制英文字符、半角符号及数字
  • 使用libintrafont加载PGF字体并绘制文字
    • 优点:至少比SDL_ttf快;缺点:只适用于PSP而且PGF格式几乎失传
  • 使用SDL_ttf绘制文字并缓存
    • 优点:没有优点;缺点:全是缺点

一个Noto CJK字体的大小就有60MB了,然而PSP的最大内存才64MB(其中PSP1000只有32MB),一个字体就占用了90%以上的内存,那别的事情就可以不要干了。

不过Vita并没有这方面的问题,它支持大小为4096×4096的贴图(虽然足够大,但仍然比PC上的16384×16384小不少),内存大小达到512MB,且其使用的ARM CPU支持Neon。完美解决PSP的一系列痛点。