Skip to main content

webrender/renderer/
gpu_buffer.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5/*
6    TODO:
7        Efficiently allow writing to buffer (better push interface)
8 */
9
10use std::i32;
11
12use crate::gpu_types::UvRectKind;
13use crate::internal_types::{FrameId, FrameMemory, FrameVec, TextureSource, TextureSourceExternal};
14use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH;
15use crate::util::ScaleOffset;
16use api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceRect, LayoutRect, PictureRect};
17use api::{PremultipliedColorF, ImageFormat};
18use crate::device::Texel;
19use crate::render_task::{RenderTaskLocation, StaticRenderTaskSurface};
20use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
21
22pub struct GpuBufferBuilder {
23    pub i32: GpuBufferBuilderI,
24    pub f32: GpuBufferBuilderF,
25}
26
27pub type GpuBufferF = GpuBuffer<GpuBufferBlockF>;
28pub type GpuBufferBuilderF = GpuBufferBuilderImpl<GpuBufferBlockF>;
29
30pub type GpuBufferI = GpuBuffer<GpuBufferBlockI>;
31pub type GpuBufferBuilderI = GpuBufferBuilderImpl<GpuBufferBlockI>;
32
33pub type GpuBufferWriterF<'l> = GpuBufferWriter<'l, GpuBufferBlockF>;
34pub type GpuBufferWriterI<'l> = GpuBufferWriter<'l, GpuBufferBlockI>;
35
36unsafe impl Texel for GpuBufferBlockF {
37    fn image_format() -> ImageFormat { ImageFormat::RGBAF32 }
38}
39
40unsafe impl Texel for GpuBufferBlockI {
41    fn image_format() -> ImageFormat { ImageFormat::RGBAI32 }
42}
43
44impl Default for GpuBufferBlockF {
45    fn default() -> Self {
46        GpuBufferBlockF::EMPTY
47    }
48}
49
50impl Default for GpuBufferBlockI {
51    fn default() -> Self {
52        GpuBufferBlockI::EMPTY
53    }
54}
55
56/// A single texel in RGBAF32 texture - 16 bytes.
57#[derive(Copy, Clone, Debug, MallocSizeOf)]
58#[cfg_attr(feature = "capture", derive(Serialize))]
59#[cfg_attr(feature = "replay", derive(Deserialize))]
60pub struct GpuBufferBlockF {
61    data: [f32; 4],
62}
63
64/// A single texel in RGBAI32 texture - 16 bytes.
65#[derive(Copy, Clone, Debug, MallocSizeOf)]
66#[cfg_attr(feature = "capture", derive(Serialize))]
67#[cfg_attr(feature = "replay", derive(Deserialize))]
68pub struct GpuBufferBlockI {
69    data: [i32; 4],
70}
71
72/// GpuBuffer handle is similar to GpuBufferAddress with additional checks
73/// to avoid accidentally using the same handle in multiple frames.
74///
75/// Do not send GpuBufferHandle to the GPU directly. Instead use a GpuBuffer
76/// or GpuBufferBuilder to resolve the handle into a GpuBufferAddress that
77/// can be placed into GPU data.
78///
79/// The extra checks consists into storing an 8 bit epoch in the upper 8 bits
80/// of the handle. The epoch will be reused every 255 frames so this is not
81/// a mechanism that one can rely on to store and reuse handles over multiple
82/// frames. It is only a mechanism to catch mistakes where a handle is
83/// accidentally used in the wrong frame and panic.
84#[repr(transparent)]
85#[derive(Copy, Clone, MallocSizeOf, Eq, PartialEq)]
86#[cfg_attr(feature = "capture", derive(Serialize))]
87#[cfg_attr(feature = "replay", derive(Deserialize))]
88pub struct GpuBufferHandle(u32);
89
90impl GpuBufferHandle {
91    pub const INVALID: GpuBufferHandle = GpuBufferHandle(u32::MAX - 1);
92    const EPOCH_MASK: u32 = 0xFC000000; // Leading 6 bits
93
94    fn new(addr: u32, epoch: u32) -> Self {
95        Self(addr | epoch)
96    }
97
98    pub fn address_unchecked(&self) -> GpuBufferAddress {
99        GpuBufferAddress(self.0 & !Self::EPOCH_MASK)
100    }
101}
102
103impl std::fmt::Debug for GpuBufferHandle {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        let addr = self.0 & !Self::EPOCH_MASK;
106        let epoch = (self.0 & Self::EPOCH_MASK) >> 26;
107        write!(f, "#{addr}@{epoch}")
108    }
109}
110
111// TODO(gw): Temporarily encode GPU Cache addresses as a single int.
112//           In the future, we can change the PrimitiveInstanceData struct
113//           to use 2x u16 for the vertex attribute instead of an i32.
114#[repr(transparent)]
115#[derive(Copy, Clone, MallocSizeOf, Eq, PartialEq)]
116#[cfg_attr(feature = "capture", derive(Serialize))]
117#[cfg_attr(feature = "replay", derive(Deserialize))]
118pub struct GpuBufferAddress(u32);
119
120impl GpuBufferAddress {
121    pub fn new(u: u16, v: u16) -> Self {
122        GpuBufferAddress(
123            v as u32 * MAX_VERTEX_TEXTURE_WIDTH as u32 + u as u32
124        )
125    }
126
127    pub fn is_valid(&self) -> bool {
128        *self != Self::INVALID
129    }
130
131    pub fn as_u32(self) -> u32 {
132        self.0
133    }
134
135    pub fn from_u32(val: u32) -> Self {
136        GpuBufferAddress(val)
137    }
138
139    #[allow(dead_code)]
140    pub fn as_int(self) -> i32 {
141        self.0 as i32
142    }
143
144    #[allow(dead_code)]
145    pub fn uv(self) -> (u16, u16) {
146        (
147            (self.0 as usize % MAX_VERTEX_TEXTURE_WIDTH) as u16,
148            (self.0 as usize / MAX_VERTEX_TEXTURE_WIDTH) as u16,
149        )
150    }
151
152    pub const INVALID: GpuBufferAddress = GpuBufferAddress(u32::MAX - 1);
153}
154
155impl std::fmt::Debug for GpuBufferAddress {
156    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
157        if *self == Self::INVALID {
158            write!(f, "<invalid>")
159        } else {
160            write!(f, "#{}", self.0)
161        }
162    }
163}
164
165impl GpuBufferBlockF {
166    pub const EMPTY: Self = GpuBufferBlockF { data: [0.0; 4] };
167}
168
169impl GpuBufferBlockI {
170    pub const EMPTY: Self = GpuBufferBlockI { data: [0; 4] };
171}
172
173impl Into<GpuBufferBlockF> for LayoutRect {
174    fn into(self) -> GpuBufferBlockF {
175        GpuBufferBlockF {
176            data: [
177                self.min.x,
178                self.min.y,
179                self.max.x,
180                self.max.y,
181            ],
182        }
183    }
184}
185
186impl Into<GpuBufferBlockF> for crate::quad::LayoutOrDeviceRect {
187    fn into(self) -> GpuBufferBlockF {
188        GpuBufferBlockF {
189            data: [
190                self.min.x,
191                self.min.y,
192                self.max.x,
193                self.max.y,
194            ],
195        }
196    }
197}
198
199impl Into<GpuBufferBlockF> for ScaleOffset {
200    fn into(self) -> GpuBufferBlockF {
201        GpuBufferBlockF {
202            data: [
203                self.scale.x,
204                self.scale.y,
205                self.offset.x,
206                self.offset.y,
207            ],
208        }
209    }
210}
211
212impl Into<GpuBufferBlockF> for PictureRect {
213    fn into(self) -> GpuBufferBlockF {
214        GpuBufferBlockF {
215            data: [
216                self.min.x,
217                self.min.y,
218                self.max.x,
219                self.max.y,
220            ],
221        }
222    }
223}
224
225impl Into<GpuBufferBlockF> for DeviceRect {
226    fn into(self) -> GpuBufferBlockF {
227        GpuBufferBlockF {
228            data: [
229                self.min.x,
230                self.min.y,
231                self.max.x,
232                self.max.y,
233            ],
234        }
235    }
236}
237
238impl Into<GpuBufferBlockF> for PremultipliedColorF {
239    fn into(self) -> GpuBufferBlockF {
240        GpuBufferBlockF {
241            data: [
242                self.r,
243                self.g,
244                self.b,
245                self.a,
246            ],
247        }
248    }
249}
250
251impl From<DeviceIntRect> for GpuBufferBlockF {
252    fn from(rect: DeviceIntRect) -> Self {
253        GpuBufferBlockF {
254            data: [
255                rect.min.x as f32,
256                rect.min.y as f32,
257                rect.max.x as f32,
258                rect.max.y as f32,
259            ],
260        }
261    }
262}
263
264impl From<DeviceIntRect> for GpuBufferBlockI {
265    fn from(rect: DeviceIntRect) -> Self {
266        GpuBufferBlockI {
267            data: [
268                rect.min.x,
269                rect.min.y,
270                rect.max.x,
271                rect.max.y,
272            ],
273        }
274    }
275}
276
277impl Into<GpuBufferBlockF> for [f32; 4] {
278    fn into(self) -> GpuBufferBlockF {
279        GpuBufferBlockF {
280            data: self,
281        }
282    }
283}
284
285impl Into<GpuBufferBlockI> for [i32; 4] {
286    fn into(self) -> GpuBufferBlockI {
287        GpuBufferBlockI {
288            data: self,
289        }
290    }
291}
292
293pub trait GpuBufferDataF {
294    const NUM_BLOCKS: usize;
295    fn write(&self, writer: &mut GpuBufferWriterF);
296}
297
298pub trait GpuBufferDataI {
299    const NUM_BLOCKS: usize;
300    fn write(&self, writer: &mut GpuBufferWriterI);
301}
302
303impl GpuBufferDataF for [f32; 4] {
304    const NUM_BLOCKS: usize = 1;
305    fn write(&self, writer: &mut GpuBufferWriterF) {
306        writer.push_one(*self);
307    }
308}
309
310impl GpuBufferDataI for [i32; 4] {
311    const NUM_BLOCKS: usize = 1;
312    fn write(&self, writer: &mut GpuBufferWriterI) {
313        writer.push_one(*self);
314    }
315}
316
317/// Record a patch to the GPU buffer for a render task
318struct DeferredBlock {
319    task_id: RenderTaskId,
320    index: usize,
321}
322
323/// Interface to allow writing multiple GPU blocks, possibly of different types
324pub struct GpuBufferWriter<'a, T> {
325    buffer: &'a mut FrameVec<T>,
326    deferred: &'a mut Vec<DeferredBlock>,
327    index: usize,
328    max_block_count: usize,
329    epoch: u32,
330}
331
332impl<'a, T> GpuBufferWriter<'a, T> where T: Texel {
333    fn new(
334        buffer: &'a mut FrameVec<T>,
335        deferred: &'a mut Vec<DeferredBlock>,
336        index: usize,
337        max_block_count: usize,
338        epoch: u32,
339    ) -> Self {
340        GpuBufferWriter {
341            buffer,
342            deferred,
343            index,
344            max_block_count,
345            epoch,
346        }
347    }
348
349    /// Push one (16 byte) block of data in to the writer
350    pub fn push_one<B>(&mut self, block: B) where B: Into<T> {
351        self.buffer.push(block.into());
352    }
353
354    /// Push a reference to a render task in to the writer. Once the render
355    /// task graph is resolved, this will be patched with the UV rect of the task
356    pub fn push_render_task(&mut self, task_id: RenderTaskId) {
357        if task_id != RenderTaskId::INVALID {
358            self.deferred.push(DeferredBlock {
359                task_id,
360                index: self.buffer.len(),
361            });
362        }
363
364        self.buffer.push(T::default());
365    }
366
367    /// Close this writer, returning the GPU address of this set of block(s).
368    pub fn finish(self) -> GpuBufferAddress {
369        assert!(self.buffer.len() <= self.index + self.max_block_count);
370
371        GpuBufferAddress(self.index as u32)
372    }
373
374    /// Close this writer, returning the GPU address of this set of block(s).
375    pub fn finish_with_handle(self) -> GpuBufferHandle {
376        assert!(self.buffer.len() <= self.index + self.max_block_count);
377        assert_eq!(self.index & (GpuBufferHandle::EPOCH_MASK as usize), 0);
378
379        GpuBufferHandle::new(self.index as u32, self.epoch)
380    }
381}
382
383impl<'a> GpuBufferWriterF<'a> {
384    pub fn push<Data: GpuBufferDataF>(&mut self, data: &Data) {
385        let _start_index = self.buffer.len();
386        data.write(self);
387        debug_assert_eq!(self.buffer.len() - _start_index, Data::NUM_BLOCKS);
388    }
389}
390
391impl<'a> GpuBufferWriterI<'a> {
392    pub fn push<Data: GpuBufferDataI>(&mut self, data: &Data) {
393        data.write(self);
394    }
395}
396
397impl<'a, T> Drop for GpuBufferWriter<'a, T> {
398    fn drop(&mut self) {
399        assert!(self.buffer.len() <= self.index + self.max_block_count, "Attempt to write too many GpuBuffer blocks");
400    }
401}
402
403pub struct GpuBufferBuilderImpl<T> {
404    // `data` will become the backing store of the GpuBuffer sent along
405    // with the frame so it uses the frame allocator.
406    data: FrameVec<T>,
407    // `deferred` is only used during frame building and not sent with the
408    // built frame, so it does not use the same allocator.
409    deferred: Vec<DeferredBlock>,
410
411    epoch: u32,
412}
413
414impl<T> GpuBufferBuilderImpl<T> where T: Texel + std::convert::From<DeviceIntRect> {
415    pub fn new(memory: &FrameMemory, capacity: usize, frame_id: FrameId) -> Self {
416        // Pick the first 8 bits of the frame id and store them in the upper bits
417        // of the handles.
418        let epoch = ((frame_id.as_u64() % 62) as u32 + 1) << 26;
419        GpuBufferBuilderImpl {
420            data: memory.new_vec_with_capacity(capacity),
421            deferred: Vec::new(),
422            epoch,
423        }
424    }
425
426    #[allow(dead_code)]
427    pub fn push_blocks(
428        &mut self,
429        blocks: &[T],
430    ) -> GpuBufferAddress {
431        assert!(blocks.len() <= MAX_VERTEX_TEXTURE_WIDTH);
432
433        ensure_row_capacity(&mut self.data, blocks.len());
434
435        let index = self.data.len();
436
437        self.data.extend_from_slice(blocks);
438
439        GpuBufferAddress(index as u32 | self.epoch)
440    }
441
442    /// Begin writing a specific number of blocks
443    pub fn write_blocks(
444        &mut self,
445        max_block_count: usize,
446    ) -> GpuBufferWriter<T> {
447        assert!(max_block_count <= MAX_VERTEX_TEXTURE_WIDTH);
448
449        ensure_row_capacity(&mut self.data, max_block_count);
450
451        let index = self.data.len();
452
453        GpuBufferWriter::new(
454            &mut self.data,
455            &mut self.deferred,
456            index,
457            max_block_count,
458            self.epoch,
459        )
460    }
461
462    // Reserve space in the gpu buffer for data that will be written by the
463    // renderer.
464    pub fn reserve_renderer_deferred_blocks(&mut self, block_count: usize) -> GpuBufferHandle {
465        ensure_row_capacity(&mut self.data, block_count);
466
467        let index = self.data.len();
468
469        self.data.reserve(block_count);
470        for _ in 0 ..block_count {
471            self.data.push(Default::default());
472        }
473
474        GpuBufferHandle::new(index as u32, self.epoch)
475    }
476
477    pub fn finalize(
478        mut self,
479        render_tasks: &RenderTaskGraph,
480    ) -> GpuBuffer<T> {
481        finish_row(&mut self.data);
482
483        let len = self.data.len();
484        assert!(len % MAX_VERTEX_TEXTURE_WIDTH == 0);
485
486        // At this point, we know that the render task graph has been built, and we can
487        // query the location of any dynamic (render target) or static (texture cache)
488        // task. This allows us to patch the UV rects in to the GPU buffer before upload
489        // to the GPU.
490        let mut deferred_uv_copies = Vec::new();
491        for block in self.deferred.drain(..) {
492            let render_task = &render_tasks[block.task_id];
493
494            // External images (for example Android SurfaceTexture sources) only have
495            // their uv rect resolved by the renderer, and it may be Y-flipped. The
496            // target rect computed below does not capture that, so instead defer copying
497            // the resolved uv rect (written by update_deferred_resolves into the task's
498            // uv_rect_handle block) into this segment block. See `apply_deferred_uv_copies`.
499            if let RenderTaskLocation::Static {
500                surface: StaticRenderTaskSurface::ReadOnly {
501                    source: TextureSource::External(TextureSourceExternal { normalized_uvs, .. }),
502                },
503                ..
504            } = render_task.location {
505                // The gpu buffer stores uv rects in device pixels, but the renderer
506                // writes normalized uvs for external images that use them. Scale by the
507                // image size (the external image task's target rect) during the copy.
508                let uv_scale = if normalized_uvs {
509                    let size = render_task.get_target_rect().size();
510                    [size.width as f32, size.height as f32]
511                } else {
512                    [1.0, 1.0]
513                };
514                deferred_uv_copies.push(DeferredUvCopy {
515                    src: render_task.get_texture_address().as_u32(),
516                    dst: block.index as u32,
517                    uv_scale,
518                });
519                continue;
520            }
521
522            let mut target_rect = render_task.get_target_rect();
523            if block.task_id.has_sub_rect() {
524                let sub = &render_tasks.sub_rects[block.task_id.sub_rect_index as usize];
525                target_rect = sub.sub_rect
526                    .translate(target_rect.min.to_vector())
527                    .intersection_unchecked(&target_rect);
528            }
529
530            let uv_rect = match render_task.uv_rect_kind() {
531                UvRectKind::Rect => {
532                    target_rect
533                }
534                UvRectKind::Quad { top_left, bottom_right, .. } => {
535                    let size = target_rect.size();
536
537                    DeviceIntRect::new(
538                        DeviceIntPoint::new(
539                            target_rect.min.x + (top_left.x * size.width as f32).round() as i32,
540                            target_rect.min.y + (top_left.y * size.height as f32).round() as i32,
541                        ),
542                        DeviceIntPoint::new(
543                            target_rect.min.x + (bottom_right.x * size.width as f32).round() as i32,
544                            target_rect.min.y + (bottom_right.y * size.height as f32).round() as i32,
545                        ),
546                    )
547                }
548            };
549
550            self.data[block.index] = uv_rect.into();
551        }
552
553        GpuBuffer {
554            data: self.data,
555            size: DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32, (len / MAX_VERTEX_TEXTURE_WIDTH) as i32),
556            format: T::image_format(),
557            deferred_uv_copies,
558            epoch: self.epoch,
559        }
560    }
561
562    pub fn resolve_handle(&self, handle: GpuBufferHandle) -> GpuBufferAddress {
563        if handle == GpuBufferHandle::INVALID {
564            return GpuBufferAddress::INVALID;
565        }
566
567        let epoch = handle.0 & GpuBufferHandle::EPOCH_MASK;
568        assert!(self.epoch == epoch);
569
570        GpuBufferAddress(handle.0 & !GpuBufferHandle::EPOCH_MASK)
571    }
572
573    /// Panics if the handle cannot be used this frame.
574    #[allow(unused)]
575    pub fn check_handle(&self, handle: GpuBufferHandle) {
576        if handle == GpuBufferHandle::INVALID {
577            return;
578        }
579        let epoch = handle.0 & GpuBufferHandle::EPOCH_MASK;
580        assert!(self.epoch == epoch);
581    }
582}
583
584impl GpuBufferBuilderF {
585    pub fn push<D>(&mut self, data: &D) -> GpuBufferAddress
586        where D: GpuBufferDataF
587    {
588        let mut writer = self.write_blocks(D::NUM_BLOCKS);
589        data.write(&mut writer);
590
591        writer.finish()
592    }
593}
594
595impl GpuBufferBuilderI {
596    pub fn push<D>(&mut self, data: &D) -> GpuBufferAddress
597        where D: GpuBufferDataI
598    {
599        let mut writer = self.write_blocks(D::NUM_BLOCKS);
600        data.write(&mut writer);
601
602        writer.finish()
603    }
604}
605
606fn ensure_row_capacity<T: Default>(data: &mut FrameVec<T>, cap: usize) {
607    if (data.len() % MAX_VERTEX_TEXTURE_WIDTH) + cap > MAX_VERTEX_TEXTURE_WIDTH {
608        finish_row(data);
609    }
610}
611
612fn finish_row<T: Default>(data: &mut FrameVec<T>) {
613    let required_len = (data.len() + MAX_VERTEX_TEXTURE_WIDTH-1) & !(MAX_VERTEX_TEXTURE_WIDTH-1);
614    for _ in 0 .. required_len - data.len() {
615        data.push(T::default());
616    }
617}
618
619/// Records that the uv rect block at `dst` must be overwritten with the block at
620/// `src` once the renderer has resolved external images.
621///
622/// TODO: This is a hack. At the end of frame building we resolve UVs from the
623/// render task graph, however this is too early to resolve the real UVs for
624/// external images (happens on the renderer thread). So this is an even-more-
625/// deferred step on top of the already deferred blocks.
626/// It would be cleaner to move the existing deferred mechanism later and avoid
627/// stacking another one on top, but the better fix would be to not write UV
628/// rects in the gpu buffer and pass render task handles to the quad shaders.
629#[cfg_attr(feature = "capture", derive(Serialize))]
630#[cfg_attr(feature = "replay", derive(Deserialize))]
631#[derive(Clone, Copy, Debug)]
632pub struct DeferredUvCopy {
633    pub src: u32,
634    pub dst: u32,
635    /// Per-axis scale applied to the copied uv rect. Used to convert the
636    /// renderer's normalized uvs (for external images that use them) into the
637    /// device pixels that the quad shaders expect. `[1.0, 1.0]` for uvs that
638    /// are already in device pixels.
639    pub uv_scale: [f32; 2],
640}
641
642#[cfg_attr(feature = "capture", derive(Serialize))]
643#[cfg_attr(feature = "replay", derive(Deserialize))]
644pub struct GpuBuffer<T> {
645    pub data: FrameVec<T>,
646    pub size: DeviceIntSize,
647    pub format: ImageFormat,
648    pub deferred_uv_copies: Vec<DeferredUvCopy>,
649    epoch: u32,
650}
651
652impl GpuBuffer<GpuBufferBlockF> {
653    /// Apply the uv rect copies deferred during `finalize`. Must be called after the
654    /// renderer has resolved external images into the gpu buffer.
655    pub fn apply_deferred_uv_copies(&mut self) {
656        for i in 0 .. self.deferred_uv_copies.len() {
657            let copy = self.deferred_uv_copies[i];
658            // The uv rect is stored as [p0.x, p0.y, p1.x, p1.y].
659            let mut uv = self.data[copy.src as usize].data;
660            uv[0] *= copy.uv_scale[0];
661            uv[1] *= copy.uv_scale[1];
662            uv[2] *= copy.uv_scale[0];
663            uv[3] *= copy.uv_scale[1];
664            self.data[copy.dst as usize] = uv.into();
665        }
666    }
667}
668
669impl<T> GpuBuffer<T> {
670    pub fn is_empty(&self) -> bool {
671        self.data.is_empty()
672    }
673
674    pub fn resolve_handle(&self, handle: GpuBufferHandle) -> GpuBufferAddress {
675        if handle == GpuBufferHandle::INVALID {
676            return GpuBufferAddress::INVALID;
677        }
678
679        let epoch = handle.0 & GpuBufferHandle::EPOCH_MASK;
680        assert!(self.epoch == epoch);
681
682        GpuBufferAddress(handle.0 & !GpuBufferHandle::EPOCH_MASK)
683    }
684}
685
686#[test]
687fn test_gpu_buffer_sizing_push() {
688    let frame_memory = FrameMemory::fallback();
689    let render_task_graph = RenderTaskGraph::new_for_testing();
690    let mut builder = GpuBufferBuilderF::new(&frame_memory, 0, FrameId::first());
691
692    let row = vec![GpuBufferBlockF::EMPTY; MAX_VERTEX_TEXTURE_WIDTH];
693    builder.push_blocks(&row);
694
695    builder.push_blocks(&[GpuBufferBlockF::EMPTY]);
696    builder.push_blocks(&[GpuBufferBlockF::EMPTY]);
697
698    let buffer = builder.finalize(&render_task_graph);
699    assert_eq!(buffer.data.len(), MAX_VERTEX_TEXTURE_WIDTH * 2);
700}
701
702#[test]
703fn test_gpu_buffer_sizing_writer() {
704    let frame_memory = FrameMemory::fallback();
705    let render_task_graph = RenderTaskGraph::new_for_testing();
706    let mut builder = GpuBufferBuilderF::new(&frame_memory, 0, FrameId::first());
707
708    let mut writer = builder.write_blocks(MAX_VERTEX_TEXTURE_WIDTH);
709    for _ in 0 .. MAX_VERTEX_TEXTURE_WIDTH {
710        writer.push_one(GpuBufferBlockF::EMPTY);
711    }
712    writer.finish();
713
714    let mut writer = builder.write_blocks(1);
715    writer.push_one(GpuBufferBlockF::EMPTY);
716    writer.finish();
717
718    let mut writer = builder.write_blocks(1);
719    writer.push_one(GpuBufferBlockF::EMPTY);
720    writer.finish();
721
722    let buffer = builder.finalize(&render_task_graph);
723    assert_eq!(buffer.data.len(), MAX_VERTEX_TEXTURE_WIDTH * 2);
724}