/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// A single texel in RGBAF32 texture - 16 bytes. #[derive(Copy, Clone, Debug, MallocSizeOf)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct GpuBufferBlockF {
data: [f32; 4],
}
/// A single texel in RGBAI32 texture - 16 bytes. #[derive(Copy, Clone, Debug, MallocSizeOf)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct GpuBufferBlockI {
data: [i32; 4],
}
/// GpuBuffer handle is similar to GpuBufferAddress with additional checks /// to avoid accidentally using the same handle in multiple frames. /// /// Do not send GpuBufferHandle to the GPU directly. Instead use a GpuBuffer /// or GpuBufferBuilder to resolve the handle into a GpuBufferAddress that /// can be placed into GPU data. /// /// The extra checks consists into storing an 8 bit epoch in the upper 8 bits /// of the handle. The epoch will be reused every 255 frames so this is not /// a mechanism that one can rely on to store and reuse handles over multiple /// frames. It is only a mechanism to catch mistakes where a handle is /// accidentally used in the wrong frame and panic. #[repr(transparent)] #[derive(Copy, Clone, MallocSizeOf, Eq, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct GpuBufferHandle(u32);
// TODO(gw): Temporarily encode GPU Cache addresses as a single int. // In the future, we can change the PrimitiveInstanceData struct // to use 2x u16 for the vertex attribute instead of an i32. #[repr(transparent)] #[derive(Copy, Clone, MallocSizeOf, Eq, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct GpuBufferAddress(u32);
impl GpuBufferAddress { pubfn new(u: u16, v: u16) -> Self {
GpuBufferAddress(
v as u32 * MAX_VERTEX_TEXTURE_WIDTH as u32 + u as u32
)
}
/// Push one (16 byte) block of data in to the writer pubfn push_one<B>(&mutself, block: B) where B: Into<T> { self.buffer.push(block.into());
}
/// Push a reference to a render task in to the writer. Once the render /// task graph is resolved, this will be patched with the UV rect of the task pubfn push_render_task(&mutself, task_id: RenderTaskId) { if task_id != RenderTaskId::INVALID { self.deferred.push(DeferredBlock {
task_id,
index: self.buffer.len(),
});
}
self.buffer.push(T::default());
}
/// Close this writer, returning the GPU address of this set of block(s). pubfn finish(self) -> GpuBufferAddress {
assert!(self.buffer.len() <= self.index + self.max_block_count);
GpuBufferAddress(self.index as u32)
}
/// Close this writer, returning the GPU address of this set of block(s). pubfn finish_with_handle(self) -> GpuBufferHandle {
assert!(self.buffer.len() <= self.index + self.max_block_count);
assert_eq!(self.index & (GpuBufferHandle::EPOCH_MASK as usize), 0);
GpuBufferHandle::new(self.index as u32, self.epoch)
}
}
impl<'a, T> Drop for GpuBufferWriter<'a, T> { fn drop(&mutself) {
assert!(self.buffer.len() <= self.index + self.max_block_count, "Attempt to write too many GpuBuffer blocks");
}
}
pubstruct GpuBufferBuilderImpl<T> { // `data` will become the backing store of the GpuBuffer sent along // with the frame so it uses the frame allocator.
data: FrameVec<T>, // `deferred` is only used during frame building and not sent with the // built frame, so it does not use the same allocator.
deferred: Vec<DeferredBlock>,
epoch: u32,
}
impl<T> GpuBufferBuilderImpl<T> where T: Texel + std::convert::From<DeviceIntRect> { pubfn new(memory: &FrameMemory, capacity: usize, frame_id: FrameId) -> Self { // Pick the first 8 bits of the frame id and store them in the upper bits // of the handles. let epoch = ((frame_id.as_u64() % 62) as u32 + 1) << 26;
GpuBufferBuilderImpl {
data: memory.new_vec_with_capacity(capacity),
deferred: Vec::new(),
epoch,
}
}
/// Begin writing a specific number of blocks pubfn write_blocks(
&mutself,
max_block_count: usize,
) -> GpuBufferWriter<T> {
assert!(max_block_count <= MAX_VERTEX_TEXTURE_WIDTH);
// Reserve space in the gpu buffer for data that will be written by the // renderer. pubfn reserve_renderer_deferred_blocks(&mutself, block_count: usize) -> GpuBufferHandle {
ensure_row_capacity(&mutself.data, block_count);
let index = self.data.len();
self.data.reserve(block_count); for _ in0 ..block_count { self.data.push(Default::default());
}
let len = self.data.len();
assert!(len % MAX_VERTEX_TEXTURE_WIDTH == 0);
// At this point, we know that the render task graph has been built, and we can // query the location of any dynamic (render target) or static (texture cache) // task. This allows us to patch the UV rects in to the GPU buffer before upload // to the GPU. letmut deferred_uv_copies = Vec::new(); for block inself.deferred.drain(..) { let render_task = &render_tasks[block.task_id];
// External images (for example Android SurfaceTexture sources) only have // their uv rect resolved by the renderer, and it may be Y-flipped. The // target rect computed below does not capture that, so instead defer copying // the resolved uv rect (written by update_deferred_resolves into the task's // uv_rect_handle block) into this segment block. See `apply_deferred_uv_copies`. iflet RenderTaskLocation::Static {
surface: StaticRenderTaskSurface::ReadOnly {
source: TextureSource::External(TextureSourceExternal { normalized_uvs, .. }),
},
..
} = render_task.location { // The gpu buffer stores uv rects in device pixels, but the renderer // writes normalized uvs for external images that use them. Scale by the // image size (the external image task's target rect) during the copy. let uv_scale = if normalized_uvs { let size = render_task.get_target_rect().size();
[size.width as f32, size.height as f32]
} else {
[1.0, 1.0]
};
deferred_uv_copies.push(DeferredUvCopy {
src: render_task.get_texture_address().as_u32(),
dst: block.index as u32,
uv_scale,
}); continue;
}
letmut target_rect = render_task.get_target_rect(); if block.task_id.has_sub_rect() { let sub = &render_tasks.sub_rects[block.task_id.sub_rect_index as usize];
target_rect = sub.sub_rect
.translate(target_rect.min.to_vector())
.intersection_unchecked(&target_rect);
}
let uv_rect = match render_task.uv_rect_kind() {
UvRectKind::Rect => {
target_rect
}
UvRectKind::Quad { top_left, bottom_right, .. } => { let size = target_rect.size();
DeviceIntRect::new(
DeviceIntPoint::new(
target_rect.min.x + (top_left.x * size.width as f32).round() as i32,
target_rect.min.y + (top_left.y * size.height as f32).round() as i32,
),
DeviceIntPoint::new(
target_rect.min.x + (bottom_right.x * size.width as f32).round() as i32,
target_rect.min.y + (bottom_right.y * size.height as f32).round() as i32,
),
)
}
};
self.data[block.index] = uv_rect.into();
}
GpuBuffer {
data: self.data,
size: DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32, (len / MAX_VERTEX_TEXTURE_WIDTH) as i32),
format: T::image_format(),
deferred_uv_copies,
epoch: self.epoch,
}
}
/// Panics if the handle cannot be used this frame. #[allow(unused)] pubfn check_handle(&self, handle: GpuBufferHandle) { if handle == GpuBufferHandle::INVALID { return;
} let epoch = handle.0 & GpuBufferHandle::EPOCH_MASK;
assert!(self.epoch == epoch);
}
}
/// Records that the uv rect block at `dst` must be overwritten with the block at /// `src` once the renderer has resolved external images. /// /// TODO: This is a hack. At the end of frame building we resolve UVs from the /// render task graph, however this is too early to resolve the real UVs for /// external images (happens on the renderer thread). So this is an even-more- /// deferred step on top of the already deferred blocks. /// It would be cleaner to move the existing deferred mechanism later and avoid /// stacking another one on top, but the better fix would be to not write UV /// rects in the gpu buffer and pass render task handles to the quad shaders. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Clone, Copy, Debug)] pubstruct DeferredUvCopy { pub src: u32, pub dst: u32, /// Per-axis scale applied to the copied uv rect. Used to convert the /// renderer's normalized uvs (for external images that use them) into the /// device pixels that the quad shaders expect. `[1.0, 1.0]` for uvs that /// are already in device pixels. pub uv_scale: [f32; 2],
}
impl GpuBuffer<GpuBufferBlockF> { /// Apply the uv rect copies deferred during `finalize`. Must be called after the /// renderer has resolved external images into the gpu buffer. pubfn apply_deferred_uv_copies(&mutself) { for i in0 .. self.deferred_uv_copies.len() { let copy = self.deferred_uv_copies[i]; // The uv rect is stored as [p0.x, p0.y, p1.x, p1.y]. letmut uv = self.data[copy.src as usize].data;
uv[0] *= copy.uv_scale[0];
uv[1] *= copy.uv_scale[1];
uv[2] *= copy.uv_scale[0];
uv[3] *= copy.uv_scale[1]; self.data[copy.dst as usize] = uv.into();
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.