ID:109639
 
A few weeks back, I was working on a light system as part of a new events sequencer. The events sequencer is meant to handle things from season changes, day and night, and weather. Everything was working nicely, until I decided to give the system a test run in a polished project: Hazordhu II. The light system works fine, up until I tested shadow-casting between multiple light sources. I began to run into lag with as few as 9 light sources near each other.




The Light System
I'm making this post because I have a funny feeling that I need to re-evaluate the design of the system, and possibly rewrite it from scratch. Here's how it works:
  • All lighting is handled via /lighting obj's placed on every tile*
  • The distance light is cast is done via a variable called incandescence
  • Lighting objects are updated every time a client moves, an incandescent object moves within view of a client.mob, an object in view of a client.mob changes incandescence, or a door/window is open/closed
  • atom/light() - all of the logic for acquiring lighting objects within the range of the light source and src's affect on their overall intensity
  • lighting/update() - all of the logic for adjusting the intensity of the lighting object, and assigning it's icon_state accordingly

* I know this is far from the most efficient way to handle it, which I think is where I can improve the performance here drastically.

Screenshots


Shadowcasting
//-----------------------------------------//
// //
// This system uses AbyssDragon.BasicMath //
// //
//-----------------------------------------//

lighting
proc
// update the light level of the lighting object
update()
var/global_light = event_lighting.global_light // light level of the world
var/result = tile_light + global_light // resultant light level
var
light // light is the light level of the light source being referenced
distance // distance of light to src

for(var/atom/l in lights) // loop through the light sources affecting the lighting object
light = lights[l]
for(var/turf/o in getline(src, l))

// IF TURFS EVER END UP CASTING SHADOWS, ADD LOGIC HERE

for(var/atom/movable/a in o)
if(a == l) continue
if(a.shadowcast)
distance = distance(src, a)
if(!distance(a, l))
result -= light
else if(distance && distance <= pi)
result -= (( (light) / (distance * distance(src, l)) ) * pi)

result = min(230, max(global_light, round(result)))
if(icon_state != "[result]")
icon_state = "[result]"


I'd like to hear what I can do differently here. Any relevant snippets can be posted, but (for obvious reasons) I'm against putting too much code up on the site for all eyes. That being said, let me know what you need to look at in order to help out, and I'll gladly share it.

Looking forward to your help
- F0lak
Can you show us a screenshot of the lighting system in work?
There you go.
Are you updating /all/ lighting objects in the world when something needs to be updated, or just the ones affected?

Have you done profiling to work out what the slow part of your lighting code is?
I'm updating only the lighting objects that are affected by a change in lighting.

I don't know what a profile would tell me that I don't already know. The system works fine until I'm walking around several light sources at once, updating my shadow.
I've been tinkering with my own lighting system and have noticed that it looks efficient (as far as profiling is concerned) but can be laggy just because the client has to draw shadow objects on each tile. Profiling would at least give you and idea of how much of a difference you could hope to make by rewriting things.

I'm not sure I understand how your shadows work. Are they based on opacity? Do you have lots of opaque objects moving around? This could really increase the rate of updates.

An easy way to improve performance is to reduce the light's radius. You only need to do something when a light changes, and having smaller circles of light means you have to update fewer tiles when there is a change.
I'm not sure I understand how your shadows work
I've added the shadowcast calculation to the post.

Are they based on opacity
No. They're based off a variable called shadowcast. Light doesn't penetrate opaque objects at all, as I'm using view().

Do you have lots of <s>opaque</s> shadowcasting objects moving around
At times, yes. But that's not really an issue. I have tested up to 15 moving shadowcasting objects at a time on a single light source, and a single shadowcasting object on multiple lightsources. The lag arises in the latter situation.

An easy way to improve performance...
Agreed. And I will likely do that. But I still may run into a problem with multiple light sources, and would prefer to have the problem actually fixed, rather than patched over.