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

Add timestamp support to OpenGL #4267

Merged
merged 4 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion wgpu-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ rustc-hash = "1.1"
log = "0.4"

# backend: Gles
glow = { version = "0.12.3", optional = true }
glow = { version = "0.13", optional = true }

[dependencies.wgt]
package = "wgpu-types"
Expand Down
54 changes: 32 additions & 22 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,28 @@ impl super::Adapter {
return None;
}

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 shading_language_version = {
cwfitzgerald marked this conversation as resolved.
Show resolved Hide resolved
let sl_version = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };
log::info!("SL version: {}", &sl_version);
Expand All @@ -251,28 +273,6 @@ impl super::Adapter {

log::trace!("Supported GL Extensions: {:#?}", extensions);

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))
Expand Down Expand Up @@ -411,6 +411,11 @@ impl super::Adapter {
wgt::DownlevelFlags::MULTISAMPLED_SHADING,
supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
);
let query_buffers = extensions.contains("GL_ARB_query_buffer_object")
|| extensions.contains("GL_AMD_query_buffer_object");
if query_buffers {
downlevel_flags.set(wgt::DownlevelFlags::NONBLOCKING_QUERY_RESOLVE, true);
}

let mut features = wgt::Features::empty()
| wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
Expand Down Expand Up @@ -450,6 +455,10 @@ impl super::Adapter {
supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
);
features.set(wgt::Features::SHADER_UNUSED_VERTEX_OUTPUT, true);
if extensions.contains("GL_ARB_timer_query") {
features.set(wgt::Features::TIMESTAMP_QUERY, true);
features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES, true);
}
let gl_bcn_exts = [
"GL_EXT_texture_compression_s3tc",
"GL_EXT_texture_compression_rgtc",
Expand Down Expand Up @@ -574,6 +583,7 @@ impl super::Adapter {
extensions.contains("OES_texture_float_linear")
},
);
private_caps.set(super::PrivateCapabilities::QUERY_BUFFERS, query_buffers);

let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32;
let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32;
Expand Down
34 changes: 32 additions & 2 deletions wgpu-hal/src/gles/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub(super) struct State {
dirty_vbuf_mask: usize,
active_first_instance: u32,
push_offset_to_uniform: ArrayVec<super::UniformDesc, { super::MAX_PUSH_CONSTANTS }>,
end_of_pass_timestamp: Option<glow::Query>,
}

impl super::CommandBuffer {
Expand Down Expand Up @@ -409,8 +410,9 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
unsafe fn end_query(&mut self, set: &super::QuerySet, _index: u32) {
self.cmd_buffer.commands.push(C::EndQuery(set.target));
}
unsafe fn write_timestamp(&mut self, _set: &super::QuerySet, _index: u32) {
unimplemented!()
unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) {
let query = set.queries[index as usize];
self.cmd_buffer.commands.push(C::TimestampQuery(query));
}
unsafe fn reset_queries(&mut self, _set: &super::QuerySet, _range: Range<u32>) {
//TODO: what do we do here?
Expand Down Expand Up @@ -439,6 +441,16 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
// render

unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor<super::Api>) {
debug_assert!(self.state.end_of_pass_timestamp.is_none());
if let Some(ref t) = desc.timestamp_writes {
if let Some(index) = t.beginning_of_pass_write_index {
unsafe { self.write_timestamp(t.query_set, index) }
}
self.state.end_of_pass_timestamp = t
.end_of_pass_write_index
.map(|index| t.query_set.queries[index as usize]);
}

self.state.render_size = desc.extent;
self.state.resolve_attachments.clear();
self.state.invalidate_attachments.clear();
Expand Down Expand Up @@ -623,6 +635,10 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
}
self.state.vertex_attributes.clear();
self.state.primitive = super::PrimitiveState::default();

if let Some(query) = self.state.end_of_pass_timestamp.take() {
self.cmd_buffer.commands.push(C::TimestampQuery(query));
}
}

unsafe fn set_bind_group(
Expand Down Expand Up @@ -1030,6 +1046,16 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
// compute

unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor<super::Api>) {
debug_assert!(self.state.end_of_pass_timestamp.is_none());
if let Some(ref t) = desc.timestamp_writes {
if let Some(index) = t.beginning_of_pass_write_index {
unsafe { self.write_timestamp(t.query_set, index) }
}
self.state.end_of_pass_timestamp = t
.end_of_pass_write_index
.map(|index| t.query_set.queries[index as usize]);
}

if let Some(label) = desc.label {
let range = self.cmd_buffer.add_marker(label);
self.cmd_buffer.commands.push(C::PushDebugGroup(range));
Expand All @@ -1041,6 +1067,10 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
self.cmd_buffer.commands.push(C::PopDebugGroup);
self.state.has_pass_label = false;
}

if let Some(query) = self.state.end_of_pass_timestamp.take() {
self.cmd_buffer.commands.push(C::TimestampQuery(query));
}
}

unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) {
Expand Down
9 changes: 9 additions & 0 deletions wgpu-hal/src/gles/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,14 @@ impl crate::Device<super::Api> for super::Device {
if gl.supports_debug() {
use std::fmt::Write;

// Initialize the query so we can label it
match desc.ty {
wgt::QueryType::Timestamp => unsafe {
gl.query_counter(query, glow::TIMESTAMP)
},
_ => (),
}

if let Some(label) = desc.label {
temp_string.clear();
let _ = write!(temp_string, "{label}[{i}]");
Expand All @@ -1238,6 +1246,7 @@ impl crate::Device<super::Api> for super::Device {
queries: queries.into_boxed_slice(),
target: match desc.ty {
wgt::QueryType::Occlusion => glow::ANY_SAMPLES_PASSED_CONSERVATIVE,
wgt::QueryType::Timestamp => glow::TIMESTAMP,
_ => unimplemented!(),
},
})
Expand Down
2 changes: 1 addition & 1 deletion wgpu-hal/src/gles/egl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ impl crate::Instance<super::Api> for Instance {
let inner = self.inner.lock();
inner.egl.make_current();

let gl = unsafe {
let mut gl = unsafe {
glow::Context::from_loader_function(|name| {
inner
.egl
Expand Down
3 changes: 3 additions & 0 deletions wgpu-hal/src/gles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ bitflags::bitflags! {
const COLOR_BUFFER_FLOAT = 1 << 9;
/// Supports linear flitering `f32` textures.
const TEXTURE_FLOAT_LINEAR = 1 << 10;
/// Supports query buffer objects.
const QUERY_BUFFERS = 1 << 11;
}
}

Expand Down Expand Up @@ -775,6 +777,7 @@ enum Command {
SetIndexBuffer(glow::Buffer),
BeginQuery(glow::Query, BindTarget),
EndQuery(BindTarget),
TimestampQuery(glow::Query),
CopyQueryResults {
query_range: Range<u32>,
dst: Buffer,
Expand Down
103 changes: 83 additions & 20 deletions wgpu-hal/src/gles/queue.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{conv::is_layered_target, Command as C};
use super::{conv::is_layered_target, Command as C, PrivateCapabilities};
use arrayvec::ArrayVec;
use glow::HasContext;
use std::{mem, slice, sync::Arc};
Expand Down Expand Up @@ -808,34 +808,97 @@ impl super::Queue {
C::EndQuery(target) => {
unsafe { gl.end_query(target) };
}
C::TimestampQuery(query) => {
unsafe { gl.query_counter(query, glow::TIMESTAMP) };
}
C::CopyQueryResults {
ref query_range,
ref dst,
dst_target,
dst_offset,
} => {
self.temp_query_results.clear();
for &query in queries[query_range.start as usize..query_range.end as usize].iter() {
let result = unsafe { gl.get_query_parameter_u32(query, glow::QUERY_RESULT) };
self.temp_query_results.push(result as u64);
}
let query_data = unsafe {
slice::from_raw_parts(
self.temp_query_results.as_ptr() as *const u8,
self.temp_query_results.len() * mem::size_of::<u64>(),
)
};
match dst.raw {
Some(buffer) => {
unsafe { gl.bind_buffer(dst_target, Some(buffer)) };
if self
.shared
.private_caps
.contains(PrivateCapabilities::QUERY_BUFFERS)
&& dst.raw.is_some()
{
unsafe {
// We're assuming that the only relevant queries are 8 byte timestamps or
// occlusion tests.
let query_size = 8;

let query_range_size = query_size * query_range.len();

let buffer = gl.create_buffer().ok();
gl.bind_buffer(glow::QUERY_BUFFER, buffer);
gl.buffer_data_size(
glow::QUERY_BUFFER,
query_range_size as _,
glow::STREAM_COPY,
);

for (i, &query) in queries
[query_range.start as usize..query_range.end as usize]
.iter()
.enumerate()
{
gl.get_query_parameter_u64_with_offset(
query,
glow::QUERY_RESULT,
query_size * i,
)
}
gl.bind_buffer(dst_target, dst.raw);
gl.copy_buffer_sub_data(
glow::QUERY_BUFFER,
dst_target,
0,
dst_offset as _,
query_range_size as _,
);
if let Some(buffer) = buffer {
gl.delete_buffer(buffer)
}
}
} else {
self.temp_query_results.clear();
for &query in
queries[query_range.start as usize..query_range.end as usize].iter()
{
let mut result: u64 = 0;
unsafe {
gl.buffer_sub_data_u8_slice(dst_target, dst_offset as i32, query_data)
let result: *mut u64 = &mut result;
gl.get_query_parameter_u64_with_offset(
query,
glow::QUERY_RESULT,
result as usize,
)
};
self.temp_query_results.push(result);
}
None => {
let data = &mut dst.data.as_ref().unwrap().lock().unwrap();
let len = query_data.len().min(data.len());
data[..len].copy_from_slice(&query_data[..len]);
let query_data = unsafe {
slice::from_raw_parts(
self.temp_query_results.as_ptr() as *const u8,
self.temp_query_results.len() * mem::size_of::<u64>(),
)
};
match dst.raw {
Some(buffer) => {
unsafe { gl.bind_buffer(dst_target, Some(buffer)) };
unsafe {
gl.buffer_sub_data_u8_slice(
dst_target,
dst_offset as i32,
query_data,
)
};
}
None => {
let data = &mut dst.data.as_ref().unwrap().lock().unwrap();
let len = query_data.len().min(data.len());
data[..len].copy_from_slice(&query_data[..len]);
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion wgpu-hal/src/gles/wgl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ impl crate::Instance<super::Api> for Instance {
)
})?;

let gl = unsafe {
let mut gl = unsafe {
glow::Context::from_loader_function(|name| load_gl_func(name, Some(opengl_module)))
};

Expand Down
17 changes: 17 additions & 0 deletions wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,23 @@ bitflags::bitflags! {
///
/// The GLES/WebGL and Vulkan on Android doesn't support this.
const SURFACE_VIEW_FORMATS = 1 << 21;

/// If this is true, calls to `CommandEncoder::resolve_query_set` will be performed on the queue timeline.
///
/// If this is false, calls to `CommandEncoder::resolve_query_set` will be performed on the device (i.e. cpu) timeline
/// and will block that timeline until the query has data. You may work around this limitation by waiting until the submit
/// whose queries you are resolving is fully finished (through use of `queue.on_submitted_work_done`) and only
/// then submitting the resolve_query_set command. The queries will be guarenteed finished, so will not block.
///
/// Supported by:
/// - Vulkan,
/// - DX12
/// - Metal
/// - OpenGL 4.4+
///
/// Not Supported by:
/// - GL ES / WebGL
const NONBLOCKING_QUERY_RESOLVE = 1 << 22;
cwfitzgerald marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down