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
7    TODO:
8        Recycle GpuBuffers in a pool (support return from render thread)
9        Efficiently allow writing to buffer (better push interface)
10        Support other texel types (e.g. i32)
11
12 */
13
14use crate::gpu_types::UvRectKind;
15use crate::internal_types::{FrameMemory, FrameVec};
16use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH;
17use crate::util::ScaleOffset;
18use api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceRect, LayoutRect, PictureRect};
19use api::{PremultipliedColorF, ImageFormat};
20use crate::device::Texel;
21use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
22
23pub struct GpuBufferBuilder {
24    pub i32: GpuBufferBuilderI,
25    pub f32: GpuBufferBuilderF,
26}
27
28pub type GpuBufferF = GpuBuffer<GpuBufferBlockF>;
29pub type GpuBufferBuilderF = GpuBufferBuilderImpl<GpuBufferBlockF>;
30
31pub type GpuBufferI = GpuBuffer<GpuBufferBlockI>;
32pub type GpuBufferBuilderI = GpuBufferBuilderImpl<GpuBufferBlockI>;
33
34pub type GpuBufferWriterF<'l> = GpuBufferWriter<'l, GpuBufferBlockF>;
35//pub type GpuBufferWriterI<'l> = GpuBufferWriter<'l, GpuBufferBlockI>;
36
37unsafe impl Texel for GpuBufferBlockF {
38    fn image_format() -> ImageFormat { ImageFormat::RGBAF32 }
39}
40
41unsafe impl Texel for GpuBufferBlockI {
42    fn image_format() -> ImageFormat { ImageFormat::RGBAI32 }
43}
44
45impl Default for GpuBufferBlockF {
46    fn default() -> Self {
47        GpuBufferBlockF::EMPTY
48    }
49}
50
51impl Default for GpuBufferBlockI {
52    fn default() -> Self {
53        GpuBufferBlockI::EMPTY
54    }
55}
56
57/// A single texel in RGBAF32 texture - 16 bytes.
58#[derive(Copy, Clone, Debug, MallocSizeOf)]
59#[cfg_attr(feature = "capture", derive(Serialize))]
60#[cfg_attr(feature = "replay", derive(Deserialize))]
61pub struct GpuBufferBlockF {
62    data: [f32; 4],
63}
64
65/// A single texel in RGBAI32 texture - 16 bytes.
66#[derive(Copy, Clone, Debug, MallocSizeOf)]
67#[cfg_attr(feature = "capture", derive(Serialize))]
68#[cfg_attr(feature = "replay", derive(Deserialize))]
69pub struct GpuBufferBlockI {
70    data: [i32; 4],
71}
72
73#[derive(Copy, Debug, Clone, MallocSizeOf, Eq, PartialEq)]
74#[cfg_attr(feature = "capture", derive(Serialize))]
75#[cfg_attr(feature = "replay", derive(Deserialize))]
76pub struct GpuBufferAddress {
77    pub u: u16,
78    pub v: u16,
79}
80
81impl GpuBufferAddress {
82    #[allow(dead_code)]
83    pub fn as_int(self) -> i32 {
84        // TODO(gw): Temporarily encode GPU Cache addresses as a single int.
85        //           In the future, we can change the PrimitiveInstanceData struct
86        //           to use 2x u16 for the vertex attribute instead of an i32.
87        self.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + self.u as i32
88    }
89
90    pub const INVALID: GpuBufferAddress = GpuBufferAddress { u: !0, v: !0 };
91}
92
93impl GpuBufferBlockF {
94    pub const EMPTY: Self = GpuBufferBlockF { data: [0.0; 4] };
95}
96
97impl GpuBufferBlockI {
98    pub const EMPTY: Self = GpuBufferBlockI { data: [0; 4] };
99}
100
101impl Into<GpuBufferBlockF> for LayoutRect {
102    fn into(self) -> GpuBufferBlockF {
103        GpuBufferBlockF {
104            data: [
105                self.min.x,
106                self.min.y,
107                self.max.x,
108                self.max.y,
109            ],
110        }
111    }
112}
113
114impl Into<GpuBufferBlockF> for ScaleOffset {
115    fn into(self) -> GpuBufferBlockF {
116        GpuBufferBlockF {
117            data: [
118                self.scale.x,
119                self.scale.y,
120                self.offset.x,
121                self.offset.y,
122            ],
123        }
124    }
125}
126
127impl Into<GpuBufferBlockF> for PictureRect {
128    fn into(self) -> GpuBufferBlockF {
129        GpuBufferBlockF {
130            data: [
131                self.min.x,
132                self.min.y,
133                self.max.x,
134                self.max.y,
135            ],
136        }
137    }
138}
139
140impl Into<GpuBufferBlockF> for DeviceRect {
141    fn into(self) -> GpuBufferBlockF {
142        GpuBufferBlockF {
143            data: [
144                self.min.x,
145                self.min.y,
146                self.max.x,
147                self.max.y,
148            ],
149        }
150    }
151}
152
153impl Into<GpuBufferBlockF> for PremultipliedColorF {
154    fn into(self) -> GpuBufferBlockF {
155        GpuBufferBlockF {
156            data: [
157                self.r,
158                self.g,
159                self.b,
160                self.a,
161            ],
162        }
163    }
164}
165
166impl From<DeviceIntRect> for GpuBufferBlockF {
167    fn from(rect: DeviceIntRect) -> Self {
168        GpuBufferBlockF {
169            data: [
170                rect.min.x as f32,
171                rect.min.y as f32,
172                rect.max.x as f32,
173                rect.max.y as f32,
174            ],
175        }
176    }
177}
178
179impl From<DeviceIntRect> for GpuBufferBlockI {
180    fn from(rect: DeviceIntRect) -> Self {
181        GpuBufferBlockI {
182            data: [
183                rect.min.x,
184                rect.min.y,
185                rect.max.x,
186                rect.max.y,
187            ],
188        }
189    }
190}
191
192impl Into<GpuBufferBlockF> for [f32; 4] {
193    fn into(self) -> GpuBufferBlockF {
194        GpuBufferBlockF {
195            data: self,
196        }
197    }
198}
199
200impl Into<GpuBufferBlockI> for [i32; 4] {
201    fn into(self) -> GpuBufferBlockI {
202        GpuBufferBlockI {
203            data: self,
204        }
205    }
206}
207
208/// Record a patch to the GPU buffer for a render task
209struct DeferredBlock {
210    task_id: RenderTaskId,
211    index: usize,
212}
213
214/// Interface to allow writing multiple GPU blocks, possibly of different types
215pub struct GpuBufferWriter<'a, T> {
216    buffer: &'a mut FrameVec<T>,
217    deferred: &'a mut Vec<DeferredBlock>,
218    index: usize,
219    block_count: usize,
220}
221
222impl<'a, T> GpuBufferWriter<'a, T> where T: Texel {
223    fn new(
224        buffer: &'a mut FrameVec<T>,
225        deferred: &'a mut Vec<DeferredBlock>,
226        index: usize,
227        block_count: usize,
228    ) -> Self {
229        GpuBufferWriter {
230            buffer,
231            deferred,
232            index,
233            block_count,
234        }
235    }
236
237    /// Push one (16 byte) block of data in to the writer
238    pub fn push_one<B>(&mut self, block: B) where B: Into<T> {
239        self.buffer.push(block.into());
240    }
241
242    /// Push a reference to a render task in to the writer. Once the render
243    /// task graph is resolved, this will be patched with the UV rect of the task
244    pub fn push_render_task(&mut self, task_id: RenderTaskId) {
245        match task_id {
246            RenderTaskId::INVALID => {
247                self.buffer.push(T::default());
248            }
249            task_id => {
250                self.deferred.push(DeferredBlock {
251                    task_id,
252                    index: self.buffer.len(),
253                });
254                self.buffer.push(T::default());
255            }
256        }
257    }
258
259    /// Close this writer, returning the GPU address of this set of block(s).
260    pub fn finish(self) -> GpuBufferAddress {
261        assert_eq!(self.buffer.len(), self.index + self.block_count);
262
263        GpuBufferAddress {
264            u: (self.index % MAX_VERTEX_TEXTURE_WIDTH) as u16,
265            v: (self.index / MAX_VERTEX_TEXTURE_WIDTH) as u16,
266        }
267    }
268}
269
270impl<'a, T> Drop for GpuBufferWriter<'a, T> {
271    fn drop(&mut self) {
272        assert_eq!(self.buffer.len(), self.index + self.block_count, "Claimed block_count was not written");
273    }
274}
275
276pub struct GpuBufferBuilderImpl<T> {
277    // `data` will become the backing store of the GpuBuffer sent along
278    // with the frame so it uses the frame allocator.
279    data: FrameVec<T>,
280    // `deferred` is only used during frame building and not sent with the
281    // built frame, so it does not use the same allocator.
282    deferred: Vec<DeferredBlock>,
283}
284
285impl<T> GpuBufferBuilderImpl<T> where T: Texel + std::convert::From<DeviceIntRect> {
286    pub fn new(memory: &FrameMemory) -> Self {
287        GpuBufferBuilderImpl {
288            data: memory.new_vec(),
289            deferred: Vec::new(),
290        }
291    }
292
293    #[allow(dead_code)]
294    pub fn push(
295        &mut self,
296        blocks: &[T],
297    ) -> GpuBufferAddress {
298        assert!(blocks.len() <= MAX_VERTEX_TEXTURE_WIDTH);
299
300        if (self.data.len() % MAX_VERTEX_TEXTURE_WIDTH) + blocks.len() > MAX_VERTEX_TEXTURE_WIDTH {
301            while self.data.len() % MAX_VERTEX_TEXTURE_WIDTH != 0 {
302                self.data.push(T::default());
303            }
304        }
305
306        let index = self.data.len();
307
308        self.data.extend_from_slice(blocks);
309
310        GpuBufferAddress {
311            u: (index % MAX_VERTEX_TEXTURE_WIDTH) as u16,
312            v: (index / MAX_VERTEX_TEXTURE_WIDTH) as u16,
313        }
314    }
315
316    /// Begin writing a specific number of blocks
317    pub fn write_blocks(
318        &mut self,
319        block_count: usize,
320    ) -> GpuBufferWriter<T> {
321        assert!(block_count <= MAX_VERTEX_TEXTURE_WIDTH);
322
323        if (self.data.len() % MAX_VERTEX_TEXTURE_WIDTH) + block_count > MAX_VERTEX_TEXTURE_WIDTH {
324            while self.data.len() % MAX_VERTEX_TEXTURE_WIDTH != 0 {
325                self.data.push(T::default());
326            }
327        }
328
329        let index = self.data.len();
330
331        GpuBufferWriter::new(
332            &mut self.data,
333            &mut self.deferred,
334            index,
335            block_count,
336        )
337    }
338
339    pub fn finalize(
340        mut self,
341        render_tasks: &RenderTaskGraph,
342    ) -> GpuBuffer<T> {
343        let required_len = (self.data.len() + MAX_VERTEX_TEXTURE_WIDTH-1) & !(MAX_VERTEX_TEXTURE_WIDTH-1);
344
345        for _ in 0 .. required_len - self.data.len() {
346            self.data.push(T::default());
347        }
348
349        let len = self.data.len();
350        assert!(len % MAX_VERTEX_TEXTURE_WIDTH == 0);
351
352        // At this point, we know that the render task graph has been built, and we can
353        // query the location of any dynamic (render target) or static (texture cache)
354        // task. This allows us to patch the UV rects in to the GPU buffer before upload
355        // to the GPU.
356        for block in self.deferred.drain(..) {
357            let render_task = &render_tasks[block.task_id];
358            let target_rect = render_task.get_target_rect();
359
360            let uv_rect = match render_task.uv_rect_kind() {
361                UvRectKind::Rect => {
362                    target_rect
363                }
364                UvRectKind::Quad { top_left, bottom_right, .. } => {
365                    let size = target_rect.size();
366
367                    DeviceIntRect::new(
368                        DeviceIntPoint::new(
369                            target_rect.min.x + (top_left.x * size.width as f32).round() as i32,
370                            target_rect.min.y + (top_left.y * size.height as f32).round() as i32,
371                        ),
372                        DeviceIntPoint::new(
373                            target_rect.min.x + (bottom_right.x * size.width as f32).round() as i32,
374                            target_rect.min.y + (bottom_right.y * size.height as f32).round() as i32,
375                        ),
376                    )
377                }
378            };
379
380            self.data[block.index] = uv_rect.into();
381        }
382
383        GpuBuffer {
384            data: self.data,
385            size: DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32, (len / MAX_VERTEX_TEXTURE_WIDTH) as i32),
386            format: T::image_format(),
387        }
388    }
389}
390
391#[cfg_attr(feature = "capture", derive(Serialize))]
392#[cfg_attr(feature = "replay", derive(Deserialize))]
393pub struct GpuBuffer<T> {
394    pub data: FrameVec<T>,
395    pub size: DeviceIntSize,
396    pub format: ImageFormat,
397}
398
399impl<T> GpuBuffer<T> {
400    pub fn is_empty(&self) -> bool {
401        self.data.is_empty()
402    }
403}
404
405#[test]
406fn test_gpu_buffer_sizing_push() {
407    let frame_memory = FrameMemory::fallback();
408    let render_task_graph = RenderTaskGraph::new_for_testing();
409    let mut builder = GpuBufferBuilderF::new(&frame_memory);
410
411    let row = vec![GpuBufferBlockF::EMPTY; MAX_VERTEX_TEXTURE_WIDTH];
412    builder.push(&row);
413
414    builder.push(&[GpuBufferBlockF::EMPTY]);
415    builder.push(&[GpuBufferBlockF::EMPTY]);
416
417    let buffer = builder.finalize(&render_task_graph);
418    assert_eq!(buffer.data.len(), MAX_VERTEX_TEXTURE_WIDTH * 2);
419}
420
421#[test]
422fn test_gpu_buffer_sizing_writer() {
423    let frame_memory = FrameMemory::fallback();
424    let render_task_graph = RenderTaskGraph::new_for_testing();
425    let mut builder = GpuBufferBuilderF::new(&frame_memory);
426
427    let mut writer = builder.write_blocks(MAX_VERTEX_TEXTURE_WIDTH);
428    for _ in 0 .. MAX_VERTEX_TEXTURE_WIDTH {
429        writer.push_one(GpuBufferBlockF::EMPTY);
430    }
431    writer.finish();
432
433    let mut writer = builder.write_blocks(1);
434    writer.push_one(GpuBufferBlockF::EMPTY);
435    writer.finish();
436
437    let mut writer = builder.write_blocks(1);
438    writer.push_one(GpuBufferBlockF::EMPTY);
439    writer.finish();
440
441    let buffer = builder.finalize(&render_task_graph);
442    assert_eq!(buffer.data.len(), MAX_VERTEX_TEXTURE_WIDTH * 2);
443}