Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Image units: initial support #1691

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -44,6 +44,7 @@ backtrace = "0.3.2"
lazy_static = "1.0"
smallvec = "0.6"
fnv = "1.0.5"
bitflags = "1.0"

[build-dependencies]
gl_generator = "0.9"
Expand Down
27 changes: 25 additions & 2 deletions build/textures.rs
Expand Up @@ -325,7 +325,7 @@ fn build_texture<W: Write>(dest: &mut W, ty: TextureType, dimensions: TextureDim
use image_format::{{CompressedSrgbFormat, SrgbFormat, UncompressedUintFormat}};

use backend::Facade;
use uniforms::{{UniformValue, AsUniformValue, Sampler}};
use uniforms::{{UniformValue, AsUniformValue, Sampler, ImageUnit}};
use framebuffer;
use Rect;

Expand Down Expand Up @@ -413,7 +413,7 @@ fn build_texture<W: Write>(dest: &mut W, ty: TextureType, dimensions: TextureDim
}}
", name)).unwrap();

// `UniformValue` trait impl
// `UniformValue` trait impl for samplers
{
match ty {
TextureType::Regular | TextureType::Compressed |
Expand Down Expand Up @@ -461,6 +461,29 @@ fn build_texture<W: Write>(dest: &mut W, ty: TextureType, dimensions: TextureDim
}
}

// Generate implementations of image_unit
if (ty == TextureType::Regular || ty == TextureType::Integral || ty == TextureType::Unsigned) &&
(dimensions != TextureDimensions::Texture2dMultisample && dimensions != TextureDimensions::Texture2dMultisampleArray ){
let image_variant = name.replace("Texture", "Image").replace("Cubemap", "ImageCube");
writeln!(dest, "
impl<'a> AsUniformValue for ImageUnit<'a, {myname}> {{
#[inline]
fn as_uniform_value(&self) -> UniformValue {{
UniformValue::{valname}(self.0, Some(self.1))
}}
}}

impl {myname} {{
/// Builds an image unit marker object that allows you to indicate how the
/// texture should be bound to an image unit.
#[inline]
pub fn image_unit(&self) -> ImageUnit<{myname}> {{
ImageUnit(self, Default::default())
}}
}}
", myname = name, valname = image_variant).unwrap();
}

// `ToXXXAttachment` trait impl
if dimensions == TextureDimensions::Texture2d || dimensions == TextureDimensions::Texture2dMultisample ||
dimensions == TextureDimensions::Texture1d
Expand Down
1 change: 1 addition & 0 deletions src/backend/mod.rs
Expand Up @@ -25,6 +25,7 @@ use version::Version;

pub use context::Context;
pub use context::ReleaseBehavior;
pub use context::MemoryBarrier;

#[cfg(feature = "glutin")]
pub mod glutin;
Expand Down
26 changes: 26 additions & 0 deletions src/context/mod.rs
Expand Up @@ -530,6 +530,14 @@ impl Context {
T::from_raw(Cow::Owned(data), dimensions.0, dimensions.1)
}

/// Creates a memory barrier, according to the ARB_shader_image_load_store extension
pub fn memory_barrier(&self, barrier: MemoryBarrier) {
let ctxt = self.make_current();
unsafe {
ctxt.gl.MemoryBarrier(barrier.bits());
}
}

/// Execute an arbitrary closure with the OpenGL context active. Useful if another
/// component needs to directly manipulate OpenGL state.
///
Expand Down Expand Up @@ -996,3 +1004,21 @@ fn init_debug_callback(context: &Rc<Context>, synchronous: bool) {
}
}
}

bitflags! {
#[doc("Represents which kind of memory barrier to insert")]
pub struct MemoryBarrier: u32 {
const VERTEX_ATTRIB_ARRAY_BARRIER = gl::VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
const ELEMENT_ARRAY_BARRIER = gl::ELEMENT_ARRAY_BARRIER_BIT;
const UNIFORM_BARRIER = gl::UNIFORM_BARRIER_BIT;
const TEXTURE_FETCH_BARRIER = gl::TEXTURE_FETCH_BARRIER_BIT;
const SHADER_IMAGE_ACCESS_BARRIER = gl::SHADER_IMAGE_ACCESS_BARRIER_BIT;
const COMMAND_BARRIER = gl::COMMAND_BARRIER_BIT;
const PIXEL_BUFFER_BARRIER = gl::PIXEL_BUFFER_BARRIER_BIT;
const TEXTURE_UPDATE_BARRIER = gl::TEXTURE_UPDATE_BARRIER_BIT;
const BUFFER_UPDATE_BARRIER = gl::BUFFER_UPDATE_BARRIER_BIT;
const FRAMEBUFFER_BARRIER = gl::FRAMEBUFFER_BARRIER_BIT;
const TRANSFORM_FEEDBACK_BARRIER = gl::TRANSFORM_FEEDBACK_BARRIER_BIT;
const ATOMIC_COUNTER_BARRIER = gl::ATOMIC_COUNTER_BARRIER_BIT;
}
}
10 changes: 9 additions & 1 deletion src/lib.rs
Expand Up @@ -97,6 +97,9 @@ result to the user.
#![allow(dead_code)]
#![allow(unused_variables)]

#[macro_use]
extern crate bitflags;

#[macro_use]
extern crate lazy_static;

Expand Down Expand Up @@ -946,6 +949,9 @@ pub enum DrawError {

/// Tried to enable a clip plane that does not exist.
ClipPlaneIndexOutOfBounds,

/// Tried to use too many image units simultaneously
InsufficientImageUnits,
}

impl Error for DrawError {
Expand Down Expand Up @@ -1005,7 +1011,9 @@ impl Error for DrawError {
FixedIndexRestartingNotSupported =>
"Restarting indices (multiple objects per draw call) is not supported by the backend",
ClipPlaneIndexOutOfBounds =>
"Tried to enable a clip plane that does not exist."
"Tried to enable a clip plane that does not exist.",
InsufficientImageUnits =>
"Tried to use more image uniforms that the implementation has support for",
}
}

Expand Down
112 changes: 110 additions & 2 deletions src/uniforms/bind.rs
Expand Up @@ -21,6 +21,7 @@ use TextureExt;
use uniforms::Uniforms;
use uniforms::UniformValue;
use uniforms::SamplerBehavior;
use uniforms::ImageUnitBehavior;

use context::CommandContext;
use buffer::Inserter;
Expand All @@ -41,6 +42,8 @@ impl<U> UniformsExt for U where U: Uniforms {
let mut texture_bind_points = Bitsfield::new();
let mut uniform_buffer_bind_points = Bitsfield::new();
let mut shared_storage_buffer_bind_points = Bitsfield::new();
let mut image_unit_bind_points = Bitsfield::new();
image_unit_bind_points.set_used(0); // Trying to attach data to image unit 0 would not go well

// Subroutine uniforms must be bound all at once, so we collect them first and process them at the end.
// The vec contains the uniform we want to set and the value we want to set it to.
Expand All @@ -64,7 +67,7 @@ impl<U> UniformsExt for U where U: Uniforms {
}

match bind_uniform(&mut ctxt, &value, program, uniform.location,
&mut texture_bind_points, name)
&mut texture_bind_points, &mut image_unit_bind_points, name)
{
Ok(_) => (),
Err(e) => {
Expand Down Expand Up @@ -241,7 +244,9 @@ fn bind_shared_storage_block<'a, P>(ctxt: &mut context::CommandContext, value: &

fn bind_uniform<P>(ctxt: &mut context::CommandContext,
value: &UniformValue, program: &P, location: gl::types::GLint,
texture_bind_points: &mut Bitsfield, name: &str)
texture_bind_points: &mut Bitsfield,
image_unit_bind_points: &mut Bitsfield,
name: &str)
-> Result<(), DrawError> where P: ProgramExt
{
assert!(location >= 0);
Expand Down Expand Up @@ -577,6 +582,69 @@ fn bind_uniform<P>(ctxt: &mut context::CommandContext,
UniformValue::BufferTexture(texture) => {
bind_texture_uniform(ctxt, &texture, None, location, program, texture_bind_points)
},
UniformValue::Image1d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::IntegralImage1d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::UnsignedImage1d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::Image2d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::IntegralImage2d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::UnsignedImage2d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::Image3d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::IntegralImage3d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::UnsignedImage3d(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::Image1dArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::IntegralImage1dArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::UnsignedImage1dArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::Image2dArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::IntegralImage2dArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::UnsignedImage2dArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::ImageCube(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::IntegralImageCube(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::UnsignedImageCube(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::ImageCubeArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::IntegralImageCubeArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
UniformValue::UnsignedImageCubeArray(texture, unit) => {
bind_image_uniform(ctxt, &**texture, unit, location, program, image_unit_bind_points)
},
}
}

Expand Down Expand Up @@ -654,3 +722,43 @@ fn bind_texture_uniform<P, T>(ctxt: &mut context::CommandContext,

Ok(())
}

fn bind_image_uniform<P, T>(
ctxt: &mut context::CommandContext,
texture: &T, unit_behavior: Option<ImageUnitBehavior>,
location: gl::types::GLint, program: &P,
image_unit_bind_points: &mut Bitsfield
) -> Result<(), DrawError>
where P: ProgramExt, T: TextureExt
{
use ToGlEnum;

let unit_behavior = unit_behavior.expect("Unit behavior should always be provided");
let image_unit = match image_unit_bind_points.get_unused() {
Some(unit) => unit,
None => return Err(DrawError::InsufficientImageUnits),
};

// Update the program to use the right unit
program.set_uniform(ctxt, location,
&RawUniformValue::SignedInt(image_unit as gl::types::GLint));

let (layered, layer) = match unit_behavior.layer {
None => (false, 0),
Some(l) => (true, l)
};

unsafe {
ctxt.gl.BindImageTexture(
image_unit as gl::types::GLuint,
texture.get_texture_id(),
unit_behavior.level as i32,
if layered { 1 } else { 0 },
layer as i32,
unit_behavior.access.to_glenum(),
unit_behavior.format.to_glenum(),
)
}

Ok(())
}