Skip to content

Commit df19625

Browse files
authored
feat: Separate API for writing from copying. (#615)
1 parent d487ac6 commit df19625

File tree

9 files changed

+363
-232
lines changed

9 files changed

+363
-232
lines changed

packages/typegpu/src/core/buffer/buffer.ts

Lines changed: 34 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,21 @@ export interface TgpuBuffer<TData extends AnyData> extends TgpuNamable {
4040
readonly label: string | undefined;
4141

4242
readonly buffer: GPUBuffer;
43-
readonly device: GPUDevice;
4443
readonly destroyed: boolean;
4544

4645
$usage<T extends ('uniform' | 'storage' | 'vertex')[]>(
4746
...usages: T
4847
): this & UnionToIntersection<LiteralToUsageType<T[number]>>;
4948
$addFlags(flags: GPUBufferUsageFlags): this;
50-
$device(device: GPUDevice): this;
5149

52-
write(data: Infer<TData> | TgpuBuffer<TData>): void;
50+
write(data: Infer<TData>): void;
51+
copyFrom(srcBuffer: TgpuBuffer<TData>): void;
5352
read(): Promise<Infer<TData>>;
5453
destroy(): void;
5554
}
5655

57-
export function createBufferImpl<TData extends AnyData>(
58-
group: ExperimentalTgpuRoot | undefined,
56+
export function INTERNAL_createBuffer<TData extends AnyData>(
57+
group: ExperimentalTgpuRoot,
5958
typeSchema: TData,
6059
initialOrBuffer?: Infer<TData> | GPUBuffer,
6160
): TgpuBuffer<TData> {
@@ -88,8 +87,8 @@ class TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {
8887
public readonly resourceType = 'buffer';
8988
public flags: GPUBufferUsageFlags =
9089
GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC;
91-
private _device: GPUDevice | null = null;
9290
private _buffer: GPUBuffer | null = null;
91+
private _ownBuffer: boolean;
9392
private _destroyed = false;
9493

9594
private _label: string | undefined;
@@ -100,13 +99,15 @@ class TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {
10099
public usableAsVertex = false;
101100

102101
constructor(
103-
private readonly _group: ExperimentalTgpuRoot | undefined,
102+
private readonly _group: ExperimentalTgpuRoot,
104103
public readonly dataType: TData,
105104
public readonly initialOrBuffer?: Infer<TData> | GPUBuffer | undefined,
106105
) {
107106
if (isGPUBuffer(initialOrBuffer)) {
107+
this._ownBuffer = false;
108108
this._buffer = initialOrBuffer;
109109
} else {
110+
this._ownBuffer = true;
110111
this.initial = initialOrBuffer;
111112
}
112113
}
@@ -116,18 +117,14 @@ class TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {
116117
}
117118

118119
get buffer() {
119-
if (!this._device) {
120-
throw new Error(
121-
'Create this buffer using `root.createBuffer` instead of `tgpu.createBuffer`.',
122-
);
123-
}
120+
const device = this._group.device;
124121

125122
if (this._destroyed) {
126123
throw new Error('This buffer has been destroyed');
127124
}
128125

129126
if (!this._buffer) {
130-
this._buffer = this._device.createBuffer({
127+
this._buffer = device.createBuffer({
131128
size: sizeOf(this.dataType),
132129
usage: this.flags,
133130
mappedAtCreation: !!this.initial,
@@ -144,15 +141,6 @@ class TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {
144141
return this._buffer;
145142
}
146143

147-
get device() {
148-
if (!this._device) {
149-
throw new Error(
150-
'This buffer has not been assigned a device. Use .$device(device) to assign a device',
151-
);
152-
}
153-
return this._device;
154-
}
155-
156144
get destroyed() {
157145
return this._destroyed;
158146
}
@@ -185,56 +173,42 @@ class TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {
185173
return this;
186174
}
187175

188-
$device(device: GPUDevice) {
189-
this._device = device;
190-
return this;
191-
}
192-
193-
write(dataOrBuffer: Infer<TData> | TgpuBuffer<TData>): void {
176+
write(data: Infer<TData>): void {
194177
const gpuBuffer = this.buffer;
195-
const device = this.device;
178+
const device = this._group.device;
196179

197180
if (gpuBuffer.mapState === 'mapped') {
198181
const mapped = gpuBuffer.getMappedRange();
199-
if (isBuffer(dataOrBuffer)) {
200-
throw new Error('Cannot copy to a mapped buffer.');
201-
}
202-
writeData(new BufferWriter(mapped), this.dataType, dataOrBuffer);
182+
writeData(new BufferWriter(mapped), this.dataType, data);
203183
return;
204184
}
205185

206186
const size = sizeOf(this.dataType);
207-
if (isBuffer(dataOrBuffer)) {
208-
const sourceBuffer = dataOrBuffer.buffer;
209-
210-
if (this._group) {
211-
const encoder = this._group.commandEncoder;
212-
encoder.copyBufferToBuffer(sourceBuffer, 0, gpuBuffer, 0, size);
213-
} else {
214-
const commandEncoder = device.createCommandEncoder();
215-
commandEncoder.copyBufferToBuffer(sourceBuffer, 0, gpuBuffer, 0, size);
216-
device.queue.submit([commandEncoder.finish()]);
217-
}
218-
} else {
219-
if (this._group) {
220-
// Flushing any commands yet to be encoded.
221-
this._group.flush();
222-
}
223187

224-
const hostBuffer = new ArrayBuffer(size);
225-
writeData(new BufferWriter(hostBuffer), this.dataType, dataOrBuffer);
226-
device.queue.writeBuffer(gpuBuffer, 0, hostBuffer, 0, size);
188+
// Flushing any commands yet to be encoded.
189+
this._group.flush();
190+
191+
const hostBuffer = new ArrayBuffer(size);
192+
writeData(new BufferWriter(hostBuffer), this.dataType, data);
193+
device.queue.writeBuffer(gpuBuffer, 0, hostBuffer, 0, size);
194+
}
195+
196+
copyFrom(srcBuffer: TgpuBuffer<TData>): void {
197+
if (this.buffer.mapState === 'mapped') {
198+
throw new Error('Cannot copy to a mapped buffer.');
227199
}
200+
201+
const size = sizeOf(this.dataType);
202+
const encoder = this._group.commandEncoder;
203+
encoder.copyBufferToBuffer(srcBuffer.buffer, 0, this.buffer, 0, size);
228204
}
229205

230206
async read(): Promise<Infer<TData>> {
231-
if (this._group) {
232-
// Flushing any commands yet to be encoded.
233-
this._group.flush();
234-
}
207+
// Flushing any commands yet to be encoded.
208+
this._group.flush();
235209

236210
const gpuBuffer = this.buffer;
237-
const device = this.device;
211+
const device = this._group.device;
238212

239213
if (gpuBuffer.mapState === 'mapped') {
240214
const mapped = gpuBuffer.getMappedRange();
@@ -283,7 +257,9 @@ class TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {
283257
return;
284258
}
285259
this._destroyed = true;
286-
this._buffer?.destroy();
260+
if (this._ownBuffer) {
261+
this._buffer?.destroy();
262+
}
287263
}
288264

289265
toString(): string {

packages/typegpu/src/core/root/init.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import {
2222
isBindGroupLayout,
2323
} from '../../tgpuBindGroupLayout';
2424
import type { TgpuSlot } from '../../types';
25-
import { type TgpuBuffer, createBufferImpl, isBuffer } from '../buffer/buffer';
25+
import {
26+
INTERNAL_createBuffer,
27+
type TgpuBuffer,
28+
isBuffer,
29+
} from '../buffer/buffer';
2630
import type { IOLayout } from '../function/fnTypes';
2731
import type { TgpuComputeFn } from '../function/tgpuComputeFn';
2832
import type { TgpuFragmentFn } from '../function/tgpuFragmentFn';
@@ -189,12 +193,8 @@ class TgpuRootImpl extends WithBindingImpl implements ExperimentalTgpuRoot {
189193
typeSchema: TData,
190194
initialOrBuffer?: Infer<TData> | GPUBuffer,
191195
): TgpuBuffer<TData> {
192-
const buffer = createBufferImpl(this, typeSchema, initialOrBuffer).$device(
193-
this.device,
194-
);
195-
196+
const buffer = INTERNAL_createBuffer(this, typeSchema, initialOrBuffer);
196197
this._disposables.push(buffer);
197-
198198
return buffer;
199199
}
200200

packages/typegpu/src/index.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
*/
44

55
import { init, initFromDevice } from './core/root/init';
6-
import { createBuffer } from './legacyBufferApi';
76
import { bindGroupLayout } from './tgpuBindGroupLayout';
8-
import { read, write } from './tgpuBufferUtils';
97

108
export const tgpu = {
119
/** @hidden @deprecated Use `'uniform'` string literal instead. */
@@ -19,13 +17,6 @@ export const tgpu = {
1917

2018
init,
2119
initFromDevice,
22-
23-
/** @hidden */
24-
createBuffer,
25-
/** @hidden */
26-
read,
27-
/** @hidden */
28-
write,
2920
};
3021
export default tgpu;
3122

packages/typegpu/src/legacyBufferApi.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

packages/typegpu/src/tgpuBufferUtils.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)