As a developer, it can be too tedious to test performance with different subsets of your playerbase, especially with mobile players due to how microprofiling works.
I’m proposing a new solution that allows developers to toggle a setting to allow players / specific players to submit their own microprofiler dumps from the game itself and an integration into the configuration of a game allowing developers to analyze it for performance issues.
Microprofiler dumps over 14 days old will be cleared to conserve storage and to preserve recency.
Microprofiler dumps might not be able to be sent by <13’s? (I’m presuming because of data laws?)
While I’m hoping that this is able to cover most cases for a developer, it may not be enough and I’m absolutely open to receiving feedback in that case, and I wouldn’t be surprised as this is unintuitive, as this is how I’d personally think about it.
Possibly a new Instance (DeviceInfo?), while reading other threads, specifically CaptureService:GetDeviceInfo() changes, I’ve noticed that fingerprinting is a huge issue, so here’s my suggestion to allow developers usable information while preventing fingerprinting:
Each Snapshot will only contain the user’s currently used ram, platform (Mobile, Computer, Console, Quest) and a generic descriptor of their system (Entry-Level Platform, Standard Platform and High-End Platform, these of course would only apply to PC and Mobile.)
A new event under MicroProfilerService called .FrameCollected with parameters for the delta of the last frame and also passes along a MicroprofilerSnapshot (as shown below), if the MicroprofilerSnapshot is not stored at the end of the event, it is discarded.
A new event on the server called .MicroprofilerUpload(), which the server uses, it passes along the parameters as Player? (can be nil if the player chooses to remain anonymous), MicroprofilerSnapshotContainer and DeviceInfo? (can be nil if the player chooses not to disclose this information.)
A new Instance (MicroprofilerSnapshot?) (this might end up literally being just what is essentially a dictionary) that stores Microprofiler information about the current frame with methods such as:
:DebugTagPresent(string) which would take a string from Microprofiler’s debug label and return true or false if it is present in the frame stemming from .FrameCollected (debug.profilebegin()),
:FunctionCalled(Script? | ModuleScript? | LocalScript?, string) this method would take a script instance and a label to check if a certain function has ran.
A new Instance (MicroprofilerSnapshotContainer?) created via MicroProfilerService:CreateSnapshotContainer() and options (example: {MaxFrames = 128} that stores MicroprofilerSnapshots, this could literally just be a table, however I figure a new Instance would make more sense.
This would include Methods such as :Insert(MicroprofilerSnapshot) :Remove(Index), :Clear() and :Destroy()
Methods on the client and server under MicroProfilerService
MicroProfilerService:UploadCapture(Player?, MicroprofilerSnapShotContainer?), if the client calls this, it will upload their container to the server and trigger .MicroprofilerUpload, if the server calls this it will upload their container to be viewed via the creator hub.
Here’s how I’d like to imagine how this would work in code:
-- Client
local MicroProfilerService = game:GetService("MicroProfilerService")
local Container = MicroProfilerService:CreateSnapshotContainer({MaxFrames = 128}) -- Must be a multiple of 2.
local PreviousDelta = nil
local Tolerance = 0.45
MicroProfilerService.FrameCollected:Connect(function(delta : number, Snapshot : MicroprofilerSnapshot)
local PercentChanged = 0
local Stored = false -- dumb bandaid idea I had, this could probably be handled internally by giving each Snapshot an ID and checking when Insert is called.
if PreviousDelta then
PercentChanged = delta/PreviousDelta
end
if Snapshot:DebugTagPresent("Expensive code we're tracking!") then
Container:Insert(Snapshot)
Stored = true
end
if Snapshot:FunctionCalled(script, "ExpensiveCode") and not Stored then
Container:Insert(Snapshot)
Stored = true
end
if PercentChanged > Tolerance and not Stored then
Container:Insert(Snapshot)
Stored = true
end
PreviousDelta = delta
end)
-- Server
local MicroProfilerService = game:GetService("MicroProfilerService")
MicroProfilerService.MicroprofilerUpload:Connect(function(Player : Player? | nil, Container : MicroprofilerSnapshotContainer, DeviceInfo : DeviceInfo? | nil)
-- As it stands right now, my idea could be susceptible to exploits, so perhaps some basic checks unless they can be handled internally?
-- Realistically, I don't think there's a need for this function, so it could be scrapped entirely?
Container:UploadCapture(Player, Container, DeviceInfo)
end)
This idea could use more fleshing out, I’ve spent about the past 45 minutes on and off thinking about this, so if you have any edge cases or anything that could pose an issue, please let me know and I’d be happy to let you know what I think could solve the issue.