计算WebGL中的uniforms变量使用数

iefreer 发表于 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(2) negative(0) views5810 comments0
私信 收藏 分享
分享到

发送私信

最新评论

请先 登录 再评论.
相关文章
  • 微信公众号在线生成二维码带参数怎么搞?

    带参数二维码是微信公众号渠道二维码的一种实现
    微信的带参数二维码有两种,一种是临时二维码,一种是永久二维码,但是永久二维码的生成是有个数限制的,微...

  • ARCore基本概念和工作原理简介

    谷歌的WebAROnARCore项目基于Android手机提供的ARCore增强现实引擎,要了解WebAROnARCore,需要先了解ARCore的工作原理。基本上ARCore做了两件事,首先跟踪手机...

  • CSS3原生变量(Native Variables)新特性简介

    对Web开发者来说,一个盼望已久的特性是CSS终于支持原生变量了!
    变量是程序语言中用来解决代码重复和进行表达式计算的关键概念(想想数学方程式中的x)。...

  • CSS3人行走动作图解和动画实现

    对于人类而言,行走是一种很自然的想要前进并防止跌倒的一组动作重复。大部分人1岁就学会了走路,但至此以后的几十年间,或许我们从来没留意过自己行走姿势。当...

  • 函数式JavaScript编程基础概念:Curry和Partial Application

    本文介绍JS函数式编程中的两个概念:柯里(Curry)和部分应用程序(Partial Application)。什么是应用程序(Application)将函数应用于其参数以产生返回值的过...

  • 使用CSS3 box-decoration-break特性实现多行文本样式

    当文章中的长文本被自动断行为多行文本时,其样式可能会出乎我们的设计。本文介绍如何使用CSS3中的box-decoration-break特性来处理多行元素样式。
    按照规范...

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

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

  • Web界面编程状态变化和JS开发框架(React/Angular/Ember)

    UI编程中的一个关键课题就是界面组件化(可复用)以及组件状态管理。稍早一些的windows程序员可能接触过MFC,其界面编程中有一个DDX(DoDataExchange)的机制,...

  • 粒子运动模拟 - Verlet积分算法简介

    Verlet算法是经典力学(牛顿力学)中的一种最为普遍的积分方法,被广泛运用在分子运动模拟(Molecular Dynamics Simulation),行星运动以及织物变形模拟等领域...

  • 纹理基础知识和过滤模式详解

    1、 为什么在纹理采样时需要texture filter(纹理过滤)。
    我们的纹理是要贴到三维图形表面的,而三维图形上的pixel中心和纹理上的texel中心并不一至(pixe...

  • WebVR简介和常用资源链接

    什么是WebVR这是一个实验性的JavaScript API,提供了在用户网页浏览器中访问虚拟现实设备的统一接口。当前主流VR设备如Oculus Rift DK2、谷歌的CardBoard、三星...

  • WebGL入门教程4 - 使用纹理贴图(Texture Map)

    3D建模和纹理贴图的关系就好比人体和皮肤(或着装)的关系,3D建模用来处理空间属性,而贴图适合用来处理细腻的表面属性。如果不使用贴图,而想在表面达到足够的...

  • 使用CSS3实现流星雨动画教程

    很多营销页面中需要实现类似流星雨的动画背景,营造节日浪漫的气氛。要实现这样的效果,有两种方法,一个是使用Canvas,一个是使用纯CSS3,我们这里介绍第2种方...

  • 在PHP网页程序中执行Sass/Compass命令

    我们需要在wow云开发平台支持sass/compass等预编译样式语言,为此我们首先尝试了scssphp扩展,但是在支持最新语法上,经常会出现异常。所以我们采用了代理的方式...

  • 更多...