Add WGL context creation and OpenGL support (#4248)

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
Zoxc 2023-10-18 20:40:33 +02:00 committed by GitHub
parent ead6348b43
commit 2b985e2fad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1140 additions and 153 deletions

View File

@ -44,6 +44,12 @@ Bottom level categories:
- Update Naga to 9eb3a1dc (2023-10-12), which includes support for WGSL constant expressions. By @jimblandy in [#4233](https://github.com/gfx-rs/wgpu/pull/4233)
#### Support desktop OpenGL via WGL on Windows
Added creating of full OpenGL contexts to the GLES backend using WGL to support older devices.
By @Zoxc in [#4248](https://github.com/gfx-rs/wgpu/pull/4248)
#### Pass timestamp queries
Addition of `TimestampWrites` to compute and render passes to allow profiling.

13
Cargo.lock generated
View File

@ -1233,7 +1233,7 @@ dependencies = [
"glutin_egl_sys",
"glutin_gles2_sys",
"glutin_glx_sys",
"glutin_wgl_sys",
"glutin_wgl_sys 0.1.5",
"libloading 0.7.4",
"log",
"objc",
@ -1286,6 +1286,15 @@ dependencies = [
"gl_generator",
]
[[package]]
name = "glutin_wgl_sys"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef89398e90033fc6bc65e9bd42fd29bbbfd483bda5b56dc5562f455550618165"
dependencies = [
"gl_generator",
]
[[package]]
name = "gpu-alloc"
version = "0.6.0"
@ -3353,6 +3362,7 @@ dependencies = [
"env_logger",
"glow",
"glutin",
"glutin_wgl_sys 0.4.0",
"gpu-alloc",
"gpu-allocator",
"gpu-descriptor",
@ -3365,6 +3375,7 @@ dependencies = [
"metal",
"naga",
"objc",
"once_cell",
"parking_lot",
"profiling",
"range-alloc",

View File

@ -70,14 +70,14 @@ We have a [wiki](https://github.com/gfx-rs/wgpu/wiki) that serves as a knowledge
## Supported Platforms
| API | Windows | Linux & Android | macOS & iOS | Web (wasm) |
| --------- | ------------------------------ | ------------------ | ------------------------- | ------------------------- |
| Vulkan | :white_check_mark: | :white_check_mark: | :ok: (vulkan-portability) | |
| Metal | | | :white_check_mark: | |
| DX12 | :white_check_mark: (W10+ only) | | | |
| DX11 | :hammer_and_wrench: | | | |
| GLES3 | :ok: (angle) | :ok: | :ok: (angle; macOS only) | :ok: (WebGL2 Only) |
| WebGPU | | | | :white_check_mark: |
| API | Windows | Linux & Android | macOS & iOS | Web (wasm) |
| ----------- | ------------------------------ | ------------------ | ------------------------- | ------------------------- |
| Vulkan | :white_check_mark: | :white_check_mark: | :ok: (vulkan-portability) | |
| Metal | | | :white_check_mark: | |
| DX12 | :white_check_mark: (W10+ only) | | | |
| DX11 | :hammer_and_wrench: | | | |
| OpenGL | :ok: (Desktop GL 3.3+) | :ok: (GL ES 3.0+) | :ok: (angle; GL ES 3.0+) | :ok: (WebGL2) |
| WebGPU | | | | :white_check_mark: |
:white_check_mark: = First Class Support — :ok: = Best Effort Support — :hammer_and_wrench: = Unsupported, but support in progress
@ -148,6 +148,7 @@ We have multiple methods of testing, each of which tests different qualities abo
| DX11/Windows 10 | :construction: | — | using WARP |
| Metal/MacOS | :heavy_check_mark: | — | using hardware runner |
| Vulkan/Linux | :heavy_check_mark: | - | using swiftshader |
| GL/Windows | | — | |
| GLES/Linux | :heavy_check_mark: | — | using llvmpipe |
| WebGL/Chrome | :heavy_check_mark: | — | using swiftshader |

View File

@ -0,0 +1,33 @@
#![cfg(not(target_arch = "wasm32"))]
async fn get() -> wgpu::Adapter {
let adapter = {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all),
..Default::default()
});
instance
.request_adapter(&wgpu::RequestAdapterOptions::default())
.await
.unwrap()
};
log::info!("Selected adapter: {:?}", adapter.get_info());
adapter
}
#[test]
fn multi_instance() {
{
env_logger::init();
// Sequential instances.
for _ in 0..3 {
pollster::block_on(get());
}
// Concurrent instances
let _instances: Vec<_> = (0..3).map(|_| pollster::block_on(get())).collect();
}
}

View File

@ -36,7 +36,7 @@ targets = [
default = ["link"]
metal = ["naga/msl-out", "block"]
vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "smallvec"]
gles = ["naga/glsl-out", "glow", "khronos-egl", "libloading"]
gles = ["naga/glsl-out", "glow", "glutin_wgl_sys", "khronos-egl", "libloading"]
dx11 = ["naga/hlsl-out", "d3d12", "libloading", "winapi/d3d11", "winapi/std", "winapi/d3d11_1", "winapi/d3d11_2", "winapi/d3d11sdklayers", "winapi/dxgi1_6"]
dx12 = ["naga/hlsl-out", "d3d12", "bit-set", "libloading", "range-alloc", "winapi/std", "winapi/winbase", "winapi/d3d12", "winapi/d3d12shader", "winapi/d3d12sdklayers", "winapi/dxgi1_6"]
# TODO: This is a separate feature until Mozilla okays windows-rs, see https://github.com/gfx-rs/wgpu/issues/3207 for the tracking issue.
@ -59,6 +59,7 @@ parking_lot = ">=0.11,<0.13"
profiling = { version = "1", default-features = false }
raw-window-handle = "0.5"
thiserror = "1"
once_cell = "1.18.0"
# backends common
arrayvec = "0.7"
@ -95,6 +96,8 @@ bit-set = { version = "0.5", optional = true }
range-alloc = { version = "0.1", optional = true }
gpu-allocator = { version = "0.23", default_features = false, features = ["d3d12", "public-winapi"], optional = true }
hassle-rs = { version = "0.10", optional = true }
# backend: Gles
glutin_wgl_sys = { version = "0.4", optional = true }
winapi = { version = "0.3", features = ["profileapi", "libloaderapi", "windef", "winuser", "dcomp"] }
d3d12 = { version = "0.7", features = ["libloading"], optional = true }

View File

@ -10,7 +10,7 @@
extern crate wgpu_hal as hal;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(any(windows, target_arch = "wasm32")))]
fn main() {
env_logger::init();
println!("Initializing external GL context");
@ -116,10 +116,10 @@ fn main() {
fill_screen(&exposed, 640, 400);
}
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
#[cfg(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten"))))]
fn main() {}
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
#[cfg(any(not(any(windows, target_arch = "wasm32")), target_os = "emscripten"))]
fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height: u32) {
use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};

View File

@ -10,18 +10,6 @@ const GL_UNMASKED_VENDOR_WEBGL: u32 = 0x9245;
const GL_UNMASKED_RENDERER_WEBGL: u32 = 0x9246;
impl super::Adapter {
/// According to the OpenGL specification, the version information is
/// expected to follow the following syntax:
///
/// ~~~bnf
/// <major> ::= <number>
/// <minor> ::= <number>
/// <revision> ::= <number>
/// <vendor-info> ::= <string>
/// <release> ::= <major> "." <minor> ["." <release>]
/// <version> ::= <release> [" " <vendor-info>]
/// ~~~
///
/// Note that this function is intentionally lenient in regards to parsing,
/// and will try to recover at least the first two version numbers without
/// resulting in an `Err`.
@ -59,6 +47,35 @@ impl super::Adapter {
None => false,
};
Self::parse_full_version(src).map(|(major, minor)| {
(
// Return WebGL 2.0 version as OpenGL ES 3.0
if is_webgl && !is_glsl {
major + 1
} else {
major
},
minor,
)
})
}
/// According to the OpenGL specification, the version information is
/// expected to follow the following syntax:
///
/// ~~~bnf
/// <major> ::= <number>
/// <minor> ::= <number>
/// <revision> ::= <number>
/// <vendor-info> ::= <string>
/// <release> ::= <major> "." <minor> ["." <release>]
/// <version> ::= <release> [" " <vendor-info>]
/// ~~~
///
/// Note that this function is intentionally lenient in regards to parsing,
/// and will try to recover at least the first two version numbers without
/// resulting in an `Err`.
pub(super) fn parse_full_version(src: &str) -> Result<(u8, u8), crate::InstanceError> {
let (version, _vendor_info) = match src.find(' ') {
Some(i) => (&src[..i], src[i + 1..].to_string()),
None => (src, String::new()),
@ -78,15 +95,7 @@ impl super::Adapter {
});
match (major, minor) {
(Some(major), Some(minor)) => Ok((
// Return WebGL 2.0 version as OpenGL ES 3.0
if is_webgl && !is_glsl {
major + 1
} else {
major
},
minor,
)),
(Some(major), Some(minor)) => Ok((major, minor)),
_ => Err(crate::InstanceError::new(format!(
"unable to extract OpenGL version from {version:?}"
))),
@ -212,29 +221,75 @@ impl super::Adapter {
log::info!("Renderer: {}", renderer);
log::info!("Version: {}", version);
log::debug!("Extensions: {:#?}", extensions);
let full_ver = Self::parse_full_version(&version).ok();
let es_ver = full_ver
.is_none()
.then_some(())
.and_then(|_| Self::parse_version(&version).ok());
let ver = Self::parse_version(&version).ok()?;
if ver < (3, 0) {
log::warn!(
"Returned GLES context is {}.{}, when 3.0+ was requested",
ver.0,
ver.1
);
if es_ver.is_none() && full_ver.is_none() {
log::warn!("Unable to parse OpenGL version");
return None;
}
let supports_storage = ver >= (3, 1);
let supports_work_group_params = ver >= (3, 1);
if let Some(es_ver) = es_ver {
if es_ver < (3, 0) {
log::warn!(
"Returned GLES context is {}.{}, when 3.0+ was requested",
es_ver.0,
es_ver.1
);
return None;
}
}
if let Some(full_ver) = full_ver {
if full_ver < (3, 3) {
log::warn!(
"Returned GL context is {}.{}, when 3.3+ is needed",
full_ver.0,
full_ver.1
);
return None;
}
}
let supported = |(req_es_major, req_es_minor), (req_full_major, req_full_minor)| {
let es_supported = es_ver
.map(|es_ver| es_ver >= (req_es_major, req_es_minor))
.unwrap_or_default();
let full_supported = full_ver
.map(|full_ver| full_ver >= (req_full_major, req_full_minor))
.unwrap_or_default();
es_supported || full_supported
};
let supports_storage =
supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_shader_storage_buffer_object");
let supports_compute =
supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_compute_shader");
let supports_work_group_params = supports_compute;
let shading_language_version = {
let sl_version = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };
log::info!("SL version: {}", &sl_version);
let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
naga::back::glsl::Version::Embedded {
version: value,
is_webgl: cfg!(target_arch = "wasm32"),
if full_ver.is_some() {
let (sl_major, sl_minor) = Self::parse_full_version(&sl_version).ok()?;
let mut value = sl_major as u16 * 100 + sl_minor as u16 * 10;
// Naga doesn't think it supports GL 460+, so we cap it at 450
if value > 450 {
value = 450;
}
naga::back::glsl::Version::Desktop(value)
} else {
let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
naga::back::glsl::Version::Embedded {
version: value,
is_webgl: cfg!(target_arch = "wasm32"),
}
}
};
@ -242,7 +297,19 @@ impl super::Adapter {
let is_angle = renderer.contains("ANGLE");
let vertex_shader_storage_blocks = if supports_storage {
(unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32)
let value =
(unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) } as u32);
if value == 0 && extensions.contains("GL_ARB_shader_storage_buffer_object") {
// The driver for AMD Radeon HD 5870 returns zero here, so assume the value matches the compute shader storage block count.
// Windows doesn't recognize `GL_MAX_VERTEX_ATTRIB_STRIDE`.
let new = (unsafe { gl.get_parameter_i32(glow::MAX_COMPUTE_SHADER_STORAGE_BLOCKS) }
as u32);
log::warn!("Max vertex shader storage blocks is zero, but GL_ARB_shader_storage_buffer_object is specified. Assuming the compute value {new}");
new
} else {
value
}
} else {
0
};
@ -295,18 +362,21 @@ impl super::Adapter {
| wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
| wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES
| wgt::DownlevelFlags::COMPARISON_SAMPLERS;
downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, ver >= (3, 1));
downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
downlevel_flags.set(
wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
max_storage_block_size != 0,
);
downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, ver >= (3, 1));
downlevel_flags.set(
wgt::DownlevelFlags::INDIRECT_EXECUTION,
supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_multi_draw_indirect"),
);
//TODO: we can actually support positive `base_vertex` in the same way
// as we emulate the `start_instance`. But we can't deal with negatives...
downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, ver >= (3, 2));
downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
downlevel_flags.set(
wgt::DownlevelFlags::INDEPENDENT_BLEND,
ver >= (3, 2) || extensions.contains("GL_EXT_draw_buffers_indexed"),
supported((3, 2), (4, 0)) || extensions.contains("GL_EXT_draw_buffers_indexed"),
);
downlevel_flags.set(
wgt::DownlevelFlags::VERTEX_STORAGE,
@ -339,7 +409,7 @@ impl super::Adapter {
);
downlevel_flags.set(
wgt::DownlevelFlags::MULTISAMPLED_SHADING,
ver >= (3, 2) || extensions.contains("OES_sample_variables"),
supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
);
let mut features = wgt::Features::empty()
@ -369,9 +439,14 @@ impl super::Adapter {
);
features.set(
wgt::Features::SHADER_PRIMITIVE_INDEX,
ver >= (3, 2) || extensions.contains("OES_geometry_shader"),
supported((3, 2), (3, 2))
|| extensions.contains("OES_geometry_shader")
|| extensions.contains("GL_ARB_geometry_shader4"),
);
features.set(
wgt::Features::SHADER_EARLY_DEPTH_TEST,
supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
);
features.set(wgt::Features::SHADER_EARLY_DEPTH_TEST, ver >= (3, 1));
features.set(wgt::Features::SHADER_UNUSED_VERTEX_OUTPUT, true);
let gles_bcn_exts = [
"GL_EXT_texture_compression_s3tc_srgb",
@ -443,16 +518,19 @@ impl super::Adapter {
);
private_caps.set(
super::PrivateCapabilities::SHADER_BINDING_LAYOUT,
ver >= (3, 1),
supports_compute,
);
private_caps.set(
super::PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD,
extensions.contains("GL_EXT_texture_shadow_lod"),
);
private_caps.set(super::PrivateCapabilities::MEMORY_BARRIERS, ver >= (3, 1));
private_caps.set(
super::PrivateCapabilities::MEMORY_BARRIERS,
supported((3, 1), (4, 2)),
);
private_caps.set(
super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
ver >= (3, 1),
supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_vertex_attrib_binding"),
);
private_caps.set(
super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
@ -483,7 +561,7 @@ impl super::Adapter {
let min_uniform_buffer_offset_alignment =
(unsafe { gl.get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT) } as u32);
let min_storage_buffer_offset_alignment = if ver >= (3, 1) {
let min_storage_buffer_offset_alignment = if supports_storage {
(unsafe { gl.get_parameter_i32(glow::SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT) } as u32)
} else {
256
@ -521,7 +599,7 @@ impl super::Adapter {
max_uniform_buffer_binding_size: unsafe {
gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
} as u32,
max_storage_buffer_binding_size: if ver >= (3, 1) {
max_storage_buffer_binding_size: if supports_storage {
unsafe { gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) }
} else {
0
@ -539,7 +617,29 @@ impl super::Adapter {
max_vertex_buffer_array_stride: if private_caps
.contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
{
(unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) } as u32)
if let Some(full_ver) = full_ver {
if full_ver >= (4, 4) {
// We can query `GL_MAX_VERTEX_ATTRIB_STRIDE` in OpenGL 4.4+
let value =
(unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) })
as u32;
if value == 0 {
// This should be at least 2048, but the driver for AMD Radeon HD 5870 on
// Windows doesn't recognize `GL_MAX_VERTEX_ATTRIB_STRIDE`.
log::warn!("Max vertex attribute stride is 0. Assuming it is 2048");
2048
} else {
value
}
} else {
log::warn!("Max vertex attribute stride unknown. Assuming it is 2048");
2048
}
} else {
(unsafe { gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) }) as u32
}
} else {
!0
},
@ -624,6 +724,7 @@ impl super::Adapter {
max_texture_size,
next_shader_id: Default::default(),
program_cache: Default::default(),
es: es_ver.is_some(),
}),
},
info: Self::make_info(vendor, renderer),
@ -643,27 +744,73 @@ impl super::Adapter {
})
}
unsafe fn compile_shader(
source: &str,
gl: &glow::Context,
shader_type: u32,
es: bool,
) -> Option<glow::Shader> {
let source = if es {
format!("#version 300 es\nprecision lowp float;\n{source}")
} else {
format!("#version 130\n{source}")
};
let shader = unsafe { gl.create_shader(shader_type) }.expect("Could not create shader");
unsafe { gl.shader_source(shader, &source) };
unsafe { gl.compile_shader(shader) };
if !unsafe { gl.get_shader_compile_status(shader) } {
let msg = unsafe { gl.get_shader_info_log(shader) };
if !msg.is_empty() {
log::error!("\tShader compile error: {}", msg);
}
unsafe { gl.delete_shader(shader) };
None
} else {
Some(shader)
}
}
unsafe fn create_shader_clear_program(
gl: &glow::Context,
) -> (glow::Program, glow::UniformLocation) {
es: bool,
) -> Option<(glow::Program, glow::UniformLocation)> {
let program = unsafe { gl.create_program() }.expect("Could not create shader program");
let vertex =
unsafe { gl.create_shader(glow::VERTEX_SHADER) }.expect("Could not create shader");
unsafe { gl.shader_source(vertex, include_str!("./shaders/clear.vert")) };
unsafe { gl.compile_shader(vertex) };
let fragment =
unsafe { gl.create_shader(glow::FRAGMENT_SHADER) }.expect("Could not create shader");
unsafe { gl.shader_source(fragment, include_str!("./shaders/clear.frag")) };
unsafe { gl.compile_shader(fragment) };
let vertex = unsafe {
Self::compile_shader(
include_str!("./shaders/clear.vert"),
gl,
glow::VERTEX_SHADER,
es,
)?
};
let fragment = unsafe {
Self::compile_shader(
include_str!("./shaders/clear.frag"),
gl,
glow::FRAGMENT_SHADER,
es,
)?
};
unsafe { gl.attach_shader(program, vertex) };
unsafe { gl.attach_shader(program, fragment) };
unsafe { gl.link_program(program) };
let linked_ok = unsafe { gl.get_program_link_status(program) };
let msg = unsafe { gl.get_program_info_log(program) };
if !msg.is_empty() {
log::warn!("Shader link error: {}", msg);
}
if !linked_ok {
return None;
}
let color_uniform_location = unsafe { gl.get_uniform_location(program, "color") }
.expect("Could not find color uniform in shader clear shader");
unsafe { gl.delete_shader(vertex) };
unsafe { gl.delete_shader(fragment) };
(program, color_uniform_location)
Some((program, color_uniform_location))
}
}
@ -688,8 +835,11 @@ impl crate::Adapter<super::Api> for super::Adapter {
// Compile the shader program we use for doing manual clears to work around Mesa fastclear
// bug.
let (shader_clear_program, shader_clear_program_color_uniform_location) =
unsafe { Self::create_shader_clear_program(gl) };
let (shader_clear_program, shader_clear_program_color_uniform_location) = unsafe {
Self::create_shader_clear_program(gl, self.shared.es)
.ok_or(crate::DeviceError::ResourceCreationFailed)?
};
Ok(crate::OpenDevice {
device: super::Device {
@ -909,7 +1059,11 @@ impl crate::Adapter<super::Api> for super::Adapter {
Some(crate::SurfaceCapabilities {
formats,
present_modes: vec![wgt::PresentMode::Fifo], //TODO
present_modes: if cfg!(windows) {
vec![wgt::PresentMode::Fifo, wgt::PresentMode::Mailbox]
} else {
vec![wgt::PresentMode::Fifo] //TODO
},
composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque], //TODO
swap_chain_sizes: 2..=2,
current_extent: None,

View File

@ -272,10 +272,6 @@ impl super::Device {
entry_point: stage.entry_point.to_owned(),
});
}
let glsl_version = match self.shared.shading_language_version {
naga::back::glsl::Version::Embedded { version, .. } => version,
naga::back::glsl::Version::Desktop(_) => unreachable!(),
};
let mut guard = self
.shared
.program_cache
@ -295,7 +291,7 @@ impl super::Device {
layout,
label,
multiview,
glsl_version,
self.shared.shading_language_version,
self.shared.private_caps,
)
})
@ -311,9 +307,13 @@ impl super::Device {
layout: &super::PipelineLayout,
#[cfg_attr(target_arch = "wasm32", allow(unused))] label: Option<&str>,
multiview: Option<std::num::NonZeroU32>,
glsl_version: u16,
glsl_version: naga::back::glsl::Version,
private_caps: super::PrivateCapabilities,
) -> Result<Arc<super::PipelineInner>, crate::PipelineError> {
let glsl_version = match glsl_version {
naga::back::glsl::Version::Embedded { version, .. } => format!("{version} es"),
naga::back::glsl::Version::Desktop(version) => format!("{version}"),
};
let program = unsafe { gl.create_program() }.unwrap();
#[cfg(not(target_arch = "wasm32"))]
if let Some(label) = label {
@ -343,7 +343,7 @@ impl super::Device {
// Create empty fragment shader if only vertex shader is present
if has_stages == wgt::ShaderStages::VERTEX {
let shader_src = format!("#version {glsl_version} es \n void main(void) {{}}",);
let shader_src = format!("#version {glsl_version}\n void main(void) {{}}",);
log::info!("Only vertex shader is present. Creating an empty fragment shader",);
let shader = unsafe {
Self::compile_shader(

View File

@ -289,55 +289,6 @@ fn choose_config(
)))
}
fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
let source_str = match source {
glow::DEBUG_SOURCE_API => "API",
glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
glow::DEBUG_SOURCE_APPLICATION => "Application",
glow::DEBUG_SOURCE_OTHER => "Other",
_ => unreachable!(),
};
let log_severity = match severity {
glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
glow::DEBUG_SEVERITY_LOW => log::Level::Info,
glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
_ => unreachable!(),
};
let type_str = match gltype {
glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
glow::DEBUG_TYPE_ERROR => "Error",
glow::DEBUG_TYPE_MARKER => "Marker",
glow::DEBUG_TYPE_OTHER => "Other",
glow::DEBUG_TYPE_PERFORMANCE => "Performance",
glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
glow::DEBUG_TYPE_PORTABILITY => "Portability",
glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
_ => unreachable!(),
};
let _ = std::panic::catch_unwind(|| {
log::log!(
log_severity,
"GLES: [{}/{}] ID {} : {}",
source_str,
type_str,
id,
message
);
});
if cfg!(debug_assertions) && log_severity == log::Level::Error {
// Set canary and continue
crate::VALIDATION_CANARY.set();
}
}
#[derive(Clone, Debug)]
struct EglContext {
instance: Arc<EglInstance>,
@ -1014,7 +965,7 @@ impl crate::Instance<super::Api> for Instance {
if self.flags.contains(wgt::InstanceFlags::VALIDATION) && gl.supports_debug() {
log::info!("Enabling GLES debug output");
unsafe { gl.enable(glow::DEBUG_OUTPUT) };
unsafe { gl.debug_message_callback(gl_debug_message_callback) };
unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
}
inner.egl.unmake_current();
@ -1094,8 +1045,9 @@ impl Surface {
pub(super) unsafe fn present(
&mut self,
_suf_texture: super::Texture,
gl: &glow::Context,
context: &AdapterContext,
) -> Result<(), crate::SurfaceError> {
let gl = unsafe { context.get_without_egl_lock() };
let sc = self.swapchain.as_ref().unwrap();
self.egl

View File

@ -57,12 +57,14 @@ To address this, we invalidate the vertex buffers based on:
*/
///cbindgen:ignore
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
#[cfg(not(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten")))))]
mod egl;
#[cfg(target_os = "emscripten")]
mod emscripten;
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
mod web;
#[cfg(windows)]
mod wgl;
mod adapter;
mod command;
@ -72,9 +74,9 @@ mod queue;
use crate::{CopyExtent, TextureDescriptor};
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
#[cfg(not(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten")))))]
pub use self::egl::{AdapterContext, AdapterContextLock};
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
#[cfg(not(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten")))))]
use self::egl::{Instance, Surface};
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
@ -82,6 +84,11 @@ pub use self::web::AdapterContext;
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
use self::web::{Instance, Surface};
#[cfg(windows)]
use self::wgl::AdapterContext;
#[cfg(windows)]
use self::wgl::{Instance, Surface};
use arrayvec::ArrayVec;
use glow::HasContext;
@ -204,6 +211,7 @@ struct AdapterShared {
max_texture_size: u32,
next_shader_id: AtomicU32,
program_cache: Mutex<ProgramCache>,
es: bool,
}
pub struct Adapter {
@ -904,3 +912,53 @@ impl fmt::Debug for CommandEncoder {
.finish()
}
}
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
let source_str = match source {
glow::DEBUG_SOURCE_API => "API",
glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
glow::DEBUG_SOURCE_APPLICATION => "Application",
glow::DEBUG_SOURCE_OTHER => "Other",
_ => unreachable!(),
};
let log_severity = match severity {
glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
glow::DEBUG_SEVERITY_LOW => log::Level::Info,
glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
_ => unreachable!(),
};
let type_str = match gltype {
glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
glow::DEBUG_TYPE_ERROR => "Error",
glow::DEBUG_TYPE_MARKER => "Marker",
glow::DEBUG_TYPE_OTHER => "Other",
glow::DEBUG_TYPE_PERFORMANCE => "Performance",
glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
glow::DEBUG_TYPE_PORTABILITY => "Portability",
glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
_ => unreachable!(),
};
let _ = std::panic::catch_unwind(|| {
log::log!(
log_severity,
"GLES: [{}/{}] ID {} : {}",
source_str,
type_str,
id,
message
);
});
if cfg!(debug_assertions) && log_severity == log::Level::Error {
// Set canary and continue
crate::VALIDATION_CANARY.set();
}
}

View File

@ -1443,13 +1443,7 @@ impl crate::Queue<super::Api> for super::Queue {
surface: &mut super::Surface,
texture: super::Texture,
) -> Result<(), crate::SurfaceError> {
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
let gl = unsafe { &self.shared.context.get_without_egl_lock() };
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
let gl = &self.shared.context.glow_context;
unsafe { surface.present(texture, gl) }
unsafe { surface.present(texture, &self.shared.context) }
}
unsafe fn get_timestamp_period(&self) -> f32 {

View File

@ -1,5 +1,3 @@
#version 300 es
precision lowp float;
uniform vec4 color;
//Hack: Some WebGL implementations don't find "color" otherwise.
uniform vec4 color_workaround;

View File

@ -1,7 +1,5 @@
#version 300 es
precision lowp float;
// A triangle that fills the whole screen
const vec2[3] TRIANGLE_POS = vec2[](
vec2[3] TRIANGLE_POS = vec2[](
vec2( 0.0, -3.0),
vec2(-3.0, 1.0),
vec2( 3.0, 1.0)

View File

@ -215,8 +215,9 @@ impl Surface {
pub(super) unsafe fn present(
&mut self,
_suf_texture: super::Texture,
gl: &glow::Context,
context: &AdapterContext,
) -> Result<(), crate::SurfaceError> {
let gl = &context.glow_context;
let swapchain = self.swapchain.as_ref().ok_or(crate::SurfaceError::Other(
"need to configure surface before presenting",
))?;

775
wgpu-hal/src/gles/wgl.rs Normal file
View File

@ -0,0 +1,775 @@
use glow::HasContext;
use glutin_wgl_sys::wgl_extra::{
Wgl, CONTEXT_CORE_PROFILE_BIT_ARB, CONTEXT_DEBUG_BIT_ARB, CONTEXT_FLAGS_ARB,
CONTEXT_PROFILE_MASK_ARB,
};
use once_cell::sync::Lazy;
use parking_lot::{Mutex, MutexGuard};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use std::{
collections::HashSet,
ffi::{c_void, CStr, CString},
io::Error,
mem,
os::raw::c_int,
ptr,
sync::Arc,
time::Duration,
};
use wgt::InstanceFlags;
use winapi::{
shared::{
minwindef::{FALSE, HMODULE, LPARAM, LRESULT, UINT, WPARAM},
windef::{HDC, HGLRC, HWND},
},
um::{
libloaderapi::{GetModuleHandleA, GetProcAddress, LoadLibraryA},
wingdi::{
wglCreateContext, wglDeleteContext, wglGetCurrentContext, wglGetProcAddress,
wglMakeCurrent, wglShareLists, ChoosePixelFormat, DescribePixelFormat, GetPixelFormat,
SetPixelFormat, SwapBuffers, PFD_DOUBLEBUFFER, PFD_DRAW_TO_WINDOW, PFD_SUPPORT_OPENGL,
PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR,
},
winuser::{
CreateWindowExA, DefWindowProcA, GetDC, RegisterClassExA, ReleaseDC, CS_OWNDC,
WNDCLASSEXA,
},
},
};
/// The amount of time to wait while trying to obtain a lock to the adapter context
const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1;
/// A wrapper around a `[`glow::Context`]` and the required WGL context that uses locking to
/// guarantee exclusive access when shared with multiple threads.
pub struct AdapterContext {
inner: Arc<Mutex<Inner>>,
}
unsafe impl Sync for AdapterContext {}
unsafe impl Send for AdapterContext {}
impl AdapterContext {
pub fn is_owned(&self) -> bool {
true
}
pub fn raw_context(&self) -> *mut c_void {
self.inner.lock().context.context as *mut _
}
/// Obtain a lock to the WGL context and get handle to the [`glow::Context`] that can be used to
/// do rendering.
#[track_caller]
pub fn lock(&self) -> AdapterContextLock<'_> {
let inner = self
.inner
// Don't lock forever. If it takes longer than 1 second to get the lock we've got a
// deadlock and should panic to show where we got stuck
.try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
.expect("Could not lock adapter context. This is most-likely a deadlock.");
inner.context.make_current(inner.device).unwrap();
AdapterContextLock { inner }
}
}
/// A guard containing a lock to an [`AdapterContext`]
pub struct AdapterContextLock<'a> {
inner: MutexGuard<'a, Inner>,
}
impl<'a> std::ops::Deref for AdapterContextLock<'a> {
type Target = glow::Context;
fn deref(&self) -> &Self::Target {
&self.inner.gl
}
}
impl<'a> Drop for AdapterContextLock<'a> {
fn drop(&mut self) {
self.inner.context.unmake_current().unwrap();
}
}
struct WglContext {
context: HGLRC,
}
impl WglContext {
fn make_current(&self, device: HDC) -> Result<(), Error> {
if unsafe { wglMakeCurrent(device, self.context) } == FALSE {
Err(Error::last_os_error())
} else {
Ok(())
}
}
fn unmake_current(&self) -> Result<(), Error> {
if unsafe { wglGetCurrentContext().is_null() } {
return Ok(());
}
if unsafe { wglMakeCurrent(ptr::null_mut(), ptr::null_mut()) } == FALSE {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
impl Drop for WglContext {
fn drop(&mut self) {
unsafe {
if wglDeleteContext(self.context) == FALSE {
log::error!("failed to delete WGL context {}", Error::last_os_error());
}
};
}
}
unsafe impl Send for WglContext {}
unsafe impl Sync for WglContext {}
struct Inner {
opengl_module: HMODULE,
gl: glow::Context,
device: HDC,
context: WglContext,
}
pub struct Instance {
srgb_capable: bool,
inner: Arc<Mutex<Inner>>,
}
unsafe impl Send for Instance {}
unsafe impl Sync for Instance {}
fn load_gl_func(name: &str, module: Option<HMODULE>) -> *const c_void {
let addr = CString::new(name.as_bytes()).unwrap();
let mut ptr = unsafe { wglGetProcAddress(addr.as_ptr()) };
if ptr.is_null() {
if let Some(module) = module {
ptr = unsafe { GetProcAddress(module, addr.as_ptr()) };
}
}
ptr.cast()
}
fn extensions(extra: &Wgl, dc: HDC) -> HashSet<String> {
if extra.GetExtensionsStringARB.is_loaded() {
unsafe { CStr::from_ptr(extra.GetExtensionsStringARB(dc as *const _)) }
.to_str()
.unwrap_or("")
} else {
""
}
.split(' ')
.map(|s| s.to_owned())
.collect()
}
unsafe fn setup_pixel_format(dc: HDC) -> Result<(), crate::InstanceError> {
let mut format: PIXELFORMATDESCRIPTOR = unsafe { mem::zeroed() };
format.nVersion = 1;
format.nSize = mem::size_of_val(&format) as u16;
format.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
format.iPixelType = PFD_TYPE_RGBA;
format.cColorBits = 8;
let index = unsafe { ChoosePixelFormat(dc, &format) };
if index == 0 {
return Err(crate::InstanceError::with_source(
String::from("unable to choose pixel format"),
Error::last_os_error(),
));
}
let current = unsafe { GetPixelFormat(dc) };
if index != current && unsafe { SetPixelFormat(dc, index, &format) } == FALSE {
return Err(crate::InstanceError::with_source(
String::from("unable to set pixel format"),
Error::last_os_error(),
));
}
let index = unsafe { GetPixelFormat(dc) };
if index == 0 {
return Err(crate::InstanceError::with_source(
String::from("unable to get pixel format index"),
Error::last_os_error(),
));
}
if unsafe { DescribePixelFormat(dc, index, mem::size_of_val(&format) as UINT, &mut format) }
== 0
{
return Err(crate::InstanceError::with_source(
String::from("unable to read pixel format"),
Error::last_os_error(),
));
}
if format.dwFlags & PFD_SUPPORT_OPENGL == 0 || format.iPixelType != PFD_TYPE_RGBA {
return Err(crate::InstanceError::new(String::from(
"unsuitable pixel format",
)));
}
Ok(())
}
fn create_global_device_context() -> Result<HDC, crate::InstanceError> {
let instance = unsafe { GetModuleHandleA(ptr::null()) };
if instance.is_null() {
return Err(crate::InstanceError::with_source(
String::from("unable to get executable instance"),
Error::last_os_error(),
));
}
// Use the address of `UNIQUE` as part of the window class name to ensure different
// `wgpu` versions use different names.
static UNIQUE: Mutex<u8> = Mutex::new(0);
let class_addr: *const _ = &UNIQUE;
let name = format!("wgpu Device Class {:x}\0", class_addr as usize);
let name = CString::from_vec_with_nul(name.into_bytes()).unwrap();
// Use a wrapper function for compatibility with `windows-rs`.
unsafe extern "system" fn wnd_proc(
window: HWND,
msg: UINT,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
unsafe { DefWindowProcA(window, msg, wparam, lparam) }
}
let window_class = WNDCLASSEXA {
cbSize: mem::size_of::<WNDCLASSEXA>() as u32,
style: CS_OWNDC,
lpfnWndProc: Some(wnd_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: instance,
hIcon: ptr::null_mut(),
hCursor: ptr::null_mut(),
hbrBackground: ptr::null_mut(),
lpszMenuName: ptr::null_mut(),
lpszClassName: name.as_ptr(),
hIconSm: ptr::null_mut(),
};
let atom = unsafe { RegisterClassExA(&window_class) };
if atom == 0 {
return Err(crate::InstanceError::with_source(
String::from("unable to register window class"),
Error::last_os_error(),
));
}
// Create a hidden window since we don't pass `WS_VISIBLE`.
let window = unsafe {
CreateWindowExA(
0,
name.as_ptr(),
name.as_ptr(),
0,
0,
0,
1,
1,
ptr::null_mut(),
ptr::null_mut(),
instance,
ptr::null_mut(),
)
};
if window.is_null() {
return Err(crate::InstanceError::with_source(
String::from("unable to create hidden instance window"),
Error::last_os_error(),
));
}
let dc = unsafe { GetDC(window) };
if dc.is_null() {
return Err(crate::InstanceError::with_source(
String::from("unable to create memory device"),
Error::last_os_error(),
));
}
unsafe { setup_pixel_format(dc)? };
// We intentionally leak the window class, window and device context handle to avoid
// spawning a thread to destroy them. We cannot use `DestroyWindow` and `ReleaseDC` on
// different threads.
Ok(dc)
}
fn get_global_device_context() -> Result<HDC, crate::InstanceError> {
#[derive(Clone, Copy)]
struct SendDc(HDC);
unsafe impl Sync for SendDc {}
unsafe impl Send for SendDc {}
static GLOBAL: Lazy<Result<SendDc, crate::InstanceError>> =
Lazy::new(|| create_global_device_context().map(SendDc));
GLOBAL.clone().map(|dc| dc.0)
}
impl crate::Instance<super::Api> for Instance {
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
let opengl_module = unsafe { LoadLibraryA("opengl32.dll\0".as_ptr() as *const _) };
if opengl_module.is_null() {
return Err(crate::InstanceError::with_source(
String::from("unable to load the OpenGL library"),
Error::last_os_error(),
));
}
let dc = get_global_device_context()?;
let context = unsafe { wglCreateContext(dc) };
if context.is_null() {
return Err(crate::InstanceError::with_source(
String::from("unable to create initial OpenGL context"),
Error::last_os_error(),
));
}
let context = WglContext { context };
context.make_current(dc).map_err(|e| {
crate::InstanceError::with_source(
String::from("unable to set initial OpenGL context as current"),
e,
)
})?;
let extra = Wgl::load_with(|name| load_gl_func(name, None));
let extentions = extensions(&extra, dc);
let can_use_profile = extentions.contains("WGL_ARB_create_context_profile")
&& extra.CreateContextAttribsARB.is_loaded();
let context = if can_use_profile {
let attributes = [
CONTEXT_PROFILE_MASK_ARB as c_int,
CONTEXT_CORE_PROFILE_BIT_ARB as c_int,
CONTEXT_FLAGS_ARB as c_int,
if desc.flags.contains(InstanceFlags::DEBUG) {
CONTEXT_DEBUG_BIT_ARB as c_int
} else {
0
},
0, // End of list
];
let context = unsafe {
extra.CreateContextAttribsARB(dc as *const _, ptr::null(), attributes.as_ptr())
};
if context.is_null() {
return Err(crate::InstanceError::with_source(
String::from("unable to create OpenGL context"),
Error::last_os_error(),
));
}
WglContext {
context: context as *mut _,
}
} else {
context
};
context.make_current(dc).map_err(|e| {
crate::InstanceError::with_source(
String::from("unable to set OpenGL context as current"),
e,
)
})?;
let gl = unsafe {
glow::Context::from_loader_function(|name| load_gl_func(name, Some(opengl_module)))
};
let extra = Wgl::load_with(|name| load_gl_func(name, None));
let extentions = extensions(&extra, dc);
let srgb_capable = extentions.contains("WGL_EXT_framebuffer_sRGB")
|| extentions.contains("WGL_ARB_framebuffer_sRGB")
|| gl
.supported_extensions()
.contains("GL_ARB_framebuffer_sRGB");
if srgb_capable {
unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
}
if desc.flags.contains(InstanceFlags::VALIDATION) && gl.supports_debug() {
log::info!("Enabling GL debug output");
unsafe { gl.enable(glow::DEBUG_OUTPUT) };
unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
}
context.unmake_current().map_err(|e| {
crate::InstanceError::with_source(
String::from("unable to unset the current WGL context"),
e,
)
})?;
Ok(Instance {
inner: Arc::new(Mutex::new(Inner {
device: dc,
opengl_module,
gl,
context,
})),
srgb_capable,
})
}
#[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
unsafe fn create_surface(
&self,
_display_handle: RawDisplayHandle,
window_handle: RawWindowHandle,
) -> Result<Surface, crate::InstanceError> {
let window = if let RawWindowHandle::Win32(handle) = window_handle {
handle
} else {
return Err(crate::InstanceError::new(format!(
"unsupported window: {window_handle:?}"
)));
};
Ok(Surface {
window: window.hwnd as *mut _,
presentable: true,
swapchain: None,
srgb_capable: self.srgb_capable,
})
}
unsafe fn destroy_surface(&self, _surface: Surface) {}
unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<super::Api>> {
unsafe {
super::Adapter::expose(AdapterContext {
inner: self.inner.clone(),
})
}
.into_iter()
.collect()
}
}
struct DeviceContextHandle {
device: HDC,
window: HWND,
}
impl Drop for DeviceContextHandle {
fn drop(&mut self) {
unsafe {
ReleaseDC(self.window, self.device);
};
}
}
pub struct Swapchain {
surface_context: WglContext,
surface_gl: glow::Context,
framebuffer: glow::Framebuffer,
renderbuffer: glow::Renderbuffer,
/// Extent because the window lies
extent: wgt::Extent3d,
format: wgt::TextureFormat,
format_desc: super::TextureFormatDesc,
#[allow(unused)]
sample_type: wgt::TextureSampleType,
}
pub struct Surface {
window: HWND,
pub(super) presentable: bool,
swapchain: Option<Swapchain>,
srgb_capable: bool,
}
unsafe impl Send for Surface {}
unsafe impl Sync for Surface {}
impl Surface {
pub(super) unsafe fn present(
&mut self,
_suf_texture: super::Texture,
context: &AdapterContext,
) -> Result<(), crate::SurfaceError> {
let sc = self.swapchain.as_ref().unwrap();
let dc = unsafe { GetDC(self.window) };
if dc.is_null() {
log::error!(
"unable to get the device context from window: {}",
Error::last_os_error()
);
return Err(crate::SurfaceError::Other(
"unable to get the device context from window",
));
}
let dc = DeviceContextHandle {
device: dc,
window: self.window,
};
// Hold the lock for the shared context as we're using resources from there.
let _inner = context.inner.lock();
if let Err(e) = sc.surface_context.make_current(dc.device) {
log::error!("unable to make the surface OpenGL context current: {e}",);
return Err(crate::SurfaceError::Other(
"unable to make the surface OpenGL context current",
));
}
let gl = &sc.surface_gl;
// Note the Y-flipping here. GL's presentation is not flipped,
// but main rendering is. Therefore, we Y-flip the output positions
// in the shader, and also this blit.
unsafe {
gl.blit_framebuffer(
0,
sc.extent.height as i32,
sc.extent.width as i32,
0,
0,
0,
sc.extent.width as i32,
sc.extent.height as i32,
glow::COLOR_BUFFER_BIT,
glow::NEAREST,
)
};
if unsafe { SwapBuffers(dc.device) } == FALSE {
log::error!("unable to swap buffers: {}", Error::last_os_error());
return Err(crate::SurfaceError::Other("unable to swap buffers"));
}
Ok(())
}
pub fn supports_srgb(&self) -> bool {
self.srgb_capable
}
}
impl crate::Surface<super::Api> for Surface {
unsafe fn configure(
&mut self,
device: &super::Device,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> {
// Remove the old configuration.
unsafe { self.unconfigure(device) };
let format_desc = device.shared.describe_texture_format(config.format);
let inner = &device.shared.context.inner.lock();
if let Err(e) = inner.context.make_current(inner.device) {
log::error!("unable to make the shared OpenGL context current: {e}",);
return Err(crate::SurfaceError::Other(
"unable to make the shared OpenGL context current",
));
}
let gl = &inner.gl;
let renderbuffer = unsafe { gl.create_renderbuffer() }.map_err(|error| {
log::error!("Internal swapchain renderbuffer creation failed: {error}");
crate::DeviceError::OutOfMemory
})?;
unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)) };
unsafe {
gl.renderbuffer_storage(
glow::RENDERBUFFER,
format_desc.internal,
config.extent.width as _,
config.extent.height as _,
)
};
// Create the swap chain OpenGL context
let dc = unsafe { GetDC(self.window) };
if dc.is_null() {
log::error!(
"unable to get the device context from window: {}",
Error::last_os_error()
);
return Err(crate::SurfaceError::Other(
"unable to get the device context from window",
));
}
let dc = DeviceContextHandle {
device: dc,
window: self.window,
};
if let Err(e) = unsafe { setup_pixel_format(dc.device) } {
log::error!("unable to setup surface pixel format: {e}",);
return Err(crate::SurfaceError::Other(
"unable to setup surface pixel format",
));
}
let context = unsafe { wglCreateContext(dc.device) };
if context.is_null() {
log::error!(
"unable to create surface OpenGL context: {}",
Error::last_os_error()
);
return Err(crate::SurfaceError::Other(
"unable to create surface OpenGL context",
));
}
let surface_context = WglContext { context };
if unsafe { wglShareLists(inner.context.context, surface_context.context) } == FALSE {
log::error!(
"unable to share objects between OpenGL contexts: {}",
Error::last_os_error()
);
return Err(crate::SurfaceError::Other(
"unable to share objects between OpenGL contexts",
));
}
if let Err(e) = surface_context.make_current(dc.device) {
log::error!("unable to make the surface OpengL context current: {e}",);
return Err(crate::SurfaceError::Other(
"unable to make the surface OpengL context current",
));
}
let extra = Wgl::load_with(|name| load_gl_func(name, None));
let extentions = extensions(&extra, dc.device);
if !(extentions.contains("WGL_EXT_swap_control") && extra.SwapIntervalEXT.is_loaded()) {
log::error!("WGL_EXT_swap_control is unsupported");
return Err(crate::SurfaceError::Other(
"WGL_EXT_swap_control is unsupported",
));
}
let vsync = match config.present_mode {
wgt::PresentMode::Mailbox => false,
wgt::PresentMode::Fifo => true,
_ => {
log::error!("unsupported present mode: {:?}", config.present_mode);
return Err(crate::SurfaceError::Other("unsupported present mode"));
}
};
if unsafe { extra.SwapIntervalEXT(if vsync { 1 } else { 0 }) } == FALSE {
log::error!("unable to set swap interval: {}", Error::last_os_error());
return Err(crate::SurfaceError::Other("unable to set swap interval"));
}
let surface_gl = unsafe {
glow::Context::from_loader_function(|name| {
load_gl_func(name, Some(inner.opengl_module))
})
};
// Check that the surface context OpenGL is new enough to support framebuffers.
let version = unsafe { gl.get_parameter_string(glow::VERSION) };
let version = super::Adapter::parse_full_version(&version);
match version {
Ok(version) => {
if version < (3, 0) {
log::error!(
"surface context OpenGL version ({}.{}) too old",
version.0,
version.1
);
return Err(crate::SurfaceError::Other(
"surface context OpenGL version too old",
));
}
}
Err(e) => {
log::error!("unable to parse surface context OpenGL version: {e}",);
return Err(crate::SurfaceError::Other(
"unable to parse surface context OpenGL version",
));
}
}
let framebuffer = unsafe { surface_gl.create_framebuffer() }.map_err(|error| {
log::error!("Internal swapchain framebuffer creation failed: {error}");
crate::DeviceError::OutOfMemory
})?;
unsafe { surface_gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
unsafe {
surface_gl.framebuffer_renderbuffer(
glow::READ_FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::RENDERBUFFER,
Some(renderbuffer),
)
};
unsafe { surface_gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
unsafe { surface_gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
unsafe { surface_gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
unsafe { surface_gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
self.swapchain = Some(Swapchain {
surface_context,
surface_gl,
renderbuffer,
framebuffer,
extent: config.extent,
format: config.format,
format_desc,
sample_type: wgt::TextureSampleType::Float { filterable: false },
});
Ok(())
}
unsafe fn unconfigure(&mut self, device: &super::Device) {
let gl = &device.shared.context.lock();
if let Some(sc) = self.swapchain.take() {
unsafe {
gl.delete_renderbuffer(sc.renderbuffer);
gl.delete_framebuffer(sc.framebuffer)
};
}
}
unsafe fn acquire_texture(
&mut self,
_timeout_ms: Option<Duration>,
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
let sc = self.swapchain.as_ref().unwrap();
let texture = super::Texture {
inner: super::TextureInner::Renderbuffer {
raw: sc.renderbuffer,
},
drop_guard: None,
array_layer_count: 1,
mip_level_count: 1,
format: sc.format,
format_desc: sc.format_desc.clone(),
copy_size: crate::CopyExtent {
width: sc.extent.width,
height: sc.extent.height,
depth: 1,
},
};
Ok(Some(crate::AcquiredSurfaceTexture {
texture,
suboptimal: false,
}))
}
unsafe fn discard_texture(&mut self, _texture: super::Texture) {}
}

View File

@ -1178,7 +1178,7 @@ impl Limits {
/// max_push_constant_size: 0,
/// min_uniform_buffer_offset_alignment: 256,
/// min_storage_buffer_offset_alignment: 256,
/// max_inter_stage_shader_components: 60,
/// max_inter_stage_shader_components: 31,
/// max_compute_workgroup_storage_size: 0, // +
/// max_compute_invocations_per_workgroup: 0, // +
/// max_compute_workgroup_size_x: 0, // +
@ -1204,6 +1204,9 @@ impl Limits {
max_compute_workgroup_size_z: 0,
max_compute_workgroups_per_dimension: 0,
// Value supported by Intel Celeron B830 on Windows (OpenGL 3.1)
max_inter_stage_shader_components: 31,
// Most of the values should be the same as the downlevel defaults
..Self::downlevel_defaults()
}

View File

@ -61,10 +61,10 @@ features = ["raw-window-handle"]
workspace = true
features = ["metal"]
# We want the wgpu-core Direct3D backends on Windows.
# We want the wgpu-core Direct3D backends and OpenGL (via WGL) on Windows.
[target.'cfg(windows)'.dependencies.wgc]
workspace = true
features = ["dx11", "dx12"]
features = ["dx11", "dx12", "gles"]
# We want the wgpu-core Vulkan backend on Unix (but not emscripten, macOS, iOS) and Windows.
[target.'cfg(any(windows, all(unix, not(target_os = "emscripten"), not(target_os = "ios"), not(target_os = "macos"))))'.dependencies.wgc]