u/NoisyJalapeno

▲ 7 r/opengl

C# .NET 10 OpenTK - Video Scheduler Internal Error on Resize

I'm using OGL to output a BGRA surface to screen. Nothing else.

OpenTK + Intel ARC iGPU. I'm extending GameWindow.

Behavior is inconsistent. Sometimes it resizes instantly. Sometimes there is a delay. If the delay is too long, then BSOD.

I believe its related to how free the CPU is. For example, silent profile is more likely to crash due to timeout. High fan profile is way less likely to exhibit issues. Original version avoided OGL code directly in favor of SkiaSharp doing the rendering. That was slower and crashed more often.

The resize code is this, done in OnFrameBufferResize event hook,

            GL.DeleteTexture(_texture);

            _texture = GL.GenTexture();
            GL.ActiveTexture(TextureUnit.Texture0);
            GL.BindTexture(TextureTarget.Texture2D, _texture);

            // No mipmaps needed for a 1:1 pixel display.
            GL.TexParameter(TextureTarget.Texture2D,
                TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
            GL.TexParameter(TextureTarget.Texture2D,
                TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
            GL.TexParameter(TextureTarget.Texture2D,
                TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
            GL.TexParameter(TextureTarget.Texture2D,
                TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);

            // Allocate GPU storage with the initial pixel data.
            GL.TexImage2D(
                TextureTarget.Texture2D,
                level: 0,
                internalformat: PixelInternalFormat.Rgba8,  // GPU stores RGBA8
                width: ClientSize.X,
                height: ClientSize.Y,
                border: 0,
                format: PixelFormat.Bgra,           // CPU supplies BGRA
                type: PixelType.UnsignedByte,
                pixels: ptr);

In OnResize hook,

GL.Viewport(0, 0, e.Width, e.Height);
u/NoisyJalapeno — 11 hours ago

Rendering Duke 3D maps in C# - June Update

I've got alternative palettes working.
I've also started researching CON format, which contains sprite angles. So, some sprites change depending on angle. Fully original renderer, not build, also can render doom maps.

C# .NET 10 running in Ultrawide on an Intel Core Ultra 7 155H

u/NoisyJalapeno — 15 days ago
▲ 48 r/csharp

PSA: Span<T>.Sort extension method allocates memory per call

I'm minimizing GC use for a rendering/game engine. This is just really odd thing for a type aimed at minimizing allocations.

The Sort is an extension method on a Span that internally uses ArraySortHelper<T> which works with a delegate.

Using IComparer<T> to sort a Span<T> creates a delegate System.Comparison<T>

https://preview.redd.it/im9vbp1kt18h1.png?width=2561&format=png&auto=webp&s=af1e7de15598d3328b0bc44e0b7bc6a7a193ec00

I had to spin up a custom ArraySorter<T, C> that doesn't pass around a delegate,

https://preview.redd.it/tu4bx2qgv18h1.png?width=1776&format=png&auto=webp&s=f400f79ea249cee2f50828a576ca53415ae4b858

Maybe this is properly optimized in .NET 11, but I haven't tried

EDIT: Image didn't upload, trying again.

reddit.com
u/NoisyJalapeno — 18 days ago
▲ 10 r/csharp

AggressiveInlining is a hint. Easy way to determine what is and is not in-lined?

I've got a lot of code that determines texture location to screen location mapping.

Then either renders directly (walls) or has extra code for blending (windows) or skipping transparent pixels (sprites).

For example,

while (screenIndexPtr &lt; screenIndexPtrEnd)
{
Vector256&lt;uint&gt; texelIndexV = (textureYPos_uV &gt;&gt; 16) &amp; textureMaskV;
texelIndexV += textureXPosV;

Vector256&lt;uint&gt; gathered = Avx2.GatherVector256(
textureBuffer,
texelIndexV.AsInt32(),
scale: sizeof(uint)
);

T.DrawLine(screenIndexPtr, gathered);

textureYPos_uV += textureXIncr_uV;
screenIndexPtr += width;
}

And the generic T extends,

internal unsafe interface IDrawPixel
{
// .. draw, drawline + mask omitted
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static abstract void DrawLine(uint* surface, Vector256&lt;uint&gt; pixels);
}

Now if debugging in Release configuration and a breakpoint doesn't hit then it was likely in-lined.

I think also `CLRStub[MethodDescPrestub` indicates it will be in-lined when viewing disassembly.

But is there a way to actually know if something was / wasn't in-lined with ease?

reddit.com
u/NoisyJalapeno — 1 month ago

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 — 2 months 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
{
    /// &lt;summary&gt;
    /// Handles Window Logic and a OpenGL surface
    /// &lt;/summary&gt;
    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() =&gt; 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&lt;int&gt; 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&lt;int&gt;(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() =&gt; StopTheGameLoop();
    }
}
reddit.com
u/NoisyJalapeno — 2 months ago