use crate::color::IntoLinSrgba;
use crate::vk::{self, DeviceOwned};
use std::error::Error as StdError;
use std::sync::Arc;
use std::{fmt, ops};
pub mod raw;
pub use self::raw::{AddCommands, RawFrame};
pub const COLOR_FORMAT: vk::Format = vk::Format::R16G16B16A16Unorm;
pub struct Frame {
raw_frame: RawFrame,
data: RenderData,
}
#[derive(Default)]
pub struct ViewFramebufferObject {
fbo: vk::Fbo,
}
pub type ViewFbo = ViewFramebufferObject;
pub(crate) struct RenderData {
intermediary: IntermediaryData,
}
struct IntermediaryData {
images: IntermediaryImages,
images_are_new: bool,
resolve_render_pass: Option<Arc<dyn vk::RenderPassAbstract + Send + Sync>>,
resolve_framebuffer: vk::Fbo,
}
struct IntermediaryImages {
lin_srgba_msaa: Option<Arc<vk::AttachmentImage>>,
lin_srgba: Arc<vk::AttachmentImage>,
}
#[derive(Copy, Clone, Debug, Default)]
struct Vertex {
position: [f32; 2],
}
vk::impl_vertex!(Vertex, position);
#[derive(Debug)]
pub enum RenderDataCreationError {
RenderPassCreation(vk::RenderPassCreationError),
ImageCreation(vk::ImageCreationError),
GraphicsPipelineCreation(GraphicsPipelineError),
DeviceMemoryAlloc(vk::memory::DeviceMemoryAllocError),
SamplerCreation(vk::SamplerCreationError),
}
#[derive(Debug)]
pub enum FrameCreationError {
ImageCreation(vk::ImageCreationError),
FramebufferCreation(vk::FramebufferCreationError),
}
#[derive(Debug)]
pub enum FrameFinishError {
BeginRenderPass(vk::command_buffer::BeginRenderPassError),
}
#[derive(Debug)]
pub enum GraphicsPipelineError {
Creation(vk::GraphicsPipelineCreationError),
VertexShaderLoad(vk::OomError),
FragmentShaderLoad(vk::OomError),
}
impl IntermediaryImages {
fn dimensions(&self) -> [u32; 2] {
vk::AttachmentImage::dimensions(&self.lin_srgba)
}
}
impl Frame {
pub const DEFAULT_MSAA_SAMPLES: u32 = 8;
pub(crate) fn new_empty(
raw_frame: RawFrame,
mut data: RenderData,
) -> Result<Self, FrameCreationError> {
let image_dims = raw_frame.swapchain_image().dimensions();
if data.intermediary.images.dimensions() != image_dims {
let msaa_samples = data
.intermediary
.images
.lin_srgba_msaa
.as_ref()
.map(|img| vk::image::ImageAccess::samples(img))
.unwrap_or(1);
data.intermediary.images = create_intermediary_images(
raw_frame.swapchain_image().swapchain().device().clone(),
image_dims,
msaa_samples,
COLOR_FORMAT,
)?;
data.intermediary.images_are_new = true;
}
{
let RenderData {
intermediary:
IntermediaryData {
ref images,
ref mut resolve_framebuffer,
ref resolve_render_pass,
..
},
} = data;
let [w, h] = images.dimensions();
let dims = [w, h, 1];
if let Some(msaa_img) = images.lin_srgba_msaa.as_ref() {
if let Some(rp) = resolve_render_pass.as_ref() {
resolve_framebuffer.update(rp.clone(), dims, |builder| {
builder.add(msaa_img.clone())?.add(images.lin_srgba.clone())
})?;
}
}
}
Ok(Frame { raw_frame, data })
}
pub(crate) fn finish(self) -> Result<(RenderData, RawFrame), FrameFinishError> {
let Frame {
mut data,
raw_frame,
} = self;
let clear_values = vec![vk::ClearValue::None, vk::ClearValue::None];
let is_secondary = false;
if let Some(fbo) = data.intermediary.resolve_framebuffer.as_ref() {
raw_frame
.add_commands()
.begin_render_pass(fbo.clone(), is_secondary, clear_values)?
.end_render_pass()
.expect("failed to add `end_render_pass` command");
}
let [w, h] = data.intermediary.images.dimensions();
let src = data.intermediary.images.lin_srgba.clone();
let src_tl = [0; 3];
let src_br = [w as i32, h as i32, 1];
let src_base_layer = 0;
let src_mip_level = 0;
let dst = raw_frame.swapchain_image().clone();
let dst_tl = [0; 3];
let dst_br = [w as i32, h as i32, 1];
let dst_base_layer = 0;
let dst_mip_level = 0;
let layer_count = 1;
let filter = vk::sampler::Filter::Linear;
raw_frame
.add_commands()
.blit_image(
src,
src_tl,
src_br,
src_base_layer,
src_mip_level,
dst,
dst_tl,
dst_br,
dst_base_layer,
dst_mip_level,
layer_count,
filter,
)
.expect("failed to blit linear sRGBA image to swapchain image");
data.intermediary.images_are_new = false;
Ok((data, raw_frame))
}
pub fn image(&self) -> &Arc<vk::AttachmentImage> {
match self.data.intermediary.images.lin_srgba_msaa.as_ref() {
None => &self.data.intermediary.images.lin_srgba,
Some(msaa_img) => msaa_img,
}
}
pub fn image_is_new(&self) -> bool {
self.raw_frame.nth() == 0 || self.data.intermediary.images_are_new
}
pub fn image_format(&self) -> vk::Format {
vk::image::ImageAccess::format(self.image())
}
pub fn image_msaa_samples(&self) -> u32 {
vk::image::ImageAccess::samples(self.image())
}
pub fn clear<C>(&self, color: C)
where
C: IntoLinSrgba<f32>,
{
let lin_srgba = color.into_lin_srgba();
let (r, g, b, a) = lin_srgba.into_components();
let value = vk::ClearValue::Float([r, g, b, a]);
let image = self.image().clone();
self.add_commands()
.clear_color_image(image, value)
.expect("failed to submit `clear_color_image` command");
}
}
impl ViewFramebufferObject {
pub fn update<R, F, A>(
&mut self,
frame: &Frame,
render_pass: R,
builder: F,
) -> Result<(), vk::FramebufferCreationError>
where
R: 'static + vk::RenderPassAbstract + Send + Sync,
F: FnOnce(
vk::FramebufferBuilder<R, ()>,
Arc<vk::AttachmentImage>,
) -> vk::FramebufferBuilderResult<R, A>,
A: 'static + vk::AttachmentsList + Send + Sync,
{
let image = frame.image().clone();
let [w, h] = image.dimensions();
let dimensions = [w, h, 1];
self.fbo
.update(render_pass, dimensions, |b| builder(b, image))
}
}
impl RenderData {
pub(crate) fn new(
device: Arc<vk::Device>,
dimensions: [u32; 2],
msaa_samples: u32,
) -> Result<Self, RenderDataCreationError> {
let intermediary_images =
create_intermediary_images(device.clone(), dimensions, msaa_samples, COLOR_FORMAT)?;
let resolve_render_pass = create_resolve_render_pass(device.clone(), msaa_samples)?;
let resolve_framebuffer = Default::default();
let intermediary = IntermediaryData {
images: intermediary_images,
images_are_new: true,
resolve_render_pass,
resolve_framebuffer,
};
Ok(RenderData { intermediary })
}
}
impl ops::Deref for Frame {
type Target = RawFrame;
fn deref(&self) -> &Self::Target {
&self.raw_frame
}
}
impl ops::Deref for ViewFramebufferObject {
type Target = vk::Fbo;
fn deref(&self) -> &Self::Target {
&self.fbo
}
}
impl From<vk::RenderPassCreationError> for RenderDataCreationError {
fn from(err: vk::RenderPassCreationError) -> Self {
RenderDataCreationError::RenderPassCreation(err)
}
}
impl From<vk::ImageCreationError> for RenderDataCreationError {
fn from(err: vk::ImageCreationError) -> Self {
RenderDataCreationError::ImageCreation(err)
}
}
impl From<GraphicsPipelineError> for RenderDataCreationError {
fn from(err: GraphicsPipelineError) -> Self {
RenderDataCreationError::GraphicsPipelineCreation(err)
}
}
impl From<vk::memory::DeviceMemoryAllocError> for RenderDataCreationError {
fn from(err: vk::memory::DeviceMemoryAllocError) -> Self {
RenderDataCreationError::DeviceMemoryAlloc(err)
}
}
impl From<vk::SamplerCreationError> for RenderDataCreationError {
fn from(err: vk::SamplerCreationError) -> Self {
RenderDataCreationError::SamplerCreation(err)
}
}
impl From<vk::ImageCreationError> for FrameCreationError {
fn from(err: vk::ImageCreationError) -> Self {
FrameCreationError::ImageCreation(err)
}
}
impl From<vk::FramebufferCreationError> for FrameCreationError {
fn from(err: vk::FramebufferCreationError) -> Self {
FrameCreationError::FramebufferCreation(err)
}
}
impl From<vk::command_buffer::BeginRenderPassError> for FrameFinishError {
fn from(err: vk::command_buffer::BeginRenderPassError) -> Self {
FrameFinishError::BeginRenderPass(err)
}
}
impl From<vk::GraphicsPipelineCreationError> for GraphicsPipelineError {
fn from(err: vk::GraphicsPipelineCreationError) -> Self {
GraphicsPipelineError::Creation(err)
}
}
impl StdError for RenderDataCreationError {
fn description(&self) -> &str {
match *self {
RenderDataCreationError::RenderPassCreation(ref err) => err.description(),
RenderDataCreationError::ImageCreation(ref err) => err.description(),
RenderDataCreationError::GraphicsPipelineCreation(ref err) => err.description(),
RenderDataCreationError::DeviceMemoryAlloc(ref err) => err.description(),
RenderDataCreationError::SamplerCreation(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&dyn StdError> {
match *self {
RenderDataCreationError::RenderPassCreation(ref err) => Some(err),
RenderDataCreationError::ImageCreation(ref err) => Some(err),
RenderDataCreationError::GraphicsPipelineCreation(ref err) => Some(err),
RenderDataCreationError::DeviceMemoryAlloc(ref err) => Some(err),
RenderDataCreationError::SamplerCreation(ref err) => Some(err),
}
}
}
impl StdError for FrameCreationError {
fn description(&self) -> &str {
match *self {
FrameCreationError::ImageCreation(ref err) => err.description(),
FrameCreationError::FramebufferCreation(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&dyn StdError> {
match *self {
FrameCreationError::ImageCreation(ref err) => Some(err),
FrameCreationError::FramebufferCreation(ref err) => Some(err),
}
}
}
impl StdError for FrameFinishError {
fn description(&self) -> &str {
match *self {
FrameFinishError::BeginRenderPass(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&dyn StdError> {
match *self {
FrameFinishError::BeginRenderPass(ref err) => Some(err),
}
}
}
impl StdError for GraphicsPipelineError {
fn description(&self) -> &str {
match *self {
GraphicsPipelineError::Creation(ref err) => err.description(),
GraphicsPipelineError::VertexShaderLoad(ref err) => err.description(),
GraphicsPipelineError::FragmentShaderLoad(ref err) => err.description(),
}
}
}
impl fmt::Display for RenderDataCreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl fmt::Display for FrameCreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl fmt::Display for FrameFinishError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl fmt::Debug for RenderData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RenderData")
}
}
impl fmt::Display for GraphicsPipelineError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
fn create_intermediary_images(
device: Arc<vk::Device>,
dimensions: [u32; 2],
msaa_samples: u32,
format: vk::Format,
) -> Result<IntermediaryImages, vk::ImageCreationError> {
let lin_srgba_msaa = match msaa_samples {
0 | 1 => None,
_ => {
let usage = vk::ImageUsage {
transfer_source: true,
transfer_destination: true,
color_attachment: true,
..vk::ImageUsage::none()
};
Some(vk::AttachmentImage::multisampled_with_usage(
device.clone(),
dimensions,
msaa_samples,
format,
usage,
)?)
}
};
let usage = vk::ImageUsage {
transfer_source: true,
transfer_destination: true,
color_attachment: true,
sampled: true,
..vk::ImageUsage::none()
};
let lin_srgba = vk::AttachmentImage::with_usage(device, dimensions, format, usage)?;
Ok(IntermediaryImages {
lin_srgba_msaa,
lin_srgba,
})
}
fn create_resolve_render_pass(
device: Arc<vk::Device>,
msaa_samples: u32,
) -> Result<Option<Arc<dyn vk::RenderPassAbstract + Send + Sync>>, vk::RenderPassCreationError> {
match msaa_samples {
0 | 1 => Ok(None),
_ => {
let rp = vk::single_pass_renderpass!(
device,
attachments: {
lin_srgba_msaa: {
load: Load,
store: Store,
format: COLOR_FORMAT,
samples: msaa_samples,
},
lin_srgba: {
load: DontCare,
store: Store,
format: COLOR_FORMAT,
samples: 1,
}
},
pass: {
color: [lin_srgba_msaa],
depth_stencil: {}
resolve: [lin_srgba],
}
)?;
Ok(Some(
Arc::new(rp) as Arc<dyn vk::RenderPassAbstract + Send + Sync>
))
}
}
}