android上openxr sdk查找并加载runtime流程-爱代码爱编程 (2024)

OpenXR SDK地址为:GitHub - KhronosGroup/OpenXR-SDK-Source: Sources for OpenXR loader, basic API layers, and example code.

Monado runtime地址为:Monado / Monado · GitLab

system broker地址为:Ryan Pavlik / openxr-android-broker · GitLab

本文主要讲解openxr应用如何选择monado runtime的主要流程。

其完整流程图如下

android上openxr sdk查找并加载runtime流程-爱代码爱编程 (1)

由于图太大,有的地方不清晰,故将某些部分截图重新展示

CreateInstance流程如下

android上openxr sdk查找并加载runtime流程-爱代码爱编程 (2)

查找runtime流程如下

android上openxr sdk查找并加载runtime流程-爱代码爱编程 (3)

API layer load流程图如下

android上openxr sdk查找并加载runtime流程-爱代码爱编程 (4)

具体流程如下

目录

1.实例化OpenXrProgram

2.CreateOpenXrProgram

3. 实例化后会CreateInstance

4. LogLayersAndExtensions

5.xrEnumerateInstanceExtensionProperties

6.LoaderXrEnumerateInstanceExtensionProperties

7. RuntimeInterface::LoadRuntime-查找并加载runtime

8.RuntimeManifestFile::FindManifestFiles--查找runtime

9.GetPlatformRuntimeVirtualManifest--查找runtime-标志1

10.getActiveRuntimeVirtualManifest--查找runtime-标志1

11. PlatformGetGlobalRuntimeFileName--查找runtime-标志3

12.CreateIfValid-标志2

13.CreateIfValid-标志4

14. TryLoadingSingleRuntime--加载runtime-标志3

以SDK中的hello_xr应用为例,其入口在main.cpp中

1.实例化OpenXrProgram

main.cppvoid android_main(struct android_app* app) {...// Initialize the OpenXR program.//实例化OpenXrProgramstd::shared_ptr<IOpenXrProgram> program = CreateOpenXrProgram(options, platformPlugin, graphicsPlugin);// Initialize the loader for this platformPFN_xrInitializeLoaderKHR initializeLoader = nullptr;//将LoaderXrInitializeLoaderKHR方法指针赋值给initializeLoaderif (XR_SUCCEEDED( xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)(&initializeLoader)))) { XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid = {XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; loaderInitInfoAndroid.applicationVM = app->activity->vm; loaderInitInfoAndroid.applicationContext = app->activity->clazz;//执行LoaderXrInitializeLoaderKHR方法 initializeLoader((const XrLoaderInitInfoBaseHeaderKHR*)&loaderInitInfoAndroid);}//CreateInstanceprogram->CreateInstance();program->InitializeSystem();...}

2.CreateOpenXrProgram

openxr_program.cpp

std::shared_ptr<IOpenXrProgram> CreateOpenXrProgram(const std::shared_ptr<Options>& options, const std::shared_ptr<IPlatformPlugin>& platformPlugin, const std::shared_ptr<IGraphicsPlugin>& graphicsPlugin) {//make_shared函数的主要功能是在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr;由于是通过shared_ptr管理内存,因此一种安全分配和使用动态内存的方法。 return std::make_shared<OpenXrProgram>(options, platformPlugin, graphicsPlugin);}
struct OpenXrProgram : IOpenXrProgram { OpenXrProgram(const std::shared_ptr<Options>& options, const std::shared_ptr<IPlatformPlugin>& platformPlugin, const std::shared_ptr<IGraphicsPlugin>& graphicsPlugin) : m_options(options),//赋值 m_platformPlugin(platformPlugin), m_graphicsPlugin(graphicsPlugin), m_acceptableBlendModes{XR_ENVIRONMENT_BLEND_MODE_OPAQUE, XR_ENVIRONMENT_BLEND_MODE_ADDITIVE, XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND} {}

3. 实例化后会CreateInstance

openxr_program.cppvoid CreateInstance() override { LogLayersAndExtensions(); CreateInstanceInternal(); LogInstanceInfo();}

4. LogLayersAndExtensions

openxr_program.cppstatic void LogLayersAndExtensions() { // Write out extension properties for a given layer. const auto logExtensions = [](const char* layerName, int indent = 0) { uint32_t instanceExtensionCount; //CHECK_XRCMD是用于判断函数是否执行FAILED //xrEnumerateInstanceExtensionProperties返回可用实例扩展的属性 CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(layerName, 0, &instanceExtensionCount, nullptr));......}

5.xrEnumerateInstanceExtensionProperties

loader_core.cppLOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateInstanceExtensionProperties(const char *layerName, uint32_t propertyCapacityInput, uint32_t *propertyCountOutput, XrExtensionProperties *properties) { return LoaderXrEnumerateInstanceExtensionProperties(layerName, propertyCapacityInput, propertyCountOutput, properties);}

6.LoaderXrEnumerateInstanceExtensionProperties

loader_core.cppstatic XRAPI_ATTR XrResult XRAPI_CALLLoaderXrEnumerateInstanceExtensionProperties(const char *layerName, uint32_t propertyCapacityInput, uint32_t *propertyCountOutput, XrExtensionProperties *properties) XRLOADER_ABI_TRY { ... // Get the layer extension properties result = ApiLayerInterface::GetInstanceExtensionProperties("xrEnumerateInstanceExtensionProperties", layerName, extension_properties); if (XR_SUCCEEDED(result) && !just_layer_properties) { // If not specific to a layer, get the runtime extension properties //如果不是特定于某个层,则获取运行时扩展属性 result = RuntimeInterface::LoadRuntime("xrEnumerateInstanceExtensionProperties"); if (XR_SUCCEEDED(result)) { RuntimeInterface::GetRuntime().GetInstanceExtensionProperties(extension_properties); ...}

7. RuntimeInterface::LoadRuntime-查找并加载runtime

runtime_interface.cppXrResult RuntimeInterface::LoadRuntime(const std::string& openxr_command) { // If something's already loaded, we're done here. //如果已经有Instance实例了,则return, 这代表一个APP只有一个Instance实例 if (GetInstance() != nullptr) { return XR_SUCCESS; }#ifdef XR_KHR_LOADER_INIT_SUPPORT if (!LoaderInitData::instance().initialized()) {//Instance没有成功初始化 LoaderLogger::LogErrorMessage( openxr_command, "RuntimeInterface::LoadRuntime cannot run because xrInitializeLoaderKHR was not successfully called."); return XR_ERROR_INITIALIZATION_FAILED; }#endif // XR_KHR_LOADER_INIT_SUPPORT std::vector<std::unique_ptr<RuntimeManifestFile>> runtime_manifest_files = {}; // Find the available runtimes which we may need to report information for. //查找可用的runtime,会对runtime_manifest_files赋值 XrResult last_error = RuntimeManifestFile::FindManifestFiles(runtime_manifest_files); if (XR_FAILED(last_error)) { LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - unknown error"); } else { last_error = XR_ERROR_RUNTIME_UNAVAILABLE; for (std::unique_ptr<RuntimeManifestFile>& manifest_file : runtime_manifest_files) { //加载runtime-----标志5 last_error = RuntimeInterface::TryLoadingSingleRuntime(openxr_command, manifest_file); if (XR_SUCCEEDED(last_error)) { break; } } } // Unsuccessful in loading any runtime, throw the runtime unavailable message. //如果没有加载到任何的runtime,会返回错误,且应用无法正常运转,表现大多为黑屏,或者卡在启动动画上 if (XR_FAILED(last_error)) { LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntimes - failed to load a runtime"); last_error = XR_ERROR_RUNTIME_UNAVAILABLE; } return last_error;}

8.RuntimeManifestFile::FindManifestFiles--查找runtime

manifest_file.cpp// Find all manifest files in the appropriate search paths/registries for the given type.XrResult RuntimeManifestFile::FindManifestFiles(std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {#if defined(XR_KHR_LOADER_INIT_SUPPORT) Json::Value virtualManifest; //获取runtime result = GetPlatformRuntimeVirtualManifest(virtualManifest);//标志1 if (XR_SUCCESS == result) { //将获取到的virtualManifest中的信息赋值给manifest_files RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files);//标志2 return result; }#endif // defined(XR_KHR_LOADER_INIT_SUPPORT)//如果上面通过GetPlatformRuntimeVirtualManifest没有找到runtime,则会通过PlatformGetGlobalRuntimeFileName继续查找 if (!PlatformGetGlobalRuntimeFileName(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) {//标志3 LoaderLogger::LogErrorMessage( "", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment"); return XR_ERROR_RUNTIME_UNAVAILABLE; } result = XR_SUCCESS; LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::FindManifestFiles - using global runtime file " + filename);#endif }  //将filename文件中的信息赋值给manifest_files  RuntimeManifestFile::CreateIfValid(filename, manifest_files);//标志4...}

9.GetPlatformRuntimeVirtualManifest--查找runtime-标志1

runtime_interface.cpp#ifdef XR_USE_PLATFORM_ANDROIDXrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) { using wrap::android::content::Context; auto& initData = LoaderInitData::instance(); if (!initData.initialized()) { return XR_ERROR_INITIALIZATION_FAILED; } auto context = Context(reinterpret_cast<jobject>(initData.getData().applicationContext)); if (context.isNull()) { return XR_ERROR_INITIALIZATION_FAILED; } Json::Value virtualManifest; //即将通过Cursor要去查系统中的runtime了 if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) { return XR_ERROR_INITIALIZATION_FAILED; } //virtualManifest赋值给out_manifest  out_manifest = virtualManifest; return XR_SUCCESS;}#endif // XR_USE_PLATFORM_ANDROID

10.getActiveRuntimeVirtualManifest--查找runtime-标志1

android_utilities.cpp

int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest) { jni::Array<std::string> projection = makeArray({active_runtime::Columns::PACKAGE_NAME,active_runtime::Columns::NATIVE_LIB_DIR, active_runtime::Columns::SO_FILENAME, active_runtime::Columns::HAS_FUNCTIONS}); // First, try getting the installable broker's provider bool systemBroker = false; Cursor cursor; //log输出:08-02 08:43:48.651 4294 4331 I OpenXR-Loader: getActiveRuntimeCursor: Querying URI: content://org.khronos.openxr.runtime_broker/openxr/1/abi/arm64-v8a/runtimes/active/0//非系统应用 if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) { // OK, try the system broker as a fallback. //是系统应用,不是通过用户去安装的 systemBroker = true; getActiveRuntimeCursor(context, projection, systemBroker, cursor); //log输出:08-02 08:43:48.654 4294 4331 I OpenXR-Loader: getActiveRuntimeCursor: Querying URI: content://org.khronos.openxr.system_runtime_broker/openxr/1/abi/arm64-v8a/runtimes/active/0 } if (cursor.isNull()) { // Couldn't find either broker ALOGE("Could access neither the installable nor system runtime broker."); return -1; } cursor.moveToFirst();//通过cursor去查column对应的内容 auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME)); auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR)); auto packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME)); auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1; __android_log_print(ANDROID_LOG_INFO, TAG, "Got runtime: package: %s, so filename: %s, native lib dir: %s, has functions: %s", packageName.c_str(), filename.c_str(), libDir.c_str(), (hasFunctions ? "yes" : "no"));//log输出: 08-02 08:39:29.543 4425 4462 I OpenXR-Loader: Got runtime: package: org.freedesktop.monado.openxr_runtime.out_of_process, so filename: libopenxr_monado.so, native lib dir:/data/app/~~ZNtBhWeJzxYgOPuiZZVjgQ==/org.freedesktop.monado.openxr_runtime.out_of_process-PVkP76Im0S4PZZp5d2Gb5g==/lib/arm64, has functions: no//可以看到通过URI查出来的包名是org.freedesktop.monado.openxr_runtime.out_of_process, 对应的runtime so名字是libopenxr_monado.so auto lib_path = libDir + "/" + filename;//lib_path就是runtime so的全路径 cursor.close();//创建json文件,并将lib_path传入,root 名为runtime, lib_path为子节点 JsonManifestBuilder builder{"runtime", lib_path}; if (hasFunctions) { int result = populateFunctions(context, systemBroker, packageName, builder); if (result != 0) { return result; } }//json文件创建 virtualManifest = builder.build(); return 0;}} // namespace openxr_android

11. PlatformGetGlobalRuntimeFileName--查找runtime-标志3

platform_utils.hpp此函数会在"/product", "/odm", "/oem", "/vendor", "/system"目录下的/etc/openxr/下查找active_runtime.json文件// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most casesstatic inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { // Prefix for the runtime JSON file name static const char* rt_dir_prefixes[] = {"/product", "/odm", "/oem", "/vendor", "/system"}; static const std::string rt_filename = "/active_runtime.json"; static const std::string subdir = "/etc/openxr/"; for (const auto prefix : rt_dir_prefixes) { auto path = prefix + subdir + std::to_string(major_version) + rt_filename; struct stat buf; if (0 == stat(path.c_str(), &buf)) { file_name = path;//返回active_runtime.json所在的目录 return true; } } return false;}

12.CreateIfValid-标志2

manifest_file.cppvoid RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std::string &filename,std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) { std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid "); JsonVersion file_version = {}; if (!ManifestFile::IsValidJson(root_node, file_version)) { error_ss << "isValidJson indicates " << filename << " is not a valid manifest file."; LoaderLogger::LogErrorMessage("", error_ss.str()); return; }// Json的root节点是runtime,子节点是library_path,如果有一个为空,则return const Json::Value &runtime_root_node = root_node["runtime"]; // The Runtime manifest file needs the "runtime" root as well as a sub-node for "library_path". If any of those aren't there, // fail. if (runtime_root_node.isNull() || runtime_root_node["library_path"].isNull() || !runtime_root_node["library_path"].isString()) { error_ss << filename << " is missing required fields. Verify all proper fields exist."; LoaderLogger::LogErrorMessage("", error_ss.str()); return; }//将子节点library_path的转化为string类型 std::string lib_path = runtime_root_node["library_path"].asString(); // If the library_path variable has no directory symbol, it's just a file name and should be accessible on the // global library path. if (lib_path.find('\\') != std::string::npos || lib_path.find('/') != std::string::npos) { // If the library_path is an absolute path, just use that if it exists//如果lib_path是绝对路径,如果存在则直接使用, 我们上面存的是绝对路径 if (FileSysUtilsIsAbsolutePath(lib_path)) { if (!FileSysUtilsPathExists(lib_path)) { error_ss << filename << " library " << lib_path << " does not appear to exist"; LoaderLogger::LogErrorMessage("", error_ss.str()); return; } } else { ... } } //add manifest files // Add this runtime manifest file manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path)); // Add any extensions to it after the fact. // Handle any renamed functions manifest_files.back()->ParseCommon(runtime_root_node);}

13.CreateIfValid-标志4

manifest_file.cppvoid RuntimeManifestFile::CreateIfValid(std::string const &filename , std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) { std::ifstream json_stream(filename, std::ifstream::in); LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::CreateIfValid - attempting to load " + filename); std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid "); if (!json_stream.is_open()) { error_ss << "failed to open " << filename << ". Does it exist?"; LoaderLogger::LogErrorMessage("", error_ss.str()); return; } Json::CharReaderBuilder builder; std::string errors; Json::Value root_node = Json::nullValue;...//会调用到12步 CreateIfValid(root_node, filename, manifest_files);}

14. TryLoadingSingleRuntime--加载runtime-标志5

runtime_interface.cppXrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command, std::unique_ptr<RuntimeManifestFile>& manifest_file) { LoaderPlatformLibraryHandle runtime_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());//dlopen runtime so...#ifdef XR_KHR_LOADER_INIT_SUPPORT if (!LoaderInitData::instance().initialized()) { LoaderLogger::LogErrorMessage(openxr_command, "RuntimeInterface::LoadRuntime skipping manifest file " +manifest_file->Filename() +" because xrInitializeLoaderKHR was not yet called.");... // If we have xrInitializeLoaderKHR exposed as an export, forward call to it. const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR"); auto initLoader = reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));//dlsym if (initLoader != nullptr) {//initLoader为空... } }...// Get and settle on an runtime interface version (using any provided name if required).std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderRuntimeInterface");auto negotiate = reinterpret_cast<PFN_xrNegotiateLoaderRuntimeInterface>(LoaderPlatformLibraryGetProcAddr(runtime_library, function_name));//dlsym...// Skip calling the negotiate function and fail if the function pointer// could not get loadedXrResult res = XR_ERROR_RUNTIME_FAILURE;if (nullptr != negotiate) { //执行runtime中的xrNegotiateLoaderRuntimeInterface方法 //会对runtime_info结构体中的子变量getInstanceProcAddr赋值,赋值成了runtime中的方法,方法名为oxr_xrGetInstanceProcAddr res = negotiate(&loader_info, &runtime_info);}...#ifdef XR_KHR_LOADER_INIT_SUPPORT if (XR_SUCCEEDED(res) && !forwardedInitLoader) { // Forward initialize loader call, where possible and if we did not do so before. PFN_xrVoidFunction initializeVoid = nullptr; PFN_xrInitializeLoaderKHR initialize = nullptr; // Now we may try asking xrGetInstanceProcAddr //执行runtime中的oxr_xrGetInstanceProcAddr方法,会将xrInitializeLoaderKHR方法指针赋值为runtime中的oxr_xrInitializeLoaderKHR if (XR_SUCCEEDED(runtime_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) {// if (initializeVoid == nullptr) { LoaderLogger::LogErrorMessage(openxr_command,"RuntimeInterface::LoadRuntime got success from xrGetInstanceProcAddr ""for xrInitializeLoaderKHR, but output a null pointer."); res = XR_ERROR_RUNTIME_FAILURE; } else {//将initializeVoid函数指针赋值给initialize  initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid); } } if (initialize != nullptr) { // we found the entry point one way or another. LoaderLogger::LogInfoMessage(openxr_command,"RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after calling xrNegotiateLoaderRuntimeInterface.");//执行runtime中的oxr_xrInitializeLoaderKHR方法 res = initialize(LoaderInitData::instance().getParam()); if (!XR_SUCCEEDED(res)) {LoaderLogger::LogErrorMessage(openxr_command,"RuntimeInterface::LoadRuntime forwarded call to xrInitializeLoaderKHR failed."); } } }...// Use this runtime//使用这个runtime GetInstance().reset(new RuntimeInterface(runtime_library, runtime_info.getInstanceProcAddr));...}
android上openxr sdk查找并加载runtime流程-爱代码爱编程 (2024)

References

Top Articles
Keto Pudding (3 ingredients!) - Healthy Recipes Blog
80+ Best Ninja Creami Recipes You Need to Try
LOST JEEPS • View forum
Spectrum Store Appointment
Your Blog - Sheri Blonde
Fantasy football rankings 2024: Sleepers, breakouts, busts from model that called Deebo Samuel's hard NFL year
Things to do in Wichita Falls this weekend Sept. 12-15
Las Mejores Tiendas Online en Estados Unidos - Aerobox Argentina
Ttw Cut Content
1800Comcast
Who should be in the Country Music Hall of Fame (but isn't yet)? Our picks
Frontier Channel Lineup Dallas
Craigslist Hoosick Falls
Soul Attraction Rs3
Papa's Games Unblocked Games
2Lookmovie
Kitchen Song Singer Violet Crossword
Dawat Restaurant Novi
Ashley Kolfa*ge Leaked
Weather Underground Shaver Lake
Live2.Dentrixascend.com
Class B Permit Jobs
My Eschedule Greatpeople Me
25Cc To Tbsp
REGULAMENTUL CAMPANIEI "Extra Smart Week" valabil in perioada 12-18 septembrie 2024
OC IDEAS TO DRAW [80+ IDEAS!] ✍🏼 | Spin the Wheel - Random Picker
Heyimbee Forum
Frequently Asked Questions | Google Fiber
By Association Only Watsonville
Calamity Shadow Fish
Noel Berry's Biography: Age, Height, Boyfriend, Family, Net Worth
Sarah Colman-Livengood Park Raytown Photos
Ltlv Las Vegas
Katie Sigmond - Net Worth 2022, Age, Height, Bio, Family, Career
Devil May Cry 3: Dante's Awakening walkthrough/M16
Xdefiant turn off crossplay ps5 cмотреть на RuClips.ru
Strange World Showtimes Near Amc Hoffman Center 22
Thomas E Schneider Jeopardy
Mellow Mushroom Nutrition Facts: What to Order & Avoid
"Rainbow Family" will im Harz bleiben: Hippie-Camp bis Anfang September geplant
Sveta Håkansson
Hobby Lobby Locations Near Me
Chets Rental Chesterfield
The Top 6 Most Expensive Hermès Birkin Bags
Pre-Order Apple Watch Series 10 – Best Prices in Dubai, UAE
Ups First And Nees
Linkbuilding Specialist Amsterdam
The Starling Girl Showtimes Near Alamo Drafthouse Brooklyn
The 7 best games similar to Among Us for Android - Sbenny’s Blog
The Ultimate Guide To Lovenexy: Exploring Intimacy And Passion
Potion To Reset Attributes Conan
Deciphering The &quot;sydneylint Leaked&quot; Conundrum
Latest Posts
Article information

Author: Terence Hammes MD

Last Updated:

Views: 5430

Rating: 4.9 / 5 (69 voted)

Reviews: 92% of readers found this page helpful

Author information

Name: Terence Hammes MD

Birthday: 1992-04-11

Address: Suite 408 9446 Mercy Mews, West Roxie, CT 04904

Phone: +50312511349175

Job: Product Consulting Liaison

Hobby: Jogging, Motor sports, Nordic skating, Jigsaw puzzles, Bird watching, Nordic skating, Sculpting

Introduction: My name is Terence Hammes MD, I am a inexpensive, energetic, jolly, faithful, cheerful, proud, rich person who loves writing and wants to share my knowledge and understanding with you.