use {
super::Op,
crate::{
color::AlphaColor,
gpu::{
def::{ColorRenderPassMode, Graphics, GraphicsMode, RenderPassMode},
driver::{
bind_graphics_descriptor_set, CommandPool, Device, Driver, Fence, Framebuffer2d,
},
pool::{Lease, Pool},
Texture2d,
},
math::Coord,
},
gfx_hal::{
command::{CommandBuffer as _, CommandBufferFlags, ImageCopy, Level, SubpassContents},
format::Aspects,
image::{Access as ImageAccess, Layout, Offset, SubresourceLayers, Usage as ImageUsage},
pool::CommandPool as _,
pso::{PipelineStage, Rect, Viewport},
queue::{CommandQueue as _, Submission},
Backend,
},
gfx_impl::Backend as _Backend,
std::{
any::Any,
iter::{empty, once},
},
};
type Path = [(Coord, AlphaColor); 2];
fn graphics_mode(preserve_dst: bool) -> GraphicsMode {
if preserve_dst {
GraphicsMode::Gradient(true)
} else {
GraphicsMode::Gradient(false)
}
}
fn must_preserve_dst(path: &Path) -> bool {
path[0].1.is_transparent() || path[1].1.is_transparent()
}
pub struct GradientOp {
back_buf: Lease<Texture2d>,
cmd_buf: <_Backend as Backend>::CommandBuffer,
cmd_pool: Lease<CommandPool>,
driver: Driver,
dst: Texture2d,
dst_preserve: bool,
fence: Lease<Fence>,
frame_buf: Framebuffer2d,
graphics: Lease<Graphics>,
pool: Option<Lease<Pool>>,
path: [(Coord, AlphaColor); 2],
}
impl GradientOp {
#[must_use]
pub(crate) fn new(
#[cfg(feature = "debug-names")] name: &str,
driver: &Driver,
mut pool: Lease<Pool>,
dst: &Texture2d,
path: Path,
) -> Self {
let family = Device::queue_family(&driver.borrow());
let mut cmd_pool = pool.cmd_pool(driver, family);
let (dims, fmt) = {
let dst = dst.borrow();
(dst.dims(), dst.format())
};
let render_pass_mode = RenderPassMode::Color(ColorRenderPassMode {
fmt,
preserve: must_preserve_dst(&path),
});
let graphics = pool.graphics(
#[cfg(feature = "debug-names")]
name,
driver,
render_pass_mode,
0,
GraphicsMode::Gradient(false),
);
let back_buf = pool.texture(
#[cfg(feature = "debug-names")]
name,
driver,
dims,
fmt,
Layout::Undefined,
ImageUsage::COLOR_ATTACHMENT | ImageUsage::INPUT_ATTACHMENT,
1,
1,
1,
);
let frame_buf = Framebuffer2d::new(
#[cfg(feature = "debug-names")]
name,
driver,
pool.render_pass(&driver, render_pass_mode),
once(back_buf.borrow().as_default_view().as_ref()),
dims,
);
let fence = pool.fence(
#[cfg(feature = "debug-names")]
name,
&driver,
);
Self {
back_buf,
cmd_buf: unsafe { cmd_pool.allocate_one(Level::Primary) },
cmd_pool,
driver: Driver::clone(driver),
dst: Texture2d::clone(dst),
dst_preserve: false,
fence,
frame_buf,
graphics,
pool: Some(pool),
path,
}
}
#[must_use]
pub fn with_preserve(&mut self, val: bool) -> &mut Self {
self.dst_preserve = val;
self
}
pub fn record(&mut self) {
{
}
unsafe {
self.submit();
}
}
unsafe fn submit(&mut self) {
trace!("submit");
let mut device = self.driver.borrow_mut();
let mut dst = self.dst.borrow_mut();
let mut back_buf = self.back_buf.borrow_mut();
let preserve = self.dst_preserve && must_preserve_dst(&self.path);
let _mode = RenderPassMode::Color(ColorRenderPassMode {
fmt: dst.format(),
preserve,
});
let dims = dst.dims();
let rect = Rect {
x: 0,
y: 0,
w: dims.x as _,
h: dims.y as _,
};
let viewport = Viewport {
rect,
depth: 0.0..1.0,
};
self.cmd_buf
.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT);
if preserve {
dst.set_layout(
&mut self.cmd_buf,
Layout::TransferSrcOptimal,
PipelineStage::TRANSFER,
ImageAccess::TRANSFER_READ,
);
back_buf.set_layout(
&mut self.cmd_buf,
Layout::TransferDstOptimal,
PipelineStage::TRANSFER,
ImageAccess::TRANSFER_WRITE,
);
self.cmd_buf.copy_image(
dst.as_ref(),
Layout::TransferSrcOptimal,
back_buf.as_ref(),
Layout::TransferDstOptimal,
once(ImageCopy {
src_subresource: SubresourceLayers {
aspects: Aspects::COLOR,
level: 0,
layers: 0..1,
},
src_offset: Offset::ZERO,
dst_subresource: SubresourceLayers {
aspects: Aspects::COLOR,
level: 0,
layers: 0..1,
},
dst_offset: Offset::ZERO,
extent: dims.as_extent_depth(1),
}),
);
}
back_buf.set_layout(
&mut self.cmd_buf,
Layout::ColorAttachmentOptimal,
PipelineStage::COLOR_ATTACHMENT_OUTPUT,
ImageAccess::COLOR_ATTACHMENT_WRITE,
);
if preserve {
dst.set_layout(
&mut self.cmd_buf,
Layout::ShaderReadOnlyOptimal,
PipelineStage::FRAGMENT_SHADER,
ImageAccess::SHADER_READ,
);
}
let _ = SubpassContents::Inline;
self.cmd_buf
.bind_graphics_pipeline(self.graphics.pipeline());
if preserve {
bind_graphics_descriptor_set(
&mut self.cmd_buf,
self.graphics.layout(),
self.graphics.desc_set(0),
);
}
self.cmd_buf.set_scissors(0, &[rect]);
self.cmd_buf.set_viewports(0, &[viewport]);
self.cmd_buf.draw(0..6, 0..1);
self.cmd_buf.end_render_pass();
back_buf.set_layout(
&mut self.cmd_buf,
Layout::TransferSrcOptimal,
PipelineStage::TRANSFER,
ImageAccess::TRANSFER_READ,
);
dst.set_layout(
&mut self.cmd_buf,
Layout::TransferDstOptimal,
PipelineStage::TRANSFER,
ImageAccess::TRANSFER_WRITE,
);
self.cmd_buf.copy_image(
back_buf.as_ref(),
Layout::TransferSrcOptimal,
dst.as_ref(),
Layout::TransferDstOptimal,
once(ImageCopy {
src_subresource: SubresourceLayers {
aspects: Aspects::COLOR,
level: 0,
layers: 0..1,
},
src_offset: Offset::ZERO,
dst_subresource: SubresourceLayers {
aspects: Aspects::COLOR,
level: 0,
layers: 0..1,
},
dst_offset: Offset::ZERO,
extent: dims.as_extent_depth(1),
}),
);
self.cmd_buf.finish();
Device::queue_mut(&mut device).submit(
Submission {
command_buffers: once(&self.cmd_buf),
wait_semaphores: empty(),
signal_semaphores: empty::<&<_Backend as Backend>::Semaphore>(),
},
Some(&self.fence),
);
}
}
impl Drop for GradientOp {
fn drop(&mut self) {
self.wait();
}
}
impl Op for GradientOp {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn take_pool(&mut self) -> Option<Lease<Pool>> {
self.pool.take()
}
fn wait(&self) {
Fence::wait(&self.fence);
}
}