Home | History | Annotate | Download | only in implementors_guide
      1 <!DOCTYPE html>
      2 <html lang="en">
      3 <head>
      4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      5 <meta name="generator" content="AsciiDoc 8.6.9">
      6 <title>Vulkan on Android Implementor&#8217;s Guide</title>
      7 <style type="text/css">
      8 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
      9 
     10 /* Default font. */
     11 body {
     12   font-family: Georgia,serif;
     13 }
     14 
     15 /* Title font. */
     16 h1, h2, h3, h4, h5, h6,
     17 div.title, caption.title,
     18 thead, p.table.header,
     19 #toctitle,
     20 #author, #revnumber, #revdate, #revremark,
     21 #footer {
     22   font-family: Arial,Helvetica,sans-serif;
     23 }
     24 
     25 body {
     26   margin: 1em 5% 1em 5%;
     27 }
     28 
     29 a {
     30   color: blue;
     31   text-decoration: underline;
     32 }
     33 a:visited {
     34   color: fuchsia;
     35 }
     36 
     37 em {
     38   font-style: italic;
     39   color: navy;
     40 }
     41 
     42 strong {
     43   font-weight: bold;
     44   color: #083194;
     45 }
     46 
     47 h1, h2, h3, h4, h5, h6 {
     48   color: #527bbd;
     49   margin-top: 1.2em;
     50   margin-bottom: 0.5em;
     51   line-height: 1.3;
     52 }
     53 
     54 h1, h2, h3 {
     55   border-bottom: 2px solid silver;
     56 }
     57 h2 {
     58   padding-top: 0.5em;
     59 }
     60 h3 {
     61   float: left;
     62 }
     63 h3 + * {
     64   clear: left;
     65 }
     66 h5 {
     67   font-size: 1.0em;
     68 }
     69 
     70 div.sectionbody {
     71   margin-left: 0;
     72 }
     73 
     74 hr {
     75   border: 1px solid silver;
     76 }
     77 
     78 p {
     79   margin-top: 0.5em;
     80   margin-bottom: 0.5em;
     81 }
     82 
     83 ul, ol, li > p {
     84   margin-top: 0;
     85 }
     86 ul > li     { color: #aaa; }
     87 ul > li > * { color: black; }
     88 
     89 .monospaced, code, pre {
     90   font-family: "Courier New", Courier, monospace;
     91   font-size: inherit;
     92   color: navy;
     93   padding: 0;
     94   margin: 0;
     95 }
     96 pre {
     97   white-space: pre-wrap;
     98 }
     99 
    100 #author {
    101   color: #527bbd;
    102   font-weight: bold;
    103   font-size: 1.1em;
    104 }
    105 #email {
    106 }
    107 #revnumber, #revdate, #revremark {
    108 }
    109 
    110 #footer {
    111   font-size: small;
    112   border-top: 2px solid silver;
    113   padding-top: 0.5em;
    114   margin-top: 4.0em;
    115 }
    116 #footer-text {
    117   float: left;
    118   padding-bottom: 0.5em;
    119 }
    120 #footer-badges {
    121   float: right;
    122   padding-bottom: 0.5em;
    123 }
    124 
    125 #preamble {
    126   margin-top: 1.5em;
    127   margin-bottom: 1.5em;
    128 }
    129 div.imageblock, div.exampleblock, div.verseblock,
    130 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
    131 div.admonitionblock {
    132   margin-top: 1.0em;
    133   margin-bottom: 1.5em;
    134 }
    135 div.admonitionblock {
    136   margin-top: 2.0em;
    137   margin-bottom: 2.0em;
    138   margin-right: 10%;
    139   color: #606060;
    140 }
    141 
    142 div.content { /* Block element content. */
    143   padding: 0;
    144 }
    145 
    146 /* Block element titles. */
    147 div.title, caption.title {
    148   color: #527bbd;
    149   font-weight: bold;
    150   text-align: left;
    151   margin-top: 1.0em;
    152   margin-bottom: 0.5em;
    153 }
    154 div.title + * {
    155   margin-top: 0;
    156 }
    157 
    158 td div.title:first-child {
    159   margin-top: 0.0em;
    160 }
    161 div.content div.title:first-child {
    162   margin-top: 0.0em;
    163 }
    164 div.content + div.title {
    165   margin-top: 0.0em;
    166 }
    167 
    168 div.sidebarblock > div.content {
    169   background: #ffffee;
    170   border: 1px solid #dddddd;
    171   border-left: 4px solid #f0f0f0;
    172   padding: 0.5em;
    173 }
    174 
    175 div.listingblock > div.content {
    176   border: 1px solid #dddddd;
    177   border-left: 5px solid #f0f0f0;
    178   background: #f8f8f8;
    179   padding: 0.5em;
    180 }
    181 
    182 div.quoteblock, div.verseblock {
    183   padding-left: 1.0em;
    184   margin-left: 1.0em;
    185   margin-right: 10%;
    186   border-left: 5px solid #f0f0f0;
    187   color: #888;
    188 }
    189 
    190 div.quoteblock > div.attribution {
    191   padding-top: 0.5em;
    192   text-align: right;
    193 }
    194 
    195 div.verseblock > pre.content {
    196   font-family: inherit;
    197   font-size: inherit;
    198 }
    199 div.verseblock > div.attribution {
    200   padding-top: 0.75em;
    201   text-align: left;
    202 }
    203 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
    204 div.verseblock + div.attribution {
    205   text-align: left;
    206 }
    207 
    208 div.admonitionblock .icon {
    209   vertical-align: top;
    210   font-size: 1.1em;
    211   font-weight: bold;
    212   text-decoration: underline;
    213   color: #527bbd;
    214   padding-right: 0.5em;
    215 }
    216 div.admonitionblock td.content {
    217   padding-left: 0.5em;
    218   border-left: 3px solid #dddddd;
    219 }
    220 
    221 div.exampleblock > div.content {
    222   border-left: 3px solid #dddddd;
    223   padding-left: 0.5em;
    224 }
    225 
    226 div.imageblock div.content { padding-left: 0; }
    227 span.image img { border-style: none; vertical-align: text-bottom; }
    228 a.image:visited { color: white; }
    229 
    230 dl {
    231   margin-top: 0.8em;
    232   margin-bottom: 0.8em;
    233 }
    234 dt {
    235   margin-top: 0.5em;
    236   margin-bottom: 0;
    237   font-style: normal;
    238   color: navy;
    239 }
    240 dd > *:first-child {
    241   margin-top: 0.1em;
    242 }
    243 
    244 ul, ol {
    245     list-style-position: outside;
    246 }
    247 ol.arabic {
    248   list-style-type: decimal;
    249 }
    250 ol.loweralpha {
    251   list-style-type: lower-alpha;
    252 }
    253 ol.upperalpha {
    254   list-style-type: upper-alpha;
    255 }
    256 ol.lowerroman {
    257   list-style-type: lower-roman;
    258 }
    259 ol.upperroman {
    260   list-style-type: upper-roman;
    261 }
    262 
    263 div.compact ul, div.compact ol,
    264 div.compact p, div.compact p,
    265 div.compact div, div.compact div {
    266   margin-top: 0.1em;
    267   margin-bottom: 0.1em;
    268 }
    269 
    270 tfoot {
    271   font-weight: bold;
    272 }
    273 td > div.verse {
    274   white-space: pre;
    275 }
    276 
    277 div.hdlist {
    278   margin-top: 0.8em;
    279   margin-bottom: 0.8em;
    280 }
    281 div.hdlist tr {
    282   padding-bottom: 15px;
    283 }
    284 dt.hdlist1.strong, td.hdlist1.strong {
    285   font-weight: bold;
    286 }
    287 td.hdlist1 {
    288   vertical-align: top;
    289   font-style: normal;
    290   padding-right: 0.8em;
    291   color: navy;
    292 }
    293 td.hdlist2 {
    294   vertical-align: top;
    295 }
    296 div.hdlist.compact tr {
    297   margin: 0;
    298   padding-bottom: 0;
    299 }
    300 
    301 .comment {
    302   background: yellow;
    303 }
    304 
    305 .footnote, .footnoteref {
    306   font-size: 0.8em;
    307 }
    308 
    309 span.footnote, span.footnoteref {
    310   vertical-align: super;
    311 }
    312 
    313 #footnotes {
    314   margin: 20px 0 20px 0;
    315   padding: 7px 0 0 0;
    316 }
    317 
    318 #footnotes div.footnote {
    319   margin: 0 0 5px 0;
    320 }
    321 
    322 #footnotes hr {
    323   border: none;
    324   border-top: 1px solid silver;
    325   height: 1px;
    326   text-align: left;
    327   margin-left: 0;
    328   width: 20%;
    329   min-width: 100px;
    330 }
    331 
    332 div.colist td {
    333   padding-right: 0.5em;
    334   padding-bottom: 0.3em;
    335   vertical-align: top;
    336 }
    337 div.colist td img {
    338   margin-top: 0.3em;
    339 }
    340 
    341 @media print {
    342   #footer-badges { display: none; }
    343 }
    344 
    345 #toc {
    346   margin-bottom: 2.5em;
    347 }
    348 
    349 #toctitle {
    350   color: #527bbd;
    351   font-size: 1.1em;
    352   font-weight: bold;
    353   margin-top: 1.0em;
    354   margin-bottom: 0.1em;
    355 }
    356 
    357 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
    358   margin-top: 0;
    359   margin-bottom: 0;
    360 }
    361 div.toclevel2 {
    362   margin-left: 2em;
    363   font-size: 0.9em;
    364 }
    365 div.toclevel3 {
    366   margin-left: 4em;
    367   font-size: 0.9em;
    368 }
    369 div.toclevel4 {
    370   margin-left: 6em;
    371   font-size: 0.9em;
    372 }
    373 
    374 span.aqua { color: aqua; }
    375 span.black { color: black; }
    376 span.blue { color: blue; }
    377 span.fuchsia { color: fuchsia; }
    378 span.gray { color: gray; }
    379 span.green { color: green; }
    380 span.lime { color: lime; }
    381 span.maroon { color: maroon; }
    382 span.navy { color: navy; }
    383 span.olive { color: olive; }
    384 span.purple { color: purple; }
    385 span.red { color: red; }
    386 span.silver { color: silver; }
    387 span.teal { color: teal; }
    388 span.white { color: white; }
    389 span.yellow { color: yellow; }
    390 
    391 span.aqua-background { background: aqua; }
    392 span.black-background { background: black; }
    393 span.blue-background { background: blue; }
    394 span.fuchsia-background { background: fuchsia; }
    395 span.gray-background { background: gray; }
    396 span.green-background { background: green; }
    397 span.lime-background { background: lime; }
    398 span.maroon-background { background: maroon; }
    399 span.navy-background { background: navy; }
    400 span.olive-background { background: olive; }
    401 span.purple-background { background: purple; }
    402 span.red-background { background: red; }
    403 span.silver-background { background: silver; }
    404 span.teal-background { background: teal; }
    405 span.white-background { background: white; }
    406 span.yellow-background { background: yellow; }
    407 
    408 span.big { font-size: 2em; }
    409 span.small { font-size: 0.6em; }
    410 
    411 span.underline { text-decoration: underline; }
    412 span.overline { text-decoration: overline; }
    413 span.line-through { text-decoration: line-through; }
    414 
    415 div.unbreakable { page-break-inside: avoid; }
    416 
    417 
    418 /*
    419  * xhtml11 specific
    420  *
    421  * */
    422 
    423 div.tableblock {
    424   margin-top: 1.0em;
    425   margin-bottom: 1.5em;
    426 }
    427 div.tableblock > table {
    428   border: 3px solid #527bbd;
    429 }
    430 thead, p.table.header {
    431   font-weight: bold;
    432   color: #527bbd;
    433 }
    434 p.table {
    435   margin-top: 0;
    436 }
    437 /* Because the table frame attribute is overriden by CSS in most browsers. */
    438 div.tableblock > table[frame="void"] {
    439   border-style: none;
    440 }
    441 div.tableblock > table[frame="hsides"] {
    442   border-left-style: none;
    443   border-right-style: none;
    444 }
    445 div.tableblock > table[frame="vsides"] {
    446   border-top-style: none;
    447   border-bottom-style: none;
    448 }
    449 
    450 
    451 /*
    452  * html5 specific
    453  *
    454  * */
    455 
    456 table.tableblock {
    457   margin-top: 1.0em;
    458   margin-bottom: 1.5em;
    459 }
    460 thead, p.tableblock.header {
    461   font-weight: bold;
    462   color: #527bbd;
    463 }
    464 p.tableblock {
    465   margin-top: 0;
    466 }
    467 table.tableblock {
    468   border-width: 3px;
    469   border-spacing: 0px;
    470   border-style: solid;
    471   border-color: #527bbd;
    472   border-collapse: collapse;
    473 }
    474 th.tableblock, td.tableblock {
    475   border-width: 1px;
    476   padding: 4px;
    477   border-style: solid;
    478   border-color: #527bbd;
    479 }
    480 
    481 table.tableblock.frame-topbot {
    482   border-left-style: hidden;
    483   border-right-style: hidden;
    484 }
    485 table.tableblock.frame-sides {
    486   border-top-style: hidden;
    487   border-bottom-style: hidden;
    488 }
    489 table.tableblock.frame-none {
    490   border-style: hidden;
    491 }
    492 
    493 th.tableblock.halign-left, td.tableblock.halign-left {
    494   text-align: left;
    495 }
    496 th.tableblock.halign-center, td.tableblock.halign-center {
    497   text-align: center;
    498 }
    499 th.tableblock.halign-right, td.tableblock.halign-right {
    500   text-align: right;
    501 }
    502 
    503 th.tableblock.valign-top, td.tableblock.valign-top {
    504   vertical-align: top;
    505 }
    506 th.tableblock.valign-middle, td.tableblock.valign-middle {
    507   vertical-align: middle;
    508 }
    509 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
    510   vertical-align: bottom;
    511 }
    512 
    513 
    514 /*
    515  * manpage specific
    516  *
    517  * */
    518 
    519 body.manpage h1 {
    520   padding-top: 0.5em;
    521   padding-bottom: 0.5em;
    522   border-top: 2px solid silver;
    523   border-bottom: 2px solid silver;
    524 }
    525 body.manpage h2 {
    526   border-style: none;
    527 }
    528 body.manpage div.sectionbody {
    529   margin-left: 3em;
    530 }
    531 
    532 @media print {
    533   body.manpage div#toc { display: none; }
    534 }
    535 
    536 
    537 </style>
    538 <script type="text/javascript">
    539 /*<![CDATA[*/
    540 var asciidoc = {  // Namespace.
    541 
    542 /////////////////////////////////////////////////////////////////////
    543 // Table Of Contents generator
    544 /////////////////////////////////////////////////////////////////////
    545 
    546 /* Author: Mihai Bazon, September 2002
    547  * http://students.infoiasi.ro/~mishoo
    548  *
    549  * Table Of Content generator
    550  * Version: 0.4
    551  *
    552  * Feel free to use this script under the terms of the GNU General Public
    553  * License, as long as you do not remove or alter this notice.
    554  */
    555 
    556  /* modified by Troy D. Hanson, September 2006. License: GPL */
    557  /* modified by Stuart Rackham, 2006, 2009. License: GPL */
    558 
    559 // toclevels = 1..4.
    560 toc: function (toclevels) {
    561 
    562   function getText(el) {
    563     var text = "";
    564     for (var i = el.firstChild; i != null; i = i.nextSibling) {
    565       if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
    566         text += i.data;
    567       else if (i.firstChild != null)
    568         text += getText(i);
    569     }
    570     return text;
    571   }
    572 
    573   function TocEntry(el, text, toclevel) {
    574     this.element = el;
    575     this.text = text;
    576     this.toclevel = toclevel;
    577   }
    578 
    579   function tocEntries(el, toclevels) {
    580     var result = new Array;
    581     var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
    582     // Function that scans the DOM tree for header elements (the DOM2
    583     // nodeIterator API would be a better technique but not supported by all
    584     // browsers).
    585     var iterate = function (el) {
    586       for (var i = el.firstChild; i != null; i = i.nextSibling) {
    587         if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
    588           var mo = re.exec(i.tagName);
    589           if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
    590             result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
    591           }
    592           iterate(i);
    593         }
    594       }
    595     }
    596     iterate(el);
    597     return result;
    598   }
    599 
    600   var toc = document.getElementById("toc");
    601   if (!toc) {
    602     return;
    603   }
    604 
    605   // Delete existing TOC entries in case we're reloading the TOC.
    606   var tocEntriesToRemove = [];
    607   var i;
    608   for (i = 0; i < toc.childNodes.length; i++) {
    609     var entry = toc.childNodes[i];
    610     if (entry.nodeName.toLowerCase() == 'div'
    611      && entry.getAttribute("class")
    612      && entry.getAttribute("class").match(/^toclevel/))
    613       tocEntriesToRemove.push(entry);
    614   }
    615   for (i = 0; i < tocEntriesToRemove.length; i++) {
    616     toc.removeChild(tocEntriesToRemove[i]);
    617   }
    618 
    619   // Rebuild TOC entries.
    620   var entries = tocEntries(document.getElementById("content"), toclevels);
    621   for (var i = 0; i < entries.length; ++i) {
    622     var entry = entries[i];
    623     if (entry.element.id == "")
    624       entry.element.id = "_toc_" + i;
    625     var a = document.createElement("a");
    626     a.href = "#" + entry.element.id;
    627     a.appendChild(document.createTextNode(entry.text));
    628     var div = document.createElement("div");
    629     div.appendChild(a);
    630     div.className = "toclevel" + entry.toclevel;
    631     toc.appendChild(div);
    632   }
    633   if (entries.length == 0)
    634     toc.parentNode.removeChild(toc);
    635 },
    636 
    637 
    638 /////////////////////////////////////////////////////////////////////
    639 // Footnotes generator
    640 /////////////////////////////////////////////////////////////////////
    641 
    642 /* Based on footnote generation code from:
    643  * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
    644  */
    645 
    646 footnotes: function () {
    647   // Delete existing footnote entries in case we're reloading the footnodes.
    648   var i;
    649   var noteholder = document.getElementById("footnotes");
    650   if (!noteholder) {
    651     return;
    652   }
    653   var entriesToRemove = [];
    654   for (i = 0; i < noteholder.childNodes.length; i++) {
    655     var entry = noteholder.childNodes[i];
    656     if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
    657       entriesToRemove.push(entry);
    658   }
    659   for (i = 0; i < entriesToRemove.length; i++) {
    660     noteholder.removeChild(entriesToRemove[i]);
    661   }
    662 
    663   // Rebuild footnote entries.
    664   var cont = document.getElementById("content");
    665   var spans = cont.getElementsByTagName("span");
    666   var refs = {};
    667   var n = 0;
    668   for (i=0; i<spans.length; i++) {
    669     if (spans[i].className == "footnote") {
    670       n++;
    671       var note = spans[i].getAttribute("data-note");
    672       if (!note) {
    673         // Use [\s\S] in place of . so multi-line matches work.
    674         // Because JavaScript has no s (dotall) regex flag.
    675         note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
    676         spans[i].innerHTML =
    677           "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
    678           "' title='View footnote' class='footnote'>" + n + "</a>]";
    679         spans[i].setAttribute("data-note", note);
    680       }
    681       noteholder.innerHTML +=
    682         "<div class='footnote' id='_footnote_" + n + "'>" +
    683         "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
    684         n + "</a>. " + note + "</div>";
    685       var id =spans[i].getAttribute("id");
    686       if (id != null) refs["#"+id] = n;
    687     }
    688   }
    689   if (n == 0)
    690     noteholder.parentNode.removeChild(noteholder);
    691   else {
    692     // Process footnoterefs.
    693     for (i=0; i<spans.length; i++) {
    694       if (spans[i].className == "footnoteref") {
    695         var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
    696         href = href.match(/#.*/)[0];  // Because IE return full URL.
    697         n = refs[href];
    698         spans[i].innerHTML =
    699           "[<a href='#_footnote_" + n +
    700           "' title='View footnote' class='footnote'>" + n + "</a>]";
    701       }
    702     }
    703   }
    704 },
    705 
    706 install: function(toclevels) {
    707   var timerId;
    708 
    709   function reinstall() {
    710     asciidoc.footnotes();
    711     if (toclevels) {
    712       asciidoc.toc(toclevels);
    713     }
    714   }
    715 
    716   function reinstallAndRemoveTimer() {
    717     clearInterval(timerId);
    718     reinstall();
    719   }
    720 
    721   timerId = setInterval(reinstall, 500);
    722   if (document.addEventListener)
    723     document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
    724   else
    725     window.onload = reinstallAndRemoveTimer;
    726 }
    727 
    728 }
    729 asciidoc.install(2);
    730 /*]]>*/
    731 </script>
    732 </head>
    733 <body class="book">
    734 <div id="header">
    735 <h1>Vulkan on Android Implementor&#8217;s Guide</h1>
    736 <span id="revnumber">version 5</span>
    737 <div id="toc">
    738   <div id="toctitle">Table of Contents</div>
    739   <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
    740 </div>
    741 </div>
    742 <div id="content">
    743 <div id="preamble">
    744 <div class="sectionbody">
    745 <div class="paragraph"><p>This document is intended for GPU IHVs writing Vulkan drivers for Android, and OEMs integrating them for specific devices. It describes how a Vulkan driver interacts with the system, how GPU-specific tools should be installed, and Android-specific requirements.</p></div>
    746 </div>
    747 </div>
    748 <div class="sect1">
    749 <h2 id="_architecture">1. Architecture</h2>
    750 <div class="sectionbody">
    751 <div class="paragraph"><p>The primary interface between Vulkan applications and a device&#8217;s Vulkan driver is the loader, which is part of AOSP and installed at <span class="monospaced">/system/lib[64]/libvulkan.so</span>. The loader provides the core Vulkan API entry points, as well as entry points of a few extensions that are required on Android and always present. In particular, the window system integration (WSI) extensions are exported by the loader and primarily implemented in it rather than the driver. The loader also supports enumerating and loading layers which can expose additional extensions and/or intercept core API calls on their way to the driver.</p></div>
    752 <div class="paragraph"><p>The NDK will include a stub <span class="monospaced">libvulkan.so</span> exporting the same symbols as the loader. Calling the Vulkan functions exported from <span class="monospaced">libvulkan.so</span> will enter trampoline functions in the loader which will dispatch to the appropriate layer or driver based on their first argument. The <span class="monospaced">vkGet*ProcAddr</span> calls will return the function pointers that the trampolines would dispatch to, so calling through these function pointers rather than the exported symbols will be slightly more efficient since it skips the trampoline and dispatch.</p></div>
    753 <div class="sect2">
    754 <h3 id="_driver_enumeration_and_loading">1.1. Driver Enumeration and Loading</h3>
    755 <div class="paragraph"><p>Android expects the GPUs available to the system to be known when the system image is built, so its driver enumeration process isn&#8217;t as elaborate as on other platforms. The loader will use the existing HAL mechanism for discovering and loading the driver. As of this writing, the preferred paths for 32-bit and 64-bit Vulkan drivers are:</p></div>
    756 <div class="literalblock">
    757 <div class="content monospaced">
    758 <pre>/vendor/lib/hw/vulkan.&lt;ro.product.platform&gt;.so
    759 /vendor/lib64/hw/vulkan.&lt;ro.product.platform&gt;.so</pre>
    760 </div></div>
    761 <div class="paragraph"><p>where <span class="monospaced">&lt;ro.product.platform&gt;</span> is replaced by the value of the system property of that name. See <a href="https://android.googlesource.com/platform/hardware/libhardware/+/master/hardware.c">libhardware/hardware.c</a> for details and supported alternative locations.</p></div>
    762 <div class="paragraph"><p>The Vulkan <span class="monospaced">hw_module_t</span> derivative is currently trivial. If support for multiple drivers is ever added, the HAL module will export a list of strings that can be passed to the module <span class="monospaced">open</span> call. For the time being, only one driver is supported, and the constant string <span class="monospaced">HWVULKAN_DEVICE_0</span> is passed to <span class="monospaced">open</span>.</p></div>
    763 <div class="paragraph"><p>The Vulkan <span class="monospaced">hw_device_t</span> derivative corresponds to a single driver, though that driver can support multiple Vulkan physical devices. The <span class="monospaced">hw_device_t</span> structure contains a function pointer for the <span class="monospaced">vkGetInstanceProcAddr</span> function. The loader finds all other driver Vulkan functions by calling that <span class="monospaced">vkGetInstanceProcAddr</span> function.</p></div>
    764 </div>
    765 <div class="sect2">
    766 <h3 id="_layer_discovery_and_loading">1.2. Layer Discovery and Loading</h3>
    767 <div class="paragraph"><p>Android&#8217;s security model and policies differ significantly from other platforms. In particular, Android does not allow loading external code into a non-debuggable process on production (non-rooted) devices, nor does it allow external code to inspect or control the process&#8217;s memory/state/etc. This includes a prohibition on saving core dumps, API traces, etc. to disk for later inspection. So only layers delivered as part of the application will be enabled on production devices, and drivers must also not provide functionality that violates these policies.</p></div>
    768 <div class="paragraph"><p>There are three major use cases for layers:</p></div>
    769 <div class="olist arabic"><ol class="arabic">
    770 <li>
    771 <p>
    772 Development-time layers: validation layers, shims for tracing/profiling/debugging tools, etc. These shouldn&#8217;t be installed on the system image of production devices: they would be a waste of space for most users, and they should be updateable without requiring a system update. A developer wishing to use one of these during development has the ability to modify their application package (e.g. adding a file to their native libraries directory). IHV and OEM engineers who are trying to diagnose failures in shipping, unmodifiable apps are assumed to have access to non-production (rooted) builds of the system image.
    773 </p>
    774 </li>
    775 <li>
    776 <p>
    777 Utility layers, such as a layer that implements a heap for device memory. These layers will almost always expose extensions. Developers choose which layers, and which versions of those layers, to use in their application; different applications that use the same layer may still use different versions. Developers will choose which of these layers to ship in their application package.
    778 </p>
    779 </li>
    780 <li>
    781 <p>
    782 Injected layers, like framerate, social network, or game launcher overlays, which are provided by the user or some other application without the application&#8217;s knowledge or consent. These violate Android&#8217;s security policies and will not be supported.
    783 </p>
    784 </li>
    785 </ol></div>
    786 <div class="paragraph"><p>In the normal state the loader will only search in the application&#8217;s normal library search path (as defined by the system ClassLoader) for layers. It will attempt to load any shared library named <span class="monospaced">libVkLayer_*.so</span> as a layer library. Android does not use manifests to describe layers: because layers must have been deliberately included in the application by the developer, the motivation for manifests on other platforms don&#8217;t apply.</p></div>
    787 <div class="paragraph"><p>On debuggable devices (<span class="monospaced">ro.debuggable</span> property exists and is non-zero, generally rooted or engineering builds) the loader will also search the directory <span class="monospaced">/data/local/debug/vulkan</span> and attempt to load layer libraries it finds there. This directory doesn&#8217;t exist by default. On Android N and later, because this location is writable by adb, SELinux policies prevent mapping code located here as executable. So to use layers from here, SELinux enforcement must be disabled: <span class="monospaced">adb shell setenforce 0</span>. This mechanism is not intended for application developers, only for IHV and OEM engineers working on test devices that don&#8217;t have private or sensitive data.</p></div>
    788 <div class="paragraph"><p>Our goal is to allow layers to be ported with only build-environment changes between Android and other platforms. For this to work, layers must properly implement things like <span class="monospaced">vkGetInstanceLayerProperties</span> and <span class="monospaced">vkGetInstanceExtensionProperties</span>, even though the LunarG loader doesn&#8217;t use them (it gets the information from manifests instead).</p></div>
    789 </div>
    790 </div>
    791 </div>
    792 <div class="sect1">
    793 <h2 id="_window_system_integration">2. Window System Integration</h2>
    794 <div class="sectionbody">
    795 <div class="paragraph"><p>The <span class="monospaced">vk_wsi_swapchin</span> and <span class="monospaced">vk_wsi_device_swapchain</span> extensions are primarily be implemented by the platform and live in <span class="monospaced">libvulkan.so</span>. The <span class="monospaced">VkSwapchain</span> object and all interaction with <span class="monospaced">ANativeWindow</span> will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver&#8217;s <span class="monospaced">vkGetDeviceProcAddr</span> functions, after passing through any enabled layers.</p></div>
    796 <div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span>, but also on the intended usage of the swapchain. The swapchain usage bits are defined as</p></div>
    797 <div class="listingblock">
    798 <div class="content"><!-- Generator: GNU source-highlight 3.1.6
    799 by Lorenzo Bettini
    800 http://www.lorenzobettini.it
    801 http://www.gnu.org/software/src-highlite -->
    802 <pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">enum</span></span> VkSwapchainImageUsageFlagBitsANDROID <span style="color: #FF0000">{</span>
    803     VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID <span style="color: #990000">=</span> <span style="color: #993399">0x00000001</span><span style="color: #990000">,</span>
    804     VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM <span style="color: #990000">=</span> <span style="color: #993399">0x7FFFFFFF</span>
    805 <span style="color: #FF0000">}</span> VkSwapchainImageUsageFlagBitsANDROID<span style="color: #990000">;</span>
    806 <span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="color: #008080">VkFlags</span> VkSwapchainImageUsageFlagsANDROID<span style="color: #990000">;</span></tt></pre></div></div>
    807 <div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling</p></div>
    808 <div class="listingblock">
    809 <div class="content"><!-- Generator: GNU source-highlight 3.1.6
    810 by Lorenzo Bettini
    811 http://www.lorenzobettini.it
    812 http://www.gnu.org/software/src-highlite -->
    813 <pre><tt>VKAPI_ATTR <span style="color: #008080">VkResult</span> <span style="color: #008080">VKAPI_CALL</span> <span style="font-weight: bold"><span style="color: #000000">vkGetSwapchainGrallocUsage2ANDROID</span></span><span style="color: #990000">(</span>
    814     <span style="color: #008080">VkDevice</span>            device<span style="color: #990000">,</span>
    815     <span style="color: #008080">VkFormat</span>            format<span style="color: #990000">,</span>
    816     <span style="color: #008080">VkImageUsageFlags</span>   imageUsage<span style="color: #990000">,</span>
    817     <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span> swapchainImageUsage<span style="color: #990000">,</span>
    818     uint64_t<span style="color: #990000">*</span>           grallocConsumerUsage<span style="color: #990000">,</span>
    819     uint64_t<span style="color: #990000">*</span>           grallocProducerUsage<span style="color: #990000">,</span>
    820 <span style="color: #990000">);</span></tt></pre></div></div>
    821 <div class="paragraph"><p>The <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span> parameters are taken from the <span class="monospaced">VkSwapchainCreateInfoKHR</span> structure. The driver should fill <span class="monospaced">*grallocConsumerUsage</span> and <span class="monospaced">*grallocProducerUsage</span> with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.</p></div>
    822 <div class="paragraph"><p>An older version of this function is deprecated but still supported for backwards compatibility; it will be used if <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> is not supported:</p></div>
    823 <div class="listingblock">
    824 <div class="content"><!-- Generator: GNU source-highlight 3.1.6
    825 by Lorenzo Bettini
    826 http://www.lorenzobettini.it
    827 http://www.gnu.org/software/src-highlite -->
    828 <pre><tt>VkResult <span style="color: #008080">VKAPI</span> <span style="font-weight: bold"><span style="color: #000000">vkGetSwapchainGrallocUsageANDROID</span></span><span style="color: #990000">(</span>
    829     <span style="color: #008080">VkDevice</span>            device<span style="color: #990000">,</span>
    830     <span style="color: #008080">VkFormat</span>            format<span style="color: #990000">,</span>
    831     <span style="color: #008080">VkImageUsageFlags</span>   imageUsage<span style="color: #990000">,</span>
    832     <span style="color: #009900">int</span><span style="color: #990000">*</span>                grallocUsage
    833 <span style="color: #990000">);</span></tt></pre></div></div>
    834 <div class="paragraph"><p><span class="monospaced">VkNativeBufferANDROID</span> is a <span class="monospaced">vkCreateImage</span> extension structure for creating an image backed by a gralloc buffer. This structure is provided to <span class="monospaced">vkCreateImage</span> in the <span class="monospaced">VkImageCreateInfo</span> structure chain. Calls to <span class="monospaced">vkCreateImage</span> with this structure will happen during the first call to <span class="monospaced">vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)</span>. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a <span class="monospaced">VkImage</span> for each one.</p></div>
    835 <div class="listingblock">
    836 <div class="content"><!-- Generator: GNU source-highlight 3.1.6
    837 by Lorenzo Bettini
    838 http://www.lorenzobettini.it
    839 http://www.gnu.org/software/src-highlite -->
    840 <pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
    841     <span style="color: #008080">VkStructureType</span>             sType<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID</span></span>
    842     <span style="font-weight: bold"><span style="color: #0000FF">const</span></span> <span style="color: #009900">void</span><span style="color: #990000">*</span>                 pNext<span style="color: #990000">;</span>
    843 
    844     <span style="font-style: italic"><span style="color: #9A1900">// Buffer handle and stride returned from gralloc alloc()</span></span>
    845     <span style="color: #008080">buffer_handle_t</span>             handle<span style="color: #990000">;</span>
    846     <span style="color: #009900">int</span>                         stride<span style="color: #990000">;</span>
    847 
    848     <span style="font-style: italic"><span style="color: #9A1900">// Gralloc format and usage requested when the buffer was allocated.</span></span>
    849     <span style="color: #009900">int</span>                         format<span style="color: #990000">;</span>
    850     <span style="color: #009900">int</span>                         usage<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// deprecated</span></span>
    851     <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
    852         <span style="color: #008080">uint64_t</span>                consumer<span style="color: #990000">;</span>
    853         <span style="color: #008080">uint64_t</span>                producer<span style="color: #990000">;</span>
    854     <span style="color: #FF0000">}</span> usage2<span style="color: #990000">;</span>
    855 <span style="color: #FF0000">}</span> VkNativeBufferANDROID<span style="color: #990000">;</span></tt></pre></div></div>
    856 <div class="paragraph"><p>When creating a gralloc-backed image, the <span class="monospaced">VkImageCreateInfo</span> will have:</p></div>
    857 <div class="listingblock">
    858 <div class="content"><!-- Generator: GNU source-highlight 3.1.6
    859 by Lorenzo Bettini
    860 http://www.lorenzobettini.it
    861 http://www.gnu.org/software/src-highlite -->
    862 <pre><tt>  .imageType           = VK_IMAGE_TYPE_2D
    863   .format              = a VkFormat matching the format requested for the gralloc buffer
    864   .extent              = the 2D dimensions requested for the gralloc buffer
    865   .mipLevels           = 1
    866   .arraySize           = 1
    867   .samples             = 1
    868   .tiling              = VK_IMAGE_TILING_OPTIMAL
    869   .usage               = VkSwapChainCreateInfoWSI::imageUsageFlags
    870   .flags               = 0
    871   .sharingMode         = VkSwapChainCreateInfoWSI::sharingMode
    872   .queueFamilyCount    = VkSwapChainCreateInfoWSI::queueFamilyCount
    873   .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</tt></pre></div></div>
    874 <div class="paragraph"><p>Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a <span class="monospaced">VkSwapchainImageCreateInfoANDROID</span> extension structure in the <span class="monospaced">VkImageCreateInfo</span> chain provided to <span class="monospaced">vkCreateImage</span>, containing the swapchain image usage flags:</p></div>
    875 <div class="listingblock">
    876 <div class="content"><!-- Generator: GNU source-highlight 3.1.6
    877 by Lorenzo Bettini
    878 http://www.lorenzobettini.it
    879 http://www.gnu.org/software/src-highlite -->
    880 <pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
    881     <span style="color: #008080">VkStructureType</span>                        sType<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID</span></span>
    882     <span style="font-weight: bold"><span style="color: #0000FF">const</span></span> <span style="color: #009900">void</span><span style="color: #990000">*</span>                            pNext<span style="color: #990000">;</span>
    883 
    884     <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span>      usage<span style="color: #990000">;</span>
    885 <span style="color: #FF0000">}</span> VkSwapchainImageCreateInfoANDROID<span style="color: #990000">;</span></tt></pre></div></div>
    886 <div class="paragraph"><p><span class="monospaced">vkAcquireImageANDROID</span> acquires ownership of a swapchain image and imports an
    887 externally-signalled native fence into both an existing VkSemaphore object
    888 and an existing VkFence object:</p></div>
    889 <div class="listingblock">
    890 <div class="content"><!-- Generator: GNU source-highlight 3.1.6
    891 by Lorenzo Bettini
    892 http://www.lorenzobettini.it
    893 http://www.gnu.org/software/src-highlite -->
    894 <pre><tt>VkResult <span style="color: #008080">VKAPI</span> <span style="font-weight: bold"><span style="color: #000000">vkAcquireImageANDROID</span></span><span style="color: #990000">(</span>
    895     <span style="color: #008080">VkDevice</span>            device<span style="color: #990000">,</span>
    896     <span style="color: #008080">VkImage</span>             image<span style="color: #990000">,</span>
    897     <span style="color: #009900">int</span>                 nativeFenceFd<span style="color: #990000">,</span>
    898     <span style="color: #008080">VkSemaphore</span>         semaphore<span style="color: #990000">,</span>
    899     VkFence             fence
    900 <span style="color: #990000">);</span></tt></pre></div></div>
    901 <div class="paragraph"><p>This function is called during <span class="monospaced">vkAcquireNextImageWSI</span> to import a native
    902 fence into the <span class="monospaced">VkSemaphore</span> and <span class="monospaced">VkFence</span> objects provided by the
    903 application. Both semaphore and fence objects are optional in this call. The
    904 driver may also use this opportunity to recognize and handle any external
    905 changes to the gralloc buffer state; many drivers won&#8217;t need to do anything
    906 here. This call puts the <span class="monospaced">VkSemaphore</span> and <span class="monospaced">VkFence</span> into the same "pending"
    907 state as <span class="monospaced">vkQueueSignalSemaphore</span> and <span class="monospaced">vkQueueSubmit</span> respectively, so queues
    908 can wait on the semaphore and the application can wait on the fence. Both
    909 objects become signalled when the underlying native fence signals; if the
    910 native fence has already signalled, then the semaphore will be in the signalled
    911 state when this function returns. The driver takes ownership of the fence fd
    912 and is responsible for closing it when no longer needed. It must do so even if
    913 neither a semaphore or fence object is provided, or even if
    914 <span class="monospaced">vkAcquireImageANDROID</span> fails and returns an error. If <span class="monospaced">fenceFd</span> is -1, it
    915 is as if the native fence was already signalled.</p></div>
    916 <div class="paragraph"><p><span class="monospaced">vkQueueSignalReleaseImageANDROID</span> prepares a swapchain image for external use, and creates a native fence and schedules it to be signalled when prior work on the queue has completed.</p></div>
    917 <div class="listingblock">
    918 <div class="content"><!-- Generator: GNU source-highlight 3.1.6
    919 by Lorenzo Bettini
    920 http://www.lorenzobettini.it
    921 http://www.gnu.org/software/src-highlite -->
    922 <pre><tt>VkResult <span style="color: #008080">VKAPI</span> <span style="font-weight: bold"><span style="color: #000000">vkQueueSignalReleaseImageANDROID</span></span><span style="color: #990000">(</span>
    923     <span style="color: #008080">VkQueue</span>             queue<span style="color: #990000">,</span>
    924     <span style="color: #008080">uint32_t</span>            waitSemaphoreCount<span style="color: #990000">,</span>
    925     <span style="font-weight: bold"><span style="color: #0000FF">const</span></span> VkSemaphore<span style="color: #990000">*</span>  pWaitSemaphores<span style="color: #990000">,</span>
    926     <span style="color: #008080">VkImage</span>             image<span style="color: #990000">,</span>
    927     <span style="color: #009900">int</span><span style="color: #990000">*</span>                pNativeFenceFd
    928 <span style="color: #990000">);</span></tt></pre></div></div>
    929 <div class="paragraph"><p>This will be called during <span class="monospaced">vkQueuePresentWSI</span> on the provided queue. Effects are similar to <span class="monospaced">vkQueueSignalSemaphore</span>, except with a native fence instead of a semaphore. The native fence must: not signal until the <span class="monospaced">waitSemaphoreCount</span> semaphores in <span class="monospaced">pWaitSemaphores</span> have signaled. Unlike <span class="monospaced">vkQueueSignalSemaphore</span>, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set <span class="monospaced">*pNativeFenceFd</span> to -1. The file descriptor returned in <span class="monospaced">*pNativeFenceFd</span> is owned and will be closed by the caller. Many drivers will be able to ignore the <span class="monospaced">image</span> parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to <span class="monospaced">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>.</p></div>
    930 <div class="paragraph"><p>If <span class="monospaced">image</span> was created with <span class="monospaced">VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID</span>, then the driver must tolerate <span class="monospaced">vkQueueSignalReleaseImageANDROID</span> being called repeatedly without intervening calls to <span class="monospaced">vkAcquireImageANDROID</span>.</p></div>
    931 </div>
    932 </div>
    933 <div class="sect1">
    934 <h2 id="_history">3. History</h2>
    935 <div class="sectionbody">
    936 <div class="olist arabic"><ol class="arabic">
    937 <li>
    938 <p>
    939 <strong>2015-07-08</strong> Initial version
    940 </p>
    941 </li>
    942 <li>
    943 <p>
    944 <strong>2015-08-16</strong>
    945 </p>
    946 <div class="ulist"><ul>
    947 <li>
    948 <p>
    949 Renamed to Implementor&#8217;s Guide
    950 </p>
    951 </li>
    952 <li>
    953 <p>
    954 Wording and formatting changes
    955 </p>
    956 </li>
    957 <li>
    958 <p>
    959 Updated based on resolution of Khronos bug 14265
    960 </p>
    961 </li>
    962 <li>
    963 <p>
    964 Deferred support for multiple drivers
    965 </p>
    966 </li>
    967 </ul></div>
    968 </li>
    969 <li>
    970 <p>
    971 <strong>2015-11-04</strong>
    972 </p>
    973 <div class="ulist"><ul>
    974 <li>
    975 <p>
    976 Added vkGetSwapchainGrallocUsageANDROID
    977 </p>
    978 </li>
    979 <li>
    980 <p>
    981 Replaced vkImportNativeFenceANDROID and vkQueueSignalNativeFenceANDROID
    982      with vkAcquireImageANDROID and vkQueueSignalReleaseImageANDROID, to allow
    983      drivers to known the ownership state of swapchain images.
    984 </p>
    985 </li>
    986 </ul></div>
    987 </li>
    988 <li>
    989 <p>
    990 <strong>2015-12-03</strong>
    991 </p>
    992 <div class="ulist"><ul>
    993 <li>
    994 <p>
    995 Added a VkFence parameter to vkAcquireImageANDROID corresponding to the
    996      parameter added to vkAcquireNextImageKHR.
    997 </p>
    998 </li>
    999 </ul></div>
   1000 </li>
   1001 <li>
   1002 <p>
   1003 <strong>2016-01-08</strong>
   1004 </p>
   1005 <div class="ulist"><ul>
   1006 <li>
   1007 <p>
   1008 Added waitSemaphoreCount and pWaitSemaphores parameters to vkQueueSignalReleaseImageANDROID.
   1009 </p>
   1010 </li>
   1011 </ul></div>
   1012 </li>
   1013 <li>
   1014 <p>
   1015 <strong>2016-06-17</strong>
   1016 </p>
   1017 <div class="ulist"><ul>
   1018 <li>
   1019 <p>
   1020 Updates to reflect final behavior, closed some TBDs now that they&#8217;ve BDed.
   1021 </p>
   1022 </li>
   1023 </ul></div>
   1024 </li>
   1025 <li>
   1026 <p>
   1027 <strong>2017-01-06</strong>
   1028 </p>
   1029 <div class="ulist"><ul>
   1030 <li>
   1031 <p>
   1032 Extension version 6
   1033 </p>
   1034 </li>
   1035 <li>
   1036 <p>
   1037 Added VkSwapchainImageUsageFlagBitsANDROID
   1038 </p>
   1039 </li>
   1040 <li>
   1041 <p>
   1042 Added vkGetSwapchainGrallocUsage2ANDROID
   1043 </p>
   1044 </li>
   1045 <li>
   1046 <p>
   1047 Added VkSwapchainImageCreateInfoANDROID
   1048 </p>
   1049 </li>
   1050 </ul></div>
   1051 </li>
   1052 <li>
   1053 <p>
   1054 <strong>2017-02-09</strong>
   1055 </p>
   1056 <div class="ulist"><ul>
   1057 <li>
   1058 <p>
   1059 Extended vkGetSwapchainGrallocUsage2ANDROID and VkNativeBufferANDROID to use gralloc1-style usage bitfields.
   1060 </p>
   1061 </li>
   1062 </ul></div>
   1063 </li>
   1064 </ol></div>
   1065 </div>
   1066 </div>
   1067 </div>
   1068 <div id="footnotes"><hr></div>
   1069 <div id="footer">
   1070 <div id="footer-text">
   1071 Version 5<br>
   1072 Last updated 2017-02-09 22:40:30 PST
   1073 </div>
   1074 </div>
   1075 </body>
   1076 </html>
   1077