Faster minimap character rendering

This commit is contained in:
Alex Dima 2017-02-13 17:51:53 +01:00
parent 8c92d305bd
commit 9c856228a9
3 changed files with 96 additions and 120 deletions

View file

@ -34,6 +34,10 @@ set ELECTRON_ENABLE_LOGGING=1
set ELECTRON_ENABLE_STACK_DUMPING=1
:: Launch Code
:: Use the following to get v8 tracing:
:: %CODE% --js-flags="--trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces" . %*
%CODE% . %*
popd

View file

@ -14,13 +14,14 @@ import { ViewContext } from 'vs/editor/common/view/viewContext';
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
import { /*createMinimapCharRenderer,*/ createMinimapCharRenderer2 } from 'vs/editor/common/view/runtimeMinimapCharRenderer';
import * as browser from 'vs/base/browser/browser';
import { MinimapColors, MinimapTokensColorTracker, Constants } from 'vs/editor/common/view/minimapCharRenderer';
import { ParsedColor, MinimapColors, MinimapTokensColorTracker, Constants } from 'vs/editor/common/view/minimapCharRenderer';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { CharCode } from 'vs/base/common/charCode';
import { MinimapLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
import { ColorId } from 'vs/editor/common/modes';
// let charRenderer = createMinimapCharRenderer();
let charRenderer2 = createMinimapCharRenderer2();
let charRenderer2 = createMinimapCharRenderer2(); // TODO@minimap
// interface IWidgetData {
// widget: IOverlayWidget;
@ -177,7 +178,7 @@ export class Minimap extends ViewPart {
public render(ctx: IRestrictedRenderingContext): void {
let pixelRatio = browser.getPixelRatio();
console.log(pixelRatio);
// console.log(pixelRatio);
const WIDTH = pixelRatio * this._minimapWidth;
const HEIGHT = pixelRatio * this._minimapHeight;
this.domNode.width = WIDTH;
@ -195,7 +196,7 @@ export class Minimap extends ViewPart {
// 8 * 4 * lineLen
// );
let start = performance.now();
// let start = performance.now();
// console.profile();
let ctx2 = this.domNode.getContext('2d');
@ -206,7 +207,8 @@ export class Minimap extends ViewPart {
let colorTracker = MinimapTokensColorTracker.getInstance();
let colors = colorTracker.getColorMaps();
let background = colors.getBackgroundColor();
let background = colors.getColor(ColorId.DefaultBackground);
// getBackgroundColor();
let backgroundR = background.r;
let backgroundG = background.g;
let backgroundB = background.b;
@ -222,18 +224,19 @@ export class Minimap extends ViewPart {
}
}
let data: MinimapLineRenderingData[] = [];
for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
let data = this._context.model.getMinimapLineRenderingData(lineIndex + 1);
// let length = Math.min(data.content.length, lineLen);
let dy = lineIndex * Constants.x2_CHAR_HEIGHT;
Minimap._render2xLine(imageData, colors, dy, this._viewportColumn, data);
// break;
data[lineIndex] = this._context.model.getMinimapLineRenderingData(lineIndex + 1);
}
let start2 = performance.now();
for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
let dy = lineIndex * Constants.x2_CHAR_HEIGHT;
Minimap._x2RenderLine(imageData, background, colors, dy, data[lineIndex]);
}
let end2 = performance.now();
console.log(`INNER LOOP TOOK ${end2 - start2} ms.`);
// console.log(imageData.data);
// ctx2.strokeStyle = '#000';
// for (i = 0; i <)
@ -242,10 +245,8 @@ export class Minimap extends ViewPart {
// console.profileEnd();
let end = performance.now();
console.log('TOOK ' + (end - start) + 'ms.');
// let end = performance.now();
// console.log('TOOK ' + (end - start) + 'ms.');
// let data = this._context.model.getViewLineRenderingData(null, 1);
@ -260,11 +261,11 @@ export class Minimap extends ViewPart {
// }
}
private static _render2xLine(target: ImageData, colors: MinimapColors, dy: number, maxColumn: number, lineData: MinimapLineRenderingData) {
private static _x2RenderLine(target: ImageData, backgroundColor: ParsedColor, colors: MinimapColors, dy: number, lineData: MinimapLineRenderingData) {
const content = lineData.content;
const tokens = lineData.tokens;
const tabSize = lineData.tabSize;
const charIndexStop = Math.min(content.length, maxColumn - 1);
const maxDx = target.width - Constants.x2_CHAR_WIDTH;
let dx = 0;
let charIndex = 0;
@ -274,10 +275,10 @@ export class Minimap extends ViewPart {
const token = tokens[tokenIndex];
const tokenEndIndex = token.endIndex;
const tokenColorId = token.getForeground();
const tokenColor = colors.getMinimapColor(tokenColorId);
const tokenColor = colors.getColor(tokenColorId);
for (; charIndex < tokenEndIndex; charIndex++) {
if (charIndex >= charIndexStop) {
if (dx > maxDx) {
// hit edge of minimap
return;
}
@ -292,7 +293,7 @@ export class Minimap extends ViewPart {
// No need to render anything since space is invisible
dx += Constants.x2_CHAR_WIDTH;
} else {
charRenderer2.x2RenderChar(target, dx, dy, charCode, tokenColor);
charRenderer2.x2RenderChar(target, dx, dy, charCode, tokenColor, backgroundColor);
dx += Constants.x2_CHAR_WIDTH;
}
}

View file

@ -20,65 +20,18 @@ export class ParsedColor {
}
}
/**
* Represents a color rendered on top of the background at all possible alpha values.
*/
export class MinimapColor {
/**
* For each 0 <= i <= 255:
* data[3*i + 0] = r
* data[3*i + 1] = g;
* data[3*i + 2] = b;
*/
public readonly data: Uint8ClampedArray;
constructor(data: Uint8ClampedArray) {
this.data = data;
}
}
export class MinimapColors {
private readonly _backgroundColor: ParsedColor;
private readonly _colors: MinimapColor[];
private readonly _colors: ParsedColor[];
constructor(colorMap: string[]) {
this._backgroundColor = MinimapColors._parseColor(colorMap[ColorId.DefaultBackground]);
let backgroundR = this._backgroundColor.r;
let backgroundG = this._backgroundColor.g;
let backgroundB = this._backgroundColor.b;
this._colors = [null];
for (let colorId = 1; colorId < colorMap.length; colorId++) {
let color = MinimapColors._parseColor(colorMap[colorId]);
let colorR = color.r;
let colorG = color.g;
let colorB = color.b;
let result = new Uint8ClampedArray(256 * 3), resultOffset = 0;
for (let alpha = 0; alpha <= 255; alpha++) {
let fAlpha = alpha / 255;
let fAlphaInverse = (255 - alpha) / 255;
let r = (colorR * fAlpha) + (backgroundR * fAlphaInverse);
let g = (colorG * fAlpha) + (backgroundG * fAlphaInverse);
let b = (colorB * fAlpha) + (backgroundB * fAlphaInverse);
result[resultOffset++] = r;
result[resultOffset++] = g;
result[resultOffset++] = b;
}
this._colors[colorId] = new MinimapColor(result);
this._colors[colorId] = MinimapColors._parseColor(colorMap[colorId]);
}
}
public getBackgroundColor(): ParsedColor {
return this._backgroundColor;
}
public getMinimapColor(colorId: ColorId): MinimapColor {
public getColor(colorId: ColorId): ParsedColor {
if (colorId < 1 || colorId >= this._colors.length) {
// background color (basically invisible)
colorId = 2;
@ -243,59 +196,77 @@ export class MinimapCharRenderer2 {
return chCode - Constants.START_CH_CODE;
}
public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, _color: MinimapColor): void {
public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: ParsedColor, backgroundColor: ParsedColor): void {
const x2CharData = this.x2charData;
const chIndex = MinimapCharRenderer2._getChIndex(chCode);
const sourceOffset = chIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH;
const c1 = x2CharData[sourceOffset];
const c2 = x2CharData[sourceOffset + 1];
const c3 = x2CharData[sourceOffset + 2];
const c4 = x2CharData[sourceOffset + 3];
const c5 = x2CharData[sourceOffset + 4];
const c6 = x2CharData[sourceOffset + 5];
const c7 = x2CharData[sourceOffset + 6];
const c8 = x2CharData[sourceOffset + 7];
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
let resultOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
const backgroundR = backgroundColor.r;
const backgroundG = backgroundColor.g;
const backgroundB = backgroundColor.b;
const deltaR = color.r - backgroundR;
const deltaG = color.g - backgroundG;
const deltaB = color.b - backgroundB;
const dest = target.data;
const color = _color.data;
dest[resultOffset + 0] = color[3 * c1 + 0];
dest[resultOffset + 1] = color[3 * c1 + 1];
dest[resultOffset + 2] = color[3 * c1 + 2];
dest[resultOffset + 3] = 255;
dest[resultOffset + 4] = color[3 * c2 + 0];
dest[resultOffset + 5] = color[3 * c2 + 1];
dest[resultOffset + 6] = color[3 * c2 + 2];
dest[resultOffset + 7] = 255;
resultOffset += outWidth;
dest[resultOffset + 0] = color[3 * c3 + 0];
dest[resultOffset + 1] = color[3 * c3 + 1];
dest[resultOffset + 2] = color[3 * c3 + 2];
dest[resultOffset + 3] = 255;
dest[resultOffset + 4] = color[3 * c4 + 0];
dest[resultOffset + 5] = color[3 * c4 + 1];
dest[resultOffset + 6] = color[3 * c4 + 2];
dest[resultOffset + 7] = 255;
resultOffset += outWidth;
dest[resultOffset + 0] = color[3 * c5 + 0];
dest[resultOffset + 1] = color[3 * c5 + 1];
dest[resultOffset + 2] = color[3 * c5 + 2];
dest[resultOffset + 3] = 255;
dest[resultOffset + 4] = color[3 * c6 + 0];
dest[resultOffset + 5] = color[3 * c6 + 1];
dest[resultOffset + 6] = color[3 * c6 + 2];
dest[resultOffset + 7] = 255;
resultOffset += outWidth;
dest[resultOffset + 0] = color[3 * c7 + 0];
dest[resultOffset + 1] = color[3 * c7 + 1];
dest[resultOffset + 2] = color[3 * c7 + 2];
dest[resultOffset + 3] = 255;
dest[resultOffset + 4] = color[3 * c8 + 0];
dest[resultOffset + 5] = color[3 * c8 + 1];
dest[resultOffset + 6] = color[3 * c8 + 2];
dest[resultOffset + 7] = 255;
const sourceOffset = chIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH;
let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
{
const c = x2CharData[sourceOffset] / 255;
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
}
{
const c = x2CharData[sourceOffset + 1] / 255;
dest[destOffset + 4] = backgroundR + deltaR * c;
dest[destOffset + 5] = backgroundG + deltaG * c;
dest[destOffset + 6] = backgroundB + deltaB * c;
}
destOffset += outWidth;
{
const c = x2CharData[sourceOffset + 2] / 255;
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
}
{
const c = x2CharData[sourceOffset + 3] / 255;
dest[destOffset + 4] = backgroundR + deltaR * c;
dest[destOffset + 5] = backgroundG + deltaG * c;
dest[destOffset + 6] = backgroundB + deltaB * c;
}
destOffset += outWidth;
{
const c = x2CharData[sourceOffset + 4] / 255;
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
}
{
const c = x2CharData[sourceOffset + 5] / 255;
dest[destOffset + 4] = backgroundR + deltaR * c;
dest[destOffset + 5] = backgroundG + deltaG * c;
dest[destOffset + 6] = backgroundB + deltaB * c;
}
destOffset += outWidth;
{
const c = x2CharData[sourceOffset + 6] / 255;
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
}
{
const c = x2CharData[sourceOffset + 7] / 255;
dest[destOffset + 4] = backgroundR + deltaR * c;
dest[destOffset + 5] = backgroundG + deltaG * c;
dest[destOffset + 6] = backgroundB + deltaB * c;
}
}
public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number): void {