use ash::vk;
use concinnity_core::gfx::render_types::{CompositeParams, TextDrawCall, TextUniforms};
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::fullscreen;
use concinnity_core::render::fullscreen::TextBindCache;
use super::super::context::VkContext;
use super::super::record::cmd_push_constants;
use super::super::upload_ring::UPLOAD_ALIGN;
pub(crate) struct VkCompositeArgs {
image_index: usize,
frame_idx: usize,
reactive_valid: bool,
}
impl fullscreen::CompositeEncoder for VkContext {
type Rec = vk::CommandBuffer;
type Args = VkCompositeArgs;
fn begin_composite(&self, cmd: &Self::Rec, args: &Self::Args) {
let device = &self.hw.device;
let extent = self.swapchain.extent;
let composite_begin = vk::RenderPassBeginInfo::default()
.render_pass(self.composite.render_pass.handle())
.framebuffer(self.composite.framebuffers[args.image_index].handle())
.render_area(vk::Rect2D::default().extent(extent));
let composite_vp = vk::Viewport {
x: 0.0,
y: 0.0,
width: extent.width as f32,
height: extent.height as f32,
min_depth: 0.0,
max_depth: 1.0,
};
let scissor = vk::Rect2D::default().extent(extent);
unsafe {
device.cmd_begin_render_pass(*cmd, &composite_begin, vk::SubpassContents::INLINE);
device.cmd_set_viewport(*cmd, 0, std::slice::from_ref(&composite_vp));
device.cmd_set_scissor(*cmd, 0, std::slice::from_ref(&scissor));
}
}
fn composite_draw(&self, cmd: &Self::Rec, args: &Self::Args) {
let device = &self.hw.device;
unsafe {
device.cmd_bind_pipeline(
*cmd,
vk::PipelineBindPoint::GRAPHICS,
self.composite.pipeline.handle(),
);
device.cmd_bind_descriptor_sets(
*cmd,
vk::PipelineBindPoint::GRAPHICS,
self.composite.pipeline_layout.handle(),
0,
std::slice::from_ref(&self.composite.sets[args.frame_idx]),
&[],
);
let (depth_near, depth_far) = CompositeParams::depth_view_range(
self.state.view.near,
self.state.view.view_distance,
);
let composite = CompositeParams {
post: self.post_process,
fade: self.state.view.scene_fade,
view_mode: if self.state.view.mode.is_gbuffer_channel() {
self.state.view.mode as u32
} else {
0
},
depth_near,
depth_far,
reactive_valid: if args.reactive_valid { 1.0 } else { 0.0 },
};
cmd_push_constants(
device,
*cmd,
self.composite.pipeline_layout.handle(),
vk::ShaderStageFlags::FRAGMENT,
&composite,
);
device.cmd_draw(*cmd, 3, 1, 0, 0);
}
self.inc_draw_calls(1);
}
fn begin_text(&self, cmd: &Self::Rec, _args: &Self::Args) -> bool {
let Some(text_pipeline) = self.text.pipeline.as_ref() else {
return false;
};
if self.text.atlas_textures.is_empty() {
return false;
}
let ui = self.logical_size();
let text_push = TextUniforms {
win_width: ui.0,
win_height: ui.1,
_pad: [0.0; 2],
};
unsafe {
self.hw.device.cmd_bind_pipeline(
*cmd,
vk::PipelineBindPoint::GRAPHICS,
text_pipeline.handle(),
);
cmd_push_constants(
&self.hw.device,
*cmd,
self.text.pipeline_layout.handle(),
vk::ShaderStageFlags::VERTEX,
&text_push,
);
}
true
}
fn text_draw(
&self,
cmd: &Self::Rec,
args: &Self::Args,
_idx: usize,
call: &TextDrawCall,
binds: &mut TextBindCache,
) -> RenderResult<()> {
if call.vertices.is_empty() || self.text.atlas_sets.is_empty() {
return Ok(());
}
let device = &self.hw.device;
let extent = self.swapchain.extent;
let ui = self.logical_size();
let scissor = match call.clip_rect {
Some(clip) => {
match fullscreen::clip_rect_to_scissor(clip, ui, (extent.width, extent.height)) {
None => return Ok(()),
Some(rect) => rect,
}
}
None => (0, 0, extent.width, extent.height),
};
let atlas_idx = call.atlas_slot.min(self.text.atlas_sets.len() - 1);
let (vert_buf, vert_offset) = self
.text
.upload
.push(args.frame_idx, bytemuck::cast_slice(&call.vertices))?;
let (idx_buf, idx_offset) = self
.text
.upload
.push(args.frame_idx, bytemuck::cast_slice(&call.indices))?;
unsafe {
if binds.atlas_changed(atlas_idx) {
device.cmd_bind_descriptor_sets(
*cmd,
vk::PipelineBindPoint::GRAPHICS,
self.text.pipeline_layout.handle(),
0,
std::slice::from_ref(&self.text.atlas_sets[atlas_idx]),
&[],
);
}
if binds.scissor_changed(scissor) {
let (x, y, width, height) = scissor;
let rect = vk::Rect2D {
offset: vk::Offset2D { x, y },
extent: vk::Extent2D { width, height },
};
device.cmd_set_scissor(*cmd, 0, std::slice::from_ref(&rect));
}
device.cmd_bind_vertex_buffers(*cmd, 0, &[vert_buf], &[vert_offset]);
device.cmd_bind_index_buffer(*cmd, idx_buf, idx_offset, vk::IndexType::UINT16);
device.cmd_draw_indexed(*cmd, call.indices.len() as u32, 1, 0, 0, 0);
}
self.inc_draw_calls(1);
Ok(())
}
fn end_composite(&self, cmd: &Self::Rec, _args: &Self::Args) {
unsafe { self.hw.device.cmd_end_render_pass(*cmd) };
}
}
impl VkContext {
pub(in crate::vulkan) fn encode_composite_and_text(
&self,
cmd: vk::CommandBuffer,
image_index: u32,
frame_idx: usize,
text_calls: &[TextDrawCall],
reactive_valid: bool,
) -> RenderResult<()> {
let text_bytes = fullscreen::text_upload_bytes(text_calls, UPLOAD_ALIGN);
self.text
.upload
.reserve(&self.hw.alloc, frame_idx, text_bytes)?;
let args = VkCompositeArgs {
image_index: image_index as usize,
frame_idx,
reactive_valid,
};
fullscreen::encode_composite_chain(self, &cmd, &args, text_calls)
}
}