A fun experiment of coercing the SCSP DSP into doing 3D math.
"AVR" stands for "Audio Video Rendering". The name is inspired by the "urban legend" that SEGA AM2 were using ALL the chips inside the Saturn to push the performance to the limit and allow the port of Virtua Fighter 3 to Saturn, including the SCSP which is the audio processor. The SCSP includes a DSP and what likely happened is that the journalists at the time confused the SCU DSP (legitimately used to accelerate 3D T&L) and the SCSP DSP (for audio effects like echo and reverb).
It turns out that with a hefty dose of persuasion, the SCSP DSP can in fact perform 3D math, as at its code it has a fixed-point Multiply-Accumulate unit.
The code in this repository is an example of using the SCSP DSP to perform 3x4 matrix-vector multiplication on up to 5 parallel vertex streams at 44,100 Hz, freeing up SH-2 some cycles for other work.
There are three examples showing different use cases, from a simple cube to a multi-pass, multi-matrix animated object (Akira from the old SGL demos)
#include "libavr/avr.h"
// lib initialization
avrInit(NULL);
// setup a buffer for your vertices
AvrBuffer buf;
AvrBufferDesc desc = {
.nStreams = 5,
.nVerts = { 9, 9, 8, 8, 8 }, // distribute vertices across 5 streams
.outXyz = { out_x, out_y, out_z },
.outOffset = { 0, 9, 18, 26, 34 },
};
avrCreateBuffer(&buf, &desc);
// upload vertex data to Sound RAM
for (int s = 0; s < 5; s++)
avrUploadStream(&buf, s, &verts[offset[s]], desc.nVerts[s]);
// transform vertices and fetch them back from Sound RAM
for (;;) {
int16_t matrix[12] = { /* Q1.15 3x4 rotation+translation */ }; // You can specify a different matrix per stream
avrBeginUniform(&buf, matrix); // start the DSP (non-blocking)
// ... do stuff with the SH-2 while you wait
avrWait(&buf); // collect the results
avrPerspectiveDivide(out_x, out_y, out_z, 42, focal, z_near); // do (bad) perspective divide if you feel like it
// draw you newly minted vertices with the VDP1 or do something else...
}Requires sh2-elf-gcc and m68k-elf-gcc cross-compilers installed in a "toolchain" subdirectory (not included)
I personally use sh2-elf-gcc version 9.3.0 and m68k-elf-gcc version 6.3.0 for this project
Also requires genisoimage
I used very conservative compiler options, remember I only did this for fun, but if you find it useful there is plenty of space for optimization
avrInit: Initialize SCSP, boot 68K, sync DEC, load DSP program. Call once at startup after SMPC_CMD_SNDOFF
avrShutdown: Stop DSP lear state
avrReset: Reset buffer allocator. Call before creating new buffers (e.g., on level change)
avrCreateBuffer: Allocate SRAM region for a vertex batch. Bump-allocates from ring buffer. Returns 0 on success
avrDestroyBuffer: Release a buffer. As it's a bump allocation, it releases memory only if you destroy buffers in reverse order
avrUploadStream: Upload vertices to a specific stream (0-4)
Vertex data is uploaded once and persists in Sound RAM until the buffer is destroyed. No re-upload needed unless the vertex array changes.
avrBegin: Start DSP transform with per-stream 3x4 matrices. Returns immediately (non-blocking)
avrBeginUniform: Same matrix for all 5 streams
avrWait: Block until DSP finishes, then copy output from SRAM to caller's arrays
avrPerspectiveDivide: Pretty bad (and optional) perspective projection using SH-2 division unit
Matrices are int16_t[12] in row-major 3x4 layout (Q1.15 fixed-point):
[ m[0] m[1] m[2] m[3] ] [ x ]
[ m[4] m[5] m[6] m[7] ] x [ y ]
[ m[8] m[9] m[10] m[11] ] [ z ]
[ w ]
Where w = 256 (1.0) (constant, not stored per-vertex). Translation goes in
m[3], m[7], m[11]. Full-range translation: m[11] = 0x7FFF gives
z offset of ~256 units.
- A bunch of Sound RAM is used, by default 128KB, but it can be reduced by changing AVR_RING_RBL in avr_shared.h
- You can't use any DSP audio effects
- The 68k sound CPU is somewhat busy running interrupts
- MIXS channels must not be used
- SCSP slot register writes corrupt SRAM where 68k interrupt vectors are stored
- SCSP Timer A and B are used
- Q1.15 translation range
- I use the pre-built IP.BIN from Jo Engine (thanks Johannes Fetz for the great work over the years!)
- The genisoimage binary included in the tools folder is from Ubuntu and is GNU General Public License (GPL)