Enhance WorkerTerritoryRenderer with view management and frame dirty tracking

- Introduced new properties to track view dimensions, scale, and offsets for improved rendering efficiency.
- Updated rendering logic to set frameDirty flag based on changes in view parameters and shader settings.
- Refactored methods to prevent unnecessary updates when view parameters remain unchanged.
- Improved error handling in shader setting to ensure smoother rendering operations.
This commit is contained in:
scamiv
2026-02-05 21:46:50 +01:00
parent aa2578514a
commit f134e5ba1a
+109 -39
View File
@@ -29,6 +29,15 @@ export class WorkerTerritoryRenderer {
private gameViewAdapter: GameViewAdapter | null = null; private gameViewAdapter: GameViewAdapter | null = null;
private ready = false; private ready = false;
private lastGpuWork: Promise<void> | null = null; private lastGpuWork: Promise<void> | null = null;
private frameDirty = true;
private lastViewWidth = 0;
private lastViewHeight = 0;
private lastViewScale = NaN;
private lastViewOffsetX = NaN;
private lastViewOffsetY = NaN;
private lastAlternativeViewEnabled: boolean | null = null;
private lastHighlightedOwnerSmallId: number | null = null;
// Compute passes // Compute passes
private computePasses: ComputePass[] = []; private computePasses: ComputePass[] = [];
@@ -126,7 +135,12 @@ export class WorkerTerritoryRenderer {
// Create compute passes // Create compute passes
this.terrainComputePass = new TerrainComputePass(); this.terrainComputePass = new TerrainComputePass();
void this.terrainComputePass.setShader(this.terrainShaderPath); void this.terrainComputePass
.setShader(this.terrainShaderPath)
.then(() => {
this.computeTerrainImmediate();
})
.catch(() => {});
this.stateUpdatePass = new StateUpdatePass(); this.stateUpdatePass = new StateUpdatePass();
this.defendedStrengthFullPass = new DefendedStrengthFullPass(); this.defendedStrengthFullPass = new DefendedStrengthFullPass();
this.defendedStrengthPass = new DefendedStrengthPass(); this.defendedStrengthPass = new DefendedStrengthPass();
@@ -197,7 +211,11 @@ export class WorkerTerritoryRenderer {
this.resources?.markPaletteDirty(); this.resources?.markPaletteDirty();
this.resources?.invalidateHistory(); this.resources?.invalidateHistory();
} }
return defensePostsDirty || rosterDirty || playersDirty; const didWork = defensePostsDirty || rosterDirty || playersDirty;
if (didWork) {
this.frameDirty = true;
}
return didWork;
} }
/** /**
@@ -255,6 +273,16 @@ export class WorkerTerritoryRenderer {
const nextWidth = Math.max(1, Math.floor(width)); const nextWidth = Math.max(1, Math.floor(width));
const nextHeight = Math.max(1, Math.floor(height)); const nextHeight = Math.max(1, Math.floor(height));
if (
nextWidth === this.lastViewWidth &&
nextHeight === this.lastViewHeight
) {
return;
}
this.lastViewWidth = nextWidth;
this.lastViewHeight = nextHeight;
this.frameDirty = true;
let sizeChanged = true; let sizeChanged = true;
if (this.canvas) { if (this.canvas) {
sizeChanged = sizeChanged =
@@ -283,6 +311,17 @@ export class WorkerTerritoryRenderer {
if (!this.resources) { if (!this.resources) {
return; return;
} }
if (
scale === this.lastViewScale &&
offsetX === this.lastViewOffsetX &&
offsetY === this.lastViewOffsetY
) {
return;
}
this.lastViewScale = scale;
this.lastViewOffsetX = offsetX;
this.lastViewOffsetY = offsetY;
this.frameDirty = true;
this.resources.setViewTransform(scale, offsetX, offsetY); this.resources.setViewTransform(scale, offsetX, offsetY);
} }
@@ -290,6 +329,11 @@ export class WorkerTerritoryRenderer {
if (!this.resources) { if (!this.resources) {
return; return;
} }
if (enabled === this.lastAlternativeViewEnabled) {
return;
}
this.lastAlternativeViewEnabled = enabled;
this.frameDirty = true;
this.resources.setAlternativeView(enabled); this.resources.setAlternativeView(enabled);
} }
@@ -297,6 +341,11 @@ export class WorkerTerritoryRenderer {
if (!this.resources) { if (!this.resources) {
return; return;
} }
if (ownerSmallId === this.lastHighlightedOwnerSmallId) {
return;
}
this.lastHighlightedOwnerSmallId = ownerSmallId;
this.frameDirty = true;
this.resources.setHighlightedOwnerId(ownerSmallId); this.resources.setHighlightedOwnerId(ownerSmallId);
} }
@@ -305,6 +354,7 @@ export class WorkerTerritoryRenderer {
this.gameViewAdapter?.setPatternsEnabled(enabled); this.gameViewAdapter?.setPatternsEnabled(enabled);
this.resources?.markPaletteDirty(); this.resources?.markPaletteDirty();
this.resources?.invalidateHistory(); this.resources?.invalidateHistory();
this.frameDirty = true;
} }
setTerritoryShader(shaderPath: string): void { setTerritoryShader(shaderPath: string): void {
@@ -313,6 +363,7 @@ export class WorkerTerritoryRenderer {
void this.territoryRenderPass.setShader(shaderPath); void this.territoryRenderPass.setShader(shaderPath);
} }
this.resources?.invalidateHistory(); this.resources?.invalidateHistory();
this.frameDirty = true;
} }
setTerrainShader(shaderPath: string): void { setTerrainShader(shaderPath: string): void {
@@ -323,6 +374,7 @@ export class WorkerTerritoryRenderer {
void this.terrainComputePass.setShader(shaderPath).then(() => { void this.terrainComputePass.setShader(shaderPath).then(() => {
this.refreshTerrain(); this.refreshTerrain();
}); });
this.frameDirty = true;
} }
setTerritoryShaderParams( setTerritoryShaderParams(
@@ -342,6 +394,7 @@ export class WorkerTerritoryRenderer {
this.territoryShaderParams1, this.territoryShaderParams1,
); );
this.resources.invalidateHistory(); this.resources.invalidateHistory();
this.frameDirty = true;
} }
setTerrainShaderParams( setTerrainShaderParams(
@@ -361,6 +414,7 @@ export class WorkerTerritoryRenderer {
this.terrainShaderParams1, this.terrainShaderParams1,
); );
this.refreshTerrain(); this.refreshTerrain();
this.frameDirty = true;
} }
setPreSmoothing( setPreSmoothing(
@@ -376,6 +430,7 @@ export class WorkerTerritoryRenderer {
this.preSmoothingParams0[i] = Number(params0[i] ?? 0); this.preSmoothingParams0[i] = Number(params0[i] ?? 0);
} }
this.applyPreSmoothingConfig(); this.applyPreSmoothingConfig();
this.frameDirty = true;
} }
setPostSmoothing( setPostSmoothing(
@@ -391,6 +446,7 @@ export class WorkerTerritoryRenderer {
this.postSmoothingParams0[i] = Number(params0[i] ?? 0); this.postSmoothingParams0[i] = Number(params0[i] ?? 0);
} }
this.applyPostSmoothingConfig(); this.applyPostSmoothingConfig();
this.frameDirty = true;
} }
private applyPreSmoothingConfig(): void { private applyPreSmoothingConfig(): void {
@@ -427,6 +483,19 @@ export class WorkerTerritoryRenderer {
1, 1,
this.device.canvasFormat, this.device.canvasFormat,
); );
const w =
(this.canvas?.width ?? this.lastViewWidth ?? 0) > 0
? (this.canvas?.width ?? this.lastViewWidth)
: 1;
const h =
(this.canvas?.height ?? this.lastViewHeight ?? 0) > 0
? (this.canvas?.height ?? this.lastViewHeight)
: 1;
this.resources.ensurePostSmoothingTextures(
w,
h,
this.device.canvasFormat,
);
} }
} else { } else {
this.temporalResolvePass.setEnabled(false); this.temporalResolvePass.setEnabled(false);
@@ -456,6 +525,7 @@ export class WorkerTerritoryRenderer {
this.resources.invalidateHistory(); this.resources.invalidateHistory();
this.terrainComputePass?.markDirty(); this.terrainComputePass?.markDirty();
this.frameDirty = true;
} }
refreshPalette(): void { refreshPalette(): void {
@@ -463,14 +533,17 @@ export class WorkerTerritoryRenderer {
return; return;
} }
this.resources.markPaletteDirty(); this.resources.markPaletteDirty();
this.frameDirty = true;
} }
markRelationsDirty(): void { markRelationsDirty(): void {
this.resources?.markRelationsDirty(); this.resources?.markRelationsDirty();
this.frameDirty = true;
} }
markRelationsPairDirty(aSmallId: number, bSmallId: number): void { markRelationsPairDirty(aSmallId: number, bSmallId: number): void {
this.resources?.markRelationsPairDirty(aSmallId, bSmallId); this.resources?.markRelationsPairDirty(aSmallId, bSmallId);
this.frameDirty = true;
} }
setPaletteFromBytes( setPaletteFromBytes(
@@ -484,6 +557,7 @@ export class WorkerTerritoryRenderer {
} }
this.resources.setPaletteOverride(paletteWidth, maxSmallId, row0, row1); this.resources.setPaletteOverride(paletteWidth, maxSmallId, row0, row1);
this.resources.invalidateHistory(); this.resources.invalidateHistory();
this.frameDirty = true;
} }
markDefensePostsDirty(): void { markDefensePostsDirty(): void {
@@ -491,6 +565,7 @@ export class WorkerTerritoryRenderer {
return; return;
} }
this.resources.markDefensePostsDirty(); this.resources.markDefensePostsDirty();
this.frameDirty = true;
} }
refreshTerrain(): void { refreshTerrain(): void {
@@ -502,6 +577,7 @@ export class WorkerTerritoryRenderer {
this.terrainComputePass.markDirty(); this.terrainComputePass.markDirty();
this.computeTerrainImmediate(); this.computeTerrainImmediate();
} }
this.frameDirty = true;
} }
dispose(): void { dispose(): void {
@@ -615,6 +691,7 @@ export class WorkerTerritoryRenderer {
} }
this.device.device.queue.submit([encoder.finish()]); this.device.device.queue.submit([encoder.finish()]);
this.frameDirty = true;
return true; return true;
} }
@@ -669,27 +746,23 @@ export class WorkerTerritoryRenderer {
* Render one frame. * Render one frame.
* Runs render passes to draw to the canvas. * Runs render passes to draw to the canvas.
*/ */
render(onGetTextureMs?: (ms: number) => void): void { render(onGetTextureMs?: (ms: number) => void): boolean {
if ( if (
!this.ready || !this.ready ||
!this.device || !this.device ||
!this.resources || !this.resources ||
!this.territoryRenderPass !this.territoryRenderPass
) { ) {
return; return false;
}
if (!this.frameDirty && !this.postSmoothingEnabled) {
return false;
} }
const nowSec = performance.now() / 1000; const nowSec = performance.now() / 1000;
this.resources.writeTemporalUniformBuffer(nowSec); this.resources.writeTemporalUniformBuffer(nowSec);
// If terrain needs recomputation, trigger it asynchronously
if (this.terrainComputePass?.needsUpdate()) {
this.resources.uploadTerrainParams();
const computeEncoder = this.device.device.createCommandEncoder();
this.terrainComputePass.execute(computeEncoder, this.resources);
this.device.device.queue.submit([computeEncoder.finish()]);
}
const encoder = this.device.device.createCommandEncoder(); const encoder = this.device.device.createCommandEncoder();
const getTexStart = performance.now(); const getTexStart = performance.now();
const swapchainView = this.device.context.getCurrentTexture().createView(); const swapchainView = this.device.context.getCurrentTexture().createView();
@@ -748,6 +821,10 @@ export class WorkerTerritoryRenderer {
} }
this.device.device.queue.submit([encoder.finish()]); this.device.device.queue.submit([encoder.finish()]);
if (!this.postSmoothingEnabled) {
this.frameDirty = false;
}
return true;
} }
async renderAsync(): Promise<{ async renderAsync(): Promise<{
@@ -762,30 +839,35 @@ export class WorkerTerritoryRenderer {
return null; return null;
} }
let waitPrevGpuMs = 0; const waitPrevGpuMs = 0;
let cpuMs = 0; let cpuMs = 0;
let getTextureMs = 0; let getTextureMs = 0;
let gpuWaitMs = 0; const gpuWaitMs = 0;
let waitPrevGpuTimedOut = false; const waitPrevGpuTimedOut = false;
let gpuWaitTimedOut = false; const gpuWaitTimedOut = false;
if (this.gpuWaitEnabled && this.lastGpuWork) { // Keep render_frame handlers cheap: do not await GPU progress here.
const t0 = performance.now(); // Backpressure is handled on the main thread (one in-flight render).
const r = await this.awaitGpuWork(this.lastGpuWork); this.lastGpuWork = null;
waitPrevGpuTimedOut = r.timedOut;
if (r.timedOut) {
this.gpuWaitEnabled = false;
}
waitPrevGpuMs = performance.now() - t0;
this.lastGpuWork = null;
}
const cpuStart = performance.now(); const cpuStart = performance.now();
this.render((ms) => { const submitted = this.render((ms) => {
getTextureMs = ms; getTextureMs = ms;
}); });
cpuMs = performance.now() - cpuStart; cpuMs = performance.now() - cpuStart;
if (!submitted) {
this.lastGpuWork = null;
return {
waitPrevGpuMs,
cpuMs,
getTextureMs,
gpuWaitMs,
waitPrevGpuTimedOut,
gpuWaitTimedOut,
};
}
const q: any = this.device.device.queue as any; const q: any = this.device.device.queue as any;
if (typeof q?.onSubmittedWorkDone !== "function") { if (typeof q?.onSubmittedWorkDone !== "function") {
this.lastGpuWork = null; this.lastGpuWork = null;
@@ -799,20 +881,8 @@ export class WorkerTerritoryRenderer {
}; };
} }
const gpuStart = performance.now();
const p = q.onSubmittedWorkDone() as Promise<void>; const p = q.onSubmittedWorkDone() as Promise<void>;
this.lastGpuWork = p.catch(() => {}); this.lastGpuWork = p.catch(() => {});
if (this.gpuWaitEnabled) {
const r = await this.awaitGpuWork(this.lastGpuWork);
gpuWaitTimedOut = r.timedOut;
if (r.timedOut) {
this.gpuWaitEnabled = false;
this.lastGpuWork = null;
} else {
this.lastGpuWork = null;
}
gpuWaitMs = performance.now() - gpuStart;
}
return { return {
waitPrevGpuMs, waitPrevGpuMs,