Forráskód Böngészése

More work on documentation

BearishSun 8 éve
szülő
commit
193af34409

+ 1 - 1
Documentation/Manuals/Native/manuals.md

@@ -95,6 +95,7 @@ A set of manuals covering advanced functionality intented for those wanting to e
  - [Any](@ref any) 
  - [Any](@ref any) 
  - [Unit tests](@ref unitTests)
  - [Unit tests](@ref unitTests)
 - [Threading](@ref threading)
 - [Threading](@ref threading)
+- [Plugins](@ref plugins)
 - **Renderer**
 - **Renderer**
  - [Renderer extensions](@ref rendererExtensions)
  - [Renderer extensions](@ref rendererExtensions)
  - [Creating a renderer manually](@ref customRenderer)
  - [Creating a renderer manually](@ref customRenderer)
@@ -108,7 +109,6 @@ Name                                      | Description
 [BSLFX](@ref bsl)    	  		  		  | Provides a reference for the Banshee Shading Language syntax.
 [BSLFX](@ref bsl)    	  		  		  | Provides a reference for the Banshee Shading Language syntax.
 [Custom GUI elements](@ref customGUI)     | Shows you how to create custom GUI elements, manually render text or modify GUI system in a general way.
 [Custom GUI elements](@ref customGUI)     | Shows you how to create custom GUI elements, manually render text or modify GUI system in a general way.
 [Custom importers](@ref customImporters)  | Shows you how to create importers that handle conversion of third party resources into engine ready formats.
 [Custom importers](@ref customImporters)  | Shows you how to create importers that handle conversion of third party resources into engine ready formats.
-[Custom plugins](@ref customPlugins)      | Shows you how to create custom plugins that can be loaded by Banshee.
 [Quick reference](@ref quickref)          | Provides a few bits of commonly required information, that might be hard to remember otherwise.
 [Quick reference](@ref quickref)          | Provides a few bits of commonly required information, that might be hard to remember otherwise.
 [Porting](@ref porting)                   | Information about how to go on about porting Banshee to a different operating system.
 [Porting](@ref porting)                   | Information about how to go on about porting Banshee to a different operating system.
 [Code style](@ref style)                  | Information about code style used when writing Banshee.
 [Code style](@ref style)                  | Information about code style used when writing Banshee.

+ 105 - 33
Documentation/Manuals/Native/plugins.md

@@ -1,32 +1,103 @@
-Creating custom plugins						{#customPlugins}
+Plugins						{#plugins}
 ===============
 ===============
 [TOC]
 [TOC]
 
 
 Many systems in Banshee are implemented through plugins, libraries that are separate from the core of the engine and can be dynamically loaded or unloaded. If possible, it is the prefered way of extending the engine.
 Many systems in Banshee are implemented through plugins, libraries that are separate from the core of the engine and can be dynamically loaded or unloaded. If possible, it is the prefered way of extending the engine.
 
 
-The default Banshee @ref bs::CoreApplication "Application" supports plugins for the following systems:
- - Rendering API - Wrappers for render APIs like DirectX or OpenGL. See the manual about working with [low level render API] (@ref renderAPI) for more information.
- - Renderer - Renderer that determines how is the scene displayed (lighting, shadows, post-processing, etc.). See the manual about implementing [custom renderers](@ref renderer).
+Banshee supports plugins for the following systems:
+ - Audio - Systems for providing audio playback.
+ - Importers - Importers that handle conversion of some third party resource format into an engine-ready format.
  - Input - Reports input events (mouse, keyboard, gamepad, etc.)
  - Input - Reports input events (mouse, keyboard, gamepad, etc.)
- - Physics - Runs the physics simulation (like NVIDIA PhysX)
- - Importers - Importers that handle conversion of some third party resource format into an engine-ready format. See the manual about implementing [custom importers](@ref customImporters).
+ - Physics - Runs the physics simulation.
+ - Renderer - Determines how is the scene displayed (lighting, shadows, post-processing, etc.). 
+ - Rendering API - Wrappers for render APIs like DirectX, OpenGL or Vulkan.
  
  
-The supported plugins will be automatically loaded and unloaded by the application as needed, all you need to do is to provide names of their libraries to the @ref bs::START_UP_DESC "START_UP_DESC" used to initialize the application. All plugins should be in the same folder as the main application executable. 
+You can choose which plugins are loaded on **Application** start-up, by filling out the @ref bs::START_UP_DESC "START_UP_DESC" structure.
 
 
-Aside from the supported plugins you can also create fully custom plugins that you load or unload manually.
+~~~~~~~~~~~~~{.cpp}
+START_UP_DESC startUpDesc;
+
+// Required plugins
+startUpDesc.renderAPI = "BansheeD3D11RenderAPI";
+startUpDesc.renderer = "RenderBeast";
+startUpDesc.audio = "BansheeOpenAudio";
+startUpDesc.physics = "BansheePhysX";
+startUpDesc.input = "BansheeOISInput";
+
+// List of importer plugins we plan on using for importing various resources
+startUpDesc.importers.push_back("BansheeFreeImgImporter"); // For importing textures
+startUpDesc.importers.push_back("BansheeFBXImporter"); // For importing meshes
+startUpDesc.importers.push_back("BansheeFontImporter"); // For importing fonts
+startUpDesc.importers.push_back("BansheeSL"); // For importing shaders
 
 
-# Implementing supported plugins {#customPlugins_a}
-All supported plugins implement an informal interface through global "extern" methods. The interface supports three methods:
- - loadPlugin() - Called when the plugin is initially loaded
- - updatePlugin() - Called every frame
- - unloadPlugin() - Called just before the plugin is unloaded
+// ... also set up primary window in startUpDesc ...
+
+Application::startUp(startUpDesc);
+
+// ... create scene, run main loop, shutdown
+~~~~~~~~~~~~~ 
  
  
-You may choose to implement some, or none of these, although usually at least `loadPlugin()` method is needed so the plugin can register itself with the necessary system. A simple implementation might look like so:
+In this manual we'll focus on general functionality common to all plugins, while we'll talk about how to implement plugins for specific systems in later manuals. 
+
+# Generating a CMake project {#plugins_a}
+Plugins are always created as their own projects/libraries. Banshee uses the CMake build system for managing its projects. Therefore the first step you need to take is to create your own CMake project. This involves creating a new folder in the /Source directory (e.g. Source/MyPlugin), with a CMakeLists.txt file inside it. CMakeLists.txt will contain references to needed header & source files, as well as dependencies to any other libraries. 
+ 
+An example CMakeLists.txt might look like so:
+~~~~~~~~~~~~~
+# --Source files--
+set(SOURCE_FILES
+	"Include/MyHeader.h"
+	"Source/MyPlugin.cpp"	
+)
+
+# --Include directories--
+# This plugin depends on BansheeUtility, BansheeCore and BansheeEngine libraries, as well as a third party library named someThirdPartyLib, so we reference the folders where their header files are located. Paths are relative to the plugin folder.
+set(INCLUDE_DIRS
+	"Include" 
+	"../BansheeUtility/Include" 
+	"../BansheeCore/Include"
+	"../BansheeEngine/Include"
+	"../../Dependencies/someThirdPartyLib/include")
+
+include_directories(${INCLUDE_DIRS})	
+	
+# --Target--
+# Registers our plugin a specific name (MyPlugin) and with the relevant source files
+add_library(MyPlugin SHARED ${SOURCE_FILES})
+
+# --Libraries--
+# Registers dependencies on any other libraries
+## Register dependency on an external library: someThirdPartyLib
+add_library_per_config(MyPlugin someThirdPartyLib Release/someThirdPartyLib Debug/someThirdPartyLib)
+
+## Register dependencies on Banshee libraries
+target_link_libraries(MyPlugin PUBLIC BansheeUtility BansheeCore BansheeEngine)
+~~~~~~~~~~~~~
+
+Note that we won't go into details about CMake syntax, it's complex and there are many other guides already written on it.
+
+After creating your project's CMake file, you need to register it with the main CMakeLists.txt present in the /Source directory. Simply append the following line:
+~~~~~~~~~~~~~
+# Provided your plugin is located in Source/MyPlugin folder
+add_subdirectory(MyPlugin)
+~~~~~~~~~~~~~
+
+# Plugin interface {#customPlugins_b}
+If you wish to create a plugin for any of the systems listed above, you will need to implement an informal interface through global "extern" methods. The interface supports three functions:
+ - **loadPlugin()** - Called when the plugin is initially loaded
+ - **updatePlugin()** - Called every frame
+ - **unloadPlugin()** - Called just before the plugin is unloaded
+ 
+You may choose to implement some, or none of these, although usually at least **loadPlugin()** method is needed so the plugin can register itself with the necessary system.
 ~~~~~~~~~~~~~{.cpp}
 ~~~~~~~~~~~~~{.cpp}
+class MyPlugin : Module<MyPlugin>
+{
+};
 
 
 extern "C" BS_MYPLUGIN_EXPORT void* loadPlugin()
 extern "C" BS_MYPLUGIN_EXPORT void* loadPlugin()
 {
 {
-	// Do something on load
+	MyPlugin::startUp();
+
 	return nullptr; // Not used
 	return nullptr; // Not used
 }
 }
 
 
@@ -37,38 +108,39 @@ extern "C" BS_MYPLUGIN_EXPORT void updatePlugin()
 
 
 extern "C" BS_MYPLUGIN_EXPORT void unloadPlugin()
 extern "C" BS_MYPLUGIN_EXPORT void unloadPlugin()
 {
 {
-	// Do something before unload
+	MyPlugin::shutDown();
 }
 }
 
 
+// BS_MYPLUGIN_EXPORT is a macro for a compiler-specific export attribute
+// (e.g. __declspec(dllexport) for Visual Studio (MSVC))
 ~~~~~~~~~~~~~
 ~~~~~~~~~~~~~
 
 
-It's important that all instances of types (classes/structs) from the plugin are deleted before plugin unload happens. If this doesn't happen, and an object instance is deleted after the plugin has been unloaded you will end up with a corrupt virtual function table and a crash. Normally this is handled for you, but it's good to keep in mind depending on what your plugin is doing.
+After you have your plugin interface, all you need to do is to pass the name of your plugin (as defined in CMake) to one of the entries in **START_UP_DESC** for it to be loaded.
 
 
-The exact implementations of these methods differ depending for which system are you implementing a plugin for, but we won't go into details for individual systems here. In most cases it just involves registering the plugin instance using some global manager. For example check "BsSLPlugin.cpp" in the @ref BansheeSL plugin, which registers a new importer that supports ".bsl" files.
+> It's important that all objects created by the plugin are deleted before plugin unload happens. If this doesn't happen, and an object instance is deleted after the plugin has been unloaded you will end up with a corrupt virtual function table and a crash. 
 
 
-# Custom plugins {#customPlugins_b}
-Custom plugins can do whatever you wish, engine has no expectations from them so its up to your to load/unload them and to call their methods.
+# Fully custom plugins {#plugins_c}
+You can also create a fully customized plugin that doesn't implement functionality for any existing engine system. The engine has no interface expectations for such plugins, and it's up to you to manually load/unload them, as well as to manually call their functions.
 
 
 To load a custom plugin you can use:
 To load a custom plugin you can use:
- - @ref bs::CoreApplication::loadPlugin "Application::loadPlugin" - Accepts the name of the plugin library and outputs the library object. Optionally you may also pass a parameter to the `loadPlugin` method, if yours accepts one.
- - @ref bs::CoreApplication::unloadPlugin "Application::unloadPlugin" - Unloads a previously loaded plugin. 
+ - @ref bs::CoreApplication::loadPlugin "Application::loadPlugin()" - Accepts the name of the plugin library and outputs the library object. Optionally you may also pass a parameter to the **loadPlugin** method, if your plugin defines one.
+ - @ref bs::CoreApplication::unloadPlugin "Application::unloadPlugin()" - Unloads a previously loaded plugin. 
 
 
-Both of those methods internally call @ref bs::DynLibManager "DynLibManager". You can use it directly if you do not need the plugin interface (`loadPlugin` and etc.), it has two methods:
- - @ref bs::DynLibManager::load "DynLibManager::load" - Accepts a file name to the library, and returns the @ref bs::DynLib "DynLib" object if the load is successful or null otherwise. 
- - @ref bs::DynLibManager::unload "DynLibManager::unload" - Unloads a previously loaded library.
- 
-Once the library is loaded you can use the @ref bs::DynLib "DynLib" object, and its @ref bs::DynLib::getSymbol "DynLib::getSymbol" method to retrieve a function pointer within the dynamic library, and call into it. For example if we wanted to retrieve a function pointer for the `loadPlugin` method from the previous chapter:
 ~~~~~~~~~~~~~{.cpp}
 ~~~~~~~~~~~~~{.cpp}
-// Load library
-DynLib* myLibrary = DynLibManager::instance().load("myPlugin");
+DynLib* pluginLib;
+gApplication()::loadPlugin("MyPlugin", &pluginLib);
+// Do something
+gApplication()::unloadPlugin(pluginLib);
+~~~~~~~~~~~~~ 
+ 
+Both of those methods internally call **DynLibManager** which we described earlier. In fact you can also use it directly for loading plugins, as an alternative to this approach.
 
 
+Once the library is loaded you can use the @ref bs::DynLib "DynLib" object, and its @ref bs::DynLib::getSymbol "DynLib::getSymbol()" method to retrieve a function pointer within the dynamic library, and call into it. 
+~~~~~~~~~~~~~{.cpp}
 // Retrieve function pointer (symbol)
 // Retrieve function pointer (symbol)
 typedef void* (*LoadPluginFunc)();
 typedef void* (*LoadPluginFunc)();
-LoadPluginFunc loadPluginFunc = (LoadPluginFunc)myLibrary->getSymbol("loadPlugin");
+LoadPluginFunc loadPluginFunc = (LoadPluginFunc)pluginLib->getSymbol("loadPlugin");
 
 
 // Call the function
 // Call the function
 loadPluginFunc();
 loadPluginFunc();
-
-// Assuming we're done, unload the plugin
-DynLibManager::instance().unload(myLibrary);
 ~~~~~~~~~~~~~
 ~~~~~~~~~~~~~

+ 159 - 0
Documentation/Manuals/Native/rendererExtensions.md

@@ -0,0 +1,159 @@
+Renderer extensions								{#rendererExtensions}
+===============
+[TOC]
+
+Renderer is a system that processes all renderable objects in the scene, renders them, applies lighting and shadows, renders overlay elements such as GUI and applies post processing effects. It is the system that determines how your game looks (together with custom materials you might specify). In Banshee the renderer is implemented as a plugin, so you may create your own and fully customize the look of your game. Banshee also comes with a default renderer called "RenderBeast".
+
+In this chapter we'll show how to create extensions to the renderer, which are primarily useful when adding systems that need to perform rendering, but you do not wish to completely replace existing renderer functionality, but rather add to it. Such systems might perform particle effect rendering, GUI overlays, custom 2D rendering and similar.
+
+# Creating extensions {#rendererExt_a}
+
+To create a renderer extensions implement your own class deriving from @ref bs::RendererExtension "RendererExtension". Note it is a core thread class, as all rendering is executed on the core thread.
+
+~~~~~~~~~~~~~{.cpp}
+class MyRendererExtension : public RendererExtension
+{
+public:
+	MyRendererExtension();
+}
+~~~~~~~~~~~~~
+
+Your implementation needs to pass two parameters to the base **RendererExtension** class. The first parameter is of type @ref bs::RenderLocation "RenderLocation" which determines at which point during rendering will your extension be triggered. It can be any of the following values, which are executed in the order specified:
+ - @ref bs::RenderLocation::PreBasePass "RenderLocation::PreBasePass" - Triggered before any scene objects are rendered. The renderer guarantees the render targets used for rendering scene objects will be bound (e.g. GBuffer).
+ - @ref bs::RenderLocation::PostBasePass "RenderLocation::PostBasePass" - Triggered after scene objects are rendered, but before they are lit. The renderer guarantees the render targets used for rendering scene objects will be bound (e.g. GBuffer).
+ - @ref bs::RenderLocation::PostLightPass "RenderLocation::PostLightPass" - Triggered after all scene objects have been rendered and their final information has been written to the final scene color buffer, without any post-processing. The renderer guarantees the final scene color render target will be bound.
+ - @ref bs::RenderLocation::Overlay "RenderLocation::Overlay" - Triggered after all scene objects have been rendered and their final information has been written to the final scene color buffer, with post-processing. The renderer guarantees the final scene color render target will be bound.
+ 
+The second parameter is the priority. It determines in what order will renderer extensions attached to the same render location be executed. Those with higher priority will execute before those with lower priority.
+
+~~~~~~~~~~~~~{.cpp}
+// Renderer extension with necessary constructor arguments
+class MyRendererExtension : public RendererExtension
+{
+public:
+	MyRendererExtension()
+		: RendererExtension(RenderLocation::PostLightPass, 0)
+	{ }
+}
+~~~~~~~~~~~~~
+ 
+Finally the implementation needs to implement the following methods:
+ - @ref bs::RendererExtension::check() "RendererExtension::check()" - Called every frame for every camera in the scene. The methods accepts a camera as a parameter and return true or false whether the extension wishes to render to that camera. Some extensions might render to all cameras, others just to one or a few.
+ - @ref bs::RendererExtension::render() "RendererExtension::render()" - Called every frame for every camera that the **ct::RendererExtension::check()** method returned true for. This is the method where you place the bulk of extension code and perform actual rendering. The rendering is performed using the low level rendering API as described previously. Note that this is the only method in the extension that you should be rendering from.
+ 
+~~~~~~~~~~~~~{.cpp}
+// Renderer extension with check() and render() methods
+class MyRendererExtension : public RendererExtension
+{
+public:
+	MyRendererExtension()
+		: RendererExtension(RenderLocation::PostLightPass, 0)
+	{ }
+	
+	bool check(const Camera& camera) override
+	{
+		// Render on any camera
+		return true;
+	}
+
+	void render(const Camera& camera) override
+	{
+		RenderAPI& rapi = RenderAPI::instance();
+		
+		// bind pipeline state, vertex/index buffers, etc.
+		rapi.drawIndexed(0, numIndices, 0, numVertices);
+	}
+}
+~~~~~~~~~~~~~
+
+> Your extension should never call **RenderAPI::setRenderTarget()**. Render targets are bound and managed exclusively by the renderer itself. The type of render target depends on the render location the extension is attached to. 
+
+# Registering an extension {#rendererExt_b}
+Once extension is implemented you need to register it with the renderer by calling @ref bs::RendererExtension::create<T> "RendererExtension::create<T>()", where the template parameter is the type of your extension. You must also provide initialization data that will be passed to the extension - this can be null.
+
+Note that while extensions are executed on the core thread, they are started from the sim (main) thread.
+
+~~~~~~~~~~~~~{.cpp}
+// Calling from sim thread
+SPtr<ct::MyRendererExtension> rendererExt = RendererExtension::create<ct::MyRendererExtension>(nullptr);
+~~~~~~~~~~~~~
+
+# Initialization {#rendererExt_c}
+When implementing your extension you may optionally override the @ref bs::RendererExtension::initialize() "RendererExtension::initialize()" method. This method will be called on the core thread once the extension is created.
+
+Note that you shouldn't use the constructor for initialization, since the constructor will trigger on the simulation thread, which is not a valid thread for rendering operations.
+
+**RendererExtension::initialize()** additionally also accepts the data passed to the **RendererExtension::create<T>()** method. The data is passed as **Any** type, meaning you can bind whatever you wish to it. Normally it is some kind of a *struct* containing the necessary initialization parameters.
+
+~~~~~~~~~~~~~{.cpp}
+struct MyInitData
+{
+	int a;
+	float b;
+	SPtr<ct::Texture> c;
+}
+
+// Core thread
+class MyRendererExtension : public RendererExtension
+{
+public:
+	// ... other extension code
+	
+	void initialize(const Any& data) override
+	{
+		const MyInitData& initData = any_cast_ref<MyInitData>(data);
+		// initialize whatever is required
+	}
+}
+
+// Sim thread
+HTexture tex = ...;
+
+MyInitData initData;
+initData.a = 5;
+initData.b = 30.0f;
+initData.c = tex->getCore(); // Get version of texture usable on core thread
+
+SPtr<ct::MyRendererExtension> rendererExt = RendererExtension::create<ct::MyRendererExtension>(initData);
+~~~~~~~~~~~~~
+
+# Destruction {#rendererExt_d}
+Similar to how you shouldn't use the constructor for initializing the extension, neither should you use the destructor for destruction. Instead if you need to perform cleanup before the extension is destroyed, override the @ref bs::RendererExtension::destroy() "RendererExtension::destroy()" method.
+
+~~~~~~~~~~~~~{.cpp}
+class MyRendererExtension : public RendererExtension
+{
+public:
+	// ... other extension code
+	
+	void destroy() override
+	{
+		// clean up
+	}
+}
+~~~~~~~~~~~~~
+
+# Communicating with the extension {#rendererExt_e}
+If you need further communication with your extension from the sim thread, you should use the command queue as described in the @ref coreThread manual. If not using the command queue then you must ensure to use some other form of thread primitives to ensure safe communication between the two threads.
+
+~~~~~~~~~~~~~{.cpp}
+class MyRendererExtension : public RendererExtension
+{
+public:
+	void myCustomUpdateMethod(float newValue)
+	{
+		// respond to change in value
+	}
+}
+
+// Sim thread
+ct::MyRendererExtension myExtension = ...;
+float newValue = 15.0f;
+
+auto executeOnCore = [&]()
+{
+	myExtension->myCustomUpdateMethod(newValue);
+};
+
+gCoreThread().queueCommand(executeOnCore);
+~~~~~~~~~~~~~