{"id":22,"date":"2016-06-14T04:28:44","date_gmt":"2016-06-14T04:28:44","guid":{"rendered":"http:\/\/blogs.igalia.com\/tonikitoo\/?p=22"},"modified":"2016-06-15T13:55:14","modified_gmt":"2016-06-15T13:55:14","slug":"understanding-chromiums-runtime-ozone-platform-selection","status":"publish","type":"post","link":"https:\/\/blogs.igalia.com\/tonikitoo\/2016\/06\/14\/understanding-chromiums-runtime-ozone-platform-selection\/","title":{"rendered":"Understanding Chromium&#8217;s runtime ozone platform selection"},"content":{"rendered":"<h3>Requisites<\/h3>\n<p>For the context of this post,\u00a0it is assumed a &#8216;content_shell&#8217; ChromeOS\u00a0GN build produced with the following commands:<\/p>\n<pre>$ gn gen --args='<strong>target_os<\/strong>=\"chromeos\"\r\n<strong>                 use_ozone<\/strong>=true\r\n                \u00a0<strong>ozone_platform_wayland<\/strong>=true\r\n                 <strong>use_wayland_egl<\/strong>=false\r\n<strong>                 ozone_platform_x11<\/strong>=true\r\n<strong>                 ozone_platform_headless<\/strong>=true\r\n                 <strong>ozone_auto_platforms<\/strong>=false' out-gn-ozone\r\n\r\n$ ninja -C out-gn-ozone blink_tests<\/pre>\n<h3>Ozone at runtime<\/h3>\n<p>Looking at the\u00a0build\u00a0arguments above, one can foresee that the\u00a0<em>content_shell <\/em>app will be able to run on the following Graphics backends: Wayland (no EGL), X11, and &#8220;headless&#8221; &#8211; and\u00a0it indeed is. This happens\u00a0thanks to\u00a0Chromium&#8217;s Graphics layer abstraction, <a href=\"https:\/\/www.chromium.org\/developers\/design-documents\/ozone\"><strong>Ozone<\/strong><\/a>.<\/p>\n<p>So, in order to run <em>content_shell<\/em> app\u00a0on Ozone platform &#8220;bleh&#8221;, one simply does:<\/p>\n<pre>$ out-gn-ozone\/content_shell --ozone-platform=\"bleh\"<\/pre>\n<p>Simple no?\u00a0Well, yes .. and not as much.<\/p>\n<p>The way the desired\u00a0Ozone platform classes\/objects are\u00a0instantiated is interesting, involving c++ templates, GYP\/GN hooks, python generated code, and more. This post aims\u00a0to\u00a0detail the process some more.<\/p>\n<h3>Ozone platform selection logic<\/h3>\n<p>Two methods kick off\u00a0OzonePlatform instantiation: <a href=\"https:\/\/cs.chromium.org\/chromium\/src\/ui\/ozone\/public\/ozone_platform.h?q=initializeforui&amp;sq=package:chromium&amp;l=51&amp;dr=CSs\">::InitializeForUI and ::InitializeForGPU<\/a>\u00a0. They both call ::CreateInstance(), which\u00a0is our starting point.<\/p>\n<p>This is how\u00a0simple it looks:<\/p>\n<pre>63 void OzonePlatform::CreateInstance() {\r\n64 \u00a0 \u00a0 if (!instance_) {\r\n(..)\r\n69 \u00a0 \u00a0 \u00a0 \u00a0 <strong>std::unique_ptr&lt;OzonePlatform&gt; platform =<\/strong>\r\n70 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <strong>PlatformObject&lt;OzonePlatform&gt;::Create();<\/strong>\r\n71\r\n72 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ TODO(spang): Currently need to leak this object.\r\n73 \u00a0 \u00a0 \u00a0 \u00a0 OzonePlatform* pl = platform.release();\r\n74 \u00a0 \u00a0 \u00a0 \u00a0 DCHECK_EQ(instance_, pl);\r\n75 \u00a0 \u00a0 }\r\n76 }\r\n<\/pre>\n<p>Essentially, when\u00a0PlatformObject&lt;T&gt;::Create is ran (lines 69 and 70), it\u00a0ends up calling a method named Create{T}<strong>Bleh<\/strong>, where<\/p>\n<ul>\n<li>&#8220;T&#8221; is the template argument name, e.g. &#8220;OzonePlatform&#8221;.<\/li>\n<li><span style=\"line-height: 1.75\">&#8220;bleh&#8221; is the value passed to &#8211;ozone-platform command line parameter.<\/span><\/li>\n<\/ul>\n<p>For instance, in the case of .\/<em>content_shell\u00a0&#8211;ozone-platform=x11<\/em>,\u00a0the method called would <em>CreateOzonePlatformX11<\/em>, following the pattern\u00a0<span style=\"line-height: 1.75\">Create{T}<\/span><strong style=\"line-height: 1.75\">Bleh\u00a0<\/strong>(i.e. &#8220;Create&#8221;+&#8221;OzonePlatform&#8221;+&#8221;X11&#8221;).<\/p>\n<h3><\/h3>\n<h3>The actual\u00a0logic<\/h3>\n<p>In order to understand how <em>PlatformObject<\/em> class\u00a0works, lets start by\u00a0looking at its\u00a0definition\u00a0(ui\/ozone\/platform_object.h &amp;\u00a0platform_internal_object.h):<\/p>\n<pre>template &lt;class T&gt;\u00a0class PlatformObject {\r\n public:\r\n  static std::unique_ptr&lt;T&gt; Create();\r\n};<\/pre>\n<pre>16 template &lt;class T&gt;\r\n17 std::unique_ptr&lt;T&gt; PlatformObject&lt;T&gt;::Create() {\r\n18 \u00a0 \u00a0 typedef typename PlatformConstructorList&lt;T&gt;::Constructor Constructor;\r\n19\r\n20 \u00a0 \u00a0 \/\/ Determine selected platform (from --ozone-platform flag, or default).\r\n<strong>21 \u00a0 \u00a0 int platform = GetOzonePlatformId();<\/strong>\r\n22\r\n23 \u00a0 \u00a0 \/\/ Look up the constructor in the constructor list.\r\n<strong>24 \u00a0 \u00a0 Constructor constructor = PlatformConstructorList&lt;T&gt;::kConstructors[platform];<\/strong>\r\n26 \u00a0 \u00a0 \/\/ Call the constructor.\r\n27 \u00a0 \u00a0 return base::WrapUnique(constructor());\r\n28 }\r\n<\/pre>\n<p>In\u00a0line 24\u00a0(highlighted above), the ozone platform runtime selection machinery actually happens. It\u00a0retrieves a <em>Constructor<\/em>, which is a typedef for a PlatformConstructorList&lt;T&gt;::Constructor.<\/p>\n<p>By looking at the definition of\u00a0<em>PlatformConstructorList<\/em> class (below), <em>Constructor<\/em> is actually a pointer to a function\u00a0that returns a T*.<\/p>\n<pre>14 template &lt;class T&gt;\r\n15 struct PlatformConstructorList {\r\n16 \u00a0 \u00a0 typedef T* (*Constructor)();\r\n17 \u00a0 \u00a0 static const Constructor <strong>kConstructors<\/strong>[kPlatformCount];\r\n18 };\r\n<\/pre>\n<p>Ok, so basically here is what\u00a0we know\u00a0this far:<\/p>\n<ol>\n<li>OzonePlatform::CreateInstance method calls\u00a0OzonePlatform&lt;bleh&gt;::Create<\/li>\n<li>OzonePlatform&lt;bleh&gt;::Create picks up an\u00a0<em>index<\/em> and\u00a0retrieves a\u00a0PlatformConstructorList&lt;bleh&gt;::Constructor (via kConstructor[index])<\/li>\n<li>PlatformConstructorList&lt;bleh&gt;::Constructor is\u00a0a typedef to a function pointer that returns a <em>bleh*.<\/em><\/li>\n<li>(..)<\/li>\n<li>This chain\u00a0ends up calling Create{bleh}{ozone_platform}()<\/li>\n<\/ol>\n<p>But wait!\u00a0<em>kConstructors<\/em>, the\u00a0array of pointers to functions &#8211; that solves the\u00a0puzzle &#8211; is\u00a0<strong>not<\/strong> defined anywhere in src\/! \ud83d\ude42<\/p>\n<p>This is because its actual definition is part of some generated code triggered by\u00a0specific GN\/GYP hooks. They are:<\/p>\n<ul>\n<li><em>generate_ozone_platform_list<\/em>\u00a0which\u00a0generates out\/..\/platform_list.cc,h,txt<\/li>\n<li><em>generate_constructor_list<\/em>\u00a0which\u00a0generates <em>out\/..\/constructor_list.cc<\/em> though\u00a0<em>generate_constructor_list.py<\/em><\/li>\n<\/ul>\n<p>In the end <em>out\/..\/generate_constructor_list.cc<\/em> has the definition of\u00a0<em>kConstructors.<\/em><\/p>\n<p>Again, in the specific case of the\u00a0given GN build arguments, <em>kConstructors <\/em>would look like:<\/p>\n<pre>template &lt;&gt; const OzonePlatformConstructor\r\nPlatformConstructorList&lt;ui::OzonePlatform&gt;::<strong>kConstructors<\/strong>[] = {\r\n    &amp;ui::CreateOzonePlatformHeadless,\r\n    &amp;ui::CreateOzonePlatformWayland,\r\n    &amp;ui::CreateOzonePlatformX11,\r\n};\r\n<\/pre>\n<h3><\/h3>\n<h3>Logic wrap up<\/h3>\n<ul>\n<li>GYP\/GN hooks are ran at build time, and generate\u00a0plaform_list.{txt,c,h} as well as constructor_list.cc files respecting <em>ozone_platform_bleh<\/em> parameters.\n<ul>\n<li>constructor_list.cc has PlatformConstructorList&lt;bleh&gt;<em>kConstructors<\/em>\u00a0actually populated.<\/li>\n<\/ul>\n<\/li>\n<li><em>.\/content_shell &#8211;ozone-platform=bleh<\/em> is called<\/li>\n<li>OzonePlatform::InitializeFor{UI,Gpu}()<\/li>\n<li>OzonePlatform::CreateInstance()<\/li>\n<li>PlaformObject&lt;OzonePlatformB<em>leh&gt;::<\/em>Create()<\/li>\n<li>PlatformConstructorList&lt;bleh&gt;::Constructor is retrieved &#8211; it is a pointer to a function stored in\u00a0PlatformConstructorList&lt;bleh&gt;::kConstructor<\/li>\n<li>function is ran and an OzonePlatformBleh instance is returned.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Requisites For the context of this post,\u00a0it is assumed a &#8216;content_shell&#8217; ChromeOS\u00a0GN build produced with the following commands: $ gn gen &#8211;args=&#8217;target_os=&#8221;chromeos&#8221; use_ozone=true \u00a0ozone_platform_wayland=true use_wayland_egl=false ozone_platform_x11=true ozone_platform_headless=true ozone_auto_platforms=false&#8217; out-gn-ozone $ ninja -C out-gn-ozone blink_tests Ozone at runtime Looking at the\u00a0build\u00a0arguments above, one can foresee that the\u00a0content_shell app will be able to run on the following &hellip; <a href=\"https:\/\/blogs.igalia.com\/tonikitoo\/2016\/06\/14\/understanding-chromiums-runtime-ozone-platform-selection\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Understanding Chromium&#8217;s runtime ozone platform selection<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":38,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,8,13,11,10],"tags":[],"class_list":["post-22","post","type-post","status-publish","format-standard","hentry","category-browsers","category-chromium","category-igalia","category-ozone","category-wayland"],"_links":{"self":[{"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/posts\/22","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/users\/38"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/comments?post=22"}],"version-history":[{"count":22,"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/posts\/22\/revisions"}],"predecessor-version":[{"id":46,"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/posts\/22\/revisions\/46"}],"wp:attachment":[{"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/media?parent=22"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/categories?post=22"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.igalia.com\/tonikitoo\/wp-json\/wp\/v2\/tags?post=22"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}