Skip to content

Commit 2345caa

Browse files
Desktop: Fix Eyedropper tool (#3764)
1 parent f2dfd42 commit 2345caa

File tree

7 files changed

+75
-51
lines changed

7 files changed

+75
-51
lines changed

desktop/src/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl App {
7474
loop {
7575
let result = runtime.block_on(DesktopWrapper::execute_node_graph());
7676
rendering_app_event_scheduler.schedule(AppEvent::NodeGraphExecutionResult(result));
77-
let _ = start_render_receiver.recv();
77+
let _ = start_render_receiver.recv_timeout(Duration::from_millis(10));
7878
}
7979
});
8080

editor/src/dispatcher.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
5151
NodeGraphMessageDiscriminant::RunDocumentGraph,
5252
))),
5353
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::SubmitActiveGraphRender),
54+
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::SubmitEyedropperPreviewRender),
5455
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontDataLoad),
5556
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateUIScale),
5657
];

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
11851185
let Some(document) = self.documents.get_mut(&document_id) else { return };
11861186

11871187
let resolution = glam::UVec2::splat(EYEDROPPER_PREVIEW_AREA_RESOLUTION);
1188+
let scale = viewport.scale();
11881189

11891190
let preview_offset_in_viewport = ipp.mouse.position - (glam::DVec2::splat(EYEDROPPER_PREVIEW_AREA_RESOLUTION as f64 / 2.));
11901191
let preview_offset_in_viewport = DAffine2::from_translation(preview_offset_in_viewport);
@@ -1196,7 +1197,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
11961197

11971198
let result = self
11981199
.executor
1199-
.submit_eyedropper_preview(document_id, preview_transform, pointer_position, resolution, timing_information);
1200+
.submit_eyedropper_preview(document_id, preview_transform, pointer_position, resolution, scale, timing_information);
12001201

12011202
match result {
12021203
Err(description) => {

editor/src/messages/tool/tool_messages/eyedropper_tool.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Eyed
4848
if let ToolMessage::Eyedropper(EyedropperToolMessage::PreviewImage { data, width, height }) = message {
4949
let image = EyedropperPreviewImage { data, width, height };
5050

51-
update_cursor_preview_common(responses, Some(image), context.input, context.global_tool_data, self.data.color_choice.clone());
52-
5351
if !self.data.preview {
5452
disable_cursor_preview(responses, &mut self.data);
53+
} else {
54+
update_cursor_preview_common(responses, Some(image), context.input, context.global_tool_data, self.data.color_choice.clone());
5555
}
5656
return;
5757
}

editor/src/node_graph_executor.rs

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use graph_craft::proto::GraphErrors;
77
use graph_craft::wasm_application_io::EditorPreferences;
88
use graphene_std::application_io::{NodeGraphUpdateMessage, RenderConfig};
99
use graphene_std::application_io::{SurfaceFrame, TimingInformation};
10+
use graphene_std::raster::{CPU, Raster};
1011
use graphene_std::renderer::{RenderMetadata, format_transform_matrix};
1112
use graphene_std::text::FontCache;
1213
use graphene_std::transform::Footprint;
@@ -44,6 +45,7 @@ pub struct CompilationResponse {
4445
pub enum NodeGraphUpdate {
4546
ExecutionResponse(ExecutionResponse),
4647
CompilationResponse(CompilationResponse),
48+
EyedropperPreview(Raster<CPU>),
4749
NodeGraphUpdateMessage(NodeGraphUpdateMessage),
4850
}
4951

@@ -59,7 +61,6 @@ pub struct NodeGraphExecutor {
5961

6062
#[derive(Debug, Clone)]
6163
struct ExecutionContext {
62-
render_config: RenderConfig,
6364
export_config: Option<ExportConfig>,
6465
document_id: DocumentId,
6566
}
@@ -164,14 +165,7 @@ impl NodeGraphExecutor {
164165
// Execute the node graph
165166
let execution_id = self.queue_execution(render_config);
166167

167-
self.futures.push_back((
168-
execution_id,
169-
ExecutionContext {
170-
render_config,
171-
export_config: None,
172-
document_id,
173-
},
174-
));
168+
self.futures.push_back((execution_id, ExecutionContext { export_config: None, document_id }));
175169

176170
Ok(DeferMessage::SetGraphSubmissionIndex { execution_id }.into())
177171
}
@@ -194,15 +188,23 @@ impl NodeGraphExecutor {
194188
}
195189

196190
#[cfg(not(target_family = "wasm"))]
197-
pub(crate) fn submit_eyedropper_preview(&mut self, document_id: DocumentId, transform: DAffine2, pointer: DVec2, resolution: UVec2, time: TimingInformation) -> Result<Message, String> {
191+
pub(crate) fn submit_eyedropper_preview(
192+
&mut self,
193+
document_id: DocumentId,
194+
transform: DAffine2,
195+
pointer: DVec2,
196+
viewport_resolution: UVec2,
197+
viewport_scale: f64,
198+
time: TimingInformation,
199+
) -> Result<Message, String> {
198200
let viewport = Footprint {
199201
transform,
200-
resolution,
202+
resolution: viewport_resolution,
201203
..Default::default()
202204
};
203205
let render_config = RenderConfig {
204206
viewport,
205-
scale: 1.,
207+
scale: viewport_scale,
206208
time,
207209
pointer,
208210
export_format: graphene_std::application_io::ExportFormat::Raster,
@@ -215,14 +217,7 @@ impl NodeGraphExecutor {
215217
// Execute the node graph
216218
let execution_id = self.queue_execution(render_config);
217219

218-
self.futures.push_back((
219-
execution_id,
220-
ExecutionContext {
221-
render_config,
222-
export_config: None,
223-
document_id,
224-
},
225-
));
220+
self.futures.push_back((execution_id, ExecutionContext { export_config: None, document_id }));
226221

227222
Ok(DeferMessage::SetGraphSubmissionIndex { execution_id }.into())
228223
}
@@ -272,7 +267,6 @@ impl NodeGraphExecutor {
272267
self.futures.push_back((
273268
execution_id,
274269
ExecutionContext {
275-
render_config,
276270
export_config: Some(export_config),
277271
document_id,
278272
},
@@ -325,9 +319,6 @@ impl NodeGraphExecutor {
325319
if let Some(export_config) = execution_context.export_config {
326320
// Special handling for exporting the artwork
327321
self.process_export(node_graph_output, export_config, responses)?;
328-
} else if execution_context.render_config.for_eyedropper {
329-
// Special handling for Eyedropper tool preview
330-
self.process_eyedropper_preview(node_graph_output, responses)?;
331322
} else {
332323
self.process_node_graph_output(node_graph_output, responses)?;
333324
}
@@ -371,6 +362,10 @@ impl NodeGraphExecutor {
371362
});
372363
responses.add(NodeGraphMessage::SendGraph);
373364
}
365+
NodeGraphUpdate::EyedropperPreview(raster) => {
366+
let (data, width, height) = raster.to_flat_u8();
367+
responses.add(EyedropperToolMessage::PreviewImage { data, width, height });
368+
}
374369
}
375370
}
376371

@@ -431,23 +426,6 @@ impl NodeGraphExecutor {
431426
Ok(())
432427
}
433428

434-
fn process_eyedropper_preview(&self, node_graph_output: TaggedValue, responses: &mut VecDeque<Message>) -> Result<(), String> {
435-
match node_graph_output {
436-
#[cfg(feature = "gpu")]
437-
TaggedValue::RenderOutput(RenderOutput {
438-
data: RenderOutputType::Buffer { data, width, height },
439-
..
440-
}) => {
441-
responses.add(EyedropperToolMessage::PreviewImage { data, width, height });
442-
}
443-
_ => {
444-
// TODO: Support Eyedropper preview in SVG mode on desktop
445-
}
446-
};
447-
448-
Ok(())
449-
}
450-
451429
fn process_export(&self, node_graph_output: TaggedValue, export_config: ExportConfig, responses: &mut VecDeque<Message>) -> Result<(), String> {
452430
let ExportConfig {
453431
file_type,

editor/src/node_graph_executor/runtime.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ impl InternalNodeGraphUpdateSender {
9999
fn send_execution_response(&self, response: ExecutionResponse) {
100100
self.0.send(NodeGraphUpdate::ExecutionResponse(response)).expect("Failed to send response")
101101
}
102+
103+
fn send_eyedropper_preview(&self, raster: Raster<CPU>) {
104+
self.0.send(NodeGraphUpdate::EyedropperPreview(raster)).expect("Failed to send response")
105+
}
102106
}
103107

104108
impl NodeGraphUpdateSender for InternalNodeGraphUpdateSender {
@@ -159,13 +163,22 @@ impl NodeRuntime {
159163
let mut font = None;
160164
let mut preferences = None;
161165
let mut graph = None;
166+
let mut eyedropper = None;
162167
let mut execution = None;
163168
for request in self.receiver.try_iter() {
164169
match request {
165170
GraphRuntimeRequest::GraphUpdate(_) => graph = Some(request),
166171
GraphRuntimeRequest::ExecutionRequest(ref execution_request) => {
172+
if execution_request.render_config.for_eyedropper {
173+
eyedropper = Some(request);
174+
175+
continue;
176+
}
177+
167178
let for_export = execution_request.render_config.for_export;
179+
168180
execution = Some(request);
181+
169182
// If we get an export request we always execute it immedeatly otherwise it could get deduplicated
170183
if for_export {
171184
break;
@@ -175,7 +188,16 @@ impl NodeRuntime {
175188
GraphRuntimeRequest::EditorPreferencesUpdate(_) => preferences = Some(request),
176189
}
177190
}
178-
let requests = [font, preferences, graph, execution].into_iter().flatten();
191+
192+
// Eydropper should use the same time and pointer to not invalidate the cache
193+
if let Some(GraphRuntimeRequest::ExecutionRequest(eyedropper)) = &mut eyedropper
194+
&& let Some(GraphRuntimeRequest::ExecutionRequest(execution)) = &execution
195+
{
196+
eyedropper.render_config.time = execution.render_config.time;
197+
eyedropper.render_config.pointer = execution.render_config.pointer;
198+
}
199+
200+
let requests = [font, preferences, graph, eyedropper, execution].into_iter().flatten();
179201

180202
for request in requests {
181203
match request {
@@ -236,7 +258,9 @@ impl NodeRuntime {
236258
let result = self.execute_network(render_config).await;
237259
let mut responses = VecDeque::new();
238260
// TODO: Only process monitor nodes if the graph has changed, not when only the Footprint changes
239-
self.process_monitor_nodes(&mut responses, self.update_thumbnails);
261+
if !render_config.for_eyedropper {
262+
self.process_monitor_nodes(&mut responses, self.update_thumbnails);
263+
}
240264
self.update_thumbnails = false;
241265

242266
// Resolve the result from the inspection by accessing the monitor node
@@ -246,7 +270,7 @@ impl NodeRuntime {
246270
Ok(TaggedValue::RenderOutput(RenderOutput {
247271
data: RenderOutputType::Texture(image_texture),
248272
metadata,
249-
})) if render_config.for_export || render_config.for_eyedropper => {
273+
})) if render_config.for_export => {
250274
let executor = self
251275
.editor_api
252276
.application_io
@@ -267,6 +291,23 @@ impl NodeRuntime {
267291
None,
268292
)
269293
}
294+
Ok(TaggedValue::RenderOutput(RenderOutput {
295+
data: RenderOutputType::Texture(image_texture),
296+
metadata: _,
297+
})) if render_config.for_eyedropper => {
298+
let executor = self
299+
.editor_api
300+
.application_io
301+
.as_ref()
302+
.unwrap()
303+
.gpu_executor()
304+
.expect("GPU executor should be available when we receive a texture");
305+
306+
let raster_cpu = Raster::new_gpu(image_texture.texture).convert(Footprint::BOUNDLESS, executor).await;
307+
308+
self.sender.send_eyedropper_preview(raster_cpu);
309+
continue;
310+
}
270311
#[cfg(all(target_family = "wasm", feature = "gpu"))]
271312
Ok(TaggedValue::RenderOutput(RenderOutput {
272313
data: RenderOutputType::Texture(image_texture),

node-graph/libraries/wgpu-executor/src/texture_conversion.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,15 @@ impl RasterGpuToRasterCpuConverter {
100100
}
101101
}
102102

103-
async fn convert(self) -> Result<Raster<CPU>, wgpu::BufferAsyncError> {
103+
async fn convert(self, device: &std::sync::Arc<wgpu::Device>) -> Result<Raster<CPU>, wgpu::BufferAsyncError> {
104104
let buffer_slice = self.buffer.slice(..);
105105
let (sender, receiver) = futures::channel::oneshot::channel();
106106
buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
107107
let _ = sender.send(result);
108108
});
109+
110+
let _ = device.poll(wgpu::wgt::PollType::wait_indefinitely());
111+
109112
receiver.await.expect("Failed to receive map result")?;
110113

111114
let view = buffer_slice.get_mapped_range();
@@ -215,7 +218,7 @@ impl<'i> Convert<Table<Raster<CPU>>, &'i WgpuExecutor> for Table<Raster<GPU>> {
215218

216219
let mut map_futures = Vec::new();
217220
for converter in converters {
218-
map_futures.push(converter.convert());
221+
map_futures.push(converter.convert(device));
219222
}
220223

221224
let map_results = futures::future::try_join_all(map_futures)
@@ -250,7 +253,7 @@ impl<'i> Convert<Raster<CPU>, &'i WgpuExecutor> for Raster<GPU> {
250253

251254
queue.submit([encoder.finish()]);
252255

253-
converter.convert().await.expect("Failed to download texture data")
256+
converter.convert(device).await.expect("Failed to download texture data")
254257
}
255258
}
256259

0 commit comments

Comments
 (0)