use skia_rs_core::{Color, Rect};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub enum DrawCommand {
Clear {
color: Color,
},
Draw {
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
},
DrawIndexed {
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
},
SetScissor {
rect: ScissorRect,
},
SetViewport {
viewport: Viewport,
},
SetBlendConstant {
color: [f32; 4],
},
SetStencilReference {
reference: u32,
},
PushDebugGroup {
label: String,
},
PopDebugGroup,
InsertDebugMarker {
label: String,
},
SetPipeline {
pipeline_id: u64,
},
SetBindGroup {
index: u32,
bind_group_id: u64,
dynamic_offsets: Vec<u32>,
},
SetVertexBuffer {
slot: u32,
buffer_id: u64,
offset: u64,
size: Option<u64>,
},
SetIndexBuffer {
buffer_id: u64,
format: IndexFormat,
offset: u64,
size: Option<u64>,
},
CopyBufferToBuffer {
src: u64,
src_offset: u64,
dst: u64,
dst_offset: u64,
size: u64,
},
CopyBufferToTexture {
src_buffer: u64,
src_layout: ImageDataLayout,
dst_texture: u64,
dst_origin: [u32; 3],
size: [u32; 3],
},
CopyTextureToBuffer {
src_texture: u64,
src_origin: [u32; 3],
dst_buffer: u64,
dst_layout: ImageDataLayout,
size: [u32; 3],
},
CopyTextureToTexture {
src_texture: u64,
src_origin: [u32; 3],
dst_texture: u64,
dst_origin: [u32; 3],
size: [u32; 3],
},
}
pub use crate::pipeline::IndexFormat;
#[derive(Debug, Clone, Copy)]
pub struct ImageDataLayout {
pub offset: u64,
pub bytes_per_row: Option<u32>,
pub rows_per_image: Option<u32>,
}
impl Default for ImageDataLayout {
fn default() -> Self {
Self {
offset: 0,
bytes_per_row: None,
rows_per_image: None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ScissorRect {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
impl ScissorRect {
pub fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
Self {
x,
y,
width,
height,
}
}
pub fn from_rect(rect: &Rect) -> Self {
Self {
x: rect.left.max(0.0) as u32,
y: rect.top.max(0.0) as u32,
width: rect.width().max(0.0) as u32,
height: rect.height().max(0.0) as u32,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Viewport {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
pub min_depth: f32,
pub max_depth: f32,
}
impl Viewport {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
Self {
x,
y,
width,
height,
min_depth: 0.0,
max_depth: 1.0,
}
}
pub fn with_depth(mut self, min: f32, max: f32) -> Self {
self.min_depth = min;
self.max_depth = max;
self
}
}
#[derive(Debug, Default)]
pub struct CommandBuffer {
commands: Vec<DrawCommand>,
debug_depth: u32,
}
impl CommandBuffer {
pub fn new() -> Self {
Self {
commands: Vec::new(),
debug_depth: 0,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
commands: Vec::with_capacity(capacity),
debug_depth: 0,
}
}
pub fn record(&mut self, command: DrawCommand) {
match &command {
DrawCommand::PushDebugGroup { .. } => self.debug_depth += 1,
DrawCommand::PopDebugGroup => {
self.debug_depth = self.debug_depth.saturating_sub(1);
}
_ => {}
}
self.commands.push(command);
}
pub fn clear(&mut self, color: Color) {
self.record(DrawCommand::Clear { color });
}
pub fn draw(&mut self, vertex_count: u32, instance_count: u32) {
self.record(DrawCommand::Draw {
vertex_count,
instance_count,
first_vertex: 0,
first_instance: 0,
});
}
pub fn draw_with_offsets(
&mut self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
self.record(DrawCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
});
}
pub fn draw_indexed(&mut self, index_count: u32, instance_count: u32) {
self.record(DrawCommand::DrawIndexed {
index_count,
instance_count,
first_index: 0,
base_vertex: 0,
first_instance: 0,
});
}
pub fn draw_indexed_with_offsets(
&mut self,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) {
self.record(DrawCommand::DrawIndexed {
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
});
}
pub fn set_scissor(&mut self, rect: ScissorRect) {
self.record(DrawCommand::SetScissor { rect });
}
pub fn set_viewport(&mut self, viewport: Viewport) {
self.record(DrawCommand::SetViewport { viewport });
}
pub fn set_blend_constant(&mut self, color: [f32; 4]) {
self.record(DrawCommand::SetBlendConstant { color });
}
pub fn set_stencil_reference(&mut self, reference: u32) {
self.record(DrawCommand::SetStencilReference { reference });
}
pub fn push_debug_group(&mut self, label: impl Into<String>) {
self.record(DrawCommand::PushDebugGroup {
label: label.into(),
});
}
pub fn pop_debug_group(&mut self) {
self.record(DrawCommand::PopDebugGroup);
}
pub fn insert_debug_marker(&mut self, label: impl Into<String>) {
self.record(DrawCommand::InsertDebugMarker {
label: label.into(),
});
}
pub fn set_pipeline(&mut self, pipeline_id: u64) {
self.record(DrawCommand::SetPipeline { pipeline_id });
}
pub fn set_bind_group(&mut self, index: u32, bind_group_id: u64, dynamic_offsets: &[u32]) {
self.record(DrawCommand::SetBindGroup {
index,
bind_group_id,
dynamic_offsets: dynamic_offsets.to_vec(),
});
}
pub fn set_vertex_buffer(&mut self, slot: u32, buffer_id: u64, offset: u64, size: Option<u64>) {
self.record(DrawCommand::SetVertexBuffer {
slot,
buffer_id,
offset,
size,
});
}
pub fn set_index_buffer(
&mut self,
buffer_id: u64,
format: IndexFormat,
offset: u64,
size: Option<u64>,
) {
self.record(DrawCommand::SetIndexBuffer {
buffer_id,
format,
offset,
size,
});
}
pub fn copy_buffer_to_buffer(
&mut self,
src: u64,
src_offset: u64,
dst: u64,
dst_offset: u64,
size: u64,
) {
self.record(DrawCommand::CopyBufferToBuffer {
src,
src_offset,
dst,
dst_offset,
size,
});
}
pub fn commands(&self) -> &[DrawCommand] {
&self.commands
}
pub fn take_commands(&mut self) -> Vec<DrawCommand> {
std::mem::take(&mut self.commands)
}
pub fn reset(&mut self) {
self.commands.clear();
self.debug_depth = 0;
}
pub fn is_empty(&self) -> bool {
self.commands.is_empty()
}
pub fn len(&self) -> usize {
self.commands.len()
}
}
pub struct CommandEncoder {
buffer: CommandBuffer,
label: Option<String>,
}
impl CommandEncoder {
pub fn new() -> Self {
Self {
buffer: CommandBuffer::new(),
label: None,
}
}
pub fn with_label(label: impl Into<String>) -> Self {
Self {
buffer: CommandBuffer::new(),
label: Some(label.into()),
}
}
pub fn begin_render_pass(&mut self, desc: &RenderPassDescriptor) -> RenderPassEncoder<'_> {
if let Some(color) = desc.clear_color {
self.buffer.clear(Color::from_argb(
(color[3] * 255.0) as u8,
(color[0] * 255.0) as u8,
(color[1] * 255.0) as u8,
(color[2] * 255.0) as u8,
));
}
RenderPassEncoder { encoder: self }
}
pub fn begin_compute_pass(&mut self) -> ComputePassEncoder<'_> {
ComputePassEncoder { encoder: self }
}
pub fn copy_buffer_to_buffer(
&mut self,
src: u64,
src_offset: u64,
dst: u64,
dst_offset: u64,
size: u64,
) {
self.buffer
.copy_buffer_to_buffer(src, src_offset, dst, dst_offset, size);
}
pub fn copy_buffer_to_texture(
&mut self,
src_buffer: u64,
src_layout: ImageDataLayout,
dst_texture: u64,
dst_origin: [u32; 3],
size: [u32; 3],
) {
self.buffer.record(DrawCommand::CopyBufferToTexture {
src_buffer,
src_layout,
dst_texture,
dst_origin,
size,
});
}
pub fn copy_texture_to_buffer(
&mut self,
src_texture: u64,
src_origin: [u32; 3],
dst_buffer: u64,
dst_layout: ImageDataLayout,
size: [u32; 3],
) {
self.buffer.record(DrawCommand::CopyTextureToBuffer {
src_texture,
src_origin,
dst_buffer,
dst_layout,
size,
});
}
pub fn copy_texture_to_texture(
&mut self,
src_texture: u64,
src_origin: [u32; 3],
dst_texture: u64,
dst_origin: [u32; 3],
size: [u32; 3],
) {
self.buffer.record(DrawCommand::CopyTextureToTexture {
src_texture,
src_origin,
dst_texture,
dst_origin,
size,
});
}
pub fn push_debug_group(&mut self, label: &str) {
self.buffer.push_debug_group(label);
}
pub fn pop_debug_group(&mut self) {
self.buffer.pop_debug_group();
}
pub fn insert_debug_marker(&mut self, label: &str) {
self.buffer.insert_debug_marker(label);
}
pub fn finish(self) -> CommandBuffer {
self.buffer
}
}
impl Default for CommandEncoder {
fn default() -> Self {
Self::new()
}
}
pub use crate::surface::RenderPassDescriptor;
pub struct RenderPassEncoder<'a> {
encoder: &'a mut CommandEncoder,
}
impl<'a> RenderPassEncoder<'a> {
pub fn set_pipeline(&mut self, pipeline_id: u64) {
self.encoder.buffer.set_pipeline(pipeline_id);
}
pub fn set_bind_group(&mut self, index: u32, bind_group_id: u64, dynamic_offsets: &[u32]) {
self.encoder
.buffer
.set_bind_group(index, bind_group_id, dynamic_offsets);
}
pub fn set_vertex_buffer(&mut self, slot: u32, buffer_id: u64, offset: u64, size: Option<u64>) {
self.encoder
.buffer
.set_vertex_buffer(slot, buffer_id, offset, size);
}
pub fn set_index_buffer(
&mut self,
buffer_id: u64,
format: IndexFormat,
offset: u64,
size: Option<u64>,
) {
self.encoder
.buffer
.set_index_buffer(buffer_id, format, offset, size);
}
pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {
self.encoder
.buffer
.set_scissor(ScissorRect::new(x, y, width, height));
}
pub fn set_viewport(
&mut self,
x: f32,
y: f32,
width: f32,
height: f32,
min_depth: f32,
max_depth: f32,
) {
self.encoder.buffer.set_viewport(Viewport {
x,
y,
width,
height,
min_depth,
max_depth,
});
}
pub fn set_blend_constant(&mut self, color: [f32; 4]) {
self.encoder.buffer.set_blend_constant(color);
}
pub fn set_stencil_reference(&mut self, reference: u32) {
self.encoder.buffer.set_stencil_reference(reference);
}
pub fn draw(
&mut self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
self.encoder.buffer.draw_with_offsets(
vertex_count,
instance_count,
first_vertex,
first_instance,
);
}
pub fn draw_indexed(
&mut self,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) {
self.encoder.buffer.draw_indexed_with_offsets(
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
);
}
pub fn push_debug_group(&mut self, label: &str) {
self.encoder.buffer.push_debug_group(label);
}
pub fn pop_debug_group(&mut self) {
self.encoder.buffer.pop_debug_group();
}
pub fn insert_debug_marker(&mut self, label: &str) {
self.encoder.buffer.insert_debug_marker(label);
}
}
pub struct ComputePassEncoder<'a> {
encoder: &'a mut CommandEncoder,
}
impl<'a> ComputePassEncoder<'a> {
pub fn set_pipeline(&mut self, pipeline_id: u64) {
self.encoder.buffer.set_pipeline(pipeline_id);
}
pub fn set_bind_group(&mut self, index: u32, bind_group_id: u64, dynamic_offsets: &[u32]) {
self.encoder
.buffer
.set_bind_group(index, bind_group_id, dynamic_offsets);
}
pub fn dispatch_workgroups(&mut self, x: u32, y: u32, z: u32) {
self.encoder.buffer.draw_with_offsets(x, y, z, 0);
}
pub fn push_debug_group(&mut self, label: &str) {
self.encoder.buffer.push_debug_group(label);
}
pub fn pop_debug_group(&mut self) {
self.encoder.buffer.pop_debug_group();
}
pub fn insert_debug_marker(&mut self, label: &str) {
self.encoder.buffer.insert_debug_marker(label);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_buffer() {
let mut buffer = CommandBuffer::new();
buffer.clear(Color::RED);
buffer.draw(6, 1);
assert_eq!(buffer.len(), 2);
assert!(!buffer.is_empty());
}
#[test]
fn test_command_encoder() {
let mut encoder = CommandEncoder::new();
{
let mut pass =
encoder.begin_render_pass(&RenderPassDescriptor::color_clear(0.0, 0.0, 0.0, 1.0));
pass.set_pipeline(1);
pass.draw(6, 1, 0, 0);
}
let buffer = encoder.finish();
assert!(buffer.len() >= 2);
}
#[test]
fn test_scissor_rect() {
let rect = Rect::from_xywh(10.0, 20.0, 100.0, 200.0);
let scissor = ScissorRect::from_rect(&rect);
assert_eq!(scissor.x, 10);
assert_eq!(scissor.y, 20);
assert_eq!(scissor.width, 100);
assert_eq!(scissor.height, 200);
}
#[test]
fn test_viewport() {
let viewport = Viewport::new(0.0, 0.0, 800.0, 600.0).with_depth(0.0, 1.0);
assert_eq!(viewport.width, 800.0);
assert_eq!(viewport.height, 600.0);
assert_eq!(viewport.min_depth, 0.0);
assert_eq!(viewport.max_depth, 1.0);
}
#[test]
fn test_debug_groups() {
let mut buffer = CommandBuffer::new();
buffer.push_debug_group("outer");
buffer.push_debug_group("inner");
buffer.pop_debug_group();
buffer.pop_debug_group();
assert_eq!(buffer.debug_depth, 0);
}
}