wgpu: Fixed panic when rendering texture to itself, by always rendering to an intermediary
This commit is contained in:
parent
68343369a3
commit
c85910b46d
|
@ -402,28 +402,21 @@ mod wrapper {
|
|||
}
|
||||
|
||||
pub fn render(&self, smoothing: bool, context: &mut RenderContext<'_, 'gc>) {
|
||||
// if try_write fails,
|
||||
// this is caused by recursive render attempt. TODO: support this.
|
||||
if let Ok(mut inner_bitmap_data) = self.0.try_write(context.gc_context) {
|
||||
if inner_bitmap_data.disposed() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note - we do a CPU -> GPU sync, but we do *not* do a GPU -> CPU sync
|
||||
// (rendering is done on the GPU, so the CPU pixels don't need to be up-to-date).
|
||||
inner_bitmap_data.update_dirty_texture(context.renderer);
|
||||
let handle = inner_bitmap_data
|
||||
.bitmap_handle(context.renderer)
|
||||
.expect("Missing bitmap handle");
|
||||
|
||||
context.commands.render_bitmap(
|
||||
handle,
|
||||
context.transform_stack.transform(),
|
||||
smoothing,
|
||||
);
|
||||
} else {
|
||||
//this is caused by recursive render attempt. TODO: support this.
|
||||
let mut inner_bitmap_data = self.0.write(context.gc_context);
|
||||
if inner_bitmap_data.disposed() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note - we do a CPU -> GPU sync, but we do *not* do a GPU -> CPU sync
|
||||
// (rendering is done on the GPU, so the CPU pixels don't need to be up-to-date).
|
||||
inner_bitmap_data.update_dirty_texture(context.renderer);
|
||||
let handle = inner_bitmap_data
|
||||
.bitmap_handle(context.renderer)
|
||||
.expect("Missing bitmap handle");
|
||||
|
||||
context
|
||||
.commands
|
||||
.render_bitmap(handle, context.transform_stack.transform(), smoothing);
|
||||
}
|
||||
|
||||
pub fn is_point_in_bounds(&self, x: i32, y: i32) -> bool {
|
||||
|
|
|
@ -111,27 +111,25 @@ impl Surface {
|
|||
|
||||
let mut buffers = vec![draw_encoder.finish()];
|
||||
|
||||
if let RenderTargetMode::FreshBuffer(_) = render_target_mode {
|
||||
let mut copy_encoder =
|
||||
descriptors
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: create_debug_label!("Frame copy command encoder").as_deref(),
|
||||
});
|
||||
run_copy_pipeline(
|
||||
descriptors,
|
||||
self.format,
|
||||
self.actual_surface_format,
|
||||
self.size,
|
||||
frame_view,
|
||||
target.color_view(),
|
||||
target.whole_frame_bind_group(descriptors),
|
||||
target.globals(),
|
||||
1,
|
||||
&mut copy_encoder,
|
||||
);
|
||||
buffers.push(copy_encoder.finish());
|
||||
}
|
||||
let mut copy_encoder =
|
||||
descriptors
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: create_debug_label!("Frame copy command encoder").as_deref(),
|
||||
});
|
||||
run_copy_pipeline(
|
||||
descriptors,
|
||||
self.format,
|
||||
self.actual_surface_format,
|
||||
self.size,
|
||||
frame_view,
|
||||
target.color_view(),
|
||||
target.whole_frame_bind_group(descriptors),
|
||||
target.globals(),
|
||||
1,
|
||||
&mut copy_encoder,
|
||||
);
|
||||
buffers.push(copy_encoder.finish());
|
||||
|
||||
buffers.insert(0, uniform_encoder.finish());
|
||||
uniform_buffer.finish();
|
||||
|
|
|
@ -28,6 +28,7 @@ impl ResolveBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn new_manual(texture: Arc<wgpu::Texture>) -> Self {
|
||||
Self {
|
||||
texture: PoolOrArcTexture::Manual((
|
||||
|
@ -69,6 +70,7 @@ pub struct FrameBuffer {
|
|||
/// (when doing an offscreen render to a BitmapData texture)
|
||||
pub enum PoolOrArcTexture {
|
||||
Pool(PoolEntry<(wgpu::Texture, wgpu::TextureView), AlwaysCompatible>),
|
||||
#[allow(dead_code)]
|
||||
Manual((Arc<wgpu::Texture>, wgpu::TextureView)),
|
||||
}
|
||||
|
||||
|
@ -98,6 +100,7 @@ impl FrameBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn new_manual(texture: Arc<wgpu::Texture>, size: wgpu::Extent3d) -> Self {
|
||||
Self {
|
||||
texture: PoolOrArcTexture::Manual((
|
||||
|
@ -232,67 +235,49 @@ impl CommandTarget {
|
|||
|
||||
let whole_frame_bind_group = OnceCell::new();
|
||||
|
||||
let (frame_buffer, resolve_buffer) = match &render_target_mode {
|
||||
// In `FreshBuffer` mode, get a new frame buffer (and resolve buffer, if necessary)
|
||||
// from the pool. They will be cleared with the provided clear color
|
||||
// in `color_attachments`
|
||||
RenderTargetMode::FreshBuffer(_) => {
|
||||
let frame_buffer = make_pooled_frame_buffer();
|
||||
let resolve_buffer = if sample_count > 1 {
|
||||
Some(ResolveBuffer::new(
|
||||
descriptors,
|
||||
size,
|
||||
format,
|
||||
wgpu::TextureUsages::COPY_SRC
|
||||
| wgpu::TextureUsages::COPY_DST
|
||||
| wgpu::TextureUsages::TEXTURE_BINDING
|
||||
| wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
pool,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(frame_buffer, resolve_buffer)
|
||||
}
|
||||
// In `ExistingTexture` mode, we will use an existing texture
|
||||
// as either the frame buffer or resolve buffer.
|
||||
RenderTargetMode::ExistingTexture(texture) => {
|
||||
if sample_count > 1 {
|
||||
// The exising texture always has a sample count of 1,
|
||||
// so we need to create a new texture for the multisampled frame
|
||||
// buffer. Our existing texture will be used as the resolve buffer,
|
||||
// which is downsampled from the frame buffer.
|
||||
let frame_buffer = make_pooled_frame_buffer();
|
||||
|
||||
// Both our frame buffer and resolve buffer need to start out
|
||||
// in the same state, so copy our existing texture to the freshly
|
||||
// allocated frame buffer. We cannot use `copy_texture_to_texture`,
|
||||
// since the sample counts are different.
|
||||
run_copy_pipeline(
|
||||
descriptors,
|
||||
format,
|
||||
format,
|
||||
size,
|
||||
frame_buffer.texture.view(),
|
||||
&texture.create_view(&Default::default()),
|
||||
get_whole_frame_bind_group(&whole_frame_bind_group, descriptors, size),
|
||||
&globals,
|
||||
sample_count,
|
||||
encoder,
|
||||
);
|
||||
|
||||
(
|
||||
frame_buffer,
|
||||
Some(ResolveBuffer::new_manual(texture.clone())),
|
||||
)
|
||||
} else {
|
||||
// If multisampling is disabled, we don't need a resolve buffer.
|
||||
// We can just use our existing texture as the frame buffer.
|
||||
(FrameBuffer::new_manual(texture.clone(), size), None)
|
||||
}
|
||||
}
|
||||
let frame_buffer = make_pooled_frame_buffer();
|
||||
let resolve_buffer = if sample_count > 1 {
|
||||
Some(ResolveBuffer::new(
|
||||
descriptors,
|
||||
size,
|
||||
format,
|
||||
wgpu::TextureUsages::COPY_SRC
|
||||
| wgpu::TextureUsages::COPY_DST
|
||||
| wgpu::TextureUsages::TEXTURE_BINDING
|
||||
| wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
pool,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let RenderTargetMode::ExistingTexture(texture) = &render_target_mode {
|
||||
if sample_count > 1 {
|
||||
// Both our frame buffer and resolve buffer need to start out
|
||||
// in the same state, so copy our existing texture to the freshly
|
||||
// allocated frame buffer. We cannot use `copy_texture_to_texture`,
|
||||
// since the sample counts are different.
|
||||
run_copy_pipeline(
|
||||
descriptors,
|
||||
format,
|
||||
format,
|
||||
size,
|
||||
frame_buffer.texture.view(),
|
||||
&texture.create_view(&Default::default()),
|
||||
get_whole_frame_bind_group(&whole_frame_bind_group, descriptors, size),
|
||||
&globals,
|
||||
sample_count,
|
||||
encoder,
|
||||
);
|
||||
} else {
|
||||
encoder.copy_texture_to_texture(
|
||||
texture.as_image_copy(),
|
||||
frame_buffer.texture().as_image_copy(),
|
||||
size,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
frame_buffer,
|
||||
blend_buffer: OnceCell::new(),
|
||||
|
|
Loading…
Reference in New Issue