This commit is contained in:
sleptworld 2024-01-22 01:57:40 +08:00
parent 04776b4f45
commit acbc81b733
8 changed files with 366 additions and 51 deletions

62
Cargo.lock generated
View File

@ -422,6 +422,7 @@ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"cairo-rs", "cairo-rs",
"crossbeam",
"epoxy", "epoxy",
"euclid", "euclid",
"femtovg", "femtovg",
@ -429,6 +430,7 @@ dependencies = [
"geo-macros", "geo-macros",
"geo-types", "geo-types",
"geojson 0.24.1", "geojson 0.24.1",
"gl",
"glib", "glib",
"glib-build-tools", "glib-build-tools",
"glib-macros", "glib-macros",
@ -631,47 +633,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam"
version = "0.5.8" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
dependencies = [ dependencies = [
"cfg-if",
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.3" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [ dependencies = [
"cfg-if",
"crossbeam-epoch", "crossbeam-epoch",
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.15" version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [ dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils", "crossbeam-utils",
"memoffset 0.9.0",
"scopeguard",
] ]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.16" version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crunchy" name = "crunchy"
@ -1358,6 +1373,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "gl"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404"
dependencies = [
"gl_generator 0.14.0",
]
[[package]] [[package]]
name = "gl_generator" name = "gl_generator"
version = "0.9.0" version = "0.9.0"
@ -3846,7 +3870,7 @@ checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
dependencies = [ dependencies = [
"proc-macro2 1.0.76", "proc-macro2 1.0.76",
"quote 1.0.35", "quote 1.0.35",
"xml-rs 0.7.0", "xml-rs 0.8.19",
] ]
[[package]] [[package]]

View File

@ -47,6 +47,8 @@ once_cell = "1.19.0"
relm4-icons = {version="0.6.0",features=["add-filled","delete-filled","chevron-up-filled","chevron-down-filled"]} relm4-icons = {version="0.6.0",features=["add-filled","delete-filled","chevron-up-filled","chevron-down-filled"]}
surfman = "0.8.1" surfman = "0.8.1"
euclid = "0.22.9" euclid = "0.22.9"
gl = "0.14.0"
crossbeam = "0.8.4"
# plotters-cairo = "0.5.0" # plotters-cairo = "0.5.0"

View File

@ -343,3 +343,53 @@ pub enum CoordType {
// .blue(250 as f32 / 255.0) // .blue(250 as f32 / 255.0)
// .build(), // .build(),
// ]; // ];
// let texture = ctx.create_texture().expect("Cannot create texture");
// ctx.bind_texture(glow::TEXTURE_2D, Some(texture));
// ctx.tex_image_2d(
// glow::TEXTURE_2D,
// 0,
// glow::RGBA as i32,
// 3000,
// 3000,
// 0,
// glow::RGBA,
// glow::UNSIGNED_BYTE,
// None,
// );
// ctx.tex_parameter_i32(
// glow::TEXTURE_2D,
// glow::TEXTURE_MIN_FILTER,
// glow::LINEAR as i32,
// );
// ctx.tex_parameter_i32(
// glow::TEXTURE_2D,
// glow::TEXTURE_MAG_FILTER,
// glow::LINEAR as i32,
// );
// ctx.framebuffer_texture_2d(
// glow::FRAMEBUFFER,
// glow::COLOR_ATTACHMENT0,
// glow::TEXTURE_2D,
// Some(texture),
// 0,
// );
// let depth_buffer = ctx
// .create_renderbuffer()
// .expect("Cannot create renderbuffer");
// ctx.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
// ctx.renderbuffer_storage(glow::RENDERBUFFER, glow::DEPTH_COMPONENT16, 3000, 3000);
// ctx.framebuffer_renderbuffer(
// glow::FRAMEBUFFER,
// glow::DEPTH_ATTACHMENT,
// glow::RENDERBUFFER,
// Some(depth_buffer),
// );
// // let id = NonZeroU32::new(ctx.get_parameter_i32(glow::DRAW_FRAMEBUFFER_BINDING) as u32)
// // .expect("No GTK provided framebuffer binding");

View File

@ -1,5 +1,6 @@
use crate::pipeline::offscreen_renderer::OffscreenRenderer; use crate::pipeline::offscreen_renderer::OffscreenRenderer;
use crate::render::{Target, CMS}; use crate::render::{Target, CMS};
// use crate::OFFSCREEN;
use crate::{ use crate::{
components::render_panel::messages::{MonitorInputMsg, MonitorOutputMsg}, components::render_panel::messages::{MonitorInputMsg, MonitorOutputMsg},
coords::{proj::Mercator, Mapper}, coords::{proj::Mercator, Mapper},
@ -82,8 +83,11 @@ impl Component for MonitorModel {
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) { fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) {
match message { match message {
MonitorInputMsg::AddLayer(layer) => { MonitorInputMsg::AddLayer(layer) => {
// let mut canvas = OFFSCREEN.lock().unwrap();
sender.oneshot_command(async move { sender.oneshot_command(async move {
let new_canvas = OffscreenRenderer::new().unwrap(); let mut back = OffscreenRenderer::new(3000, 3000).unwrap();
let canvas = back.create_canvas();
// let new_canvas = OffscreenRenderer::new().unwrap();
let f = { let f = {
let p = layer.get_prepare(); let p = layer.get_prepare();
let mut _p = p.lock().unwrap(); let mut _p = p.lock().unwrap();
@ -94,9 +98,9 @@ impl Component for MonitorModel {
let map: Mapper = Mercator::default().into(); let map: Mapper = Mercator::default().into();
let cms = CMS::new(map, (3000.0, 3000.0)); let cms = CMS::new(map, (3000.0, 3000.0));
let canvas = new_canvas.create_canvas();
let canvas = Arc::new(Mutex::new(canvas)); let canvas = Arc::new(Mutex::new(canvas));
let c = f(imp, canvas, cms).await; let c = f(imp, canvas, cms).await;
Some(c) Some(c)
// None // None
} else { } else {

View File

@ -1,12 +1,8 @@
mod utils; mod utils;
use gtk::prelude::*;
use gtk::{gio, glib, Application, ApplicationWindow}; use gtk::{gio, glib, Application, ApplicationWindow};
use pipeline::offscreen_renderer::OffscreenRenderer; use pipeline::offscreen_renderer::OffscreenRenderer;
use relm4::menu;
use relm4::RelmApp;
use std::ptr; use std::ptr;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use utils::creator;
mod chart; mod chart;
mod components; mod components;
mod coords; mod coords;
@ -19,13 +15,16 @@ mod render;
mod window; mod window;
use components::app::{AppMode, AppModel}; use components::app::{AppMode, AppModel};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::sync::Mutex;
const APP_ID: &str = "org.gtk_rs.HelloWorld2"; const APP_ID: &str = "org.gtk_rs.HelloWorld2";
static RUNTIME: Lazy<Runtime> = static RUNTIME: Lazy<Runtime> =
Lazy::new(|| Runtime::new().expect("Setting up tokio runtime needs to succeed.")); Lazy::new(|| Runtime::new().expect("Setting up tokio runtime needs to succeed."));
// static OFFSCREEN: Lazy<OffscreenRenderer> = Lazy::new(|| OffscreenRenderer::new().expect("Can't create offscreen renderer.")); // static OFFSCREEN: Lazy<Mutex<OffscreenRenderer>> = Lazy::new(|| {
// Mutex::new(OffscreenRenderer::new(3000, 3000).expect("Can't create offscreen renderer."))
// });
fn main() { fn main() {
// Load GL pointers from epoxy (GL context management library used by GTK). // Load GL pointers from epoxy (GL context management library used by GTK).

View File

@ -6,6 +6,7 @@ use ndarray::parallel::prelude::*;
use ndarray::{Array2, ArrayView2}; use ndarray::{Array2, ArrayView2};
use num_traits::{Num, AsPrimitive, FromPrimitive}; use num_traits::{Num, AsPrimitive, FromPrimitive};
pub mod offscreen_renderer; pub mod offscreen_renderer;
mod utils;
use crate::{ use crate::{
coords::Mapper, coords::Mapper,

View File

@ -1,12 +1,13 @@
use super::utils::*;
use euclid::Size2D; use euclid::Size2D;
use femtovg::{renderer::OpenGl, Canvas}; use femtovg::{renderer::OpenGl, Canvas};
use lazy_static::__Deref; use gl;
use std::borrow::BorrowMut; use gl::types::{GLchar, GLenum, GLint, GLuint, GLvoid};
use glow::HasContext;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::{Mutex, RwLock}; use std::sync::{Mutex, RwLock};
use std::{cell::RefCell, sync::Arc}; use std::{cell::RefCell, sync::Arc};
use surfman::platform::system::connection;
use surfman::{ use surfman::{
device, Adapter, Connection, Context, ContextAttributeFlags, Device, Error, GLApi, device, Adapter, Connection, Context, ContextAttributeFlags, Device, Error, GLApi,
NativeConnection, NativeDevice, NativeConnection, NativeDevice,
@ -16,66 +17,129 @@ pub struct OffscreenRenderer {
context: Arc<RwLock<Context>>, context: Arc<RwLock<Context>>,
device: Device, device: Device,
fbo: NonZeroU32, fbo: NonZeroU32,
// canvas: Arc<Mutex<CanvasWrapper>>, glow_ctx: Option<glow::Context>,
size: (i32, i32), // canvas: Arc<Mutex<CanvasWrapper>>,
} }
impl OffscreenRenderer { impl OffscreenRenderer {
pub fn new() -> Result<Self, surfman::Error> { pub fn new(width: i32, height: i32) -> Result<Self, surfman::Error> {
let connection = Connection::new()?; let connection = Connection::new()?;
let adapter = connection.create_hardware_adapter()?; let adapter = connection.create_adapter()?;
let mut device = connection.create_device(&adapter)?; let mut device = connection.create_device(&adapter)?;
let api = connection.gl_api();
let descriptor = device.create_context_descriptor(&surfman::ContextAttributes { let descriptor = device.create_context_descriptor(&surfman::ContextAttributes {
version: surfman::GLVersion::new(3, 3), version: surfman::GLVersion::new(4, 1),
flags: ContextAttributeFlags::DEPTH.union(ContextAttributeFlags::STENCIL), flags: ContextAttributeFlags::empty(),
})?; })?;
let mut context = device.create_context(&descriptor, None)?; let mut context = device.create_context(&descriptor, None)?;
let surface = device.create_surface( let surface = device.create_surface(
&context, &context,
surfman::SurfaceAccess::GPUCPU, surfman::SurfaceAccess::GPUOnly,
surfman::SurfaceType::Generic { surfman::SurfaceType::Generic {
size: euclid::Size2D::new(3000, 3000), size: euclid::Size2D::new(width, height),
}, },
)?; )?;
let surface_info = device.surface_info(&surface);
device device
.bind_surface_to_context(&mut context, surface) .bind_surface_to_context(&mut context, surface)
.expect("Failed to bind surface to context"); .expect("Failed to bind surface to context");
device.make_context_current(&context).unwrap(); device.make_context_current(&context).unwrap();
let surface_info = device.context_surface_info(&context).unwrap().unwrap();
gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name));
unsafe {
debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
gl::BindFramebuffer(gl::FRAMEBUFFER, surface_info.framebuffer_object);
debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
}
Ok(Self { Ok(Self {
context: Arc::new(RwLock::new(context)), context: Arc::new(RwLock::new(context)),
device, device,
fbo: NonZeroU32::new(surface_info.framebuffer_object).unwrap(), fbo: NonZeroU32::new(surface_info.framebuffer_object).unwrap(),
glow_ctx: None,
size: (width, height),
}) })
} }
pub fn create_canvas(&self) -> CanvasWrapper { pub fn create_canvas(&mut self) -> CanvasWrapper {
use glow::HasContext; let (w, h) = self.size;
static LOAD_FN: fn(&str) -> *const std::ffi::c_void = let mut pixels: Vec<u8> = vec![0; w as usize * h as usize * 4];
|s| epoxy::get_proc_addr(s) as *const _;
let (mut renderer, fbo) = unsafe { let (mut renderer, fbo, ctx) = unsafe {
let renderer = OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer"); let renderer = OpenGl::new_from_function(|s| {
let ctx = glow::Context::from_loader_function(LOAD_FN); self.device
.get_proc_address(&self.context.read().unwrap(), s) as *const _
})
.expect("Cannot create renderer");
let fbo = ctx.create_framebuffer().expect("can't create framebuffer"); let ctx = glow::Context::from_loader_function(|s| {
let cbo = ctx.create_buffer().expect("can't create color buffer"); self.device
.get_proc_address(&self.context.read().unwrap(), s) as *const _
});
// let id = NonZeroU32::new(ctx.get_parameter_i32(glow::DRAW_FRAMEBUFFER_BINDING) as u32) let fbo = glow::NativeFramebuffer(self.fbo);
// .expect("No GTK provided framebuffer binding");
ctx.bind_framebuffer(glow::FRAMEBUFFER, None); ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
// let fbo = glow::NativeFramebuffer(id); (renderer, fbo, ctx)
(renderer, fbo)
}; };
renderer.set_screen_target(Some(fbo)); renderer.set_screen_target(Some(fbo));
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas"); let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
canvas.set_size(3000, 3000, 1.0); canvas.set_size(3000, 3000, 1.0);
let img = canvas
.create_image_empty(
3000,
3000,
femtovg::PixelFormat::Rgba8,
femtovg::ImageFlags::empty(),
)
.unwrap();
canvas.set_render_target(femtovg::RenderTarget::Image(img));
let mut path = femtovg::Path::new();
path.rect(0.0, 0.0, 300.0, 300.0);
canvas.fill_path(
&mut path,
&femtovg::Paint::color(femtovg::Color::rgb(255, 0, 0)),
);
unsafe {
ctx.get_tex_image(
glow::TEXTURE_2D,
0,
glow::RGBA,
glow::UNSIGNED_BYTE,
glow::PixelPackData::Slice(&mut pixels),
);
}
// unsafe {
// let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
// println!("status: {}", status);
// gl::ReadPixels(
// 0,
// 0,
// w,
// h,
// gl::RGBA,
// gl::UNSIGNED_BYTE,
// pixels.as_mut_ptr() as *mut GLvoid,
// );
// debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
// }
if let Some(max) = pixels.iter().max() {
println!("The maximum value is {}", max);
} else {
println!("The vector is empty");
}
CanvasWrapper::new(canvas) CanvasWrapper::new(canvas)
} }
} }

171
src/pipeline/utils.rs Normal file
View File

@ -0,0 +1,171 @@
// examples/common/mod.rs
//
// OpenGL convenience wrappers used in the examples.
use gl;
use gl::types::{GLchar, GLenum, GLint, GLuint};
use std::fs::File;
use std::io::Read;
use std::os::raw::c_void;
use std::ptr;
use surfman::GLApi;
pub struct Program {
pub object: GLuint,
#[allow(dead_code)]
vertex_shader: Shader,
#[allow(dead_code)]
fragment_shader: Shader,
}
impl Program {
pub fn new(vertex_shader: Shader, fragment_shader: Shader) -> Program {
unsafe {
let program = gl::CreateProgram();
ck();
gl::AttachShader(program, vertex_shader.object);
ck();
gl::AttachShader(program, fragment_shader.object);
ck();
gl::LinkProgram(program);
ck();
Program {
object: program,
vertex_shader,
fragment_shader,
}
}
}
}
pub struct Shader {
object: GLuint,
}
impl Shader {
pub fn new(
name: &str,
kind: ShaderKind,
gl_api: GLApi,
gl_texture_target: GLenum,
resource_loader: &dyn ResourceLoader,
) -> Shader {
let mut source = vec![];
match gl_api {
GLApi::GL => source.extend_from_slice(b"#version 330\n"),
GLApi::GLES => source.extend_from_slice(b"#version 300 es\n"),
}
match gl_texture_target {
gl::TEXTURE_2D => {}
gl::TEXTURE_RECTANGLE => source.extend_from_slice(b"#define SAMPLER_RECT\n"),
_ => {}
}
resource_loader.slurp(&mut source, &format!("{}.{}.glsl", name, kind.extension()));
unsafe {
let shader = gl::CreateShader(kind.to_gl());
ck();
gl::ShaderSource(
shader,
1,
&(source.as_ptr() as *const GLchar),
&(source.len() as GLint),
);
ck();
gl::CompileShader(shader);
ck();
let mut compile_status = 0;
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut compile_status);
ck();
if compile_status != gl::TRUE as GLint {
let mut info_log_length = 0;
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut info_log_length);
let mut info_log = vec![0; info_log_length as usize + 1];
gl::GetShaderInfoLog(
shader,
info_log_length,
ptr::null_mut(),
info_log.as_mut_ptr() as *mut _,
);
eprintln!(
"Failed to compile shader:\n{}",
String::from_utf8_lossy(&info_log)
);
panic!("Shader compilation failed!");
}
debug_assert_eq!(compile_status, gl::TRUE as GLint);
Shader { object: shader }
}
}
}
pub struct Buffer {
pub object: GLuint,
}
impl Buffer {
pub fn from_data(data: &[u8]) -> Buffer {
unsafe {
let mut buffer = 0;
gl::GenBuffers(1, &mut buffer);
ck();
gl::BindBuffer(gl::ARRAY_BUFFER, buffer);
ck();
gl::BufferData(
gl::ARRAY_BUFFER,
data.len() as isize,
data.as_ptr() as *const c_void,
gl::STATIC_DRAW,
);
ck();
Buffer { object: buffer }
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ShaderKind {
Vertex,
Fragment,
}
impl ShaderKind {
fn extension(self) -> &'static str {
match self {
ShaderKind::Vertex => "vs",
ShaderKind::Fragment => "fs",
}
}
fn to_gl(self) -> GLenum {
match self {
ShaderKind::Vertex => gl::VERTEX_SHADER,
ShaderKind::Fragment => gl::FRAGMENT_SHADER,
}
}
}
pub trait ResourceLoader {
fn slurp(&self, dest: &mut Vec<u8>, filename: &str);
}
#[allow(dead_code)]
pub struct FilesystemResourceLoader;
impl ResourceLoader for FilesystemResourceLoader {
fn slurp(&self, dest: &mut Vec<u8>, filename: &str) {
let path = format!("resources/examples/{}", filename);
File::open(&path)
.expect("Failed to open file!")
.read_to_end(dest)
.unwrap();
}
}
pub fn ck() {
unsafe {
debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
}
}