Created
June 17, 2024 06:20
-
-
Save Beherith/c965cff2a81253e37aed25f4db0c0fce to your computer and use it in GitHub Desktop.
LuaVBO updating cost tester
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function widget:GetInfo() | |
return { | |
name = "LuaVBO Drawing Tester", | |
desc = "Simplest proof-of-concept VBO Drawer", | |
author = "Beherith GL4", | |
date = "2024.06.15", | |
license = "GNU GPL, v2 or later", | |
layer = 2, | |
enabled = true, | |
} | |
end | |
-- ================================================================================= | |
-- The goal of this widget is to test the performance cost of 6 things | |
-- [ ] A. Cost of updating (or adding) a single instance to a VBO | |
-- [ ] B. Cost of updating (or adding) a single Unit to an instanceVBO | |
-- 17 us | |
-- [ ] C. Cost of uploading 1-10 elements to a VBO | |
-- [ ] D. Cost of uploadint 1-10 units to an instanceVBO | |
-- [ ] E. Cost of doing ALL uploads in one pass for single units | |
-- [ ] F. Cost of doing ALL uploads in one pass for a unitVBO | |
-- | |
-- | |
-- General notes: | |
-- - A single call costs about 13 us | |
-- - Adding a unitID costs 33% more (17us), and this is true across the board, even for batched uploads | |
-- - Each successive hot cached call costs only 40% of the first call (~5 us) | |
-- - The most expensive part of single calls is the upload to VBO part, account for 70% of the total time | |
-- - Batched uploading costs about 20% of the time of individual uploads (!) | |
-- - E.g if we have more than 100 items, and modify 20% of them , then batched uploads are faster than individual | |
-- | |
-- | |
-- | |
-- | |
-- | |
-- ================================================================================= | |
local testVBO = nil | |
local testInstanceVBO = nil | |
local testShader = nil | |
local luaShaderDir = "LuaUI/Widgets/Include/" | |
local LuaShader = VFS.Include(luaShaderDir.."LuaShader.lua") | |
VFS.Include(luaShaderDir.."instancevbotable.lua") | |
local rand = math.random | |
local isUnitVBO = true | |
local totalcnt = 100 | |
local allunits = {} | |
local instanceCache = {} | |
for i=1,20 do instanceCache[i] = 0 end | |
if isUnitVBO then instanceCache[5] = 1 end | |
instanceCache[10] = 1.0 | |
local vsSrc = [[ | |
#version 420 | |
#extension GL_ARB_uniform_buffer_object : require | |
#extension GL_ARB_shader_storage_buffer_object : require | |
#extension GL_ARB_shading_language_420pack: require | |
layout (location = 0) in vec3 position; | |
layout (location = 1) in vec3 normals; | |
layout (location = 2) in vec2 uvs; | |
layout (location = 3) in vec4 worldposrad; | |
layout (location = 4) in vec4 params; // Contains isworld shit | |
layout (location = 5) in vec4 color1; | |
layout (location = 6) in vec4 color2; | |
layout (location = 7) in uvec4 instData; // unitID, teamID | |
//__ENGINEUNIFORMBUFFERDEFS__ | |
struct SUniformsBuffer { | |
uint composite; // u8 drawFlag; u8 unused1; u16 id; | |
uint unused2; | |
uint unused3; | |
uint unused4; | |
float maxHealth; | |
float health; | |
float unused5; | |
float unused6; | |
vec4 drawPos; | |
vec4 speed; | |
vec4[4] userDefined; //can't use float[16] because float in arrays occupies 4 * float space | |
}; | |
layout(std140, binding=1) readonly buffer UniformsBuffer { | |
SUniformsBuffer uni[]; | |
}; | |
out DataVS { | |
vec4 vs_color; | |
}; | |
void main() { | |
vec4 vertexPosition = vec4(worldposrad.xyz + position.xyz * worldposrad.w, 1.0); | |
if (params.x > 0.5) { | |
vertexPosition.xyz += uni[instData.y].drawPos.xyz; | |
} | |
vs_color = color1; | |
gl_Position = cameraViewProj * vec4(vertexPosition.xyz, 1.0); | |
}]] | |
local fsSrc = [[ | |
#version 330 | |
#extension GL_ARB_uniform_buffer_object : require | |
#extension GL_ARB_shading_language_420pack: require | |
uniform sampler2D myTexture; | |
//__ENGINEUNIFORMBUFFERDEFS__ | |
in DataVS { | |
vec4 vs_color; | |
}; | |
out vec4 fragColor; | |
void main() { | |
fragColor.rgba = vec4(vs_color.rgb, 1.0); | |
}]] | |
local function AddTestElement(instanceID, unitID, px,py,pz,r, noupload) | |
instanceCache[1] = px | |
instanceCache[2] = py | |
instanceCache[3] = pz | |
instanceCache[4] = r | |
pushElementInstance(testInstanceVBO, instanceCache, instanceID, true, noupload, unitID) | |
end | |
function widget:Initialize() | |
local engineUniformBufferDefs = LuaShader.GetEngineUniformBufferDefs() | |
testShader = LuaShader( { | |
vertex = vsSrc:gsub("//__ENGINEUNIFORMBUFFERDEFS__", engineUniformBufferDefs), | |
fragment = fsSrc:gsub("//__ENGINEUNIFORMBUFFERDEFS__", engineUniformBufferDefs), | |
uniformInt = { myTexture = 0,}, | |
uniformFloat = { }, | |
}, | |
"LuaVBO Drawing Tester GL4" | |
) | |
testShader:Initialize() | |
local sphereVBO, numVerts, sphereIndexVBO, numIndices = makeSphereVBO(16,8,1) | |
local testVBOLayout = { | |
{id = 3, name = 'worldposrad', size = 4}, -- widthlength | |
{id = 4, name = 'params', size = 4}, -- emit dir | |
{id = 5, name = 'color1', size = 4}, --- color | |
{id = 6, name = 'color2', size = 4}, --- color | |
{id = 7, name = 'instData', type = GL.UNSIGNED_INT, size= 4}, | |
} | |
if isUnitVBO then | |
testInstanceVBO = makeInstanceVBOTable(testVBOLayout,256, "testVBO", 7) | |
else | |
testInstanceVBO = makeInstanceVBOTable(testVBOLayout,256, "testVBO", nil) | |
end | |
if not testInstanceVBO then Spring.Echo("failed to create testInstanceVBO") end | |
testInstanceVBO.numVertices = numIndices | |
testInstanceVBO.vertexVBO = sphereVBO | |
testInstanceVBO.indexVBO = sphereIndexVBO | |
testInstanceVBO.VAO = makeVAOandAttach(testInstanceVBO.vertexVBO, testInstanceVBO.instanceVBO) | |
testInstanceVBO.VAO:AttachIndexBuffer(testInstanceVBO.indexVBO) | |
allunits = Spring.GetAllUnits() | |
for i= 1,totalcnt do | |
if isUnitVBO then | |
local testUnitID = allunits[math.random(1,#allunits)] | |
AddTestElement(i, testUnitID, 1000 * rand(), 1000* rand(), 1000*rand(), 10*rand()) | |
else | |
AddTestElement(i, nil, 1000 * rand(), 1000* rand(), 1000*rand(), 10*rand()) | |
end | |
end | |
end | |
local drawFrame | |
function widget:Update() | |
-- [ ] A. Cost of updating (or adding) a single instance to a VBO | |
-- [ ] B. Cost of updating (or adding) a single Unit to an instanceVBO | |
-- [ ] C. Cost of uploading 1-10 elements to a VBO | |
-- [ ] D. Cost of uploadint 1-10 units to an instanceVBO | |
-- [ ] E. Cost of doing ALL uploads in one pass for single units | |
-- [ ] F. Cost of doing ALL uploads in one pass for a unitVBO | |
drawFrame = Spring.GetDrawFrame() | |
if isUnitVBO or true then | |
if drawFrame%4 ==0 then | |
-- [ ] B. Cost of updating (or adding) a single Unit to an instanceVBO | |
-- Costs on average 16us per call | |
local testUnitID = allunits[math.random(1,#allunits)] | |
tracy.ZoneBeginN("testVBO:".. (isUnitVBO and "B" or "A")) | |
AddTestElement(math.random(1,totalcnt-1) , isUnitVBO and testUnitID or nil, 1000 * rand(), 1000* rand(), 1000*rand(), 10*rand()) | |
tracy.ZoneEnd() | |
elseif drawFrame%4 ==1 then | |
-- [ ] D. Cost of uploading 1-10 units to an instanceVBO | |
-- Costs on average 16 us for first, + 6 us per additional unit | |
local count = math.floor(drawFrame/4) % 20 | |
local testUnitID = allunits[math.random(1,#allunits)] | |
tracy.ZoneBeginN(string.format("testVBO:%s:%03d",isUnitVBO and "D" or "C" , count)) | |
for j =1, count do | |
AddTestElement(math.random(1,totalcnt-1) , isUnitVBO and testUnitID or nil, 1000 * rand(), 1000* rand(), 1000*rand(), 10*rand()) | |
end | |
tracy.ZoneEnd() | |
elseif drawFrame %4 == 2 then | |
-- [ ] F. Cost of uploading 1-10 DIFFERENT units to an instanceVBO | |
-- Costs on average 16 us for first, + 6 us per additional unit | |
local count = math.floor(drawFrame/4) % 20 | |
tracy.ZoneBeginN(string.format("testVBO:%s:%03d",isUnitVBO and "F" or "E" , count)) | |
for j =1, count do | |
local testUnitID = allunits[math.random(1,#allunits)] | |
AddTestElement(math.random(1,totalcnt-1) , isUnitVBO and testUnitID or nil, 1000 * rand(), 1000* rand(), 1000*rand(), 10*rand()) | |
end | |
tracy.ZoneEnd() | |
else | |
-- [ ] H. Cost of NOT uploading 1-10 DIFFERENT units to an instanceVBO, then uploading all! | |
-- Costs on average 16 us for first, + 6 us per additional unit | |
local count = math.floor(drawFrame/4) % 20 | |
tracy.ZoneBeginN(string.format("testVBO:%s:%03d",isUnitVBO and "H" or "G" , count)) | |
for j =1, count do | |
local testUnitID = allunits[math.random(1,#allunits)] | |
AddTestElement(math.random(1,totalcnt-1) , isUnitVBO and testUnitID or nil, 1000 * rand(), 1000* rand(), 1000*rand(), 10*rand(), true) -- note NOUPLOAD | |
end | |
tracy.ZoneEnd() | |
tracy.ZoneBeginN(string.format("testVBO:%s:%04d", isUnitVBO and "H" or "G", totalcnt)) | |
uploadAllElements(testInstanceVBO) | |
tracy.ZoneEnd() | |
end | |
else | |
end | |
end | |
function widget:DrawWorld() | |
if testInstanceVBO.usedElements > 0 then | |
gl.Culling(false) | |
testShader:Activate() | |
drawInstanceVBO(testInstanceVBO) | |
testShader:Deactivate() | |
end | |
end | |
function widget:Shutdown() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment