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",
"async-trait",
"cairo-rs",
"crossbeam",
"epoxy",
"euclid",
"femtovg",
@ -429,6 +430,7 @@ dependencies = [
"geo-macros",
"geo-types",
"geojson 0.24.1",
"gl",
"glib",
"glib-build-tools",
"glib-macros",
@ -631,47 +633,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
name = "crossbeam"
version = "0.8.4"
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 = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
version = "0.9.18"
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 = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset 0.9.0",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crunchy"
@ -1358,6 +1373,15 @@ dependencies = [
"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]]
name = "gl_generator"
version = "0.9.0"
@ -3846,7 +3870,7 @@ checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
dependencies = [
"proc-macro2 1.0.76",
"quote 1.0.35",
"xml-rs 0.7.0",
"xml-rs 0.8.19",
]
[[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"]}
surfman = "0.8.1"
euclid = "0.22.9"
gl = "0.14.0"
crossbeam = "0.8.4"
# plotters-cairo = "0.5.0"

View File

@ -343,3 +343,53 @@ pub enum CoordType {
// .blue(250 as f32 / 255.0)
// .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::render::{Target, CMS};
// use crate::OFFSCREEN;
use crate::{
components::render_panel::messages::{MonitorInputMsg, MonitorOutputMsg},
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) {
match message {
MonitorInputMsg::AddLayer(layer) => {
// let mut canvas = OFFSCREEN.lock().unwrap();
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 p = layer.get_prepare();
let mut _p = p.lock().unwrap();
@ -94,9 +98,9 @@ impl Component for MonitorModel {
let map: Mapper = Mercator::default().into();
let cms = CMS::new(map, (3000.0, 3000.0));
let canvas = new_canvas.create_canvas();
let canvas = Arc::new(Mutex::new(canvas));
let c = f(imp, canvas, cms).await;
Some(c)
// None
} else {

View File

@ -1,12 +1,8 @@
mod utils;
use gtk::prelude::*;
use gtk::{gio, glib, Application, ApplicationWindow};
use pipeline::offscreen_renderer::OffscreenRenderer;
use relm4::menu;
use relm4::RelmApp;
use std::ptr;
use tokio::runtime::Runtime;
use utils::creator;
mod chart;
mod components;
mod coords;
@ -19,13 +15,16 @@ mod render;
mod window;
use components::app::{AppMode, AppModel};
use once_cell::sync::Lazy;
use std::sync::Mutex;
const APP_ID: &str = "org.gtk_rs.HelloWorld2";
static RUNTIME: Lazy<Runtime> =
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() {
// 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 num_traits::{Num, AsPrimitive, FromPrimitive};
pub mod offscreen_renderer;
mod utils;
use crate::{
coords::Mapper,

View File

@ -1,12 +1,13 @@
use super::utils::*;
use euclid::Size2D;
use femtovg::{renderer::OpenGl, Canvas};
use lazy_static::__Deref;
use std::borrow::BorrowMut;
use gl;
use gl::types::{GLchar, GLenum, GLint, GLuint, GLvoid};
use glow::HasContext;
use std::num::NonZeroU32;
use std::ops::{Deref, DerefMut};
use std::sync::{Mutex, RwLock};
use std::{cell::RefCell, sync::Arc};
use surfman::platform::system::connection;
use surfman::{
device, Adapter, Connection, Context, ContextAttributeFlags, Device, Error, GLApi,
NativeConnection, NativeDevice,
@ -16,66 +17,129 @@ pub struct OffscreenRenderer {
context: Arc<RwLock<Context>>,
device: Device,
fbo: NonZeroU32,
// canvas: Arc<Mutex<CanvasWrapper>>,
glow_ctx: Option<glow::Context>,
size: (i32, i32), // canvas: Arc<Mutex<CanvasWrapper>>,
}
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 adapter = connection.create_hardware_adapter()?;
let adapter = connection.create_adapter()?;
let mut device = connection.create_device(&adapter)?;
let api = connection.gl_api();
let descriptor = device.create_context_descriptor(&surfman::ContextAttributes {
version: surfman::GLVersion::new(3, 3),
flags: ContextAttributeFlags::DEPTH.union(ContextAttributeFlags::STENCIL),
version: surfman::GLVersion::new(4, 1),
flags: ContextAttributeFlags::empty(),
})?;
let mut context = device.create_context(&descriptor, None)?;
let surface = device.create_surface(
&context,
surfman::SurfaceAccess::GPUCPU,
surfman::SurfaceAccess::GPUOnly,
surfman::SurfaceType::Generic {
size: euclid::Size2D::new(3000, 3000),
size: euclid::Size2D::new(width, height),
},
)?;
let surface_info = device.surface_info(&surface);
device
.bind_surface_to_context(&mut context, surface)
.expect("Failed to bind surface to context");
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 {
context: Arc::new(RwLock::new(context)),
device,
fbo: NonZeroU32::new(surface_info.framebuffer_object).unwrap(),
glow_ctx: None,
size: (width, height),
})
}
pub fn create_canvas(&self) -> CanvasWrapper {
use glow::HasContext;
static LOAD_FN: fn(&str) -> *const std::ffi::c_void =
|s| epoxy::get_proc_addr(s) as *const _;
pub fn create_canvas(&mut self) -> CanvasWrapper {
let (w, h) = self.size;
let mut pixels: Vec<u8> = vec![0; w as usize * h as usize * 4];
let (mut renderer, fbo) = unsafe {
let renderer = OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
let ctx = glow::Context::from_loader_function(LOAD_FN);
let (mut renderer, fbo, ctx) = unsafe {
let renderer = OpenGl::new_from_function(|s| {
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 cbo = ctx.create_buffer().expect("can't create color buffer");
let ctx = glow::Context::from_loader_function(|s| {
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)
// .expect("No GTK provided framebuffer binding");
let fbo = glow::NativeFramebuffer(self.fbo);
ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
// let fbo = glow::NativeFramebuffer(id);
(renderer, fbo)
(renderer, fbo, ctx)
};
renderer.set_screen_target(Some(fbo));
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
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)
}
}

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);
}
}