mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-27 05:01:07 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 452529985f |
+1
-1
@@ -14,7 +14,7 @@ indent_size = 4
|
|||||||
indent_style = space
|
indent_style = space
|
||||||
tab_width = 4
|
tab_width = 4
|
||||||
|
|
||||||
[*.{md,json,yml,xml,props,csproj}]
|
[*.{md,json,yml,props,csproj}]
|
||||||
|
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
tab_width = 2
|
tab_width = 2
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
@@ -26,7 +26,6 @@ arm64/
|
|||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
|
||||||
.packages
|
|
||||||
packages/
|
packages/
|
||||||
*.nupkg
|
*.nupkg
|
||||||
.nuget/
|
.nuget/
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
Copyright (C) 2026 SharpEmu Emulator Project
|
Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
@@ -9,7 +10,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Iced" Version="1.21.0" />
|
<PackageVersion Include="Iced" Version="1.21.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
|
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Windowing" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Windowing" Version="2.23.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
+1
-3
@@ -3,14 +3,12 @@ version = 1
|
|||||||
[[annotations]]
|
[[annotations]]
|
||||||
path = [
|
path = [
|
||||||
"REUSE.toml",
|
"REUSE.toml",
|
||||||
"nuget.config",
|
|
||||||
"global.json",
|
"global.json",
|
||||||
"**/packages.lock.json",
|
"**/packages.lock.json",
|
||||||
"scripts/ps5_names.txt",
|
"scripts/ps5_names.txt",
|
||||||
"src/SharpEmu.HLE/Aerolib/aerolib.bin",
|
"src/SharpEmu.HLE/Aerolib/aerolib.bin",
|
||||||
"_logs/**",
|
"_logs/**",
|
||||||
".github/images/**",
|
".github/images/**"
|
||||||
"assets/images/**"
|
|
||||||
]
|
]
|
||||||
precedence = "aggregate"
|
precedence = "aggregate"
|
||||||
SPDX-FileCopyrightText = "SharpEmu Emulator Project"
|
SPDX-FileCopyrightText = "SharpEmu Emulator Project"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB |
@@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<configuration>
|
|
||||||
<config>
|
|
||||||
<add key="globalPackagesFolder" value=".packages" />
|
|
||||||
</config>
|
|
||||||
|
|
||||||
<packageSources>
|
|
||||||
<clear />
|
|
||||||
<add key="Nuget" value="https://api.nuget.org/v3/index.json" />
|
|
||||||
</packageSources>
|
|
||||||
|
|
||||||
<packageSourceMapping>
|
|
||||||
<packageSource key="Nuget">
|
|
||||||
<package pattern="*" />
|
|
||||||
</packageSource>
|
|
||||||
</packageSourceMapping>
|
|
||||||
</configuration>
|
|
||||||
@@ -16,7 +16,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
<!-- <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> -->
|
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
@@ -30,11 +30,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-x64' Or '$(RuntimeIdentifier)' == ''">
|
|
||||||
<ApplicationIcon>..\..\assets\images\SharpEmu.ico</ApplicationIcon>
|
|
||||||
<Win32Icon>..\..\assets\images\SharpEmu.ico</Win32Icon>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="..\..\LICENSE.txt">
|
<Content Include="..\..\LICENSE.txt">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
|||||||
@@ -81,7 +81,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"SharpEmu.HLE": "[1.0.0, )",
|
"SharpEmu.HLE": "[1.0.0, )",
|
||||||
"Silk.NET.Vulkan": "[2.23.0, )",
|
"Silk.NET.Vulkan": "[2.23.0, )",
|
||||||
"Silk.NET.Vulkan.Extensions.EXT": "[2.23.0, )",
|
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
|
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
|
||||||
"Silk.NET.Windowing": "[2.23.0, )"
|
"Silk.NET.Windowing": "[2.23.0, )"
|
||||||
}
|
}
|
||||||
@@ -104,16 +103,6 @@
|
|||||||
"Silk.NET.Core": "2.23.0"
|
"Silk.NET.Core": "2.23.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Silk.NET.Vulkan.Extensions.EXT": {
|
|
||||||
"type": "CentralTransitive",
|
|
||||||
"requested": "[2.23.0, )",
|
|
||||||
"resolved": "2.23.0",
|
|
||||||
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
|
|
||||||
"dependencies": {
|
|
||||||
"Silk.NET.Core": "2.23.0",
|
|
||||||
"Silk.NET.Vulkan": "2.23.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": {
|
"Silk.NET.Vulkan.Extensions.KHR": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.23.0, )",
|
"requested": "[2.23.0, )",
|
||||||
|
|||||||
@@ -338,15 +338,10 @@ public sealed partial class DirectExecutionBackend
|
|||||||
{
|
{
|
||||||
orbisGen2Result = DispatchBootstrapBridge();
|
orbisGen2Result = DispatchBootstrapBridge();
|
||||||
}
|
}
|
||||||
else if (string.Equals(importStubEntry.Nid, RuntimeStubNids.KernelDynlibDlsym, StringComparison.Ordinal) ||
|
else if (string.Equals(importStubEntry.Nid, RuntimeStubNids.KernelDynlibDlsym, StringComparison.Ordinal))
|
||||||
string.Equals(importStubEntry.Nid, "LwG8g3niqwA", StringComparison.Ordinal))
|
|
||||||
{
|
{
|
||||||
orbisGen2Result = DispatchKernelDynlibDlsym();
|
orbisGen2Result = DispatchKernelDynlibDlsym();
|
||||||
}
|
}
|
||||||
else if (string.Equals(importStubEntry.Nid, "r8mvOaWdi28", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
orbisGen2Result = DispatchIl2CppApiLookupSymbol();
|
|
||||||
}
|
|
||||||
else if (importStubEntry.Export is { } cachedExport &&
|
else if (importStubEntry.Export is { } cachedExport &&
|
||||||
(cachedExport.Target & cpuContext.TargetGeneration) != 0)
|
(cachedExport.Target & cpuContext.TargetGeneration) != 0)
|
||||||
{
|
{
|
||||||
@@ -1185,11 +1180,8 @@ public sealed partial class DirectExecutionBackend
|
|||||||
cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
|
cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
|
||||||
return OrbisGen2Result.ORBIS_GEN2_OK;
|
return OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
if (!TryResolveRuntimeSymbolAddress(symbolName, out var resolvedAddress) &&
|
if (!TryResolveRuntimeSymbolAddress(symbolName, out var resolvedAddress))
|
||||||
!TryResolveRuntimeSymbolAlias(symbolName, out resolvedAddress))
|
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] sceKernelDlsym failed: handle=0x{cpuContext[CpuRegister.Rdi]:X} symbol='{symbolName}'");
|
|
||||||
cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
|
cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
|
||||||
return OrbisGen2Result.ORBIS_GEN2_OK;
|
return OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
@@ -1202,62 +1194,6 @@ public sealed partial class DirectExecutionBackend
|
|||||||
return OrbisGen2Result.ORBIS_GEN2_OK;
|
return OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryResolveRuntimeSymbolAlias(string symbolName, out ulong address)
|
|
||||||
{
|
|
||||||
address = 0;
|
|
||||||
var alias = symbolName switch
|
|
||||||
{
|
|
||||||
"scriptingGetMem" => "malloc",
|
|
||||||
"scriptingFreeMem" => "free",
|
|
||||||
"scriptingRealloc" => "realloc",
|
|
||||||
"scriptingCalloc" => "calloc",
|
|
||||||
_ => null,
|
|
||||||
};
|
|
||||||
|
|
||||||
return alias != null && TryResolveRuntimeSymbolAddress(alias, out address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OrbisGen2Result DispatchIl2CppApiLookupSymbol()
|
|
||||||
{
|
|
||||||
var cpuContext = ActiveCpuContext;
|
|
||||||
if (cpuContext == null)
|
|
||||||
{
|
|
||||||
return OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
var symbolNameAddress = cpuContext[CpuRegister.Rdi];
|
|
||||||
var outputAddress = cpuContext[CpuRegister.Rsi];
|
|
||||||
if (!TryReadAsciiZ(symbolNameAddress, 512, out var symbolName) ||
|
|
||||||
outputAddress == 0 ||
|
|
||||||
!TryResolveIl2CppApiAddress(symbolName, out var resolvedAddress) ||
|
|
||||||
!TryWriteUInt64Compat(outputAddress, resolvedAddress))
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][WARN] il2cpp_api_lookup_symbol failed: name='{symbolName}' out=0x{outputAddress:X16}");
|
|
||||||
if (outputAddress != 0)
|
|
||||||
{
|
|
||||||
_ = TryWriteUInt64Compat(outputAddress, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuContext[CpuRegister.Rax] = ulong.MaxValue;
|
|
||||||
return OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuContext[CpuRegister.Rax] = 0;
|
|
||||||
return OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryResolveIl2CppApiAddress(string symbolName, out ulong address)
|
|
||||||
{
|
|
||||||
if (TryResolveRuntimeSymbolAddress(symbolName, out address))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Aerolib.Instance.TryGetByExportName(symbolName, out var symbol) &&
|
|
||||||
TryResolveRuntimeSymbolAddress(symbol.Nid, out address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OrbisGen2Result DispatchBootstrapBridge()
|
private OrbisGen2Result DispatchBootstrapBridge()
|
||||||
{
|
{
|
||||||
var cpuContext = ActiveCpuContext;
|
var cpuContext = ActiveCpuContext;
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterWriteLock();
|
_gate.EnterWriteLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
InsertRegionSorted(new MemoryRegion
|
_regions.Add(new MemoryRegion
|
||||||
{
|
{
|
||||||
VirtualAddress = actualAddress,
|
VirtualAddress = actualAddress,
|
||||||
Size = alignedSize,
|
Size = alignedSize,
|
||||||
@@ -210,7 +210,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterWriteLock();
|
_gate.EnterWriteLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
InsertRegionSorted(new MemoryRegion
|
_regions.Add(new MemoryRegion
|
||||||
{
|
{
|
||||||
VirtualAddress = actualAddress,
|
VirtualAddress = actualAddress,
|
||||||
Size = alignedSize,
|
Size = alignedSize,
|
||||||
@@ -473,34 +473,30 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterReadLock();
|
_gate.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var region = FindRegion(virtualAddress, (ulong)destination.Length);
|
foreach (var region in _regions)
|
||||||
if (region is not null &&
|
|
||||||
TryResolveRegionOffset(
|
|
||||||
virtualAddress,
|
|
||||||
(ulong)destination.Length,
|
|
||||||
region,
|
|
||||||
out var offset))
|
|
||||||
{
|
{
|
||||||
var srcPtr = (void*)(region.VirtualAddress + offset);
|
if (TryResolveRegionOffset(virtualAddress, (ulong)destination.Length, region, out var offset))
|
||||||
if (destination.IsEmpty)
|
|
||||||
{
|
{
|
||||||
return true;
|
var srcPtr = (void*)(region.VirtualAddress + offset);
|
||||||
}
|
if (destination.IsEmpty)
|
||||||
|
|
||||||
if (region.IsReservedOnly)
|
|
||||||
{
|
|
||||||
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
|
||||||
{
|
{
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region.IsReservedOnly)
|
||||||
|
{
|
||||||
|
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanReadWithoutProtectionChange((ulong)srcPtr, (ulong)destination.Length, region))
|
||||||
|
{
|
||||||
|
requiresExclusiveAccess = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!CanReadWithoutProtectionChange((ulong)srcPtr, (ulong)destination.Length, region))
|
|
||||||
{
|
|
||||||
requiresExclusiveAccess = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fixed (byte* destPtr = destination)
|
fixed (byte* destPtr = destination)
|
||||||
{
|
{
|
||||||
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)destination.Length, (nuint)destination.Length);
|
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)destination.Length, (nuint)destination.Length);
|
||||||
@@ -537,34 +533,30 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterReadLock();
|
_gate.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var region = FindRegion(virtualAddress, (ulong)source.Length);
|
foreach (var region in _regions)
|
||||||
if (region is not null &&
|
|
||||||
TryResolveRegionOffset(
|
|
||||||
virtualAddress,
|
|
||||||
(ulong)source.Length,
|
|
||||||
region,
|
|
||||||
out var offset))
|
|
||||||
{
|
{
|
||||||
var destPtr = (void*)(region.VirtualAddress + offset);
|
if (TryResolveRegionOffset(virtualAddress, (ulong)source.Length, region, out var offset))
|
||||||
if (source.IsEmpty)
|
|
||||||
{
|
{
|
||||||
return true;
|
var destPtr = (void*)(region.VirtualAddress + offset);
|
||||||
}
|
if (source.IsEmpty)
|
||||||
|
|
||||||
if (region.IsReservedOnly)
|
|
||||||
{
|
|
||||||
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
|
||||||
{
|
{
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region.IsReservedOnly)
|
||||||
|
{
|
||||||
|
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanWriteWithoutProtectionChange((ulong)destPtr, (ulong)source.Length, region))
|
||||||
|
{
|
||||||
|
requiresExclusiveAccess = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!CanWriteWithoutProtectionChange((ulong)destPtr, (ulong)source.Length, region))
|
|
||||||
{
|
|
||||||
requiresExclusiveAccess = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fixed (byte* srcPtr = source)
|
fixed (byte* srcPtr = source)
|
||||||
{
|
{
|
||||||
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)source.Length, (nuint)source.Length);
|
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)source.Length, (nuint)source.Length);
|
||||||
@@ -597,14 +589,13 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
|
|
||||||
private bool TryReadExclusive(ulong virtualAddress, Span<byte> destination)
|
private bool TryReadExclusive(ulong virtualAddress, Span<byte> destination)
|
||||||
{
|
{
|
||||||
var region = FindRegion(virtualAddress, (ulong)destination.Length);
|
foreach (var region in _regions)
|
||||||
if (region is not null &&
|
|
||||||
TryResolveRegionOffset(
|
|
||||||
virtualAddress,
|
|
||||||
(ulong)destination.Length,
|
|
||||||
region,
|
|
||||||
out var offset))
|
|
||||||
{
|
{
|
||||||
|
if (!TryResolveRegionOffset(virtualAddress, (ulong)destination.Length, region, out var offset))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var srcPtr = (void*)(region.VirtualAddress + offset);
|
var srcPtr = (void*)(region.VirtualAddress + offset);
|
||||||
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
||||||
{
|
{
|
||||||
@@ -646,14 +637,13 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
|
|
||||||
private bool TryWriteExclusive(ulong virtualAddress, ReadOnlySpan<byte> source)
|
private bool TryWriteExclusive(ulong virtualAddress, ReadOnlySpan<byte> source)
|
||||||
{
|
{
|
||||||
var region = FindRegion(virtualAddress, (ulong)source.Length);
|
foreach (var region in _regions)
|
||||||
if (region is not null &&
|
|
||||||
TryResolveRegionOffset(
|
|
||||||
virtualAddress,
|
|
||||||
(ulong)source.Length,
|
|
||||||
region,
|
|
||||||
out var offset))
|
|
||||||
{
|
{
|
||||||
|
if (!TryResolveRegionOffset(virtualAddress, (ulong)source.Length, region, out var offset))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var destPtr = (void*)(region.VirtualAddress + offset);
|
var destPtr = (void*)(region.VirtualAddress + offset);
|
||||||
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
||||||
{
|
{
|
||||||
@@ -709,9 +699,15 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterReadLock();
|
_gate.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return FindRegion(virtualAddress, 1) is not null
|
foreach (var region in _regions)
|
||||||
? (void*)virtualAddress
|
{
|
||||||
: null;
|
if (virtualAddress >= region.VirtualAddress &&
|
||||||
|
virtualAddress < region.VirtualAddress + region.Size)
|
||||||
|
{
|
||||||
|
return (void*)virtualAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -724,7 +720,14 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterReadLock();
|
_gate.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return FindRegion(virtualAddress, size) is not null;
|
foreach (var region in _regions)
|
||||||
|
{
|
||||||
|
if (TryResolveRegionOffset(virtualAddress, size, region, out _))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -734,48 +737,14 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
|
|
||||||
private MemoryRegion? FindRegion(ulong address, ulong size)
|
private MemoryRegion? FindRegion(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
var low = 0;
|
foreach (var region in _regions)
|
||||||
var high = _regions.Count - 1;
|
|
||||||
MemoryRegion? candidate = null;
|
|
||||||
while (low <= high)
|
|
||||||
{
|
{
|
||||||
var middle = low + ((high - low) >> 1);
|
if (TryResolveRegionOffset(address, size, region, out _))
|
||||||
var region = _regions[middle];
|
|
||||||
if (region.VirtualAddress <= address)
|
|
||||||
{
|
{
|
||||||
candidate = region;
|
return region;
|
||||||
low = middle + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
high = middle - 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return candidate is not null &&
|
|
||||||
TryResolveRegionOffset(address, size, candidate, out _)
|
|
||||||
? candidate
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InsertRegionSorted(MemoryRegion region)
|
|
||||||
{
|
|
||||||
var low = 0;
|
|
||||||
var high = _regions.Count;
|
|
||||||
while (low < high)
|
|
||||||
{
|
|
||||||
var middle = low + ((high - low) >> 1);
|
|
||||||
if (_regions[middle].VirtualAddress < region.VirtualAddress)
|
|
||||||
{
|
|
||||||
low = middle + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
high = middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_regions.Insert(low, region);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetOverlappingRegionEnd(ulong address, ulong size, out ulong overlapEnd)
|
private bool TryGetOverlappingRegionEnd(ulong address, ulong size, out ulong overlapEnd)
|
||||||
|
|||||||
@@ -543,7 +543,6 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
|
|||||||
{
|
{
|
||||||
Path.Combine(ebootDirectory, "sce_module"),
|
Path.Combine(ebootDirectory, "sce_module"),
|
||||||
Path.Combine(ebootDirectory, "sce_modules"),
|
Path.Combine(ebootDirectory, "sce_modules"),
|
||||||
Path.Combine(ebootDirectory, "Media", "Modules"),
|
|
||||||
}
|
}
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.Where(Directory.Exists)
|
.Where(Directory.Exists)
|
||||||
@@ -555,9 +554,7 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
|
|||||||
}
|
}
|
||||||
|
|
||||||
var allModulePaths = moduleDirectories
|
var allModulePaths = moduleDirectories
|
||||||
.SelectMany(directory => Directory
|
.SelectMany(Directory.EnumerateFiles)
|
||||||
.EnumerateFiles(directory)
|
|
||||||
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase))
|
|
||||||
.Where(path =>
|
.Where(path =>
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path);
|
||||||
@@ -565,6 +562,7 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
|
|||||||
string.Equals(extension, ".sprx", StringComparison.OrdinalIgnoreCase);
|
string.Equals(extension, ".sprx", StringComparison.OrdinalIgnoreCase);
|
||||||
})
|
})
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var modulePaths = allModulePaths
|
var modulePaths = allModulePaths
|
||||||
|
|||||||
@@ -72,7 +72,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"SharpEmu.HLE": "[1.0.0, )",
|
"SharpEmu.HLE": "[1.0.0, )",
|
||||||
"Silk.NET.Vulkan": "[2.23.0, )",
|
"Silk.NET.Vulkan": "[2.23.0, )",
|
||||||
"Silk.NET.Vulkan.Extensions.EXT": "[2.23.0, )",
|
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
|
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
|
||||||
"Silk.NET.Windowing": "[2.23.0, )"
|
"Silk.NET.Windowing": "[2.23.0, )"
|
||||||
}
|
}
|
||||||
@@ -89,16 +88,6 @@
|
|||||||
"Silk.NET.Core": "2.23.0"
|
"Silk.NET.Core": "2.23.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Silk.NET.Vulkan.Extensions.EXT": {
|
|
||||||
"type": "CentralTransitive",
|
|
||||||
"requested": "[2.23.0, )",
|
|
||||||
"resolved": "2.23.0",
|
|
||||||
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
|
|
||||||
"dependencies": {
|
|
||||||
"Silk.NET.Core": "2.23.0",
|
|
||||||
"Silk.NET.Vulkan": "2.23.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": {
|
"Silk.NET.Vulkan.Extensions.KHR": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.23.0, )",
|
"requested": "[2.23.0, )",
|
||||||
|
|||||||
@@ -155,16 +155,6 @@ public static class AgcExports
|
|||||||
(ulong Cs, ulong State, uint LocalX, uint LocalY, uint LocalZ),
|
(ulong Cs, ulong State, uint LocalX, uint LocalY, uint LocalZ),
|
||||||
byte[]> _computeSpirvCache = new();
|
byte[]> _computeSpirvCache = new();
|
||||||
private static readonly Dictionary<ulong, ulong> _shaderHeadersByCode = new();
|
private static readonly Dictionary<ulong, ulong> _shaderHeadersByCode = new();
|
||||||
private static readonly bool _traceAgc = string.Equals(
|
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"),
|
|
||||||
"1",
|
|
||||||
StringComparison.Ordinal);
|
|
||||||
private static readonly bool _traceAgcShader =
|
|
||||||
_traceAgc ||
|
|
||||||
string.Equals(
|
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC_SHADER"),
|
|
||||||
"1",
|
|
||||||
StringComparison.Ordinal);
|
|
||||||
private static long _dcbWriteDataTraceCount;
|
private static long _dcbWriteDataTraceCount;
|
||||||
private static long _dcbWaitRegMemTraceCount;
|
private static long _dcbWaitRegMemTraceCount;
|
||||||
private static long _createShaderTraceCount;
|
private static long _createShaderTraceCount;
|
||||||
@@ -1204,51 +1194,6 @@ public static class AgcExports
|
|||||||
return ReturnPointer(ctx, commandAddress);
|
return ReturnPointer(ctx, commandAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "q88lQ+GP5Yk",
|
|
||||||
ExportName = "sceAgcDcbDrawIndex",
|
|
||||||
Target = Generation.Gen5,
|
|
||||||
LibraryName = "libSceAgc")]
|
|
||||||
public static int DcbDrawIndex(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var commandBufferAddress = ctx[CpuRegister.Rdi];
|
|
||||||
var indexCount = (uint)ctx[CpuRegister.Rsi];
|
|
||||||
var indexAddress = ctx[CpuRegister.Rdx];
|
|
||||||
var modifier = (uint)ctx[CpuRegister.Rcx];
|
|
||||||
|
|
||||||
if (commandBufferAddress == 0 || modifier != 0x4000_0000)
|
|
||||||
{
|
|
||||||
return ReturnPointer(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryAllocateCommandDwords(ctx, commandBufferAddress, 5, out var baseCommand) ||
|
|
||||||
!TryWriteUInt32(ctx, baseCommand, Pm4(3, ItIndexBase, 0)) ||
|
|
||||||
!TryWriteUInt32(ctx, baseCommand + 4, (uint)indexAddress) ||
|
|
||||||
!TryWriteUInt32(ctx, baseCommand + 8, (uint)(indexAddress >> 32)) ||
|
|
||||||
!TryWriteUInt32(ctx, baseCommand + 12, Pm4(2, ItIndexBufferSize, 0)) ||
|
|
||||||
!TryWriteUInt32(ctx, baseCommand + 16, indexCount))
|
|
||||||
{
|
|
||||||
return ReturnPointer(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryAllocateCommandDwords(ctx, commandBufferAddress, 5, out var drawCommand) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand, Pm4(5, ItDrawIndex2, 0)) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand + 4, indexCount) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand + 8, 0) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand + 12, 0) ||
|
|
||||||
!TryWriteUInt32(ctx, drawCommand + 16, 0))
|
|
||||||
{
|
|
||||||
return ReturnPointer(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TraceAgc(
|
|
||||||
$"agc.dcb_draw_index buf=0x{commandBufferAddress:X16} " +
|
|
||||||
$"base=0x{baseCommand:X16} draw=0x{drawCommand:X16} " +
|
|
||||||
$"count={indexCount} index=0x{indexAddress:X16}");
|
|
||||||
|
|
||||||
return ReturnPointer(ctx, drawCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "Yw0jKSqop+E",
|
Nid = "Yw0jKSqop+E",
|
||||||
ExportName = "sceAgcDcbDrawIndexAuto",
|
ExportName = "sceAgcDcbDrawIndexAuto",
|
||||||
@@ -2051,88 +1996,6 @@ public static class AgcExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "uJziRsODk1c",
|
|
||||||
ExportName = "sceAgcDriverGetResourceRegistrationMaxNameLength",
|
|
||||||
Target = Generation.Gen5,
|
|
||||||
LibraryName = "libSceAgc")]
|
|
||||||
public static int DriverGetResourceRegistrationMaxNameLength(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var outAddress = ctx[CpuRegister.Rdi];
|
|
||||||
|
|
||||||
if (outAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryWriteUInt32(ctx, outAddress, 256))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
TraceAgc($"agc.driver_get_resource_registration_max_name_length out=0x{outAddress:X16} value=256");
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
private const uint DefaultAgcOwner = 1;
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "F0ZXt5q0ZTA",
|
|
||||||
ExportName = "sceAgcDriverGetDefaultOwner",
|
|
||||||
Target = Generation.Gen5,
|
|
||||||
LibraryName = "libSceAgc")]
|
|
||||||
public static int DriverGetDefaultOwner(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var ownerAddress = ctx[CpuRegister.Rdi];
|
|
||||||
|
|
||||||
if (ownerAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryWriteUInt32(ctx, ownerAddress, DefaultAgcOwner))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
TraceAgc($"agc.driver_get_default_owner out=0x{ownerAddress:X16} owner={DefaultAgcOwner}");
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "W5z4eZrjEas",
|
|
||||||
ExportName = "sceAgcDriverRegisterResource",
|
|
||||||
Target = Generation.Gen5,
|
|
||||||
LibraryName = "libSceAgc")]
|
|
||||||
public static int DriverRegisterResource(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var resourceAddress = ctx[CpuRegister.Rdi];
|
|
||||||
var owner = (uint)ctx[CpuRegister.Rsi];
|
|
||||||
var nameAddress = ctx[CpuRegister.Rdx];
|
|
||||||
var type = (uint)ctx[CpuRegister.R8];
|
|
||||||
var flags = (uint)ctx[CpuRegister.R9];
|
|
||||||
|
|
||||||
TraceAgc(
|
|
||||||
$"agc.driver_register_resource resource=0x{resourceAddress:X16} owner={owner} " +
|
|
||||||
$"name=0x{nameAddress:X16} type={type} flags={flags}");
|
|
||||||
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "-KRzWekV120",
|
|
||||||
ExportName = "sceAgcDriverUnknown_KRzWekV120",
|
|
||||||
Target = Generation.Gen5,
|
|
||||||
LibraryName = "libSceAgc")]
|
|
||||||
public static int DriverUnknownKRzWekV120(CpuContext ctx)
|
|
||||||
{
|
|
||||||
TraceAgc(
|
|
||||||
$"agc.driver_unknown_krz rdi=0x{ctx[CpuRegister.Rdi]:X16} " +
|
|
||||||
$"rsi=0x{ctx[CpuRegister.Rsi]:X16} rdx=0x{ctx[CpuRegister.Rdx]:X16} " +
|
|
||||||
$"rcx=0x{ctx[CpuRegister.Rcx]:X16} r8=0x{ctx[CpuRegister.R8]:X16} r9=0x{ctx[CpuRegister.R9]:X16}");
|
|
||||||
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "h9z6+0hEydk",
|
Nid = "h9z6+0hEydk",
|
||||||
ExportName = "sceAgcSuspendPoint",
|
ExportName = "sceAgcSuspendPoint",
|
||||||
@@ -3766,31 +3629,6 @@ public static class AgcExports
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isStorage &&
|
|
||||||
descriptor.Address != 0 &&
|
|
||||||
VulkanVideoPresenter.IsGuestImageAvailable(
|
|
||||||
descriptor.Address,
|
|
||||||
descriptor.Format,
|
|
||||||
descriptor.NumberType))
|
|
||||||
{
|
|
||||||
texture = new VulkanGuestDrawTexture(
|
|
||||||
descriptor.Address,
|
|
||||||
descriptor.Width,
|
|
||||||
descriptor.Height,
|
|
||||||
descriptor.Format,
|
|
||||||
descriptor.NumberType,
|
|
||||||
[],
|
|
||||||
IsFallback: false,
|
|
||||||
IsStorage: false,
|
|
||||||
MipLevels: descriptor.MipLevels,
|
|
||||||
MipLevel: mipLevel,
|
|
||||||
Pitch: sourceWidth,
|
|
||||||
TileMode: descriptor.TileMode,
|
|
||||||
DstSelect: descriptor.DstSelect,
|
|
||||||
Sampler: ToVulkanSampler(samplerDescriptor));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStorage)
|
if (isStorage)
|
||||||
{
|
{
|
||||||
var initialPixels = Array.Empty<byte>();
|
var initialPixels = Array.Empty<byte>();
|
||||||
@@ -5567,7 +5405,7 @@ public static class AgcExports
|
|||||||
|
|
||||||
private static void TraceAgc(string message)
|
private static void TraceAgc(string message)
|
||||||
{
|
{
|
||||||
if (!_traceAgc)
|
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -5577,7 +5415,8 @@ public static class AgcExports
|
|||||||
|
|
||||||
private static void TraceAgcShader(string message)
|
private static void TraceAgcShader(string message)
|
||||||
{
|
{
|
||||||
if (!_traceAgcShader)
|
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal) &&
|
||||||
|
!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC_SHADER"), "1", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
using SharpEmu.Libs.VideoOut;
|
using SharpEmu.Libs.VideoOut;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpEmu.Libs.Agc;
|
namespace SharpEmu.Libs.Agc;
|
||||||
@@ -14,14 +13,6 @@ internal static class Gen5ShaderTranslator
|
|||||||
private const int MaxInstructions = 4096;
|
private const int MaxInstructions = 4096;
|
||||||
private const int MinimumUserDataDwords = 16;
|
private const int MinimumUserDataDwords = 16;
|
||||||
private const int MaximumUserDataDwords = 256;
|
private const int MaximumUserDataDwords = 256;
|
||||||
private static readonly ConditionalWeakTable<object, ShaderDecodeCache> _decodeCaches = new();
|
|
||||||
|
|
||||||
private sealed class ShaderDecodeCache
|
|
||||||
{
|
|
||||||
public object Gate { get; } = new();
|
|
||||||
public Dictionary<ulong, Gen5ShaderProgram> Programs { get; } = new();
|
|
||||||
public Dictionary<ulong, Gen5ShaderMetadata?> Metadata { get; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly uint[] FullscreenBarycentricEs =
|
private static readonly uint[] FullscreenBarycentricEs =
|
||||||
[
|
[
|
||||||
@@ -126,51 +117,16 @@ internal static class Gen5ShaderTranslator
|
|||||||
uint userDataScalarRegisterBase = 0)
|
uint userDataScalarRegisterBase = 0)
|
||||||
{
|
{
|
||||||
state = default!;
|
state = default!;
|
||||||
error = string.Empty;
|
if (!TryDecodeProgram(ctx, shaderAddress, out var program, out error))
|
||||||
var cache = _decodeCaches.GetValue(ctx.Memory, static _ => new ShaderDecodeCache());
|
|
||||||
Gen5ShaderProgram? program;
|
|
||||||
lock (cache.Gate)
|
|
||||||
{
|
{
|
||||||
cache.Programs.TryGetValue(shaderAddress, out program);
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (program is null)
|
|
||||||
{
|
|
||||||
if (!TryDecodeProgram(ctx, shaderAddress, out program, out error))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (cache.Gate)
|
|
||||||
{
|
|
||||||
cache.Programs.TryAdd(shaderAddress, program);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Gen5ShaderMetadata? metadata = null;
|
Gen5ShaderMetadata? metadata = null;
|
||||||
if (shaderHeaderAddress != 0)
|
if (shaderHeaderAddress != 0 &&
|
||||||
|
Gen5ShaderMetadataReader.TryRead(ctx, shaderHeaderAddress, out var decodedMetadata))
|
||||||
{
|
{
|
||||||
var metadataCached = false;
|
metadata = decodedMetadata;
|
||||||
lock (cache.Gate)
|
|
||||||
{
|
|
||||||
metadataCached = cache.Metadata.TryGetValue(shaderHeaderAddress, out metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!metadataCached)
|
|
||||||
{
|
|
||||||
if (Gen5ShaderMetadataReader.TryRead(
|
|
||||||
ctx,
|
|
||||||
shaderHeaderAddress,
|
|
||||||
out var decodedMetadata))
|
|
||||||
{
|
|
||||||
metadata = decodedMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (cache.Gate)
|
|
||||||
{
|
|
||||||
cache.Metadata.TryAdd(shaderHeaderAddress, metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var userData = new uint[GetUserDataDwordCount(metadata)];
|
var userData = new uint[GetUserDataDwordCount(metadata)];
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace SharpEmu.Libs.Audio;
|
|
||||||
|
|
||||||
public static class AudioOutExports
|
|
||||||
{
|
|
||||||
private static readonly ConcurrentDictionary<int, PortState> Ports = new();
|
|
||||||
private static int _nextPortHandle;
|
|
||||||
|
|
||||||
private sealed record PortState(int UserId, int Type, uint BufferLength, uint Frequency, int Format);
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "JfEPXVxhFqA",
|
|
||||||
ExportName = "sceAudioOutInit",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceAudioOut")]
|
|
||||||
public static int AudioOutInit(CpuContext ctx) => SetReturn(ctx, 0);
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "ekNvsT22rsY",
|
|
||||||
ExportName = "sceAudioOutOpen",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceAudioOut")]
|
|
||||||
public static int AudioOutOpen(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var userId = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
||||||
var type = unchecked((int)ctx[CpuRegister.Rsi]);
|
|
||||||
var bufferLength = unchecked((uint)ctx[CpuRegister.Rcx]);
|
|
||||||
var frequency = unchecked((uint)ctx[CpuRegister.R8]);
|
|
||||||
var format = unchecked((int)ctx[CpuRegister.R9]);
|
|
||||||
if (bufferLength == 0 || frequency == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
var handle = Interlocked.Increment(ref _nextPortHandle);
|
|
||||||
Ports[handle] = new PortState(userId, type, bufferLength, frequency, format);
|
|
||||||
return SetReturn(ctx, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "s1--uE9mBFw",
|
|
||||||
ExportName = "sceAudioOutClose",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceAudioOut")]
|
|
||||||
public static int AudioOutClose(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
||||||
return SetReturn(
|
|
||||||
ctx,
|
|
||||||
Ports.TryRemove(handle, out _)
|
|
||||||
? 0
|
|
||||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "QOQtbeDqsT4",
|
|
||||||
ExportName = "sceAudioOutOutput",
|
|
||||||
Target = Generation.Gen5,
|
|
||||||
LibraryName = "libSceAudioOut")]
|
|
||||||
public static int AudioOutOutput(CpuContext ctx)
|
|
||||||
{
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "b+uAV89IlxE",
|
|
||||||
ExportName = "sceAudioOutSetVolume",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceAudioOut")]
|
|
||||||
public static int AudioOutSetVolume(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
||||||
return SetReturn(
|
|
||||||
ctx,
|
|
||||||
Ports.ContainsKey(handle)
|
|
||||||
? 0
|
|
||||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int SetReturn(CpuContext ctx, int result)
|
|
||||||
{
|
|
||||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
|
||||||
using SharpEmu.Libs.Kernel;
|
|
||||||
|
|
||||||
namespace SharpEmu.Libs.AvPlayer;
|
|
||||||
|
|
||||||
public static class AvPlayerExports
|
|
||||||
{
|
|
||||||
private const int InvalidParameters = unchecked((int)0x806A0001);
|
|
||||||
private static readonly object StateGate = new();
|
|
||||||
private static readonly HashSet<ulong> Players = new();
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "aS66RI0gGgo",
|
|
||||||
ExportName = "sceAvPlayerInit",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceAvPlayer")]
|
|
||||||
public static int AvPlayerInit(CpuContext ctx)
|
|
||||||
{
|
|
||||||
if (ctx[CpuRegister.Rdi] == 0 ||
|
|
||||||
!KernelMemoryCompatExports.TryAllocateHleData(ctx, 0x40, 16, out var handle))
|
|
||||||
{
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
Players.Add(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = handle;
|
|
||||||
return unchecked((int)handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "HD1YKVU26-M",
|
|
||||||
ExportName = "sceAvPlayerPostInit",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceAvPlayer")]
|
|
||||||
public static int AvPlayerPostInit(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var handle = ctx[CpuRegister.Rdi];
|
|
||||||
var dataAddress = ctx[CpuRegister.Rsi];
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
return SetReturn(
|
|
||||||
ctx,
|
|
||||||
handle != 0 && dataAddress != 0 && Players.Contains(handle)
|
|
||||||
? 0
|
|
||||||
: InvalidParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "NkJwDzKmIlw",
|
|
||||||
ExportName = "sceAvPlayerClose",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceAvPlayer")]
|
|
||||||
public static int AvPlayerClose(CpuContext ctx)
|
|
||||||
{
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
return SetReturn(
|
|
||||||
ctx,
|
|
||||||
Players.Remove(ctx[CpuRegister.Rdi]) ? 0 : InvalidParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int SetReturn(CpuContext ctx, int result)
|
|
||||||
{
|
|
||||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace SharpEmu.Libs.CommonDialog;
|
|
||||||
|
|
||||||
public static class MsgDialogExports
|
|
||||||
{
|
|
||||||
private const int AlreadyInitialized = unchecked((int)0x80B80002);
|
|
||||||
private static int _initialized;
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "lDqxaY1UbEo",
|
|
||||||
ExportName = "sceMsgDialogInitialize",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceMsgDialog")]
|
|
||||||
public static int MsgDialogInitialize(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var result = Interlocked.Exchange(ref _initialized, 1) == 0
|
|
||||||
? 0
|
|
||||||
: AlreadyInitialized;
|
|
||||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
|
||||||
|
|
||||||
namespace SharpEmu.Libs.Kernel;
|
|
||||||
|
|
||||||
public static class KernelExceptionCompatExports
|
|
||||||
{
|
|
||||||
private static readonly HashSet<int> AllowedSignals = new() { 1, 4, 8, 10, 11, 30 };
|
|
||||||
private static readonly Dictionary<int, ulong> _installedHandlers = new();
|
|
||||||
private static readonly object _gate = new();
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "WkwEd3N7w0Y",
|
|
||||||
ExportName = "sceKernelInstallExceptionHandler",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libKernel")]
|
|
||||||
public static int InstallExceptionHandler(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var signum = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
||||||
var handler = ctx[CpuRegister.Rsi];
|
|
||||||
|
|
||||||
if (!AllowedSignals.Contains(signum))
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
if (_installedHandlers.ContainsKey(signum))
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_ALREADY_EXISTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
_installedHandlers[signum] = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "Qhv5ARAoOEc",
|
|
||||||
ExportName = "sceKernelRemoveExceptionHandler",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libKernel")]
|
|
||||||
public static int RemoveExceptionHandler(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var signum = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
||||||
|
|
||||||
if (!AllowedSignals.Contains(signum))
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
_installedHandlers.Remove(signum);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -365,13 +365,6 @@ public static class KernelExports
|
|||||||
LibraryName = "libKernel")]
|
LibraryName = "libKernel")]
|
||||||
public static int KernelOpen(CpuContext ctx) => KernelMemoryCompatExports.KernelOpenUnderscore(ctx);
|
public static int KernelOpen(CpuContext ctx) => KernelMemoryCompatExports.KernelOpenUnderscore(ctx);
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "mqQMh1zPPT8",
|
|
||||||
ExportName = "fstat",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libc")]
|
|
||||||
public static int Fstat(CpuContext ctx) => KernelMemoryCompatExports.KernelFstat(ctx);
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "hcuQgD53UxM",
|
Nid = "hcuQgD53UxM",
|
||||||
ExportName = "printf",
|
ExportName = "printf",
|
||||||
|
|||||||
@@ -103,7 +103,6 @@ public static class KernelMemoryCompatExports
|
|||||||
private static readonly Dictionary<ulong, DirectAllocation> _directAllocations = new();
|
private static readonly Dictionary<ulong, DirectAllocation> _directAllocations = new();
|
||||||
private static readonly Dictionary<ulong, LibcHeapAllocation> _libcAllocations = new();
|
private static readonly Dictionary<ulong, LibcHeapAllocation> _libcAllocations = new();
|
||||||
private static readonly Dictionary<ulong, MappedRegion> _mappedRegions = new();
|
private static readonly Dictionary<ulong, MappedRegion> _mappedRegions = new();
|
||||||
private static readonly Dictionary<ulong, string> _mappedRegionNames = new();
|
|
||||||
private static readonly Dictionary<ulong, ulong> _tlsModuleBlocks = new();
|
private static readonly Dictionary<ulong, ulong> _tlsModuleBlocks = new();
|
||||||
private static readonly Dictionary<string, string> _guestMounts = new(StringComparer.OrdinalIgnoreCase);
|
private static readonly Dictionary<string, string> _guestMounts = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private static readonly HashSet<string> _tracedStatResults = new(StringComparer.Ordinal);
|
private static readonly HashSet<string> _tracedStatResults = new(StringComparer.Ordinal);
|
||||||
@@ -232,25 +231,6 @@ public static class KernelMemoryCompatExports
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void RegisterReservedVirtualRange(ulong address, ulong length)
|
|
||||||
{
|
|
||||||
if (address == 0 || length == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_memoryGate)
|
|
||||||
{
|
|
||||||
_mappedRegions[address] = new MappedRegion(
|
|
||||||
address,
|
|
||||||
length,
|
|
||||||
Protection: 0,
|
|
||||||
IsFlexible: false,
|
|
||||||
IsDirect: false,
|
|
||||||
DirectStart: 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "8zTFvBIAIN8",
|
Nid = "8zTFvBIAIN8",
|
||||||
ExportName = "memset",
|
ExportName = "memset",
|
||||||
@@ -2455,36 +2435,6 @@ public static class KernelMemoryCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "hwVSPCmp5tM",
|
|
||||||
ExportName = "sceKernelCheckedReleaseDirectMemory",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libKernel")]
|
|
||||||
public static int KernelCheckedReleaseDirectMemory(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var start = ctx[CpuRegister.Rdi];
|
|
||||||
var length = ctx[CpuRegister.Rsi];
|
|
||||||
|
|
||||||
if (length == 0)
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_memoryGate)
|
|
||||||
{
|
|
||||||
if (!_directAllocations.TryGetValue(start, out var allocation) ||
|
|
||||||
allocation.Length != length)
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
_directAllocations.Remove(start);
|
|
||||||
_nextPhysicalAddress = GetDirectMemoryHighWaterMarkLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "L-Q3LEjIbgA",
|
Nid = "L-Q3LEjIbgA",
|
||||||
ExportName = "sceKernelMapDirectMemory",
|
ExportName = "sceKernelMapDirectMemory",
|
||||||
@@ -2664,16 +2614,6 @@ public static class KernelMemoryCompatExports
|
|||||||
return KernelMapNamedFlexibleMemory(ctx);
|
return KernelMapNamedFlexibleMemory(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "4h6F1LLbTiw",
|
|
||||||
ExportName = "sceKernelMapFlexibleMemoryInternal",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libKernel")]
|
|
||||||
public static int KernelMapFlexibleMemoryInternal(CpuContext ctx)
|
|
||||||
{
|
|
||||||
return KernelMapNamedFlexibleMemory(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "2SKEx6bSq-4",
|
Nid = "2SKEx6bSq-4",
|
||||||
ExportName = "sceKernelBatchMap",
|
ExportName = "sceKernelBatchMap",
|
||||||
@@ -2727,44 +2667,6 @@ public static class KernelMemoryCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "DGMG3JshrZU",
|
|
||||||
ExportName = "sceKernelSetVirtualRangeName",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libKernel")]
|
|
||||||
public static int KernelSetVirtualRangeName(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var address = ctx[CpuRegister.Rdi];
|
|
||||||
var length = ctx[CpuRegister.Rsi];
|
|
||||||
var nameAddress = ctx[CpuRegister.Rdx];
|
|
||||||
if (nameAddress == 0)
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryReadCString(ctx, nameAddress, OrbisKernelMaximumNameLength, out var nameBytes))
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = Encoding.UTF8.GetString(nameBytes);
|
|
||||||
lock (_memoryGate)
|
|
||||||
{
|
|
||||||
if (!TryFindVirtualQueryRegionLocked(address, findNext: false, out var region) ||
|
|
||||||
length > region.Length ||
|
|
||||||
address < region.Address ||
|
|
||||||
length > region.Address + region.Length - address)
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
_mappedRegionNames[region.Address] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "rVjRvHJ0X6c",
|
Nid = "rVjRvHJ0X6c",
|
||||||
ExportName = "sceKernelVirtualQuery",
|
ExportName = "sceKernelVirtualQuery",
|
||||||
@@ -2822,16 +2724,11 @@ public static class KernelMemoryCompatExports
|
|||||||
BinaryPrimitives.WriteInt32LittleEndian(payload[28..32], memoryType);
|
BinaryPrimitives.WriteInt32LittleEndian(payload[28..32], memoryType);
|
||||||
payload[32] = unchecked((byte)stateFlags);
|
payload[32] = unchecked((byte)stateFlags);
|
||||||
|
|
||||||
string name;
|
var name = region.IsDirect
|
||||||
lock (_memoryGate)
|
? "direct"
|
||||||
{
|
: region.IsFlexible
|
||||||
name = _mappedRegionNames.GetValueOrDefault(region.Address)
|
? "flexible"
|
||||||
?? (region.IsDirect
|
: string.Empty;
|
||||||
? "direct"
|
|
||||||
: region.IsFlexible
|
|
||||||
? "flexible"
|
|
||||||
: string.Empty);
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
{
|
{
|
||||||
var nameBytes = Encoding.ASCII.GetBytes(name);
|
var nameBytes = Encoding.ASCII.GetBytes(name);
|
||||||
|
|||||||
@@ -95,13 +95,6 @@ public static class KernelPthreadCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "7Xl257M4VNI",
|
|
||||||
ExportName = "pthread_equal",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libc")]
|
|
||||||
public static int PosixPthreadEqual(CpuContext ctx) => PthreadEqual(ctx);
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "T72hz6ffq08",
|
Nid = "T72hz6ffq08",
|
||||||
ExportName = "scePthreadYield",
|
ExportName = "scePthreadYield",
|
||||||
|
|||||||
@@ -717,35 +717,6 @@ public static class KernelPthreadExtendedCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "FXPWHNk8Of0",
|
|
||||||
ExportName = "scePthreadAttrGetschedparam",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libKernel")]
|
|
||||||
public static int PthreadAttrGetschedparam(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var attrAddress = ctx[CpuRegister.Rdi];
|
|
||||||
var schedParamAddress = ctx[CpuRegister.Rsi];
|
|
||||||
if (attrAddress == 0 || schedParamAddress == 0)
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
PthreadAttrState state;
|
|
||||||
lock (_stateGate)
|
|
||||||
{
|
|
||||||
state = GetOrCreateAttrStateLocked(attrAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryWriteInt32(ctx, schedParamAddress, state.SchedPriority))
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "DzES9hQF4f4",
|
Nid = "DzES9hQF4f4",
|
||||||
ExportName = "scePthreadAttrSetschedparam",
|
ExportName = "scePthreadAttrSetschedparam",
|
||||||
|
|||||||
@@ -752,7 +752,6 @@ public static class KernelRuntimeCompatExports
|
|||||||
_nextReservedVirtualBase = Math.Max(_nextReservedVirtualBase, mappedAddress + length);
|
_nextReservedVirtualBase = Math.Max(_nextReservedVirtualBase, mappedAddress + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelMemoryCompatExports.RegisterReservedVirtualRange(mappedAddress, length);
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,322 +0,0 @@
|
|||||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
|
||||||
using SharpEmu.Libs.Kernel;
|
|
||||||
using System.Buffers.Binary;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace SharpEmu.Libs.Ngs2;
|
|
||||||
|
|
||||||
public static class Ngs2Exports
|
|
||||||
{
|
|
||||||
private const int OrbisNgs2ErrorInvalidOutAddress = unchecked((int)0x804A0053);
|
|
||||||
private const int OrbisNgs2ErrorInvalidSystemHandle = unchecked((int)0x804A0230);
|
|
||||||
private const int OrbisNgs2ErrorInvalidRackHandle = unchecked((int)0x804A0261);
|
|
||||||
private const int OrbisNgs2ErrorInvalidVoiceHandle = unchecked((int)0x804A0300);
|
|
||||||
private const ulong HandleStorageSize = 0x20;
|
|
||||||
private const int RenderBufferInfoSize = 0x18;
|
|
||||||
private const ulong MaximumRenderBufferSize = 16 * 1024 * 1024;
|
|
||||||
|
|
||||||
private static readonly object StateGate = new();
|
|
||||||
private static readonly Dictionary<ulong, SystemState> Systems = new();
|
|
||||||
private static readonly Dictionary<ulong, RackState> Racks = new();
|
|
||||||
private static readonly Dictionary<ulong, VoiceState> Voices = new();
|
|
||||||
private static long _nextUid;
|
|
||||||
private static long _renderCount;
|
|
||||||
|
|
||||||
private sealed record SystemState(uint Uid);
|
|
||||||
private sealed record RackState(ulong SystemHandle, uint RackId);
|
|
||||||
private sealed record VoiceState(ulong RackHandle, uint VoiceIndex);
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "mPYgU4oYpuY",
|
|
||||||
ExportName = "sceNgs2SystemCreateWithAllocator",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceNgs2")]
|
|
||||||
public static int Ngs2SystemCreateWithAllocator(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var outHandleAddress = ctx[CpuRegister.Rdx];
|
|
||||||
if (outHandleAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryCreateHandle(ctx, type: 1, ownerHandle: 0, out var handle) ||
|
|
||||||
!ctx.TryWriteUInt64(outHandleAddress, handle))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
Systems[handle] = new SystemState(unchecked((uint)Interlocked.Increment(ref _nextUid)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "u-WrYDaJA3k",
|
|
||||||
ExportName = "sceNgs2SystemDestroy",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceNgs2")]
|
|
||||||
public static int Ngs2SystemDestroy(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var handle = ctx[CpuRegister.Rdi];
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
if (!Systems.Remove(handle))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
var rackHandles = Racks
|
|
||||||
.Where(pair => pair.Value.SystemHandle == handle)
|
|
||||||
.Select(pair => pair.Key)
|
|
||||||
.ToArray();
|
|
||||||
foreach (var rackHandle in rackHandles)
|
|
||||||
{
|
|
||||||
RemoveRackLocked(rackHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "U546k6orxQo",
|
|
||||||
ExportName = "sceNgs2RackCreateWithAllocator",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceNgs2")]
|
|
||||||
public static int Ngs2RackCreateWithAllocator(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var systemHandle = ctx[CpuRegister.Rdi];
|
|
||||||
var rackId = unchecked((uint)ctx[CpuRegister.Rsi]);
|
|
||||||
var outHandleAddress = ctx[CpuRegister.R8];
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
if (!Systems.ContainsKey(systemHandle))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outHandleAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryCreateHandle(ctx, type: 2, systemHandle, out var handle) ||
|
|
||||||
!ctx.TryWriteUInt64(outHandleAddress, handle))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
Racks[handle] = new RackState(systemHandle, rackId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "lCqD7oycmIM",
|
|
||||||
ExportName = "sceNgs2RackDestroy",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceNgs2")]
|
|
||||||
public static int Ngs2RackDestroy(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var handle = ctx[CpuRegister.Rdi];
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
if (!Racks.ContainsKey(handle))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidRackHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveRackLocked(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "MwmHz8pAdAo",
|
|
||||||
ExportName = "sceNgs2RackGetVoiceHandle",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceNgs2")]
|
|
||||||
public static int Ngs2RackGetVoiceHandle(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var rackHandle = ctx[CpuRegister.Rdi];
|
|
||||||
var voiceIndex = unchecked((uint)ctx[CpuRegister.Rsi]);
|
|
||||||
var outHandleAddress = ctx[CpuRegister.Rdx];
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
if (!Racks.ContainsKey(rackHandle))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidRackHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
var existing = Voices.FirstOrDefault(
|
|
||||||
pair => pair.Value.RackHandle == rackHandle && pair.Value.VoiceIndex == voiceIndex);
|
|
||||||
if (existing.Key != 0)
|
|
||||||
{
|
|
||||||
return ctx.TryWriteUInt64(outHandleAddress, existing.Key)
|
|
||||||
? SetReturn(ctx, 0)
|
|
||||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outHandleAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryCreateHandle(ctx, type: 4, rackHandle, out var handle) ||
|
|
||||||
!ctx.TryWriteUInt64(outHandleAddress, handle))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
Voices[handle] = new VoiceState(rackHandle, voiceIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "uu94irFOGpA",
|
|
||||||
ExportName = "sceNgs2VoiceControl",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceNgs2")]
|
|
||||||
public static int Ngs2VoiceControl(CpuContext ctx)
|
|
||||||
{
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
return SetReturn(
|
|
||||||
ctx,
|
|
||||||
Voices.ContainsKey(ctx[CpuRegister.Rdi]) ? 0 : OrbisNgs2ErrorInvalidVoiceHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "AbYvTOZ8Pts",
|
|
||||||
ExportName = "sceNgs2VoiceRunCommands",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceNgs2")]
|
|
||||||
public static int Ngs2VoiceRunCommands(CpuContext ctx) => Ngs2VoiceControl(ctx);
|
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "i0VnXM-C9fc",
|
|
||||||
ExportName = "sceNgs2SystemRender",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceNgs2")]
|
|
||||||
public static int Ngs2SystemRender(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var systemHandle = ctx[CpuRegister.Rdi];
|
|
||||||
var bufferInfoAddress = ctx[CpuRegister.Rsi];
|
|
||||||
var bufferInfoCount = unchecked((uint)ctx[CpuRegister.Rdx]);
|
|
||||||
lock (StateGate)
|
|
||||||
{
|
|
||||||
if (!Systems.ContainsKey(systemHandle))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bufferInfoCount != 0 && bufferInfoAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint i = 0; i < bufferInfoCount; i++)
|
|
||||||
{
|
|
||||||
var entryAddress = bufferInfoAddress + (i * RenderBufferInfoSize);
|
|
||||||
if (!ctx.TryReadUInt64(entryAddress, out var bufferAddress) ||
|
|
||||||
!ctx.TryReadUInt64(entryAddress + 8, out var bufferSize))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bufferAddress != 0 && bufferSize != 0)
|
|
||||||
{
|
|
||||||
if (bufferSize > MaximumRenderBufferSize || !TryClearGuestBuffer(ctx, bufferAddress, bufferSize))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = Interlocked.Increment(ref _renderCount);
|
|
||||||
if (ShouldTrace() && (count <= 4 || count % 10_000 == 0))
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][TRACE] ngs2.render#{count} system=0x{systemHandle:X16} buffers={bufferInfoCount}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryCreateHandle(CpuContext ctx, uint type, ulong ownerHandle, out ulong handle)
|
|
||||||
{
|
|
||||||
handle = 0;
|
|
||||||
if (!KernelMemoryCompatExports.TryAllocateHleData(ctx, HandleStorageSize, 16, out handle))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<byte> data = stackalloc byte[(int)HandleStorageSize];
|
|
||||||
data.Clear();
|
|
||||||
BinaryPrimitives.WriteUInt64LittleEndian(data[0..8], handle);
|
|
||||||
BinaryPrimitives.WriteUInt64LittleEndian(data[8..16], ownerHandle);
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(data[16..20], 1);
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(data[24..28], type);
|
|
||||||
return ctx.Memory.TryWrite(handle, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryClearGuestBuffer(CpuContext ctx, ulong address, ulong length)
|
|
||||||
{
|
|
||||||
Span<byte> zeroes = stackalloc byte[4096];
|
|
||||||
zeroes.Clear();
|
|
||||||
for (ulong offset = 0; offset < length;)
|
|
||||||
{
|
|
||||||
var chunkSize = (int)Math.Min((ulong)zeroes.Length, length - offset);
|
|
||||||
if (!ctx.Memory.TryWrite(address + offset, zeroes[..chunkSize]))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += unchecked((uint)chunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RemoveRackLocked(ulong rackHandle)
|
|
||||||
{
|
|
||||||
Racks.Remove(rackHandle);
|
|
||||||
foreach (var voiceHandle in Voices
|
|
||||||
.Where(pair => pair.Value.RackHandle == rackHandle)
|
|
||||||
.Select(pair => pair.Key)
|
|
||||||
.ToArray())
|
|
||||||
{
|
|
||||||
Voices.Remove(voiceHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ShouldTrace() =>
|
|
||||||
string.Equals(
|
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_NGS2"),
|
|
||||||
"1",
|
|
||||||
StringComparison.Ordinal);
|
|
||||||
|
|
||||||
private static int SetReturn(CpuContext ctx, int result)
|
|
||||||
{
|
|
||||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -160,16 +160,6 @@ public static class PadExports
|
|||||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "W2G-yoyMF5U",
|
|
||||||
ExportName = "scePadSetVibrationMode",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libScePad")]
|
|
||||||
public static int PadSetVibrationMode(CpuContext ctx)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool WriteNeutralPadData(CpuContext ctx, ulong dataAddress)
|
private static bool WriteNeutralPadData(CpuContext ctx, ulong dataAddress)
|
||||||
{
|
{
|
||||||
Span<byte> data = stackalloc byte[PadDataSize];
|
Span<byte> data = stackalloc byte[PadDataSize];
|
||||||
|
|||||||
@@ -244,36 +244,6 @@ public static class SaveDataExports
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int _nextTransactionResource;
|
|
||||||
[SysAbiExport(
|
|
||||||
Nid = "gjRZNnw0JPE",
|
|
||||||
ExportName = "sceSaveDataCreateTransactionResource",
|
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
|
||||||
LibraryName = "libSceSaveData")]
|
|
||||||
public static int SaveDataCreateTransactionResource(CpuContext ctx)
|
|
||||||
{
|
|
||||||
var userId = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
||||||
var reserved = ctx[CpuRegister.Rsi];
|
|
||||||
var resourceAddress = ctx[CpuRegister.Rdx];
|
|
||||||
|
|
||||||
if (resourceAddress == 0)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, OrbisSaveDataErrorParameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
var id = (uint)Interlocked.Increment(ref _nextTransactionResource);
|
|
||||||
|
|
||||||
if (!TryWriteUInt32(ctx, resourceAddress, id))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
TraceSaveData(
|
|
||||||
$"create_transaction_resource user={userId} reserved=0x{reserved:X} resource_addr=0x{resourceAddress:X} id={id}");
|
|
||||||
|
|
||||||
return SetReturn(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryReadSearchCond(CpuContext ctx, ulong address, out SearchCond cond)
|
private static bool TryReadSearchCond(CpuContext ctx, ulong address, out SearchCond cond)
|
||||||
{
|
{
|
||||||
cond = default;
|
cond = default;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Silk.NET.Vulkan" />
|
<PackageReference Include="Silk.NET.Vulkan" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
||||||
<PackageReference Include="Silk.NET.Windowing" />
|
<PackageReference Include="Silk.NET.Windowing" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
using SharpEmu.Libs.Kernel;
|
using SharpEmu.Libs.Kernel;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@@ -49,13 +48,6 @@ public static class VideoOutExports
|
|||||||
private static int _frameDumpCount;
|
private static int _frameDumpCount;
|
||||||
private static long _nextFrameDumpIndex;
|
private static long _nextFrameDumpIndex;
|
||||||
private static string _windowTitle = "SharpEmu VideoOut";
|
private static string _windowTitle = "SharpEmu VideoOut";
|
||||||
private static readonly bool _logFrameRate = string.Equals(
|
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_VIDEOOUT_FPS"),
|
|
||||||
"1",
|
|
||||||
StringComparison.Ordinal);
|
|
||||||
private static long _frameRateWindowStart = Stopwatch.GetTimestamp();
|
|
||||||
private static long _submittedFrameCount;
|
|
||||||
private static long _presentedFrameCount;
|
|
||||||
|
|
||||||
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version)
|
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version)
|
||||||
{
|
{
|
||||||
@@ -820,46 +812,9 @@ public static class VideoOutExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}");
|
TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}");
|
||||||
ReportFrameRate(presented: false);
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ReportPresentedFrame() =>
|
|
||||||
ReportFrameRate(presented: true);
|
|
||||||
|
|
||||||
private static void ReportFrameRate(bool presented)
|
|
||||||
{
|
|
||||||
if (!_logFrameRate)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (presented)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _presentedFrameCount);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _submittedFrameCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
var started = Volatile.Read(ref _frameRateWindowStart);
|
|
||||||
var now = Stopwatch.GetTimestamp();
|
|
||||||
var elapsedTicks = now - started;
|
|
||||||
if (elapsedTicks < Stopwatch.Frequency ||
|
|
||||||
Interlocked.CompareExchange(ref _frameRateWindowStart, now, started) != started)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var elapsedSeconds = (double)elapsedTicks / Stopwatch.Frequency;
|
|
||||||
var submitted = Interlocked.Exchange(ref _submittedFrameCount, 0);
|
|
||||||
var presentedCount = Interlocked.Exchange(ref _presentedFrameCount, 0);
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][PERF] videoout submitted_fps={submitted / elapsedSeconds:F1} " +
|
|
||||||
$"presented_fps={presentedCount / elapsedSeconds:F1}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int RegisterBufferRange(VideoOutPortState port, int startIndex, ReadOnlySpan<ulong> addresses, BufferAttribute attribute, int requestedGroupIndex = -1)
|
private static int RegisterBufferRange(VideoOutPortState port, int startIndex, ReadOnlySpan<ulong> addresses, BufferAttribute attribute, int requestedGroupIndex = -1)
|
||||||
{
|
{
|
||||||
lock (_stateGate)
|
lock (_stateGate)
|
||||||
|
|||||||
@@ -7,11 +7,8 @@ using Silk.NET.Maths;
|
|||||||
using SharpEmu.Libs.Agc;
|
using SharpEmu.Libs.Agc;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using Silk.NET.Vulkan.Extensions.EXT;
|
|
||||||
using Silk.NET.Windowing;
|
using Silk.NET.Windowing;
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||||
using VkSemaphore = Silk.NET.Vulkan.Semaphore;
|
using VkSemaphore = Silk.NET.Vulkan.Semaphore;
|
||||||
@@ -600,24 +597,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsGuestImageAvailable(
|
|
||||||
ulong address,
|
|
||||||
uint format,
|
|
||||||
uint numberType)
|
|
||||||
{
|
|
||||||
var guestFormat = GetGuestTextureFormat(format, numberType);
|
|
||||||
if (address == 0 || guestFormat == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
return _availableGuestImages.TryGetValue(address, out var availableFormat) &&
|
|
||||||
availableFormat == guestFormat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TrySubmitGuestImageBlit(
|
public static bool TrySubmitGuestImageBlit(
|
||||||
ulong sourceAddress,
|
ulong sourceAddress,
|
||||||
uint sourceWidth,
|
uint sourceWidth,
|
||||||
@@ -847,8 +826,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private delegate* unmanaged<CommandBuffer, void> _cmdEndDebugUtilsLabel;
|
private delegate* unmanaged<CommandBuffer, void> _cmdEndDebugUtilsLabel;
|
||||||
private Instance _instance;
|
private Instance _instance;
|
||||||
private SurfaceKHR _surface;
|
private SurfaceKHR _surface;
|
||||||
private DebugUtilsMessengerEXT _debugMessenger;
|
|
||||||
private ExtDebugUtils? _debugUtils;
|
|
||||||
private PhysicalDevice _physicalDevice;
|
private PhysicalDevice _physicalDevice;
|
||||||
private Device _device;
|
private Device _device;
|
||||||
private Queue _queue;
|
private Queue _queue;
|
||||||
@@ -890,50 +867,14 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private int _tracedVertexBufferCount;
|
private int _tracedVertexBufferCount;
|
||||||
private readonly Dictionary<byte[], Pipeline> _computePipelines =
|
private readonly Dictionary<byte[], Pipeline> _computePipelines =
|
||||||
new(ReferenceEqualityComparer.Instance);
|
new(ReferenceEqualityComparer.Instance);
|
||||||
private readonly Dictionary<GraphicsPipelineKey, Pipeline> _graphicsPipelines = new();
|
|
||||||
private readonly Dictionary<VulkanGuestSampler, Sampler> _samplers = new();
|
|
||||||
private readonly Dictionary<byte[], string> _shaderDigests =
|
|
||||||
new(ReferenceEqualityComparer.Instance);
|
|
||||||
private readonly Dictionary<DescriptorLayoutKey, DescriptorLayoutBundle>
|
|
||||||
_descriptorLayouts = new();
|
|
||||||
private readonly Dictionary<HostBufferPoolKey, Stack<HostBufferAllocation>>
|
|
||||||
_hostBufferPool = new();
|
|
||||||
private readonly Dictionary<ulong, HostBufferAllocation> _hostBufferAllocations = new();
|
|
||||||
private readonly Queue<PendingGuestSubmission> _pendingGuestSubmissions = new();
|
private readonly Queue<PendingGuestSubmission> _pendingGuestSubmissions = new();
|
||||||
|
|
||||||
private readonly record struct GraphicsPipelineKey(
|
|
||||||
string VertexShader,
|
|
||||||
string FragmentShader,
|
|
||||||
ulong RenderPass,
|
|
||||||
PrimitiveTopology Topology,
|
|
||||||
VulkanGuestBlendState Blend,
|
|
||||||
string ResourceLayout,
|
|
||||||
string VertexLayout);
|
|
||||||
|
|
||||||
private readonly record struct HostBufferPoolKey(
|
|
||||||
BufferUsageFlags Usage,
|
|
||||||
ulong Capacity);
|
|
||||||
|
|
||||||
private readonly record struct DescriptorLayoutKey(
|
|
||||||
ShaderStageFlags Stages,
|
|
||||||
string Resources);
|
|
||||||
|
|
||||||
private sealed record DescriptorLayoutBundle(
|
|
||||||
DescriptorSetLayout DescriptorSetLayout,
|
|
||||||
PipelineLayout PipelineLayout);
|
|
||||||
|
|
||||||
private sealed record HostBufferAllocation(
|
|
||||||
VkBuffer Buffer,
|
|
||||||
DeviceMemory Memory,
|
|
||||||
HostBufferPoolKey Key);
|
|
||||||
|
|
||||||
private sealed class TranslatedDrawResources
|
private sealed class TranslatedDrawResources
|
||||||
{
|
{
|
||||||
public string DebugName = "SharpEmu translated";
|
public string DebugName = "SharpEmu translated";
|
||||||
public PipelineLayout PipelineLayout;
|
public PipelineLayout PipelineLayout;
|
||||||
public Pipeline Pipeline;
|
public Pipeline Pipeline;
|
||||||
public bool PipelineCached;
|
public bool PipelineCached;
|
||||||
public bool DescriptorLayoutCached;
|
|
||||||
public DescriptorSetLayout DescriptorSetLayout;
|
public DescriptorSetLayout DescriptorSetLayout;
|
||||||
public DescriptorPool DescriptorPool;
|
public DescriptorPool DescriptorPool;
|
||||||
public DescriptorSet DescriptorSet;
|
public DescriptorSet DescriptorSet;
|
||||||
@@ -1244,9 +1185,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private void CreateInstance()
|
private void CreateInstance()
|
||||||
{
|
{
|
||||||
var applicationName = (byte*)SilkMarshal.StringToPtr("SharpEmu");
|
var applicationName = (byte*)SilkMarshal.StringToPtr("SharpEmu");
|
||||||
var enableValidation = Environment.GetEnvironmentVariable("SHARPEMU_VK_VALIDATION") == "1";
|
|
||||||
byte* validationLayerName = null;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var applicationInfo = new ApplicationInfo
|
var applicationInfo = new ApplicationInfo
|
||||||
@@ -1274,29 +1212,12 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
enabledExtensions[enabledExtensionCount++] = debugUtilsExtension;
|
enabledExtensions[enabledExtensionCount++] = debugUtilsExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableValidation && IsInstanceLayerAvailable("VK_LAYER_KHRONOS_validation"))
|
|
||||||
{
|
|
||||||
validationLayerName = (byte*)SilkMarshal.StringToPtr("VK_LAYER_KHRONOS_validation");
|
|
||||||
}
|
|
||||||
else if (enableValidation)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("[LOADER][WARN] SHARPEMU_VK_VALIDATION=1 but VK_LAYER_KHRONOS_validation not found (Vulkan SDK installed?).");
|
|
||||||
}
|
|
||||||
|
|
||||||
var layers = stackalloc byte*[1];
|
|
||||||
if (validationLayerName is not null)
|
|
||||||
{
|
|
||||||
layers[0] = validationLayerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
var createInfo = new InstanceCreateInfo
|
var createInfo = new InstanceCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.InstanceCreateInfo,
|
SType = StructureType.InstanceCreateInfo,
|
||||||
PApplicationInfo = &applicationInfo,
|
PApplicationInfo = &applicationInfo,
|
||||||
EnabledExtensionCount = (uint)enabledExtensionCount,
|
EnabledExtensionCount = (uint)enabledExtensionCount,
|
||||||
PpEnabledExtensionNames = enabledExtensions,
|
PpEnabledExtensionNames = enabledExtensions,
|
||||||
EnabledLayerCount = validationLayerName is not null ? 1u : 0u,
|
|
||||||
PpEnabledLayerNames = validationLayerName is not null ? layers : null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -1306,13 +1227,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("VK_KHR_surface is unavailable.");
|
throw new InvalidOperationException("VK_KHR_surface is unavailable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validationLayerName is not null && _vk.TryGetInstanceExtension(_instance, out ExtDebugUtils debugUtils))
|
|
||||||
{
|
|
||||||
_debugUtils = debugUtils;
|
|
||||||
RegisterDebugMessenger(debugUtils);
|
|
||||||
Console.Error.WriteLine("[LOADER][INFO] Vulkan Validation Layers active (SHARPEMU_VK_VALIDATION=1).");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -1325,74 +1239,9 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
SilkMarshal.Free((nint)applicationName);
|
SilkMarshal.Free((nint)applicationName);
|
||||||
if (validationLayerName is not null)
|
|
||||||
{
|
|
||||||
SilkMarshal.Free((nint)validationLayerName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsInstanceLayerAvailable(string layerName)
|
|
||||||
{
|
|
||||||
uint layerCount = 0;
|
|
||||||
if (_vk.EnumerateInstanceLayerProperties(&layerCount, null) != Result.Success || layerCount == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var properties = new LayerProperties[layerCount];
|
|
||||||
fixed (LayerProperties* propertyPointer = properties)
|
|
||||||
{
|
|
||||||
if (_vk.EnumerateInstanceLayerProperties(&layerCount, propertyPointer) != Result.Success)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var expected = Encoding.UTF8.GetBytes(layerName);
|
|
||||||
for (var index = 0; index < layerCount; index++)
|
|
||||||
{
|
|
||||||
if (Utf8NullTerminatedEquals(propertyPointer[index].LayerName, expected))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
private void RegisterDebugMessenger(ExtDebugUtils debugUtils)
|
|
||||||
{
|
|
||||||
var messengerInfo = new DebugUtilsMessengerCreateInfoEXT
|
|
||||||
{
|
|
||||||
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
|
||||||
MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt
|
|
||||||
| DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
|
||||||
MessageType = DebugUtilsMessageTypeFlagsEXT.ValidationBitExt
|
|
||||||
| DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt
|
|
||||||
| DebugUtilsMessageTypeFlagsEXT.GeneralBitExt,
|
|
||||||
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugCallback),
|
|
||||||
};
|
|
||||||
|
|
||||||
Check(debugUtils.CreateDebugUtilsMessenger(_instance, &messengerInfo, null, out _debugMessenger),
|
|
||||||
"vkCreateDebugUtilsMessengerEXT");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static unsafe uint DebugCallback(
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT severity,
|
|
||||||
DebugUtilsMessageTypeFlagsEXT type,
|
|
||||||
DebugUtilsMessengerCallbackDataEXT* callbackData,
|
|
||||||
void* userData)
|
|
||||||
{
|
|
||||||
var message = SilkMarshal.PtrToString((nint)callbackData->PMessage);
|
|
||||||
var prefix = severity switch
|
|
||||||
{
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt => "[VULKAN][ERROR]",
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt => "[VULKAN][WARN]",
|
|
||||||
_ => "[VULKAN][INFO]",
|
|
||||||
};
|
|
||||||
Console.Error.WriteLine($"{prefix} {message}");
|
|
||||||
return Vk.False;
|
|
||||||
}
|
|
||||||
private void CreateSurface()
|
private void CreateSurface()
|
||||||
{
|
{
|
||||||
var instanceHandle = new VkHandle(_instance.Handle);
|
var instanceHandle = new VkHandle(_instance.Handle);
|
||||||
@@ -1453,27 +1302,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
QueueCount = 1,
|
QueueCount = 1,
|
||||||
PQueuePriorities = &priority,
|
PQueuePriorities = &priority,
|
||||||
};
|
};
|
||||||
_vk.GetPhysicalDeviceFeatures(_physicalDevice, out var supportedFeatures);
|
|
||||||
var enabledFeatures = new PhysicalDeviceFeatures
|
|
||||||
{
|
|
||||||
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
|
|
||||||
FragmentStoresAndAtomics = supportedFeatures.FragmentStoresAndAtomics,
|
|
||||||
ShaderInt64 = supportedFeatures.ShaderInt64,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!supportedFeatures.ShaderInt64)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
"[LOADER][WARN] GPU does not support shaderInt64 " +
|
|
||||||
"translated shaders using 64-bit integers will fail.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!supportedFeatures.VertexPipelineStoresAndAtomics || !supportedFeatures.FragmentStoresAndAtomics)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
"[LOADER][WARN] GPU does not support vertexPipelineStoresAndAtomics/fragmentStoresAndAtomics " +
|
|
||||||
"translated shaders using storage buffers in vertex/fragment stages may fail.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var swapchainExtension = (byte*)SilkMarshal.StringToPtr("VK_KHR_swapchain");
|
var swapchainExtension = (byte*)SilkMarshal.StringToPtr("VK_KHR_swapchain");
|
||||||
try
|
try
|
||||||
@@ -1485,7 +1313,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
PQueueCreateInfos = &queueInfo,
|
PQueueCreateInfos = &queueInfo,
|
||||||
EnabledExtensionCount = 1,
|
EnabledExtensionCount = 1,
|
||||||
PpEnabledExtensionNames = &swapchainExtension,
|
PpEnabledExtensionNames = &swapchainExtension,
|
||||||
PEnabledFeatures = &enabledFeatures,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Check(_vk.CreateDevice(_physicalDevice, &createInfo, null, out _device), "vkCreateDevice");
|
Check(_vk.CreateDevice(_physicalDevice, &createInfo, null, out _device), "vkCreateDevice");
|
||||||
@@ -2151,16 +1978,81 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
var storageImageCount = textureCount - sampledImageCount;
|
var storageImageCount = textureCount - sampledImageCount;
|
||||||
var globalBufferCount = resources.GlobalMemoryBuffers.Length;
|
var globalBufferCount = resources.GlobalMemoryBuffers.Length;
|
||||||
var bindingCount = textureCount + (globalBufferCount == 0 ? 0 : 1);
|
var bindingCount = textureCount + (globalBufferCount == 0 ? 0 : 1);
|
||||||
var layout = GetOrCreateDescriptorLayout(resources, stageFlags, bindingCount);
|
|
||||||
resources.DescriptorSetLayout = layout.DescriptorSetLayout;
|
|
||||||
resources.PipelineLayout = layout.PipelineLayout;
|
|
||||||
resources.DescriptorLayoutCached = true;
|
|
||||||
if (bindingCount == 0)
|
if (bindingCount == 0)
|
||||||
{
|
{
|
||||||
|
var layoutInfo = new PipelineLayoutCreateInfo
|
||||||
|
{
|
||||||
|
SType = StructureType.PipelineLayoutCreateInfo,
|
||||||
|
};
|
||||||
|
PipelineLayout pipelineLayout;
|
||||||
|
Check(
|
||||||
|
_vk.CreatePipelineLayout(_device, &layoutInfo, null, out pipelineLayout),
|
||||||
|
"vkCreatePipelineLayout");
|
||||||
|
resources.PipelineLayout = pipelineLayout;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var setLayout = layout.DescriptorSetLayout;
|
var bindings = new DescriptorSetLayoutBinding[bindingCount];
|
||||||
|
var bindingOffset = 0;
|
||||||
|
if (globalBufferCount != 0)
|
||||||
|
{
|
||||||
|
bindings[bindingOffset++] = new DescriptorSetLayoutBinding
|
||||||
|
{
|
||||||
|
Binding = 0,
|
||||||
|
DescriptorType = DescriptorType.StorageBuffer,
|
||||||
|
DescriptorCount = (uint)globalBufferCount,
|
||||||
|
StageFlags = stageFlags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var index = 0; index < textureCount; index++)
|
||||||
|
{
|
||||||
|
bindings[bindingOffset + index] = new DescriptorSetLayoutBinding
|
||||||
|
{
|
||||||
|
Binding = (uint)(index + 1),
|
||||||
|
DescriptorType = resources.Textures[index].IsStorage
|
||||||
|
? DescriptorType.StorageImage
|
||||||
|
: DescriptorType.CombinedImageSampler,
|
||||||
|
DescriptorCount = 1,
|
||||||
|
StageFlags = stageFlags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed (DescriptorSetLayoutBinding* bindingPointer = bindings)
|
||||||
|
{
|
||||||
|
var layoutInfo = new DescriptorSetLayoutCreateInfo
|
||||||
|
{
|
||||||
|
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||||
|
BindingCount = (uint)bindings.Length,
|
||||||
|
PBindings = bindingPointer,
|
||||||
|
};
|
||||||
|
DescriptorSetLayout descriptorSetLayout;
|
||||||
|
Check(
|
||||||
|
_vk.CreateDescriptorSetLayout(
|
||||||
|
_device,
|
||||||
|
&layoutInfo,
|
||||||
|
null,
|
||||||
|
out descriptorSetLayout),
|
||||||
|
"vkCreateDescriptorSetLayout");
|
||||||
|
resources.DescriptorSetLayout = descriptorSetLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
var setLayout = resources.DescriptorSetLayout;
|
||||||
|
var pipelineLayoutInfo = new PipelineLayoutCreateInfo
|
||||||
|
{
|
||||||
|
SType = StructureType.PipelineLayoutCreateInfo,
|
||||||
|
SetLayoutCount = 1,
|
||||||
|
PSetLayouts = &setLayout,
|
||||||
|
};
|
||||||
|
PipelineLayout translatedPipelineLayout;
|
||||||
|
Check(
|
||||||
|
_vk.CreatePipelineLayout(
|
||||||
|
_device,
|
||||||
|
&pipelineLayoutInfo,
|
||||||
|
null,
|
||||||
|
out translatedPipelineLayout),
|
||||||
|
"vkCreatePipelineLayout");
|
||||||
|
resources.PipelineLayout = translatedPipelineLayout;
|
||||||
|
|
||||||
var poolSizes = new DescriptorPoolSize[
|
var poolSizes = new DescriptorPoolSize[
|
||||||
(sampledImageCount == 0 ? 0 : 1) +
|
(sampledImageCount == 0 ? 0 : 1) +
|
||||||
@@ -2310,21 +2202,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
RenderPass renderPass,
|
RenderPass renderPass,
|
||||||
Extent2D extent)
|
Extent2D extent)
|
||||||
{
|
{
|
||||||
var pipelineKey = new GraphicsPipelineKey(
|
|
||||||
GetShaderDigest(vertexSpirv),
|
|
||||||
GetShaderDigest(fragmentSpirv),
|
|
||||||
renderPass.Handle,
|
|
||||||
resources.Topology,
|
|
||||||
resources.Blend,
|
|
||||||
GetResourceLayoutKey(resources),
|
|
||||||
GetVertexLayoutKey(resources));
|
|
||||||
if (_graphicsPipelines.TryGetValue(pipelineKey, out var cachedPipeline))
|
|
||||||
{
|
|
||||||
resources.Pipeline = cachedPipeline;
|
|
||||||
resources.PipelineCached = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var vertexModule = CreateShaderModule(vertexSpirv);
|
var vertexModule = CreateShaderModule(vertexSpirv);
|
||||||
var fragmentModule = CreateShaderModule(fragmentSpirv);
|
var fragmentModule = CreateShaderModule(fragmentSpirv);
|
||||||
var entryPoint = (byte*)SilkMarshal.StringToPtr("main");
|
var entryPoint = (byte*)SilkMarshal.StringToPtr("main");
|
||||||
@@ -2476,8 +2353,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
out pipeline),
|
out pipeline),
|
||||||
"vkCreateGraphicsPipelines(translated)");
|
"vkCreateGraphicsPipelines(translated)");
|
||||||
resources.Pipeline = pipeline;
|
resources.Pipeline = pipeline;
|
||||||
resources.PipelineCached = true;
|
|
||||||
_graphicsPipelines.Add(pipelineKey, pipeline);
|
|
||||||
SetDebugName(
|
SetDebugName(
|
||||||
ObjectType.Pipeline,
|
ObjectType.Pipeline,
|
||||||
pipeline.Handle,
|
pipeline.Handle,
|
||||||
@@ -2492,129 +2367,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DescriptorLayoutBundle GetOrCreateDescriptorLayout(
|
|
||||||
TranslatedDrawResources resources,
|
|
||||||
ShaderStageFlags stageFlags,
|
|
||||||
int bindingCount)
|
|
||||||
{
|
|
||||||
var key = new DescriptorLayoutKey(stageFlags, GetResourceLayoutKey(resources));
|
|
||||||
if (_descriptorLayouts.TryGetValue(key, out var cached))
|
|
||||||
{
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayout descriptorSetLayout = default;
|
|
||||||
if (bindingCount != 0)
|
|
||||||
{
|
|
||||||
var bindings = new DescriptorSetLayoutBinding[bindingCount];
|
|
||||||
var bindingOffset = 0;
|
|
||||||
if (resources.GlobalMemoryBuffers.Length != 0)
|
|
||||||
{
|
|
||||||
bindings[bindingOffset++] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = 0,
|
|
||||||
DescriptorType = DescriptorType.StorageBuffer,
|
|
||||||
DescriptorCount = (uint)resources.GlobalMemoryBuffers.Length,
|
|
||||||
StageFlags = stageFlags,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var index = 0; index < resources.Textures.Length; index++)
|
|
||||||
{
|
|
||||||
bindings[bindingOffset + index] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = (uint)(index + 1),
|
|
||||||
DescriptorType = resources.Textures[index].IsStorage
|
|
||||||
? DescriptorType.StorageImage
|
|
||||||
: DescriptorType.CombinedImageSampler,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = stageFlags,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed (DescriptorSetLayoutBinding* bindingPointer = bindings)
|
|
||||||
{
|
|
||||||
var descriptorInfo = new DescriptorSetLayoutCreateInfo
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
BindingCount = (uint)bindings.Length,
|
|
||||||
PBindings = bindingPointer,
|
|
||||||
};
|
|
||||||
Check(
|
|
||||||
_vk.CreateDescriptorSetLayout(
|
|
||||||
_device,
|
|
||||||
&descriptorInfo,
|
|
||||||
null,
|
|
||||||
out descriptorSetLayout),
|
|
||||||
"vkCreateDescriptorSetLayout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var pipelineInfo = new PipelineLayoutCreateInfo
|
|
||||||
{
|
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
|
||||||
};
|
|
||||||
if (descriptorSetLayout.Handle != 0)
|
|
||||||
{
|
|
||||||
pipelineInfo.SetLayoutCount = 1;
|
|
||||||
pipelineInfo.PSetLayouts = &descriptorSetLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
PipelineLayout pipelineLayout;
|
|
||||||
Check(
|
|
||||||
_vk.CreatePipelineLayout(
|
|
||||||
_device,
|
|
||||||
&pipelineInfo,
|
|
||||||
null,
|
|
||||||
out pipelineLayout),
|
|
||||||
"vkCreatePipelineLayout");
|
|
||||||
var created = new DescriptorLayoutBundle(descriptorSetLayout, pipelineLayout);
|
|
||||||
_descriptorLayouts.Add(key, created);
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetShaderDigest(byte[] spirv)
|
|
||||||
{
|
|
||||||
if (_shaderDigests.TryGetValue(spirv, out var digest))
|
|
||||||
{
|
|
||||||
return digest;
|
|
||||||
}
|
|
||||||
|
|
||||||
digest = Convert.ToHexString(SHA256.HashData(spirv));
|
|
||||||
_shaderDigests.Add(spirv, digest);
|
|
||||||
return digest;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetResourceLayoutKey(TranslatedDrawResources resources)
|
|
||||||
{
|
|
||||||
var key = new StringBuilder();
|
|
||||||
key.Append(resources.GlobalMemoryBuffers.Length).Append(':');
|
|
||||||
foreach (var texture in resources.Textures)
|
|
||||||
{
|
|
||||||
key.Append(texture.IsStorage ? 'S' : 'T');
|
|
||||||
}
|
|
||||||
|
|
||||||
return key.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetVertexLayoutKey(TranslatedDrawResources resources)
|
|
||||||
{
|
|
||||||
var key = new StringBuilder();
|
|
||||||
foreach (var buffer in resources.VertexBuffers)
|
|
||||||
{
|
|
||||||
key.Append(buffer.Location).Append(',')
|
|
||||||
.Append(buffer.ComponentCount).Append(',')
|
|
||||||
.Append(buffer.DataFormat).Append(',')
|
|
||||||
.Append(buffer.NumberFormat).Append(',')
|
|
||||||
.Append(buffer.Stride == 0
|
|
||||||
? Math.Max(buffer.ComponentCount, 1) * sizeof(float)
|
|
||||||
: buffer.Stride)
|
|
||||||
.Append(';');
|
|
||||||
}
|
|
||||||
|
|
||||||
return key.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private void CreateComputePipeline(
|
private void CreateComputePipeline(
|
||||||
TranslatedDrawResources resources,
|
TranslatedDrawResources resources,
|
||||||
@@ -3027,7 +2779,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
SetDebugName(ObjectType.Buffer, stagingBuffer.Handle, $"{debugName} staging");
|
SetDebugName(ObjectType.Buffer, stagingBuffer.Handle, $"{debugName} staging");
|
||||||
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
||||||
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
||||||
var resource = new TextureResource
|
return new TextureResource
|
||||||
{
|
{
|
||||||
Address = texture.Address,
|
Address = texture.Address,
|
||||||
StagingBuffer = stagingBuffer,
|
StagingBuffer = stagingBuffer,
|
||||||
@@ -3043,38 +2795,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
OwnsStorage = true,
|
OwnsStorage = true,
|
||||||
SamplerState = texture.Sampler,
|
SamplerState = texture.Sampler,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (texture.Address != 0 &&
|
|
||||||
!_guestImages.ContainsKey(texture.Address))
|
|
||||||
{
|
|
||||||
var guestImage = new GuestImageResource
|
|
||||||
{
|
|
||||||
Address = texture.Address,
|
|
||||||
Width = width,
|
|
||||||
Height = height,
|
|
||||||
MipLevels = 1,
|
|
||||||
Format = vkFormat,
|
|
||||||
Image = image,
|
|
||||||
Memory = imageMemory,
|
|
||||||
View = view,
|
|
||||||
InitialUploadPending = true,
|
|
||||||
};
|
|
||||||
_guestImages.Add(texture.Address, guestImage);
|
|
||||||
resource.OwnsStorage = false;
|
|
||||||
resource.GuestImage = guestImage;
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
var guestFormat = VulkanVideoPresenter.GetGuestTextureFormat(
|
|
||||||
texture.Format,
|
|
||||||
texture.NumberType);
|
|
||||||
if (guestFormat != 0)
|
|
||||||
{
|
|
||||||
_availableGuestImages[texture.Address] = guestFormat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DumpTextureUpload(
|
private void DumpTextureUpload(
|
||||||
@@ -3175,11 +2895,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
|
|
||||||
private Sampler CreateSampler(VulkanGuestSampler sampler)
|
private Sampler CreateSampler(VulkanGuestSampler sampler)
|
||||||
{
|
{
|
||||||
if (_samplers.TryGetValue(sampler, out var cachedSampler))
|
|
||||||
{
|
|
||||||
return cachedSampler;
|
|
||||||
}
|
|
||||||
|
|
||||||
var minLod = DecodeSamplerMipFilter(sampler) == 0
|
var minLod = DecodeSamplerMipFilter(sampler) == 0
|
||||||
? 0f
|
? 0f
|
||||||
: DecodeSamplerMinLod(sampler);
|
: DecodeSamplerMinLod(sampler);
|
||||||
@@ -3206,7 +2921,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
Check(
|
Check(
|
||||||
_vk.CreateSampler(_device, &samplerInfo, null, out vkSampler),
|
_vk.CreateSampler(_device, &samplerInfo, null, out vkSampler),
|
||||||
"vkCreateSampler(texture)");
|
"vkCreateSampler(texture)");
|
||||||
_samplers.Add(sampler, vkSampler);
|
|
||||||
return vkSampler;
|
return vkSampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3321,31 +3035,11 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
out DeviceMemory memory)
|
out DeviceMemory memory)
|
||||||
{
|
{
|
||||||
var size = (ulong)Math.Max(data.Length, sizeof(uint));
|
var size = (ulong)Math.Max(data.Length, sizeof(uint));
|
||||||
var capacity = BitOperations.RoundUpToPowerOf2(size);
|
var buffer = CreateBuffer(
|
||||||
var key = new HostBufferPoolKey(usage, capacity);
|
size,
|
||||||
if (!_hostBufferPool.TryGetValue(key, out var available))
|
usage,
|
||||||
{
|
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
||||||
available = new Stack<HostBufferAllocation>();
|
out memory);
|
||||||
_hostBufferPool.Add(key, available);
|
|
||||||
}
|
|
||||||
|
|
||||||
HostBufferAllocation allocation;
|
|
||||||
if (available.TryPop(out var pooled))
|
|
||||||
{
|
|
||||||
allocation = pooled;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var buffer = CreateBuffer(
|
|
||||||
capacity,
|
|
||||||
usage,
|
|
||||||
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
|
||||||
out var allocatedMemory);
|
|
||||||
allocation = new HostBufferAllocation(buffer, allocatedMemory, key);
|
|
||||||
_hostBufferAllocations.Add(buffer.Handle, allocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
memory = allocation.Memory;
|
|
||||||
void* mapped;
|
void* mapped;
|
||||||
Check(_vk.MapMemory(_device, memory, 0, size, 0, &mapped), "vkMapMemory(host)");
|
Check(_vk.MapMemory(_device, memory, 0, size, 0, &mapped), "vkMapMemory(host)");
|
||||||
try
|
try
|
||||||
@@ -3364,28 +3058,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.UnmapMemory(_device, memory);
|
_vk.UnmapMemory(_device, memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return allocation.Buffer;
|
return buffer;
|
||||||
}
|
|
||||||
|
|
||||||
private void RecycleHostBuffer(VkBuffer buffer, DeviceMemory memory)
|
|
||||||
{
|
|
||||||
if (buffer.Handle == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_hostBufferAllocations.TryGetValue(buffer.Handle, out var allocation) &&
|
|
||||||
allocation.Memory.Handle == memory.Handle)
|
|
||||||
{
|
|
||||||
_hostBufferPool[allocation.Key].Push(allocation);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_vk.DestroyBuffer(_device, buffer, null);
|
|
||||||
if (memory.Handle != 0)
|
|
||||||
{
|
|
||||||
_vk.FreeMemory(_device, memory, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PrimitiveTopology GetPrimitiveTopology(uint primitiveType) =>
|
private static PrimitiveTopology GetPrimitiveTopology(uint primitiveType) =>
|
||||||
@@ -3935,7 +3608,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
resources,
|
resources,
|
||||||
GetTraceImages(resources));
|
GetTraceImages(resources));
|
||||||
submitted = true;
|
submitted = true;
|
||||||
MarkSampledImagesInitialized(resources);
|
|
||||||
MarkStorageImagesInitialized(resources, traceContents: false);
|
MarkStorageImagesInitialized(resources, traceContents: false);
|
||||||
TraceVulkanShader(
|
TraceVulkanShader(
|
||||||
$"vk.compute_dispatch groups={work.GroupCountX}x" +
|
$"vk.compute_dispatch groups={work.GroupCountX}x" +
|
||||||
@@ -4124,7 +3796,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
GetTraceImages(resources, target));
|
GetTraceImages(resources, target));
|
||||||
submitted = true;
|
submitted = true;
|
||||||
target.Initialized = true;
|
target.Initialized = true;
|
||||||
MarkSampledImagesInitialized(resources);
|
|
||||||
MarkStorageImagesInitialized(resources, traceContents: false);
|
MarkStorageImagesInitialized(resources, traceContents: false);
|
||||||
|
|
||||||
var guestTextureFormat = GetGuestTextureFormat(target.Format);
|
var guestTextureFormat = GetGuestTextureFormat(target.Format);
|
||||||
@@ -4827,7 +4498,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
CheckSwapchainResult(presentResult, "vkQueuePresentKHR");
|
CheckSwapchainResult(presentResult, "vkQueuePresentKHR");
|
||||||
recreateAfterPresent |= presentResult == Result.SuboptimalKhr;
|
recreateAfterPresent |= presentResult == Result.SuboptimalKhr;
|
||||||
Check(_vk.QueueWaitIdle(_queue), "vkQueueWaitIdle");
|
Check(_vk.QueueWaitIdle(_queue), "vkQueueWaitIdle");
|
||||||
VideoOutExports.ReportPresentedFrame();
|
|
||||||
if (_swapchainReadbackPending)
|
if (_swapchainReadbackPending)
|
||||||
{
|
{
|
||||||
TraceSwapchainReadback();
|
TraceSwapchainReadback();
|
||||||
@@ -4835,7 +4505,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
CollectCompletedGuestSubmissions(waitForOldest: false);
|
CollectCompletedGuestSubmissions(waitForOldest: false);
|
||||||
if (translatedResources is not null)
|
if (translatedResources is not null)
|
||||||
{
|
{
|
||||||
MarkSampledImagesInitialized(translatedResources);
|
|
||||||
MarkStorageImagesInitialized(translatedResources);
|
MarkStorageImagesInitialized(translatedResources);
|
||||||
DestroyTranslatedDrawResources(translatedResources);
|
DestroyTranslatedDrawResources(translatedResources);
|
||||||
}
|
}
|
||||||
@@ -5341,27 +5010,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MarkSampledImagesInitialized(
|
|
||||||
TranslatedDrawResources resources)
|
|
||||||
{
|
|
||||||
lock (_gate)
|
|
||||||
{
|
|
||||||
foreach (var texture in resources.Textures)
|
|
||||||
{
|
|
||||||
if (!texture.NeedsUpload ||
|
|
||||||
texture.IsStorage ||
|
|
||||||
texture.Address == 0 ||
|
|
||||||
texture.GuestImage is not { } guestImage)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
guestImage.Initialized = true;
|
|
||||||
guestImage.InitialUploadPending = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ShouldTraceGuestImageContents(GuestImageResource image)
|
private bool ShouldTraceGuestImageContents(GuestImageResource image)
|
||||||
{
|
{
|
||||||
if (image.Address == 0)
|
if (image.Address == 0)
|
||||||
@@ -5600,6 +5248,11 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.FreeMemory(_device, texture.StagingMemory, null);
|
_vk.FreeMemory(_device, texture.StagingMemory, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (texture.Sampler.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.DestroySampler(_device, texture.Sampler, null);
|
||||||
|
}
|
||||||
|
|
||||||
if (texture.NeedsUpload &&
|
if (texture.NeedsUpload &&
|
||||||
texture.GuestImage is { Initialized: false } guestImage)
|
texture.GuestImage is { Initialized: false } guestImage)
|
||||||
{
|
{
|
||||||
@@ -5614,7 +5267,15 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecycleHostBuffer(globalBuffer.Buffer, globalBuffer.Memory);
|
if (globalBuffer.Buffer.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.DestroyBuffer(_device, globalBuffer.Buffer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globalBuffer.Memory.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.FreeMemory(_device, globalBuffer.Memory, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var vertexBuffer in resources.VertexBuffers)
|
foreach (var vertexBuffer in resources.VertexBuffers)
|
||||||
@@ -5624,10 +5285,26 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecycleHostBuffer(vertexBuffer.Buffer, vertexBuffer.Memory);
|
if (vertexBuffer.Buffer.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.DestroyBuffer(_device, vertexBuffer.Buffer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vertexBuffer.Memory.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.FreeMemory(_device, vertexBuffer.Memory, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RecycleHostBuffer(resources.IndexBuffer, resources.IndexMemory);
|
if (resources.IndexBuffer.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.DestroyBuffer(_device, resources.IndexBuffer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.IndexMemory.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.FreeMemory(_device, resources.IndexMemory, null);
|
||||||
|
}
|
||||||
|
|
||||||
if (!resources.PipelineCached && resources.Pipeline.Handle != 0)
|
if (!resources.PipelineCached && resources.Pipeline.Handle != 0)
|
||||||
{
|
{
|
||||||
@@ -5639,14 +5316,12 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.DestroyDescriptorPool(_device, resources.DescriptorPool, null);
|
_vk.DestroyDescriptorPool(_device, resources.DescriptorPool, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resources.DescriptorLayoutCached &&
|
if (resources.PipelineLayout.Handle != 0)
|
||||||
resources.PipelineLayout.Handle != 0)
|
|
||||||
{
|
{
|
||||||
_vk.DestroyPipelineLayout(_device, resources.PipelineLayout, null);
|
_vk.DestroyPipelineLayout(_device, resources.PipelineLayout, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resources.DescriptorLayoutCached &&
|
if (resources.DescriptorSetLayout.Handle != 0)
|
||||||
resources.DescriptorSetLayout.Handle != 0)
|
|
||||||
{
|
{
|
||||||
_vk.DestroyDescriptorSetLayout(_device, resources.DescriptorSetLayout, null);
|
_vk.DestroyDescriptorSetLayout(_device, resources.DescriptorSetLayout, null);
|
||||||
}
|
}
|
||||||
@@ -6073,10 +5748,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_debugUtils is not null && _debugMessenger.Handle != 0)
|
|
||||||
{
|
|
||||||
_debugUtils.DestroyDebugUtilsMessenger(_instance, _debugMessenger, null);
|
|
||||||
}
|
|
||||||
_vulkanReady = false;
|
_vulkanReady = false;
|
||||||
_vk.DeviceWaitIdle(_device);
|
_vk.DeviceWaitIdle(_device);
|
||||||
CollectCompletedGuestSubmissions(waitForOldest: false);
|
CollectCompletedGuestSubmissions(waitForOldest: false);
|
||||||
@@ -6085,36 +5756,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.DestroyPipeline(_device, pipeline, null);
|
_vk.DestroyPipeline(_device, pipeline, null);
|
||||||
}
|
}
|
||||||
_computePipelines.Clear();
|
_computePipelines.Clear();
|
||||||
foreach (var pipeline in _graphicsPipelines.Values)
|
|
||||||
{
|
|
||||||
_vk.DestroyPipeline(_device, pipeline, null);
|
|
||||||
}
|
|
||||||
_graphicsPipelines.Clear();
|
|
||||||
foreach (var layout in _descriptorLayouts.Values)
|
|
||||||
{
|
|
||||||
_vk.DestroyPipelineLayout(_device, layout.PipelineLayout, null);
|
|
||||||
if (layout.DescriptorSetLayout.Handle != 0)
|
|
||||||
{
|
|
||||||
_vk.DestroyDescriptorSetLayout(
|
|
||||||
_device,
|
|
||||||
layout.DescriptorSetLayout,
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_descriptorLayouts.Clear();
|
|
||||||
foreach (var sampler in _samplers.Values)
|
|
||||||
{
|
|
||||||
_vk.DestroySampler(_device, sampler, null);
|
|
||||||
}
|
|
||||||
_samplers.Clear();
|
|
||||||
_shaderDigests.Clear();
|
|
||||||
foreach (var allocation in _hostBufferAllocations.Values)
|
|
||||||
{
|
|
||||||
_vk.DestroyBuffer(_device, allocation.Buffer, null);
|
|
||||||
_vk.FreeMemory(_device, allocation.Memory, null);
|
|
||||||
}
|
|
||||||
_hostBufferAllocations.Clear();
|
|
||||||
_hostBufferPool.Clear();
|
|
||||||
foreach (var guestImage in _guestImages.Values)
|
foreach (var guestImage in _guestImages.Values)
|
||||||
{
|
{
|
||||||
DestroyGuestImage(guestImage);
|
DestroyGuestImage(guestImage);
|
||||||
|
|||||||
@@ -11,16 +11,6 @@
|
|||||||
"Silk.NET.Core": "2.23.0"
|
"Silk.NET.Core": "2.23.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Silk.NET.Vulkan.Extensions.EXT": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[2.23.0, )",
|
|
||||||
"resolved": "2.23.0",
|
|
||||||
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
|
|
||||||
"dependencies": {
|
|
||||||
"Silk.NET.Core": "2.23.0",
|
|
||||||
"Silk.NET.Vulkan": "2.23.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": {
|
"Silk.NET.Vulkan.Extensions.KHR": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.23.0, )",
|
"requested": "[2.23.0, )",
|
||||||
|
|||||||
Reference in New Issue
Block a user