// Copyright 2018 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::mem::size_of; use std::os::raw::c_void; use std::slice::{from_raw_parts, from_raw_parts_mut}; use generated::virgl_protocol::*; use Resource; /// Helper struct for making a virgl command buffer. #[derive(Default)] pub struct CommandBufferBuilder { cbuf: Vec, } impl AsRef<[u8]> for CommandBufferBuilder { fn as_ref(&self) -> &[u8] { // Safe because the returned slice is a trivial reinterpretation of the same number of // bytes. unsafe { from_raw_parts(self.cbuf.as_ptr() as *const u8, self.cbuf.len() * size_of::()) } } } impl AsMut<[u8]> for CommandBufferBuilder { fn as_mut(&mut self) -> &mut [u8] { // Safe because the returned slice is a trivial reinterpretation of the same number of // bytes. unsafe { from_raw_parts_mut(self.cbuf.as_mut_ptr() as *mut u8, self.cbuf.len() * size_of::()) } } } impl CommandBufferBuilder { /// Constructs an empty command pub fn new() -> CommandBufferBuilder { Default::default() } fn push(&mut self, dw: u32) { self.cbuf.push(dw); } fn push_qw(&mut self, qw: u64) { self.cbuf.push(qw as u32); self.cbuf.push((qw >> 32) as u32); } fn push_cmd(&mut self, cmd: u32, obj_type: u32, len: u32) { self.cbuf.push((cmd & 0xff) | ((obj_type & 0xff) << 8) | ((len & 0xffff) << 16)); } /// Gets the command buffer as a pointer to the beginning. pub fn as_mut_ptr(&mut self) -> *mut c_void { self.cbuf.as_mut_ptr() as *mut c_void } /// Gets the size of the command buffer content in dwords. pub fn dword_count(&self) -> usize { self.cbuf.len() } /// Clears the command buffer content. pub fn clear(&mut self) { self.cbuf.clear(); } /// Checks that the command buffer is well formed. pub fn is_valid(&self) -> bool { let mut i = 0; while i < self.cbuf.len() { i += 1 + (self.cbuf[i] >> 16) as usize; } i == self.cbuf.len() } /// Pushes a clear command to this command buffer. pub fn e_clear(&mut self, buffers: u32, color: [f32; 4], depth: f64, stencil: u32) { self.push_cmd(VIRGL_CCMD_CLEAR, 0, VIRGL_OBJ_CLEAR_SIZE); self.push(buffers); for &c in color.iter() { self.push(c.to_bits()) } self.push_qw(depth.to_bits()); self.push(stencil); assert!(self.is_valid()); } /// Pushes a create surface command to this command buffer. pub fn e_create_surface(&mut self, new_id: u32, res: &Resource, format: u32, level: u32, first_layer: u32, last_layer: u32) { self.push_cmd(VIRGL_CCMD_CREATE_OBJECT, VIRGL_OBJECT_SURFACE, VIRGL_OBJ_SURFACE_SIZE); self.push(new_id); self.push(res.id()); self.push(format); self.push(level); self.push(first_layer | (last_layer << 16)); assert!(self.is_valid()); } /// Pushes a set framebuffer state command to this command buffer. pub fn e_set_fb_state(&mut self, surface_handles: &[u32], zbuf: Option) { fn cmd_set_fb_state_size(surface_count: u32) -> u32 { 2 + surface_count } self.push_cmd(VIRGL_CCMD_SET_FRAMEBUFFER_STATE, 0, cmd_set_fb_state_size(surface_handles.len() as u32)); self.push(surface_handles.len() as u32); self.push(zbuf.unwrap_or(0)); for &surface_handle in surface_handles { self.push(surface_handle); } assert!(self.is_valid()); } }