Home | History | Annotate | Download | only in opengl
      1 Android Hardware OpenGLES emulation design overview
      2 ===================================================
      3 
      4 Introduction:
      5 -------------
      6 
      7 Hardware GLES emulation in the Android platform is implemented with a mix
      8 of components, which are:
      9 
     10   - Several host "translator" libraries. They implement the EGL, GLES 1.1 and
     11     GLES 2.0 ABIs defined by Khronos, and translate the corresponding function
     12     calls into calls to the appropriate desktop APIs, i.e.:
     13 
     14       - Xgl (Linux), AGL (OS X) or WGL (Windows) for EGL
     15       - desktop GL 2.0 for GLES 1.1 and GLES 2.0
     16 
     17          _________            __________          __________
     18         |         |          |          |        |          |     HOST
     19         |TRANSLATOR          |TRANSLATOR|        |TRANSLATOR|     HOST
     20         |   EGL   |          | GLES 1.1 |        | GLES 2.0 |     TRANSLATOR
     21         |_________|          |__________|        |__________|     LIBRARIES
     22              |                    |                    |
     23        - - - | - - - - - - - - -  | - - - - - - - - -  | - - - - -
     24              |                    |                    |
     25          ____v____            ____v_____          _____v____      HOST
     26         |         |          |          |        |          |     SYSTEM
     27         |   Xgl   |          |  GL 2.0  |        |  GL 2.0  |     LIBRARIES
     28         |_________|          |__________|        |__________|
     29 
     30 
     31 
     32   - Several system libraries inside the emulated guest system that implement
     33     the same EGL / GLES 1.1 and GLES 2.0 ABIs.
     34 
     35     They collect the sequence of EGL/GLES function calls and translate then
     36     into a custom wire protocol stream that is sent to the emulator program
     37     through a high-speed communication channel called a "QEMU Pipe".
     38 
     39     For now, all you need to know is that the pipe is implemented with a
     40     custom kernel driver, and provides for _very_ fast bandwidth. All read()
     41     and writes() from/to the pipes are essentially instantaneous from the
     42     guest's point of view.
     43 
     44 
     45          _________            __________          __________
     46         |         |          |          |        |          |
     47         |EMULATION|          |EMULATION |        |EMULATION |     GUEST
     48         |   EGL   |          | GLES 1.1 |        | GLES 2.0 |     SYSTEM
     49         |_________|          |__________|        |__________|     LIBRARIES
     50              |                    |                    |
     51        - - - | - - - - - - - - -  | - - - - - - - - -  | - - - - -
     52              |                    |                    |
     53          ____v____________________v____________________v____      GUEST
     54         |                                                   |     KERNEL
     55         |                       QEMU PIPE                   |
     56         |___________________________________________________|
     57                                   |
     58        - - - - - - - - - - - - - -|- - - - - - - - - - - - - - - -
     59                                   |
     60                                   v
     61                                EMULATOR
     62 
     63   - Specific code inside the emulator program that is capable of transmitting
     64     the wire protocol stream to a special rendering library or process (called
     65     the "renderer" here), which understands the format.
     66 
     67                                  |
     68                                  |    PROTOCOL BYTE STREAM
     69                             _____v_____
     70                            |           |
     71                            |  EMULATOR |
     72                            |___________|
     73                                  |
     74                                  |   UNMODIFIED PROTOCOL BYTE STREAM
     75                             _____v_____
     76                            |           |
     77                            |  RENDERER |
     78                            |___________|
     79 
     80 
     81   - The renderer decodes the EGL/GLES commands from the wire
     82     protocol stream, and dispatches them to the translator libraries
     83     appropriately.
     84 
     85                                  |
     86                                  |   PROTOCOL BYTE STREAM
     87                             _____v_____
     88                            |           |
     89                            |  RENDERER |
     90                            |___________|
     91                                | |  |
     92              +-----------------+ |  +-----------------+
     93              |                   |                    |
     94          ____v____            ___v______          ____v_____
     95         |         |          |          |        |          |     HOST
     96         |TRANSLATOR          |TRANSLATOR|        |TRANSLATOR|     HOST
     97         |   EGL   |          | GLES 1.1 |        | GLES 2.0 |     TRANSLATOR
     98         |_________|          |__________|        |__________|     LIBRARIES
     99 
    100 
    101 
    102   - In reality, the protocol stream flows in both directions, even though most
    103     of the commands result in data going from the guest to the host. A complete
    104     picture of the emulation would thus be:
    105 
    106 
    107 
    108 
    109 
    110          _________            __________          __________
    111         |         |          |          |        |          |
    112         |EMULATION|          |EMULATION |        |EMULATION |     GUEST
    113         |   EGL   |          | GLES 1.1 |        | GLES 2.0 |     SYSTEM
    114         |_________|          |__________|        |__________|     LIBRARIES
    115              ^                    ^                    ^
    116              |                    |                    |
    117        - - - | - - - - - - - - -  | - - - - - - - - -  | - - - - -
    118              |                    |                    |
    119          ____v____________________v____________________v____      GUEST
    120         |                                                   |     KERNEL
    121         |                       QEMU PIPE                   |
    122         |___________________________________________________|
    123                                  ^
    124                                  |
    125       - - - - - - - - - - - - - -|- - - - - - - - - - - - - - - -
    126                                  |
    127                                  |    PROTOCOL BYTE STREAM
    128                             _____v_____
    129                            |           |
    130                            |  EMULATOR |
    131                            |___________|
    132                                  ^
    133                                  |   UNMODIFIED PROTOCOL BYTE STREAM
    134                             _____v_____
    135                            |           |
    136                            |  RENDERER |
    137                            |___________|
    138                                ^ ^  ^
    139                                | |  |
    140              +-----------------+ |  +-----------------+
    141              |                   |                    |
    142          ____v____            ___v______          ____v_____
    143         |         |          |          |        |          |
    144         |TRANSLATOR          |TRANSLATOR|        |TRANSLATOR|     HOST
    145         |   EGL   |          | GLES 1.1 |        | GLES 2.0 |     TRANSLATOR
    146         |_________|          |__________|        |__________|     LIBRARIES
    147              ^                    ^                    ^
    148              |                    |                    |
    149        - - - | - - - - - - - - -  | - - - - - - - - -  | - - - - -
    150              |                    |                    |
    151          ____v____            ____v_____          _____v____      HOST 
    152         |         |          |          |        |          |     SYSTEM
    153         |   Xgl   |          |  GL 2.0  |        |  GL 2.0  |     LIBRARIES
    154         |_________|          |__________|        |__________|
    155 
    156     (NOTE: 'Xgl' is for Linux only, replace 'AGL' on OS X, and 'WGL' on Windows).
    157 
    158 
    159 Note that, in the above graphics, only the host system libraries at the bottom
    160 are _not_ provided by Android.
    161 
    162 
    163 Design Requirements:
    164 --------------------
    165 
    166 The above design comes from several important requirements that were decided
    167 early in the project:
    168 
    169 1 - The ability to run the renderer in a separate process from the emulator
    170     itself is important.
    171 
    172     For various practical reasons, we plan to completely separate the core QEMU
    173     emulation from the UI window by using two distinct processes. As such, the
    174     renderer will be implemented as a library inside the UI program, but will
    175     need to receive protocol bytes from the QEMU process.
    176 
    177     The communication channel will be either a fast Unix socket or a Win32
    178     named pipe between these two. A shared memory segment with appropriate
    179     synchronization primitives might also be used if performance becomes
    180     an issue.
    181 
    182     This explains why the emulator doesn't alter or even try to parse the
    183     protocol byte stream. It only acts as a dumb proxy between the guest
    184     system and the renderer. This also avoids adding lots of GLES-specific
    185     code inside the QEMU code base which is terribly complex.
    186 
    187 2 - The ability to use vendor-specific desktop EGL/GLES libraries is
    188     important.
    189 
    190     GPU vendors like NVidia, AMD or ARM all provide host versions of the
    191     EGL/GLES libraries that emulate their respectivie embedded graphics
    192     chipset.
    193 
    194     The renderer library can be configured to use these instead of the
    195     translator libraries provided with this project. This can be useful to
    196     more accurately emulate the behaviour of specific devices.
    197 
    198     Moreover, these vendor libraries typically expose vendor-specific
    199     extensions that are not provided by the translator libraries. We cannot
    200     expose them without modifying our code, but it's important to be able
    201     to do so without too much pain.
    202 
    203 
    204 Code organization:
    205 ------------------
    206 
    207 All source code for the components above is spread over multiple directories
    208 in the Android source trees:
    209 
    210   - The emulator sources are under $ANDROID/external/qemu, which we'll
    211     call $QEMU in the rest of this document.
    212 
    213   - The guest and system libraries are under
    214     $ANDROID/development/tools/emulator/opengl, which we'll call $EMUGL
    215 
    216   - The QEMU Pipe kernel driver is under $KERNEL/drivers/misc/qemupipe
    217 
    218 Where $ANDROID is the top of the open-source Android source tree, and
    219 $KERNEL is the top of the qemu-specific kernel source tree (using one
    220 of the android-goldfish-xxxx branches here).
    221 
    222 The emulator sources related to this projects are:
    223 
    224    $QEMU/hw/goldfish_pipe.c  -> implement QEMU pipe virtual hardware
    225    $QEMU/hw/opengles.c       -> implement GLES initialization
    226    $QEMU/hw/hw-pipe-net.c    -> implements the communication channel
    227                                 between the QEMU Pipe and the renderer library
    228 
    229 The other sources are:
    230 
    231    $EMUGL/system   -> system libraries
    232    $EMUGL/host     -> host libraries (translator + renderer)
    233    $EMUGL/shared   -> shared libraries, used both in the guest and the host
    234    $EMUGL/tests    -> various test programs
    235 
    236 
    237 Translator libraries:
    238 ---------------------
    239 
    240 There are three translator host libraries provided by this project:
    241 
    242    libEGL_translator       -> EGL 1.2 translation
    243    libGLES_CM_translator   -> GLES 1.1 translation
    244    libGLES_V2_translator   -> GLES 2.0 translation
    245 
    246 The full name of the library will depend on the host system.
    247 For simplicity, only the library name suffix will change (i.e. the
    248 'lib' prefix is not dropped on Windows), i.e.:
    249 
    250    libEGL_translator.so    -> for Linux
    251    libEGL_translator.dylib -> for OS X
    252    libEGL_translator.dll   -> for Windows
    253 
    254 The source code for these libraries is located under the following
    255 path in the Android source tree:
    256 
    257    $EMUGL/host/libs/Translator/EGL
    258    $EMUGL/host/libs/Translator/GLES_CM
    259    $EMUGL/host/libs/Translator/GLES_V2
    260 
    261 The translator libraries also use a common routines defined under:
    262 
    263    $EMUGL/host/libs/Translator/GLcommon
    264 
    265 
    266 Wire Protocol Overiew:
    267 ----------------------
    268 
    269 The "wire protocol" is implemented as follows:
    270 
    271   - EGL/GLES function calls are described through several "specification"
    272     files, which describes the types, function signatures and various
    273     attributes for each one of them.
    274 
    275   - These files are read by a tool called "emugen" which generates C
    276     source files and headers based on the specification. These correspond
    277     to both encoding, decoding and "wrappers" (more on this later).
    278 
    279   - System "encoder" static libraries are built using some of these generated
    280     files. They contain code that can serialize EGL/GLES calls into simple
    281     byte messages and send it through a generic "IOStream" object.
    282 
    283   - Host "decoder" static libraries are also built using some of these
    284     generated files. Their code retrieves byte messages from an "IOStream"
    285     object, and translates them into function callbacks.
    286 
    287 IOStream abstraction:
    288 - - - - - - - - - - -
    289 
    290 The "IOStream" is a very simple abstract class used to send byte messages
    291 both in the guest and host. It is defined through a shared header under
    292 $EMUGL/host/include/libOpenglRender/IOStream.h
    293 
    294 Note that despite the path, this header is included by *both* host and guest
    295 source code. The main idea around IOStream's design is that to send a message,
    296 one does the following:
    297 
    298   1/ call stream->allocBuffer(size), which returns the address of a
    299      memory buffer of at least 'size' bytes.
    300 
    301   2/ write the content of the serialized command (usually a header + some
    302      payload) directly into the buffer
    303 
    304   3/ call stream->commitBuffer() to send it.
    305 
    306 Alternatively, one can also pack several commands into a single buffer with
    307 stream->alloc() and stream->flush(), as in:
    308 
    309   1/ buf1 =  stream->alloc(size1)
    310   2/ write first command bytes into buf1
    311   3/ buf2 = stream->alloc(size2)
    312   4/ write second command bytes into buf2
    313   5/ stream->flush()
    314 
    315 Finally, there are also explict read/write methods like stream->readFully()
    316 or stream->writeFully() which can be used when you don't want an intermediate
    317 buffer. This is used in certain cases by the implementation, e.g. to avoid
    318 an intermediate memory copy when sending texture data from the guest to the
    319 host.
    320 
    321 The host IOStream implementations are under $EMUGL/shared/OpenglCodecCommon/,
    322 see in particular:
    323 
    324    $EMUGL/shared/OpenglCodecCommon/TcpStream.cpp  -> using local TCP sockets
    325    $EMUGL/shared/OpenglCodecCommon/UnixStream.cpp -> using Unix sockets
    326    $EMUGL/shared/OpenglCodecCommon/Win32PipeStream.cpp -> using Win32 named pipes
    327 
    328 The guest IOStream implementation uses the TcpStream.cpp above, as well as
    329 an alternative QEMU-specific source:
    330 
    331    $EMUGL/system/OpenglSystemCommon/QemuPipeStream.cpp -> uses QEMU pipe from the guest
    332 
    333 The QEMU Pipe implementation is _significantly_ faster (about 20x) due to
    334 several reasons:
    335 
    336   - all succesful read() and write() operations through it are instantaneous
    337     from the guest's point of view.
    338 
    339   - all buffer/memory copies are performed directly by the emulator, and thus
    340     much faster than performing the same thing inside the kernel with emulated
    341     ARM instructions.
    342 
    343   - it doesn't need to go through a kernel TCP/IP stack that will wrap the
    344     data into TCP/IP/MAC packets, send them to an emulated ethernet device,
    345     which is itself connected to an internal firewall implementation that
    346     will unwrap the packets, re-assemble them, then send them through BSD
    347     sockets to the host kernel.
    348 
    349 However, would it be necessary, you could write a guest IOStream implementation
    350 that uses a different transport. If you do, please look at
    351 $EMUGL/system/OpenglCodecCommon/HostConnection.cpp which contains the code
    352 used to connect the guest to the host, on a per-thread basis.
    353 
    354 
    355 Source code auto-generation:
    356 - - - - - - - - - - - - - -
    357 
    358 The 'emugen' tool is located under $EMUGL/host/tools/emugen. There is a README
    359 file that explains how it works.
    360 
    361 You can also look at the following specifications files:
    362 
    363 For GLES 1.1:
    364     $EMUGL/system/GLESv1_enc/gl.types
    365     $EMUGL/system/GLESv1_enc/gl.in
    366     $EMUGL/system/GLESv1_enc/gl.attrib
    367     $EMUGL/system/GLESv1_enc/gl.addon
    368 
    369 For GLES 2.0:
    370     $EMUGL/system/GLESv2_enc/gl2.types
    371     $EMUGL/system/GLESv2_enc/gl2.in
    372     $EMUGL/system/GLESv2_enc/gl2.attrib
    373     $EMUGL/system/GLESv2_enc/gl2.addon
    374 
    375 For EGL:
    376     $EMUGL/system/renderControl_enc/renderControl.types
    377     $EMUGL/system/renderControl_enc/renderControl.in
    378     $EMUGL/system/renderControl_enc/renderControl.attrib
    379     $EMUGL/system/renderControl_enc/renderControl.addon
    380 
    381 Note that the EGL specification files are under a directory named
    382 "renderControl_enc" and have filenames that begin with "renderControl"
    383 
    384 This is mainly for historic reasons now, but is also related to the fact that
    385 this part of the wire protocol contains support functions/calls/specifications
    386 that are not part of the EGL specification itself, but add a few features
    387 required to make everything works. For example, they have calls related to
    388 the "gralloc" system library module used to manage graphics surfaces at a
    389 lower level than EGL.
    390 
    391 Generally speaking, guest encoder sources are located under directories
    392 named $EMUGL/system/<name>_enc/, while the corresponding host decoder
    393 sources will be under $EMUGL/host/libs/<name>_dec/
    394 
    395 However, all these sources use the same spec files located under the
    396 encoding directories. The decoders may even need to include a few
    397 non-auto-generated header files from the encoder directories.
    398 
    399 
    400 
    401 System libraries:
    402 -----------------
    403 
    404 Meta EGL/GLES system libraries, and egl.cfg:
    405 - - - - - - - - - - - - - - - - - - - - - -
    406 
    407 It is important to understand that the emulation-specific EGL/GLES libraries
    408 are not directly linked by applications at runtime. Instead, the system
    409 provides a set of "meta" EGL/GLES libraries that will load the appropriate
    410 hardware-specific libraries on first use.
    411 
    412 More specifically, the system libEGL.so contains a "loader" which will try
    413 to load:
    414 
    415   - hardware-specific EGL/GLES libraries
    416   - the software-based rendering libraries (called "libagl")
    417 
    418 The system libEGL.so is also capable of merging the EGL configs of both the
    419 hardware and software libraries transparently to the application. The system
    420 libGLESv1_CM.so and libGLESv2.so, work with it to ensure that the thread's
    421 current context will be linked to either the hardware or software libraries
    422 depending on the config selected.
    423 
    424 For the record, the loader's source code in under
    425 frameworks/base/opengl/libs/EGL/Loader.cpp. It depends on a file named
    426 /system/lib/egl/egl.cfg which must contain two lines that look like:
    427 
    428     0 1 <name>
    429     0 0 android
    430 
    431 The first number in each line is a display number, and must be 0 since the
    432 system's EGL/GLES libraries don't support anything else.
    433 
    434 The second number must be 1 to indicate hardware libraries, and 0 to indicate
    435 a software one. The line corresponding to the hardware library, if any, must
    436 always appear before the one for the software library.
    437 
    438 The third field is a name corresponding to a shared library suffix. It really
    439 means that the corresponding libraries will be named libEGL_<name>.so,
    440 libGLESv1_CM_<name>.so and libGLESv2_<name>.so. Moreover these libraries must
    441 be placed under /system/lib/egl/
    442 
    443 The name "android" is reserved for the system software renderer.
    444 
    445 The egl.cfg that comes with this project uses the name "emulation" for the
    446 hardware libraries. This means that it provides an egl.cfg file that contains
    447 the following lines:
    448 
    449    0 1 emulation
    450    0 0 android
    451 
    452 See $EMUGL/system/egl/egl.cfg and more generally the following build files:
    453 
    454    $EMUGL/system/egl/Android.mk
    455    $EMUGL/system/GLESv1/Android.mk
    456    $EMUGL/system/GLESv2/Android.mk
    457 
    458 to see how the libraries are named and placed under /system/lib/egl/ by the
    459 build system.
    460 
    461 
    462 Emulation libraries:
    463 - - - - - - - - - - -
    464 
    465 The emulator-specific libraries are under the following:
    466 
    467   $EMUGL/system/egl/
    468   $EMUGL/system/GLESv1/
    469   $EMUGL/system/GLESv2/
    470 
    471 The code for GLESv1 and GLESv2 is pretty small, since it mostly link against
    472 the static encoding libraries.
    473 
    474 The code for EGL is a bit more complex, because it needs to deal with
    475 extensions dynamically. I.e. if an extension is not available on the host
    476 it shouldn't be exposed by the library at runtime. So the EGL code queries
    477 the host for the list of available extensions in order to return them to
    478 clients. Similarly, it must query the list of valid EGLConfigs for the
    479 current host system.
    480 
    481 
    482 "gralloc" module implementation:
    483 - - - - - - - - - - - - - - - - -
    484 
    485 In addition to EGL/GLES libraries, the Android system requires a
    486 hardware-specific library to manage graphics surfaces at a level lower than
    487 EGL. This library must be what is called in Android land as a "HAL module".
    488 
    489 A "HAL module" must provide interfaces defined by Android's HAL
    490 (Hardware Abstraction Library). These interface definitions can be found
    491 under $ANDROID/hardware/libhardware/include/
    492 
    493 Of all possible HAL modules, the "gralloc" one is used by the system's
    494 SurfaceFlinger to allocate framebuffers and other graphics memory regions,
    495 as well as eventually lock/unlock/swap them when needed.
    496 
    497 The code under $EMUGL/system/gralloc/ implements the module required by the
    498 GLES emulation project. It's not very long, but there are a few things to
    499 notice here:
    500 
    501 - first, it will probe the guest system to determine if the emulator that
    502   is running the virtual device really supports GPU emulation. In certain
    503   circumstances this may not be possible.
    504 
    505   If this is the case, then the module will redirect all calls to the
    506   "default" gralloc module that is normally used by the system when
    507   software-only rendering is enabled.
    508 
    509   The probing happens in the function "fallback_init" which gets called
    510   when the module is first opened. This initializes the 'sFallback' variable
    511   to a pointer to the default gralloc module when required.
    512 
    513 - second, this module is used by SurfaceFlinger to display "software surfaces",
    514   i.e. those that are backed by system memory pixel buffers, and written to
    515   directly through the Skia graphics library (i.e. the non-accelerated ones).
    516 
    517   the default module simply copies the pixel data from the surface to the
    518   virtual framebuffer i/o memory, but this project's gralloc module sends it
    519   to the renderer through the QEMU Pipe instead.
    520 
    521   It turns out that this results in _faster_ rendering/frame-rates overall,
    522   because memory copies inside the guest are slow, while QEMU pipe transfers
    523   are done directly in the emulator.
    524 
    525 
    526 Host Renderer:
    527 --------------
    528 
    529 The host renderer library is located under $EMUGL/host/libs/libOpenglRender,
    530 and it provides an interface described by the headers under
    531 $EMUGL/host/include/libOpenglRender/render_api.h (e.g. for use by the emulator).
    532 
    533 In a nutshell, the rendering library is responsible for the following:
    534 
    535   - Providing a virtual off-screen video surface where everything will get
    536     rendered at runtime. Its dimensions are fixed by the call to
    537     initOpenglRender() that must happen just after the library is
    538     initialized.
    539 
    540   - Provide a way to display the virtual video surface on a host application's
    541     UI. This is done by calling createOpenGLSubWindow() which takes as argument
    542     the window ID or handle of a parent window, some display dimensions and
    543     a rotation angle. This allows the surface to be scaled/rotated when it is
    544     displayed, even if the dimensions of the video surface do not change.
    545 
    546   - Provide a way to listen to incoming EGL/GLES commands from the guest.
    547     This is done by providing a so-called "port number" to initOpenglRender().
    548 
    549     By default, the port number corresponds to a local TCP port number that the
    550     renderer will bind to and listen. Every new connection to this port will
    551     correspond to the creation of a new guest host connection, each such
    552     connection corresponding to a distinct thread in the guest system.
    553 
    554     For performance reasons, it is possible to listen to either Unix sockets
    555     (on Linux and OS X), or to a Win32 named pipe (on Windows). To do so, one
    556     had to call setStreamType() between library initialization
    557     (i.e. initLibrary()) and construction (i.e. initOpenglRender()).
    558 
    559     Note that in these modes, the port number is still used to differentiate
    560     between several emulator instances. These details are normally handled by
    561     the emulator code so you shouldn't care too much.
    562 
    563 Note that an earlier version of the interface allowed a client of the renderer
    564 library to provide its own IOStream implementation. However, this wasn't very
    565 convenient for a number of reasons. This maybe something that could be done
    566 again if it makes sense, but for now the performance numbers are pretty good.
    567 
    568 
    569 Host emulator:
    570 --------------
    571 
    572 The code under $QEMU/android/opengles.c is in charge of dynamically loading
    573 the rendering library and initializing / constructing it properly.
    574 
    575 QEMU pipe connections to the 'opengles' service are piped through the code
    576 in $QEMU/android/hw-pipe-net.c. Look for the openglesPipe_init() function,
    577 which is in charge of creating a connection to the renderer library
    578 (either through a TCP socket, or a Unix pipe depending on configuration.
    579 support for Win32 named pipes hasn't been implemented yet in the emulator)
    580 whenever a guest process opens the "opengles" service through /dev/qemu_pipe.
    581 
    582 There is also some support code for the display of the GLES framebuffer
    583 (through the renderer library's subwindow) under $QEMU/skin/window.
    584 
    585 Note that at the moment, scaling and rotation are supported. However,
    586 brightness emulation (which used to modify the pixel values from the
    587 hardware framebuffer before displaying them) doesn't work.
    588 
    589 Another issue is that it is not possible to display anything on top of the
    590 GL subwindow at the moment. E.g. this will obscure the emulated trackball
    591 image (that is normally toggled with Ctrl-T during emulation, or enabled
    592 by pressing the Delete key).
    593 
    594