rt(gl): port to OpenGL runtime to glow

This commit is contained in:
chyyran 2024-02-05 18:39:01 -05:00 committed by Ronny Chan
parent 77b957bf5e
commit 1bdadaa449
36 changed files with 1388 additions and 1373 deletions

18
Cargo.lock generated
View file

@ -1118,6 +1118,17 @@ dependencies = [
"web-sys",
]
[[package]]
name = "glow"
version = "0.14.0"
source = "git+https://github.com/grovesNL/glow/#eb44a878a756d5ddce8505158690ec9bd272be8f"
dependencies = [
"js-sys",
"slotmap",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "glslang"
version = "0.4.0"
@ -1537,7 +1548,7 @@ name = "librashader-common"
version = "0.4.5"
dependencies = [
"ash",
"gl",
"glow 0.14.0",
"halfbrown",
"num-traits",
"objc2-metal",
@ -1678,8 +1689,8 @@ name = "librashader-runtime-gl"
version = "0.4.5"
dependencies = [
"bytemuck",
"gl",
"glfw 0.47.0",
"glow 0.14.0",
"librashader-cache",
"librashader-common",
"librashader-preprocess",
@ -1688,7 +1699,6 @@ dependencies = [
"librashader-runtime",
"rayon",
"spirv-cross2",
"sptr",
"thiserror",
]
@ -3500,7 +3510,7 @@ dependencies = [
"cfg_aliases",
"core-graphics-types",
"d3d12",
"glow",
"glow 0.13.1",
"glutin_wgl_sys",
"gpu-alloc",
"gpu-allocator 0.26.0",

View file

@ -23,7 +23,7 @@ ash = "0.38"
spirv-cross2 = { version = "0.4", default-features = false }
objc2-metal = { version = "0.2" }
objc2 = { version = "0.5.0" }
glow = { version = "0.14", git = "https://github.com/grovesNL/glow/" }
wgpu = { version = "22", default-features = false }
wgpu-types = { version = "22" }

View file

@ -13,7 +13,7 @@ description = "RetroArch shaders for all."
[features]
default = []
opengl = ["gl"]
opengl = ["glow"]
d3d9 = ["windows"]
d3d11 = ["windows", "dxgi"]
d3d12 = ["windows", "dxgi"]
@ -28,7 +28,7 @@ rustc-hash = "2.0.0"
halfbrown = "0.2.4"
smartstring = "1.0"
gl = { version = "0.14", optional = true }
glow = { workspace = true, optional = true }
ash = { workspace = true, optional = true }
wgpu-types = { workspace = true, optional = true }

View file

@ -1,71 +1,71 @@
use crate::{FilterMode, ImageFormat, WrapMode};
impl From<ImageFormat> for gl::types::GLenum {
impl From<ImageFormat> for u32 {
fn from(format: ImageFormat) -> Self {
match format {
ImageFormat::Unknown => 0 as gl::types::GLenum,
ImageFormat::R8Unorm => gl::R8,
ImageFormat::R8Uint => gl::R8UI,
ImageFormat::R8Sint => gl::R8I,
ImageFormat::R8G8Unorm => gl::RG8,
ImageFormat::R8G8Uint => gl::RG8UI,
ImageFormat::R8G8Sint => gl::RG8I,
ImageFormat::R8G8B8A8Unorm => gl::RGBA8,
ImageFormat::R8G8B8A8Uint => gl::RGBA8UI,
ImageFormat::R8G8B8A8Sint => gl::RGBA8I,
ImageFormat::R8G8B8A8Srgb => gl::SRGB8_ALPHA8,
ImageFormat::A2B10G10R10UnormPack32 => gl::RGB10_A2,
ImageFormat::A2B10G10R10UintPack32 => gl::RGB10_A2UI,
ImageFormat::R16Uint => gl::R16UI,
ImageFormat::R16Sint => gl::R16I,
ImageFormat::R16Sfloat => gl::R16F,
ImageFormat::R16G16Uint => gl::RG16UI,
ImageFormat::R16G16Sint => gl::RG16I,
ImageFormat::R16G16Sfloat => gl::RG16F,
ImageFormat::R16G16B16A16Uint => gl::RGBA16UI,
ImageFormat::R16G16B16A16Sint => gl::RGBA16I,
ImageFormat::R16G16B16A16Sfloat => gl::RGBA16F,
ImageFormat::R32Uint => gl::R32UI,
ImageFormat::R32Sint => gl::R32I,
ImageFormat::R32Sfloat => gl::R32F,
ImageFormat::R32G32Uint => gl::RG32UI,
ImageFormat::R32G32Sint => gl::RG32I,
ImageFormat::R32G32Sfloat => gl::RG32F,
ImageFormat::R32G32B32A32Uint => gl::RGBA32UI,
ImageFormat::R32G32B32A32Sint => gl::RGBA32I,
ImageFormat::R32G32B32A32Sfloat => gl::RGBA32F,
ImageFormat::Unknown => 0,
ImageFormat::R8Unorm => glow::R8,
ImageFormat::R8Uint => glow::R8UI,
ImageFormat::R8Sint => glow::R8I,
ImageFormat::R8G8Unorm => glow::RG8,
ImageFormat::R8G8Uint => glow::RG8UI,
ImageFormat::R8G8Sint => glow::RG8I,
ImageFormat::R8G8B8A8Unorm => glow::RGBA8,
ImageFormat::R8G8B8A8Uint => glow::RGBA8UI,
ImageFormat::R8G8B8A8Sint => glow::RGBA8I,
ImageFormat::R8G8B8A8Srgb => glow::SRGB8_ALPHA8,
ImageFormat::A2B10G10R10UnormPack32 => glow::RGB10_A2,
ImageFormat::A2B10G10R10UintPack32 => glow::RGB10_A2UI,
ImageFormat::R16Uint => glow::R16UI,
ImageFormat::R16Sint => glow::R16I,
ImageFormat::R16Sfloat => glow::R16F,
ImageFormat::R16G16Uint => glow::RG16UI,
ImageFormat::R16G16Sint => glow::RG16I,
ImageFormat::R16G16Sfloat => glow::RG16F,
ImageFormat::R16G16B16A16Uint => glow::RGBA16UI,
ImageFormat::R16G16B16A16Sint => glow::RGBA16I,
ImageFormat::R16G16B16A16Sfloat => glow::RGBA16F,
ImageFormat::R32Uint => glow::R32UI,
ImageFormat::R32Sint => glow::R32I,
ImageFormat::R32Sfloat => glow::R32F,
ImageFormat::R32G32Uint => glow::RG32UI,
ImageFormat::R32G32Sint => glow::RG32I,
ImageFormat::R32G32Sfloat => glow::RG32F,
ImageFormat::R32G32B32A32Uint => glow::RGBA32UI,
ImageFormat::R32G32B32A32Sint => glow::RGBA32I,
ImageFormat::R32G32B32A32Sfloat => glow::RGBA32F,
}
}
}
impl From<WrapMode> for gl::types::GLenum {
impl From<WrapMode> for i32 {
fn from(value: WrapMode) -> Self {
match value {
WrapMode::ClampToBorder => gl::CLAMP_TO_BORDER,
WrapMode::ClampToEdge => gl::CLAMP_TO_EDGE,
WrapMode::Repeat => gl::REPEAT,
WrapMode::MirroredRepeat => gl::MIRRORED_REPEAT,
WrapMode::ClampToBorder => glow::CLAMP_TO_BORDER as i32,
WrapMode::ClampToEdge => glow::CLAMP_TO_EDGE as i32,
WrapMode::Repeat => glow::REPEAT as i32,
WrapMode::MirroredRepeat => glow::MIRRORED_REPEAT as i32,
}
}
}
impl From<FilterMode> for gl::types::GLenum {
impl From<FilterMode> for i32 {
fn from(value: FilterMode) -> Self {
match value {
FilterMode::Linear => gl::LINEAR,
_ => gl::NEAREST,
FilterMode::Linear => glow::LINEAR as i32,
_ => glow::NEAREST as i32,
}
}
}
impl FilterMode {
/// Get the mipmap filtering mode for the given combination.
pub fn gl_mip(&self, mip: FilterMode) -> gl::types::GLenum {
pub fn gl_mip(&self, mip: FilterMode) -> u32 {
match (self, mip) {
(FilterMode::Linear, FilterMode::Linear) => gl::LINEAR_MIPMAP_LINEAR,
(FilterMode::Linear, FilterMode::Nearest) => gl::LINEAR_MIPMAP_NEAREST,
(FilterMode::Nearest, FilterMode::Linear) => gl::NEAREST_MIPMAP_LINEAR,
_ => gl::NEAREST_MIPMAP_NEAREST,
(FilterMode::Linear, FilterMode::Linear) => glow::LINEAR_MIPMAP_LINEAR,
(FilterMode::Linear, FilterMode::Nearest) => glow::LINEAR_MIPMAP_NEAREST,
(FilterMode::Nearest, FilterMode::Linear) => glow::NEAREST_MIPMAP_LINEAR,
_ => glow::NEAREST_MIPMAP_NEAREST,
}
}
}

View file

@ -145,7 +145,7 @@ impl CompileShader<GLSL> for CrossReflect<targets::Glsl> {
)?;
self.fragment
.set_decoration(res.id, Decoration::Binding, DecorationValue::unset())?;
let mut name = res.name.to_string();
let name = res.name.to_string();
texture_fixups.push((name, binding));
}

View file

@ -20,13 +20,11 @@ librashader-runtime = { path = "../librashader-runtime" , version = "0.4.5" }
librashader-cache = { path = "../librashader-cache", version = "0.4.5" }
spirv-cross2 = { workspace = true, features = ["glsl"] }
gl = "0.14.0"
glow = { workspace = true}
bytemuck = { version = "1.12.3", features = ["derive"] }
thiserror = "1.0.37"
rayon = "1.6.1"
sptr = "0.3"
[features]
stable = ["librashader-reflect/stable"]

View file

@ -1,15 +1,19 @@
use gl::types::GLint;
use glow::HasContext;
use librashader_reflect::reflect::semantics::{BindingStage, UniformMemberBlock};
use librashader_runtime::uniforms::{BindUniform, UniformScalar, UniformStorage};
use std::fmt::Display;
#[derive(Debug, Copy, Clone)]
pub struct VariableLocation {
pub(crate) ubo: Option<UniformLocation<GLint>>,
pub(crate) push: Option<UniformLocation<GLint>>,
pub(crate) ubo: Option<UniformLocation<Option<glow::UniformLocation>>>,
pub(crate) push: Option<UniformLocation<Option<glow::UniformLocation>>>,
}
impl VariableLocation {
pub fn location(&self, offset_type: UniformMemberBlock) -> Option<UniformLocation<GLint>> {
pub fn location(
&self,
offset_type: UniformMemberBlock,
) -> Option<UniformLocation<Option<glow::UniformLocation>>> {
match offset_type {
UniformMemberBlock::Ubo => self.ubo,
UniformMemberBlock::PushConstant => self.push,
@ -23,16 +27,13 @@ pub struct UniformLocation<T> {
pub fragment: T,
}
impl UniformLocation<GLint> {
impl UniformLocation<Option<glow::UniformLocation>> {
#[allow(unused_comparisons)]
pub fn is_valid(&self, stage: BindingStage) -> bool {
let mut validity = false;
if stage.contains(BindingStage::FRAGMENT) {
validity = validity || self.fragment >= 0;
}
if stage.contains(BindingStage::VERTEX) {
validity = validity || self.vertex >= 0;
}
validity
// since glow::UniformLocation is None or NonZeroU32,
// is_some is sufficient for validity
(stage.contains(BindingStage::FRAGMENT) && self.fragment.is_some())
|| (stage.contains(BindingStage::VERTEX) && self.vertex.is_some())
}
pub fn bindable(&self) -> bool {
@ -40,26 +41,30 @@ impl UniformLocation<GLint> {
}
}
pub(crate) type GlUniformStorage = UniformStorage<GlUniformBinder, VariableLocation>;
pub(crate) type GlUniformStorage =
UniformStorage<GlUniformBinder, VariableLocation, Box<[u8]>, Box<[u8]>, glow::Context>;
pub trait GlUniformScalar: UniformScalar {
const FACTORY: unsafe fn(GLint, Self) -> ();
pub trait GlUniformScalar: UniformScalar + Display {
const FACTORY: unsafe fn(&glow::Context, Option<&glow::UniformLocation>, Self) -> ();
}
impl GlUniformScalar for f32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1f;
const FACTORY: unsafe fn(&glow::Context, Option<&glow::UniformLocation>, Self) -> () =
glow::Context::uniform_1_f32;
}
impl GlUniformScalar for i32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1i;
const FACTORY: unsafe fn(&glow::Context, Option<&glow::UniformLocation>, Self) -> () =
glow::Context::uniform_1_i32;
}
impl GlUniformScalar for u32 {
const FACTORY: unsafe fn(GLint, Self) -> () = gl::Uniform1ui;
const FACTORY: unsafe fn(&glow::Context, Option<&glow::UniformLocation>, Self) -> () =
glow::Context::uniform_1_u32;
}
pub(crate) struct GlUniformBinder;
impl<T> BindUniform<VariableLocation, T, ()> for GlUniformBinder
impl<T> BindUniform<VariableLocation, T, glow::Context> for GlUniformBinder
where
T: GlUniformScalar,
{
@ -67,7 +72,7 @@ where
block: UniformMemberBlock,
value: T,
location: VariableLocation,
_: &(),
device: &glow::Context,
) -> Option<()> {
if let Some(location) = location
.location(block)
@ -75,12 +80,12 @@ where
{
if location.is_valid(BindingStage::VERTEX) {
unsafe {
T::FACTORY(location.vertex, value);
T::FACTORY(device, location.vertex.as_ref(), value);
}
}
if location.is_valid(BindingStage::FRAGMENT) {
unsafe {
T::FACTORY(location.fragment, value);
T::FACTORY(device, location.fragment.as_ref(), value);
}
}
Some(())
@ -90,12 +95,12 @@ where
}
}
impl BindUniform<VariableLocation, &[f32; 4], ()> for GlUniformBinder {
impl BindUniform<VariableLocation, &[f32; 4], glow::Context> for GlUniformBinder {
fn bind_uniform(
block: UniformMemberBlock,
vec4: &[f32; 4],
location: VariableLocation,
_: &(),
device: &glow::Context,
) -> Option<()> {
if let Some(location) = location
.location(block)
@ -103,10 +108,10 @@ impl BindUniform<VariableLocation, &[f32; 4], ()> for GlUniformBinder {
{
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::Uniform4fv(location.vertex, 1, vec4.as_ptr());
device.uniform_4_f32_slice(location.vertex.as_ref(), vec4);
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::Uniform4fv(location.fragment, 1, vec4.as_ptr());
device.uniform_4_f32_slice(location.fragment.as_ref(), vec4);
}
}
Some(())
@ -116,12 +121,12 @@ impl BindUniform<VariableLocation, &[f32; 4], ()> for GlUniformBinder {
}
}
impl BindUniform<VariableLocation, &[f32; 16], ()> for GlUniformBinder {
impl BindUniform<VariableLocation, &[f32; 16], glow::Context> for GlUniformBinder {
fn bind_uniform(
block: UniformMemberBlock,
mat4: &[f32; 16],
location: VariableLocation,
_: &(),
device: &glow::Context,
) -> Option<()> {
if let Some(location) = location
.location(block)
@ -129,10 +134,10 @@ impl BindUniform<VariableLocation, &[f32; 16], ()> for GlUniformBinder {
{
unsafe {
if location.is_valid(BindingStage::VERTEX) {
gl::UniformMatrix4fv(location.vertex, 1, gl::FALSE, mat4.as_ptr());
device.uniform_matrix_4_f32_slice(location.vertex.as_ref(), false, mat4);
}
if location.is_valid(BindingStage::FRAGMENT) {
gl::UniformMatrix4fv(location.fragment, 1, gl::FALSE, mat4.as_ptr());
device.uniform_matrix_4_f32_slice(location.fragment.as_ref(), false, mat4);
}
}
Some(())

View file

@ -1,6 +1,5 @@
//! OpenGL shader runtime errors.
use gl::types::GLenum;
use librashader_preprocess::PreprocessError;
use librashader_presets::ParsePresetError;
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
@ -11,7 +10,7 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum FilterChainError {
#[error("fbo initialization error")]
FramebufferInit(GLenum),
FramebufferInit(u32),
#[error("SPIRV reflection error")]
SpirvCrossReflectError(#[from] spirv_cross2::SpirvCrossError),
#[error("shader preset parse error")]
@ -30,6 +29,12 @@ pub enum FilterChainError {
GLLinkError,
#[error("opengl could not compile program")]
GlCompileError,
#[error("opengl could not create samplers")]
GlSamplerError,
#[error("opengl could not create samplers")]
GlProgramError,
#[error("opengl error: {0}")]
GlError(String),
}
/// Result type for OpenGL filter chains.

View file

@ -9,7 +9,6 @@ use crate::samplers::SamplerSet;
use crate::texture::InputTexture;
use crate::util::{gl_get_version, gl_u16_to_version};
use crate::{error, GLImage};
use gl::types::GLuint;
use librashader_common::Viewport;
use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig};
@ -19,6 +18,7 @@ use librashader_reflect::back::{CompileReflectShader, CompileShader};
use librashader_reflect::front::SpirvCompilation;
use librashader_reflect::reflect::semantics::{ShaderSemantics, UniformMeta};
use glow::HasContext;
use librashader_cache::CachedCompilation;
use librashader_common::map::FastHashMap;
use librashader_reflect::reflect::cross::SpirvCross;
@ -30,6 +30,7 @@ use librashader_runtime::quad::QuadType;
use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer;
use std::collections::VecDeque;
use std::sync::Arc;
pub(crate) struct FilterChainImpl<T: GLInterface> {
pub(crate) common: FilterCommon,
@ -51,10 +52,15 @@ pub(crate) struct FilterCommon {
pub feedback_textures: Box<[InputTexture]>,
pub history_textures: Box<[InputTexture]>,
pub disable_mipmaps: bool,
pub context: Arc<glow::Context>,
}
impl<T: GLInterface> FilterChainImpl<T> {
fn reflect_uniform_location(pipeline: GLuint, meta: &dyn UniformMeta) -> VariableLocation {
fn reflect_uniform_location(
ctx: &glow::Context,
pipeline: glow::Program,
meta: &dyn UniformMeta,
) -> VariableLocation {
let mut location = VariableLocation {
ubo: None,
push: None,
@ -66,9 +72,8 @@ impl<T: GLInterface> FilterChainImpl<T> {
let vert_name = format!("LIBRA_UBO_VERTEX_INSTANCE.{}", meta.id());
let frag_name = format!("LIBRA_UBO_FRAGMENT_INSTANCE.{}", meta.id());
unsafe {
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
let vertex = ctx.get_uniform_location(pipeline, &vert_name);
let fragment = ctx.get_uniform_location(pipeline, &frag_name);
location.ubo = Some(UniformLocation { vertex, fragment })
}
}
@ -77,9 +82,8 @@ impl<T: GLInterface> FilterChainImpl<T> {
let vert_name = format!("LIBRA_PUSH_VERTEX_INSTANCE.{}", meta.id());
let frag_name = format!("LIBRA_PUSH_FRAGMENT_INSTANCE.{}", meta.id());
unsafe {
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
let vertex = ctx.get_uniform_location(pipeline, &vert_name);
let fragment = ctx.get_uniform_location(pipeline, &frag_name);
location.push = Some(UniformLocation { vertex, fragment })
}
}
@ -128,14 +132,18 @@ impl<T: GLInterface> FilterChainImpl<T> {
/// Load a filter chain from a pre-parsed `ShaderPreset`.
pub(crate) unsafe fn load_from_preset(
preset: ShaderPreset,
context: glow::Context,
options: Option<&FilterChainOptionsGL>,
) -> error::Result<Self> {
let disable_cache = options.map_or(false, |o| o.disable_cache);
let (passes, semantics) = compile_passes(preset.shaders, &preset.textures, disable_cache)?;
let version = options.map_or_else(gl_get_version, |o| gl_u16_to_version(o.glsl_version));
let version = options.map_or_else(
|| gl_get_version(&context),
|o| gl_u16_to_version(&context, o.glsl_version),
);
// initialize passes
let filters = Self::init_passes(version, passes, &semantics, disable_cache)?;
let filters = Self::init_passes(&context, version, passes, &semantics, disable_cache)?;
let default_filter = filters.first().map(|f| f.config.filter).unwrap_or_default();
let default_wrap = filters
@ -143,12 +151,13 @@ impl<T: GLInterface> FilterChainImpl<T> {
.map(|f| f.config.wrap_mode)
.unwrap_or_default();
let samplers = SamplerSet::new();
let samplers = SamplerSet::new(&context)?;
// load luts
let luts = T::LoadLut::load_luts(&preset.textures)?;
let luts = T::LoadLut::load_luts(&context, &preset.textures)?;
let framebuffer_gen = || Ok::<_, FilterChainError>(T::FramebufferInterface::new(1));
let context = Arc::new(context);
let framebuffer_gen = || T::FramebufferInterface::new(&context, 1);
let input_gen = || InputTexture {
image: Default::default(),
filter: default_filter,
@ -173,7 +182,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
let (history_framebuffers, history_textures) = framebuffer_init.init_history()?;
// create vertex objects
let draw_quad = T::DrawQuad::new();
let draw_quad = T::DrawQuad::new(&context)?;
Ok(FilterChainImpl {
draw_last_pass_feedback: framebuffer_init.uses_final_pass_as_feedback(),
@ -190,12 +199,14 @@ impl<T: GLInterface> FilterChainImpl<T> {
output_textures,
feedback_textures,
history_textures,
context,
},
default_options: Default::default(),
})
}
fn init_passes(
context: &glow::Context,
version: GlslVersion,
passes: Vec<ShaderPassMeta>,
semantics: &ShaderSemantics,
@ -208,10 +219,11 @@ impl<T: GLInterface> FilterChainImpl<T> {
let reflection = reflect.reflect(index, semantics)?;
let glsl = reflect.compile(version)?;
let (program, ubo_location) = T::CompileShader::compile_program(glsl, !disable_cache)?;
let (program, ubo_location) =
T::CompileShader::compile_program(context, glsl, !disable_cache)?;
let ubo_ring = if let Some(ubo) = &reflection.ubo {
let ring = UboRing::new(ubo.size);
let ring = T::UboRing::new(&context, ubo.size)?;
Some(ring)
} else {
None
@ -227,7 +239,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
let uniform_bindings = reflection.meta.create_binding_map(|param| {
UniformOffset::new(
Self::reflect_uniform_location(program, param),
Self::reflect_uniform_location(&context, program, param),
param.offset(),
)
});
@ -290,7 +302,8 @@ impl<T: GLInterface> FilterChainImpl<T> {
// do not need to rebind FBO 0 here since first `draw` will
// bind automatically.
self.draw_quad.bind_vertices(QuadType::Offscreen);
self.draw_quad
.bind_vertices(&self.common.context, QuadType::Offscreen);
let filter = passes[0].config.filter;
let wrap_mode = passes[0].config.wrap_mode;
@ -343,7 +356,8 @@ impl<T: GLInterface> FilterChainImpl<T> {
let passes_len = passes.len();
let (pass, last) = passes.split_at_mut(passes_len - 1);
self.draw_quad.bind_vertices(QuadType::Offscreen);
self.draw_quad
.bind_vertices(&self.common.context, QuadType::Offscreen);
for (index, pass) in pass.iter_mut().enumerate() {
let target = &self.output_framebuffers[index];
source.filter = pass.config.filter;
@ -366,7 +380,8 @@ impl<T: GLInterface> FilterChainImpl<T> {
source = target;
}
self.draw_quad.bind_vertices(QuadType::Final);
self.draw_quad
.bind_vertices(&self.common.context, QuadType::Final);
// try to hint the optimizer
assert_eq!(last.len(), 1);
if let Some(pass) = last.iter_mut().next() {
@ -412,7 +427,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
self.push_history(input)?;
self.draw_quad.unbind_vertices();
self.draw_quad.unbind_vertices(&self.common.context);
Ok(())
}

View file

@ -1,4 +1,4 @@
use crate::filter_chain::filter_impl::FilterChainImpl;
use crate::filter_chain::chain::FilterChainImpl;
pub(in crate::filter_chain) enum FilterChainDispatch {
DirectStateAccess(FilterChainImpl<crate::gl::gl46::DirectStateAccessGL>),

View file

@ -1,18 +1,18 @@
use std::panic::catch_unwind;
use std::path::Path;
use crate::error::{FilterChainError, Result};
use crate::filter_chain::filter_impl::FilterChainImpl;
use crate::filter_chain::chain::FilterChainImpl;
use crate::filter_chain::inner::FilterChainDispatch;
use crate::options::{FilterChainOptionsGL, FrameOptionsGL};
use crate::{GLFramebuffer, GLImage};
use librashader_presets::ShaderPreset;
use std::panic::catch_unwind;
use std::path::Path;
use std::sync::Arc;
mod filter_impl;
mod chain;
mod inner;
mod parameters;
pub(crate) use filter_impl::FilterCommon;
pub(crate) use chain::FilterCommon;
use librashader_common::Viewport;
use librashader_presets::context::VideoDriver;
@ -24,6 +24,7 @@ pub struct FilterChainGL {
impl FilterChainGL {
/// Load a filter chain from a pre-parsed `ShaderPreset`.
pub unsafe fn load_from_preset(
ctx: glow::Context,
preset: ShaderPreset,
options: Option<&FilterChainOptionsGL>,
) -> Result<Self> {
@ -31,13 +32,13 @@ impl FilterChainGL {
if options.is_some_and(|options| options.use_dsa) {
return Ok(Self {
filter: FilterChainDispatch::DirectStateAccess(unsafe {
FilterChainImpl::load_from_preset(preset, options)?
FilterChainImpl::load_from_preset(preset, ctx, options)?
}),
});
}
Ok(Self {
filter: FilterChainDispatch::Compatibility(unsafe {
FilterChainImpl::load_from_preset(preset, options)?
FilterChainImpl::load_from_preset(preset, ctx, options)?
}),
})
});
@ -46,12 +47,13 @@ impl FilterChainGL {
/// Load the shader preset at the given path into a filter chain.
pub unsafe fn load_from_path(
ctx: glow::Context,
path: impl AsRef<Path>,
options: Option<&FilterChainOptionsGL>,
) -> Result<Self> {
// load passes from preset
let preset = ShaderPreset::try_parse_with_driver_context(path, VideoDriver::GlCore)?;
unsafe { Self::load_from_preset(preset, options) }
unsafe { Self::load_from_preset(ctx, preset, options) }
}
/// Process a frame with the input image.
@ -74,4 +76,12 @@ impl FilterChainGL {
},
}
}
/// Get the GL context associated with this filter chain
pub fn get_context(&self) -> &Arc<glow::Context> {
match &self.filter {
FilterChainDispatch::DirectStateAccess(p) => &p.common.context,
FilterChainDispatch::Compatibility(p) => &p.common.context,
}
}
}

View file

@ -1,4 +1,4 @@
use crate::filter_chain::filter_impl::FilterChainImpl;
use crate::filter_chain::chain::FilterChainImpl;
use crate::filter_chain::inner::FilterChainDispatch;
use crate::gl::GLInterface;
use crate::FilterChainGL;

View file

@ -1,4 +1,4 @@
use gl::types::{GLint, GLsizei, GLuint};
use glow::HasContext;
use librashader_reflect::reflect::ShaderReflection;
use librashader_common::map::FastHashMap;
@ -32,8 +32,8 @@ impl UniformOffset {
pub(crate) struct FilterPass<T: GLInterface> {
pub reflection: ShaderReflection,
pub program: GLuint,
pub ubo_location: UniformLocation<GLuint>,
pub program: glow::Program,
pub ubo_location: UniformLocation<Option<u32>>,
pub ubo_ring: Option<T::UboRing>,
pub(crate) uniform_storage: GlUniformStorage,
pub uniform_bindings: FastHashMap<UniformBinding, UniformOffset>,
@ -47,7 +47,7 @@ impl TextureInput for InputTexture {
}
}
impl ContextOffset<GlUniformBinder, VariableLocation> for UniformOffset {
impl ContextOffset<GlUniformBinder, VariableLocation, glow::Context> for UniformOffset {
fn offset(&self) -> MemberOffset {
self.offset
}
@ -61,7 +61,7 @@ impl<T: GLInterface> BindSemantics<GlUniformBinder, VariableLocation> for Filter
type InputTexture = InputTexture;
type SamplerSet = SamplerSet;
type DescriptorSet<'a> = ();
type DeviceContext = ();
type DeviceContext = glow::Context;
type UniformOffset = UniformOffset;
fn bind_texture<'a>(
@ -69,9 +69,9 @@ impl<T: GLInterface> BindSemantics<GlUniformBinder, VariableLocation> for Filter
samplers: &Self::SamplerSet,
binding: &TextureBinding,
texture: &Self::InputTexture,
_device: &Self::DeviceContext,
device: &Self::DeviceContext,
) {
T::BindTexture::bind_texture(samplers, binding, texture);
T::BindTexture::bind_texture(device, samplers, binding, texture);
}
}
@ -85,17 +85,19 @@ impl<T: GLInterface> FilterPass<T> {
viewport: &Viewport<&GLFramebuffer>,
original: &InputTexture,
source: &InputTexture,
output: RenderTarget<GLFramebuffer, GLint>,
output: RenderTarget<GLFramebuffer, i32>,
) {
let framebuffer = output.output;
if self.config.mipmap_input && !parent.disable_mipmaps {
T::BindTexture::gen_mipmaps(source);
T::BindTexture::gen_mipmaps(&parent.context, source);
}
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.fbo);
gl::UseProgram(self.program);
parent
.context
.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer.fbo));
parent.context.use_program(Some(self.program));
}
self.build_semantics(
@ -110,38 +112,48 @@ impl<T: GLInterface> FilterPass<T> {
source,
);
if self.ubo_location.vertex != gl::INVALID_INDEX
&& self.ubo_location.fragment != gl::INVALID_INDEX
if self
.ubo_location
.vertex
.is_some_and(|index| index != glow::INVALID_INDEX)
&& self
.ubo_location
.fragment
.is_some_and(|index| index != glow::INVALID_INDEX)
{
if let (Some(ubo), Some(ring)) = (&self.reflection.ubo, &mut self.ubo_ring) {
ring.bind_for_frame(ubo, &self.ubo_location, &self.uniform_storage)
ring.bind_for_frame(
&parent.context,
ubo,
&self.ubo_location,
&self.uniform_storage,
)
}
}
unsafe {
framebuffer.clear::<T::FramebufferInterface, false>();
let framebuffer_size = framebuffer.size;
gl::Viewport(
parent.context.viewport(
output.x,
output.y,
framebuffer_size.width as GLsizei,
framebuffer_size.height as GLsizei,
framebuffer_size.width as i32,
framebuffer_size.height as i32,
);
if framebuffer.format == gl::SRGB8_ALPHA8 {
gl::Enable(gl::FRAMEBUFFER_SRGB);
if framebuffer.format == glow::SRGB8_ALPHA8 {
parent.context.enable(glow::FRAMEBUFFER_SRGB);
} else {
gl::Disable(gl::FRAMEBUFFER_SRGB);
parent.context.disable(glow::FRAMEBUFFER_SRGB);
}
gl::Disable(gl::CULL_FACE);
gl::Disable(gl::BLEND);
gl::Disable(gl::DEPTH_TEST);
parent.context.disable(glow::CULL_FACE);
parent.context.disable(glow::BLEND);
parent.context.disable(glow::DEPTH_TEST);
gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
gl::Disable(gl::FRAMEBUFFER_SRGB);
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
parent.context.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
parent.context.disable(glow::FRAMEBUFFER_SRGB);
parent.context.bind_framebuffer(glow::FRAMEBUFFER, None);
}
}
}
@ -171,7 +183,7 @@ impl<T: GLInterface> FilterPass<T> {
source: &InputTexture,
) {
Self::bind_semantics(
&(),
&parent.context,
&parent.samplers,
&mut self.uniform_storage,
&mut (),

View file

@ -1,4 +1,3 @@
use gl::types::{GLenum, GLuint};
use librashader_common::Size;
/// A handle to an OpenGL texture with format and size information.
@ -7,9 +6,9 @@ use librashader_common::Size;
#[derive(Default, Debug, Copy, Clone)]
pub struct GLImage {
/// A GLuint to the texture.
pub handle: GLuint,
pub handle: Option<glow::Texture>,
/// The format of the texture.
pub format: GLenum,
pub format: u32,
/// The size of the texture.
pub size: Size<u32>,
}

View file

@ -2,23 +2,25 @@ use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage;
use crate::gl::FramebufferInterface;
use crate::texture::InputTexture;
use gl::types::{GLenum, GLuint};
use glow::HasContext;
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::ScaleFramebuffer;
use std::sync::Arc;
/// A handle to an OpenGL FBO and its backing texture with format and size information.
///
/// Generally for use as render targets.
#[derive(Debug)]
pub struct GLFramebuffer {
pub(crate) image: GLuint,
pub(crate) fbo: GLuint,
pub(crate) image: Option<glow::Texture>,
pub(crate) fbo: glow::Framebuffer,
pub(crate) size: Size<u32>,
pub(crate) format: GLenum,
pub(crate) format: u32,
pub(crate) max_levels: u32,
pub(crate) mip_levels: u32,
pub(crate) is_raw: bool,
pub(crate) ctx: Arc<glow::Context>,
}
impl GLFramebuffer {
@ -26,9 +28,10 @@ impl GLFramebuffer {
///
/// The framebuffer will not be deleted when this struct is dropped.
pub fn new_from_raw(
texture: GLuint,
fbo: GLuint,
format: GLenum,
ctx: Arc<glow::Context>,
texture: Option<glow::Texture>,
fbo: glow::Framebuffer,
format: u32,
size: Size<u32>,
miplevels: u32,
) -> GLFramebuffer {
@ -38,8 +41,9 @@ impl GLFramebuffer {
format,
max_levels: miplevels,
mip_levels: miplevels,
fbo: fbo,
fbo,
is_raw: true,
ctx,
}
}
@ -92,11 +96,9 @@ impl Drop for GLFramebuffer {
}
unsafe {
if self.fbo != 0 {
gl::DeleteFramebuffers(1, &self.fbo);
}
if self.image != 0 {
gl::DeleteTextures(1, &self.image);
self.ctx.delete_framebuffer(self.fbo);
if let Some(image) = self.image {
self.ctx.delete_texture(image);
}
}
}

View file

@ -1,8 +1,9 @@
use crate::binding::UniformLocation;
use crate::error;
use crate::error::FilterChainError;
use crate::gl::CompileProgram;
use crate::util;
use gl::types::{GLint, GLuint};
use glow::HasContext;
use librashader_reflect::back::glsl::CrossGlslContext;
use librashader_reflect::back::ShaderCompilerOutput;
use spirv_cross2::reflect::ResourceType;
@ -12,18 +13,20 @@ pub struct Gl3CompileProgram;
impl CompileProgram for Gl3CompileProgram {
fn compile_program(
ctx: &glow::Context,
glsl: ShaderCompilerOutput<String, CrossGlslContext>,
_cache: bool,
) -> crate::error::Result<(GLuint, UniformLocation<GLuint>)> {
) -> error::Result<(glow::Program, UniformLocation<Option<u32>>)> {
let vertex_resources = glsl.context.artifact.vertex.shader_resources()?;
let (program, ubo_location) = unsafe {
let vertex = util::gl_compile_shader(gl::VERTEX_SHADER, glsl.vertex.as_str())?;
let fragment = util::gl_compile_shader(gl::FRAGMENT_SHADER, glsl.fragment.as_str())?;
let vertex = util::gl_compile_shader(ctx, glow::VERTEX_SHADER, glsl.vertex.as_str())?;
let fragment =
util::gl_compile_shader(ctx, glow::FRAGMENT_SHADER, glsl.fragment.as_str())?;
let program = gl::CreateProgram();
gl::AttachShader(program, vertex);
gl::AttachShader(program, fragment);
let program = ctx.create_program().map_err(FilterChainError::GlError)?;
ctx.attach_shader(program, vertex);
ctx.attach_shader(program, fragment);
for res in vertex_resources.resources_for_type(ResourceType::StageInput)? {
let Some(loc) = glsl
@ -36,43 +39,33 @@ impl CompileProgram for Gl3CompileProgram {
continue;
};
let mut name = res.name.to_string();
name.push('\0');
gl::BindAttribLocation(program, loc, name.as_str().as_ptr().cast())
ctx.bind_attrib_location(program, loc, &res.name.as_ref());
}
gl::LinkProgram(program);
gl::DeleteShader(vertex);
gl::DeleteShader(fragment);
let mut status = 0;
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
if status != 1 {
ctx.link_program(program);
ctx.delete_shader(vertex);
ctx.delete_shader(fragment);
if !ctx.get_program_link_status(program) {
return Err(FilterChainError::GLLinkError);
}
gl::UseProgram(program);
ctx.use_program(Some(program));
for (name, binding) in &glsl.context.sampler_bindings {
let location = gl::GetUniformLocation(program, name.as_str().as_ptr().cast());
if location >= 0 {
let location = ctx.get_uniform_location(program, name);
if let Some(location) = location {
// eprintln!("setting sampler {location} to sample from {binding}");
gl::Uniform1i(location, *binding as GLint);
ctx.uniform_1_i32(Some(&location), *binding as i32);
}
}
gl::UseProgram(0);
ctx.use_program(None);
(
program,
UniformLocation {
vertex: gl::GetUniformBlockIndex(
program,
b"LIBRA_UBO_VERTEX\0".as_ptr().cast(),
),
fragment: gl::GetUniformBlockIndex(
program,
b"LIBRA_UBO_FRAGMENT\0".as_ptr().cast(),
),
vertex: ctx.get_uniform_block_index(program, "LIBRA_UBO_VERTEX"),
fragment: ctx.get_uniform_block_index(program, "LIBRA_UBO_FRAGMENT"),
},
)
};

View file

@ -1,102 +1,110 @@
use crate::gl::DrawQuad;
use crate::gl::{FINAL_VBO_DATA, OFFSCREEN_VBO_DATA};
use crate::error;
use crate::error::FilterChainError;
use crate::gl::{DrawQuad, FINAL_VBO_DATA, OFFSCREEN_VBO_DATA};
use bytemuck::offset_of;
use gl::types::{GLsizei, GLsizeiptr, GLuint};
use glow::HasContext;
use librashader_runtime::quad::{QuadType, VertexInput};
pub struct Gl3DrawQuad {
vbo: [GLuint; 2],
vao: GLuint,
vbo: [glow::Buffer; 2],
vao: glow::VertexArray,
}
impl DrawQuad for Gl3DrawQuad {
fn new() -> Gl3DrawQuad {
let mut vbo = [0, 0];
let mut vao = 0;
fn new(ctx: &glow::Context) -> error::Result<Self> {
let vbo;
let vao;
unsafe {
gl::GenBuffers(2, vbo.as_mut_ptr());
gl::BindBuffer(gl::ARRAY_BUFFER, vbo[0]);
gl::BufferData(
gl::ARRAY_BUFFER,
std::mem::size_of_val(OFFSCREEN_VBO_DATA) as GLsizeiptr,
OFFSCREEN_VBO_DATA.as_ptr().cast(),
gl::STATIC_DRAW,
vbo = [
ctx.create_buffer()
.map_err(FilterChainError::GlError)?,
ctx.create_buffer()
.map_err(FilterChainError::GlError)?,
];
ctx.bind_buffer(glow::ARRAY_BUFFER, Some(vbo[0]));
ctx.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
bytemuck::cast_slice(OFFSCREEN_VBO_DATA),
glow::STATIC_DRAW,
);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo[1]);
gl::BufferData(
gl::ARRAY_BUFFER,
std::mem::size_of_val(FINAL_VBO_DATA) as GLsizeiptr,
FINAL_VBO_DATA.as_ptr().cast(),
gl::STATIC_DRAW,
ctx.bind_buffer(glow::ARRAY_BUFFER, Some(vbo[1]));
ctx.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
bytemuck::cast_slice(FINAL_VBO_DATA),
glow::STATIC_DRAW,
);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::GenVertexArrays(1, &mut vao);
ctx.bind_buffer(glow::ARRAY_BUFFER, None);
vao = ctx
.create_vertex_array()
.map_err(FilterChainError::GlError)?;
}
Self { vbo, vao }
Ok(Self { vbo, vao })
}
fn bind_vertices(&self, quad_type: QuadType) {
fn bind_vertices(&self, ctx: &glow::Context, quad_type: QuadType) {
let buffer_index = match quad_type {
QuadType::Offscreen => 0,
QuadType::Final => 1,
};
unsafe {
gl::BindVertexArray(self.vao);
gl::EnableVertexAttribArray(0);
gl::EnableVertexAttribArray(1);
ctx.bind_vertex_array(Some(self.vao));
ctx.enable_vertex_attrib_array(0);
ctx.enable_vertex_attrib_array(1);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo[buffer_index]);
ctx.bind_buffer(glow::ARRAY_BUFFER, Some(self.vbo[buffer_index]));
// the provided pointers are of OpenGL provenance with respect to the buffer bound to quad_vbo,
// and not a known provenance to the Rust abstract machine, therefore we give it invalid pointers.
// that are inexpressible in Rust
gl::VertexAttribPointer(
ctx.vertex_attrib_pointer_f32(
0,
4,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<VertexInput>() as GLsizei,
sptr::invalid(offset_of!(VertexInput, position)),
glow::FLOAT,
false,
std::mem::size_of::<VertexInput>() as i32,
offset_of!(VertexInput, position) as i32,
);
gl::VertexAttribPointer(
ctx.vertex_attrib_pointer_f32(
1,
2,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<VertexInput>() as GLsizei,
sptr::invalid(offset_of!(VertexInput, texcoord)),
glow::FLOAT,
false,
std::mem::size_of::<VertexInput>() as i32,
offset_of!(VertexInput, texcoord) as i32,
);
}
}
fn unbind_vertices(&self) {
fn unbind_vertices(&self, ctx: &glow::Context) {
unsafe {
gl::DisableVertexAttribArray(0);
gl::DisableVertexAttribArray(1);
gl::BindVertexArray(0);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
ctx.disable_vertex_attrib_array(0);
ctx.disable_vertex_attrib_array(1);
ctx.bind_vertex_array(None);
ctx.bind_buffer(glow::ARRAY_BUFFER, None);
}
}
}
impl Drop for Gl3DrawQuad {
fn drop(&mut self) {
unsafe {
if self.vbo[0] != 0 {
gl::DeleteBuffers(1, &self.vbo[0]);
}
if self.vbo[1] != 0 {
gl::DeleteBuffers(1, &self.vbo[1]);
}
if self.vao != 0 {
gl::DeleteVertexArrays(1, &self.vao)
}
}
}
}
// impl Drop for Gl3DrawQuad {
// fn drop(&mut self) {
// unsafe {
// if let Some(vbo) = self.vbo {
// glow::DeleteBuffers(1, &self.vbo[0]);
// }
//
// if self.vbo[1] != 0 {
// glow::DeleteBuffers(1, &self.vbo[1]);
// }
//
// if self.vao != 0 {
// glow::DeleteVertexArrays(1, &self.vao)
// }
// }
// }
// }

View file

@ -1,82 +1,51 @@
use crate::error;
use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage;
use crate::gl::framebuffer::GLFramebuffer;
use crate::gl::FramebufferInterface;
use gl::types::{GLenum, GLint, GLsizei};
use glow::HasContext;
use librashader_common::{ImageFormat, Size};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
use librashader_runtime::scaling::MipmapSize;
use std::sync::Arc;
#[derive(Debug)]
pub struct Gl3Framebuffer;
impl FramebufferInterface for Gl3Framebuffer {
fn new(max_levels: u32) -> GLFramebuffer {
let mut framebuffer = 0;
fn new(ctx: &Arc<glow::Context>, max_levels: u32) -> error::Result<GLFramebuffer> {
unsafe {
gl::GenFramebuffers(1, &mut framebuffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
}
let framebuffer = ctx
.create_framebuffer()
.map_err(FilterChainError::GlError)?;
ctx.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer));
ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
GLFramebuffer {
image: 0,
size: Size {
width: 1,
height: 1,
},
format: 0,
max_levels,
mip_levels: 0,
fbo: framebuffer,
is_raw: false,
}
}
fn scale(
fb: &mut GLFramebuffer,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
original_size: &Size<u32>,
mipmap: bool,
) -> Result<Size<u32>> {
if fb.is_raw {
return Ok(fb.size);
}
let size = source_size.scale_viewport(scaling, *viewport_size, *original_size);
if fb.size != size || (mipmap && fb.max_levels == 1) || (!mipmap && fb.max_levels != 1) {
fb.size = size;
if mipmap {
fb.max_levels = u32::MAX;
} else {
fb.max_levels = 1
}
Self::init(
fb,
size,
if format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
format
Ok(GLFramebuffer {
image: None,
size: Size {
width: 1,
height: 1,
},
)?;
format: 0,
max_levels,
mip_levels: 0,
fbo: framebuffer,
is_raw: false,
ctx: Arc::clone(&ctx),
})
}
Ok(size)
}
fn clear<const REBIND: bool>(fb: &GLFramebuffer) {
unsafe {
if REBIND {
gl::BindFramebuffer(gl::FRAMEBUFFER, fb.fbo);
fb.ctx.bind_framebuffer(glow::FRAMEBUFFER, Some(fb.fbo));
}
gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
gl::ClearColor(0.0, 0.0, 0.0, 0.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
fb.ctx.color_mask(true, true, true, true);
fb.ctx.clear_color(0.0, 0.0, 0.0, 0.0);
fb.ctx.clear(glow::COLOR_BUFFER_BIT);
if REBIND {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
fb.ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
}
}
}
@ -87,70 +56,70 @@ impl FramebufferInterface for Gl3Framebuffer {
}
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, fb.fbo);
fb.ctx.bind_framebuffer(glow::FRAMEBUFFER, Some(fb.fbo));
gl::FramebufferTexture2D(
gl::READ_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
fb.ctx.framebuffer_texture_2d(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
image.handle,
0,
);
gl::FramebufferTexture2D(
gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT1,
gl::TEXTURE_2D,
fb.ctx.framebuffer_texture_2d(
glow::DRAW_FRAMEBUFFER,
glow::COLOR_ATTACHMENT1,
glow::TEXTURE_2D,
fb.image,
0,
);
gl::ReadBuffer(gl::COLOR_ATTACHMENT0);
gl::DrawBuffer(gl::COLOR_ATTACHMENT1);
gl::BlitFramebuffer(
fb.ctx.read_buffer(glow::COLOR_ATTACHMENT0);
fb.ctx.draw_buffer(glow::COLOR_ATTACHMENT1);
fb.ctx.blit_framebuffer(
0,
0,
fb.size.width as GLint,
fb.size.height as GLint,
fb.size.width as i32,
fb.size.height as i32,
0,
0,
fb.size.width as GLint,
fb.size.height as GLint,
gl::COLOR_BUFFER_BIT,
gl::NEAREST,
fb.size.width as i32,
fb.size.height as i32,
glow::COLOR_BUFFER_BIT,
glow::NEAREST,
);
// cleanup after ourselves.
gl::FramebufferTexture2D(
gl::READ_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
0,
fb.ctx.framebuffer_texture_2d(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
None,
0,
);
gl::FramebufferTexture2D(
gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT1,
gl::TEXTURE_2D,
0,
fb.ctx.framebuffer_texture_2d(
glow::DRAW_FRAMEBUFFER,
glow::COLOR_ATTACHMENT1,
glow::TEXTURE_2D,
None,
0,
);
// set this back to color_attachment 0
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
fb.ctx.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
fb.image,
0,
);
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
fb.ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
}
Ok(())
}
fn init(fb: &mut GLFramebuffer, mut size: Size<u32>, format: impl Into<GLenum>) -> Result<()> {
fn init(fb: &mut GLFramebuffer, mut size: Size<u32>, format: impl Into<u32>) -> Result<()> {
if fb.is_raw {
return Ok(());
}
@ -158,22 +127,23 @@ impl FramebufferInterface for Gl3Framebuffer {
fb.size = size;
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, fb.fbo);
fb.ctx.bind_framebuffer(glow::FRAMEBUFFER, Some(fb.fbo));
// reset the framebuffer image
if fb.image != 0 {
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
0,
if let Some(image) = fb.image {
fb.ctx.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
None,
0,
);
gl::DeleteTextures(1, &fb.image);
fb.ctx.delete_texture(image);
}
gl::GenTextures(1, &mut fb.image);
gl::BindTexture(gl::TEXTURE_2D, fb.image);
fb.image = Some(fb.ctx.create_texture().map_err(FilterChainError::GlError)?);
fb.ctx.bind_texture(glow::TEXTURE_2D, fb.image);
if size.width == 0 {
size.width = 1;
@ -190,36 +160,41 @@ impl FramebufferInterface for Gl3Framebuffer {
fb.mip_levels = 1;
}
gl::TexStorage2D(
gl::TEXTURE_2D,
fb.mip_levels as GLsizei,
fb.ctx.tex_storage_2d(
glow::TEXTURE_2D,
fb.mip_levels as i32,
fb.format,
size.width as GLsizei,
size.height as GLsizei,
size.width as i32,
size.height as i32,
);
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
fb.ctx.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
fb.image,
0,
);
let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
if status != gl::FRAMEBUFFER_COMPLETE {
let status = fb.ctx.check_framebuffer_status(glow::FRAMEBUFFER);
if status != glow::FRAMEBUFFER_COMPLETE {
match status {
gl::FRAMEBUFFER_UNSUPPORTED => {
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
0,
glow::FRAMEBUFFER_UNSUPPORTED => {
fb.ctx.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
None,
0,
);
gl::DeleteTextures(1, &fb.image);
gl::GenTextures(1, &mut fb.image);
gl::BindTexture(gl::TEXTURE_2D, fb.image);
if let Some(image) = fb.image {
fb.ctx.delete_texture(image);
}
fb.image =
Some(fb.ctx.create_texture().map_err(FilterChainError::GlError)?);
fb.ctx.bind_texture(glow::TEXTURE_2D, fb.image);
fb.mip_levels = size.calculate_miplevels();
if fb.mip_levels > fb.max_levels {
@ -229,29 +204,31 @@ impl FramebufferInterface for Gl3Framebuffer {
fb.mip_levels = 1;
}
gl::TexStorage2D(
gl::TEXTURE_2D,
fb.mip_levels as GLsizei,
fb.ctx.tex_storage_2d(
glow::TEXTURE_2D,
fb.mip_levels as i32,
ImageFormat::R8G8B8A8Unorm.into(),
size.width as GLsizei,
size.height as GLsizei,
size.width as i32,
size.height as i32,
);
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
fb.ctx.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
fb.image,
0,
);
// fb.init =
// gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE;
// glow::CheckFramebufferStatus(glow::FRAMEBUFFER) == glow::FRAMEBUFFER_COMPLETE;
}
_ => return Err(FilterChainError::FramebufferInit(status)),
}
}
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::BindTexture(gl::TEXTURE_2D, 0);
fb.ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
fb.ctx.bind_texture(glow::TEXTURE_2D, None);
}
Ok(())

View file

@ -1,23 +1,23 @@
use crate::error::Result;
use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage;
use crate::gl::LoadLut;
use crate::texture::InputTexture;
use gl::types::{GLsizei, GLuint};
use glow::{HasContext, PixelUnpackData};
use librashader_common::map::FastHashMap;
use librashader_presets::TextureConfig;
use librashader_runtime::image::{Image, ImageError, UVDirection};
use librashader_runtime::scaling::MipmapSize;
use rayon::prelude::*;
use std::num::NonZeroU32;
pub struct Gl3LutLoad;
impl LoadLut for Gl3LutLoad {
fn load_luts(textures: &[TextureConfig]) -> Result<FastHashMap<usize, InputTexture>> {
fn load_luts(
ctx: &glow::Context,
textures: &[TextureConfig],
) -> Result<FastHashMap<usize, InputTexture>> {
let mut luts = FastHashMap::default();
let pixel_unpack = unsafe {
let mut binding = 0;
gl::GetIntegerv(gl::PIXEL_UNPACK_BUFFER_BINDING, &mut binding);
binding
};
let pixel_unpack = unsafe { ctx.get_parameter_i32(glow::PIXEL_UNPACK_BUFFER_BINDING) };
let images = textures
.par_iter()
@ -31,47 +31,49 @@ impl LoadLut for Gl3LutLoad {
1u32
};
let mut handle = 0;
unsafe {
gl::GenTextures(1, &mut handle);
gl::BindTexture(gl::TEXTURE_2D, handle);
gl::TexStorage2D(
gl::TEXTURE_2D,
levels as GLsizei,
gl::RGBA8,
image.size.width as GLsizei,
image.size.height as GLsizei,
let handle = unsafe {
let handle = ctx.create_texture().map_err(FilterChainError::GlError)?;
ctx.bind_texture(glow::TEXTURE_2D, Some(handle));
ctx.tex_storage_2d(
glow::TEXTURE_2D,
levels as i32,
glow::RGBA8,
image.size.width as i32,
image.size.height as i32,
);
gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0);
gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4);
gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, 0);
gl::TexSubImage2D(
gl::TEXTURE_2D,
ctx.pixel_store_i32(glow::UNPACK_ROW_LENGTH, 0);
ctx.pixel_store_i32(glow::UNPACK_ALIGNMENT, 4);
ctx.bind_buffer(glow::PIXEL_UNPACK_BUFFER, None);
ctx.tex_sub_image_2d(
glow::TEXTURE_2D,
0,
0,
0,
image.size.width as GLsizei,
image.size.height as GLsizei,
gl::RGBA,
gl::UNSIGNED_BYTE,
image.bytes.as_ptr().cast(),
image.size.width as i32,
image.size.height as i32,
glow::RGBA,
glow::UNSIGNED_BYTE,
PixelUnpackData::Slice(&image.bytes),
);
let mipmap = levels > 1;
if mipmap {
gl::GenerateMipmap(gl::TEXTURE_2D);
ctx.generate_mipmap(glow::TEXTURE_2D);
}
gl::BindTexture(gl::TEXTURE_2D, 0);
}
ctx.bind_texture(glow::TEXTURE_2D, None);
handle
};
luts.insert(
index,
InputTexture {
image: GLImage {
handle,
format: gl::RGBA8,
handle: Some(handle),
format: glow::RGBA8,
size: image.size,
},
filter: texture.filter_mode,
@ -82,7 +84,12 @@ impl LoadLut for Gl3LutLoad {
}
unsafe {
gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, pixel_unpack as GLuint);
// todo: webgl doesn't support this.
let pixel_unpack = NonZeroU32::try_from(pixel_unpack as u32)
.ok()
.map(glow::NativeBuffer);
ctx.bind_buffer(glow::PIXEL_UNPACK_BUFFER, pixel_unpack);
};
Ok(luts)
}

View file

@ -1,29 +1,34 @@
use crate::gl::BindTexture;
use crate::samplers::SamplerSet;
use crate::texture::InputTexture;
use glow::HasContext;
use librashader_reflect::reflect::semantics::TextureBinding;
pub struct Gl3BindTexture;
impl BindTexture for Gl3BindTexture {
fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &InputTexture) {
fn bind_texture(
ctx: &glow::Context,
samplers: &SamplerSet,
binding: &TextureBinding,
texture: &InputTexture,
) {
unsafe {
// eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding);
gl::ActiveTexture(gl::TEXTURE0 + binding.binding);
gl::BindTexture(gl::TEXTURE_2D, texture.image.handle);
gl::BindSampler(
// eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding);;
ctx.active_texture(glow::TEXTURE0 + binding.binding);
ctx.bind_texture(glow::TEXTURE_2D, texture.image.handle);
ctx.bind_sampler(
binding.binding,
samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter),
Some(samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter)),
);
}
}
fn gen_mipmaps(texture: &InputTexture) {
fn gen_mipmaps(ctx: &glow::Context, texture: &InputTexture) {
unsafe {
gl::BindTexture(gl::TEXTURE_2D, texture.image.handle);
gl::GenerateMipmap(gl::TEXTURE_2D);
gl::BindTexture(gl::TEXTURE_2D, 0);
ctx.bind_texture(glow::TEXTURE_2D, texture.image.handle);
ctx.generate_mipmap(glow::TEXTURE_2D);
ctx.bind_texture(glow::TEXTURE_2D, None);
}
}
}

View file

@ -1,58 +1,88 @@
use crate::binding::UniformLocation;
use crate::error;
use crate::error::FilterChainError;
use crate::gl::UboRing;
use gl::types::{GLsizei, GLsizeiptr, GLuint};
use glow::HasContext;
use librashader_reflect::reflect::semantics::BufferReflection;
use librashader_runtime::ringbuffer::InlineRingBuffer;
use librashader_runtime::ringbuffer::RingBuffer;
use librashader_runtime::uniforms::UniformStorageAccess;
use std::mem::MaybeUninit;
pub struct Gl3UboRing<const SIZE: usize> {
ring: InlineRingBuffer<GLuint, SIZE>,
ring: InlineRingBuffer<glow::Buffer, SIZE>,
}
impl<const SIZE: usize> Gl3UboRing<SIZE> {
const _ASSERT_TRANSMUTABLE: () = assert!(
std::mem::size_of::<[glow::Buffer; SIZE]>()
== std::mem::size_of::<[MaybeUninit<glow::Buffer>; SIZE]>()
);
}
impl<const SIZE: usize> UboRing<SIZE> for Gl3UboRing<SIZE> {
fn new(buffer_size: u32) -> Self {
let mut ring: InlineRingBuffer<GLuint, SIZE> = InlineRingBuffer::new();
unsafe {
gl::GenBuffers(SIZE as GLsizei, ring.items_mut().as_mut_ptr());
for buffer in ring.items() {
gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer);
gl::BufferData(
gl::UNIFORM_BUFFER,
buffer_size as GLsizeiptr,
std::ptr::null(),
gl::STREAM_DRAW,
);
fn new(ctx: &glow::Context, buffer_size: u32) -> error::Result<Self> {
// TODO: array::try_from_fn whenever that gets stabilized
// this is basically blocking on try_trait_v2
let mut items: [MaybeUninit<glow::Buffer>; SIZE] = [MaybeUninit::zeroed(); SIZE];
for items in items.iter_mut() {
unsafe {
let buffer = ctx
.create_buffer()
.map(|buffer| {
ctx.bind_buffer(glow::UNIFORM_BUFFER, Some(buffer));
ctx.buffer_data_size(
glow::UNIFORM_BUFFER,
buffer_size as i32,
glow::STREAM_DRAW,
);
ctx.bind_buffer(glow::UNIFORM_BUFFER, None);
buffer
})
.map_err(FilterChainError::GlError)?;
*items = MaybeUninit::new(buffer)
}
gl::BindBuffer(gl::UNIFORM_BUFFER, 0);
}
Gl3UboRing { ring }
// SAFETY: everything was initialized above.
// MaybeUninit<glow::Buffer> and glow::Buffer have the same size.
let items: [glow::Buffer; SIZE] = unsafe { std::mem::transmute_copy(&items) };
let ring: InlineRingBuffer<glow::Buffer, SIZE> = InlineRingBuffer::from_array(items);
Ok(Gl3UboRing { ring })
}
fn bind_for_frame(
&mut self,
ctx: &glow::Context,
ubo: &BufferReflection<u32>,
ubo_location: &UniformLocation<GLuint>,
ubo_location: &UniformLocation<Option<u32>>,
storage: &impl UniformStorageAccess,
) {
let size = ubo.size;
let buffer = self.ring.current();
let buffer = *self.ring.current();
unsafe {
gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer);
gl::BufferSubData(
gl::UNIFORM_BUFFER,
ctx.bind_buffer(glow::UNIFORM_BUFFER, Some(buffer));
ctx.buffer_sub_data_u8_slice(
glow::UNIFORM_BUFFER,
0,
size as GLsizeiptr,
storage.ubo_pointer().cast(),
&storage.ubo_slice()[0..ubo.size as usize],
);
gl::BindBuffer(gl::UNIFORM_BUFFER, 0);
ctx.bind_buffer(glow::UNIFORM_BUFFER, None);
if ubo_location.vertex != gl::INVALID_INDEX {
gl::BindBufferBase(gl::UNIFORM_BUFFER, ubo_location.vertex, *buffer);
if let Some(vertex) = ubo_location
.vertex
.filter(|vertex| *vertex != glow::INVALID_INDEX)
{
ctx.bind_buffer_base(glow::UNIFORM_BUFFER, vertex, Some(buffer));
}
if ubo_location.fragment != gl::INVALID_INDEX {
gl::BindBufferBase(gl::UNIFORM_BUFFER, ubo_location.fragment, *buffer);
if let Some(fragment) = ubo_location
.fragment
.filter(|fragment| *fragment != glow::INVALID_INDEX)
{
ctx.bind_buffer_base(glow::UNIFORM_BUFFER, fragment, Some(buffer));
}
}
self.ring.next()

View file

@ -2,7 +2,7 @@ use crate::binding::UniformLocation;
use crate::error::FilterChainError;
use crate::gl::CompileProgram;
use crate::util;
use gl::types::{GLint, GLsizei, GLuint};
use glow::HasContext;
use librashader_cache::Cacheable;
use librashader_reflect::back::glsl::CrossGlslContext;
use librashader_reflect::back::ShaderCompilerOutput;
@ -11,10 +11,7 @@ use spirv_cross2::spirv::Decoration;
pub struct Gl4CompileProgram;
struct GlProgramBinary {
program: Vec<u8>,
format: GLuint,
}
struct GlProgramBinary(Option<glow::ProgramBinary>);
impl Cacheable for GlProgramBinary {
fn from_bytes(cached: &[u8]) -> Option<Self>
@ -23,45 +20,52 @@ impl Cacheable for GlProgramBinary {
{
let mut cached = Vec::from(cached);
let format = cached.split_off(cached.len() - std::mem::size_of::<u32>());
let format: Option<&GLuint> = bytemuck::try_from_bytes(&format).ok();
let format: Option<&u32> = bytemuck::try_from_bytes(&format).ok();
let Some(format) = format else {
return None;
};
return Some(GlProgramBinary {
program: cached,
return Some(GlProgramBinary(Some(glow::ProgramBinary {
buffer: cached,
format: *format,
});
})));
}
fn to_bytes(&self) -> Option<Vec<u8>> {
let mut slice = self.program.clone();
slice.extend(bytemuck::bytes_of(&self.format));
let Some(binary) = &self.0 else { return None };
let mut slice = binary.buffer.clone();
slice.extend(bytemuck::bytes_of(&binary.format));
Some(slice)
}
}
impl CompileProgram for Gl4CompileProgram {
fn compile_program(
context: &glow::Context,
glsl: ShaderCompilerOutput<String, CrossGlslContext>,
cache: bool,
) -> crate::error::Result<(GLuint, UniformLocation<GLuint>)> {
let vertex_resources = glsl.context.artifact.vertex.shader_resources()?;
) -> crate::error::Result<(glow::Program, UniformLocation<Option<u32>>)> {
fn compile_shader(
context: &glow::Context,
resources: &CrossGlslContext,
vertex: &str,
fragment: &str,
) -> crate::error::Result<glow::Program> {
unsafe {
let vertex_resources = resources.artifact.vertex.shader_resources()?;
let vertex = util::gl_compile_shader(context, glow::VERTEX_SHADER, vertex)?;
let fragment = util::gl_compile_shader(context, glow::FRAGMENT_SHADER, fragment)?;
let program = librashader_cache::cache_shader_object(
"opengl4",
&[glsl.vertex.as_str(), glsl.fragment.as_str()],
|&[vertex, fragment]| unsafe {
let vertex = util::gl_compile_shader(gl::VERTEX_SHADER, vertex)?;
let fragment = util::gl_compile_shader(gl::FRAGMENT_SHADER, fragment)?;
let program = context
.create_program()
.map_err(|_| FilterChainError::GlProgramError)?;
let program = gl::CreateProgram();
gl::AttachShader(program, vertex);
gl::AttachShader(program, fragment);
context.attach_shader(program, vertex);
context.attach_shader(program, fragment);
for res in vertex_resources.resources_for_type(ResourceType::StageInput)? {
let Some(loc) = glsl
.context
let Some(loc) = resources
.artifact
.vertex
.decoration(res.id, Decoration::Location)?
@ -70,60 +74,48 @@ impl CompileProgram for Gl4CompileProgram {
continue;
};
let mut name = res.name.to_string();
name.push('\0');
gl::BindAttribLocation(program, loc, name.as_str().as_ptr().cast())
context.bind_attrib_location(program, loc, &res.name);
}
gl::LinkProgram(program);
gl::DeleteShader(vertex);
gl::DeleteShader(fragment);
context.program_binary_retrievable_hint(program, true);
context.link_program(program);
context.delete_shader(vertex);
context.delete_shader(fragment);
let mut status = 0;
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
if status != 1 {
if !context.get_program_link_status(program) {
return Err(FilterChainError::GLLinkError);
}
Ok(program)
}
}
let mut length = 0;
gl::GetProgramiv(program, gl::PROGRAM_BINARY_LENGTH, &mut length);
let mut binary = vec![0; length as usize];
let mut format = 0;
gl::GetProgramBinary(
program,
length,
std::ptr::null_mut(),
&mut format,
binary.as_mut_ptr().cast(),
);
gl::DeleteProgram(program);
Ok(GlProgramBinary {
program: binary,
format,
})
let program = librashader_cache::cache_shader_object(
"opengl4",
&[glsl.vertex.as_str(), glsl.fragment.as_str()],
|&[vertex, fragment]| unsafe {
let program = compile_shader(context, &glsl.context, vertex, fragment)?;
let program_binary = context.get_program_binary(program);
context.delete_program(program);
Ok(GlProgramBinary(program_binary))
},
|GlProgramBinary {
program: blob,
format,
}| {
let program = unsafe {
let program = gl::CreateProgram();
gl::ProgramBinary(program, format, blob.as_ptr().cast(), blob.len() as GLsizei);
program
};
|binary| unsafe {
let program = context
.create_program()
.map_err(|_| FilterChainError::GlProgramError)?;
unsafe {
let mut status = 0;
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
if status != 1 {
return Err(FilterChainError::GLLinkError);
}
if gl::GetError() == gl::INVALID_ENUM {
return Err(FilterChainError::GLLinkError);
}
if let Some(binary) = &binary.0 {
context.program_binary(program, binary);
}
if !context.get_program_link_status(program) {
context.delete_program(program);
return compile_shader(
context,
&glsl.context,
glsl.vertex.as_str(),
glsl.fragment.as_str(),
);
}
return Ok(program);
},
!cache,
@ -131,18 +123,15 @@ impl CompileProgram for Gl4CompileProgram {
let ubo_location = unsafe {
for (name, binding) in &glsl.context.sampler_bindings {
let location = gl::GetUniformLocation(program, name.as_str().as_ptr().cast());
if location >= 0 {
gl::ProgramUniform1i(program, location, *binding as GLint);
let location = context.get_uniform_location(program, name.as_str());
if let Some(location) = location {
context.program_uniform_1_i32(program, Some(&location), *binding as i32);
}
}
UniformLocation {
vertex: gl::GetUniformBlockIndex(program, b"LIBRA_UBO_VERTEX\0".as_ptr().cast()),
fragment: gl::GetUniformBlockIndex(
program,
b"LIBRA_UBO_FRAGMENT\0".as_ptr().cast(),
),
vertex: context.get_uniform_block_index(program, "LIBRA_UBO_VERTEX"),
fragment: context.get_uniform_block_index(program, "LIBRA_UBO_FRAGMENT"),
}
};

View file

@ -1,104 +1,95 @@
use crate::error;
use crate::error::FilterChainError;
use crate::gl::DrawQuad;
use crate::gl::{FINAL_VBO_DATA, OFFSCREEN_VBO_DATA};
use bytemuck::offset_of;
use gl::types::{GLint, GLsizeiptr, GLuint};
use glow::HasContext;
use librashader_runtime::quad::{QuadType, VertexInput};
pub struct Gl46DrawQuad {
vbo: [GLuint; 2],
vao: GLuint,
vbo: [glow::Buffer; 2],
vao: glow::VertexArray,
}
impl DrawQuad for Gl46DrawQuad {
fn new() -> Self {
let mut vbo = [0, 0];
let mut vao = 0;
fn new(context: &glow::Context) -> error::Result<Self> {
let vbo;
let vao;
unsafe {
gl::CreateBuffers(2, vbo.as_mut_ptr());
gl::NamedBufferData(
vbo = [
context
.create_named_buffer()
.map_err(FilterChainError::GlError)?,
context
.create_named_buffer()
.map_err(FilterChainError::GlError)?,
];
context.named_buffer_data_u8_slice(
vbo[0],
std::mem::size_of_val(OFFSCREEN_VBO_DATA) as GLsizeiptr,
OFFSCREEN_VBO_DATA.as_ptr().cast(),
gl::STATIC_DRAW,
bytemuck::cast_slice(OFFSCREEN_VBO_DATA),
glow::STATIC_DRAW,
);
gl::NamedBufferData(
context.named_buffer_data_u8_slice(
vbo[1],
std::mem::size_of_val(FINAL_VBO_DATA) as GLsizeiptr,
FINAL_VBO_DATA.as_ptr().cast(),
gl::STATIC_DRAW,
bytemuck::cast_slice(FINAL_VBO_DATA),
glow::STATIC_DRAW,
);
gl::CreateVertexArrays(1, &mut vao);
vao = context
.create_named_vertex_array()
.map_err(FilterChainError::GlError)?;
context.enable_vertex_array_attrib(vao, 0);
context.enable_vertex_array_attrib(vao, 1);
gl::EnableVertexArrayAttrib(vao, 0);
gl::EnableVertexArrayAttrib(vao, 1);
gl::VertexArrayAttribFormat(
context.vertex_array_attrib_format_f32(
vao,
0,
4,
gl::FLOAT,
gl::FALSE,
offset_of!(VertexInput, position) as GLuint,
glow::FLOAT,
false,
offset_of!(VertexInput, position) as u32,
);
gl::VertexArrayAttribFormat(
context.vertex_array_attrib_format_f32(
vao,
1,
2,
gl::FLOAT,
gl::FALSE,
offset_of!(VertexInput, texcoord) as GLuint,
glow::FLOAT,
false,
offset_of!(VertexInput, texcoord) as u32,
);
gl::VertexArrayAttribBinding(vao, 0, 0);
gl::VertexArrayAttribBinding(vao, 1, 0);
context.vertex_array_attrib_binding_f32(vao, 0, 0);
context.vertex_array_attrib_binding_f32(vao, 1, 0);
}
Self { vbo, vao }
Ok(Self { vbo, vao })
}
fn bind_vertices(&self, quad_type: QuadType) {
fn bind_vertices(&self, context: &glow::Context, quad_type: QuadType) {
let buffer_index = match quad_type {
QuadType::Offscreen => 0,
QuadType::Final => 1,
};
unsafe {
gl::VertexArrayVertexBuffer(
context.vertex_array_vertex_buffer(
self.vao,
0,
self.vbo[buffer_index],
Some(self.vbo[buffer_index]),
0,
std::mem::size_of::<VertexInput>() as GLint,
std::mem::size_of::<VertexInput>() as i32,
);
gl::BindVertexArray(self.vao);
context.bind_vertex_array(Some(self.vao))
}
}
fn unbind_vertices(&self) {
fn unbind_vertices(&self, context: &glow::Context) {
unsafe {
gl::BindVertexArray(0);
}
}
}
impl Drop for Gl46DrawQuad {
fn drop(&mut self) {
unsafe {
if self.vbo[0] != 0 {
gl::DeleteBuffers(1, &self.vbo[0]);
}
if self.vbo[1] != 0 {
gl::DeleteBuffers(1, &self.vbo[1]);
}
if self.vao != 0 {
gl::DeleteVertexArrays(1, &self.vao)
}
context.bind_vertex_array(None);
}
}
}

View file

@ -1,24 +1,26 @@
use crate::error;
use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage;
use crate::gl::framebuffer::GLFramebuffer;
use crate::gl::FramebufferInterface;
use gl::types::{GLenum, GLint, GLsizei};
use glow::HasContext;
use librashader_common::{ImageFormat, Size};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
use librashader_runtime::scaling::MipmapSize;
use std::sync::Arc;
#[derive(Debug)]
pub struct Gl46Framebuffer;
impl FramebufferInterface for Gl46Framebuffer {
fn new(max_levels: u32) -> GLFramebuffer {
let mut framebuffer = 0;
unsafe {
gl::CreateFramebuffers(1, &mut framebuffer);
}
fn new(context: &Arc<glow::Context>, max_levels: u32) -> error::Result<GLFramebuffer> {
let framebuffer = unsafe {
context
.create_named_framebuffer()
.map_err(FilterChainError::GlError)?
};
GLFramebuffer {
image: 0,
Ok(GLFramebuffer {
image: None,
size: Size {
width: 1,
height: 1,
@ -28,92 +30,66 @@ impl FramebufferInterface for Gl46Framebuffer {
mip_levels: 0,
fbo: framebuffer,
is_raw: false,
}
ctx: Arc::clone(&context),
})
}
fn scale(
fb: &mut GLFramebuffer,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
original_size: &Size<u32>,
mipmap: bool,
) -> Result<Size<u32>> {
if fb.is_raw {
return Ok(fb.size);
}
let size = source_size.scale_viewport(scaling, *viewport_size, *original_size);
if fb.size != size || (mipmap && fb.max_levels == 1) || (!mipmap && fb.max_levels != 1) {
fb.size = size;
if mipmap {
fb.max_levels = u32::MAX;
} else {
fb.max_levels = 1
}
Self::init(
fb,
size,
if format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
format
},
)?;
}
Ok(size)
}
fn clear<const REBIND: bool>(fb: &GLFramebuffer) {
unsafe {
gl::ClearNamedFramebufferfv(
fb.fbo,
gl::COLOR,
fb.ctx.clear_named_framebuffer_f32_slice(
Some(fb.fbo),
glow::COLOR,
0,
[0.0f32, 0.0, 0.0, 0.0].as_ptr().cast(),
&[0.0f32, 0.0, 0.0, 0.0],
);
}
}
fn copy_from(fb: &mut GLFramebuffer, image: &GLImage) -> Result<()> {
// todo: confirm this behaviour for unbound image.
if image.handle == None {
return Ok(());
}
// todo: may want to use a shader and draw a quad to be faster.
if image.size != fb.size || image.format != fb.format {
Self::init(fb, image.size, image.format)?;
}
if image.handle == 0 {
return Ok(());
}
unsafe {
// gl::NamedFramebufferDrawBuffer(fb.handle, gl::COLOR_ATTACHMENT1);
gl::NamedFramebufferReadBuffer(fb.fbo, gl::COLOR_ATTACHMENT0);
gl::NamedFramebufferDrawBuffer(fb.fbo, gl::COLOR_ATTACHMENT1);
// glow::NamedFramebufferDrawBuffer(fb.handle, glow::COLOR_ATTACHMENT1);
fb.ctx
.named_framebuffer_read_buffer(Some(fb.fbo), glow::COLOR_ATTACHMENT0);
fb.ctx
.named_framebuffer_draw_buffer(Some(fb.fbo), glow::COLOR_ATTACHMENT1);
gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, image.handle, 0);
gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT1, fb.image, 0);
fb.ctx.named_framebuffer_texture(
Some(fb.fbo),
glow::COLOR_ATTACHMENT0,
image.handle,
0,
);
fb.ctx
.named_framebuffer_texture(Some(fb.fbo), glow::COLOR_ATTACHMENT1, fb.image, 0);
gl::BlitNamedFramebuffer(
fb.fbo,
fb.fbo,
fb.ctx.blit_named_framebuffer(
Some(fb.fbo),
Some(fb.fbo),
0,
0,
image.size.width as GLint,
image.size.height as GLint,
image.size.width as i32,
image.size.height as i32,
0,
0,
fb.size.width as GLint,
fb.size.height as GLint,
gl::COLOR_BUFFER_BIT,
gl::NEAREST,
fb.size.width as i32,
fb.size.height as i32,
glow::COLOR_BUFFER_BIT,
glow::NEAREST,
);
}
Ok(())
}
fn init(fb: &mut GLFramebuffer, mut size: Size<u32>, format: impl Into<GLenum>) -> Result<()> {
fn init(fb: &mut GLFramebuffer, mut size: Size<u32>, format: impl Into<u32>) -> Result<()> {
if fb.is_raw {
return Ok(());
}
@ -122,12 +98,16 @@ impl FramebufferInterface for Gl46Framebuffer {
unsafe {
// reset the framebuffer image
if fb.image != 0 {
gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, 0, 0);
gl::DeleteTextures(1, &fb.image);
if let Some(image) = fb.image {
fb.ctx
.named_framebuffer_texture(Some(fb.fbo), glow::COLOR_ATTACHMENT0, None, 0);
fb.ctx.delete_texture(image);
}
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut fb.image);
let image = fb
.ctx
.create_named_texture(glow::TEXTURE_2D)
.map_err(FilterChainError::GlError)?;
if size.width == 0 {
size.width = 1;
@ -144,23 +124,37 @@ impl FramebufferInterface for Gl46Framebuffer {
fb.mip_levels = 1;
}
gl::TextureStorage2D(
fb.image,
fb.mip_levels as GLsizei,
fb.ctx.texture_storage_2d(
image,
fb.mip_levels as i32,
fb.format,
size.width as GLsizei,
size.height as GLsizei,
size.width as i32,
size.height as i32,
);
gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, fb.image, 0);
fb.image = Some(image);
let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
if status != gl::FRAMEBUFFER_COMPLETE {
fb.ctx
.named_framebuffer_texture(Some(fb.fbo), glow::COLOR_ATTACHMENT0, fb.image, 0);
let status = fb
.ctx
.check_named_framebuffer_status(Some(fb.fbo), glow::FRAMEBUFFER);
if status != glow::FRAMEBUFFER_COMPLETE {
match status {
gl::FRAMEBUFFER_UNSUPPORTED => {
gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, 0, 0);
gl::DeleteTextures(1, &fb.image);
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut fb.image);
glow::FRAMEBUFFER_UNSUPPORTED => {
fb.ctx.named_framebuffer_texture(
Some(fb.fbo),
glow::COLOR_ATTACHMENT0,
None,
0,
);
fb.ctx.delete_texture(image);
let image = fb
.ctx
.create_named_texture(glow::TEXTURE_2D)
.map_err(FilterChainError::GlError)?;
fb.mip_levels = size.calculate_miplevels();
if fb.mip_levels > fb.max_levels {
@ -170,16 +164,22 @@ impl FramebufferInterface for Gl46Framebuffer {
fb.mip_levels = 1;
}
gl::TextureStorage2D(
fb.image,
fb.mip_levels as GLsizei,
fb.ctx.texture_storage_2d(
image,
fb.mip_levels as i32,
ImageFormat::R8G8B8A8Unorm.into(),
size.width as GLsizei,
size.height as GLsizei,
size.width as i32,
size.height as i32,
);
fb.image = Some(image);
fb.ctx.named_framebuffer_texture(
Some(fb.fbo),
glow::COLOR_ATTACHMENT0,
fb.image,
0,
);
gl::NamedFramebufferTexture(fb.fbo, gl::COLOR_ATTACHMENT0, fb.image, 0);
// fb.init =
// gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE;
}
_ => return Err(FilterChainError::FramebufferInit(status)),
}

View file

@ -1,8 +1,8 @@
use crate::error::Result;
use crate::error::{FilterChainError, Result};
use crate::framebuffer::GLImage;
use crate::gl::LoadLut;
use crate::texture::InputTexture;
use gl::types::{GLsizei, GLuint};
use glow::{HasContext, PixelUnpackData};
use librashader_common::map::FastHashMap;
use librashader_presets::TextureConfig;
use librashader_runtime::image::{Image, ImageError, UVDirection};
@ -11,17 +11,13 @@ use rayon::prelude::*;
pub struct Gl46LutLoad;
impl LoadLut for Gl46LutLoad {
fn load_luts(textures: &[TextureConfig]) -> Result<FastHashMap<usize, InputTexture>> {
fn load_luts(
context: &glow::Context,
textures: &[TextureConfig],
) -> Result<FastHashMap<usize, InputTexture>> {
let mut luts = FastHashMap::default();
let pixel_unpack = unsafe {
let mut binding = 0;
gl::GetIntegerv(gl::PIXEL_UNPACK_BUFFER_BINDING, &mut binding);
binding
};
unsafe {
gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, 0);
}
// don't need this for texture DSA api.
let images = textures
.par_iter()
@ -35,45 +31,48 @@ impl LoadLut for Gl46LutLoad {
1u32
};
let mut handle = 0;
unsafe {
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut handle);
let handle = unsafe {
let handle = context
.create_named_texture(glow::TEXTURE_2D)
.map_err(FilterChainError::GlError)?;
gl::TextureStorage2D(
context.texture_storage_2d(
handle,
levels as GLsizei,
gl::RGBA8,
image.size.width as GLsizei,
image.size.height as GLsizei,
levels as i32,
glow::RGBA8,
image.size.width as i32,
image.size.height as i32,
);
gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0);
gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4);
context.pixel_store_i32(glow::UNPACK_ROW_LENGTH, 0);
context.pixel_store_i32(glow::UNPACK_ALIGNMENT, 4);
gl::TextureSubImage2D(
context.texture_sub_image_2d(
handle,
0,
0,
0,
image.size.width as GLsizei,
image.size.height as GLsizei,
gl::RGBA,
gl::UNSIGNED_BYTE,
image.bytes.as_ptr().cast(),
image.size.width as i32,
image.size.height as i32,
glow::RGBA,
glow::UNSIGNED_BYTE,
PixelUnpackData::Slice(&image.bytes),
);
let mipmap = levels > 1;
if mipmap {
gl::GenerateTextureMipmap(handle);
context.generate_texture_mipmap(handle);
}
}
handle
};
luts.insert(
index,
InputTexture {
image: GLImage {
handle,
format: gl::RGBA8,
handle: Some(handle),
format: glow::RGBA8,
size: image.size,
},
filter: texture.filter_mode,
@ -83,9 +82,9 @@ impl LoadLut for Gl46LutLoad {
);
}
unsafe {
gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, pixel_unpack as GLuint);
};
// unsafe {
// context.bind_buffer(glow::PIXEL_UNPACK_BUFFER, pixel_unpack);
// };
Ok(luts)
}
}

View file

@ -1,23 +1,31 @@
use crate::gl::BindTexture;
use crate::samplers::SamplerSet;
use crate::texture::InputTexture;
use glow::HasContext;
use librashader_reflect::reflect::semantics::TextureBinding;
pub struct Gl46BindTexture;
impl BindTexture for Gl46BindTexture {
fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &InputTexture) {
fn bind_texture(
context: &glow::Context,
samplers: &SamplerSet,
binding: &TextureBinding,
texture: &InputTexture,
) {
unsafe {
// eprintln!("setting {} to texunit {}", texture.image.handle, binding.binding);
gl::BindTextureUnit(binding.binding, texture.image.handle);
gl::BindSampler(
context.bind_texture_unit(binding.binding, texture.image.handle);
context.bind_sampler(
binding.binding,
samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter),
);
Some(samplers.get(texture.wrap_mode, texture.filter, texture.mip_filter)),
)
}
}
fn gen_mipmaps(texture: &InputTexture) {
unsafe { gl::GenerateTextureMipmap(texture.image.handle) }
fn gen_mipmaps(context: &glow::Context, texture: &InputTexture) {
if let Some(texture) = texture.image.handle {
unsafe { context.generate_texture_mipmap(texture) }
}
}
}

View file

@ -1,50 +1,82 @@
use crate::binding::UniformLocation;
use crate::error;
use crate::error::FilterChainError;
use crate::gl::UboRing;
use gl::types::{GLsizei, GLsizeiptr, GLuint};
use glow::HasContext;
use librashader_reflect::reflect::semantics::BufferReflection;
use librashader_runtime::ringbuffer::InlineRingBuffer;
use librashader_runtime::ringbuffer::RingBuffer;
use librashader_runtime::uniforms::UniformStorageAccess;
use std::mem::MaybeUninit;
pub struct Gl46UboRing<const SIZE: usize> {
ring: InlineRingBuffer<GLuint, SIZE>,
ring: InlineRingBuffer<glow::Buffer, SIZE>,
}
impl<const SIZE: usize> Gl46UboRing<SIZE> {
const _ASSERT_TRANSMUTABLE: () = assert!(
std::mem::size_of::<[glow::Buffer; SIZE]>()
== std::mem::size_of::<[MaybeUninit<glow::Buffer>; SIZE]>()
);
}
impl<const SIZE: usize> UboRing<SIZE> for Gl46UboRing<SIZE> {
fn new(buffer_size: u32) -> Self {
let mut ring: InlineRingBuffer<GLuint, SIZE> = InlineRingBuffer::new();
unsafe {
gl::CreateBuffers(SIZE as GLsizei, ring.items_mut().as_mut_ptr());
for buffer in ring.items() {
gl::NamedBufferData(
*buffer,
buffer_size as GLsizeiptr,
std::ptr::null(),
gl::STREAM_DRAW,
);
}
};
fn new(context: &glow::Context, buffer_size: u32) -> error::Result<Self> {
// TODO: array::try_from_fn whenever that gets stabilized
// this is basically blocking on try_trait_v2
let mut items: [MaybeUninit<glow::Buffer>; SIZE] = [MaybeUninit::zeroed(); SIZE];
for items in items.iter_mut() {
unsafe {
let buffer = context
.create_named_buffer()
.map(|buffer| {
context.named_buffer_data_size(
buffer,
buffer_size as i32,
glow::STREAM_DRAW,
);
buffer
})
.map_err(FilterChainError::GlError)?;
Gl46UboRing { ring }
*items = MaybeUninit::new(buffer)
}
}
// SAFETY: everything was initialized above.
let items: [glow::Buffer; SIZE] = unsafe { std::mem::transmute_copy(&items) };
let ring: InlineRingBuffer<glow::Buffer, SIZE> = InlineRingBuffer::from_array(items);
Ok(Gl46UboRing { ring })
}
fn bind_for_frame(
&mut self,
context: &glow::Context,
ubo: &BufferReflection<u32>,
ubo_location: &UniformLocation<GLuint>,
ubo_location: &UniformLocation<Option<u32>>,
storage: &impl UniformStorageAccess,
) {
let size = ubo.size;
let buffer = self.ring.current();
let buffer = *self.ring.current();
unsafe {
gl::NamedBufferSubData(*buffer, 0, size as GLsizeiptr, storage.ubo_pointer().cast());
context.named_buffer_sub_data_u8_slice(
buffer,
0,
&storage.ubo_slice()[0..ubo.size as usize],
);
if ubo_location.vertex != gl::INVALID_INDEX {
gl::BindBufferBase(gl::UNIFORM_BUFFER, ubo_location.vertex, *buffer);
if let Some(vertex) = ubo_location
.vertex
.filter(|vertex| *vertex != glow::INVALID_INDEX)
{
context.bind_buffer_base(glow::UNIFORM_BUFFER, vertex, Some(buffer));
}
if ubo_location.fragment != gl::INVALID_INDEX {
gl::BindBufferBase(gl::UNIFORM_BUFFER, ubo_location.fragment, *buffer);
if let Some(fragment) = ubo_location
.fragment
.filter(|fragment| *fragment != glow::INVALID_INDEX)
{
context.bind_buffer_base(glow::UNIFORM_BUFFER, fragment, Some(buffer));
}
}
self.ring.next()

View file

@ -8,7 +8,6 @@ use crate::framebuffer::GLImage;
use crate::samplers::SamplerSet;
use crate::texture::InputTexture;
pub use framebuffer::GLFramebuffer;
use gl::types::{GLenum, GLuint};
use librashader_common::map::FastHashMap;
use librashader_common::{ImageFormat, Size};
use librashader_presets::{Scale2D, TextureConfig};
@ -16,7 +15,9 @@ use librashader_reflect::back::glsl::CrossGlslContext;
use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::reflect::semantics::{BufferReflection, TextureBinding};
use librashader_runtime::quad::{QuadType, VertexInput};
use librashader_runtime::scaling::ViewportSize;
use librashader_runtime::uniforms::UniformStorageAccess;
use std::sync::Arc;
static OFFSCREEN_VBO_DATA: &[VertexInput; 4] = &[
VertexInput {
@ -57,34 +58,43 @@ static FINAL_VBO_DATA: &[VertexInput; 4] = &[
];
pub(crate) trait LoadLut {
fn load_luts(textures: &[TextureConfig]) -> Result<FastHashMap<usize, InputTexture>>;
fn load_luts(
context: &glow::Context,
textures: &[TextureConfig],
) -> Result<FastHashMap<usize, InputTexture>>;
}
pub(crate) trait CompileProgram {
fn compile_program(
context: &glow::Context,
shader: ShaderCompilerOutput<String, CrossGlslContext>,
cache: bool,
) -> Result<(GLuint, UniformLocation<GLuint>)>;
) -> Result<(glow::Program, UniformLocation<Option<u32>>)>;
}
pub(crate) trait DrawQuad {
fn new() -> Self;
fn bind_vertices(&self, quad_type: QuadType);
fn unbind_vertices(&self);
fn new(context: &glow::Context) -> Result<Self>
where
Self: Sized;
fn bind_vertices(&self, context: &glow::Context, quad_type: QuadType);
fn unbind_vertices(&self, context: &glow::Context);
}
pub(crate) trait UboRing<const SIZE: usize> {
fn new(buffer_size: u32) -> Self;
fn new(context: &glow::Context, buffer_size: u32) -> Result<Self>
where
Self: Sized;
fn bind_for_frame(
&mut self,
context: &glow::Context,
ubo: &BufferReflection<u32>,
ubo_location: &UniformLocation<GLuint>,
ubo_location: &UniformLocation<Option<u32>>,
storage: &impl UniformStorageAccess,
);
}
pub(crate) trait FramebufferInterface {
fn new(max_levels: u32) -> GLFramebuffer;
fn new(context: &Arc<glow::Context>, max_levels: u32) -> Result<GLFramebuffer>;
fn scale(
fb: &mut GLFramebuffer,
scaling: Scale2D,
@ -93,15 +103,48 @@ pub(crate) trait FramebufferInterface {
source_size: &Size<u32>,
original_size: &Size<u32>,
mipmap: bool,
) -> Result<Size<u32>>;
) -> Result<Size<u32>> {
if fb.is_raw {
return Ok(fb.size);
}
let size = source_size.scale_viewport(scaling, *viewport_size, *original_size);
if fb.size != size || (mipmap && fb.max_levels == 1) || (!mipmap && fb.max_levels != 1) {
fb.size = size;
if mipmap {
fb.max_levels = u32::MAX;
} else {
fb.max_levels = 1
}
Self::init(
fb,
size,
if format == ImageFormat::Unknown {
ImageFormat::R8G8B8A8Unorm
} else {
format
},
)?;
}
Ok(size)
}
fn clear<const REBIND: bool>(fb: &GLFramebuffer);
fn copy_from(fb: &mut GLFramebuffer, image: &GLImage) -> Result<()>;
fn init(fb: &mut GLFramebuffer, size: Size<u32>, format: impl Into<GLenum>) -> Result<()>;
fn init(fb: &mut GLFramebuffer, size: Size<u32>, format: impl Into<u32>) -> Result<()>;
}
pub(crate) trait BindTexture {
fn bind_texture(samplers: &SamplerSet, binding: &TextureBinding, texture: &InputTexture);
fn gen_mipmaps(texture: &InputTexture);
fn bind_texture(
context: &glow::Context,
samplers: &SamplerSet,
binding: &TextureBinding,
texture: &InputTexture,
);
fn gen_mipmaps(context: &glow::Context, texture: &InputTexture);
}
pub(crate) trait GLInterface {

View file

@ -1,15 +1,17 @@
use gl::types::{GLenum, GLint, GLuint};
use crate::error;
use crate::error::FilterChainError;
use glow::HasContext;
use librashader_common::map::FastHashMap;
use librashader_common::{FilterMode, WrapMode};
pub struct SamplerSet {
// todo: may need to deal with differences in mip filter.
samplers: FastHashMap<(WrapMode, FilterMode, FilterMode), GLuint>,
samplers: FastHashMap<(WrapMode, FilterMode, FilterMode), glow::Sampler>,
}
impl SamplerSet {
#[inline(always)]
pub fn get(&self, wrap: WrapMode, filter: FilterMode, mipmap: FilterMode) -> GLuint {
pub fn get(&self, wrap: WrapMode, filter: FilterMode, mipmap: FilterMode) -> glow::Sampler {
// SAFETY: the sampler set is complete for the matrix
// wrap x filter x mipmap
unsafe {
@ -20,21 +22,26 @@ impl SamplerSet {
}
}
fn make_sampler(sampler: GLuint, wrap: WrapMode, filter: FilterMode, mip: FilterMode) {
fn make_sampler(
context: &glow::Context,
sampler: glow::Sampler,
wrap: WrapMode,
filter: FilterMode,
mip: FilterMode,
) {
unsafe {
gl::SamplerParameteri(sampler, gl::TEXTURE_WRAP_S, GLenum::from(wrap) as GLint);
gl::SamplerParameteri(sampler, gl::TEXTURE_WRAP_T, GLenum::from(wrap) as GLint);
gl::SamplerParameteri(
context.sampler_parameter_i32(sampler, glow::TEXTURE_WRAP_S, wrap.into());
context.sampler_parameter_i32(sampler, glow::TEXTURE_WRAP_T, wrap.into());
context.sampler_parameter_i32(sampler, glow::TEXTURE_MAG_FILTER, filter.into());
context.sampler_parameter_i32(
sampler,
gl::TEXTURE_MAG_FILTER,
GLenum::from(filter) as GLint,
glow::TEXTURE_MIN_FILTER,
filter.gl_mip(mip) as i32,
);
gl::SamplerParameteri(sampler, gl::TEXTURE_MIN_FILTER, filter.gl_mip(mip) as GLint);
}
}
pub fn new() -> SamplerSet {
pub fn new(context: &glow::Context) -> error::Result<SamplerSet> {
let mut samplers = FastHashMap::default();
let wrap_modes = &[
WrapMode::ClampToBorder,
@ -45,10 +52,18 @@ impl SamplerSet {
for wrap_mode in wrap_modes {
for filter_mode in &[FilterMode::Linear, FilterMode::Nearest] {
for mip_filter in &[FilterMode::Linear, FilterMode::Nearest] {
let mut sampler = 0;
unsafe {
gl::GenSamplers(1, &mut sampler);
SamplerSet::make_sampler(sampler, *wrap_mode, *filter_mode, *mip_filter);
let sampler = context
.create_sampler()
.map_err(|_| FilterChainError::GlSamplerError)?;
SamplerSet::make_sampler(
context,
sampler,
*wrap_mode,
*filter_mode,
*mip_filter,
);
samplers.insert((*wrap_mode, *filter_mode, *mip_filter), sampler);
}
@ -58,6 +73,6 @@ impl SamplerSet {
// assert all samplers were created.
assert_eq!(samplers.len(), wrap_modes.len() * 2 * 2);
SamplerSet { samplers }
Ok(SamplerSet { samplers })
}
}

View file

@ -13,7 +13,7 @@ pub(crate) struct InputTexture {
/// An OpenGL texture bound as a shader resource.
impl InputTexture {
pub fn is_bound(&self) -> bool {
self.image.handle != 0
self.image.handle.is_some()
}
/// Returns a reference to itself if the texture is bound.

View file

@ -1,34 +1,36 @@
use gl::types::{GLenum, GLint, GLuint};
use glow::HasContext;
use crate::error;
use crate::error::FilterChainError;
use librashader_reflect::back::glsl::GlslVersion;
pub unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> error::Result<GLuint> {
let (shader, compile_status) = unsafe {
let lens = [source.len() as GLint];
let shader = gl::CreateShader(stage);
gl::ShaderSource(shader, 1, &source.as_ptr().cast(), lens.as_ptr());
gl::CompileShader(shader);
let mut compile_status = 0;
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut compile_status);
(shader, compile_status)
};
pub fn gl_compile_shader(
context: &glow::Context,
stage: u32,
source: &str,
) -> error::Result<glow::Shader> {
unsafe {
let shader = context
.create_shader(stage)
.map_err(|_| FilterChainError::GlCompileError)?;
if compile_status == 0 {
Err(FilterChainError::GlCompileError)
} else {
Ok(shader)
context.shader_source(shader, &source);
context.compile_shader(shader);
let compile_status = context.get_shader_compile_status(shader);
if !compile_status {
Err(FilterChainError::GlCompileError)
} else {
Ok(shader)
}
}
}
pub fn gl_get_version() -> GlslVersion {
let mut maj_ver = 0;
let mut min_ver = 0;
unsafe {
gl::GetIntegerv(gl::MAJOR_VERSION, &mut maj_ver);
gl::GetIntegerv(gl::MINOR_VERSION, &mut min_ver);
}
pub fn gl_get_version(context: &glow::Context) -> GlslVersion {
let version = context.version();
let maj_ver = version.major;
let min_ver = version.minor;
match maj_ver {
3 => match min_ver {
@ -52,9 +54,9 @@ pub fn gl_get_version() -> GlslVersion {
}
}
pub fn gl_u16_to_version(version: u16) -> GlslVersion {
pub fn gl_u16_to_version(context: &glow::Context, version: u16) -> GlslVersion {
match version {
0 => gl_get_version(),
0 => gl_get_version(context),
300 => GlslVersion::Glsl130,
310 => GlslVersion::Glsl140,
320 => GlslVersion::Glsl150,

View file

@ -1,111 +1,25 @@
use std::convert::TryInto;
use std::ffi::{c_void, CStr};
use std::sync::mpsc::Receiver;
use std::sync::Arc;
use glfw::{Context, Glfw, Window, WindowEvent};
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
use glow::HasContext;
use librashader_common::{Size, Viewport};
use librashader_runtime_gl::{FilterChainGL, GLFramebuffer, GLImage};
const WIDTH: u32 = 800;
const HEIGHT: u32 = 600;
const TITLE: &str = "librashader OpenGL";
const TITLE: &str = "librashader OpenGL 3.3";
pub fn compile_program(vertex: &str, fragment: &str) -> GLuint {
let vertex_shader = unsafe { gl::CreateShader(gl::VERTEX_SHADER) };
unsafe {
gl::ShaderSource(
vertex_shader,
1,
&vertex.as_bytes().as_ptr().cast(),
&vertex.len().try_into().unwrap(),
);
gl::CompileShader(vertex_shader);
let mut success = 0;
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
if success == 0 {
let mut log_len = 0_i32;
// gl::GetShaderiv(vertex_shader, gl::INFO_LOG_LENGTH, &mut log_len);
// let mut v: Vec<u8> = Vec::with_capacity(log_len as usize);
// gl::GetShaderInfoLog(vertex_shader, log_len, &mut log_len, v.as_mut_ptr().cast());
let mut v: Vec<u8> = Vec::with_capacity(1024);
gl::GetShaderInfoLog(vertex_shader, 1024, &mut log_len, v.as_mut_ptr().cast());
v.set_len(log_len.try_into().unwrap());
panic!(
"Vertex Shader Compile Error: {}",
String::from_utf8_lossy(&v)
);
}
}
let fragment_shader = unsafe { gl::CreateShader(gl::FRAGMENT_SHADER) };
unsafe {
gl::ShaderSource(
fragment_shader,
1,
&fragment.as_bytes().as_ptr().cast(),
&fragment.len().try_into().unwrap(),
);
gl::CompileShader(fragment_shader);
let mut success = 0;
gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
if success == 0 {
let mut v: Vec<u8> = Vec::with_capacity(1024);
let mut log_len = 0_i32;
gl::GetShaderInfoLog(fragment_shader, 1024, &mut log_len, v.as_mut_ptr().cast());
v.set_len(log_len.try_into().unwrap());
panic!(
"Fragment Shader Compile Error: {}",
String::from_utf8_lossy(&v)
);
}
}
let shader_program = unsafe { gl::CreateProgram() };
unsafe {
gl::AttachShader(shader_program, vertex_shader);
gl::AttachShader(shader_program, fragment_shader);
gl::LinkProgram(shader_program);
let mut success = 0;
gl::GetProgramiv(shader_program, gl::LINK_STATUS, &mut success);
if success == 0 {
let mut v: Vec<u8> = Vec::with_capacity(1024);
let mut log_len = 0_i32;
gl::GetProgramInfoLog(shader_program, 1024, &mut log_len, v.as_mut_ptr().cast());
v.set_len(log_len.try_into().unwrap());
panic!("Program Link Error: {}", String::from_utf8_lossy(&v));
}
gl::DetachShader(shader_program, vertex_shader);
gl::DetachShader(shader_program, fragment_shader);
gl::DeleteShader(vertex_shader);
gl::DeleteShader(fragment_shader);
}
shader_program
}
extern "system" fn debug_callback(
_source: GLenum,
_err_type: GLenum,
_id: GLuint,
_severity: GLenum,
_length: GLsizei,
message: *const GLchar,
_user: *mut c_void,
pub fn setup() -> (
Glfw,
Window,
Receiver<(f64, WindowEvent)>,
glow::Program,
glow::VertexArray,
Arc<glow::Context>,
) {
unsafe {
let message = CStr::from_ptr(message);
println!("[gl] {message:?}");
}
}
pub fn setup() -> (Glfw, Window, Receiver<(f64, WindowEvent)>, GLuint, GLuint) {
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(
@ -122,26 +36,20 @@ pub fn setup() -> (Glfw, Window, Receiver<(f64, WindowEvent)>, GLuint, GLuint) {
window.make_current();
window.set_key_polling(true);
gl::load_with(|ptr| window.get_proc_address(ptr) as *const _);
let mut gl = unsafe { glow::Context::from_loader_function(|ptr| window.get_proc_address(ptr)) };
unsafe {
gl::Enable(gl::DEBUG_OUTPUT);
gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
gl.enable(glow::DEBUG_OUTPUT);
gl.enable(glow::DEBUG_OUTPUT_SYNCHRONOUS);
gl::DebugMessageCallback(Some(debug_callback), std::ptr::null_mut());
gl::DebugMessageControl(
gl::DONT_CARE,
gl::DONT_CARE,
gl::DONT_CARE,
0,
std::ptr::null(),
gl::TRUE,
);
gl.debug_message_callback(super::debug_callback);
gl.debug_message_control(glow::DONT_CARE, glow::DONT_CARE, glow::DONT_CARE, &[], true);
}
unsafe {
gl::Viewport(0, 0, screen_width, screen_height);
clear_color(Color(0.4, 0.4, 0.4, 1.0));
gl.viewport(0, 0, screen_width, screen_height);
gl.clear_color(0.4, 0.4, 0.4, 1.0);
}
// -------------------------------------------
@ -172,11 +80,11 @@ void main()
{
Color = vec4(IN.Color, 1.0f);
}";
let shader_program = compile_program(VERT_SHADER, FRAG_SHADER);
let shader_program = super::compile_program(&gl, VERT_SHADER, FRAG_SHADER);
// unsafe {
// gl::ObjectLabel(
// gl::SHADER,
// glow::ObjectLabel(
// glow::SHADER,
// shader_program,
// -1,
// b"color_shader\0".as_ptr().cast(),
@ -189,150 +97,161 @@ void main()
-0.5, -0.5, 0.0, 0.0, 1.0, 0.0, // bottom left
0.0, 0.5, 0.0, 0.0, 0.0, 1.0, // top
];
let mut vbo: gl::types::GLuint = 0;
let vbo;
unsafe {
gl::GenBuffers(1, &mut vbo);
// gl::ObjectLabel(gl::BUFFER, vbo, -1, b"triangle_vbo\0".as_ptr().cast());
vbo = gl.create_buffer().unwrap();
// glow::ObjectLabel(glow::BUFFER, vbo, -1, b"triangle_vbo\0".as_ptr().cast());
}
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER, // target
(vertices.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
vertices.as_ptr() as *const gl::types::GLvoid, // pointer to data
gl::STATIC_DRAW, // usage
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER, // target
bytemuck::cast_slice(vertices),
glow::STATIC_DRAW, // usage
);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl.bind_buffer(glow::ARRAY_BUFFER, None);
}
// set up vertex array object
let mut vao: gl::types::GLuint = 0;
let vao;
unsafe {
gl::GenVertexArrays(1, &mut vao);
// gl::ObjectLabel(gl::VERTEX_ARRAY, vao, -1, b"triangle_vao\0".as_ptr().cast());
vao = gl.create_vertex_array().unwrap();
// glow::ObjectLabel(glow::VERTEX_ARRAY, vao, -1, b"triangle_vao\0".as_ptr().cast());
}
unsafe {
gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl.bind_vertex_array(Some(vao));
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
gl::EnableVertexAttribArray(0); // this is "layout (location = 0)" in vertex shader
gl::VertexAttribPointer(
0, // index of the generic vertex attribute ("layout (location = 0)")
3, // the number of components per generic vertex attribute
gl::FLOAT, // data type
gl::FALSE, // normalized (int-to-float conversion)
(6 * std::mem::size_of::<f32>()) as gl::types::GLint, // stride (byte offset between consecutive attributes)
std::ptr::null(), // offset of the first component
gl.enable_vertex_attrib_array(0); // this is "layout (location = 0)" in vertex shader
gl.vertex_attrib_pointer_f32(
0, // index of the generic vertex attribute ("layout (location = 0)")
3, // the number of components per generic vertex attribute
glow::FLOAT, // data type
false, // normalized (int-to-float conversion)
(6 * std::mem::size_of::<f32>()) as i32, // stride (byte offset between consecutive attributes)
0, // offset of the first component
);
gl::EnableVertexAttribArray(1); // this is "layout (location = 0)" in vertex shader
gl::VertexAttribPointer(
1, // index of the generic vertex attribute ("layout (location = 0)")
3, // the number of components per generic vertex attribute
gl::FLOAT, // data type
gl::FALSE, // normalized (int-to-float conversion)
(6 * std::mem::size_of::<f32>()) as gl::types::GLint, // stride (byte offset between consecutive attributes)
(3 * std::mem::size_of::<f32>()) as *const gl::types::GLvoid, // offset of the first component
gl.enable_vertex_attrib_array(1);
gl.vertex_attrib_pointer_f32(
1, // index of the generic vertex attribute ("layout (location = 0)")
3, // the number of components per generic vertex attribute
glow::FLOAT, // data type
false, // normalized (int-to-float conversion)
(6 * std::mem::size_of::<f32>()) as i32, // stride (byte offset between consecutive attributes)
(3 * std::mem::size_of::<f32>()) as i32, // offset of the first component
);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
gl.bind_buffer(glow::ARRAY_BUFFER, None);
gl.bind_vertex_array(None);
}
// set up shared state for window
unsafe {
gl::Viewport(0, 0, 900, 700);
gl::ClearColor(0.3, 0.3, 0.5, 1.0);
gl.viewport(0, 0, 900, 700);
gl.clear_color(0.3, 0.3, 0.5, 1.0);
}
// -------------------------------------------
println!("OpenGL version: {}", gl_get_string(gl::VERSION));
println!(
"GLSL version: {}",
gl_get_string(gl::SHADING_LANGUAGE_VERSION)
);
unsafe {
// -------------------------------------------
println!("OpenGL version: {}", gl.get_parameter_string(glow::VERSION));
println!(
"GLSL version: {}",
gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION)
);
}
(glfw, window, events, shader_program, vao)
(glfw, window, events, shader_program, vao, Arc::new(gl))
}
pub fn do_loop(
gl: &Arc<glow::Context>,
mut glfw: Glfw,
mut window: Window,
events: Receiver<(f64, WindowEvent)>,
triangle_program: GLuint,
triangle_vao: GLuint,
triangle_program: glow::Program,
triangle_vao: glow::VertexArray,
filter: &mut FilterChainGL,
) {
let mut framecount = 0;
let mut rendered_framebuffer = 0;
let mut rendered_texture = 0;
let mut quad_vbuf = 0;
let rendered_framebuffer;
let rendered_texture;
let quad_vbuf;
let mut output_texture = 0;
let mut output_framebuffer_handle = 0;
let mut output_quad_vbuf = 0;
let output_texture;
let output_framebuffer_handle;
let output_quad_vbuf;
unsafe {
// do frmaebuffer
gl::GenFramebuffers(1, &mut rendered_framebuffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, rendered_framebuffer);
rendered_framebuffer = gl.create_framebuffer().unwrap();
// gl::ObjectLabel(
// gl::FRAMEBUFFER,
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(rendered_framebuffer));
// glow::ObjectLabel(
// glow::FRAMEBUFFER,
// rendered_framebuffer,
// -1,
// b"rendered_framebuffer\0".as_ptr().cast(),
// );
// make tetxure
gl::GenTextures(1, &mut rendered_texture);
gl::BindTexture(gl::TEXTURE_2D, rendered_texture);
rendered_texture = gl.create_texture().unwrap();
gl.bind_texture(glow::TEXTURE_2D, Some(rendered_texture));
// gl::ObjectLabel(
// gl::TEXTURE,
// glow::ObjectLabel(
// glow::TEXTURE,
// rendered_texture,
// -1,
// b"rendered_texture\0".as_ptr().cast(),
// );
// empty image
gl::TexStorage2D(
gl::TEXTURE_2D,
gl.tex_storage_2d(
glow::TEXTURE_2D,
1,
gl::RGBA8,
WIDTH as GLsizei,
HEIGHT as GLsizei,
glow::RGBA8,
WIDTH as i32,
HEIGHT as i32,
);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_S,
gl::CLAMP_TO_EDGE as GLint,
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MAG_FILTER,
glow::NEAREST as i32,
);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_T,
gl::CLAMP_TO_EDGE as GLint,
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MIN_FILTER,
glow::NEAREST as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_WRAP_S,
glow::CLAMP_TO_EDGE as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_WRAP_T,
glow::CLAMP_TO_EDGE as i32,
);
// set color attachment
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
rendered_texture,
gl.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
Some(rendered_texture),
0,
);
let buffers = [gl::COLOR_ATTACHMENT0];
gl::DrawBuffers(1, buffers.as_ptr());
gl.draw_buffer(glow::COLOR_ATTACHMENT0);
if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
panic!("failed to create fbo")
}
@ -341,74 +260,80 @@ pub fn do_loop(
1.0, 1.0, 0.0,
];
gl::GenBuffers(1, &mut quad_vbuf);
gl::BindBuffer(gl::ARRAY_BUFFER, quad_vbuf);
gl::BufferData(
gl::ARRAY_BUFFER, // target
(fullscreen_fbo.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
fullscreen_fbo.as_ptr() as *const gl::types::GLvoid, // pointer to data
gl::STATIC_DRAW, // usage
quad_vbuf = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(quad_vbuf));
gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
bytemuck::cast_slice(&fullscreen_fbo),
glow::STATIC_DRAW,
);
}
unsafe {
// do frmaebuffer
gl::GenFramebuffers(1, &mut output_framebuffer_handle);
gl::BindFramebuffer(gl::FRAMEBUFFER, output_framebuffer_handle);
output_framebuffer_handle = gl.create_framebuffer().unwrap();
// gl::ObjectLabel(
// gl::FRAMEBUFFER,
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(output_framebuffer_handle));
// glow::ObjectLabel(
// glow::FRAMEBUFFER,
// output_framebuffer_handle,
// -1,
// b"output_framebuffer\0".as_ptr().cast(),
// );
// make tetxure
gl::GenTextures(1, &mut output_texture);
gl::BindTexture(gl::TEXTURE_2D, output_texture);
output_texture = gl.create_texture().unwrap();
gl.bind_texture(glow::TEXTURE_2D, Some(output_texture));
// gl::ObjectLabel(
// gl::TEXTURE,
// glow::ObjectLabel(
// glow::TEXTURE,
// output_texture,
// -1,
// b"output_texture\0".as_ptr().cast(),
// );
// empty image
gl::TexStorage2D(
gl::TEXTURE_2D,
gl.tex_storage_2d(
glow::TEXTURE_2D,
1,
gl::RGBA8,
WIDTH as GLsizei,
HEIGHT as GLsizei,
glow::RGBA8,
WIDTH as i32,
HEIGHT as i32,
);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_S,
gl::CLAMP_TO_EDGE as GLint,
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MAG_FILTER,
glow::NEAREST as i32,
);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_T,
gl::CLAMP_TO_EDGE as GLint,
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MIN_FILTER,
glow::NEAREST as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_WRAP_S,
glow::CLAMP_TO_EDGE as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_WRAP_T,
glow::CLAMP_TO_EDGE as i32,
);
// set color attachment
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
output_texture,
gl.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
Some(output_texture),
0,
);
let buffers = [gl::COLOR_ATTACHMENT0];
gl::DrawBuffers(1, buffers.as_ptr());
if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
gl.draw_buffer(glow::COLOR_ATTACHMENT0);
if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
panic!("failed to create fbo")
}
@ -417,13 +342,12 @@ pub fn do_loop(
1.0, 1.0, 0.0,
];
gl::GenBuffers(1, &mut output_quad_vbuf);
gl::BindBuffer(gl::ARRAY_BUFFER, output_quad_vbuf);
gl::BufferData(
gl::ARRAY_BUFFER, // target
(fullscreen_fbo.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
fullscreen_fbo.as_ptr() as *const gl::types::GLvoid, // pointer to data
gl::STATIC_DRAW, // usage
output_quad_vbuf = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(output_quad_vbuf));
gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER, // target
bytemuck::cast_slice(&fullscreen_fbo),
glow::STATIC_DRAW, // usage
);
}
@ -452,19 +376,20 @@ void main()
color=texture(texSampler, v_tex);
}";
let quad_programid = compile_program(VERT_SHADER, FRAG_SHADER);
let mut quad_vao = 0;
let quad_programid = super::compile_program(gl, VERT_SHADER, FRAG_SHADER);
let quad_vao;
unsafe {
gl::GenVertexArrays(1, &mut quad_vao);
quad_vao = gl.create_vertex_array().unwrap();
}
let (fb_width, fb_height) = window.get_framebuffer_size();
let (vp_width, vp_height) = window.get_size();
let output = GLFramebuffer::new_from_raw(
output_texture,
Arc::clone(gl),
Some(output_texture),
output_framebuffer_handle,
gl::RGBA8,
glow::RGBA8,
Size::new(vp_width as u32, vp_height as u32),
1,
);
@ -477,26 +402,26 @@ void main()
unsafe {
// render to fb
gl::BindFramebuffer(gl::FRAMEBUFFER, rendered_framebuffer);
gl::Viewport(0, 0, vp_width, vp_height);
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(rendered_framebuffer));
gl.viewport(0, 0, vp_width, vp_height);
// clear color
clear_color(Color(0.3, 0.4, 0.6, 1.0));
gl::Clear(gl::COLOR_BUFFER_BIT);
gl.clear_color(0.3, 0.4, 0.6, 1.0);
gl.clear(glow::COLOR_BUFFER_BIT);
// do the drawing
gl::UseProgram(triangle_program);
gl.use_program(Some(triangle_program));
// select vertices
gl::BindVertexArray(triangle_vao);
gl.bind_vertex_array(Some(triangle_vao));
// draw to bound target
gl::DrawArrays(gl::TRIANGLES, 0, 3);
gl.draw_arrays(glow::TRIANGLES, 0, 3);
// unselect vertices
gl::BindVertexArray(0);
gl.bind_vertex_array(None);
// unselect fbo
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl.bind_framebuffer(glow::FRAMEBUFFER, None);
}
let viewport = Viewport {
@ -507,8 +432,8 @@ void main()
};
let rendered = GLImage {
handle: rendered_texture,
format: gl::RGBA8,
handle: Some(rendered_texture),
format: glow::RGBA8,
size: Size {
width: fb_width as u32,
height: fb_height as u32,
@ -524,13 +449,13 @@ void main()
unsafe {
// texture is done now.
// draw quad to screen
gl::UseProgram(quad_programid);
gl.use_program(Some(quad_programid));
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, output_texture);
gl.active_texture(glow::TEXTURE0);
gl.bind_texture(glow::TEXTURE_2D, Some(output_texture));
gl::BindVertexArray(quad_vao);
gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
gl.bind_vertex_array(Some(quad_vao));
gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
}
framecount += 1;
@ -538,18 +463,6 @@ void main()
}
}
pub struct Color(f32, f32, f32, f32);
pub fn clear_color(c: Color) {
unsafe { gl::ClearColor(c.0, c.1, c.2, c.3) }
}
pub fn gl_get_string<'a>(name: gl::types::GLenum) -> &'a str {
let v = unsafe { gl::GetString(name) };
let v: &std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(v as *const i8) };
v.to_str().unwrap()
}
fn glfw_handle_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
use glfw::Action;
use glfw::Key;

View file

@ -1,10 +1,9 @@
use std::convert::TryInto;
use std::ffi::{c_void, CStr};
use std::sync::mpsc::Receiver;
use std::sync::Arc;
use glfw::{Context, Glfw, Window, WindowEvent};
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
use glow::HasContext;
use librashader_common::{Size, Viewport};
use librashader_runtime_gl::{FilterChainGL, GLFramebuffer, GLImage};
@ -13,101 +12,16 @@ const WIDTH: u32 = 800;
const HEIGHT: u32 = 600;
const TITLE: &str = "librashader OpenGL 4.6";
pub fn compile_program(vertex: &str, fragment: &str) -> GLuint {
let vertex_shader = unsafe { gl::CreateShader(gl::VERTEX_SHADER) };
unsafe {
gl::ShaderSource(
vertex_shader,
1,
&vertex.as_bytes().as_ptr().cast(),
&vertex.len().try_into().unwrap(),
);
gl::CompileShader(vertex_shader);
let mut success = 0;
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
if success == 0 {
let mut log_len = 0_i32;
// gl::GetShaderiv(vertex_shader, gl::INFO_LOG_LENGTH, &mut log_len);
// let mut v: Vec<u8> = Vec::with_capacity(log_len as usize);
// gl::GetShaderInfoLog(vertex_shader, log_len, &mut log_len, v.as_mut_ptr().cast());
let mut v: Vec<u8> = Vec::with_capacity(1024);
gl::GetShaderInfoLog(vertex_shader, 1024, &mut log_len, v.as_mut_ptr().cast());
v.set_len(log_len.try_into().unwrap());
panic!(
"Vertex Shader Compile Error: {}",
String::from_utf8_lossy(&v)
);
}
}
let fragment_shader = unsafe { gl::CreateShader(gl::FRAGMENT_SHADER) };
unsafe {
gl::ShaderSource(
fragment_shader,
1,
&fragment.as_bytes().as_ptr().cast(),
&fragment.len().try_into().unwrap(),
);
gl::CompileShader(fragment_shader);
let mut success = 0;
gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
if success == 0 {
let mut v: Vec<u8> = Vec::with_capacity(1024);
let mut log_len = 0_i32;
gl::GetShaderInfoLog(fragment_shader, 1024, &mut log_len, v.as_mut_ptr().cast());
v.set_len(log_len.try_into().unwrap());
panic!(
"Fragment Shader Compile Error: {}",
String::from_utf8_lossy(&v)
);
}
}
let shader_program = unsafe { gl::CreateProgram() };
unsafe {
gl::AttachShader(shader_program, vertex_shader);
gl::AttachShader(shader_program, fragment_shader);
gl::LinkProgram(shader_program);
let mut success = 0;
gl::GetProgramiv(shader_program, gl::LINK_STATUS, &mut success);
if success == 0 {
let mut v: Vec<u8> = Vec::with_capacity(1024);
let mut log_len = 0_i32;
gl::GetProgramInfoLog(shader_program, 1024, &mut log_len, v.as_mut_ptr().cast());
v.set_len(log_len.try_into().unwrap());
panic!("Program Link Error: {}", String::from_utf8_lossy(&v));
}
gl::DetachShader(shader_program, vertex_shader);
gl::DetachShader(shader_program, fragment_shader);
gl::DeleteShader(vertex_shader);
gl::DeleteShader(fragment_shader);
}
shader_program
}
extern "system" fn debug_callback(
_source: GLenum,
_err_type: GLenum,
_id: GLuint,
_severity: GLenum,
_length: GLsizei,
message: *const GLchar,
_user: *mut c_void,
pub fn setup() -> (
Glfw,
Window,
Receiver<(f64, WindowEvent)>,
glow::Program,
glow::VertexArray,
Arc<glow::Context>,
) {
unsafe {
let message = CStr::from_ptr(message);
println!("[gl] {message:?}");
}
}
pub fn setup() -> (Glfw, Window, Receiver<(f64, WindowEvent)>, GLuint, GLuint) {
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
glfw.window_hint(glfw::WindowHint::ContextVersion(4, 6));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(
glfw::OpenGlProfileHint::Core,
));
@ -122,26 +36,20 @@ pub fn setup() -> (Glfw, Window, Receiver<(f64, WindowEvent)>, GLuint, GLuint) {
window.make_current();
window.set_key_polling(true);
gl::load_with(|ptr| window.get_proc_address(ptr) as *const _);
let mut gl = unsafe { glow::Context::from_loader_function(|ptr| window.get_proc_address(ptr)) };
unsafe {
gl::Enable(gl::DEBUG_OUTPUT);
gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
gl.enable(glow::DEBUG_OUTPUT);
gl.enable(glow::DEBUG_OUTPUT_SYNCHRONOUS);
gl::DebugMessageCallback(Some(debug_callback), std::ptr::null_mut());
gl::DebugMessageControl(
gl::DONT_CARE,
gl::DONT_CARE,
gl::DONT_CARE,
0,
std::ptr::null(),
gl::TRUE,
);
gl.debug_message_callback(super::debug_callback);
gl.debug_message_control(glow::DONT_CARE, glow::DONT_CARE, glow::DONT_CARE, &[], true);
}
unsafe {
gl::Viewport(0, 0, screen_width, screen_height);
clear_color(Color(0.4, 0.4, 0.4, 1.0));
gl.viewport(0, 0, screen_width, screen_height);
gl.clear_color(0.4, 0.4, 0.4, 1.0);
}
// -------------------------------------------
@ -172,15 +80,10 @@ void main()
{
Color = vec4(IN.Color, 1.0f);
}";
let shader_program = compile_program(VERT_SHADER, FRAG_SHADER);
let shader_program = super::compile_program(&gl, VERT_SHADER, FRAG_SHADER);
unsafe {
gl::ObjectLabel(
gl::SHADER,
shader_program,
-1,
b"color_shader\0".as_ptr().cast(),
);
gl.object_label(glow::SHADER, shader_program.0.get(), Some("color_shader"));
}
let vertices = &[
@ -189,146 +92,143 @@ void main()
-0.5, -0.5, 0.0, 0.0, 1.0, 0.0, // bottom left
0.0, 0.5, 0.0, 0.0, 0.0, 1.0, // top
];
let mut vbo: gl::types::GLuint = 0;
let vbo = unsafe { gl.create_named_buffer().unwrap() };
unsafe {
gl::CreateBuffers(1, &mut vbo);
gl::ObjectLabel(gl::BUFFER, vbo, -1, b"triangle_vbo\0".as_ptr().cast());
gl.object_label(glow::BUFFER, vbo.0.get(), Some("triangle_vbo"));
}
unsafe {
gl::NamedBufferData(
gl.named_buffer_data_u8_slice(
vbo,
(vertices.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
vertices.as_ptr() as *const gl::types::GLvoid, // pointer to data
gl::STATIC_DRAW, // usage
bytemuck::cast_slice(vertices),
glow::STATIC_DRAW, // usage
);
}
// set up vertex array object
let mut vao: gl::types::GLuint = 0;
let vao = unsafe { gl.create_named_vertex_array().unwrap() };
// todo: figure this shit out
unsafe {
gl::CreateVertexArrays(1, &mut vao);
gl::ObjectLabel(gl::VERTEX_ARRAY, vao, -1, b"triangle_vao\0".as_ptr().cast());
// gl.object_label(glow::VERTEX_ARRAY, vao.0.get(), Some("triangle_vao"));
gl::VertexArrayVertexBuffer(vao, 0, vbo, 0, 6 * std::mem::size_of::<f32>() as GLint);
gl.vertex_array_vertex_buffer(vao, 0, Some(vbo), 0, 6 * std::mem::size_of::<f32>() as i32);
gl::EnableVertexArrayAttrib(vao, 0); // this is "layout (location = 0)" in vertex shader
gl::VertexArrayAttribFormat(vao, 0, 3, gl::FLOAT, gl::FALSE, 0);
gl.enable_vertex_array_attrib(vao, 0); // this is "layout (location = 0)" in vertex shader
gl.vertex_array_attrib_format_f32(vao, 0, 3, glow::FLOAT, false, 0);
gl::EnableVertexArrayAttrib(vao, 1);
gl::VertexArrayAttribFormat(
gl.enable_vertex_array_attrib(vao, 1);
gl.vertex_array_attrib_format_f32(
vao,
1,
3,
gl::FLOAT,
gl::FALSE,
3 * std::mem::size_of::<f32>() as GLuint,
glow::FLOAT,
false,
3 * std::mem::size_of::<f32>() as u32,
);
gl::VertexArrayAttribBinding(vao, 0, 0);
gl::VertexArrayAttribBinding(vao, 1, 0);
gl.vertex_array_attrib_binding_f32(vao, 0, 0);
gl.vertex_array_attrib_binding_f32(vao, 1, 0);
}
// set up shared state for window
unsafe {
gl::Viewport(0, 0, 900, 700);
gl::ClearColor(0.3, 0.3, 0.5, 1.0);
gl.viewport(0, 0, 900, 700);
gl.clear_color(0.3, 0.3, 0.5, 1.0);
}
// -------------------------------------------
println!("OpenGL version: {}", gl_get_string(gl::VERSION));
println!(
"GLSL version: {}",
gl_get_string(gl::SHADING_LANGUAGE_VERSION)
);
unsafe {
// -------------------------------------------
println!("OpenGL version: {}", gl.get_parameter_string(glow::VERSION));
println!(
"GLSL version: {}",
gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION)
);
}
(glfw, window, events, shader_program, vao)
(glfw, window, events, shader_program, vao, Arc::new(gl))
}
pub fn do_loop(
gl: &Arc<glow::Context>,
mut glfw: Glfw,
mut window: Window,
events: Receiver<(f64, WindowEvent)>,
triangle_program: GLuint,
triangle_vao: GLuint,
triangle_program: glow::Program,
triangle_vao: glow::VertexArray,
filter: &mut FilterChainGL,
) {
let mut framecount = 0;
let mut rendered_framebuffer = 0;
let mut rendered_texture = 0;
let mut quad_vbuf = 0;
let rendered_framebuffer;
let rendered_texture;
let quad_vbuf;
let mut output_texture = 0;
let mut output_framebuffer_handle = 0;
let mut output_quad_vbuf = 0;
let output_texture;
let output_framebuffer_handle;
let output_quad_vbuf;
unsafe {
// do frmaebuffer
gl::CreateFramebuffers(1, &mut rendered_framebuffer);
gl::ObjectLabel(
gl::FRAMEBUFFER,
rendered_framebuffer,
-1,
b"rendered_framebuffer\0".as_ptr().cast(),
rendered_framebuffer = gl.create_named_framebuffer().unwrap();
gl.object_label(
glow::FRAMEBUFFER,
rendered_framebuffer.0.get(),
Some("rendered_framebuffer"),
);
// make tetxure
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut rendered_texture);
gl::ObjectLabel(
gl::TEXTURE,
rendered_texture,
-1,
b"rendered_texture\0".as_ptr().cast(),
rendered_texture = gl.create_named_texture(glow::TEXTURE_2D).unwrap();
gl.object_label(
glow::TEXTURE,
rendered_texture.0.get(),
Some("rendered_texture"),
);
// empty image
gl::TextureStorage2D(
gl.texture_storage_2d(
rendered_texture,
1,
gl::RGBA8,
WIDTH as GLsizei,
HEIGHT as GLsizei,
glow::RGBA8,
WIDTH as i32,
HEIGHT as i32,
);
gl::TextureParameteri(
gl.texture_parameter_i32(
rendered_texture,
gl::TEXTURE_MAG_FILTER,
gl::NEAREST as GLint,
glow::TEXTURE_MAG_FILTER,
glow::NEAREST as i32,
);
gl::TextureParameteri(
gl.texture_parameter_i32(
rendered_texture,
gl::TEXTURE_MIN_FILTER,
gl::NEAREST as GLint,
glow::TEXTURE_MIN_FILTER,
glow::NEAREST as i32,
);
gl::TextureParameteri(
gl.texture_parameter_i32(
rendered_texture,
gl::TEXTURE_WRAP_S,
gl::CLAMP_TO_EDGE as GLint,
glow::TEXTURE_WRAP_S,
glow::CLAMP_TO_EDGE as i32,
);
gl::TextureParameteri(
gl.texture_parameter_i32(
rendered_texture,
gl::TEXTURE_WRAP_T,
gl::CLAMP_TO_EDGE as GLint,
glow::TEXTURE_WRAP_T,
glow::CLAMP_TO_EDGE as i32,
);
// set color attachment
gl::NamedFramebufferTexture(
rendered_framebuffer,
gl::COLOR_ATTACHMENT0,
rendered_texture,
gl.named_framebuffer_texture(
Some(rendered_framebuffer),
glow::COLOR_ATTACHMENT0,
Some(rendered_texture),
0,
);
let buffers = [gl::COLOR_ATTACHMENT0];
gl::NamedFramebufferDrawBuffers(rendered_framebuffer, 1, buffers.as_ptr());
gl.named_framebuffer_draw_buffer(Some(rendered_framebuffer), glow::COLOR_ATTACHMENT0);
if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
if gl.check_named_framebuffer_status(Some(rendered_framebuffer), glow::FRAMEBUFFER)
!= glow::FRAMEBUFFER_COMPLETE
{
panic!("failed to create fbo")
}
@ -337,70 +237,70 @@ pub fn do_loop(
1.0, 1.0, 0.0,
];
gl::CreateBuffers(1, &mut quad_vbuf);
gl::NamedBufferData(
quad_vbuf, // target
(fullscreen_fbo.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
fullscreen_fbo.as_ptr() as *const gl::types::GLvoid, // pointer to data
gl::STATIC_DRAW, // usage
quad_vbuf = gl.create_named_buffer().unwrap();
gl.named_buffer_data_u8_slice(
quad_vbuf,
bytemuck::cast_slice(&fullscreen_fbo),
glow::STATIC_DRAW,
);
}
unsafe {
// do frmaebuffer
gl::CreateFramebuffers(1, &mut output_framebuffer_handle);
output_framebuffer_handle = gl.create_named_framebuffer().unwrap();
gl::ObjectLabel(
gl::FRAMEBUFFER,
output_framebuffer_handle,
-1,
b"output_framebuffer\0".as_ptr().cast(),
gl.object_label(
glow::FRAMEBUFFER,
output_framebuffer_handle.0.get(),
Some("output_framebuffer"),
);
// make tetxure
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut output_texture);
gl::ObjectLabel(
gl::TEXTURE,
output_texture,
-1,
b"output_texture\0".as_ptr().cast(),
output_texture = gl.create_named_texture(glow::TEXTURE_2D).unwrap();
//
gl.object_label(
glow::TEXTURE,
output_texture.0.get(),
Some("output_texture"),
);
// empty image
gl::TextureStorage2D(
output_texture,
1,
gl::RGBA8,
WIDTH as GLsizei,
HEIGHT as GLsizei,
);
gl.texture_storage_2d(output_texture, 1, glow::RGBA8, WIDTH as i32, HEIGHT as i32);
gl::TextureParameteri(output_texture, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
gl::TextureParameteri(output_texture, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
gl::TextureParameteri(
gl.texture_parameter_i32(
output_texture,
gl::TEXTURE_WRAP_S,
gl::CLAMP_TO_EDGE as GLint,
glow::TEXTURE_MAG_FILTER,
glow::NEAREST as i32,
);
gl::TextureParameteri(
gl.texture_parameter_i32(
output_texture,
gl::TEXTURE_WRAP_T,
gl::CLAMP_TO_EDGE as GLint,
glow::TEXTURE_MIN_FILTER,
glow::NEAREST as i32,
);
gl.texture_parameter_i32(
output_texture,
glow::TEXTURE_WRAP_S,
glow::CLAMP_TO_EDGE as i32,
);
gl.texture_parameter_i32(
output_texture,
glow::TEXTURE_WRAP_T,
glow::CLAMP_TO_EDGE as i32,
);
// set color attachment
gl::NamedFramebufferTexture(
output_framebuffer_handle,
gl::COLOR_ATTACHMENT0,
output_texture,
gl.named_framebuffer_texture(
Some(output_framebuffer_handle),
glow::COLOR_ATTACHMENT0,
Some(output_texture),
0,
);
let buffers = [gl::COLOR_ATTACHMENT0];
gl::NamedFramebufferDrawBuffers(output_framebuffer_handle, 1, buffers.as_ptr());
gl.named_framebuffer_draw_buffer(Some(output_framebuffer_handle), glow::COLOR_ATTACHMENT0);
if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
if gl.check_named_framebuffer_status(Some(output_framebuffer_handle), glow::FRAMEBUFFER)
!= glow::FRAMEBUFFER_COMPLETE
{
panic!("failed to create fbo")
}
@ -409,12 +309,11 @@ pub fn do_loop(
1.0, 1.0, 0.0,
];
gl::CreateBuffers(1, &mut output_quad_vbuf);
gl::NamedBufferData(
output_quad_vbuf = gl.create_named_buffer().unwrap();
gl.named_buffer_data_u8_slice(
output_quad_vbuf,
(fullscreen_fbo.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
fullscreen_fbo.as_ptr() as *const gl::types::GLvoid, // pointer to data
gl::STATIC_DRAW, // usage
bytemuck::cast_slice(&fullscreen_fbo),
glow::STATIC_DRAW,
);
}
@ -443,19 +342,17 @@ void main()
color=texture(texSampler, v_tex);
}";
let quad_programid = compile_program(VERT_SHADER, FRAG_SHADER);
let mut quad_vao = 0;
unsafe {
gl::CreateVertexArrays(1, &mut quad_vao);
}
let quad_programid = super::compile_program(gl, VERT_SHADER, FRAG_SHADER);
let quad_vao = unsafe { gl.create_named_vertex_array().unwrap() };
let (fb_width, fb_height) = window.get_framebuffer_size();
let (vp_width, vp_height) = window.get_size();
let output = GLFramebuffer::new_from_raw(
output_texture,
Arc::clone(gl),
Some(output_texture),
output_framebuffer_handle,
gl::RGBA8,
glow::RGBA8,
Size::new(vp_width as u32, vp_height as u32),
1,
);
@ -469,28 +366,28 @@ void main()
unsafe {
// render to fb
gl::ClearNamedFramebufferfv(
rendered_framebuffer,
gl::COLOR,
gl.clear_named_framebuffer_f32_slice(
Some(rendered_framebuffer),
glow::COLOR,
0,
[0.3f32, 0.4, 0.6, 1.0].as_ptr().cast(),
&[0.3f32, 0.4, 0.6, 1.0],
);
gl::BindFramebuffer(gl::FRAMEBUFFER, rendered_framebuffer);
gl::Viewport(0, 0, vp_width, vp_height);
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(rendered_framebuffer));
gl.viewport(0, 0, vp_width, vp_height);
// do the drawing
gl::UseProgram(triangle_program);
gl.use_program(Some(triangle_program));
// select vertices
gl::BindVertexArray(triangle_vao);
gl.bind_vertex_array(Some(triangle_vao));
// draw to bound target
gl::DrawArrays(gl::TRIANGLES, 0, 3);
gl.draw_arrays(glow::TRIANGLES, 0, 3);
// unselect vertices
gl::BindVertexArray(0);
gl.bind_vertex_array(None);
// unselect fbo
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl.bind_framebuffer(glow::FRAMEBUFFER, None);
}
let viewport = Viewport {
@ -501,8 +398,8 @@ void main()
};
let rendered = GLImage {
handle: rendered_texture,
format: gl::RGBA8,
handle: Some(rendered_texture),
format: glow::RGBA8,
size: Size {
width: fb_width as u32,
height: fb_height as u32,
@ -518,31 +415,18 @@ void main()
unsafe {
// texture is done now.
// draw quad to screen
gl::UseProgram(quad_programid);
gl.use_program(Some(quad_programid));
gl::BindTextureUnit(0, output_texture);
gl.bind_texture_unit(0, Some(output_texture));
gl::BindVertexArray(quad_vao);
gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
gl.bind_vertex_array(Some(quad_vao));
gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
}
framecount += 1;
window.swap_buffers();
}
}
pub struct Color(f32, f32, f32, f32);
pub fn clear_color(c: Color) {
unsafe { gl::ClearColor(c.0, c.1, c.2, c.3) }
}
pub fn gl_get_string<'a>(name: gl::types::GLenum) -> &'a str {
let v = unsafe { gl::GetString(name) };
let v: &std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(v as *const i8) };
v.to_str().unwrap()
}
fn glfw_handle_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
use glfw::Action;
use glfw::Key;

View file

@ -1,2 +1,51 @@
use glow::HasContext;
pub mod gl3;
pub mod gl46;
pub fn compile_program(gl: &glow::Context, vertex: &str, fragment: &str) -> glow::Program {
let vertex_shader = unsafe { gl.create_shader(glow::VERTEX_SHADER).unwrap() };
unsafe {
gl.shader_source(vertex_shader, &vertex);
gl.compile_shader(vertex_shader);
if !gl.get_shader_compile_status(vertex_shader) {
let error = gl.get_shader_info_log(vertex_shader);
panic!("Vertex Shader Compile Error: {error}",);
}
}
let fragment_shader = unsafe { gl.create_shader(glow::FRAGMENT_SHADER).unwrap() };
unsafe {
gl.shader_source(fragment_shader, &fragment);
gl.compile_shader(fragment_shader);
if !gl.get_shader_compile_status(fragment_shader) {
let error = gl.get_shader_info_log(fragment_shader);
panic!("Fragment Shader Compile Error: {error}",);
}
}
let shader_program = unsafe { gl.create_program().unwrap() };
unsafe {
gl.attach_shader(shader_program, vertex_shader);
gl.attach_shader(shader_program, fragment_shader);
gl.link_program(shader_program);
if !gl.get_program_link_status(shader_program) {
let error = gl.get_program_info_log(shader_program);
panic!("Program Link Error: {error}",);
}
gl.detach_shader(shader_program, vertex_shader);
gl.detach_shader(shader_program, fragment_shader);
gl.delete_shader(vertex_shader);
gl.delete_shader(fragment_shader);
}
shader_program
}
fn debug_callback(_source: u32, _err_type: u32, _id: u32, _severity: u32, message: &str) {
println!("[gl] {message:?}");
}

View file

@ -2,14 +2,17 @@ mod hello_triangle;
use librashader_runtime_gl::options::FilterChainOptionsGL;
use librashader_runtime_gl::FilterChainGL;
use std::sync::Arc;
#[test]
fn triangle_gl() {
let (glfw, window, events, shader, vao) = hello_triangle::gl3::setup();
let (glfw, window, events, shader, vao, context) = hello_triangle::gl3::setup();
unsafe {
let mut filter = FilterChainGL::load_from_path(
"../test/shaders_slang/test/feedback.slangp",
Arc::clone(&context),
// "../test/basic.slangp",
"../test/shaders_slang/crt/crt-royale.slangp",
Some(&FilterChainOptionsGL {
glsl_version: 0,
use_dsa: false,
@ -19,21 +22,23 @@ fn triangle_gl() {
)
// FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
.unwrap();
hello_triangle::gl3::do_loop(glfw, window, events, shader, vao, &mut filter);
hello_triangle::gl3::do_loop(&context, glfw, window, events, shader, vao, &mut filter);
}
}
#[test]
fn triangle_gl46() {
let (glfw, window, events, shader, vao) = hello_triangle::gl46::setup();
let (glfw, window, events, shader, vao, context) = hello_triangle::gl46::setup();
unsafe {
let mut filter = FilterChainGL::load_from_path(
Arc::clone(&context),
// "../test/slang-shaders/vhs/VHSPro.slangp",
// "../test/slang-shaders/test/history.slangp",
// "../test/shaders_slang/crt/crt-royale.slangp",
"../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// "../test/basic.slangp",
"../test/shaders_slang/crt/crt-royale.slangp",
// "../test/shadersslang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
Some(&FilterChainOptionsGL {
glsl_version: 0,
glsl_version: 330,
use_dsa: true,
force_no_mipmaps: false,
disable_cache: false,
@ -41,6 +46,6 @@ fn triangle_gl46() {
)
// FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
.unwrap();
hello_triangle::gl46::do_loop(glfw, window, events, shader, vao, &mut filter);
hello_triangle::gl46::do_loop(&context, glfw, window, events, shader, vao, &mut filter);
}
}

View file

@ -50,6 +50,15 @@ where
index: 0,
}
}
}
impl<T, const SIZE: usize> InlineRingBuffer<T, SIZE>
where
T: Copy,
{
pub fn from_array(items: [T; SIZE]) -> Self {
Self { items, index: 0 }
}
/// Get a borrow to all the items in this ring buffer.
pub fn items(&self) -> &[T; SIZE] {