Skip to content

Commit 2762b73

Browse files
Avoid copying the image buffer when calling the JS callback to render an image update. The Rust side can provide a pointer to the JS runtime which bindgen converts to a number. This number represents an index into the wasm module's memory which the JS runtime has read-only access to in the form of an ArrayBuffer. One additional change of significance is that the render callback no longer registers itself with 'requestAnimationFrame', and instead writes the update out synchronously with 'putImageData'.
1 parent e09c1ec commit 2762b73

File tree

3 files changed

+44
-54
lines changed

3 files changed

+44
-54
lines changed

web/packages/shared/components/CanvasRenderer.tsx

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import React, {
2727
import { Logger } from 'design/logger';
2828
import { BitmapFrame, PngFrame } from 'shared/libs/tdp';
2929
import { debounce } from 'shared/utils/highbar';
30+
import {wasm} from 'shared/libs/tdp/client'
3031

3132
const logger = new Logger('CanvasRenderer');
3233

@@ -231,21 +232,9 @@ function makeBitmapFrameRenderer(
231232
): (frame: BitmapFrame) => void {
232233
const ctx = canvas.getContext('2d');
233234

234-
// Buffered rendering logic
235-
let bitmapBuffer: BitmapFrame[] = [];
236-
const renderBuffer = () => {
237-
if (bitmapBuffer.length) {
238-
for (let i = 0; i < bitmapBuffer.length; i++) {
239-
if (bitmapBuffer[i].image_data.data.length != 0) {
240-
const bmpFrame = bitmapBuffer[i];
241-
ctx.putImageData(bmpFrame.image_data, bmpFrame.left, bmpFrame.top, bmpFrame.dirty_x, bmpFrame.dirty_y, bmpFrame.dirty_width, bmpFrame.dirty_height);
242-
}
243-
}
244-
bitmapBuffer = [];
245-
}
246-
requestAnimationFrame(renderBuffer);
247-
};
248-
requestAnimationFrame(renderBuffer);
249-
250-
return frame => bitmapBuffer.push(frame);
235+
return (frame) => {
236+
let buf = new Uint8ClampedArray(wasm.memory.buffer, frame.image_buffer_ptr, frame.image_buffer_length);
237+
let imageData = new ImageData(buf, frame.image_width,frame.image_height)
238+
ctx.putImageData(imageData, 0, 0, frame.dirty_x, frame.dirty_y, frame.dirty_width, frame.dirty_height);
239+
}
251240
}

web/packages/shared/libs/ironrdp/src/lib.rs

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -71,26 +71,26 @@ pub fn init_wasm_log(log_level: &str) {
7171

7272
#[wasm_bindgen]
7373
pub struct BitmapFrame {
74-
top: u16,
75-
left: u16,
76-
74+
image_width: u16,
75+
image_height: u16,
7776
dirty_x: u16,
7877
dirty_y: u16,
7978
dirty_width: u16,
8079
dirty_height: u16,
81-
image_data: ImageData,
80+
image_buffer_ptr: *const u8,
81+
image_buffer_length: usize,
8282
}
8383

8484
#[wasm_bindgen]
8585
impl BitmapFrame {
8686
#[wasm_bindgen(getter)]
87-
pub fn top(&self) -> u16 {
88-
self.top
87+
pub fn image_height(&self) -> u16 {
88+
self.image_height
8989
}
9090

9191
#[wasm_bindgen(getter)]
92-
pub fn left(&self) -> u16 {
93-
self.left
92+
pub fn image_width(&self) -> u16 {
93+
self.image_width
9494
}
9595

9696
#[wasm_bindgen(getter)]
@@ -113,9 +113,14 @@ impl BitmapFrame {
113113
self.dirty_height
114114
}
115115

116+
#[wasm_bindgen(getter)]
117+
pub fn image_buffer_ptr(&self) -> *const u8 {
118+
self.image_buffer_ptr
119+
}
120+
116121
#[wasm_bindgen(getter)]
117-
pub fn image_data(&self) -> ImageData {
118-
self.image_data.clone() // TODO(isaiah): can we remove this clone?
122+
pub fn image_buffer_length(&self) -> usize {
123+
self.image_buffer_length
119124
}
120125
}
121126

@@ -310,28 +315,18 @@ impl FastPathProcessor {
310315
image_width: u16,
311316
image_height: u16,
312317
) -> Result<(), JsValue> {
313-
let image_data = create_image_data_from_image_and_region(&image_data,
314-
&InclusiveRectangle {
315-
left: 0,
316-
top: 0,
317-
right: image_width - 1,
318-
bottom: image_height - 1,
319-
})?;
320-
321-
let bitmap_frame = BitmapFrame {
322-
top: 0,
323-
left: 0,
318+
319+
// TODO(isaiah): return this?
320+
let _ret = callback.call1(cb_context, &JsValue::from(BitmapFrame {
321+
image_height: image_height,
322+
image_width: image_width,
324323
dirty_x: image_location.left,
325324
dirty_y: image_location.top,
326-
dirty_width: image_location.width(),
327325
dirty_height: image_location.height(),
328-
image_data,
329-
};
330-
331-
let bitmap_frame = &JsValue::from(bitmap_frame);
332-
333-
// TODO(isaiah): return this?
334-
let _ret = callback.call1(cb_context, bitmap_frame)?;
326+
dirty_width: image_location.width(),
327+
image_buffer_ptr: image_data.as_ptr(),
328+
image_buffer_length: image_data.len(),
329+
}))?;
335330
Ok(())
336331
}
337332
}

web/packages/shared/libs/tdp/client.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ import {
5757
type FileOrDirInfo,
5858
} from './sharedDirectoryAccess';
5959

60+
import { InitOutput } from 'shared/libs/ironrdp/pkg/ironrdp';
61+
62+
let wasm: InitOutput;
63+
export {wasm};
64+
6065
export enum TdpClientEvent {
6166
TDP_CLIENT_SCREEN_SPEC = 'tdp client screen spec',
6267
TDP_PNG_FRAME = 'tdp png frame',
@@ -323,7 +328,7 @@ export class TdpClient extends EventEmitter<EventMap> {
323328
wasmLogLevel = LogType.TRACE;
324329
}
325330

326-
await init();
331+
wasm = await init();
327332
init_wasm_log(wasmLogLevel);
328333
}
329334

@@ -887,13 +892,14 @@ export class TdpClient extends EventEmitter<EventMap> {
887892

888893
// Mimics the BitmapFrame struct in rust.
889894
export type BitmapFrame = {
890-
top: number;
891-
left: number;
892-
dirty_x: number;
893-
dirty_y: number;
894-
dirty_width: number;
895-
dirty_height: number;
896-
image_data: ImageData;
895+
image_height: number,
896+
image_width: number,
897+
dirty_x: number,
898+
dirty_y: number,
899+
dirty_width: number,
900+
dirty_height: number,
901+
image_buffer_ptr: number,
902+
image_buffer_length: number,
897903
};
898904

899905
export function useListener<T extends any[]>(

0 commit comments

Comments
 (0)