UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

126 lines (108 loc) 4.26 kB
import { query_bvh_frustum_from_texture } from "./query/query_bvh_frustum_from_texture.js"; import { compute_light_data_hash_0 } from "./cluster/compute_light_data_hash_0.js"; import { testClusterEquality } from "./testClusterEquality.js"; const LOOKUP_CACHE_SIZE = 1024; // must be power of two /** * Using continuous buffer for related attributes to get better cache utilization * @type {ArrayBuffer} * @private */ const _scratch_array_buffer = new ArrayBuffer( 24 * 4 + 24 * 4 + LOOKUP_CACHE_SIZE * 4 ); /** * * @type {number[]|Float32Array|Float64Array} */ export const scratch_corners = new Float32Array(_scratch_array_buffer, 0, 24); /** * Linear access data structure with parameters of each plane of the frustum * 6 planes, 4 values per plane * Layout: [normal.x, normal.y, normal.z, constant] * @type {number[]|Float32Array|Float64Array} */ export const scratch_frustum_planes = new Float32Array(_scratch_array_buffer, 24 * 4, 24); /** * Cache used to identify duplicate cells. Helps reduce memory usage and just point to equivalent existing cells instead of allocating new ones. * @type {Uint16Array|Uint32Array} */ export const LOOKUP_CACHE = new Uint32Array(_scratch_array_buffer, (24 + 24) * 4, LOOKUP_CACHE_SIZE); /** * * @type {number[]} */ const scratch_light_nodes = []; /** * * @param {number} tile_texture_data_offset * @param {BinaryUint32BVH} bvh * @param {number} lookup_address_offset * @param {number[]|ArrayLike<number>|Uint8ClampedArray|Uint8Array|Uint16Array|Uint32Array} lookup_data * @param {number[]|ArrayLike<number>|Uint8Array|Uint8ClampedArray|Uint16Array|Uint32Array} cluster_data * @param {number[]|ArrayLike<number>|Float32Array} source_data Light/Decal source information is stored in here, such as position, color etc. * @return {number} */ export function assign_cluster( tile_texture_data_offset, bvh, lookup_address_offset, lookup_data, cluster_data, source_data ) { // at this point we have a fully constructed cluster, we can query lights within the cluster const match_count = query_bvh_frustum_from_texture( scratch_light_nodes, 0, bvh, source_data, scratch_frustum_planes, scratch_corners ); // construct tile data // write number of light entries cluster_data[tile_texture_data_offset + 1] = match_count; if (match_count === 0) { // don't waste time on assignment, there are no lights in the cluster return 0; } /* We can sort lights by index to get higher cache utilization, but it turns out quite costly. Through experimentation, sorting was taking around 20% of the whole assignment time while improving number of cache hits by around 40% (8000 without sorting -> 11000 with sorting) */ const cluster_data_hash = compute_light_data_hash_0(scratch_light_nodes, match_count) & (LOOKUP_CACHE_SIZE - 1); const existing_lookup_address = LOOKUP_CACHE[cluster_data_hash]; if (existing_lookup_address !== 0xFFFFFFFF) { // found a hash match, validate equality const existing_lookup_entry_match = testClusterEquality( scratch_light_nodes, match_count, lookup_data, existing_lookup_address ); if (existing_lookup_entry_match) { cluster_data[tile_texture_data_offset] = existing_lookup_address; //hash_reuse_count++; return 0; } } else { // no hash entry exists, create one LOOKUP_CACHE[cluster_data_hash] = lookup_address_offset; } // if we get to this point, that means no hash match was found, or equality check was failed // write address of the lookup table cluster_data[tile_texture_data_offset] = lookup_address_offset; for (let i = 0; i < match_count; i++) { /** * * @type {number} */ const light_descriptor = scratch_light_nodes[i]; // construct lookup table lookup_data[lookup_address_offset + i] = light_descriptor; } return match_count; }