From 4bf4154cad127560ffe831e7b2cd1a0b960d0f03 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Tue, 22 Nov 2016 09:05:39 -0500 Subject: [PATCH] Fix superflous shader setting updates (#4800) This improves rendering performance by ~40% --- src/game.cpp | 184 ++++++++++++++++++++++++++++++++++--------------- src/shader.cpp | 129 +++++++++++++++++----------------- src/shader.h | 63 ++++++++++++++--- 3 files changed, 249 insertions(+), 127 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index e0ffa1be5..16287fe0d 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -888,40 +888,73 @@ public: } }; + +// before 1.8 there isn't a "integer interface", only float +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) +typedef f32 SamplerLayer_t; +#else +typedef s32 SamplerLayer_t; +#endif + + class GameGlobalShaderConstantSetter : public IShaderConstantSetter { Sky *m_sky; bool *m_force_fog_off; f32 *m_fog_range; + bool m_fog_enabled; + CachedPixelShaderSetting m_sky_bg_color; + CachedPixelShaderSetting m_fog_distance; + CachedVertexShaderSetting m_animation_timer_vertex; + CachedPixelShaderSetting m_animation_timer_pixel; + CachedPixelShaderSetting m_day_night_ratio; + CachedPixelShaderSetting m_eye_position_pixel; + CachedVertexShaderSetting m_eye_position_vertex; + CachedPixelShaderSetting m_minimap_yaw; + CachedPixelShaderSetting m_base_texture; + CachedPixelShaderSetting m_normal_texture; + CachedPixelShaderSetting m_texture_flags; Client *m_client; - bool m_fogEnabled; public: void onSettingsChange(const std::string &name) { if (name == "enable_fog") - m_fogEnabled = g_settings->getBool("enable_fog"); + m_fog_enabled = g_settings->getBool("enable_fog"); } - static void SettingsCallback(const std::string &name, void *userdata) + static void settingsCallback(const std::string &name, void *userdata) { reinterpret_cast(userdata)->onSettingsChange(name); } + void setSky(Sky *sky) { m_sky = sky; } + GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off, f32 *fog_range, Client *client) : m_sky(sky), m_force_fog_off(force_fog_off), m_fog_range(fog_range), + m_sky_bg_color("skyBgColor"), + m_fog_distance("fogDistance"), + m_animation_timer_vertex("animationTimer"), + m_animation_timer_pixel("animationTimer"), + m_day_night_ratio("dayNightRatio"), + m_eye_position_pixel("eyePosition"), + m_eye_position_vertex("eyePosition"), + m_minimap_yaw("yawVec"), + m_base_texture("baseTexture"), + m_normal_texture("normalTexture"), + m_texture_flags("textureFlags"), m_client(client) { - g_settings->registerChangedCallback("enable_fog", SettingsCallback, this); - m_fogEnabled = g_settings->getBool("enable_fog"); + g_settings->registerChangedCallback("enable_fog", settingsCallback, this); + m_fog_enabled = g_settings->getBool("enable_fog"); } ~GameGlobalShaderConstantSetter() { - g_settings->deregisterChangedCallback("enable_fog", SettingsCallback, this); + g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this); } virtual void onSetConstants(video::IMaterialRendererServices *services, @@ -939,54 +972,92 @@ public: bgcolorf.b, bgcolorf.a, }; - services->setPixelShaderConstant("skyBgColor", bgcolorfa, 4); + m_sky_bg_color.set(bgcolorfa, services); // Fog distance float fog_distance = 10000 * BS; - if (m_fogEnabled && !*m_force_fog_off) + if (m_fog_enabled && !*m_force_fog_off) fog_distance = *m_fog_range; - services->setPixelShaderConstant("fogDistance", &fog_distance, 1); + m_fog_distance.set(&fog_distance, services); - // Day-night ratio - u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); - float daynight_ratio_f = (float)daynight_ratio / 1000.0; - services->setPixelShaderConstant("dayNightRatio", &daynight_ratio_f, 1); + float daynight_ratio = (float)m_client->getEnv().getDayNightRatio() / 1000.f; + m_day_night_ratio.set(&daynight_ratio, services); u32 animation_timer = porting::getTimeMs() % 100000; - float animation_timer_f = (float)animation_timer / 100000.0; - services->setPixelShaderConstant("animationTimer", &animation_timer_f, 1); - services->setVertexShaderConstant("animationTimer", &animation_timer_f, 1); + float animation_timer_f = (float)animation_timer / 100000.f; + m_animation_timer_vertex.set(&animation_timer_f, services); + m_animation_timer_pixel.set(&animation_timer_f, services); - LocalPlayer *player = m_client->getEnv().getLocalPlayer(); - v3f eye_position = player->getEyePosition(); - services->setPixelShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3); - services->setVertexShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3); - - v3f minimap_yaw_vec = m_client->getMapper()->getYawVec(); - services->setPixelShaderConstant("yawVec", (irr::f32 *)&minimap_yaw_vec, 3); + float eye_position_array[3]; + v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition(); +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + eye_position_array[0] = epos.X; + eye_position_array[1] = epos.Y; + eye_position_array[2] = epos.Z; +#else + epos.getAs3Values(eye_position_array); +#endif + m_eye_position_pixel.set(eye_position_array, services); + m_eye_position_vertex.set(eye_position_array, services); - // Uniform sampler layers - // before 1.8 there isn't a "integer interface", only float + float minimap_yaw_array[3]; + v3f minimap_yaw = m_client->getMapper()->getYawVec(); #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) - f32 layer0 = 0; - f32 layer1 = 1; - f32 layer2 = 2; - services->setPixelShaderConstant("baseTexture" , (irr::f32 *)&layer0, 1); - services->setPixelShaderConstant("normalTexture" , (irr::f32 *)&layer1, 1); - services->setPixelShaderConstant("textureFlags" , (irr::f32 *)&layer2, 1); + minimap_yaw_array[0] = minimap_yaw.X; + minimap_yaw_array[1] = minimap_yaw.Y; + minimap_yaw_array[2] = minimap_yaw.Z; #else - s32 layer0 = 0; - s32 layer1 = 1; - s32 layer2 = 2; - services->setPixelShaderConstant("baseTexture" , (irr::s32 *)&layer0, 1); - services->setPixelShaderConstant("normalTexture" , (irr::s32 *)&layer1, 1); - services->setPixelShaderConstant("textureFlags" , (irr::s32 *)&layer2, 1); + minimap_yaw.getAs3Values(minimap_yaw_array); #endif + m_minimap_yaw.set(minimap_yaw_array, services); + + SamplerLayer_t base_tex = 0, + normal_tex = 1, + flags_tex = 2; + m_base_texture.set(&base_tex, services); + m_normal_texture.set(&normal_tex, services); + m_texture_flags.set(&flags_tex, services); } }; + +class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory +{ + Sky *m_sky; + bool *m_force_fog_off; + f32 *m_fog_range; + Client *m_client; + std::vector created_nosky; +public: + GameGlobalShaderConstantSetterFactory(bool *force_fog_off, + f32 *fog_range, Client *client) : + m_sky(NULL), + m_force_fog_off(force_fog_off), + m_fog_range(fog_range), + m_client(client) + {} + + void setSky(Sky *sky) { + m_sky = sky; + for (size_t i = 0; i < created_nosky.size(); ++i) { + created_nosky[i]->setSky(m_sky); + } + created_nosky.clear(); + } + + virtual IShaderConstantSetter* create() + { + GameGlobalShaderConstantSetter *scs = new GameGlobalShaderConstantSetter( + m_sky, m_force_fog_off, m_fog_range, m_client); + if (!m_sky) + created_nosky.push_back(scs); + return scs; + } +}; + + bool nodePlacementPrediction(Client &client, const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos) { @@ -1695,6 +1766,9 @@ private: Hud *hud; Mapper *mapper; + GameRunData runData; + VolatileRunFlags flags; + /* 'cache' This class does take ownership/responsibily for cleaning up etc of any of these items (e.g. device) @@ -1886,6 +1960,18 @@ bool Game::startup(bool *kill, smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true); + memset(&runData, 0, sizeof(runData)); + runData.time_from_last_punch = 10.0; + runData.profiler_max_page = 3; + runData.update_wielded_item_trigger = true; + + memset(&flags, 0, sizeof(flags)); + flags.show_chat = true; + flags.show_hud = true; + flags.show_debug = g_settings->getBool("show_debug"); + flags.invert_mouse = g_settings->getBool("invert_mouse"); + flags.first_loop_after_window_activation = true; + if (!init(map_dir, address, port, gamespec)) return false; @@ -1902,34 +1988,15 @@ void Game::run() RunStats stats = { 0 }; CameraOrientation cam_view_target = { 0 }; CameraOrientation cam_view = { 0 }; - GameRunData runData = { 0 }; FpsControl draw_times = { 0 }; - VolatileRunFlags flags = { 0 }; f32 dtime; // in seconds - runData.time_from_last_punch = 10.0; - runData.profiler_max_page = 3; - runData.update_wielded_item_trigger = true; - - flags.show_chat = true; - flags.show_hud = true; - flags.show_minimap = g_settings->getBool("enable_minimap"); - flags.show_debug = g_settings->getBool("show_debug"); - flags.invert_mouse = g_settings->getBool("invert_mouse"); - flags.first_loop_after_window_activation = true; - /* Clear the profiler */ Profiler::GraphValues dummyvalues; g_profiler->graphGet(dummyvalues); draw_times.last_time = device->getTimer()->getTime(); - shader_src->addGlobalConstantSetter(new GameGlobalShaderConstantSetter( - sky, - &flags.force_fog_off, - &runData.fog_range, - client)); - set_light_table(g_settings->getFloat("display_gamma")); #ifdef __ANDROID__ @@ -2169,6 +2236,10 @@ bool Game::createClient(const std::string &playername, return false; } + GameGlobalShaderConstantSetterFactory *scsf = new GameGlobalShaderConstantSetterFactory( + &flags.force_fog_off, &runData.fog_range, client); + shader_src->addShaderConstantSetterFactory(scsf); + // Update cached textures, meshes and materials client->afterContentReceived(device); @@ -2193,6 +2264,7 @@ bool Game::createClient(const std::string &playername, /* Skybox */ sky = new Sky(smgr->getRootSceneNode(), smgr, -1, texture_src); + scsf->setSky(sky); skybox = NULL; // This is used/set later on in the main run loop local_inventory = new Inventory(itemdef_manager); diff --git a/src/shader.cpp b/src/shader.cpp index b0131c952..170b0354d 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -167,29 +167,27 @@ private: } }; + /* ShaderCallback: Sets constants that can be used in shaders */ -class IShaderConstantSetterRegistry -{ -public: - virtual ~IShaderConstantSetterRegistry(){}; - virtual void onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel, const std::string &name) = 0; -}; - class ShaderCallback : public video::IShaderConstantSetCallBack { - IShaderConstantSetterRegistry *m_scsr; - std::string m_name; + std::vector m_setters; public: - ShaderCallback(IShaderConstantSetterRegistry *scsr, const std::string &name): - m_scsr(scsr), - m_name(name) - {} - ~ShaderCallback() {} + ShaderCallback(const std::vector &factories) + { + for (u32 i = 0; i < factories.size(); ++i) + m_setters.push_back(factories[i]->create()); + } + + ~ShaderCallback() + { + for (u32 i = 0; i < m_setters.size(); ++i) + delete m_setters[i]; + } virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) { @@ -198,18 +196,25 @@ public: bool is_highlevel = userData; - m_scsr->onSetConstants(services, is_highlevel, m_name); + for (u32 i = 0; i < m_setters.size(); ++i) + m_setters[i]->onSetConstants(services, is_highlevel); } }; + /* MainShaderConstantSetter: Set basic constants required for almost everything */ class MainShaderConstantSetter : public IShaderConstantSetter { + CachedVertexShaderSetting m_world_view_proj; + CachedVertexShaderSetting m_world; + public: - MainShaderConstantSetter(IrrlichtDevice *device) + MainShaderConstantSetter() : + m_world_view_proj("mWorldViewProj"), + m_world("mWorld") {} ~MainShaderConstantSetter() {} @@ -219,31 +224,40 @@ public: video::IVideoDriver *driver = services->getVideoDriver(); sanity_check(driver); - // set clip matrix + // Set clip matrix core::matrix4 worldViewProj; worldViewProj = driver->getTransform(video::ETS_PROJECTION); worldViewProj *= driver->getTransform(video::ETS_VIEW); worldViewProj *= driver->getTransform(video::ETS_WORLD); - if(is_highlevel) - services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16); + if (is_highlevel) + m_world_view_proj.set(*reinterpret_cast(worldViewProj.pointer()), services); else - services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4); + services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4); - // set world matrix + // Set world matrix core::matrix4 world = driver->getTransform(video::ETS_WORLD); - if(is_highlevel) - services->setVertexShaderConstant("mWorld", world.pointer(), 16); + if (is_highlevel) + m_world.set(*reinterpret_cast(world.pointer()), services); else - services->setVertexShaderConstant(world.pointer(), 8, 4); + services->setVertexShaderConstant(world.pointer(), 4, 4); } }; + +class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory +{ +public: + virtual IShaderConstantSetter* create() + { return new MainShaderConstantSetter(); } +}; + + /* ShaderSource */ -class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry +class ShaderSource : public IWritableShaderSource { public: ShaderSource(IrrlichtDevice *device); @@ -286,22 +300,17 @@ public: // Shall be called from the main thread. void rebuildShaders(); - void addGlobalConstantSetter(IShaderConstantSetter *setter) + void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter) { - m_global_setters.push_back(setter); + m_setter_factories.push_back(setter); } - void onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel, const std::string &name); - private: // The id of the thread that is allowed to use irrlicht directly threadid_t m_main_thread; // The irrlicht device IrrlichtDevice *m_device; - // The set-constants callback - ShaderCallback *m_shader_callback; // Cache of source shaders // This should be only accessed from the main thread @@ -316,9 +325,11 @@ private: // Queued shader fetches (to be processed by the main thread) RequestQueue m_get_shader_queue; - // Global constant setters - // TODO: Delete these in the destructor - std::vector m_global_setters; + // Global constant setter factories + std::vector m_setter_factories; + + // Shader callbacks + std::vector m_callbacks; }; IWritableShaderSource* createShaderSource(IrrlichtDevice *device) @@ -331,8 +342,8 @@ IWritableShaderSource* createShaderSource(IrrlichtDevice *device) */ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype, - IrrlichtDevice *device, - video::IShaderConstantSetCallBack *callback, + IrrlichtDevice *device, std::vector &callbacks, + const std::vector &setter_factories, SourceShaderCache *sourcecache); /* @@ -348,28 +359,24 @@ ShaderSource::ShaderSource(IrrlichtDevice *device): { assert(m_device); // Pre-condition - m_shader_callback = new ShaderCallback(this, "default"); - m_main_thread = thr_get_current_thread_id(); // Add a dummy ShaderInfo as the first index, named "" m_shaderinfo_cache.push_back(ShaderInfo()); // Add main global constant setter - addGlobalConstantSetter(new MainShaderConstantSetter(device)); + addShaderConstantSetterFactory(new MainShaderConstantSetterFactory()); } ShaderSource::~ShaderSource() { - for (std::vector::iterator iter = m_global_setters.begin(); - iter != m_global_setters.end(); ++iter) { + for (std::vector::iterator iter = m_callbacks.begin(); + iter != m_callbacks.end(); ++iter) { delete *iter; } - m_global_setters.clear(); - - if (m_shader_callback) { - m_shader_callback->drop(); - m_shader_callback = NULL; + for (std::vector::iterator iter = m_setter_factories.begin(); + iter != m_setter_factories.end(); ++iter) { + delete *iter; } } @@ -445,8 +452,8 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name, return 0; } - ShaderInfo info = generate_shader(name, material_type, drawtype, m_device, - m_shader_callback, &m_sourcecache); + ShaderInfo info = generate_shader(name, material_type, drawtype, + m_device, m_callbacks, m_setter_factories, &m_sourcecache); /* Add shader to caches (add dummy shaders too) @@ -511,22 +518,16 @@ void ShaderSource::rebuildShaders() ShaderInfo *info = &m_shaderinfo_cache[i]; if(info->name != ""){ *info = generate_shader(info->name, info->material_type, - info->drawtype, m_device, m_shader_callback, &m_sourcecache); + info->drawtype, m_device, m_callbacks, + m_setter_factories, &m_sourcecache); } } } -void ShaderSource::onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel, const std::string &name) -{ - for(u32 i=0; ionSetConstants(services, is_highlevel); - } -} ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype, - IrrlichtDevice *device, video::IShaderConstantSetCallBack *callback, + IrrlichtDevice *device, std::vector &callbacks, + const std::vector &setter_factories, SourceShaderCache *sourcecache) { ShaderInfo shaderinfo; @@ -766,6 +767,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype, geometry_program = shaders_header + geometry_program; geometry_program_ptr = geometry_program.c_str(); } + ShaderCallback *cb = new ShaderCallback(setter_factories); s32 shadermat = -1; if(is_highlevel){ infostream<<"Compiling high level shaders for "<addShaderMaterial( vertex_program_ptr, // Vertex shader program pixel_program_ptr, // Pixel shader program - callback, // Set-constant callback + cb, // Set-constant callback shaderinfo.base_material, // Base material 0 // Userdata passed to callback ); @@ -814,9 +817,11 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype, < #include "irrlichttypes_extrabloated.h" #include "threads.h" #include @@ -43,8 +44,7 @@ class IGameDef; std::string getShaderPath(const std::string &name_of_shader, const std::string &filename); -struct ShaderInfo -{ +struct ShaderInfo { std::string name; video::E_MATERIAL_TYPE base_material; video::E_MATERIAL_TYPE material; @@ -66,20 +66,66 @@ namespace irr { namespace video { class IMaterialRendererServices; } } -class IShaderConstantSetter -{ + +class IShaderConstantSetter { public: virtual ~IShaderConstantSetter(){}; virtual void onSetConstants(video::IMaterialRendererServices *services, bool is_highlevel) = 0; }; + +class IShaderConstantSetterFactory { +public: + virtual ~IShaderConstantSetterFactory() {}; + virtual IShaderConstantSetter* create() = 0; +}; + + +template +class CachedShaderSetting { + const char *m_name; + T m_sent[count]; + bool has_been_set; + bool is_pixel; +protected: + CachedShaderSetting(const char *name, bool is_pixel) : + m_name(name), has_been_set(false), is_pixel(is_pixel) + {} +public: + void set(const T value[count], video::IMaterialRendererServices *services) + { + if (has_been_set && std::equal(m_sent, m_sent + count, value)) + return; + if (is_pixel) + services->setPixelShaderConstant(m_name, value, count); + else + services->setVertexShaderConstant(m_name, value, count); + std::copy(value, value + count, m_sent); + has_been_set = true; + } +}; + +template +class CachedPixelShaderSetting : public CachedShaderSetting { +public: + CachedPixelShaderSetting(const char *name) : + CachedShaderSetting(name, true){} +}; + +template +class CachedVertexShaderSetting : public CachedShaderSetting { +public: + CachedVertexShaderSetting(const char *name) : + CachedShaderSetting(name, false){} +}; + + /* ShaderSource creates and caches shaders. */ -class IShaderSource -{ +class IShaderSource { public: IShaderSource(){} virtual ~IShaderSource(){} @@ -90,8 +136,7 @@ public: const u8 material_type, const u8 drawtype){return 0;} }; -class IWritableShaderSource : public IShaderSource -{ +class IWritableShaderSource : public IShaderSource { public: IWritableShaderSource(){} virtual ~IWritableShaderSource(){} @@ -105,7 +150,7 @@ public: virtual void insertSourceShader(const std::string &name_of_shader, const std::string &filename, const std::string &program)=0; virtual void rebuildShaders()=0; - virtual void addGlobalConstantSetter(IShaderConstantSetter *setter)=0; + virtual void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter) = 0; }; IWritableShaderSource* createShaderSource(IrrlichtDevice *device); -- 2.25.1