#![allow(missing_docs, missing_copy_implementations)]
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
extern crate gfx_gl as gl;
extern crate gfx_hal as hal;
#[cfg(feature = "glutin")]
pub extern crate glutin;
extern crate smallvec;
extern crate spirv_cross;
use std::cell::Cell;
use std::fmt;
use std::ops::Deref;
use std::sync::{Arc, Weak};
use std::thread::{self, ThreadId};
use crate::hal::queue::{QueueFamilyId, Queues};
use crate::hal::{error, image, pso};
pub use self::device::Device;
pub use self::info::{Info, PlatformName, Version};
mod command;
mod conv;
mod device;
mod info;
mod native;
mod pool;
mod queue;
mod state;
mod window;
#[cfg(feature = "glutin")]
pub use crate::window::glutin::{config_context, Headless, Surface, Swapchain};
pub(crate) struct GlContainer {
context: gl::Gl,
}
impl GlContainer {
fn make_current(&self) {
}
}
impl Deref for GlContainer {
type Target = gl::Gl;
fn deref(&self) -> &gl::Gl {
#[cfg(feature = "glutin")]
self.make_current();
&self.context
}
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Backend {}
impl hal::Backend for Backend {
type PhysicalDevice = PhysicalDevice;
type Device = Device;
type Surface = Surface;
type Swapchain = Swapchain;
type QueueFamily = QueueFamily;
type CommandQueue = queue::CommandQueue;
type CommandBuffer = command::RawCommandBuffer;
type Memory = native::Memory;
type CommandPool = pool::RawCommandPool;
type ShaderModule = native::ShaderModule;
type RenderPass = native::RenderPass;
type Framebuffer = native::FrameBuffer;
type Buffer = native::Buffer;
type BufferView = native::BufferView;
type Image = native::Image;
type ImageView = native::ImageView;
type Sampler = native::FatSampler;
type ComputePipeline = native::ComputePipeline;
type GraphicsPipeline = native::GraphicsPipeline;
type PipelineLayout = native::PipelineLayout;
type PipelineCache = ();
type DescriptorSetLayout = native::DescriptorSetLayout;
type DescriptorPool = native::DescriptorPool;
type DescriptorSet = native::DescriptorSet;
type Fence = native::Fence;
type Semaphore = native::Semaphore;
type QueryPool = ();
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Error {
NoError,
InvalidEnum,
InvalidValue,
InvalidOperation,
InvalidFramebufferOperation,
OutOfMemory,
UnknownError,
}
impl Error {
pub fn from_error_code(error_code: gl::types::GLenum) -> Error {
match error_code {
gl::NO_ERROR => Error::NoError,
gl::INVALID_ENUM => Error::InvalidEnum,
gl::INVALID_VALUE => Error::InvalidValue,
gl::INVALID_OPERATION => Error::InvalidOperation,
gl::INVALID_FRAMEBUFFER_OPERATION => Error::InvalidFramebufferOperation,
gl::OUT_OF_MEMORY => Error::OutOfMemory,
_ => Error::UnknownError,
}
}
}
struct Share {
context: GlContainer,
info: Info,
features: hal::Features,
legacy_features: info::LegacyFeatures,
limits: hal::Limits,
private_caps: info::PrivateCaps,
open: Cell<bool>,
}
impl Share {
fn check(&self) -> Result<(), Error> {
if cfg!(debug_assertions) {
let gl = &self.context;
let err = Error::from_error_code(unsafe { gl.GetError() });
if err != Error::NoError {
return Err(err);
}
}
Ok(())
}
}
pub struct Starc<T: ?Sized> {
arc: Arc<T>,
thread: ThreadId,
}
impl<T: ?Sized> Clone for Starc<T> {
fn clone(&self) -> Self {
Self {
arc: self.arc.clone(),
thread: self.thread,
}
}
}
impl<T: ?Sized> fmt::Debug for Starc<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{:p}@{:?}", self.arc, self.thread)
}
}
impl<T> Starc<T> {
#[inline]
fn new(value: T) -> Self {
Starc {
arc: Arc::new(value),
thread: thread::current().id(),
}
}
#[inline]
pub fn try_unwrap(self) -> Result<T, Self> {
let a = Arc::try_unwrap(self.arc);
let thread = self.thread;
a.map_err(|a| Starc {
arc: a,
thread: thread,
})
}
#[inline]
pub fn downgrade(this: &Starc<T>) -> Wstarc<T> {
Wstarc {
weak: Arc::downgrade(&this.arc),
thread: this.thread,
}
}
#[inline]
pub fn get_mut(this: &mut Starc<T>) -> Option<&mut T> {
Arc::get_mut(&mut this.arc)
}
}
unsafe impl<T: ?Sized> Send for Starc<T> {}
unsafe impl<T: ?Sized> Sync for Starc<T> {}
impl<T: ?Sized> Deref for Starc<T> {
type Target = T;
fn deref(&self) -> &T {
assert_eq!(thread::current().id(), self.thread);
&*self.arc
}
}
pub struct Wstarc<T: ?Sized> {
weak: Weak<T>,
thread: ThreadId,
}
impl<T> Wstarc<T> {
pub fn upgrade(&self) -> Option<Starc<T>> {
let thread = self.thread;
self.weak.upgrade().map(|arc| Starc { arc, thread })
}
}
unsafe impl<T: ?Sized> Send for Wstarc<T> {}
unsafe impl<T: ?Sized> Sync for Wstarc<T> {}
#[derive(Debug)]
pub struct PhysicalDevice(Starc<Share>);
impl PhysicalDevice {
fn new_adapter<F>(fn_proc: F) -> hal::Adapter<Backend>
where
F: FnMut(&str) -> *const std::os::raw::c_void,
{
let gl = GlContainer {
context: gl::Gl::load_with(fn_proc),
};
let (info, features, legacy_features, limits, private_caps) = info::query_all(&gl);
info!("Vendor: {:?}", info.platform_name.vendor);
info!("Renderer: {:?}", info.platform_name.renderer);
info!("Version: {:?}", info.version);
info!("Shading Language: {:?}", info.shading_language);
info!("Features: {:?}", features);
info!("Legacy Features: {:?}", legacy_features);
debug!("Loaded Extensions:");
for extension in info.extensions.iter() {
debug!("- {}", *extension);
}
let name = info.platform_name.renderer.into();
let vendor: std::string::String = info.platform_name.vendor.into();
let renderer: std::string::String = info.platform_name.renderer.into();
let share = Share {
context: gl,
info,
features,
legacy_features,
limits,
private_caps,
open: Cell::new(false),
};
if let Err(err) = share.check() {
panic!("Error querying info: {:?}", err);
}
let vendor_lower = vendor.to_lowercase();
let renderer_lower = renderer.to_lowercase();
let strings_that_imply_integrated = [
" xpress",
"radeon hd 4200",
"radeon hd 4250",
"radeon hd 4290",
"radeon hd 4270",
"radeon hd 4225",
"radeon hd 3100",
"radeon hd 3200",
"radeon hd 3000",
"radeon hd 3300",
"radeon(tm) r4 graphics",
"radeon(tm) r5 graphics",
"radeon(tm) r6 graphics",
"radeon(tm) r7 graphics",
"radeon r7 graphics",
"nforce",
"tegra",
"shield",
"igp",
"mali",
"intel",
];
let inferred_device_type = if vendor_lower.contains("qualcomm")
|| vendor_lower.contains("intel")
|| strings_that_imply_integrated
.into_iter()
.any(|&s| renderer_lower.contains(s))
{
hal::adapter::DeviceType::IntegratedGpu
} else {
hal::adapter::DeviceType::DiscreteGpu
};
let vendor_id = if vendor_lower.contains("amd") {
0x1002
} else if vendor_lower.contains("imgtec") {
0x1010
} else if vendor_lower.contains("nvidia") {
0x10DE
} else if vendor_lower.contains("arm") {
0x13B5
} else if vendor_lower.contains("qualcomm") {
0x5143
} else if vendor_lower.contains("intel") {
0x8086
} else {
0
};
hal::Adapter {
info: hal::AdapterInfo {
name,
vendor: vendor_id,
device: 0,
device_type: inferred_device_type,
},
physical_device: PhysicalDevice(Starc::new(share)),
queue_families: vec![QueueFamily],
}
}
pub fn legacy_features(&self) -> &info::LegacyFeatures {
&self.0.legacy_features
}
}
impl hal::PhysicalDevice<Backend> for PhysicalDevice {
unsafe fn open(
&self,
families: &[(&QueueFamily, &[hal::QueuePriority])],
requested_features: hal::Features,
) -> Result<hal::Gpu<Backend>, error::DeviceCreationError> {
if self.0.open.get() {
return Err(error::DeviceCreationError::TooManyObjects);
}
self.0.open.set(true);
if !self.features().contains(requested_features) {
return Err(error::DeviceCreationError::MissingFeature);
}
let gl = &self.0.context;
if self
.0
.legacy_features
.contains(info::LegacyFeatures::SRGB_COLOR)
{
gl.Enable(gl::FRAMEBUFFER_SRGB);
}
gl.PixelStorei(gl::UNPACK_ALIGNMENT, 1);
let mut vao = 0;
if self.0.private_caps.vertex_array {
gl.GenVertexArrays(1, &mut vao);
gl.BindVertexArray(vao);
}
if let Err(err) = self.0.check() {
panic!("Error opening adapter: {:?}", err);
}
Ok(hal::Gpu {
device: Device::new(self.0.clone()),
queues: Queues::new(
families
.into_iter()
.map(|&(proto_family, priorities)| {
assert_eq!(priorities.len(), 1);
let mut family = hal::backend::RawQueueGroup::new(proto_family.clone());
let queue = queue::CommandQueue::new(&self.0, vao);
family.add_queue(queue);
family
})
.collect(),
),
})
}
fn format_properties(&self, _: Option<hal::format::Format>) -> hal::format::Properties {
unimplemented!()
}
fn image_format_properties(
&self,
_format: hal::format::Format,
_dimensions: u8,
_tiling: image::Tiling,
_usage: image::Usage,
_view_caps: image::ViewCapabilities,
) -> Option<image::FormatProperties> {
None
}
fn memory_properties(&self) -> hal::MemoryProperties {
use crate::hal::memory::Properties;
let memory_types = if self.0.private_caps.map {
vec![
hal::MemoryType {
properties: Properties::DEVICE_LOCAL,
heap_index: 1,
},
hal::MemoryType {
properties: Properties::CPU_VISIBLE | Properties::COHERENT,
heap_index: 0,
},
hal::MemoryType {
properties: Properties::CPU_VISIBLE
| Properties::COHERENT
| Properties::CPU_CACHED,
heap_index: 0,
},
]
} else {
vec![hal::MemoryType {
properties: Properties::DEVICE_LOCAL,
heap_index: 0,
}]
};
hal::MemoryProperties {
memory_types,
memory_heaps: vec![!0, !0],
}
}
fn features(&self) -> hal::Features {
self.0.features
}
fn limits(&self) -> hal::Limits {
self.0.limits
}
}
#[derive(Debug, Clone, Copy)]
pub struct QueueFamily;
impl hal::QueueFamily for QueueFamily {
fn queue_type(&self) -> hal::QueueType {
hal::QueueType::General
}
fn max_queues(&self) -> usize {
1
}
fn id(&self) -> QueueFamilyId {
QueueFamilyId(0)
}
}