Requisites
For the context of this post, it is assumed a ‘content_shell’ ChromeOS GN build produced with the following commands:
$ gn gen --args='target_os="chromeos" use_ozone=true ozone_platform_wayland=true use_wayland_egl=false ozone_platform_x11=true ozone_platform_headless=true ozone_auto_platforms=false' out-gn-ozone $ ninja -C out-gn-ozone blink_tests
Ozone at runtime
Looking at the build arguments above, one can foresee that the content_shell app will be able to run on the following Graphics backends: Wayland (no EGL), X11, and “headless” – and it indeed is. This happens thanks to Chromium’s Graphics layer abstraction, Ozone.
So, in order to run content_shell app on Ozone platform “bleh”, one simply does:
$ out-gn-ozone/content_shell --ozone-platform="bleh"
Simple no? Well, yes .. and not as much.
The way the desired Ozone platform classes/objects are instantiated is interesting, involving c++ templates, GYP/GN hooks, python generated code, and more. This post aims to detail the process some more.
Ozone platform selection logic
Two methods kick off OzonePlatform instantiation: ::InitializeForUI and ::InitializeForGPU . They both call ::CreateInstance(), which is our starting point.
This is how simple it looks:
63 void OzonePlatform::CreateInstance() { 64 if (!instance_) { (..) 69 std::unique_ptr<OzonePlatform> platform = 70 PlatformObject<OzonePlatform>::Create(); 71 72 // TODO(spang): Currently need to leak this object. 73 OzonePlatform* pl = platform.release(); 74 DCHECK_EQ(instance_, pl); 75 } 76 }
Essentially, when PlatformObject<T>::Create is ran (lines 69 and 70), it ends up calling a method named Create{T}Bleh, where
- “T” is the template argument name, e.g. “OzonePlatform”.
- “bleh” is the value passed to –ozone-platform command line parameter.
For instance, in the case of ./content_shell –ozone-platform=x11, the method called would CreateOzonePlatformX11, following the pattern Create{T}Bleh (i.e. “Create”+”OzonePlatform”+”X11”).
The actual logic
In order to understand how PlatformObject class works, lets start by looking at its definition (ui/ozone/platform_object.h & platform_internal_object.h):
template <class T> class PlatformObject { public: static std::unique_ptr<T> Create(); };
16 template <class T> 17 std::unique_ptr<T> PlatformObject<T>::Create() { 18 typedef typename PlatformConstructorList<T>::Constructor Constructor; 19 20 // Determine selected platform (from --ozone-platform flag, or default). 21 int platform = GetOzonePlatformId(); 22 23 // Look up the constructor in the constructor list. 24 Constructor constructor = PlatformConstructorList<T>::kConstructors[platform]; 26 // Call the constructor. 27 return base::WrapUnique(constructor()); 28 }
In line 24 (highlighted above), the ozone platform runtime selection machinery actually happens. It retrieves a Constructor, which is a typedef for a PlatformConstructorList<T>::Constructor.
By looking at the definition of PlatformConstructorList class (below), Constructor is actually a pointer to a function that returns a T*.
14 template <class T> 15 struct PlatformConstructorList { 16 typedef T* (*Constructor)(); 17 static const Constructor kConstructors[kPlatformCount]; 18 };
Ok, so basically here is what we know this far:
- OzonePlatform::CreateInstance method calls OzonePlatform<bleh>::Create
- OzonePlatform<bleh>::Create picks up an index and retrieves a PlatformConstructorList<bleh>::Constructor (via kConstructor[index])
- PlatformConstructorList<bleh>::Constructor is a typedef to a function pointer that returns a bleh*.
- (..)
- This chain ends up calling Create{bleh}{ozone_platform}()
But wait! kConstructors, the array of pointers to functions – that solves the puzzle – is not defined anywhere in src/! 🙂
This is because its actual definition is part of some generated code triggered by specific GN/GYP hooks. They are:
- generate_ozone_platform_list which generates out/../platform_list.cc,h,txt
- generate_constructor_list which generates out/../constructor_list.cc though generate_constructor_list.py
In the end out/../generate_constructor_list.cc has the definition of kConstructors.
Again, in the specific case of the given GN build arguments, kConstructors would look like:
template <> const OzonePlatformConstructor PlatformConstructorList<ui::OzonePlatform>::kConstructors[] = { &ui::CreateOzonePlatformHeadless, &ui::CreateOzonePlatformWayland, &ui::CreateOzonePlatformX11, };
Logic wrap up
- GYP/GN hooks are ran at build time, and generate plaform_list.{txt,c,h} as well as constructor_list.cc files respecting ozone_platform_bleh parameters.
- constructor_list.cc has PlatformConstructorList<bleh>kConstructors actually populated.
- ./content_shell –ozone-platform=bleh is called
- OzonePlatform::InitializeFor{UI,Gpu}()
- OzonePlatform::CreateInstance()
- PlaformObject<OzonePlatformBleh>::Create()
- PlatformConstructorList<bleh>::Constructor is retrieved – it is a pointer to a function stored in PlatformConstructorList<bleh>::kConstructor
- function is ran and an OzonePlatformBleh instance is returned.