【问题标题】:What is the point of an SDL2 Texture?SDL2 纹理的意义何在?
【发布时间】:2012-09-12 11:40:01
【问题描述】:

我有点卡在 SDL2 纹理背后的逻辑上。对我来说,它们毫无意义,因为你无法吸引它们。

在我的程序中,我有几个表面(或者在我切换到 SDL2 之前是表面),我只是将它们拼凑在一起形成层。现在看来,我必须创建几个渲染器和纹理来创建相同的效果,因为SDL_RenderCopy 需要一个纹理指针。

不仅如此,所有渲染器都必须来自一个窗口,这我理解,但仍然让我更讨厌。

这一切看起来都非常庞大和缓慢。我错过了什么吗?有没有办法直接绘制到纹理?纹理的意义何在?我可以安全地使用多个(如果不是数百个)渲染器来代替表面吗?

【问题讨论】:

  • 引入 2.0 后 SDL 架构发生的变化迫使我进入 SFML。由于 API 的变化,我无法重新构建我的整个程序;基本上,没有像 SFML 支持的屏幕外渲染。
  • SDL2 处于预发布状态。现在判断它并不完全公平。尽管是一个 WIP,但开发人员一直很友好地让我们在它的进展过程中搞砸它。
  • @Di-0xide 旧 wiki 仍然可用。 libsdl.org/cgi/docwiki.cgi
  • 三年后回想起来,我不明白什么是图形纹理。对于那些仍在投票的人,请记住纹理不一定是图像或图形;它们实际上是发送到卡上的任何数据。
  • 又过了三年,response I wrote for a Reddit ELI5 有点意义链接。这是对图形管道如何工作的非常简单的解释,并涉及一些基本理论。如果有人发现这对这个主题是全新的,那么很好的潜水点。

标签: c++ textures sdl renderer sdl-2


【解决方案1】:

SDL_Texture 对象存储在尽可能靠近显卡内存的位置,因此您的 GPU 可以轻松加速。调整大小、alpha 混合、抗锯齿和几乎所有计算繁重的操作都会受到这种性能提升的严重影响。如果您的程序需要在纹理上运行逐像素逻辑,建议您暂时将纹理转换为表面。也可以使用流式纹理实现解决方法。

编辑: 由于这个答案很受关注,我想详细说明我的建议。

如果您更喜欢使用Texture -> Surface -> Texture 工作流来应用您的逐像素操作,请确保缓存您的最终纹理,除非您需要在每个渲染周期重新计算它。此解决方案中的纹理是使用 SDL_TEXTUREACCESS_STATIC 标志创建的。

对于像素数据的来源是网络、设备、帧服务器或超出 SDL 应用程序完全范围的其他来源的用例,鼓励使用流式纹理(创建标志为 SDL_TEXTUREACCESS_STREAMING),并且显然从源缓存帧效率低下或不起作用。

如果使用SDL_TEXTUREACCESS_TARGET 标志创建纹理,则可以在纹理顶部进行渲染。这将绘制操作的来源限制为其他纹理,尽管这可能已经是您首先需要的。 “将纹理作为渲染目标”是 SDL2 最新且最不广泛支持的功能之一。

给好奇的读者的书呆子信息:

由于 SDL 实施的性质,前两种方法取决于应用程序级别的读取和复制操作,尽管它们针对建议的场景进行了优化,并且对于实时应用程序来说足够快。

与 GPU 上的后处理相比,从应用程序级别复制数据几乎总是很慢。如果您的要求比 SDL 所能提供的更严格,并且您的逻辑不依赖于某些外部像素数据源,那么分配从您的 SDL 表面绘制的原始 OpenGL 纹理并应用 着色器(GPU逻辑)给他们。

着色器是用 GLSL 编写的,GLSL 是一种编译成 GPU 程序集的语言。 硬件/GPU 加速实际上是指在 GPU 内核上并行化的代码,并且使用着色器是实现渲染目的的首选方法。

注意!将原始 OpenGL 纹理和着色器与 SDL 渲染函数和结构结合使用可能会导致一些意外冲突或库提供的灵活性损失。

TLDR; 尽管修改它们有时很麻烦,但渲染和操作纹理比表面更快。

【讨论】:

  • 这是什么streaming texture
  • “静态纹理是为精灵和背景以及其他变化不大的图像设计的……流式纹理是为频繁更新的东西设计的,每隔几秒,或者每一帧。 "引用自slouken.blogspot.com/2011/02/… 从技术上讲,它们是可变像素数据,在每次渲染之间都会进行修改,因此需要格外小心以避免由于对其缓冲区的大量访问而导致卡顿。
  • texture > surface > texture背后的逻辑是什么?为什么不是surface > texture 每一帧?
  • 在第一个渲染循环之后,许多材质将被缓存为纹理。对它们进行小的修改(例如更改一些较小的矩形部分)将从该流程中受益。
【解决方案2】:

通过将 SDL2 纹理创建为 STREAMING 类型,可以锁定和解锁整个纹理或仅一个像素区域以执行直接像素操作。必须先创建一个 SDL2 Surface,并与 lock-unlock 链接如下:

SDL_Surface surface = SDL_CreateSurface(..);
SDL_LockTexture(texture, &rect, &surface->pixels, &surface->pitch);
// paint into surface pixels
SDL_UnlockTexture(texture);

关键是,如果您绘制到更大尺寸的纹理,并且绘制是增量的(例如实时数据图),请确保仅锁定和解锁实际区域以进行更新。否则操作会很慢,内存复制量很大。

我体验过合理的性能,使用模型也不是太难理解。

【讨论】:

  • 首先创建表面不是必需的,也可能是不可取的。 SDL_LockTexture() 为您获取纹理的像素和间距,将它们写入提供的地址,这些地址不必(也可能不应该)成为任何表面的一部分。说paint into surface pixels 没有意义:您实际上在这里所做的是用纹理中的成员覆盖表面的*pixelspitch 成员,然后通过该指针更新纹理的像素,而不是表面。这在某些情况下可能有效,并且可能在您的情况下有效,但这似乎既没有必要也不是一个好主意
【解决方案3】:

在 SDL2 中,可以在屏幕外渲染/直接渲染到纹理。要使用的功能是:

int SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture);

这仅在渲染器启用 SDL_RENDERER_TARGETTEXTURE 时才有效。

【讨论】:

  • 有谁知道 SDL_RENDERER_TARGETTEXTURE 的支持范围有多广?
  • 看来你可以把SDL_RENDERER_TARGETTEXTURE传给SDL_CreateRenderer,见stackoverflow.com/q/38813605
猜你喜欢
  • 1970-01-01
  • 2021-04-08
  • 2017-04-14
  • 2014-01-14
  • 1970-01-01
  • 1970-01-01
  • 2016-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多