Skip to content

Commit

Permalink
Fix memory leaks (#107)
Browse files Browse the repository at this point in the history
* Fixes #106

* Fixes #105
  • Loading branch information
highbyte authored Jul 17, 2024
1 parent f2e0d39 commit 45a222d
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 21 deletions.
13 changes: 13 additions & 0 deletions src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
using Highbyte.DotNet6502.Impl.NAudio.NAudioOpenALProvider;
using Highbyte.DotNet6502.Impl.SilkNet;
using Highbyte.DotNet6502.Impl.Skia;
using Highbyte.DotNet6502.Impl.Skia.Commodore64.Video.v2;
using Highbyte.DotNet6502.Instrumentation;
using Highbyte.DotNet6502.Instrumentation.Stats;
using Highbyte.DotNet6502.Logging;
using Highbyte.DotNet6502.Monitor;
using Highbyte.DotNet6502.Systems;
using Highbyte.DotNet6502.Systems.Commodore64.Config;
using Highbyte.DotNet6502.Systems.Commodore64;
using Microsoft.Extensions.Logging;
using Silk.NET.SDL;
using Highbyte.DotNet6502.App.SilkNetNative.SystemSetup;
using Highbyte.DotNet6502.Impl.SilkNet.Commodore64.Video;
using AutoMapper.Internal.Mappers;

namespace Highbyte.DotNet6502.App.SilkNetNative;

Expand Down Expand Up @@ -209,6 +216,8 @@ private void SetUninitializedWindow()

private void InitRendering()
{
_silkNetRenderContextContainer?.Cleanup();

// Init SkipSharp resources (must be done in OnLoad, otherwise no OpenGL context will exist create by SilkNet.)
//_skiaRenderContext = new SkiaRenderContext(s_window.Size.X, s_window.Size.Y, _canvasScale);
GRGlGetProcedureAddressDelegate getProcAddress = (name) =>
Expand Down Expand Up @@ -274,6 +283,10 @@ public void Start()
if (!_systemList.IsValidConfig(_currentSystemName).Result)
throw new DotNet6502Exception("Internal error. Cannot start emulator if current system config is invalid.");

// Force a full GC to free up memory, so it won't risk accumulate memory usage if GC has not run for a while.
var m0 = GC.GetTotalMemory(forceFullCollection: true);
_logger.LogInformation("Allocated memory before starting emulator: " + m0);

// Only create a new instance of SystemRunner if we previously has not started (so resume after pause works).
if (EmulatorState == EmulatorState.Uninitialized)
_systemRunner = _systemList.BuildSystemRunner(_currentSystemName).Result;
Expand Down
11 changes: 8 additions & 3 deletions src/apps/Highbyte.DotNet6502.App.WASM/Pages/Index.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,11 @@ public async Task OnStart(MouseEventArgs mouseEventArgs)
if (!isOk)
return;

// TODO: Is forcing a full GC a good idea in Blazor WASM?
// Force a full GC to free up memory, so it won't risk accumulate memory usage if GC has not run for a while.
var m0 = GC.GetTotalMemory(forceFullCollection: true);
_logger.LogInformation("Allocated memory before starting emulator: " + m0);

InitEmulator();
}

Expand Down Expand Up @@ -614,7 +619,7 @@ private async Task OnStatsToggle(MouseEventArgs mouseEventArgs)

private void OnKeyDown(KeyboardEventArgs e)
{
if (_wasmHost == null)
if (_wasmHost == null || _wasmHost.InputHandlerContext == null)
return;
_wasmHost.InputHandlerContext.KeyDown(e);

Expand All @@ -624,14 +629,14 @@ private void OnKeyDown(KeyboardEventArgs e)

private void OnKeyUp(KeyboardEventArgs e)
{
if (_wasmHost == null)
if (_wasmHost == null || _wasmHost.InputHandlerContext == null)
return;
_wasmHost.InputHandlerContext.KeyUp(e);
}

private void OnFocus(FocusEventArgs e)
{
if (_wasmHost == null)
if (_wasmHost == null || _wasmHost.InputHandlerContext == null)
return;
_wasmHost.InputHandlerContext.OnFocus(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private enum ShaderLineData : int
private ElapsedMillisecondsTimedStatSystem _textAndBitmapScreenStat;
private ElapsedMillisecondsTimedStatSystem _spritesStat;
private ElapsedMillisecondsTimedStatSystem _lineDataImageStat;
private ElapsedMillisecondsTimedStatSystem _drawCanvasWithShader;
private ElapsedMillisecondsTimedStatSystem _drawCanvasWithShaderStat;

public C64SkiaRenderer2()

Check warning on line 139 in src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v2/C64SkiaRenderer2.cs

View workflow job for this annotation

GitHub Actions / deploy

Non-nullable field '_skiaPixelArrayBitmap_TextAndBitmap' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 139 in src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v2/C64SkiaRenderer2.cs

View workflow job for this annotation

GitHub Actions / deploy

Non-nullable field '_skiaPixelArrayBitmap_Sprites' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 139 in src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v2/C64SkiaRenderer2.cs

View workflow job for this annotation

GitHub Actions / deploy

Non-nullable field '_skiaPixelArrayBitmap_LineData' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 139 in src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v2/C64SkiaRenderer2.cs

View workflow job for this annotation

GitHub Actions / deploy

Non-nullable field '_sKRuntimeEffect' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 139 in src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v2/C64SkiaRenderer2.cs

View workflow job for this annotation

GitHub Actions / deploy

Non-nullable field '_sKRuntimeEffectUniforms' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
{
Expand All @@ -159,7 +159,7 @@ public void Init(C64 c64, SkiaRenderContext skiaRenderContext)
_textAndBitmapScreenStat = Instrumentations.Add($"{StatsCategory}-Screen", new ElapsedMillisecondsTimedStatSystem(c64));
_spritesStat = Instrumentations.Add($"{StatsCategory}-Sprites", new ElapsedMillisecondsTimedStatSystem(c64));
_lineDataImageStat = Instrumentations.Add($"{StatsCategory}-LineDataImage", new ElapsedMillisecondsTimedStatSystem(c64));
_drawCanvasWithShader = Instrumentations.Add($"{StatsCategory}-DrawCanvasWithShader", new ElapsedMillisecondsTimedStatSystem(c64));
_drawCanvasWithShaderStat = Instrumentations.Add($"{StatsCategory}-DrawCanvasWithShader", new ElapsedMillisecondsTimedStatSystem(c64));
}

public void Init(ISystem system, IRenderContext renderContext)
Expand Down Expand Up @@ -488,7 +488,7 @@ private void InitLineDataBitmap(C64 c64)
private void WriteBitmapToCanvas(SKBitmap bitmap, SKBitmap spritesBitmap, SKBitmap lineDataBitmap, SKCanvas canvas, C64 c64)
{

_drawCanvasWithShader.Start();
_drawCanvasWithShaderStat.Start();
// shader uniform values
_sKRuntimeEffectUniforms["bg0Color"] = _sKColorToShaderColorMap[(uint)_bg0DrawColorActual];
_sKRuntimeEffectUniforms["bg1Color"] = _sKColorToShaderColorMap[(uint)_bg1DrawColor];
Expand Down Expand Up @@ -523,12 +523,12 @@ private void WriteBitmapToCanvas(SKBitmap bitmap, SKBitmap spritesBitmap, SKBitm

// Shader uniform texture sampling values
// Convert bitmap (that one have written the C64 screen to) to shader texture
var shaderTexture = bitmap.ToShader();
using var shaderTexture = bitmap.ToShader();
// Convert shader bitmap (that one have written the C64 sprites to) to shader texture
var spritesTexture = spritesBitmap.ToShader();
using var spritesTexture = spritesBitmap.ToShader();

// Convert other bitmaps to shader texture
var lineDataBitmapShaderTexture = lineDataBitmap.ToShader();
using var lineDataBitmapShaderTexture = lineDataBitmap.ToShader();

_sKRuntimeEffectChildren["bitmap_texture"] = shaderTexture;
_sKRuntimeEffectChildren["sprites_texture"] = spritesTexture;
Expand All @@ -539,7 +539,7 @@ private void WriteBitmapToCanvas(SKBitmap bitmap, SKBitmap spritesBitmap, SKBitm

canvas.DrawRect(0, 0, bitmap.Width, bitmap.Height, _shaderPaint);

_drawCanvasWithShader.Stop();
_drawCanvasWithShaderStat.Stop();
}

private void DrawBorderAndScreenToBitmapBackedByPixelArray(C64 c64, uint[] pixelArray)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private enum ShaderLineData : int
private const string StatsCategory = "SkiaSharp-Custom";
private ElapsedMillisecondsTimedStatSystem _spritesStat;
private ElapsedMillisecondsTimedStatSystem _lineDataImageStat;
private ElapsedMillisecondsTimedStatSystem _drawCanvasWithShader;
private ElapsedMillisecondsTimedStatSystem _drawCanvasWithShaderStat;


// Keep track of C64 data that should update each new line
Expand Down Expand Up @@ -276,7 +276,7 @@ public void Init(C64 c64, SkiaRenderContext skiaRenderContext)
Instrumentations.Clear();
_spritesStat = Instrumentations.Add($"{StatsCategory}-Sprites", new ElapsedMillisecondsTimedStatSystem(c64));
_lineDataImageStat = Instrumentations.Add($"{StatsCategory}-LineDataImage", new ElapsedMillisecondsTimedStatSystem(c64));
_drawCanvasWithShader = Instrumentations.Add($"{StatsCategory}-DrawCanvasWithShader", new ElapsedMillisecondsTimedStatSystem(c64));
_drawCanvasWithShaderStat = Instrumentations.Add($"{StatsCategory}-DrawCanvasWithShader", new ElapsedMillisecondsTimedStatSystem(c64));
}

public void Init(ISystem system, IRenderContext renderContext)
Expand Down Expand Up @@ -549,7 +549,7 @@ private void InitLineDataBitmap(C64 c64)
private void WriteBitmapToCanvas(SKBitmap backgroundAndBorderBitmap, SKBitmap foregroundBitmap, SKBitmap lineDataBitmap, SKCanvas canvas, C64 c64)
{

_drawCanvasWithShader.Start();
_drawCanvasWithShaderStat.Start();
// shader uniform values
_sKRuntimeEffectUniforms["transparentColor"] = _sKColorToShaderColorMap[(uint)_transparentColor];

Expand Down Expand Up @@ -579,11 +579,11 @@ private void WriteBitmapToCanvas(SKBitmap backgroundAndBorderBitmap, SKBitmap fo

// Shader uniform texture sampling values
// Convert bitmaps to shader textures
var backgroundAndBorderTexture = backgroundAndBorderBitmap.ToShader();
var foregroundTexture = foregroundBitmap.ToShader();
using var backgroundAndBorderTexture = backgroundAndBorderBitmap.ToShader();
using var foregroundTexture = foregroundBitmap.ToShader();

// Convert "data" bitmaps to shader texture
var lineDataBitmapShaderTexture = lineDataBitmap.ToShader();
using var lineDataBitmapShaderTexture = lineDataBitmap.ToShader();

_sKRuntimeEffectChildren["background_and_border_texture"] = backgroundAndBorderTexture;
_sKRuntimeEffectChildren["foreground_texture"] = foregroundTexture;
Expand All @@ -594,7 +594,7 @@ private void WriteBitmapToCanvas(SKBitmap backgroundAndBorderBitmap, SKBitmap fo

canvas.DrawRect(0, 0, backgroundAndBorderBitmap.Width, backgroundAndBorderBitmap.Height, _shaderPaint);

_drawCanvasWithShader.Stop();
_drawCanvasWithShaderStat.Stop();
}


Expand Down
15 changes: 11 additions & 4 deletions src/libraries/Highbyte.DotNet6502.Impl.Skia/SkiaRenderContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Highbyte.DotNet6502.Systems;
using SkiaSharp;

namespace Highbyte.DotNet6502.Impl.Skia;

Expand All @@ -16,6 +17,8 @@ public class SkiaRenderContext : IRenderContext
private GRContext? _grContext;
private readonly Func<GRContext>? _getGrContextExternal;

private GRGlInterface? _glInterface;

private SKCanvas GetCanvasInternal()
{
if (_canvas != null)
Expand Down Expand Up @@ -44,10 +47,11 @@ public SkiaRenderContext(GRGlGetProcedureAddressDelegate getProcAddress, int siz
{
// Create the SkiaSharp context
//var glInterface = GRGlInterface.Create();
var glInterface = GRGlInterface.Create(name => getProcAddress(name));
glInterface.Validate();

_glInterface = GRGlInterface.Create(name => getProcAddress(name));
_glInterface.Validate();
var grContextOptions = new GRContextOptions { };
_grContext = GRContext.CreateGl(glInterface, grContextOptions);
_grContext = GRContext.CreateGl(_glInterface, grContextOptions);
if (_grContext == null)
throw new DotNet6502Exception("Cannot create OpenGL context.");

Expand Down Expand Up @@ -77,8 +81,11 @@ public void Cleanup()
_canvas = null;
_renderSurface?.Dispose();
_renderSurface = null;
_renderTarget?.Dispose();
_renderTarget = null;
_grContext?.Dispose();
_grContext = null;
_glInterface?.Dispose();
_glInterface = null;
}

}

0 comments on commit 45a222d

Please sign in to comment.