Refactor rendering projects

This commit is contained in:
2026-02-24 20:08:26 +09:00
parent 93c58fa7fb
commit 30090f84ab
88 changed files with 1350 additions and 1136 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.dll filter=lfs diff=lfs merge=lfs -text

75
ARCHITECTURE_CN.md Normal file
View File

@@ -0,0 +1,75 @@
# GhostEngine 架构详解
本文档深入探讨 GhostEngine 核心模块的设计实现,为协同开发提供详细的技术参考。
## 1. 实体组件系统 (Ghost.Entities)
Ghost.Entities 采用 Archetype (原型) 模式,旨在优化大规模实体的遍历效率和内存布局。
### 1.1 核心组件
- **World**: 包含实体生命周期管理的顶层容器,包含 `EntityManager`
- **Archetype**: 定义了一组特定组件的组合。具有相同组件集的所有实体都存储在同一 Archetype 中。
- **Chunk**: Archetype 内部的数据块,按列存储 (Columnar Storage) 组件数据,以确保对单个组件的线性访问具备极佳的 CPU 缓存亲和性。
- **EntityQuery**: 通过位掩码 (Bitmask) 快速匹配 Archetype支持 `WithAll`, `WithAny`, `WithNone` 过滤规则。
### 1.2 并行处理
- 实体查询支持多线程作业系统 (Job System) 迭代。
- **EntityCommandBuffer (ECB)**: 允许在并行作业中排队待处理的实体修改请求 (如创建、删除、添加组件),并在单线程同步点进行统一应用,以避免竞态条件。
## 2. 渲染架构 (Ghost.Graphics)
渲染系统设计目标是支持高效、现代的图形渲染流水线,同时降低 D3D12 的开发复杂度。
### 2.1 RHI (渲染硬件接口)
RHI 位于 `Ghost.Graphics.RHI` 命名空间下,抽象了底层的渲染资源:
- **IRenderDevice**: 逻辑渲染设备。
- **ICommandBuffer**: 用于录制 GPU 指令。
- **IPipelineLibrary**: 缓存并管理渲染管线状态对象 (PSO)。
- **IResourceDatabase**: 管理显存资源 (Buffer, Texture) 及其生命周期。
### 2.2 Render Graph (渲染图)
Render Graph 是图形模块的核心组件,负责帧内资源的依赖分析和自动调度:
- **Pass Builder**: 在帧开始阶段,每个 Pass 声明其读取 (`Read`) 和写入 (`Write`) 的资源。
- **Resource Aliasing**: 自动识别不重叠的资源生命周期,实现显存的物理地址复用。
- **Automatic Barrier**: 自动根据资源的读写关系插入 `ResourceBarrier` (例如从 `RenderTarget` 状态转换到 `PixelShaderResource` 状态)。
### 2.3 自定义着色器语言 (Ghost.DSL)
引擎支持 `.gshdr` 文件,其语法借鉴了 HLSL 但增强了元数据支持:
- **自动属性映射**: DSL 编译器会解析着色器定义的属性,并自动生成与之匹配的 C# 结构体 (`ShaderStructGenerator`),简化 CPU 到 GPU 的数据传递。
## 3. 编辑器架构 (Ghost.Editor)
编辑器框架采用模块化设计,重点在于扩展性和资源工作流。
### 3.1 资源数据库 (Asset Database)
- **AssetRegistry**: 通过资源文件的 GUID 进行追踪,支持跨文件引用的完整性校验。
- **AssetProcessor**: 插件化系统,针对不同文件扩展名 (如 `.png`, `.fbx`) 提供特定的导入和转换逻辑 (如调用 Nvtt 压缩纹理)。
### 3.2 检查器 (Inspector)
- **Service-driven**: `InspectorService` 动态检测当前选中实体的组件列表,并根据组件类型匹配对应的 `ComponentEditor` 进行 UI 渲染。
- **Data Binding**: 利用 `ReflectionBinding` 实现编辑器 UI 与运行时组件数据的双向同步。
## 4. 核心设计原则 (Core Design Principles)
项目在底层代码中遵循以下核心设计原则,以确保高性能和系统稳定性:
### 4.1 Result over Exception (结果对象胜于异常)
在引擎的核心运行时(尤其是 `Ghost.Graphics``Ghost.Entities`)中,我们避免使用异常来处理预期的错误。
- **Result 结构体**: 使用 `Ghost.Core.Result``Result<T>` 结构体返回操作结果。
- **性能**: 避免了异常产生的堆栈跟踪开销。
- **显性处理**: 强制调用者检查 `IsSuccess` 或使用 `Deconstruct` 模式处理错误,使错误流更加清晰。
### 4.2 Handle over Ptr/Reference (句柄胜于指针/引用)
为了内存安全和支持序列化,引擎广泛使用句柄而非直接的内存指针。
- **Handle<T>**: 资源(如 `Texture`, `Buffer`, `Entity`)通过强类型句柄进行引用。
- **安全性**: 防止野指针问题,支持资源的延迟加载和卸载,而不破坏引用。
- **解耦**: 句柄作为后端资源的索引,使得底层的资源管理器(如 `D3D12ResourceDatabase`)可以自由地重新分配或移动物理资源。
## 5. 协同开发建议
- **跨项目引用**: 尽量避免 `Runtime` 项目引用 `Editor` 项目。`Editor` 应依赖 `Runtime` 以提供实时预览。
- **性能关键点**: 在 `Ghost.Entities``Ghost.Graphics` 模块中,应尽量避免堆内存分配 (GC Allocation),优先使用 `Span<T>`, `Memory<T>` 及非托管内存。
---
*注:本架构基于当前代码实现,如有变更将及时更新文档。*

76
DEVELOPMENT_CN.md Normal file
View File

@@ -0,0 +1,76 @@
# GhostEngine 开发指南
欢迎参与 GhostEngine 项目。本文档旨在帮助你快速了解项目的当前状态、架构设计、开发规范及环境配置。
## 1. 开发环境要求 (Prerequisites)
在开始开发之前,请确保你的开发环境满足以下要求:
- **操作系统**: Windows 10 版本 1809 (17763) 或更高版本。
- **IDE**: Visual Studio 2022 (建议使用最新预览版以支持 .NET 10)。
- **.NET SDK**: .NET 10.0 SDK。
- **Windows App SDK**: 项目目前使用 Windows App SDK (WinUI 3) 版本 1.8.260101001。
- **显卡**: 支持 DirectX 12 (Feature Level 11.0+) 的显卡。
- **Visual Studio 工作负载**:
- .NET 桌面开发
- 使用 C++ 的桌面开发 (部分第三方库包装需要)
- 通用 Windows 平台开发 (用于 Windows App SDK 支持)
## 2. 项目结构 (Project Structure)
项目代码主要位于 `src` 目录下,按功能分为三个主要部分:
### 2.1 Runtime (引擎运行时)
- **Ghost.Graphics**: 图形渲染核心。包含 RHI (渲染硬件接口) 定义及其 D3D12 实现。集成了 Render Graph (渲染图) 模块用于管理复杂的渲染流水线。
- **Ghost.Entities**: 基于 Archetype (原型) 的高性能 ECS (实体组件系统) 实现。包含 `World`, `EntityManager`, `EntityQuery` 等核心组件。
- **Ghost.Engine**: 引擎基础逻辑。包含场景管理 (`Scene`)、基础组件 (如 `Hierarchy`, `LocalToWorld`) 及数学工具类。
### 2.2 Editor (编辑器)
- **Ghost.Editor**: 基于 WinUI 3 编写的桌面编辑器前端。采用 MVVM 架构。
- **Ghost.Editor.Core**: 编辑器框架层。包含资源管理 (`AssetRegistry`)、检查器服务 (`InspectorService`)、场景树管理 (`SceneGraph`) 等非 UI 逻辑。
- **Ghost.DSL**: 引擎自定义着色器语言 (GSL) 的编译器和解析器,用于处理着色器代码生成。
### 2.3 ThirdParty (第三方库)
- 包含对 FMOD (音频)、Nvtt (纹理压缩)、MeshOptimizer (模型优化) 等 C++ 库的 C# 包装。
## 3. 命名规范 (Naming Conventions)
项目严格遵守以下 C# 编程规范:
- **命名空间**: 以 `Ghost.` 开头,后跟模块名 (例如 `Ghost.Graphics.D3D12`)。
- **类与方法**: 使用 `PascalCase` (大驼峰命名法)。
- **私有字段**: 使用 `_camelCase` (下划线前缀的小驼峰命名法)。
- **接口**: 必须以 `I` 作为前缀 (例如 `IRenderDevice`)。
- **异步方法**: 必须以 `Async` 作为后缀 (例如 `LoadAssetAsync`)。
- **实现类**: 针对接口的具体实现应体现技术细节 (例如 `D3D12GraphicsEngine` 实现了 `IGraphicsEngine`)。
## 4. 架构设计与模式 (Architecture & Patterns)
### 4.1 ECS (Entity Component System)
运行时逻辑优先使用 ECS 模式以获得最佳的缓存命中率和并行性能:
- **Entity**: 纯 ID。
- **Component**: 纯数据结构 (Struct)。
- **System**: 处理特定组件组合的逻辑类。
### 4.2 RHI & Render Graph
- **RHI (Render Hardware Interface)**: 抽象了底层的图形 API。目前主要实现为 D3D12通过 Vortice.Windows 进行绑定。
- **Render Graph**: 采用有向无环图 (DAG) 管理每一帧的渲染顺序、资源屏障 (Resource Barriers) 和资源复用,简化了 D3D12 的资源管理负担。
### 4.3 MVVM & Service Pattern (Editor)
编辑器部分遵循:
- **MVVM**: 分离 UI (`View`) 与业务逻辑 (`ViewModel`)。
- **Service/Contract**: 通过接口定义服务 (如 `IAssetRegistry`),并利用依赖注入 (Dependency Injection) 进行解耦。
### 4.4 核心设计原则 (Core Design Principles)
- **Result over Exception (结果对象胜于异常)**: 在核心运行时中,避免使用 `throw` 处理预期错误,优先返回 `Ghost.Core.Result` 结构体,以提高性能和错误流的可控性。
- **Handle over Ptr/Reference (句柄胜于指针/引用)**: 资源引用(如实体、纹理、缓冲区)应优先使用 `Handle<T>`,避免持有直接的内存地址,以确保资源管理的安全性并支持高效序列化。
## 5. 核心逻辑当前状态
- **渲染系统**: 已具备基础的 D3D12 渲染器、Render Graph 编译器及简单的 Mesh 渲染 pass。支持自定义着色器加载。
- **实体系统**: 已实现 Archetype-based ECS支持高性能的实体查询 (`EntityQuery`) 和作业系统迭代。
- **资源管理**: 编辑器端已实现初步的资产数据库 (`AssetRegistry`),支持纹理和模型的导入流程。
- **场景管理**: 支持基础的层级结构 (`Hierarchy`) 和变换同步 (`LocalToWorld`)。
---
*注:关于后续的开发计划 (TODO) 及改进方向,我将直接通过会议或即时通讯工具进行沟通。*

View File

@@ -52,7 +52,7 @@ public static class AssetHandlerExtensions
return await handler.ExportAsync(assetStream, targetStream, options, token); return await handler.ExportAsync(assetStream, targetStream, options, token);
} }
public static async ValueTask<Result<Asset>> ReadAsync(this IAssetHandler handler, string assetFilePath, IAssetRegistry assetDatabase, CancellationToken token = default) public static async ValueTask<Result<Asset>> LoadAsync(this IAssetHandler handler, string assetFilePath, IAssetRegistry assetDatabase, CancellationToken token = default)
{ {
await using var sourceStream = new FileStream(assetFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); await using var sourceStream = new FileStream(assetFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
return await handler.LoadAsync(sourceStream, assetDatabase, token); return await handler.LoadAsync(sourceStream, assetDatabase, token);

View File

@@ -4,8 +4,11 @@ using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Image; using Misaki.HighPerformance.Image;
using System.Buffers; using System.Buffers;
using System.Configuration;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.Windows;
using static Ghost.Editor.Core.AssetHandler.TextureAssetSettings;
namespace Ghost.Editor.Core.AssetHandler; namespace Ghost.Editor.Core.AssetHandler;
@@ -211,8 +214,14 @@ public class TextureAssetSettings : IAssetSettings
{ {
get; set; get; set;
} = new SamplerSettings(); } = new SamplerSettings();
}
public async ValueTask<Result<long>> WriteToStreamAsync(Stream stream, CancellationToken token = default) [CustomAssetHandler(ID = TextureAsset._TYPE_ID, SupportedExtensions = new[] { ".png", ".jpg", ".jpeg", ".tga", ".bmp", ".hdr" })]
internal class TextureAssetHandler : IImportableAssetHandler
{
private const int _CURRENT_VERSION = 1;
private static async ValueTask<Result<long>> WriteSettingsToStreamAsync(TextureAssetSettings settings, Stream stream, CancellationToken token = default)
{ {
var size = Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>() + Unsafe.SizeOf<SamplerSettings>(); var size = Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>() + Unsafe.SizeOf<SamplerSettings>();
var tempArray = ArrayPool<byte>.Shared.Rent(size); var tempArray = ArrayPool<byte>.Shared.Rent(size);
@@ -220,9 +229,9 @@ public class TextureAssetSettings : IAssetSettings
try try
{ {
ref byte address = ref MemoryMarshal.GetReference(tempArray); ref byte address = ref MemoryMarshal.GetReference(tempArray);
Unsafe.WriteUnaligned(ref address, Basic); Unsafe.WriteUnaligned(ref address, settings.Basic);
Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, Unsafe.SizeOf<BasicSettings>()), Advanced); Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, Unsafe.SizeOf<BasicSettings>()), settings.Advanced);
Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>()), Sampler); Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>()), settings.Sampler);
await stream.WriteAsync(tempArray.AsMemory(0, size), token).ConfigureAwait(false); await stream.WriteAsync(tempArray.AsMemory(0, size), token).ConfigureAwait(false);
@@ -238,7 +247,7 @@ public class TextureAssetSettings : IAssetSettings
} }
} }
public async ValueTask<Result<IAssetSettings>> ReadFromStreamAsync(Stream stream, CancellationToken token = default) private static async ValueTask<Result<IAssetSettings>> ReadSettingsFromStreamAsync(Stream stream, CancellationToken token = default)
{ {
var size = Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>() + Unsafe.SizeOf<SamplerSettings>(); var size = Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>() + Unsafe.SizeOf<SamplerSettings>();
var tempArray = ArrayPool<byte>.Shared.Rent(size); var tempArray = ArrayPool<byte>.Shared.Rent(size);
@@ -248,9 +257,9 @@ public class TextureAssetSettings : IAssetSettings
await stream.ReadAsync(tempArray.AsMemory(0, size), token).ConfigureAwait(false); await stream.ReadAsync(tempArray.AsMemory(0, size), token).ConfigureAwait(false);
// Use index-based reads after the await to avoid 'ref across await' errors. // Use index-based reads after the await to avoid 'ref across await' errors.
var basic = Unsafe.ReadUnaligned<BasicSettings>(ref tempArray[0]); var basic = Unsafe.ReadUnaligned<BasicSettings>(ref tempArray[0]);
var advanced = Unsafe.ReadUnaligned<AdvancedSettings>(ref tempArray[Unsafe.SizeOf<BasicSettings>()]); var advanced = Unsafe.ReadUnaligned<AdvancedSettings>(ref tempArray[Unsafe.SizeOf<BasicSettings>()]);
var sampler = Unsafe.ReadUnaligned<SamplerSettings>(ref tempArray[Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>()]); var sampler = Unsafe.ReadUnaligned<SamplerSettings>(ref tempArray[Unsafe.SizeOf<BasicSettings>() + Unsafe.SizeOf<AdvancedSettings>()]);
var settings = new TextureAssetSettings var settings = new TextureAssetSettings
{ {
@@ -270,12 +279,6 @@ public class TextureAssetSettings : IAssetSettings
ArrayPool<byte>.Shared.Return(tempArray); ArrayPool<byte>.Shared.Return(tempArray);
} }
} }
}
[CustomAssetHandler(ID = TextureAsset._TYPE_ID, SupportedExtensions = new[] { ".png", ".jpg", ".jpeg", ".tga", ".bmp", ".hdr" })]
internal class TextureAssetHandler : IImportableAssetHandler
{
private const int _CURRENT_VERSION = 1;
public ValueTask<Result> ExportAsync(Stream assetStream, Stream targetStream, IAssetExportOptions? options, CancellationToken token = default) public ValueTask<Result> ExportAsync(Stream assetStream, Stream targetStream, IAssetExportOptions? options, CancellationToken token = default)
{ {
@@ -287,7 +290,9 @@ internal class TextureAssetHandler : IImportableAssetHandler
// ---- 1. Probe image info ----------------------------------------------- // ---- 1. Probe image info -----------------------------------------------
var info = ImageInfo.FromStream(sourceStream); var info = ImageInfo.FromStream(sourceStream);
if (info.BitsPerChannel <= 0) if (info.BitsPerChannel <= 0)
{
return Result.Failure($"Unsupported image format with {info.BitsPerChannel} bits per channel."); return Result.Failure($"Unsupported image format with {info.BitsPerChannel} bits per channel.");
}
var isFloat = info.BitsPerChannel > 8; var isFloat = info.BitsPerChannel > 8;
var width = info.Width; var width = info.Width;
@@ -329,14 +334,6 @@ internal class TextureAssetHandler : IImportableAssetHandler
token).ConfigureAwait(false); token).ConfigureAwait(false);
// ---- 4. Write asset file: header + settings + raw image data ----------- // ---- 4. Write asset file: header + settings + raw image data -----------
// Content layout (all little-endian):
// int32 width
// int32 height
// byte isFloat (0 = byte, 1 = float)
// int32 colorComponents (cast of ColorComponents enum)
// byte[] pixelBytes
const int _CONTENT_HEADER_SIZE = 4 + 4 + 1 + 4; // 13 bytes
var contentSize = _CONTENT_HEADER_SIZE + pixelBytes.Length;
var header = new AssetMetadata(id, TextureAsset.s_typeGuid) var header = new AssetMetadata(id, TextureAsset.s_typeGuid)
{ {
@@ -344,18 +341,24 @@ internal class TextureAssetHandler : IImportableAssetHandler
SettingsOffset = AssetMetadata.SIZE, SettingsOffset = AssetMetadata.SIZE,
}; };
// Reserve space for the header, then write settings
targetStream.Seek(0, SeekOrigin.Begin);
AssetMetadata.WriteToStream(targetStream, ref header);
targetStream.Seek(header.SettingsOffset, SeekOrigin.Begin); targetStream.Seek(header.SettingsOffset, SeekOrigin.Begin);
var sizeResult = await settings.WriteToStreamAsync(targetStream, token).ConfigureAwait(false); var sizeResult = await WriteSettingsToStreamAsync(settings, targetStream, token).ConfigureAwait(false);
if (sizeResult.IsFailure) if (sizeResult.IsFailure)
{
return Result.Failure($"Failed to write texture asset settings: {sizeResult.Message}"); return Result.Failure($"Failed to write texture asset settings: {sizeResult.Message}");
}
// Content layout (all little-endian):
// int32 width
// int32 height
// byte isFloat (0 = byte, 1 = float)
// int32 colorComponents (cast of ColorComponents enum)
// byte[] pixelBytes
const int _CONTENT_HEADER_SIZE = 4 + 4 + 1 + 4; // 13 bytes
header.SettingsSize = sizeResult.Value; header.SettingsSize = sizeResult.Value;
header.ContentOffset = header.SettingsOffset + sizeResult.Value; header.ContentOffset = header.SettingsOffset + sizeResult.Value;
header.ContentSize = contentSize; header.ContentSize = _CONTENT_HEADER_SIZE + pixelBytes.Length;
// Write raw image content // Write raw image content
targetStream.Seek(header.ContentOffset, SeekOrigin.Begin); targetStream.Seek(header.ContentOffset, SeekOrigin.Begin);
@@ -367,6 +370,7 @@ internal class TextureAssetHandler : IImportableAssetHandler
BitConverter.TryWriteBytes(contentHeader.AsSpan(4, 4), height); BitConverter.TryWriteBytes(contentHeader.AsSpan(4, 4), height);
contentHeader[8] = isFloat ? (byte)1 : (byte)0; contentHeader[8] = isFloat ? (byte)1 : (byte)0;
BitConverter.TryWriteBytes(contentHeader.AsSpan(9, 4), (int)colorComponents); BitConverter.TryWriteBytes(contentHeader.AsSpan(9, 4), (int)colorComponents);
await targetStream.WriteAsync(contentHeader.AsMemory(0, _CONTENT_HEADER_SIZE), token).ConfigureAwait(false); await targetStream.WriteAsync(contentHeader.AsMemory(0, _CONTENT_HEADER_SIZE), token).ConfigureAwait(false);
} }
finally finally

View File

@@ -22,10 +22,6 @@ internal static unsafe class TextureProcessor
{ {
private const string _TEXTURE_CACHE_SUBFOLDER = "TextureCache"; private const string _TEXTURE_CACHE_SUBFOLDER = "TextureCache";
// -------------------------------------------------------------------------
// Public entry point
// -------------------------------------------------------------------------
/// <summary> /// <summary>
/// Compresses <paramref name="pixelData"/> according to <paramref name="settings"/> /// Compresses <paramref name="pixelData"/> according to <paramref name="settings"/>
/// and writes the result to the texture cache. /// and writes the result to the texture cache.
@@ -69,10 +65,6 @@ internal static unsafe class TextureProcessor
return cachePath; return cachePath;
} }
// -------------------------------------------------------------------------
// NVTT pipeline
// -------------------------------------------------------------------------
private static void RunNvttPipeline( private static void RunNvttPipeline(
string outputPath, string outputPath,
ReadOnlySpan<byte> pixelData, ReadOnlySpan<byte> pixelData,
@@ -110,7 +102,7 @@ internal static unsafe class TextureProcessor
if (settings.Advanced.StretchToPowerOfTwo) if (settings.Advanced.StretchToPowerOfTwo)
{ {
surface.ResizeMakeSquare(maxExtent, surface.ResizeMakeSquare(maxExtent,
NvttRoundMode.NVTT_RoundMode_ToPreviousPowerOfTwo, NvttRoundMode.NVTT_RoundMode_ToNearestPowerOfTwo,
NvttResizeFilter.NVTT_ResizeFilter_Box); NvttResizeFilter.NVTT_ResizeFilter_Box);
} }
else if (surface.Width > maxExtent || surface.Height > maxExtent) else if (surface.Width > maxExtent || surface.Height > maxExtent)
@@ -177,7 +169,7 @@ internal static unsafe class TextureProcessor
} }
// ---- 8. enable CUDA if available --------------------------------------- // ---- 8. enable CUDA if available ---------------------------------------
ctx.SetCudaAcceleration(Ghost.Nvtt.NvttGlobal.IsCudaSupported); ctx.SetCudaAcceleration(NvttGlobal.IsCudaSupported);
// ---- 9. write DDS header ----------------------------------------------- // ---- 9. write DDS header -----------------------------------------------
ctx.OutputHeader(surface, mipmapCount, compOpts, outOpts); ctx.OutputHeader(surface, mipmapCount, compOpts, outOpts);
@@ -185,18 +177,18 @@ internal static unsafe class TextureProcessor
// ---- 10. compress mip chain using a working clone ---------------------- // ---- 10. compress mip chain using a working clone ----------------------
using var mip = surface.Clone(); using var mip = surface.Clone();
for (int level = 0; level < mipmapCount; level++) for (var level = 0; level < mipmapCount; level++)
{ {
// Scale alpha for coverage on each mip (if requested) // Scale alpha for coverage on each mip (if requested)
if (settings.Advanced.ScaleAlphaForMipCoverage && level > 0) if (settings.Advanced.ScaleAlphaForMipCoverage && level > 0)
{ {
float refCoverage = mip.AlphaTestCoverage( var refCoverage = mip.AlphaTestCoverage(
settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f); settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f);
mip.ScaleAlphaToCoverage(refCoverage, mip.ScaleAlphaToCoverage(refCoverage,
settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f); settings.Advanced.ScaleAlphaForMipCoverageThreshold / 255f);
} }
ctx.Compress(mip, face: 0, mipmap: level, compOpts, outOpts); ctx.Compress(mip, 0, level, compOpts, outOpts);
if (level + 1 < mipmapCount) if (level + 1 < mipmapCount)
{ {
@@ -205,53 +197,45 @@ internal static unsafe class TextureProcessor
} }
} }
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
private static NvttFormat SelectFormat(TextureAssetSettings settings) private static NvttFormat SelectFormat(TextureAssetSettings settings)
=> settings.Basic.TextureType switch => settings.Basic.TextureType switch
{ {
TextureType.Normal => NvttFormat.NVTT_Format_BC5, // RG normal map TextureType.Normal => NvttFormat.NVTT_Format_BC5, // RG normal map
TextureType.SingleChannel => NvttFormat.NVTT_Format_BC4, // single channel TextureType.SingleChannel => NvttFormat.NVTT_Format_BC4, // single channel
TextureType.Lightmap => NvttFormat.NVTT_Format_BC6U, // HDR lightmap (unsigned) TextureType.Lightmap => NvttFormat.NVTT_Format_BC6U, // HDR lightmap (unsigned)
_ => NvttFormat.NVTT_Format_BC7, // default colour _ => NvttFormat.NVTT_Format_BC7, // default colour
}; };
private static NvttQuality SelectQuality(TextureCompressionLevel level) private static NvttQuality SelectQuality(TextureCompressionLevel level)
=> level switch => level switch
{ {
TextureCompressionLevel.Low => NvttQuality.NVTT_Quality_Fastest, TextureCompressionLevel.Low => NvttQuality.NVTT_Quality_Fastest,
TextureCompressionLevel.High => NvttQuality.NVTT_Quality_Production, TextureCompressionLevel.High => NvttQuality.NVTT_Quality_Production,
_ => NvttQuality.NVTT_Quality_Normal, _ => NvttQuality.NVTT_Quality_Normal,
}; };
private static NvttMipmapFilter SelectMipmapFilter(MipmapFilter filter) private static NvttMipmapFilter SelectMipmapFilter(MipmapFilter filter)
=> filter switch => filter switch
{ {
MipmapFilter.Box => NvttMipmapFilter.NVTT_MipmapFilter_Box, MipmapFilter.Box => NvttMipmapFilter.NVTT_MipmapFilter_Box,
MipmapFilter.Triangle => NvttMipmapFilter.NVTT_MipmapFilter_Triangle, MipmapFilter.Triangle => NvttMipmapFilter.NVTT_MipmapFilter_Triangle,
MipmapFilter.MitchellNetravali => NvttMipmapFilter.NVTT_MipmapFilter_Mitchell, MipmapFilter.MitchellNetravali => NvttMipmapFilter.NVTT_MipmapFilter_Mitchell,
_ => NvttMipmapFilter.NVTT_MipmapFilter_Kaiser, _ => NvttMipmapFilter.NVTT_MipmapFilter_Kaiser,
}; };
/// <summary>
/// Produces a stable 64-bit hash of the settings structs so the cache file
/// name changes whenever any setting changes.
/// </summary>
private static ulong ComputeSettingsHash(TextureAssetSettings s) private static ulong ComputeSettingsHash(TextureAssetSettings s)
{ {
var basicSize = Unsafe.SizeOf<TextureAssetSettings.BasicSettings>(); var basicSize = Unsafe.SizeOf<TextureAssetSettings.BasicSettings>();
var advancedSize = Unsafe.SizeOf<TextureAssetSettings.AdvancedSettings>(); var advancedSize = Unsafe.SizeOf<TextureAssetSettings.AdvancedSettings>();
var samplerSize = Unsafe.SizeOf<TextureAssetSettings.SamplerSettings>(); var samplerSize = Unsafe.SizeOf<TextureAssetSettings.SamplerSettings>();
var total = basicSize + advancedSize + samplerSize; var total = basicSize + advancedSize + samplerSize;
Span<byte> buf = stackalloc byte[total]; Span<byte> buf = stackalloc byte[total];
var basic = s.Basic; var basic = s.Basic;
var advanced = s.Advanced; var advanced = s.Advanced;
var sampler = s.Sampler; var sampler = s.Sampler;
MemoryMarshal.Write(buf, in basic); MemoryMarshal.Write(buf, in basic);
MemoryMarshal.Write(buf.Slice(basicSize), in advanced); MemoryMarshal.Write(buf.Slice(basicSize), in advanced);
MemoryMarshal.Write(buf.Slice(basicSize + advancedSize), in sampler); MemoryMarshal.Write(buf.Slice(basicSize + advancedSize), in sampler);
return XxHash64.HashToUInt64(buf); return XxHash64.HashToUInt64(buf);

View File

@@ -37,9 +37,9 @@ internal sealed partial class ScenePage : NavigationTabPage
//private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e) //private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
//{ //{
// if (e.NewSize.Width > 8.0 && e.NewSize.Height > 8.0) // if (e.NewSize.ActualWidth > 8.0 && e.NewSize.ActualHeight > 8.0)
// { // {
// _renderView?.RequestResize((uint)e.NewSize.Width, (uint)e.NewSize.Height); // _renderView?.RequestResize((uint)e.NewSize.ActualWidth, (uint)e.NewSize.ActualHeight);
// } // }
//} //}
} }

View File

@@ -5,6 +5,7 @@
<Platform Name="x86" /> <Platform Name="x86" />
</Configurations> </Configurations>
<Folder Name="/Editor/"> <Folder Name="/Editor/">
<Project Path="Editor/Ghost.DSL/Ghost.DSL.csproj" />
<Project Path="Editor/Ghost.Editor.Core/Ghost.Editor.Core.csproj" /> <Project Path="Editor/Ghost.Editor.Core/Ghost.Editor.Core.csproj" />
<Project Path="Editor/Ghost.Editor/Ghost.Editor.csproj"> <Project Path="Editor/Ghost.Editor/Ghost.Editor.csproj">
<Platform Solution="*|ARM64" Project="ARM64" /> <Platform Solution="*|ARM64" Project="ARM64" />
@@ -12,7 +13,6 @@
<Platform Solution="*|x86" Project="x86" /> <Platform Solution="*|x86" Project="x86" />
<Deploy /> <Deploy />
</Project> </Project>
<Project Path="Editor/Ghost.DSL/Ghost.DSL.csproj" />
</Folder> </Folder>
<Folder Name="/ThridParty/"> <Folder Name="/ThridParty/">
<Project Path="ThridParty/Ghost.FMOD/Ghost.FMOD.csproj" /> <Project Path="ThridParty/Ghost.FMOD/Ghost.FMOD.csproj" />
@@ -24,6 +24,8 @@
<Project Path="Runtime/Ghost.Engine/Ghost.Engine.csproj" /> <Project Path="Runtime/Ghost.Engine/Ghost.Engine.csproj" />
<Project Path="Runtime/Ghost.Entities/Ghost.Entities.csproj" /> <Project Path="Runtime/Ghost.Entities/Ghost.Entities.csproj" />
<Project Path="Runtime/Ghost.Generator/Ghost.Generator.csproj" /> <Project Path="Runtime/Ghost.Generator/Ghost.Generator.csproj" />
<Project Path="Runtime/Ghost.Graphics.D3D12/Ghost.Graphics.D3D12.csproj" Id="e56a0674-beab-414f-8b42-01191a0238fc" />
<Project Path="Runtime/Ghost.Graphics.RHI/Ghost.Graphics.RHI.csproj" Id="d6831a89-ea86-4aef-9879-2bdd9b519d1e" />
<Project Path="Runtime/Ghost.Graphics/Ghost.Graphics.csproj" /> <Project Path="Runtime/Ghost.Graphics/Ghost.Graphics.csproj" />
</Folder> </Folder>
<Folder Name="/Test/"> <Folder Name="/Test/">

View File

@@ -1,67 +0,0 @@
using System.Runtime.CompilerServices;
namespace Ghost.Core;
public readonly struct TypeHandle
{
public readonly IntPtr Value
{
get;
}
private TypeHandle(IntPtr value)
{
Value = value;
}
/// <summary>
/// Gets the space handle for the specified space.
/// </summary>
/// <param name="type">The space to get the handle for.</param>
/// <returns>The space handle as a nint.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TypeHandle Get(Type type) => new TypeHandle(type.TypeHandle.Value);
/// <summary>
/// Gets the space handle for the specified space.
/// </summary>
/// <typeparam name="T">The space to get the handle for.</typeparam>
/// <returns>The space handle as a nint.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TypeHandle Get<T>() => Get(typeof(T));
/// <summary>
/// Converts a TypeHandle to a Type.
/// </summary>
/// <param name="handle">The TypeHandle to convert.</param>
/// <returns>The corresponding Type.</returns>
public Type? ToType()
{
return Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(Value));
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public static implicit operator TypeHandle(IntPtr value)
{
return new TypeHandle(value);
}
public static implicit operator IntPtr(TypeHandle handle)
{
return handle.Value;
}
public static implicit operator TypeHandle(Type type)
{
return Get(type);
}
public static implicit operator Type?(TypeHandle handle)
{
return handle.ToType();
}
}

View File

@@ -10,80 +10,6 @@ namespace Ghost.Core.Utilities;
[SupportedOSPlatform("windows10.0.19041.0")] [SupportedOSPlatform("windows10.0.19041.0")]
internal static unsafe partial class Win32Utility internal static unsafe partial class Win32Utility
{ {
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly ref struct IID_PPV
{
public readonly Guid* iid;
public readonly void** ppv;
public IID_PPV(Guid* iid, void** ppv)
{
this.iid = iid;
this.ppv = ppv;
}
public void Deconstruct(out Guid* iid, out void** ppv)
{
iid = this.iid;
ppv = this.ppv;
}
}
public static Guid* IID_NULL
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID.IID_NULL));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IID_PPV IID_PPV_ARGS<T>(ComPtr<T>* comPtr)
where T : unmanaged, IUnknown.Interface
{
return new IID_PPV(Windows.__uuidof<T>(), (void**)comPtr);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Attach<T>(ref this UniquePtr<T> uPtr, T* other)
where T : unmanaged, IUnknown.Interface
{
var ptr = uPtr.Get();
if (ptr != null)
{
var refCount = ptr->Release();
Debug.Assert((refCount != 0) || (ptr != other));
}
uPtr = new UniquePtr<T>(other);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Dispose<T>(ref this UniquePtr<T> uPtr)
where T : unmanaged, IUnknown.Interface
{
var ptr = uPtr.Detach();
if (ptr != null)
{
ptr->Release();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result ToResult(this HRESULT hr, [CallerArgumentExpression(nameof(hr))] string? op = null)
{
if (hr.SUCCEEDED)
{
return Result.Success();
}
return Result.Failure($"{op} failed with code {hr}");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void** ReleaseAndGetVoidAddressOf<T>(ref this ComPtr<T> comPtr)
where T : unmanaged, IUnknown.Interface
{
return (void**)comPtr.ReleaseAndGetAddressOf();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ComPtr<T> Move<T>(ref this ComPtr<T> comPtr) public static ComPtr<T> Move<T>(ref this ComPtr<T> comPtr)

View File

@@ -1,5 +1,6 @@
using Ghost.Entities; using Ghost.Entities;
using Ghost.Graphics; using Ghost.Graphics;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Jobs; using Misaki.HighPerformance.Jobs;
namespace Ghost.Engine; namespace Ghost.Engine;

View File

@@ -208,10 +208,10 @@ public class ComponentManager : IDisposable
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Identifier<EntityQuery> CreateEntityQuery(EntityQueryMask mask, int maskHash) internal Identifier<EntityQuery> CreateEntityQuery(ref readonly EntityQueryMask mask, int maskHash)
{ {
var queryID = new Identifier<EntityQuery>(_entityQueries.Count); var queryID = new Identifier<EntityQuery>(_entityQueries.Count);
_entityQueries.Add(new EntityQuery(queryID, _world.ID, mask)); _entityQueries.Add(new EntityQuery(queryID, _world.ID, in mask));
_querieLookup.Add(maskHash, queryID); _querieLookup.Add(maskHash, queryID);
ref var query = ref _entityQueries[queryID.Value]; ref var query = ref _entityQueries[queryID.Value];

View File

@@ -1,4 +1,3 @@
using Ghost.Core;
using Misaki.HighPerformance.Jobs; using Misaki.HighPerformance.Jobs;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -64,7 +63,7 @@ public unsafe partial struct EntityQuery
{ {
ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); ref var arch = ref world.ComponentManager.GetArchetypeReference(archID);
for (int i = 0; i < arch.ChunkCount; i++) for (var i = 0; i < arch.ChunkCount; i++)
{ {
var pChunk = (Chunk*)arch._chunks.GetUnsafePtr() + i; var pChunk = (Chunk*)arch._chunks.GetUnsafePtr() + i;

View File

@@ -340,7 +340,7 @@ public unsafe partial struct EntityQuery : IDisposable
private readonly Identifier<EntityQuery> _id; private readonly Identifier<EntityQuery> _id;
private readonly Identifier<World> _worldID; private readonly Identifier<World> _worldID;
internal EntityQuery(Identifier<EntityQuery> id, Identifier<World> worldID, EntityQueryMask mask) internal EntityQuery(Identifier<EntityQuery> id, Identifier<World> worldID, ref readonly EntityQueryMask mask)
{ {
_id = id; _id = id;
_worldID = worldID; _worldID = worldID;
@@ -632,7 +632,7 @@ public ref partial struct QueryBuilder
} }
// NOTE: We do not dispose the mask here, as it is now owned by the EntityQuery. // NOTE: We do not dispose the mask here, as it is now owned by the EntityQuery.
queryID = world.ComponentManager.CreateEntityQuery(mask, maskHash); queryID = world.ComponentManager.CreateEntityQuery(in mask, maskHash);
Return: Return:
Dispose(); Dispose();

View File

@@ -0,0 +1,8 @@
global using static TerraFX.Interop.DirectX.D3D12;
global using static TerraFX.Interop.DirectX.DirectX;
global using static TerraFX.Interop.DirectX.DXGI;
global using static TerraFX.Interop.Windows.Windows;
using System.Runtime.Versioning;
[assembly: SupportedOSPlatform("windows10.0.19041.0")]

View File

@@ -1,6 +1,5 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Utilities; using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;

View File

@@ -1,4 +1,5 @@
using Ghost.Core.Utilities; using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;

View File

@@ -1,4 +1,5 @@
using Ghost.Core.Utilities; using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;

View File

@@ -1,4 +1,3 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
@@ -18,9 +17,11 @@ internal unsafe class D3D12DescriptorHeap : IDisposable
private UniquePtr<ID3D12DescriptorHeap> _heap; private UniquePtr<ID3D12DescriptorHeap> _heap;
private UniquePtr<ID3D12DescriptorHeap> _shaderVisibleHeap; private UniquePtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandle; private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandle;
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandleShaderVisible; private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandleShaderVisible;
private D3D12_GPU_DESCRIPTOR_HANDLE _startGpuHandleShaderVisible; private D3D12_GPU_DESCRIPTOR_HANDLE _startGpuHandleShaderVisible;
private int _searchStart; private int _searchStart;
private UnsafeBitSet _allocatedDescriptors; private UnsafeBitSet _allocatedDescriptors;

View File

@@ -3,22 +3,31 @@
#endif #endif
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.RHI;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
public static class D3D12GraphicsEngineFactory
{
public static IGraphicsEngine Create(IRenderSystem renderSystem)
{
return new D3D12GraphicsEngine(renderSystem);
}
}
internal class D3D12GraphicsEngine : IGraphicsEngine internal class D3D12GraphicsEngine : IGraphicsEngine
{ {
private GCHandle _thisHandle;
private readonly IRenderSystem _renderSystem; private readonly IRenderSystem _renderSystem;
#if ENABLE_DEBUG #if ENABLE_DEBUG
private readonly D3D12DebugLayer _debugLayer; private readonly D3D12DebugLayer _debugLayer;
#endif #endif
private readonly D3D12RenderDevice _device; private readonly D3D12RenderDevice _device;
private readonly DxcShaderCompiler _shaderCompiler; private readonly DxcShaderCompiler _shaderCompiler;
private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12DescriptorAllocator _descriptorAllocator;
@@ -71,7 +80,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
{ {
ThrowIfDisposed(); ThrowIfDisposed();
var renderer = new D3D12Renderer(this, _resourceDatabase); var renderer = new D3D12Renderer(this);
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Add(renderer)); ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Add(renderer));
return renderer; return renderer;
} }
@@ -157,6 +166,8 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
_debugLayer.Dispose(); _debugLayer.Dispose();
#endif #endif
_thisHandle.Free();
_disposed = true; _disposed = true;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }

View File

@@ -1,8 +1,5 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Core.Utilities;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;

View File

@@ -1,4 +1,4 @@
using Ghost.Core.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
@@ -78,7 +78,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
pAdapter->GetDesc1(&desc); pAdapter->GetDesc1(&desc);
// Don't select the Basic Render Driver adapter. // Don't select the Basic Render Driver adapter.
if (desc.Flags.HasFlag(DXGI_ADAPTER_FLAG_SOFTWARE)) if ((desc.Flags & (uint)DXGI_ADAPTER_FLAG_SOFTWARE) != 0)
{ {
goto NEXT_ITERATION; goto NEXT_ITERATION;
} }
@@ -106,7 +106,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
FeatureSupport support = FeatureSupport.None; var support = FeatureSupport.None;
D3D12_FEATURE_DATA_D3D12_OPTIONS options = default; D3D12_FEATURE_DATA_D3D12_OPTIONS options = default;
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED) if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)

View File

@@ -0,0 +1,124 @@
using Ghost.Core;
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of the renderer interface using RHI abstractions
/// </summary>
internal class D3D12Renderer : IRenderer
{
private readonly D3D12GraphicsEngine _graphicsEngine;
private readonly ICommandBuffer _commandBuffer;
private bool _disposed;
public IRenderOutput? RenderOutput
{
get; set;
}
public Func<RenderContext, Error>? RenderFunc
{
get; set;
}
public D3D12Renderer(D3D12GraphicsEngine graphicsEngine)
{
_graphicsEngine = graphicsEngine;
_commandBuffer = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
}
~D3D12Renderer()
{
Dispose();
}
public Result Render(ICommandAllocator commandAllocator)
{
if (RenderFunc is null)
{
return Result.Success(); // No render function set, skip rendering.
}
if (RenderOutput is null)
{
return Result.Failure("Render target strategy is not set.");
}
var target = RenderOutput.GetRenderTarget();
if (target.IsInvalid)
{
return Result.Failure("Render target is invalid.");
}
_commandBuffer.Begin(commandAllocator);
RenderOutput.BeginRender(_commandBuffer);
var ctx = new RenderContext();
var error = RenderFunc.Invoke(ctx);
if (error != Error.None)
{
_commandBuffer.End();
return Result.Failure(error);
}
RenderOutput.EndRender(_commandBuffer);
var r = _commandBuffer.End();
if (r.IsFailure)
{
return r;
}
_graphicsEngine.Device.GraphicsQueue.Submit(_commandBuffer);
RenderOutput.Present();
return Result.Success();
}
//// TODO: A proper render graph integration.
//private Error RenderScene(Handle<Texture> target, ViewportDesc viewport, RectDesc rect)
//{
// // NOTE: Testing only.
// var ctx = new RenderingContext(_graphicsEngine, _commandBuffer);
// if (_frameIndex == 0)
// {
// _pass.Initialize(ref ctx);
// }
// //_commandBuffer.BeginRenderPass(rtDesc, depthDesc, false);
// _commandBuffer.SetViewport(viewport);
// _commandBuffer.SetScissorRect(rect);
// _renderGraph.Reset();
// var backBuffer = _renderGraph.ImportTexture(target, "Back Buffer");
// _pass.Build(_renderGraph, backBuffer);
// // Create view state from viewport
// var viewState = new ViewState((uint)viewport.Width, (uint)viewport.Height);
// // Compile with view state
// _renderGraph.Compile(in viewState);
// _renderGraph.Execute(_commandBuffer);
// //_commandBuffer.EndRenderPass();
// _frameIndex++;
// return Error.None;
//}
public void Dispose()
{
if (_disposed)
{
return;
}
_commandBuffer.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -1,7 +1,6 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Core.Utilities; using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
@@ -866,71 +865,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
return _resourceDatabase.CreateSampler(in desc, samplerDescriptor.Value); return _resourceDatabase.CreateSampler(in desc, samplerDescriptor.Value);
} }
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var vertexBufferDesc = new BufferDesc
{
Size = (uint)(vertices.Count * sizeof(Vertex)),
Stride = (uint)sizeof(Vertex),
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default,
};
var indexBufferDesc = new BufferDesc
{
Size = (uint)(indices.Count * sizeof(uint)),
Stride = sizeof(uint),
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default,
};
var objectBufferDesc = new BufferDesc
{
Size = (uint)sizeof(PerObjectData),
Stride = (uint)sizeof(PerObjectData),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Default,
};
var vertexBuffer = CreateBuffer(in vertexBufferDesc, "VertexBuffer");
var indexBuffer = CreateBuffer(in indexBufferDesc, "IndexBuffer");
var objectBuffer = CreateBuffer(in objectBufferDesc, "ObjectBuffer");
var data = new Mesh
{
Vertices = vertices,
Indices = indices,
VertexBuffer = vertexBuffer,
IndexBuffer = indexBuffer,
ObjectDataBuffer = objectBuffer,
};
return _resourceDatabase.AddMesh(in data);
}
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var material = new Material();
if (material.SetShader(shader, this, _resourceDatabase) != Error.None)
{
return Handle<Material>.Invalid;
}
return _resourceDatabase.AddMaterial(in material);
}
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var shader = new Shader(descriptor);
return _resourceDatabase.AddShader(shader);
}
public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Collections; using Misaki.HighPerformance.Collections;
@@ -102,15 +101,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase
private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12DescriptorAllocator _descriptorAllocator;
private UnsafeSlotMap<ResourceRecord> _resources; private UnsafeSlotMap<ResourceRecord> _resources;
private UnsafeHashMap<SamplerDesc, Identifier<Sampler>> _samplers;
#if DEBUG || GHOST_EDITOR #if DEBUG || GHOST_EDITOR
private readonly Dictionary<Handle<GPUResource>, string> _resourceName; private readonly Dictionary<Handle<GPUResource>, string> _resourceName;
#endif #endif
private UnsafeHashMap<SamplerDesc, Identifier<Sampler>> _samplers;
private UnsafeSlotMap<Mesh> _meshes;
private UnsafeSlotMap<Material> _materials;
private readonly DynamicArray<Shader> _shaders; // TODO: Use SlotMap?
private UnsafeQueue<ReleaseEntry> _releaseQueue; private UnsafeQueue<ReleaseEntry> _releaseQueue;
private bool _disposed; private bool _disposed;
@@ -121,13 +116,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase
_descriptorAllocator = descriptorAllocator; _descriptorAllocator = descriptorAllocator;
_resources = new UnsafeSlotMap<ResourceRecord>(64, Allocator.Persistent, AllocationOption.Clear); _resources = new UnsafeSlotMap<ResourceRecord>(64, Allocator.Persistent, AllocationOption.Clear);
_samplers = new UnsafeHashMap<SamplerDesc, Identifier<Sampler>>(32, Allocator.Persistent);
#if DEBUG || GHOST_EDITOR #if DEBUG || GHOST_EDITOR
_resourceName = new Dictionary<Handle<GPUResource>, string>(64); _resourceName = new Dictionary<Handle<GPUResource>, string>(64);
#endif #endif
_samplers = new UnsafeHashMap<SamplerDesc, Identifier<Sampler>>(32, Allocator.Persistent);
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent, AllocationOption.Clear);
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent, AllocationOption.Clear);
_shaders = new DynamicArray<Shader>(16);
_releaseQueue = new UnsafeQueue<ReleaseEntry>(32, Allocator.Persistent); _releaseQueue = new UnsafeQueue<ReleaseEntry>(32, Allocator.Persistent);
} }
@@ -137,12 +129,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
Dispose(); Dispose();
} }
private void ReleaseResource<T>(T resource)
where T : IResourceReleasable
{
resource.ReleaseResource(this);
}
internal unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceBarrierData initialBarrierData, ResourceViewGroup viewGroup, string? name = null) internal unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceBarrierData initialBarrierData, ResourceViewGroup viewGroup, string? name = null)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
@@ -356,122 +342,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
_descriptorAllocator.Release(new Identifier<SamplerDescriptor>(id.Value)); _descriptorAllocator.Release(new Identifier<SamplerDescriptor>(id.Value));
} }
public Handle<Mesh> AddMesh(ref readonly Mesh mesh)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = _meshes.Add(mesh, out var generation);
return new Handle<Mesh>(id, generation);
}
public bool HasMesh(Handle<Mesh> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _meshes.Contains(handle.ID, handle.Generation);
}
public RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle)
{
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return Error.NotFound;
}
return RefResult<Mesh, Error>.Success(ref mesh);
}
public void ReleaseMesh(Handle<Mesh> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return;
}
ReleaseResource(mesh);
_meshes.Remove(handle.ID, handle.Generation);
}
public Handle<Material> AddMaterial(ref readonly Material material)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = _materials.Add(material, out var generation);
return new Handle<Material>(id, generation);
}
public bool HasMaterial(Handle<Material> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _materials.Contains(handle.ID, handle.Generation);
}
public RefResult<Material, Error> GetMaterialReference(Handle<Material> handle)
{
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return Error.NotFound;
}
return RefResult<Material, Error>.Success(ref material);
}
public void ReleaseMaterial(Handle<Material> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return;
}
ReleaseResource(material);
_materials.Remove(handle.ID, handle.Generation);
}
public Identifier<Shader> AddShader(Shader shader)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = _shaders.Count;
_shaders.Add(shader);
return new Identifier<Shader>(id);
}
public bool HasShader(Identifier<Shader> id)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return id.Value >= 0 && id.Value < _shaders.Count;
}
public RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id)
{
if (!HasShader(id))
{
return Error.NotFound;
}
return RefResult<Shader, Error>.Success(ref _shaders[id.Value]);
}
public void ReleaseShader(Identifier<Shader> id)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (!HasShader(id))
{
return;
}
ref var shader = ref _shaders[id.Value]!;
ReleaseResource(shader);
}
public void EndFrame() public void EndFrame()
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
@@ -494,21 +364,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var mesh in _meshes)
{
ReleaseResource(mesh);
}
foreach (var material in _materials)
{
ReleaseResource(material);
}
foreach (var shader in _shaders)
{
ReleaseResource(shader);
}
foreach (ref var record in _resources) foreach (ref var record in _resources)
{ {
record.Release(_descriptorAllocator); record.Release(_descriptorAllocator);
@@ -524,8 +379,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
_resources.Dispose(); _resources.Dispose();
_samplers.Dispose(); _samplers.Dispose();
_meshes.Dispose();
_materials.Dispose();
_releaseQueue.Dispose(); _releaseQueue.Dispose();
_disposed = true; _disposed = true;

View File

@@ -1,7 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;

View File

@@ -1,7 +1,6 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Core.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
@@ -229,41 +228,41 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
switch (bindDesc.Type) switch (bindDesc.Type)
{ {
case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER: case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER:
{
var cbuffer = pReflection->GetConstantBufferByName(bindDesc.Name);
D3D12_SHADER_BUFFER_DESC cbufferDesc;
ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc));
var variables = new List<CBufferPropertyInfo>((int)cbufferDesc.Variables);
// Now we iterate all variables for *every* cbuffer, not just b3
for (uint j = 0; j < cbufferDesc.Variables; j++)
{ {
var cbuffer = pReflection->GetConstantBufferByName(bindDesc.Name); var variable = cbuffer->GetVariableByIndex(j);
D3D12_SHADER_BUFFER_DESC cbufferDesc; D3D12_SHADER_VARIABLE_DESC varDesc;
ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc)); variable->GetDesc(&varDesc);
var variables = new List<CBufferPropertyInfo>((int)cbufferDesc.Variables); var variableName = Marshal.PtrToStringUTF8((IntPtr)varDesc.Name);
if (variableName == null)
// Now we iterate all variables for *every* cbuffer, not just b3
for (uint j = 0; j < cbufferDesc.Variables; j++)
{ {
var variable = cbuffer->GetVariableByIndex(j); continue;
D3D12_SHADER_VARIABLE_DESC varDesc;
variable->GetDesc(&varDesc);
var variableName = Marshal.PtrToStringUTF8((IntPtr)varDesc.Name);
if (variableName == null)
{
continue;
}
variables.Add(new CBufferPropertyInfo
{
Name = variableName,
StartOffset = varDesc.StartOffset,
Size = varDesc.Size
});
} }
info.Size = cbufferDesc.Size; variables.Add(new CBufferPropertyInfo
info.Properties = variables; {
Name = variableName,
break; StartOffset = varDesc.StartOffset,
Size = varDesc.Size
});
} }
// NOTE: Currently we do not support resource bindings yet, everything access through bindless heaps. info.Size = cbufferDesc.Size;
info.Properties = variables;
break;
}
// NOTE: Currently we do not support resource bindings yet, everything access through bindless heaps.
} }
reflectionData.ResourcesBindings.Add(info); reflectionData.ResourcesBindings.Add(info);
@@ -318,8 +317,7 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
Encoding = DXC_CP_UTF8 Encoding = DXC_CP_UTF8
}; };
var (iid, ppv) = Win32Utility.IID_PPV_ARGS(&result); ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler, __uuidof(result.Get()), (void**)&result));
ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler, iid, ppv));
// Check compilation result // Check compilation result
HRESULT hrStatus; HRESULT hrStatus;
@@ -351,9 +349,7 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler
if (config.options.HasFlag(CompilerOption.KeepReflections)) if (config.options.HasFlag(CompilerOption.KeepReflections))
{ {
using ComPtr<IDxcBlob> reflection = default; using ComPtr<IDxcBlob> reflection = default;
(iid, ppv) = Win32Utility.IID_PPV_ARGS(&reflection); if (result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof(reflection.Get()), (void**)&reflection, null).SUCCEEDED)
if (result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, iid, ppv, null).SUCCEEDED)
{ {
reflectionData = PerformDXCReflection(reflection).GetValueOrDefault(); reflectionData = PerformDXCReflection(reflection).GetValueOrDefault();
} }

View File

@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PublishAot>true</PublishAot>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<Content Include="runtimes\win-x64\native\dxcompiler.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="runtimes\win-x64\native\dxil.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="TerraFX.Interop.D3D12MemoryAllocator" Version="3.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
<ProjectReference Include="..\Ghost.Graphics.RHI\Ghost.Graphics.RHI.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,9 +1,8 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.Contracts; namespace Ghost.Graphics.D3D12;
public unsafe readonly struct ISwapChainPanelNative : ISwapChainPanelNative.Interface, IDisposable public readonly unsafe struct ISwapChainPanelNative : ISwapChainPanelNative.Interface, IDisposable
{ {
[ComImport] [ComImport]
[Guid("63aad0b8-7c24-40ff-85a8-640d944cc325")] [Guid("63aad0b8-7c24-40ff-85a8-640d944cc325")]

View File

@@ -0,0 +1,27 @@
using Ghost.Graphics.RHI;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using static TerraFX.Interop.DirectX.D3D12_INPUT_CLASSIFICATION;
using static TerraFX.Interop.DirectX.DXGI_FORMAT;
namespace Ghost.Graphics.D3D12.Utilities;
internal static unsafe class D3D12PipelineResource
{
private static readonly D3D12_INPUT_ELEMENT_DESC[] s_inputElementDescs = [
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Position.GetUnsafePtr(), SemanticIndex = 0u, Format = DXGI_FORMAT_R32G32B32A32_FLOAT, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Normal.GetUnsafePtr(), SemanticIndex = 0u, Format = DXGI_FORMAT_R32G32B32A32_FLOAT, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Tangent.GetUnsafePtr(), SemanticIndex = 0u, Format = DXGI_FORMAT_R32G32B32A32_FLOAT, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Uv.GetUnsafePtr(), SemanticIndex = 0u, Format = DXGI_FORMAT_R32G32B32A32_FLOAT, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Color.GetUnsafePtr(), SemanticIndex = 0u, Format = DXGI_FORMAT_R32G32B32A32_FLOAT, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
];
public const DXGI_FORMAT SWAP_CHAIN_BACK_BUFFER_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM;
public static D3D12_INPUT_LAYOUT_DESC InputLayoutDescription => new()
{
pInputElementDescs = (D3D12_INPUT_ELEMENT_DESC*)Unsafe.AsPointer(ref s_inputElementDescs[0]),
NumElements = (uint)s_inputElementDescs.Length
};
}

View File

@@ -1,7 +1,12 @@
using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using static TerraFX.Aliases.D3D12_Alias; using static TerraFX.Aliases.D3D12_Alias;
using static TerraFX.Aliases.DXGI_Alias; using static TerraFX.Aliases.DXGI_Alias;
@@ -9,6 +14,82 @@ namespace Ghost.Graphics.D3D12.Utilities;
internal static unsafe class D3D12Utility internal static unsafe class D3D12Utility
{ {
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly ref struct IID_PPV
{
public readonly Guid* iid;
public readonly void** ppv;
public IID_PPV(Guid* iid, void** ppv)
{
this.iid = iid;
this.ppv = ppv;
}
public void Deconstruct(out Guid* iid, out void** ppv)
{
iid = this.iid;
ppv = this.ppv;
}
}
public static Guid* IID_NULL
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID.IID_NULL));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IID_PPV IID_PPV_ARGS<T>(ComPtr<T>* comPtr)
where T : unmanaged, IUnknown.Interface
{
return new IID_PPV(__uuidof<T>(), (void**)comPtr);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Attach<T>(ref this UniquePtr<T> uPtr, T* other)
where T : unmanaged, IUnknown.Interface
{
var ptr = uPtr.Get();
if (ptr != null)
{
var refCount = ptr->Release();
Debug.Assert((refCount != 0) || (ptr != other));
}
uPtr = new UniquePtr<T>(other);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Dispose<T>(ref this UniquePtr<T> uPtr)
where T : unmanaged, IUnknown.Interface
{
var ptr = uPtr.Detach();
if (ptr != null)
{
var refCount = ptr->Release();
Debug.Assert((refCount != 0));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result ToResult(this HRESULT hr, [CallerArgumentExpression(nameof(hr))] string? op = null)
{
if (hr.SUCCEEDED)
{
return Result.Success();
}
return Result.Failure($"{op} failed with code {hr}");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void** ReleaseAndGetVoidAddressOf<T>(ref this ComPtr<T> comPtr)
where T : unmanaged, IUnknown.Interface
{
return (void**)comPtr.ReleaseAndGetAddressOf();
}
public static void SetName<T>(ref this T obj, ReadOnlySpan<char> name) public static void SetName<T>(ref this T obj, ReadOnlySpan<char> name)
where T : unmanaged, ID3D12Object.Interface where T : unmanaged, ID3D12Object.Interface
{ {

View File

@@ -1,16 +1,178 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Graphics.Core; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
/// <summary>
/// Represents a color with 4 bytes components.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 4)]
public struct Color32 : IEquatable<Color32>
{
public byte r;
public byte g;
public byte b;
public byte a;
public Color32(byte r, byte g, byte b, byte a)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public Color32(Color color)
: this(color.R, color.G, color.B, color.A)
{
}
public Color32(Color128 color128)
: this((byte)(color128.r * 255.0f), (byte)(color128.g * 255.0f), (byte)(color128.b * 255.0f), (byte)(color128.a * 255.0f))
{
}
public Color32(float4 v)
: this((byte)(v.x * 255.0f), (byte)(v.y * 255.0f), (byte)(v.z * 255.0f), (byte)(v.w * 255.0f))
{
}
public readonly bool Equals(Color32 other)
{
return r == other.r && g == other.g && b == other.b && a == other.a;
}
public override readonly bool Equals(object? obj)
{
return obj is Color32 color && Equals(color);
}
public override readonly int GetHashCode()
{
return HashCode.Combine(r, g, b, a);
}
public static bool operator ==(Color32 left, Color32 right)
{
return left.Equals(right);
}
public static bool operator !=(Color32 left, Color32 right)
{
return !(left == right);
}
}
/// <summary>
/// Represents a color with 16 bytes components.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct Color128 : IEquatable<Color128>
{
public float r;
public float g;
public float b;
public float a;
public Color128(float r, float g, float b, float a)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public Color128(Color color)
: this(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f)
{
}
public Color128(Color32 color32)
: this(color32.r / 255.0f, color32.g / 255.0f, color32.b / 255.0f, color32.a / 255.0f)
{
}
public Color128(float4 v)
: this(v.x, v.y, v.z, v.w)
{
}
public readonly bool Equals(Color128 other)
{
return r.Equals(other.r) && g.Equals(other.g) && b.Equals(other.b) && a.Equals(other.a);
}
public override readonly bool Equals(object? obj)
{
return obj is Color128 color && Equals(color);
}
public readonly override int GetHashCode()
{
return HashCode.Combine(r, g, b, a);
}
public static bool operator ==(Color128 left, Color128 right)
{
return left.Equals(right);
}
public static bool operator !=(Color128 left, Color128 right)
{
return !(left == right);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
public static class Semantic
{
public const int COUNT = 5;
public static readonly FixedText32 Position = new("POSITION");
public static readonly FixedText32 Normal = new("NORMAL");
public static readonly FixedText32 Tangent = new("TANGENT");
public static readonly FixedText32 Uv = new("TEXCOORD");
public static readonly FixedText32 Color = new("COLOR");
}
public float4 position;
public float4 normal;
public float4 tangent;
public float4 uv;
public Color128 color;
}
public readonly struct ShaderVariant; public readonly struct ShaderVariant;
public readonly struct GraphicsPipeline; public readonly struct GraphicsPipeline;
public readonly struct ShaderPass
{
public Key64<ShaderPass> Key
{
get; init;
}
public PipelineState DefaultState
{
get; init;
}
public LocalKeywordSet KeywordIDs
{
get; init;
}
}
public readonly struct PassPipelineHash : IEquatable<PassPipelineHash> public readonly struct PassPipelineHash : IEquatable<PassPipelineHash>
{ {
public readonly UInt128 value; public readonly UInt128 value;
@@ -431,31 +593,23 @@ public struct ResourceDesc
get; init; get; init;
} }
public TextureDesc TextureDescription [UnscopedRef]
public ref TextureDesc TextureDescription
{ {
readonly get get
{ {
Debug.Assert(Type == ResourceType.Texture); Debug.Assert(Type == ResourceType.Texture);
return _desc.textureDescription; return ref _desc.textureDescription;
}
set
{
Debug.Assert(Type == ResourceType.Texture);
_desc.textureDescription = value;
} }
} }
public BufferDesc BufferDescription [UnscopedRef]
public ref BufferDesc BufferDescription
{ {
readonly get get
{ {
Debug.Assert(Type == ResourceType.Buffer); Debug.Assert(Type == ResourceType.Buffer);
return _desc.bufferDescription; return ref _desc.bufferDescription;
}
set
{
Debug.Assert(Type == ResourceType.Buffer);
_desc.bufferDescription = value;
} }
} }

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Contracts;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
@@ -31,15 +30,15 @@ public interface IGraphicsEngine : IDisposable
} }
/// <summary> /// <summary>
/// Creates a new instance of an object that implements the IRenderer interface. /// Creates a new instance of a renderer for drawing graphical content.
/// </summary> /// </summary>
/// <returns>An object that provides rendering functionality through the IRenderer interface.</returns> /// <returns>An object that implements the IRenderer interface, which can be used to render graphics.</returns>
IRenderer CreateRenderer(); IRenderer CreateRenderer();
/// <summary> /// <summary>
/// Removes the specified renderer from the collection of active renderers. /// Removes the specified renderer from the collection of active renderers.
/// </summary> /// </summary>
/// <param name="renderer">The renderer instance to remove. Cannot be null.</param> /// <param name="renderer">The renderer instance to remove.</param>
void RemoveRenderer(IRenderer renderer); void RemoveRenderer(IRenderer renderer);
/// <summary> /// <summary>

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Contracts;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;

View File

@@ -1,8 +1,6 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.Contracts; namespace Ghost.Graphics.RHI;
public interface IRenderOutput public interface IRenderOutput
{ {

View File

@@ -0,0 +1,47 @@
using Misaki.HighPerformance.Mathematics;
namespace Ghost.Graphics.RHI;
public interface IFenceSynchronizer
{
uint CPUFenceValue
{
get;
}
uint GPUFenceValue
{
get;
}
uint FrameIndex
{
get;
}
uint MaxFrameLatency
{
get;
}
bool WaitForGPUReady(int timeOut = -1);
void SignalCPUReady();
void WaitIdle();
}
public interface IRenderSystem : IFenceSynchronizer, IDisposable
{
IGraphicsEngine GraphicsEngine
{
get;
}
bool IsRunning
{
get;
}
void Start();
void Stop();
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
}

View File

@@ -1,18 +1,33 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Contracts;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
public readonly struct RenderContext
{
public ICommandBuffer CommandBuffer { get; init; }
}
/// <summary> /// <summary>
/// High-level renderer interface that uses RHI abstractions /// High-level renderer interface that uses RHI abstractions
/// </summary> /// </summary>
public interface IRenderer : IDisposable public interface IRenderer : IDisposable
{ {
/// <summary>
/// Gets or sets the render output target for this renderer.
/// </summary>
IRenderOutput? RenderOutput IRenderOutput? RenderOutput
{ {
get; set; get; set;
} }
/// <summary>
/// The function that performs the actual rendering operations. Skip rendering if this is null.
/// </summary>
Func<RenderContext, Error>? RenderFunc
{
get; set;
}
/// <summary> /// <summary>
/// Renders a frame /// Renders a frame
/// </summary> /// </summary>

View File

@@ -1,6 +1,5 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Graphics.Core;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
@@ -138,26 +137,4 @@ public interface IResourceAllocator : IDisposable
/// <param name="desc">A read-only reference to a <see cref="SamplerDesc"/> structure that defines the properties of the sampler to be created.</param> /// <param name="desc">A read-only reference to a <see cref="SamplerDesc"/> structure that defines the properties of the sampler to be created.</param>
/// <returns>An <see cref="Identifier{Sampler}"/> that uniquely identifies the created sampler object.</returns> /// <returns>An <see cref="Identifier{Sampler}"/> that uniquely identifies the created sampler object.</returns>
Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc); Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc);
/// <summary>
/// Creates a new mesh from the specified vertex and index data.
/// </summary>
/// <param name="vertices">A UnsafeList containing the vertices that define the geometry of the mesh. Must contain at least one vertex.</param>
/// <param name="indices">A UnsafeList containing the indices that specify how vertices are connected to form primitives. Must contain at least one index.</param>
/// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns>
Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices);
/// <summary>
/// Creates a new material instance using the specified shader.
/// </summary>
/// <param name="shader">The identifier of the shader to associate with the new material. Cannot be null.</param>
/// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns>
Handle<Material> CreateMaterial(Identifier<Shader> shader);
/// <summary>
/// Creates a new shader and returns its unique identifier.
/// </summary>
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor);
} }

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
@@ -129,85 +128,4 @@ public interface IResourceDatabase : IDisposable
/// </summary> /// </summary>
/// <param name="id">The identifier of the sampler to release. Must reference a valid, existing sampler.</param> /// <param name="id">The identifier of the sampler to release. Must reference a valid, existing sampler.</param>
void ReleaseSampler(Identifier<Sampler> id); void ReleaseSampler(Identifier<Sampler> id);
/// <summary>
/// Adds a mesh to the resource database and returns its handle.
/// </summary>
/// <param name="mesh">The mesh data to be added to the database.</param>
/// <returns>The <see cref="Handle{Mesh}"/> representing the newly added mesh.</returns>"/>
Handle<Mesh> AddMesh(ref readonly Mesh mesh);
/// <summary>
/// Determines whether a mesh with the specified Handle exists.
/// </summary>
/// <param name="handle">The handle of the mesh to check for existence. Cannot be null.</param>
/// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns>
bool HasMesh(Handle<Mesh> handle);
/// <summary>
/// Returns a reference to the mesh associated with the specified handle.
/// </summary>
/// <param name="handle">The handle of the mesh to retrieve. Must refer to a valid mesh; otherwise, the behavior is undefined.</param>
/// <returns>A result containing a reference to the mesh corresponding to the specified handle, or an error status if the handle is invalid.</returns>
RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle);
/// <summary>
/// Releases the mesh resource associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources.
/// </summary>
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
void ReleaseMesh(Handle<Mesh> handle);
/// <summary>
/// Adds a new material to the collection and returns its unique handle.
/// </summary>
/// <param name="material">The material to add. The material must be fully initialized before calling this method.</param>
/// <returns>The <see cref="Handle{Material}"/> representing the newly added material.</returns>
Handle<Material> AddMaterial(ref readonly Material material);
/// <summary>
/// Determines whether a material with the specified handle exists in the collection.
/// </summary>
/// <param name="handle">The handle of the material to check for existence.</param>
/// <returns>true if a material with the specified handle exists; otherwise, false.</returns>
bool HasMaterial(Handle<Material> handle);
/// <summary>
/// Gets a reference to the material associated with the specified handle.
/// </summary>
/// <param name="handle">The handle of the material to retrieve. Must refer to a valid material.</param>
/// <returns>A result containing a reference to the material corresponding to the specified handle, or an error status if the handle is invalid.</returns>
RefResult<Material, Error> GetMaterialReference(Handle<Material> handle);
/// <summary>
/// Releases the material associated with the specified handle, making it available for reuse or disposal.
/// </summary>
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
void ReleaseMaterial(Handle<Material> handle);
/// <summary>
/// Adds the specified shader to the collection and returns its unique identifier.
/// </summary>
/// <param name="shader">The shader to add. The shader is passed by read-only reference and will not be modified.</param>
/// <returns>The <see cref="Identifier{Shader}"/> representing the newly added shader.</returns>
Identifier<Shader> AddShader(Shader shader);
/// <summary>
/// Determines whether a shader with the specified identifier exists in the collection.
/// </summary>
/// <param name="id">The identifier of the shader to check for existence.</param>
/// <returns>true if a shader with the specified identifier exists; otherwise, false.</returns>
bool HasShader(Identifier<Shader> id);
/// <summary>
/// Returns a reference to the shader associated with the specified identifier.
/// </summary>
/// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
/// <returns>A result containing a reference to the shader corresponding to the specified identifier, or an error status if the identifier is invalid.</returns>
RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id);
/// <summary>
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
/// </summary>
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
void ReleaseShader(Identifier<Shader> id);
} }

View File

@@ -1,10 +1,9 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Contracts; namespace Ghost.Graphics.RHI;
public struct ShaderCompileResult : IDisposable public struct ShaderCompileResult : IDisposable
{ {

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;

View File

@@ -1,8 +1,7 @@
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using TerraFX.Interop.Windows;
using ElementType = uint; using ElementType = uint;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.RHI;
public unsafe struct LocalKeywordSet public unsafe struct LocalKeywordSet
{ {
@@ -42,7 +41,7 @@ public unsafe struct LocalKeywordSet
public ulong GetHash64() public ulong GetHash64()
{ {
ulong hash = 14695981039346656037ul; // FNV Offset basis var hash = 14695981039346656037ul; // FNV Offset basis
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++) for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
{ {

View File

@@ -1,13 +1,12 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Core.Utilities; using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using System.IO.Hashing; using System.IO.Hashing;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
internal static class RHIUtility public static class RHIUtility
{ {
public const int MAX_RENDER_TARGETS = 8; public const int MAX_RENDER_TARGETS = 8;

View File

@@ -1,6 +1,6 @@
using Ghost.Core; using Ghost.Core;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.RHI;
public readonly struct GPUResource; public readonly struct GPUResource;
public readonly struct Texture; public readonly struct Texture;
@@ -20,12 +20,12 @@ public static class ResourceHandleExtensions
return new Handle<GPUResource>(buffer.ID, buffer.Generation); return new Handle<GPUResource>(buffer.ID, buffer.Generation);
} }
internal static Handle<Texture> AsTexture(this Handle<GPUResource> resource) public static Handle<Texture> AsTexture(this Handle<GPUResource> resource)
{ {
return new Handle<Texture>(resource.ID, resource.Generation); return new Handle<Texture>(resource.ID, resource.Generation);
} }
internal static Handle<GraphicsBuffer> AsGraphicsBuffer(this Handle<GPUResource> resource) public static Handle<GraphicsBuffer> AsGraphicsBuffer(this Handle<GPUResource> resource)
{ {
return new Handle<GraphicsBuffer>(resource.ID, resource.Generation); return new Handle<GraphicsBuffer>(resource.ID, resource.Generation);
} }

View File

@@ -1,7 +1,7 @@
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.RHI;
/// <summary> /// <summary>
/// The layout of the root signature is: /// The layout of the root signature is:

View File

@@ -1,18 +1,10 @@
global using static TerraFX.Interop.DirectX.D3D12;
global using static TerraFX.Interop.DirectX.DirectX;
global using static TerraFX.Interop.DirectX.DXGI;
global using static TerraFX.Interop.Windows.Windows;
using Ghost.Core.Attributes; using Ghost.Core.Attributes;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
[assembly: InternalsVisibleTo("Ghost.Engine")] [assembly: InternalsVisibleTo("Ghost.Engine")]
[assembly: InternalsVisibleTo("Ghost.Editor")] [assembly: InternalsVisibleTo("Ghost.Editor")]
[assembly: InternalsVisibleTo("Ghost.Editor.Core")] [assembly: InternalsVisibleTo("Ghost.Editor.Core")]
[assembly: InternalsVisibleTo("Ghost.Graphics.Test")] [assembly: InternalsVisibleTo("Ghost.Graphics.Test")]
[assembly: InternalsVisibleTo("Ghost.Graphics.Test-Winui")] [assembly: InternalsVisibleTo("Ghost.Graphics.Test-Winui")]
[assembly: SupportedOSPlatform("windows10.0.19041.0")]
[assembly: EngineAssembly] [assembly: EngineAssembly]

View File

@@ -1,13 +1,11 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RenderGraphModule; using Ghost.Graphics.RenderGraphModule;
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.Contracts; namespace Ghost.Graphics.Core.Contracts;
public interface IRenderPass public interface IRenderPass
{ {
void Initialize(ref readonly RenderingContext ctx); void Initialize(ref readonly RenderingContext ctx);
void Build(RenderGraph graph, Identifier<RGTexture> backbuffer); void Build(RenderGraph graph, Identifier<RGTexture> backbuffer);
void Cleanup(IResourceDatabase resourceDatabase); void Cleanup(IResourceManager resourceManager);
} }

View File

@@ -1,5 +1,72 @@
using Ghost.Core;
using Ghost.Graphics.RenderGraphModule;
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.Core;
public class Camera public class Camera
{ {
private readonly IRenderer _renderer;
private Handle<Texture> _colorTexture;
private Handle<Texture> _depthTexture;
private uint _actualWidth;
private uint _actualHeight;
private uint _virtualWidth;
private uint _virtualHeight;
public IRenderer Renderer => _renderer;
/// <summary>
/// Gets the actual width of the camera's render target in pixels. If upscaler is used, this is the width before upscaling.
/// </summary>
public uint ActualWidth => _actualWidth;
/// <summary>
/// Gets the actual height of the camera's render target in pixels. If upscaler is used, this is the height before upscaling.
/// </summary>
public uint ActualHeight => _actualHeight;
/// <summary>
/// Gets the virtual width of the camera's render target in pixels. If upscaler is used, this is the width after upscaling.
/// </summary>
public uint VirtualWidth => _virtualWidth;
/// <summary>
/// Gets the virtual height of the camera's render target in pixels. If upscaler is used, this is the height after upscaling.
/// </summary>
public uint VirtualHeight => _virtualHeight;
public RenderGraph? RenderGraph
{
get; set;
}
public Camera(IGraphicsEngine graphicsEngine)
{
_renderer = graphicsEngine.CreateRenderer();
}
public Camera(IGraphicsEngine graphicsEngine, RenderGraph renderGraph)
{
_renderer = graphicsEngine.CreateRenderer();
RenderGraph = renderGraph;
_renderer.RenderFunc = DefaultRenderFunc;
}
private Error DefaultRenderFunc(RenderContext context)
{
if (RenderGraph == null)
{
return Error.None;
}
RenderGraph.Reset();
var view = new ViewState(_virtualWidth, _virtualHeight, _actualWidth, _actualHeight);
RenderGraph.Compile(in view);
RenderGraph.Execute(context.CommandBuffer);
return Error.None;
}
} }

View File

@@ -1,150 +0,0 @@
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics;
using System.Drawing;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
namespace Ghost.Graphics.Core;
/// <summary>
/// Represents a color with 4 bytes components.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 4)]
public struct Color32 : IEquatable<Color32>
{
public byte r;
public byte g;
public byte b;
public byte a;
public Color32(byte r, byte g, byte b, byte a)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public Color32(Color color)
: this(color.R, color.G, color.B, color.A)
{
}
public Color32(Color128 color128)
: this((byte)(color128.r * 255.0f), (byte)(color128.g * 255.0f), (byte)(color128.b * 255.0f), (byte)(color128.a * 255.0f))
{
}
public Color32(float4 v)
: this((byte)(v.x * 255.0f), (byte)(v.y * 255.0f), (byte)(v.z * 255.0f), (byte)(v.w * 255.0f))
{
}
public readonly bool Equals(Color32 other)
{
return r == other.r && g == other.g && b == other.b && a == other.a;
}
public override readonly bool Equals(object? obj)
{
return obj is Color32 color && Equals(color);
}
public override readonly int GetHashCode()
{
return HashCode.Combine(r, g, b, a);
}
public static bool operator ==(Color32 left, Color32 right)
{
return left.Equals(right);
}
public static bool operator !=(Color32 left, Color32 right)
{
return !(left == right);
}
}
/// <summary>
/// Represents a color with 16 bytes components.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct Color128 : IEquatable<Color128>
{
public float r;
public float g;
public float b;
public float a;
public Color128(float r, float g, float b, float a)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public Color128(Color color)
: this(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f)
{
}
public Color128(Color32 color32)
: this(color32.r / 255.0f, color32.g / 255.0f, color32.b / 255.0f, color32.a / 255.0f)
{
}
public Color128(float4 v)
: this(v.x, v.y, v.z, v.w)
{
}
public readonly bool Equals(Color128 other)
{
return r.Equals(other.r) && g.Equals(other.g) && b.Equals(other.b) && a.Equals(other.a);
}
public override readonly bool Equals(object? obj)
{
return obj is Color128 color && Equals(color);
}
public readonly override int GetHashCode()
{
return HashCode.Combine(r, g, b, a);
}
public static bool operator ==(Color128 left, Color128 right)
{
return left.Equals(right);
}
public static bool operator !=(Color128 left, Color128 right)
{
return !(left == right);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
public static class Semantic
{
public const DXGI_FORMAT ALIGNED_FORMAT = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT;
public const int COUNT = 5;
public static readonly FixedText32 Position = new("POSITION");
public static readonly FixedText32 Normal = new("NORMAL");
public static readonly FixedText32 Tangent = new("TANGENT");
public static readonly FixedText32 Uv = new("TEXCOORD");
public static readonly FixedText32 Color = new("COLOR");
}
public float4 position;
public float4 normal;
public float4 tangent;
public float4 uv;
public Color128 color;
}

View File

@@ -65,17 +65,17 @@ public struct Material : IResourceReleasable
get; set; get; set;
} }
public Error SetShader(Identifier<Shader> shaderId, IResourceAllocator allocator, IResourceDatabase database) public Error SetShader(Identifier<Shader> shaderId, IResourceManager manager)
{ {
if (!shaderId.IsValid) if (!shaderId.IsValid)
{ {
return Error.InvalidArgument; return Error.InvalidArgument;
} }
_cBufferCache.ReleaseResource(database); _cBufferCache.ReleaseResource(manager.ResourceDatabase);
_shader = shaderId; _shader = shaderId;
var r = database.GetShaderReference(shaderId); var r = manager.GetShaderReference(shaderId);
if (r.IsFailure) if (r.IsFailure)
{ {
return r.Error; return r.Error;
@@ -101,7 +101,7 @@ public struct Material : IResourceReleasable
_passPipelineOverride[i] = new PipelineOverride _passPipelineOverride[i] = new PipelineOverride
{ {
shaderPass = pass.Key, shaderPass = pass.Key,
options = pass.DeafaultState, options = pass.DefaultState,
}; };
} }
@@ -114,7 +114,7 @@ public struct Material : IResourceReleasable
MemoryType = ResourceMemoryType.Default, MemoryType = ResourceMemoryType.Default,
}; };
var buffer = allocator.CreateBuffer(ref desc, "MaterialCBuffer"); var buffer = manager.ResourceAllocator.CreateBuffer(ref desc, "MaterialCBuffer");
_cBufferCache = new CBufferCache(buffer, shader.CBufferSize); _cBufferCache = new CBufferCache(buffer, shader.CBufferSize);
} }
@@ -201,9 +201,9 @@ public struct Material : IResourceReleasable
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Error SetKeyword(IResourceDatabase resourceDatabase, int keywordId, bool enabled) public Error SetKeyword(IResourceManager manager, int keywordId, bool enabled)
{ {
var r = resourceDatabase.GetShaderReference(_shader); var r = manager.GetShaderReference(_shader);
if (r.IsFailure) if (r.IsFailure)
{ {
return r.Error; return r.Error;
@@ -223,9 +223,9 @@ public struct Material : IResourceReleasable
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool IsKeywordEnabled(IResourceDatabase resourceDatabase, int keywordId) public readonly bool IsKeywordEnabled(IResourceManager manager, int keywordId)
{ {
var r = resourceDatabase.GetShaderReference(_shader); var r = manager.GetShaderReference(_shader);
if (r.IsFailure) if (r.IsFailure)
{ {
return false; return false;

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.Core;

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;

View File

@@ -7,24 +7,6 @@ using System.Runtime.InteropServices;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.Core;
public readonly struct ShaderPass
{
public Key64<ShaderPass> Key
{
get; init;
}
public PipelineState DeafaultState
{
get; init;
}
public LocalKeywordSet KeywordIDs
{
get; init;
}
}
public struct ShaderProperty; public struct ShaderProperty;
public partial struct Shader public partial struct Shader
@@ -146,7 +128,7 @@ public partial struct Shader : IResourceReleasable
_shaderPasses[i] = new ShaderPass _shaderPasses[i] = new ShaderPass
{ {
Key = passKey, Key = passKey,
DeafaultState = pass.localPipeline, DefaultState = pass.localPipeline,
KeywordIDs = keywords, KeywordIDs = keywords,
}; };

View File

@@ -1,139 +0,0 @@
using Ghost.Core;
using Ghost.Graphics.RHI;
using Ghost.Graphics.Core;
using Ghost.Graphics.RenderPasses;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.RenderGraphModule;
namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of the renderer interface using RHI abstractions
/// </summary>
internal class D3D12Renderer : IRenderer
{
private readonly D3D12GraphicsEngine _graphicsEngine;
private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly ICommandBuffer _commandBuffer;
private readonly RenderGraph _renderGraph;
private uint _frameIndex;
private bool _disposed;
// NOTE: Testing only.
private readonly MeshRenderPass _pass;
public IRenderOutput? RenderOutput
{
get; set;
}
// TODO: Add render graph support
public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceDatabase resourceDatabase)
{
_graphicsEngine = graphicsEngine;
_resourceDatabase = resourceDatabase;
_commandBuffer = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
_renderGraph = new RenderGraph(_graphicsEngine);
// NOTE: Testing only.
_pass = new();
}
~D3D12Renderer()
{
Dispose();
}
public Result Render(ICommandAllocator commandAllocator)
{
if (RenderOutput is null)
{
return Result.Failure("Render target strategy is not set.");
}
var target = RenderOutput.GetRenderTarget();
if (target.IsInvalid)
{
return Result.Failure("Render target is invalid.");
}
_commandBuffer.Begin(commandAllocator);
RenderOutput.BeginRender(_commandBuffer);
// NOTE: Temporary solution: render directly to the swap chain back buffer if available.
// HACK: This is hard coded for testing purposes only.
var error = RenderScene(target, RenderOutput.Viewport, RenderOutput.Scissor);
if (error != Error.None)
{
_commandBuffer.End();
return Result.Failure(error);
}
RenderOutput.EndRender(_commandBuffer);
var r = _commandBuffer.End();
if (r.IsFailure)
{
return r;
}
_graphicsEngine.Device.GraphicsQueue.Submit(_commandBuffer);
RenderOutput.Present();
return Result.Success();
}
// TODO: A proper render graph integration.
private Error RenderScene(Handle<Texture> target, ViewportDesc viewport, RectDesc rect)
{
// NOTE: Testing only.
var ctx = new RenderingContext(_graphicsEngine, _commandBuffer);
if (_frameIndex == 0)
{
_pass.Initialize(ref ctx);
}
//_commandBuffer.BeginRenderPass(rtDesc, depthDesc, false);
_commandBuffer.SetViewport(viewport);
_commandBuffer.SetScissorRect(rect);
_renderGraph.Reset();
var backBuffer = _renderGraph.ImportTexture(target, "Back Buffer");
_pass.Build(_renderGraph, backBuffer);
// Create view state from viewport
var viewState = new ViewState((uint)viewport.Width, (uint)viewport.Height);
// Compile with view state
_renderGraph.Compile(in viewState);
_renderGraph.Execute(_commandBuffer);
//_commandBuffer.EndRenderPass();
_frameIndex++;
return Error.None;
}
public void Dispose()
{
if (_disposed)
{
return;
}
// NOTE: Testing only.
_pass.Cleanup(_resourceDatabase);
_renderGraph.Dispose();
_commandBuffer.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -1,24 +0,0 @@
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.D3D12.Utilities;
internal unsafe static class D3D12PipelineResource
{
private readonly static D3D12_INPUT_ELEMENT_DESC[] s_inputElementDescs = [
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Position.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Normal.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Tangent.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Uv.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Color.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
];
public const DXGI_FORMAT SWAP_CHAIN_BACK_BUFFER_FORMAT = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
public static D3D12_INPUT_LAYOUT_DESC InputLayoutDescription => new()
{
pInputElementDescs = (D3D12_INPUT_ELEMENT_DESC*)Unsafe.AsPointer(ref s_inputElementDescs[0]),
NumElements = (uint)s_inputElementDescs.Length
};
}

View File

@@ -9,6 +9,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -16,27 +17,19 @@
<IsTrimmable>True</IsTrimmable> <IsTrimmable>True</IsTrimmable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Include="runtimes\win-x64\native\dxcompiler.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="runtimes\win-x64\native\dxil.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Misaki.HighPerformance.Analyzer" Version="1.0.0"> <PackageReference Include="Misaki.HighPerformance.Analyzer" Version="1.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Misaki.HighPerformance.Image" Version="1.1.0" /> <PackageReference Include="Misaki.HighPerformance.Image" Version="1.1.0" />
<PackageReference Include="TerraFX.Interop.D3D12MemoryAllocator" Version="3.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../Runtime/Ghost.Core/Ghost.Core.csproj" /> <ProjectReference Include="..\..\Runtime\Ghost.Core\Ghost.Core.csproj" />
<ProjectReference Include="../../Editor/Ghost.DSL/Ghost.DSL.csproj" /> <ProjectReference Include="..\..\Editor\Ghost.DSL\Ghost.DSL.csproj" />
<ProjectReference Include="..\Ghost.Graphics.D3D12\Ghost.Graphics.D3D12.csproj" />
<ProjectReference Include="..\Ghost.Graphics.RHI\Ghost.Graphics.RHI.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
namespace Ghost.Graphics.RenderGraphModule; namespace Ghost.Graphics.RenderGraphModule;
@@ -10,7 +9,7 @@ namespace Ghost.Graphics.RenderGraphModule;
/// </summary> /// </summary>
public sealed class RenderGraph : IDisposable public sealed class RenderGraph : IDisposable
{ {
private readonly IGraphicsEngine _graphicsEngine; private readonly IResourceManager _resourceManager;
private readonly RenderGraphObjectPool _objectPool; private readonly RenderGraphObjectPool _objectPool;
private readonly RenderGraphResourceRegistry _resources; private readonly RenderGraphResourceRegistry _resources;
@@ -31,16 +30,15 @@ public sealed class RenderGraph : IDisposable
private readonly RenderGraphExecutor _executor; private readonly RenderGraphExecutor _executor;
private readonly RenderGraphNativePassBuilder _nativePassBuilder; private readonly RenderGraphNativePassBuilder _nativePassBuilder;
private readonly RenderGraphBlackboard _blackboard;
private bool _compiled; private bool _compiled;
public RenderGraphBlackboard Blackboard public RenderGraphBlackboard Blackboard => _blackboard;
{
get;
}
public RenderGraph(IGraphicsEngine graphicsEngine) public RenderGraph(IResourceManager resourceManager, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler)
{ {
_graphicsEngine = graphicsEngine; _resourceManager = resourceManager;
_objectPool = new RenderGraphObjectPool(); _objectPool = new RenderGraphObjectPool();
_resources = new RenderGraphResourceRegistry(_objectPool); _resources = new RenderGraphResourceRegistry(_objectPool);
@@ -50,40 +48,32 @@ public sealed class RenderGraph : IDisposable
_nativePasses = new List<NativeRenderPass>(32); _nativePasses = new List<NativeRenderPass>(32);
_builder = new RenderGraphBuilder(); _builder = new RenderGraphBuilder();
_aliasingManager = new ResourceAliasingManager(graphicsEngine.ResourceAllocator, _objectPool); _aliasingManager = new ResourceAliasingManager(resourceManager.ResourceAllocator, _objectPool);
_compilationCache = new RenderGraphCompilationCache(); _compilationCache = new RenderGraphCompilationCache();
_context = new RenderGraphContext( _context = new RenderGraphContext(
_graphicsEngine.ResourceDatabase, resourceManager,
_graphicsEngine.PipelineLibrary, pipelineLibrary,
_graphicsEngine.ShaderCompiler, shaderCompiler,
_resources _resources
); );
_nativePassBuilder = new RenderGraphNativePassBuilder(_objectPool, _resources); _nativePassBuilder = new RenderGraphNativePassBuilder(_objectPool, _resources);
_compiler = new RenderGraphCompiler(_graphicsEngine, _resources, _aliasingManager, _nativePassBuilder, _compilationCache); _compiler = new RenderGraphCompiler(resourceManager, _resources, _aliasingManager, _nativePassBuilder, _compilationCache);
_executor = new RenderGraphExecutor(_graphicsEngine, _resources, _context); _executor = new RenderGraphExecutor(resourceManager, _resources, _context);
Blackboard = new RenderGraphBlackboard(); _blackboard = new RenderGraphBlackboard();
} }
/// <summary> /// <summary>
/// Resets the render graph for a new frame. /// Resets the render graph for a new frame.
/// Reuses existing allocations to minimize GC.
/// </summary> /// </summary>
public void Reset() public void Reset()
{ {
// Clear blackboard data _blackboard.Clear();
Blackboard.Clear(); _resources.Clear();
_aliasingManager.Clear();
// Reset resources but keep allocations
_resources.Reset();
// Reset aliasing manager
_aliasingManager.Reset();
// Clear compiled barriers
_compiledBarriers.Clear(); _compiledBarriers.Clear();
// Return passes to the pool and reset count // Return passes to the pool and reset count
@@ -94,11 +84,8 @@ public sealed class RenderGraph : IDisposable
} }
_passes.Clear(); _passes.Clear();
// Clear compiled passes list
_compiledPasses.Clear(); _compiledPasses.Clear();
// Return native passes to pool
for (var i = 0; i < _nativePasses.Count; i++) for (var i = 0; i < _nativePasses.Count; i++)
{ {
_objectPool.Return(_nativePasses[i]); _objectPool.Return(_nativePasses[i]);
@@ -117,14 +104,14 @@ public sealed class RenderGraph : IDisposable
Color128 clearColor = default, float clearDepth = 1.0f, byte clearStencil = 0, Color128 clearColor = default, float clearDepth = 1.0f, byte clearStencil = 0,
bool clearAtFirstUse = true, bool discardAtLastUse = true) bool clearAtFirstUse = true, bool discardAtLastUse = true)
{ {
var r = _graphicsEngine.ResourceDatabase.GetResourceDescription(texture.AsResource()); var r = _resourceManager.ResourceDatabase.GetResourceDescription(texture.AsResource());
if (r.IsFailure) if (r.IsFailure)
{ {
return Identifier<RGTexture>.Invalid; return Identifier<RGTexture>.Invalid;
} }
var desc = r.Value; var desc = r.Value;
return _resources.ImportTexture(in desc._desc.textureDescription, texture, name, clearColor, clearDepth, clearStencil, clearAtFirstUse, discardAtLastUse); return _resources.ImportTexture(in desc.TextureDescription, texture, name, clearColor, clearDepth, clearStencil, clearAtFirstUse, discardAtLastUse);
} }
/// <summary> /// <summary>
@@ -134,14 +121,14 @@ public sealed class RenderGraph : IDisposable
/// <returns>The identifier of the imported render graph buffer. Invalid if import fails.</returns> /// <returns>The identifier of the imported render graph buffer. Invalid if import fails.</returns>
public Identifier<RGBuffer> ImportBuffer(Handle<GraphicsBuffer> buffer, string name) public Identifier<RGBuffer> ImportBuffer(Handle<GraphicsBuffer> buffer, string name)
{ {
var r = _graphicsEngine.ResourceDatabase.GetResourceDescription(buffer.AsResource()); var r = _resourceManager.ResourceDatabase.GetResourceDescription(buffer.AsResource());
if (r.IsFailure) if (r.IsFailure)
{ {
return Identifier<RGBuffer>.Invalid; return Identifier<RGBuffer>.Invalid;
} }
var desc = r.Value; var desc = r.Value;
return _resources.ImportBuffer(in desc._desc.bufferDescription, buffer, name); return _resources.ImportBuffer(in desc.BufferDescription, buffer, name);
} }
public IRasterRenderGraphBuilder AddRasterRenderPass<TPassData>(string name, out TPassData passData) public IRasterRenderGraphBuilder AddRasterRenderPass<TPassData>(string name, out TPassData passData)

View File

@@ -326,7 +326,7 @@ internal sealed class ResourceAliasingManager
_logicalToPlaced = new Dictionary<int, int>(64); _logicalToPlaced = new Dictionary<int, int>(64);
} }
public void Reset() public void Clear()
{ {
for (var i = 0; i < _placedResources.Count; i++) for (var i = 0; i < _placedResources.Count; i++)
{ {

View File

@@ -74,7 +74,7 @@ internal sealed class ResourceStateTracker
/// <summary> /// <summary>
/// Represents a compiled barrier with only the target state. /// Represents a compiled barrier with only the target state.
/// The before state is always queried from ResourceDatabase at execution time. /// The before state is always queried from ResourceManager at execution time.
/// </summary> /// </summary>
internal struct CompiledBarrier internal struct CompiledBarrier
{ {
@@ -208,7 +208,7 @@ internal static class RenderGraphBarriers
/// <summary> /// <summary>
/// Compiles implicit state transitions for all resources accessed by a pass. /// Compiles implicit state transitions for all resources accessed by a pass.
/// Stores only the target state - the before state will be queried from ResourceDatabase at execution time. /// Stores only the target state - the before state will be queried from ResourceManager at execution time.
/// </summary> /// </summary>
private static void CompileImplicitTransitions( private static void CompileImplicitTransitions(
RenderGraphPassBase pass, RenderGraphPassBase pass,

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@@ -23,7 +22,7 @@ internal sealed class CachedCompilation
// Placed resource metadata // Placed resource metadata
public readonly List<PlacedResourceData> placedResources = new(32); public readonly List<PlacedResourceData> placedResources = new(32);
// Compiled barriers (stores only target states, queries before state from ResourceDatabase) // Compiled barriers (stores only target states, queries before state from ResourceManager)
public readonly List<CompiledBarrier> compiledBarriers = new(128); public readonly List<CompiledBarrier> compiledBarriers = new(128);
// Real gpu resource // Real gpu resource

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
namespace Ghost.Graphics.RenderGraphModule; namespace Ghost.Graphics.RenderGraphModule;
@@ -10,7 +9,7 @@ namespace Ghost.Graphics.RenderGraphModule;
/// </summary> /// </summary>
internal sealed class RenderGraphCompiler internal sealed class RenderGraphCompiler
{ {
private readonly IGraphicsEngine _graphicsEngine; private readonly IResourceManager _resourceManager;
private readonly RenderGraphResourceRegistry _resources; private readonly RenderGraphResourceRegistry _resources;
private readonly ResourceAliasingManager _aliasingManager; private readonly ResourceAliasingManager _aliasingManager;
private readonly RenderGraphNativePassBuilder _nativePassBuilder; private readonly RenderGraphNativePassBuilder _nativePassBuilder;
@@ -19,13 +18,13 @@ internal sealed class RenderGraphCompiler
private Handle<GPUResource> _resourceHeap; private Handle<GPUResource> _resourceHeap;
public RenderGraphCompiler( public RenderGraphCompiler(
IGraphicsEngine graphicsEngine, IResourceManager resourceManager,
RenderGraphResourceRegistry resources, RenderGraphResourceRegistry resources,
ResourceAliasingManager aliasingManager, ResourceAliasingManager aliasingManager,
RenderGraphNativePassBuilder nativePassBuilder, RenderGraphNativePassBuilder nativePassBuilder,
RenderGraphCompilationCache compilationCache) RenderGraphCompilationCache compilationCache)
{ {
_graphicsEngine = graphicsEngine; _resourceManager = resourceManager;
_resources = resources; _resources = resources;
_aliasingManager = aliasingManager; _aliasingManager = aliasingManager;
_nativePassBuilder = nativePassBuilder; _nativePassBuilder = nativePassBuilder;
@@ -212,10 +211,10 @@ internal sealed class RenderGraphCompiler
continue; continue;
} }
_graphicsEngine.ResourceDatabase.ScheduleReleaseResource(res.backingResource); _resourceManager.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
} }
_graphicsEngine.ResourceDatabase.ScheduleReleaseResource(_resourceHeap); _resourceManager.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
} }
if (_aliasingManager.Heap.size == 0) if (_aliasingManager.Heap.size == 0)
@@ -231,7 +230,7 @@ internal sealed class RenderGraphCompiler
HeapType = HeapType.Default HeapType = HeapType.Default
}; };
_resourceHeap = _graphicsEngine.ResourceAllocator.Allocate(in allocationDesc, "RenderGraphResourceHeap"); _resourceHeap = _resourceManager.ResourceAllocator.Allocate(in allocationDesc, "RenderGraphResourceHeap");
for (var i = 0; i < _resources.Resources.Count; i++) for (var i = 0; i < _resources.Resources.Count; i++)
{ {
@@ -253,11 +252,11 @@ internal sealed class RenderGraphCompiler
if (res.type == RenderGraphResourceType.Texture) if (res.type == RenderGraphResourceType.Texture)
{ {
var textureDesc = res.rgTextureDesc.ToTextureDesc(res.resolvedWidth, res.resolvedHeight); var textureDesc = res.rgTextureDesc.ToTextureDesc(res.resolvedWidth, res.resolvedHeight);
res.backingResource = _graphicsEngine.ResourceAllocator.CreateTexture(in textureDesc, res.name, ops).AsResource(); res.backingResource = _resourceManager.ResourceAllocator.CreateTexture(in textureDesc, res.name, ops).AsResource();
} }
else if (res.type == RenderGraphResourceType.Buffer) else if (res.type == RenderGraphResourceType.Buffer)
{ {
res.backingResource = _graphicsEngine.ResourceAllocator.CreateBuffer(in res.bufferDesc, res.name, ops).AsResource(); res.backingResource = _resourceManager.ResourceAllocator.CreateBuffer(in res.bufferDesc, res.name, ops).AsResource();
} }
else else
{ {
@@ -380,11 +379,11 @@ internal sealed class RenderGraphCompiler
{ {
if (!res.isImported) if (!res.isImported)
{ {
_graphicsEngine.ResourceDatabase.ScheduleReleaseResource(res.backingResource); _resourceManager.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
} }
} }
_graphicsEngine.ResourceDatabase.ScheduleReleaseResource(_resourceHeap); _resourceManager.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
_resourceHeap = Handle<GPUResource>.Invalid; _resourceHeap = Handle<GPUResource>.Invalid;
} }
} }

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
@@ -8,7 +7,7 @@ namespace Ghost.Graphics.RenderGraphModule;
public interface IRenderGraphContext public interface IRenderGraphContext
{ {
IResourceDatabase ResourceDatabase { get; } IResourceManager ResourceManager { get; }
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource); Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
Handle<Texture> GetActualTexture(Identifier<RGTexture> texture); Handle<Texture> GetActualTexture(Identifier<RGTexture> texture);
@@ -38,7 +37,7 @@ public interface IUnsafeRenderContext : IRasterRenderContext, IRenderGraphContex
internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderContext, IUnsafeRenderContext internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderContext, IUnsafeRenderContext
{ {
private readonly IResourceDatabase _resourceDatabase; private readonly IResourceManager _resourceManager;
private readonly IPipelineLibrary _pipelineLibrary; private readonly IPipelineLibrary _pipelineLibrary;
private readonly IShaderCompiler _shaderCompiler; private readonly IShaderCompiler _shaderCompiler;
private readonly RenderGraphResourceRegistry _resources; private readonly RenderGraphResourceRegistry _resources;
@@ -53,15 +52,15 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
private Handle<GraphicsBuffer> _activePerMeshData; private Handle<GraphicsBuffer> _activePerMeshData;
private int _activeMeshIndexCount; private int _activeMeshIndexCount;
public IResourceDatabase ResourceDatabase => _resourceDatabase; public IResourceManager ResourceManager => _resourceManager;
public int ActiveMeshIndexCount => _activeMeshIndexCount; public int ActiveMeshIndexCount => _activeMeshIndexCount;
public ICommandBuffer CommandBuffer => _commandBuffer; public ICommandBuffer CommandBuffer => _commandBuffer;
internal RenderGraphContext(IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources) internal RenderGraphContext(IResourceManager resourceManager, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources)
{ {
_resourceDatabase = resourceDatabase; _resourceManager = resourceManager;
_pipelineLibrary = pipelineLibrary; _pipelineLibrary = pipelineLibrary;
_shaderCompiler = shaderCompiler; _shaderCompiler = shaderCompiler;
_resources = resources; _resources = resources;
@@ -103,7 +102,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
public void SetActiveMaterial(Handle<Material> material) public void SetActiveMaterial(Handle<Material> material)
{ {
var r = _resourceDatabase.GetMaterialReference(material); var r = _resourceManager.GetMaterialReference(material);
if (r.IsFailure) if (r.IsFailure)
{ {
_activePerMaterialData = Handle<GraphicsBuffer>.Invalid; _activePerMaterialData = Handle<GraphicsBuffer>.Invalid;
@@ -116,7 +115,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
public void SetActiveMaterial(ref readonly Material material) public void SetActiveMaterial(ref readonly Material material)
{ {
var shaderResult = _resourceDatabase.GetShaderReference(material.Shader); var shaderResult = _resourceManager.GetShaderReference(material.Shader);
if (shaderResult.IsFailure) if (shaderResult.IsFailure)
{ {
_activePerMaterialData = Handle<GraphicsBuffer>.Invalid; _activePerMaterialData = Handle<GraphicsBuffer>.Invalid;
@@ -161,7 +160,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
public void SetActiveMesh(Handle<Mesh> mesh) public void SetActiveMesh(Handle<Mesh> mesh)
{ {
var r = _resourceDatabase.GetMeshReference(mesh); var r = _resourceManager.GetMeshReference(mesh);
if (r.IsFailure) if (r.IsFailure)
{ {
_activePerMeshData = Handle<GraphicsBuffer>.Invalid; _activePerMeshData = Handle<GraphicsBuffer>.Invalid;
@@ -184,8 +183,8 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
// TODO: Global and view constants // TODO: Global and view constants
var data = new PushConstantsData var data = new PushConstantsData
{ {
objectIndex = _resourceDatabase.GetBindlessIndex(_activePerMeshData.AsResource()), objectIndex = _resourceManager.ResourceDatabase.GetBindlessIndex(_activePerMeshData.AsResource()),
materialIndex = _resourceDatabase.GetBindlessIndex(_activePerMaterialData.AsResource()), materialIndex = _resourceManager.ResourceDatabase.GetBindlessIndex(_activePerMaterialData.AsResource()),
}; };
var pushConstantSpan = new ReadOnlySpan<uint>(&data, sizeof(PushConstantsData) / sizeof(uint)); var pushConstantSpan = new ReadOnlySpan<uint>(&data, sizeof(PushConstantsData) / sizeof(uint));

View File

@@ -1,5 +1,4 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
namespace Ghost.Graphics.RenderGraphModule; namespace Ghost.Graphics.RenderGraphModule;
@@ -9,16 +8,16 @@ namespace Ghost.Graphics.RenderGraphModule;
/// </summary> /// </summary>
internal sealed class RenderGraphExecutor internal sealed class RenderGraphExecutor
{ {
private readonly IGraphicsEngine _graphicsEngine; private readonly IResourceManager _resourceManager;
private readonly RenderGraphResourceRegistry _resources; private readonly RenderGraphResourceRegistry _resources;
private readonly RenderGraphContext _context; private readonly RenderGraphContext _context;
public RenderGraphExecutor( public RenderGraphExecutor(
IGraphicsEngine graphicsEngine, IResourceManager resourceManager,
RenderGraphResourceRegistry resources, RenderGraphResourceRegistry resources,
RenderGraphContext context) RenderGraphContext context)
{ {
_graphicsEngine = graphicsEngine; _resourceManager = resourceManager;
_resources = resources; _resources = resources;
_context = context; _context = context;
} }
@@ -130,7 +129,7 @@ internal sealed class RenderGraphExecutor
/// <summary> /// <summary>
/// Executes all barriers for a specific pass. /// Executes all barriers for a specific pass.
/// Uses pre-compiled barriers and queries before state from ResourceDatabase. /// Uses pre-compiled barriers and queries before state from ResourceManager.
/// </summary> /// </summary>
private unsafe void ExecuteBarriersForPass( private unsafe void ExecuteBarriersForPass(
ICommandBuffer cmd, ICommandBuffer cmd,
@@ -158,8 +157,8 @@ internal sealed class RenderGraphExecutor
var resource = _resources.GetResource(compiledBarrier.Resource); var resource = _resources.GetResource(compiledBarrier.Resource);
var resourceHandle = resource.backingResource; var resourceHandle = resource.backingResource;
// Always query the before state from ResourceDatabase (single source of truth) // Always query the before state from ResourceManager (single source of truth)
var currentState = _graphicsEngine.ResourceDatabase.GetResourceBarrierData(resourceHandle).GetValueOrThrow(); var currentState = _resourceManager.ResourceDatabase.GetResourceBarrierData(resourceHandle).GetValueOrThrow();
BarrierLayout layoutBefore; BarrierLayout layoutBefore;
BarrierAccess accessBefore; BarrierAccess accessBefore;
@@ -169,7 +168,7 @@ internal sealed class RenderGraphExecutor
if (compiledBarrier.AliasingPredecessor.IsValid) if (compiledBarrier.AliasingPredecessor.IsValid)
{ {
var predHandle = _resources.GetResource(compiledBarrier.AliasingPredecessor).backingResource; var predHandle = _resources.GetResource(compiledBarrier.AliasingPredecessor).backingResource;
var predState = _graphicsEngine.ResourceDatabase.GetResourceBarrierData(predHandle).GetValueOrThrow(); var predState = _resourceManager.ResourceDatabase.GetResourceBarrierData(predHandle).GetValueOrThrow();
layoutBefore = BarrierLayout.Undefined; layoutBefore = BarrierLayout.Undefined;
accessBefore = BarrierAccess.NoAccess; accessBefore = BarrierAccess.NoAccess;

View File

@@ -168,7 +168,7 @@ internal sealed class RenderGraphResourceRegistry
} }
} }
public void Reset() public void Clear()
{ {
// Return all resources to pool // Return all resources to pool
for (var i = 0; i < _resources.Count; i++) for (var i = 0; i < _resources.Count; i++)

View File

@@ -37,15 +37,22 @@ public struct ViewState : IEquatable<ViewState>
public uint viewportWidth; public uint viewportWidth;
public uint viewportHeight; public uint viewportHeight;
public ViewState(uint width, uint height) // For upscalers that need to know the original render target size before upscaling
public uint actualWidth;
public uint actualHeight;
public ViewState(uint width, uint height, uint actualWidth, uint actualHeight)
{ {
viewportWidth = width; viewportWidth = width;
viewportHeight = height; viewportHeight = height;
this.actualWidth = actualWidth;
this.actualHeight = actualHeight;
} }
public readonly bool Equals(ViewState other) public readonly bool Equals(ViewState other)
{ {
return viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight; return viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight
&& actualWidth == other.actualWidth && actualHeight == other.actualHeight;
} }
public override readonly bool Equals(object? obj) public override readonly bool Equals(object? obj)

View File

@@ -1,8 +1,8 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.DSL.ShaderCompiler; using Ghost.DSL.ShaderCompiler;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.Core.Contracts;
using Ghost.Graphics.RenderGraphModule; using Ghost.Graphics.RenderGraphModule;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities; using Ghost.Graphics.Utilities;
@@ -159,7 +159,7 @@ internal class MeshRenderPass : IRenderPass
} }
else else
{ {
var shaderResult = ctx.ResourceDatabase.GetShaderReference(_shader); var shaderResult = ctx.ResourceManager.GetShaderReference(_shader);
if (shaderResult.IsFailure) if (shaderResult.IsFailure)
{ {
throw new InvalidOperationException("Failed to get shader reference."); throw new InvalidOperationException("Failed to get shader reference.");
@@ -201,7 +201,6 @@ internal class MeshRenderPass : IRenderPass
{ {
using var stream = File.OpenRead(_textureFiles[i]); using var stream = File.OpenRead(_textureFiles[i]);
using var imageData = ImageResult.FromStream(stream, ColorComponents.RGBA); using var imageData = ImageResult.FromStream(stream, ColorComponents.RGBA);
var desc = new TextureDesc var desc = new TextureDesc
{ {
Width = imageData.Width, Width = imageData.Width,
@@ -227,7 +226,7 @@ internal class MeshRenderPass : IRenderPass
_sampler = ctx.ResourceAllocator.CreateSampler(in samplerDesc); _sampler = ctx.ResourceAllocator.CreateSampler(in samplerDesc);
var meshResult = ctx.ResourceDatabase.GetMaterialReference(_material); var meshResult = ctx.ResourceManager.GetMaterialReference(_material);
if (meshResult.IsFailure) if (meshResult.IsFailure)
{ {
throw new InvalidOperationException("Failed to get material reference."); throw new InvalidOperationException("Failed to get material reference.");
@@ -283,7 +282,7 @@ internal class MeshRenderPass : IRenderPass
builder.SetRenderFunc<BlitPassData>(static (data, ctx) => builder.SetRenderFunc<BlitPassData>(static (data, ctx) =>
{ {
var r = ctx.ResourceDatabase.GetMaterialReference(data.blitMaterial); var r = ctx.ResourceManager.GetMaterialReference(data.blitMaterial);
if (r.IsFailure) if (r.IsFailure)
{ {
return; return;
@@ -292,12 +291,12 @@ internal class MeshRenderPass : IRenderPass
ref var matRef = ref r.Value; ref var matRef = ref r.Value;
var blitProps = new ShaderProperties_Hidden_Blit var blitProps = new ShaderProperties_Hidden_Blit
{ {
mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())), mainTex = ctx.ResourceManager.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
sampler_mainTex = (uint)data.sampler.Value, sampler_mainTex = (uint)data.sampler.Value,
}; };
matRef.SetPropertyCache(in blitProps).ThrowIfFailed(); matRef.SetPropertyCache(in blitProps).ThrowIfFailed();
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceDatabase); matRef.UploadData(ctx.CommandBuffer, ctx.ResourceManager.ResourceDatabase);
ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid); ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid);
@@ -308,20 +307,20 @@ internal class MeshRenderPass : IRenderPass
} }
} }
public void Cleanup(IResourceDatabase resourceDatabase) public void Cleanup(IResourceManager resourceManager)
{ {
resourceDatabase.ReleaseMaterial(_blitMaterial); resourceManager.ReleaseMaterial(_blitMaterial);
resourceDatabase.ReleaseMaterial(_material); resourceManager.ReleaseMaterial(_material);
resourceDatabase.ReleaseShader(_shader); resourceManager.ReleaseShader(_shader);
resourceDatabase.ReleaseMesh(_mesh); resourceManager.ReleaseMesh(_mesh);
resourceDatabase.ReleaseSampler(_sampler); resourceManager.ResourceDatabase.ReleaseSampler(_sampler);
if (_textures != null) if (_textures != null)
{ {
foreach (var texture in _textures) foreach (var texture in _textures)
{ {
resourceDatabase.ScheduleReleaseResource(texture.AsResource()); resourceManager.ResourceDatabase.ScheduleReleaseResource(texture.AsResource());
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.D3D12;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -23,50 +24,6 @@ public struct RenderingConfig
} }
} }
public interface IFenceSynchronizer
{
uint CPUFenceValue
{
get;
}
uint GPUFenceValue
{
get;
}
uint FrameIndex
{
get;
}
uint MaxFrameLatency
{
get;
}
bool WaitForGPUReady(int timeOut = -1);
void SignalCPUReady();
void WaitIdle();
}
public interface IRenderSystem : IFenceSynchronizer, IDisposable
{
IGraphicsEngine GraphicsEngine
{
get;
}
bool IsRunning
{
get;
}
void Start();
void Stop();
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
}
/// <summary> /// <summary>
/// Application-level render system that orchestrates multiple renderers /// Application-level render system that orchestrates multiple renderers
/// and handles frame synchronization /// and handles frame synchronization
@@ -130,11 +87,25 @@ internal class RenderSystem : IRenderSystem
public RenderSystem(RenderingConfig config) public RenderSystem(RenderingConfig config)
{ {
_config = config; _config = config;
_graphicsEngine = config.GraphicsAPI switch
switch (config.GraphicsAPI)
{ {
GraphicsAPI.Direct3D12 => new D3D12.D3D12GraphicsEngine(this), case GraphicsAPI.Direct3D12:
_ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.") if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
}; {
_graphicsEngine = D3D12GraphicsEngineFactory.Create(this);
}
else
{
// TODO: Fallback to Vulkan once it's implemented.
throw new PlatformNotSupportedException("Direct3D12 requires Windows 10 version 2004 (build 19041) or later.");
}
break;
default:
throw new NotSupportedException($"The specified graphics API '{config.GraphicsAPI}' is not supported.");
}
// Create frame resources for synchronization // Create frame resources for synchronization
_frameResources = new FrameResource[config.FrameBufferCount]; _frameResources = new FrameResource[config.FrameBufferCount];

View File

@@ -0,0 +1,330 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics;
public interface IResourceManager
{
IResourceAllocator ResourceAllocator
{
get;
}
IResourceDatabase ResourceDatabase
{
get;
}
/// <summary>
/// Creates a new mesh from the specified vertex and index data.
/// </summary>
/// <param name="vertices">A UnsafeList containing the vertices that define the geometry of the mesh. Must contain at least one vertex.</param>
/// <param name="indices">A UnsafeList containing the indices that specify how vertices are connected to form primitives. Must contain at least one index.</param>
/// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns>
Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices);
/// <summary>
/// Creates a new material instance using the specified shader.
/// </summary>
/// <param name="shader">The identifier of the shader to associate with the new material.</param>
/// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns>
Handle<Material> CreateMaterial(Identifier<Shader> shader);
/// <summary>
/// Creates a new shader and returns its unique identifier.
/// </summary>
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor);
/// <summary>
/// Determines whether a mesh with the specified Handle exists.
/// </summary>
/// <param name="handle">The handle of the mesh to check for existence. Cannot be null.</param>
/// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns>
bool HasMesh(Handle<Mesh> handle);
/// <summary>
/// Returns a reference to the mesh associated with the specified handle.
/// </summary>
/// <param name="handle">The handle of the mesh to retrieve. Must refer to a valid mesh; otherwise, the behavior is undefined.</param>
/// <returns>A result containing a reference to the mesh corresponding to the specified handle, or an error status if the handle is invalid.</returns>
RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle);
/// <summary>
/// Releases the mesh resource associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources.
/// </summary>
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
void ReleaseMesh(Handle<Mesh> handle);
/// <summary>
/// Determines whether a material with the specified handle exists in the collection.
/// </summary>
/// <param name="handle">The handle of the material to check for existence.</param>
/// <returns>true if a material with the specified handle exists; otherwise, false.</returns>
bool HasMaterial(Handle<Material> handle);
/// <summary>
/// Gets a reference to the material associated with the specified handle.
/// </summary>
/// <param name="handle">The handle of the material to retrieve. Must refer to a valid material.</param>
/// <returns>A result containing a reference to the material corresponding to the specified handle, or an error status if the handle is invalid.</returns>
RefResult<Material, Error> GetMaterialReference(Handle<Material> handle);
/// <summary>
/// Releases the material associated with the specified handle, making it available for reuse or disposal.
/// </summary>
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
void ReleaseMaterial(Handle<Material> handle);
/// <summary>
/// Determines whether a shader with the specified identifier exists in the collection.
/// </summary>
/// <param name="id">The identifier of the shader to check for existence.</param>
/// <returns>true if a shader with the specified identifier exists; otherwise, false.</returns>
bool HasShader(Identifier<Shader> id);
/// <summary>
/// Returns a reference to the shader associated with the specified identifier.
/// </summary>
/// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
/// <returns>A result containing a reference to the shader corresponding to the specified identifier, or an error status if the identifier is invalid.</returns>
RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id);
/// <summary>
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
/// </summary>
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
void ReleaseShader(Identifier<Shader> id);
}
internal sealed class ResourceManager : IResourceManager, IDisposable
{
private readonly IResourceAllocator _resourceAllocator;
private readonly IResourceDatabase _resourceDatabase;
private UnsafeSlotMap<Mesh> _meshes;
private UnsafeSlotMap<Material> _materials;
private UnsafeList<Shader> _shaders; // TODO: Use SlotMap?
private bool _disposed;
public IResourceAllocator ResourceAllocator => _resourceAllocator;
public IResourceDatabase ResourceDatabase => _resourceDatabase;
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
{
_resourceAllocator = resourceAllocator;
_resourceDatabase = resourceDatabase;
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent);
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
}
~ResourceManager()
{
Dispose();
}
public unsafe Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var vertexBufferDesc = new BufferDesc
{
Size = (uint)(vertices.Count * sizeof(Vertex)),
Stride = (uint)sizeof(Vertex),
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default,
};
var indexBufferDesc = new BufferDesc
{
Size = (uint)(indices.Count * sizeof(uint)),
Stride = sizeof(uint),
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default,
};
var objectBufferDesc = new BufferDesc
{
Size = (uint)sizeof(PerObjectData),
Stride = (uint)sizeof(PerObjectData),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Default,
};
var vertexBuffer = _resourceAllocator.CreateBuffer(in vertexBufferDesc, "VertexBuffer");
var indexBuffer = _resourceAllocator.CreateBuffer(in indexBufferDesc, "IndexBuffer");
var objectBuffer = _resourceAllocator.CreateBuffer(in objectBufferDesc, "ObjectBuffer");
var mesh = new Mesh
{
Vertices = vertices,
Indices = indices,
VertexBuffer = vertexBuffer,
IndexBuffer = indexBuffer,
ObjectDataBuffer = objectBuffer,
};
var id = _meshes.Add(mesh, out var generation);
return new Handle<Mesh>(id, generation);
}
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var material = new Material();
if (material.SetShader(shader, this) != Error.None)
{
return Handle<Material>.Invalid;
}
var id = _materials.Add(material, out var generation);
return new Handle<Material>(id, generation);
}
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var shader = new Shader(descriptor);
var id = _shaders.Count;
_shaders.Add(shader);
return new Identifier<Shader>(id);
}
public bool HasMesh(Handle<Mesh> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _meshes.Contains(handle.ID, handle.Generation);
}
public RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle)
{
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return Error.NotFound;
}
return RefResult<Mesh, Error>.Success(ref mesh);
}
public void ReleaseMesh(Handle<Mesh> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return;
}
ReleaseResource(mesh);
_meshes.Remove(handle.ID, handle.Generation);
}
public bool HasMaterial(Handle<Material> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _materials.Contains(handle.ID, handle.Generation);
}
public RefResult<Material, Error> GetMaterialReference(Handle<Material> handle)
{
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return Error.NotFound;
}
return RefResult<Material, Error>.Success(ref material);
}
public void ReleaseMaterial(Handle<Material> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
if (!exist)
{
return;
}
ReleaseResource(material);
_materials.Remove(handle.ID, handle.Generation);
}
public bool HasShader(Identifier<Shader> id)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return id.Value >= 0 && id.Value < _shaders.Count;
}
public RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id)
{
if (!HasShader(id))
{
return Error.NotFound;
}
return RefResult<Shader, Error>.Success(ref _shaders[id.Value]);
}
public void ReleaseShader(Identifier<Shader> id)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (!HasShader(id))
{
return;
}
ref var shader = ref _shaders[id.Value]!;
ReleaseResource(shader);
}
private void ReleaseResource<T>(T resource)
where T : IResourceReleasable
{
resource.ReleaseResource(_resourceDatabase);
}
public void Dispose()
{
if (_disposed)
{
return;
}
foreach (var mesh in _meshes)
{
ReleaseResource(mesh);
}
foreach (var material in _materials)
{
ReleaseResource(material);
}
foreach (var shader in _shaders)
{
ReleaseResource(shader);
}
_meshes.Dispose();
_materials.Dispose();
_shaders.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -1,11 +1,11 @@
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.Utilities; namespace Ghost.Graphics.Utilities;
public unsafe static class MeshBuilder public static unsafe class MeshBuilder
{ {
/// <summary> /// <summary>
/// Creates a unit cube centered at the origin with size 1. /// Creates a unit cube centered at the origin with size 1.

View File

@@ -32,13 +32,22 @@ public partial class UnitTestApp : Application
OperatingSystem.IsMacOS() ? "osx" : "unknown"; OperatingSystem.IsMacOS() ? "osx" : "unknown";
var arch = Environment.Is64BitProcess ? "x64" : "x86"; var arch = Environment.Is64BitProcess ? "x64" : "x86";
var nativeDllDir = Path.Combine(currentDir, "runtimes", platform + "-" + arch, "native"); var nativeDllDir = Path.Combine(currentDir, "runtimes", platform + "-" + arch, "native");
if (Directory.Exists(nativeDllDir)) //if (Directory.Exists(nativeDllDir))
//{
// foreach (var dll in Directory.EnumerateFiles(nativeDllDir, "*.dll"))
// {
// NativeLibrary.Load(dll);
// }
//}
NativeLibrary.SetDllImportResolver(typeof(UnitTestApp).Assembly, (libraryName, assembly, searchPath) =>
{ {
foreach (var dll in Directory.EnumerateFiles(nativeDllDir, "*.dll")) if (libraryName == "dxcompiler")
{ {
NativeLibrary.Load(dll); NativeLibrary.Load(Path.Combine(nativeDllDir, "dxil.dll"));
} }
}
return IntPtr.Zero;
});
} }
/// <summary> /// <summary>

View File

@@ -6,6 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PublishAot>True</PublishAot>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>