| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2026 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use async_trait::async_trait;
- use darkfi_serial::{SerialEncodable, SerialDecodable, serialize, Encodable, Decodable, deserialize};
- use std::{
- fs::{OpenOptions, File},
- collections::HashMap,
- sync::{mpsc, Arc, Mutex as SyncMutex},
- time::{Duration, Instant},
- ops::{Add, Mul},
- };
- use futures::AsyncWriteExt;
- use miniquad::{
- conf, window, Backend, Bindings, BlendFactor, BlendState, BlendValue, BufferLayout,
- BufferSource, BufferType, BufferUsage, Equation, EventHandler, KeyCode, KeyMods, MouseButton,
- PassAction, Pipeline, PipelineParams, RenderingBackend, ShaderMeta, ShaderSource, TouchPhase,
- TextureFormat, TextureKind, TextureParams, TextureWrap, UniformDesc, UniformType,
- VertexAttribute, VertexFormat,
- UniformBlockLayout,
- };
- const FILENAME: &str = "drawinstrs.dat";
- const DEBUG_RENDER: bool = false;
- const DEBUG_GFXAPI: bool = false;
- #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
- #[repr(C)]
- pub struct Vertex {
- pub pos: [f32; 2],
- pub color: [f32; 4],
- pub uv: [f32; 2],
- }
- #[derive(Clone, Copy, Debug, SerialEncodable, SerialDecodable)]
- pub struct Point {
- pub x: f32,
- pub y: f32,
- }
- impl Point {
- pub fn zero() -> Self {
- Self { x: 0., y: 0. }
- }
- }
- impl From<[f32; 2]> for Point {
- fn from(pos: [f32; 2]) -> Self {
- Self { x: pos[0], y: pos[1] }
- }
- }
- impl Add for Point {
- type Output = Self;
- fn add(self, other: Self) -> Self::Output {
- Self { x: self.x + other.x, y: self.y + other.y }
- }
- }
- #[derive(Debug, Clone, Copy, SerialEncodable, SerialDecodable)]
- pub struct Rectangle {
- pub x: f32,
- pub y: f32,
- pub w: f32,
- pub h: f32,
- }
- impl From<[f32; 4]> for Rectangle {
- fn from(rect: [f32; 4]) -> Self {
- Self { x: rect[0], y: rect[1], w: rect[2], h: rect[3] }
- }
- }
- impl Mul<f32> for Rectangle {
- type Output = Rectangle;
- fn mul(self, scale: f32) -> Self::Output {
- Self { x: self.x * scale, y: self.y * scale, w: self.w * scale, h: self.h * scale }
- }
- }
- pub type GfxTextureId = u32;
- pub type GfxBufferId = u32;
- #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
- pub struct GfxDrawCall {
- pub instrs: Vec<GfxDrawInstruction>,
- pub dcs: Vec<u64>,
- pub z_index: u32,
- }
- impl GfxDrawCall {
- fn compile(
- self,
- textures: &HashMap<GfxTextureId, miniquad::TextureId>,
- buffers: &HashMap<GfxBufferId, miniquad::BufferId>,
- ) -> DrawCall {
- DrawCall {
- instrs: self.instrs.into_iter().map(|i| i.compile(textures, buffers)).collect(),
- dcs: self.dcs,
- z_index: self.z_index,
- }
- }
- }
- #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
- pub enum GfxDrawInstruction {
- SetScale(f32),
- Move(Point),
- ApplyView(Rectangle),
- Draw(GfxDrawMesh),
- }
- impl GfxDrawInstruction {
- fn compile(
- self,
- textures: &HashMap<GfxTextureId, miniquad::TextureId>,
- buffers: &HashMap<GfxBufferId, miniquad::BufferId>,
- ) -> DrawInstruction {
- match self {
- Self::SetScale(scale) => DrawInstruction::SetScale(scale),
- Self::Move(off) => DrawInstruction::Move(off),
- Self::ApplyView(view) => DrawInstruction::ApplyView(view),
- Self::Draw(mesh) => DrawInstruction::Draw(mesh.compile(textures, buffers)),
- }
- }
- }
- #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
- pub struct GfxDrawMesh {
- pub vertex_buffer: GfxBufferId,
- pub index_buffer: GfxBufferId,
- pub texture: Option<GfxTextureId>,
- pub num_elements: i32,
- }
- impl GfxDrawMesh {
- fn compile(
- self,
- textures: &HashMap<GfxTextureId, miniquad::TextureId>,
- buffers: &HashMap<GfxBufferId, miniquad::BufferId>,
- ) -> DrawMesh {
- DrawMesh {
- vertex_buffer: buffers[&self.vertex_buffer],
- index_buffer: buffers[&self.index_buffer],
- texture: self.texture.map(|t| textures[&t]),
- num_elements: self.num_elements,
- }
- }
- }
- #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
- pub enum GraphicsMethod {
- NewTexture((u16, u16, Vec<u8>, GfxTextureId)),
- DeleteTexture(GfxTextureId),
- NewVertexBuffer((Vec<Vertex>, GfxBufferId)),
- NewIndexBuffer((Vec<u16>, GfxBufferId)),
- DeleteBuffer(GfxBufferId),
- ReplaceDrawCalls(Vec<(u64, GfxDrawCall)>),
- }
- #[derive(Debug, SerialEncodable, SerialDecodable)]
- struct Instruction {
- timest: u64,
- method: GraphicsMethod
- }
- pub fn read_instrs() -> Vec<Instruction> {
- let mut instrs = vec![];
- let mut f = File::open(FILENAME).unwrap();
- loop {
- let Ok(data) = Vec::<u8>::decode(&mut f) else { break };
- let instr: Instruction = deserialize(&data).unwrap();
- instrs.push(instr);
- }
- instrs
- }
- #[derive(Clone, Debug)]
- struct DrawMesh {
- vertex_buffer: miniquad::BufferId,
- index_buffer: miniquad::BufferId,
- texture: Option<miniquad::TextureId>,
- num_elements: i32,
- }
- #[derive(Debug, Clone)]
- enum DrawInstruction {
- SetScale(f32),
- Move(Point),
- ApplyView(Rectangle),
- Draw(DrawMesh),
- }
- #[derive(Debug)]
- struct DrawCall {
- instrs: Vec<DrawInstruction>,
- dcs: Vec<u64>,
- z_index: u32,
- }
- struct Stage {
- ctx: Box<dyn RenderingBackend>,
- pipeline: Pipeline,
- white_texture: miniquad::TextureId,
- draw_calls: HashMap<u64, DrawCall>,
- textures: HashMap<GfxTextureId, miniquad::TextureId>,
- buffers: HashMap<GfxBufferId, miniquad::BufferId>,
- instant: Instant,
- instrs: Vec<Instruction>,
- }
- impl Stage {
- pub fn new(
- ) -> Self {
- let mut instrs = read_instrs();
- instrs.reverse();
- println!("Loaded instrs");
- let mut ctx: Box<dyn RenderingBackend> = window::new_rendering_backend();
- let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]);
- let mut shader_meta: ShaderMeta = shader::meta();
- shader_meta.uniforms.uniforms.push(UniformDesc::new("Projection", UniformType::Mat4));
- shader_meta.uniforms.uniforms.push(UniformDesc::new("Model", UniformType::Mat4));
- let shader = ctx
- .new_shader(
- match ctx.info().backend {
- Backend::OpenGl => ShaderSource::Glsl {
- vertex: shader::GL_VERTEX,
- fragment: shader::GL_FRAGMENT,
- },
- Backend::Metal => ShaderSource::Msl { program: shader::METAL },
- },
- shader_meta,
- )
- .unwrap();
- let params = PipelineParams {
- color_blend: Some(BlendState::new(
- Equation::Add,
- BlendFactor::Value(BlendValue::SourceAlpha),
- BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
- )),
- ..Default::default()
- };
- let pipeline = ctx.new_pipeline(
- &[BufferLayout::default()],
- &[
- VertexAttribute::new("in_pos", VertexFormat::Float2),
- VertexAttribute::new("in_color", VertexFormat::Float4),
- VertexAttribute::new("in_uv", VertexFormat::Float2),
- ],
- shader,
- params,
- );
- Stage {
- ctx,
- pipeline,
- white_texture,
- draw_calls: HashMap::from([(0, DrawCall { instrs: vec![], dcs: vec![], z_index: 0 })]),
- textures: HashMap::new(),
- buffers: HashMap::new(),
- instant: Instant::now(),
- instrs,
- }
- }
- fn process_method(&mut self, method: GraphicsMethod) {
- //println!("Received method: {:?}", method);
- match method {
- GraphicsMethod::NewTexture((width, height, data, fmt, gfx_texture_id, _)) => {
- self.method_new_texture(width, height, data, fmt, gfx_texture_id)
- }
- GraphicsMethod::DeleteTexture(texture) => self.method_delete_texture(texture),
- GraphicsMethod::NewVertexBuffer((verts, sendr)) => {
- self.method_new_vertex_buffer(verts, sendr)
- }
- GraphicsMethod::NewIndexBuffer((indices, sendr)) => {
- self.method_new_index_buffer(indices, sendr)
- }
- GraphicsMethod::DeleteBuffer(buffer) => self.method_delete_buffer(buffer),
- GraphicsMethod::ReplaceDrawCalls(dcs) => self.method_replace_draw_calls(dcs),
- };
- }
- fn method_new_texture(
- &mut self,
- width: u16,
- height: u16,
- data: Vec<u8>,
- fmt: TextureFormat,
- gfx_texture_id: GfxTextureId,
- ) {
- let texture = self.ctx.new_texture_from_data_and_format(
- &data,
- TextureParams {
- kind: TextureKind::Texture2D,
- format: fmt,
- width: width as _,
- height: height as _,
- wrap: TextureWrap::Clamp,
- min_filter: miniquad::FilterMode::Linear,
- mag_filter: miniquad::FilterMode::Linear,
- mipmap_filter: miniquad::MipmapFilterMode::None,
- allocate_mipmaps: false,
- sample_count: 1,
- },
- );
- if DEBUG_GFXAPI {
- println!("Invoked method: new_texture({}, {}, ..., {}) -> {:?}",
- width, height, gfx_texture_id, texture);
- //println!("Invoked method: new_texture({}, {}, ..., {}) -> {:?}\n{}",
- // width, height, gfx_texture_id, texture,
- // ansi_texture(width as usize, height as usize, &data));
- }
- self.textures.insert(gfx_texture_id, texture);
- }
- fn method_delete_texture(&mut self, gfx_texture_id: GfxTextureId) {
- let texture = self.textures.remove(&gfx_texture_id).expect("couldn't find gfx_texture_id");
- if DEBUG_GFXAPI {
- println!("Invoked method: delete_texture({} => {:?})",
- gfx_texture_id, texture);
- }
- self.ctx.delete_texture(texture);
- }
- fn method_new_vertex_buffer(&mut self, verts: Vec<Vertex>, gfx_buffer_id: GfxBufferId) {
- let buffer = self.ctx.new_buffer(
- BufferType::VertexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&verts),
- );
- if DEBUG_GFXAPI {
- println!("Invoked method: new_vertex_buffer(..., {}) -> {:?}",
- gfx_buffer_id, buffer);
- //println!("Invoked method: new_vertex_buffer({:?}, {}) -> {:?}",
- // verts, gfx_buffer_id, buffer);
- }
- self.buffers.insert(gfx_buffer_id, buffer);
- }
- fn method_new_index_buffer(&mut self, indices: Vec<u16>, gfx_buffer_id: GfxBufferId) {
- let buffer = self.ctx.new_buffer(
- BufferType::IndexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&indices),
- );
- if DEBUG_GFXAPI {
- println!("Invoked method: new_index_buffer({}) -> {:?}",
- gfx_buffer_id, buffer);
- //println!("Invoked method: new_index_buffer({:?}, {}) -> {:?}",
- // indices, gfx_buffer_id, buffer);
- }
- self.buffers.insert(gfx_buffer_id, buffer);
- }
- fn method_delete_buffer(&mut self, gfx_buffer_id: GfxBufferId) {
- let buffer = self.buffers.remove(&gfx_buffer_id).expect("couldn't find gfx_buffer_id");
- if DEBUG_GFXAPI {
- println!("Invoked method: delete_buffer({} => {:?})",
- gfx_buffer_id, buffer);
- }
- self.ctx.delete_buffer(buffer);
- }
- fn method_replace_draw_calls(&mut self, dcs: Vec<(u64, GfxDrawCall)>) {
- if DEBUG_GFXAPI {
- println!("Invoked method: replace_draw_calls({:?})", dcs);
- }
- for (key, val) in dcs {
- let val = val.compile(&self.textures, &self.buffers);
- self.draw_calls.insert(key, val);
- }
- }
- }
- impl EventHandler for Stage {
- fn update(&mut self) {
- let timest = self.instant.elapsed().as_millis() as u64;
- while let Some(instr) = self.instrs.last() {
- if instr.timest > timest {
- break
- }
- let instr = self.instrs.pop().unwrap();
- self.process_method(instr.method);
- }
- }
- fn draw(&mut self) {
- self.ctx.begin_default_pass(PassAction::Nothing);
- self.ctx.apply_pipeline(&self.pipeline);
- // This will make the top left (0, 0) and the bottom right (1, 1)
- // Default is (-1, 1) -> (1, -1)
- let proj = glam::Mat4::from_translation(glam::Vec3::new(-1., 1., 0.)) *
- glam::Mat4::from_scale(glam::Vec3::new(2., -2., 1.));
- let mut uniforms_data = [0u8; 128];
- let data: [u8; 64] = unsafe { std::mem::transmute_copy(&proj) };
- uniforms_data[0..64].copy_from_slice(&data);
- //let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
- //uniforms_data[64..].copy_from_slice(&data);
- assert_eq!(128, 2 * UniformType::Mat4.size());
- let (screen_w, screen_h) = miniquad::window::screen_size();
- let mut render_ctx = RenderContext {
- ctx: &mut self.ctx,
- draw_calls: &self.draw_calls,
- uniforms_data,
- white_texture: self.white_texture,
- scale: 1.,
- view: Rectangle::from([0., 0., screen_w, screen_h]),
- cursor: Point::from([0., 0.]),
- };
- render_ctx.draw();
- self.ctx.commit_frame();
- }
- }
- struct RenderContext<'a> {
- ctx: &'a mut Box<dyn RenderingBackend>,
- draw_calls: &'a HashMap<u64, DrawCall>,
- uniforms_data: [u8; 128],
- white_texture: miniquad::TextureId,
- scale: f32,
- view: Rectangle,
- cursor: Point,
- }
- impl<'a> RenderContext<'a> {
- fn draw(&mut self) {
- if DEBUG_RENDER {
- println!("RenderContext::draw()");
- }
- let curr_pos = Point::zero();
- self.draw_call(&self.draw_calls[&0], 0);
- if DEBUG_RENDER {
- println!("RenderContext::draw() [DONE]");
- }
- }
- fn apply_view(&mut self) {
- let view = self.view * self.scale;
- let (_, screen_height) = window::screen_size();
- let view_x = view.x.round() as i32;
- let view_y = screen_height - (view.y + view.h);
- let view_y = view_y.round() as i32;
- let view_w = view.w.round() as i32;
- let view_h = view.h.round() as i32;
- //if DEBUG_RENDER {
- // println!("=> viewport {view_x} {view_y} {view_w} {view_h}");
- //}
- self.ctx.apply_viewport(view_x, view_y, view_w, view_h);
- self.ctx.apply_scissor_rect(view_x, view_y, view_w, view_h);
- }
- fn apply_model(&mut self) {
- let off_x = self.cursor.x / self.view.w;
- let off_y = self.cursor.y / self.view.h;
- let scale_w = 1. / self.view.w;
- let scale_h = 1. / self.view.h;
- let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
- glam::Mat4::from_scale(glam::Vec3::new(scale_w, scale_h, 1.));
- let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
- self.uniforms_data[64..].copy_from_slice(&data);
- self.ctx.apply_uniforms_from_bytes(self.uniforms_data.as_ptr(), self.uniforms_data.len());
- }
- fn draw_call(&mut self, draw_call: &DrawCall, indent: u32) {
- let ws = if DEBUG_RENDER { " ".repeat(indent as usize * 4) } else { String::new() };
- let old_view = self.view;
- let old_cursor = self.cursor;
- for instr in &draw_call.instrs {
- match instr {
- DrawInstruction::SetScale(scale) => {
- self.scale = *scale;
- if DEBUG_RENDER {
- println!("{ws}set_scale({scale})");
- }
- }
- DrawInstruction::Move(off) => {
- self.cursor = old_cursor + *off;
- if DEBUG_RENDER {
- println!(
- "{ws}move({off:?}) cursor={:?}, scale={}, view={:?}",
- self.cursor, self.scale, self.view
- );
- }
- self.apply_model();
- }
- DrawInstruction::ApplyView(view) => {
- self.view = *view;
- if DEBUG_RENDER {
- println!(
- "{ws}apply_view({view:?}) scale={}, view={:?}",
- self.scale, self.view
- );
- }
- self.apply_view();
- }
- DrawInstruction::Draw(mesh) => {
- if DEBUG_RENDER {
- println!("{ws}draw({mesh:?})");
- }
- let texture = match mesh.texture {
- Some(texture) => texture,
- None => self.white_texture,
- };
- let bindings = Bindings {
- vertex_buffers: vec![mesh.vertex_buffer],
- index_buffer: mesh.index_buffer,
- images: vec![texture],
- };
- self.ctx.apply_bindings(&bindings);
- self.ctx.draw(0, mesh.num_elements, 1);
- }
- }
- }
- let mut draw_calls: Vec<_> =
- draw_call.dcs.iter().map(|key| (key, &self.draw_calls[key])).collect();
- draw_calls.sort_unstable_by_key(|(_, dc)| dc.z_index);
- for (dc_key, dc) in draw_calls {
- if DEBUG_RENDER {
- println!("{ws}drawcall {dc_key}");
- }
- self.draw_call(dc, indent + 1);
- }
- self.cursor = old_cursor;
- self.apply_model();
- self.view = old_view;
- self.apply_view();
- }
- }
- fn main() {
- let mut conf = miniquad::conf::Conf {
- high_dpi: true,
- window_resizable: true,
- platform: miniquad::conf::Platform {
- linux_backend: miniquad::conf::LinuxBackend::WaylandWithX11Fallback,
- wayland_use_fallback_decorations: false,
- //blocking_event_loop: true,
- ..Default::default()
- },
- ..Default::default()
- };
- let metal = std::env::args().nth(1).as_deref() == Some("metal");
- conf.platform.apple_gfx_api =
- if metal { conf::AppleGfxApi::Metal } else { conf::AppleGfxApi::OpenGl };
- miniquad::start(conf, || Box::new(Stage::new()));
- }
- mod shader {
- use super::*;
- pub const GL_VERTEX: &str = r#"#version 100
- attribute vec2 in_pos;
- attribute vec4 in_color;
- attribute vec2 in_uv;
- varying lowp vec4 color;
- varying lowp vec2 uv;
- uniform mat4 Projection;
- uniform mat4 Model;
- void main() {
- gl_Position = Projection * Model * vec4(in_pos, 0, 1);
- color = in_color;
- uv = in_uv;
- }"#;
- pub const GL_FRAGMENT: &str = r#"#version 100
- varying lowp vec4 color;
- varying lowp vec2 uv;
- uniform sampler2D tex;
- void main() {
- gl_FragColor = color * texture2D(tex, uv);
- }"#;
- pub const METAL: &str = r#"
- #include <metal_stdlib>
- using namespace metal;
- struct Uniforms
- {
- float4x4 Projection;
- float4x4 Model;
- };
- struct Vertex
- {
- float2 in_pos [[attribute(0)]];
- float4 in_color [[attribute(1)]];
- float2 in_uv [[attribute(2)]];
- };
- struct RasterizerData
- {
- float4 position [[position]];
- float4 color [[user(locn0)]];
- float2 uv [[user(locn1)]];
- };
- vertex RasterizerData vertexShader(Vertex v [[stage_in]])
- {
- RasterizerData out;
- out.position = uniforms.Model * uniforms.Projection * float4(v.in_pos.xy, 0.0, 1.0);
- out.color = v.in_color;
- out.uv = v.texcoord;
- return out;
- }
- fragment float4 fragmentShader(RasterizerData in [[stage_in]], texture2d<float> tex [[texture(0)]], sampler texSmplr [[sampler(0)]])
- {
- return in.color * tex.sample(texSmplr, in.uv);
- }
- "#;
- pub fn meta() -> ShaderMeta {
- ShaderMeta {
- images: vec!["tex".to_string()],
- uniforms: UniformBlockLayout { uniforms: vec![] },
- }
- }
- }
|