//! Minimal code for drawing any level, based on traits from stockton-levels use anyhow::{Context, Result}; use hal::{ buffer::SubRange, command::{ClearColor, ClearValue, RenderAttachmentInfo, SubpassContents}, format::Format, image::Layout, pass::Attachment, pso::{ BlendDesc, BlendOp, BlendState, ColorBlendDesc, ColorMask, DepthStencilDesc, Face, Factor, FrontFace, InputAssemblerDesc, LogicOp, PolygonMode, Primitive, Rasterizer, State, VertexInputRate, }, }; use legion::{Entity, IntoQuery}; use std::{ array::IntoIter, iter::{empty, once}, }; use stockton_skeleton::{ buffers::draw::DrawBuffers, builders::{ AttachmentSpec, CompletePipeline, PipelineSpecBuilder, RenderpassSpec, ShaderDesc, ShaderKind, VertexBufferSpec, VertexPrimitiveAssemblerSpec, }, draw_passes::util::TargetSpecificResources, mem::{DataPool, StagingPool}, queue_negotiator::QueueFamilyNegotiator, types::*, DrawPass, IntoDrawPass, PassPosition, RenderingContext, Session, }; use crate::ExampleState; /// The vertices that go to the shader (XY + RGB) #[derive(Debug, Clone, Copy)] #[repr(C)] struct Vertex(pub Vector2, pub Vector3); /// An example draw pass pub struct ExampleDrawPass<'a> { /// Index and vertex buffer pair draw_buffers: DrawBuffers<'a, Vertex, DataPool, StagingPool>, /// Resources that depend on the surface. This is seperate so that we can deal with surface changes more easily. surface_resources: SurfaceDependentResources, /// Entity we get our state from state_ent: Entity, } /// Config for our draw pass. This is turned into our drawpass using [`IntoDrawPass`] pub struct ExampleDrawPassConfig { pub state_ent: Entity, } impl<'a, P: PassPosition> DrawPass
for ExampleDrawPass<'a> {
/// Called every frame to queue actual drawing.
fn queue_draw(
&mut self,
session: &Session,
img_view: &ImageViewT,
cmd_buffer: &mut CommandBufferT,
) -> anyhow::Result<()> {
// Commit any changes to our vertex buffers
// We queue this first so that it's executed before any draw commands
self.draw_buffers
.vertex_buffer
.record_commit_cmds(cmd_buffer)?;
self.draw_buffers
.index_buffer
.record_commit_cmds(cmd_buffer)?;
// Get framebuffer
let fb = self.surface_resources.framebuffers.get_next();
// Get state
let (state,) = <(&ExampleState,)>::query().get(&session.world, self.state_ent)?;
// Begin render pass & bind everything needed
unsafe {
cmd_buffer.begin_render_pass(
&self.surface_resources.pipeline.renderpass,
fb,
self.surface_resources.pipeline.render_area,
vec![RenderAttachmentInfo {
image_view: img_view,
clear_value: ClearValue {
color: ClearColor {
float32: [0.0, 0.0, 0.0, 1.0],
},
},
}]
.into_iter(),
SubpassContents::Inline,
);
cmd_buffer.bind_graphics_pipeline(&self.surface_resources.pipeline.pipeline);
// Bind buffers
cmd_buffer.bind_vertex_buffers(
0,
once((
self.draw_buffers.vertex_buffer.get_buffer(),
SubRange {
offset: 0,
size: None,
},
)),
);
cmd_buffer.bind_index_buffer(
self.draw_buffers.index_buffer.get_buffer(),
SubRange {
offset: 0,
size: None,
},
hal::IndexType::U16,
);
}
// Draw an example
self.draw_buffers.index_buffer[0] = (0, 1, 2);
self.draw_buffers.vertex_buffer[0] = Vertex(Vector2::new(0.5, 0.5), state.color());
self.draw_buffers.vertex_buffer[1] = Vertex(Vector2::new(0.0, -0.5), state.color());
self.draw_buffers.vertex_buffer[2] = Vertex(Vector2::new(-0.5, 0.5), state.color());
unsafe {
cmd_buffer.draw_indexed(0..3, 0, 0..1);
}
// Remember to clean up afterwards!
unsafe {
cmd_buffer.end_render_pass();
}
Ok(())
}
/// Destroy all our vulkan objects
fn deactivate(self, context: &mut RenderingContext) -> Result<()> {
self.draw_buffers.deactivate(context);
self.surface_resources.deactivate(context)?;
Ok(())
}
/// Deal with a surface change
fn handle_surface_change(
mut self,
_session: &Session,
context: &mut RenderingContext,
) -> Result (context) {
Ok(x) => x,
Err(e) => {
(context)?;
let draw_buffers =
match DrawBuffers::from_context(context).context("Error creating draw buffers") {
Ok(x) => x,
Err(e) => {
surface_resources.deactivate(context)?;
return Err(e);
}
};
Ok(ExampleDrawPass {
draw_buffers,
surface_resources,
state_ent: self.state_ent,
})
}
fn find_aux_queues(
_adapter: &Adapter,
_queue_negotiator: &mut QueueFamilyNegotiator,
) -> Result<()> {
// We don't need any queues, but we'd need code to find their families here if we did.
Ok(())
}
}
/// Used to store resources which depend on the surface, for convenience in handle_surface_change
struct SurfaceDependentResources {
pub pipeline: CompletePipeline,
pub framebuffers: TargetSpecificResources