• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / luma.gl / 17155211498

22 Aug 2025 12:25PM UTC coverage: 75.081% (-0.02%) from 75.101%
17155211498

push

github

web-flow
fix: Various framebuffer/texture resize related fixes (#2436)

2146 of 2814 branches covered (76.26%)

Branch coverage included in aggregate %.

14 of 65 new or added lines in 4 files covered. (21.54%)

1 existing line in 1 file now uncovered.

27905 of 37211 relevant lines covered (74.99%)

57.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.57
/modules/core/src/adapter/resources/framebuffer.ts
1
// luma.gl
1✔
2
// SPDX-License-Identifier: MIT
1✔
3
// Copyright (c) vis.gl contributors
1✔
4

1✔
5
import type {
1✔
6
  TextureFormatColor,
1✔
7
  TextureFormatDepthStencil,
1✔
8
  TextureFormat
1✔
9
} from '../../shadertypes/textures/texture-formats';
1✔
10
import type {Device} from '../device';
1✔
11
import {Resource, ResourceProps} from './resource';
1✔
12
import {Texture} from './texture';
1✔
13
import {TextureView} from './texture-view';
1✔
14
import {log} from '../../utils/log';
1✔
15

1✔
16
export type FramebufferProps = ResourceProps & {
1✔
17
  width?: number;
1✔
18
  height?: number;
1✔
19
  colorAttachments?: (TextureView | Texture | TextureFormatColor)[];
1✔
20
  depthStencilAttachment?: (TextureView | Texture | TextureFormatDepthStencil) | null;
1✔
21
};
1✔
22

1✔
23
/**
1✔
24
 * Create new textures with correct size for all attachments.
1✔
25
 * @note resize() destroys existing textures (if size has changed).
1✔
26
 */
1✔
27
export abstract class Framebuffer extends Resource<FramebufferProps> {
1✔
28
  override get [Symbol.toStringTag](): string {
1✔
29
    return 'Framebuffer';
123✔
30
  }
123✔
31

1✔
32
  /** Width of all attachments in this framebuffer */
1✔
33
  width: number;
1✔
34
  /** Height of all attachments in this framebuffer */
1✔
35
  height: number;
1✔
36
  /** Color attachments */
1✔
37
  abstract colorAttachments: TextureView[];
1✔
38
  /** Depth-stencil attachment, if provided */
1✔
39
  abstract depthStencilAttachment: TextureView | null;
1✔
40

1✔
41
  constructor(device: Device, props: FramebufferProps = {}) {
1✔
42
    super(device, props, Framebuffer.defaultProps);
60✔
43
    this.width = this.props.width;
60✔
44
    this.height = this.props.height;
60✔
45
  }
60✔
46

1✔
47
  /**
1✔
48
   * Create a copy of this framebuffer with new attached textures, with same props but of the specified size.
1✔
49
   * @note Does not copy contents of the attached textures.
1✔
50
   */
1✔
51
  clone(size?: {width: number; height: number}): Framebuffer {
1✔
52
    const colorAttachments = this.colorAttachments.map(colorAttachment =>
×
53
      colorAttachment.texture.clone(size)
×
54
    );
×
55

×
56
    const depthStencilAttachment =
×
57
      this.depthStencilAttachment && this.depthStencilAttachment.texture.clone(size);
×
58

×
NEW
59
    return this.device.createFramebuffer({
×
NEW
60
      ...this.props,
×
NEW
61
      ...size,
×
NEW
62
      colorAttachments,
×
NEW
63
      depthStencilAttachment
×
NEW
64
    });
×
UNCOV
65
  }
×
66

1✔
67
  /**
1✔
68
   * Resizes all attachments
1✔
69
   * @note resize() destroys existing textures (if size has changed).
1✔
70
   * @deprecated Use framebuffer.clone()
1✔
71
   */
1✔
72
  resize(size: {width: number; height: number}): void;
1✔
73
  resize(size: [width: number, height: number]): void;
1✔
74
  resize(): void;
1✔
75
  resize(size?: {width: number; height: number} | [width: number, height: number]): void {
1✔
76
    let updateSize: boolean = !size;
6✔
77
    if (size) {
6✔
78
      const [width, height] = Array.isArray(size) ? size : [size.width, size.height];
6!
79
      updateSize = updateSize || height !== this.height || width !== this.width;
6✔
80
      this.width = width;
6✔
81
      this.height = height;
6✔
82
    }
6✔
83
    if (updateSize) {
6✔
84
      log.log(2, `Resizing framebuffer ${this.id} to ${this.width}x${this.height}`)();
3✔
85
      this.resizeAttachments(this.width, this.height);
3✔
86
    }
3✔
87
  }
6✔
88

1✔
89
  /** Auto creates any textures */
1✔
90
  protected autoCreateAttachmentTextures(): void {
1✔
91
    if (this.props.colorAttachments.length === 0 && !this.props.depthStencilAttachment) {
59✔
92
      throw new Error('Framebuffer has noattachments');
3✔
93
    }
3✔
94

56✔
95
    this.colorAttachments = this.props.colorAttachments.map((attachment, index) => {
56✔
96
      if (typeof attachment === 'string') {
56✔
97
        const texture = this.createColorTexture(attachment, index);
11✔
98
        this.attachResource(texture);
11✔
99
        return texture.view;
11✔
100
      }
11✔
101
      if (attachment instanceof Texture) {
45✔
102
        return attachment.view;
45✔
103
      }
45!
104
      return attachment;
×
105
    });
56✔
106

56✔
107
    const attachment = this.props.depthStencilAttachment;
56✔
108
    if (attachment) {
59✔
109
      if (typeof attachment === 'string') {
4✔
110
        const texture = this.createDepthStencilTexture(attachment);
4✔
111
        this.attachResource(texture);
4✔
112
        this.depthStencilAttachment = texture.view;
4✔
113
      } else if (attachment instanceof Texture) {
4!
114
        this.depthStencilAttachment = attachment.view;
×
115
      } else {
×
116
        this.depthStencilAttachment = attachment;
×
117
      }
×
118
    }
4✔
119
  }
59✔
120

1✔
121
  /** Create a color texture */
1✔
122
  protected createColorTexture(format: TextureFormat, index: number): Texture {
1✔
123
    return this.device.createTexture({
11✔
124
      id: `${this.id}-color-attachment-${index}`,
11✔
125
      usage: Texture.RENDER_ATTACHMENT,
11✔
126
      format,
11✔
127
      width: this.width,
11✔
128
      height: this.height,
11✔
129
      // TODO deprecated? - luma.gl v8 compatibility
11✔
130
      sampler: {
11✔
131
        magFilter: 'linear',
11✔
132
        minFilter: 'linear'
11✔
133
      }
11✔
134
    });
11✔
135
  }
11✔
136

1✔
137
  /** Create depth stencil texture */
1✔
138
  protected createDepthStencilTexture(format: TextureFormat): Texture {
1✔
139
    return this.device.createTexture({
4✔
140
      id: `${this.id}-depth-stencil-attachment`,
4✔
141
      usage: Texture.RENDER_ATTACHMENT,
4✔
142
      format,
4✔
143
      width: this.width,
4✔
144
      height: this.height
4✔
145
    });
4✔
146
  }
4✔
147

1✔
148
  /**
1✔
149
   * Default implementation of resize
1✔
150
   * Creates new textures with correct size for all attachments.
1✔
151
   * and destroys existing textures if owned
1✔
152
   */
1✔
153
  protected resizeAttachments(width: number, height: number): void {
1✔
154
    for (let i = 0; i < this.colorAttachments.length; ++i) {
3✔
155
      if (this.colorAttachments[i]) {
3✔
156
        const resizedTexture = this.colorAttachments[i].texture.clone({
3✔
157
          width,
3✔
158
          height
3✔
159
        });
3✔
160
        this.destroyAttachedResource(this.colorAttachments[i]);
3✔
161
        this.colorAttachments[i] = resizedTexture.view;
3✔
162
        this.attachResource(resizedTexture.view);
3✔
163
      }
3✔
164
    }
3✔
165

3✔
166
    if (this.depthStencilAttachment) {
3✔
167
      const resizedTexture = this.depthStencilAttachment.texture.clone({
3✔
168
        width,
3✔
169
        height
3✔
170
      });
3✔
171
      this.destroyAttachedResource(this.depthStencilAttachment);
3✔
172
      this.depthStencilAttachment = resizedTexture.view;
3✔
173
      this.attachResource(resizedTexture);
3✔
174
    }
3✔
175

3✔
176
    this.updateAttachments();
3✔
177
  }
3✔
178

1✔
179
  /** Implementation is expected to update any underlying binding (WebGL framebuffer attachment) */
1✔
180
  protected abstract updateAttachments(): void;
1✔
181

1✔
182
  static override defaultProps: Required<FramebufferProps> = {
1✔
183
    ...Resource.defaultProps,
1✔
184
    width: 1,
1✔
185
    height: 1,
1✔
186
    colorAttachments: [], // ['rgba8unorm'],
1✔
187
    depthStencilAttachment: null // 'depth24plus-stencil8'
1✔
188
  };
1✔
189
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc