mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
672 lines
21 KiB
Rust
672 lines
21 KiB
Rust
/* 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![] },
|
|
}
|
|
}
|
|
}
|