mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-21 00:41:29 +08:00
Compare commits
3 Commits
v0.0.3-release.3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a2241d0e83 | |||
| fe6521f617 | |||
| 034ddcc092 |
@@ -1,6 +1,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
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using SharpEmu.Core.Loader;
|
using SharpEmu.Core.Loader;
|
||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
@@ -18,7 +19,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
private readonly object _allocationSearchHintGate = new();
|
private readonly object _allocationSearchHintGate = new();
|
||||||
private readonly List<MemoryRegion> _regions = new();
|
private readonly List<MemoryRegion> _regions = new();
|
||||||
private readonly Dictionary<(ulong DesiredAddress, ulong Alignment, bool Executable), ulong> _allocationSearchHints = new();
|
private readonly Dictionary<(ulong DesiredAddress, ulong Alignment, bool Executable), ulong> _allocationSearchHints = new();
|
||||||
private readonly Dictionary<ulong, ProgramHeaderFlags> _pageProtections = new();
|
private readonly ConcurrentDictionary<ulong, ProgramHeaderFlags> _pageProtections = new();
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
|
|||||||
@@ -12174,15 +12174,18 @@ public static partial class AgcExports
|
|||||||
|
|
||||||
if (dispatchEndX == 0 || dispatchEndY == 0 || dispatchEndZ == 0)
|
if (dispatchEndX == 0 || dispatchEndY == 0 || dispatchEndZ == 0)
|
||||||
{
|
{
|
||||||
// Indirect dispatches read their dimensions from a guest buffer a
|
// For indirect dispatches (both absolute and base), zero dimensions are a valid outcome
|
||||||
// prior GPU dispatch fills. Zero here means that producer has not run
|
// of GPU culling passes (0 workgroups). VulkanVideoPresenter handles groupCount = 0 as a clean no-op.
|
||||||
// yet — signal the caller to suspend on the dims buffer and retry,
|
if (opcode == ItDispatchIndirect || dispatchSource is "absolute-indirect" or "base-indirect")
|
||||||
// rather than dropping the work (which black-screens GPU-driven games
|
|
||||||
// like Astro Bot). Direct dispatches carry dims inline, so a zero is
|
|
||||||
// genuinely malformed and still rejected.
|
|
||||||
if (opcode == ItDispatchIndirect)
|
|
||||||
{
|
{
|
||||||
indirectDimsRetryAddress = dimensionsAddress;
|
var waveCount = (initiator & (1u << 15)) != 0 ? 32u : 64u;
|
||||||
|
dispatch = new ComputeDispatch(
|
||||||
|
0, 0, 0,
|
||||||
|
0, 0, 0,
|
||||||
|
waveCount,
|
||||||
|
IsIndirect: true,
|
||||||
|
0, 0, 0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RejectComputeDispatch(
|
return RejectComputeDispatch(
|
||||||
|
|||||||
@@ -13828,16 +13828,10 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
existing.LogicalDepth == depth &&
|
existing.LogicalDepth == depth &&
|
||||||
existing.Type == type &&
|
existing.Type == type &&
|
||||||
existing.MipLevels == mipLevels &&
|
existing.MipLevels == mipLevels &&
|
||||||
|
(!requiresStorage || existing.SupportsStorageUsage) &&
|
||||||
(exactFormatMatch ||
|
(exactFormatMatch ||
|
||||||
(IsAliasableGuestImageFormat(existing.Format, format) &&
|
IsAliasableGuestImageFormat(existing.Format, format)))
|
||||||
(!requiresStorage || existing.SupportsStorageUsage))))
|
|
||||||
{
|
{
|
||||||
if (requiresStorage && !existing.SupportsStorageUsage)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
$"Guest image 0x{target.Address:X16} was created without storage usage.");
|
|
||||||
}
|
|
||||||
|
|
||||||
existing.IsCpuBacked = false;
|
existing.IsCpuBacked = false;
|
||||||
existing.CpuContentFingerprint = 0;
|
existing.CpuContentFingerprint = 0;
|
||||||
if (existing.RenderPass.Handle == 0 &&
|
if (existing.RenderPass.Handle == 0 &&
|
||||||
@@ -13870,14 +13864,9 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
if (existing.Width == target.Width &&
|
if (existing.Width == target.Width &&
|
||||||
existing.Height == target.Height &&
|
existing.Height == target.Height &&
|
||||||
existing.MipLevels == mipLevels &&
|
existing.MipLevels == mipLevels &&
|
||||||
|
(!requiresStorage || existing.SupportsStorageUsage) &&
|
||||||
IsCompatibleViewFormat(existing.Format, format))
|
IsCompatibleViewFormat(existing.Format, format))
|
||||||
{
|
{
|
||||||
if (requiresStorage && !existing.SupportsStorageUsage)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
$"Guest image 0x{target.Address:X16} was created without storage usage.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_traceGuestImageEvents)
|
if (_traceGuestImageEvents)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
@@ -13952,50 +13941,52 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
{
|
{
|
||||||
if (requiresStorage && !retained.SupportsStorageUsage)
|
if (requiresStorage && !retained.SupportsStorageUsage)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
// Do not reuse retained image if it lacks required storage usage
|
||||||
$"Retained guest image 0x{target.Address:X16} was created without storage usage.");
|
DestroyGuestImage(retained);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
retained.IsCpuBacked = false;
|
|
||||||
retained.CpuContentFingerprint = 0;
|
|
||||||
_guestImages.Add(target.Address, retained);
|
|
||||||
var retainedByteCount = GetTextureByteCount(
|
|
||||||
target.Format,
|
|
||||||
target.Width,
|
|
||||||
target.Height,
|
|
||||||
depth);
|
|
||||||
lock (_gate)
|
|
||||||
{
|
{
|
||||||
_cpuBackedUploadGenerations.Remove(target.Address);
|
retained.IsCpuBacked = false;
|
||||||
_guestImageExtents[target.Address] = (
|
retained.CpuContentFingerprint = 0;
|
||||||
|
_guestImages.Add(target.Address, retained);
|
||||||
|
var retainedByteCount = GetTextureByteCount(
|
||||||
|
target.Format,
|
||||||
target.Width,
|
target.Width,
|
||||||
target.Height,
|
target.Height,
|
||||||
retainedByteCount);
|
depth);
|
||||||
}
|
lock (_gate)
|
||||||
|
{
|
||||||
|
_cpuBackedUploadGenerations.Remove(target.Address);
|
||||||
|
_guestImageExtents[target.Address] = (
|
||||||
|
target.Width,
|
||||||
|
target.Height,
|
||||||
|
retainedByteCount);
|
||||||
|
}
|
||||||
|
|
||||||
// Arm the exact extent the flip/acquire sync path would read
|
// Arm the exact extent the flip/acquire sync path would read
|
||||||
// back, budgeted by bytes rather than by resolution: the old
|
// back, budgeted by bytes rather than by resolution: the old
|
||||||
// 1920x1080 cap left every 4K surface permanently
|
// 1920x1080 cap left every 4K surface permanently
|
||||||
// un-invalidated, so a guest CPU rewrite of one was never
|
// un-invalidated, so a guest CPU rewrite of one was never
|
||||||
// reflected and the sample served stale bytes.
|
// reflected and the sample served stale bytes.
|
||||||
if (ShouldTrackGuestImageWrites(retainedByteCount))
|
if (ShouldTrackGuestImageWrites(retainedByteCount))
|
||||||
{
|
{
|
||||||
SharpEmu.HLE.GuestImageWriteTracker.Track(
|
SharpEmu.HLE.GuestImageWriteTracker.Track(
|
||||||
target.Address,
|
target.Address,
|
||||||
retainedByteCount,
|
retainedByteCount,
|
||||||
CurrentGuestWorkSequenceForDiagnostics,
|
CurrentGuestWorkSequenceForDiagnostics,
|
||||||
"vulkan.render-target");
|
"vulkan.render-target");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_traceGuestImageEvents)
|
if (_traceGuestImageEvents)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
$"[GIMG] retained addr=0x{target.Address:X} " +
|
$"[GIMG] retained addr=0x{target.Address:X} " +
|
||||||
$"{target.Width}x{target.Height} fmt={format} " +
|
$"{target.Width}x{target.Height} fmt={format} " +
|
||||||
$"initialized={retained.Initialized}");
|
$"initialized={retained.Initialized}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return retained;
|
return retained;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageInfo = new ImageCreateInfo
|
var imageInfo = new ImageCreateInfo
|
||||||
|
|||||||
@@ -2366,17 +2366,16 @@ public static partial class Gen5SpirvTranslator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GLOBAL_STORE/LOAD_DWORD(x2/x3/x4) are dword-aligned by the GCN ISA, so read/write dwords directly instead of the per-byte loop.
|
||||||
for (uint index = 0; index < control.DwordCount; index++)
|
for (uint index = 0; index < control.DwordCount; index++)
|
||||||
{
|
{
|
||||||
var address = index == 0
|
var indexedDwordAddress = index == 0
|
||||||
? byteAddress
|
? dwordAddress
|
||||||
: IAdd(byteAddress, UInt(index * sizeof(uint)));
|
: IAdd(dwordAddress, UInt(index));
|
||||||
StoreBufferBytes(
|
StoreBufferWord(
|
||||||
bindingIndex,
|
bindingIndex,
|
||||||
address,
|
indexedDwordAddress,
|
||||||
LoadV(control.VectorData + index),
|
LoadV(control.VectorData + index));
|
||||||
sizeof(uint),
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@@ -2404,12 +2403,12 @@ public static partial class Gen5SpirvTranslator
|
|||||||
|
|
||||||
for (uint index = 0; index < control.DwordCount; index++)
|
for (uint index = 0; index < control.DwordCount; index++)
|
||||||
{
|
{
|
||||||
var address = index == 0
|
var indexedDwordAddress = index == 0
|
||||||
? byteAddress
|
? dwordAddress
|
||||||
: IAdd(byteAddress, UInt(index * sizeof(uint)));
|
: IAdd(dwordAddress, UInt(index));
|
||||||
StoreV(
|
StoreV(
|
||||||
control.VectorData + index,
|
control.VectorData + index,
|
||||||
LoadUnalignedBufferWord(bindingIndex, address));
|
LoadBufferWord(bindingIndex, indexedDwordAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -2510,17 +2509,16 @@ public static partial class Gen5SpirvTranslator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BUFFER_STORE/LOAD_DWORD(x2/x3/x4) are dword-aligned by the GCN ISA, same as the GLOBAL case above — no per-byte reassembly needed.
|
||||||
for (uint index = 0; index < control.DwordCount; index++)
|
for (uint index = 0; index < control.DwordCount; index++)
|
||||||
{
|
{
|
||||||
var address = index == 0
|
var indexedDwordAddress = index == 0
|
||||||
? byteAddress
|
? dwordAddress
|
||||||
: IAdd(byteAddress, UInt(index * sizeof(uint)));
|
: IAdd(dwordAddress, UInt(index));
|
||||||
StoreBufferBytes(
|
StoreBufferWord(
|
||||||
bindingIndex,
|
bindingIndex,
|
||||||
address,
|
indexedDwordAddress,
|
||||||
LoadV(control.VectorData + index),
|
LoadV(control.VectorData + index));
|
||||||
sizeof(uint),
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2576,12 +2574,12 @@ public static partial class Gen5SpirvTranslator
|
|||||||
|
|
||||||
for (uint index = 0; index < control.DwordCount; index++)
|
for (uint index = 0; index < control.DwordCount; index++)
|
||||||
{
|
{
|
||||||
var address = index == 0
|
var indexedDwordAddress = index == 0
|
||||||
? byteAddress
|
? dwordAddress
|
||||||
: IAdd(byteAddress, UInt(index * sizeof(uint)));
|
: IAdd(dwordAddress, UInt(index));
|
||||||
StoreV(
|
StoreV(
|
||||||
control.VectorData + index,
|
control.VectorData + index,
|
||||||
LoadUnalignedBufferWord(bindingIndex, address));
|
LoadBufferWord(bindingIndex, indexedDwordAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user