Lazily enable HW accel for Electron windows
This commit is contained in:
parent
ca8c1246ce
commit
8b640d9f91
2 changed files with 154 additions and 76 deletions
214
electron/dist/main.js
vendored
214
electron/dist/main.js
vendored
|
|
@ -214,6 +214,15 @@ function debugLog(...args) {
|
||||||
console.log(...args);
|
console.log(...args);
|
||||||
}
|
}
|
||||||
debugLog("[Wo] Electron starting, NODE_VERSION=", process.version);
|
debugLog("[Wo] Electron starting, NODE_VERSION=", process.version);
|
||||||
|
app.commandLine.appendSwitch("ignore-gpu-blocklist");
|
||||||
|
app.commandLine.appendSwitch("enable-gpu-rasterization");
|
||||||
|
app.commandLine.appendSwitch("enable-zero-copy");
|
||||||
|
app.commandLine.appendSwitch("enable-native-gpu-memory-buffers");
|
||||||
|
app.commandLine.appendSwitch("disable-software-rasterizer");
|
||||||
|
app.commandLine.appendSwitch("enable-features", "CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder");
|
||||||
|
app.commandLine.appendSwitch("use-gl", "egl");
|
||||||
|
app.commandLine.appendSwitch("enable-accelerated-video-decode");
|
||||||
|
debugLog("[Wo] GPU acceleration flags applied");
|
||||||
var IPC_SOCKET = process.env.WO_IPC_SOCKET || "/run/user/1000/wo-ipc.sock";
|
var IPC_SOCKET = process.env.WO_IPC_SOCKET || "/run/user/1000/wo-ipc.sock";
|
||||||
debugLog("[Wo] WO_WINDOW_CONFIG env =", process.env.WO_WINDOW_CONFIG);
|
debugLog("[Wo] WO_WINDOW_CONFIG env =", process.env.WO_WINDOW_CONFIG);
|
||||||
var WINDOW_CONFIG = JSON.parse(process.env.WO_WINDOW_CONFIG || "{}");
|
var WINDOW_CONFIG = JSON.parse(process.env.WO_WINDOW_CONFIG || "{}");
|
||||||
|
|
@ -237,11 +246,41 @@ var windowPositionUpdateTimer = null;
|
||||||
var dmabufSender = null;
|
var dmabufSender = null;
|
||||||
var damageBuffer = null;
|
var damageBuffer = null;
|
||||||
var nativeDmabuf = null;
|
var nativeDmabuf = null;
|
||||||
var compositorRxBuffer = Buffer.alloc(0);
|
var compositorRxBuffer = Buffer.alloc(64 * 1024);
|
||||||
|
var rxWriteOffset = 0;
|
||||||
|
var rxReadOffset = 0;
|
||||||
var surfaceBufferPending = new Map;
|
var surfaceBufferPending = new Map;
|
||||||
var surfaceBufferTimer = null;
|
var surfaceBufferFlushScheduled = false;
|
||||||
var shmReadBufferCache = new Map;
|
var shmReadBufferCache = new Map;
|
||||||
var shmFdCache = new Map;
|
var shmFdCache = new Map;
|
||||||
|
function rxAppend(chunk) {
|
||||||
|
const needed = rxWriteOffset + chunk.length;
|
||||||
|
if (needed > compositorRxBuffer.length) {
|
||||||
|
if (rxReadOffset > 0) {
|
||||||
|
compositorRxBuffer.copy(compositorRxBuffer, 0, rxReadOffset, rxWriteOffset);
|
||||||
|
rxWriteOffset -= rxReadOffset;
|
||||||
|
rxReadOffset = 0;
|
||||||
|
}
|
||||||
|
if (rxWriteOffset + chunk.length > compositorRxBuffer.length) {
|
||||||
|
const next = Buffer.alloc(Math.max(compositorRxBuffer.length * 2, rxWriteOffset + chunk.length));
|
||||||
|
compositorRxBuffer.copy(next, 0, 0, rxWriteOffset);
|
||||||
|
compositorRxBuffer = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunk.copy(compositorRxBuffer, rxWriteOffset);
|
||||||
|
rxWriteOffset += chunk.length;
|
||||||
|
}
|
||||||
|
function rxAvailable() {
|
||||||
|
return rxWriteOffset - rxReadOffset;
|
||||||
|
}
|
||||||
|
function rxConsume(n) {
|
||||||
|
rxReadOffset += n;
|
||||||
|
if (rxReadOffset > compositorRxBuffer.length / 2) {
|
||||||
|
compositorRxBuffer.copy(compositorRxBuffer, 0, rxReadOffset, rxWriteOffset);
|
||||||
|
rxWriteOffset -= rxReadOffset;
|
||||||
|
rxReadOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
function closeShmFdCache() {
|
function closeShmFdCache() {
|
||||||
for (const entry of shmFdCache.values()) {
|
for (const entry of shmFdCache.values()) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -275,7 +314,7 @@ function getReusableShmBuffer(windowName, size) {
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
function flushSurfaceBuffers() {
|
function flushSurfaceBuffers() {
|
||||||
surfaceBufferTimer = null;
|
surfaceBufferFlushScheduled = false;
|
||||||
if (!mainWindow || mainWindow.isDestroyed()) {
|
if (!mainWindow || mainWindow.isDestroyed()) {
|
||||||
surfaceBufferPending.clear();
|
surfaceBufferPending.clear();
|
||||||
return;
|
return;
|
||||||
|
|
@ -998,23 +1037,23 @@ function connectToCompositor() {
|
||||||
ipcDataLastLog = now;
|
ipcDataLastLog = now;
|
||||||
}
|
}
|
||||||
const dataT0 = Date.now();
|
const dataT0 = Date.now();
|
||||||
compositorRxBuffer = Buffer.concat([compositorRxBuffer, chunk]);
|
rxAppend(chunk);
|
||||||
while (compositorRxBuffer.length >= 4) {
|
while (rxAvailable() >= 4) {
|
||||||
const magic = compositorRxBuffer.readUInt32LE(0);
|
const magic = compositorRxBuffer.readUInt32LE(rxReadOffset);
|
||||||
if (magic === MAGIC.FRAME_ACK) {
|
if (magic === MAGIC.FRAME_ACK) {
|
||||||
if (compositorRxBuffer.length < 12)
|
if (rxAvailable() < 12)
|
||||||
break;
|
break;
|
||||||
const seq = compositorRxBuffer.readBigUInt64LE(4).toString();
|
const seq = compositorRxBuffer.readBigUInt64LE(rxReadOffset + 4).toString();
|
||||||
inFlightFrameSeqs.delete(seq);
|
inFlightFrameSeqs.delete(seq);
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(12);
|
rxConsume(12);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.MOUSE_MOVE) {
|
if (magic === MAGIC.MOUSE_MOVE) {
|
||||||
if (compositorRxBuffer.length < 20)
|
if (rxAvailable() < 20)
|
||||||
break;
|
break;
|
||||||
const x = compositorRxBuffer.readDoubleLE(4);
|
const x = compositorRxBuffer.readDoubleLE(rxReadOffset + 4);
|
||||||
const y = compositorRxBuffer.readDoubleLE(12);
|
const y = compositorRxBuffer.readDoubleLE(rxReadOffset + 12);
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(20);
|
rxConsume(20);
|
||||||
pointerX = Math.round(x);
|
pointerX = Math.round(x);
|
||||||
pointerY = Math.round(y);
|
pointerY = Math.round(y);
|
||||||
if (!inputFlushTimer) {
|
if (!inputFlushTimer) {
|
||||||
|
|
@ -1028,11 +1067,11 @@ function connectToCompositor() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.MOUSE_BUTTON) {
|
if (magic === MAGIC.MOUSE_BUTTON) {
|
||||||
if (compositorRxBuffer.length < 16)
|
if (rxAvailable() < 16)
|
||||||
break;
|
break;
|
||||||
const button = compositorRxBuffer.readUInt32LE(4);
|
const button = compositorRxBuffer.readUInt32LE(rxReadOffset + 4);
|
||||||
const pressed = compositorRxBuffer.readUInt32LE(8) !== 0;
|
const pressed = compositorRxBuffer.readUInt32LE(rxReadOffset + 8) !== 0;
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(16);
|
rxConsume(16);
|
||||||
if (pendingMouseMove && mainWindow && !mainWindow.isDestroyed()) {
|
if (pendingMouseMove && mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.sendInputEvent({
|
mainWindow.webContents.sendInputEvent({
|
||||||
type: "mouseMove",
|
type: "mouseMove",
|
||||||
|
|
@ -1058,11 +1097,11 @@ function connectToCompositor() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.KEYBOARD) {
|
if (magic === MAGIC.KEYBOARD) {
|
||||||
if (compositorRxBuffer.length < 16)
|
if (rxAvailable() < 16)
|
||||||
break;
|
break;
|
||||||
const key = compositorRxBuffer.readUInt32LE(4);
|
const key = compositorRxBuffer.readUInt32LE(rxReadOffset + 4);
|
||||||
const pressed = compositorRxBuffer.readUInt32LE(8) !== 0;
|
const pressed = compositorRxBuffer.readUInt32LE(rxReadOffset + 8) !== 0;
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(16);
|
rxConsume(16);
|
||||||
const modKey = MODIFIER_KEYCODES[key];
|
const modKey = MODIFIER_KEYCODES[key];
|
||||||
if (modKey) {
|
if (modKey) {
|
||||||
modifierState[modKey] = pressed;
|
modifierState[modKey] = pressed;
|
||||||
|
|
@ -1090,11 +1129,11 @@ function connectToCompositor() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.SCROLL) {
|
if (magic === MAGIC.SCROLL) {
|
||||||
if (compositorRxBuffer.length < 16)
|
if (rxAvailable() < 16)
|
||||||
break;
|
break;
|
||||||
const vertical = compositorRxBuffer.readInt32LE(4);
|
const vertical = compositorRxBuffer.readInt32LE(rxReadOffset + 4);
|
||||||
const horizontal = compositorRxBuffer.readInt32LE(8);
|
const horizontal = compositorRxBuffer.readInt32LE(rxReadOffset + 8);
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(16);
|
rxConsume(16);
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.sendInputEvent({
|
mainWindow.webContents.sendInputEvent({
|
||||||
type: "mouseWheel",
|
type: "mouseWheel",
|
||||||
|
|
@ -1108,27 +1147,27 @@ function connectToCompositor() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.FOCUS_CHANGE) {
|
if (magic === MAGIC.FOCUS_CHANGE) {
|
||||||
if (compositorRxBuffer.length < 12)
|
if (rxAvailable() < 12)
|
||||||
break;
|
break;
|
||||||
const nameLen = compositorRxBuffer.readUInt32LE(4);
|
const nameLen = compositorRxBuffer.readUInt32LE(rxReadOffset + 4);
|
||||||
if (compositorRxBuffer.length < 12 + nameLen)
|
if (rxAvailable() < 12 + nameLen)
|
||||||
break;
|
break;
|
||||||
const focused = compositorRxBuffer.readUInt32LE(8) !== 0;
|
const focused = compositorRxBuffer.readUInt32LE(rxReadOffset + 8) !== 0;
|
||||||
const windowName = compositorRxBuffer.slice(12, 12 + nameLen).toString("utf8");
|
const windowName = compositorRxBuffer.slice(rxReadOffset + 12, rxReadOffset + 12 + nameLen).toString("utf8");
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(12 + nameLen);
|
rxConsume(12 + nameLen);
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send("wo:focus-change", { window: windowName, focused });
|
mainWindow.webContents.send("wo:focus-change", { window: windowName, focused });
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.WINDOW_META) {
|
if (magic === MAGIC.WINDOW_META) {
|
||||||
if (compositorRxBuffer.length < 8)
|
if (rxAvailable() < 8)
|
||||||
break;
|
break;
|
||||||
const payloadLen = compositorRxBuffer.readUInt32LE(4);
|
const payloadLen = compositorRxBuffer.readUInt32LE(rxReadOffset + 4);
|
||||||
if (compositorRxBuffer.length < 8 + payloadLen)
|
if (rxAvailable() < 8 + payloadLen)
|
||||||
break;
|
break;
|
||||||
const metadata = compositorRxBuffer.slice(8, 8 + payloadLen).toString("utf8");
|
const metadata = compositorRxBuffer.slice(rxReadOffset + 8, rxReadOffset + 8 + payloadLen).toString("utf8");
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(8 + payloadLen);
|
rxConsume(8 + payloadLen);
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send("wo:window-metadata", { type: "windowMetadata", metadata });
|
mainWindow.webContents.send("wo:window-metadata", { type: "windowMetadata", metadata });
|
||||||
try {
|
try {
|
||||||
|
|
@ -1142,13 +1181,13 @@ function connectToCompositor() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.SHM_BUFFER) {
|
if (magic === MAGIC.SHM_BUFFER) {
|
||||||
if (compositorRxBuffer.length < 8)
|
if (rxAvailable() < 8)
|
||||||
break;
|
break;
|
||||||
const nameLen = compositorRxBuffer.readUInt32LE(4);
|
const nameLen = compositorRxBuffer.readUInt32LE(rxReadOffset + 4);
|
||||||
const baseHeader = 8 + nameLen + 20 + 4;
|
const baseHeader = 8 + nameLen + 20 + 4;
|
||||||
if (compositorRxBuffer.length < baseHeader)
|
if (rxAvailable() < baseHeader)
|
||||||
break;
|
break;
|
||||||
let off = 8;
|
let off = rxReadOffset + 8;
|
||||||
const windowName = compositorRxBuffer.slice(off, off + nameLen).toString("utf8");
|
const windowName = compositorRxBuffer.slice(off, off + nameLen).toString("utf8");
|
||||||
off += nameLen;
|
off += nameLen;
|
||||||
const sbWidth = compositorRxBuffer.readUInt32LE(off);
|
const sbWidth = compositorRxBuffer.readUInt32LE(off);
|
||||||
|
|
@ -1164,7 +1203,7 @@ function connectToCompositor() {
|
||||||
const numRects = compositorRxBuffer.readUInt32LE(off);
|
const numRects = compositorRxBuffer.readUInt32LE(off);
|
||||||
off += 4;
|
off += 4;
|
||||||
const fullHeader = baseHeader + numRects * 16;
|
const fullHeader = baseHeader + numRects * 16;
|
||||||
if (compositorRxBuffer.length < fullHeader)
|
if (rxAvailable() < fullHeader)
|
||||||
break;
|
break;
|
||||||
const damageRects = [];
|
const damageRects = [];
|
||||||
for (let i = 0;i < numRects; i++) {
|
for (let i = 0;i < numRects; i++) {
|
||||||
|
|
@ -1176,7 +1215,7 @@ function connectToCompositor() {
|
||||||
});
|
});
|
||||||
off += 16;
|
off += 16;
|
||||||
}
|
}
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(fullHeader);
|
rxConsume(fullHeader);
|
||||||
try {
|
try {
|
||||||
const extFd = getOrOpenShmFd(windowName, pid, fd);
|
const extFd = getOrOpenShmFd(windowName, pid, fd);
|
||||||
const fullSize = sbStride * sbHeight;
|
const fullSize = sbStride * sbHeight;
|
||||||
|
|
@ -1220,8 +1259,9 @@ function connectToCompositor() {
|
||||||
pixels: safeBuffer,
|
pixels: safeBuffer,
|
||||||
damageRects: hasUsableRects ? damageRects : undefined
|
damageRects: hasUsableRects ? damageRects : undefined
|
||||||
});
|
});
|
||||||
if (!surfaceBufferTimer) {
|
if (!surfaceBufferFlushScheduled) {
|
||||||
surfaceBufferTimer = setTimeout(flushSurfaceBuffers, 10);
|
surfaceBufferFlushScheduled = true;
|
||||||
|
setImmediate(flushSurfaceBuffers);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const cached = shmFdCache.get(windowName);
|
const cached = shmFdCache.get(windowName);
|
||||||
|
|
@ -1236,13 +1276,13 @@ function connectToCompositor() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.DMABUF_FRAME) {
|
if (magic === MAGIC.DMABUF_FRAME) {
|
||||||
if (compositorRxBuffer.length < 8)
|
if (rxAvailable() < 8)
|
||||||
break;
|
break;
|
||||||
const nameLen = compositorRxBuffer.readUInt32LE(4);
|
const nameLen = compositorRxBuffer.readUInt32LE(rxReadOffset + 4);
|
||||||
const numPlanesOffset = 8 + nameLen + 12;
|
const numPlanesOffset = 8 + nameLen + 12;
|
||||||
if (compositorRxBuffer.length < numPlanesOffset + 4)
|
if (rxAvailable() < numPlanesOffset + 4)
|
||||||
break;
|
break;
|
||||||
let off = 8;
|
let off = rxReadOffset + 8;
|
||||||
const dmabufName = compositorRxBuffer.slice(off, off + nameLen).toString("utf8");
|
const dmabufName = compositorRxBuffer.slice(off, off + nameLen).toString("utf8");
|
||||||
off += nameLen;
|
off += nameLen;
|
||||||
const dmabufW = compositorRxBuffer.readUInt32LE(off);
|
const dmabufW = compositorRxBuffer.readUInt32LE(off);
|
||||||
|
|
@ -1253,8 +1293,8 @@ function connectToCompositor() {
|
||||||
off += 4;
|
off += 4;
|
||||||
const numPlanes = compositorRxBuffer.readUInt32LE(off);
|
const numPlanes = compositorRxBuffer.readUInt32LE(off);
|
||||||
off += 4;
|
off += 4;
|
||||||
const totalSize = off + numPlanes * 24;
|
const totalSize = off - rxReadOffset + numPlanes * 24;
|
||||||
if (compositorRxBuffer.length < totalSize)
|
if (rxAvailable() < totalSize)
|
||||||
break;
|
break;
|
||||||
try {
|
try {
|
||||||
if (nativeDmabuf && ipcSocket) {
|
if (nativeDmabuf && ipcSocket) {
|
||||||
|
|
@ -1281,31 +1321,31 @@ function connectToCompositor() {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugLog(`[Wo] DMABUF import failed for ${dmabufName}:`, err);
|
debugLog(`[Wo] DMABUF import failed for ${dmabufName}:`, err);
|
||||||
}
|
}
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(totalSize);
|
rxConsume(totalSize);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.POINTER_LOCK_REQUEST) {
|
if (magic === MAGIC.POINTER_LOCK_REQUEST) {
|
||||||
if (compositorRxBuffer.length < 12)
|
if (rxAvailable() < 12)
|
||||||
break;
|
break;
|
||||||
const nameLen = compositorRxBuffer.readUInt32LE(4);
|
const nameLen = compositorRxBuffer.readUInt32LE(rxReadOffset + 4);
|
||||||
if (compositorRxBuffer.length < 12 + nameLen)
|
if (rxAvailable() < 12 + nameLen)
|
||||||
break;
|
break;
|
||||||
const lock = compositorRxBuffer.readUInt32LE(8) !== 0;
|
const lock = compositorRxBuffer.readUInt32LE(rxReadOffset + 8) !== 0;
|
||||||
const windowName = compositorRxBuffer.slice(12, 12 + nameLen).toString("utf8");
|
const windowName = compositorRxBuffer.slice(rxReadOffset + 12, rxReadOffset + 12 + nameLen).toString("utf8");
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(12 + nameLen);
|
rxConsume(12 + nameLen);
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send("wo:pointer-lock-request", { window: windowName, lock });
|
mainWindow.webContents.send("wo:pointer-lock-request", { window: windowName, lock });
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.ENV_UPDATE) {
|
if (magic === MAGIC.ENV_UPDATE) {
|
||||||
if (compositorRxBuffer.length < 8)
|
if (rxAvailable() < 8)
|
||||||
break;
|
break;
|
||||||
const jsonLen = compositorRxBuffer.readUInt32LE(4);
|
const jsonLen = compositorRxBuffer.readUInt32LE(rxReadOffset + 4);
|
||||||
if (compositorRxBuffer.length < 8 + jsonLen)
|
if (rxAvailable() < 8 + jsonLen)
|
||||||
break;
|
break;
|
||||||
const jsonStr = compositorRxBuffer.slice(8, 8 + jsonLen).toString("utf8");
|
const jsonStr = compositorRxBuffer.slice(rxReadOffset + 8, rxReadOffset + 8 + jsonLen).toString("utf8");
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(8 + jsonLen);
|
rxConsume(8 + jsonLen);
|
||||||
try {
|
try {
|
||||||
const vars = JSON.parse(jsonStr);
|
const vars = JSON.parse(jsonStr);
|
||||||
for (const [key, value] of Object.entries(vars)) {
|
for (const [key, value] of Object.entries(vars)) {
|
||||||
|
|
@ -1321,18 +1361,18 @@ function connectToCompositor() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (magic === MAGIC.SCREENCOPY_EVENT) {
|
if (magic === MAGIC.SCREENCOPY_EVENT) {
|
||||||
if (compositorRxBuffer.length < 12)
|
if (rxAvailable() < 12)
|
||||||
break;
|
break;
|
||||||
const active = compositorRxBuffer.readUInt32LE(4) !== 0;
|
const active = compositorRxBuffer.readUInt32LE(rxReadOffset + 4) !== 0;
|
||||||
const clientCount = compositorRxBuffer.readUInt32LE(8);
|
const clientCount = compositorRxBuffer.readUInt32LE(rxReadOffset + 8);
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(12);
|
rxConsume(12);
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send("wo:screencopy-event", { active, clientCount });
|
mainWindow.webContents.send("wo:screencopy-event", { active, clientCount });
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
debugLog("[Wo] UNKNOWN magic 0x" + magic.toString(16).padStart(8, "0") + " bufLen=" + compositorRxBuffer.length + " — stream may be corrupted");
|
debugLog("[Wo] UNKNOWN magic 0x" + magic.toString(16).padStart(8, "0") + " bufLen=" + rxAvailable() + " — stream may be corrupted");
|
||||||
compositorRxBuffer = compositorRxBuffer.slice(1);
|
rxConsume(1);
|
||||||
}
|
}
|
||||||
const dataElapsed = Date.now() - dataT0;
|
const dataElapsed = Date.now() - dataT0;
|
||||||
if (dataElapsed > 50) {
|
if (dataElapsed > 50) {
|
||||||
|
|
@ -1716,7 +1756,9 @@ async function createWindow() {
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
offscreen: !CLIENT_MODE
|
offscreen: !CLIENT_MODE,
|
||||||
|
webgl: true,
|
||||||
|
backgroundThrottling: false
|
||||||
},
|
},
|
||||||
show: CLIENT_MODE
|
show: CLIENT_MODE
|
||||||
});
|
});
|
||||||
|
|
@ -1757,7 +1799,7 @@ async function createWindow() {
|
||||||
}
|
}
|
||||||
}, frameIntervalMs);
|
}, frameIntervalMs);
|
||||||
let skippedCount = 0;
|
let skippedCount = 0;
|
||||||
mainWindow.webContents.on("paint", (_event, _dirty, image) => {
|
mainWindow.webContents.on("paint", (_event, dirty, image) => {
|
||||||
if (!dmabufSender || !ipcConnected) {
|
if (!dmabufSender || !ipcConnected) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
debugLog("[Wo] SKIPPED paint (dmabufSender=" + !!dmabufSender + " ipcConnected=" + ipcConnected + ")");
|
debugLog("[Wo] SKIPPED paint (dmabufSender=" + !!dmabufSender + " ipcConnected=" + ipcConnected + ")");
|
||||||
|
|
@ -1778,11 +1820,33 @@ async function createWindow() {
|
||||||
damageBuffer = new DamageBuffer(size.width, size.height);
|
damageBuffer = new DamageBuffer(size.width, size.height);
|
||||||
}
|
}
|
||||||
const pixels = image.getBitmap();
|
const pixels = image.getBitmap();
|
||||||
applyDamagePayload(damageBuffer, {
|
const pixelData = new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength);
|
||||||
width: size.width,
|
const isFullFrame = dirty.x === 0 && dirty.y === 0 && dirty.width === size.width && dirty.height === size.height;
|
||||||
height: size.height,
|
if (isFullFrame) {
|
||||||
fullFrame: new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength)
|
applyDamagePayload(damageBuffer, {
|
||||||
});
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
fullFrame: pixelData
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const stride = size.width * 4;
|
||||||
|
const patchStride = dirty.width * 4;
|
||||||
|
const patchData = new Uint8Array(dirty.width * dirty.height * 4);
|
||||||
|
for (let row = 0;row < dirty.height; row++) {
|
||||||
|
const srcOff = (dirty.y + row) * stride + dirty.x * 4;
|
||||||
|
const dstOff = row * patchStride;
|
||||||
|
patchData.set(pixelData.subarray(srcOff, srcOff + patchStride), dstOff);
|
||||||
|
}
|
||||||
|
applyDamagePayload(damageBuffer, {
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
patches: [{
|
||||||
|
rect: { x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height },
|
||||||
|
rgba: patchData,
|
||||||
|
stride: patchStride
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const seq = dmabufSender.send(damageBuffer);
|
const seq = dmabufSender.send(damageBuffer);
|
||||||
inFlightFrameSeqs.add(seq.toString());
|
inFlightFrameSeqs.add(seq.toString());
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,18 @@ function debugLog(...args: any[]) {
|
||||||
|
|
||||||
debugLog('[Wo] Electron starting, NODE_VERSION=', process.version);
|
debugLog('[Wo] Electron starting, NODE_VERSION=', process.version);
|
||||||
|
|
||||||
|
// GPU acceleration & rendering performance flags (must be set before app 'ready')
|
||||||
|
app.commandLine.appendSwitch('ignore-gpu-blocklist');
|
||||||
|
app.commandLine.appendSwitch('enable-gpu-rasterization');
|
||||||
|
app.commandLine.appendSwitch('enable-zero-copy');
|
||||||
|
app.commandLine.appendSwitch('enable-native-gpu-memory-buffers');
|
||||||
|
app.commandLine.appendSwitch('disable-software-rasterizer');
|
||||||
|
app.commandLine.appendSwitch('enable-features',
|
||||||
|
'CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder');
|
||||||
|
app.commandLine.appendSwitch('use-gl', 'egl');
|
||||||
|
app.commandLine.appendSwitch('enable-accelerated-video-decode');
|
||||||
|
debugLog('[Wo] GPU acceleration flags applied');
|
||||||
|
|
||||||
type ClientWindowInfo = {
|
type ClientWindowInfo = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -1717,7 +1729,9 @@ async function createWindow() {
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
offscreen: !CLIENT_MODE
|
offscreen: !CLIENT_MODE,
|
||||||
|
webgl: true,
|
||||||
|
backgroundThrottling: false,
|
||||||
},
|
},
|
||||||
show: CLIENT_MODE,
|
show: CLIENT_MODE,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue