3D-graphics.html revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1{{+bindTo:partials.standard_nacl_article}} 2 3<section id="d-graphics"> 4<span id="devguide-coding-3d-graphics"></span><h1 id="d-graphics"><span id="devguide-coding-3d-graphics"></span>3D Graphics</h1> 5<p>Native Client applications use the <a class="reference external" href="http://en.wikipedia.org/wiki/OpenGL_ES">OpenGL ES 2.0</a> API for 3D rendering. This document 6describes how to call the OpenGL ES 2.0 interface in a Native Client module and 7how to build an efficient rendering loop. It also explains how to validate GPU 8drivers and test for specific GPU capabilities, and provides tips to help ensure 9your rendering code runs efficiently.</p> 10<aside class="note"> 11<strong>Note</strong>: 3D drawing and OpenGL are complex topics. This document deals only 12with issues directly related to programming in the Native Client 13environment. To learn more about OpenGL ES 2.0 itself, see the <a class="reference external" href="http://opengles-book.com/">OpenGL ES 2.0 14Programming Guide</a>. 15</aside> 16<section id="validating-the-client-graphics-platform"> 17<h2 id="validating-the-client-graphics-platform">Validating the client graphics platform</h2> 18<p>Native Client is a software technology that lets you code an application once 19and run it on multiple platforms without worrying about the implementation 20details on every possible target platform. It’s difficult to provide the same 21support at the hardware level. Graphics hardware comes from many different 22manufacturers and is controlled by drivers of varying quality. A particular GPU 23driver may not support every OpenGL ES 2.0 feature, and some drivers are known 24to have vulnerabilities that can be exploited.</p> 25<p>Even if the GPU driver is safe to use, your program should perform a validation 26check before you launch your application to ensure that the driver supports all 27the features you need.</p> 28<section id="vetting-the-driver-in-javascript"> 29<h3 id="vetting-the-driver-in-javascript">Vetting the driver in JavaScript</h3> 30<p>At startup, the application should perform a few additional tests that can be 31implemented in JavaScript on its hosting web page. The script that performs 32these tests should be included before the module’s <code>embed</code> tag, and ideally 33the <code>embed</code> tag should appear on the hosting page only if these tests succeed.</p> 34<p>The first thing to check is whether you can create a graphics context. If you 35can, use the context to confirm the existence of any required OpenGL ES 2.0 36extensions. You may want to refer to the <a class="reference external" href="http://www.khronos.org/registry/webgl/extensions/">extension registry</a> and include <a class="reference external" href="https://developer.mozilla.org/en-US/docs/WebGL/Using_Extensions">vendor 37prefixes</a> 38when checking for extensions.</p> 39</section><section id="vetting-the-driver-in-native-client"> 40<h3 id="vetting-the-driver-in-native-client">Vetting the driver in Native Client</h3> 41<section id="create-a-context"> 42<h4 id="create-a-context">Create a context</h4> 43<p>Once you’ve passed the JavaScript validation tests, it’s safe to add a Native 44Client embed tag to the hosting web page and load the module. As part of the 45module initialization code, you must create a graphics context for the app by 46either creating a C++ <code>Graphics3D</code> object or calling <code>PPB_Graphics3D</code> API 47function <code>Create</code>. Don’t assume this will always succeed; you still might have 48problems creating the context. If you are in development mode and can’t create 49the context, try creating a simpler version to see if you’re asking for an 50unsupported feature or exceeding a driver resource limit. Your production code 51should always check that the context was created and fail gracefully if that’s 52not the case.</p> 53</section><section id="check-for-extensions-and-capabilities"> 54<h4 id="check-for-extensions-and-capabilities">Check for extensions and capabilities</h4> 55<p>Not every GPU supports every extension or has the same amount of texture units, 56vertex attributes, etc. On startup, call <code>glGetString(GL_EXTENSIONS)</code> and 57check for the extensions and the features you need. For example:</p> 58<ul class="small-gap"> 59<li>If you are using non power-of-2 texture with mipmaps, make sure 60<code>GL_OES_texture_npot</code> exists.</li> 61<li>If you are using floating point textures, make sure <code>GL_OES_texture_float</code> 62exists.</li> 63<li>If you are using DXT1, DXT3, or DXT5 textures, make sure the corresponding 64extensions <code>EXT_texture_compression_dxt1</code>, 65<code>GL_CHROMIUM_texture_compression_dxt3</code>, and 66<code>GL_CHROMIUM_texture_compression_dxt5</code> exist.</li> 67<li>If you are using the functions <code>glDrawArraysInstancedANGLE</code>, 68<code>glDrawElementsInstancedANGLE</code>, <code>glVertexAttribDivisorANGLE</code>, or the PPAPI 69interface <code>PPB_OpenGLES2InstancedArrays</code>, make sure the corresponding 70extension <code>GL_ANGLE_instanced_arrays</code> exists.</li> 71<li>If you are using the function <code>glRenderbufferStorageMultisampleEXT</code>, or the 72PPAPI interface <code>PPB_OpenGLES2FramebufferMultisample</code>, make sure the 73corresponding extension <code>GL_CHROMIUM_framebuffer_multisample</code> exists.</li> 74<li>If you are using the functions <code>glGenQueriesEXT</code>, <code>glDeleteQueriesEXT</code>, 75<code>glIsQueryEXT</code>, <code>glBeginQueryEXT</code>, <code>glEndQueryEXT</code>, <code>glGetQueryivEXT</code>, 76<code>glGetQueryObjectuivEXT</code>, or the PPAPI interface <code>PPB_OpenGLES2Query</code>, 77make sure the corresponding extension <code>GL_EXT_occlusion_query_boolean</code> 78exists.</li> 79<li>If you are using the functions <code>glMapBufferSubDataCHROMIUM</code>, 80<code>glUnmapBufferSubDataCHROMIUM</code>, <code>glMapTexSubImage2DCHROMIUM</code>, 81<code>glUnmapTexSubImage2DCHROMIUM</code>, or the PPAPI interface 82<code>PPB_OpenGLES2ChromiumMapSub</code>, make sure the corresponding extension 83<code>GL_CHROMIUM_map_sub</code> exists.</li> 84</ul> 85<p>Check for system capabilites with <code>glGetIntegerv</code> and adjust shader programs 86as well as texture and vertex data accordingly:</p> 87<ul class="small-gap"> 88<li>If you are using textures in vertex shaders, make sure 89<code>glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, ...)</code> and 90<code>glGetIntegerv(GL_MAX_TEXTURE_SIZE, ...)</code> return values greater than 0.</li> 91<li>If you are using more than 8 textures in a single shader, make sure 92<code>glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, ...)</code> returns a value greater 93than or equal to the number of simultaneous textures you need.</li> 94</ul> 95</section></section><section id="vetting-the-driver-in-the-chrome-web-store"> 96<h3 id="vetting-the-driver-in-the-chrome-web-store">Vetting the driver in the Chrome Web Store</h3> 97<p>If you choose to place your application in the <a class="reference external" href="/webstore">Chrome Web Store</a>, 98its Web Store <a class="reference external" href="/extensions/manifest">manifest file</a> can include the <code>webgl</code> 99feature in the requirements parameter. It looks like this:</p> 100<pre class="prettyprint"> 101"requirements": { 102 "3D": { 103 "features": ["webgl"] 104 } 105} 106</pre> 107<p>While WebGL is technically a JavaScript API, specifying the <code>webgl</code> feature 108also works for OpenGL ES 2.0 because both interfaces use the same driver.</p> 109<p>This manifest item is not required, but if you include it, the Chrome Web Store 110will prevent a user from installing the application if the browser is running on 111a machine that does not support OpenGL ES 2.0 or that is using a known 112blacklisted GPU driver that could invite an attack.</p> 113<p>If the Web Store determines that the user’s driver is deficient, the app won’t 114appear on the store’s tile display. However, it will appear in store search 115results or if the user links to it directly, in which case the user could still 116download it. But the manifest requirements will be checked when the user reaches 117the install page, and if there is a problem, the browser will display the 118message “This application is not supported on this computer. Installation has 119been disabled.”</p> 120<p>The manifest-based check applies only to downloads directly from the Chrome Web 121Store. It is not performed when an application is loaded via <a class="reference external" href="/webstore/inline_installation">inline 122installation</a>.</p> 123</section><section id="what-to-do-when-there-are-problems"> 124<h3 id="what-to-do-when-there-are-problems">What to do when there are problems</h3> 125<p>Using the vetting procedure described above, you should be able to detect the 126most common problems before your application runs. If there are problems, your 127code should describe the issue as clearly as possible. That’s easy if there is a 128missing feature. Failure to create a graphics context is tougher to diagnose. At 129the very least, you can suggest that the user try to update the driver. You 130might want to linke to the Chrome page that describes <a class="reference external" href="http://support.google.com/chrome/bin/answer.py?hl=en&answer=1202946">how to do updates</a>.</p> 131<p>If a user can’t update the driver, or their problem persists, be sure to gather 132information about their graphics environment. Ask for the contents of the Chrome 133<code>about:gpu</code> page.</p> 134</section><section id="document-unreliable-drivers"> 135<h3 id="document-unreliable-drivers">Document unreliable drivers</h3> 136<p>It can be helpful to include information about known dubious drivers in your 137user documentation. This might help identify if a rogue driver is the cause of a 138problem. There are many sources of GPU driver blacklists. Two such lists can be 139found at the <a class="reference external" href="http://src.chromium.org/viewvc/chrome/trunk/deps/gpu/software_rendering_list/software_rendering_list.json">Chromium project</a> 140and <a class="reference external" href="http://www.khronos.org/webgl/wiki/BlacklistsAndWhitelists">Khronos</a>. You 141can use these lists to include information in your documentation that warns 142users about dangerous drivers.</p> 143</section><section id="test-your-defenses"> 144<h3 id="test-your-defenses">Test your defenses</h3> 145<p>You can test your driver validation code by running Chrome with the following 146flags (all at once) and watching how your application responds:</p> 147<ul class="small-gap"> 148<li><code>--disable-webgl</code></li> 149<li><code>--disable-pepper-3d</code></li> 150<li><code>--disable_multisampling</code></li> 151<li><code>--disable-accelerated-compositing</code></li> 152<li><code>--disable-accelerated-2d-canvas</code></li> 153</ul> 154</section></section><section id="calling-opengl-es-2-0-commands"> 155<h2 id="calling-opengl-es-2-0-commands">Calling OpenGL ES 2.0 commands</h2> 156<p>There are three ways to write OpenGL ES 2.0 calls in Native Client.</p> 157<section id="use-pure-opengl-es-2-0-function-calls"> 158<h3 id="use-pure-opengl-es-2-0-function-calls">Use “pure” OpenGL ES 2.0 function calls</h3> 159<p>You can make OpenGL ES 2.0 calls through a Pepper extension library. The SDK 160example <code>examples/api/graphics_3d</code> works this way. In the file 161<code>graphics_3d.cc</code>, the key initialization steps are as follows:</p> 162<ul class="small-gap"> 163<li><p class="first">Add these includes at the top of the file:</p> 164<pre class="prettyprint"> 165#include <GLES2/gl2.h> 166#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" 167</pre> 168</li> 169<li><p class="first">Define the function <code>InitGL</code>. The exact specification of <code>attrib_list</code> 170will be application specific.</p> 171<pre class="prettyprint"> 172bool InitGL(int32_t new_width, int32_t new_height) { 173 if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface())) { 174 fprintf(stderr, "Unable to initialize GL PPAPI!\n"); 175 return false; 176 } 177 178 const int32_t attrib_list[] = { 179 PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, 180 PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, 181 PP_GRAPHICS3DATTRIB_WIDTH, new_width, 182 PP_GRAPHICS3DATTRIB_HEIGHT, new_height, 183 PP_GRAPHICS3DATTRIB_NONE 184 }; 185 186 context_ = pp::Graphics3D(this, attrib_list); 187 if (!BindGraphics(context_)) { 188 fprintf(stderr, "Unable to bind 3d context!\n"); 189 context_ = pp::Graphics3D(); 190 glSetCurrentContextPPAPI(0); 191 return false; 192 } 193 194 glSetCurrentContextPPAPI(context_.pp_resource()); 195 return true; 196} 197</pre> 198</li> 199<li>Include logic in <code>Instance::DidChangeView</code> to call <code>InitGL</code> whenever 200necessary: upon application launch (when the graphics context is NULL) and 201whenever the module’s View changes size.</li> 202</ul> 203</section><section id="use-regal"> 204<h3 id="use-regal">Use Regal</h3> 205<p>If you are porting an OpenGL ES 2.0 application, or are comfortable writing in 206OpenGL ES 2.0, you should stick with the Pepper APIs or pure OpenGL ES 2.0 calls 207described above. If you are porting an application that uses features not in 208OpenGL ES 2.0, consider using Regal. Regal is an open source library that 209supports many versions of OpenGL. Regal recently added support for Native 210Client. Regal forwards most OpenGL calls directly to the underlying graphics 211library, but it can also emulate other calls that are not included (when 212hardware support exists). See <a class="reference external" href="http://www.altdevblogaday.com/2012/09/04/bringing-regal-opengl-to-native-client/">libregal</a> 213for more info.</p> 214</section><section id="use-the-pepper-api"> 215<h3 id="use-the-pepper-api">Use the Pepper API</h3> 216<p>Your code can call the Pepper PPB_OpenGLES2 API directly, as with any Pepper 217interface. When you write in this way, each invocation of an OpenGL ES 2.0 218function must begin with a reference to the Pepper interface, and the first 219argument is the graphics context. To invoke the function <code>glCompileShader</code>, 220your code might look like:</p> 221<pre class="prettyprint"> 222ppb_g3d_interface->CompileShader(graphicsContext, shader); 223</pre> 224<p>This approach specifically targets the Pepper APIs. Each call corresponds to a 225OpenGL ES 2.0 function, but the syntax is unique to Native Client, so the source 226file is not portable.</p> 227</section></section><section id="implementing-a-rendering-loop"> 228<h2 id="implementing-a-rendering-loop">Implementing a rendering loop</h2> 229<p>Graphics applications require a continuous frame render-and-redraw cycle that 230runs at a high frequency. To achieve the best frame rate, is important to 231understand how the OpenGL ES 2.0 code in a Native Client module interacts with 232Chrome.</p> 233<section id="the-chrome-and-native-client-processes"> 234<h3 id="the-chrome-and-native-client-processes">The Chrome and Native Client processes</h3> 235<p>Chrome is a multi-process browser. Each Chrome tab is a separate process that is 236running an application with its own main thread (we’ll call it the Chrome main 237thread). When an application launches a Native Client module, the module runs in 238a new, separate sandboxed process. The module’s process has its own main thread 239(the Native Client thread). The Chrome and Native Client processes communicate 240with each other using Pepper API calls on their main threads.</p> 241<p>When the Chrome main thread calls the Native Client thread (keyboard and mouse 242callbacks, for example), the Chrome main thread will block. This means that 243lengthy operations on the Native Client thread can steal cycles from Chrome, and 244performing blocking operations on the Native Client thread can bring your app to 245a standstill.</p> 246<p>Native Client uses callback functions to synchronize the main threads of the 247two processes. Only certain Pepper functions use callbacks; <a class="reference external" href="/native-client/pepper_stable/c/struct_p_p_b___graphics3_d__1__0#a293c6941c0da084267ffba3954793497">SwapBuffers</a> 248is one.</p> 249</section><section id="swapbuffers-and-its-callback-function"> 250<h3 id="swapbuffers-and-its-callback-function"><code>SwapBuffers</code> and its callback function</h3> 251<p><code>SwapBuffers</code> is non-blocking; it is called from the Native Client thread and 252returns immediately. When <code>SwapBuffers</code> is called, it runs asynchronously on 253the Chrome main thread. It switches the graphics data buffers, handles any 254needed compositing operations, and redraws the screen. When the screen update is 255complete, the callback function that was included as one of <code>SwapBuffer</code>‘s 256arguments will be called from the Chrome thread and executed on the Native 257Client thread.</p> 258<p>To create a rendering loop, your Native Client module should include a function 259that does the rendering work and then executes <code>SwapBuffers</code>, passing itself 260as the <code>SwapBuffer</code> callback. If your rendering code is efficient and runs 261quickly, this scheme will achieve the highest frame rate possible. The 262documentation for <code>SwapBuffers</code> explains why this is optimal: because the 263callback is executed only when the plugin’s current state is actually on the 264screen, this function provides a way to rate-limit animations. By waiting until 265the image is on the screen before painting the next frame, you can ensure you’re 266not generating updates faster than the screen can be updated.</p> 267<p>The following diagram illustrates the interaction between the Chrome and Native 268Client processes. The application-specific rendering code runs in the function 269called <code>Draw</code> on the Native Client thread. Blue down-arrows are blocking calls 270from the main thread to Native Client, green up-arrows are non-blocking 271<code>SwapBuffers</code> calls from Native Client to the main thread. All OpenGL ES 2.0 272calls are made from <code>Draw</code> in the Native Client thread.</p> 273<img alt="/native-client/images/3d-graphics-render-loop.png" src="/native-client/images/3d-graphics-render-loop.png" /> 274</section><section id="sdk-example-graphics-3d"> 275<h3 id="sdk-example-graphics-3d">SDK example <code>graphics_3d</code></h3> 276<p>The SDK example <code>graphics_3d</code> uses the function <code>MainLoop</code> (in 277<code>hello_world.cc</code>) to create a rendering loop as described above. <code>MainLoop</code> 278calls <code>Render</code> to do the rendering work, and then invokes <code>SwapBuffers</code>, 279passing itself as the callback.</p> 280<pre class="prettyprint"> 281void MainLoop(void* foo, int bar) { 282 if (g_LoadCnt == 3) { 283 InitProgram(); 284 g_LoadCnt++; 285 } 286 if (g_LoadCnt > 3) { 287 Render(); 288 PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0); 289 ppb_g3d_interface->SwapBuffers(g_context, cc); 290 } else { 291 PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0); 292 ppb_core_interface->CallOnMainThread(0, cc, 0); 293 } 294} 295</pre> 296</section></section><section id="managing-the-opengl-es-2-0-pipeline"> 297<h2 id="managing-the-opengl-es-2-0-pipeline">Managing the OpenGL ES 2.0 pipeline</h2> 298<p>OpenGL ES 2.0 commands do not run in the Chrome or Native Client processes. They 299are passed into a FIFO queue in shared memory which is best understood as a <a class="reference external" href="http://www.chromium.org/developers/design-documents/gpu-command-buffer">GPU 300command buffer</a>. The 301command buffer is shared by a dedicated GPU process. By using a separate GPU 302process, Chrome implements another layer of runtime security, vetting all OpenGL 303ES 2.0 commands and their arguments before they are sent on to the 304GPU. Buffering commands through the FIFO also speeds up your code, since each 305OpenGL ES 2.0 call in your Native Client thread returns immediately, while the 306processing may be delayed as the GPU works down the commands queued up in the 307FIFO.</p> 308<p>Before the screen is updated, all the intervening OpenGL ES 2.0 commands must be 309processed by the GPU. Programmers often try to ensure this by using the 310<code>glFlush</code> and <code>glFinish</code> commands in their rendering code. In the case of 311Native Client this is usually unnecessary. The <code>SwapBuffers</code> command does an 312implicit flush, and the Chrome team is continually tweaking the GPU code to 313consume the OpenGL ES 2.0 FIFO as fast as possible.</p> 314<p>Sometimes a 3D application can write to the FIFO in a way that’s difficult to 315handle. The command pipeline may fill up and your code will have to wait for the 316GPU to flush the FIFO. If this is the case, you may be able to add <code>glFlush</code> 317calls to speed up the flow of the OpenGL ES 2.0 command FIFO. Before you start 318to add your own flushes, first try to determine if pipeline saturation is really 319the problem by monitoring the rendering time per frame and looking for irregular 320spikes that do not consistently fall on the same OpenGL ES 2.0 call. If you’re 321convinced the pipeline needs to be accelerated, insert <code>glFlush</code> calls in your 322code before starting blocks of processing that do not generate OpenGL ES 2.0 323commands. For example, issue a flush before you begin any multithreaded particle 324work, so that the command buffer will be clear when you start doing OpenGL ES 3252.0 calls again. Determining where and how often to call <code>glFlush</code> can be 326tricky, you will need to experiment to find the sweet spot.</p> 327</section><section id="rendering-and-inactive-tabs"> 328<h2 id="rendering-and-inactive-tabs">Rendering and inactive tabs</h2> 329<p>Users will often switch between tabs in a multi-tab browser. A well-behaved 330application that’s performing 3D rendering should pause any real-time processing 331and yield cycles to other processes when its tab becomes inactive.</p> 332<p>In Chrome, an inactive tab will continue to execute timed functions (such as 333<code>setInterval</code> and <code>setTimeout</code>) but the timer interval will be automatically 334overridden and limited to not less than one second while the tab is inactive. In 335addition, any callback associated with a <code>SwapBuffers</code> call will not be sent 336until the tab is active again. You may receive asynchronous callbacks from 337functions other than <code>SwapBuffers</code> while a tab is inactive. Depending on the 338design of your application, you might choose to handle them as they arrive, or 339to queue them in a buffer and process them when the tab becomes active.</p> 340<p>The time that passes while a tab is inactive can be considerable. If your main 341thread pulse is based on the <code>SwapBuffers</code> callback, your app won’t update 342while a tab is inactive. A Native Client module should be able to detect and 343respond to the state of the tab in which it’s running. For example, when a tab 344becomes inactive, you can set an atomic flag in the Native Client thread that 345will skip the 3D rendering and <code>SwapBuffers</code> calls and continue to call the 346main thread every 30 msec or so. This provides time to update features that 347should still run in the background, like audio. It may also be helpful to call 348<code>sched_yield</code> or <code>usleep</code> on any worker threads to release resources and 349cede cycles to the OS.</p> 350<section id="handling-tab-activation-from-the-main-thread"> 351<h3 id="handling-tab-activation-from-the-main-thread">Handling tab activation from the main thread</h3> 352<p>You can detect and respond to the activation or deactivation of a tab with 353JavaScript on your hosting page. Add an EventListener for <code>visibilitychange</code> 354that sends a message to the Native Client module, as in this example:</p> 355<pre class="prettyprint"> 356document.addEventListener('visibilitychange', function(){ 357 if (document.hidden) { 358 // PostMessage to your Native Client module 359 document.nacl_module.postMessage('INACTIVE'); 360 } else { 361 // PostMessage to your Native Client module 362 document.nacl_module.postMessage('ACTIVE'); 363 } 364 365}, false); 366</pre> 367</section><section id="handling-tab-activation-from-the-native-client-thread"> 368<h3 id="handling-tab-activation-from-the-native-client-thread">Handling tab activation from the Native Client thread</h3> 369<p>You can also detect and respond to the activation or deactivation of a tab 370directly from your Native Client module by including code in the function 371<code>pp::Instance::DidChangeView</code>, which is called whenever a change in the 372module’s view occurs. The code can call <code>ppb::View::IsPageVisible</code> to 373determine if the page is visible or not. The most common cause of invisible 374pages is that the page is in a background tab.</p> 375</section></section><section id="tips-and-best-practices"> 376<h2 id="tips-and-best-practices">Tips and best practices</h2> 377<p>Here are some suggestions for writing safe code and getting the maximum 378performance with the Pepper 3D API.</p> 379<section id="do-s"> 380<h3 id="do-s">Do’s</h3> 381<ul class="small-gap"> 382<li><p class="first"><strong>Make sure to enable attrib 0.</strong> OpenGL requires that you enable attrib 0, 383but OpenGL ES 2.0 does not. For example, you can define a vertex shader with 2 384attributes, numbered like this:</p> 385<pre class="prettyprint"> 386glBindAttribLocation(program, "positions", 1); 387glBindAttribLocation(program, "normals", 2); 388</pre> 389<p>In this case the shader is not using attrib 0 and Chrome may have to perform 390some additional work if it is emulating OpenGL ES 2.0 on top of OpenGL. It’s 391always more efficient to enable attrib 0, even if you do not use it.</p> 392</li> 393<li><strong>Check how shaders compile.</strong> Shaders can compile differently on different 394systems, which can result in <code>glGetAttrib*</code> functions returning different 395results. Be sure that the vertex attribute indices match the corresponding 396name each time you recompile a shader.</li> 397<li><strong>Update indices sparingly.</strong> For security reasons, all indices must be 398validated. If you change indices, Native Client will validate them 399again. Structure your code so indices are not updated often.</li> 400<li><strong>Use a smaller plugin and let CSS scale it.</strong> If you’re running into fillrate 401issues, it may be beneficial to perform scaling via CSS. The size your plugin 402renders is determined by the width and height attributes of the <code><embed></code> 403element for the module. The actual size displayed on the web page is 404controlled by the CSS styles applied to the element.</li> 405<li><strong>Avoid matrix-to-matrix conversions.</strong> With some versions of Mac OS, there is 406a driver problem when compiling shaders. If you get compiler errors for matrix 407transforms, avoid matrix-to-matrix conversions. For instance, upres a vec3 to 408a vec4 before transforming it by a mat4, rather than converting the mat4 to a 409mat3.</li> 410</ul> 411</section><section id="don-ts"> 412<h3 id="don-ts">Don’ts</h3> 413<ul class="small-gap"> 414<li><strong>Don’t use client side buffers.</strong> OpenGL ES 2.0 can use client side data with 415<code>glVertexAttribPointer</code> and <code>glDrawElements</code>, but this is really slow. Try 416to avoid client side buffers. Use Vertex Buffer Objects (VBOs) instead.</li> 417<li><strong>Don’t mix vertex data and index data.</strong> By default, Pepper 3D binds buffers 418to a single point. You could create a buffer and bind it to both 419<code>GL_ARRAY_BUFFER</code> and <code>GL_ELEMENT_ARRAY_BUFFER</code>, but that would be 420expensive overhead and it is not recommended.</li> 421<li><strong>Don’t call ``glGet*`` or ``glCheck*`` during rendering.</strong> This is normal 422advice for OpenGL programs, but is particularly important for 3D on 423Chrome. Calls to any OpenGL ES 2.0 function whose name begins with these 424strings blocks the Native Client thread. This includes <code>glGetError</code>; avoid 425calling it in release builds.</li> 426<li><strong>Don’t use fixed point (``GL_FIXED``) vertex attributes.</strong> Fixed point 427attributes are not supported in OpenGL ES 2.0, so emulating them in OpenGL ES 4282.0 is slow. By default, <code>GL_FIXED</code> support is turned off in the Pepper 3D 429API.</li> 430<li><strong>Don’t read data from the GPU.</strong> Don’t call <code>glReadPixels</code>, as it is slow.</li> 431<li><strong>Don’t update a small portion of a large buffer.</strong> In the current OpenGL ES 4322.0 implementation when you update a portion of a buffer (with 433<code>glSubBufferData</code> for example) the entire buffer must be reprocessed. To 434avoid this problem, keep static and dynamic data in different buffers.</li> 435<li><strong>Don’t call ``glDisable(GL_TEXTURE_2D)``.</strong> This is an OpenGL ES 2.0 436error. Each time it is called, an error messages will appear in Chrome’s 437<code>about:gpu</code> tab.</li> 438</ul> 439</section></section></section> 440 441{{/partials.standard_nacl_article}} 442