Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: TypeScript Check

on:
pull_request:
branches: [main]

jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Setup env files
run: |
for pkg in packages/*/; do
if [ -f "$pkg.env.example" ]; then
cp "$pkg.env.example" "$pkg.env"
fi
done

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build contracts
run: pnpm --filter 'contracts' run build

- name: Build libs
run: pnpm build:libs && pnpm --filter 'query-server' run build

- name: TypeScript check
# claim-frontend, auction-frontend: deprecated, events over
run: pnpm --filter '!claim-frontend' --filter '!auction-frontend' check
2 changes: 1 addition & 1 deletion packages/client/src/lib/components/Admin/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function calculateProfitLossForTrip(
const previousOutcome = tripOutcomes[i - 1]

const outcomeTime = new Date(outcome._createdAt).getTime()
const previousOutcomeValue = previousOutcome?.tripValue || Number(trip.tripCreationCost) // Value from the previous trip, and if it's the first outcome, creation cost of the trip
const previousOutcomeValue = previousOutcome?.tripValue ?? outcome.oldTripValue ?? Number(trip.tripCreationCost) // Value from the previous trip, or oldTripValue (accounts for boosts), or creation cost
const currentTripValue = outcome.tripValue || 0
const valueChange = currentTripValue - previousOutcomeValue

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { playSound } from "$lib/modules/sound"
import { backgroundMusic } from "$lib/modules/sound/stores"
import { shaderManager } from "$lib/modules/webgl/shaders/index.svelte"
import { singleFrameRender } from "$lib/modules/ui/state.svelte"
import TripProcessingPopUp from "./TripProcessingPopUp.svelte"

const {
Expand Down Expand Up @@ -45,9 +46,11 @@
shaderManager.enableContinuousRendering()

// Set the trip processing shader with custom uniforms
// u_quality: 0.0 on mobile (skip madness layer), 1.0 on desktop (full quality)
shaderManager.setShader("tripProcessing", false, {
u_seed1: { type: "float", value: seed1 },
u_seed2: { type: "float", value: seed2 }
u_seed2: { type: "float", value: seed2 },
u_quality: { type: "float", value: $singleFrameRender ? 0.0 : 1.0 }
})

// Start timer
Expand Down
15 changes: 11 additions & 4 deletions packages/webgl/src/ShaderManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { WebGLGeneralRenderer } from "./WebGLGeneralRenderer"
import { shaders } from "./shaders"
import { ShaderInitializationError } from "@ratfun/common/error-handling"
import type { UniformType, UniformDefinition } from "./types"
import { logger } from "./logger"

export type ErrorHandler = (error: Error, context?: string) => void

Expand All @@ -11,6 +12,8 @@ export interface ShaderManagerOptions {
singleFrameRender: () => boolean
/** Preserve drawing buffer for canvas capture (enables toDataURL) - impacts performance */
preserveDrawingBuffer?: boolean
/** Target FPS (default: 60 desktop, 30 mobile) */
targetFPS?: number
}

export class ShaderManager {
Expand All @@ -31,6 +34,7 @@ export class ShaderManager {
private singleFrameRender: () => boolean
private singleFramePauseRafId: number | null = null
private preserveDrawingBuffer: boolean
private targetFPS: number
currentShaderKey: string | null = null
private shaderChangeListeners: Array<(shaderKey: string | null) => void> = []
private shaderChangeTimeout: ReturnType<typeof setTimeout> | null = null
Expand All @@ -44,6 +48,8 @@ export class ShaderManager {
this.errorHandler = options.errorHandler
this.singleFrameRender = options.singleFrameRender
this.preserveDrawingBuffer = options.preserveDrawingBuffer ?? false
// 30fps on mobile, 60fps on desktop
this.targetFPS = options.targetFPS ?? (options.singleFrameRender() ? 30 : 60)
}

/**
Expand Down Expand Up @@ -239,7 +245,7 @@ export class ShaderManager {
}
} catch (error) {
// Error already logged to Sentry in initializeRenderer
console.error(`Failed to initialize shader "${pendingKey}":`, error)
logger.error(`shader init failed: ${pendingKey}`)

// Mark context as exhausted and hide canvas to show CSS fallback
this._contextExhausted = true
Expand Down Expand Up @@ -312,7 +318,8 @@ export class ShaderManager {
shader: shaderSource,
uniforms: initialUniforms,
onError: this.errorHandler,
preserveDrawingBuffer: this.preserveDrawingBuffer
preserveDrawingBuffer: this.preserveDrawingBuffer,
targetFPS: this.targetFPS
})

this._renderer.render()
Expand All @@ -321,7 +328,7 @@ export class ShaderManager {
window.removeEventListener("resize", this.handleResize)
window.addEventListener("resize", this.handleResize)
} catch (error) {
console.error("Failed to initialize WebGL renderer:", error)
logger.error("renderer init failed")
this._renderer = null

// Report to Sentry
Expand Down Expand Up @@ -398,7 +405,7 @@ export class ShaderManager {
}
} catch (error) {
// Still failing, try again
console.warn(`Recovery attempt ${this.recoveryAttempts} failed, will retry...`)
logger.warn(`recovery attempt ${this.recoveryAttempts} failed, retrying`)
this.scheduleContextRecovery(shaderKey)
}
}
Expand Down
11 changes: 6 additions & 5 deletions packages/webgl/src/WebGLGeneralRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
WebGLError,
UniformLocationError
} from "@ratfun/common/error-handling"
import { logger } from "./logger"

/**
* A general-purpose WebGL renderer that can render shaders to a canvas.
Expand Down Expand Up @@ -53,7 +54,7 @@ export class WebGLGeneralRenderer implements WebGLRenderer {
this.startTime = performance.now()
this.uniforms = options.uniforms || {}
this.autoRender = options.autoRender ?? true
this.frameInterval = 1000 / 60
this.frameInterval = 1000 / (options.targetFPS ?? 60)
this.shaderSource = options.shader
this.onError = options.onError
this.preserveDrawingBuffer = options.preserveDrawingBuffer ?? false
Expand Down Expand Up @@ -239,14 +240,14 @@ export class WebGLGeneralRenderer implements WebGLRenderer {
this.animationId = undefined
}

console.warn("WebGL context lost")
logger.warn("context lost")
}

/**
* Handles WebGL context restore event.
*/
private handleContextRestored = (): void => {
console.log("WebGL context restored, reinitializing...")
logger.log("context restored, reinitializing")
this.contextLost = false

try {
Expand All @@ -255,7 +256,7 @@ export class WebGLGeneralRenderer implements WebGLRenderer {
this.render()
}
} catch (error) {
console.error("Failed to restore WebGL context:", error)
logger.error("failed to restore context")
// Error already reported in initWebGL
}
}
Expand Down Expand Up @@ -520,7 +521,7 @@ export class WebGLGeneralRenderer implements WebGLRenderer {
}
} catch (error) {
// WebGL context may be lost or invalid, ignore cleanup errors
console.warn("WebGL cleanup failed (context may be lost):", error)
logger.warn("cleanup failed, context may be lost")
} finally {
this.uniformLocations.clear()
// Clear references to allow garbage collection
Expand Down
1 change: 1 addition & 0 deletions packages/webgl/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Main exports
export { WebGLGeneralRenderer, createWebGLRenderer } from "./WebGLGeneralRenderer"
export { ShaderManager, createShaderManager } from "./ShaderManager"
export { setWebGLLoggingEnabled, isWebGLLoggingEnabled } from "./logger"

// Type exports
export type {
Expand Down
29 changes: 29 additions & 0 deletions packages/webgl/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// webgl package logger - disabled by default to prevent memory leaks
// enable via setWebGLLoggingEnabled(true) during development

let loggingEnabled = false

export function setWebGLLoggingEnabled(enabled: boolean): void {
loggingEnabled = enabled
}

export function isWebGLLoggingEnabled(): boolean {
return loggingEnabled
}

export const logger = {
log: (...args: unknown[]): void => {
if (loggingEnabled) {
console.log("[WebGL]", ...args)
}
},
warn: (...args: unknown[]): void => {
if (loggingEnabled) {
console.warn("[WebGL]", ...args)
}
},
error: (...args: unknown[]): void => {
// errors always logged regardless of loggingEnabled
console.error("[WebGL]", ...args)
}
}
16 changes: 13 additions & 3 deletions packages/webgl/src/shaders/trip-processing/fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ precision highp float;
#define WARP3_MULTIPLIER 3.33 // Warp3 intensity multiplier
#define WARP3_STRENGTH 1.5 // Warp3 displacement strength

// LOD time thresholds (must match complexity thresholds to avoid negative amplitude)
// complexityFactor = u_time / 10.0, so threshold 0.6 = 6 seconds
#define LOD_TIME_TURB 6.0 // Enable turbulence at t=6s (complexity=0.6)
#define LOD_TIME_WARP3 7.0 // Enable warp3 at t=7s (complexity=0.7)
#define LOD_TIME_MADNESS 8.0 // Enable madness at t=8s (complexity=0.8)

// Turbulence parameters
#define TURB_THRESHOLD 0.6 // Complexity threshold for turbulence
#define TURB_MULTIPLIER 2.5 // Turbulence pattern multiplier
Expand Down Expand Up @@ -126,6 +132,7 @@ uniform vec2 u_resolution; // Screen resolution (width, height)
uniform bool u_invert; // Color inversion toggle
uniform float u_seed1; // First seed value (0-1)
uniform float u_seed2; // Second seed value (0-1)
uniform float u_quality; // Quality tier (0.0=mobile, 1.0=desktop)

// ============================================================================
// HASH FUNCTIONS
Expand Down Expand Up @@ -315,7 +322,8 @@ void main() {
warpedUV += vec2(sin(warp2 * TWO_PI), cos(warp2 * TWO_PI)) * (WARP2_STRENGTH_BASE + chaosIntensity * WARP2_STRENGTH_CHAOS);

// Coordinate warping - layer 3 (extreme chaos)
if (complexityFactor > WARP3_THRESHOLD) {
// LOD: uniform-based branch allows GPU to skip entirely before 7s
if (u_time > LOD_TIME_WARP3) {
float warp3a = rotatedNoise(warpedUV * (10.0 + seedOffset1 * 0.1), noiseRotation2, smoothness1);
float warp3b = ridgedNoise(warpedUV * (8.0 + seedOffset2 * 0.1), smoothness2);
float warp3 = mix(warp3a, warp3b, combinedSeed) * (complexityFactor - WARP3_THRESHOLD) * WARP3_MULTIPLIER;
Expand Down Expand Up @@ -359,7 +367,8 @@ void main() {
pattern += noisePattern2 * chaosIntensity * LAYER5_CHAOS_WEIGHT;

// Layer 6: Extreme turbulence (conditional)
if (complexityFactor > TURB_THRESHOLD) {
// LOD: uniform-based branch allows GPU to skip entirely before 6s
if (u_time > LOD_TIME_TURB) {
vec2 turbCoord = warpedUV * (12.0 + combinedSeed * 20.0);
vec2 spiralVec = vec2(spiralTime);

Expand All @@ -374,7 +383,8 @@ void main() {
}

// Layer 7: Complete madness (conditional)
if (complexityFactor > MAD_THRESHOLD) {
// LOD: skip on mobile (u_quality < 1.0) and before 5s
if (u_time > LOD_TIME_MADNESS && u_quality > 0.5) {
float madness = (complexityFactor - MAD_THRESHOLD) * MAD_MULTIPLIER;
vec2 madCoord = warpedUV * (20.0 + seedOffset1 * HALF) + vec2(spiralTime * TWO, -spiralTime * 1.5);

Expand Down