summaryrefslogtreecommitdiffstats
path: root/native_client_sdk/src
diff options
context:
space:
mode:
authorstichnot@chromium.org <stichnot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-25 20:21:48 +0000
committerstichnot@chromium.org <stichnot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-25 20:21:48 +0000
commit5a8ba4666b5cae5f18d7dab6e7fda7e9b7f8b11c (patch)
treeebf41667d7139c809da833d2217abd5fbb4528e7 /native_client_sdk/src
parent81500215d19fc4868bda4c6199bc3b3cfca0b491 (diff)
downloadchromium_src-5a8ba4666b5cae5f18d7dab6e7fda7e9b7f8b11c.zip
chromium_src-5a8ba4666b5cae5f18d7dab6e7fda7e9b7f8b11c.tar.gz
chromium_src-5a8ba4666b5cae5f18d7dab6e7fda7e9b7f8b11c.tar.bz2
Convert 3D Graphics section from devsite to reST.
BUG= https://code.google.com/p/nativeclient/issues/detail?id=3634 NOTRY=true (documentation-only change) Review URL: https://codereview.chromium.org/24824002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@231101 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk/src')
-rw-r--r--native_client_sdk/src/doc/devguide/coding/3D-graphics.rst506
-rw-r--r--native_client_sdk/src/doc/images/3d-graphics-render-loop.pngbin20414 -> 21468 bytes
2 files changed, 505 insertions, 1 deletions
diff --git a/native_client_sdk/src/doc/devguide/coding/3D-graphics.rst b/native_client_sdk/src/doc/devguide/coding/3D-graphics.rst
index 5c432e0..7ca1513 100644
--- a/native_client_sdk/src/doc/devguide/coding/3D-graphics.rst
+++ b/native_client_sdk/src/doc/devguide/coding/3D-graphics.rst
@@ -4,5 +4,509 @@
3D Graphics
###########
-foo
+Native Client applications use the `OpenGL ES 2.0
+<http://en.wikipedia.org/wiki/OpenGL_ES>`_ API for 3D rendering. This document
+describes how to call the OpenGL ES 2.0 interface in a Native Client module and
+how to build an efficient rendering loop. It also explains how to validate GPU
+drivers and test for specific GPU capabilities, and provides tips to help ensure
+your rendering code runs efficiently.
+.. Note::
+ :class: note
+
+ **Note**: 3D drawing and OpenGL are complex topics. This document deals only
+ with issues directly related to programming in the Native Client
+ environment. To learn more about OpenGL ES 2.0 itself, see the `OpenGL ES 2.0
+ Programming Guide <http://opengles-book.com/>`_.
+
+Validating the client graphics platform
+=======================================
+
+Native Client is a software technology that lets you code an application once
+and run it on multiple platforms without worrying about the implementation
+details on every possible target platform. It's difficult to provide the same
+support at the hardware level. Graphics hardware comes from many different
+manufacturers and is controlled by drivers of varying quality. A particular GPU
+driver may not support every OpenGL ES 2.0 feature, and some drivers are known
+to have vulnerabilities that can be exploited.
+
+Even if the GPU driver is safe to use, your program should perform a validation
+check before you launch your application to ensure that the driver supports all
+the features you need.
+
+Vetting the driver in JavaScript
+--------------------------------
+
+At startup, the application should perform a few additional tests that can be
+implemented in JavaScript on its hosting web page. The script that performs
+these tests should be included before the module's ``embed`` tag, and ideally
+the ``embed`` tag should appear on the hosting page only if these tests succeed.
+
+The first thing to check is whether you can create a graphics context. If you
+can, use the context to confirm the existence of any required OpenGL ES 2.0
+extensions. You may want to refer to the `extension registry
+<http://www.khronos.org/registry/webgl/extensions/>`_ and include `vendor
+prefixes <https://developer.mozilla.org/en-US/docs/WebGL/Using_Extensions>`_
+when checking for extensions.
+
+Vetting the driver in Native Client
+-----------------------------------
+
+Create a context
+^^^^^^^^^^^^^^^^
+
+Once you've passed the JavaScript validation tests, it's safe to add a Native
+Client embed tag to the hosting web page and load the module. As part of the
+module initialization code, you must create a graphics context for the app by
+either creating a C++ ``Graphics3D`` object or calling ``PPB_Graphics3D`` API
+function ``Create``. Don't assume this will always succeed; you still might have
+problems creating the context. If you are in development mode and can't create
+the context, try creating a simpler version to see if you're asking for an
+unsupported feature or exceeding a driver resource limit. Your production code
+should always check that the context was created and fail gracefully if that's
+not the case.
+
+Check for extensions and capabilities
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Not every GPU supports every extension or has the same amount of texture units,
+vertex attributes, etc. On startup, call ``glGetString(GLEXTENSIONS)`` and check
+for the extensions and the features you need. For example:
+
+* If you are using non power-of-2 texture with mipmaps, make sure
+ ``GL_OES_texture_npot`` exists.
+
+* If you are using floating point textures, make sure ``GL_OES_texture_float``
+ exists.
+
+* If you are using DXT1, DXT3, or DXT5 textures, make sure the corresponding
+ extensions ``EXT_texture_compression_dxt1``,
+ ``GL_CHROMIUM_texture_compression_dxt3``, and
+ ``GL_CHROMIUM_texture_compression_dxt5`` exist.
+
+Check for system capabilites with ``glGetIntegerv`` and adjust shader programs
+as well as texture and vertex data accordingly:
+
+* If you are using textures in vertex shaders, make sure
+ ``glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, ...)`` and
+ ``glGetIntegerv(GL_MAX_TEXTURE_SIZE, ...)`` return values greater than 0.
+
+* If you are using more than 8 textures in a single shader, make sure
+ ``glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, ...)`` returns a value greater
+ than or equal to the number of simultaneous textures you need.
+
+Vetting the driver in the Chrome Web Store
+------------------------------------------
+
+If you choose to place your application in the `Chrome Web
+Store <https://developers.google.com/chrome/web-store/docs/>`_, its Web Store
+`manifest file <http://code.google.com/chrome/extensions/manifest.html>`_ can
+include the ``webgl`` feature in the requirements parameter. It looks like this:
+
+.. naclcode::
+
+ "requirements": {
+ "3D": {
+ "features": ["webgl"]
+ }
+ }
+
+While WebGL is technically a JavaScript API, specifying the ``webgl`` feature
+also works for OpenGL ES 2.0 because both interfaces use the same driver.
+
+This manifest item is not required, but if you include it, the Chrome Web Store
+will prevent a user from installing the application if the browser is running on
+a machine that does not support OpenGL ES 2.0 or that is using a known
+blacklisted GPU driver that could invite an attack.
+
+If the Web Store determines that the user's driver is deficient, the app won't
+appear on the store's tile display. However, it will appear in store search
+results or if the user links to it directly, in which case the user could still
+download it. But the manifest requirements will be checked when the user reaches
+the install page, and if there is a problem, the browser will display the
+message "This application is not supported on this computer. Installation has
+been disabled."
+
+The manifest-based check applies only to downloads directly from the Chrome Web
+Store. It is not performed when an application is loaded via `inline
+installation
+<https://developers.google.com/chrome/web-store/docs/inline_installation>`_.
+
+What to do when there are problems
+----------------------------------
+
+Using the vetting procedure described above, you should be able to detect the
+most common problems before your application runs. If there are problems, your
+code should describe the issue as clearly as possible. That's easy if there is a
+missing feature. Failure to create a graphics context is tougher to diagnose. At
+the very least, you can suggest that the user try to update the driver. You
+might want to linke to the Chrome page that describes `how to do updates
+<http://support.google.com/chrome/bin/answer.py?hl=en&answer=1202946>`_.
+
+If a user can't update the driver, or their problem persists, be sure to gather
+information about their graphics environment. Ask for the contents of the Chrome
+``about:gpu`` page.
+
+Document unreliable drivers
+---------------------------
+
+It can be helpful to include information about known dubious drivers in your
+user documentation. This might help identify if a rogue driver is the cause of a
+problem. There are many sources of GPU driver blacklists. Two such lists can be
+found at the `Chromium project
+<http://src.chromium.org/viewvc/chrome/trunk/deps/gpu/software_rendering_list/software_rendering_list.json>`_
+and `Khronos <http://www.khronos.org/webgl/wiki/BlacklistsAndWhitelists>`_. You
+can use these lists to include information in your documentation that warns
+users about dangerous drivers.
+
+Test your defenses
+------------------
+
+You can test your driver validation code by running Chrome with the following
+flags (all at once) and watching how your application responds:
+
+* ``--disable-webgl``
+* ``--disable-pepper-3d``
+* ``--disable-gl-multisampling``
+* ``--disable-accelerated-compositing``
+* ``--disable-accelerated-2d-canvas``
+
+Calling OpenGL ES 2.0 commands
+==============================
+
+There are three ways to write OpenGL ES 2.0 calls in Native Client.
+
+Use "pure" OpenGL ES 2.0 function calls
+---------------------------------------
+
+You can make OpenGL ES 2.0 calls through a Pepper extension library. The SDK
+example ``examples/api/graphics_3d`` works this way. In the file
+``graphics_3d.cc``, the key initialization steps are as follows:
+
+* Add these includes at the top of the file:
+
+ .. naclcode::
+
+ #include <GLES2/gl2.h>
+ #include "ppapi/lib/gl/gles2/gl2ext_ppapi.h"
+
+* Define the function ``InitGL``. The exact specification of ``attrib_list``
+ will be application specific.
+
+ .. naclcode::
+
+ bool InitGL(int32_t new_width, int32_t new_height) {
+ if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface())) {
+ fprintf(stderr, "Unable to initialize GL PPAPI!\n");
+ return false;
+ }
+
+ const int32_t attrib_list[] = {
+ PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24,
+ PP_GRAPHICS3DATTRIB_WIDTH, new_width,
+ PP_GRAPHICS3DATTRIB_HEIGHT, new_height,
+ PP_GRAPHICS3DATTRIB_NONE
+ };
+
+ context_ = pp::Graphics3D(this, attrib_list);
+ if (!BindGraphics(context_)) {
+ fprintf(stderr, "Unable to bind 3d context!\n");
+ context_ = pp::Graphics3D();
+ glSetCurrentContextPPAPI(0);
+ return false;
+ }
+
+ glSetCurrentContextPPAPI(context_.pp_resource());
+ return true;
+ }
+
+* Include logic in ``Instance::DidChangeView`` to call ``InitGL`` whenever
+ necessary: upon application launch (when the graphics context is NULL) and
+ whenever the module's View changes size.
+
+Use Regal
+---------
+
+If you are porting an OpenGL ES 2.0 application, or are comfortable writing in
+OpenGL ES 2.0, you should stick with the Pepper APIs or pure OpenGL ES 2.0 calls
+described above. If you are porting an application that uses features not in
+OpenGL ES 2.0, consider using Regal. Regal is an open source library that
+supports many versions of OpenGL. Regal recently added support for Native
+Client. Regal forwards most OpenGL calls directly to the underlying graphics
+library, but it can also emulate other calls that are not included (when
+hardware support exists). See `libregal
+<http://www.altdevblogaday.com/2012/09/04/bringing-regal-opengl-to-native-client/>`_
+for more info.
+
+Use the Pepper API
+------------------
+
+Your code can call the Pepper `PPB_OpenGLES2
+<https://developers.google.com/native-client/pepperc/struct_p_p_b___open_g_l_e_s2>`_
+API directly, as with any Pepper interface. When you write in this way, each
+invocation of an OpenGL ES 2.0 function must begin with a reference to the
+Pepper interface, and the first argument is the graphics context. To invoke the
+function ``glCompileShader``, your code might look like:
+
+.. naclcode::
+
+ ppb_g3d_interface->CompileShader(graphicsContext, shader);
+
+This approach specifically targets the Pepper APIs. Each call corresponds to a
+OpenGL ES 2.0 function, but the syntax is unique to Native Client, so the source
+file is not portable.
+
+Implementing a rendering loop
+=============================
+
+Graphics applications require a continuous frame render-and-redraw cycle that
+runs at a high frequency. To achieve the best frame rate, is important to
+understand how the OpenGL ES 2.0 code in a Native Client module interacts with
+Chrome.
+
+The Chrome and Native Client processes
+--------------------------------------
+
+Chrome is a multi-process browser. Each Chrome tab is a separate process that is
+running an application with its own main thread (we'll call it the Chrome main
+thread). When an application launches a Native Client module, the module runs in
+a new, separate sandboxed process. The module's process has its own main thread
+(the Native Client thread). The Chrome and Native Client processes communicate
+with each other using Pepper API calls on their main threads.
+
+When the Chrome main thread calls the Native Client thread (keyboard and mouse
+callbacks, for example), the Chrome main thread will block. This means that
+lengthy operations on the Native Client thread can steal cycles from Chrome, and
+performing blocking operations on the Native Client thread can bring your app to
+a standstill.
+
+Native Client uses callback functions to synchronize the main threads of the two
+processes. Only certain Pepper functions use callbacks; `SwapBuffers
+<https://developers.google.com/native-client/pepperc/struct_p_p_b___graphics3_d__1__0#a293c6941c0da084267ffba3954793497>`_
+is one.
+
+``SwapBuffers`` and its callback function
+-----------------------------------------
+
+``SwapBuffers`` is non-blocking; it is called from the Native Client thread and
+returns immediately. When ``SwapBuffers`` is called, it runs asynchronously on
+the Chrome main thread. It switches the graphics data buffers, handles any
+needed compositing operations, and redraws the screen. When the screen update is
+complete, the callback function that was included as one of ``SwapBuffer``'s
+arguments will be called from the Chrome thread and executed on the Native
+Client thread.
+
+To create a rendering loop, your Native Client module should include a function
+that does the rendering work and then executes ``SwapBuffers``, passing itself
+as the ``SwapBuffer`` callback. If your rendering code is efficient and runs
+quickly, this scheme will achieve the highest frame rate possible. The
+documentation for ``SwapBuffers`` explains why this is optimal: because the
+callback is executed only when the plugin's current state is actually on the
+screen, this function provides a way to rate-limit animations. By waiting until
+the image is on the screen before painting the next frame, you can ensure you're
+not generating updates faster than the screen can be updated.
+
+The following diagram illustrates the interaction between the Chrome and Native
+Client processes. The application-specific rendering code runs in the function
+called ``Draw`` on the Native Client thread. Blue down-arrows are blocking calls
+from the main thread to Native Client, green up-arrows are non-blocking
+``SwapBuffers`` calls from Native Client to the main thread. All OpenGL ES 2.0
+calls are made from ``Draw`` in the Native Client thread.
+
+.. image:: /images/3d-graphics-render-loop.png
+
+SDK example ``graphics_3d``
+---------------------------
+
+The SDK example ``graphics_3d`` uses the function ``MainLoop`` (in
+``hello_world.cc``) to create a rendering loop as described above. ``MainLoop``
+calls ``Render`` to do the rendering work, and then invokes ``SwapBuffers``,
+passing itself as the callback.
+
+.. naclcode::
+
+ void MainLoop(void* foo, int bar) {
+ if (g_LoadCnt == 3) {
+ InitProgram();
+ g_LoadCnt++;
+ }
+ if (g_LoadCnt > 3) {
+ Render();
+ PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0);
+ ppb_g3d_interface->SwapBuffers(g_context, cc);
+ } else {
+ PP_CompletionCallback cc = PP_MakeCompletionCallback(MainLoop, 0);
+ ppb_core_interface->CallOnMainThread(0, cc, 0);
+ }
+ }
+
+Managing the OpenGL ES 2.0 pipeline
+===================================
+
+OpenGL ES 2.0 commands do not run in the Chrome or Native Client processes. They
+are passed into a FIFO queue in shared memory which is best understood as a `GPU
+command buffer
+<http://www.chromium.org/developers/design-documents/gpu-command-buffer>`_. The
+command buffer is shared by a dedicated GPU process. By using a separate GPU
+process, Chrome implements another layer of runtime security, vetting all OpenGL
+ES 2.0 commands and their arguments before they are sent on to the
+GPU. Buffering commands through the FIFO also speeds up your code, since each
+OpenGL ES 2.0 call in your Native Client thread returns immediately, while the
+processing may be delayed as the GPU works down the commands queued up in the
+FIFO.
+
+Before the screen is updated, all the intervening OpenGL ES 2.0 commands must be
+processed by the GPU. Programmers often try to ensure this by using the
+``glFlush`` and ``glFinish`` commands in their rendering code. In the case of
+Native Client this is usually unnecessary. The ``SwapBuffers`` command does an
+implicit flush, and the Chrome team is continually tweaking the GPU code to
+consume the OpenGL ES 2.0 FIFO as fast as possible.
+
+Sometimes a 3D application can write to the FIFO in a way that's difficult to
+handle. The command pipeline may fill up and your code will have to wait for the
+GPU to flush the FIFO. If this is the case, you may be able to add ``glFlush``
+calls to speed up the flow of the OpenGL ES 2.0 command FIFO. Before you start
+to add your own flushes, first try to determine if pipeline saturation is really
+the problem by monitoring the rendering time per frame and looking for irregular
+spikes that do not consistently fall on the same OpenGL ES 2.0 call. If you're
+convinced the pipeline needs to be accelerated, insert ``glFlush`` calls in your
+code before starting blocks of processing that do not generate OpenGL ES 2.0
+commands. For example, issue a flush before you begin any multithreaded particle
+work, so that the command buffer will be clear when you start doing OpenGL ES
+2.0 calls again. Determining where and how often to call ``glFlush`` can be
+tricky, you will need to experiment to find the sweet spot.
+
+Rendering and inactive tabs
+===========================
+
+Users will often switch between tabs in a multi-tab browser. A well-behaved
+application that's performing 3D rendering should pause any real-time processing
+and yield cycles to other processes when its tab becomes inactive.
+
+In Chrome, an inactive tab will continue to execute timed functions (such as
+``setInterval`` and ``setTimeout``) but the timer interval will be automatically
+overridden and limited to not less than one second while the tab is inactive. In
+addition, any callback associated with a ``SwapBuffers`` call will not be sent
+until the tab is active again. You may receive asynchronous callbacks from
+functions other than ``SwapBuffers`` while a tab is inactive. Depending on the
+design of your application, you might choose to handle them as they arrive, or
+to queue them in a buffer and process them when the tab becomes active.
+
+The time that passes while a tab is inactive can be considerable. If your main
+thread pulse is based on the ``SwapBuffers`` callback, your app won't update
+while a tab is inactive. A Native Client module should be able to detect and
+respond to the state of the tab in which it's running. For example, when a tab
+becomes inactive, you can set an atomic flag in the Native Client thread that
+will skip the 3D rendering and ``SwapBuffers`` calls and continue to call the
+main thread every 30 msec or so. This provides time to update features that
+should still run in the background, like audio. It may also be helpful to call
+``sched_yield`` or ``usleep`` on any worker threads to release resources and
+cede cycles to the OS.
+
+Handling tab activation from the main thread
+--------------------------------------------
+
+You can detect and respond to the activation or deactivation of a tab with
+JavaScript on your hosting page. Add an EventListener for ``visibilitychange``
+that sends a message to the Native Client module, as in this example:
+
+.. naclcode::
+
+ document.addEventListener('visibilitychange', function(){
+ if (document.hidden) {
+ // PostMessage to your Native Client module
+ document.nacl_module.postMessage('INACTIVE');
+ } else {
+ // PostMessage to your Native Client module
+ document.nacl_module.postMessage('ACTIVE');
+ }
+
+ }, false);
+
+Handling tab activation from the Native Client thread
+-----------------------------------------------------
+
+You can also detect and respond to the activation or deactivation of a tab
+directly from your Native Client module by including code in the function
+``pp::Instance::DidChangeView``, which is called whenever a change in the
+module's view occurs. The code can call ``ppb::View::IsPageVisible`` to
+determine if the page is visible or not. The most common cause of invisible
+pages is that the page is in a background tab.
+
+Tips and best practices
+=======================
+
+Here are some suggestions for writing safe code and getting the maximum
+performance with the Pepper 3D API.
+
+Do's
+----
+
+* **Make sure to enable attrib 0.** OpenGL requires that you enable attrib 0,
+ but OpenGL ES 2.0 does not. For example, you can define a vertex shader with 2
+ attributes, numbered like this:
+
+ .. naclcode::
+
+ glBindAttribLocation(program, "positions", 1);
+ glBindAttribLocation(program, "normals", 2);
+
+ In this case the shader is not using attrib 0 and Chrome may have to perform
+ some additional work if it is emulating OpenGL ES 2.0 on top of OpenGL. It's
+ always more efficient to enable attrib 0, even if you do not use it.
+
+* **Check how shaders compile.** Shaders can compile differently on different
+ systems, which can result in ``glGetAttrib*`` functions returning different
+ results. Be sure that the vertex attribute indices match the corresponding
+ name each time you recompile a shader.
+
+* **Update indices sparingly.** For security reasons, all indices must be
+ validated. If you change indices, Native Client will validate them
+ again. Structure your code so indices are not updated often.
+
+* **Use a smaller plugin and let CSS scale it.** If you're running into fillrate
+ issues, it may be beneficial to perform scaling via CSS. The size your plugin
+ renders is determined by the width and height attributes of the ``<embed>``
+ element for the module. The actual size displayed on the web page is
+ controlled by the CSS styles applied to the element.
+
+* **Avoid matrix-to-matrix conversions.** With some versions of Mac OS, there is
+ a driver problem when compiling shaders. If you get compiler errors for matrix
+ transforms, avoid matrix-to-matrix conversions. For instance, upres a vec3 to
+ a vec4 before transforming it by a mat4, rather than converting the mat4 to a
+ mat3.
+
+Don'ts
+------
+
+* **Don't use client side buffers.** OpenGL ES 2.0 can use client side data with
+ ``glVertexAttribPointer`` and ``glDrawElements``, but this is really slow. Try
+ to avoid client side buffers. Use Vertex Buffer Objects (VBOs) instead.
+
+* **Don't mix vertex data and index data.** By default, Pepper 3D binds buffers
+ to a single point. You could create a buffer and bind it to both
+ ``GL_ARRAY_BUFFER`` and ``GL_ELEMENT_ARRAY_BUFFER``, but that would be
+ expensive overhead and it is not recommended.
+
+* **Don't call ``glGet*`` or ``glCheck*`` during rendering.** This is normal
+ advice for OpenGL programs, but is particularly important for 3D on
+ Chrome. Calls to any OpenGL ES 2.0 function whose name begins with these
+ strings blocks the Native Client thread. This includes ``glGetError``; avoid
+ calling it in release builds.
+
+* **Don't use fixed point (``GL_FIXED``) vertex attributes.** Fixed point
+ attributes are not supported in OpenGL ES 2.0, so emulating them in OpenGL ES
+ 2.0 is slow. By default, ``GL_FIXED`` support is turned off in the Pepper 3D
+ API.
+
+* **Don't read data from the GPU.** Don't call ``glReadPixels``, as it is slow.
+
+* **Don't update a small portion of a large buffer.** In the current OpenGL ES
+ 2.0 implementation when you update a portion of a buffer (with
+ ``glSubBufferData`` for example) the entire buffer must be reprocessed. To
+ avoid this problem, keep static and dynamic data in different buffers.
+
+* **Don't call ``glDisable(GL_TEXTURE_2D)``.** This is an OpenGL ES 2.0
+ error. Each time it is called, an error messages will appear in Chrome's
+ ``about:gpu`` tab.
diff --git a/native_client_sdk/src/doc/images/3d-graphics-render-loop.png b/native_client_sdk/src/doc/images/3d-graphics-render-loop.png
index 66e1121..3d2aac5 100644
--- a/native_client_sdk/src/doc/images/3d-graphics-render-loop.png
+++ b/native_client_sdk/src/doc/images/3d-graphics-render-loop.png
Binary files differ