mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Backed out changeset bd37586e34ed (bug 1903977) for causing WR bustages CLOSED TREE
This commit is contained in:
parent
8252cf3aa6
commit
ebaeeedc6c
@ -483,7 +483,6 @@ struct Job {
|
|||||||
dirty_rect: BlobDirtyRect,
|
dirty_rect: BlobDirtyRect,
|
||||||
visible_rect: DeviceIntRect,
|
visible_rect: DeviceIntRect,
|
||||||
tile_size: TileSize,
|
tile_size: TileSize,
|
||||||
output: MutableTileBuffer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rasterizes gecko blob images.
|
/// Rasterizes gecko blob images.
|
||||||
@ -503,7 +502,6 @@ impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
|
|||||||
&mut self,
|
&mut self,
|
||||||
requests: &[BlobImageParams],
|
requests: &[BlobImageParams],
|
||||||
low_priority: bool,
|
low_priority: bool,
|
||||||
tile_pool: &mut BlobTilePool,
|
|
||||||
) -> Vec<(BlobImageRequest, BlobImageResult)> {
|
) -> Vec<(BlobImageRequest, BlobImageResult)> {
|
||||||
// All we do here is spin up our workers to callback into gecko to replay the drawing commands.
|
// All we do here is spin up our workers to callback into gecko to replay the drawing commands.
|
||||||
gecko_profiler_label!(Graphics, Rasterization);
|
gecko_profiler_label!(Graphics, Rasterization);
|
||||||
@ -521,8 +519,6 @@ impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
|
|||||||
let blob = Arc::clone(&command.data);
|
let blob = Arc::clone(&command.data);
|
||||||
assert!(!params.descriptor.rect.is_empty());
|
assert!(!params.descriptor.rect.is_empty());
|
||||||
|
|
||||||
let buf_size = (params.descriptor.rect.area() * params.descriptor.format.bytes_per_pixel()) as usize;
|
|
||||||
|
|
||||||
Job {
|
Job {
|
||||||
request: params.request,
|
request: params.request,
|
||||||
descriptor: params.descriptor,
|
descriptor: params.descriptor,
|
||||||
@ -530,7 +526,6 @@ impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
|
|||||||
visible_rect: command.visible_rect,
|
visible_rect: command.visible_rect,
|
||||||
dirty_rect: params.dirty_rect,
|
dirty_rect: params.dirty_rect,
|
||||||
tile_size: command.tile_size,
|
tile_size: command.tile_size,
|
||||||
output: tile_pool.get_buffer(buf_size),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -548,7 +543,7 @@ impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
|
|||||||
requests.len() > 4
|
requests.len() > 4
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = if should_parallelize {
|
if should_parallelize {
|
||||||
// Parallel version synchronously installs a job on the thread pool which will
|
// Parallel version synchronously installs a job on the thread pool which will
|
||||||
// try to do the work in parallel.
|
// try to do the work in parallel.
|
||||||
// This thread is blocked until the thread pool is done doing the work.
|
// This thread is blocked until the thread pool is done doing the work.
|
||||||
@ -562,9 +557,7 @@ impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
requests.into_iter().map(rasterize_blob).collect()
|
requests.into_iter().map(rasterize_blob).collect()
|
||||||
};
|
}
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,9 +574,12 @@ fn autoreleasepool<T, F: FnOnce() -> T>(f: F) -> T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rasterize_blob(mut job: Job) -> (BlobImageRequest, BlobImageResult) {
|
fn rasterize_blob(job: Job) -> (BlobImageRequest, BlobImageResult) {
|
||||||
gecko_profiler_label!(Graphics, Rasterization);
|
gecko_profiler_label!(Graphics, Rasterization);
|
||||||
let descriptor = job.descriptor;
|
let descriptor = job.descriptor;
|
||||||
|
let buf_size = (descriptor.rect.area() * descriptor.format.bytes_per_pixel()) as usize;
|
||||||
|
|
||||||
|
let mut output = vec![0u8; buf_size];
|
||||||
|
|
||||||
let dirty_rect = match job.dirty_rect {
|
let dirty_rect = match job.dirty_rect {
|
||||||
DirtyRect::Partial(rect) => Some(rect),
|
DirtyRect::Partial(rect) => Some(rect),
|
||||||
@ -591,8 +587,6 @@ fn rasterize_blob(mut job: Job) -> (BlobImageRequest, BlobImageResult) {
|
|||||||
};
|
};
|
||||||
assert!(!descriptor.rect.is_empty());
|
assert!(!descriptor.rect.is_empty());
|
||||||
|
|
||||||
let request = job.request;
|
|
||||||
|
|
||||||
let result = autoreleasepool(|| {
|
let result = autoreleasepool(|| {
|
||||||
unsafe {
|
unsafe {
|
||||||
if wr_moz2d_render_cb(
|
if wr_moz2d_render_cb(
|
||||||
@ -601,9 +595,9 @@ fn rasterize_blob(mut job: Job) -> (BlobImageRequest, BlobImageResult) {
|
|||||||
&descriptor.rect,
|
&descriptor.rect,
|
||||||
&job.visible_rect,
|
&job.visible_rect,
|
||||||
job.tile_size,
|
job.tile_size,
|
||||||
&request.tile,
|
&job.request.tile,
|
||||||
dirty_rect.as_ref(),
|
dirty_rect.as_ref(),
|
||||||
MutByteSlice::new(job.output.as_mut_slice()),
|
MutByteSlice::new(output.as_mut_slice()),
|
||||||
) {
|
) {
|
||||||
// We want the dirty rect local to the tile rather than the whole image.
|
// We want the dirty rect local to the tile rather than the whole image.
|
||||||
// TODO(nical): move that up and avoid recomupting the tile bounds in the callback
|
// TODO(nical): move that up and avoid recomupting the tile bounds in the callback
|
||||||
@ -613,7 +607,7 @@ fn rasterize_blob(mut job: Job) -> (BlobImageRequest, BlobImageResult) {
|
|||||||
|
|
||||||
Ok(RasterizedBlobImage {
|
Ok(RasterizedBlobImage {
|
||||||
rasterized_rect,
|
rasterized_rect,
|
||||||
data: job.output.into_arc(),
|
data: Arc::new(output),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
panic!("Moz2D replay problem");
|
panic!("Moz2D replay problem");
|
||||||
@ -621,7 +615,7 @@ fn rasterize_blob(mut job: Job) -> (BlobImageRequest, BlobImageResult) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
(request, result)
|
(job.request, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlobImageHandler for Moz2dBlobImageHandler {
|
impl BlobImageHandler for Moz2dBlobImageHandler {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use api::{BlobImageHandler, ColorF, CrashAnnotator, DocumentId, IdNamespace};
|
use api::{BlobImageHandler, ColorF, IdNamespace, DocumentId, CrashAnnotator};
|
||||||
use api::{VoidPtrToSizeFn, FontRenderMode, ImageFormat};
|
use api::{VoidPtrToSizeFn, FontRenderMode, ImageFormat};
|
||||||
use api::{RenderNotifier, ImageBufferKind};
|
use api::{RenderNotifier, ImageBufferKind};
|
||||||
use api::units::*;
|
use api::units::*;
|
||||||
@ -617,8 +617,6 @@ pub fn create_webrender_instance(
|
|||||||
let lp_builder = LowPrioritySceneBuilderThread {
|
let lp_builder = LowPrioritySceneBuilderThread {
|
||||||
rx: low_priority_scene_rx,
|
rx: low_priority_scene_rx,
|
||||||
tx: scene_tx.clone(),
|
tx: scene_tx.clone(),
|
||||||
// TODO: hard-coded tile size.
|
|
||||||
tile_pool: api::BlobTilePool::new(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
thread::Builder::new().name(lp_scene_thread_name.clone()).spawn(move || {
|
thread::Builder::new().name(lp_scene_thread_name.clone()).spawn(move || {
|
||||||
|
@ -40,11 +40,11 @@ use crate::util::drain_filter;
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
fn rasterize_blobs(txn: &mut TransactionMsg, is_low_priority: bool, tile_pool: &mut api::BlobTilePool) {
|
fn rasterize_blobs(txn: &mut TransactionMsg, is_low_priority: bool) {
|
||||||
profile_scope!("rasterize_blobs");
|
profile_scope!("rasterize_blobs");
|
||||||
|
|
||||||
if let Some(ref mut rasterizer) = txn.blob_rasterizer {
|
if let Some(ref mut rasterizer) = txn.blob_rasterizer {
|
||||||
let mut rasterized_blobs = rasterizer.rasterize(&txn.blob_requests, is_low_priority, tile_pool);
|
let mut rasterized_blobs = rasterizer.rasterize(&txn.blob_requests, is_low_priority);
|
||||||
// try using the existing allocation if our current list is empty
|
// try using the existing allocation if our current list is empty
|
||||||
if txn.rasterized_blobs.is_empty() {
|
if txn.rasterized_blobs.is_empty() {
|
||||||
txn.rasterized_blobs = rasterized_blobs;
|
txn.rasterized_blobs = rasterized_blobs;
|
||||||
@ -244,7 +244,6 @@ pub struct SceneBuilderThread {
|
|||||||
capture_config: Option<CaptureConfig>,
|
capture_config: Option<CaptureConfig>,
|
||||||
debug_flags: DebugFlags,
|
debug_flags: DebugFlags,
|
||||||
recycler: SceneRecycler,
|
recycler: SceneRecycler,
|
||||||
tile_pool: api::BlobTilePool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SceneBuilderThreadChannels {
|
pub struct SceneBuilderThreadChannels {
|
||||||
@ -291,8 +290,6 @@ impl SceneBuilderThread {
|
|||||||
capture_config: None,
|
capture_config: None,
|
||||||
debug_flags: DebugFlags::default(),
|
debug_flags: DebugFlags::default(),
|
||||||
recycler: SceneRecycler::new(),
|
recycler: SceneRecycler::new(),
|
||||||
// TODO: tile size is hard-coded here.
|
|
||||||
tile_pool: api::BlobTilePool::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +331,6 @@ impl SceneBuilderThread {
|
|||||||
|
|
||||||
// Now that we off the critical path, do some memory bookkeeping.
|
// Now that we off the critical path, do some memory bookkeeping.
|
||||||
self.recycler.recycle_built_scene();
|
self.recycler.recycle_built_scene();
|
||||||
self.tile_pool.cleanup();
|
|
||||||
}
|
}
|
||||||
Ok(SceneBuilderRequest::AddDocument(document_id, initial_size)) => {
|
Ok(SceneBuilderRequest::AddDocument(document_id, initial_size)) => {
|
||||||
let old = self.documents.insert(document_id, Document::new(
|
let old = self.documents.insert(document_id, Document::new(
|
||||||
@ -645,7 +641,7 @@ impl SceneBuilderThread {
|
|||||||
profile.start_time(profiler::BLOB_RASTERIZATION_TIME);
|
profile.start_time(profiler::BLOB_RASTERIZATION_TIME);
|
||||||
|
|
||||||
let is_low_priority = false;
|
let is_low_priority = false;
|
||||||
rasterize_blobs(&mut txn, is_low_priority, &mut self.tile_pool);
|
rasterize_blobs(&mut txn, is_low_priority);
|
||||||
|
|
||||||
profile.end_time(profiler::BLOB_RASTERIZATION_TIME);
|
profile.end_time(profiler::BLOB_RASTERIZATION_TIME);
|
||||||
Telemetry::record_rasterize_blobs_time(Duration::from_micros((profile.get(profiler::BLOB_RASTERIZATION_TIME).unwrap() * 1000.00) as u64));
|
Telemetry::record_rasterize_blobs_time(Duration::from_micros((profile.get(profiler::BLOB_RASTERIZATION_TIME).unwrap() * 1000.00) as u64));
|
||||||
@ -775,7 +771,6 @@ impl SceneBuilderThread {
|
|||||||
pub struct LowPrioritySceneBuilderThread {
|
pub struct LowPrioritySceneBuilderThread {
|
||||||
pub rx: Receiver<SceneBuilderRequest>,
|
pub rx: Receiver<SceneBuilderRequest>,
|
||||||
pub tx: Sender<SceneBuilderRequest>,
|
pub tx: Sender<SceneBuilderRequest>,
|
||||||
pub tile_pool: api::BlobTilePool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LowPrioritySceneBuilderThread {
|
impl LowPrioritySceneBuilderThread {
|
||||||
@ -787,7 +782,6 @@ impl LowPrioritySceneBuilderThread {
|
|||||||
.map(|txn| self.process_transaction(txn))
|
.map(|txn| self.process_transaction(txn))
|
||||||
.collect();
|
.collect();
|
||||||
self.tx.send(SceneBuilderRequest::Transactions(txns)).unwrap();
|
self.tx.send(SceneBuilderRequest::Transactions(txns)).unwrap();
|
||||||
self.tile_pool.cleanup();
|
|
||||||
}
|
}
|
||||||
Ok(SceneBuilderRequest::ShutDown(sync)) => {
|
Ok(SceneBuilderRequest::ShutDown(sync)) => {
|
||||||
self.tx.send(SceneBuilderRequest::ShutDown(sync)).unwrap();
|
self.tx.send(SceneBuilderRequest::ShutDown(sync)).unwrap();
|
||||||
@ -806,7 +800,7 @@ impl LowPrioritySceneBuilderThread {
|
|||||||
fn process_transaction(&mut self, mut txn: Box<TransactionMsg>) -> Box<TransactionMsg> {
|
fn process_transaction(&mut self, mut txn: Box<TransactionMsg>) -> Box<TransactionMsg> {
|
||||||
let is_low_priority = true;
|
let is_low_priority = true;
|
||||||
txn.profile.start_time(profiler::BLOB_RASTERIZATION_TIME);
|
txn.profile.start_time(profiler::BLOB_RASTERIZATION_TIME);
|
||||||
rasterize_blobs(&mut txn, is_low_priority, &mut self.tile_pool);
|
rasterize_blobs(&mut txn, is_low_priority);
|
||||||
txn.profile.end_time(profiler::BLOB_RASTERIZATION_TIME);
|
txn.profile.end_time(profiler::BLOB_RASTERIZATION_TIME);
|
||||||
Telemetry::record_rasterize_blobs_time(Duration::from_micros((txn.profile.get(profiler::BLOB_RASTERIZATION_TIME).unwrap() * 1000.00) as u64));
|
Telemetry::record_rasterize_blobs_time(Duration::from_micros((txn.profile.get(profiler::BLOB_RASTERIZATION_TIME).unwrap() * 1000.00) as u64));
|
||||||
txn.blob_requests = Vec::new();
|
txn.blob_requests = Vec::new();
|
||||||
|
@ -421,8 +421,7 @@ pub trait AsyncBlobImageRasterizer : Send {
|
|||||||
fn rasterize(
|
fn rasterize(
|
||||||
&mut self,
|
&mut self,
|
||||||
requests: &[BlobImageParams],
|
requests: &[BlobImageParams],
|
||||||
low_priority: bool,
|
low_priority: bool
|
||||||
tile_pool: &mut crate::BlobTilePool,
|
|
||||||
) -> Vec<(BlobImageRequest, BlobImageResult)>;
|
) -> Vec<(BlobImageRequest, BlobImageResult)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,6 @@ mod display_list;
|
|||||||
mod font;
|
mod font;
|
||||||
mod gradient_builder;
|
mod gradient_builder;
|
||||||
mod image;
|
mod image;
|
||||||
mod tile_pool;
|
|
||||||
pub mod units;
|
pub mod units;
|
||||||
|
|
||||||
pub use crate::color::*;
|
pub use crate::color::*;
|
||||||
@ -52,7 +51,6 @@ pub use crate::display_list::*;
|
|||||||
pub use crate::font::*;
|
pub use crate::font::*;
|
||||||
pub use crate::gradient_builder::*;
|
pub use crate::gradient_builder::*;
|
||||||
pub use crate::image::*;
|
pub use crate::image::*;
|
||||||
pub use crate::tile_pool::*;
|
|
||||||
|
|
||||||
use crate::units::*;
|
use crate::units::*;
|
||||||
use crate::channel::Receiver;
|
use crate::channel::Receiver;
|
||||||
|
@ -1,147 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
const NUM_TILE_BUCKETS: usize = 6;
|
|
||||||
|
|
||||||
/// A pool of blob tile buffers to mitigate the overhead of
|
|
||||||
/// allocating and deallocating blob tiles.
|
|
||||||
///
|
|
||||||
/// The pool keeps a strong reference to each allocated buffers and
|
|
||||||
/// reuses the ones with a strong count of 1.
|
|
||||||
pub struct BlobTilePool {
|
|
||||||
largest_size_class: usize,
|
|
||||||
buckets: [Vec<Arc<Vec<u8>>>; NUM_TILE_BUCKETS],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlobTilePool {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
// The default max tile size is actually 256, using 512 here
|
|
||||||
// so that this still works when experimenting with larger
|
|
||||||
// tile sizes. If we ever make larger adjustments, the buckets
|
|
||||||
// should be changed accordingly.
|
|
||||||
let max_tile_size = 512;
|
|
||||||
BlobTilePool {
|
|
||||||
largest_size_class: max_tile_size * max_tile_size * 4,
|
|
||||||
buckets: [
|
|
||||||
Vec::with_capacity(32),
|
|
||||||
Vec::with_capacity(32),
|
|
||||||
Vec::with_capacity(32),
|
|
||||||
Vec::with_capacity(32),
|
|
||||||
Vec::with_capacity(32),
|
|
||||||
Vec::with_capacity(32),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get or allocate a tile buffer of the requested size.
|
|
||||||
///
|
|
||||||
/// The returned buffer is zero-inizitalized.
|
|
||||||
/// The length of the returned buffer is equal to the requested size,
|
|
||||||
/// however the buffer may be allocated with a larger capacity to
|
|
||||||
/// confirm to the pool's corresponding bucket tile size.
|
|
||||||
pub fn get_buffer(&mut self, requested_size: usize) -> MutableTileBuffer {
|
|
||||||
assert!(requested_size <= self.largest_size_class);
|
|
||||||
|
|
||||||
let (bucket_idx, cap) = self.bucket_and_size(requested_size);
|
|
||||||
let bucket = &mut self.buckets[bucket_idx];
|
|
||||||
let mut selected_idx = None;
|
|
||||||
for (buf_idx, buffer) in bucket.iter().enumerate() {
|
|
||||||
if Arc::strong_count(buffer) == 1 {
|
|
||||||
selected_idx = Some(buf_idx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ptr;
|
|
||||||
let strong_ref;
|
|
||||||
if let Some(idx) = selected_idx {
|
|
||||||
{
|
|
||||||
// This works because we just ensured the pool has the only strong
|
|
||||||
// ref to the buffer.
|
|
||||||
let buffer = Arc::get_mut(&mut bucket[idx]).unwrap();
|
|
||||||
debug_assert!(buffer.capacity() >= requested_size);
|
|
||||||
// Ensure the length is equal to the requested size. It's not
|
|
||||||
// strictly necessay for the tile pool but the texture upload
|
|
||||||
// code relies on it.
|
|
||||||
unsafe { buffer.set_len(requested_size); }
|
|
||||||
|
|
||||||
// zero-initialize
|
|
||||||
buffer.fill(0);
|
|
||||||
|
|
||||||
ptr = buffer.as_mut_ptr();
|
|
||||||
}
|
|
||||||
strong_ref = Arc::clone(&bucket[idx]);
|
|
||||||
} else {
|
|
||||||
// Allocate a buffer with the adequate capacity for the requested
|
|
||||||
// size's bucket.
|
|
||||||
let mut buf = vec![0; cap];
|
|
||||||
// Force the length to be the requested size.
|
|
||||||
unsafe { buf.set_len(requested_size) };
|
|
||||||
|
|
||||||
ptr = buf.as_mut_ptr();
|
|
||||||
strong_ref = Arc::new(buf);
|
|
||||||
// Track the new buffer.
|
|
||||||
bucket.push(Arc::clone(&strong_ref));
|
|
||||||
};
|
|
||||||
|
|
||||||
MutableTileBuffer {
|
|
||||||
ptr,
|
|
||||||
strong_ref,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bucket_and_size(&self, size: usize) -> (usize, usize) {
|
|
||||||
let mut next_size_class = self.largest_size_class / 4;
|
|
||||||
let mut idx = 0;
|
|
||||||
while size < next_size_class && idx < NUM_TILE_BUCKETS - 1 {
|
|
||||||
next_size_class /= 4;
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
(idx, next_size_class * 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Go over all allocated tile buffers. For each bucket, deallocate some buffers
|
|
||||||
/// until the number of unused buffer is more than half of the buffers for that
|
|
||||||
/// bucket.
|
|
||||||
///
|
|
||||||
/// In practice, if called regularly, this gradually lets go of blob tiles when
|
|
||||||
/// they are not used.
|
|
||||||
pub fn cleanup(&mut self) {
|
|
||||||
for bucket in &mut self.buckets {
|
|
||||||
let threshold = bucket.len() / 2;
|
|
||||||
let mut num_available = 0;
|
|
||||||
bucket.retain(&mut |buffer: &Arc<Vec<u8>>| {
|
|
||||||
if Arc::strong_count(buffer) > 1 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
num_available += 1;
|
|
||||||
num_available < threshold
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// The role of tile buffer is to encapsulate an Arc to the underlying buffer
|
|
||||||
// with a reference count of at most 2 and a way to view the buffer's content
|
|
||||||
// as a mutable slice, even though the reference count may be more than 1.
|
|
||||||
// The safety of this relies on the other strong reference being held by the
|
|
||||||
// tile pool which never accesses the buffer's content, so the only reference
|
|
||||||
// that can access it is the `TileBuffer` itself.
|
|
||||||
pub struct MutableTileBuffer {
|
|
||||||
strong_ref: Arc<Vec<u8>>,
|
|
||||||
ptr: *mut u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MutableTileBuffer {
|
|
||||||
pub fn as_mut_slice(&mut self) -> &mut[u8] {
|
|
||||||
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.strong_ref.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_arc(self) -> Arc<Vec<u8>> {
|
|
||||||
self.strong_ref
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for MutableTileBuffer {}
|
|
Loading…
Reference in New Issue
Block a user