Skip to content

Commit

Permalink
Merge pull request #228855 from microsoft/tyriar/228834
Browse files Browse the repository at this point in the history
Expand object collection buffer when capacity hit
  • Loading branch information
Tyriar committed Sep 17, 2024
2 parents b94e29f + 6c478a6 commit 5a02744
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 17 deletions.
18 changes: 17 additions & 1 deletion src/vs/editor/browser/gpu/objectCollectionBuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export interface IObjectCollectionBuffer<T extends ObjectCollectionBufferPropert
*/
readonly onDidChange: Event<void>;

/**
* Fires when the buffer is recreated.
*/
readonly onDidChangeBuffer: Event<void>;

/**
* Creates an entry in the collection. This will return a managed object that can be modified
* which will update the underlying buffer.
Expand Down Expand Up @@ -96,6 +101,8 @@ class ObjectCollectionBuffer<T extends ObjectCollectionBufferPropertySpec[]> ext

private readonly _onDidChange = this._register(new Emitter<void>());
readonly onDidChange = this._onDidChange.event;
private readonly _onDidChangeBuffer = this._register(new Emitter<void>());
readonly onDidChangeBuffer = this._onDidChangeBuffer.event;

constructor(
public propertySpecs: T,
Expand All @@ -118,7 +125,8 @@ class ObjectCollectionBuffer<T extends ObjectCollectionBufferPropertySpec[]> ext

createEntry(data: ObjectCollectionPropertyValues<T>): IObjectCollectionBufferEntry<T> {
if (this._entries.size === this.capacity) {
throw new Error(`Cannot create more entries ObjectCollectionBuffer entries (capacity=${this.capacity})`);
this._expandBuffer();
this._onDidChangeBuffer.fire();
}

const value = new ObjectCollectionBufferEntry(this.view, this._propertySpecsMap, this._dirtyTracker, this._entries.size, data);
Expand All @@ -143,6 +151,14 @@ class ObjectCollectionBuffer<T extends ObjectCollectionBufferPropertySpec[]> ext
}));
return value;
}

private _expandBuffer() {
this.capacity *= 2;
const newView = new Float32Array(this.capacity * this._entrySize);
newView.set(this.view);
this.view = newView;
this.buffer = this.view.buffer;
}
}

class ObjectCollectionBufferEntry<T extends ObjectCollectionBufferPropertySpec[]> extends Disposable implements IObjectCollectionBufferEntry<T> {
Expand Down
42 changes: 28 additions & 14 deletions src/vs/editor/browser/gpu/rectangleRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*--------------------------------------------------------------------------------------------*/

import { getActiveWindow } from '../../../base/browser/dom.js';
import { Event } from '../../../base/common/event.js';
import { IReference, MutableDisposable } from '../../../base/common/lifecycle.js';
import { EditorOption } from '../../common/config/editorOptions.js';
import { ViewEventHandler } from '../../common/viewEventHandler.js';
import type { ViewScrollChangedEvent } from '../../common/viewEvents.js';
Expand Down Expand Up @@ -34,7 +36,7 @@ export class RectangleRenderer extends ViewEventHandler {
private _pipeline!: GPURenderPipeline;

private _vertexBuffer!: GPUBuffer;
private _shapeBindBuffer!: GPUBuffer;
private readonly _shapeBindBuffer: MutableDisposable<IReference<GPUBuffer>> = this._register(new MutableDisposable());

private _scrollOffsetBindBuffer!: GPUBuffer;
private _scrollOffsetValueBuffer!: Float32Array;
Expand Down Expand Up @@ -141,11 +143,20 @@ export class RectangleRenderer extends ViewEventHandler {

// #region Storage buffers

this._shapeBindBuffer = this._register(GPULifecycle.createBuffer(this._device, {
label: 'Monaco rectangle renderer shape buffer',
size: this._shapeCollection.buffer.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
})).object;
const createShapeBindBuffer = () => {
return GPULifecycle.createBuffer(this._device, {
label: 'Monaco rectangle renderer shape buffer',
size: this._shapeCollection.buffer.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
};
this._shapeBindBuffer.value = createShapeBindBuffer();
this._register(Event.runAndSubscribe(this._shapeCollection.onDidChangeBuffer, () => {
this._shapeBindBuffer.value = createShapeBindBuffer();
if (this._pipeline) {
this._updateBindGroup(this._pipeline, layoutInfoUniformBuffer);
}
}));

// #endregion Storage buffers

Expand Down Expand Up @@ -208,23 +219,26 @@ export class RectangleRenderer extends ViewEventHandler {

// #region Bind group

this._updateBindGroup(this._pipeline, layoutInfoUniformBuffer);

// endregion Bind group

this._initialized = true;
}

private _updateBindGroup(pipeline: GPURenderPipeline, layoutInfoUniformBuffer: GPUBuffer) {
this._bindGroup = this._device.createBindGroup({
label: 'Monaco rectangle renderer bind group',
layout: this._pipeline.getBindGroupLayout(0),
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: RectangleRendererBindingId.Shapes, resource: { buffer: this._shapeBindBuffer } },
{ binding: RectangleRendererBindingId.Shapes, resource: { buffer: this._shapeBindBuffer.value!.object } },
{ binding: RectangleRendererBindingId.LayoutInfoUniform, resource: { buffer: layoutInfoUniformBuffer } },
{ binding: RectangleRendererBindingId.ScrollOffset, resource: { buffer: this._scrollOffsetBindBuffer } },
],
});

// endregion Bind group

this._initialized = true;
}

register(x: number, y: number, width: number, height: number, red: number, green: number, blue: number, alpha: number): IObjectCollectionBufferEntry<RectangleRendererEntrySpec> {
// TODO: Expand buffer if needed
return this._shapeCollection.createEntry({ x, y, width, height, red, green, blue, alpha });
}

Expand All @@ -240,7 +254,7 @@ export class RectangleRenderer extends ViewEventHandler {
private _update() {
const shapes = this._shapeCollection;
if (shapes.dirtyTracker.isDirty) {
this._device.queue.writeBuffer(this._shapeBindBuffer, 0, shapes.buffer, shapes.dirtyTracker.dataOffset, shapes.dirtyTracker.dirtySize! * shapes.view.BYTES_PER_ELEMENT);
this._device.queue.writeBuffer(this._shapeBindBuffer.value!.object, 0, shapes.buffer, shapes.dirtyTracker.dataOffset, shapes.dirtyTracker.dirtySize! * shapes.view.BYTES_PER_ELEMENT);
shapes.dirtyTracker.clear();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { deepStrictEqual, strictEqual, throws } from 'assert';
import { deepStrictEqual, strictEqual } from 'assert';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { createObjectCollectionBuffer, type IObjectCollectionBuffer } from '../../../../browser/gpu/objectCollectionBuffer.js';

Expand Down Expand Up @@ -35,7 +35,11 @@ suite('ObjectCollectionBuffer', () => {
{ name: 'b' },
] as const, 1));
store.add(buffer.createEntry({ a: 1, b: 2 }));
throws(() => buffer.createEntry({ a: 3, b: 4 }));
strictEqual(buffer.entryCount, 1);
strictEqual(buffer.buffer.byteLength, 8);
buffer.createEntry({ a: 3, b: 4 });
strictEqual(buffer.entryCount, 2);
strictEqual(buffer.buffer.byteLength, 16);
});

test('dispose entry', () => {
Expand Down

0 comments on commit 5a02744

Please sign in to comment.