WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/desktop/src-tauri/src/editor_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fn strip_frame_padding(frame: RenderedFrame) -> Result<(Vec<u8>, u32), &'static
.width
.checked_mul(4)
.ok_or("overflow computing expected_stride")?;

if frame.padded_bytes_per_row == expected_stride {
Ok((frame.data, expected_stride))
} else {
Expand All @@ -30,6 +31,7 @@ fn strip_frame_padding(frame: RenderedFrame) -> Result<(Vec<u8>, u32), &'static
let end = start + expected_stride as usize;
stripped.extend_from_slice(&frame.data[start..end]);
}

Ok((stripped, expected_stride))
}
}
Expand Down
33 changes: 31 additions & 2 deletions apps/desktop/src-tauri/src/gpu_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,49 @@ pub struct SharedGpuContext {
pub queue: Arc<wgpu::Queue>,
pub adapter: Arc<wgpu::Adapter>,
pub instance: Arc<wgpu::Instance>,
pub is_software_adapter: bool,
}

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

pub async fn get_shared_gpu() -> Option<&'static SharedGpuContext> {
GPU.get_or_init(|| async {
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
let adapter = instance

let hardware_adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
force_fallback_adapter: false,
compatible_surface: None,
})
.await
.ok()?;
.ok();

let (adapter, is_software_adapter) = if let Some(adapter) = hardware_adapter {
tracing::info!(
adapter_name = adapter.get_info().name,
adapter_backend = ?adapter.get_info().backend,
"Using hardware GPU adapter for shared context"
);
(adapter, false)
} else {
tracing::warn!("No hardware GPU adapter found, attempting software fallback for shared context");
let software_adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::LowPower,
force_fallback_adapter: true,
compatible_surface: None,
})
.await
.ok()?;

tracing::info!(
adapter_name = software_adapter.get_info().name,
adapter_backend = ?software_adapter.get_info().backend,
"Using software adapter for shared context (CPU rendering - performance may be reduced)"
);
(software_adapter, true)
};

let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
Expand All @@ -72,6 +100,7 @@ pub async fn get_shared_gpu() -> Option<&'static SharedGpuContext> {
queue: Arc::new(queue),
adapter: Arc::new(adapter),
instance: Arc::new(instance),
is_software_adapter,
})
})
.await
Expand Down
62 changes: 42 additions & 20 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ pub struct RecordingInfo {
enum CurrentRecordingTarget {
Window {
id: WindowId,
bounds: LogicalBounds,
bounds: Option<LogicalBounds>,
},
Screen {
id: DisplayId,
Expand Down Expand Up @@ -841,33 +841,55 @@ struct CurrentRecording {
async fn get_current_recording(
state: MutableState<'_, App>,
) -> Result<JsonValue<Option<CurrentRecording>>, ()> {
tracing::debug!("get_current_recording called");
let state = state.read().await;

let (mode, capture_target, status) = match &state.recording_state {
RecordingState::None => return Ok(JsonValue::new(&None)),
RecordingState::Pending { mode, target } => (*mode, target, RecordingStatus::Pending),
RecordingState::Active(inner) => (
inner.mode(),
inner.capture_target(),
RecordingStatus::Recording,
),
RecordingState::None => {
tracing::debug!("get_current_recording: state is None");
return Ok(JsonValue::new(&None));
}
RecordingState::Pending { mode, target } => {
tracing::debug!("get_current_recording: state is Pending");
(*mode, target, RecordingStatus::Pending)
}
RecordingState::Active(inner) => {
tracing::debug!("get_current_recording: state is Active");
(
inner.mode(),
inner.capture_target(),
RecordingStatus::Recording,
)
}
};

let target = match capture_target {
ScreenCaptureTarget::Display { id } => CurrentRecordingTarget::Screen { id: id.clone() },
ScreenCaptureTarget::Window { id } => CurrentRecordingTarget::Window {
id: id.clone(),
bounds: scap_targets::Window::from_id(id)
.ok_or(())?
.display_relative_logical_bounds()
.ok_or(())?,
},
ScreenCaptureTarget::Area { screen, bounds } => CurrentRecordingTarget::Area {
screen: screen.clone(),
bounds: *bounds,
},
ScreenCaptureTarget::Display { id } => {
tracing::debug!("get_current_recording: target is Display");
CurrentRecordingTarget::Screen { id: id.clone() }
}
ScreenCaptureTarget::Window { id } => {
let bounds =
scap_targets::Window::from_id(id).and_then(|w| w.display_relative_logical_bounds());
tracing::debug!(
"get_current_recording: target is Window, bounds={:?}",
bounds
);
CurrentRecordingTarget::Window {
id: id.clone(),
bounds,
}
}
ScreenCaptureTarget::Area { screen, bounds } => {
tracing::debug!("get_current_recording: target is Area");
CurrentRecordingTarget::Area {
screen: screen.clone(),
bounds: *bounds,
}
}
};

tracing::debug!("get_current_recording: returning Some(CurrentRecording)");
Ok(JsonValue::new(&Some(CurrentRecording {
target,
mode,
Expand Down
12 changes: 9 additions & 3 deletions apps/desktop/src-tauri/src/screenshot_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,14 @@ impl ScreenshotEditorInstances {
}
};

let (instance, adapter, device, queue) =
let (instance, adapter, device, queue, is_software_adapter) =
if let Some(shared) = gpu_context::get_shared_gpu().await {
(
shared.instance.clone(),
shared.adapter.clone(),
shared.device.clone(),
shared.queue.clone(),
shared.is_software_adapter,
)
} else {
let instance =
Expand All @@ -262,7 +263,7 @@ impl ScreenshotEditorInstances {
})
.await
.map_err(|e| e.to_string())?;
(instance, adapter, Arc::new(device), Arc::new(queue))
(instance, adapter, Arc::new(device), Arc::new(queue), false)
};

let options = cap_rendering::RenderOptions {
Expand All @@ -285,6 +286,7 @@ impl ScreenshotEditorInstances {
meta: studio_meta,
recording_meta: recording_meta.clone(),
background_textures: Arc::new(tokio::sync::RwLock::new(HashMap::new())),
is_software_adapter,
};

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

tokio::spawn(async move {
let mut frame_renderer = FrameRenderer::new(&constants);
let mut layers = RendererLayers::new(&constants.device, &constants.queue);
let mut layers = RendererLayers::new_with_options(
&constants.device,
&constants.queue,
constants.is_software_adapter,
);
let shutdown_token = render_shutdown_token;

// Initial render
Expand Down
70 changes: 64 additions & 6 deletions apps/desktop/src-tauri/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,40 @@ impl ShowCapWindow {
let title = CapWindowId::RecordingControls.title();
let should_protect = should_protect_window(app, &title);

let pos_x = ((monitor.size().width as f64) / monitor.scale_factor() - width) / 2.0;
let pos_y =
(monitor.size().height as f64) / monitor.scale_factor() - height - 120.0;

debug!(
"InProgressRecording window: monitor size={:?}, scale={}, pos=({}, {})",
monitor.size(),
monitor.scale_factor(),
pos_x,
pos_y
);

#[cfg(target_os = "macos")]
let window = {
self.window_builder(app, "/in-progress-recording")
.maximized(false)
.resizable(false)
.fullscreen(false)
.shadow(false)
.always_on_top(true)
.transparent(true)
.visible_on_all_workspaces(true)
.content_protected(should_protect)
.inner_size(width, height)
.position(pos_x, pos_y)
.skip_taskbar(true)
.initialization_script(format!(
"window.COUNTDOWN = {};",
countdown.unwrap_or_default()
))
.build()?
};

#[cfg(windows)]
let window = self
.window_builder(app, "/in-progress-recording")
.maximized(false)
Expand All @@ -719,23 +753,47 @@ impl ShowCapWindow {
.visible_on_all_workspaces(true)
.content_protected(should_protect)
.inner_size(width, height)
.position(
((monitor.size().width as f64) / monitor.scale_factor() - width) / 2.0,
(monitor.size().height as f64) / monitor.scale_factor() - height - 120.0,
)
.skip_taskbar(true)
.position(pos_x, pos_y)
.skip_taskbar(false)
.initialization_script(format!(
"window.COUNTDOWN = {};",
countdown.unwrap_or_default()
))
.build()?;

debug!(
"InProgressRecording window created: label={}, inner_size={:?}, outer_position={:?}",
window.label(),
window.inner_size(),
window.outer_position()
);

#[cfg(target_os = "macos")]
{
crate::platform::set_window_level(window.as_ref().window(), 1000);
}

fake_window::spawn_fake_window_listener(app.clone(), window.clone());
#[cfg(target_os = "macos")]
{
let show_result = window.show();
debug!(
"InProgressRecording window.show() result: {:?}",
show_result
);
fake_window::spawn_fake_window_listener(app.clone(), window.clone());
}

#[cfg(windows)]
{
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let show_result = window.show();
debug!(
"InProgressRecording window.show() result: {:?}",
show_result
);
window.set_focus().ok();
fake_window::spawn_fake_window_listener(app.clone(), window.clone());
}

window
}
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import titlebar from "./utils/titlebar-state";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
experimental_prefetchInRender: true,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop/src/routes/editor/Player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -485,11 +485,19 @@ function PreviewCanvas() {
}
});

const isWindows = navigator.userAgent.includes("Windows");

const initCanvas = (canvas: HTMLCanvasElement) => {
if (canvasTransferredRef.current) return;
const controls = canvasControls();
if (!controls) return;

if (isWindows) {
controls.initDirectCanvas(canvas);
canvasTransferredRef.current = true;
return;
}

try {
const offscreen = canvas.transferControlToOffscreen();
controls.initCanvas(offscreen);
Expand Down
25 changes: 23 additions & 2 deletions apps/desktop/src/routes/in-progress-recording.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
createSignal,
For,
onCleanup,
onMount,
Show,
} from "solid-js";
import { createStore, produce } from "solid-js/store";
Expand Down Expand Up @@ -60,6 +61,17 @@ const NO_WEBCAM = "No Webcam";
const FAKE_WINDOW_BOUNDS_NAME = "recording-controls-interactive-area";

export default function () {
console.log("[in-progress-recording] Wrapper rendering");

document.documentElement.setAttribute("data-transparent-window", "true");
document.body.style.background = "transparent";

return <InProgressRecordingInner />;
}

function InProgressRecordingInner() {
console.log("[in-progress-recording] Inner component rendering");

const [state, setState] = createSignal<State>(
window.COUNTDOWN === 0
? { variant: "initializing" }
Expand All @@ -75,7 +87,16 @@ export default function () {
const optionsQuery = createOptionsQuery();
const startedWithMicrophone = optionsQuery.rawOptions.micName != null;
const startedWithCameraInput = optionsQuery.rawOptions.cameraID != null;
const auth = authStore.createQuery();

const [authData, setAuthData] = createSignal<{
plan?: { upgraded?: boolean };
} | null>(null);
onMount(() => {
authStore
.get()
.then(setAuthData)
.catch(() => setAuthData(null));
});

const audioLevel = createAudioInputLevel();
const [disconnectedInputs, setDisconnectedInputs] =
Expand Down Expand Up @@ -498,7 +519,7 @@ export default function () {
return (
optionsQuery.rawOptions.mode === "instant" &&
// If the data is loaded and the user is not upgraded
auth.data?.plan?.upgraded === false
authData()?.plan?.upgraded === false
);
};

Expand Down
Loading