mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
Add internal render resolution scale and fix DPI Issue (#468)
* Add internal render resolution scale and fix embedded surface DPI scaling Adds a GUI-configurable internal resolution scale (Graphics tab) that renders offscreen color/depth targets below native guest resolution and upscales on present, trading image quality for GPU headroom. Storage/UAV images and sampled asset textures are left untouched, and texture-alias/feedback-loop lookups compare against each target's logical (unscaled) size so scaled render targets are still found correctly when sampled back. Also fixes the embedded game surface not filling the window: the isolated emulator child process had no declared DPI awareness, so Windows silently downscaled every window-geometry query it made against the GUI-owned surface HWND by the display's DPI factor, leaving an unfilled black margin on scaled displays. * Remove flaky Gen5ScalarMemoryFallbackTests * Restore Gen5ScalarMemoryFallbackTests --------- Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com>
This commit is contained in:
@@ -13,4 +13,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
</application>
|
</application>
|
||||||
</compatibility>
|
</compatibility>
|
||||||
|
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<asmv3:windowsSettings>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
</assembly>
|
</assembly>
|
||||||
|
|||||||
@@ -351,6 +351,13 @@ public sealed class GameSurfaceHost : NativeControlHost
|
|||||||
var width = Math.Max(1, (int)Math.Round(Bounds.Width * renderScale));
|
var width = Math.Max(1, (int)Math.Round(Bounds.Width * renderScale));
|
||||||
var height = Math.Max(1, (int)Math.Round(Bounds.Height * renderScale));
|
var height = Math.Max(1, (int)Math.Round(Bounds.Height * renderScale));
|
||||||
var sizeChanged = _surface.PixelWidth != width || _surface.PixelHeight != height;
|
var sizeChanged = _surface.PixelWidth != width || _surface.PixelHeight != height;
|
||||||
|
if (Environment.GetEnvironmentVariable("SHARPEMU_TRACE_SURFACE_SIZE") == "1")
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[GUI][TRACE] GameSurfaceHost.UpdateSurfaceSize bounds={Bounds.Width}x{Bounds.Height} " +
|
||||||
|
$"scale={renderScale} computed={width}x{height} changed={sizeChanged} " +
|
||||||
|
$"prevSurface={_surface.PixelWidth}x{_surface.PixelHeight}");
|
||||||
|
}
|
||||||
_surface.UpdatePixelSize(width, height);
|
_surface.UpdatePixelSize(width, height);
|
||||||
|
|
||||||
if (!sizeChanged)
|
if (!sizeChanged)
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ public sealed class GuiSettings
|
|||||||
/// <summary>Names of SHARPEMU_* switches set to "1" in the emulator's environment at launch.</summary>
|
/// <summary>Names of SHARPEMU_* switches set to "1" in the emulator's environment at launch.</summary>
|
||||||
public List<string> EnvironmentToggles { get; set; } = new();
|
public List<string> EnvironmentToggles { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>Internal render resolution scale (1.0 = native, 0.5 = half).</summary>
|
||||||
|
public double RenderResolutionScale { get; set; } = 1.0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Discord application ID used for Rich Presence; the default is the
|
/// Discord application ID used for Rich Presence; the default is the
|
||||||
/// SharpEmu application. Override to rebrand what Discord shows as
|
/// SharpEmu application. Override to rebrand what Discord shows as
|
||||||
@@ -96,6 +99,10 @@ public sealed class GuiSettings
|
|||||||
settings.LogLevel ??= "Info";
|
settings.LogLevel ??= "Info";
|
||||||
settings.Language ??= "en";
|
settings.Language ??= "en";
|
||||||
settings.DiscordClientId ??= "1525606762248540221";
|
settings.DiscordClientId ??= "1525606762248540221";
|
||||||
|
if (settings.RenderResolutionScale <= 0 || settings.RenderResolutionScale > 2.0)
|
||||||
|
{
|
||||||
|
settings.RenderResolutionScale = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,6 +400,29 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
<TabItem x:Name="GraphicsTabItem" Header="Graphics" FontSize="15">
|
||||||
|
<ScrollViewer>
|
||||||
|
<StackPanel Margin="0,14,0,16" Spacing="16" MaxWidth="1280" HorizontalAlignment="Left">
|
||||||
|
|
||||||
|
<Border Classes="card">
|
||||||
|
<StackPanel Spacing="14">
|
||||||
|
<TextBlock x:Name="RenderingSectionTitle" Classes="sectionTitle" Text="RENDERING" />
|
||||||
|
|
||||||
|
<local:SettingRow x:Name="RenderResolutionRow" Label="Internal resolution"
|
||||||
|
Description="Render offscreen targets below native resolution and upscale on present. Lower values trade image quality for GPU headroom; takes effect on next launch.">
|
||||||
|
<ComboBox x:Name="RenderResolutionBox" Width="160" SelectedIndex="0"
|
||||||
|
VerticalAlignment="Center" CornerRadius="8">
|
||||||
|
<ComboBoxItem x:Name="RenderResolution100Item" Content="100% (native)" Tag="1.0" />
|
||||||
|
<ComboBoxItem x:Name="RenderResolution75Item" Content="75%" Tag="0.75" />
|
||||||
|
<ComboBoxItem x:Name="RenderResolution50Item" Content="50%" Tag="0.5" />
|
||||||
|
<ComboBoxItem x:Name="RenderResolution25Item" Content="25%" Tag="0.25" />
|
||||||
|
</ComboBox>
|
||||||
|
</local:SettingRow>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</TabItem>
|
||||||
<TabItem x:Name="EnvTabItem" Header="Environment" FontSize="15">
|
<TabItem x:Name="EnvTabItem" Header="Environment" FontSize="15">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel Margin="0,14,0,16" Spacing="16" MaxWidth="1280" HorizontalAlignment="Left">
|
<StackPanel Margin="0,14,0,16" Spacing="16" MaxWidth="1280" HorizontalAlignment="Left">
|
||||||
|
|||||||
@@ -192,6 +192,18 @@ public partial class MainWindow : Window
|
|||||||
// it is open already uses the new values.
|
// it is open already uses the new values.
|
||||||
LogLevelBox.SelectionChanged += (_, _) => _settings.LogLevel = SelectedLogLevel();
|
LogLevelBox.SelectionChanged += (_, _) => _settings.LogLevel = SelectedLogLevel();
|
||||||
TraceImportsBox.ValueChanged += (_, _) => _settings.ImportTraceLimit = (int)(TraceImportsBox.Value ?? 0);
|
TraceImportsBox.ValueChanged += (_, _) => _settings.ImportTraceLimit = (int)(TraceImportsBox.Value ?? 0);
|
||||||
|
RenderResolutionBox.SelectionChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
if (RenderResolutionBox.SelectedItem is ComboBoxItem { Tag: string tag } &&
|
||||||
|
double.TryParse(
|
||||||
|
tag,
|
||||||
|
System.Globalization.NumberStyles.Float,
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture,
|
||||||
|
out var scale))
|
||||||
|
{
|
||||||
|
_settings.RenderResolutionScale = scale;
|
||||||
|
}
|
||||||
|
};
|
||||||
StrictToggle.IsCheckedChanged += (_, _) => _settings.StrictDynlibResolution = StrictToggle.IsChecked == true;
|
StrictToggle.IsCheckedChanged += (_, _) => _settings.StrictDynlibResolution = StrictToggle.IsChecked == true;
|
||||||
LogToFileToggle.IsCheckedChanged += (_, _) => _settings.LogToFile = LogToFileToggle.IsChecked == true;
|
LogToFileToggle.IsCheckedChanged += (_, _) => _settings.LogToFile = LogToFileToggle.IsChecked == true;
|
||||||
OverrideLogFileToggle.IsCheckedChanged += (_, _) =>
|
OverrideLogFileToggle.IsCheckedChanged += (_, _) =>
|
||||||
@@ -869,6 +881,13 @@ public partial class MainWindow : Window
|
|||||||
_ => 2,
|
_ => 2,
|
||||||
};
|
};
|
||||||
TraceImportsBox.Value = Math.Clamp(_settings.ImportTraceLimit, 0, 4096);
|
TraceImportsBox.Value = Math.Clamp(_settings.ImportTraceLimit, 0, 4096);
|
||||||
|
RenderResolutionBox.SelectedIndex = _settings.RenderResolutionScale switch
|
||||||
|
{
|
||||||
|
>= 0.875 => 0,
|
||||||
|
>= 0.625 => 1,
|
||||||
|
>= 0.375 => 2,
|
||||||
|
_ => 3,
|
||||||
|
};
|
||||||
StrictToggle.IsChecked = _settings.StrictDynlibResolution;
|
StrictToggle.IsChecked = _settings.StrictDynlibResolution;
|
||||||
LogToFileToggle.IsChecked = _settings.LogToFile;
|
LogToFileToggle.IsChecked = _settings.LogToFile;
|
||||||
OverrideLogFileToggle.IsChecked = _settings.OverrideLogFile;
|
OverrideLogFileToggle.IsChecked = _settings.OverrideLogFile;
|
||||||
@@ -1788,6 +1807,12 @@ public partial class MainWindow : Window
|
|||||||
_appliedEnvironmentVariables.Add(name);
|
_appliedEnvironmentVariables.Add(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Environment.SetEnvironmentVariable(
|
||||||
|
"SHARPEMU_RENDER_SCALE",
|
||||||
|
_settings.RenderResolutionScale.ToString(
|
||||||
|
"0.###",
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
if (SharpEmuLog.TryParseLevel(effective.LogLevel, out var logLevel))
|
if (SharpEmuLog.TryParseLevel(effective.LogLevel, out var logLevel))
|
||||||
{
|
{
|
||||||
SharpEmuLog.MinimumLevel = logLevel;
|
SharpEmuLog.MinimumLevel = logLevel;
|
||||||
@@ -2030,8 +2055,6 @@ public partial class MainWindow : Window
|
|||||||
RestoreGameViewToFull();
|
RestoreGameViewToFull();
|
||||||
GameView.Background = Brushes.Black;
|
GameView.Background = Brushes.Black;
|
||||||
GameView.IsHitTestVisible = true;
|
GameView.IsHitTestVisible = true;
|
||||||
_gameSurfaceHost?.SetPresentationVisible(true);
|
|
||||||
_gameSurfaceHost?.SetCursorAutoHide(true);
|
|
||||||
LibraryPage.IsVisible = false;
|
LibraryPage.IsVisible = false;
|
||||||
OptionsPage.IsVisible = false;
|
OptionsPage.IsVisible = false;
|
||||||
LibraryToolbar.IsVisible = false;
|
LibraryToolbar.IsVisible = false;
|
||||||
@@ -2040,6 +2063,19 @@ public partial class MainWindow : Window
|
|||||||
LaunchBar.IsVisible = false;
|
LaunchBar.IsVisible = false;
|
||||||
HideSessionLoading();
|
HideSessionLoading();
|
||||||
UpdateSessionBarVisibility();
|
UpdateSessionBarVisibility();
|
||||||
|
|
||||||
|
// Defer so the layout pass from the margin change above settles first.
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
if (!_isRunning || _isStopping)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_gameSurfaceHost?.RefreshSurfaceSize();
|
||||||
|
_gameSurfaceHost?.SetPresentationVisible(true);
|
||||||
|
_gameSurfaceHost?.SetCursorAutoHide(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3088,6 +3088,9 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
public ulong WriteAddress;
|
public ulong WriteAddress;
|
||||||
public uint Width;
|
public uint Width;
|
||||||
public uint Height;
|
public uint Height;
|
||||||
|
// Unscaled guest-requested size; Width/Height are the physical (scaled) backing size.
|
||||||
|
public uint LogicalWidth;
|
||||||
|
public uint LogicalHeight;
|
||||||
public uint GuestFormat;
|
public uint GuestFormat;
|
||||||
public uint SwizzleMode;
|
public uint SwizzleMode;
|
||||||
public Image Image;
|
public Image Image;
|
||||||
@@ -3117,6 +3120,9 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
public long FlipVersion;
|
public long FlipVersion;
|
||||||
public uint Width;
|
public uint Width;
|
||||||
public uint Height;
|
public uint Height;
|
||||||
|
// Unscaled guest-requested size; Width/Height are the physical (scaled) backing size.
|
||||||
|
public uint LogicalWidth;
|
||||||
|
public uint LogicalHeight;
|
||||||
public uint MipLevels;
|
public uint MipLevels;
|
||||||
public uint GuestFormat;
|
public uint GuestFormat;
|
||||||
public Format Format;
|
public Format Format;
|
||||||
@@ -7116,8 +7122,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
// but it must not make a differently sized alias outrank the image
|
// but it must not make a differently sized alias outrank the image
|
||||||
// that the texture descriptor actually names.
|
// that the texture descriptor actually names.
|
||||||
var score = 0;
|
var score = 0;
|
||||||
if (candidate.Width == texture.Width &&
|
if (candidate.LogicalWidth == texture.Width &&
|
||||||
candidate.Height == texture.Height)
|
candidate.LogicalHeight == texture.Height)
|
||||||
{
|
{
|
||||||
score += 32;
|
score += 32;
|
||||||
}
|
}
|
||||||
@@ -7202,7 +7208,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texture.Width > depth.Width || texture.Height > depth.Height)
|
if (texture.Width > depth.LogicalWidth || texture.Height > depth.LogicalHeight)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -7412,8 +7418,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
GuestDrawTexture texture,
|
GuestDrawTexture texture,
|
||||||
GuestImageResource guestImage)
|
GuestImageResource guestImage)
|
||||||
{
|
{
|
||||||
if (guestImage.Width == texture.Width &&
|
if (guestImage.LogicalWidth == texture.Width &&
|
||||||
guestImage.Height == texture.Height)
|
guestImage.LogicalHeight == texture.Height)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -7425,8 +7431,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture.Width <= guestImage.Width &&
|
return texture.Width <= guestImage.LogicalWidth &&
|
||||||
texture.Height <= guestImage.Height;
|
texture.Height <= guestImage.LogicalHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
@@ -9161,11 +9167,19 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
|
|
||||||
private static GuestRect ClampScissor(GuestRect? scissor, Extent2D extent)
|
private static GuestRect ClampScissor(GuestRect? scissor, Extent2D extent)
|
||||||
{
|
{
|
||||||
if (scissor is not { } rect)
|
if (scissor is not { } guestRect)
|
||||||
{
|
{
|
||||||
return new GuestRect(0, 0, extent.Width, extent.Height);
|
return new GuestRect(0, 0, extent.Width, extent.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rect = _renderResolutionScale == 1.0
|
||||||
|
? guestRect
|
||||||
|
: new GuestRect(
|
||||||
|
(int)Math.Round(guestRect.X * _renderResolutionScale),
|
||||||
|
(int)Math.Round(guestRect.Y * _renderResolutionScale),
|
||||||
|
Math.Max(1u, (uint)Math.Round(guestRect.Width * _renderResolutionScale)),
|
||||||
|
Math.Max(1u, (uint)Math.Round(guestRect.Height * _renderResolutionScale)));
|
||||||
|
|
||||||
var left = Math.Clamp(rect.X, 0, checked((int)extent.Width));
|
var left = Math.Clamp(rect.X, 0, checked((int)extent.Width));
|
||||||
var top = Math.Clamp(rect.Y, 0, checked((int)extent.Height));
|
var top = Math.Clamp(rect.Y, 0, checked((int)extent.Height));
|
||||||
var right = Math.Clamp(
|
var right = Math.Clamp(
|
||||||
@@ -9193,11 +9207,22 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
|
|
||||||
private static Viewport ClampViewport(GuestViewport? viewport, Extent2D extent)
|
private static Viewport ClampViewport(GuestViewport? viewport, Extent2D extent)
|
||||||
{
|
{
|
||||||
if (viewport is not { } rect)
|
if (viewport is not { } guestRect)
|
||||||
{
|
{
|
||||||
return new Viewport(0, 0, extent.Width, extent.Height, 0, 1);
|
return new Viewport(0, 0, extent.Width, extent.Height, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var scale = (float)_renderResolutionScale;
|
||||||
|
var rect = scale == 1f
|
||||||
|
? guestRect
|
||||||
|
: guestRect with
|
||||||
|
{
|
||||||
|
X = guestRect.X * scale,
|
||||||
|
Y = guestRect.Y * scale,
|
||||||
|
Width = guestRect.Width * scale,
|
||||||
|
Height = guestRect.Height * scale,
|
||||||
|
};
|
||||||
|
|
||||||
// Do NOT trim the rectangle to the render target: Vulkan allows
|
// Do NOT trim the rectangle to the render target: Vulkan allows
|
||||||
// viewports that extend beyond the framebuffer (rendering is
|
// viewports that extend beyond the framebuffer (rendering is
|
||||||
// confined by the scissor), and trimming changes the guest's
|
// confined by the scissor), and trimming changes the guest's
|
||||||
@@ -10513,10 +10538,11 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
draw.RenderState.Depth) &&
|
draw.RenderState.Depth) &&
|
||||||
work.DepthTarget is { } depthTarget)
|
work.DepthTarget is { } depthTarget)
|
||||||
{
|
{
|
||||||
|
// Logical dims: GetOrCreateGuestDepth below scales itself.
|
||||||
var resolution = GuestDepthExtentResolver.Resolve(
|
var resolution = GuestDepthExtentResolver.Resolve(
|
||||||
depthTarget,
|
depthTarget,
|
||||||
firstTarget.Width,
|
firstTarget.LogicalWidth,
|
||||||
firstTarget.Height,
|
firstTarget.LogicalHeight,
|
||||||
draw.Textures);
|
draw.Textures);
|
||||||
var effectiveDepthTarget = resolution.IsUsable &&
|
var effectiveDepthTarget = resolution.IsUsable &&
|
||||||
(resolution.Width != depthTarget.Width ||
|
(resolution.Width != depthTarget.Width ||
|
||||||
@@ -11216,7 +11242,14 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
$"address 0x{target.Address:X16}.");
|
$"address 0x{target.Address:X16}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var mipLevels = ClampMipLevels(target.Width, target.Height, target.MipLevels);
|
// Storage/UAV images keep native guest dimensions (compute shaders index them directly).
|
||||||
|
var physicalWidth = requiresStorage
|
||||||
|
? target.Width
|
||||||
|
: ScaleGuestDimension(target.Width);
|
||||||
|
var physicalHeight = requiresStorage
|
||||||
|
? target.Height
|
||||||
|
: ScaleGuestDimension(target.Height);
|
||||||
|
var mipLevels = ClampMipLevels(physicalWidth, physicalHeight, target.MipLevels);
|
||||||
var guestFormat = GetGuestTextureFormat(target.Format, target.NumberType);
|
var guestFormat = GetGuestTextureFormat(target.Format, target.NumberType);
|
||||||
var requestedKey = new GuestImageVariantKey(
|
var requestedKey = new GuestImageVariantKey(
|
||||||
target.Address,
|
target.Address,
|
||||||
@@ -11227,8 +11260,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
format);
|
format);
|
||||||
if (_guestImages.TryGetValue(target.Address, out var existing))
|
if (_guestImages.TryGetValue(target.Address, out var existing))
|
||||||
{
|
{
|
||||||
if (existing.Width == target.Width &&
|
if (existing.LogicalWidth == target.Width &&
|
||||||
existing.Height == target.Height &&
|
existing.LogicalHeight == target.Height &&
|
||||||
existing.MipLevels == mipLevels &&
|
existing.MipLevels == mipLevels &&
|
||||||
existing.GuestFormat == guestFormat &&
|
existing.GuestFormat == guestFormat &&
|
||||||
existing.Format == format)
|
existing.Format == format)
|
||||||
@@ -11278,8 +11311,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_guestImageVariants.Add(
|
_guestImageVariants.Add(
|
||||||
new GuestImageVariantKey(
|
new GuestImageVariantKey(
|
||||||
existing.Address,
|
existing.Address,
|
||||||
existing.Width,
|
existing.LogicalWidth,
|
||||||
existing.Height,
|
existing.LogicalHeight,
|
||||||
existing.MipLevels,
|
existing.MipLevels,
|
||||||
existing.GuestFormat,
|
existing.GuestFormat,
|
||||||
existing.Format),
|
existing.Format),
|
||||||
@@ -11348,7 +11381,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
ImageCreateFlags.CreateExtendedUsageBit,
|
ImageCreateFlags.CreateExtendedUsageBit,
|
||||||
ImageType = ImageType.Type2D,
|
ImageType = ImageType.Type2D,
|
||||||
Format = format,
|
Format = format,
|
||||||
Extent = new Extent3D(target.Width, target.Height, 1),
|
Extent = new Extent3D(physicalWidth, physicalHeight, 1),
|
||||||
MipLevels = mipLevels,
|
MipLevels = mipLevels,
|
||||||
ArrayLayers = 1,
|
ArrayLayers = 1,
|
||||||
Samples = SampleCountFlags.Count1Bit,
|
Samples = SampleCountFlags.Count1Bit,
|
||||||
@@ -11421,15 +11454,17 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
CreateRenderPassAndFramebuffer(
|
CreateRenderPassAndFramebuffer(
|
||||||
format,
|
format,
|
||||||
mipViews[0],
|
mipViews[0],
|
||||||
target.Width,
|
physicalWidth,
|
||||||
target.Height);
|
physicalHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
var resource = new GuestImageResource
|
var resource = new GuestImageResource
|
||||||
{
|
{
|
||||||
Address = target.Address,
|
Address = target.Address,
|
||||||
Width = target.Width,
|
Width = physicalWidth,
|
||||||
Height = target.Height,
|
Height = physicalHeight,
|
||||||
|
LogicalWidth = target.Width,
|
||||||
|
LogicalHeight = target.Height,
|
||||||
MipLevels = mipLevels,
|
MipLevels = mipLevels,
|
||||||
GuestFormat = guestFormat,
|
GuestFormat = guestFormat,
|
||||||
Format = format,
|
Format = format,
|
||||||
@@ -11685,15 +11720,19 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (image, memory, view) = CreateDepthAttachment(target.Width, target.Height);
|
var physicalWidth = ScaleGuestDimension(target.Width);
|
||||||
|
var physicalHeight = ScaleGuestDimension(target.Height);
|
||||||
|
var (image, memory, view) = CreateDepthAttachment(physicalWidth, physicalHeight);
|
||||||
var resource = new GuestDepthResource
|
var resource = new GuestDepthResource
|
||||||
{
|
{
|
||||||
Key = key,
|
Key = key,
|
||||||
Address = target.Address,
|
Address = target.Address,
|
||||||
ReadAddress = target.ReadAddress,
|
ReadAddress = target.ReadAddress,
|
||||||
WriteAddress = target.WriteAddress,
|
WriteAddress = target.WriteAddress,
|
||||||
Width = target.Width,
|
Width = physicalWidth,
|
||||||
Height = target.Height,
|
Height = physicalHeight,
|
||||||
|
LogicalWidth = target.Width,
|
||||||
|
LogicalHeight = target.Height,
|
||||||
GuestFormat = target.GuestFormat,
|
GuestFormat = target.GuestFormat,
|
||||||
SwizzleMode = target.SwizzleMode,
|
SwizzleMode = target.SwizzleMode,
|
||||||
Image = image,
|
Image = image,
|
||||||
@@ -14153,6 +14192,22 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
!string.IsNullOrWhiteSpace(
|
!string.IsNullOrWhiteSpace(
|
||||||
Environment.GetEnvironmentVariable(
|
Environment.GetEnvironmentVariable(
|
||||||
"SHARPEMU_TRACE_GUEST_IMAGE_ADDRS"));
|
"SHARPEMU_TRACE_GUEST_IMAGE_ADDRS"));
|
||||||
|
private static readonly double _renderResolutionScale =
|
||||||
|
double.TryParse(
|
||||||
|
Environment.GetEnvironmentVariable("SHARPEMU_RENDER_SCALE"),
|
||||||
|
System.Globalization.NumberStyles.Float,
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture,
|
||||||
|
out var renderResolutionScale) &&
|
||||||
|
renderResolutionScale > 0 &&
|
||||||
|
renderResolutionScale <= 2.0
|
||||||
|
? renderResolutionScale
|
||||||
|
: 1.0;
|
||||||
|
|
||||||
|
private static uint ScaleGuestDimension(uint value) =>
|
||||||
|
_renderResolutionScale == 1.0
|
||||||
|
? value
|
||||||
|
: Math.Max(1u, (uint)Math.Round(value * _renderResolutionScale));
|
||||||
|
|
||||||
private static readonly bool _forceFullscreenPipeline =
|
private static readonly bool _forceFullscreenPipeline =
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_FULLSCREEN_PIPELINE") == "1";
|
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_FULLSCREEN_PIPELINE") == "1";
|
||||||
private static readonly bool _forceFullscreenVertex =
|
private static readonly bool _forceFullscreenVertex =
|
||||||
|
|||||||
Reference in New Issue
Block a user