简体   繁体   English

SDL2:渲染引擎​​设计

[英]SDL2: Render engine design

I am creating simple game rendering engine based on SDL2 library. 我正在创建基于SDL2库的简单游戏渲染引擎。 My design looks similar to this ASCII graph. 我的设计类似于此ASCII图。

DrawTarget     RenderTarget
    |                |
    |                |
    +---> Window <---+

DrawTarget is an abstract class for draw target suitable to SDL_Surface blitting. DrawTarget是适用于SDL_Surface DrawTarget的绘画目标的抽象类。 RenderTarget is and abstract class for render target suitable to SDL_Texture. RenderTarget是适合SDL_Texture的呈现目标的抽象类。 Window is derivated class managing SDL_Window, SDL_Renderer and all draw and rendering functions. Window是派生类,用于管理SDL_Window,SDL_Renderer和所有绘制和渲染功能。

Renderable
  |  |  |
  |  |  |
  |  |  +-> Player
  |  |         ^
  |  |         |
  |  +----> Animation
  |            ^
  |            |
  +-------> Texture

Renderable is everything, which could be rendered to the screen or another RenderTarget. Renderable是所有可以渲染到屏幕或另一个RenderTarget的东西。 Texture is SDL_Texture implementation. Texture是SDL_Texture实现。 Texture should not know its own position. 纹理不应该知道自己的位置。 Animation has a private Texture member allowing set up of animation frames. Animation有一个私有的Texture成员,允许设置动画帧。 Animation itself should not known the render position. 动画本身不应该知道渲染位置。 Player has a private Animation member. Player有一个私人动画成员。 Player should know the current position. 玩家应该知道当前位置。

In this time, I have one SDL_Renderer per window and pass it all around Textures, Animations etc. Texture is with knowledge of SDL_Renderer render itself to the screen. 在这个时候,我每个窗口有一个SDL_Renderer并将其传递给Textures,Animation等SDL_Renderer具有SDL_Renderer知识, SDL_Renderer将自身呈现到屏幕上。 But I do not think calling Texture->draw( x, y ) all the time is efficient and cache friendly. 但是我不认为一直都在调用Texture-> draw(x,y)是高效且对缓存友好的。

I like the way SFML renders. 我喜欢SFML呈现的方式。 All renderable object have draw method, which just calls the window's draw method. 所有可渲染对象都具有draw方法,该方法仅调用窗口的draw方法。 I would like to implement similar system. 我想实施类似的系统。

This will break my requirement some Renderable objects know their render position, some do not. 这将打破我的要求,有些可渲染对象知道其渲染位置,有些则不知道。

For example class Player should not have render( x, y ) method, because it knows its position. 例如,类Player不应具有render( x, y )方法,因为它知道其位置。 On the other hand, Texture class should not have render( void ) method, because it does not its position. 另一方面,Texture类不应具有render( void )方法,因为它没有位置。

My ask is following: How to design SDL2 suitable render engine with as little overhead as possible? 我的问题如下:如何以尽可能少的开销设计适合SDL2的渲染引擎?

Answering your question of: "My ask is following: How to design SDL2 suitable render engine with as little overhead as possible?" 回答您的问题:“我的问题是:如何以尽可能少的开销设计适合SDL2的渲染引擎?”

A quick explanation of a scalable, lazy renderer scheme for minimizing SDL2 renderer overhead: 快速解释可扩展的惰性渲染器方案,以最小化SDL2渲染器开销:

A "layer" consists of all the textures that have the same z attribute. “层”由具有相同z属性的所有纹理组成。 Layers are marked as invalid whenever a texture in that layer is modified. 只要修改了该图层中的纹理,这些图层就会被标记为无效。 Layers are managed when the renderer runs, which is at a set framerate. 在渲染器以设置的帧速率运行时管理图层。 The max layer and the highest invalid layer are kept track of. 跟踪最大层和最高无效层。 The renderer starts at the highest invalid layer and ends at the max layer. 渲染器从最高无效层开始,到最大层结束。 Each layer is set as the renderer target and cleared. 将每一层设置为渲染器目标并清除。 The previous layer is then copied to the current layer. 然后将上一层复制到当前层。 Then it iterates through each texture in the layer, redrawing those marked invalid, and copies each texture on top of the current layer. 然后,迭代遍历该图层中的每个纹理,重新绘制那些标记为无效的纹理,并将每个纹理复制到当前图层的顶部。 The result is that the previous layer is the background while the current layer drawn on top of it. 结果是,上一层是背景,而当前层则绘制在其上。 This texture is cached and invalidated when the layer is marked invalid. 当图层标记为无效时,将缓存此纹理并使其无效。 After running through any invalid layers, set the render target back to default (null), copy the highest texture to the screen, and present. 在遍历所有无效层之后,将渲染目标设置回默认值(空),将最高纹理复制到屏幕上并显示。

In a 2d with layers user interface type of environment, the above is scalable and lazy. 在具有层次的2d用户界面类型的环境中,以上内容是可伸缩且懒惰的。 On my low end Celeron n2830 my test applications idle at 0% cpu usage. 在我的低端Celeron n2830上,我的测试应用程序在CPU使用率为0%时处于空闲状态。 It more or less stayed that way even when I added a hundred test layers. 即使我添加了一百个测试层,它或多或少都保持了这种状态。 Since it only draws from what layer changed upwards, and all changes are enqueued at a reasonable framerate, very little work actually ends up being done. 由于它仅从向上变化的层汲取,并且所有变化都以合理的帧速率入队,因此实际上很少完成任何工作。 Iterating through the invalid layer to the max layer appears asymptotically friendlier then iterating through all layers every run. 遍历无效层到最大层似乎渐近友好,然后每次运行遍历所有层。 Running the renderer at a set interval and doing all the drawing then helps to save time because it will ignore excess changes that wouldn't have been seen anyways. 以设定的时间间隔运行渲染器并进行所有绘制有助于节省时间,因为它将忽略本来不会看到的过多更改。 It's been worth it to use some application code to avoid having to draw stuff and copy textures. 使用一些应用程序代码来避免不得不绘制内容和复制纹理是值得的。 Those tend to take longer then a few if checks and attribute lookups. 如果进行检查和属性查找,则这些操作通常会花费更长的时间,然后花费一些时间。

note that this design was intended for 2d with layers, like a user interface. 请注意,该设计旨在用于带有图层的2D,例如用户界面。 some principles may not neccessarily work well in other environments. 有些原则在其他环境中可能不一定会很好地起作用。

Also the above only supports textures and not surfaces. 同样,以上内容仅支持纹理,不支持表面。 Mixing the two was for me not worth the complexity. 对我来说,将两者混合是不值得的。 Getting the sdl2 renderer to draw to a particular texture took some figuring out, but serves my needs well enough alone. 使sdl2渲染器绘制为特定的纹理需要花费一些时间,但单独满足我的需求就足够了。 If you mix the two it appears difficult to avoid some additional work. 如果将两者混合使用,似乎很难避免进行其他工作。 Plus textures benefit from a gpu, which adds to efficiency wins in terms of cpu time. 另外,纹理受益于GPU,这在CPU时间方面增加了效率。

In my system, window objects have a draw method. 在我的系统中,窗口对象具有draw方法。 This method enqueues draw operations with the renderer instead of drawing them immediately. 此方法使渲染操作进入绘制操作的队列,而不是立即绘制它们。 Window objects have an area attribute. 窗口对象具有区域属性。 To support objects that do not have an area attribute, maybe you could apply a wrapper that does around your object that does not. 为了支持不具有area属性的对象,也许您可​​以对没有对象的对象应用包装器。 That way when prompted for an area it can still provide one, even though it doesn't have one internally. 这样,当提示您提供区域时,即使内部没有区域,它仍可以提供一个区域。 Or maybe use a table that maps your object to an area, or a method call of whatever object does track its texture. 或者也许使用一个将您的对象映射到一个区域的表,或者对任何跟踪其纹理的对象进行方法调用。 I'm not sure of the nature of that limitation. 我不确定该限制的性质。

Note that this draw method is a hybrid approach of objects drawing themselves and the renderer drawing objects. 请注意,此绘制方法是对象自身绘制和渲染器绘制对象的混合方法。 The objects appear to draw themselves, while the renderer actually does the work. 这些对象似乎是在绘制自己,而渲染器实际上是在工作。

In short: 简而言之:

  • textures are signficantly faster then surfaces 纹理比表面快得多
  • enqueue draw and copy operations for batch processing 排队进行绘制和复制操作以进行批处理
  • cache textures when they are drawn 绘制时缓存纹理
  • cache layers after copying all textures to them 将所有纹理复制到图层后缓存图层
  • use the caches to avoid rendering all but the invalid layers to max layer 使用缓存来避免将无效层以外的所有层渲染为最大层

It seems to me what you are saying is that you want entities to only know their position, but for rendering you want it to be completely separate. 在我看来,您要说的是您希望实体仅知道它们的位置,但是对于渲染,您希望它完全独立。 Every entity will need to store some kind of rendering data. 每个实体都需要存储某种渲染数据。 I would say you should look at a component based entity system. 我要说的是,您应该看一下基于组件的实体系统。 There are different classes for position, renderdata, input etc. Try Component Based Entity System 位置,渲染数据,输入等有不同的类。尝试基于组件的实体系统

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM