Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/gpu/browser/gpuActions.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { getActiveWindow } from '../../../../base/browser/dom.js';
7
import { VSBuffer } from '../../../../base/common/buffer.js';
8
import { URI } from '../../../../base/common/uri.js';
9
import { localize, localize2 } from '../../../../nls.js';
10
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
11
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
12
import { IFileService } from '../../../../platform/files/common/files.js';
13
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
14
import { ILogService } from '../../../../platform/log/common/log.js';
15
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
16
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
17
import type { ICodeEditor } from '../../../browser/editorBrowser.js';
18
import { EditorAction, registerEditorAction, type ServicesAccessor } from '../../../browser/editorExtensions.js';
19
import { ensureNonNullable } from '../../../browser/gpu/gpuUtils.js';
20
import { GlyphRasterizer } from '../../../browser/gpu/raster/glyphRasterizer.js';
21
import { ViewGpuContext } from '../../../browser/gpu/viewGpuContext.js';
22
23
class DebugEditorGpuRendererAction extends EditorAction {
24
25
constructor() {
26
super({
27
id: 'editor.action.debugEditorGpuRenderer',
28
label: localize2('gpuDebug.label', "Developer: Debug Editor GPU Renderer"),
29
// TODO: Why doesn't `ContextKeyExpr.equals('config:editor.experimentalGpuAcceleration', 'on')` work?
30
precondition: ContextKeyExpr.true(),
31
});
32
}
33
34
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
35
const instantiationService = accessor.get(IInstantiationService);
36
const quickInputService = accessor.get(IQuickInputService);
37
const choice = await quickInputService.pick([
38
{
39
label: localize('logTextureAtlasStats.label', "Log Texture Atlas Stats"),
40
id: 'logTextureAtlasStats',
41
},
42
{
43
label: localize('saveTextureAtlas.label', "Save Texture Atlas"),
44
id: 'saveTextureAtlas',
45
},
46
{
47
label: localize('drawGlyph.label', "Draw Glyph"),
48
id: 'drawGlyph',
49
},
50
], { canPickMany: false });
51
if (!choice) {
52
return;
53
}
54
switch (choice.id) {
55
case 'logTextureAtlasStats':
56
instantiationService.invokeFunction(accessor => {
57
const logService = accessor.get(ILogService);
58
59
const atlas = ViewGpuContext.atlas;
60
if (!ViewGpuContext.atlas) {
61
logService.error('No texture atlas found');
62
return;
63
}
64
65
const stats = atlas.getStats();
66
logService.info(['Texture atlas stats', ...stats].join('\n\n'));
67
});
68
break;
69
case 'saveTextureAtlas':
70
instantiationService.invokeFunction(async accessor => {
71
const workspaceContextService = accessor.get(IWorkspaceContextService);
72
const fileService = accessor.get(IFileService);
73
const folders = workspaceContextService.getWorkspace().folders;
74
if (folders.length > 0) {
75
const atlas = ViewGpuContext.atlas;
76
const promises = [];
77
for (const [layerIndex, page] of atlas.pages.entries()) {
78
promises.push(...[
79
fileService.writeFile(
80
URI.joinPath(folders[0].uri, `textureAtlasPage${layerIndex}_actual.png`),
81
VSBuffer.wrap(new Uint8Array(await (await page.source.convertToBlob()).arrayBuffer()))
82
),
83
fileService.writeFile(
84
URI.joinPath(folders[0].uri, `textureAtlasPage${layerIndex}_usage.png`),
85
VSBuffer.wrap(new Uint8Array(await (await page.getUsagePreview()).arrayBuffer()))
86
),
87
]);
88
}
89
await Promise.all(promises);
90
}
91
});
92
break;
93
case 'drawGlyph':
94
instantiationService.invokeFunction(async accessor => {
95
const configurationService = accessor.get(IConfigurationService);
96
const fileService = accessor.get(IFileService);
97
const quickInputService = accessor.get(IQuickInputService);
98
const workspaceContextService = accessor.get(IWorkspaceContextService);
99
100
const folders = workspaceContextService.getWorkspace().folders;
101
if (folders.length === 0) {
102
return;
103
}
104
105
const atlas = ViewGpuContext.atlas;
106
const fontFamily = configurationService.getValue<string>('editor.fontFamily');
107
const fontSize = configurationService.getValue<number>('editor.fontSize');
108
const rasterizer = new GlyphRasterizer(fontSize, fontFamily, getActiveWindow().devicePixelRatio, ViewGpuContext.decorationStyleCache);
109
let chars = await quickInputService.input({
110
prompt: 'Enter a character to draw (prefix with 0x for code point))'
111
});
112
if (!chars) {
113
return;
114
}
115
const codePoint = chars.match(/0x(?<codePoint>[0-9a-f]+)/i)?.groups?.codePoint;
116
if (codePoint !== undefined) {
117
chars = String.fromCodePoint(parseInt(codePoint, 16));
118
}
119
const tokenMetadata = 0;
120
const charMetadata = 0;
121
const rasterizedGlyph = atlas.getGlyph(rasterizer, chars, tokenMetadata, charMetadata, 0);
122
if (!rasterizedGlyph) {
123
return;
124
}
125
const imageData = atlas.pages[rasterizedGlyph.pageIndex].source.getContext('2d')?.getImageData(
126
rasterizedGlyph.x,
127
rasterizedGlyph.y,
128
rasterizedGlyph.w,
129
rasterizedGlyph.h
130
);
131
if (!imageData) {
132
return;
133
}
134
const canvas = new OffscreenCanvas(imageData.width, imageData.height);
135
const ctx = ensureNonNullable(canvas.getContext('2d'));
136
ctx.putImageData(imageData, 0, 0);
137
const blob = await canvas.convertToBlob({ type: 'image/png' });
138
const resource = URI.joinPath(folders[0].uri, `glyph_${chars}_${tokenMetadata}_${fontSize}px_${fontFamily.replaceAll(/[,\\\/\.'\s]/g, '_')}.png`);
139
await fileService.writeFile(resource, VSBuffer.wrap(new Uint8Array(await blob.arrayBuffer())));
140
});
141
break;
142
}
143
}
144
}
145
146
registerEditorAction(DebugEditorGpuRendererAction);
147
148