dbsdk_rs/
vdp.rs

1use std::convert::TryInto;
2use std::fmt::Debug;
3
4use crate::db_internal::{vdp_allocRenderTexture, vdp_allocTexture, vdp_bindTexture, vdp_bindTextureSlot, vdp_blendEquation, vdp_blendFunc, vdp_clearColor, vdp_clearDepth, vdp_copyFbToTexture, vdp_depthFunc, vdp_depthWrite, vdp_drawGeometry, vdp_drawGeometryPacked, vdp_getDepthQueryResult, vdp_getUsage, vdp_releaseTexture, vdp_setCulling, vdp_setRenderTarget, vdp_setSampleParams, vdp_setSampleParamsSlot, vdp_setTexCombine, vdp_setTextureData, vdp_setTextureDataRegion, vdp_setTextureDataYUV, vdp_setVUCData, vdp_setVULayout, vdp_setVUStride, vdp_setVsyncHandler, vdp_setWinding, vdp_submitDepthQuery, vdp_submitVU, vdp_uploadVUProgram, vdp_viewport};
5use crate::math::{Vector4, Vector2};
6
7static mut VSYNC_HANDLER: Option<fn()> = Option::None;
8
9#[repr(C)]
10#[derive(Clone, Copy)]
11pub struct Color32 {
12    pub r: u8,
13    pub g: u8,
14    pub b: u8,
15    pub a: u8,
16}
17
18impl Color32 {
19    pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Color32 {
20        return Color32 { r: r, g: g, b: b, a: a };
21    }
22}
23
24#[repr(C)]
25#[derive(Clone, Copy)]
26pub struct Vertex {
27    pub position: Vector4,
28    pub color: Vector4,
29    pub ocolor: Vector4,
30    pub texcoord: Vector4,
31}
32
33impl Vertex {
34    pub const fn new(position: Vector4, color: Vector4, ocolor: Vector4, texcoord: Vector4) -> Vertex {
35        return Vertex { position: position, color: color, ocolor: ocolor, texcoord: texcoord };
36    }
37}
38
39#[repr(C)]
40#[derive(Clone, Copy)]
41pub struct PackedVertex {
42    pub position: Vector4,
43    pub texcoord: Vector2,
44    pub color: Color32,
45    pub ocolor: Color32,
46}
47
48impl PackedVertex {
49    pub const fn new(position: Vector4, texcoord: Vector2, color: Color32, ocolor: Color32) -> PackedVertex {
50        return PackedVertex { position: position, texcoord: texcoord, color: color, ocolor: ocolor };
51    }
52}
53
54#[repr(C)]
55#[derive(Clone, Copy)]
56pub struct Rectangle {
57    pub x: i32,
58    pub y: i32,
59    pub width: i32,
60    pub height: i32,
61}
62
63impl Rectangle {
64    pub const fn new(x: i32, y: i32, width: i32, height: i32) -> Rectangle {
65        return Rectangle { x: x, y: y, width: width, height: height };
66    }
67}
68
69#[derive(Clone, Copy, Debug)]
70pub enum TextureError {
71    DimensionsInvalid,
72    AllocationFailed
73}
74
75pub trait DBTex {
76    fn get_handle(self: &Self) -> i32;
77}
78
79#[repr(C)]
80pub struct Texture {
81    pub format: TextureFormat,
82    pub width: i32,
83    pub height: i32,
84    pub mipmap: bool,
85    handle: i32,
86}
87
88#[repr(C)]
89pub struct RenderTexture {
90    pub width: i32,
91    pub height: i32,
92    handle: i32,
93}
94
95impl DBTex for Texture {
96    fn get_handle(self: &Self) -> i32 {
97        self.handle
98    }
99}
100
101impl DBTex for RenderTexture {
102    fn get_handle(self: &Self) -> i32 {
103        self.handle
104    }
105}
106
107impl Texture {
108    pub fn new(width: i32, height: i32, mipmap: bool, format: TextureFormat) -> Result<Texture,TextureError> {
109        // dimensions must be power of two (unless this is a YUV420 image)
110        if format != TextureFormat::YUV420 && ((width & (width - 1)) != 0 || (height & (height - 1)) != 0) {
111            return Result::Err(TextureError::DimensionsInvalid);
112        }
113
114        // allocate and check to see if allocation failed
115        let handle = unsafe { vdp_allocTexture(mipmap, format, width, height) };
116        if handle == -1 {
117            return Result::Err(TextureError::AllocationFailed);
118        }
119
120        return Result::Ok(Texture {
121            format: format,
122            mipmap: mipmap,
123            width: width,
124            height: height,
125            handle: handle
126        });
127    }
128
129    /// Upload texture data for the given mip level of this texture
130    pub fn set_texture_data<T>(&self, level: i32, data: &[T]) {
131        unsafe {
132            let len_bytes = data.len() * size_of::<T>();
133            vdp_setTextureData(self.handle, level, data.as_ptr().cast(), len_bytes.try_into().unwrap());
134        }
135    }
136
137    /// Upload individual planes for this YUV texture
138    pub fn set_texture_data_yuv(&self, y_data: &[u8], u_data: &[u8], v_data: &[u8]) {
139        unsafe {
140            vdp_setTextureDataYUV(self.handle, 
141                y_data.as_ptr().cast(), y_data.len().try_into().unwrap(),
142                u_data.as_ptr().cast(), u_data.len().try_into().unwrap(),
143                v_data.as_ptr().cast(), v_data.len().try_into().unwrap());
144        }
145    }
146
147    /// Upload texture data for the given mip level and region of this texture
148    pub fn set_texture_data_region<T>(&self, level: i32, dst_rect: Option<Rectangle>, data: &[T]) {
149        unsafe {
150            match dst_rect {
151                Some(v) => {
152                    let len_bytes = data.len() * size_of::<T>();
153                    vdp_setTextureDataRegion(self.handle, level, &v, data.as_ptr().cast(), len_bytes.try_into().unwrap());
154                }
155                None => {
156                    vdp_setTextureData(self.handle, level, data.as_ptr().cast(), data.len().try_into().unwrap());
157                }
158            }
159        }
160    }
161
162    /// Copy a region of the framebuffer into a region of the given texture
163    pub fn copy_framebuffer_to_texture(target: &Texture, src_rect: Rectangle, dst_rect: Rectangle) {
164        unsafe {
165            vdp_copyFbToTexture(&src_rect, &dst_rect, target.handle);
166        }
167    }
168}
169
170impl Drop for Texture {
171    fn drop(&mut self) {
172        unsafe { vdp_releaseTexture(self.handle) };
173    }
174}
175
176impl RenderTexture {
177    pub fn new(width: i32, height: i32) -> Result<RenderTexture,TextureError> {
178        // dimensions must be power of two
179        if (width & (width - 1)) != 0 || (height & (height - 1)) != 0 {
180            return Result::Err(TextureError::DimensionsInvalid);
181        }
182
183        // allocate and check to see if allocation failed
184        let handle = unsafe { vdp_allocRenderTexture(width, height) };
185        if handle == -1 {
186            return Result::Err(TextureError::AllocationFailed);
187        }
188
189        return Result::Ok(RenderTexture {
190            width: width,
191            height: height,
192            handle: handle
193        });
194    }
195}
196
197impl Drop for RenderTexture {
198    fn drop(&mut self) {
199        unsafe { vdp_releaseTexture(self.handle) };
200    }
201}
202
203#[repr(C)]
204#[derive(Clone, Copy)]
205pub enum Compare {
206    Never           = 0x0200,
207    Less            = 0x0201,
208    Equal           = 0x0202,
209    LessOrEqual     = 0x0203,
210    Greater         = 0x0204,
211    NotEqual        = 0x0205,
212    GreaterOrEqual  = 0x0206,
213    Always          = 0x0207,
214}
215
216#[repr(C)]
217#[derive(Clone, Copy)]
218pub enum BlendEquation {
219    Add                 = 0x8006,
220    Subtract            = 0x800A,
221    ReverseSubtract     = 0x800B,
222}
223
224#[repr(C)]
225#[derive(Clone, Copy)]
226pub enum BlendFactor {
227    Zero                = 0,
228    One                 = 1,
229    SrcColor            = 0x0300,
230    OneMinusSrcColor    = 0x0301,
231    SrcAlpha            = 0x0302,
232    OneMinusSrcAlpha    = 0x0303,
233    DstAlpha            = 0x0304,
234    OneMinusDstAlpha    = 0x0305,
235    DstColor            = 0x0306,
236    OneMinusDstColor    = 0x0307,
237}
238
239#[repr(C)]
240#[derive(Clone, Copy)]
241pub enum WindingOrder {
242    Clockwise  = 0x0900,
243    CounterClockwise = 0x0901,
244}
245
246#[repr(C)]
247#[derive(Clone, Copy)]
248pub enum Topology {
249    LineList       = 0x0000,
250    LineStrip      = 0x0001,
251    TriangleList   = 0x0002,
252    TriangleStrip  = 0x0003,
253}
254
255#[repr(C)]
256#[derive(Clone, Copy)]
257#[derive(PartialEq)]
258pub enum TextureFormat {
259    RGB565   = 0,
260    RGBA4444 = 1,
261    RGBA8888 = 2,
262    DXT1     = 3,
263    DXT3     = 4,
264    YUV420   = 5,
265}
266
267#[repr(C)]
268#[derive(Clone, Copy)]
269pub enum TextureFilter {
270    Nearest     = 0x2600,
271    Linear      = 0x2601,
272}
273
274#[repr(C)]
275#[derive(Clone, Copy)]
276pub enum TextureWrap {
277    Clamp       = 0x812F,
278    Repeat      = 0x2901,
279    Mirror      = 0x8370,
280}
281
282#[repr(C)]
283#[derive(Clone, Copy)]
284#[derive(PartialEq)]
285pub enum VertexSlotFormat {
286    FLOAT1   = 0,
287    FLOAT2   = 1,
288    FLOAT3   = 2,
289    FLOAT4   = 3,
290    UNORM4   = 4,
291    SNORM4   = 5,
292}
293
294#[repr(C)]
295#[derive(Clone, Copy)]
296#[derive(PartialEq)]
297pub enum TexCombine {
298    None    = 0,
299    Mul     = 1,
300    Add     = 2,
301    Sub     = 3,
302    Mix     = 4,
303    Dot3    = 5,
304}
305
306#[repr(C)]
307#[derive(Clone, Copy)]
308#[derive(PartialEq)]
309pub enum TextureUnit {
310    TU0 = 0,
311    TU1 = 1,
312}
313
314unsafe extern "C" fn real_vsync_handler() {
315    if let Some(handler) = VSYNC_HANDLER {
316        handler();
317    }
318}
319
320/// Clear the backbuffer to the given color
321pub fn clear_color(color: Color32) {
322    unsafe { vdp_clearColor(&color); }
323}
324
325/// Clear the depth buffer to the given depth value
326pub fn clear_depth(depth: f32) {
327    unsafe { vdp_clearDepth(depth); }
328}
329
330/// Set whether depth writes are enabled
331pub fn depth_write(enable: bool) {
332    unsafe { vdp_depthWrite(enable) };
333}
334
335/// Set the current depth test comparison
336pub fn depth_func(compare: Compare) {
337    unsafe { vdp_depthFunc(compare) };
338}
339
340/// Set the blend equation mode
341pub fn blend_equation(mode: BlendEquation) {
342    unsafe { vdp_blendEquation(mode) };
343}
344
345/// Set the source and destination blend factors
346pub fn blend_func(src_factor: BlendFactor, dst_factor: BlendFactor) {
347    unsafe { vdp_blendFunc(src_factor, dst_factor) };
348}
349
350/// Set the winding order for backface culling
351pub fn set_winding(winding: WindingOrder) {
352    unsafe { vdp_setWinding(winding) };
353}
354
355/// Set backface culling enabled or disabled
356pub fn set_culling(enabled: bool) {
357    unsafe { vdp_setCulling(enabled) };
358}
359
360/// Submit a buffer of geometry to draw
361#[deprecated(since = "0.1.16", note = "New apps should prefer a custom vertex layout in combination with submit_vu instead")]
362pub fn draw_geometry(topology: Topology, vertex_data: &[Vertex]) {
363    unsafe { vdp_drawGeometry(topology, 0, vertex_data.len().try_into().unwrap(), vertex_data.as_ptr()) };
364}
365
366/// Submit a buffer of geometry to draw
367#[deprecated(since = "0.1.16", note = "Use submit_vu instead")]
368pub fn draw_geometry_packed(topology: Topology, vertex_data: &[PackedVertex]) {
369    unsafe { vdp_drawGeometryPacked(topology, 0, vertex_data.len().try_into().unwrap(), vertex_data.as_ptr()) };
370}
371
372/// Get total texture memory usage in bytes
373pub fn get_usage() -> i32 {
374    unsafe { return vdp_getUsage() };
375}
376
377/// Set currently active texture sampling parameters
378#[deprecated(since = "0.1.16", note = "Use set_sample_params_slot instead")]
379pub fn set_sample_params(filter: TextureFilter, wrap_u: TextureWrap, wrap_v: TextureWrap) {
380    unsafe { vdp_setSampleParams(filter, wrap_u, wrap_v) };
381}
382
383/// Bind a texture for drawing
384#[deprecated(since = "0.1.16", note = "Use bind_texture_slot instead")]
385pub fn bind_texture(texture: Option<&Texture>) {
386    if texture.is_some() {
387        unsafe { vdp_bindTexture(texture.unwrap().handle) };
388    } else {
389        unsafe { vdp_bindTexture(-1) };
390    }
391}
392
393/// Set the current viewport rect
394pub fn viewport(rect: Rectangle) {
395    unsafe {
396        vdp_viewport(rect.x, rect.y, rect.width, rect.height);
397    }
398}
399
400/// Compare a region of the depth buffer against the given reference value
401pub fn submit_depth_query(ref_val: f32, compare: Compare, rect: Rectangle) {
402    unsafe {
403        vdp_submitDepthQuery(ref_val, compare, rect.x, rect.y, rect.width, rect.height);
404    }
405}
406
407/// Get the number of pixels which passed the submitted depth query
408pub fn get_depth_query_result() -> i32 {
409    unsafe {
410        return vdp_getDepthQueryResult();
411    }
412}
413
414/// Set one of VU's 16 const data slots to given vector
415pub fn set_vu_cdata(offset: usize, data: &Vector4) {
416    unsafe {
417        vdp_setVUCData(offset as i32, (data as *const Vector4).cast());
418    }
419}
420
421/// Configure input vertex element slot layout
422pub fn set_vu_layout(slot: usize, offset: usize, format: VertexSlotFormat) {
423    unsafe {
424        vdp_setVULayout(slot as i32, offset as i32, format);
425    }
426}
427
428/// Set stride of input vertex data (size of each vertex in bytes)
429pub fn set_vu_stride(stride: usize) {
430    unsafe {
431        vdp_setVUStride(stride as i32);
432    }
433}
434
435/// Upload a new VU program
436pub fn upload_vu_program(program: &[u32]) {
437    unsafe {
438        let program_len = program.len() * 4;
439        vdp_uploadVUProgram(program.as_ptr().cast(), program_len as i32);
440    }
441}
442
443/// Submit geometry to be processed by the VU
444pub fn submit_vu<T>(topology: Topology, data: &[T]) {
445    unsafe {
446        let data_len = data.len() * size_of::<T>();
447        vdp_submitVU(topology, data.as_ptr().cast(), data_len as i32);
448    }
449}
450
451/// Set the current render target
452pub fn set_render_target(texture: Option<RenderTexture>) {
453    unsafe {
454        match texture {
455            Some(v) => {
456                vdp_setRenderTarget(v.handle);
457            }
458            None => {
459                vdp_setRenderTarget(-1);
460            }
461        }
462    }
463}
464
465/// Set texture sample params for the given texture unit
466pub fn set_sample_params_slot(slot: TextureUnit, filter: TextureFilter, wrap_u: TextureWrap, wrap_v: TextureWrap) {
467    unsafe {
468        vdp_setSampleParamsSlot(slot, filter, wrap_u, wrap_v);
469    }
470}
471
472/// Bind a texture to the given texture unit
473pub fn bind_texture_slot<T: DBTex>(slot: TextureUnit, texture: Option<&T>) {
474    unsafe {
475        match texture {
476            Some(v) => {
477                vdp_bindTextureSlot(slot, v.get_handle());
478            }
479            None => {
480                vdp_bindTextureSlot(slot, -1);
481            }
482        }
483    }
484}
485
486/// Set texture combiner mode
487pub fn set_tex_combine(tex_combine: TexCombine, vtx_combine: TexCombine) {
488    unsafe {
489        vdp_setTexCombine(tex_combine, vtx_combine);
490    }
491}
492
493/// Set an optional handler for vertical sync
494pub fn set_vsync_handler(handler: Option<fn()>) {
495    unsafe {
496        VSYNC_HANDLER = handler;
497        vdp_setVsyncHandler(real_vsync_handler);
498    }
499}