// SPDX-License-Identifier: LGPL-3.0-or-later OR MPL-2.0 // This file is a part of `piet-glow`. // // `piet-glow` is free software: you can redistribute it and/or modify it under the terms of // either: // // * GNU Lesser General Public License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // * Mozilla Public License as published by the Mozilla Foundation, version 2. // // `piet-glow` 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 Lesser General Public License or the Mozilla Public License for more details. // // You should have received a copy of the GNU Lesser General Public License and the Mozilla // Public License along with `piet-glow`. If not, see . //! Test harness for the comparison generator. //! //! This can only be run on desktop for now. use std::cell::RefCell; use glow::{Context, HasContext}; use piet::samples; use piet_glow::GlContext; use winit::event_loop::EventLoop; #[path = "util/setup_context.rs"] mod util; fn main() -> Result<(), Box> { util::init(); let event_loop = EventLoop::new(); let mut glutin_setup = util::glutin_impl::GlutinSetup::new(&event_loop)?; event_loop.run(move |ev, elwt, flow| { flow.set_wait(); if let winit::event::Event::<()>::Resumed = ev { // Create a rendering context. let glow_context = glutin_setup.make_current(elwt)(); // Create the piet-glow context. let context = unsafe { piet_glow::GlContext::new(glow_context).unwrap() }; // piet takes a raw function pointer, making this workaround necessary. std::thread_local! { static CONTEXT: RefCell>> = RefCell::new(None); } CONTEXT.with(move |slot| *slot.borrow_mut() = Some(context)); samples::samples_main( |number, scale, path| { CONTEXT.with(|slot| { let mut guard = slot.borrow_mut(); let context = guard.as_mut().unwrap(); let picture = samples::get(number)?; let size = picture.size(); let scaled_width = (size.width * scale) as u32; let scaled_height = (size.height * scale) as u32; let multisampled = number != 16; // Create a texture to render into. let ctx = context.context(); let texture = if multisampled { unsafe { ctx.enable(glow::MULTISAMPLE); let texture = ctx.create_texture().unwrap(); ctx.bind_texture(glow::TEXTURE_2D_MULTISAMPLE, Some(texture)); ctx.tex_image_2d_multisample( glow::TEXTURE_2D_MULTISAMPLE, 16, glow::RGBA as i32, scaled_width as i32, scaled_height as i32, true, ); ctx.bind_texture(glow::TEXTURE_2D_MULTISAMPLE, None); Some(texture) } } else { None }; // Create a normal, non-multisampled texture. let render_texture = unsafe { let texture = ctx.create_texture().unwrap(); ctx.bind_texture(glow::TEXTURE_2D, Some(texture)); ctx.tex_image_2d( glow::TEXTURE_2D, 0, glow::RGBA as i32, scaled_width as i32, scaled_height as i32, 0, glow::RGBA, glow::UNSIGNED_BYTE, None, ); // Set up the texture parameters. 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.bind_texture(glow::TEXTURE_2D, None); texture }; // Use a framebuffer to render into the texture and make it current. let framebuffer = texture.map(|texture| unsafe { let framebuffer = ctx.create_framebuffer().unwrap(); ctx.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer)); ctx.bind_texture(glow::TEXTURE_2D_MULTISAMPLE, Some(texture)); ctx.framebuffer_texture_2d( glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::TEXTURE_2D_MULTISAMPLE, Some(texture), 0, ); // Check that the framebuffer is complete. assert_eq!( ctx.check_framebuffer_status(glow::FRAMEBUFFER), glow::FRAMEBUFFER_COMPLETE, "main fbo" ); ctx.bind_framebuffer(glow::FRAMEBUFFER, None); ctx.bind_texture(glow::TEXTURE_2D_MULTISAMPLE, None); framebuffer }); // Create an intermediate FBO to use with the render target texture. let intermediate_fbo = unsafe { let framebuffer = ctx.create_framebuffer().unwrap(); ctx.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer)); ctx.bind_texture(glow::TEXTURE_2D, Some(render_texture)); ctx.framebuffer_texture_2d( glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::TEXTURE_2D, Some(render_texture), 0, ); // Check that the framebuffer is complete. assert_eq!( ctx.check_framebuffer_status(glow::FRAMEBUFFER), glow::FRAMEBUFFER_COMPLETE, "intermediate fbo", ); ctx.bind_framebuffer(glow::FRAMEBUFFER, None); ctx.bind_texture(glow::TEXTURE_2D, None); framebuffer }; unsafe { ctx.bind_framebuffer( glow::FRAMEBUFFER, Some(if let Some(framebuffer) = framebuffer { framebuffer } else { intermediate_fbo }), ); } // Use a renderbuffer to render into the texture and make it current. let renderbuffer = unsafe { let renderbuffer = ctx.create_renderbuffer().unwrap(); ctx.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)); if multisampled { ctx.renderbuffer_storage_multisample( glow::RENDERBUFFER, 16, glow::DEPTH_COMPONENT16, scaled_width as i32, scaled_height as i32, ); } else { ctx.renderbuffer_storage( glow::RENDERBUFFER, glow::DEPTH_COMPONENT16, scaled_width as i32, scaled_height as i32, ); } ctx.framebuffer_renderbuffer( glow::FRAMEBUFFER, glow::DEPTH_ATTACHMENT, glow::RENDERBUFFER, Some(renderbuffer), ); // Check that the framebuffer is complete. assert_eq!( ctx.check_framebuffer_status(glow::FRAMEBUFFER), glow::FRAMEBUFFER_COMPLETE ); renderbuffer }; // Create a piet-glow render context. let mut rc = unsafe { context.render_context(scaled_width, scaled_height) }; piet::RenderContext::text(&mut rc).set_dpi(72.0); rc.set_bitmap_scale(scale); // Draw with the context. picture.draw(&mut rc)?; drop(rc); // Blit to the render target texture. let ctx = context.context(); if let Some(framebuffer) = framebuffer { unsafe { ctx.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)); ctx.bind_framebuffer( glow::DRAW_FRAMEBUFFER, Some(intermediate_fbo), ); ctx.blit_framebuffer( 0, 0, scaled_width as _, scaled_height as _, 0, 0, scaled_width as _, scaled_height as _, glow::COLOR_BUFFER_BIT, glow::NEAREST, ); ctx.bind_framebuffer(glow::FRAMEBUFFER, Some(intermediate_fbo)); } } // Get the data out of the texture. let mut data = vec![0; scaled_width as usize * scaled_height as usize * 4]; unsafe { ctx.read_pixels( 0, 0, scaled_width as i32, scaled_height as i32, glow::RGBA, glow::UNSIGNED_BYTE, glow::PixelPackData::Slice(&mut data), ); } // Write the data to a file. let mut img = image::RgbaImage::from_vec(scaled_width, scaled_height, data).unwrap(); // Flip it around. image::imageops::flip_vertical_in_place(&mut img); img.save(path)?; // Delete the texture and framebuffer. unsafe { if let Some(texture) = texture { ctx.delete_texture(texture); } if let Some(framebuffer) = framebuffer { ctx.delete_framebuffer(framebuffer); } ctx.delete_renderbuffer(renderbuffer); } Ok(()) }) }, "piet-glow", None, ); } }) }