OpenRA开源红色警戒游戏RPG源码Renderer.cs解读

部署运行你感兴趣的模型镜像

python编程示例系列
python编程示例系列二
python的Web神器Streamlit
如何应聘高薪职位
C#视觉应用开发问题系列
c#串口应用开发问题系列
microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
OpenRA开源红色警戒游戏RPG源码解读
在这里插入图片描述# OpenRA渲染器代码详解

以下是对OpenRA游戏引擎中Renderer类的详细分析,包括中文注释、逻辑解读、流程图和特殊语法技巧的解析。

代码详解(带中文注释)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;

namespace OpenRA
{
    public sealed class Renderer : IDisposable
    {
        // 渲染类型枚举:无渲染、世界渲染、UI渲染
        enum RenderType { None, World, UI }

        // 世界渲染相关的渲染器
        public SpriteRenderer WorldSpriteRenderer { get; } // 世界精灵渲染器
        public RgbaSpriteRenderer WorldRgbaSpriteRenderer { get; } // 世界RGBA精灵渲染器
        public RgbaColorRenderer WorldRgbaColorRenderer { get; } // 世界RGBA颜色渲染器
        public IRenderer[] WorldRenderers = Array.Empty<IRenderer>(); // 世界渲染器数组,默认为空
        
        // UI渲染相关的渲染器
        public RgbaColorRenderer RgbaColorRenderer { get; } // RGBA颜色渲染器
        public SpriteRenderer SpriteRenderer { get; } // 精灵渲染器
        public RgbaSpriteRenderer RgbaSpriteRenderer { get; } // RGBA精灵渲染器

        // 窗口状态属性
        public bool WindowHasInputFocus => Window.HasInputFocus; // 窗口是否有输入焦点
        public bool WindowIsSuspended => Window.IsSuspended; // 窗口是否被挂起

        // 字体字典
        public IReadOnlyDictionary<string, SpriteFont> Fonts;

        // 平台相关接口
        internal IPlatformWindow Window { get; } // 平台窗口接口
        internal IGraphicsContext Context { get; } // 图形上下文接口

        // 纹理和缓冲区大小
        internal int SheetSize { get; } // 纹理表大小
        internal int TempVertexBufferSize { get; } // 临时顶点缓冲区大小
        internal int TempIndexBufferSize { get; } // 临时索引缓冲区大小

        // 渲染缓冲区
        readonly IVertexBuffer<Vertex> tempVertexBuffer; // 临时顶点缓冲区
        readonly IIndexBuffer quadIndexBuffer; // 四边形索引缓冲区
        readonly Stack<Rectangle> scissorState = new(); // 裁剪区域状态栈
        readonly ITexture worldBufferSnapshot; // 世界缓冲区快照纹理

        // 屏幕帧缓冲区
        IFrameBuffer screenBuffer; // 屏幕帧缓冲区
        Sprite screenSprite; // 屏幕精灵

        // 世界帧缓冲区
        IFrameBuffer worldBuffer; // 世界帧缓冲区
        Sheet worldSheet; // 世界纹理表
        Sprite worldSprite; // 世界精灵
        Size lastMaximumViewportSize; // 上次最大视口大小
        Size lastWorldViewportSize; // 上次世界视口大小

        // 世界帧缓冲区属性
        public Size WorldFrameBufferSize => worldSheet.Size; // 世界帧缓冲区大小
        public int WorldDownscaleFactor { get; private set; } = 1; // 世界下采样因子,默认为1

        /// <summary>
        /// 复制并返回当前渲染的世界状态作为临时纹理
        /// </summary>
        public ITexture WorldBufferSnapshot()
        {
            worldBufferSnapshot.SetDataFromReadBuffer(new Rectangle(int2.Zero, worldSheet.Size));
            return worldBufferSnapshot;
        }

        // 字体相关
        SheetBuilder fontSheetBuilder; // 字体纹理表构建器
        readonly IPlatform platform; // 平台接口

        // 深度缓冲区相关
        float depthMargin; // 深度边距

        // 缓存的状态
        Size lastBufferSize = new(-1, -1); // 上次缓冲区大小
        Rectangle lastWorldViewport = Rectangle.Empty; // 上次世界视口
        ITexture currentPaletteTexture; // 当前调色板纹理
        int currentPaletteHeight = 0; // 当前调色板高度
        IBatchRenderer currentBatchRenderer; // 当前批处理渲染器
        RenderType renderType = RenderType.None; // 当前渲染类型,默认为无

        /// <summary>
        /// 渲染器构造函数
        /// </summary>
        /// <param name="platform">平台接口</param>
        /// <param name="graphicSettings">图形设置</param>
        public Renderer(IPlatform platform, GraphicSettings graphicSettings)
        {
            this.platform = platform;
            var resolution = GetResolution(graphicSettings); // 获取分辨率

            // 计算临时缓冲区大小
            TempVertexBufferSize = graphicSettings.BatchSize - graphicSettings.BatchSize % 4;
            TempIndexBufferSize = TempVertexBufferSize / 4 * 6;

            // 创建窗口
            Window = platform.CreateWindow(new Size(resolution.Width, resolution.Height),
                graphicSettings.Mode, graphicSettings.UIScale, TempVertexBufferSize, TempIndexBufferSize,
                graphicSettings.VideoDisplay, graphicSettings.GLProfile);

            Context = Window.Context; // 获取图形上下文

            SheetSize = graphicSettings.SheetSize; // 设置纹理表大小

            // 创建各种渲染器
            var combinedBindings = new CombinedShaderBindings();
            WorldSpriteRenderer = new SpriteRenderer(this, Context.CreateShader(combinedBindings));
            WorldRgbaSpriteRenderer = new RgbaSpriteRenderer(WorldSpriteRenderer);
            WorldRgbaColorRenderer = new RgbaColorRenderer(WorldSpriteRenderer);
            SpriteRenderer = new SpriteRenderer(this, Context.CreateShader(combinedBindings));
            RgbaSpriteRenderer = new RgbaSpriteRenderer(SpriteRenderer);
            RgbaColorRenderer = new RgbaColorRenderer(SpriteRenderer);

            // 创建缓冲区
            tempVertexBuffer = Context.CreateVertexBuffer<Vertex>(TempVertexBufferSize);
            quadIndexBuffer = Context.CreateIndexBuffer(Util.CreateQuadIndices(TempIndexBufferSize / 6));
            worldBufferSnapshot = Context.CreateTexture();
        }

        /// <summary>
        /// 根据图形设置获取分辨率
        /// </summary>
        static Size GetResolution(GraphicSettings graphicsSettings)
        {
            var size = (graphicsSettings.Mode == WindowMode.Windowed)
                ? graphicsSettings.WindowedSize // 窗口模式
                : graphicsSettings.FullscreenSize; // 全屏模式
            return new Size(size.X, size.Y);
        }

        /// <summary>
        /// 设置UI缩放
        /// </summary>
        public void SetUIScale(float scale)
        {
            Window.SetScaleModifier(scale);
        }

        /// <summary>
        /// 初始化字体
        /// </summary>
        public void InitializeFonts(ModData modData)
        {
            // 释放旧字体资源
            if (Fonts != null)
                foreach (var font in Fonts.Values)
                    font.Dispose();
            
            using (new PerfTimer("SpriteFonts")) // 性能计时器
            {
                fontSheetBuilder?.Dispose(); // 释放旧的字体纹理表构建器
                fontSheetBuilder = new SheetBuilder(SheetType.BGRA, modData.Manifest.FontSheetSize);
                
                // 创建字体字典
                Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key,
                    x => new SpriteFont(
                        platform, x.Value.Font, modData.DefaultFileSystem.Open(x.Value.Font).ReadAllBytes(),
                        x.Value.Size, x.Value.Ascender, Window.EffectiveWindowScale, fontSheetBuilder));
            }

            // 注册窗口缩放变化事件处理器
            Window.OnWindowScaleChanged += (oldNative, oldEffective, newNative, newEffective) =>
            {
                Game.RunAfterTick(() =>
                {
                    // 重新计算新窗口缩放的下采样因子
                    SetMaximumViewportSize(lastMaximumViewportSize);

                    // 更新Chrome提供者的DPI缩放
                    ChromeProvider.SetDPIScale(newEffective);

                    // 更新所有字体的缩放
                    foreach (var f in Fonts)
                        f.Value.SetScale(newEffective);
                });
            };
        }

        /// <summary>
        /// 初始化深度缓冲区
        /// </summary>
        public void InitializeDepthBuffer(MapGrid mapGrid)
        {
            // 深度缓冲区需要初始化足够的范围来覆盖:
            //  - 屏幕的高度
            //  - 从屏幕底部下方的MaxTerrainHeight的瓦片的z偏移(推入视图)
            //  - 来自MaxTerrainHeight地形上方的actor的额外z偏移
            //  - 一个小边距,使得部分渲染在屏幕顶部边缘上方的瓦片不会被推到裁剪平面后面
            // 我们需要mapGrid.MaximumTerrainHeight * mapGrid.TileSize.Height / 2的偏移来覆盖地形高度
            // 并选择使用mapGrid.MaximumTerrainHeight * mapGrid.TileSize.Height / 4来处理actor和顶部边缘情况
            depthMargin = mapGrid == null || !mapGrid.EnableDepthBuffer ? 0 : mapGrid.TileSize.Height * mapGrid.MaximumTerrainHeight;
        }

        /// <summary>
        /// 开始一帧的渲染
        /// </summary>
        void BeginFrame()
        {
            Context.Clear(); // 清除上下文

            var surfaceSize = Window.SurfaceSize; // 获取表面大小
            var surfaceBufferSize = surfaceSize.NextPowerOf2(); // 计算下一个2的幂大小

            // 如果屏幕精灵为空或大小不匹配,重新创建屏幕缓冲区
            if (screenSprite == null || screenSprite.Sheet.Size != surfaceBufferSize)
            {
                screenBuffer?.Dispose(); // 释放旧的屏幕缓冲区

                // 将屏幕渲染到帧缓冲区以简化截图回读
                screenBuffer = Context.CreateFrameBuffer(surfaceBufferSize, Color.FromArgb(0xFF, 0, 0, 0));
            }

            // 如果屏幕精灵为空或尺寸不匹配,重新创建屏幕精灵
            if (screenSprite == null || surfaceSize.Width != screenSprite.Bounds.Width || -surfaceSize.Height != screenSprite.Bounds.Height)
            {
                var screenSheet = new Sheet(SheetType.BGRA, screenBuffer.Texture);

                // 在Y轴翻转精灵以匹配OpenGL的左下角原点
                var screenBounds = Rectangle.FromLTRB(0, surfaceSize.Height, surfaceSize.Width, 0);
                screenSprite = new Sprite(screenSheet, screenBounds, TextureChannel.RGBA);
            }

            // 在HiDPI窗口中,我们遵循Apple的约定,将窗口坐标定义为标准分辨率窗口
            // 但有一个更高分辨率的后备表面,每个视口像素有多个纹理像素
            // 我们必须将表面缓冲区大小转换为视口大小 - 通常这不仅仅是窗口大小
            // 四舍五入到下一个2的幂,因为NextPowerOf2计算是在表面像素坐标中完成的
            var scale = Window.EffectiveWindowScale;
            var bufferSize = new Size((int)(surfaceBufferSize.Width / scale), (int)(surfaceBufferSize.Height / scale));
            
            // 如果缓冲区大小发生变化,更新视口参数
            if (lastBufferSize != bufferSize)
            {
                SpriteRenderer.SetViewportParams(bufferSize, 1, 0f, int2.Zero);
                lastBufferSize = bufferSize;
            }
        }

        /// <summary>
        /// 设置最大视口大小
        /// </summary>
        public void SetMaximumViewportSize(Size size)
        {
            // 目标是以1:1缩放将世界渲染到帧缓冲区,然后使用自定义
            // 过滤器进行上/下采样,以提供清晰的缩放并避免使用深度缓冲区时的渲染故障
            // 这种方法对大尺寸缩放不好,首先会饱和GPU填充率,然后在
            // 达到帧缓冲区大小限制(通常为16k)时崩溃。因此,我们将最大帧缓冲区大小限制为
            // 窗口表面大小的两倍,这在渲染质量和性能之间取得了合理的平衡。
            // 使用深度缓冲区的模组必须限制其艺术品分辨率或最大缩小级别。
            Size worldBufferSize;
            if (depthMargin == 0)
            {
                var surfaceSize = Window.SurfaceSize;
                worldBufferSize = new Size(Math.Min(size.Width, 2 * surfaceSize.Width), Math.Min(size.Height, 2 * surfaceSize.Height)).NextPowerOf2();
            }
            else
                worldBufferSize = size.NextPowerOf2();

            // 如果世界精灵为空或世界表大小不匹配,重新创建世界缓冲区
            if (worldSprite == null || worldSheet.Size != worldBufferSize)
            {
                worldBuffer?.Dispose(); // 释放旧的世界缓冲区

                // 如果启用世界帧缓冲区下采样且世界大小超过最终输出大小的两倍,我们允许它被下采样!
                worldBuffer = Context.CreateFrameBuffer(worldBufferSize);

                // 像素艺术缩放模式是自定义的双线性采样
                worldBuffer.Texture.ScaleFilter = TextureScaleFilter.Linear;
                worldSheet = new Sheet(SheetType.BGRA, worldBuffer.Texture);

                // 使缓存的状态无效以强制着色器更新
                lastWorldViewport = Rectangle.Empty;
                worldSprite = null;
            }

            lastMaximumViewportSize = size;
        }

        /// <summary>
        /// 开始世界渲染
        /// </summary>
        public void BeginWorld(Rectangle worldViewport)
        {
            // 确保当前没有进行渲染
            if (renderType != RenderType.None)
                throw new InvalidOperationException($"BeginWorld called with renderType = {renderType}, expected RenderType.None.");

            BeginFrame(); // 开始一帧

            // 确保已设置最大视口大小
            if (worldSheet == null)
                throw new InvalidOperationException("BeginWorld called before SetMaximumViewportSize has been set.");

            // 如果世界精灵为空或视口大小发生变化,重新创建世界精灵
            if (worldSprite == null || worldViewport.Size != lastWorldViewportSize)
            {
                // 如果需要,下采样世界渲染以适应帧缓冲区
                var vw = worldViewport.Size.Width;
                var vh = worldViewport.Size.Height;
                var bw = worldSheet.Size.Width;
                var bh = worldSheet.Size.Height;
                WorldDownscaleFactor = 1;
                while (vw / WorldDownscaleFactor > bw || vh / WorldDownscaleFactor > bh)
                    WorldDownscaleFactor++;

                var s = new Size(vw / WorldDownscaleFactor, vh / WorldDownscaleFactor);
                worldSprite = new Sprite(worldSheet, new Rectangle(int2.Zero, s), TextureChannel.RGBA);
                lastWorldViewportSize = worldViewport.Size;
            }

            worldBuffer.Bind(); // 绑定世界缓冲区

            // 如果世界视口发生变化,更新视口参数
            if (lastWorldViewport != worldViewport)
            {
                WorldSpriteRenderer.SetViewportParams(worldSheet.Size, WorldDownscaleFactor, depthMargin, worldViewport.Location);
                lastWorldViewport = worldViewport;
            }

            renderType = RenderType.World; // 设置渲染类型为世界
        }

        /// <summary>
        /// 开始UI渲染
        /// </summary>
        public void BeginUI()
        {
            if (renderType == RenderType.World)
            {
                // 完成世界渲染
                Flush();
                worldBuffer.Unbind();

                // 将世界缓冲区渲染到UI缓冲区
                screenBuffer.Bind();

                var scale = Window.EffectiveWindowScale;
                var bufferScale = new float3(
                    (int)(screenSprite.Bounds.Width / scale) / worldSprite.Size.X,
                    (int)(-screenSprite.Bounds.Height / scale) / worldSprite.Size.Y,
                    1f);

                // 启用像素艺术缩放,绘制世界精灵,然后禁用像素艺术缩放
                SpriteRenderer.EnablePixelArtScaling(true);
                RgbaSpriteRenderer.DrawSprite(worldSprite, float3.Zero, bufferScale);
                Flush();
                SpriteRenderer.EnablePixelArtScaling(false);
            }
            else
            {
                // 跳过了世界渲染
                BeginFrame();
                screenBuffer.Bind();
            }

            renderType = RenderType.UI; // 设置渲染类型为UI
        }

        /// <summary>
        /// 设置调色板
        /// </summary>
        public void SetPalette(HardwarePalette palette)
        {
            // 注意:palette.Texture和palette.ColorShifts同时更新
            // 所以我们只需要检查其中一个来知道是否必须更新纹理
            // 同时比较高度,以防添加了新的调色板
            if (palette.Texture == currentPaletteTexture && palette.Height == currentPaletteHeight)
                return;

            Flush(); // 刷新渲染
            currentPaletteTexture = palette.Texture;
            currentPaletteHeight = palette.Height;

            // 为所有渲染器设置调色板
            SpriteRenderer.SetPalette(palette);
            WorldSpriteRenderer.SetPalette(palette);

            foreach (var r in WorldRenderers)
                r.SetPalette(palette);
        }

        /// <summary>
        /// 结束一帧的渲染
        /// </summary>
        public void EndFrame(IInputHandler inputHandler)
        {
            // 确保当前正在进行UI渲染
            if (renderType != RenderType.UI)
                throw new InvalidOperationException($"EndFrame called with renderType = {renderType}, expected RenderType.UI.");

            Flush(); // 刷新渲染

            screenBuffer.Unbind(); // 解绑屏幕缓冲区

            // 将合成器缓冲区渲染到屏幕
            // HACK / PERF:调整坐标以覆盖实际窗口,同时保持缓冲区视口参数
            // 这样可以节省每帧两次冗余(且昂贵)的SetViewportParams调用
            RgbaSpriteRenderer.DrawSprite(screenSprite, new float3(0, lastBufferSize.Height, 0),
                new float3(lastBufferSize.Width / screenSprite.Size.X, -lastBufferSize.Height / screenSprite.Size.Y, 1f));
            Flush(); // 再次刷新渲染

            Window.PumpInput(inputHandler); // 处理输入
            Context.Present(); // 提交渲染结果

            renderType = RenderType.None; // 重置渲染类型
        }

        /// <summary>
        /// 绘制批处理
        /// </summary>
        public void DrawBatch<T>(IVertexBuffer<T> vertices, IShader shader,
            int firstVertex, int numVertices, PrimitiveType type)
            where T : struct
        {
            vertices.Bind(); // 绑定顶点缓冲区
            shader.Bind(); // 绑定着色器
            Context.DrawPrimitives(type, firstVertex, numVertices); // 绘制图元
            PerfHistory.Increment("batches", 1); // 增加批处理计数
        }

        /// <summary>
        /// 绘制四边形批处理
        /// </summary>
        public void DrawQuadBatch(ref Vertex[] vertices, IShader shader, int numVertices)
        {
            tempVertexBuffer.SetData(ref vertices, numVertices); // 设置临时顶点缓冲区数据
            DrawQuadBatch(tempVertexBuffer, quadIndexBuffer, shader, numVertices / 4 * 6, 0);
        }

        /// <summary>
        /// 绘制四边形批处理(泛型版本)
        /// </summary>
        public void DrawQuadBatch<T>(IVertexBuffer<T> vertices, IIndexBuffer indices, IShader shader, int numIndices, int start)
            where T : struct
        {
            vertices.Bind(); // 绑定顶点缓冲区
            indices.Bind(); // 绑定索引缓冲区
            shader.Bind(); // 绑定着色器
            Context.DrawElements(numIndices, start); // 绘制元素
            PerfHistory.Increment("batches", 1); // 增加批处理计数
        }

        /// <summary>
        /// 刷新渲染
        /// </summary>
        public void Flush()
        {
            CurrentBatchRenderer = null; // 设置当前批处理渲染器为null,触发刷新
        }

        // 各种窗口和渲染属性
        public Size Resolution => Window.EffectiveWindowSize; // 有效窗口大小
        public Size NativeResolution => Window.NativeWindowSize; // 原生窗口大小
        public float WindowScale => Window.EffectiveWindowScale; // 有效窗口缩放
        public float NativeWindowScale => Window.NativeWindowScale; // 原生窗口缩放
        public GLProfile GLProfile => Window.GLProfile; // GL配置文件
        public GLProfile[] SupportedGLProfiles => Window.SupportedGLProfiles; // 支持的GL配置文件

        /// <summary>
        /// 批处理渲染器接口
        /// </summary>
        public interface IBatchRenderer { void Flush(); }

        /// <summary>
        /// 当前批处理渲染器
        /// </summary>
        public IBatchRenderer CurrentBatchRenderer
        {
            get => currentBatchRenderer;

            set
            {
                if (currentBatchRenderer == value)
                    return;
                currentBatchRenderer?.Flush(); // 如果当前批处理渲染器不为null,刷新它
                currentBatchRenderer = value; // 设置新的批处理渲染器
            }
        }

        /// <summary>
        /// 创建帧缓冲区
        /// </summary>
        public IFrameBuffer CreateFrameBuffer(Size s)
        {
            return Context.CreateFrameBuffer(s);
        }

        /// <summary>
        /// 创建着色器
        /// </summary>
        public IShader CreateShader(IShaderBindings bindings)
        {
            return Context.CreateShader(bindings);
        }

        /// <summary>
        /// 创建顶点缓冲区
        /// </summary>
        public IVertexBuffer<T> CreateVertexBuffer<T>(int length) where T : struct
        {
            return Context.CreateVertexBuffer<T>(length);
        }

        /// <summary>
        /// 启用裁剪
        /// </summary>
        public void EnableScissor(Rectangle rect)
        {
            // 必须保持在当前裁剪矩形内
            if (scissorState.Count > 0)
                rect = Rectangle.Intersect(rect, scissorState.Peek());

            Flush(); // 刷新渲染

            if (renderType == RenderType.World)
            {
                // 为世界渲染计算裁剪矩形
                var r = Rectangle.FromLTRB(
                    rect.Left / WorldDownscaleFactor,
                    rect.Top / WorldDownscaleFactor,
                    (rect.Right + WorldDownscaleFactor - 1) / WorldDownscaleFactor,
                    (rect.Bottom + WorldDownscaleFactor - 1) / WorldDownscaleFactor);
                worldBuffer.EnableScissor(r);
            }
            else
                Context.EnableScissor(rect.X, rect.Y, rect.Width, rect.Height);

            scissorState.Push(rect); // 将裁剪矩形压入栈
        }

        /// <summary>
        /// 禁用裁剪
        /// </summary>
        public void DisableScissor()
        {
            scissorState.Pop(); // 弹出栈顶的裁剪矩形
            Flush(); // 刷新渲染

            if (renderType == RenderType.World)
            {
                // 恢复之前的裁剪矩形
                if (scissorState.Count > 0)
                {
                    var rect = scissorState.Peek();
                    var r = Rectangle.FromLTRB(
                        rect.Left / WorldDownscaleFactor,
                        rect.Top / WorldDownscaleFactor,
                        (rect.Right + WorldDownscaleFactor - 1) / WorldDownscaleFactor,
                        (rect.Bottom + WorldDownscaleFactor - 1) / WorldDownscaleFactor);
                    worldBuffer.EnableScissor(r);
                }
                else
                    worldBuffer.DisableScissor();
            }
            else
            {
                // 恢复之前的裁剪矩形
                if (scissorState.Count > 0)
                {
                    var rect = scissorState.Peek();
                    Context.EnableScissor(rect.X, rect.Y, rect.Width, rect.Height);
                }
                else
                    Context.DisableScissor();
            }
        }

        /// <summary>
        /// 启用深度缓冲区
        /// </summary>
        public void EnableDepthBuffer()
        {
            Flush(); // 刷新渲染
            Context.EnableDepthBuffer();
        }

        /// <summary>
        /// 禁用深度缓冲区
        /// </summary>
        public void DisableDepthBuffer()
        {
            Flush(); // 刷新渲染
            Context.DisableDepthBuffer();
        }

        /// <summary>
        /// 清除深度缓冲区
        /// </summary>
        public void ClearDepthBuffer()
        {
            Flush(); // 刷新渲染
            Context.ClearDepthBuffer();
        }

        /// <summary>
        /// 启用抗锯齿过滤器
        /// </summary>
        public void EnableAntialiasingFilter()
        {
            // 确保当前正在进行UI渲染
            if (renderType != RenderType.UI)
                throw new InvalidOperationException($"EndFrame called with renderType = {renderType}, expected RenderType.UI.");

            Flush(); // 刷新渲染
            SpriteRenderer.EnablePixelArtScaling(true);
        }

        /// <summary>
        /// 禁用抗锯齿过滤器
        /// </summary>
        public void DisableAntialiasingFilter()
        {
            // 确保当前正在进行UI渲染
            if (renderType != RenderType.UI)
                throw new InvalidOperationException($"EndFrame called with renderType = {renderType}, expected RenderType.UI.");

            Flush(); // 刷新渲染
            SpriteRenderer.EnablePixelArtScaling(false);
        }

        /// <summary>
        /// 抓取窗口鼠标焦点
        /// </summary>
        public void GrabWindowMouseFocus()
        {
            Window.GrabWindowMouseFocus();
        }

        /// <summary>
        /// 释放窗口鼠标焦点
        /// </summary>
        public void ReleaseWindowMouseFocus()
        {
            Window.ReleaseWindowMouseFocus();
        }

        /// <summary>
        /// 保存屏幕截图
        /// </summary>
        public void SaveScreenshot(string path)
        {
            // 直接从纹理获取数据以防止表格缓冲它
            var src = screenBuffer.Texture.GetData();
            var srcWidth = screenSprite.Sheet.Size.Width;
            var destWidth = screenSprite.Bounds.Width;
            var destHeight = -screenSprite.Bounds.Height;

            // 在线程池中处理截图保存
            ThreadPool.QueueUserWorkItem(_ =>
            {
                // 从(较大的)后备表面提取屏幕矩形
                var dest = new byte[4 * destWidth * destHeight];
                for (var y = 0; y < destHeight; y++)
                    Array.Copy(src, 4 * y * srcWidth, dest, 4 * y * destWidth, 4 * destWidth);

                // 保存为PNG格式
                new Png(dest, SpriteFrameType.Bgra32, destWidth, destHeight).Save(path);
            });
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            worldBuffer?.Dispose();
            screenBuffer.Dispose();
            worldBufferSnapshot.Dispose();
            tempVertexBuffer.Dispose();
            quadIndexBuffer.Dispose();
            fontSheetBuilder?.Dispose();
            if (Fonts != null)
                foreach (var font in Fonts.Values)
                    font.Dispose();
            Window.Dispose();
        }

        /// <summary>
        /// 设置垂直同步
        /// </summary>
        public void SetVSyncEnabled(bool enabled)
        {
            Window.Context.SetVSyncEnabled(enabled);
        }

        /// <summary>
        /// 获取剪贴板文本
        /// </summary>
        public string GetClipboardText()
        {
            return Window.GetClipboardText();
        }

        /// <summary>
        /// 设置剪贴板文本
        /// </summary>
        public bool SetClipboardText(string text)
        {
            return Window.SetClipboardText(text);
        }

        // 其他属性
        public string GLVersion => Context.GLVersion; // GL版本
        public int DisplayCount => Window.DisplayCount; // 显示器数量
        public int CurrentDisplay => Window.CurrentDisplay; // 当前显示器
    }
}

逻辑解读

Renderer类是OpenRA游戏引擎的核心渲染组件,负责管理和协调游戏的渲染流程。它主要处理两种渲染类型:世界渲染和UI渲染。

主要功能:

  1. 初始化与配置

    • 创建和管理窗口及图形上下文
    • 设置各种渲染器和缓冲区
    • 初始化字体和深度缓冲区
  2. 渲染流程管理

    • 提供BeginWorld()和BeginUI()方法,分别开始世界和UI的渲染
    • 通过EndFrame()完成一帧的渲染并提交结果
    • 使用帧缓冲区进行离屏渲染,然后将结果合成到屏幕上
  3. 绘制操作

    • 提供批量绘制精灵和几何图形的方法
    • 管理裁剪区域和深度缓冲区
    • 支持抗锯齿和像素艺术缩放
  4. 资源管理

    • 创建和管理纹理、着色器和缓冲区
    • 处理窗口缩放和分辨率变化
    • 实现IDisposable接口以正确释放资源
  5. 辅助功能

    • 截图功能
    • 剪贴板操作
    • 鼠标焦点控制

渲染流程:

  1. 通过BeginWorld()开始世界渲染,将世界内容渲染到worldBuffer中
  2. 通过BeginUI()开始UI渲染,可能会将worldBuffer的内容合成到screenBuffer中
  3. 通过EndFrame()完成渲染,将screenBuffer的内容绘制到实际屏幕上

整个设计支持高效的批处理渲染,并且能够处理不同的分辨率、缩放和HiDPI显示。

流程图

EndFrame流程
Flush
检查渲染状态
解绑screenBuffer
将合成缓冲区渲染到屏幕
处理输入
提交渲染结果
重置renderType为None
BeginUI流程
是否为World?
检查当前渲染类型
完成世界渲染
将世界缓冲区渲染到UI缓冲区
BeginFrame
绑定screenBuffer
设置renderType为UI
BeginWorld流程
BeginFrame
检查渲染状态
检查worldSheet
创建/更新worldSprite
绑定worldBuffer
设置视口参数
设置renderType为World
初始化Renderer
SetMaximumViewportSize
渲染循环
BeginWorld
世界渲染操作
BeginUI
UI渲染操作
EndFrame

特殊语法与技巧

  1. 属性表达式体

    public Size WorldFrameBufferSize => worldSheet.Size;
    

    这是C# 6.0引入的表达式体属性,简化了只读属性的定义。

  2. 空条件运算符

    worldBuffer?.Dispose();
    

    如果worldBuffer为null,不会调用Dispose方法;否则调用它的Dispose方法。

  3. 字典创建与LINQ

    Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key, x => new SpriteFont(...));
    

    使用LINQ的ToDictionary方法从集合创建字典。

  4. Lambda表达式

    Window.OnWindowScaleChanged += (oldNative, oldEffective, newNative, newEffective) => {...};
    

    用于事件处理和回调的简洁语法。

  5. 泛型约束

    public void DrawBatch<T>(IVertexBuffer<T> vertices, ...) where T : struct
    

    限制泛型类型T必须是值类型(struct)。

  6. 线程池使用

    ThreadPool.QueueUserWorkItem(_ => {...});
    

    用于在后台线程执行耗时操作,如保存截图。

  7. 字符串内插

    throw new InvalidOperationException($"BeginWorld called with renderType = {renderType}, expected RenderType.None.");
    

    使用$前缀和{}将变量嵌入字符串中。

  8. using语句与性能计时

    using (new PerfTimer("SpriteFonts")) {...}
    

    利用using语句和IDisposable模式自动调用Dispose方法,用于性能计时。

  9. 泛型方法

    public IVertexBuffer<T> CreateVertexBuffer<T>(int length) where T : struct
    

    允许方法根据调用时指定的类型参数工作。

  10. 空合并运算符

    public IRenderer[] WorldRenderers = Array.Empty<IRenderer>();
    

    使用Array.Empty()返回一个空数组,比new T[0]更高效。

  11. 接口定义在类内部

    public interface IBatchRenderer { void Flush(); }
    

    在类内部定义接口,将相关功能组织在一起。

  12. 属性的自动实现

    public SpriteRenderer WorldSpriteRenderer { get; }
    

    简化了只读属性的定义,编译器自动生成后备字段。

  13. 三元运算符

    var size = (graphicsSettings.Mode == WindowMode.Windowed) ? graphicsSettings.WindowedSize : graphicsSettings.FullscreenSize;
    

    简洁地根据条件选择不同的值。

  14. 默认参数值

    public int WorldDownscaleFactor { get; private set; } = 1;
    

    为属性或字段提供默认值。

  15. Stack数据结构

    readonly Stack<Rectangle> scissorState = new();
    

    用于管理裁剪状态的堆栈,支持嵌套裁剪区域。

  16. 密封类

    public sealed class Renderer : IDisposable
    

    防止类被继承,可能出于性能或设计考虑。

  17. 枚举定义

    enum RenderType { None, World, UI }
    

    在类内部定义枚举,限制其作用域。

这个Renderer类展示了现代C#编程的许多最佳实践和模式,特别是在图形渲染和资源管理方面。它采用了面向对象设计,并利用了C#的众多语言特性来编写高效、可维护的代码。

python的paramiko 库如何使用
ptyhon 如何为自闭症儿童的定制图像查看游戏
python如何自动生成markdown格式文件
microPython的源码解析之 objgetitemiter.c
chatGPT真的会给出windows序列号
C#进行串口应用开发如何判断串口通信故障的具体原因
python生成伪随机数序列库randomstate
量化交易系统中+如何设计和实现高频交易算法?
如何用c#语言进行开发一个edge浏览器插件
c++ qt如何进行对象序列化与反序列化
C#进行串口应用开发如何实现串口数据的校验
python 只用20行代码完成一个web应用开发
c#如何使用 USB(Universal Serial Bus)进行通信
车载系统软件工程师如何处理车载系统的多语言支持
python开发的开源数学软件系统SageMath
microPython的源码解析之 objboundmeth.c
python的fractions库如何使用
python 如何控制鼠标键盘
python加PyQT如何开发一个端口扫描工具
python的scipy提供什么功能
车载系统软件工程师如何处理车载系统的启动时间和响应速度优化
NI-Motion通过National Instruments的FlexMotion软件控制运动控制卡上的轴进行运动C语言代码示例
python进行函数式编程的工具toolz
python编写一个简单神经网络计算代码带有反向传播,不用任何框架
python如何开发一个端口转发工具
python 如何不用循环利用对数欧拉方法实现全向量化
车载系统软件工程师如何处理车载系统的实时数据流处理
python的xmlrpc库如何使用
c#视觉应用开发中如何在C#中进行图像去重叠?
车载系统软件工程师如何处理车载系统的电磁兼容性(EMC)
如何将列的数字编号,转化为EXcel的字母表示的列编号
python如何绘制股票的K线图
Python在空域交通管理中的应用
智能农业设备软件工程师如何实现农业设备的电磁兼容性(EMC)
c#视觉应用开发中如何在C#中进行图像去色散?
c#视觉应用开发中如何在C#中处理图像噪声?
车载系统软件工程师如何实现车载系统的个性化设置和配置
python的Godot Engine库如何安装使用以及功能和用途
智能农业设备软件工程师如何实现农业设备的故障预测和预防
C#进行串口应用开发如何改变串口的默认端口号
C#进行串口应用开发如何通过串口实现模拟串口的虚拟化
OpenCV的图像分割分水岭算法(watershed algorithm)示例
Python如何实现粒子效果如烟雾、火焰、雨滴等.
python有哪些定时触发的框架
python如何快速创建命令行接口(CLI)
C#进行串口应用开发如何管理同时打开多个串口的通信
量化交易系统如何获取历史市场数据?
量化交易系统中+如何处理回测中的数据偏差问题?
python web应用开发神器 入门十六
C#进行串口应用开发如何区分串口接收到的数据帧
NI-Motion 实现一个轴的基于速度的直线运动期间还包括速度覆盖(即在运动过程中改变速度)的操作 C语言示例代码
microPython的源码解析之 compile.c
python如何创建SOCKS 代理连接
车载系统软件工程师如何处理车载系统的兼容性测试和验证
windows程序如何转linux开发
c#视觉应用开发中如何在C#中进行图像锐化?
pyrhon 如何将实时的tick行情,处理成1分钟k线数据
streamlit如何布局
opencv库的功能
python的pure-eval库
c#视觉应用开发中如何在C#中实现光流(Optical Flow)算法?
量化交易系统中+如何设计高效的数据库架构?
openai 的API 从自然语言生成sql语句
车载系统软件工程师如何确保车载系统的法规和标准符合性
车载系统软件工程师如何处理车载传感器数据(如雷达、激光雷达)
Python如何使用pickle库来复制、保存和加载一个空间
microPython的源码解析之 bc.c
microPython的源码解析之 modio.c
智能农业设备软件工程师如何集成和管理农业设备的系统维护
openai联合创始人用1000行纯C语言手搓的GPT-2训练代码
为什么Python对VR社区很重要
车载系统软件工程师如何实现车载导航和GPS系统
C#进行串口应用开发如何定位串口通信故障的原因与解决方案
microPython的源码解析之 reader.c
C#进行串口应用开发如何实现串口的监听服务
python生成和解决迷宫的库maze
Python的faker库,测试工作者的福音
C#进行串口应用开发如何修改Windows下的串口权限
microPython的源码解析之 runtime.c
microPython的源码解析之 objtype.c
c#视觉应用开发中如何在C#中处理3D图像数据?
python web应用开发神器 入门二十五
车载系统软件工程师如何实现车载语音识别和控制
智能农业设备软件工程师如何实现农业设备的用户权限管理
linux 的gdb软件如何使用
python的装饰器模式
量化交易系统中+如何扩展系统以支持更多用户和更大交易量?
microPython的源码解析之 frozenmod.c
车载系统软件工程师如何实现车载系统的紧急制动和碰撞预警
智能农业设备软件工程师如何处理设备的实时数据流处理
Python的使用opencv库人脸识别
C#进行串口应用开发如何优化串口通信的实时性与吞吐量
如何应聘普通测试工程师
C#进行串口应用开发如何实现基于串口的远程监控与报警系统
如何知道对方主机用了虚拟ip
python的sympy库介绍
使用 Python 和 Gretel.ai 生成合成位置数据
C#进行串口应用开发如何实现串口通信的自动重连与重传功能
量化交易系统中+如何处理实时流数据?
Python如何进行时间同步

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值