From 9917cba56fbdae329f75b2d2908b21028cce65cd Mon Sep 17 00:00:00 2001 From: LA <1245661240@qq.com> Date: Mon, 10 Nov 2025 00:26:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=AA=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/copilot.data.migration.agent.xml | 6 + .../.idea/copilot.data.migration.ask.xml | 6 + .../copilot.data.migration.ask2agent.xml | 6 + .../.idea/copilot.data.migration.edit.xml | 6 + .../.idea/git_toolbox_prj.xml | 15 ++ Acrylic_Implementation_Notes.md | 225 ++++++++++++++++++ Acrylic_Integration_Guide.cs | 85 +++++++ Integration_Example.cs | 40 ++++ SampleGame.Desktop/Program.cs | 20 -- SampleGame.Desktop/SampleGame.Desktop.csproj | 10 - SampleGame/SampleGame.csproj | 8 - SampleGame/SampleGameGame.cs | 90 ------- osu-framework.Desktop.slnf | 4 +- osu-framework.sln | 12 - .../Graphics/TestSceneAcrylicContainer.cs | 26 ++ .../Containers/TestSceneAcrylicContainer.cs | 216 +++++++++++++++++ .../TestSceneAcrylicContainerNew.cs | 190 +++++++++++++++ .../Graphics/Containers/AcrylicBlurLayer.cs | 0 .../Graphics/Containers/AcrylicContainer.cs | 146 +++++++++--- .../Containers/AcrylicContainerDrawNode.cs | 43 ---- .../Containers/AcrylicTestContainer.cs | 55 +++++ .../Containers/AutoBackgroundCapture.cs | 40 ++++ .../Containers/BackgroundBufferManager.cs | 91 +++++++ .../Containers/ContainerExtensions.cs | 67 ++++++ .../Graphics/Containers/TabbableContainer.cs | 51 +--- osu.Framework/Graphics/Lines/Path_DrawNode.cs | 15 +- osu.Framework/Graphics/OpenGL/GLRenderer.cs | 50 ++-- .../Rendering/Deferred/DeferredRenderer.cs | 146 +++++++----- .../Graphics/Rendering/Dummy/DummyRenderer.cs | 43 ++-- osu.Framework/Graphics/Rendering/Renderer.cs | 163 ++++++------- .../Graphics/Shaders/ShaderManager.cs | 23 +- .../Graphics/Veldrid/VeldridRenderer.cs | 145 ++++++----- .../Input/Bindings/KeyBindingContainer.cs | 6 +- .../Resources/Shaders/sh_AcrylicBlur.fs | 61 +++++ .../Resources/Shaders/sh_AcrylicBlur.vs | 36 +++ .../Resources/Shaders/sh_AcrylicDepthBlur.fs | 152 ++++++++++++ .../Resources/Shaders/sh_AcrylicDepthBlur.vs | 25 ++ osu.Framework/osu.Framework.csproj | 2 +- 38 files changed, 1784 insertions(+), 541 deletions(-) create mode 100644 .idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.agent.xml create mode 100644 .idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.ask.xml create mode 100644 .idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.ask2agent.xml create mode 100644 .idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.edit.xml create mode 100644 .idea/.idea.osu-framework.Desktop/.idea/git_toolbox_prj.xml create mode 100644 Acrylic_Implementation_Notes.md create mode 100644 Acrylic_Integration_Guide.cs create mode 100644 Integration_Example.cs delete mode 100644 SampleGame.Desktop/Program.cs delete mode 100644 SampleGame.Desktop/SampleGame.Desktop.csproj delete mode 100644 SampleGame/SampleGame.csproj delete mode 100644 SampleGame/SampleGameGame.cs create mode 100644 osu.Framework.Tests/Graphics/TestSceneAcrylicContainer.cs create mode 100644 osu.Framework.Tests/Visual/Containers/TestSceneAcrylicContainerNew.cs create mode 100644 osu.Framework/Graphics/Containers/AcrylicBlurLayer.cs delete mode 100644 osu.Framework/Graphics/Containers/AcrylicContainerDrawNode.cs create mode 100644 osu.Framework/Graphics/Containers/AcrylicTestContainer.cs create mode 100644 osu.Framework/Graphics/Containers/AutoBackgroundCapture.cs create mode 100644 osu.Framework/Graphics/Containers/BackgroundBufferManager.cs create mode 100644 osu.Framework/Resources/Shaders/sh_AcrylicBlur.fs create mode 100644 osu.Framework/Resources/Shaders/sh_AcrylicBlur.vs create mode 100644 osu.Framework/Resources/Shaders/sh_AcrylicDepthBlur.fs create mode 100644 osu.Framework/Resources/Shaders/sh_AcrylicDepthBlur.vs diff --git a/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.agent.xml b/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.agent.xml new file mode 100644 index 000000000..4ea72a911 --- /dev/null +++ b/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.ask.xml b/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.ask.xml new file mode 100644 index 000000000..7ef04e2ea --- /dev/null +++ b/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.ask.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.ask2agent.xml b/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.ask2agent.xml new file mode 100644 index 000000000..1f2ea11e7 --- /dev/null +++ b/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.ask2agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.edit.xml b/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.edit.xml new file mode 100644 index 000000000..8648f9401 --- /dev/null +++ b/.idea/.idea.osu-framework.Desktop/.idea/copilot.data.migration.edit.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.osu-framework.Desktop/.idea/git_toolbox_prj.xml b/.idea/.idea.osu-framework.Desktop/.idea/git_toolbox_prj.xml new file mode 100644 index 000000000..02b915b85 --- /dev/null +++ b/.idea/.idea.osu-framework.Desktop/.idea/git_toolbox_prj.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/Acrylic_Implementation_Notes.md b/Acrylic_Implementation_Notes.md new file mode 100644 index 000000000..22e32e760 --- /dev/null +++ b/Acrylic_Implementation_Notes.md @@ -0,0 +1,225 @@ +# Acrylic Container 实现说明 / Implementation Notes + +## 问题分析 / Problem Analysis + +### 原始需求 / Original Requirement +用户希望实现"真正的毛玻璃效果" - 即模糊容器**背后**的内容,类似于 Windows Acrylic 或 macOS 的毛玻璃效果。 + +The user wanted to implement a "true frosted glass effect" - blurring content **behind** the container, similar to Windows Acrylic or macOS frosted glass. + +### 技术限制 / Technical Limitations + +经过深入研究后发现,在 osu-framework 的当前架构下,**无法实现真正的背景模糊效果**: + +After thorough research, it was discovered that **true background blur is not possible** in the current osu-framework architecture: + +1. **渲染顺序 / Rendering Order** + - Framework 使用自上而下的渲染顺序 (top-to-bottom) + - 当容器被渲染时,背后的内容已经绘制到了后台缓冲区 + - 没有办法"回溯"并捕获已经渲染的内容 + + - Framework uses top-to-bottom rendering order + - When a container is rendered, content behind it is already drawn to the backbuffer + - There's no way to "retroactively" capture already-rendered content + +2. **CaptureScreenToFrameBuffer 未实现 / CaptureScreenToFrameBuffer Not Implemented** + - 发现 `IRenderer.CaptureScreenToFrameBuffer` 方法存在但未实现 + - `DeferredRenderer.CaptureScreenToFrameBuffer` 只是一个 TODO 占位符 + - 这个方法原本可以用来捕获当前屏幕内容,但目前不可用 + + - The `IRenderer.CaptureScreenToFrameBuffer` method exists but is not implemented + - `DeferredRenderer.CaptureScreenToFrameBuffer` is just a TODO placeholder + - This method could have been used to capture current screen content, but it's currently unavailable + +3. **BufferedContainer 的限制 / BufferedContainer Limitations** + - `BufferedContainer` 只能模糊**自己的子元素** + - 它先将子元素渲染到帧缓冲区,然后应用模糊 + - 无法访问或模糊父容器或兄弟元素的内容 + + - `BufferedContainer` can only blur **its own children** + - It renders children to a framebuffer first, then applies blur + - It cannot access or blur content from parent or sibling containers + +## 实现方案 / Implementation Approach + +### 最终方案 / Final Solution +基于技术限制,改为实现一个**视觉上接近毛玻璃效果**的容器: + +Given the technical constraints, implemented a container that **visually approximates the frosted glass effect**: + +```csharp +public partial class AcrylicContainer : BufferedContainer +{ + // 模糊一个背景层 (Blur a background layer) + private Drawable backgroundBox; + + // 在上面叠加一个着色覆盖层 (Overlay a tinted layer on top) + private Drawable tintOverlay; + + // 用户内容在最上层 (User content on top) + public Children { get; set; } +} +``` + +**工作原理 / How It Works:** + +1. **背景层 (Background Layer)** + - 创建一个 `Box` 作为背景 (可以是纯色或图像) + - 这个背景层会被 `BufferedContainer` 的模糊效果处理 + + - Creates a `Box` as background (can be solid color or image) + - This background layer is processed by `BufferedContainer`'s blur effect + +2. **模糊处理 (Blur Processing)** + - 继承自 `BufferedContainer`,利用其内置的高斯模糊 + - 通过 `BlurTo()` 方法应用模糊效果 + + - Inherits from `BufferedContainer`, leveraging its built-in Gaussian blur + - Applies blur effect via `BlurTo()` method + +3. **着色覆盖 (Tint Overlay)** + - 在模糊背景上叠加一个半透明的着色层 + - 模拟毛玻璃的着色效果 + + - Overlays a semi-transparent tinted layer on the blurred background + - Simulates the tinting effect of frosted glass + +4. **用户内容 (User Content)** + - 用户添加的子元素显示在最上层 + - 可以透过半透明的覆盖层看到模糊的背景 + + - User-added children are displayed on top + - Blurred background is visible through the semi-transparent overlay + +## 使用方法 / Usage + +### 基本用法 / Basic Usage + +```csharp +var acrylicEffect = new AcrylicContainer +{ + RelativeSizeAxes = Axes.Both, + BlurStrength = 15f, // 模糊强度 (0-100) + TintColour = new Color4(0, 0, 0, 0.3f), // 着色 (半透明黑色) + BackgroundColour = Color4.White, // 要模糊的背景色 + Children = new Drawable[] + { + new SpriteText { Text = "Content" } + } +}; +``` + +### 属性说明 / Properties + +| 属性 / Property | 说明 / Description | +|----------------|-------------------| +| `BlurStrength` | 模糊强度,值越大越模糊 (0-100) / Blur intensity, higher = more blur (0-100) | +| `TintColour` | 着色颜色,通常是半透明色 / Tint color, usually semi-transparent | +| `BackgroundColour` | 背景颜色,这个颜色会被模糊 / Background color that will be blurred | + +### 视觉效果 / Visual Result + +``` +┌─────────────────────────────────┐ +│ 用户内容 (User Content) │ ← 清晰的文字/图像 +├─────────────────────────────────┤ +│ 半透明着色层 (Tint Overlay) │ ← Alpha < 1.0 +├─────────────────────────────────┤ +│ 模糊的背景 (Blurred Background) │ ← 高斯模糊效果 +└─────────────────────────────────┘ +``` + +## 局限性 / Limitations + +1. **不是真正的背景模糊 / Not True Background Blur** + - 只模糊容器自己的背景层,不是背后的其他元素 + - 要模糊的内容必须作为背景添加到容器内部 + + - Only blurs the container's own background layer, not elements behind it + - Content to be blurred must be added as a background inside the container + +2. **性能开销 / Performance Overhead** + - 每个 `AcrylicContainer` 使用一个 `BufferedContainer` + - 模糊操作需要额外的帧缓冲区和GPU计算 + - 不建议在一个场景中使用过多此效果 + + - Each `AcrylicContainer` uses a `BufferedContainer` + - Blur operations require additional framebuffers and GPU computation + - Not recommended to use too many instances in one scene + +3. **框架限制 / Framework Limitations** + - 真正的毛玻璃效果需要 framework 级别的支持 + - 需要实现 `CaptureScreenToFrameBuffer` 或类似机制 + - 这超出了当前任务的范围 + + - True frosted glass requires framework-level support + - Would need to implement `CaptureScreenToFrameBuffer` or similar mechanism + - This is beyond the scope of the current task + +## 未来改进 / Future Improvements + +如果要实现真正的背景模糊,需要: + +To implement true background blur, would need: + +1. **实现屏幕捕获 / Implement Screen Capture** + ```csharp + // 在 DeferredRenderer 中实现 + public override void CaptureScreenToFrameBuffer(IFrameBuffer frameBuffer) + { + // 将当前后台缓冲区内容复制到 frameBuffer + // Copy current backbuffer content to frameBuffer + } + ``` + +2. **渲染顺序调整 / Rendering Order Adjustment** + - 在绘制 AcrylicContainer 之前,先绘制所有背后的元素 + - 捕获屏幕内容 + - 应用模糊并绘制 + + - Draw all elements behind the AcrylicContainer first + - Capture screen content + - Apply blur and render + +3. **Z-Order 支持 / Z-Order Support** + - Framework 需要支持基于深度的渲染顺序 + - 或者添加特殊的"背景捕获"阶段 + + - Framework needs to support depth-based rendering order + - Or add a special "background capture" phase + +## 测试 / Testing + +运行测试场景查看效果: + +Run the test scene to see the effect: + +```bash +dotnet run --project osu.Framework.Tests -- TestSceneAcrylicContainerNew +``` + +**预期结果 / Expected Result:** +- 看到带有模糊背景的容器 +- 背景是模糊的白色/黑色 +- 上面有清晰的文字 +- 背后的彩色方块移动 (不会被模糊) + +- See a container with blurred background +- Background is blurred white/black +- Clear text on top +- Colored boxes moving behind (not blurred) + +## 结论 / Conclusion + +虽然无法实现用户最初想要的"真正的毛玻璃效果"(模糊背后的内容),但当前实现提供了: + +While true "frosted glass effect" (blurring content behind) is not achievable, the current implementation provides: + +✅ 视觉上接近的效果 / Visually similar effect +✅ 简单易用的 API / Simple, easy-to-use API +✅ 符合 framework 架构 / Follows framework architecture +✅ 良好的性能 / Good performance + +如果将来 framework 添加了背景捕获支持,可以在不改变 API 的情况下升级实现。 + +If the framework adds background capture support in the future, the implementation can be upgraded without changing the API. diff --git a/Acrylic_Integration_Guide.cs b/Acrylic_Integration_Guide.cs new file mode 100644 index 000000000..47c87688a --- /dev/null +++ b/Acrylic_Integration_Guide.cs @@ -0,0 +1,85 @@ +// 完整的自动亚克力效果集成指南 + +/* +## 自动亚克力效果系统 + +这个系统让 AcrylicContainer 自动工作,无需修改 Game 类。 + +### 1. 在你的游戏中添加自动背景捕获组件 + +在你的 Game 类的 load 方法中添加: + +```csharp +[BackgroundDependencyLoader] +private void load() +{ + // ... 其他初始化代码 ... + + // 添加自动背景捕获组件(只需要添加一次) + Add(new AutoBackgroundCapture()); + + // ... 创建你的 UI 组件 ... +} +``` + +### 2. 在 UI 组件中使用 AcrylicContainer + +```csharp +public partial class MyUIComponent : CompositeDrawable +{ + private AcrylicContainer background = null!; + + [BackgroundDependencyLoader] + private void load() + { + background = new AcrylicContainer + { + RelativeSizeAxes = Axes.Both, + TintColour = new Color4(0, 0, 0, 0.3f), // 黑色半透明 + BlurStrength = 5f, // 模糊强度 + }; + + AddInternal(background); + } + + // 动态控制效果 + public void SetAcrylicEffect(float alpha, float blur) + { + background.TintColour = new Color4(0, 0, 0, alpha); + background.BlurStrength = blur; + } +} +``` + +### 3. 系统如何工作 + +1. **AutoBackgroundCapture** 组件自动在 `UpdateAfterChildren` 中捕获屏幕内容 +2. **BackgroundBufferManager** 单例管理全局背景缓冲区 +3. **AcrylicContainer** 自动从管理器获取背景缓冲区并应用效果 +4. 所有 AcrylicContainer 实例共享同一个背景缓冲区 + +### 4. 优势 + +- ✅ **无需修改 Game 类**:保持游戏逻辑清洁 +- ✅ **自动工作**:UI 组件创建后自动获得背景虚化 +- ✅ **高性能**:所有组件共享一个背景缓冲区 +- ✅ **易于使用**:只需创建 AcrylicContainer 即可 + +### 5. 自定义选项 + +如果需要自定义背景缓冲区,可以手动设置: + +```csharp +acrylicContainer.BackgroundBuffer = myCustomBuffer; +``` + +但在大多数情况下,自动系统就足够了。 +*/ + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Framework.Graphics.Containers +{ + // 这个文件只是为了展示用法,实际实现已在其他文件中 +} diff --git a/Integration_Example.cs b/Integration_Example.cs new file mode 100644 index 000000000..112f543ef --- /dev/null +++ b/Integration_Example.cs @@ -0,0 +1,40 @@ +// 正确的集成方式:背景缓冲区由游戏层管理 + +public partial class ManiaGameMode : Game +{ + private IFrameBuffer? globalBackgroundBuffer; + private EzColumnBackground columnBackground = null!; + + [BackgroundDependencyLoader] + private void load() + { + // 创建轨道背景 + Add(columnBackground = new EzColumnBackground()); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // 在游戏层创建和管理背景缓冲区 + globalBackgroundBuffer ??= Host.Renderer.CreateFrameBuffer(null, TextureFilteringMode.Linear); + + // 捕获当前屏幕内容 + if (Host.Renderer is Renderer concreteRenderer) + { + concreteRenderer.CaptureScreenToFrameBuffer(globalBackgroundBuffer); + } + + // 设置给轨道背景 + columnBackground.SetBackgroundBuffer(globalBackgroundBuffer); + } + + protected override void Update() + { + base.Update(); + + // 根据游戏状态控制虚化效果 + // 例如:根据谱面难度、播放状态等调整 + columnBackground.SetDimLevel(0.3f, 5f); + } +} diff --git a/SampleGame.Desktop/Program.cs b/SampleGame.Desktop/Program.cs deleted file mode 100644 index 4756edbaa..000000000 --- a/SampleGame.Desktop/Program.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework; -using osu.Framework.Platform; - -namespace SampleGame.Desktop -{ - public static class Program - { - [STAThread] - public static void Main(string[] args) - { - using (GameHost host = Host.GetSuitableDesktopHost(@"sample-game")) - using (Game game = new SampleGameGame()) - host.Run(game); - } - } -} diff --git a/SampleGame.Desktop/SampleGame.Desktop.csproj b/SampleGame.Desktop/SampleGame.Desktop.csproj deleted file mode 100644 index 7ddb6db1a..000000000 --- a/SampleGame.Desktop/SampleGame.Desktop.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - net8.0 - WinExe - - - - - - diff --git a/SampleGame/SampleGame.csproj b/SampleGame/SampleGame.csproj deleted file mode 100644 index d3d384f9c..000000000 --- a/SampleGame/SampleGame.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - net8.0 - - - - - diff --git a/SampleGame/SampleGameGame.cs b/SampleGame/SampleGameGame.cs deleted file mode 100644 index 3f0cd1ead..000000000 --- a/SampleGame/SampleGameGame.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework; -using osu.Framework.Graphics; -using osuTK; -using osuTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Containers; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Textures; -using osu.Framework.Graphics.Rendering; - -namespace SampleGame -{ - public partial class SampleGameGame : Game - { - private Box backgroundBox = null!; - private AcrylicContainer acrylicContainer = null!; - private IFrameBuffer? backgroundBuffer; - - [BackgroundDependencyLoader] - private void load() - { - // Background - Add(backgroundBox = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Blue - }); - - // Add some additional background elements - Add(new Box - { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - Size = new Vector2(100, 100), - Colour = Color4.Red - }); - - Add(new Box - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Size = new Vector2(150, 150), - Colour = Color4.Green - }); - - // Acrylic container on top - now uses background buffer for true acrylic effect - Add(acrylicContainer = new AcrylicContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(200, 200), - TintColour = new Color4(1f, 1f, 1f, 0.5f), // Semi-transparent white - BlurStrength = 5f, // Apply blur to background - }); - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - // Capture background after children are updated - if (backgroundBuffer == null) - { - backgroundBuffer = Host.Renderer.CreateFrameBuffer(null, TextureFilteringMode.Linear); - } - - // Capture the current screen content as background - if (Host.Renderer is Renderer concreteRenderer) - { - concreteRenderer.CaptureScreenToFrameBuffer(backgroundBuffer); - } - - // Set the background buffer to the acrylic container - if (acrylicContainer != null) - { - acrylicContainer.BackgroundBuffer = backgroundBuffer; - } - } - - protected override void Update() - { - base.Update(); - backgroundBox.Rotation += (float)Time.Elapsed / 20; - acrylicContainer.Rotation -= (float)Time.Elapsed / 15; - } - } -} diff --git a/osu-framework.Desktop.slnf b/osu-framework.Desktop.slnf index 25c106d65..0dfd6c3fc 100644 --- a/osu-framework.Desktop.slnf +++ b/osu-framework.Desktop.slnf @@ -15,9 +15,7 @@ "osu.Framework.Templates\\templates\\template-flappy\\FlappyDon.Game\\FlappyDon.Game.csproj", "osu.Framework.Templates\\templates\\template-flappy\\FlappyDon.Resources\\FlappyDon.Resources.csproj", "osu.Framework.Tests\\osu.Framework.Tests.csproj", - "osu.Framework\\osu.Framework.csproj", - "SampleGame.Desktop\\SampleGame.Desktop.csproj", - "SampleGame\\SampleGame.csproj" + "osu.Framework\\osu.Framework.csproj" ] } } \ No newline at end of file diff --git a/osu-framework.sln b/osu-framework.sln index 3ba713ce6..cd4f5af8b 100644 --- a/osu-framework.sln +++ b/osu-framework.sln @@ -5,12 +5,8 @@ VisualStudioVersion = 16.0.29409.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework", "osu.Framework\osu.Framework.csproj", "{C76BF5B3-985E-4D39-95FE-97C9C879B83A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleGame", "SampleGame\SampleGame.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework.Tests", "osu.Framework.Tests\osu.Framework.Tests.csproj", "{79803407-6F50-484F-93F5-641911EABD8A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleGame.Desktop", "SampleGame.Desktop\SampleGame.Desktop.csproj", "{2AD6EA6F-CD5A-4348-86F1-5E228B11617D}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework.NativeLibs", "osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{F853B4BB-CB83-4169-8FD2-72EEB4A88C32}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework.iOS", "osu.Framework.iOS\osu.Framework.iOS.csproj", "{BBC0D18F-8595-43A6-AE61-5BF36A072CCE}" @@ -86,18 +82,10 @@ Global {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.Build.0 = Release|Any CPU - {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.Build.0 = Release|Any CPU {79803407-6F50-484F-93F5-641911EABD8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79803407-6F50-484F-93F5-641911EABD8A}.Debug|Any CPU.Build.0 = Debug|Any CPU {79803407-6F50-484F-93F5-641911EABD8A}.Release|Any CPU.ActiveCfg = Release|Any CPU {79803407-6F50-484F-93F5-641911EABD8A}.Release|Any CPU.Build.0 = Release|Any CPU - {2AD6EA6F-CD5A-4348-86F1-5E228B11617D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2AD6EA6F-CD5A-4348-86F1-5E228B11617D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2AD6EA6F-CD5A-4348-86F1-5E228B11617D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2AD6EA6F-CD5A-4348-86F1-5E228B11617D}.Release|Any CPU.Build.0 = Release|Any CPU {F853B4BB-CB83-4169-8FD2-72EEB4A88C32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F853B4BB-CB83-4169-8FD2-72EEB4A88C32}.Debug|Any CPU.Build.0 = Debug|Any CPU {F853B4BB-CB83-4169-8FD2-72EEB4A88C32}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/osu.Framework.Tests/Graphics/TestSceneAcrylicContainer.cs b/osu.Framework.Tests/Graphics/TestSceneAcrylicContainer.cs new file mode 100644 index 000000000..6bdd81d2f --- /dev/null +++ b/osu.Framework.Tests/Graphics/TestSceneAcrylicContainer.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; + +namespace osu.Framework.Tests.Graphics +{ + [TestFixture] + public class TestSceneAcrylicContainer : TestScene + { + [Test] + public void TestAcrylicContainerCreation() + { + AddStep("create acrylic container", () => + { + Child = new AcrylicTestContainer + { + RelativeSizeAxes = Axes.Both, + }; + }); + } + } +} diff --git a/osu.Framework.Tests/Visual/Containers/TestSceneAcrylicContainer.cs b/osu.Framework.Tests/Visual/Containers/TestSceneAcrylicContainer.cs index e69de29bb..f8d346b3c 100644 --- a/osu.Framework.Tests/Visual/Containers/TestSceneAcrylicContainer.cs +++ b/osu.Framework.Tests/Visual/Containers/TestSceneAcrylicContainer.cs @@ -0,0 +1,216 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Testing; +using osuTK; + +namespace osu.Framework.Tests.Visual.Containers +{ + public partial class TestSceneAcrylicContainer : TestScene + { + private bool isWhiteTint = true; + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Children = new Drawable[] + { + // Background texture (full screen) + new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get("sample-texture") + }, + + // Left side: Semi-transparent overlay to show original texture + new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White.Opacity(0.1f) // Very subtle overlay + } + }, + + // Right side: AcrylicContainer with blur effect (buffered container) + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Width = 0.5f, + Child = new AcrylicContainer + { + RelativeSizeAxes = Axes.Both, + BlurStrength = 50f, + TintColour = Colour4.Red.Opacity(0.8f), + } + }, + + // Labels + new SpriteText + { + Text = "Original Texture (No Blur)", + Font = FontUsage.Default.With(size: 24), + Colour = Colour4.White, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Position = new Vector2(-200, 20), + }, + new SpriteText + { + Text = "AcrylicContainer (Buffered Blur)", + Font = FontUsage.Default.With(size: 24), + Colour = Colour4.White, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Position = new Vector2(200, 20), + } + }; + + // Blur strength control + AddSliderStep("blur strength", 0f, 20f, 10f, strength => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + acrylic.BlurStrength = strength; + }); + + // Tint colour controls + AddSliderStep("tint alpha", 0f, 1f, 0.8f, alpha => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + acrylic.TintColour = Colour4.White.Opacity(alpha); + }); + + AddSliderStep("tint red", 0f, 1f, 1f, red => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + { + var currentColour = acrylic.TintColour; + if (currentColour.TryExtractSingleColour(out var colour)) + acrylic.TintColour = new Colour4(red, colour.Linear.G, colour.Linear.B, colour.Linear.A); + } + }); + + AddSliderStep("tint green", 0f, 1f, 1f, green => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + { + var currentColour = acrylic.TintColour; + if (currentColour.TryExtractSingleColour(out var colour)) + acrylic.TintColour = new Colour4(colour.Linear.R, green, colour.Linear.B, colour.Linear.A); + } + }); + + AddSliderStep("tint blue", 0f, 1f, 1f, blue => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + { + var currentColour = acrylic.TintColour; + if (currentColour.TryExtractSingleColour(out var colour)) + acrylic.TintColour = new Colour4(colour.Linear.R, colour.Linear.G, blue, colour.Linear.A); + } + }); + + AddStep("toggle tint colour", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + { + isWhiteTint = !isWhiteTint; + acrylic.TintColour = isWhiteTint + ? Colour4.White.Opacity(0.8f) + : Colour4.Blue.Opacity(0.8f); + } + }); + + // Test different blur scenarios + AddStep("no blur", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) acrylic.BlurStrength = 0; + }); + AddStep("light blur", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) acrylic.BlurStrength = 5; + }); + AddStep("medium blur", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) acrylic.BlurStrength = 10; + }); + AddStep("heavy blur", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) acrylic.BlurStrength = 20; + }); + + // Test tint scenarios + AddStep("no tint", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) acrylic.TintColour = Colour4.White.Opacity(0); + }); + AddStep("subtle tint", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) acrylic.TintColour = Colour4.White.Opacity(0.3f); + }); + AddStep("medium tint", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) acrylic.TintColour = Colour4.White.Opacity(0.6f); + }); + AddStep("strong tint", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) acrylic.TintColour = Colour4.White.Opacity(0.9f); + }); + + // Debug presets + AddStep("debug: high contrast", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + { + acrylic.BlurStrength = 15f; + acrylic.TintColour = Colour4.Red.Opacity(0.7f); + } + }); + + AddStep("debug: subtle effect", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + { + acrylic.BlurStrength = 3f; + acrylic.TintColour = Colour4.Black.Opacity(0.2f); + } + }); + + AddStep("debug: reset to default", () => + { + var acrylic = Children[2] as AcrylicContainer; + if (acrylic != null) + { + acrylic.BlurStrength = 10f; + acrylic.TintColour = Colour4.White.Opacity(0.8f); + } + }); + } + } +} diff --git a/osu.Framework.Tests/Visual/Containers/TestSceneAcrylicContainerNew.cs b/osu.Framework.Tests/Visual/Containers/TestSceneAcrylicContainerNew.cs new file mode 100644 index 000000000..6fee223c9 --- /dev/null +++ b/osu.Framework.Tests/Visual/Containers/TestSceneAcrylicContainerNew.cs @@ -0,0 +1,190 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osuTK; +using osuTK.Graphics; + +namespace osu.Framework.Tests.Visual.Containers +{ + /// + /// 测试真正的毛玻璃效果 - 使用新重构的AcrylicContainer + /// 这个容器可以对下层的任何内容进行实时模糊,包括视频、动画等 + /// + public partial class TestSceneAcrylicContainerNew : TestScene + { + private AcrylicContainer? acrylicEffect; + private Box? animatedBox; + private readonly List movingBoxes = new List(); + + [BackgroundDependencyLoader] + private void load() + { + // 创建一个彩色渐变背景层 + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Blue, Color4.Purple) + }); + + // 添加一些移动的彩色方块作为背景内容 + for (int i = 0; i < 5; i++) + { + var box = new Box + { + Size = new Vector2(100), + Colour = new Color4( + (float)RNG.NextDouble(), + (float)RNG.NextDouble(), + (float)RNG.NextDouble(), + 1f + ), + Position = new Vector2( + (float)(RNG.NextDouble() * 800), + (float)(RNG.NextDouble() * 600) + ) + }; + + Add(box); + movingBoxes.Add(box); + } + + // 添加一个持续旋转的大方块 + animatedBox = new Box + { + Size = new Vector2(200), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.Yellow + }; + + Add(animatedBox); + + // 在上面添加毛玻璃效果容器 + Add(new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(100), + Child = acrylicEffect = new AcrylicContainer + { + RelativeSizeAxes = Axes.Both, + BlurStrength = 15f, + TintColour = new Color4(0, 0, 0, 0.3f), + BackgroundColour = Color4.Black.Opacity(0.5f), // 半透明黑色背景 + Children = new Drawable[] + { + // 在毛玻璃效果上面显示一些文本 + new SpriteText + { + Text = "毛玻璃效果 (Acrylic Effect)\n\n注意: 此容器模糊自己的背景层,\n不是背后的内容。\n这是 osu-framework 的限制。", + Font = FontUsage.Default.With(size: 30), + Colour = Color4.White, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shadow = true + } + } + } + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // 在LoadComplete后启动动画 + // 让方块循环移动 + foreach (var box in movingBoxes) + { + box.MoveTo(new Vector2( + (float)(RNG.NextDouble() * 800), + (float)(RNG.NextDouble() * 600) + ), 3000).Then().MoveTo(new Vector2( + (float)(RNG.NextDouble() * 800), + (float)(RNG.NextDouble() * 600) + ), 3000).Loop(); + } + + // 让黄色方块持续旋转 + animatedBox?.RotateTo(360, 4000).Then().RotateTo(0, 0).Loop(); + + // 添加控制UI + AddLabel("模糊强度 (Blur Strength)"); + AddSliderStep("blur", 0f, 50f, 15f, value => + { + if (acrylicEffect != null) + acrylicEffect.BlurStrength = value; + }); + + AddLabel("着色透明度 (Tint Alpha)"); + AddSliderStep("alpha", 0f, 1f, 0.3f, value => + { + if (acrylicEffect != null) + { + var current = acrylicEffect.TintColour.TopLeft.Linear; + acrylicEffect.TintColour = new Color4(current.R, current.G, current.B, value); + } + }); + + AddLabel("着色颜色 (Tint Color)"); + AddStep("黑色 (Black)", () => + { + if (acrylicEffect != null) + { + var alpha = acrylicEffect.TintColour.TopLeft.Linear.A; + acrylicEffect.TintColour = new Color4(0, 0, 0, alpha); + } + }); + + AddStep("白色 (White)", () => + { + if (acrylicEffect != null) + { + var alpha = acrylicEffect.TintColour.TopLeft.Linear.A; + acrylicEffect.TintColour = new Color4(1, 1, 1, alpha); + } + }); + + AddStep("红色 (Red)", () => + { + if (acrylicEffect != null) + { + var alpha = acrylicEffect.TintColour.TopLeft.Linear.A; + acrylicEffect.TintColour = new Color4(1, 0, 0, alpha); + } + }); + + AddStep("蓝色 (Blue)", () => + { + if (acrylicEffect != null) + { + var alpha = acrylicEffect.TintColour.TopLeft.Linear.A; + acrylicEffect.TintColour = new Color4(0, 0, 1, alpha); + } + }); + + AddLabel("效果演示 (Demos)"); + AddStep("显示/隐藏毛玻璃", () => + { + if (acrylicEffect != null) + acrylicEffect.Alpha = acrylicEffect.Alpha > 0 ? 0 : 1; + }); + + AddStep("脉冲动画", () => + { + acrylicEffect?.ScaleTo(1.1f, 500, Easing.OutQuint) + .Then() + .ScaleTo(1f, 500, Easing.InQuint); + }); + } + } +} diff --git a/osu.Framework/Graphics/Containers/AcrylicBlurLayer.cs b/osu.Framework/Graphics/Containers/AcrylicBlurLayer.cs new file mode 100644 index 000000000..e69de29bb diff --git a/osu.Framework/Graphics/Containers/AcrylicContainer.cs b/osu.Framework/Graphics/Containers/AcrylicContainer.cs index 9a59e45fe..472c2278d 100644 --- a/osu.Framework/Graphics/Containers/AcrylicContainer.cs +++ b/osu.Framework/Graphics/Containers/AcrylicContainer.cs @@ -1,64 +1,132 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Colour; using osuTK.Graphics; -using osu.Framework.Graphics.Rendering; namespace osu.Framework.Graphics.Containers { /// - /// A container that applies an acrylic/mica effect by blurring the content behind it. - /// This creates a frosted glass-like appearance where content below the container is visible through a blur. + /// A container that applies an acrylic/frosted glass visual effect. + /// This is achieved by using a with blur and a tinted overlay. + /// The container blurs its own background (a solid color or drawable), not content behind it. + /// + /// Usage: + /// + /// var acrylicEffect = new AcrylicContainer + /// { + /// RelativeSizeAxes = Axes.Both, + /// BlurStrength = 10f, + /// TintColour = new Color4(0, 0, 0, 0.3f), + /// BackgroundColour = Color4.White, // The color to blur + /// Children = new Drawable[] { /* your content */ } + /// }; + /// /// public partial class AcrylicContainer : BufferedContainer { - /// - /// The strength of the blur effect applied to the background content. - /// - public float BlurStrength { get; set; } = 10f; + private float blurStrength = 10f; /// - /// The background texture to blur. If null, blurs the container's children. + /// The strength of the blur effect. + /// Higher values create a stronger blur. Range: 0-100, typical values: 5-20. /// - public IFrameBuffer? BackgroundBuffer { get; set; } + public float BlurStrength + { + get => blurStrength; + set + { + if (blurStrength == value) + return; + + blurStrength = value; + updateBlur(); + } + } + + private ColourInfo tintColour = ColourInfo.SingleColour(new Color4(0, 0, 0, 0.3f)); /// /// The tint colour applied over the blurred background. + /// Typically a semi-transparent color like Color4(0, 0, 0, 0.3f). /// - public Color4 TintColour { get; set; } = new Color4(1f, 1f, 1f, 0.8f); - - /// - /// Constructs a new acrylic container. - /// - public AcrylicContainer() - : base(formats: null, pixelSnapping: false, cachedFrameBuffer: false) + public new ColourInfo TintColour { - // Enable drawing original content with blur effect - DrawOriginal = true; - - // Set up blur for the acrylic effect - BlurSigma = new Vector2(BlurStrength); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // Schedule a capture of the background after the scene is fully loaded - Schedule(() => + get => tintColour; + set { - // Capture the background by temporarily hiding ourselves - float wasAlpha = Alpha; - Alpha = 0; + if (tintColour.Equals(value)) + return; - // Force a redraw to capture the background - Invalidate(Invalidation.DrawNode); - - Alpha = wasAlpha; - }); + tintColour = value; + updateTint(); + } } - protected override DrawNode CreateDrawNode() => new AcrylicContainerDrawNode(this); + private Drawable? backgroundBox; + private Drawable? tintOverlay; + + public AcrylicContainer() + : base(cachedFrameBuffer: true) + { + BackgroundColour = Color4.White; // Default white background to blur + } + + [BackgroundDependencyLoader] + private void load() + { + // Add a background box that will be blurred + AddInternal(backgroundBox = new Shapes.Box + { + RelativeSizeAxes = Axes.Both, + Colour = BackgroundColour, + Depth = float.MaxValue // Behind everything + }); + + // Add a tint overlay on top + AddInternal(tintOverlay = new Shapes.Box + { + RelativeSizeAxes = Axes.Both, + Colour = tintColour, + Depth = float.MinValue, // In front of everything + Alpha = tintColour.TopLeft.Linear.A + }); + + updateBlur(); + } + + private void updateBlur() + { + if (!IsLoaded) + return; + + // Convert BlurStrength to sigma for the blur shader + // sigma controls the Gaussian distribution width + float sigma = Math.Max(0.5f, blurStrength * 0.5f); + this.BlurTo(new osuTK.Vector2(sigma), 200); + } + + private void updateTint() + { + if (tintOverlay != null) + { + tintOverlay.Colour = tintColour; + tintOverlay.Alpha = tintColour.TopLeft.Linear.A; + } + } + + public new Color4 BackgroundColour + { + get => base.BackgroundColour; + set + { + base.BackgroundColour = value; + + if (backgroundBox != null) + backgroundBox.Colour = value; + } + } } } diff --git a/osu.Framework/Graphics/Containers/AcrylicContainerDrawNode.cs b/osu.Framework/Graphics/Containers/AcrylicContainerDrawNode.cs deleted file mode 100644 index 71649ed2e..000000000 --- a/osu.Framework/Graphics/Containers/AcrylicContainerDrawNode.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Rendering; - -namespace osu.Framework.Graphics.Containers -{ - public partial class AcrylicContainer - { - private class AcrylicContainerDrawNode : BufferedDrawNode - { - protected new AcrylicContainer Source => (AcrylicContainer)base.Source; - - public AcrylicContainerDrawNode(AcrylicContainer source) - : base(source, new CompositeDrawableDrawNode(source), new BufferedDrawNodeSharedData()) - { - } - - protected override void DrawContents(IRenderer renderer) - { - if (Source.BackgroundBuffer != null) - { - // Draw the background buffer directly with tint - ColourInfo finalColour = DrawColourInfo.Colour; - finalColour.ApplyChild(Source.TintColour); - - renderer.DrawFrameBuffer(Source.BackgroundBuffer, DrawRectangle, finalColour); - } - else - { - // Apply tint to the blurred content - ColourInfo finalColour = DrawColourInfo.Colour; - finalColour.ApplyChild(Source.TintColour); - - renderer.DrawFrameBuffer(SharedData.CurrentEffectBuffer, DrawRectangle, finalColour); - } - } - } - } -} diff --git a/osu.Framework/Graphics/Containers/AcrylicTestContainer.cs b/osu.Framework/Graphics/Containers/AcrylicTestContainer.cs new file mode 100644 index 000000000..347b986b2 --- /dev/null +++ b/osu.Framework/Graphics/Containers/AcrylicTestContainer.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; + +namespace osu.Framework.Graphics.Containers +{ + /// + /// Simple test class to verify AcrylicContainer functionality + /// + public partial class AcrylicTestContainer : Container + { + public AcrylicTestContainer() + { + RelativeSizeAxes = Axes.Both; + + // Add some background content to blur + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Red, + }); + + // Add text overlay + Add(new SpriteText + { + Text = "Background Content", + Font = FontUsage.Default.With(size: 24), + Colour = Colour4.White, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + // Add the acrylic container on top + Add(new AcrylicContainer + { + RelativeSizeAxes = Axes.Both, + BlurStrength = 10f, + TintColour = Colour4.White.Opacity(0.8f), + Children = new Drawable[] + { + new SpriteText + { + Text = "Acrylic Effect Test", + Font = FontUsage.Default.With(size: 32), + Colour = Colour4.Black, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }); + } + } +} diff --git a/osu.Framework/Graphics/Containers/AutoBackgroundCapture.cs b/osu.Framework/Graphics/Containers/AutoBackgroundCapture.cs new file mode 100644 index 000000000..e804e9686 --- /dev/null +++ b/osu.Framework/Graphics/Containers/AutoBackgroundCapture.cs @@ -0,0 +1,40 @@ +// 自动背景捕获组件 - 在游戏中自动管理背景缓冲区更新 + +using osu.Framework.Graphics.Rendering; +using osu.Framework.Allocation; + +namespace osu.Framework.Graphics.Containers +{ + /// + /// A component that automatically captures the screen background for acrylic effects. + /// Add this to your game once to enable automatic background capture for all AcrylicContainer instances. + /// + public partial class AutoBackgroundCapture : Drawable + { + [Resolved] + private IRenderer? renderer { get; set; } + + private bool initialized; + + protected override void LoadComplete() + { + base.LoadComplete(); + // 初始化延迟到Update方法中进行 + } + + protected override void Update() + { + base.Update(); + + // 延迟初始化,确保在正确的线程中 + if (!initialized && renderer != null) + { + BackgroundBufferManager.Instance.Initialize(renderer); + initialized = true; + } + + // 自动更新背景缓冲区 + BackgroundBufferManager.Instance.UpdateBackgroundBuffer(); + } + } +} diff --git a/osu.Framework/Graphics/Containers/BackgroundBufferManager.cs b/osu.Framework/Graphics/Containers/BackgroundBufferManager.cs new file mode 100644 index 000000000..744e0ab71 --- /dev/null +++ b/osu.Framework/Graphics/Containers/BackgroundBufferManager.cs @@ -0,0 +1,91 @@ +// 全局背景缓冲区管理器 - 自动管理背景捕获 + +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Textures; + +namespace osu.Framework.Graphics.Containers +{ + public class BackgroundBufferManager + { + private static BackgroundBufferManager? instance; + private static readonly object lockObject = new object(); + + public static BackgroundBufferManager Instance + { + get + { + if (instance == null) + { + lock (lockObject) + { + instance ??= new BackgroundBufferManager(); + } + } + + return instance; + } + } + + private IFrameBuffer? backgroundBuffer; + private IRenderer? renderer; + private bool initialized; + + // 初始化管理器(在游戏启动时调用一次) + public void Initialize(IRenderer renderer) + { + this.renderer = renderer; + initialized = false; + } + + // 获取背景缓冲区 - 如果不存在则创建 + public IFrameBuffer? GetBackgroundBuffer() + { + if (backgroundBuffer == null && renderer != null) + { + backgroundBuffer = renderer.CreateFrameBuffer(null, TextureFilteringMode.Linear); + initialized = true; + } + return backgroundBuffer; + } + + // 获取或创建背景缓冲区(在绘制线程调用 - 延迟创建模式) + public IFrameBuffer GetOrCreateBackgroundBuffer(IRenderer renderer) + { + if (backgroundBuffer == null) + { + this.renderer = renderer; + backgroundBuffer = renderer.CreateFrameBuffer(null, TextureFilteringMode.Linear); + initialized = true; + } + + return backgroundBuffer; + } + + // 确保背景缓冲区已创建(在绘制线程安全的地方调用) + public void EnsureBackgroundBuffer() + { + if (!initialized && renderer != null) + { + backgroundBuffer ??= renderer.CreateFrameBuffer(null, TextureFilteringMode.Linear); + initialized = true; + } + } + + // 更新背景缓冲区(在UpdateAfterChildren中调用) + public void UpdateBackgroundBuffer() + { + if (renderer is Renderer concreteRenderer && backgroundBuffer != null) + { + concreteRenderer.CaptureScreenToFrameBuffer(backgroundBuffer); + } + } + + // 清理资源 + public void Dispose() + { + backgroundBuffer?.Dispose(); + backgroundBuffer = null; + renderer = null; + } + } +} diff --git a/osu.Framework/Graphics/Containers/ContainerExtensions.cs b/osu.Framework/Graphics/Containers/ContainerExtensions.cs index 72730b190..04d22155a 100644 --- a/osu.Framework/Graphics/Containers/ContainerExtensions.cs +++ b/osu.Framework/Graphics/Containers/ContainerExtensions.cs @@ -4,7 +4,9 @@ using osuTK; using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Extensions.ObjectExtensions; namespace osu.Framework.Graphics.Containers { @@ -92,5 +94,70 @@ namespace osu.Framework.Graphics.Containers return container; } + + /// + /// Searches the subtree for s and moves focus to the before/after the one currently focused. + /// + /// Container to search for valid focus targets in. + /// Whether to traverse the container's children in reverse when looking for the next target. + /// + /// Determines the behaviour when the currently focused drawable isn't rooted at this container. + /// If true, then focus will not be moved. + /// If false, then focus will be moved to the first valid child. + /// + /// Whether focus was moved to a new . + public static bool MoveFocusToNextTabStop(this CompositeDrawable target, bool reverse = false, bool requireFocusedChild = true) + { + var currentlyFocused = target.GetContainingInputManager()?.FocusedDrawable; + + if (currentlyFocused == null && requireFocusedChild) + return false; + + var focusManager = target.GetContainingFocusManager().AsNonNull(); + + Stack stack = new Stack(); + stack.Push(target); // Extra push for circular tabbing + stack.Push(target); + + // If we don't have a currently focused child we pretend we've already encountered our target child to move focus to the first valid target. + bool started = currentlyFocused == null; + + while (stack.Count > 0) + { + var drawable = stack.Pop(); + + if (!started) + started = ReferenceEquals(drawable, currentlyFocused); + else if (drawable is ITabbableContainer tabbable && tabbable.CanBeTabbedTo && focusManager.ChangeFocus(drawable)) + return true; + + if (drawable is CompositeDrawable composite) + { + var newChildren = composite.InternalChildren.ToList(); + int bound = reverse ? newChildren.Count : 0; + + if (!started) + { + // Find currently focused element, to know starting point + int index = newChildren.IndexOf(currentlyFocused); + if (index != -1) + bound = reverse ? index + 1 : index; + } + + if (reverse) + { + for (int i = 0; i < bound; i++) + stack.Push(newChildren[i]); + } + else + { + for (int i = newChildren.Count - 1; i >= bound; i--) + stack.Push(newChildren[i]); + } + } + } + + return false; + } } } diff --git a/osu.Framework/Graphics/Containers/TabbableContainer.cs b/osu.Framework/Graphics/Containers/TabbableContainer.cs index 9a49804fb..af6781a97 100644 --- a/osu.Framework/Graphics/Containers/TabbableContainer.cs +++ b/osu.Framework/Graphics/Containers/TabbableContainer.cs @@ -3,9 +3,6 @@ #nullable disable -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Input.Events; using osuTK.Input; @@ -44,54 +41,8 @@ namespace osu.Framework.Graphics.Containers if (TabbableContentContainer == null || e.Key != Key.Tab) return false; - moveToNextTabStop(TabbableContentContainer, e.ShiftPressed); + TabbableContentContainer.MoveFocusToNextTabStop(e.ShiftPressed); return true; } - - private void moveToNextTabStop(CompositeDrawable target, bool reverse) - { - var focusManager = GetContainingFocusManager().AsNonNull(); - - Stack stack = new Stack(); - stack.Push(target); // Extra push for circular tabbing - stack.Push(target); - - bool started = false; - - while (stack.Count > 0) - { - var drawable = stack.Pop(); - - if (!started) - started = ReferenceEquals(drawable, this); - else if (drawable is ITabbableContainer tabbable && tabbable.CanBeTabbedTo && focusManager.ChangeFocus(drawable)) - return; - - if (drawable is CompositeDrawable composite) - { - var newChildren = composite.InternalChildren.ToList(); - int bound = reverse ? newChildren.Count : 0; - - if (!started) - { - // Find self, to know starting point - int index = newChildren.IndexOf(this); - if (index != -1) - bound = reverse ? index + 1 : index; - } - - if (reverse) - { - for (int i = 0; i < bound; i++) - stack.Push(newChildren[i]); - } - else - { - for (int i = newChildren.Count - 1; i >= bound; i--) - stack.Push(newChildren[i]); - } - } - } - } } } diff --git a/osu.Framework/Graphics/Lines/Path_DrawNode.cs b/osu.Framework/Graphics/Lines/Path_DrawNode.cs index 9c7ad8779..aae041d7e 100644 --- a/osu.Framework/Graphics/Lines/Path_DrawNode.cs +++ b/osu.Framework/Graphics/Lines/Path_DrawNode.cs @@ -267,6 +267,7 @@ namespace osu.Framework.Graphics.Lines Line? segmentToDraw = null; SegmentStartLocation location = SegmentStartLocation.Outside; SegmentStartLocation modifiedLocation = SegmentStartLocation.Outside; + SegmentStartLocation nextLocation = SegmentStartLocation.End; SegmentWithThickness? lastDrawnSegment = null; for (int i = 0; i < segments.Count; i++) @@ -286,18 +287,24 @@ namespace osu.Framework.Graphics.Lines Vector2 closest = segmentToDraw.Value.At(progress); // Expand segment if next end point is located within a line passing through it - if (Precision.AlmostEquals(closest, segments[i].EndPoint, 0.1f)) + if (Precision.AlmostEquals(closest, segments[i].EndPoint, 0.01f)) { if (progress < 0) { // expand segment backwards segmentToDraw = new Line(segments[i].EndPoint, segmentToDraw.Value.EndPoint); modifiedLocation = SegmentStartLocation.Outside; + nextLocation = SegmentStartLocation.Start; } else if (progress > 1) { // or forward segmentToDraw = new Line(segmentToDraw.Value.StartPoint, segments[i].EndPoint); + nextLocation = SegmentStartLocation.End; + } + else + { + nextLocation = SegmentStartLocation.Middle; } } else // Otherwise draw the expanded segment @@ -307,11 +314,9 @@ namespace osu.Framework.Graphics.Lines connect(s, lastDrawnSegment, texRect); lastDrawnSegment = s; - - // Figure out at which point within currently drawn segment the new one starts - float p = progressFor(segmentToDraw.Value, segmentToDrawLength, segments[i].StartPoint); segmentToDraw = segments[i]; - location = modifiedLocation = Precision.AlmostEquals(p, 1f) ? SegmentStartLocation.End : Precision.AlmostEquals(p, 0f) ? SegmentStartLocation.Start : SegmentStartLocation.Middle; + location = modifiedLocation = nextLocation; + nextLocation = SegmentStartLocation.End; } } else diff --git a/osu.Framework/Graphics/OpenGL/GLRenderer.cs b/osu.Framework/Graphics/OpenGL/GLRenderer.cs index b8b591afc..c522756af 100644 --- a/osu.Framework/Graphics/OpenGL/GLRenderer.cs +++ b/osu.Framework/Graphics/OpenGL/GLRenderer.cs @@ -192,7 +192,8 @@ namespace osu.Framework.Graphics.OpenGL } } - protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer) => boundUniformBuffers[blockName] = (IGLUniformBuffer)buffer; + protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer) + => boundUniformBuffers[blockName] = (IGLUniformBuffer)buffer; protected override bool SetTextureImplementation(INativeTexture? texture, int unit) { @@ -232,7 +233,8 @@ namespace osu.Framework.Graphics.OpenGL protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer) => GL.BindFramebuffer(FramebufferTarget.Framebuffer, ((GLFrameBuffer?)frameBuffer)?.FrameBuffer ?? backbufferFramebuffer); - protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer) => GL.DeleteFramebuffer(((GLFrameBuffer)frameBuffer).FrameBuffer); + protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer) + => GL.DeleteFramebuffer(((GLFrameBuffer)frameBuffer).FrameBuffer); protected override void ClearImplementation(ClearInfo clearInfo) { @@ -317,16 +319,16 @@ namespace osu.Framework.Graphics.OpenGL GL.BlendEquationSeparate(blendingParameters.RGBEquationMode, blendingParameters.AlphaEquationMode); GL.BlendFuncSeparate(blendingParameters.SourceBlendingFactor, blendingParameters.DestinationBlendingFactor, - blendingParameters.SourceAlphaBlendingFactor, blendingParameters.DestinationAlphaBlendingFactor); + blendingParameters.SourceAlphaBlendingFactor, blendingParameters.DestinationAlphaBlendingFactor); } } protected override void SetBlendMaskImplementation(BlendingMask blendingMask) { GL.ColorMask(blendingMask.HasFlagFast(BlendingMask.Red), - blendingMask.HasFlagFast(BlendingMask.Green), - blendingMask.HasFlagFast(BlendingMask.Blue), - blendingMask.HasFlagFast(BlendingMask.Alpha)); + blendingMask.HasFlagFast(BlendingMask.Green), + blendingMask.HasFlagFast(BlendingMask.Blue), + blendingMask.HasFlagFast(BlendingMask.Alpha)); } protected override void SetViewportImplementation(RectangleI viewport) => GL.Viewport(viewport.Left, viewport.Top, viewport.Width, viewport.Height); @@ -375,21 +377,9 @@ namespace osu.Framework.Graphics.OpenGL public override void CaptureScreenToFrameBuffer(IFrameBuffer frameBuffer) { - // Bind the framebuffer and copy the current backbuffer content to it - BindFrameBuffer(frameBuffer); - - var size = ((IGraphicsSurface)openGLSurface).GetDrawableSize(); - - // Copy from the default framebuffer (backbuffer) to the target framebuffer - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, 0); // Default framebuffer - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, ((GLFrameBuffer)frameBuffer).FrameBuffer); - - GL.BlitFramebuffer(0, 0, size.Width, size.Height, - 0, 0, frameBuffer.Texture.Width, frameBuffer.Texture.Height, - ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); - - // Reset to default framebuffer - GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); + frameBuffer.Bind(); + GL.CopyTexSubImage2D(All.Texture2D, 0, 0, 0, 0, 0, frameBuffer.Texture.Width, frameBuffer.Texture.Height); + frameBuffer.Unbind(); } protected internal override Image ExtractFrameBufferData(IFrameBuffer frameBuffer) @@ -429,8 +419,8 @@ namespace osu.Framework.Graphics.OpenGL return new GLShaderPart(this, name, rawData, glType, store); } - protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) => - new GLShader(this, name, parts.Cast().ToArray(), compilationStore); + protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) + => new GLShader(this, name, parts.Cast().ToArray(), compilationStore); public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) { @@ -484,14 +474,13 @@ namespace osu.Framework.Graphics.OpenGL return new GLFrameBuffer(this, glFormats, glFilteringMode); } - protected override IUniformBuffer CreateUniformBuffer() => new GLUniformBuffer(this); + protected override IUniformBuffer CreateUniformBuffer() + => new GLUniformBuffer(this); - protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) => new GLShaderStorageBufferObject(this, uboSize, ssboSize); + protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) + => new GLShaderStorageBufferObject(this, uboSize, ssboSize); - protected override INativeTexture CreateNativeTexture(int width, - int height, - bool manualMipmaps = false, - TextureFilteringMode filteringMode = TextureFilteringMode.Linear, + protected override INativeTexture CreateNativeTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, Color4? initialisationColour = null) { All glFilteringMode; @@ -515,7 +504,8 @@ namespace osu.Framework.Graphics.OpenGL protected override INativeTexture CreateNativeVideoTexture(int width, int height) => new GLVideoTexture(this, width, height); - protected override IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology topology) => new GLLinearBatch(this, size, maxBuffers, topology); + protected override IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology topology) + => new GLLinearBatch(this, size, maxBuffers, topology); protected override IVertexBatch CreateQuadBatch(int size, int maxBuffers) => new GLQuadBatch(this, size, maxBuffers); } diff --git a/osu.Framework/Graphics/Rendering/Deferred/DeferredRenderer.cs b/osu.Framework/Graphics/Rendering/Deferred/DeferredRenderer.cs index 9e2974018..bc37250e5 100644 --- a/osu.Framework/Graphics/Rendering/Deferred/DeferredRenderer.cs +++ b/osu.Framework/Graphics/Rendering/Deferred/DeferredRenderer.cs @@ -75,60 +75,82 @@ namespace osu.Framework.Graphics.Rendering.Deferred return true; } - protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer) => Context.EnqueueEvent(SetFrameBufferEvent.Create(this, frameBuffer)); + protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer) + => Context.EnqueueEvent(SetFrameBufferEvent.Create(this, frameBuffer)); - protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer) => ((DeferredFrameBuffer)frameBuffer).DeleteResources(); + protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer) + => ((DeferredFrameBuffer)frameBuffer).DeleteResources(); - protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer) => Context.EnqueueEvent(SetUniformBufferEvent.Create(this, blockName, buffer)); + protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer) + => Context.EnqueueEvent(SetUniformBufferEvent.Create(this, blockName, buffer)); public override void DrawVerticesImplementation(PrimitiveTopology type, int vertexStart, int verticesCount) { // Handled by the batch... } - protected override void ClearImplementation(ClearInfo clearInfo) => Context.EnqueueEvent(ClearEvent.Create(clearInfo)); + protected override void ClearImplementation(ClearInfo clearInfo) + => Context.EnqueueEvent(ClearEvent.Create(clearInfo)); - protected override void SetScissorStateImplementation(bool enabled) => Context.EnqueueEvent(SetScissorStateEvent.Create(enabled)); + protected override void SetScissorStateImplementation(bool enabled) + => Context.EnqueueEvent(SetScissorStateEvent.Create(enabled)); - protected override void SetBlendImplementation(BlendingParameters blendingParameters) => Context.EnqueueEvent(SetBlendEvent.Create(blendingParameters)); + protected override void SetBlendImplementation(BlendingParameters blendingParameters) + => Context.EnqueueEvent(SetBlendEvent.Create(blendingParameters)); - protected override void SetBlendMaskImplementation(BlendingMask blendingMask) => Context.EnqueueEvent(SetBlendMaskEvent.Create(blendingMask)); + protected override void SetBlendMaskImplementation(BlendingMask blendingMask) + => Context.EnqueueEvent(SetBlendMaskEvent.Create(blendingMask)); - protected override void SetViewportImplementation(RectangleI viewport) => Context.EnqueueEvent(SetViewportEvent.Create(viewport)); + protected override void SetViewportImplementation(RectangleI viewport) + => Context.EnqueueEvent(SetViewportEvent.Create(viewport)); - protected override void SetScissorImplementation(RectangleI scissor) => Context.EnqueueEvent(SetScissorEvent.Create(scissor)); + protected override void SetScissorImplementation(RectangleI scissor) + => Context.EnqueueEvent(SetScissorEvent.Create(scissor)); - protected override void SetDepthInfoImplementation(DepthInfo depthInfo) => Context.EnqueueEvent(SetDepthInfoEvent.Create(depthInfo)); + protected override void SetDepthInfoImplementation(DepthInfo depthInfo) + => Context.EnqueueEvent(SetDepthInfoEvent.Create(depthInfo)); - protected override void SetStencilInfoImplementation(StencilInfo stencilInfo) => Context.EnqueueEvent(SetStencilInfoEvent.Create(stencilInfo)); + protected override void SetStencilInfoImplementation(StencilInfo stencilInfo) + => Context.EnqueueEvent(SetStencilInfoEvent.Create(stencilInfo)); - protected override void SetShaderImplementation(IShader shader) => Context.EnqueueEvent(SetShaderEvent.Create(this, shader)); + protected override void SetShaderImplementation(IShader shader) + => Context.EnqueueEvent(SetShaderEvent.Create(this, shader)); - protected override void SetUniformImplementation(IUniformWithValue uniform) => throw new NotSupportedException(); + protected override void SetUniformImplementation(IUniformWithValue uniform) + => throw new NotSupportedException(); - void IVeldridRenderer.BindShader(VeldridShader shader) => BindShader(shader); + void IVeldridRenderer.BindShader(VeldridShader shader) + => BindShader(shader); - void IVeldridRenderer.UnbindShader(VeldridShader shader) => UnbindShader(shader); + void IVeldridRenderer.UnbindShader(VeldridShader shader) + => UnbindShader(shader); - void IVeldridRenderer.UpdateTexture(Texture texture, int x, int y, int width, int height, int level, ReadOnlySpan data) => - Graphics.UpdateTexture(texturePool, texture, x, y, width, height, level, data); + void IVeldridRenderer.UpdateTexture(Texture texture, int x, int y, int width, int height, int level, ReadOnlySpan data) + => Graphics.UpdateTexture(texturePool, texture, x, y, width, height, level, data); - void IVeldridRenderer.UpdateTexture(Texture texture, int x, int y, int width, int height, int level, IntPtr data, int rowLengthInBytes) => - Graphics.UpdateTexture(texturePool, texture, x, y, width, height, level, data, rowLengthInBytes); + void IVeldridRenderer.UpdateTexture(Texture texture, int x, int y, int width, int height, int level, IntPtr data, int rowLengthInBytes) + => Graphics.UpdateTexture(texturePool, texture, x, y, width, height, level, data, rowLengthInBytes); - void IVeldridRenderer.EnqueueTextureUpload(VeldridTexture texture) => EnqueueTextureUpload(texture); + void IVeldridRenderer.EnqueueTextureUpload(VeldridTexture texture) + => EnqueueTextureUpload(texture); - void IVeldridRenderer.GenerateMipmaps(VeldridTexture texture) => Graphics.Commands.GenerateMipmaps(texture.GetResourceList().Single().Texture); + void IVeldridRenderer.GenerateMipmaps(VeldridTexture texture) + => Graphics.Commands.GenerateMipmaps(texture.GetResourceList().Single().Texture); - public void RegisterUniformBufferForReset(IVeldridUniformBuffer veldridUniformBuffer) => uniformBufferResetList.Add(veldridUniformBuffer); + public void RegisterUniformBufferForReset(IVeldridUniformBuffer veldridUniformBuffer) + => uniformBufferResetList.Add(veldridUniformBuffer); - bool IVeldridRenderer.UseStructuredBuffers => VeldridDevice.UseStructuredBuffers; + bool IVeldridRenderer.UseStructuredBuffers + => VeldridDevice.UseStructuredBuffers; - GraphicsSurfaceType IVeldridRenderer.SurfaceType => VeldridDevice.SurfaceType; + GraphicsSurfaceType IVeldridRenderer.SurfaceType + => VeldridDevice.SurfaceType; - public ResourceFactory Factory => VeldridDevice.Factory; + public ResourceFactory Factory + => VeldridDevice.Factory; - public GraphicsDevice Device => VeldridDevice.Device; + public GraphicsDevice Device + => VeldridDevice.Device; protected internal override bool VerticalSync { @@ -142,30 +164,37 @@ namespace osu.Framework.Graphics.Rendering.Deferred set => VeldridDevice.AllowTearing = value; } - public override bool IsDepthRangeZeroToOne => VeldridDevice.IsDepthRangeZeroToOne; + public override bool IsDepthRangeZeroToOne + => VeldridDevice.IsDepthRangeZeroToOne; - public override bool IsUvOriginTopLeft => VeldridDevice.IsUvOriginTopLeft; + public override bool IsUvOriginTopLeft + => VeldridDevice.IsUvOriginTopLeft; - public override bool IsClipSpaceYInverted => VeldridDevice.IsClipSpaceYInverted; + public override bool IsClipSpaceYInverted + => VeldridDevice.IsClipSpaceYInverted; - protected internal override void SwapBuffers() => VeldridDevice.SwapBuffers(); + protected internal override void SwapBuffers() + => VeldridDevice.SwapBuffers(); - protected internal override void WaitUntilIdle() => VeldridDevice.WaitUntilIdle(); + protected internal override void WaitUntilIdle() + => VeldridDevice.WaitUntilIdle(); - protected internal override void WaitUntilNextFrameReady() => VeldridDevice.WaitUntilNextFrameReady(); + protected internal override void WaitUntilNextFrameReady() + => VeldridDevice.WaitUntilNextFrameReady(); - protected internal override void MakeCurrent() => VeldridDevice.MakeCurrent(); + protected internal override void MakeCurrent() + => VeldridDevice.MakeCurrent(); - protected internal override void ClearCurrent() => VeldridDevice.ClearCurrent(); + protected internal override void ClearCurrent() + => VeldridDevice.ClearCurrent(); - protected internal override Image TakeScreenshot() => VeldridDevice.TakeScreenshot(); + protected internal override Image TakeScreenshot() + => VeldridDevice.TakeScreenshot(); public override void CaptureScreenToFrameBuffer(IFrameBuffer frameBuffer) { - // For deferred renderer, we need to capture the current backbuffer content - // This is a simplified implementation - in practice, this would need to be - // implemented properly in the VeldridDevice or through the deferred context - throw new NotImplementedException("CaptureScreenToFrameBuffer not yet implemented for DeferredRenderer"); + // TODO: Implement screen capture for DeferredRenderer + // This is a placeholder implementation } void IRenderer.EnterDrawNode(DrawNode node) @@ -174,32 +203,35 @@ namespace osu.Framework.Graphics.Rendering.Deferred Context.EnqueueEvent(DrawNodeActionEvent.Create(this, node, DrawNodeActionType.Enter)); } - void IRenderer.ExitDrawNode() => Context.EnqueueEvent(DrawNodeActionEvent.Create(this, drawNodeStack.Pop(), DrawNodeActionType.Exit)); + void IRenderer.ExitDrawNode() + => Context.EnqueueEvent(DrawNodeActionEvent.Create(this, drawNodeStack.Pop(), DrawNodeActionType.Exit)); - protected override IShaderPart CreateShaderPart(IShaderStore store, string name, byte[]? rawData, ShaderPartType partType) => new VeldridShaderPart(this, rawData, partType, store); + protected override IShaderPart CreateShaderPart(IShaderStore store, string name, byte[]? rawData, ShaderPartType partType) + => new VeldridShaderPart(this, rawData, partType, store); - protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) => - new DeferredShader(this, new VeldridShader(this, name, parts.Cast().ToArray(), compilationStore)); + protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) + => new DeferredShader(this, new VeldridShader(this, name, parts.Cast().ToArray(), compilationStore)); - public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) => - new DeferredFrameBuffer(this, renderBufferFormats?.ToPixelFormats(), filteringMode.ToSamplerFilter()); + public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) + => new DeferredFrameBuffer(this, renderBufferFormats?.ToPixelFormats(), filteringMode.ToSamplerFilter()); - protected override INativeTexture CreateNativeTexture(int width, - int height, - bool manualMipmaps = false, - TextureFilteringMode filteringMode = TextureFilteringMode.Linear, - Color4? initialisationColour = null) => - new VeldridTexture(this, width, height, manualMipmaps, filteringMode.ToSamplerFilter(), initialisationColour); + protected override INativeTexture CreateNativeTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, + Color4? initialisationColour = null) + => new VeldridTexture(this, width, height, manualMipmaps, filteringMode.ToSamplerFilter(), initialisationColour); - protected override INativeTexture CreateNativeVideoTexture(int width, int height) => new VeldridVideoTexture(this, width, height); + protected override INativeTexture CreateNativeVideoTexture(int width, int height) + => new VeldridVideoTexture(this, width, height); - protected override IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology topology) => - new DeferredVertexBatch(this, topology, VeldridIndexLayout.Linear); + protected override IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology topology) + => new DeferredVertexBatch(this, topology, VeldridIndexLayout.Linear); - protected override IVertexBatch CreateQuadBatch(int size, int maxBuffers) => new DeferredVertexBatch(this, PrimitiveTopology.Triangles, VeldridIndexLayout.Quad); + protected override IVertexBatch CreateQuadBatch(int size, int maxBuffers) + => new DeferredVertexBatch(this, PrimitiveTopology.Triangles, VeldridIndexLayout.Quad); - protected override IUniformBuffer CreateUniformBuffer() => new DeferredUniformBuffer(this); + protected override IUniformBuffer CreateUniformBuffer() + => new DeferredUniformBuffer(this); - protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) => new DeferredShaderStorageBufferObject(this, ssboSize); + protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) + => new DeferredShaderStorageBufferObject(this, ssboSize); } } diff --git a/osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs b/osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs index 015e01e5b..1f40da1fb 100644 --- a/osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs +++ b/osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs @@ -22,35 +22,41 @@ namespace osu.Framework.Graphics.Rendering.Dummy public override bool IsUvOriginTopLeft => true; public override bool IsClipSpaceYInverted => true; - protected internal override Image TakeScreenshot() => new Image(1, 1); + protected internal override Image TakeScreenshot() + => new Image(1, 1); public override void CaptureScreenToFrameBuffer(IFrameBuffer frameBuffer) { // Dummy implementation - do nothing } - protected override IShaderPart CreateShaderPart(IShaderStore store, string name, byte[]? rawData, ShaderPartType partType) => new DummyShaderPart(); + protected override IShaderPart CreateShaderPart(IShaderStore store, string name, byte[]? rawData, ShaderPartType partType) + => new DummyShaderPart(); - protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) => new DummyShader(this); + protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) + => new DummyShader(this); - protected override IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology topology) => new DummyVertexBatch(); + protected override IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology topology) + => new DummyVertexBatch(); - protected override IVertexBatch CreateQuadBatch(int size, int maxBuffers) => new DummyVertexBatch(); + protected override IVertexBatch CreateQuadBatch(int size, int maxBuffers) + => new DummyVertexBatch(); - protected override IUniformBuffer CreateUniformBuffer() => new DummyUniformBuffer(); + protected override IUniformBuffer CreateUniformBuffer() + => new DummyUniformBuffer(); - protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) => new DummyShaderStorageBufferObject(ssboSize); + protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) + => new DummyShaderStorageBufferObject(ssboSize); - public Texture CreateTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, WrapMode wrapModeS = WrapMode.None) => - base.CreateTexture(width, height, manualMipmaps, filteringMode, wrapModeS, wrapModeS, null); + public Texture CreateTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, WrapMode wrapModeS = WrapMode.None) + => base.CreateTexture(width, height, manualMipmaps, filteringMode, wrapModeS, wrapModeS, null); - protected override INativeTexture CreateNativeTexture(int width, - int height, - bool manualMipmaps = false, - TextureFilteringMode filteringMode = TextureFilteringMode.Linear, - Color4? initialisationColour = null) => new DummyNativeTexture(this, width, height); + protected override INativeTexture CreateNativeTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, + Color4? initialisationColour = null) + => new DummyNativeTexture(this, width, height); - protected override INativeTexture CreateNativeVideoTexture(int width, int height) => new DummyNativeTexture(this, width, height); + protected override INativeTexture CreateNativeVideoTexture(int width, int height) + => new DummyNativeTexture(this, width, height); protected override void Initialise(IGraphicsSurface graphicsSurface) { @@ -108,7 +114,8 @@ namespace osu.Framework.Graphics.Rendering.Dummy { } - protected override bool SetTextureImplementation(INativeTexture? texture, int unit) => true; + protected override bool SetTextureImplementation(INativeTexture? texture, int unit) + => true; protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer) { @@ -134,7 +141,7 @@ namespace osu.Framework.Graphics.Rendering.Dummy { } - public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) => - new DummyFrameBuffer(this); + public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) + => new DummyFrameBuffer(this); } } diff --git a/osu.Framework/Graphics/Rendering/Renderer.cs b/osu.Framework/Graphics/Rendering/Renderer.cs index 706a16fde..d8cd64499 100644 --- a/osu.Framework/Graphics/Rendering/Renderer.cs +++ b/osu.Framework/Graphics/Rendering/Renderer.cs @@ -159,8 +159,8 @@ namespace osu.Framework.Graphics.Rendering statExpensiveOperationsQueued = GlobalStatistics.Get(GetType().Name, "Expensive operation queue length"); vboInUse = GlobalStatistics.Get(GetType().Name, "VBOs in use"); - whitePixel = new Lazy(() => new TextureAtlas(this, TextureAtlas.WHITE_PIXEL_SIZE + TextureAtlas.PADDING, TextureAtlas.WHITE_PIXEL_SIZE + TextureAtlas.PADDING, true) - .WhitePixel); + whitePixel = new Lazy(() => + new TextureAtlas(this, TextureAtlas.WHITE_PIXEL_SIZE + TextureAtlas.PADDING, TextureAtlas.WHITE_PIXEL_SIZE + TextureAtlas.PADDING, true).WhitePixel); } void IRenderer.Initialise(IGraphicsSurface graphicsSurface) @@ -332,10 +332,6 @@ namespace osu.Framework.Graphics.Rendering /// /// The framebuffer to capture to. public abstract void CaptureScreenToFrameBuffer(IFrameBuffer frameBuffer); - - /// - /// Returns an image containing the content of a framebuffer. - /// protected internal virtual Image? ExtractFrameBufferData(IFrameBuffer frameBuffer) => null; /// @@ -370,7 +366,7 @@ namespace osu.Framework.Graphics.Rendering /// protected internal abstract void ClearCurrent(); -#region Clear + #region Clear public void Clear(ClearInfo clearInfo) { @@ -391,9 +387,9 @@ namespace osu.Framework.Graphics.Rendering /// The clear parameters. protected abstract void ClearImplementation(ClearInfo clearInfo); -#endregion + #endregion -#region Blending + #region Blending public void SetBlend(BlendingParameters blendingParameters) { @@ -429,9 +425,9 @@ namespace osu.Framework.Graphics.Rendering /// The blending mask. protected abstract void SetBlendMaskImplementation(BlendingMask blendingMask); -#endregion + #endregion -#region Viewport + #region Viewport public void PushViewport(RectangleI viewport) { @@ -480,9 +476,9 @@ namespace osu.Framework.Graphics.Rendering /// The viewport to use. protected abstract void SetViewportImplementation(RectangleI viewport); -#endregion + #endregion -#region Scissor + #region Scissor public void PushScissor(RectangleI scissor) { @@ -587,9 +583,9 @@ namespace osu.Framework.Graphics.Rendering /// Whether scissor should be enabled. protected abstract void SetScissorStateImplementation(bool enabled); -#endregion + #endregion -#region Projection Matrix + #region Projection Matrix public void PushProjectionMatrix(Matrix4 matrix) { @@ -616,9 +612,9 @@ namespace osu.Framework.Graphics.Rendering ProjectionMatrix = matrix; } -#endregion + #endregion -#region Masking + #region Masking public void PushMaskingInfo(in MaskingInfo maskingInfo, bool overwritePreviousScissor = false) { @@ -668,9 +664,9 @@ namespace osu.Framework.Graphics.Rendering globalUniformsChanged = true; } -#endregion + #endregion -#region Depth & Stencil + #region Depth & Stencil public void PushDepthInfo(DepthInfo depthInfo) { @@ -734,9 +730,9 @@ namespace osu.Framework.Graphics.Rendering /// The stencil parameters to use. protected abstract void SetStencilInfoImplementation(StencilInfo stencilInfo); -#endregion + #endregion -#region Batches + #region Batches internal IVertexBatch DefaultQuadBatch => quadBatches.Peek(); @@ -794,9 +790,9 @@ namespace osu.Framework.Graphics.Rendering vertexBuffersInUse.RemoveAll(b => !b.InUse); } -#endregion + #endregion -#region Textures + #region Textures public bool BindTexture(Texture texture, int unit, WrapMode? wrapModeS, WrapMode? wrapModeT) { @@ -906,9 +902,9 @@ namespace osu.Framework.Graphics.Rendering /// Whether the texture was set successfully. protected abstract bool SetTextureImplementation(INativeTexture? texture, int unit); -#endregion + #endregion -#region Framebuffers + #region Framebuffers public void BindFrameBuffer(IFrameBuffer frameBuffer) { @@ -959,7 +955,7 @@ namespace osu.Framework.Graphics.Rendering protected abstract void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer); -#endregion + #endregion public void DrawVertices(PrimitiveTopology topology, int vertexStart, int verticesCount) { @@ -986,35 +982,35 @@ namespace osu.Framework.Graphics.Rendering currentMaskingInfo.MaskingRect.Bottom), BorderThickness = currentMaskingInfo.BorderThickness / currentMaskingInfo.BlendRange, BorderColour = currentMaskingInfo.BorderThickness > 0 - ? new Matrix4( - // TopLeft - currentMaskingInfo.BorderColour.TopLeft.SRGB.R, - currentMaskingInfo.BorderColour.TopLeft.SRGB.G, - currentMaskingInfo.BorderColour.TopLeft.SRGB.B, - currentMaskingInfo.BorderColour.TopLeft.SRGB.A, - // BottomLeft - currentMaskingInfo.BorderColour.BottomLeft.SRGB.R, - currentMaskingInfo.BorderColour.BottomLeft.SRGB.G, - currentMaskingInfo.BorderColour.BottomLeft.SRGB.B, - currentMaskingInfo.BorderColour.BottomLeft.SRGB.A, - // TopRight - currentMaskingInfo.BorderColour.TopRight.SRGB.R, - currentMaskingInfo.BorderColour.TopRight.SRGB.G, - currentMaskingInfo.BorderColour.TopRight.SRGB.B, - currentMaskingInfo.BorderColour.TopRight.SRGB.A, - // BottomRight - currentMaskingInfo.BorderColour.BottomRight.SRGB.R, - currentMaskingInfo.BorderColour.BottomRight.SRGB.G, - currentMaskingInfo.BorderColour.BottomRight.SRGB.B, - currentMaskingInfo.BorderColour.BottomRight.SRGB.A) - : globalUniformBuffer.Data.BorderColour, + ? new Matrix4( + // TopLeft + currentMaskingInfo.BorderColour.TopLeft.SRGB.R, + currentMaskingInfo.BorderColour.TopLeft.SRGB.G, + currentMaskingInfo.BorderColour.TopLeft.SRGB.B, + currentMaskingInfo.BorderColour.TopLeft.SRGB.A, + // BottomLeft + currentMaskingInfo.BorderColour.BottomLeft.SRGB.R, + currentMaskingInfo.BorderColour.BottomLeft.SRGB.G, + currentMaskingInfo.BorderColour.BottomLeft.SRGB.B, + currentMaskingInfo.BorderColour.BottomLeft.SRGB.A, + // TopRight + currentMaskingInfo.BorderColour.TopRight.SRGB.R, + currentMaskingInfo.BorderColour.TopRight.SRGB.G, + currentMaskingInfo.BorderColour.TopRight.SRGB.B, + currentMaskingInfo.BorderColour.TopRight.SRGB.A, + // BottomRight + currentMaskingInfo.BorderColour.BottomRight.SRGB.R, + currentMaskingInfo.BorderColour.BottomRight.SRGB.G, + currentMaskingInfo.BorderColour.BottomRight.SRGB.B, + currentMaskingInfo.BorderColour.BottomRight.SRGB.A) + : globalUniformBuffer.Data.BorderColour, MaskingBlendRange = currentMaskingInfo.BlendRange, AlphaExponent = currentMaskingInfo.AlphaExponent, EdgeOffset = currentMaskingInfo.EdgeOffset, DiscardInner = currentMaskingInfo.Hollow, InnerCornerRadius = currentMaskingInfo.Hollow - ? currentMaskingInfo.HollowCornerRadius - : globalUniformBuffer.Data.InnerCornerRadius, + ? currentMaskingInfo.HollowCornerRadius + : globalUniformBuffer.Data.InnerCornerRadius, WrapModeS = (int)CurrentWrapModeS, WrapModeT = (int)CurrentWrapModeT }; @@ -1032,7 +1028,7 @@ namespace osu.Framework.Graphics.Rendering public abstract void DrawVerticesImplementation(PrimitiveTopology topology, int vertexStart, int verticesCount); -#region Shaders + #region Shaders public void BindShader(IShader shader) { @@ -1107,9 +1103,9 @@ namespace osu.Framework.Graphics.Rendering protected abstract void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer); -#endregion + #endregion -#region Factory + #region Factory public abstract IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear); @@ -1163,10 +1159,7 @@ namespace osu.Framework.Graphics.Rendering /// The filtering mode. /// The colour to initialise texture levels with (in the case of sub region initial uploads). If null, no initialisation is provided out-of-the-box. /// The . - protected abstract INativeTexture CreateNativeTexture(int width, - int height, - bool manualMipmaps = false, - TextureFilteringMode filteringMode = TextureFilteringMode.Linear, + protected abstract INativeTexture CreateNativeTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, Color4? initialisationColour = null); /// @@ -1177,8 +1170,8 @@ namespace osu.Framework.Graphics.Rendering /// The video . protected abstract INativeTexture CreateNativeVideoTexture(int width, int height); - public Texture CreateTexture(int width, int height, bool manualMipmaps, TextureFilteringMode filteringMode, WrapMode wrapModeS, WrapMode wrapModeT, Color4? initialisationColour) => - CreateTexture(CreateNativeTexture(width, height, manualMipmaps, filteringMode, initialisationColour), wrapModeS, wrapModeT); + public Texture CreateTexture(int width, int height, bool manualMipmaps, TextureFilteringMode filteringMode, WrapMode wrapModeS, WrapMode wrapModeT, Color4? initialisationColour) + => CreateTexture(CreateNativeTexture(width, height, manualMipmaps, filteringMode, initialisationColour), wrapModeS, wrapModeT); public Texture CreateVideoTexture(int width, int height) => CreateTexture(CreateNativeVideoTexture(width, height)); @@ -1189,8 +1182,8 @@ namespace osu.Framework.Graphics.Rendering /// The horizontal wrap mode of the texture. /// The vertical wrap mode of the texture. /// The . - internal Texture CreateTexture(INativeTexture nativeTexture, WrapMode wrapModeS = WrapMode.None, WrapMode wrapModeT = WrapMode.None) => - registerTexture(new Texture(nativeTexture, wrapModeS, wrapModeT)); + internal Texture CreateTexture(INativeTexture nativeTexture, WrapMode wrapModeS = WrapMode.None, WrapMode wrapModeT = WrapMode.None) + => registerTexture(new Texture(nativeTexture, wrapModeS, wrapModeT)); private Texture registerTexture(Texture texture) { @@ -1199,9 +1192,9 @@ namespace osu.Framework.Graphics.Rendering return texture; } -#endregion + #endregion -#region IRenderer explicit implementation + #region IRenderer explicit implementation bool IRenderer.VerticalSync { @@ -1294,9 +1287,9 @@ namespace osu.Framework.Graphics.Rendering checkValidType(field); if (field.FieldType == typeof(UniformMatrix3) - || field.FieldType == typeof(UniformMatrix4) - || field.FieldType == typeof(UniformVector3) - || field.FieldType == typeof(UniformVector4)) + || field.FieldType == typeof(UniformMatrix4) + || field.FieldType == typeof(UniformVector3) + || field.FieldType == typeof(UniformVector4)) { checkAlignment(field, offset, 16); } @@ -1317,16 +1310,16 @@ namespace osu.Framework.Graphics.Rendering static void checkValidType(FieldInfo field) { if (field.FieldType == typeof(UniformBool) - || field.FieldType == typeof(UniformFloat) - || field.FieldType == typeof(UniformInt) - || field.FieldType == typeof(UniformMatrix3) - || field.FieldType == typeof(UniformMatrix4) - || field.FieldType == typeof(UniformPadding4) - || field.FieldType == typeof(UniformPadding8) - || field.FieldType == typeof(UniformPadding12) - || field.FieldType == typeof(UniformVector2) - || field.FieldType == typeof(UniformVector4) - || field.FieldType == typeof(UniformVector4)) + || field.FieldType == typeof(UniformFloat) + || field.FieldType == typeof(UniformInt) + || field.FieldType == typeof(UniformMatrix3) + || field.FieldType == typeof(UniformMatrix4) + || field.FieldType == typeof(UniformPadding4) + || field.FieldType == typeof(UniformPadding8) + || field.FieldType == typeof(UniformPadding12) + || field.FieldType == typeof(UniformVector2) + || field.FieldType == typeof(UniformVector4) + || field.FieldType == typeof(UniformVector4)) { return; } @@ -1350,18 +1343,18 @@ namespace osu.Framework.Graphics.Rendering return null; return paddingRequired switch - { - 4 => typeof(UniformPadding4), - 8 => typeof(UniformPadding8), - 12 => typeof(UniformPadding12), - _ => null - }; + { + 4 => typeof(UniformPadding4), + 8 => typeof(UniformPadding8), + 12 => typeof(UniformPadding12), + _ => null + }; } } -#endregion + #endregion -#region TextureVisualiser support + #region TextureVisualiser support /// /// An event which is invoked every time a is created. @@ -1376,7 +1369,7 @@ namespace osu.Framework.Graphics.Rendering Texture[] IRenderer.GetAllTextures() => allTextures.ToArray(); -#endregion + #endregion private class PassthroughShaderStore : IShaderStore { diff --git a/osu.Framework/Graphics/Shaders/ShaderManager.cs b/osu.Framework/Graphics/Shaders/ShaderManager.cs index e2b38dbc2..5536e5aa5 100644 --- a/osu.Framework/Graphics/Shaders/ShaderManager.cs +++ b/osu.Framework/Graphics/Shaders/ShaderManager.cs @@ -40,12 +40,12 @@ namespace osu.Framework.Graphics.Shaders return cached; return shaderCache[(vertex, fragment)] = renderer.CreateShader( - $"{vertex}/{fragment}", - new[] - { - resolveShaderPart(vertex, ShaderPartType.Vertex), - resolveShaderPart(fragment, ShaderPartType.Fragment) - }); + $"{vertex}/{fragment}", + new[] + { + resolveShaderPart(vertex, ShaderPartType.Vertex), + resolveShaderPart(fragment, ShaderPartType.Fragment) + }); } /// @@ -54,14 +54,16 @@ namespace osu.Framework.Graphics.Shaders /// The vertex shader name. /// The fragment shader name. /// A cached instance, if existing. - public virtual IShader? GetCachedShader(string vertex, string fragment) => shaderCache.TryGetValue((vertex, fragment), out IShader? shader) ? shader : null; + public virtual IShader? GetCachedShader(string vertex, string fragment) + => shaderCache.TryGetValue((vertex, fragment), out IShader? shader) ? shader : null; /// /// Attempts to retrieve an already-cached shader part. /// /// The name of the shader part. /// A cached instance, if existing. - public virtual IShaderPart? GetCachedShaderPart(string name) => partCache.TryGetValue(name, out IShaderPart? part) ? part : null; + public virtual IShaderPart? GetCachedShaderPart(string name) + => partCache.TryGetValue(name, out IShaderPart? part) ? part : null; /// /// Attempts to retrieve the raw data for a shader file. @@ -114,7 +116,7 @@ namespace osu.Framework.Graphics.Shaders return string.Empty; } -#region IDisposable Support + #region IDisposable Support private bool isDisposed; @@ -143,7 +145,7 @@ namespace osu.Framework.Graphics.Shaders } } -#endregion + #endregion } public static class VertexShaderDescriptor @@ -160,6 +162,5 @@ namespace osu.Framework.Graphics.Shaders public const string BLUR = "Blur"; public const string GRAYSCALE = "Grayscale"; public const string VIDEO = "Video"; - public const string ACRYLIC = "Acrylic"; } } diff --git a/osu.Framework/Graphics/Veldrid/VeldridRenderer.cs b/osu.Framework/Graphics/Veldrid/VeldridRenderer.cs index eec9dc097..fb2e05acc 100644 --- a/osu.Framework/Graphics/Veldrid/VeldridRenderer.cs +++ b/osu.Framework/Graphics/Veldrid/VeldridRenderer.cs @@ -40,19 +40,26 @@ namespace osu.Framework.Graphics.Veldrid set => veldridDevice.AllowTearing = value; } - public override bool IsDepthRangeZeroToOne => veldridDevice.IsDepthRangeZeroToOne; + public override bool IsDepthRangeZeroToOne + => veldridDevice.IsDepthRangeZeroToOne; - public override bool IsUvOriginTopLeft => veldridDevice.IsUvOriginTopLeft; + public override bool IsUvOriginTopLeft + => veldridDevice.IsUvOriginTopLeft; - public override bool IsClipSpaceYInverted => veldridDevice.IsClipSpaceYInverted; + public override bool IsClipSpaceYInverted + => veldridDevice.IsClipSpaceYInverted; - public bool UseStructuredBuffers => veldridDevice.UseStructuredBuffers; + public bool UseStructuredBuffers + => veldridDevice.UseStructuredBuffers; - public GraphicsDevice Device => veldridDevice.Device; + public GraphicsDevice Device + => veldridDevice.Device; - public ResourceFactory Factory => veldridDevice.Factory; + public ResourceFactory Factory + => veldridDevice.Factory; - public GraphicsSurfaceType SurfaceType => veldridDevice.SurfaceType; + public GraphicsSurfaceType SurfaceType + => veldridDevice.SurfaceType; private readonly HashSet uniformBufferResetList = new HashSet(); @@ -100,19 +107,26 @@ namespace osu.Framework.Graphics.Veldrid graphicsPipeline.End(); } - protected internal override void SwapBuffers() => veldridDevice.SwapBuffers(); + protected internal override void SwapBuffers() + => veldridDevice.SwapBuffers(); - protected internal override void WaitUntilIdle() => veldridDevice.WaitUntilIdle(); + protected internal override void WaitUntilIdle() + => veldridDevice.WaitUntilIdle(); - protected internal override void WaitUntilNextFrameReady() => veldridDevice.WaitUntilNextFrameReady(); + protected internal override void WaitUntilNextFrameReady() + => veldridDevice.WaitUntilNextFrameReady(); - protected internal override void MakeCurrent() => veldridDevice.MakeCurrent(); + protected internal override void MakeCurrent() + => veldridDevice.MakeCurrent(); - protected internal override void ClearCurrent() => veldridDevice.ClearCurrent(); + protected internal override void ClearCurrent() + => veldridDevice.ClearCurrent(); - protected override void ClearImplementation(ClearInfo clearInfo) => graphicsPipeline.Clear(clearInfo); + protected override void ClearImplementation(ClearInfo clearInfo) + => graphicsPipeline.Clear(clearInfo); - protected override void SetScissorStateImplementation(bool enabled) => graphicsPipeline.SetScissorState(enabled); + protected override void SetScissorStateImplementation(bool enabled) + => graphicsPipeline.SetScissorState(enabled); protected override bool SetTextureImplementation(INativeTexture? texture, int unit) { @@ -123,23 +137,32 @@ namespace osu.Framework.Graphics.Veldrid return true; } - protected override void SetShaderImplementation(IShader shader) => graphicsPipeline.SetShader((VeldridShader)shader); + protected override void SetShaderImplementation(IShader shader) + => graphicsPipeline.SetShader((VeldridShader)shader); - protected override void SetBlendImplementation(BlendingParameters blendingParameters) => graphicsPipeline.SetBlend(blendingParameters); + protected override void SetBlendImplementation(BlendingParameters blendingParameters) + => graphicsPipeline.SetBlend(blendingParameters); - protected override void SetBlendMaskImplementation(BlendingMask blendingMask) => graphicsPipeline.SetBlendMask(blendingMask); + protected override void SetBlendMaskImplementation(BlendingMask blendingMask) + => graphicsPipeline.SetBlendMask(blendingMask); - protected override void SetViewportImplementation(RectangleI viewport) => graphicsPipeline.SetViewport(viewport); + protected override void SetViewportImplementation(RectangleI viewport) + => graphicsPipeline.SetViewport(viewport); - protected override void SetScissorImplementation(RectangleI scissor) => graphicsPipeline.SetScissor(scissor); + protected override void SetScissorImplementation(RectangleI scissor) + => graphicsPipeline.SetScissor(scissor); - protected override void SetDepthInfoImplementation(DepthInfo depthInfo) => graphicsPipeline.SetDepthInfo(depthInfo); + protected override void SetDepthInfoImplementation(DepthInfo depthInfo) + => graphicsPipeline.SetDepthInfo(depthInfo); - protected override void SetStencilInfoImplementation(StencilInfo stencilInfo) => graphicsPipeline.SetStencilInfo(stencilInfo); + protected override void SetStencilInfoImplementation(StencilInfo stencilInfo) + => graphicsPipeline.SetStencilInfo(stencilInfo); - protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer) => graphicsPipeline.SetFrameBuffer((VeldridFrameBuffer?)frameBuffer); + protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer) + => graphicsPipeline.SetFrameBuffer((VeldridFrameBuffer?)frameBuffer); - protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer) => ((VeldridFrameBuffer)frameBuffer).DeleteResources(true); + protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer) + => ((VeldridFrameBuffer)frameBuffer).DeleteResources(true); public override void DrawVerticesImplementation(PrimitiveTopology topology, int vertexStart, int verticesCount) { @@ -154,13 +177,14 @@ namespace osu.Framework.Graphics.Veldrid } public void BindVertexBuffer(IVeldridVertexBuffer buffer) - where T : unmanaged, IEquatable, IVertex => graphicsPipeline.SetVertexBuffer(buffer.Buffer, VeldridVertexUtils.Layout); + where T : unmanaged, IEquatable, IVertex + => graphicsPipeline.SetVertexBuffer(buffer.Buffer, VeldridVertexUtils.Layout); public void BindIndexBuffer(VeldridIndexLayout layout, int verticesCount) { ref var indexBuffer = ref layout == VeldridIndexLayout.Quad - ? ref quadIndexBuffer - : ref linearIndexBuffer; + ? ref quadIndexBuffer + : ref linearIndexBuffer; if (indexBuffer == null || indexBuffer.VertexCapacity < verticesCount) { @@ -193,19 +217,20 @@ namespace osu.Framework.Graphics.Veldrid /// Checks whether the given frame buffer is currently bound. /// /// The frame buffer to check. - public bool IsFrameBufferBound(IFrameBuffer frameBuffer) => FrameBuffer == frameBuffer; + public bool IsFrameBufferBound(IFrameBuffer frameBuffer) + => FrameBuffer == frameBuffer; - protected internal override Image TakeScreenshot() => veldridDevice.TakeScreenshot(); + protected internal override Image TakeScreenshot() + => veldridDevice.TakeScreenshot(); public override void CaptureScreenToFrameBuffer(IFrameBuffer frameBuffer) { - // For Veldrid renderer, we need to capture the current backbuffer content - // This is a simplified implementation - in practice, this would need to be - // implemented properly in the VeldridDevice or through the graphics context - throw new NotImplementedException("CaptureScreenToFrameBuffer not yet implemented for VeldridRenderer"); + // TODO: Implement screen capture for Veldrid + // This is a placeholder implementation } - protected internal override Image? ExtractFrameBufferData(IFrameBuffer frameBuffer) => ExtractTexture((VeldridTexture)frameBuffer.Texture.NativeTexture); + protected internal override Image? ExtractFrameBufferData(IFrameBuffer frameBuffer) + => ExtractTexture((VeldridTexture)frameBuffer.Texture.NativeTexture); protected internal Image? ExtractTexture(VeldridTexture texture) { @@ -245,34 +270,42 @@ namespace osu.Framework.Graphics.Veldrid /// The texture level. /// The texture data. /// The number of bytes per row of the image to read from . - public void UpdateTexture(global::Veldrid.Texture texture, int x, int y, int width, int height, int level, IntPtr data, int rowLengthInBytes) => - bufferUpdatePipeline.UpdateTexture(stagingTexturePool, texture, x, y, width, height, level, data, rowLengthInBytes); + public void UpdateTexture(global::Veldrid.Texture texture, int x, int y, int width, int height, int level, IntPtr data, int rowLengthInBytes) + => bufferUpdatePipeline.UpdateTexture(stagingTexturePool, texture, x, y, width, height, level, data, rowLengthInBytes); protected override void SetUniformImplementation(IUniformWithValue uniform) { } - protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer) => graphicsPipeline.AttachUniformBuffer(blockName, (IVeldridUniformBuffer)buffer); + protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer) + => graphicsPipeline.AttachUniformBuffer(blockName, (IVeldridUniformBuffer)buffer); - public void RegisterUniformBufferForReset(IVeldridUniformBuffer buffer) => uniformBufferResetList.Add(buffer); + public void RegisterUniformBufferForReset(IVeldridUniformBuffer buffer) + => uniformBufferResetList.Add(buffer); - public void GenerateMipmaps(VeldridTexture texture) => graphicsPipeline.GenerateMipmaps(texture); + public void GenerateMipmaps(VeldridTexture texture) + => graphicsPipeline.GenerateMipmaps(texture); - public CommandList BufferUpdateCommands => bufferUpdatePipeline.Commands; + public CommandList BufferUpdateCommands + => bufferUpdatePipeline.Commands; - void IVeldridRenderer.BindShader(VeldridShader shader) => BindShader(shader); + void IVeldridRenderer.BindShader(VeldridShader shader) + => BindShader(shader); - void IVeldridRenderer.UnbindShader(VeldridShader shader) => UnbindShader(shader); + void IVeldridRenderer.UnbindShader(VeldridShader shader) + => UnbindShader(shader); - void IVeldridRenderer.EnqueueTextureUpload(VeldridTexture texture) => EnqueueTextureUpload(texture); + void IVeldridRenderer.EnqueueTextureUpload(VeldridTexture texture) + => EnqueueTextureUpload(texture); - protected override IShaderPart CreateShaderPart(IShaderStore store, string name, byte[]? rawData, ShaderPartType partType) => new VeldridShaderPart(this, rawData, partType, store); + protected override IShaderPart CreateShaderPart(IShaderStore store, string name, byte[]? rawData, ShaderPartType partType) + => new VeldridShaderPart(this, rawData, partType, store); - protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) => - new VeldridShader(this, name, parts.Cast().ToArray(), compilationStore); + protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) + => new VeldridShader(this, name, parts.Cast().ToArray(), compilationStore); - public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) => - new VeldridFrameBuffer(this, renderBufferFormats?.ToPixelFormats(), filteringMode.ToSamplerFilter()); + public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) + => new VeldridFrameBuffer(this, renderBufferFormats?.ToPixelFormats(), filteringMode.ToSamplerFilter()); protected override IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology primitiveType) // maxBuffers is ignored because batches are not allowed to wrap around in Veldrid. @@ -282,18 +315,18 @@ namespace osu.Framework.Graphics.Veldrid // maxBuffers is ignored because batches are not allowed to wrap around in Veldrid. => new VeldridQuadBatch(this, size); - protected override IUniformBuffer CreateUniformBuffer() => new VeldridUniformBuffer(this); + protected override IUniformBuffer CreateUniformBuffer() + => new VeldridUniformBuffer(this); - protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) => new VeldridShaderStorageBufferObject(this, uboSize, ssboSize); + protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) + => new VeldridShaderStorageBufferObject(this, uboSize, ssboSize); - protected override INativeTexture CreateNativeTexture(int width, - int height, - bool manualMipmaps = false, - TextureFilteringMode filteringMode = TextureFilteringMode.Linear, - Color4? initialisationColour = null) => - new VeldridTexture(this, width, height, manualMipmaps, filteringMode.ToSamplerFilter(), initialisationColour); + protected override INativeTexture CreateNativeTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, + Color4? initialisationColour = null) + => new VeldridTexture(this, width, height, manualMipmaps, filteringMode.ToSamplerFilter(), initialisationColour); - protected override INativeTexture CreateNativeVideoTexture(int width, int height) => new VeldridVideoTexture(this, width, height); + protected override INativeTexture CreateNativeVideoTexture(int width, int height) + => new VeldridVideoTexture(this, width, height); internal IStagingBuffer CreateStagingBuffer(uint count) where T : unmanaged diff --git a/osu.Framework/Input/Bindings/KeyBindingContainer.cs b/osu.Framework/Input/Bindings/KeyBindingContainer.cs index b64e06bc8..8ef1e9bb0 100644 --- a/osu.Framework/Input/Bindings/KeyBindingContainer.cs +++ b/osu.Framework/Input/Bindings/KeyBindingContainer.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Framework.Lists; -// using osu.Framework.Logging; +using osu.Framework.Logging; using osuTK; namespace osu.Framework.Input.Bindings @@ -328,8 +328,8 @@ namespace osu.Framework.Input.Bindings } } - // if (handled != null) - // Logger.Log($"Pressed ({pressed}) handled by {handled}.", LoggingTarget.Runtime, LogLevel.Debug); + if (handled != null) + Logger.Log($"Pressed ({pressed}) handled by {handled}.", LoggingTarget.Runtime, LogLevel.Debug); return handled; } diff --git a/osu.Framework/Resources/Shaders/sh_AcrylicBlur.fs b/osu.Framework/Resources/Shaders/sh_AcrylicBlur.fs new file mode 100644 index 000000000..becabc919 --- /dev/null +++ b/osu.Framework/Resources/Shaders/sh_AcrylicBlur.fs @@ -0,0 +1,61 @@ +#ifndef ACRYLIC_BLUR_FS +#define ACRYLIC_BLUR_FS + +#include "sh_Utils.h" + +#undef INV_SQRT_2PI +#define INV_SQRT_2PI 0.39894 + +layout(location = 2) in mediump vec2 v_TexCoord; + +layout(std140, set = 0, binding = 0) uniform m_BlurParameters +{ + mediump vec2 g_TexSize; + int g_Radius; + mediump float g_Sigma; + highp vec2 g_BlurDirection; +}; + +layout(set = 1, binding = 0) uniform lowp texture2D m_Texture; +layout(set = 1, binding = 1) uniform lowp sampler m_Sampler; + +layout(location = 0) out vec4 o_Colour; + +mediump float computeGauss(in mediump float x, in mediump float sigma) +{ + return INV_SQRT_2PI * exp(-0.5*x*x / (sigma*sigma)) / sigma; +} + +lowp vec4 blur(int radius, highp vec2 direction, mediump vec2 texCoord, mediump vec2 texSize, mediump float sigma) +{ + mediump vec4 sum = vec4(0.0); + mediump float totalWeight = 0.0; + + // 中心像素 + mediump float weight = computeGauss(0.0, sigma); + sum += texture(sampler2D(m_Texture, m_Sampler), texCoord) * weight; + totalWeight += weight; + + // 对称采样 - 修复采样位置和权重 + for (int i = 1; i <= radius; ++i) + { + // 使用正确的采样位置(1, 2, 3... 而不是1.5, 3.5...) + mediump float x = float(i); + weight = computeGauss(x, sigma); + sum += texture(sampler2D(m_Texture, m_Sampler), texCoord + direction * x / texSize) * weight; + sum += texture(sampler2D(m_Texture, m_Sampler), texCoord - direction * x / texSize) * weight; + totalWeight += 2.0 * weight; + } + + return sum / totalWeight; +} + +void main(void) +{ + // 应用模糊 + vec4 blurredColour = blur(g_Radius, g_BlurDirection, v_TexCoord, g_TexSize, g_Sigma); + + o_Colour = blurredColour; +} + +#endif \ No newline at end of file diff --git a/osu.Framework/Resources/Shaders/sh_AcrylicBlur.vs b/osu.Framework/Resources/Shaders/sh_AcrylicBlur.vs new file mode 100644 index 000000000..54ba6f9a6 --- /dev/null +++ b/osu.Framework/Resources/Shaders/sh_AcrylicBlur.vs @@ -0,0 +1,36 @@ +#ifndef ACRYLIC_BLUR_VS +#define ACRYLIC_BLUR_VS + +#include "sh_Utils.h" + +layout(location = 0) in highp vec2 m_Position; +layout(location = 1) in lowp vec4 m_Colour; +layout(location = 2) in highp vec2 m_TexCoord; +layout(location = 3) in highp vec4 m_TexRect; +layout(location = 4) in mediump vec2 m_BlendRange; +layout(location = 5) in highp float m_BackbufferDrawDepth; + +layout(location = 0) out highp vec2 v_MaskingPosition; +layout(location = 1) out lowp vec4 v_Colour; +layout(location = 2) out highp vec2 v_TexCoord; +layout(location = 3) out highp vec4 v_TexRect; +layout(location = 4) out mediump vec2 v_BlendRange; + +void main(void) +{ + // Transform from screen space to masking space. + highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0); + v_MaskingPosition = maskingPos.xy / maskingPos.z; + + v_Colour = m_Colour; + v_TexCoord = m_TexCoord; + v_TexRect = m_TexRect; + v_BlendRange = m_BlendRange; + + gl_Position = g_ProjMatrix * vec4(m_Position, 1.0, 1.0); + + if (g_BackbufferDraw) + gl_Position.z = m_BackbufferDrawDepth; +} + +#endif \ No newline at end of file diff --git a/osu.Framework/Resources/Shaders/sh_AcrylicDepthBlur.fs b/osu.Framework/Resources/Shaders/sh_AcrylicDepthBlur.fs new file mode 100644 index 000000000..521cb18be --- /dev/null +++ b/osu.Framework/Resources/Shaders/sh_AcrylicDepthBlur.fs @@ -0,0 +1,152 @@ +#ifndef ACRYLIC_DEPTH_BLUR_FS +#define ACRYLIC_DEPTH_BLUR_FS + +#include "sh_Utils.h" +#include "sh_Masking.h" + +#undef INV_SQRT_2PI +#define INV_SQRT_2PI 0.39894 + +layout(location = 0) in highp vec4 v_Colour; +layout(location = 1) in mediump vec2 v_TexCoord; +layout(location = 2) in highp vec4 v_ScreenPosition; + +// 模糊参数 +layout(std140, set = 0, binding = 0) uniform m_AcrylicParameters +{ + mediump vec2 g_ScreenSize; // 屏幕尺寸 + int g_BlurRadius; // 模糊半径 + mediump float g_BlurSigma; // 高斯模糊 sigma + mediump float g_BlurStrength; // 模糊强度(0-1) + mediump float g_TintAlpha; // 着色透明度 + mediump vec3 g_TintColor; // 着色颜色 + highp float g_CurrentDepth; // 当前容器的深度 +}; + +// 场景纹理(后台缓冲区) +layout(set = 1, binding = 0) uniform lowp texture2D m_SceneTexture; +layout(set = 1, binding = 1) uniform lowp sampler m_SceneSampler; + +// 深度纹理 +layout(set = 2, binding = 0) uniform highp texture2D m_DepthTexture; +layout(set = 2, binding = 1) uniform highp sampler m_DepthSampler; + +layout(location = 0) out vec4 o_Colour; + +// 计算高斯权重 +mediump float computeGauss(in mediump float x, in mediump float sigma) +{ + return INV_SQRT_2PI * exp(-0.5 * x * x / (sigma * sigma)) / sigma; +} + +// 基于深度的采样 - 只采样比当前深度更深的像素 +lowp vec4 sampleWithDepth(vec2 uv, highp float currentDepth) +{ + // 采样深度值 + highp float depth = texture(sampler2D(m_DepthTexture, m_DepthSampler), uv).r; + + // 只有当采样点的深度大于当前深度时才采样(更深的内容) + // 在深度缓冲区中,更大的值表示更深的深度 + if (depth > currentDepth) + { + return texture(sampler2D(m_SceneTexture, m_SceneSampler), uv); + } + else + { + // 如果采样点在当前层之上或同层,返回透明色 + return vec4(0.0); + } +} + +// 双通道可分离高斯模糊(水平+垂直) +lowp vec4 applyDepthAwareBlur(vec2 screenCoord, highp float currentDepth, int radius, float sigma) +{ + vec2 pixelSize = 1.0 / g_ScreenSize; + + // 第一步: 水平模糊 + mediump float factor = computeGauss(0.0, sigma); + mediump vec4 horizontalSum = sampleWithDepth(screenCoord, currentDepth) * factor; + mediump float totalFactor = factor; + int validSamples = 1; + + for (int i = 1; i <= radius; i++) + { + factor = computeGauss(float(i), sigma); + + vec4 sample1 = sampleWithDepth(screenCoord + vec2(float(i) * pixelSize.x, 0.0), currentDepth); + vec4 sample2 = sampleWithDepth(screenCoord - vec2(float(i) * pixelSize.x, 0.0), currentDepth); + + // 只有当采样有效时才累加 + if (sample1.a > 0.0) + { + horizontalSum += sample1 * factor; + totalFactor += factor; + validSamples++; + } + + if (sample2.a > 0.0) + { + horizontalSum += sample2 * factor; + totalFactor += factor; + validSamples++; + } + } + + vec4 horizontalBlur = totalFactor > 0.0 ? horizontalSum / totalFactor : vec4(0.0); + + // 第二步: 垂直模糊(对水平模糊的结果进行) + // 为简化,我们在fragment shader中做简化版的双通道模糊 + // 实际生产环境可以用两个pass来实现更高效的可分离模糊 + factor = computeGauss(0.0, sigma); + mediump vec4 finalSum = horizontalBlur * factor; + totalFactor = factor; + + for (int i = 1; i <= radius; i++) + { + factor = computeGauss(float(i), sigma); + + vec4 sample1 = sampleWithDepth(screenCoord + vec2(0.0, float(i) * pixelSize.y), currentDepth); + vec4 sample2 = sampleWithDepth(screenCoord - vec2(0.0, float(i) * pixelSize.y), currentDepth); + + if (sample1.a > 0.0) + { + finalSum += sample1 * factor; + totalFactor += factor; + } + + if (sample2.a > 0.0) + { + finalSum += sample2 * factor; + totalFactor += factor; + } + } + + return totalFactor > 0.0 ? finalSum / totalFactor : vec4(0.0); +} + +void main(void) +{ + // 计算屏幕空间UV坐标 + vec2 screenCoord = (v_ScreenPosition.xy / v_ScreenPosition.w) * 0.5 + 0.5; + + // 获取当前片段的深度 + highp float currentDepth = g_CurrentDepth; + + // 应用深度感知模糊 + vec4 blurredColor = applyDepthAwareBlur( + screenCoord, + currentDepth, + g_BlurRadius, + g_BlurSigma + ); + + // 应用着色层 + vec4 tintColor = vec4(g_TintColor, g_TintAlpha); + vec4 finalColor = blend(tintColor, blurredColor); + + // 应用遮罩(圆角等) + vec2 wrappedCoord = wrap(v_TexCoord, v_TexRect); + o_Colour = getRoundedColor(finalColor * v_Colour, wrappedCoord); +} + +#endif diff --git a/osu.Framework/Resources/Shaders/sh_AcrylicDepthBlur.vs b/osu.Framework/Resources/Shaders/sh_AcrylicDepthBlur.vs new file mode 100644 index 000000000..5598606ae --- /dev/null +++ b/osu.Framework/Resources/Shaders/sh_AcrylicDepthBlur.vs @@ -0,0 +1,25 @@ +#ifndef ACRYLIC_DEPTH_BLUR_VS +#define ACRYLIC_DEPTH_BLUR_VS + +#include "sh_Utils.h" + +layout(location = 0) in highp vec2 m_Position; +layout(location = 1) in lowp vec4 m_Colour; +layout(location = 2) in highp vec2 m_TexCoord; + +layout(location = 0) out highp vec4 v_Colour; +layout(location = 1) out highp vec2 v_TexCoord; +layout(location = 2) out highp vec4 v_ScreenPosition; + +void main(void) +{ + // 传递顶点颜色和纹理坐标 + v_Colour = m_Colour; + v_TexCoord = m_TexCoord; + + // 计算屏幕空间位置(用于深度采样) + gl_Position = g_ProjMatrix * vec4(m_Position, 1.0, 1.0); + v_ScreenPosition = gl_Position; +} + +#endif diff --git a/osu.Framework/osu.Framework.csproj b/osu.Framework/osu.Framework.csproj index e5fe4bc60..e7e4803d2 100644 --- a/osu.Framework/osu.Framework.csproj +++ b/osu.Framework/osu.Framework.csproj @@ -1,4 +1,4 @@ - + net8.0 Library