SDL中文论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 8062|回复: 0
打印 上一主题 下一主题

[Discuss] SDL 1.2到2.0升级指南

[复制链接]

187

主题

346

帖子

2450

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2450
跳转到指定楼层
楼主
发表于 2016-5-23 07:51:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 ancientcc 于 2016-7-5 09:41 编辑

英文原文:SDL 1.2 to 2.0 Migration Guide


一、概述
  1.1 新特色
  1.2 获得更多信息
二、从1.2升级到2.0
  2.1 一些事实
  2.2 图像
    2.2.1 使用新图像API
    2.2.2 程序只是渲染全屏尺寸的帧
    2.2.3 程序要块移若干图面到屏幕
    2.2.4 包括了以上两种情况的程序
    2.2.5 其它的渲染API注意事项
  2.3 OpenGL
  2.4 输入
  2.5 事件



一、概述
  经过数年开发,正式发布SDL 2.0!
  我们相当确信,那些使用SDL 1.2的应用程序应该尽快升级到2.0。为让升级看去不那么可怕,此文档给出了升级的大致流程。通过这个文档,你会发现升级没像想象那么难,它往往就是用同功能函数代替旧函数,就是函数名不同而已,或撤消一些为弥补1.2不足而加上的补丁代码。
  你会非常希望使用2.0,它增加了新功能,即使是已有功能也是1.2基础上结合多年经验的改进版。此文档不论及SDL2中新东西,——只说那些你现在立即需要知道的。一旦移植代码成功,根据你自个须要就可向程序添加新功能。

1.1 新特色
  以下列举SDL 2.0一些新特色。
  • Full 3D hardware acceleration
  • Support for OpenGL 3.0+ in various profiles (core, compatibility, debug, robust, etc)
  • 支持OpenGL ES
  • 支持多窗口
  • 支持多显示器
  • 支持多音频设备
  • 支持Android、iOS
  • Simple 2D rendering API that can use Direct3D, OpenGL, OpenGL ES, or software rendering behind the scenes
  • Force Feedback available on Windows, Mac OS X and Linux
  • XInput and XAudio2 support for Windows
  • Atomic operations
  • Power management (exposes battery life remaining, etc)
  • Shaped windows
  • 32-bit audio (int and float)
  • Simplified Game Controller API (the Joystick API is still here, too!)
  • Touch support (multitouch, gestures, etc)
  • Better fullscreen support
  • Better keyboard support (scancodes vs keycodes, etc).
  • Message boxes
  • Clipboard support
  • Basic Drag'n'Drop support
  • Proper unicode input and IME support
  • A really powerful assert macro
  • 开源协议上放弃LGPL,改而使用zlib
  • Lots of old annoyances from 1.2 are gone
  • Many other things!

1.2 获得更多信息
  SDL维基百科
  源码包附带的测试代码(<SDL2-2.0.x>/src/test目录)
  SDL邮件组(sdl@lists.libsdl.org)  

二、从1.2升级到2.0
2.1 一些事实
  SDL2中不存在为了和1.2兼容而特意存在的层。如果2.0要改一个API,它的修改依据往往就是怎么高效怎么改,不大会去考虑兼容性。如果你只是简单把1.2头文件替换为2.0,那一般不可能通过编译。此文档希望让你知道2.0改了哪些重要细节,免得走过多弯路。

  SDL_main没了!Well, okay, there is, and it now does what it was always meant to: be a small piece of code that hides the difference between main() and WinMain() on Windows. There's no initialization code in it, and it's completely optional. This means you can use SDL without it taking over your mainline, which is nice for plugins that use SDL, or scripting languages with an SDL module. All the stuff you'd want the 1.2 SDL_main for is now in SDL_Init() where it belongs.

  There's no SDL parachute anymore. What 1.2 called SDL_INIT_NOPARACHUTE is a default and only state now. This would cause problems if something other than the main thread crashed, and it would interfere with apps setting up their own signal/exception handlers. On the downside, some platforms don't clean up fullscreen video well when crashing. You should install your own crash handler, or call SDL_Quit() in an atexit() function or whatnot if this is a concern. Note that on Unix platforms, SDL still catches SIGINT and maps it to an SDL_QUIT event.

2.2 图像
2.2.1 使用新图像API
  最重大改变体现在图像API。为适应现代硬件和操作系统,上世纪90年代创立的API显得不堪重负,不得不做出重大修改。
  不要担心改变会失去实用性,一旦你理解了改变具体内容,你会非常高兴SDL做了这些改变,并会把它们用在你的程序中。稍后我们会讨论它们。
  有个好消息:那些使用OpenGL的程序可省去很多事,只须改去调用对应SDL函数,你的程序就能很好工作。
  在渲染2D图像上SDL 1.2引入一个概念:图面,它其实是个像素内存块,同时主屏幕也是图面。为提高图面间复制效率,SDL提供了称为“块移”的操作,在复制时,块移会根据不同格式自动执行像素转换。这些操作是在系统内存执行,不在显存上。SDL 2.0改了,你可享受硬件加速,修改后的API会反映这个加速。
  如果你写的是2D程序,会使用以下要说的三种方式中的一种去渲染,但在深入渲染方式前让说些在它之前要被执行的东西。
  SDL_SetVideoMode(),全完没有了!SDL2.0要支持多窗口,而SDL_SetVideoMode基于设计理念是单窗口,只好废弃它。
  SDL1.2时你可能使用了类似以下代码。
  1. SDL_WM_SetCaption("My Game Window", "game");
  2. SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_FULLSCREEN | SDL_OPENGL);
复制代码

  改为
  1. SDL_Window *screen = SDL_CreateWindow("My Game Window",
  2.                           SDL_WINDOWPOS_UNDEFINED,
  3.                           SDL_WINDOWPOS_UNDEFINED,
  4.                           640, 480,
  5.                           SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
复制代码

  对比改动,改动后代码和1.2还是很像的,不同的是你想创建多少个窗口(SDL_Window)不受限了。为让每个窗口有自已标题,SDL_WM_SetCaption也不能要了,取而代之是使用SDL_SetWindowTitle去修改各窗口标题,除了标题,你还要为每个窗口指定位置(不关心放哪儿可用SDL_WINDOWNPOS_UNDEFINED,固定放中央的话可用SDL_WINDOWPOS_CENTERED)。
  SDL2提供一个新功能:你可为每窗口挂接各自显示器。SDL2提供了管理多显示器功能。
  为平滑显示,不论是SDL1.2还是SDL2.0,它们都使用双缓冲机制,准备好的窗口是先放在后台,需要时再调到前台去显示。SDL2依旧存在图面(SDL_Surface),但正如你可能猜到的,SDL2加了它的替代品,是的,这替代品叫纹理(SDL_Texture)。在2.0,图面将总是创建在系统内存,它的操作一定要通过CPU,为整系统效率考虑,程序应该尽可能使用GPU。SDL2 has a new rendering API. It's meant for use by simple 2D games, but most notably, it's meant to get all that software rendering into video RAM and onto the GPU. And even if you just want to use it to get your software renderer's work to the screen, it brings some very nice benefits: if possible, it will use OpenGL or Direct3D behind the scenes, which means you'll get faster blits, a working Steam Overlay, and scaling for free.
  The setup looks like this.
  之前有说到SDL_CreateWindow()要代替SDL_SetVideoMode(),那要如何处理分辨率。如果你的程序硬编码为640x480,但使用的显示器不是这分辨率,那就不能全屏只能是窗口模式,游戏看起来更像是贴邮票。为让强制全屏SDL2下提供了种更好办法。
  SDL_ListModes已没了,作为它的替代实现,SDL2建议是使用一个循环,调用SDL_GetNumDisplayModes()次SDL_GetDisplayMode。要把640x480强制为全屏可使用新方法:全屏桌面。它告诉SDL“把整个窗口让我显示,而且不要改变我希望的分辨率”,对假设的640x480游戏,代码看去就是以下样子。
  1. SDL_Window *sdlWindow = SDL_CreateWindow(title,
  2.                              SDL_WINDOWPOS_UNDEFINED,
  3.                              SDL_WINDOWPOS_UNDEFINED,
  4.                              0, 0,
  5.                              SDL_WINDOW_FULLSCREEN_DESKTOP);
复制代码

  Notice how we didn't specify 640 or 480...fullscreen desktop gives you the whole display and ignores any dimensions you specify. The game window should come up immediately, without waiting for the monitor to click into a new resolution, and we'll be using the GPU to scale to the desktop size, which tends to be faster and cleaner-looking than if an LCD is faking a lower resolution. Added bonus: none of your background windows are resizing themselves right now.

  准备好窗口,接下须要一个渲染上下文。
  1. SDL_Renderer *renderer = SDL_CreateRenderer(sdlWindow, -1, 0);
复制代码

  渲染上下文隐藏了SDL是如何把窗口画到屏幕的具体细节。根据系统不同,SDL会使用下面方式的一种,Direct3D、OpenGL、OpenGL ES或软件模拟。选择哪种由SDL内部决定,你无法修改,regardless of what SDL chooses (although you are welcome to force one kind of renderer or another). If you want to attempt to force sync-to-vblank to reduce tearing, you can use SDL_RENDERER_PRESENTVSYNC instead of zero for the third parameter. You shouldn't create a window with the SDL_WINDOW_OPENGL flag here. If SDL_CreateRenderer() decides it wants to use OpenGL, it'll update the window appropriately for you.

  到现在你大概知道创建如何个过程,为简单你还可使用SDL_CreateWindowAndRenderer()一次性实现创建窗口、创建渲染上下文。
  1. SDL_Window *sdlWindow;
  2. SDL_Renderer *sdlRenderer;
  3. SDL_CreateWindowAndRenderer(0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP, &sdlWindow, &sdlRenderer);
复制代码

  假设上面的创建函数都成功执行,接下是该向屏幕画图了。让首先实现画黑屏。
  1. SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255);
  2. SDL_RenderClear(sdlRenderer);
  3. SDL_RenderPresent(sdlRenderer);
复制代码

  正如你认为,代码首先置黑色画刷(r、g、b都是0,不透明),然后用黑色画刷清空整窗口,最后把清空后的窗口调到前台去显示。对于调去前台操作,SDL1.2使用SDL_UpdateRect()或SDL_Flip(),SDL2.0改为使用SDL_RenderPresent()。

  One more general thing to set up here. Since we're using SDL_WINDOW_FULLSCREEN_DESKTOP, we don't actually know how much screen we've got to draw to. Fortunately, we don't have to know. One of the nice things about 1.2 is that you could say "I want a 640x480 window and I don't care how you get it done," even if getting it done meant centering the window in a larger resolution on behalf of your application.

  For 2.0, the render API lets you do this...
  1. SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");  // make the scaled rendering look smoother.
  2. SDL_RenderSetLogicalSize(sdlRenderer, 640, 480);
复制代码


  ...and it will do the right thing for you. This is nice in that you can change the logical rendering size to achieve various effects, but the primary use is this: instead of trying to make the system work with your rendering size, we can now make your rendering size work with the system. On my 1920x1200 monitor, this app thinks it's talking to a 640x480 resolution now, but SDL is using the GPU to scale it up to use all those pixels. Note that 640x480 and 1920x1200 aren't the same aspect ratio: SDL takes care of that, too, scaling as much as possible and letterboxing the difference.
  介绍了基础性的,接下让进入具体升级。

2.2.2 程序只是渲染全屏尺寸的帧
  一些带有教程性质程序会自已画每个像素,然后集合这些像素到一全屏尺寸的帧,最后用一个全屏尺寸块移渲染到前台,“Doom”、“Duke Nukem 3D”就是这样例子。
  针对这种情况,你须要创建一个表示屏幕的纹理。以下代码创建一个屏幕尺寸是640x480的纹理。
  1. sdlTexture = SDL_CreateTexture(sdlRenderer,
  2.                                SDL_PIXELFORMAT_ARGB8888,
  3.                                SDL_TEXTUREACCESS_STREAMING,
  4.                                640, 480);
复制代码

  它会在GPU上创建一个纹理。为完成渲染,接下逻辑是1)填充纹理;2)把纹理渲染到后备帧;3)把后备帧调到前台。SDL_TEXTUREACCESS_STREAMING告诉SDL,该纹理内容会频繁变化。
  在SDL 1.2,把主图面调到前台显示一般通过SDL_Flip(),而主图面则来自于SDL_SetVideoMode()。现在你要改为在内存创建一图面,或用malloc()创建像素数据块,然后把要显示的内容填充向主图面或像素块。
  1. extern Uint32 *myPixels;  // maybe this is a surface->pixels, or a malloc()'d buffer, or whatever.
复制代码

  填充完主图面或像素块后,用以下代码把它上传到纹理。
  1. SDL_UpdateTexture(sdlTexture, NULL, myPixels, 640 * sizeof (Uint32));
复制代码

  它会把像素数据放入GPU内存。第二个参数NULL指示你是要刷新整个屏幕。第四个参数是一行像素占用字节数,这里每像素是RGBA格式,占用四字节,所以一行占用字节数是640乘以4。
  接下是把纹理渲染到屏幕(前台)。
  1. SDL_RenderClear(sdlRenderer);
  2. SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
  3. SDL_RenderPresent(sdlRenderer);
复制代码

  SDL_RenderClear擦空纹理,SDL_RenderCopy()把你想要的纹理渲染到后备帧,SDL_RenderPresent()执行双缓冲机制中翻转,把后备帧调到前台。

2.2.3 程序要块移若干图面到屏幕
  有些SDL 1.2游戏会从硬盘加载若干图像文件进而形成若干图面(SDL_Surface),每张图像形成图面操作一次就够了,但接下来块移到一主图面时,则要按要求一次又一次地混合,但每次混合都会保持不修改图像图面。这种情况下,你可把每个由图像直接形成的图面认为是“精灵”,形成主图面过程就是把众多“精灵”一次又一次块移。
  类似之前说的创建一个表示屏幕的纹理,你可为每图像单独形成一纹理(相比于图面,纹理是位在GPU内存)。
  1. sdlTexture = SDL_CreateTexture(sdlRenderer,
  2.                                SDL_PIXELFORMAT_ARGB8888,
  3.                                SDL_TEXTUREACCESS_STATIC,
  4.                                myWidth, myHeight);
复制代码
  以上代码用于形成一纹理。参数用了SDL_TEXTUREACCESS_STATIC,因为在此过程中该纹理只会被刷新一次,即从磁盘读出图像形成纹理时。为简化操作,你也可使用另一方法。
  1. sdlTexture 与= SDL_CreateTextureFromSurface(sdlRenderer, mySurface);
复制代码

  在逻辑上,你按通常方法生成图面,并处理图面,最后用这函数由图面生成纹理。一旦由图面生成了纹理,你就可释放图面占用的资源了。
  在这种情形中,SDL 1.2是创建若干图面,然后把它们按要求一次又一次混合向主图面,最后由SDL_Flip()调到前台。到SDL 2.0后,要改为创建若干纹理,然后按要求用SDL_RenderCopy一次又一次混合向后备帧,最后用SDL_RenderPresennt()把后备帧调到前台,从而显示到屏幕。It's that simple. If these textures never need modification, you might find your framerate has just gone through the roof, too.

2.2.4 包括了以上两种情况的程序
  既要块移图面到后备帧、又要直接修改后备帧中像素数据,这情况就有点复杂了。要清楚一个事实,从纹理读回数据是挺费力的,你要尽量避免掉。对这种程序,推荐方法是先在内存进行处理,在内存形成结果帧,然后把这帧一次性刷向后备纹理。
  好消息:1.2中和图面相关的绝大多数API在2.0依旧存存在。因此你要做的只是按类似以下代码进行修改。
  1. SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, 0);
复制代码

改为
  1. // if all this hex scares you, check out SDL_PixelFormatEnumToMasks()!
  2. SDL_Surface *screen = SDL_CreateRGBSurface(0, 640, 480, 32,
  3.                                         0x00FF0000,
  4.                                         0x0000FF00,
  5.                                         0x000000FF,
  6.                                         0xFF000000);
  7. SDL_Texture *sdlTexture = SDL_CreateTexture(sdlRenderer,
  8.                                             SDL_PIXELFORMAT_ARGB8888,
  9.                                             SDL_TEXTUREACCESS_STREAMING,
  10.                                             640, 480);
复制代码
  接下来就是按自要求把“精灵”块移到这图面、或在这图面相应位置直接修改像素数据,最后形成你希望的图面。一旦你形成了希望图面,你就可以按照情形一的方式进行渲染。
  1. SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch);
  2. SDL_RenderClear(sdlRenderer);
  3. SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
  4. SDL_RenderPresent(sdlRenderer);
复制代码

  注意,创建纹理会较耗CPU,而且因为它是位在显存,往往也不能创建得太多,因而不要每次渲染时都调用一次SDL_CreateTextureFromSurface。只须创建一次纹理、图面,接下你只要复用它们就行了。
  渲染API还有不少新加内容,它们有可能代替你现在代码,像缩放、画线、等等。If you are reading this section because you have simple needs beyond blitting surfaces, you might be able to stop poking individual pixels and move everything onto the GPU, which will give your program a significant speed boost and probably simplify your code greatly

2.2.5  其它的渲染API注意事项
  • 颜色透明度。SDL_Color包含第四字段:alpha分量。在1.2版本,这字段叫unsed,在2.0该字段改为表示透明度。
  • Alpha混合。不要再使用SDL_SetAlpha(),改为使用SDL_SetSurfaceAlphaMod、SDL_SetTextureAlphaMod。SDL_SetSurfaceBlendMode()、SDL_SetTextureBlendMode()不能禁用Alpha混合。
  • Colorkey: When calling SDL_SetColorKey(), you should pass SDL_TRUE instead of SDL_SRCCOLORKEY.
  • 模颜色。一些渲染环境支持一个对颜色整体调整,像srcC = srcC * color,细节参考SDL_SetTextureColorMod()。

2.3 OpenGL
  If you were already using OpenGL directly, your migration is pretty simple. Change your SDL_SetVideoMode() call to SDL_CreateWindow() followed by SDL_GL_CreateContext(), and your SDL_GL_SwapBuffers() call to SDL_GL_SwapWindow(window). All the actual calls into the GL are exactly the same.
  If you had used SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, x), this has changed. There is now an SDL_GL_SetSwapInterval(x) call, so you can change this on an existing GL context.
  Note that SDL 2.0 can toggle windowed/fullscreen and back with OpenGL windows without losing the GL context (hooray!). Use SDL_SetWindowFullscreen() for this.

2.4 输入
  好消息是SDL 2.0已支持Unicode输入,坏消息是你得对代码做些改动。
  在SDL之前版本,为接收ASCII外字符,程序需调用SDL_EnableUNICODE(1)。即使这么做了,也不能很好处理字符输入。
  事实证明,在那版本要做到国际化非常困难。
  SDL 2.0对此做了重大改动,SDL_EnableUNICODE没有了,同时去掉的还有SDL_Keysym中的unicode字段。从SDL_KEYDOWN你有可能不能准确知道按下的是什么字符,更好的处理方式是把SDL_KEYDOWN视作101个按钮的游戏杆,得用其它方法去获知按下什么键。
  新增SDL_TEXTIPUT事件。一旦玩家输入字符,就会触发该事件。输入或是来自一次按键输入,或是来自IME(由多次按键组合而成的输入,像通过中文输入法)。SDL_TEXTINPUT返回的是最后输出,这意味着在IME输入时,通过它得到的是中文输入法输出的最后形成的汉字(串)。另外,SDL_TEXTINPUT返回的字符都是UTF-8格式。
  SDL_KEYDOWN可处理一次性按键输入,在这种情况下,SDK_KEYDOWN会形成两种码:键盘码、扫描码。
  扫描码是和键盘布局无关。
  键盘码和特定键盘布局相关。
  举个例子,你在US QWERTY键盘按下CAPS Lock和S键,SDK_KEYDOWN会报出扫描码SDL_SCANCODE_S、键盘码SDLK_S。或改在Dvorak键盘按下CAPS Lock和S键,这时改为报出扫描码SDL_SCANCODE_S、键盘码SDLK_O。(译者注:为简单,可理解为SDL把US QWERTY认为是标准键盘,扫描码就是根据这键盘得出的。)
  不论是键盘码还是扫描码,它们都是32位表示,这意味可表示2^32的范围值。There's no SDLK_LAST anymore. If your program had a lookup table of SDLK_LAST elements, to map between SDL keys and whatever your application wanted internally, that's no longer feasible. Use a hash table instead. A std::map will do. If you're mapping scancodes instead of keycodes, there's SDL_NUM_SCANCODES, which you can use for array bounds. It's 512 at the moment.
  SDL_Keymode代替了SDLMode,“META”键(Windows键)已改叫“GUI”键。  
  SDL_GetKeyState改名叫SDL_GetKeyboardState,它返回以扫描码(SDL_SCANCODE_*)为索引的状态数组。

  现在说说鼠标输入。
  第一个改变,差不也就是全部了,滚动鼠标不再归入按下事件(SDL_MOUSEBUTTONDOWN)。把滚动归入按下是个历史遗留错误,SDL 2.0把它修正了。SDL 2.0新加一事件:SDL_MOUSEWHEEL,用它表示鼠标滚动,滚动包括水平、垂直两方向,在一些平台,还把两手指滚动也归入SDL_MOUSEWHEEL。玩家滚动鼠标时,程序将不会再收到SDL_MOUSEBUTTONDOWN。SDL 1.2为表示滚动用了“按钮值是4(水平)、5(垂直)的SDL_MOUSEBUTTONDOWN”,2.0开始这4、5也和1(左键)、2(中键)、3(右键)一样表示真实按钮。
  If your game needed to roll the mouse in one direction forever, for example to let a player in an FPS to spin around without the mouse hitting the edge of the screen and stopping, you probably hid the mouse cursor and grabbed input:
  1. SDL_ShowCursor(0);
  2. SDL_WM_GrabInput(SDL_GRAB_ON);
复制代码

  In SDL2, this works slightly differently. You call...
  1. SDL_SetRelativeMouseMode(SDL_TRUE);
复制代码

  ...and SDL does the rest.

2.5 事件
  如果成功执行,SDL_PushEvent()返回1,而不是之前的0。
  使用范围(两个变量)来表示事件掩码
  1. SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN));
复制代码

  改为
  1. SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONDOWN);
复制代码




后面章节请参阅原文。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|丽谷软件|libsdl.cn

GMT+8, 2024-5-9 09:11 , Processed in 0.057314 second(s), 19 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表