Browse Source

Optimized lightmap uploads by converting the raw byte pointer from Quake to a NativeArray (unsafe code required) and passing that directly to SetPixelData. This does away with the extra copying step and removes garbage created by Texture2D's implementation.

The old code path is still kept around since the NativeArray version doesn't work inside the Editor for some reason, but it does in standalone builds.
readme
Nico de Poel 5 years ago
parent
commit
764f2a8a0f
  1. 25
      Assets/Scripts/Game/GameAssets.cs
  2. 21
      Assets/Scripts/Modules/RenderModule.Interop.cs
  3. 10
      Assets/Scripts/Modules/RenderModule.cs
  4. 5
      Assets/Scripts/UniQuake.cs
  5. 2
      ProjectSettings/ProjectSettings.asset

25
Assets/Scripts/Game/GameAssets.cs

@ -1,5 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
public class GameAssets
@ -63,15 +64,33 @@ public class GameAssets
}
public void UploadLightmap(int lightmapNum, int width, int height, byte[] data)
{
var lightmap = GetOrCreateLightmap(lightmapNum, width, height);
lightmap.SetPixelData(data, 0);
lightmap.Apply();
}
public void UploadLightmap(int lightmapNum, int width, int height, NativeArray<byte> data)
{
var lightmap = GetOrCreateLightmap(lightmapNum, width, height);
lightmap.SetPixelData(data, 0);
lightmap.Apply();
}
private Texture2D GetOrCreateLightmap(int lightmapNum, int width, int height)
{
if (!lightmaps.TryGetValue(lightmapNum, out var lightmap))
{
lightmap = new Texture2D(width, height, TextureFormat.RGBA32, false) { name = $"Lightmap_{lightmapNum}", wrapMode = TextureWrapMode.Clamp };
lightmap = new Texture2D(width, height, TextureFormat.RGBA32, false)
{
name = $"Lightmap_{lightmapNum}",
wrapMode = TextureWrapMode.Clamp,
};
lightmaps.Add(lightmapNum, lightmap);
}
lightmap.SetPixelData(data, 0);
lightmap.Apply();
return lightmap;
}
public bool TryGetLightmap(int lightmapNum, out Texture2D lightmap)

21
Assets/Scripts/Modules/RenderModule.Interop.cs

@ -1,6 +1,8 @@
using System;
using System.Runtime.InteropServices;
using AOT;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Profiling;
@ -138,7 +140,9 @@ public partial class RenderModule: CallbackHandler<RenderModule>
return result;
}
#if UNITY_EDITOR
private readonly byte[] lightmapBytes = new byte[QConstants.LightmapBlockWidth * QConstants.LightmapBlockHeight * 4]; // 32 bits per pixel
#endif
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool UploadLightmapCallback(IntPtr context, int lmap, IntPtr data);
@ -146,12 +150,25 @@ public partial class RenderModule: CallbackHandler<RenderModule>
[MonoPInvokeCallback(typeof(UploadLightmapCallback))]
private static bool Callback_UploadLightmap(IntPtr context, int lmap, IntPtr data)
{
bool result;
Profiler.BeginSample("UploadLightmap");
// TODO: this is a fairly pointless additional data copy step; we could probably make this faster by wrapping the IntPtr directly into a NativeArray
#if UNITY_EDITOR
// This is fairly pointless additional data copy step; the below NativeArray approach is faster and doesn't
// produce any garbage, but it doesn't work inside the Unity Editor for some reason.
var self = GetSelf(context);
Marshal.Copy(data, self.lightmapBytes, 0, self.lightmapBytes.Length);
bool result = self.UploadLightmap(lmap, QConstants.LightmapBlockWidth, QConstants.LightmapBlockHeight, self.lightmapBytes);
result = self.UploadLightmap(lmap, QConstants.LightmapBlockWidth, QConstants.LightmapBlockHeight, self.lightmapBytes);
#else
unsafe
{
// More efficient code path that passes the native byte buffer directly to the Texture2D's pixel data
var dataArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(data.ToPointer(),
QConstants.LightmapBlockWidth * QConstants.LightmapBlockHeight * 4, Allocator.None);
result = GetSelf(context).UploadLightmap(lmap, QConstants.LightmapBlockWidth, QConstants.LightmapBlockHeight, dataArray);
}
#endif
Profiler.EndSample();
return result;

10
Assets/Scripts/Modules/RenderModule.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;
@ -77,6 +78,15 @@ public partial class RenderModule
return true;
}
private bool UploadLightmap(int lightmapNum, int width, int height, NativeArray<byte> data)
{
if (width == 0 || height == 0)
return false;
uq.GameAssets.UploadLightmap(lightmapNum, width, height, data);
return true;
}
private void SetupView(QVec3 origin, QVec3 angles, QLeaf viewLeaf)
{
var cam = uq.Camera;

5
Assets/Scripts/UniQuake.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Profiling;
public partial class UniQuake: MonoBehaviour
{
@ -133,6 +134,8 @@ public partial class UniQuake: MonoBehaviour
logHandler = CollectLog; // Collect and dump logs to Unity once per frame
Profiler.BeginSample("QuakeEngineUpdate");
try
{
UniQuake_Update(Time.deltaTime);
@ -152,6 +155,8 @@ public partial class UniQuake: MonoBehaviour
Shutdown();
}
Profiler.EndSample();
FlushLog();
}

2
ProjectSettings/ProjectSettings.asset

@ -609,7 +609,7 @@ PlayerSettings:
managedStrippingLevel: {}
incrementalIl2cppBuild: {}
suppressCommonWarnings: 1
allowUnsafeCode: 0
allowUnsafeCode: 1
useDeterministicCompilation: 1
useReferenceAssemblies: 1
enableRoslynAnalyzers: 1

Loading…
Cancel
Save