VNC-132 Fix image rendering logic to decrease memory usage (#141)
* VNC-132 Fix image rendering logic to decrease memory usage * Updating the recalculate primary display container size logic to ensure an even resolution is set * VNC-132 Free memory after rendering bitmap image * VNC-132 Update screen channel logic
This commit is contained in:
parent
4d5609e9c4
commit
b2a6b2a2e4
19
app/ui.js
19
app/ui.js
|
|
@ -41,6 +41,7 @@ import Keyboard from "../core/input/keyboard.js";
|
||||||
import RFB from "../core/rfb.js";
|
import RFB from "../core/rfb.js";
|
||||||
import { MouseButtonMapper, XVNC_BUTTONS } from "../core/mousebuttonmapper.js";
|
import { MouseButtonMapper, XVNC_BUTTONS } from "../core/mousebuttonmapper.js";
|
||||||
import * as WebUtil from "./webutil.js";
|
import * as WebUtil from "./webutil.js";
|
||||||
|
import { uuidv4 } from '../core/util/strings.js';
|
||||||
|
|
||||||
const PAGE_TITLE = "KasmVNC";
|
const PAGE_TITLE = "KasmVNC";
|
||||||
|
|
||||||
|
|
@ -70,7 +71,8 @@ const UI = {
|
||||||
selectedMonitor: null,
|
selectedMonitor: null,
|
||||||
refreshRotation: 0,
|
refreshRotation: 0,
|
||||||
currentDisplay: null,
|
currentDisplay: null,
|
||||||
displayWindows: ['primary'],
|
displayWindows: new Map([['primary', 'primary']]),
|
||||||
|
registeredWindows: new Map([['primary', 'primary']]),
|
||||||
|
|
||||||
supportsBroadcastChannel: (typeof BroadcastChannel !== "undefined"),
|
supportsBroadcastChannel: (typeof BroadcastChannel !== "undefined"),
|
||||||
|
|
||||||
|
|
@ -2009,8 +2011,9 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async addSecondaryMonitor() {
|
async addSecondaryMonitor() {
|
||||||
let new_display_path = window.location.pathname.replace(/[^/]*$/, '')
|
let new_display_path = window.location.pathname.replace(/[^/]*$/, '');
|
||||||
let new_display_url = `${window.location.protocol}//${window.location.host}${new_display_path}screen.html`;
|
const windowId = uuidv4();
|
||||||
|
let new_display_url = `${window.location.protocol}//${window.location.host}${new_display_path}screen.html?windowId=${windowId}`;
|
||||||
|
|
||||||
const auto_placement = document.getElementById('noVNC_auto_placement').checked
|
const auto_placement = document.getElementById('noVNC_auto_placement').checked
|
||||||
if (auto_placement && 'getScreenDetails' in window) {
|
if (auto_placement && 'getScreenDetails' in window) {
|
||||||
|
|
@ -2024,7 +2027,7 @@ const UI = {
|
||||||
let screen = details.screens[current]
|
let screen = details.screens[current]
|
||||||
const options = 'left='+screen.availLeft+',top='+screen.availTop+',width='+screen.availWidth+',height='+screen.availHeight+',fullscreen'
|
const options = 'left='+screen.availLeft+',top='+screen.availTop+',width='+screen.availWidth+',height='+screen.availHeight+',fullscreen'
|
||||||
let newdisplay = window.open(new_display_url, '_blank', options);
|
let newdisplay = window.open(new_display_url, '_blank', options);
|
||||||
UI.displayWindows.push(newdisplay);
|
UI.displayWindows.set(windowId, newdisplay);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -2035,14 +2038,16 @@ const UI = {
|
||||||
|
|
||||||
Log.Debug(`Opening a secondary display ${new_display_url}`)
|
Log.Debug(`Opening a secondary display ${new_display_url}`)
|
||||||
let newdisplay = window.open(new_display_url, '_blank', 'toolbar=0,location=0,menubar=0');
|
let newdisplay = window.open(new_display_url, '_blank', 'toolbar=0,location=0,menubar=0');
|
||||||
UI.displayWindows.push(newdisplay);
|
if (newdisplay) {
|
||||||
|
UI.displayWindows.set(windowId, newdisplay);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initMonitors(screenPlan) {
|
initMonitors(screenPlan) {
|
||||||
const { scale } = UI.multiMonitorSettings()
|
const { scale } = UI.multiMonitorSettings()
|
||||||
let monitors = []
|
let monitors = []
|
||||||
let showNativeResolution = false
|
let showNativeResolution = false
|
||||||
let num = 1
|
let num = 1;
|
||||||
screenPlan.screens.forEach(screen => {
|
screenPlan.screens.forEach(screen => {
|
||||||
if (parseFloat(screen.pixelRatio) != 1) {
|
if (parseFloat(screen.pixelRatio) != 1) {
|
||||||
showNativeResolution = true
|
showNativeResolution = true
|
||||||
|
|
@ -2223,7 +2228,9 @@ const UI = {
|
||||||
serverWidth: Math.round(width * scale),
|
serverWidth: Math.round(width * scale),
|
||||||
screens
|
screens
|
||||||
}
|
}
|
||||||
|
if (UI.rfb) {
|
||||||
UI.rfb.applyScreenPlan(screenPlan);
|
UI.rfb.applyScreenPlan(screenPlan);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ export default class Display {
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
addScreen(screenID, width, height, pixelRatio, containerHeight, containerWidth, scale, serverWidth, serverHeight, x, y) {
|
addScreen(screenID, width, height, pixelRatio, containerHeight, containerWidth, scale, serverWidth, serverHeight, x, y, windowId) {
|
||||||
if (!this._isPrimaryDisplay) {
|
if (!this._isPrimaryDisplay) {
|
||||||
throw new Error("Cannot add a screen to a secondary display.");
|
throw new Error("Cannot add a screen to a secondary display.");
|
||||||
}
|
}
|
||||||
|
|
@ -432,14 +432,18 @@ export default class Display {
|
||||||
pixelRatio: pixelRatio,
|
pixelRatio: pixelRatio,
|
||||||
containerHeight: containerHeight,
|
containerHeight: containerHeight,
|
||||||
containerWidth: containerWidth,
|
containerWidth: containerWidth,
|
||||||
channel: UI.displayWindows[this.screens.length],
|
channel: UI.displayWindows.get(windowId),
|
||||||
scale: scale,
|
scale: scale,
|
||||||
x2: x + serverWidth,
|
x2: x + serverWidth,
|
||||||
y2: serverHeight
|
y2: serverHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
this._screens.push(new_screen);
|
this._screens.push(new_screen);
|
||||||
|
if (new_screen.channel) {
|
||||||
|
UI.registeredWindows.set(screenID, windowId);
|
||||||
new_screen.channel.postMessage({eventType: "registered", screenIndex: new_screen.screenIndex});
|
new_screen.channel.postMessage({eventType: "registered", screenIndex: new_screen.screenIndex});
|
||||||
|
} else
|
||||||
|
Log.Debug(`Channel not found for screenId ${screenID}`);
|
||||||
|
|
||||||
return new_screen.screenIndex;
|
return new_screen.screenIndex;
|
||||||
}
|
}
|
||||||
|
|
@ -454,7 +458,11 @@ export default class Display {
|
||||||
if (this._screens[i].screenID == screenID) {
|
if (this._screens[i].screenID == screenID) {
|
||||||
//flush all rects on target screen
|
//flush all rects on target screen
|
||||||
this._flushRectsScreen(i);
|
this._flushRectsScreen(i);
|
||||||
UI.displayWindows.splice(i, 1);
|
const windowId = UI.registeredWindows.get(screenID);
|
||||||
|
if (windowId) {
|
||||||
|
UI.registeredWindows.delete(screenID);
|
||||||
|
UI.displayWindows.delete(windowId);
|
||||||
|
}
|
||||||
this._screens.splice(i, 1);
|
this._screens.splice(i, 1);
|
||||||
removed = true;
|
removed = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -757,30 +765,22 @@ export default class Display {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rect = {
|
const blob = new Blob([arr], { type: mime });
|
||||||
'type': 'img',
|
const rect = {
|
||||||
|
'type': 'bitmap',
|
||||||
'img': null,
|
'img': null,
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': y,
|
'y': y,
|
||||||
'width': width,
|
'width': width,
|
||||||
'height': height,
|
'height': height,
|
||||||
'frame_id': frame_id
|
'frame_id': frame_id,
|
||||||
}
|
'mime': mime
|
||||||
|
};
|
||||||
this._processRectScreens(rect);
|
this._processRectScreens(rect);
|
||||||
|
createImageBitmap(blob).then((bitmapImg) => {
|
||||||
if (rect.inPrimary) {
|
rect.img = bitmapImg;
|
||||||
const img = new Image();
|
|
||||||
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
|
||||||
rect.img = img;
|
|
||||||
} else {
|
|
||||||
rect.type = "_img";
|
|
||||||
}
|
|
||||||
if (rect.inSecondary) {
|
|
||||||
rect.mime = mime;
|
|
||||||
rect.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._asyncRenderQPush(rect);
|
this._asyncRenderQPush(rect);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
transparentRect(x, y, width, height, img, frame_id, hashId) {
|
transparentRect(x, y, width, height, img, frame_id, hashId) {
|
||||||
|
|
@ -1027,6 +1027,10 @@ export default class Display {
|
||||||
this.drawImage(a.img, pos.x, pos.y, a.width, a.height);
|
this.drawImage(a.img, pos.x, pos.y, a.width, a.height);
|
||||||
a.img.close();
|
a.img.close();
|
||||||
break;
|
break;
|
||||||
|
case 'bitmap':
|
||||||
|
this.drawImage(a.img, pos.x, pos.y, a.width, a.height);
|
||||||
|
a.img.close();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this._syncFrameQueue.shift();
|
this._syncFrameQueue.shift();
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1238,11 +1242,18 @@ export default class Display {
|
||||||
case 'vid':
|
case 'vid':
|
||||||
this.drawImage(a.img, screenLocation.x, screenLocation.y, a.width, a.height);
|
this.drawImage(a.img, screenLocation.x, screenLocation.y, a.width, a.height);
|
||||||
break;
|
break;
|
||||||
|
case 'bitmap':
|
||||||
|
this.drawImage(a.img, screenLocation.x, screenLocation.y, a.width, a.height);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
primaryScreenRects++;
|
primaryScreenRects++;
|
||||||
} else {
|
} else {
|
||||||
|
if (!this._screens[screenLocation.screenIndex]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch (a.type) {
|
switch (a.type) {
|
||||||
case 'dummy':
|
case 'dummy':
|
||||||
case 'transparent':
|
case 'transparent':
|
||||||
|
|
@ -1250,7 +1261,7 @@ export default class Display {
|
||||||
break;
|
break;
|
||||||
case 'vid':
|
case 'vid':
|
||||||
secondaryScreenRects++;
|
secondaryScreenRects++;
|
||||||
if (this._screens[screenLocation.screenIndex].channel) {
|
if (this._screens[screenLocation.screenIndex]?.channel) {
|
||||||
this._screens[screenLocation.screenIndex].channel.postMessage({
|
this._screens[screenLocation.screenIndex].channel.postMessage({
|
||||||
eventType: 'rect',
|
eventType: 'rect',
|
||||||
rect: {
|
rect: {
|
||||||
|
|
@ -1267,6 +1278,25 @@ export default class Display {
|
||||||
}, [a.img]);
|
}, [a.img]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'bitmap':
|
||||||
|
secondaryScreenRects++;
|
||||||
|
if (this._screens[screenLocation.screenIndex].channel) {
|
||||||
|
this._screens[screenLocation.screenIndex].channel.postMessage({
|
||||||
|
eventType: 'rect',
|
||||||
|
rect: {
|
||||||
|
'type': 'bitmap',
|
||||||
|
'img': a.img,
|
||||||
|
'x': a.x,
|
||||||
|
'y': a.y,
|
||||||
|
'width': a.width,
|
||||||
|
'height': a.height,
|
||||||
|
'frame_id': a.frame_id,
|
||||||
|
'screenLocations': a.screenLocations
|
||||||
|
},
|
||||||
|
screenLocationIndex: sI
|
||||||
|
}, [a.img]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'blit':
|
case 'blit':
|
||||||
secondaryScreenRects++;
|
secondaryScreenRects++;
|
||||||
let buf = a.data.buffer;
|
let buf = a.data.buffer;
|
||||||
|
|
@ -1434,6 +1464,8 @@ export default class Display {
|
||||||
this._target.style.imageRendering = 'auto'; //auto is really smooth (blurry) using trilinear of linear
|
this._target.style.imageRendering = 'auto'; //auto is really smooth (blurry) using trilinear of linear
|
||||||
Log.Debug('Smoothing enabled');
|
Log.Debug('Smoothing enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame( () => { this._pushAsyncFrame(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
_setFillColor(color) {
|
_setFillColor(color) {
|
||||||
|
|
|
||||||
|
|
@ -1817,7 +1817,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
...event.data.details,
|
...event.data.details,
|
||||||
screenID: event.data.screenID
|
screenID: event.data.screenID
|
||||||
}
|
}
|
||||||
let screenIndex = this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth, event.data.scale, event.data.serverWidth, event.data.serverHeight, event.data.x, event.data.y);
|
let screenIndex = this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth, event.data.scale, event.data.serverWidth, event.data.serverHeight, event.data.x, event.data.y, event.data.windowId);
|
||||||
this._proxyRFBMessage('screenRegistrationConfirmed', [ this._display.screens[screenIndex].screenID, screenIndex ]);
|
this._proxyRFBMessage('screenRegistrationConfirmed', [ this._display.screens[screenIndex].screenID, screenIndex ]);
|
||||||
this._sendEncodings();
|
this._sendEncodings();
|
||||||
clearTimeout(this._resizeTimeout);
|
clearTimeout(this._resizeTimeout);
|
||||||
|
|
@ -1826,7 +1826,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
Log.Info(`Secondary monitor (${event.data.screenID}) has been registered.`);
|
Log.Info(`Secondary monitor (${event.data.screenID}) has been registered.`);
|
||||||
break;
|
break;
|
||||||
case 'reattach':
|
case 'reattach':
|
||||||
let changes = this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth, event.data.scale, event.data.serverWidth, event.data.serverHeight, event.data.x, event.data.y);
|
let changes = this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth, event.data.scale, event.data.serverWidth, event.data.serverHeight, event.data.x, event.data.y, event.data.windowId);
|
||||||
|
|
||||||
clearTimeout(this._resizeTimeout);
|
clearTimeout(this._resizeTimeout);
|
||||||
this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
|
this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
|
||||||
|
|
@ -1971,10 +1971,12 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._display.autoscale(size.screens[0].serverWidth, size.screens[0].serverHeight, size.screens[0].scale);
|
this._display.autoscale(size.screens[0].serverWidth, size.screens[0].serverHeight, size.screens[0].scale);
|
||||||
|
|
||||||
let screen = size.screens[0];
|
let screen = size.screens[0];
|
||||||
|
const windowId = new URLSearchParams(document.location.search).get('windowId');
|
||||||
|
|
||||||
let message = {
|
let message = {
|
||||||
eventType: registerType,
|
eventType: registerType,
|
||||||
screenID: screen.screenID,
|
screenID: screen.screenID,
|
||||||
|
windowId,
|
||||||
width: screen.width,
|
width: screen.width,
|
||||||
height: screen.height,
|
height: screen.height,
|
||||||
x: currentScreen.x || 0,
|
x: currentScreen.x || 0,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue