OLD | NEW |
1 {{+bindTo:partials.standard_nacl_article}} | 1 {{+bindTo:partials.standard_nacl_article}} |
2 | 2 |
3 <section id="d-graphics"> | 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> | 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 | 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 | 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 | 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 | 8 drivers and test for specific GPU capabilities, and provides tips to help ensure |
9 your rendering code runs efficiently.</p> | 9 your rendering code runs efficiently.</p> |
10 <aside class="note"> | 10 <aside class="note"> |
11 <strong>Note</strong>: 3D drawing and OpenGL are complex topics. This document d
eals only | 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 | 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 | 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>. | 14 Programming Guide</a>. |
15 </aside> | 15 </aside> |
16 <section id="validating-the-client-graphics-platform"> | |
17 <h2 id="validating-the-client-graphics-platform">Validating the client graphics
platform</h2> | 16 <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 | 17 <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 | 18 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 | 19 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 | 20 support at the hardware level. Graphics hardware comes from many different |
22 manufacturers and is controlled by drivers of varying quality. A particular GPU | 21 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 | 22 driver may not support every OpenGL ES 2.0 feature, and some drivers are known |
24 to have vulnerabilities that can be exploited.</p> | 23 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 | 24 <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 | 25 check before you launch your application to ensure that the driver supports all |
27 the features you need.</p> | 26 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> | 27 <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 | 28 <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 | 29 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 | 30 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> | 31 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 | 32 <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 | 33 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 | 34 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> | 35 prefixes</a> |
38 when checking for extensions.</p> | 36 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> | 37 <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> | 38 <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 | 39 <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 | 40 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 | 41 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 | 42 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 | 43 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 | 44 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 | 45 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 | 46 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 | 47 should always check that the context was created and fail gracefully if thatR
17;s |
52 not the case.</p> | 48 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> | 49 <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, | 50 <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 | 51 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> | 52 check for the extensions and the features you need. For example:</p> |
58 <ul class="small-gap"> | 53 <ul class="small-gap"> |
59 <li>If you are using non power-of-2 texture with mipmaps, make sure | 54 <li>If you are using non power-of-2 texture with mipmaps, make sure |
60 <code>GL_OES_texture_npot</code> exists.</li> | 55 <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> | 56 <li>If you are using floating point textures, make sure <code>GL_OES_texture_flo
at</code> |
62 exists.</li> | 57 exists.</li> |
63 <li>If you are using DXT1, DXT3, or DXT5 textures, make sure the corresponding | 58 <li>If you are using DXT1, DXT3, or DXT5 textures, make sure the corresponding |
(...skipping 21 matching lines...) Expand all Loading... |
85 <p>Check for system capabilites with <code>glGetIntegerv</code> and adjust shade
r programs | 80 <p>Check for system capabilites with <code>glGetIntegerv</code> and adjust shade
r programs |
86 as well as texture and vertex data accordingly:</p> | 81 as well as texture and vertex data accordingly:</p> |
87 <ul class="small-gap"> | 82 <ul class="small-gap"> |
88 <li>If you are using textures in vertex shaders, make sure | 83 <li>If you are using textures in vertex shaders, make sure |
89 <code>glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, ...)</code> and | 84 <code>glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, ...)</code> and |
90 <code>glGetIntegerv(GL_MAX_TEXTURE_SIZE, ...)</code> return values greater than
0.</li> | 85 <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 | 86 <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 | 87 <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> | 88 than or equal to the number of simultaneous textures you need.</li> |
94 </ul> | 89 </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> | 90 <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="/webstore">Chrome Web Store</a>, | 91 <p>If you choose to place your application in the <a class="reference external"
href="/webstore">Chrome Web Store</a>, |
98 its Web Store <a class="reference external" href="/extensions/manifest">manifest
file</a> can include the <code>webgl</code> | 92 its Web Store <a class="reference external" href="/extensions/manifest">manifest
file</a> can include the <code>webgl</code> |
99 feature in the requirements parameter. It looks like this:</p> | 93 feature in the requirements parameter. It looks like this:</p> |
100 <pre class="prettyprint"> | 94 <pre class="prettyprint"> |
101 "requirements": { | 95 "requirements": { |
102 "3D": { | 96 "3D": { |
103 "features": ["webgl"] | 97 "features": ["webgl"] |
104 } | 98 } |
105 } | 99 } |
106 </pre> | 100 </pre> |
107 <p>While WebGL is technically a JavaScript API, specifying the <code>webgl</code
> feature | 101 <p>While WebGL is technically a JavaScript API, specifying the <code>webgl</code
> feature |
108 also works for OpenGL ES 2.0 because both interfaces use the same driver.</p> | 102 also 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 Sto
re | 103 <p>This manifest item is not required, but if you include it, the Chrome Web Sto
re |
110 will prevent a user from installing the application if the browser is running on | 104 will prevent a user from installing the application if the browser is running on |
111 a machine that does not support OpenGL ES 2.0 or that is using a known | 105 a machine that does not support OpenGL ES 2.0 or that is using a known |
112 blacklisted GPU driver that could invite an attack.</p> | 106 blacklisted GPU driver that could invite an attack.</p> |
113 <p>If the Web Store determines that the user’s driver is deficient, the ap
p won’t | 107 <p>If the Web Store determines that the user’s driver is deficient, the ap
p won’t |
114 appear on the store’s tile display. However, it will appear in store searc
h | 108 appear on the store’s tile display. However, it will appear in store searc
h |
115 results or if the user links to it directly, in which case the user could still | 109 results or if the user links to it directly, in which case the user could still |
116 download it. But the manifest requirements will be checked when the user reaches | 110 download it. But the manifest requirements will be checked when the user reaches |
117 the install page, and if there is a problem, the browser will display the | 111 the install page, and if there is a problem, the browser will display the |
118 message “This application is not supported on this computer. Installation
has | 112 message “This application is not supported on this computer. Installation
has |
119 been disabled.”</p> | 113 been disabled.”</p> |
120 <p>The manifest-based check applies only to downloads directly from the Chrome W
eb | 114 <p>The manifest-based check applies only to downloads directly from the Chrome W
eb |
121 Store. It is not performed when an application is loaded via <a class="reference
external" href="/webstore/inline_installation">inline | 115 Store. It is not performed when an application is loaded via <a class="reference
external" href="/webstore/inline_installation">inline |
122 installation</a>.</p> | 116 installation</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> | 117 <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 | 118 <p>Using the vetting procedure described above, you should be able to detect the |
126 most common problems before your application runs. If there are problems, your | 119 most common problems before your application runs. If there are problems, your |
127 code should describe the issue as clearly as possible. That’s easy if ther
e is a | 120 code should describe the issue as clearly as possible. That’s easy if ther
e is a |
128 missing feature. Failure to create a graphics context is tougher to diagnose. At | 121 missing feature. Failure to create a graphics context is tougher to diagnose. At |
129 the very least, you can suggest that the user try to update the driver. You | 122 the very least, you can suggest that the user try to update the driver. You |
130 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> | 123 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> |
131 <p>If a user can’t update the driver, or their problem persists, be sure t
o gather | 124 <p>If a user can’t update the driver, or their problem persists, be sure t
o gather |
132 information about their graphics environment. Ask for the contents of the Chrome | 125 information about their graphics environment. Ask for the contents of the Chrome |
133 <code>about:gpu</code> page.</p> | 126 <code>about:gpu</code> page.</p> |
134 </section><section id="document-unreliable-drivers"> | |
135 <h3 id="document-unreliable-drivers">Document unreliable drivers</h3> | 127 <h3 id="document-unreliable-drivers">Document unreliable drivers</h3> |
136 <p>It can be helpful to include information about known dubious drivers in your | 128 <p>It can be helpful to include information about known dubious drivers in your |
137 user documentation. This might help identify if a rogue driver is the cause of a | 129 user documentation. This might help identify if a rogue driver is the cause of a |
138 problem. There are many sources of GPU driver blacklists. Two such lists can be | 130 problem. There are many sources of GPU driver blacklists. Two such lists can be |
139 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> | 131 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> |
140 and <a class="reference external" href="http://www.khronos.org/webgl/wiki/Blackl
istsAndWhitelists">Khronos</a>. You | 132 and <a class="reference external" href="http://www.khronos.org/webgl/wiki/Blackl
istsAndWhitelists">Khronos</a>. You |
141 can use these lists to include information in your documentation that warns | 133 can use these lists to include information in your documentation that warns |
142 users about dangerous drivers.</p> | 134 users about dangerous drivers.</p> |
143 </section><section id="test-your-defenses"> | |
144 <h3 id="test-your-defenses">Test your defenses</h3> | 135 <h3 id="test-your-defenses">Test your defenses</h3> |
145 <p>You can test your driver validation code by running Chrome with the following | 136 <p>You can test your driver validation code by running Chrome with the following |
146 flags (all at once) and watching how your application responds:</p> | 137 flags (all at once) and watching how your application responds:</p> |
147 <ul class="small-gap"> | 138 <ul class="small-gap"> |
148 <li><code>--disable-webgl</code></li> | 139 <li><code>--disable-webgl</code></li> |
149 <li><code>--disable-pepper-3d</code></li> | 140 <li><code>--disable-pepper-3d</code></li> |
150 <li><code>--disable_multisampling</code></li> | 141 <li><code>--disable_multisampling</code></li> |
151 <li><code>--disable-accelerated-compositing</code></li> | 142 <li><code>--disable-accelerated-compositing</code></li> |
152 <li><code>--disable-accelerated-2d-canvas</code></li> | 143 <li><code>--disable-accelerated-2d-canvas</code></li> |
153 </ul> | 144 </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> | 145 <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> | 146 <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> | 147 <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 | 148 <p>You can make OpenGL ES 2.0 calls through a Pepper extension library. The SDK |
160 example <code>examples/api/graphics_3d</code> works this way. In the file | 149 example <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> | 150 <code>graphics_3d.cc</code>, the key initialization steps are as follows:</p> |
162 <ul class="small-gap"> | 151 <ul class="small-gap"> |
163 <li><p class="first">Add these includes at the top of the file:</p> | 152 <li><p class="first">Add these includes at the top of the file:</p> |
164 <pre class="prettyprint"> | 153 <pre class="prettyprint"> |
165 #include <GLES2/gl2.h> | 154 #include <GLES2/gl2.h> |
166 #include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" | 155 #include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" |
167 </pre> | 156 </pre> |
(...skipping 25 matching lines...) Expand all Loading... |
193 | 182 |
194 glSetCurrentContextPPAPI(context_.pp_resource()); | 183 glSetCurrentContextPPAPI(context_.pp_resource()); |
195 return true; | 184 return true; |
196 } | 185 } |
197 </pre> | 186 </pre> |
198 </li> | 187 </li> |
199 <li>Include logic in <code>Instance::DidChangeView</code> to call <code>InitGL</
code> whenever | 188 <li>Include logic in <code>Instance::DidChangeView</code> to call <code>InitGL</
code> whenever |
200 necessary: upon application launch (when the graphics context is NULL) and | 189 necessary: upon application launch (when the graphics context is NULL) and |
201 whenever the module’s View changes size.</li> | 190 whenever the module’s View changes size.</li> |
202 </ul> | 191 </ul> |
203 </section><section id="use-regal"> | |
204 <h3 id="use-regal">Use Regal</h3> | 192 <h3 id="use-regal">Use Regal</h3> |
205 <p>If you are porting an OpenGL ES 2.0 application, or are comfortable writing i
n | 193 <p>If you are porting an OpenGL ES 2.0 application, or are comfortable writing i
n |
206 OpenGL ES 2.0, you should stick with the Pepper APIs or pure OpenGL ES 2.0 calls | 194 OpenGL ES 2.0, you should stick with the Pepper APIs or pure OpenGL ES 2.0 calls |
207 described above. If you are porting an application that uses features not in | 195 described above. If you are porting an application that uses features not in |
208 OpenGL ES 2.0, consider using Regal. Regal is an open source library that | 196 OpenGL ES 2.0, consider using Regal. Regal is an open source library that |
209 supports many versions of OpenGL. Regal recently added support for Native | 197 supports many versions of OpenGL. Regal recently added support for Native |
210 Client. Regal forwards most OpenGL calls directly to the underlying graphics | 198 Client. Regal forwards most OpenGL calls directly to the underlying graphics |
211 library, but it can also emulate other calls that are not included (when | 199 library, but it can also emulate other calls that are not included (when |
212 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> | 200 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> |
213 for more info.</p> | 201 for more info.</p> |
214 </section><section id="use-the-pepper-api"> | |
215 <h3 id="use-the-pepper-api">Use the Pepper API</h3> | 202 <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 | 203 <p>Your code can call the Pepper PPB_OpenGLES2 API directly, as with any Pepper |
217 interface. When you write in this way, each invocation of an OpenGL ES 2.0 | 204 interface. When you write in this way, each invocation of an OpenGL ES 2.0 |
218 function must begin with a reference to the Pepper interface, and the first | 205 function must begin with a reference to the Pepper interface, and the first |
219 argument is the graphics context. To invoke the function <code>glCompileShader</
code>, | 206 argument is the graphics context. To invoke the function <code>glCompileShader</
code>, |
220 your code might look like:</p> | 207 your code might look like:</p> |
221 <pre class="prettyprint"> | 208 <pre class="prettyprint"> |
222 ppb_g3d_interface->CompileShader(graphicsContext, shader); | 209 ppb_g3d_interface->CompileShader(graphicsContext, shader); |
223 </pre> | 210 </pre> |
224 <p>This approach specifically targets the Pepper APIs. Each call corresponds to
a | 211 <p>This approach specifically targets the Pepper APIs. Each call corresponds to
a |
225 OpenGL ES 2.0 function, but the syntax is unique to Native Client, so the source | 212 OpenGL ES 2.0 function, but the syntax is unique to Native Client, so the source |
226 file is not portable.</p> | 213 file 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> | 214 <h2 id="implementing-a-rendering-loop">Implementing a rendering loop</h2> |
229 <p>Graphics applications require a continuous frame render-and-redraw cycle that | 215 <p>Graphics applications require a continuous frame render-and-redraw cycle that |
230 runs at a high frequency. To achieve the best frame rate, is important to | 216 runs at a high frequency. To achieve the best frame rate, is important to |
231 understand how the OpenGL ES 2.0 code in a Native Client module interacts with | 217 understand how the OpenGL ES 2.0 code in a Native Client module interacts with |
232 Chrome.</p> | 218 Chrome.</p> |
233 <section id="the-chrome-and-native-client-processes"> | |
234 <h3 id="the-chrome-and-native-client-processes">The Chrome and Native Client pro
cesses</h3> | 219 <h3 id="the-chrome-and-native-client-processes">The Chrome and Native Client pro
cesses</h3> |
235 <p>Chrome is a multi-process browser. Each Chrome tab is a separate process that
is | 220 <p>Chrome is a multi-process browser. Each Chrome tab is a separate process that
is |
236 running an application with its own main thread (we’ll call it the Chrome
main | 221 running an application with its own main thread (we’ll call it the Chrome
main |
237 thread). When an application launches a Native Client module, the module runs in | 222 thread). When an application launches a Native Client module, the module runs in |
238 a new, separate sandboxed process. The module’s process has its own main t
hread | 223 a new, separate sandboxed process. The module’s process has its own main t
hread |
239 (the Native Client thread). The Chrome and Native Client processes communicate | 224 (the Native Client thread). The Chrome and Native Client processes communicate |
240 with each other using Pepper API calls on their main threads.</p> | 225 with 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 mous
e | 226 <p>When the Chrome main thread calls the Native Client thread (keyboard and mous
e |
242 callbacks, for example), the Chrome main thread will block. This means that | 227 callbacks, for example), the Chrome main thread will block. This means that |
243 lengthy operations on the Native Client thread can steal cycles from Chrome, and | 228 lengthy operations on the Native Client thread can steal cycles from Chrome, and |
244 performing blocking operations on the Native Client thread can bring your app to | 229 performing blocking operations on the Native Client thread can bring your app to |
245 a standstill.</p> | 230 a standstill.</p> |
246 <p>Native Client uses callback functions to synchronize the main threads of the | 231 <p>Native Client uses callback functions to synchronize the main threads of the |
247 two 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> | 232 two 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> |
248 is one.</p> | 233 is 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> | 234 <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 | 235 <p><code>SwapBuffers</code> is non-blocking; it is called from the Native Client
thread and |
252 returns immediately. When <code>SwapBuffers</code> is called, it runs asynchrono
usly on | 236 returns immediately. When <code>SwapBuffers</code> is called, it runs asynchrono
usly on |
253 the Chrome main thread. It switches the graphics data buffers, handles any | 237 the Chrome main thread. It switches the graphics data buffers, handles any |
254 needed compositing operations, and redraws the screen. When the screen update is | 238 needed compositing operations, and redraws the screen. When the screen update is |
255 complete, the callback function that was included as one of <code>SwapBuffer</co
de>‘s | 239 complete, the callback function that was included as one of <code>SwapBuffer</co
de>‘s |
256 arguments will be called from the Chrome thread and executed on the Native | 240 arguments will be called from the Chrome thread and executed on the Native |
257 Client thread.</p> | 241 Client thread.</p> |
258 <p>To create a rendering loop, your Native Client module should include a functi
on | 242 <p>To create a rendering loop, your Native Client module should include a functi
on |
259 that does the rendering work and then executes <code>SwapBuffers</code>, passing
itself | 243 that does the rendering work and then executes <code>SwapBuffers</code>, passing
itself |
260 as the <code>SwapBuffer</code> callback. If your rendering code is efficient and
runs | 244 as the <code>SwapBuffer</code> callback. If your rendering code is efficient and
runs |
261 quickly, this scheme will achieve the highest frame rate possible. The | 245 quickly, this scheme will achieve the highest frame rate possible. The |
262 documentation for <code>SwapBuffers</code> explains why this is optimal: because
the | 246 documentation for <code>SwapBuffers</code> explains why this is optimal: because
the |
263 callback is executed only when the plugin’s current state is actually on t
he | 247 callback is executed only when the plugin’s current state is actually on t
he |
264 screen, this function provides a way to rate-limit animations. By waiting until | 248 screen, this function provides a way to rate-limit animations. By waiting until |
265 the image is on the screen before painting the next frame, you can ensure you
217;re | 249 the image is on the screen before painting the next frame, you can ensure you
217;re |
266 not generating updates faster than the screen can be updated.</p> | 250 not generating updates faster than the screen can be updated.</p> |
267 <p>The following diagram illustrates the interaction between the Chrome and Nati
ve | 251 <p>The following diagram illustrates the interaction between the Chrome and Nati
ve |
268 Client processes. The application-specific rendering code runs in the function | 252 Client processes. The application-specific rendering code runs in the function |
269 called <code>Draw</code> on the Native Client thread. Blue down-arrows are block
ing calls | 253 called <code>Draw</code> on the Native Client thread. Blue down-arrows are block
ing calls |
270 from the main thread to Native Client, green up-arrows are non-blocking | 254 from 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 | 255 <code>SwapBuffers</code> calls from Native Client to the main thread. All OpenGL
ES 2.0 |
272 calls are made from <code>Draw</code> in the Native Client thread.</p> | 256 calls 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" /> | 257 <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> | 258 <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</co
de> (in | 259 <p>The SDK example <code>graphics_3d</code> uses the function <code>MainLoop</co
de> (in |
277 <code>hello_world.cc</code>) to create a rendering loop as described above. <cod
e>MainLoop</code> | 260 <code>hello_world.cc</code>) to create a rendering loop as described above. <cod
e>MainLoop</code> |
278 calls <code>Render</code> to do the rendering work, and then invokes <code>SwapB
uffers</code>, | 261 calls <code>Render</code> to do the rendering work, and then invokes <code>SwapB
uffers</code>, |
279 passing itself as the callback.</p> | 262 passing itself as the callback.</p> |
280 <pre class="prettyprint"> | 263 <pre class="prettyprint"> |
281 void MainLoop(void* foo, int bar) { | 264 void MainLoop(void* foo, int bar) { |
282 if (g_LoadCnt == 3) { | 265 if (g_LoadCnt == 3) { |
283 InitProgram(); | 266 InitProgram(); |
284 g_LoadCnt++; | 267 g_LoadCnt++; |
285 } | 268 } |
286 if (g_LoadCnt > 3) { | 269 if (g_LoadCnt > 3) { |
287 Render(); | 270 Render(); |
288 PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0); | 271 PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0); |
289 ppb_g3d_interface->SwapBuffers(g_context, cc); | 272 ppb_g3d_interface->SwapBuffers(g_context, cc); |
290 } else { | 273 } else { |
291 PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0); | 274 PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0); |
292 ppb_core_interface->CallOnMainThread(0, cc, 0); | 275 ppb_core_interface->CallOnMainThread(0, cc, 0); |
293 } | 276 } |
294 } | 277 } |
295 </pre> | 278 </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> | 279 <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. T
hey | 280 <p>OpenGL ES 2.0 commands do not run in the Chrome or Native Client processes. T
hey |
299 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 | 281 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 |
300 command buffer</a>. The | 282 command buffer</a>. The |
301 command buffer is shared by a dedicated GPU process. By using a separate GPU | 283 command buffer is shared by a dedicated GPU process. By using a separate GPU |
302 process, Chrome implements another layer of runtime security, vetting all OpenGL | 284 process, Chrome implements another layer of runtime security, vetting all OpenGL |
303 ES 2.0 commands and their arguments before they are sent on to the | 285 ES 2.0 commands and their arguments before they are sent on to the |
304 GPU. Buffering commands through the FIFO also speeds up your code, since each | 286 GPU. Buffering commands through the FIFO also speeds up your code, since each |
305 OpenGL ES 2.0 call in your Native Client thread returns immediately, while the | 287 OpenGL ES 2.0 call in your Native Client thread returns immediately, while the |
306 processing may be delayed as the GPU works down the commands queued up in the | 288 processing may be delayed as the GPU works down the commands queued up in the |
(...skipping 10 matching lines...) Expand all Loading... |
317 calls to speed up the flow of the OpenGL ES 2.0 command FIFO. Before you start | 299 calls to speed up the flow of the OpenGL ES 2.0 command FIFO. Before you start |
318 to add your own flushes, first try to determine if pipeline saturation is really | 300 to add your own flushes, first try to determine if pipeline saturation is really |
319 the problem by monitoring the rendering time per frame and looking for irregular | 301 the problem by monitoring the rendering time per frame and looking for irregular |
320 spikes that do not consistently fall on the same OpenGL ES 2.0 call. If you̵
7;re | 302 spikes that do not consistently fall on the same OpenGL ES 2.0 call. If you̵
7;re |
321 convinced the pipeline needs to be accelerated, insert <code>glFlush</code> call
s in your | 303 convinced the pipeline needs to be accelerated, insert <code>glFlush</code> call
s in your |
322 code before starting blocks of processing that do not generate OpenGL ES 2.0 | 304 code before starting blocks of processing that do not generate OpenGL ES 2.0 |
323 commands. For example, issue a flush before you begin any multithreaded particle | 305 commands. For example, issue a flush before you begin any multithreaded particle |
324 work, so that the command buffer will be clear when you start doing OpenGL ES | 306 work, so that the command buffer will be clear when you start doing OpenGL ES |
325 2.0 calls again. Determining where and how often to call <code>glFlush</code> ca
n be | 307 2.0 calls again. Determining where and how often to call <code>glFlush</code> ca
n be |
326 tricky, you will need to experiment to find the sweet spot.</p> | 308 tricky, 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> | 309 <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 | 310 <p>Users will often switch between tabs in a multi-tab browser. A well-behaved |
330 application that’s performing 3D rendering should pause any real-time proc
essing | 311 application that’s performing 3D rendering should pause any real-time proc
essing |
331 and yield cycles to other processes when its tab becomes inactive.</p> | 312 and 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 | 313 <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 wil
l be automatically | 314 <code>setInterval</code> and <code>setTimeout</code>) but the timer interval wil
l be automatically |
334 overridden and limited to not less than one second while the tab is inactive. In | 315 overridden and limited to not less than one second while the tab is inactive. In |
335 addition, any callback associated with a <code>SwapBuffers</code> call will not
be sent | 316 addition, any callback associated with a <code>SwapBuffers</code> call will not
be sent |
336 until the tab is active again. You may receive asynchronous callbacks from | 317 until the tab is active again. You may receive asynchronous callbacks from |
337 functions other than <code>SwapBuffers</code> while a tab is inactive. Depending
on the | 318 functions other than <code>SwapBuffers</code> while a tab is inactive. Depending
on the |
338 design of your application, you might choose to handle them as they arrive, or | 319 design of your application, you might choose to handle them as they arrive, or |
339 to queue them in a buffer and process them when the tab becomes active.</p> | 320 to 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 mai
n | 321 <p>The time that passes while a tab is inactive can be considerable. If your mai
n |
341 thread pulse is based on the <code>SwapBuffers</code> callback, your app wonR
17;t update | 322 thread pulse is based on the <code>SwapBuffers</code> callback, your app wonR
17;t update |
342 while a tab is inactive. A Native Client module should be able to detect and | 323 while a tab is inactive. A Native Client module should be able to detect and |
343 respond to the state of the tab in which it’s running. For example, when a
tab | 324 respond to the state of the tab in which it’s running. For example, when a
tab |
344 becomes inactive, you can set an atomic flag in the Native Client thread that | 325 becomes inactive, you can set an atomic flag in the Native Client thread that |
345 will skip the 3D rendering and <code>SwapBuffers</code> calls and continue to ca
ll the | 326 will skip the 3D rendering and <code>SwapBuffers</code> calls and continue to ca
ll the |
346 main thread every 30 msec or so. This provides time to update features that | 327 main thread every 30 msec or so. This provides time to update features that |
347 should still run in the background, like audio. It may also be helpful to call | 328 should 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 | 329 <code>sched_yield</code> or <code>usleep</code> on any worker threads to release
resources and |
349 cede cycles to the OS.</p> | 330 cede 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 fr
om the main thread</h3> | 331 <h3 id="handling-tab-activation-from-the-main-thread">Handling tab activation fr
om the main thread</h3> |
352 <p>You can detect and respond to the activation or deactivation of a tab with | 332 <p>You can detect and respond to the activation or deactivation of a tab with |
353 JavaScript on your hosting page. Add an EventListener for <code>visibilitychange
</code> | 333 JavaScript on your hosting page. Add an EventListener for <code>visibilitychange
</code> |
354 that sends a message to the Native Client module, as in this example:</p> | 334 that sends a message to the Native Client module, as in this example:</p> |
355 <pre class="prettyprint"> | 335 <pre class="prettyprint"> |
356 document.addEventListener('visibilitychange', function(){ | 336 document.addEventListener('visibilitychange', function(){ |
357 if (document.hidden) { | 337 if (document.hidden) { |
358 // PostMessage to your Native Client module | 338 // PostMessage to your Native Client module |
359 document.nacl_module.postMessage('INACTIVE'); | 339 document.nacl_module.postMessage('INACTIVE'); |
360 } else { | 340 } else { |
361 // PostMessage to your Native Client module | 341 // PostMessage to your Native Client module |
362 document.nacl_module.postMessage('ACTIVE'); | 342 document.nacl_module.postMessage('ACTIVE'); |
363 } | 343 } |
364 | 344 |
365 }, false); | 345 }, false); |
366 </pre> | 346 </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 acti
vation from the Native Client thread</h3> | 347 <h3 id="handling-tab-activation-from-the-native-client-thread">Handling tab acti
vation from the Native Client thread</h3> |
369 <p>You can also detect and respond to the activation or deactivation of a tab | 348 <p>You can also detect and respond to the activation or deactivation of a tab |
370 directly from your Native Client module by including code in the function | 349 directly from your Native Client module by including code in the function |
371 <code>pp::Instance::DidChangeView</code>, which is called whenever a change in t
he | 350 <code>pp::Instance::DidChangeView</code>, which is called whenever a change in t
he |
372 module’s view occurs. The code can call <code>ppb::View::IsPageVisible</co
de> to | 351 module’s view occurs. The code can call <code>ppb::View::IsPageVisible</co
de> to |
373 determine if the page is visible or not. The most common cause of invisible | 352 determine if the page is visible or not. The most common cause of invisible |
374 pages is that the page is in a background tab.</p> | 353 pages 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> | 354 <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 | 355 <p>Here are some suggestions for writing safe code and getting the maximum |
378 performance with the Pepper 3D API.</p> | 356 performance with the Pepper 3D API.</p> |
379 <section id="do-s"> | |
380 <h3 id="do-s">Do’s</h3> | 357 <h3 id="do-s">Do’s</h3> |
381 <ul class="small-gap"> | 358 <ul class="small-gap"> |
382 <li><p class="first"><strong>Make sure to enable attrib 0.</strong> OpenGL requi
res that you enable attrib 0, | 359 <li><p class="first"><strong>Make sure to enable attrib 0.</strong> OpenGL requi
res that you enable attrib 0, |
383 but OpenGL ES 2.0 does not. For example, you can define a vertex shader with 2 | 360 but OpenGL ES 2.0 does not. For example, you can define a vertex shader with 2 |
384 attributes, numbered like this:</p> | 361 attributes, numbered like this:</p> |
385 <pre class="prettyprint"> | 362 <pre class="prettyprint"> |
386 glBindAttribLocation(program, "positions", 1); | 363 glBindAttribLocation(program, "positions", 1); |
387 glBindAttribLocation(program, "normals", 2); | 364 glBindAttribLocation(program, "normals", 2); |
388 </pre> | 365 </pre> |
389 <p>In this case the shader is not using attrib 0 and Chrome may have to perform | 366 <p>In this case the shader is not using attrib 0 and Chrome may have to perform |
(...skipping 11 matching lines...) Expand all Loading... |
401 issues, it may be beneficial to perform scaling via CSS. The size your plugin | 378 issues, it may be beneficial to perform scaling via CSS. The size your plugin |
402 renders is determined by the width and height attributes of the <code><embed&
gt;</code> | 379 renders is determined by the width and height attributes of the <code><embed&
gt;</code> |
403 element for the module. The actual size displayed on the web page is | 380 element for the module. The actual size displayed on the web page is |
404 controlled by the CSS styles applied to the element.</li> | 381 controlled by the CSS styles applied to the element.</li> |
405 <li><strong>Avoid matrix-to-matrix conversions.</strong> With some versions of M
ac OS, there is | 382 <li><strong>Avoid matrix-to-matrix conversions.</strong> With some versions of M
ac OS, there is |
406 a driver problem when compiling shaders. If you get compiler errors for matrix | 383 a driver problem when compiling shaders. If you get compiler errors for matrix |
407 transforms, avoid matrix-to-matrix conversions. For instance, upres a vec3 to | 384 transforms, avoid matrix-to-matrix conversions. For instance, upres a vec3 to |
408 a vec4 before transforming it by a mat4, rather than converting the mat4 to a | 385 a vec4 before transforming it by a mat4, rather than converting the mat4 to a |
409 mat3.</li> | 386 mat3.</li> |
410 </ul> | 387 </ul> |
411 </section><section id="don-ts"> | |
412 <h3 id="don-ts">Don’ts</h3> | 388 <h3 id="don-ts">Don’ts</h3> |
413 <ul class="small-gap"> | 389 <ul class="small-gap"> |
414 <li><strong>Don’t use client side buffers.</strong> OpenGL ES 2.0 can use
client side data with | 390 <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 | 391 <code>glVertexAttribPointer</code> and <code>glDrawElements</code>, but this is
really slow. Try |
416 to avoid client side buffers. Use Vertex Buffer Objects (VBOs) instead.</li> | 392 to 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, Pep
per 3D binds buffers | 393 <li><strong>Don’t mix vertex data and index data.</strong> By default, Pep
per 3D binds buffers |
418 to a single point. You could create a buffer and bind it to both | 394 to 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 | 395 <code>GL_ARRAY_BUFFER</code> and <code>GL_ELEMENT_ARRAY_BUFFER</code>, but that
would be |
420 expensive overhead and it is not recommended.</li> | 396 expensive overhead and it is not recommended.</li> |
421 <li><strong>Don’t call ``glGet*`` or ``glCheck*`` during rendering.</stron
g> This is normal | 397 <li><strong>Don’t call ``glGet*`` or ``glCheck*`` during rendering.</stron
g> This is normal |
422 advice for OpenGL programs, but is particularly important for 3D on | 398 advice for OpenGL programs, but is particularly important for 3D on |
423 Chrome. Calls to any OpenGL ES 2.0 function whose name begins with these | 399 Chrome. Calls to any OpenGL ES 2.0 function whose name begins with these |
424 strings blocks the Native Client thread. This includes <code>glGetError</code>;
avoid | 400 strings blocks the Native Client thread. This includes <code>glGetError</code>;
avoid |
425 calling it in release builds.</li> | 401 calling it in release builds.</li> |
426 <li><strong>Don’t use fixed point (``GL_FIXED``) vertex attributes.</stron
g> Fixed point | 402 <li><strong>Don’t use fixed point (``GL_FIXED``) vertex attributes.</stron
g> Fixed point |
427 attributes are not supported in OpenGL ES 2.0, so emulating them in OpenGL ES | 403 attributes are not supported in OpenGL ES 2.0, so emulating them in OpenGL ES |
428 2.0 is slow. By default, <code>GL_FIXED</code> support is turned off in the Pepp
er 3D | 404 2.0 is slow. By default, <code>GL_FIXED</code> support is turned off in the Pepp
er 3D |
429 API.</li> | 405 API.</li> |
430 <li><strong>Don’t read data from the GPU.</strong> Don’t call <code>
glReadPixels</code>, as it is slow.</li> | 406 <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 th
e current OpenGL ES | 407 <li><strong>Don’t update a small portion of a large buffer.</strong> In th
e current OpenGL ES |
432 2.0 implementation when you update a portion of a buffer (with | 408 2.0 implementation when you update a portion of a buffer (with |
433 <code>glSubBufferData</code> for example) the entire buffer must be reprocessed.
To | 409 <code>glSubBufferData</code> for example) the entire buffer must be reprocessed.
To |
434 avoid this problem, keep static and dynamic data in different buffers.</li> | 410 avoid 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 O
penGL ES 2.0 | 411 <li><strong>Don’t call ``glDisable(GL_TEXTURE_2D)``.</strong> This is an O
penGL ES 2.0 |
436 error. Each time it is called, an error messages will appear in Chrome’s | 412 error. Each time it is called, an error messages will appear in Chrome’s |
437 <code>about:gpu</code> tab.</li> | 413 <code>about:gpu</code> tab.</li> |
438 </ul> | 414 </ul> |
439 </section></section></section> | 415 </section> |
440 | 416 |
441 {{/partials.standard_nacl_article}} | 417 {{/partials.standard_nacl_article}} |
OLD | NEW |