mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
use native webgl2 buffers for MAP_READ usage (#2201)
* use native gl buffers for map_read * Buffer refactor * lint
This commit is contained in:
@@ -201,10 +201,9 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
if !bar.usage.start.contains(crate::BufferUses::STORAGE_WRITE) {
|
||||
continue;
|
||||
}
|
||||
self.cmd_buffer.commands.push(C::BufferBarrier(
|
||||
bar.buffer.inner.as_native().unwrap(),
|
||||
bar.usage.end,
|
||||
));
|
||||
self.cmd_buffer
|
||||
.commands
|
||||
.push(C::BufferBarrier(bar.buffer.raw.unwrap(), bar.usage.end));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +238,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
|
||||
unsafe fn clear_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange) {
|
||||
self.cmd_buffer.commands.push(C::ClearBuffer {
|
||||
dst: buffer.inner.clone(),
|
||||
dst: buffer.clone(),
|
||||
dst_target: buffer.target,
|
||||
range,
|
||||
});
|
||||
@@ -260,9 +259,9 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
};
|
||||
for copy in regions {
|
||||
self.cmd_buffer.commands.push(C::CopyBufferToBuffer {
|
||||
src: src.inner.clone(),
|
||||
src: src.clone(),
|
||||
src_target,
|
||||
dst: dst.inner.clone(),
|
||||
dst: dst.clone(),
|
||||
dst_target,
|
||||
copy,
|
||||
})
|
||||
@@ -305,7 +304,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
for mut copy in regions {
|
||||
copy.clamp_size_to_virtual(&dst.copy_size);
|
||||
self.cmd_buffer.commands.push(C::CopyBufferToTexture {
|
||||
src: src.inner.clone(),
|
||||
src: src.clone(),
|
||||
src_target: src.target,
|
||||
dst: dst_raw,
|
||||
dst_target,
|
||||
@@ -331,7 +330,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
src: src_raw,
|
||||
src_target,
|
||||
src_format: src.format,
|
||||
dst: dst.inner.clone(),
|
||||
dst: dst.clone(),
|
||||
dst_target: dst.target,
|
||||
copy,
|
||||
})
|
||||
@@ -368,7 +367,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
let query_range = start as u32..self.cmd_buffer.queries.len() as u32;
|
||||
self.cmd_buffer.commands.push(C::CopyQueryResults {
|
||||
query_range,
|
||||
dst: buffer.inner.clone(),
|
||||
dst: buffer.clone(),
|
||||
dst_target: buffer.target,
|
||||
dst_offset: offset,
|
||||
});
|
||||
@@ -742,7 +741,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
self.state.index_format = format;
|
||||
self.cmd_buffer
|
||||
.commands
|
||||
.push(C::SetIndexBuffer(binding.buffer.inner.as_native().unwrap()));
|
||||
.push(C::SetIndexBuffer(binding.buffer.raw.unwrap()));
|
||||
}
|
||||
unsafe fn set_vertex_buffer<'a>(
|
||||
&mut self,
|
||||
@@ -752,7 +751,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
self.state.dirty_vbuf_mask |= 1 << index;
|
||||
let (_, ref mut vb) = self.state.vertex_buffers[index as usize];
|
||||
*vb = Some(super::BufferBinding {
|
||||
raw: binding.buffer.inner.as_native().unwrap(),
|
||||
raw: binding.buffer.raw.unwrap(),
|
||||
offset: binding.offset,
|
||||
});
|
||||
}
|
||||
@@ -834,7 +833,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
offset + draw * mem::size_of::<wgt::DrawIndirectArgs>() as wgt::BufferAddress;
|
||||
self.cmd_buffer.commands.push(C::DrawIndirect {
|
||||
topology: self.state.topology,
|
||||
indirect_buf: buffer.inner.as_native().unwrap(),
|
||||
indirect_buf: buffer.raw.unwrap(),
|
||||
indirect_offset,
|
||||
});
|
||||
}
|
||||
@@ -856,7 +855,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
self.cmd_buffer.commands.push(C::DrawIndexedIndirect {
|
||||
topology: self.state.topology,
|
||||
index_type,
|
||||
indirect_buf: buffer.inner.as_native().unwrap(),
|
||||
indirect_buf: buffer.raw.unwrap(),
|
||||
indirect_offset,
|
||||
});
|
||||
}
|
||||
@@ -907,7 +906,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
}
|
||||
unsafe fn dispatch_indirect(&mut self, buffer: &super::Buffer, offset: wgt::BufferAddress) {
|
||||
self.cmd_buffer.commands.push(C::DispatchIndirect {
|
||||
indirect_buf: buffer.inner.as_native().unwrap(),
|
||||
indirect_buf: buffer.raw.unwrap(),
|
||||
indirect_offset: offset,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
use super::{conv, BufferInner};
|
||||
use super::conv;
|
||||
use crate::auxil::map_naga_stage;
|
||||
use glow::HasContext;
|
||||
use std::{convert::TryInto, iter, ptr, sync::Arc};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
iter, ptr,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::mem;
|
||||
@@ -329,16 +333,13 @@ impl crate::Device<super::Api> for super::Device {
|
||||
.private_caps
|
||||
.contains(super::PrivateCapabilities::BUFFER_ALLOCATION);
|
||||
|
||||
if emulate_map
|
||||
&& desc
|
||||
.usage
|
||||
.intersects(crate::BufferUses::MAP_WRITE | crate::BufferUses::MAP_READ)
|
||||
{
|
||||
if emulate_map && desc.usage.intersects(crate::BufferUses::MAP_WRITE) {
|
||||
return Ok(super::Buffer {
|
||||
inner: BufferInner::data_with_capacity(desc.size),
|
||||
raw: None,
|
||||
target,
|
||||
size: desc.size,
|
||||
map_flags: 0,
|
||||
data: Some(Arc::new(Mutex::new(vec![0; desc.size as usize]))),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -365,8 +366,8 @@ impl crate::Device<super::Api> for super::Device {
|
||||
map_flags |= glow::MAP_WRITE_BIT;
|
||||
}
|
||||
|
||||
let raw = gl.create_buffer().unwrap();
|
||||
gl.bind_buffer(target, Some(raw));
|
||||
let raw = Some(gl.create_buffer().unwrap());
|
||||
gl.bind_buffer(target, raw);
|
||||
let raw_size = desc
|
||||
.size
|
||||
.try_into()
|
||||
@@ -412,15 +413,22 @@ impl crate::Device<super::Api> for super::Device {
|
||||
}
|
||||
}
|
||||
|
||||
let data = if emulate_map && desc.usage.contains(crate::BufferUses::MAP_READ) {
|
||||
Some(Arc::new(Mutex::new(vec![0; desc.size as usize])))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(super::Buffer {
|
||||
inner: BufferInner::Buffer(raw),
|
||||
raw,
|
||||
target,
|
||||
size: desc.size,
|
||||
map_flags,
|
||||
data,
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_buffer(&self, buffer: super::Buffer) {
|
||||
if let BufferInner::Buffer(raw) = buffer.inner {
|
||||
if let Some(raw) = buffer.raw {
|
||||
let gl = &self.shared.context.lock();
|
||||
gl.delete_buffer(raw);
|
||||
}
|
||||
@@ -432,21 +440,28 @@ impl crate::Device<super::Api> for super::Device {
|
||||
range: crate::MemoryRange,
|
||||
) -> Result<crate::BufferMapping, crate::DeviceError> {
|
||||
let is_coherent = buffer.map_flags & glow::MAP_COHERENT_BIT != 0;
|
||||
let ptr = match buffer.inner {
|
||||
BufferInner::Data(ref data) => {
|
||||
let mut vec = data.lock().unwrap();
|
||||
let ptr = match buffer.raw {
|
||||
None => {
|
||||
let mut vec = buffer.data.as_ref().unwrap().lock().unwrap();
|
||||
let slice = &mut vec.as_mut_slice()[range.start as usize..range.end as usize];
|
||||
slice.as_mut_ptr()
|
||||
}
|
||||
BufferInner::Buffer(raw) => {
|
||||
Some(raw) => {
|
||||
let gl = &self.shared.context.lock();
|
||||
gl.bind_buffer(buffer.target, Some(raw));
|
||||
let ptr = gl.map_buffer_range(
|
||||
buffer.target,
|
||||
range.start as i32,
|
||||
(range.end - range.start) as i32,
|
||||
buffer.map_flags,
|
||||
);
|
||||
let ptr = if let Some(ref map_read_allocation) = buffer.data {
|
||||
let mut guard = map_read_allocation.lock().unwrap();
|
||||
let slice = guard.as_mut_slice();
|
||||
gl.get_buffer_sub_data(buffer.target, 0, slice);
|
||||
slice.as_mut_ptr()
|
||||
} else {
|
||||
gl.map_buffer_range(
|
||||
buffer.target,
|
||||
range.start as i32,
|
||||
(range.end - range.start) as i32,
|
||||
buffer.map_flags,
|
||||
)
|
||||
};
|
||||
gl.bind_buffer(buffer.target, None);
|
||||
ptr
|
||||
}
|
||||
@@ -457,11 +472,17 @@ impl crate::Device<super::Api> for super::Device {
|
||||
})
|
||||
}
|
||||
unsafe fn unmap_buffer(&self, buffer: &super::Buffer) -> Result<(), crate::DeviceError> {
|
||||
if let BufferInner::Buffer(raw) = buffer.inner {
|
||||
let gl = &self.shared.context.lock();
|
||||
gl.bind_buffer(buffer.target, Some(raw));
|
||||
gl.unmap_buffer(buffer.target);
|
||||
gl.bind_buffer(buffer.target, None);
|
||||
if let Some(raw) = buffer.raw {
|
||||
if !self
|
||||
.shared
|
||||
.workarounds
|
||||
.contains(super::Workarounds::EMULATE_BUFFER_MAP)
|
||||
{
|
||||
let gl = &self.shared.context.lock();
|
||||
gl.bind_buffer(buffer.target, Some(raw));
|
||||
gl.unmap_buffer(buffer.target);
|
||||
gl.bind_buffer(buffer.target, None);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -469,7 +490,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
where
|
||||
I: Iterator<Item = crate::MemoryRange>,
|
||||
{
|
||||
if let BufferInner::Buffer(raw) = buffer.inner {
|
||||
if let Some(raw) = buffer.raw {
|
||||
let gl = &self.shared.context.lock();
|
||||
gl.bind_buffer(buffer.target, Some(raw));
|
||||
for range in ranges {
|
||||
@@ -862,7 +883,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
wgt::BindingType::Buffer { .. } => {
|
||||
let bb = &desc.buffers[entry.resource_index as usize];
|
||||
super::RawBinding::Buffer {
|
||||
raw: bb.buffer.inner.as_native().unwrap(),
|
||||
raw: bb.buffer.raw.unwrap(),
|
||||
offset: bb.offset as i32,
|
||||
size: match bb.size {
|
||||
Some(s) => s.get() as i32,
|
||||
@@ -1094,15 +1115,25 @@ impl crate::Device<super::Api> for super::Device {
|
||||
wait_value: crate::FenceValue,
|
||||
timeout_ms: u32,
|
||||
) -> Result<bool, crate::DeviceError> {
|
||||
if cfg!(not(target_arch = "wasm32")) && fence.last_completed < wait_value {
|
||||
if fence.last_completed < wait_value {
|
||||
let gl = &self.shared.context.lock();
|
||||
let timeout_ns = (timeout_ms as u64 * 1_000_000).min(!0u32 as u64);
|
||||
let timeout_ns = if cfg!(target_arch = "wasm32") {
|
||||
0
|
||||
} else {
|
||||
(timeout_ms as u64 * 1_000_000).min(!0u32 as u64)
|
||||
};
|
||||
let &(_, sync) = fence
|
||||
.pending
|
||||
.iter()
|
||||
.find(|&&(value, _)| value >= wait_value)
|
||||
.unwrap();
|
||||
match gl.client_wait_sync(sync, glow::SYNC_FLUSH_COMMANDS_BIT, timeout_ns as i32) {
|
||||
// for some reason firefox returns WAIT_FAILED, to investigate
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
glow::WAIT_FAILED => {
|
||||
log::warn!("wait failed!");
|
||||
Ok(false)
|
||||
}
|
||||
glow::TIMEOUT_EXPIRED => Ok(false),
|
||||
glow::CONDITION_SATISFIED | glow::ALREADY_SIGNALED => Ok(true),
|
||||
_ => Err(crate::DeviceError::Lost),
|
||||
|
||||
@@ -210,30 +210,13 @@ pub struct Queue {
|
||||
current_index_buffer: Option<glow::Buffer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BufferInner {
|
||||
Buffer(glow::Buffer),
|
||||
Data(Arc<std::sync::Mutex<Vec<u8>>>),
|
||||
}
|
||||
|
||||
impl BufferInner {
|
||||
pub fn data_with_capacity(size: u64) -> BufferInner {
|
||||
BufferInner::Data(Arc::new(std::sync::Mutex::new(vec![0; size as usize])))
|
||||
}
|
||||
pub fn as_native(&self) -> Option<glow::Buffer> {
|
||||
match *self {
|
||||
BufferInner::Buffer(ref buffer) => Some(*buffer),
|
||||
BufferInner::Data(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Buffer {
|
||||
inner: BufferInner,
|
||||
raw: Option<glow::Buffer>,
|
||||
target: BindTarget,
|
||||
size: wgt::BufferAddress,
|
||||
map_flags: u32,
|
||||
data: Option<Arc<std::sync::Mutex<Vec<u8>>>>,
|
||||
}
|
||||
|
||||
// Safe: WASM doesn't have threads
|
||||
@@ -584,14 +567,14 @@ enum Command {
|
||||
indirect_offset: wgt::BufferAddress,
|
||||
},
|
||||
ClearBuffer {
|
||||
dst: BufferInner,
|
||||
dst: Buffer,
|
||||
dst_target: BindTarget,
|
||||
range: crate::MemoryRange,
|
||||
},
|
||||
CopyBufferToBuffer {
|
||||
src: BufferInner,
|
||||
src: Buffer,
|
||||
src_target: BindTarget,
|
||||
dst: BufferInner,
|
||||
dst: Buffer,
|
||||
dst_target: BindTarget,
|
||||
copy: crate::BufferCopy,
|
||||
},
|
||||
@@ -603,7 +586,7 @@ enum Command {
|
||||
copy: crate::TextureCopy,
|
||||
},
|
||||
CopyBufferToTexture {
|
||||
src: BufferInner,
|
||||
src: Buffer,
|
||||
#[allow(unused)]
|
||||
src_target: BindTarget,
|
||||
dst: glow::Texture,
|
||||
@@ -615,7 +598,7 @@ enum Command {
|
||||
src: glow::Texture,
|
||||
src_target: BindTarget,
|
||||
src_format: wgt::TextureFormat,
|
||||
dst: BufferInner,
|
||||
dst: Buffer,
|
||||
#[allow(unused)]
|
||||
dst_target: BindTarget,
|
||||
copy: crate::BufferTextureCopy,
|
||||
@@ -625,7 +608,7 @@ enum Command {
|
||||
EndQuery(BindTarget),
|
||||
CopyQueryResults {
|
||||
query_range: Range<u32>,
|
||||
dst: BufferInner,
|
||||
dst: Buffer,
|
||||
dst_target: BindTarget,
|
||||
dst_offset: wgt::BufferAddress,
|
||||
},
|
||||
|
||||
@@ -205,8 +205,8 @@ impl super::Queue {
|
||||
ref dst,
|
||||
dst_target,
|
||||
ref range,
|
||||
} => match *dst {
|
||||
super::BufferInner::Buffer(buffer) => {
|
||||
} => match dst.raw {
|
||||
Some(buffer) => {
|
||||
gl.bind_buffer(glow::COPY_READ_BUFFER, Some(self.zero_buffer));
|
||||
gl.bind_buffer(dst_target, Some(buffer));
|
||||
let mut dst_offset = range.start;
|
||||
@@ -222,8 +222,9 @@ impl super::Queue {
|
||||
dst_offset += size;
|
||||
}
|
||||
}
|
||||
super::BufferInner::Data(ref data) => {
|
||||
data.lock().unwrap().as_mut_slice()[range.start as usize..range.end as usize]
|
||||
None => {
|
||||
dst.data.as_ref().unwrap().lock().unwrap().as_mut_slice()
|
||||
[range.start as usize..range.end as usize]
|
||||
.fill(0);
|
||||
}
|
||||
},
|
||||
@@ -249,11 +250,8 @@ impl super::Queue {
|
||||
glow::COPY_WRITE_BUFFER
|
||||
};
|
||||
let size = copy.size.get() as usize;
|
||||
match (src, dst) {
|
||||
(
|
||||
&super::BufferInner::Buffer(ref src),
|
||||
&super::BufferInner::Buffer(ref dst),
|
||||
) => {
|
||||
match (src.raw, dst.raw) {
|
||||
(Some(ref src), Some(ref dst)) => {
|
||||
gl.bind_buffer(copy_src_target, Some(*src));
|
||||
gl.bind_buffer(copy_dst_target, Some(*dst));
|
||||
gl.copy_buffer_sub_data(
|
||||
@@ -264,16 +262,16 @@ impl super::Queue {
|
||||
copy.size.get() as _,
|
||||
);
|
||||
}
|
||||
(&super::BufferInner::Buffer(src), &super::BufferInner::Data(ref data)) => {
|
||||
let mut data = data.lock().unwrap();
|
||||
(Some(src), None) => {
|
||||
let mut data = dst.data.as_ref().unwrap().lock().unwrap();
|
||||
let dst_data = &mut data.as_mut_slice()
|
||||
[copy.dst_offset as usize..copy.dst_offset as usize + size];
|
||||
|
||||
gl.bind_buffer(copy_src_target, Some(src));
|
||||
gl.get_buffer_sub_data(copy_src_target, copy.src_offset as i32, dst_data);
|
||||
}
|
||||
(&super::BufferInner::Data(ref data), &super::BufferInner::Buffer(dst)) => {
|
||||
let data = data.lock().unwrap();
|
||||
(None, Some(dst)) => {
|
||||
let data = src.data.as_ref().unwrap().lock().unwrap();
|
||||
let src_data = &data.as_slice()
|
||||
[copy.src_offset as usize..copy.src_offset as usize + size];
|
||||
gl.bind_buffer(copy_dst_target, Some(dst));
|
||||
@@ -283,7 +281,7 @@ impl super::Queue {
|
||||
src_data,
|
||||
);
|
||||
}
|
||||
(&super::BufferInner::Data(_), &super::BufferInner::Data(_)) => {
|
||||
(None, None) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -374,14 +372,14 @@ impl super::Queue {
|
||||
let mut unbind_unpack_buffer = false;
|
||||
if format_info.block_dimensions == (1, 1) {
|
||||
let buffer_data;
|
||||
let unpack_data = match *src {
|
||||
super::BufferInner::Buffer(buffer) => {
|
||||
let unpack_data = match src.raw {
|
||||
Some(buffer) => {
|
||||
gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, Some(buffer));
|
||||
unbind_unpack_buffer = true;
|
||||
glow::PixelUnpackData::BufferOffset(copy.buffer_layout.offset as u32)
|
||||
}
|
||||
super::BufferInner::Data(ref data) => {
|
||||
buffer_data = data.lock().unwrap();
|
||||
None => {
|
||||
buffer_data = src.data.as_ref().unwrap().lock().unwrap();
|
||||
let src_data =
|
||||
&buffer_data.as_slice()[copy.buffer_layout.offset as usize..];
|
||||
glow::PixelUnpackData::Slice(src_data)
|
||||
@@ -454,16 +452,16 @@ impl super::Queue {
|
||||
let offset = copy.buffer_layout.offset as u32;
|
||||
|
||||
let buffer_data;
|
||||
let unpack_data = match *src {
|
||||
super::BufferInner::Buffer(buffer) => {
|
||||
let unpack_data = match src.raw {
|
||||
Some(buffer) => {
|
||||
gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, Some(buffer));
|
||||
unbind_unpack_buffer = true;
|
||||
glow::CompressedPixelUnpackData::BufferRange(
|
||||
offset..offset + bytes_per_image,
|
||||
)
|
||||
}
|
||||
super::BufferInner::Data(ref data) => {
|
||||
buffer_data = data.lock().unwrap();
|
||||
None => {
|
||||
buffer_data = src.data.as_ref().unwrap().lock().unwrap();
|
||||
let src_data = &buffer_data.as_slice()
|
||||
[(offset as usize)..(offset + bytes_per_image) as usize];
|
||||
glow::CompressedPixelUnpackData::Slice(src_data)
|
||||
@@ -578,14 +576,14 @@ impl super::Queue {
|
||||
);
|
||||
}
|
||||
let mut buffer_data;
|
||||
let unpack_data = match *dst {
|
||||
super::BufferInner::Buffer(buffer) => {
|
||||
let unpack_data = match dst.raw {
|
||||
Some(buffer) => {
|
||||
gl.pixel_store_i32(glow::PACK_ROW_LENGTH, row_texels as i32);
|
||||
gl.bind_buffer(glow::PIXEL_PACK_BUFFER, Some(buffer));
|
||||
glow::PixelPackData::BufferOffset(copy.buffer_layout.offset as u32)
|
||||
}
|
||||
super::BufferInner::Data(ref data) => {
|
||||
buffer_data = data.lock().unwrap();
|
||||
None => {
|
||||
buffer_data = dst.data.as_ref().unwrap().lock().unwrap();
|
||||
let dst_data =
|
||||
&mut buffer_data.as_mut_slice()[copy.buffer_layout.offset as usize..];
|
||||
glow::PixelPackData::Slice(dst_data)
|
||||
@@ -626,13 +624,13 @@ impl super::Queue {
|
||||
self.temp_query_results.as_ptr() as *const u8,
|
||||
self.temp_query_results.len() * mem::size_of::<u64>(),
|
||||
);
|
||||
match *dst {
|
||||
super::BufferInner::Buffer(buffer) => {
|
||||
match dst.raw {
|
||||
Some(buffer) => {
|
||||
gl.bind_buffer(dst_target, Some(buffer));
|
||||
gl.buffer_sub_data_u8_slice(dst_target, dst_offset as i32, query_data);
|
||||
}
|
||||
super::BufferInner::Data(ref data) => {
|
||||
let data = &mut *data.lock().unwrap();
|
||||
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]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user