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