Skip to content

Commit 0286670

Browse files
Merge pull request #1453 from CapSoftware/0.4-windows-fixes
feat: 0.4 windows fixes + optimisations
2 parents db01c9d + ff3941f commit 0286670

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+4434
-394
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/desktop/src-tauri/src/editor_window.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ fn strip_frame_padding(frame: RenderedFrame) -> Result<(Vec<u8>, u32), &'static
1616
.width
1717
.checked_mul(4)
1818
.ok_or("overflow computing expected_stride")?;
19+
1920
if frame.padded_bytes_per_row == expected_stride {
2021
Ok((frame.data, expected_stride))
2122
} else {
@@ -30,6 +31,7 @@ fn strip_frame_padding(frame: RenderedFrame) -> Result<(Vec<u8>, u32), &'static
3031
let end = start + expected_stride as usize;
3132
stripped.extend_from_slice(&frame.data[start..end]);
3233
}
34+
3335
Ok((stripped, expected_stride))
3436
}
3537
}

apps/desktop/src-tauri/src/gpu_context.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,49 @@ pub struct SharedGpuContext {
4242
pub queue: Arc<wgpu::Queue>,
4343
pub adapter: Arc<wgpu::Adapter>,
4444
pub instance: Arc<wgpu::Instance>,
45+
pub is_software_adapter: bool,
4546
}
4647

4748
static GPU: OnceCell<Option<SharedGpuContext>> = OnceCell::const_new();
4849

4950
pub async fn get_shared_gpu() -> Option<&'static SharedGpuContext> {
5051
GPU.get_or_init(|| async {
5152
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
52-
let adapter = instance
53+
54+
let hardware_adapter = instance
5355
.request_adapter(&wgpu::RequestAdapterOptions {
5456
power_preference: wgpu::PowerPreference::HighPerformance,
5557
force_fallback_adapter: false,
5658
compatible_surface: None,
5759
})
5860
.await
59-
.ok()?;
61+
.ok();
62+
63+
let (adapter, is_software_adapter) = if let Some(adapter) = hardware_adapter {
64+
tracing::info!(
65+
adapter_name = adapter.get_info().name,
66+
adapter_backend = ?adapter.get_info().backend,
67+
"Using hardware GPU adapter for shared context"
68+
);
69+
(adapter, false)
70+
} else {
71+
tracing::warn!("No hardware GPU adapter found, attempting software fallback for shared context");
72+
let software_adapter = instance
73+
.request_adapter(&wgpu::RequestAdapterOptions {
74+
power_preference: wgpu::PowerPreference::LowPower,
75+
force_fallback_adapter: true,
76+
compatible_surface: None,
77+
})
78+
.await
79+
.ok()?;
80+
81+
tracing::info!(
82+
adapter_name = software_adapter.get_info().name,
83+
adapter_backend = ?software_adapter.get_info().backend,
84+
"Using software adapter for shared context (CPU rendering - performance may be reduced)"
85+
);
86+
(software_adapter, true)
87+
};
6088

6189
let (device, queue) = adapter
6290
.request_device(&wgpu::DeviceDescriptor {
@@ -72,6 +100,7 @@ pub async fn get_shared_gpu() -> Option<&'static SharedGpuContext> {
72100
queue: Arc::new(queue),
73101
adapter: Arc::new(adapter),
74102
instance: Arc::new(instance),
103+
is_software_adapter,
75104
})
76105
})
77106
.await

apps/desktop/src-tauri/src/lib.rs

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ pub struct RecordingInfo {
809809
enum CurrentRecordingTarget {
810810
Window {
811811
id: WindowId,
812-
bounds: LogicalBounds,
812+
bounds: Option<LogicalBounds>,
813813
},
814814
Screen {
815815
id: DisplayId,
@@ -841,33 +841,55 @@ struct CurrentRecording {
841841
async fn get_current_recording(
842842
state: MutableState<'_, App>,
843843
) -> Result<JsonValue<Option<CurrentRecording>>, ()> {
844+
tracing::debug!("get_current_recording called");
844845
let state = state.read().await;
845846

846847
let (mode, capture_target, status) = match &state.recording_state {
847-
RecordingState::None => return Ok(JsonValue::new(&None)),
848-
RecordingState::Pending { mode, target } => (*mode, target, RecordingStatus::Pending),
849-
RecordingState::Active(inner) => (
850-
inner.mode(),
851-
inner.capture_target(),
852-
RecordingStatus::Recording,
853-
),
848+
RecordingState::None => {
849+
tracing::debug!("get_current_recording: state is None");
850+
return Ok(JsonValue::new(&None));
851+
}
852+
RecordingState::Pending { mode, target } => {
853+
tracing::debug!("get_current_recording: state is Pending");
854+
(*mode, target, RecordingStatus::Pending)
855+
}
856+
RecordingState::Active(inner) => {
857+
tracing::debug!("get_current_recording: state is Active");
858+
(
859+
inner.mode(),
860+
inner.capture_target(),
861+
RecordingStatus::Recording,
862+
)
863+
}
854864
};
855865

856866
let target = match capture_target {
857-
ScreenCaptureTarget::Display { id } => CurrentRecordingTarget::Screen { id: id.clone() },
858-
ScreenCaptureTarget::Window { id } => CurrentRecordingTarget::Window {
859-
id: id.clone(),
860-
bounds: scap_targets::Window::from_id(id)
861-
.ok_or(())?
862-
.display_relative_logical_bounds()
863-
.ok_or(())?,
864-
},
865-
ScreenCaptureTarget::Area { screen, bounds } => CurrentRecordingTarget::Area {
866-
screen: screen.clone(),
867-
bounds: *bounds,
868-
},
867+
ScreenCaptureTarget::Display { id } => {
868+
tracing::debug!("get_current_recording: target is Display");
869+
CurrentRecordingTarget::Screen { id: id.clone() }
870+
}
871+
ScreenCaptureTarget::Window { id } => {
872+
let bounds =
873+
scap_targets::Window::from_id(id).and_then(|w| w.display_relative_logical_bounds());
874+
tracing::debug!(
875+
"get_current_recording: target is Window, bounds={:?}",
876+
bounds
877+
);
878+
CurrentRecordingTarget::Window {
879+
id: id.clone(),
880+
bounds,
881+
}
882+
}
883+
ScreenCaptureTarget::Area { screen, bounds } => {
884+
tracing::debug!("get_current_recording: target is Area");
885+
CurrentRecordingTarget::Area {
886+
screen: screen.clone(),
887+
bounds: *bounds,
888+
}
889+
}
869890
};
870891

892+
tracing::debug!("get_current_recording: returning Some(CurrentRecording)");
871893
Ok(JsonValue::new(&Some(CurrentRecording {
872894
target,
873895
mode,

apps/desktop/src-tauri/src/screenshot_editor.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,14 @@ impl ScreenshotEditorInstances {
232232
}
233233
};
234234

235-
let (instance, adapter, device, queue) =
235+
let (instance, adapter, device, queue, is_software_adapter) =
236236
if let Some(shared) = gpu_context::get_shared_gpu().await {
237237
(
238238
shared.instance.clone(),
239239
shared.adapter.clone(),
240240
shared.device.clone(),
241241
shared.queue.clone(),
242+
shared.is_software_adapter,
242243
)
243244
} else {
244245
let instance =
@@ -262,7 +263,7 @@ impl ScreenshotEditorInstances {
262263
})
263264
.await
264265
.map_err(|e| e.to_string())?;
265-
(instance, adapter, Arc::new(device), Arc::new(queue))
266+
(instance, adapter, Arc::new(device), Arc::new(queue), false)
266267
};
267268

268269
let options = cap_rendering::RenderOptions {
@@ -285,6 +286,7 @@ impl ScreenshotEditorInstances {
285286
meta: studio_meta,
286287
recording_meta: recording_meta.clone(),
287288
background_textures: Arc::new(tokio::sync::RwLock::new(HashMap::new())),
289+
is_software_adapter,
288290
};
289291

290292
let (config_tx, mut config_rx) = watch::channel(loaded_config.unwrap_or_default());
@@ -304,7 +306,11 @@ impl ScreenshotEditorInstances {
304306

305307
tokio::spawn(async move {
306308
let mut frame_renderer = FrameRenderer::new(&constants);
307-
let mut layers = RendererLayers::new(&constants.device, &constants.queue);
309+
let mut layers = RendererLayers::new_with_options(
310+
&constants.device,
311+
&constants.queue,
312+
constants.is_software_adapter,
313+
);
308314
let shutdown_token = render_shutdown_token;
309315

310316
// Initial render

apps/desktop/src-tauri/src/windows.rs

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,40 @@ impl ShowCapWindow {
708708
let title = CapWindowId::RecordingControls.title();
709709
let should_protect = should_protect_window(app, &title);
710710

711+
let pos_x = ((monitor.size().width as f64) / monitor.scale_factor() - width) / 2.0;
712+
let pos_y =
713+
(monitor.size().height as f64) / monitor.scale_factor() - height - 120.0;
714+
715+
debug!(
716+
"InProgressRecording window: monitor size={:?}, scale={}, pos=({}, {})",
717+
monitor.size(),
718+
monitor.scale_factor(),
719+
pos_x,
720+
pos_y
721+
);
722+
723+
#[cfg(target_os = "macos")]
724+
let window = {
725+
self.window_builder(app, "/in-progress-recording")
726+
.maximized(false)
727+
.resizable(false)
728+
.fullscreen(false)
729+
.shadow(false)
730+
.always_on_top(true)
731+
.transparent(true)
732+
.visible_on_all_workspaces(true)
733+
.content_protected(should_protect)
734+
.inner_size(width, height)
735+
.position(pos_x, pos_y)
736+
.skip_taskbar(true)
737+
.initialization_script(format!(
738+
"window.COUNTDOWN = {};",
739+
countdown.unwrap_or_default()
740+
))
741+
.build()?
742+
};
743+
744+
#[cfg(windows)]
711745
let window = self
712746
.window_builder(app, "/in-progress-recording")
713747
.maximized(false)
@@ -719,23 +753,47 @@ impl ShowCapWindow {
719753
.visible_on_all_workspaces(true)
720754
.content_protected(should_protect)
721755
.inner_size(width, height)
722-
.position(
723-
((monitor.size().width as f64) / monitor.scale_factor() - width) / 2.0,
724-
(monitor.size().height as f64) / monitor.scale_factor() - height - 120.0,
725-
)
726-
.skip_taskbar(true)
756+
.position(pos_x, pos_y)
757+
.skip_taskbar(false)
727758
.initialization_script(format!(
728759
"window.COUNTDOWN = {};",
729760
countdown.unwrap_or_default()
730761
))
731762
.build()?;
732763

764+
debug!(
765+
"InProgressRecording window created: label={}, inner_size={:?}, outer_position={:?}",
766+
window.label(),
767+
window.inner_size(),
768+
window.outer_position()
769+
);
770+
733771
#[cfg(target_os = "macos")]
734772
{
735773
crate::platform::set_window_level(window.as_ref().window(), 1000);
736774
}
737775

738-
fake_window::spawn_fake_window_listener(app.clone(), window.clone());
776+
#[cfg(target_os = "macos")]
777+
{
778+
let show_result = window.show();
779+
debug!(
780+
"InProgressRecording window.show() result: {:?}",
781+
show_result
782+
);
783+
fake_window::spawn_fake_window_listener(app.clone(), window.clone());
784+
}
785+
786+
#[cfg(windows)]
787+
{
788+
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
789+
let show_result = window.show();
790+
debug!(
791+
"InProgressRecording window.show() result: {:?}",
792+
show_result
793+
);
794+
window.set_focus().ok();
795+
fake_window::spawn_fake_window_listener(app.clone(), window.clone());
796+
}
739797

740798
window
741799
}

apps/desktop/src/App.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import titlebar from "./utils/titlebar-state";
2222
const queryClient = new QueryClient({
2323
defaultOptions: {
2424
queries: {
25-
experimental_prefetchInRender: true,
2625
refetchOnWindowFocus: false,
2726
refetchOnReconnect: false,
2827
},

apps/desktop/src/routes/editor/Player.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,19 @@ function PreviewCanvas() {
485485
}
486486
});
487487

488+
const isWindows = navigator.userAgent.includes("Windows");
489+
488490
const initCanvas = (canvas: HTMLCanvasElement) => {
489491
if (canvasTransferredRef.current) return;
490492
const controls = canvasControls();
491493
if (!controls) return;
492494

495+
if (isWindows) {
496+
controls.initDirectCanvas(canvas);
497+
canvasTransferredRef.current = true;
498+
return;
499+
}
500+
493501
try {
494502
const offscreen = canvas.transferControlToOffscreen();
495503
controls.initCanvas(offscreen);

apps/desktop/src/routes/in-progress-recording.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
createSignal,
2121
For,
2222
onCleanup,
23+
onMount,
2324
Show,
2425
} from "solid-js";
2526
import { createStore, produce } from "solid-js/store";
@@ -60,6 +61,17 @@ const NO_WEBCAM = "No Webcam";
6061
const FAKE_WINDOW_BOUNDS_NAME = "recording-controls-interactive-area";
6162

6263
export default function () {
64+
console.log("[in-progress-recording] Wrapper rendering");
65+
66+
document.documentElement.setAttribute("data-transparent-window", "true");
67+
document.body.style.background = "transparent";
68+
69+
return <InProgressRecordingInner />;
70+
}
71+
72+
function InProgressRecordingInner() {
73+
console.log("[in-progress-recording] Inner component rendering");
74+
6375
const [state, setState] = createSignal<State>(
6476
window.COUNTDOWN === 0
6577
? { variant: "initializing" }
@@ -75,7 +87,16 @@ export default function () {
7587
const optionsQuery = createOptionsQuery();
7688
const startedWithMicrophone = optionsQuery.rawOptions.micName != null;
7789
const startedWithCameraInput = optionsQuery.rawOptions.cameraID != null;
78-
const auth = authStore.createQuery();
90+
91+
const [authData, setAuthData] = createSignal<{
92+
plan?: { upgraded?: boolean };
93+
} | null>(null);
94+
onMount(() => {
95+
authStore
96+
.get()
97+
.then(setAuthData)
98+
.catch(() => setAuthData(null));
99+
});
79100

80101
const audioLevel = createAudioInputLevel();
81102
const [disconnectedInputs, setDisconnectedInputs] =
@@ -498,7 +519,7 @@ export default function () {
498519
return (
499520
optionsQuery.rawOptions.mode === "instant" &&
500521
// If the data is loaded and the user is not upgraded
501-
auth.data?.plan?.upgraded === false
522+
authData()?.plan?.upgraded === false
502523
);
503524
};
504525

0 commit comments

Comments
 (0)