计算WebGL中的uniforms变量使用数

techbrood 发表于 2016-09-02 23:36:38

标签: webgl, uniforms

- +

在使用Three.js为人体模型加载皮肤材料时,启用了skinning:true的参数。

有时候会导致GL编译错误,提示“too many uniforms”。下面的文章有助于理解错误原因和检测uniforms的使用情况。

For a recent consulting project I was attempting to render some fairly complex skeletal animations in WebGL on Firefox and Chrome. I quickly ran into a situation where the animation was rendering on Linux and Mac computers, but not on Windows. All the test machines had up-to-date graphics drivers, but the Windows machines threw a “too many uniforms” error when attempting to link the shader programs. So what does this error mean, and how do we fix it? In this post I’ll share the results of my research that allowed me to get everything working.

Uniforms

So what is a uniform? According to the OpenGL wiki, a uniform is “a global GLSL variable declared with the ‘uniform’ storage qualifier.” To be a little more specific: your shader executes on the GPU, which is physically distinct from the rest of the computer, separated by a bus. This means there’s a very limited amount of data that can be shared quickly between the GPU and your web browser. A uniform variable is what lets you declare “this is data that is exposed externally for transport over the bus”, and there’s a specific API on the JavaScript/WebGL side that lets you get and set this data. For example, you might declare the following variables at the start of a GLSL vertex shader:

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;
    uniform vec3 someVector;
    varying vec4 vColor;

That shader has, you guessed it, three uniforms. Later, in your JavaScript program code, you might access, transform, and return one of the uniforms like this:

    // get a copy of our vec3 uniform
    var vec3Uni = gl.getUniformLocation(shaderProgram, "someVector");
    // increment the x component of the vector by 10
    vec3Uni[0] += 10;
    // write it back to the GPU
    gl.uniform3fv(shaderProgram, vec3Uni);

If you’ve ever used web workers, this data flow should be familiar to you. This is for good reason: we’re simply doing parallel computing here, but instead of the shader code running on a spawned worker thread, it’s running on the GPU.

How Many is Too Many?

So, we know what a uniform is: it’s data that’s transferred back and forth between the GPU. Because the GPU is physically distinct from the rest of the computer, we are severely limited in the amount of data that we can quickly push back and forth over the bus between the CPU and the GPU. So we need to watch our uniform usage very closely, and we need to understand that different combinations of graphics cards and graphics drivers allow for different uniform caps.

When it seems like I’m hitting a performance limit, the first thing I do is benchmark my system. Fortunately, webgl-bench is a great website that can benchmark your WebGL performance in seconds, giving you a lot of statistics that can be valuable for debugging. For this problem in particular, we’re interested in the value returned forMAX_VERTEX_UNIFORM_VECTORS. As you might imagine, having more active uniforms than the maximum is what causes a “too many uniforms” error.

Running the benchmark on Mac, Linux, and Windows machines highlighted something odd: while most of the Mac and Linux machines capped out at 1024 uniforms, the Windows computers supported only 256 vertex uniforms, even if they had amazing graphics cards and the latest drivers.

This is due to a peculiarity in how Chrome and Firefox render WebGL. In Mac and Linux, the browsers render WebGL directly, using native OpenGL drivers. The problem on Windows is that the native OpenGL drivers are notoriously unstable, causing sometimes massive performance hits. To avoid these problems, the Windows versions of both browsers default to using ANGLE, which translates OpenGL ES 2.0 API calls to DirectX 9 or DirectX 11 API calls.

(Frank Olivier of the IE GPU team informs me that IE11 WebGL supports more than 256 vertex uniforms because it’s already using the DX11 runtime.)

The problem is that the ANGLE configuration currently used by both browsers uses DirectX 9. The DX9 configuration imposes an artificial cap of 256 vertex uniforms in use at any given time, no matter how good the graphics card is. Users can manually disable ANGLE (more info here) but it’s an onerous process only meant for developers; you can’t expect an end user to do it.

Fortunately, an upcoming Chrome release (version 34 or possibly earlier) is going to default to using DirectX 11, which should increase the cap to 1024 maximum vertex uniforms. You can follow the tracking issue for this feature here. Firefox is actively looking into doing the same, but they don’t have a timetable yet.

Counting the Uniforms

So now I needed to figure out how many uniforms I was using with my complex model. Obviously I was using more than 256, but exactly how many more?

For simple shaders you can manually count the number of uniforms by visually inspecting your script. But for most combinations of shaders and dynamic scenes, it’s likely that the number of uniforms in use will scale with the complexity and the number of the models being rendered. We need to dynamically count the active uniforms.

After a bunch of research, I wrote a function does this for you. You pass it a shader program and it returns an object containing useful data.

Here’s the code for the function:

function getProgramInfo(gl, program) {
    var result = {
        attributes: [],
        uniforms: [],
        attributeCount: 0,
        uniformCount: 0
    },
        activeUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS),
        activeAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);

    // Taken from the WebGl spec:
    // http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14
    var enums = {
        0x8B50: 'FLOAT_VEC2',
        0x8B51: 'FLOAT_VEC3',
        0x8B52: 'FLOAT_VEC4',
        0x8B53: 'INT_VEC2',
        0x8B54: 'INT_VEC3',
        0x8B55: 'INT_VEC4',
        0x8B56: 'BOOL',
        0x8B57: 'BOOL_VEC2',
        0x8B58: 'BOOL_VEC3',
        0x8B59: 'BOOL_VEC4',
        0x8B5A: 'FLOAT_MAT2',
        0x8B5B: 'FLOAT_MAT3',
        0x8B5C: 'FLOAT_MAT4',
        0x8B5E: 'SAMPLER_2D',
        0x8B60: 'SAMPLER_CUBE',
        0x1400: 'BYTE',
        0x1401: 'UNSIGNED_BYTE',
        0x1402: 'SHORT',
        0x1403: 'UNSIGNED_SHORT',
        0x1404: 'INT',
        0x1405: 'UNSIGNED_INT',
        0x1406: 'FLOAT'
    };

    // Loop through active uniforms
    for (var i=0; i < activeUniforms; i++) {
        var uniform = gl.getActiveUniform(program, i);
        uniform.typeName = enums[uniform.type];
        result.uniforms.push(uniform);
        result.uniformCount += uniform.size;
    }

    // Loop through active attributes
    for (var i=0; i < activeAttributes; i++) {
        var attribute = gl.getActiveAttrib(program, i);
        attribute.typeName = enums[attribute.type];
        result.attributes.push(attribute);
        result.attributeCount += attribute.size;
    }

    return result;
}

It returns an object containing the following data:

  • uniformCount – the total number of currently active uniforms in this program

  • uniforms – an array containing the name, size, type, and typeName of the each active uniform. In particular, typeName can be useful since otherwise you’d have to look up the integer type value and cross reference it to the enum declarations in the webGL spec

  • attributeCount, attributes – same as above, but for attributes (attributes are out of scope for this article, but are similarly capped and nice to keep count of)

Here’s some sample output using console.table (more info on that function here) to show the information from the returned object’s uniforms array. This is data from my original, complex model:

Looking at this I was able to immediately discern that we had 288 active vertex uniforms, and most of them were in the joint matrix (aka the complexity of our model’s skeleton). This helped me tell our artist that we needed to reduce the number of joints in the model by about 20% for it to work on Windows machines.

原文链接:

https://bocoup.com/weblog/counting-uniforms-in-webgl

possitive(12) views12138 comments3

发送私信

最新评论

王露 2020-12-23 14:52:19

fda


王露 2020-12-23 14:52:15

fdsa


王露 2020-12-23 14:52:11

fdaf


请先 登录 再评论.
相关文章
  • CSS3属性选择器特性使用详解

    CSS3除了引入动画、滤镜(用于特效)以及新的布局技术外,在选择器(selector)方面也有增强。属性选择器根据元素的属性(attributes)来匹配。这可以是一个单独...

  • NodeJS、Java和PHP性能考量和若干参考结论

    首先需要说明的是,严格而言NodeJS和Java、PHP并非对等概念,NodeJS是基于JS的一个应用程序,而Java/PHP是语言。我们这里实际指的是分别使用node、java和php来实...

  • CSS3弹性布局内容对齐(justify-content)属性使用详解

    内容对齐(justify-content)属性应用在弹性容器上,把弹性项沿着弹性容器的主轴线(main axis)对齐。该操作发生在弹性长度以及自动边距被确定后。 它用来在存...

  • 使用HTML5 FileReader和Canvas压缩用户上传的图片

    手机用户拍的照片通常会有2M以上,这对服务器带宽产生较大压力。因此在某些应用下(对图片要求不那么高)我们可以在客户端来压缩图片,然后再提交给服务器。总体...

  • HTTP/2背景和新特性简介

    在前面的一篇文章中已经介绍了

  • Three.js 对象局部坐标转换为世界坐标

    在Three.js中进行顶点几何计算时,一个需要注意的地方是,需要统一坐标系。比如你通过Three.js提供的API创建了一个球体网孔对象,那么默认情况下,各网孔顶点的...

  • Three.js入门教程4 - 创建粒子系统动画

    嗨,又见面了。这么说我们已经开始学习Three.js了,如果你还没有看过之前三篇教程,建议你先读完。如果你已经读完前面的教程了,你可能会想做一些关于粒子的东西。让我们直面这个话题吧,每个人都爱粒子效果。不管你是否知道,你可以很轻易地创建它们。

  • WebGL入门教程6 - 光照效果和Phong光照模型

    正是因为有了光,世界才能被我们看见,在3D的世界里,光照给物体带来真实的视觉感受。当光照射在某一表面上时,它可能被吸收、反射或投射。其中入射到表面上的一...

  • 如何使用WebGL创建一个逼真的下雨动画

    之前写过文章来分别讲解如何使用CSS3和Canvas2D实现过雨滴和下雨动画。通过背景处理看起来也有视觉上的3D效果,但并非真正的3D场景,如果要加入用户交互,进行36...

  • WebGL入门教程1 - 3D绘图基础知识

    现代浏览器努力使得Web用户体验更为丰富,而WebGL正处于这样的技术生态系统的中心位置。其应用范围覆盖在线游戏、大数据可视化、计算机辅助设计、虚拟现实以及数...

  • 使用top/left/margin和CSS3 translate两种方法实现标题居中的性能差异详解

    要实现标题全屏居中(同时在垂直和水平方向居中),有若干种方法,包括使用弹性布局、表格单元、绝对定位、自动外边距和CSS3平移变换等。你可能已经使用了这些方...

  • 如何使用CSS3实现书页(书本)卷角效果

    我们有时候想在页面显示一个公告或用户提示信息。一个常用设计是使用书签形状。我们可以给书签添加卷角效果,以使其更为逼真。所谓的“卷角”实际上可以用小角度...

  • 更多...