#![allow(missing_docs)]
use super::connection::Connection;
use super::context::{Context, ContextDescriptor, NativeContext};
use super::device::{Adapter, Device};
use super::surface::Surface;
use crate::gl;
use crate::{ContextAttributeFlags, ContextAttributes, Error, GLApi, GLVersion, Gl, SurfaceAccess};
use crate::{SurfaceType, WindowingApiError};
use euclid::default::Size2D;
use glow::{Framebuffer, HasContext, PixelPackData, Texture};
#[cfg(not(feature = "sm-test"))]
use serial_test::serial;
use std::sync::mpsc;
use std::thread;
static GL_VERSIONS: [GLVersion; 6] = [
GLVersion { major: 2, minor: 0 },
GLVersion { major: 3, minor: 0 },
GLVersion { major: 3, minor: 1 },
GLVersion { major: 3, minor: 2 },
GLVersion { major: 4, minor: 0 },
GLVersion { major: 4, minor: 1 },
];
static GL_ES_VERSIONS: [GLVersion; 4] = [
GLVersion { major: 2, minor: 0 },
GLVersion { major: 3, minor: 0 },
GLVersion { major: 3, minor: 1 },
GLVersion { major: 3, minor: 2 },
];
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_adapter_creation() {
let connection = Connection::new().unwrap();
connection.create_hardware_adapter().unwrap();
connection.create_low_power_adapter().unwrap();
connection.create_software_adapter().unwrap();
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_device_creation() {
let connection = Connection::new().unwrap();
let adapter = connection
.create_low_power_adapter()
.expect("Failed to create adapter!");
match connection.create_device(&adapter) {
Ok(_) => {}
Err(Error::RequiredExtensionUnavailable) => {
}
Err(err) => panic!("Failed to create device: {:?}", err),
}
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_device_accessors() {
let connection = Connection::new().unwrap();
let adapter = connection.create_low_power_adapter().unwrap();
let device = match connection.create_device(&adapter) {
Ok(device) => device,
Err(Error::RequiredExtensionUnavailable) => {
return;
}
Err(err) => panic!("Failed to create device: {:?}", err),
};
drop(device.connection());
drop(device.adapter());
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_context_creation() {
let connection = Connection::new().unwrap();
let adapter = connection
.create_low_power_adapter()
.expect("Failed to create adapter!");
let device = match connection.create_device(&adapter) {
Ok(device) => device,
Err(Error::RequiredExtensionUnavailable) => {
return;
}
Err(err) => panic!("Failed to create device: {:?}", err),
};
let gl_api = device.gl_api();
let versions = match gl_api {
GLApi::GL => &GL_VERSIONS[..],
GLApi::GLES => &GL_ES_VERSIONS[..],
};
for &version in versions {
for flag_bits in 0..(ContextAttributeFlags::all().bits() + 1) {
let flags = ContextAttributeFlags::from_bits_truncate(flag_bits);
let attributes = ContextAttributes { version, flags };
let descriptor = match device.create_context_descriptor(&attributes) {
Ok(descriptor) => descriptor,
Err(Error::UnsupportedGLProfile) | Err(Error::UnsupportedGLVersion) => {
continue;
}
Err(err) => panic!("Context descriptor creation failed: {:?}", err),
};
match device.create_context(&descriptor, None) {
Ok(mut context) => {
let actual_descriptor = device.context_descriptor(&context);
let actual_attributes =
device.context_descriptor_attributes(&actual_descriptor);
if !actual_attributes.flags.contains(attributes.flags) {
device.destroy_context(&mut context).unwrap();
panic!(
"Expected at least attribute flags {:?} for {:?} {:?} but got \
{:?}",
attributes.flags, gl_api, version, actual_attributes.flags
);
}
if actual_attributes.version.major < attributes.version.major
|| (actual_attributes.version.major == attributes.version.major
&& actual_attributes.version.minor < attributes.version.minor)
{
device.destroy_context(&mut context).unwrap();
panic!(
"Expected at least GL version {:?} but got version {:?}",
attributes, actual_attributes
);
}
device.destroy_context(&mut context).unwrap();
}
Err(Error::ContextCreationFailed(WindowingApiError::BadPixelFormat))
| Err(Error::ContextCreationFailed(WindowingApiError::BadConfig))
| Err(Error::ContextCreationFailed(WindowingApiError::BadMatch)) => {
}
Err(error) => {
let error = format!(
"Failed to create context ({:?}/{:?}): {:?}",
version, flags, error
);
if cfg!(all(feature = "sm-no-wgl", feature = "sm-angle"))
&& (flags.contains(ContextAttributeFlags::COMPATIBILITY_PROFILE)
|| version == *GL_ES_VERSIONS.last().unwrap())
{
println!("{error}");
} else {
panic!("{error}");
}
}
}
}
}
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_newly_created_contexts_are_current() {
let connection = Connection::new().unwrap();
let adapter = connection
.create_low_power_adapter()
.expect("Failed to create adapter!");
let mut device = match connection.create_device(&adapter) {
Ok(device) => device,
Err(Error::RequiredExtensionUnavailable) => {
return;
}
Err(err) => panic!("Failed to create device: {:?}", err),
};
let context_descriptor = device
.create_context_descriptor(&ContextAttributes {
version: GLVersion::new(3, 0),
flags: ContextAttributeFlags::empty(),
})
.unwrap();
device.make_no_context_current().unwrap();
let mut context = device.create_context(&context_descriptor, None).unwrap();
let gl =
unsafe { Gl::from_loader_function(|symbol| device.get_proc_address(&context, symbol)) };
unsafe {
let surface = make_surface(&mut device, &context);
device
.bind_surface_to_context(&mut context, surface)
.unwrap();
device.make_context_current(&context).unwrap();
let framebuffer_object = device
.context_surface_info(&context)
.unwrap()
.unwrap()
.framebuffer_object;
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_object);
clear(&gl, &[0, 255, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&gl), [0, 255, 0, 255]);
let mut surface = device
.unbind_surface_from_context(&mut context)
.unwrap()
.unwrap();
device.make_no_context_current().unwrap();
clear(&gl, &[255, 0, 0, 255]);
assert_ne!(get_pixel_from_bottom_row(&gl), [255, 0, 0, 255]);
device.destroy_surface(&mut context, &mut surface).unwrap();
device.destroy_context(&mut context).unwrap();
}
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_context_sharing() {
let connection = Connection::new().unwrap();
let adapter = connection
.create_low_power_adapter()
.expect("Failed to create adapter!");
let device = match connection.create_device(&adapter) {
Ok(device) => device,
Err(Error::RequiredExtensionUnavailable) => {
return;
}
Err(err) => panic!("Failed to create device: {:?}", err),
};
let context_descriptor = device
.create_context_descriptor(&ContextAttributes {
version: GLVersion::new(3, 0),
flags: ContextAttributeFlags::empty(),
})
.unwrap();
let mut parent_context = device.create_context(&context_descriptor, None).unwrap();
let mut child1_context = device
.create_context(&context_descriptor, Some(&parent_context))
.unwrap();
let mut child2_context = device
.create_context(&context_descriptor, Some(&parent_context))
.unwrap();
let mut child3_context = device
.create_context(&context_descriptor, Some(&child1_context))
.unwrap();
device.destroy_context(&mut child3_context).unwrap();
device.destroy_context(&mut child2_context).unwrap();
device.destroy_context(&mut child1_context).unwrap();
device.destroy_context(&mut parent_context).unwrap();
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_generic_surface_creation() {
let connection = Connection::new().unwrap();
let adapter = connection
.create_low_power_adapter()
.expect("Failed to create adapter!");
let device = match connection.create_device(&adapter) {
Ok(device) => device,
Err(Error::RequiredExtensionUnavailable) => {
return;
}
Err(err) => panic!("Failed to create device: {:?}", err),
};
let descriptor = device
.create_context_descriptor(&ContextAttributes {
version: GLVersion::new(3, 0),
flags: ContextAttributeFlags::empty(),
})
.unwrap();
let mut context = device.create_context(&descriptor, None).unwrap();
let context_id = device.context_id(&context);
let surfaces: Vec<_> = [
SurfaceAccess::GPUOnly,
SurfaceAccess::GPUCPU,
SurfaceAccess::GPUCPUWriteCombined,
]
.iter()
.map(|&access| {
let surface = device
.create_surface(
&context,
access,
SurfaceType::Generic {
size: Size2D::new(640, 480),
},
)
.unwrap();
let info = device.surface_info(&surface);
assert_eq!(info.size, Size2D::new(640, 480));
assert_eq!(info.context_id, context_id);
surface
})
.collect();
for (surface_index, surface) in surfaces.iter().enumerate() {
for (other_surface_index, other_surface) in surfaces.iter().enumerate() {
if surface_index != other_surface_index {
assert_ne!(
device.surface_info(surface).id,
device.surface_info(other_surface).id
);
}
}
}
for mut surface in surfaces.into_iter() {
device.destroy_surface(&mut context, &mut surface).unwrap();
}
device.destroy_context(&mut context).unwrap();
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_gl() {
let mut env = match BasicEnvironment::new() {
None => return,
Some(env) => env,
};
unsafe {
env.gl.clear_color(0.0, 1.0, 0.0, 1.0);
env.gl.clear(gl::COLOR_BUFFER_BIT);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
env.device.make_no_context_current().unwrap();
env.gl.clear_color(1.0, 0.0, 0.0, 1.0);
env.gl.get_error();
env.gl.clear(gl::COLOR_BUFFER_BIT);
env.gl.get_error();
env.device.make_context_current(&env.context).unwrap();
let framebuffer_object = env
.device
.context_surface_info(&env.context)
.unwrap()
.unwrap()
.framebuffer_object;
env.gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_object);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
let green_surface = env
.device
.unbind_surface_from_context(&mut env.context)
.unwrap()
.unwrap();
env.gl.bind_framebuffer(gl::FRAMEBUFFER, None);
env.gl.clear_color(1.0, 0.0, 0.0, 1.0);
env.gl.get_error();
env.gl.clear(gl::COLOR_BUFFER_BIT);
env.gl.get_error();
env.device
.bind_surface_to_context(&mut env.context, green_surface)
.unwrap();
env.gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_object);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
let green_surface = env
.device
.unbind_surface_from_context(&mut env.context)
.unwrap()
.unwrap();
let red_surface = make_surface(&mut env.device, &env.context);
env.device
.bind_surface_to_context(&mut env.context, red_surface)
.unwrap();
let red_framebuffer_object = env
.device
.context_surface_info(&env.context)
.unwrap()
.unwrap()
.framebuffer_object;
env.gl
.bind_framebuffer(gl::FRAMEBUFFER, red_framebuffer_object);
env.gl.clear_color(1.0, 0.0, 0.0, 1.0);
env.gl.clear(gl::COLOR_BUFFER_BIT);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [255, 0, 0, 255]);
let mut red_surface = env
.device
.unbind_surface_from_context(&mut env.context)
.unwrap()
.unwrap();
env.device
.bind_surface_to_context(&mut env.context, green_surface)
.unwrap();
env.gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_object);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
env.device
.destroy_surface(&mut env.context, &mut red_surface)
.unwrap();
}
env.device.destroy_context(&mut env.context).unwrap();
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_surface_texture_blit_framebuffer() {
let mut env = match BasicEnvironment::new() {
None => return,
Some(env) => env,
};
unsafe {
clear(&env.gl, &[0, 255, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
let green_surface = env
.device
.unbind_surface_from_context(&mut env.context)
.unwrap()
.unwrap();
let green_surface_texture = env
.device
.create_surface_texture(&mut env.context, green_surface)
.unwrap();
let main_surface = make_surface(&mut env.device, &env.context);
env.device
.bind_surface_to_context(&mut env.context, main_surface)
.unwrap();
let main_framebuffer_object = env
.device
.context_surface_info(&env.context)
.unwrap()
.unwrap()
.framebuffer_object;
env.gl
.bind_framebuffer(gl::FRAMEBUFFER, main_framebuffer_object);
clear(&env.gl, &[255, 0, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [255, 0, 0, 255]);
let green_framebuffer_object = make_fbo(
&env.gl,
env.device.surface_gl_texture_target(),
env.device.surface_texture_object(&green_surface_texture),
);
blit_fbo(
&env.gl,
main_framebuffer_object,
Some(green_framebuffer_object),
);
env.gl
.bind_framebuffer(gl::FRAMEBUFFER, main_framebuffer_object);
check_gl(&env.gl);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
env.gl.bind_framebuffer(gl::FRAMEBUFFER, None);
check_gl(&env.gl);
env.gl.delete_framebuffer(green_framebuffer_object);
let mut green_surface = env
.device
.destroy_surface_texture(&mut env.context, green_surface_texture)
.unwrap();
env.device
.destroy_surface(&mut env.context, &mut green_surface)
.unwrap();
env.device.destroy_context(&mut env.context).unwrap();
}
}
#[ignore]
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_cross_device_surface_texture_blit_framebuffer() {
let mut env = match BasicEnvironment::new() {
None => return,
Some(env) => env,
};
let mut other_device = env.connection.create_device(&env.adapter).unwrap();
unsafe {
clear(&env.gl, &[255, 0, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [255, 0, 0, 255]);
let mut other_context = other_device
.create_context(&env.context_descriptor, None)
.unwrap();
let other_surface = make_surface(&mut other_device, &other_context);
other_device
.bind_surface_to_context(&mut other_context, other_surface)
.unwrap();
other_device.make_context_current(&other_context).unwrap();
bind_context_fbo(&env.gl, &other_device, &other_context);
clear(&env.gl, &[0, 255, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
let green_surface = other_device
.unbind_surface_from_context(&mut other_context)
.unwrap()
.unwrap();
let green_surface_texture = env
.device
.create_surface_texture(&mut env.context, green_surface)
.unwrap();
env.device.make_context_current(&env.context).unwrap();
assert_eq!(get_pixel_from_bottom_row(&env.gl), [255, 0, 0, 255]);
let green_framebuffer_object = make_fbo(
&env.gl,
env.device.surface_gl_texture_target(),
env.device.surface_texture_object(&green_surface_texture),
);
blit_fbo(
&env.gl,
context_fbo(&env.device, &env.context),
Some(green_framebuffer_object),
);
bind_context_fbo(&env.gl, &env.device, &env.context);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
env.gl.bind_framebuffer(gl::FRAMEBUFFER, None);
check_gl(&env.gl);
env.gl.delete_framebuffer(green_framebuffer_object);
let mut green_surface = env
.device
.destroy_surface_texture(&mut env.context, green_surface_texture)
.unwrap();
other_device
.destroy_surface(&mut other_context, &mut green_surface)
.unwrap();
other_device.destroy_context(&mut other_context).unwrap();
env.device.destroy_context(&mut env.context).unwrap();
}
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_cross_thread_surface_texture_blit_framebuffer() {
let mut env = match BasicEnvironment::new() {
None => return,
Some(env) => env,
};
let (to_main_sender, to_main_receiver) = mpsc::channel();
let (to_worker_sender, to_worker_receiver) = mpsc::channel();
let other_connection = env.connection.clone();
let other_adapter = env.adapter.clone();
let other_context_descriptor = env.context_descriptor.clone();
thread::spawn(move || {
let mut device = other_connection.create_device(&other_adapter).unwrap();
let mut context = device
.create_context(&other_context_descriptor, None)
.unwrap();
let surface = make_surface(&mut device, &context);
device
.bind_surface_to_context(&mut context, surface)
.unwrap();
device.make_context_current(&context).unwrap();
let gl =
unsafe { Gl::from_loader_function(|symbol| device.get_proc_address(&context, symbol)) };
bind_context_fbo(&gl, &device, &context);
clear(&gl, &[0, 255, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&gl), [0, 255, 0, 255]);
let surface = device
.unbind_surface_from_context(&mut context)
.unwrap()
.unwrap();
to_main_sender.send(surface).unwrap();
let mut surface = to_worker_receiver.recv().unwrap();
device.destroy_surface(&mut context, &mut surface).unwrap();
device.destroy_context(&mut context).unwrap();
});
unsafe {
clear(&env.gl, &[255, 0, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [255, 0, 0, 255]);
let green_surface = to_main_receiver.recv().unwrap();
let green_surface_texture = env
.device
.create_surface_texture(&mut env.context, green_surface)
.unwrap();
env.device.make_context_current(&env.context).unwrap();
assert_eq!(get_pixel_from_bottom_row(&env.gl), [255, 0, 0, 255]);
let green_framebuffer_object = make_fbo(
&env.gl,
env.device.surface_gl_texture_target(),
env.device.surface_texture_object(&green_surface_texture),
);
blit_fbo(
&env.gl,
context_fbo(&env.device, &env.context),
Some(green_framebuffer_object),
);
bind_context_fbo(&env.gl, &env.device, &env.context);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
env.gl.bind_framebuffer(gl::FRAMEBUFFER, None);
check_gl(&env.gl);
env.gl.delete_framebuffer(green_framebuffer_object);
let green_surface = env
.device
.destroy_surface_texture(&mut env.context, green_surface_texture)
.unwrap();
to_worker_sender.send(green_surface).unwrap();
env.device.destroy_context(&mut env.context).unwrap();
}
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_surface_texture_right_side_up() {
let mut env = match BasicEnvironment::new() {
None => return,
Some(env) => env,
};
unsafe {
clear(&env.gl, &[255, 0, 0, 255]);
clear_bottom_row(&env.gl, &[0, 255, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
let subsurface = env
.device
.unbind_surface_from_context(&mut env.context)
.unwrap()
.unwrap();
let subsurface_texture = env
.device
.create_surface_texture(&mut env.context, subsurface)
.unwrap();
let main_surface = make_surface(&mut env.device, &env.context);
env.device
.bind_surface_to_context(&mut env.context, main_surface)
.unwrap();
let main_framebuffer_object = env
.device
.context_surface_info(&env.context)
.unwrap()
.unwrap()
.framebuffer_object;
env.gl
.bind_framebuffer(gl::FRAMEBUFFER, main_framebuffer_object);
clear(&env.gl, &[255, 0, 0, 255]);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [255, 0, 0, 255]);
let subframebuffer_object = make_fbo(
&env.gl,
env.device.surface_gl_texture_target(),
env.device.surface_texture_object(&subsurface_texture),
);
blit_fbo(
&env.gl,
main_framebuffer_object,
Some(subframebuffer_object),
);
env.gl
.bind_framebuffer(gl::FRAMEBUFFER, main_framebuffer_object);
check_gl(&env.gl);
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
assert_eq!(
get_pixel_from_second_from_bottom_row(&env.gl),
[255, 0, 0, 255]
);
env.gl.bind_framebuffer(gl::FRAMEBUFFER, None);
check_gl(&env.gl);
env.gl.delete_framebuffer(subframebuffer_object);
let mut subsurface = env
.device
.destroy_surface_texture(&mut env.context, subsurface_texture)
.unwrap();
env.device
.destroy_surface(&mut env.context, &mut subsurface)
.unwrap();
env.device.destroy_context(&mut env.context).unwrap();
}
}
#[cfg(not(any(target_os = "android", target_env = "ohos")))]
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_depth_and_stencil() {
use core::slice;
use std::ptr::addr_of_mut;
use glow::PixelPackData;
let connection = Connection::new().unwrap();
let adapter = connection
.create_low_power_adapter()
.expect("Failed to create adapter!");
let mut device = match connection.create_device(&adapter) {
Ok(device) => device,
Err(Error::RequiredExtensionUnavailable) => {
return;
}
Err(err) => panic!("Failed to create device: {:?}", err),
};
if device.gl_api() == GLApi::GLES {
return;
}
let depth_context_descriptor = device
.create_context_descriptor(&ContextAttributes {
version: GLVersion::new(3, 0),
flags: ContextAttributeFlags::DEPTH,
})
.unwrap();
let mut depth_context = device
.create_context(&depth_context_descriptor, None)
.unwrap();
let depth_surface = make_surface(&mut device, &depth_context);
device
.bind_surface_to_context(&mut depth_context, depth_surface)
.unwrap();
device.make_context_current(&depth_context).unwrap();
let gl = unsafe {
Gl::from_loader_function(|symbol| device.get_proc_address(&depth_context, symbol))
};
unsafe {
let framebuffer_object = device
.context_surface_info(&depth_context)
.unwrap()
.unwrap()
.framebuffer_object;
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_object);
gl.viewport(0, 0, 640, 480);
gl.clear_depth(0.5);
gl.clear(gl::DEPTH_BUFFER_BIT);
let mut depth_value: f32 = -1.0;
gl.read_pixels(
0,
0,
1,
1,
gl::DEPTH_COMPONENT,
gl::FLOAT,
PixelPackData::Slice(Some(slice::from_raw_parts_mut(
addr_of_mut!(depth_value) as *mut u8,
size_of_val(&depth_value),
))),
);
assert!(
approx_eq(depth_value, 0.5),
"actual depth value was {}, but expected 0.5",
depth_value
);
}
device.destroy_context(&mut depth_context).unwrap();
let stencil_context_descriptor = device
.create_context_descriptor(&ContextAttributes {
version: GLVersion::new(3, 0),
flags: ContextAttributeFlags::STENCIL,
})
.unwrap();
let mut stencil_context = device
.create_context(&stencil_context_descriptor, None)
.unwrap();
let stencil_surface = make_surface(&mut device, &stencil_context);
device
.bind_surface_to_context(&mut stencil_context, stencil_surface)
.unwrap();
device.make_context_current(&stencil_context).unwrap();
let gl = unsafe {
Gl::from_loader_function(|symbol| device.get_proc_address(&stencil_context, symbol))
};
unsafe {
let framebuffer_object = device
.context_surface_info(&stencil_context)
.unwrap()
.unwrap()
.framebuffer_object;
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_object);
gl.viewport(0, 0, 640, 480);
gl.clear_stencil(99);
gl.clear(gl::STENCIL_BUFFER_BIT);
let mut stencil_value: u8 = 200;
gl.read_pixels(
0,
0,
1,
1,
gl::STENCIL_INDEX,
gl::UNSIGNED_BYTE,
PixelPackData::Slice(Some(slice::from_raw_parts_mut(&mut stencil_value, 1))),
);
assert_eq!(stencil_value, 99);
}
device.destroy_context(&mut stencil_context).unwrap();
}
#[cfg_attr(not(feature = "sm-test"), test)]
#[cfg_attr(not(feature = "sm-test"), serial)]
pub fn test_get_native_context() {
let mut env = match BasicEnvironment::new() {
None => return,
Some(env) => env,
};
let native_context = NativeContext::current().unwrap();
unsafe {
clear(&env.gl, &[0, 255, 0, 255]);
let mut other_context = env
.device
.create_context_from_native_context(native_context)
.unwrap();
env.device.make_context_current(&other_context).unwrap();
assert_eq!(get_pixel_from_bottom_row(&env.gl), [0, 255, 0, 255]);
env.device.destroy_context(&mut other_context).unwrap();
}
env.device.destroy_context(&mut env.context).unwrap();
}
fn bind_context_fbo(gl: &Gl, device: &Device, context: &Context) {
unsafe {
gl.bind_framebuffer(gl::FRAMEBUFFER, context_fbo(device, context));
check_gl(gl);
}
}
fn context_fbo(device: &Device, context: &Context) -> Option<Framebuffer> {
device
.context_surface_info(context)
.unwrap()
.unwrap()
.framebuffer_object
}
fn make_surface(device: &mut Device, context: &Context) -> Surface {
device
.create_surface(
context,
SurfaceAccess::GPUOnly,
SurfaceType::Generic {
size: Size2D::new(640, 480),
},
)
.unwrap()
}
fn blit_fbo(gl: &Gl, dest_fbo: Option<Framebuffer>, src_fbo: Option<Framebuffer>) {
unsafe {
gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, dest_fbo);
check_gl(gl);
gl.bind_framebuffer(gl::READ_FRAMEBUFFER, src_fbo);
check_gl(gl);
gl.blit_framebuffer(
0,
0,
640,
480,
0,
0,
640,
480,
gl::COLOR_BUFFER_BIT,
gl::NEAREST,
);
check_gl(gl);
}
}
fn make_fbo(gl: &Gl, texture_target: u32, texture: Option<Texture>) -> Framebuffer {
unsafe {
let framebuffer_object = gl.create_framebuffer().unwrap();
check_gl(gl);
gl.bind_framebuffer(gl::FRAMEBUFFER, Some(framebuffer_object));
check_gl(gl);
gl.framebuffer_texture_2d(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
texture_target,
texture,
0,
);
check_gl(gl);
assert_eq!(
gl.check_framebuffer_status(gl::FRAMEBUFFER),
gl::FRAMEBUFFER_COMPLETE
);
framebuffer_object
}
}
struct BasicEnvironment {
connection: Connection,
adapter: Adapter,
device: Device,
context_descriptor: ContextDescriptor,
context: Context,
gl: Gl,
}
impl BasicEnvironment {
fn new() -> Option<BasicEnvironment> {
let connection = Connection::new().unwrap();
let adapter = connection
.create_low_power_adapter()
.expect("Failed to create adapter!");
let mut device = match connection.create_device(&adapter) {
Ok(device) => device,
Err(Error::RequiredExtensionUnavailable) => {
return None;
}
Err(err) => panic!("Failed to create device: {:?}", err),
};
let context_descriptor = device
.create_context_descriptor(&ContextAttributes {
version: GLVersion::new(3, 0),
flags: ContextAttributeFlags::empty(),
})
.unwrap();
let mut context = device.create_context(&context_descriptor, None).unwrap();
let surface = make_surface(&mut device, &context);
device
.bind_surface_to_context(&mut context, surface)
.unwrap();
device.make_context_current(&context).unwrap();
let gl =
unsafe { Gl::from_loader_function(|symbol| device.get_proc_address(&context, symbol)) };
unsafe {
let framebuffer_object = device
.context_surface_info(&context)
.unwrap()
.unwrap()
.framebuffer_object;
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_object);
gl.viewport(0, 0, 640, 480);
}
Some(BasicEnvironment {
connection,
adapter,
device,
context_descriptor,
context,
gl,
})
}
}
fn clear(gl: &Gl, color: &[u8; 4]) {
unsafe {
gl.clear_color(
color[0] as f32 / 255.0,
color[1] as f32 / 255.0,
color[2] as f32 / 255.0,
color[3] as f32 / 255.0,
);
gl.clear(gl::COLOR_BUFFER_BIT);
}
}
fn clear_bottom_row(gl: &Gl, color: &[u8; 4]) {
unsafe {
gl.scissor(0, 0, 640, 1);
gl.enable(gl::SCISSOR_TEST);
gl.clear_color(
color[0] as f32 / 255.0,
color[1] as f32 / 255.0,
color[2] as f32 / 255.0,
color[3] as f32 / 255.0,
);
gl.clear(gl::COLOR_BUFFER_BIT);
gl.disable(gl::SCISSOR_TEST);
gl.scissor(0, 0, 640, 480);
}
}
fn get_pixel_from_bottom_row(gl: &Gl) -> [u8; 4] {
unsafe {
let mut pixel: [u8; 4] = [0; 4];
gl.read_pixels(
0,
0,
1,
1,
gl::RGBA,
gl::UNSIGNED_BYTE,
PixelPackData::Slice(Some(&mut pixel)),
);
pixel
}
}
fn get_pixel_from_second_from_bottom_row(gl: &Gl) -> [u8; 4] {
unsafe {
let mut pixel: [u8; 4] = [0; 4];
gl.read_pixels(
0,
1,
1,
1,
gl::RGBA,
gl::UNSIGNED_BYTE,
PixelPackData::Slice(Some(&mut pixel)),
);
pixel
}
}
fn check_gl(gl: &Gl) {
unsafe {
assert_eq!(gl.get_error(), gl::NO_ERROR);
}
}
fn approx_eq(a: f32, b: f32) -> bool {
f32::abs(a - b) < 0.001
}