Jump to content

Performance question


Recommended Posts

Hi!

I want to make a paintjob system, and I found two examples. What do you think, which one is more performance/fps friendly? 

1.:

availablePaintJobs = {
	[480] = {
			{'remap', 'textures/comet/1.dds'},
			{'remap', 'textures/comet/2.dds'},
			{'remap', 'textures/comet/3.dds'},
			{'remap', 'textures/comet/4.dds'},
			{'remap', 'textures/r8/r8_4.dds'},
			{'remap', 'textures/lada4.dds'},
			{'remap', 'textures/mc13.dds'},
	},

	[436] = {
		{'*remap*', 'textures/y5/1.dds'},
		{'*remap*', 'textures/y5/2.dds'},
		{'*remap*', 'textures/y5/3.dds'},
		{'*remap*', 'textures/y5/4.dds'},
		{'*remap*', 'textures/y5/5.dds'},

	},

	[604] = {
		{'*remap*', 'textures/1.dds'},
		{'*remap*', 'textures/2.dds'},
		{'*remap*', 'textures/3.dds'},
		{'*remap*', 'textures/4.dds'},
		{'*remap*', 'textures/5.dds'},
		{'*remap*', 'textures/6.dds'},
		{'*remap*', 'textures/7.dds'},
	},
};

local shaders = {}
local elementShaders = {}
local textureCache = {}
local textureCount = {}
local textureSize = 768

function applyShader(texture, img, distance, element)
	if element then
		destroyShaderCache(element)
	end
	local this = #shaders + 1
	shaders[this] = {}
	shaders[this][1] = dxCreateShader("texturechanger.fx",0,distance,layered)
	if not textureCount[img] then
		textureCount[img] = 0
	end
	if textureCount[img] == 0 then
		textureCache[img] = dxCreateTexture(img)
	end
	textureCount[img] = textureCount[img] + 1
	shaders[this][2] = textureCache[img]
	shaders[this][3] = texture
	if element then
		if not elementShaders[element] then
			elementShaders[element] = {shaders[this], img}
		end
	end
	if shaders[this][1] and shaders[this][2] then
		dxSetShaderValue(shaders[this][1], "TEXTURE", shaders[this][2])
		engineApplyShaderToWorldTexture(shaders[this][1], texture, element)
	end
end

function destroyShaderCache(element)
	if elementShaders[element] then
		destroyElement(elementShaders[element][1][1])
		local old_img = elementShaders[element][2]
		textureCount[old_img] = textureCount[old_img] - 1
		if textureCount[old_img] == 0 then
			destroyElement(elementShaders[element][1][2])
		end
		elementShaders[element] = nil
	end
end
addEvent("destroyShaderCache", true)
addEventHandler("destroyShaderCache", root, destroyShaderCache)


addEventHandler("onClientResourceStart", resourceRoot, function()
	for k,v in ipairs(getElementsByType("vehicle", root, true)) do
		local pj = tonumber(getElementData(v, "tuning.paintjob")) or 0
		if pj > 0 then
			addVehiclePaintJob(v, getElementModel(v), pj)
		end
	end
end)

function getVehiclePaintJobs(model)
	if availablePaintJobs[model] then
		return #availablePaintJobs[model]
	else
		return 0
	end
end

function addVehiclePaintJob(veh, model, id)
	local pj = availablePaintJobs[model]
	if pj then
		local pj = pj[id]
		if pj then
			applyShader(pj[1], pj[2], 100, veh)
		end
	end    
end
addEvent("addVehiclePaintJob", true)
addEventHandler("addVehiclePaintJob", root, addVehiclePaintJob)

function removeVehiclePaintJob(veh)
	if veh then
		destroyShaderCache(veh)
	end
end
addEvent("removeVehiclePaintJob", true)
addEventHandler("removeVehiclePaintJob", root, removeVehiclePaintJob)

addEventHandler("onClientElementDestroy", getRootElement(), function()
	if getElementType(source) == "vehicle" then
		destroyShaderCache(source)
	end
end)

addEventHandler("onClientElementStreamIn", getRootElement(), function()
	if getElementType(source) == "vehicle" then
		local pj = tonumber(getElementData(source, "tuning.paintjob")) or 0
		if pj > 0 then
			addVehiclePaintJob(source, getElementModel(source), pj)
		end
	end
end)

addEventHandler("onClientElementStreamOut", getRootElement(), function()
	if getElementType(source) == "vehicle" then
		destroyShaderCache(source)
	end
end)

or 2.:

local modelTextures = {
	[582] = "template",
	[445] = "remap",
	[402] = "remap",
	[400] = "remapsrt8",
	[555] = "remap",
	[416] = "remap",
	[503] = "remap",
	[580] = "remap",
	[558] = "remap",
	[598] = "remap",
	[458] = "remap",
	[589] = "remap",
	[420] = "face",
 	[566] = "bodykit",
	[518] = "remap",
	[561] = "remap",

	[597] = "r_map",
	[596] = "remap_taurus",
	[599] = "remap",
}

local availableTextures = {
	[1] = "sprinter/1.dds",
	[2] = "sprinter/2.dds",
	[3] = "sprinter/3.dds",
	[4] = "sprinter/4.dds",
	[5] = "sprinter/5.dds",
	[6] = "sprinter/6.dds",
	[7] = "sprinter/7.dds",
	[8] = "sprinter/8.dds",
	[9] = "sprinter/9.dds",

	[12] = "e60/4.dds",
	[13] = "e60/5.dds",
	[14] = "e60/m51.dds",
	[15] = "e60/m52.dds",
	[16] = "e60/m53.dds",
	[17] = "e60/m61.dds",
	[18] = "e60/m63.dds",
	[19] = "e60/m62.dds",

	[20] = "250gto/1.dds",
	[21] = "250gto/2.dds",
	[22] = "250gto/3.dds",
	[23] = "250gto/4.dds",

	[24] = "ambulance/1.dds",

	[25] = "amggt/1.dds",
	[26] = "amggt/2.dds",
	[27] = "amggt/3.dds",
	[28] = "amggt/4.dds",
	[29] = "amggt/5.dds",
	[30] = "amggt/6.dds",

	[31] = "audirs4/1.dds",
	[32] = "audirs4/2.dds",
	[33] = "audirs4/3.dds",
	[34] = "audirs4/4.dds",
	[35] = "audirs4/5.dds",
	[36] = "audirs4/6.dds",
	[37] = "audirs4/7.dds",

	[38] = "bmwm3/1.dds",
	[39] = "bmwm3/2.dds",
	[40] = "bmwm3/3.dds",

	[41] = "copcarpj/1.dds",
	[42] = "copcarpj/2.dds",
	[43] = "copcarpj/3.dds",

	[44] = "e250/1.dds",
	[45] = "e250/2.dds",

	[46] = "golf/1.dds",
	[47] = "golf/2.dds",
	[48] = "golf/3.dds",
	[49] = "golf/4.dds",
	[50] = "golf/5.dds",

	[51] = "viper/1.dds",
	[52] = "viper/2.dds",
	[53] = "viper/3.dds",
	[54] = "viper/4.dds",
	[55] = "viper/5.dds",
	[56] = "viper/6.dds",
	[57] = "viper/7.dds",
	[58] = "viper/8.dds",

	[59] = "nissan240/1.dds",
	[60] = "nissan240/2.dds",
	[61] = "nissan240/3.dds",
	[62] = "nissan240/4.dds",
	[63] = "nissan240/5.dds",
	[64] = "nissan240/6.dds",
	[65] = "nissan240/7.dds",
	[66] = "nissan240/8.dds",

	[67] = "grandsrt8/1.dds",
	[68] = "grandsrt8/2.dds",
	[69] = "grandsrt8/3.dds",

	[70] = "tahoe/1.dds",
	[71] = "tahoe/2.dds",
	[72] = "tahoe/3.dds",
	[73] = "tahoe/4.dds",

	[74] = "taurus/1.dds",
	[75] = "taurus/2.dds",
	[76] = "taurus/3.dds",

	[77] = "suburban/1.dds",
	[78] = "suburban/2.dds",
	[79] = "suburban/3.dds",

	[80] = "polmav/1.dds",
	[81] = "polmav/2.dds",
	[82] = "polmav/3.dds",
	[83] = "polmav/4.dds",
	[84] = "polmav/5.dds",

	[85] = "f10/1.dds",
	[86] = "f10/2.dds",

	[87] = "e34/1.dds",
	[88] = "e34/2.dds",
	[89] = "e34/3.dds",
}

local paintjobs = {
	["template"] = {
		[582] = {1, 2, 3, 4, 5, 6, 7, 8, 9}
	},
	["bodykit"] = {
		[566] = {85, 86}
	},
	["face"] = {
		[420] = {87, 88, 89}
	},
	["remap"] = {
		[402] = {10},
		[445] = {12, 13, 14, 15, 16, 17, 18, 19},
		[555] = {20, 21, 22, 23},
		[416] = {24},
		[503] = {25,26,27,28,29,30},
		[580] = {31,32,33,34,35,36,37},
		[558] = {38,39,40},
		[598] = {41,42,43},
		[458] = {44,45},
		[589] = {46,47,48,49,50},
		[518] = {51,52,53,54,55,56,57,58},
		[561] = {59,60,61,62,63,64,65,66},
		
	},

	["remapsrt8"] = {
		[400] = {67,68,69},
	},
	["remap_taurus"] = {
		[596] = {70,71,72,73},
	},

	["r_map"] = {
		[596] = {80, 81, 82, 83, 84},
		[597] = {77,78,79},
	},
}

local textures = {}
local shaders = {}

addEventHandler("onClientResourceStart", getResourceRootElement(),
	function ()
		for k, v in pairs(availableTextures) do
			textures[k] = dxCreateTexture("textures/" .. v, "dxt1")
		end

		for k, v in pairs(getElementsByType("vehicle"), getRootElement(), true) do
			applyTexture(v)
		end
	end)

addEventHandler("onClientElementStreamIn", getRootElement(),
	function ()
		if getElementType(source) == "vehicle" then
			applyTexture(source)
		end
	end)

addEventHandler("onClientResourceStop", getResourceRootElement(),
	function ()
		for k, v in pairs(shaders) do
			if isElement(v) then
				destroyElement(v)
			end
		end

		for k, v in pairs(textures) do
			destroyElement(v)
		end
	end)

addEventHandler("onClientElementDataChange", getRootElement(),
	function (dataName)
		if dataName == "vehicle.tuning.Paintjob" then
			applyTexture(source)
		end
	end)

addEventHandler("onClientElementDestroy", getRootElement(),
	function ()
		if shaders[source] then
			if isElement(shaders[source]) then
				destroyElement(shaders[source])
			end

			shaders[source] = nil
		end
	end)

function applyTexture(vehicle)
	local paintjobId = getElementData(vehicle, "vehicle.tuning.Paintjob") or 0

	if paintjobId then
		if paintjobId == 0 then
			if isElement(shaders[vehicle]) then
				destroyElement(shaders[vehicle])
			end

			shaders[vehicle] = nil
		elseif paintjobId > 0 then
			local model = getElementModel(vehicle)

			if not isElement(shaders[vehicle]) then
				shaders[vehicle] = dxCreateShader("texturechanger.fx")
			end

			local modelTexture = modelTextures[model]

			if modelTexture then
				local paintjob = paintjobs[modelTexture]

				if paintjob and paintjob[model] then
					local textureId = paintjob[model][paintjobId]

					if textureId and shaders[vehicle] then
						if textures[textureId] then
							dxSetShaderValue(shaders[vehicle], "gTexture", textures[textureId])
							engineApplyShaderToWorldTexture(shaders[vehicle], modelTexture, vehicle)
						end
					end
				end
			end
		end
	end
end

function isTextureIdValid(model, textureId)
	local modelTexture = modelTextures[model]

	if modelTexture then
		local paintjob = paintjobs[modelTexture]

		if paintjob then
			if paintjob[model] then
				if paintjob[model][textureId] then
					return true
				else
					return false
				end
			else
				return false
			end
		else
			return false
		end
	else
		return false
	end
end

function applyPaintJob(paintjobId, sync)
	local pedveh = getPedOccupiedVehicle(localPlayer)

	paintjobId = tonumber(paintjobId)

	if paintjobId then
		if isElement(pedveh) then
			local model = getElementModel(pedveh)

			if isTextureIdValid(model, paintjobId) or paintjobId == 0 then
				setElementData(pedveh, "vehicle.tuning.Paintjob", paintjobId, sync)
			end
		end
	end
end

function getPaintJobCount(model)
	model = tonumber(model)

	local modelTexture = modelTextures[model]
	local paintjob = paintjobs[modelTexture]

	if model == 467 then
		return #paintjob[model] - 2
	end

	if model == 500 or model == 438 then
		return #paintjob[model] - 1
	end

	if model and modelTexture and paintjob and paintjob[model] then
		return #paintjob[model]
	end

	return false
end

 

Link to comment

2nd seems more sophisticated and more likely to be performance friendly since it somewhat has logic for ElementStreamIn event, however it'd need more checks to avoid unnecessarily calling the applyTexture function (like check if the vehicle already has it applied, handle ElementStreamOut as well, etc etc.)

Link to comment
16 hours ago, Dzsozi (h03) said:

2nd seems more sophisticated and more likely to be performance friendly since it somewhat has logic for ElementStreamIn event, however it'd need more checks to avoid unnecessarily calling the applyTexture function (like check if the vehicle already has it applied, handle ElementStreamOut as well, etc etc.)

1st one also has logic for streamIn and streamOut, creating/destroying and applying textures.

But the 2nd option creates all of the textures(dxCreateTexture) on the resource start, and I don't know that is a good or a bad idea. The 1st one is only creating textures when an element streamed in, and it is need the texture.

Link to comment
1 hour ago, pixel77 said:

1st one also has logic for streamIn and streamOut, creating/destroying and applying textures.

But the 2nd option creates all of the textures(dxCreateTexture) on the resource start, and I don't know that is a good or a bad idea. The 1st one is only creating textures when an element streamed in, and it is need the texture.

Yeah you are right, I didn't pay close enough attention to the first code. However in my opinion, constantly creating and destroying textures per element as they stream in/out makes a larger performance impact, rather than keeping all of the textures in memory and applying a texture replace shader when needed. As far as I know when you create or destroy a texture it makes an impact on the CPU, but when you apply or remove or use a shader it rather uses the resources of the GPU, which could handle more.

In my opinion it would be better to just create every texture needed on the start of the resource.

If you'd have 100+ vehicle paintjob textures to create and use later, it may freeze for a few seconds when starting the resource/script, but after that you will have access to all of them. Then you could handle the shader creating and destroying on streaming events, other than the texture creating and destroying.

If you have 5 vehicle paintjob textures in high quality, and suddenly 5 HQ model/texture vehicle requires the creation of the HQ paintjob textures, a client might more likely to stutter when the 5 vehicles stream in/out at the same time.

I think it is more efficient to store what you need in RAM and take a toll on the GPU, rather than constantly burdening the CPU.

Correct me if I am wrong, but so far this is my understanding of how these kinds of things work.

Link to comment

The necessary textures should be created at the start of the resource, because although they are not necessarily needed immediately, in the case of intermediate creation, lag spikes may occur, which may degrade the gameplay.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...