【问题标题】:Webgl Upload Texture Data to the gpu without a draw callWebgl 将纹理数据上传到 GPU,无需绘制调用
【发布时间】:2019-10-10 11:49:47
【问题描述】:

我正在使用 webgl 在自定义视频编解码器上进行 YUV 到 RGB 的转换。

视频必须以 30 fps 的速度播放。为了实现这一点,我每隔一个 requestAnimationFrame 都会做所有的数学运算。

这很好用,但我注意到在分析时将纹理上传到 gpu 需要的时间最长。

所以我分别上传了“Y”纹理和“UV”纹理。

现在第一个“requestAnimationFrame”将像这样上传“Y”纹理:

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, yTextureRef);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, textureWidth, textureHeight, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, yData);

第二个“requestAnimationFrame”将以同样的方式上传“UV”纹理,并对片段着色器进行绘制调用,在它们之间进行数学运算。

但这不会改变探查器中的任何内容。我仍然在上传“Y”纹理的帧上显示近 0 gpu 时间,在上传“UV”纹理的帧上显示的时间与之前相同。

但是,如果我向“Y”纹理上传函数添加绘图调用,则分析器会显示预期结果。每一帧都有将近一半的 GPU 时间。

据此,我猜测 Y 纹理并没有真正使用 texImage2d 函数上传到 gpu。

但是我真的不想在屏幕上绘制 Y 纹理,因为它没有正确的 UV 纹理,直到一帧之后才能做任何事情。那么有什么办法可以强制gpu在不执行draw call的情况下上传这个纹理呢?

【问题讨论】:

  • 我不这么认为 - 纹理上传是渲染状态的副作用,它绑定到绘图调用。
  • @DacreDenny 这真是太糟糕了,但我觉得确实是这样。感谢您的意见!

标签: webgl


【解决方案1】:

更新

我误解了这个问题

这真的取决于驱动程序。问题是 OpenGL/OpenGL ES/WebGL 的纹理 API 真的很烂。 Sucks 是“具有意想不到的后果”的技术术语。

问题是驱动程序在您绘制之前无法真正完全上传数据,因为它不知道您要更改哪些内容。您可以按任意顺序和任意大小更改所有 mip 级别,然后将它们全部固定在两者之间,因此在您绘制之前,它不知道您将调用哪些其他函数来操作纹理。

假设您创建了一个 4x4 0 级 mip

gl.texImage2D(
   gl.TEXTURE_2D,
   0,        // mip level
   gl.RGBA,
   4,        // width
   4,        // height
   ...);

它应该分配什么内存? 4(宽度)* 4(高度)* 4(RGBA)?但是如果你打电话给gl.generateMipmap呢?现在它需要 4*4*4+2*2*4+1*1*4。好的,但是现在您在第 3 级分配了一个 8x8 mip。您打算然后分别用 64x64、32x32、16x16 替换第 0 到第 2 级,但您先执行第 3 级。在替换以上级别之前替换级别3应该怎么办?然后将级别 4 8x8、5 作为 4x4、6 作为 2x2 和 7 作为 1x1 添加。

如您所见,API 允许您按任意顺序更改 mip。事实上,我可以将级别 7 分配为 723x234,然后再修复它。该 API 旨在直到绘制时间才关心所有 mip 必须具有正确的大小,此时它们最终可以在 GPU 上分配内存并复制 mip。

你可以看到这个问题here的演示和测试。测试会乱序上传 mips,以验证 WebGL 实现是否正确失败,因为它们的大小不正确,并且一旦大小正确,就可以正确开始工作。

您可以看出这可能是一个糟糕的 API 设计。

他们添加了gl.texStorage2D 来修复它,但gl.texStorage2D 在 WebGL1 中不可用,仅在 WebGL2 中可用。 gl.texStorage2D 有新问题 :(

TLDR;当您调用 gl.texImage2D 时,纹理会上传到驱动程序,但驱动程序在绘制时间之前无法上传到 GPU。

可能的解决方案:使用gl.texSubImage2D,因为它不分配内存,因此驱动程序可能会更快上传。我怀疑大多数司机不会,因为你可以在绘图前使用gl.texSubImage2D。还是值得一试

我还要补充一点,gl.LUMIANCE 也可能是一个瓶颈。 IIRC DirectX 没有相应的格式,OpenGL 核心配置文件也没有。两者都支持仅 RED 格式,但 WebGL1 不支持。因此,必须通过扩展上传数据来模拟 LUMIANCE。

旧答案

很遗憾,除了通过texImage2DtexSubImage2D 之外,无法将视频上传到WebGL

一些浏览器试图让这种情况发生得更快。我注意到您使用的是gl.LUMINANCE。您可以尝试使用gl.RGBgl.RGBA 看看事情是否会加快。浏览器可能只针对更常见的情况进行优化。另一方面,它们可能根本没有优化。

已经提出了两个允许在没有副本的情况下使用视频的扩展,但 AFAIK 没有像以往那样实现它们的浏览器。

WEBGL_video_texture

WEBGL_texture_source_iframe

这实际上是一个比听起来要困难得多的问题。

  • 视频数据可以有多种格式。你提到了 YUV,但还有其他的。浏览器应该告诉应用程序格式还是浏览器应该转换为标准格式?

    告诉的问题是很多开发者会弄错,然后用户会提供他们不支持的格式的视频

    WEBGL_video_texture 扩展通过重写着色器转换为标准格式。你告诉它uniform samplerVideoWEBGL video 然后它知道它可以将你的color = texture2D(video, uv) 重写为color = convertFromVideoFormatToRGB(texture(video, uv))。这也意味着如果您播放不同格式的视频,他们必须即时重新编写着色器。

  • 同步

    将视频数据传输到 WebGL 听起来很棒,但现在您遇到的问题是,当您获取数据并将其渲染到屏幕时,您已经添加了几帧延迟,因此音频不再同步.

    如何处理它超出了 WebGL 的范围,因为 WebGL 与音频没有任何关系,但它确实指出它并不像仅仅向 WebGL 提供数据那么简单。一旦你提供了数据,人们就会要求更多的 API 来获取音频和更多信息,这样他们就可以延迟一个或两个并保持同步。

TLDR;除了texImage2DtexSubImage2D 之外,无法将视频上传到 WebGL

【讨论】:

  • 嗨,格曼!恐怕我的问题没有说清楚,我没有使用标准的视频编解码器。我正在使用自定义的内部解码字节流并为 webl 着色器提供 3 个数组缓冲区。一个包含 y,一个包含 u,一个包含 v。这是我正在尝试优化的这些缓冲区的上传。从性能报告中可以看出,在它们上调用“texImage2d”并没有真正上传它们,只有绘制调用才可以。
猜你喜欢
  • 1970-01-01
  • 2016-07-05
  • 2012-07-29
  • 2014-09-26
  • 2016-08-28
  • 2014-12-05
  • 2012-09-02
  • 1970-01-01
  • 2013-11-28
相关资源
最近更新 更多