Skip to content

Commit

Permalink
* audio bus implementation
Browse files Browse the repository at this point in the history
* photo improvements
* modified mode switch sfx
  • Loading branch information
BeauchesneFieldDay committed Dec 12, 2024
1 parent bf70735 commit d11ceed
Show file tree
Hide file tree
Showing 45 changed files with 890 additions and 194 deletions.
13 changes: 12 additions & 1 deletion Assets/Code/Tablet/Photo/TabletPhoto.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
using System;
using BeauPools;
using BeauRoutine;
using BeauUtil;
using BeauUtil.Debugger;
using FieldDay.Assets;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Experimental.Rendering;

namespace Pennycook.Tablet {
public class TabletPhoto {
public class TabletPhoto : IPoolConstructHandler {
public Texture2D Texture;
public AsyncHandle UploadHandle;
public string Tag;
Expand All @@ -16,6 +18,15 @@ public class TabletPhoto {
public Vector2Int CachedSize;
public GraphicsFormat CachedFormat;
public NativeArray<byte> CachedCPUData;

void IPoolConstructHandler.OnConstruct() {
}

void IPoolConstructHandler.OnDestruct() {
UploadHandle.Cancel();
UnityHelper.SafeDestroy(ref Texture);
CachedCPUData = default;
}
}

static public partial class PhotoUtility {
Expand Down
13 changes: 13 additions & 0 deletions Assets/Code/Tablet/Photo/TabletPhotoAnimation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using UnityEngine;
using UnityEngine.UI;

namespace Pennycook.Tablet {
public sealed class TabletPhotoAnimation : MonoBehaviour {
public LayoutOffset Offset;
public CanvasGroup Group;
public RawImage PhotoRenderer;

public Material DefaultMaterial;
public Material FailureMaterial;
}
}
11 changes: 11 additions & 0 deletions Assets/Code/Tablet/Photo/TabletPhotoAnimation.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Assets/Code/Tablet/Photo/TabletPhotoState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ public sealed class TabletPhotoState : SharedStateComponent, IRegistrationCallba
public Canvas[] UICanvases;
public Material TabletMaterial;
public CanvasGroup Flash;
public TabletPhotoAnimation Animation;

[NonSerialized] public Stage CurrentStage;
[NonSerialized] public double NextAllowedPhotoTS;
[NonSerialized] public float Cooldown;

[NonSerialized] public TabletPhoto QueuedPhoto;
[NonSerialized] public FixedPool<TabletPhoto> PhotoPool;
[NonSerialized] public RingBuffer<TabletPhoto> ActivePhotos;

Expand Down Expand Up @@ -75,6 +77,7 @@ static public void TakePhoto(double ts) {
TabletPhotoState photoState = Find.State<TabletPhotoState>();
RequestPhoto(photoState);
photoState.NextAllowedPhotoTS = ts + 2;
photoState.QueuedPhoto = photoState.PhotoPool.Alloc();
TabletUtility.PlaySfx("Tablet.Photo.Snap");
}

Expand Down
5 changes: 3 additions & 2 deletions Assets/Code/Tablet/Photo/TabletPhotoSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class TabletPhotoSystem : SharedStateSystemBehaviour<TabletPhotoSt
public override void ProcessWork(float deltaTime) {
if (GameLoop.IsPhase(GameLoopPhase.PreUpdate)) {
if (m_State.CurrentStage == TabletPhotoState.Stage.PhotoReady) {
TabletPhoto photo = m_State.PhotoPool.Alloc();
TabletPhoto photo = m_State.QueuedPhoto;
PhotoUtility.CopyRTToTextureCentered(m_State.PhotoRT, photo.Texture);
photo.Texture.Apply();
UploadPhoto(photo, DateTime.Now);
Expand All @@ -26,6 +26,7 @@ public override void ProcessWork(float deltaTime) {
m_State.Flash.alpha = 1;
m_State.CurrentStage = TabletPhotoState.Stage.Cooldown;
m_State.Cooldown = 1f;
m_State.QueuedPhoto = null;
}
return;
}
Expand Down Expand Up @@ -65,7 +66,7 @@ static private IEnumerator EncodeAndUpload(TabletPhoto photo) {
#if UNITY_EDITOR
directory = "DebugPhotos/";
#else
directory = Path.Combine(Application.persistentDataPath, "/Photos/");
directory = Path.Combine(Application.persistentDataPath, "Photos/");
#endif // UNITY_EDITOR

Directory.CreateDirectory(directory);
Expand Down
51 changes: 48 additions & 3 deletions Assets/FieldDay/Audio/AudioMgr.Bus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,45 @@
using BeauRoutine;
using BeauUtil;
using BeauUtil.Debugger;
using FieldDay.Mathematics;
using UnityEngine;

namespace FieldDay.Audio {
public sealed partial class AudioMgr {
#region Bus Data

private unsafe struct BusData {
public AudioPropertyBlock BusProperties;
public AudioPropertyBlock* ScriptProperties;
public float ConfigVolume;
public int ParentIndex;
public float ConfigVolume;

public AudioPropertyBlock LastKnownProperties;

public UniqueId16 Handle;
public FloatTweenIndices FloatTweens;
public StringHash32 Name;
}

#endregion // Bus Data

#region Bus Creation

private int CreateBus(StringHash32 id) {
private void CreateBus(StringHash32 id, AudioPropertyBlock busProperties, StringHash32 parentId) {
if (m_BusCount >= MaxBuses) {
throw new InvalidOperationException("Maximum number of audio buses created");
}

int idx = m_BusCount++;
m_BusNameToIndex.Add(id.HashValue, idx);
return idx;
m_BusData[idx].Name = id;
m_BusData[idx].BusProperties = busProperties;

if (!parentId.IsEmpty) {
m_BusData[idx].ParentIndex = FindBusIndexForId(parentId);
}

Log.Msg("[AudioMgr] Created bus '{0}'", id.ToDebugString());
}

#endregion // Bus Creation
Expand Down Expand Up @@ -70,18 +80,53 @@ private int FindBusIndexForId(StringHash32 id) {
#region Bindings

private void ProcessLateBindings() {
// create buses
if (m_BusLateBindQueue.Count > 0) {
ProcessBusDependencies();
}

while (m_EventLateBindQueue.TryPopFront(out AudioEvent evt)) {
evt.CachedBusIndex = FindBusIndexForId(evt.Bus);
}
}

private unsafe void ProcessBusDependencies() {
int busCount = m_BusLateBindQueue.Count;
DependencySolver.Node<StringHash32>* nodes = stackalloc DependencySolver.Node<StringHash32>[busCount];
DependencySolver.Edge<StringHash32>* edges = stackalloc DependencySolver.Edge<StringHash32>[busCount];

for(int i = 0; i < busCount; i++) {
AudioBus bus = m_BusLateBindQueue[i];
nodes[i].Id = bus.AssetId;
if (!bus.ParentId.IsEmpty) {
edges[i].Endpoint = bus.ParentId;
nodes[i].Edges = new OffsetLengthU16((ushort) i, 1);
} else {
nodes[i].Edges = default;
}
}

DependencySolver.OutputNode<StringHash32>* outputNodes = stackalloc DependencySolver.OutputNode<StringHash32>[busCount];
DependencySolver.Result result = DependencySolver.Solve<StringHash32>(new UnsafeSpan<DependencySolver.Node<StringHash32>>(nodes, busCount), new UnsafeSpan<DependencySolver.Edge<StringHash32>>(edges, busCount), new UnsafeSpan<DependencySolver.OutputNode<StringHash32>>(outputNodes, busCount));
Assert.True(result == DependencySolver.Result.Success);

for(int i = 0; i < busCount; i++) {
var output = outputNodes[i];
AudioBus bus = m_BusLateBindQueue[output.OriginalIndex];
CreateBus(bus.AssetId, bus.Properties, bus.ParentId);
}

m_BusLateBindQueue.Clear();
}

#endregion // Bindings

private unsafe void UpdateBuses() {
AudioPropertyBlock block;
for(int i = 0; i < m_BusCount; i++) {
ref BusData bus = ref m_BusData[i];
block = bus.ParentIndex < 0 ? AudioPropertyBlock.Default : m_BusData[bus.ParentIndex].LastKnownProperties;
AudioPropertyBlock.Combine(block, bus.BusProperties, ref block);
AudioPropertyBlock.Combine(block, *bus.ScriptProperties, ref block);
#if DEVELOPMENT
AudioPropertyBlock.Combine(block, m_DebugBusProperties[i], ref block);
Expand Down
12 changes: 10 additions & 2 deletions Assets/FieldDay/Audio/AudioMgr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,14 @@ internal unsafe AudioMgr(Config config) {
*bus.ScriptProperties = AudioPropertyBlock.Default;
bus.LastKnownProperties = AudioPropertyBlock.Default;

bus.ConfigVolume = 1;

bus.Handle = m_VoiceIdAllocator.Alloc();
bus.FloatTweens.Reset();
}

#if DEVELOPMENT
m_DebugBusProperties = new AudioPropertyBlock[MaxBuses];
for(int i = 0; i < MaxBuses; i++) {
m_DebugBusProperties[i] = AudioPropertyBlock.Default;
}
Expand Down Expand Up @@ -125,7 +128,7 @@ internal unsafe AudioMgr(Config config) {
Game.Assets.SetNamedAssetLoadCallbacks<AudioBus>(OnAudioBusLoaded, OnAudioBusUnloaded);

m_BusNameToIndex.Add(0, 0);
CreateBus(AudioBus.Master);
CreateBus(AudioBus.Master, AudioPropertyBlock.Default, default);
}

#region Events
Expand Down Expand Up @@ -226,7 +229,12 @@ private void OnAudioEventUnloaded(AudioEvent evt) {

private void OnAudioBusLoaded(AudioBus bus) {
if (m_BusNameToIndex.ContainsKey(bus.AssetId.HashValue)) {
Log.Error("Bus '{0}' already loaded!", bus.AssetId);
Log.Error("[AudioMgr] Bus '{0}' already loaded!", bus.AssetId);
return;
}

if (m_BusCount > 1) {
Log.Error("[AudioMgr] Buses must all be registered at startup.");
return;
}

Expand Down
8 changes: 8 additions & 0 deletions Assets/FieldDay/Audio/Data/AudioBus.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
using System;
using BeauUtil;
using FieldDay.Assets;
using UnityEngine;

namespace FieldDay.Audio {
/// <summary>
/// Audio bus information.
/// </summary>
[CreateAssetMenu(menuName = "Field Day/Audio/Audio Bus")]
public sealed class AudioBus : NamedAsset {
[AudioBusId] public StringHash32 ParentId;

[Inline(InlineAttribute.DisplayType.HeaderLabel)]
public AudioPropertyBlock Properties = AudioPropertyBlock.Default;

#region Lookup

static public readonly StringHash32 Master = "Master";
Expand Down
115 changes: 110 additions & 5 deletions Assets/FieldDay/Sys/Math/DependencySolver.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,119 @@
using BeauUtil;
using BeauUtil.Debugger;

namespace FieldDay.Mathematics {
static public class DependencySolver {
public struct SourceNode<TIdentifier> where TIdentifier : unmanaged {
public TIdentifier Id;
public UnsafeSpan<TIdentifier> Dependencies;
public enum Result {
Success = 0,
CycleDetected = 1,
MissingNode = 2
}

public struct OutputNode {
public UnsafeSpan<uint> DependencyIndices;
public struct Node<T> where T : unmanaged {
public T Id;
public OffsetLengthU16 Edges;
}

public struct Edge<T> where T : unmanaged {
public T Endpoint;
}

public struct OutputNode<T> where T : unmanaged {
public T Id;
public int OriginalIndex;
}

private struct MultiEdgeSolverData<T> where T : unmanaged {
public UnsafeSpan<Node<T>> Nodes;
public UnsafeSpan<Edge<T>> Edges;
public UnsafeSpan<OutputNode<T>> Output;
public UnsafeBitSet VisitBits;
public UnsafeBitSet CycleDetectionBits;
public int OutputCount;
}

static public unsafe Result Solve<T>(UnsafeSpan<Node<T>> nodes, UnsafeSpan<Edge<T>> edges, UnsafeSpan<OutputNode<T>> output) where T : unmanaged {
Assert.True(output.Length >= nodes.Length);

int nodeCount = nodes.Length;
int requiredBackingMemSize = Unsafe.AlignUp32(nodeCount) / 32;
uint* visitMem = stackalloc uint[requiredBackingMemSize];
uint* cycleMem = stackalloc uint[requiredBackingMemSize];

UnsafeBitSet visited = new UnsafeBitSet(visitMem, requiredBackingMemSize);
UnsafeBitSet cycle = new UnsafeBitSet(cycleMem, requiredBackingMemSize);

MultiEdgeSolverData<T> solverData = new MultiEdgeSolverData<T>() {
Nodes = nodes,
Edges = edges,
Output = output,
VisitBits = visited,
CycleDetectionBits = cycle
};

Result result = Result.Success;

for(int i = 0; i < nodeCount; i++) {
if (!visited.IsSet(i)) {
result = Traverse(ref solverData, i);
if (result != Result.Success) {
break;
}
}
}

Assert.True(result != Result.Success || solverData.OutputCount == nodes.Length);
return result;
}

static private Result Traverse<T>(ref MultiEdgeSolverData<T> solverData, int nodeIndex) where T : unmanaged {
if (solverData.VisitBits.IsSet(nodeIndex)) {
return Result.Success;
}
if (solverData.CycleDetectionBits.IsSet(nodeIndex)) {
return Result.CycleDetected;
}

solverData.CycleDetectionBits.Set(nodeIndex);
Node<T> node = solverData.Nodes[nodeIndex];

for(int edgeIdx = node.Edges.Offset; edgeIdx < node.Edges.End; edgeIdx++) {
int nextIdx = IndexOfNode(solverData.Nodes, solverData.Edges[edgeIdx].Endpoint);
if (nextIdx < 0) {
return Result.MissingNode;
}
Result edgeResult = Traverse(ref solverData, nextIdx);
if (edgeResult != Result.Success) {
return edgeResult;
}
}

solverData.CycleDetectionBits.Unset(nodeIndex);
solverData.VisitBits.Set(nodeIndex);

solverData.Output[solverData.OutputCount++] = new OutputNode<T>() {
Id = node.Id,
OriginalIndex = nodeIndex
};
return Result.Success;
}

static private int IndexOfNode<T>(UnsafeSpan<Node<T>> nodes, in T id) where T : unmanaged {
for(int i = 0; i < nodes.Length; i++) {
if (CompareUtils.Equals(id, nodes[i].Id)) {
return i;
}
}
return -1;
}

static private int IndexOfNode<T>(UnsafeSpan<T> nodes, in T id) where T : unmanaged {
for (int i = 0; i < nodes.Length; i++) {
if (CompareUtils.Equals(id, nodes[i])) {
return i;
}
}
return -1;
}
}
}
Loading

0 comments on commit d11ceed

Please sign in to comment.