From d0e746c5d2e2911c83d31d98b46f4535123118bf Mon Sep 17 00:00:00 2001
From: DarkGod <darkgod@net-core.org>
Date: Sat, 24 May 2014 02:13:14 +0200
Subject: [PATCH] Improve shaders performance in some cases

---
 game/engines/default/engine/Particles.lua     |   1 +
 game/engines/default/engine/Shader.lua        |  48 ++++--
 .../tome/data/gfx/shaders/boneshield.lua      |   3 +-
 .../tome/data/talents/corruptions/bone.lua    |  11 +-
 src/shaders.c                                 | 151 +++++++++++++++++-
 src/useshader.h                               |  16 +-
 6 files changed, 213 insertions(+), 17 deletions(-)

diff --git a/game/engines/default/engine/Particles.lua b/game/engines/default/engine/Particles.lua
index e89212a8ef..36a805d3b6 100644
--- a/game/engines/default/engine/Particles.lua
+++ b/game/engines/default/engine/Particles.lua
@@ -41,6 +41,7 @@ function _M:save()
 	return class.save(self, {
 		ps = true,
 		gl_texture = true,
+		_shader = true,
 	})
 end
 
diff --git a/game/engines/default/engine/Shader.lua b/game/engines/default/engine/Shader.lua
index 3d9ff0499a..bfe449b1eb 100644
--- a/game/engines/default/engine/Shader.lua
+++ b/game/engines/default/engine/Shader.lua
@@ -41,7 +41,11 @@ function _M:cleanup()
 	for name, s in pairs(self.progs) do
 		if s.dieat < time then todel[name] = true end
 	end
-	for name, _ in pairs(todel) do self.progs[name] = nil end
+	for name, _ in pairs(todel) do
+		self.progs[name] = nil
+		self.progsreset[name] = nil
+		print("Deleting temp shader", name)
+	end
 end
 
 --- Make a shader
@@ -152,10 +156,13 @@ function _M:loaded()
 	if _M.progsperm[self.totalname] then
 		-- print("[SHADER] using permcached shader "..self.totalname)
 		self.shad = _M.progsperm[self.totalname]
-	elseif _M.progs[self.totalname] and not _M.progsreset[self.totalname] then
+	elseif _M.progs[self.totalname] then
 		-- print("[SHADER] using cached shader "..self.totalname)
 		self.shad = _M.progs[self.totalname].shad
-		_M.progs[self.totalname].dieat = os.time() + 60
+		_M.progs[self.totalname].dieat = os.time() + 60*4
+		if _M.progsreset[self.totalname] then
+			self.shad = self.shad:clone()
+		end
 	else
 		print("[SHADER] Loading from /data/gfx/shaders/"..self.name..".lua")
 		local f, err = loadfile("/data/gfx/shaders/"..self.name..".lua")
@@ -170,18 +177,17 @@ function _M:loaded()
 			if not core.shader.allow(def.require_kind) then return end
 		end
 
-		if def.resetargs then
-			self.totalname = self:makeTotalName(def.resetargs)
-		end
 		print("[SHADER] Loaded shader with totalname", self.totalname)
 
 		if not _M.progs[self.totalname] then
-			_M.progs[self.totalname] = {shad=self:createProgram(def), dieat=def.resetargs and (os.time() + 3) or (os.time() + 60)}
-			_M.progsreset[self.totalname] = def.resetargs
+			_M.progs[self.totalname] = {shad=self:createProgram(def), dieat=(os.time() + 60*4)}
 		else
-			_M.progs[self.totalname].dieat = def.resetargs and (os.time() + 3) or (os.time() + 60)
+			_M.progs[self.totalname].dieat = (os.time() + 60*4)
 		end
 
+		if def.resetargs then
+			_M.progsreset[self.totalname] = def.resetargs
+		end
 
 		self.shad = _M.progs[self.totalname].shad
 		if self.shad then
@@ -194,8 +200,9 @@ function _M:loaded()
 	end
 
 	if self.shad and _M.progsreset[self.totalname] then
+		self.shad:resetClean()
 		for k, v in pairs(_M.progsreset[self.totalname]) do
-			self:setUniform(k, v(self))
+			self:setResetUniform(k, v(self))
 		end
 	end
 end
@@ -221,6 +228,27 @@ function _M:setUniform(k, v)
 	end
 end
 
+function _M:setResetUniform(k, v)
+	if type(v) == "number" then
+		print("[SHADER] setting reset param", k, v)
+		self.shad:resetParamNumber(k, v)
+	elseif type(v) == "table" then
+		if v.texture then
+--			print("[SHADER] setting texture param", k, v.texture)
+			self.shad:resetParamTexture(k, v.texture, v.is3d)
+		elseif #v == 2 then
+--			print("[SHADER] setting vec2 param", k, v[1], v[2])
+			self.shad:resetParamNumber2(k, v[1], v[2])
+		elseif #v == 3 then
+--			print("[SHADER] setting vec3 param", k, v[1], v[2], v[3])
+			self.shad:resetParamNumber3(k, v[1], v[2], v[3])
+		elseif #v == 4 then
+--			print("[SHADER] setting vec4 param", k, v[1], v[2], v[3], v[4])
+			self.shad:resetParamNumber4(k, v[1], v[2], v[3], v[4])
+		end
+	end
+end
+
 ----------------------------------------------------------------------------
 -- Default shaders
 ----------------------------------------------------------------------------
diff --git a/game/modules/tome/data/gfx/shaders/boneshield.lua b/game/modules/tome/data/gfx/shaders/boneshield.lua
index 16ccacd4e3..8193f99de2 100644
--- a/game/modules/tome/data/gfx/shaders/boneshield.lua
+++ b/game/modules/tome/data/gfx/shaders/boneshield.lua
@@ -26,10 +26,9 @@ return {
 		side = noup or 0,
 		verticalIntensityAdjust = 0,
 		scrollingSpeed = scrollingSpeed or 0.004,
-		chargesCount = chargesCount or 6,
 	},
 	resetargs = {
-		unused = function() return rng.range(1, 99999) end,
+		chargesCount = function(self) return self.args.chargesCount or 0 end,
 	},
 	clone = true,
 }
diff --git a/game/modules/tome/data/talents/corruptions/bone.lua b/game/modules/tome/data/talents/corruptions/bone.lua
index 0ee56b08b5..32d425f531 100644
--- a/game/modules/tome/data/talents/corruptions/bone.lua
+++ b/game/modules/tome/data/talents/corruptions/bone.lua
@@ -144,7 +144,8 @@ newTalent{
 				p.nb = p.nb + 1
 				if p.adv_gfx then
 					if p.particles[1] and p.particles[1]._shader and p.particles[1]._shader.shad then
-						p.particles[1]._shader:setUniform("chargesCount", util.bound(p.nb, 0, 10))
+						p.particles[1]._shader.shad:resetClean()
+						p.particles[1]._shader:setResetUniform("chargesCount", util.bound(p.nb, 0, 10))
 						p.particles[1].shader.chargesCount = util.bound(p.nb, 0, 10)
 					end
 				else
@@ -160,7 +161,8 @@ newTalent{
 		p.nb = p.nb - 1
 		if p.adv_gfx then
 			if p.particles[1] and p.particles[1]._shader and p.particles[1]._shader.shad then
-				p.particles[1]._shader:setUniform("chargesCount", util.bound(p.nb, 0, 10))
+				p.particles[1]._shader.shad:resetClean()
+				p.particles[1]._shader:setResetUniform("chargesCount", util.bound(p.nb, 0, 10))
 				p.particles[1].shader.chargesCount = util.bound(p.nb, 0, 10)
 			end
 		else
@@ -177,7 +179,10 @@ newTalent{
 		local adv_gfx = core.shader.allow("adv") and true or false
 		local ps = {}
 		if adv_gfx then
-			ps[1] = self:addParticles(Particles.new("shader_ring_rotating", 1, {toback=true, a=0.5, rotation=0, radius=1.5, img="bone_shield"}, {type="boneshield", chargesCount=util.bound(nb, 0, 10)}))
+			ps[1] = self:addParticles(Particles.new("shader_ring_rotating", 1, {toback=true, a=0.5, rotation=0, radius=1.5, img="bone_shield"}, {type="boneshield"}))
+			ps[1]._shader.shad:resetClean()
+			ps[1]._shader:setResetUniform("chargesCount", util.bound(nb, 0, 10))
+			ps[1].shader.chargesCount = util.bound(nb, 0, 10)
 		else
 			for i = 1, nb do ps[#ps+1] = self:addParticles(Particles.new("bone_shield", 1)) end
 		end
diff --git a/src/shaders.c b/src/shaders.c
index 83a6623e5c..afa73a087e 100644
--- a/src/shaders.c
+++ b/src/shaders.c
@@ -52,6 +52,25 @@ void useShader(shader_type *p, int x, int y, int w, int h, float r, float g, flo
 	c[0] = w;
 	c[1] = h;
 	glUniform2fvARB(p->p_texsize, 1, c);
+
+	shader_reset_uniform *ru = p->reset_uniforms;
+	while (ru) {
+		switch (ru->kind) {
+			case UNIFORM_NUMBER:
+				glUniform1fvARB(ru->p, 1, &ru->data.number);
+				break;
+			case UNIFORM_VEC2:
+				glUniform2fvARB(ru->p, 1, ru->data.vec2);
+				break;
+			case UNIFORM_VEC3:
+				glUniform3fvARB(ru->p, 1, ru->data.vec3);
+				break;
+			case UNIFORM_VEC4:
+				glUniform4fvARB(ru->p, 1, ru->data.vec4);
+				break;
+		}
+		ru = ru->next;
+	}
 }
 
 static GLuint loadShader(const char* code, GLuint type)
@@ -96,6 +115,8 @@ static int program_new(lua_State *L)
 	auxiliar_setclass(L, "gl{program}", -1);
 
 	p->shader = glCreateProgramObjectARB();
+	p->reset_uniforms = NULL;
+	p->clone = FALSE;
 
 	printf("New GL Shader program %d\n", p->shader);
 
@@ -106,12 +127,32 @@ static int program_free(lua_State *L)
 {
 	shader_type *p = (shader_type*)lua_touserdata(L, 1);
 
-	glDeleteObjectARB(p->shader);
+	printf("Deleting shader %d (is clone %d)\n", p->shader, p->clone);
+	if (!p->clone) glDeleteObjectARB(p->shader);
+
+	while (p->reset_uniforms) {
+		shader_reset_uniform *ru = p->reset_uniforms;
+		free(ru);
+		p->reset_uniforms = p->reset_uniforms->next;
+	}
 
 	lua_pushnumber(L, 1);
 	return 1;
 }
 
+static int program_remove_resets(lua_State *L)
+{
+	shader_type *p = (shader_type*)lua_touserdata(L, 1);
+
+	while (p->reset_uniforms) {
+		shader_reset_uniform *ru = p->reset_uniforms;
+		free(ru);
+		p->reset_uniforms = p->reset_uniforms->next;
+	}
+
+	return 0;
+}
+
 static int program_attach(lua_State *L)
 {
 	shader_type *p = (shader_type*)lua_touserdata(L, 1);
@@ -132,6 +173,46 @@ static int program_detach(lua_State *L)
 	return 0;
 }
 
+static int program_clone(lua_State *L)
+{
+	shader_type *p = (shader_type*)lua_touserdata(L, 1);
+
+	shader_type *np = (shader_type*)lua_newuserdata(L, sizeof(shader_type)); // 2
+	auxiliar_setclass(L, "gl{program}", -1);
+
+	np->clone = TRUE;
+	np->shader = p->shader;
+	np->p_tick = p->p_tick;
+	np->p_color = p->p_color;
+	np->p_mapcoord = p->p_mapcoord;
+	np->p_texsize = p->p_texsize;
+	np->reset_uniforms = NULL;
+
+	lua_getmetatable(L, 1); // 3
+	lua_newtable(L); // 4
+
+	// Iterate old table and copy to new table
+	lua_pushnil(L);
+	while (lua_next(L, 3) != 0) {
+		lua_pushvalue(L, -2);
+		lua_pushvalue(L, -2);
+		lua_rawset(L, 4);
+		lua_pop(L, 1);
+	}
+
+	// Capture a reference to the parent so it is not GC'ed before us
+	lua_pushstring(L, "_parent_clone");
+	lua_pushvalue(L, 1);
+	lua_rawset(L, 4);
+
+	lua_setmetatable(L, 2);
+	lua_pop(L, 1);
+
+	printf("Cloned shader %d\n", p->shader);
+
+	return 1;
+}
+
 static int program_set_uniform_number(lua_State *L)
 {
 	shader_type *p = (shader_type*)lua_touserdata(L, 1);
@@ -196,6 +277,68 @@ static int program_set_uniform_number4(lua_State *L)
 	return 0;
 }
 
+static int program_reset_uniform_number(lua_State *L)
+{
+	shader_type *p = (shader_type*)lua_touserdata(L, 1);
+	const char *var = luaL_checkstring(L, 2);
+	
+	shader_reset_uniform *ru = malloc(sizeof(shader_reset_uniform));
+	ru->next = p->reset_uniforms;
+	p->reset_uniforms = ru;
+	ru->p = glGetUniformLocationARB(p->shader, var);
+	ru->kind = UNIFORM_NUMBER;
+	ru->data.number = luaL_checknumber(L, 3);
+	return 0;
+}
+
+static int program_reset_uniform_number2(lua_State *L)
+{
+	shader_type *p = (shader_type*)lua_touserdata(L, 1);
+	const char *var = luaL_checkstring(L, 2);
+
+	shader_reset_uniform *ru = malloc(sizeof(shader_reset_uniform));
+	ru->next = p->reset_uniforms;
+	p->reset_uniforms = ru;
+	ru->p = glGetUniformLocationARB(p->shader, var);
+	ru->kind = UNIFORM_VEC2;
+	ru->data.vec2[0] = luaL_checknumber(L, 3);
+	ru->data.vec2[1] = luaL_checknumber(L, 4);
+	return 0;
+}
+
+static int program_reset_uniform_number3(lua_State *L)
+{
+	shader_type *p = (shader_type*)lua_touserdata(L, 1);
+	const char *var = luaL_checkstring(L, 2);
+
+	shader_reset_uniform *ru = malloc(sizeof(shader_reset_uniform));
+	ru->next = p->reset_uniforms;
+	p->reset_uniforms = ru;
+	ru->p = glGetUniformLocationARB(p->shader, var);
+	ru->kind = UNIFORM_VEC3;
+	ru->data.vec3[0] = luaL_checknumber(L, 3);
+	ru->data.vec3[1] = luaL_checknumber(L, 4);
+	ru->data.vec3[2] = luaL_checknumber(L, 5);
+	return 0;
+}
+
+static int program_reset_uniform_number4(lua_State *L)
+{
+	shader_type *p = (shader_type*)lua_touserdata(L, 1);
+	const char *var = luaL_checkstring(L, 2);
+
+	shader_reset_uniform *ru = malloc(sizeof(shader_reset_uniform));
+	ru->next = p->reset_uniforms;
+	p->reset_uniforms = ru;
+	ru->p = glGetUniformLocationARB(p->shader, var);
+	ru->kind = UNIFORM_VEC4;
+	ru->data.vec4[0] = luaL_checknumber(L, 3);
+	ru->data.vec4[1] = luaL_checknumber(L, 4);
+	ru->data.vec4[2] = luaL_checknumber(L, 5);
+	ru->data.vec4[3] = luaL_checknumber(L, 6);
+	return 0;
+}
+
 static int program_set_uniform_texture(lua_State *L)
 {
 	shader_type *p = (shader_type*)lua_touserdata(L, 1);
@@ -471,6 +614,7 @@ static const struct luaL_Reg shaderlib[] =
 static const struct luaL_Reg program_reg[] =
 {
 	{"__gc", program_free},
+	{"clone", program_clone},
 	{"compile", program_compile},
 	{"attach", program_attach},
 	{"detach", program_detach},
@@ -479,6 +623,11 @@ static const struct luaL_Reg program_reg[] =
 	{"paramNumber3", program_set_uniform_number3},
 	{"paramNumber4", program_set_uniform_number4},
 	{"paramTexture", program_set_uniform_texture},
+	{"resetClean", program_remove_resets},
+	{"resetParamNumber", program_reset_uniform_number},
+	{"resetParamNumber2", program_reset_uniform_number2},
+	{"resetParamNumber3", program_reset_uniform_number3},
+	{"resetParamNumber4", program_reset_uniform_number4},
 	{"use", program_use},
 	{NULL, NULL},
 };
diff --git a/src/useshader.h b/src/useshader.h
index af77810665..9eb5b8a04a 100644
--- a/src/useshader.h
+++ b/src/useshader.h
@@ -1,10 +1,24 @@
 #ifndef __USESHADER_H__
 #define __USESHADER_H__
 
+struct s_shader_reset_uniform {
+	enum{UNIFORM_NUMBER, UNIFORM_VEC2, UNIFORM_VEC3, UNIFORM_VEC4} kind;
+	GLint p;
+	union {
+		GLfloat number;
+		GLfloat vec2[2];
+		GLfloat vec3[3];
+		GLfloat vec4[4];
+	} data;
+	struct s_shader_reset_uniform *next;
+};
+typedef struct s_shader_reset_uniform shader_reset_uniform;
+
 typedef struct {
+	bool clone;
 	GLuint shader;
 	GLint p_tick, p_color, p_mapcoord, p_texsize;
-	int params_ref;
+	struct s_shader_reset_uniform *reset_uniforms;
 } shader_type;
 
 extern bool shaders_active;
-- 
GitLab