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