mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 17:53:29 +00:00
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:
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user