u/NoisyJalapeno

Image 1 —
Image 2 —
Image 3 —
Image 4 —
Image 5 —

This is the second update.

It's not a "game engine" yet, as there is nothing but rendering and moving.

I've mostly figured out sloped floors and walls are horizontally drawn now (for performance).

This is NOT a port of Build Engine or Doom source code.

u/NoisyJalapeno — 11 days ago
▲ 2 r/csharp

I'm using Silk .NET to create a basic surface to render to. Then I have a BGRA array which I copy to the surface using Skia Sharp. The idea is simple - basic window to show software rendered BGRA array. Its significantly faster than a barebones WPF application with a bitmap and cross platform.

This just might be an Intel iGPU issue though.

Any tips appreciated!

Here is the main code,

using RenderingEngine;
using RenderingEngine.Models;
using Silk.NET.Input;
using Silk.NET.Maths;
using Silk.NET.OpenGL;
using Silk.NET.Windowing;
using SkiaSharp;
using System.Diagnostics.CodeAnalysis;

namespace SoftwareRenderer
{
    /// <summary>
    /// Handles Window Logic and a OpenGL surface
    /// </summary>
    internal sealed class SilkSkiaApp : IDisposable
    {
        private IWindow window;
        private GRGlInterface? grgInterface;
        private GRContext? grContext;
        private SKSurface? surface;
        private SKCanvas? canvas;
        private GRBackendRenderTarget? renderTarget;

        private readonly PortalEngine Engine;

        public SilkSkiaApp(Arguments arguments)
        {
            SetupWindow();
            Engine = new PortalEngine(arguments);
        }

        public void Run() => window.Run();

        #region IWindow Events

        [MemberNotNull(nameof(grgInterface), nameof(grContext))]
        private void Window_Load()
        {
            grgInterface = GRGlInterface.Create();
            grContext = GRContext.CreateGl(grgInterface);
            
            SetupRenderAndCanvas();
            SetupKeyEvents();

            Engine.StartTheGameLoop(window.Size.X, window.Size.Y);
        }

        private unsafe void Window_Render(double delta)
        {
            ThrowIfNull(canvas);

            void* bgraPtr = Engine.RenderNextFrame();

            if (bgraPtr == null)
            {
                return;
            }

            var info = new SKImageInfo(window.Size.X, window.Size.Y)
            {
                AlphaType = SKAlphaType.Premul,
                ColorType = SKColorType.Bgra8888,
            };

            SKImage image = SKImage.FromPixels(info, (nint)bgraPtr, info.RowBytes);

            // prevent crash due to minimizing/maximizing window
            if (image == null)
            {
                return;
            }

            canvas.DrawImage(image, 0f, 0f, SKSamplingOptions.Default, null);
            canvas.Flush();

            image.Dispose();
        }

        private void Window_Update(double delta)
        {
            Engine.Update();
        }

        private void Window_Resize(Vector2D<int> size)
        {
            StopTheGameLoop();

            SetupRenderAndCanvas();

            StartTheGameLoop();
        }

        private void Window_Closing()
        {
            StopTheGameLoop();
        }

        #endregion

        #region IInputContext Events

        private void OnKeyUp(IKeyboard keyboard, Key key, int arg3)
        {
            Engine.OnKeyUp(key);
        }

        private void OnKeyDown(IKeyboard keyboard, Key key, int arg3)
        {
            Engine.OnKeyDown(key);
        }

        #endregion

        [MemberNotNull(nameof(window))]
        private void SetupWindow()
        {
            WindowOptions windowOptions = WindowOptions.Default;
            windowOptions.Title = "Renderer";
            windowOptions.Size = new Vector2D<int>(800, 450);
            windowOptions.FramesPerSecond = 60.0;
            windowOptions.UpdatesPerSecond = 60.0;

            window = Window.Create(windowOptions);

            window.Load += Window_Load;
            window.Render += Window_Render;
            window.Update += Window_Update;
            window.Resize += Window_Resize;
            window.Closing += Window_Closing;
        }


        [MemberNotNull(nameof(renderTarget), nameof(surface), nameof(canvas))]
        private void SetupRenderAndCanvas()
        {
            ThrowIfNull(window);

            renderTarget = new GRBackendRenderTarget(window.Size.X, window.Size.Y, 0, 8, new GRGlFramebufferInfo(0, (uint)SizedInternalFormat.Rgba8));
            surface = SKSurface.Create(grContext, renderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Bgra8888);
            canvas = surface.Canvas;
        }

        private void SetupKeyEvents()
        {
            IInputContext input = window.CreateInput();

            foreach (var keyboard in input.Keyboards)
            {
                // Subscribe to key events
                keyboard.KeyDown += OnKeyDown;
                keyboard.KeyUp += OnKeyUp;
            }
        }

        private void StopTheGameLoop()
        {
            Engine.StopTheGameLoop();

            renderTarget?.Dispose();
            surface?.Dispose();
        }

        private void StartTheGameLoop()
        {
            if (window.Size.X == 0 || window.Size.Y == 0)
            {
                return;
            }

            Engine.StartTheGameLoop(window.Size.X, window.Size.Y);
        }

        private static void ThrowIfNull([NotNull] object? obj)
        {
            if (obj is null) throw new Exception("Window Setup Error");
        }

        public void Dispose() => StopTheGameLoop();
    }
}
reddit.com
u/NoisyJalapeno — 12 days ago