diff --git a/src/client/graphics/webgpu/TerritoryRenderer.ts b/src/client/graphics/webgpu/TerritoryRenderer.ts index 12861303c..f5910727c 100644 --- a/src/client/graphics/webgpu/TerritoryRenderer.ts +++ b/src/client/graphics/webgpu/TerritoryRenderer.ts @@ -316,18 +316,16 @@ export class TerritoryRenderer { // Upload palette if needed this.resources.uploadPalette(); - // Upload defense posts if needed (tracks if it was dirty before upload) - const wasDefensePostsDirty = (this.resources as any) - .needsDefensePostsUpload; + // Upload defense posts if needed this.resources.uploadDefensePosts(); // Initial state upload this.resources.uploadState(); // Check if we need to run compute passes - const numUpdates = this.stateUpdatePass - ? ((this.stateUpdatePass as any).pendingTiles?.size ?? 0) - : 0; + const hasStateUpdates = this.stateUpdatePass + ? this.stateUpdatePass.needsUpdate() + : false; const needsTerrainCompute = this.terrainComputePass ? this.terrainComputePass.needsUpdate() : false; @@ -341,14 +339,13 @@ export class TerritoryRenderer { // Use explicit boolean checks to satisfy linter (|| is correct for boolean OR) const shouldRebuildDefended = this.needsDefendedRebuild === true || - wasDefensePostsDirty === true || rangeChanged === true || countChanged === true || - (hasPosts && numUpdates > 0); + (hasPosts && hasStateUpdates === true); const needsCompute = needsTerrainCompute === true || - numUpdates > 0 || + hasStateUpdates === true || shouldRebuildDefended === true || this.needsDefendedHardClear === true; @@ -366,15 +363,13 @@ export class TerritoryRenderer { // Handle defended rebuild (before executing passes) if (shouldRebuildDefended) { - // Increment epoch for this rebuild - const epochBefore = this.resources.getDefendedEpoch(); - this.resources.incrementDefendedEpoch(); - const epochAfter = this.resources.getDefendedEpoch(); + // Hard-clear defended texture before restamping. This avoids relying on + // epoch-stamping for correctness and prevents transient mismatches where + // defended rendering disappears between rebuilds. + this.needsDefendedHardClear = true; - // If epoch wrapped, we need a hard clear - if (epochAfter === 0 || epochAfter < epochBefore) { - this.needsDefendedHardClear = true; - this.resources.incrementDefendedEpoch(); + if (this.defendedUpdatePass) { + this.defendedUpdatePass.markDirty(); } this.needsDefendedRebuild = false; diff --git a/src/client/graphics/webgpu/compute/DefendedUpdatePass.ts b/src/client/graphics/webgpu/compute/DefendedUpdatePass.ts index c68d1358b..abac77c7f 100644 --- a/src/client/graphics/webgpu/compute/DefendedUpdatePass.ts +++ b/src/client/graphics/webgpu/compute/DefendedUpdatePass.ts @@ -87,7 +87,7 @@ export class DefendedUpdatePass implements ComputePass { const oldBuffer = this.resources?.defensePostsBuffer; const bufferChanged = oldBuffer !== resources.defensePostsBuffer; - if (bufferChanged) { + if (bufferChanged || !this.bindGroup) { this.rebuildBindGroup(); }