Compare commits

...
73 Commits
Author SHA1 Message Date
ElTioRataandGitHub 4c62c82dab GameDB: Hardware Fixes for Burnout 2 (#9770) 2023-08-17 01:46:58 +01:00
refractionpcsx2 92a9554240 GameDB: Add VU0 clamping to Naruto Ultimate Ninja 5 (Accel 2) 2023-08-16 17:01:02 +01:00
refractionpcsx2 324a700fb7 GS/TC: Don't allow tex is rt for PSMT8 on 16bit targets 2023-08-16 09:48:23 +01:00
refractionpcsx2 a6b934758b GS/HW: Only preserve valid channels on target overlap 2023-08-16 01:13:01 +01:00
Stenzek cd5ec59f6a GS/DX11: Commit clear before clearing stencil
Stops the discard happening later, and blowing away the cleared value.
2023-08-15 13:08:51 +10:00
RedPanda4552andConnor McLaughlin 80fe813116 [SAVEVERSION+] Remove old trigger deadzone references, replace with button deadzone
Was removed from UI but not the backend
2023-08-14 20:06:47 +10:00
Stenzek 1262afef63 GS/DX12: Use WinPixEventRuntime for debug messages 2023-08-14 20:06:16 +10:00
Stenzek 987489b34a 3rdparty: Add WinPixEventRuntime 2023-08-14 20:06:16 +10:00
TellowKrinkleandConnor McLaughlin 9310861cc2 GS:MTL: Properly set stencil reference on draw encoders 2023-08-14 20:06:04 +10:00
kamfretozandrefractionpcsx2 53f0db99ca GameDB: Various Fixes 2023-08-14 09:09:43 +01:00
Christian Kennyandrefractionpcsx2 2eb125f6e2 gitignore: Don't include TXT files generated by crash dumps 2023-08-12 23:02:33 +01:00
Mrlinkwiiandrefractionpcsx2 f805d9432e UI: remove mention of Half Screen Fix 2023-08-12 23:02:14 +01:00
refractionpcsx2 054cb9e9fa GS/HW: Correctly remove offset when detecting shuffle coords 2023-08-12 19:39:25 +01:00
refractionpcsx2 1f77e7d90a GS/CRC: Fix the sizing on Sonic Unleashed copy CRC 2023-08-12 19:34:06 +01:00
lightningterror 7ed33832b8 GS/HW: Isolate early returns for blending.
Optimization.
2023-08-12 18:14:37 +02:00
lightningterror d19e9cc662 GS: Bump shader cache version. 2023-08-12 18:14:37 +02:00
lightningterror 846c9cec6a GS/HW: Adjust how we handle RT alpha in blending.
When both rt min and max are equal then we know what Ad value is,
if so use Af bit instead and set AFIX value from rt alpha value that we know.

On OpenGL when BLEND C == 1 but reading the rt is disabled, set the value to 0 instead
of reading an undefined value.
2023-08-12 18:14:37 +02:00
Sanjay Govindandrefractionpcsx2 460e4b8bb3 USB-Turntable: Default multiplier to 1 2023-08-12 14:07:37 +01:00
Stenzek 2e6bd23f22 Qt: Fix arrow key handling on macOS 2023-08-12 16:17:47 +10:00
refractionpcsx2 7763948fe4 GS/HW: Restrict double buffer detection to widths greater than 256 2023-08-12 01:54:31 +01:00
refractionpcsx2 2a0f001902 GIF: Adjust run length of split in intermittent packets 2023-08-12 00:28:28 +01:00
refractionpcsx2 14df69e180 GS/HW: Fix crash where it could process a texture with no RT 2023-08-12 00:28:28 +01:00
refractionpcsx2 28b6389b36 GS/HW: Stop errantly halfing normal height 2023-08-12 00:28:28 +01:00
lightningterror 905c0b8c7a GS: Fix warnings.
GSRendererHW:
Fix Wtautological-constant-out-of-range-compare.

GSSstate:
Fix Wunused-variable.
Fix Wsign-compare.
2023-08-11 21:46:29 +02:00
lightningterror b674cc95ac Qt: Fix Wreorder-ctor warning. 2023-08-11 21:46:29 +02:00
Mrlinkwiiandlightningterror bccb2509b5 GameDB: Fixes for Ghost Recon - Advanced Warfighter & Battlefield 2 2023-08-11 18:16:33 +02:00
refractionpcsx2 cd980c6604 GS/TC: Fix crash in debug message (only affects debug device) 2023-08-11 16:08:38 +01:00
lightningterror ddff4ec083 GameDB: Add full mipmap plus triliear ps2 to The Godfather.
Improves building textures to match sw renderer.
2023-08-11 17:05:21 +02:00
kamfretozandrefractionpcsx2 5bf97be8ef Qt: Even further icon refinement 2023-08-11 15:24:04 +01:00
lightningterrorandrefractionpcsx2 7fadb6ed05 GS/HW: Cleanup texture shuffle shaders. 2023-08-11 15:23:50 +01:00
lightningterrorandrefractionpcsx2 89688d67dd GS: Bump shader cache version. 2023-08-11 15:23:50 +01:00
refractionpcsx2 279007544a GS/HW: Allow double clear when src is lowest bitsize denominator 2023-08-11 14:28:34 +01:00
lightningterrorandrefractionpcsx2 1c0f0e1423 GS: Bump shader cache version. 2023-08-11 13:14:19 +01:00
lightningterrorandrefractionpcsx2 1ab909addc Config/gamedb: Remove screen fix from config entirely.
No longer needed.
2023-08-11 13:14:19 +01:00
lightningterrorandrefractionpcsx2 99dfd15469 ImGui: Remove Half Screen Fix from the ui.
No longer needed.
2023-08-11 13:14:19 +01:00
lightningterrorandrefractionpcsx2 be18ecf74e Qt: Remove Half Screen Fix from the ui.
No longer needed.
2023-08-11 13:14:19 +01:00
lightningterrorandrefractionpcsx2 713bd132dc GS/D3D: Add initial support for complex shuffles. 2023-08-11 13:14:19 +01:00
refractionpcsx2 20f03e237e GameDB: Add Autoflush to Minority Report 2023-08-11 13:14:19 +01:00
refractionpcsx2 7d3c3911ea GS/HW: Update BigMuthaTruckers hack to a GSC 2023-08-11 13:14:19 +01:00
refractionpcsx2 ccfee2be83 GameDB: Add Tex in RT to Godfather + remove skipdraw 2023-08-11 13:14:19 +01:00
refractionpcsx2 1193b0b990 GS/TC: Delete old targets which aren't preserved and don't match size 2023-08-11 13:14:19 +01:00
refractionpcsx2 5a3ba4e563 GS/HW: Add support for complex offset shuffles 2023-08-11 13:14:19 +01:00
refractionpcsx2 e9c342ef74 GS/Autoflush: Account for CLAMP & better handle different formats 2023-08-11 12:35:42 +01:00
refractionpcsx2 8927ffa035 GS: Fix autoflush behaviour when using Z + C formats together 2023-08-11 12:35:42 +01:00
Ty Lamontagneandrefractionpcsx2 fdb0312326 Dis5900: Mask and extend the pmfhl & pmthl LUT
Fixes an OOB access when disassembling an invalid opcode (71756972h)
2023-08-11 00:09:25 +01:00
refractionpcsx2 2f21caa41c GameDB: Add Instant DMA to Jak X Combat Racing 2023-08-10 10:40:43 +01:00
Stenzekandrefractionpcsx2 51a35d40e5 3rdparty: Bump rcheevos to 3af1e2fc5188d6e932ee379942f4049ea877e648 2023-08-10 01:27:20 +01:00
Stenzekandrefractionpcsx2 6ec524adec USB/GunCon2: Fix cursor not starting in center 2023-08-10 01:27:10 +01:00
Stenzekandrefractionpcsx2 7d4a7ec8c7 ImGuiManager: Fix window width/height not being initialized 2023-08-10 01:27:10 +01:00
Silentandrefractionpcsx2 0256c4521d GameList: Support adding custom title and region to files 2023-08-10 01:26:06 +01:00
Silentandrefractionpcsx2 be3ed181c1 SettingsInterface: Fix constness of a few methods 2023-08-10 01:26:06 +01:00
PCSX2 BotandConnor McLaughlin 38cf7884fe Qt: Update Base Translation 2023-08-09 21:37:15 +10:00
JordanTheToasterandConnor McLaughlin 25d426e8b2 GameDB: Various fixes
Fixes for missing recommended blending on MGS 2 Sub and post processing positioning on Project Snowblind.
2023-08-09 21:35:34 +10:00
TellowKrinkleandConnor McLaughlin edff9ae684 GS:MTL: Include full error messages in aborts 2023-08-09 21:34:38 +10:00
TellowKrinkleandConnor McLaughlin a622e3bda6 Mac: Mark crash info as used so it doesn't get DCE'd 2023-08-09 21:34:38 +10:00
MrlinkwiiandConnor McLaughlin 4d54e28e63 GameDB: remove all god hand getSkipCount 2023-08-09 21:34:07 +10:00
MrlinkwiiandConnor McLaughlin c89d023f91 CRC/HW: remove god hand GSC 2023-08-09 21:34:07 +10:00
Stenzek c7cfea1daa GS/Vulkan: Make VK_EXT_provoking_vertex optional again
RenderDoc doesn't support it.
2023-08-09 21:33:45 +10:00
Stenzek beb07365a0 Build: Disable Vulkan on macOS builds
MoltenVK does not support the extensions we now require.
2023-08-09 19:34:23 +10:00
Stenzek 0787c65e51 GS/Vulkan: Mandate well-supported extensions
- VK_EXT_provoking_vertex and provokingVertexLast
 - VK_EXT_line_rasterization and bresenhamLines
 - VK_KHR_shader_draw_parameters

Lack of these would cause rendering issues anyway.
2023-08-09 19:34:23 +10:00
Stenzek d48dea7273 GS/Vulkan: Don't track vertex buffer state
It never changes.
2023-08-09 19:34:23 +10:00
Stenzek 2432cc6ed2 GS/Vulkan: clang-format 2023-08-09 19:34:23 +10:00
Stenzek cf523d4215 GS/Vulkan: Use push descriptors instead of per-frame allocations 2023-08-09 19:34:23 +10:00
lightningterror 906d87c4b8 GS/HW: Fix nfs undercover crashing on dev/debug builds.
We were reading the wrong value.
2023-08-09 10:23:47 +02:00
Connor McLaughlinandTellowKrinkle 9c4a92c015 GS/Metal: Fix crash when Z write is on second pass 2023-08-08 18:27:54 -05:00
lightningterror 0c056ba691 USB: Fix function parameter should be passed by const reference warnings.
Codacy.
2023-08-08 21:27:36 +02:00
lightningterror 424158b28d DEV9: Fix function parameter should be passed by const reference warnings.
Codacy.
2023-08-08 21:27:36 +02:00
JordanTheToasterandrefractionpcsx2 57f118a5b5 GameDB: Various fixes
Fixes line across the screen and offset blur in Samurai Westerner and corrects region of Chinese entry's to properly use the flag included in the icons folder.
2023-08-08 08:58:55 +01:00
refractionpcsx2 65e75f3da2 GS/TC: Ignore linear part of rect when translating 2023-08-08 08:57:09 +01:00
refractionpcsx2 41174cde45 GS: Elide flushes on GS transfer if not required 2023-08-08 08:57:09 +01:00
refractionpcsx2 cb7d01fb36 GameDB: Improve Myst 3 patch 2023-08-08 08:56:52 +01:00
refractionpcsx2 3e78f8e87d IPU: Improve DMA/IPU call locations to reduce looping 2023-08-08 08:56:52 +01:00
BacklogOdysseyandConnor McLaughlin f2c032ba07 RetroAchievements/Qt: Add configurable achievement notification duration 2023-08-08 13:17:28 +10:00
106 changed files with 4772 additions and 1813 deletions
+1
View File
@@ -105,6 +105,7 @@ jobs:
cmake -DCMAKE_PREFIX_PATH="$HOME/deps" \
-DCMAKE_BUILD_TYPE=Release \
-DUSE_OPENGL=OFF \
-DUSE_VULKAN=OFF \
-DDISABLE_ADVANCE_SIMD=ON \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
-DUSE_SYSTEM_LIBS=OFF \
+1
View File
@@ -44,6 +44,7 @@ Thumbs.db
Debug.txt
install_log.txt
bad_shader_*
crash-*.txt
Debug
Release
+37
View File
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="UserMacros">
<BinaryOutputDir>$(SolutionDir)bin\</BinaryOutputDir>
<WinPixEventRuntimeDir>$(SolutionDir)3rdparty\winpixeventruntime\</WinPixEventRuntimeDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(WinPixEventRuntimeDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link Condition="'$(Platform)'=='x64'">
<AdditionalLibraryDirectories>$(WinPixEventRuntimeDir)lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>WinPixEventRuntime.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib Condition="'$(Platform)'=='x64'">
<AdditionalLibraryDirectories>$(WinPixEventRuntimeDir)lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>WinPixEventRuntime.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<!--Copy the needed dlls-->
<ItemGroup>
<WinPixEventRuntimeDlls Condition="'$(Platform)'=='x64'" Include="$(WinPixEventRuntimeDir)bin\WinPixEventRuntime.dll" />
</ItemGroup>
<Target Name="WinPixEventRuntimeCopyBinaries"
AfterTargets="Build"
Condition="'$(Platform)'=='x64'"
Inputs="@(WinPixEventRuntimeDlls)"
Outputs="@(WinPixEventRuntimeDlls -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)')">
<Message Text="Copying WinPixEventRuntime .dlls" Importance="High" />
<Copy
SourceFiles="@(WinPixEventRuntimeDlls)"
DestinationFolder="$(BinaryOutputDir)"
SkipUnchangedFiles="true"
/>
</Target>
</Project>
Binary file not shown.
@@ -0,0 +1,661 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
/*==========================================================================;
*
* Copyright (C) Microsoft Corporation. All Rights Reserved.
*
* File: PIXEvents.h
* Content: PIX include file
* Don't include this file directly - use pix3.h
*
****************************************************************************/
#pragma once
#ifndef _PixEvents_H_
#define _PixEvents_H_
#ifndef _PIX3_H_
# error Do not include this file directly - use pix3.h
#endif
#include "PIXEventsCommon.h"
#if _MSC_VER < 1800
# error This version of pix3.h is only supported on Visual Studio 2013 or higher
#elif _MSC_VER < 1900
# ifndef constexpr // Visual Studio 2013 doesn't support constexpr
# define constexpr
# define PIX3__DEFINED_CONSTEXPR
# endif
#endif
// Xbox does not support CPU events for retail scenarios
#if defined(USE_PIX) || !defined(PIX_XBOX)
#define PIX_CONTEXT_EMIT_CPU_EVENTS
#endif
namespace PIXEventsDetail
{
template<typename... ARGS>
struct PIXEventTypeInferer
{
static constexpr PIXEventType Begin() { return PIXEvent_BeginEvent_VarArgs; }
static constexpr PIXEventType SetMarker() { return PIXEvent_SetMarker_VarArgs; }
static constexpr PIXEventType BeginOnContext() { return PIXEvent_BeginEvent_OnContext_VarArgs; }
static constexpr PIXEventType SetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_VarArgs; }
// Xbox and Windows store different types of events for context events.
// On Xbox these include a context argument, while on Windows they do
// not. It is important not to change the event types used on the
// Windows version as there are OS components (eg debug layer & DRED)
// that decode event structs.
#ifdef PIX_XBOX
static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_OnContext_VarArgs; }
static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_VarArgs; }
#else
static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_VarArgs; }
static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_VarArgs; }
#endif
};
template<>
struct PIXEventTypeInferer<>
{
static constexpr PIXEventType Begin() { return PIXEvent_BeginEvent_NoArgs; }
static constexpr PIXEventType SetMarker() { return PIXEvent_SetMarker_NoArgs; }
static constexpr PIXEventType BeginOnContext() { return PIXEvent_BeginEvent_OnContext_NoArgs; }
static constexpr PIXEventType SetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_NoArgs; }
#ifdef PIX_XBOX
static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_OnContext_NoArgs; }
static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_NoArgs; }
#else
static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_NoArgs; }
static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_NoArgs; }
#endif
};
inline void PIXCopyEventArguments(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit)
{
// nothing
UNREFERENCED_PARAMETER(destination);
UNREFERENCED_PARAMETER(limit);
}
template<typename ARG, typename... ARGS>
void PIXCopyEventArguments(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, ARG const& arg, ARGS const&... args)
{
PIXCopyEventArgument(destination, limit, arg);
PIXCopyEventArguments(destination, limit, args...);
}
template<typename STR, typename... ARGS>
__declspec(noinline) void PIXBeginEventAllocate(PIXEventsThreadInfo* threadInfo, UINT64 color, STR formatString, ARGS... args)
{
#ifdef PIX_XBOX
UINT64 time = PIXEventsReplaceBlock(false);
#else
UINT64 time = PIXEventsReplaceBlock(threadInfo, false);
#endif
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::Begin());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
template<typename STR, typename... ARGS>
void PIXBeginEvent(UINT64 color, STR formatString, ARGS... args)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* limit = threadInfo->biasedLimit;
if (limit != nullptr)
{
UINT64* destination = threadInfo->destination;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::Begin());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else
{
PIXBeginEventAllocate(threadInfo, color, formatString, args...);
}
}
}
template<typename STR, typename... ARGS>
__declspec(noinline) void PIXSetMarkerAllocate(PIXEventsThreadInfo* threadInfo, UINT64 color, STR formatString, ARGS... args)
{
#ifdef PIX_XBOX
UINT64 time = PIXEventsReplaceBlock(false);
#else
UINT64 time = PIXEventsReplaceBlock(threadInfo, false);
#endif
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::SetMarker());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
template<typename STR, typename... ARGS>
void PIXSetMarker(UINT64 color, STR formatString, ARGS... args)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* limit = threadInfo->biasedLimit;
if (limit != nullptr)
{
UINT64* destination = threadInfo->destination;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::SetMarker());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else
{
PIXSetMarkerAllocate(threadInfo, color, formatString, args...);
}
}
}
template<typename STR, typename... ARGS>
__declspec(noinline) void PIXBeginEventOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context, UINT64 color, STR formatString, ARGS... args)
{
#ifdef PIX_XBOX
UINT64 time = PIXEventsReplaceBlock(false);
#else
UINT64 time = PIXEventsReplaceBlock(threadInfo, false);
#endif
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::BeginOnContext());
*destination++ = color;
#ifdef PIX_XBOX
UNREFERENCED_PARAMETER(context);
PIXCopyEventArguments(destination, limit, formatString, args...);
#else
PIXCopyEventArguments(destination, limit, context, formatString, args...);
#endif
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
template<typename STR, typename... ARGS>
void PIXBeginEventOnContextCpu(void* context, UINT64 color, STR formatString, ARGS... args)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* limit = threadInfo->biasedLimit;
if (limit != nullptr)
{
UINT64* destination = threadInfo->destination;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::BeginOnContext());
*destination++ = color;
#ifdef PIX_XBOX
PIXCopyEventArguments(destination, limit, formatString, args...);
#else
PIXCopyEventArguments(destination, limit, context, formatString, args...);
#endif
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else
{
PIXBeginEventOnContextCpuAllocate(threadInfo, context, color, formatString, args...);
}
}
}
template<typename CONTEXT, typename STR, typename... ARGS>
void PIXBeginEvent(CONTEXT* context, UINT64 color, STR formatString, ARGS... args)
{
#ifdef PIX_CONTEXT_EMIT_CPU_EVENTS
PIXBeginEventOnContextCpu(context, color, formatString, args...);
#endif
// TODO: we've already encoded this once for the CPU event - figure out way to avoid doing it again
UINT64 buffer[PIXEventsGraphicsRecordSpaceQwords];
UINT64* destination = buffer;
UINT64* limit = buffer + PIXEventsGraphicsRecordSpaceQwords - PIXEventsReservedTailSpaceQwords;
*destination++ = PIXEncodeEventInfo(0, PIXEventTypeInferer<ARGS...>::GpuBeginOnContext());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = 0ull;
PIXBeginGPUEventOnContext(context, static_cast<void*>(buffer), static_cast<UINT>(reinterpret_cast<BYTE*>(destination) - reinterpret_cast<BYTE*>(buffer)));
}
template<typename STR, typename... ARGS>
__declspec(noinline) void PIXSetMarkerOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context, UINT64 color, STR formatString, ARGS... args)
{
#ifdef PIX_XBOX
UINT64 time = PIXEventsReplaceBlock(false);
#else
UINT64 time = PIXEventsReplaceBlock(threadInfo, false);
#endif
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::SetMarkerOnContext());
*destination++ = color;
#ifdef PIX_XBOX
UNREFERENCED_PARAMETER(context);
PIXCopyEventArguments(destination, limit, formatString, args...);
#else
PIXCopyEventArguments(destination, limit, context, formatString, args...);
#endif
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
template<typename STR, typename... ARGS>
void PIXSetMarkerOnContextCpu(void* context, UINT64 color, STR formatString, ARGS... args)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* limit = threadInfo->biasedLimit;
if (limit != nullptr)
{
UINT64* destination = threadInfo->destination;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::SetMarkerOnContext());
*destination++ = color;
#ifdef PIX_XBOX
PIXCopyEventArguments(destination, limit, formatString, args...);
#else
PIXCopyEventArguments(destination, limit, context, formatString, args...);
#endif
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else
{
PIXSetMarkerOnContextCpuAllocate(threadInfo, context, color, formatString, args...);
}
}
}
template<typename CONTEXT, typename STR, typename... ARGS>
void PIXSetMarker(CONTEXT* context, UINT64 color, STR formatString, ARGS... args)
{
#ifdef PIX_CONTEXT_EMIT_CPU_EVENTS
PIXSetMarkerOnContextCpu(context, color, formatString, args...);
#endif
UINT64 buffer[PIXEventsGraphicsRecordSpaceQwords];
UINT64* destination = buffer;
UINT64* limit = buffer + PIXEventsGraphicsRecordSpaceQwords - PIXEventsReservedTailSpaceQwords;
*destination++ = PIXEncodeEventInfo(0, PIXEventTypeInferer<ARGS...>::GpuSetMarkerOnContext());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = 0ull;
PIXSetGPUMarkerOnContext(context, static_cast<void*>(buffer), static_cast<UINT>(reinterpret_cast<BYTE*>(destination) - reinterpret_cast<BYTE*>(buffer)));
}
__declspec(noinline) inline void PIXEndEventAllocate(PIXEventsThreadInfo* threadInfo)
{
#ifdef PIX_XBOX
UINT64 time = PIXEventsReplaceBlock(true);
#else
UINT64 time = PIXEventsReplaceBlock(threadInfo, true);
#endif
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
inline void PIXEndEvent()
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* limit = threadInfo->biasedLimit;
if (limit != nullptr)
{
UINT64* destination = threadInfo->destination;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else
{
PIXEndEventAllocate(threadInfo);
}
}
}
__declspec(noinline) inline void PIXEndEventOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context)
{
#ifdef PIX_XBOX
UINT64 time = PIXEventsReplaceBlock(true);
#else
UINT64 time = PIXEventsReplaceBlock(threadInfo, true);
#endif
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent_OnContext);
#ifdef PIX_XBOX
UNREFERENCED_PARAMETER(context);
#else
PIXCopyEventArgument(destination, limit, context);
#endif
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
inline void PIXEndEventOnContextCpu(void* context)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* limit = threadInfo->biasedLimit;
if (limit != nullptr)
{
UINT64* destination = threadInfo->destination;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent_OnContext);
#ifndef PIX_XBOX
PIXCopyEventArgument(destination, limit, context);
#endif
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else
{
PIXEndEventOnContextCpuAllocate(threadInfo, context);
}
}
}
template<typename CONTEXT>
void PIXEndEvent(CONTEXT* context)
{
#ifdef PIX_CONTEXT_EMIT_CPU_EVENTS
PIXEndEventOnContextCpu(context);
#endif
PIXEndGPUEventOnContext(context);
}
}
#if defined(USE_PIX)
template<typename... ARGS>
void PIXBeginEvent(UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(color, formatString, args...);
}
template<typename... ARGS>
void PIXBeginEvent(UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(color, formatString, args...);
}
template<typename... ARGS>
void PIXSetMarker(UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(color, formatString, args...);
}
template<typename... ARGS>
void PIXSetMarker(UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXBeginEvent(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXBeginEvent(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXSetMarker(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXSetMarker(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(context, color, formatString, args...);
}
inline void PIXEndEvent()
{
PIXEventsDetail::PIXEndEvent();
}
template<typename CONTEXT>
void PIXEndEvent(CONTEXT* context)
{
PIXEventsDetail::PIXEndEvent(context);
}
#else // USE_PIX_RETAIL
inline void PIXBeginEvent(UINT64, _In_ PCSTR, ...) {}
inline void PIXBeginEvent(UINT64, _In_ PCWSTR, ...) {}
inline void PIXBeginEvent(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXBeginEvent(void*, UINT64, _In_ PCWSTR, ...) {}
inline void PIXEndEvent() {}
inline void PIXEndEvent(void*) {}
inline void PIXSetMarker(UINT64, _In_ PCSTR, ...) {}
inline void PIXSetMarker(UINT64, _In_ PCWSTR, ...) {}
inline void PIXSetMarker(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXSetMarker(void*, UINT64, _In_ PCWSTR, ...) {}
#endif // USE_PIX
template<typename CONTEXT, typename... ARGS>
void PIXBeginRetailEvent(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXBeginRetailEvent(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXSetRetailMarker(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXSetRetailMarker(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(context, color, formatString, args...);
}
template<typename CONTEXT>
void PIXEndRetailEvent(CONTEXT* context)
{
PIXEventsDetail::PIXEndEvent(context);
}
template<typename CONTEXT>
class PIXScopedEventObject
{
CONTEXT* m_context;
public:
template<typename... ARGS>
PIXScopedEventObject(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
: m_context(context)
{
PIXBeginEvent(m_context, color, formatString, args...);
}
template<typename... ARGS>
PIXScopedEventObject(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
: m_context(context)
{
PIXBeginEvent(m_context, color, formatString, args...);
}
~PIXScopedEventObject()
{
PIXEndEvent(m_context);
}
};
template<typename CONTEXT>
class PIXScopedRetailEventObject
{
CONTEXT* m_context;
public:
template<typename... ARGS>
PIXScopedRetailEventObject(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
: m_context(context)
{
PIXBeginRetailEvent(m_context, color, formatString, args...);
}
template<typename... ARGS>
PIXScopedRetailEventObject(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
: m_context(context)
{
PIXBeginRetailEvent(m_context, color, formatString, args...);
}
~PIXScopedRetailEventObject()
{
PIXEndRetailEvent(m_context);
}
};
template<>
class PIXScopedEventObject<void>
{
public:
template<typename... ARGS>
PIXScopedEventObject(UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXBeginEvent(color, formatString, args...);
}
template<typename... ARGS>
PIXScopedEventObject(UINT64 color, PCSTR formatString, ARGS... args)
{
PIXBeginEvent(color, formatString, args...);
}
~PIXScopedEventObject()
{
PIXEndEvent();
}
};
#define PIXConcatenate(a, b) a ## b
#define PIXGetScopedEventVariableName(a, b) PIXConcatenate(a, b)
#define PIXScopedEvent(context, ...) PIXScopedEventObject<PIXInferScopedEventType<decltype(context)>::Type> PIXGetScopedEventVariableName(pixEvent, __LINE__)(context, __VA_ARGS__)
#ifdef PIX3__DEFINED_CONSTEXPR
#undef constexpr
#undef PIX3__DEFINED_CONSTEXPR
#endif
#endif // _PIXEvents_H__
@@ -0,0 +1,605 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
/*==========================================================================;
*
* Copyright (C) Microsoft Corporation. All Rights Reserved.
*
* File: PIXEventsCommon.h
* Content: PIX include file
* Don't include this file directly - use pix3.h
*
****************************************************************************/
#pragma once
#ifndef _PIXEventsCommon_H_
#define _PIXEventsCommon_H_
#if defined(XBOX) || defined(_XBOX_ONE) || defined(_DURANGO) || defined(_GAMING_XBOX) || defined(_GAMING_XBOX_SCARLETT)
#define PIX_XBOX
#endif
#include <cstdint>
#if defined(_M_X64) || defined(_M_IX86)
#include <emmintrin.h>
#endif
//
// The PIXBeginEvent and PIXSetMarker functions have an optimized path for
// copying strings that work by copying 128-bit or 64-bits at a time. In some
// circumstances this may result in PIX logging the remaining memory after the
// null terminator.
//
// By default this optimization is enabled unless Address Sanitizer is enabled,
// since this optimization can trigger a global-buffer-overflow when copying
// string literals.
//
// The PIX_ENABLE_BLOCK_ARGUMENT_COPY controls whether or not this optimization
// is enabled. Applications may also explicitly set this macro to 0 to disable
// the optimization if necessary.
//
// Check for Address Sanitizer on either Clang or MSVC
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#define PIX_ASAN_ENABLED
#endif
#elif defined(__SANITIZE_ADDRESS__)
#define PIX_ASAN_ENABLED
#endif
#if defined(PIX_ENABLE_BLOCK_ARGUMENT_COPY)
// Previously set values override everything
# define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 0
#elif defined(PIX_ASAN_ENABLED)
// Disable block argument copy when address sanitizer is enabled
#define PIX_ENABLE_BLOCK_ARGUMENT_COPY 0
#define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 1
#endif
#if !defined(PIX_ENABLE_BLOCK_ARGUMENT_COPY)
// Default to enabled.
#define PIX_ENABLE_BLOCK_ARGUMENT_COPY 1
#define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 1
#endif
struct PIXEventsBlockInfo;
struct PIXEventsThreadInfo
{
PIXEventsBlockInfo* block;
UINT64* biasedLimit;
UINT64* destination;
};
#ifdef PIX_XBOX
extern "C" UINT64 WINAPI PIXEventsReplaceBlock(bool getEarliestTime) noexcept;
#else
extern "C" UINT64 WINAPI PIXEventsReplaceBlock(PIXEventsThreadInfo * threadInfo, bool getEarliestTime) noexcept;
#endif
enum PIXEventType
{
PIXEvent_EndEvent = 0x000,
PIXEvent_BeginEvent_VarArgs = 0x001,
PIXEvent_BeginEvent_NoArgs = 0x002,
PIXEvent_SetMarker_VarArgs = 0x007,
PIXEvent_SetMarker_NoArgs = 0x008,
PIXEvent_EndEvent_OnContext = 0x010,
PIXEvent_BeginEvent_OnContext_VarArgs = 0x011,
PIXEvent_BeginEvent_OnContext_NoArgs = 0x012,
PIXEvent_SetMarker_OnContext_VarArgs = 0x017,
PIXEvent_SetMarker_OnContext_NoArgs = 0x018,
};
static const UINT64 PIXEventsReservedRecordSpaceQwords = 64;
//this is used to make sure SSE string copy always will end 16-byte write in the current block
//this way only a check if destination < limit can be performed, instead of destination < limit - 1
//since both these are UINT64* and SSE writes in 16 byte chunks, 8 bytes are kept in reserve
//so even if SSE overwrites 8 extra bytes, those will still belong to the correct block
//on next iteration check destination will be greater than limit
//this is used as well for fixed size UMD events and PIXEndEvent since these require less space
//than other variable length user events and do not need big reserved space
static const UINT64 PIXEventsReservedTailSpaceQwords = 2;
static const UINT64 PIXEventsSafeFastCopySpaceQwords = PIXEventsReservedRecordSpaceQwords - PIXEventsReservedTailSpaceQwords;
static const UINT64 PIXEventsGraphicsRecordSpaceQwords = 64;
//Bits 7-19 (13 bits)
static const UINT64 PIXEventsBlockEndMarker = 0x00000000000FFF80;
//Bits 10-19 (10 bits)
static const UINT64 PIXEventsTypeReadMask = 0x00000000000FFC00;
static const UINT64 PIXEventsTypeWriteMask = 0x00000000000003FF;
static const UINT64 PIXEventsTypeBitShift = 10;
//Bits 20-63 (44 bits)
static const UINT64 PIXEventsTimestampReadMask = 0xFFFFFFFFFFF00000;
static const UINT64 PIXEventsTimestampWriteMask = 0x00000FFFFFFFFFFF;
static const UINT64 PIXEventsTimestampBitShift = 20;
inline UINT64 PIXEncodeEventInfo(UINT64 timestamp, PIXEventType eventType)
{
return ((timestamp & PIXEventsTimestampWriteMask) << PIXEventsTimestampBitShift) |
(((UINT64)eventType & PIXEventsTypeWriteMask) << PIXEventsTypeBitShift);
}
//Bits 60-63 (4)
static const UINT64 PIXEventsStringAlignmentWriteMask = 0x000000000000000F;
static const UINT64 PIXEventsStringAlignmentReadMask = 0xF000000000000000;
static const UINT64 PIXEventsStringAlignmentBitShift = 60;
//Bits 55-59 (5)
static const UINT64 PIXEventsStringCopyChunkSizeWriteMask = 0x000000000000001F;
static const UINT64 PIXEventsStringCopyChunkSizeReadMask = 0x0F80000000000000;
static const UINT64 PIXEventsStringCopyChunkSizeBitShift = 55;
//Bit 54
static const UINT64 PIXEventsStringIsANSIWriteMask = 0x0000000000000001;
static const UINT64 PIXEventsStringIsANSIReadMask = 0x0040000000000000;
static const UINT64 PIXEventsStringIsANSIBitShift = 54;
//Bit 53
static const UINT64 PIXEventsStringIsShortcutWriteMask = 0x0000000000000001;
static const UINT64 PIXEventsStringIsShortcutReadMask = 0x0020000000000000;
static const UINT64 PIXEventsStringIsShortcutBitShift = 53;
inline UINT64 PIXEncodeStringInfo(UINT64 alignment, UINT64 copyChunkSize, BOOL isANSI, BOOL isShortcut)
{
return ((alignment & PIXEventsStringAlignmentWriteMask) << PIXEventsStringAlignmentBitShift) |
((copyChunkSize & PIXEventsStringCopyChunkSizeWriteMask) << PIXEventsStringCopyChunkSizeBitShift) |
(((UINT64)isANSI & PIXEventsStringIsANSIWriteMask) << PIXEventsStringIsANSIBitShift) |
(((UINT64)isShortcut & PIXEventsStringIsShortcutWriteMask) << PIXEventsStringIsShortcutBitShift);
}
template<UINT alignment, class T>
inline bool PIXIsPointerAligned(T* pointer)
{
return !(((UINT64)pointer) & (alignment - 1));
}
// Generic template version slower because of the additional clear write
template<class T>
inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, T argument)
{
if (destination < limit)
{
*destination = 0ull;
*((T*)destination) = argument;
++destination;
}
}
// int32 specialization to avoid slower double memory writes
template<>
inline void PIXCopyEventArgument<INT32>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, INT32 argument)
{
if (destination < limit)
{
*reinterpret_cast<INT64*>(destination) = static_cast<INT64>(argument);
++destination;
}
}
// unsigned int32 specialization to avoid slower double memory writes
template<>
inline void PIXCopyEventArgument<UINT32>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, UINT32 argument)
{
if (destination < limit)
{
*destination = static_cast<UINT64>(argument);
++destination;
}
}
// int64 specialization to avoid slower double memory writes
template<>
inline void PIXCopyEventArgument<INT64>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, INT64 argument)
{
if (destination < limit)
{
*reinterpret_cast<INT64*>(destination) = argument;
++destination;
}
}
// unsigned int64 specialization to avoid slower double memory writes
template<>
inline void PIXCopyEventArgument<UINT64>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, UINT64 argument)
{
if (destination < limit)
{
*destination = argument;
++destination;
}
}
//floats must be cast to double during writing the data to be properly printed later when reading the data
//this is needed because when float is passed to varargs function it's cast to double
template<>
inline void PIXCopyEventArgument<float>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, float argument)
{
if (destination < limit)
{
*reinterpret_cast<double*>(destination) = static_cast<double>(argument);
++destination;
}
}
//char has to be cast to a longer signed integer type
//this is due to printf not ignoring correctly the upper bits of unsigned long long for a char format specifier
template<>
inline void PIXCopyEventArgument<char>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, char argument)
{
if (destination < limit)
{
*reinterpret_cast<INT64*>(destination) = static_cast<INT64>(argument);
++destination;
}
}
//unsigned char has to be cast to a longer unsigned integer type
//this is due to printf not ignoring correctly the upper bits of unsigned long long for a char format specifier
template<>
inline void PIXCopyEventArgument<unsigned char>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, unsigned char argument)
{
if (destination < limit)
{
*destination = static_cast<UINT64>(argument);
++destination;
}
}
//bool has to be cast to an integer since it's not explicitly supported by string format routines
//there's no format specifier for bool type, but it should work with integer format specifiers
template<>
inline void PIXCopyEventArgument<bool>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, bool argument)
{
if (destination < limit)
{
*destination = static_cast<UINT64>(argument);
++destination;
}
}
inline void PIXCopyEventArgumentSlowest(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument)
{
*destination++ = PIXEncodeStringInfo(0, 8, TRUE, FALSE);
while (destination < limit)
{
UINT64 c = static_cast<uint8_t>(argument[0]);
if (!c)
{
*destination++ = 0;
return;
}
UINT64 x = c;
c = static_cast<uint8_t>(argument[1]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 8;
c = static_cast<uint8_t>(argument[2]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 16;
c = static_cast<uint8_t>(argument[3]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 24;
c = static_cast<uint8_t>(argument[4]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 32;
c = static_cast<uint8_t>(argument[5]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 40;
c = static_cast<uint8_t>(argument[6]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 48;
c = static_cast<uint8_t>(argument[7]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 56;
*destination++ = x;
argument += 8;
}
}
inline void PIXCopyEventArgumentSlow(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument)
{
#if PIX_ENABLE_BLOCK_ARGUMENT_COPY
if (PIXIsPointerAligned<8>(argument))
{
*destination++ = PIXEncodeStringInfo(0, 8, TRUE, FALSE);
UINT64* source = (UINT64*)argument;
while (destination < limit)
{
UINT64 qword = *source++;
*destination++ = qword;
//check if any of the characters is a terminating zero
if (!((qword & 0xFF00000000000000) &&
(qword & 0xFF000000000000) &&
(qword & 0xFF0000000000) &&
(qword & 0xFF00000000) &&
(qword & 0xFF000000) &&
(qword & 0xFF0000) &&
(qword & 0xFF00) &&
(qword & 0xFF)))
{
break;
}
}
}
else
#endif // PIX_ENABLE_BLOCK_ARGUMENT_COPY
{
PIXCopyEventArgumentSlowest(destination, limit, argument);
}
}
template<>
inline void PIXCopyEventArgument<PCSTR>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument)
{
if (destination < limit)
{
if (argument != nullptr)
{
#if (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY
if (PIXIsPointerAligned<16>(argument))
{
*destination++ = PIXEncodeStringInfo(0, 16, TRUE, FALSE);
__m128i zero = _mm_setzero_si128();
if (PIXIsPointerAligned<16>(destination))
{
while (destination < limit)
{
__m128i mem = _mm_load_si128((__m128i*)argument);
_mm_store_si128((__m128i*)destination, mem);
//check if any of the characters is a terminating zero
__m128i res = _mm_cmpeq_epi8(mem, zero);
destination += 2;
if (_mm_movemask_epi8(res))
break;
argument += 16;
}
}
else
{
while (destination < limit)
{
__m128i mem = _mm_load_si128((__m128i*)argument);
_mm_storeu_si128((__m128i*)destination, mem);
//check if any of the characters is a terminating zero
__m128i res = _mm_cmpeq_epi8(mem, zero);
destination += 2;
if (_mm_movemask_epi8(res))
break;
argument += 16;
}
}
}
else
#endif // (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY
{
PIXCopyEventArgumentSlow(destination, limit, argument);
}
}
else
{
*destination++ = 0ull;
}
}
}
template<>
inline void PIXCopyEventArgument<PSTR>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PSTR argument)
{
PIXCopyEventArgument(destination, limit, (PCSTR)argument);
}
inline void PIXCopyEventArgumentSlowest(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument)
{
*destination++ = PIXEncodeStringInfo(0, 8, FALSE, FALSE);
while (destination < limit)
{
UINT64 c = static_cast<uint16_t>(argument[0]);
if (!c)
{
*destination++ = 0;
return;
}
UINT64 x = c;
c = static_cast<uint16_t>(argument[1]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 16;
c = static_cast<uint16_t>(argument[2]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 32;
c = static_cast<uint16_t>(argument[3]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 48;
*destination++ = x;
argument += 4;
}
}
inline void PIXCopyEventArgumentSlow(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument)
{
#if PIX_ENABLE_BLOCK_ARGUMENT_COPY
if (PIXIsPointerAligned<8>(argument))
{
*destination++ = PIXEncodeStringInfo(0, 8, FALSE, FALSE);
UINT64* source = (UINT64*)argument;
while (destination < limit)
{
UINT64 qword = *source++;
*destination++ = qword;
//check if any of the characters is a terminating zero
//TODO: check if reversed condition is faster
if (!((qword & 0xFFFF000000000000) &&
(qword & 0xFFFF00000000) &&
(qword & 0xFFFF0000) &&
(qword & 0xFFFF)))
{
break;
}
}
}
else
#endif // PIX_ENABLE_BLOCK_ARGUMENT_COPY
{
PIXCopyEventArgumentSlowest(destination, limit, argument);
}
}
template<>
inline void PIXCopyEventArgument<PCWSTR>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument)
{
if (destination < limit)
{
if (argument != nullptr)
{
#if (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY
if (PIXIsPointerAligned<16>(argument))
{
*destination++ = PIXEncodeStringInfo(0, 16, FALSE, FALSE);
__m128i zero = _mm_setzero_si128();
if (PIXIsPointerAligned<16>(destination))
{
while (destination < limit)
{
__m128i mem = _mm_load_si128((__m128i*)argument);
_mm_store_si128((__m128i*)destination, mem);
//check if any of the characters is a terminating zero
__m128i res = _mm_cmpeq_epi16(mem, zero);
destination += 2;
if (_mm_movemask_epi8(res))
break;
argument += 8;
}
}
else
{
while (destination < limit)
{
__m128i mem = _mm_load_si128((__m128i*)argument);
_mm_storeu_si128((__m128i*)destination, mem);
//check if any of the characters is a terminating zero
__m128i res = _mm_cmpeq_epi16(mem, zero);
destination += 2;
if (_mm_movemask_epi8(res))
break;
argument += 8;
}
}
}
else
#endif // (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY
{
PIXCopyEventArgumentSlow(destination, limit, argument);
}
}
else
{
*destination++ = 0ull;
}
}
}
template<>
inline void PIXCopyEventArgument<PWSTR>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PWSTR argument)
{
PIXCopyEventArgument(destination, limit, (PCWSTR)argument);
};
#if defined(__d3d12_x_h__) || defined(__d3d12_xs_h__) || defined(__d3d12_h__)
inline void PIXSetGPUMarkerOnContext(_In_ ID3D12GraphicsCommandList* commandList, _In_reads_bytes_(size) void* data, UINT size)
{
commandList->SetMarker(D3D12_EVENT_METADATA, data, size);
}
inline void PIXSetGPUMarkerOnContext(_In_ ID3D12CommandQueue* commandQueue, _In_reads_bytes_(size) void* data, UINT size)
{
commandQueue->SetMarker(D3D12_EVENT_METADATA, data, size);
}
inline void PIXBeginGPUEventOnContext(_In_ ID3D12GraphicsCommandList* commandList, _In_reads_bytes_(size) void* data, UINT size)
{
commandList->BeginEvent(D3D12_EVENT_METADATA, data, size);
}
inline void PIXBeginGPUEventOnContext(_In_ ID3D12CommandQueue* commandQueue, _In_reads_bytes_(size) void* data, UINT size)
{
commandQueue->BeginEvent(D3D12_EVENT_METADATA, data, size);
}
inline void PIXEndGPUEventOnContext(_In_ ID3D12GraphicsCommandList* commandList)
{
commandList->EndEvent();
}
inline void PIXEndGPUEventOnContext(_In_ ID3D12CommandQueue* commandQueue)
{
commandQueue->EndEvent();
}
#endif //__d3d12_h__
template<class T> struct PIXInferScopedEventType { typedef T Type; };
template<class T> struct PIXInferScopedEventType<const T> { typedef T Type; };
template<class T> struct PIXInferScopedEventType<T*> { typedef T Type; };
template<class T> struct PIXInferScopedEventType<T* const> { typedef T Type; };
template<> struct PIXInferScopedEventType<UINT64> { typedef void Type; };
template<> struct PIXInferScopedEventType<const UINT64> { typedef void Type; };
template<> struct PIXInferScopedEventType<INT64> { typedef void Type; };
template<> struct PIXInferScopedEventType<const INT64> { typedef void Type; };
template<> struct PIXInferScopedEventType<UINT> { typedef void Type; };
template<> struct PIXInferScopedEventType<const UINT> { typedef void Type; };
template<> struct PIXInferScopedEventType<INT> { typedef void Type; };
template<> struct PIXInferScopedEventType<const INT> { typedef void Type; };
#if PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET
#undef PIX_ENABLE_BLOCK_ARGUMENT_COPY
#endif
#undef PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET
#endif //_PIXEventsCommon_H_
@@ -0,0 +1,175 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
/*==========================================================================;
*
* Copyright (C) Microsoft Corporation. All Rights Reserved.
*
* File: pix3.h
* Content: PIX include file
*
****************************************************************************/
#pragma once
#ifndef _PIX3_H_
#define _PIX3_H_
#include <sal.h>
#ifndef __cplusplus
#error "Only C++ files can include pix3.h. C is not supported."
#endif
#if !defined(USE_PIX_SUPPORTED_ARCHITECTURE)
#if defined(_M_X64) || defined(USE_PIX_ON_ALL_ARCHITECTURES) || defined(_M_ARM64)
#define USE_PIX_SUPPORTED_ARCHITECTURE
#endif
#endif
#if !defined(USE_PIX)
#if defined(USE_PIX_SUPPORTED_ARCHITECTURE) && (defined(_DEBUG) || DBG || defined(PROFILE) || defined(PROFILE_BUILD)) && !defined(_PREFAST_)
#define USE_PIX
#endif
#endif
#if defined(USE_PIX) && !defined(USE_PIX_SUPPORTED_ARCHITECTURE)
#pragma message("Warning: Pix markers are only supported on AMD64 and ARM64")
#endif
// These flags are used by both PIXBeginCapture and PIXGetCaptureState
#define PIX_CAPTURE_TIMING (1 << 0)
#define PIX_CAPTURE_GPU (1 << 1)
#define PIX_CAPTURE_FUNCTION_SUMMARY (1 << 2)
#define PIX_CAPTURE_FUNCTION_DETAILS (1 << 3)
#define PIX_CAPTURE_CALLGRAPH (1 << 4)
#define PIX_CAPTURE_INSTRUCTION_TRACE (1 << 5)
#define PIX_CAPTURE_SYSTEM_MONITOR_COUNTERS (1 << 6)
#define PIX_CAPTURE_VIDEO (1 << 7)
#define PIX_CAPTURE_AUDIO (1 << 8)
#define PIX_CAPTURE_RESERVED (1 << 15)
union PIXCaptureParameters
{
enum PIXCaptureStorage
{
Memory = 0,
};
struct GpuCaptureParameters
{
PCWSTR FileName;
} GpuCaptureParameters;
struct TimingCaptureParameters
{
PCWSTR FileName;
UINT32 MaximumToolingMemorySizeMb;
PIXCaptureStorage CaptureStorage;
BOOL CaptureGpuTiming;
BOOL CaptureCallstacks;
BOOL CaptureCpuSamples;
UINT32 CpuSamplesPerSecond;
BOOL CaptureFileIO;
BOOL CaptureVirtualAllocEvents;
BOOL CaptureHeapAllocEvents;
BOOL CaptureXMemEvents; // Xbox only
BOOL CapturePixMemEvents; // Xbox only
} TimingCaptureParameters;
};
typedef PIXCaptureParameters* PPIXCaptureParameters;
#if defined(XBOX) || defined(_XBOX_ONE) || defined(_DURANGO) || defined(_GAMING_XBOX) || defined(_GAMING_XBOX_SCARLETT)
#include "pix3_xbox.h"
#else
#include "pix3_win.h"
#endif
#if defined(USE_PIX_SUPPORTED_ARCHITECTURE) && (defined(USE_PIX) || defined(USE_PIX_RETAIL))
#define PIX_EVENTS_ARE_TURNED_ON
#include "PIXEventsCommon.h"
#include "PIXEvents.h"
#ifdef USE_PIX
// Starts a programmatically controlled capture.
// captureFlags uses the PIX_CAPTURE_* family of flags to specify the type of capture to take
extern "C" HRESULT WINAPI PIXBeginCapture2(DWORD captureFlags, _In_opt_ const PPIXCaptureParameters captureParameters);
inline HRESULT PIXBeginCapture(DWORD captureFlags, _In_opt_ const PPIXCaptureParameters captureParameters) { return PIXBeginCapture2(captureFlags, captureParameters); }
// Stops a programmatically controlled capture
// If discard == TRUE, the captured data is discarded
// If discard == FALSE, the captured data is saved
// discard parameter is not supported on Windows
extern "C" HRESULT WINAPI PIXEndCapture(BOOL discard);
extern "C" DWORD WINAPI PIXGetCaptureState();
extern "C" void WINAPI PIXReportCounter(_In_ PCWSTR name, float value);
#endif // USE_PIX
#endif // (USE_PIX_SUPPORTED_ARCHITECTURE) && (USE_PIX || USE_PIX_RETAIL)
#if !defined(USE_PIX_SUPPORTED_ARCHITECTURE) || !defined(USE_PIX)
// Eliminate these APIs when not using PIX
inline HRESULT PIXBeginCapture2(DWORD, _In_opt_ const PIXCaptureParameters*) { return S_OK; }
inline HRESULT PIXBeginCapture(DWORD, _In_opt_ const PIXCaptureParameters*) { return S_OK; }
inline HRESULT PIXEndCapture(BOOL) { return S_OK; }
inline HRESULT PIXGpuCaptureNextFrames(PCWSTR, UINT32) { return S_OK; }
inline HRESULT PIXSetTargetWindow(HWND) { return S_OK; }
inline HRESULT PIXForceD3D11On12() { return S_OK; }
inline HRESULT WINAPI PIXSetHUDOptions(PIXHUDOptions) { return S_OK; }
inline bool WINAPI PIXIsAttachedForGpuCapture() { return false; }
inline HINSTANCE WINAPI PIXOpenCaptureInUI(PCWSTR) { return 0; }
inline HMODULE PIXLoadLatestWinPixGpuCapturerLibrary() { return nullptr; }
inline HMODULE PIXLoadLatestWinPixTimingCapturerLibrary() { return nullptr; }
inline DWORD PIXGetCaptureState() { return 0; }
inline void PIXReportCounter(_In_ PCWSTR, float) {}
inline void PIXNotifyWakeFromFenceSignal(_In_ HANDLE) {}
#if !defined(USE_PIX_RETAIL)
inline void PIXBeginEvent(UINT64, _In_ PCSTR, ...) {}
inline void PIXBeginEvent(UINT64, _In_ PCWSTR, ...) {}
inline void PIXBeginEvent(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXBeginEvent(void*, UINT64, _In_ PCWSTR, ...) {}
inline void PIXEndEvent() {}
inline void PIXEndEvent(void*) {}
inline void PIXSetMarker(UINT64, _In_ PCSTR, ...) {}
inline void PIXSetMarker(UINT64, _In_ PCWSTR, ...) {}
inline void PIXSetMarker(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXSetMarker(void*, UINT64, _In_ PCWSTR, ...) {}
inline void PIXBeginRetailEvent(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXBeginRetailEvent(void*, UINT64, _In_ PCWSTR, ...) {}
inline void PIXEndRetailEvent(void*) {}
inline void PIXSetRetailMarker(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXSetRetailMarker(void*, UINT64, _In_ PCWSTR, ...) {}
inline void PIXScopedEvent(UINT64, _In_ PCSTR, ...) {}
inline void PIXScopedEvent(UINT64, _In_ PCWSTR, ...) {}
inline void PIXScopedEvent(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXScopedEvent(void*, UINT64, _In_ PCWSTR, ...) {}
#endif // !USE_PIX_RETAIL
// don't show warnings about expressions with no effect
#pragma warning(disable:4548)
#pragma warning(disable:4555)
#endif // !USE_PIX_SUPPORTED_ARCHITECTURE || !USE_PIX
// Use these functions to specify colors to pass as metadata to a PIX event/marker API.
// Use PIX_COLOR() to specify a particular color for an event.
// Or, use PIX_COLOR_INDEX() to specify a set of unique event categories, and let PIX choose
// the colors to represent each category.
inline UINT PIX_COLOR(BYTE r, BYTE g, BYTE b) { return 0xff000000 | (r << 16) | (g << 8) | b; }
inline UINT PIX_COLOR_INDEX(BYTE i) { return i; }
const UINT PIX_COLOR_DEFAULT = PIX_COLOR_INDEX(0);
#endif // _PIX3_H_
@@ -0,0 +1,439 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
/*==========================================================================;
*
* Copyright (C) Microsoft Corporation. All Rights Reserved.
*
* File: PIX3_win.h
* Content: PIX include file
* Don't include this file directly - use pix3.h
*
****************************************************************************/
#pragma once
#ifndef _PIX3_H_
#error Don't include this file directly - use pix3.h
#endif
#ifndef _PIX3_WIN_H_
#define _PIX3_WIN_H_
// PIXEventsThreadInfo is defined in PIXEventsCommon.h
struct PIXEventsThreadInfo;
extern "C" PIXEventsThreadInfo* WINAPI PIXGetThreadInfo() noexcept;
#if defined(USE_PIX) && defined(USE_PIX_SUPPORTED_ARCHITECTURE)
// Notifies PIX that an event handle was set as a result of a D3D12 fence being signaled.
// The event specified must have the same handle value as the handle
// used in ID3D12Fence::SetEventOnCompletion.
extern "C" void WINAPI PIXNotifyWakeFromFenceSignal(_In_ HANDLE event);
// Notifies PIX that a block of memory was allocated
extern "C" void WINAPI PIXRecordMemoryAllocationEvent(USHORT allocatorId, void* baseAddress, size_t size, UINT64 metadata);
// Notifies PIX that a block of memory was freed
extern "C" void WINAPI PIXRecordMemoryFreeEvent(USHORT allocatorId, void* baseAddress, size_t size, UINT64 metadata);
#else
// Eliminate these APIs when not using PIX
inline void PIXRecordMemoryAllocationEvent(USHORT, void*, size_t, UINT64) {}
inline void PIXRecordMemoryFreeEvent(USHORT, void*, size_t, UINT64) {}
#endif
// The following defines denote the different metadata values that have been used
// by tools to denote how to parse pix marker event data. The first two values
// are legacy values.
#define WINPIX_EVENT_UNICODE_VERSION 0
#define WINPIX_EVENT_ANSI_VERSION 1
#define WINPIX_EVENT_PIX3BLOB_VERSION 2
#define D3D12_EVENT_METADATA WINPIX_EVENT_PIX3BLOB_VERSION
__forceinline UINT64 PIXGetTimestampCounter()
{
LARGE_INTEGER time = {};
QueryPerformanceCounter(&time);
return static_cast<UINT64>(time.QuadPart);
}
enum PIXHUDOptions
{
PIX_HUD_SHOW_ON_ALL_WINDOWS = 0x1,
PIX_HUD_SHOW_ON_TARGET_WINDOW_ONLY = 0x2,
PIX_HUD_SHOW_ON_NO_WINDOWS = 0x4
};
DEFINE_ENUM_FLAG_OPERATORS(PIXHUDOptions);
#if defined(USE_PIX_SUPPORTED_ARCHITECTURE) && defined(USE_PIX)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
#include <shlobj.h>
#include <strsafe.h>
#include <knownfolders.h>
#include <shellapi.h>
#define PIXERRORCHECK(value) do { \
if (FAILED(value)) \
return nullptr; \
} while(0)
namespace PixImpl
{
#ifndef PIX3_WIN_UNIT_TEST
__forceinline BOOL GetModuleHandleExW(
DWORD dwFlags,
LPCWSTR lpModuleName,
HMODULE* phModule)
{
return ::GetModuleHandleExW(dwFlags, lpModuleName, phModule);
}
__forceinline HRESULT SHGetKnownFolderPath(
REFKNOWNFOLDERID rfid,
DWORD dwFlags,
HANDLE hToken,
PWSTR* ppszPath)
{
return ::SHGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath);
}
__forceinline void CoTaskMemFree(LPVOID pv)
{
return ::CoTaskMemFree(pv);
}
__forceinline HANDLE FindFirstFileW(
LPCWSTR lpFileName,
LPWIN32_FIND_DATAW lpFindFileData)
{
return ::FindFirstFileW(lpFileName, lpFindFileData);
}
__forceinline DWORD GetFileAttributesW(LPCWSTR lpFileName)
{
return ::GetFileAttributesW(lpFileName);
}
__forceinline BOOL FindNextFileW(
HANDLE hFindFile,
LPWIN32_FIND_DATAW lpFindFileData)
{
return ::FindNextFileW(hFindFile, lpFindFileData);
}
__forceinline BOOL FindClose(HANDLE hFindFile)
{
return ::FindClose(hFindFile);
}
__forceinline HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, DWORD flags)
{
return ::LoadLibraryExW(lpLibFileName, NULL, flags);
}
#endif // !PIX3_WIN_UNIT_TESTS
__forceinline void * GetGpuCaptureFunctionPtr(LPCSTR fnName) noexcept
{
HMODULE module = GetModuleHandleW(L"WinPixGpuCapturer.dll");
if (module == NULL)
{
return nullptr;
}
auto fn = (void*)GetProcAddress(module, fnName);
if (fn == nullptr)
{
return nullptr;
}
return fn;
}
__forceinline void* GetTimingCaptureFunctionPtr(LPCSTR fnName) noexcept
{
HMODULE module = GetModuleHandleW(L"WinPixTimingCapturer.dll");
if (module == NULL)
{
return nullptr;
}
auto fn = (void*)GetProcAddress(module, fnName);
if (fn == nullptr)
{
return nullptr;
}
return fn;
}
__forceinline HMODULE PIXLoadLatestCapturerLibrary(wchar_t const* capturerDllName, DWORD flags)
{
HMODULE libHandle{};
if (PixImpl::GetModuleHandleExW(0, capturerDllName, &libHandle))
{
return libHandle;
}
LPWSTR programFilesPath = nullptr;
if (FAILED(PixImpl::SHGetKnownFolderPath(FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &programFilesPath)))
{
PixImpl::CoTaskMemFree(programFilesPath);
return nullptr;
}
wchar_t pixSearchPath[MAX_PATH];
if (FAILED(StringCchCopyW(pixSearchPath, MAX_PATH, programFilesPath)))
{
PixImpl::CoTaskMemFree(programFilesPath);
return nullptr;
}
PixImpl::CoTaskMemFree(programFilesPath);
PIXERRORCHECK(StringCchCatW(pixSearchPath, MAX_PATH, L"\\Microsoft PIX\\*"));
WIN32_FIND_DATAW findData;
bool foundPixInstallation = false;
wchar_t newestVersionFound[MAX_PATH];
wchar_t output[MAX_PATH];
wchar_t possibleOutput[MAX_PATH];
HANDLE hFind = PixImpl::FindFirstFileW(pixSearchPath, &findData);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) &&
(findData.cFileName[0] != '.'))
{
if (!foundPixInstallation || wcscmp(newestVersionFound, findData.cFileName) <= 0)
{
// length - 1 to get rid of the wildcard character in the search path
PIXERRORCHECK(StringCchCopyNW(possibleOutput, MAX_PATH, pixSearchPath, wcslen(pixSearchPath) - 1));
PIXERRORCHECK(StringCchCatW(possibleOutput, MAX_PATH, findData.cFileName));
PIXERRORCHECK(StringCchCatW(possibleOutput, MAX_PATH, L"\\"));
PIXERRORCHECK(StringCchCatW(possibleOutput, MAX_PATH, capturerDllName));
DWORD result = PixImpl::GetFileAttributesW(possibleOutput);
if (result != INVALID_FILE_ATTRIBUTES && !(result & FILE_ATTRIBUTE_DIRECTORY))
{
foundPixInstallation = true;
PIXERRORCHECK(StringCchCopyW(newestVersionFound, _countof(newestVersionFound), findData.cFileName));
PIXERRORCHECK(StringCchCopyW(output, _countof(possibleOutput), possibleOutput));
}
}
}
} while (PixImpl::FindNextFileW(hFind, &findData) != 0);
}
PixImpl::FindClose(hFind);
if (!foundPixInstallation)
{
SetLastError(ERROR_FILE_NOT_FOUND);
return nullptr;
}
return PixImpl::LoadLibraryExW(output, flags);
}
}
#undef PIXERRORCHECK
__forceinline HMODULE PIXLoadLatestWinPixGpuCapturerLibrary()
{
return PixImpl::PIXLoadLatestCapturerLibrary(
L"WinPixGpuCapturer.dll",
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
}
__forceinline HMODULE PIXLoadLatestWinPixTimingCapturerLibrary()
{
return PixImpl::PIXLoadLatestCapturerLibrary(
L"WinPixTimingCapturer.dll",
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
}
__forceinline HRESULT WINAPI PIXSetTargetWindow(HWND hwnd)
{
typedef void(WINAPI* SetGlobalTargetWindowFn)(HWND);
auto fn = (SetGlobalTargetWindowFn)PixImpl::GetGpuCaptureFunctionPtr("SetGlobalTargetWindow");
if (fn == nullptr)
{
return HRESULT_FROM_WIN32(GetLastError());
}
fn(hwnd);
return S_OK;
}
__forceinline HRESULT WINAPI PIXGpuCaptureNextFrames(PCWSTR fileName, UINT32 numFrames)
{
typedef HRESULT(WINAPI* CaptureNextFrameFn)(PCWSTR, UINT32);
auto fn = (CaptureNextFrameFn)PixImpl::GetGpuCaptureFunctionPtr("CaptureNextFrame");
if (fn == nullptr)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return fn(fileName, numFrames);
}
extern "C" __forceinline HRESULT WINAPI PIXBeginCapture2(DWORD captureFlags, _In_opt_ const PPIXCaptureParameters captureParameters)
{
if (captureFlags == PIX_CAPTURE_GPU)
{
typedef HRESULT(WINAPI* BeginProgrammaticGpuCaptureFn)(const PPIXCaptureParameters);
auto fn = (BeginProgrammaticGpuCaptureFn)PixImpl::GetGpuCaptureFunctionPtr("BeginProgrammaticGpuCapture");
if (fn == nullptr)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return fn(captureParameters);
}
else if (captureFlags == PIX_CAPTURE_TIMING)
{
typedef HRESULT(WINAPI* BeginProgrammaticTimingCaptureFn)(void const*, UINT64);
auto fn = (BeginProgrammaticTimingCaptureFn)PixImpl::GetTimingCaptureFunctionPtr("BeginProgrammaticTimingCapture");
if (fn == nullptr)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return fn(&captureParameters->TimingCaptureParameters, sizeof(captureParameters->TimingCaptureParameters));
}
else
{
return E_NOTIMPL;
}
}
extern "C" __forceinline HRESULT WINAPI PIXEndCapture(BOOL discard)
{
UNREFERENCED_PARAMETER(discard);
// We can't tell if the user wants to end a GPU Capture or a Timing Capture.
// The user shouldn't have both WinPixGpuCapturer and WinPixTimingCapturer loaded in the process though,
// so we can just look for one of them and call it.
typedef HRESULT(WINAPI* EndProgrammaticGpuCaptureFn)(void);
auto gpuFn = (EndProgrammaticGpuCaptureFn)PixImpl::GetGpuCaptureFunctionPtr("EndProgrammaticGpuCapture");
if (gpuFn != NULL)
{
return gpuFn();
}
typedef HRESULT(WINAPI* EndProgrammaticTimingCaptureFn)(BOOL);
auto timingFn = (EndProgrammaticTimingCaptureFn)PixImpl::GetTimingCaptureFunctionPtr("EndProgrammaticTimingCapture");
if (timingFn != NULL)
{
return timingFn(discard);
}
return HRESULT_FROM_WIN32(GetLastError());
}
__forceinline HRESULT WINAPI PIXForceD3D11On12()
{
typedef HRESULT (WINAPI* ForceD3D11On12Fn)(void);
auto fn = (ForceD3D11On12Fn)PixImpl::GetGpuCaptureFunctionPtr("ForceD3D11On12");
if (fn == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return fn();
}
__forceinline HRESULT WINAPI PIXSetHUDOptions(PIXHUDOptions hudOptions)
{
typedef HRESULT(WINAPI* SetHUDOptionsFn)(PIXHUDOptions);
auto fn = (SetHUDOptionsFn)PixImpl::GetGpuCaptureFunctionPtr("SetHUDOptions");
if (fn == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return fn(hudOptions);
}
__forceinline bool WINAPI PIXIsAttachedForGpuCapture()
{
typedef bool(WINAPI* GetIsAttachedToPixFn)(void);
auto fn = (GetIsAttachedToPixFn)PixImpl::GetGpuCaptureFunctionPtr("GetIsAttachedToPix");
if (fn == NULL)
{
OutputDebugStringW(L"WinPixEventRuntime error: Mismatched header/dll. Please ensure that pix3.h and WinPixGpuCapturer.dll match");
return false;
}
return fn();
}
__forceinline HINSTANCE WINAPI PIXOpenCaptureInUI(PCWSTR fileName)
{
return ShellExecuteW(0, 0, fileName, 0, 0, SW_SHOW);
}
#else
__forceinline HMODULE PIXLoadLatestWinPixGpuCapturerLibrary()
{
return nullptr;
}
__forceinline HMODULE PIXLoadLatestWinPixTimingCapturerLibrary()
{
return nullptr;
}
__forceinline HRESULT WINAPI PIXSetTargetWindow(HWND)
{
return E_NOTIMPL;
}
__forceinline HRESULT WINAPI PIXGpuCaptureNextFrames(PCWSTR, UINT32)
{
return E_NOTIMPL;
}
extern "C" __forceinline HRESULT WINAPI PIXBeginCapture2(DWORD, _In_opt_ const PPIXCaptureParameters)
{
return E_NOTIMPL;
}
extern "C" __forceinline HRESULT WINAPI PIXEndCapture(BOOL)
{
return E_NOTIMPL;
}
__forceinline HRESULT WINAPI PIXForceD3D11On12()
{
return E_NOTIMPL;
}
__forceinline HRESULT WINAPI PIXSetHUDOptions(PIXHUDOptions)
{
return E_NOTIMPL;
}
__forceinline bool WINAPI PIXIsAttachedForGpuCapture()
{
return false;
}
__forceinline HINSTANCE WINAPI PIXOpenCaptureInUI(PCWSTR)
{
return 0;
}
#endif // WINAPI_PARTITION
#endif // USE_PIX_SUPPORTED_ARCHITECTURE || USE_PIX
#endif //_PIX3_WIN_H_
Binary file not shown.
File diff suppressed because it is too large Load Diff
+25 -20
View File
@@ -52,6 +52,7 @@
#define PS_POINT_SAMPLER 0
#define PS_REGION_RECT 0
#define PS_SHUFFLE 0
#define PS_SHUFFLE_SAME 0
#define PS_READ_BA 0
#define PS_READ16_SRC 0
#define PS_DFMT 0
@@ -940,8 +941,17 @@ PS_OUTPUT ps_main(PS_INPUT input)
{
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(float2(TA.xy) * 255.0f + 0.5f);
if (PS_READ16_SRC)
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
if (PS_SHUFFLE_SAME)
{
if (PS_READ_BA)
C = (float4)(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
else
C.ga = C.rg;
}
// Copy of a 16bit source in to this target
else if (PS_READ16_SRC)
{
C.rb = (float2)float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5));
if (denorm_c.a & 0x80u)
@@ -949,28 +959,23 @@ PS_OUTPUT ps_main(PS_INPUT input)
else
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u));
}
// Write RB part. Mask will take care of the correct destination
else if (PS_READ_BA)
{
C.rb = C.bb;
if (denorm_c.a & 0x80u)
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
else
{
// Mask will take care of the correct destination
if (PS_READ_BA)
C.rb = C.bb;
else
C.rb = C.rr;
C.rb = C.rr;
if (denorm_c.g & 0x80u)
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
if (PS_READ_BA)
{
if (denorm_c.a & 0x80u)
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
else
{
if (denorm_c.g & 0x80u)
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
}
+20 -14
View File
@@ -779,17 +779,19 @@ float As = As_rgba.a;
return;
#endif
vec3 Cs = Color.rgb;
#if SW_BLEND_NEEDS_RT
vec4 RT = trunc(fetch_rt() * 255.0f + 0.1f);
#else
// Not used, but we define it to make the selection below simpler.
vec4 RT = vec4(0.0f);
#endif
// FIXME FMT_16 case
// FIXME Ad or Ad * 2?
float Ad = RT.a / 128.0f;
// Let the compiler do its jobs !
vec3 Cd = RT.rgb;
#endif
vec3 Cs = Color.rgb;
#if PS_BLEND_A == 0
vec3 A = Cs;
@@ -950,26 +952,30 @@ void ps_main()
#if PS_SHUFFLE
uvec4 denorm_c = uvec4(C);
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
#if PS_READ16_SRC
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
#if PS_SHUFFLE_SAME
#if (PS_READ_BA)
C = vec4(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
#else
C.ga = C.rg;
#endif
// Copy of a 16bit source in to this target
#elif PS_READ16_SRC
C.rb = vec2(float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5)));
if (bool(denorm_c.a & 0x80u))
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
#else
// Write RB part. Mask will take care of the correct destination
#if PS_READ_BA
// Write RB part. Mask will take care of the correct destination
#elif PS_READ_BA
C.rb = C.bb;
#else
C.rb = C.rr;
#endif
// FIXME precompute my_TA & 0x80
// Write GA part. Mask will take care of the correct destination
// Note: GLSL 4.50/GL_EXT_shader_integer_mix support a mix instruction to select a component\n"
// However Nvidia emulate it with an if (at least on kepler arch) ...\n"
#if PS_READ_BA
// bit field operation requires GL4 HW. Could be nice to merge it with step/mix below
// uint my_ta = (bool(bitfieldExtract(denorm_c.a, 7, 1))) ? denorm_TA.y : denorm_TA.x;
// denorm_c.a = bitfieldInsert(denorm_c.a, bitfieldExtract(my_ta, 7, 1), 7, 1);
@@ -981,6 +987,7 @@ void ps_main()
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
#else
C.rb = C.rr;
if (bool(denorm_c.g & 0x80u))
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
@@ -992,9 +999,8 @@ void ps_main()
// float sel = step(128.0f, c.g);
// vec2 c_shuffle = vec2((denorm_c.gg & 0x7Fu) | (denorm_TA & 0x80u));
// c.ga = mix(c_shuffle.xx, c_shuffle.yy, sel);
#endif // PS_READ_BA
#endif // READ16_SRC
#endif // PS_SHUFFLE_SAME
#endif // PS_SHUFFLE
// Must be done before alpha correction
+30 -25
View File
@@ -277,7 +277,9 @@ void main()
#define PS_TCOFFSETHACK 0
#define PS_POINT_SAMPLER 0
#define PS_SHUFFLE 0
#define PS_SHUFFLE_SAME 0
#define PS_READ_BA 0
#define PS_WRITE_RG 0
#define PS_READ16_SRC 0
#define PS_DFMT 0
#define PS_DEPTH_FMT 0
@@ -300,7 +302,7 @@ void main()
#endif
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
#define SW_BLEND_NEEDS_RT (PS_BLEND_A == 1 || PS_BLEND_B == 1 || PS_BLEND_C == 1 || PS_BLEND_D == 1)
#define SW_BLEND_NEEDS_RT (SW_BLEND && (PS_BLEND_A == 1 || PS_BLEND_B == 1 || PS_BLEND_C == 1 || PS_BLEND_D == 1))
#define SW_AD_TO_HW (PS_BLEND_C == 1 && PS_A_MASKED)
#define PS_FEEDBACK_LOOP_IS_NEEDED (PS_TEX_IS_FB == 1 || PS_FBMASK || SW_BLEND_NEEDS_RT || (PS_DATE >= 5))
@@ -352,16 +354,16 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
#if PS_FEEDBACK_LOOP_IS_NEEDED
#if defined(DISABLE_TEXTURE_BARRIER) || defined(HAS_FEEDBACK_LOOP_LAYOUT)
layout(set = 2, binding = 0) uniform texture2D RtSampler;
layout(set = 1, binding = 2) uniform texture2D RtSampler;
vec4 sample_from_rt() { return texelFetch(RtSampler, ivec2(gl_FragCoord.xy), 0); }
#else
layout(input_attachment_index = 0, set = 2, binding = 0) uniform subpassInput RtSampler;
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
#endif
#endif
#if PS_DATE > 0
layout(set = 2, binding = 1) uniform texture2D PrimMinTexture;
layout(set = 1, binding = 3) uniform texture2D PrimMinTexture;
#endif
#if NEEDS_TEX
@@ -1197,33 +1199,36 @@ void main()
#if PS_SHUFFLE
uvec4 denorm_c = uvec4(C);
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
#if PS_READ16_SRC
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
#if PS_SHUFFLE_SAME
#if (PS_READ_BA)
C = vec4(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
#else
C.ga = C.rg;
#endif
// Copy of a 16bit source in to this target
#elif PS_READ16_SRC
C.rb = vec2(float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5)));
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
// Write RB part. Mask will take care of the correct destination
#elif PS_READ_BA
C.rb = C.bb;
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
#else
// Mask will take care of the correct destination
#if PS_READ_BA
C.rb = C.bb;
#else
C.rb = C.rr;
#endif
#if PS_READ_BA
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
#else
if ((denorm_c.g & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
#endif
#endif
#endif
C.rb = C.rr;
if ((denorm_c.g & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
#endif // PS_SHUFFLE_SAME
#endif // PS_SHUFFLE
// Must be done before alpha correction
+1 -1
View File
@@ -110,7 +110,7 @@ struct crash_info_t
u64 reserved;
u64 reserved2;
};
#define CRASH_ANNOTATION __attribute__((section("__DATA,__crash_info")))
#define CRASH_ANNOTATION __attribute__((used, section("__DATA,__crash_info")))
#define CRASH_VERSION 4
extern "C" crash_info_t gCRAnnotations CRASH_ANNOTATION = { CRASH_VERSION };
#endif
+5 -5
View File
@@ -92,31 +92,31 @@ public:
return value;
}
__fi std::optional<int> GetOptionalIntValue(const char* section, const char* key, std::optional<int> default_value = std::nullopt)
__fi std::optional<int> GetOptionalIntValue(const char* section, const char* key, std::optional<int> default_value = std::nullopt) const
{
int ret;
return GetIntValue(section, key, &ret) ? std::optional<int>(ret) : default_value;
}
__fi std::optional<uint> GetOptionalUIntValue(const char* section, const char* key, std::optional<uint> default_value = std::nullopt)
__fi std::optional<uint> GetOptionalUIntValue(const char* section, const char* key, std::optional<uint> default_value = std::nullopt) const
{
uint ret;
return GetUIntValue(section, key, &ret) ? std::optional<uint>(ret) : default_value;
}
__fi std::optional<float> GetOptionalFloatValue(const char* section, const char* key, std::optional<float> default_value = std::nullopt)
__fi std::optional<float> GetOptionalFloatValue(const char* section, const char* key, std::optional<float> default_value = std::nullopt) const
{
float ret;
return GetFloatValue(section, key, &ret) ? std::optional<float>(ret) : default_value;
}
__fi std::optional<double> GetOptionalDoubleValue(const char* section, const char* key, std::optional<double> default_value = std::nullopt)
__fi std::optional<double> GetOptionalDoubleValue(const char* section, const char* key, std::optional<double> default_value = std::nullopt) const
{
double ret;
return GetDoubleValue(section, key, &ret) ? std::optional<double>(ret) : default_value;
}
__fi std::optional<bool> GetOptionalBoolValue(const char* section, const char* key, std::optional<bool> default_value = std::nullopt)
__fi std::optional<bool> GetOptionalBoolValue(const char* section, const char* key, std::optional<bool> default_value = std::nullopt) const
{
bool ret;
return GetBoolValue(section, key, &ret) ? std::optional<bool>(ret) : default_value;
+18 -1
View File
@@ -513,5 +513,22 @@ std::optional<std::string> InputManager::ConvertHostKeyboardCodeToString(u32 cod
u32 QtUtils::KeyEventToCode(const QKeyEvent* ev)
{
return static_cast<u32>(ev->key()) | (static_cast<u32>(ev->modifiers()) & static_cast<u32>(Qt::KeypadModifier));
int key = ev->key();
Qt::KeyboardModifiers modifiers = ev->modifiers();
#ifdef __APPLE__
// On macOS, Qt applies the Keypad modifier regardless of whether the arrow keys, or numpad was pressed.
// The only way to differentiate between the keypad and the arrow keys is by the text.
// Hopefully some keyboard layouts don't change the numpad positioning...
if (modifiers & Qt::KeypadModifier && key >= Qt::Key_Insert && key <= Qt::Key_PageDown)
{
if (ev->text().isEmpty())
{
// Drop the modifier, because it's probably not actually a numpad push.
modifiers &= ~Qt::KeypadModifier;
}
}
#endif
return static_cast<u32>(key) | (static_cast<u32>(modifiers) & static_cast<u32>(Qt::KeypadModifier));
}
+11 -4
View File
@@ -1078,7 +1078,10 @@ namespace SettingWidgetBinder
static inline void BindSliderToIntSetting(SettingsInterface* sif, QSlider* slider, QLabel* label, const QString& label_suffix,
std::string section, std::string key, s32 default_value)
{
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
//Clamp in case setting was updated manually using INI
global_value = std::clamp(global_value, slider->minimum(), slider->maximum());
if (sif)
{
@@ -1086,7 +1089,9 @@ namespace SettingWidgetBinder
QFont bold_font(orig_font);
bold_font.setBold(true);
const s32 current_value = sif->GetOptionalIntValue(section.c_str(), key.c_str()).value_or(global_value);
s32 current_value = sif->GetOptionalIntValue(section.c_str(), key.c_str()).value_or(global_value);
current_value = std::clamp(current_value, slider->minimum(), slider->maximum());
slider->setValue(current_value);
label->setText(QStringLiteral("%1%2").arg(current_value).arg(label_suffix));
@@ -1100,8 +1105,10 @@ namespace SettingWidgetBinder
[sif, slider, label, label_suffix, orig_font = std::move(orig_font), section, key, default_value](const QPoint& pt) {
QMenu menu(slider);
slider->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, slider,
[sif, label, label_suffix, orig_font, section, key, default_value]() {
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
[sif, slider, label, label_suffix, orig_font, section, key, default_value]() {
s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
global_value = std::clamp(global_value, slider->minimum(), slider->maximum());
label->setText(QStringLiteral("%1%2").arg(global_value).arg(label_suffix));
label->setFont(orig_font);
@@ -30,6 +30,8 @@
#include <QtCore/QDateTime>
#include <QtWidgets/QMessageBox>
static constexpr s32 DEFAULT_NOTIFICATIONS_DURATION = 5;
AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
, m_dialog(dialog)
@@ -48,6 +50,9 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Achievements", "SoundEffects", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.primedIndicators, "Achievements", "PrimedIndicators", true);
SettingWidgetBinder::BindSliderToIntSetting(sif, m_ui.notifications_duration, m_ui.notifications_duration_seconds,
tr(" seconds"), "Achievements", "NotificationsDuration", DEFAULT_NOTIFICATIONS_DURATION);
dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"),
tr("When enabled and logged in, PCSX2 will scan for achievements on game load."));
dialog->registerWidgetHelp(m_ui.testMode, tr("Enable Test Mode"), tr("Unchecked"),
@@ -70,10 +75,18 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
dialog->registerWidgetHelp(m_ui.primedIndicators, tr("Show Challenge Indicators"), tr("Checked"),
tr("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."));
dialog->registerWidgetHelp(m_ui.notifications_duration, tr("Notification Duration"),
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
dialog->registerWidgetHelp(m_ui.notifications_duration_label, tr("Notification Duration"),
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
dialog->registerWidgetHelp(m_ui.notifications_duration_seconds, tr("Notification Duration"),
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
connect(m_ui.enable, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.notifications, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::onChallengeModeStateChanged);
connect(m_ui.notifications_duration, &QSlider::valueChanged, this, &AchievementSettingsWidget::onNotificationsDurationChanged);
if (!m_dialog->isPerGameSettings())
{
@@ -105,6 +118,8 @@ void AchievementSettingsWidget::updateEnableState()
{
const bool enabled = m_dialog->getEffectiveBoolValue("Achievements", "Enabled", false);
const bool challenge = m_dialog->getEffectiveBoolValue("Achievements", "ChallengeMode", false);
const bool notifications = m_dialog->getEffectiveBoolValue("Achievements", "Notifications", true);
m_ui.testMode->setEnabled(enabled);
m_ui.unofficialTestMode->setEnabled(enabled);
m_ui.richPresence->setEnabled(enabled);
@@ -113,6 +128,10 @@ void AchievementSettingsWidget::updateEnableState()
m_ui.notifications->setEnabled(enabled);
m_ui.soundEffects->setEnabled(enabled);
m_ui.primedIndicators->setEnabled(enabled);
m_ui.notifications_duration->setEnabled(enabled && notifications);
m_ui.notifications_duration_label->setEnabled(enabled && notifications);
m_ui.notifications_duration_seconds->setEnabled(enabled && notifications);
}
void AchievementSettingsWidget::onChallengeModeStateChanged()
@@ -196,3 +215,9 @@ void AchievementSettingsWidget::onAchievementsRefreshed(quint32 id, const QStrin
{
m_ui.gameInfo->setText(game_info_string);
}
void AchievementSettingsWidget::onNotificationsDurationChanged()
{
m_ui.notifications_duration_seconds->setText(tr("%1 seconds")
.arg(m_ui.notifications_duration->value()));
}
@@ -33,6 +33,7 @@ private Q_SLOTS:
void onLoginLogoutPressed();
void onViewProfilePressed();
void onAchievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
void onNotificationsDurationChanged();
private:
void updateLoginState();
+83 -16
View File
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>658</width>
<height>496</height>
<width>829</width>
<height>641</height>
</rect>
</property>
<property name="windowTitle">
@@ -39,13 +39,6 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="primedIndicators">
<property name="text">
<string>Show Challenge Indicators</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="richPresence">
<property name="text">
@@ -74,13 +67,6 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="testMode">
<property name="text">
<string>Enable Test Mode</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="soundEffects">
<property name="text">
@@ -89,12 +75,93 @@
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="primedIndicators">
<property name="text">
<string>Show Challenge Indicators</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="testMode">
<property name="text">
<string>Enable Test Mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="notificationBox">
<property name="title">
<string>Notifications</string>
</property>
<layout class="QVBoxLayout" name="notifications_box_layout" stretch="0,0">
<item>
<widget class="QCheckBox" name="notifications">
<property name="text">
<string>Show Notifications</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="notifications_duration_layout" stretch="0,0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="notifications_duration_label">
<property name="text">
<string>Duration</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="notifications_duration">
<property name="minimum">
<number>3</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="notifications_duration_seconds">
<property name="text">
<string>5 seconds</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
+52 -7
View File
@@ -63,6 +63,11 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialo
connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged);
connect(m_ui.verify, &QAbstractButton::clicked, this, &GameSummaryWidget::onVerifyClicked);
connect(m_ui.searchHash, &QAbstractButton::clicked, this, &GameSummaryWidget::onSearchHashClicked);
bool has_custom_title = false, has_custom_region = false;
GameList::CheckCustomAttributesForPath(m_entry_path, has_custom_title, has_custom_region);
m_ui.restoreTitle->setEnabled(has_custom_title);
m_ui.restoreRegion->setEnabled(has_custom_region);
}
GameSummaryWidget::~GameSummaryWidget() = default;
@@ -88,6 +93,24 @@ void GameSummaryWidget::populateDetails(const GameList::Entry* entry)
m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value())));
else
m_ui.inputProfile->setCurrentIndex(0);
connect(m_ui.title, &QLineEdit::editingFinished, this, [this]() {
if (m_ui.title->isModified())
{
setCustomTitle(m_ui.title->text().toStdString());
m_ui.title->setModified(false);
}
});
connect(m_ui.restoreTitle, &QAbstractButton::clicked, this, [this]() {
setCustomTitle("");
});
connect(m_ui.region, &QComboBox::currentIndexChanged, this, [this](int index) {
setCustomRegion(index);
});
connect(m_ui.restoreRegion, &QAbstractButton::clicked, this, [this]() {
setCustomRegion(-1);
});
}
void GameSummaryWidget::populateDiscPath(const GameList::Entry* entry)
@@ -129,12 +152,7 @@ void GameSummaryWidget::onDiscPathChanged(const QString& value)
// force rescan of elf to update the serial
g_main_window->rescanFile(m_entry_path);
// and re-fill our details (mainly the serial)
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(m_entry_path.c_str());
if (entry)
populateDetails(entry);
repopulateCurrentDetails();
}
void GameSummaryWidget::onDiscPathBrowseClicked()
@@ -352,4 +370,31 @@ void GameSummaryWidget::setVerifyResult(QString error)
m_ui.verifyResult->setPlainText(error);
m_ui.verifyResult->setVisible(true);
m_ui.searchHash->setVisible(true);
}
}
void GameSummaryWidget::repopulateCurrentDetails()
{
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(m_entry_path.c_str());
if (entry)
{
populateDetails(entry);
m_dialog->setWindowTitle(QString::fromStdString(entry->title));
}
}
void GameSummaryWidget::setCustomTitle(const std::string& text)
{
m_ui.restoreTitle->setEnabled(!text.empty());
GameList::SaveCustomTitleForPath(m_entry_path, text);
repopulateCurrentDetails();
}
void GameSummaryWidget::setCustomRegion(int region)
{
m_ui.restoreRegion->setEnabled(region >= 0);
GameList::SaveCustomRegionForPath(m_entry_path, region);
repopulateCurrentDetails();
}
+4
View File
@@ -47,6 +47,10 @@ private:
void populateDiscPath(const GameList::Entry* entry);
void populateTrackList(const GameList::Entry* entry);
void setVerifyResult(QString error);
void repopulateCurrentDetails();
void setCustomTitle(const std::string& text);
void setCustomRegion(int region);
Ui::GameSummaryWidget m_ui;
SettingsDialog* m_dialog;
+196 -174
View File
@@ -34,11 +34,25 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="title">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="title">
<property name="placeholderText">
<string>Clear the line to restore the original title...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreTitle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Restore</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
@@ -140,170 +154,178 @@
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="region">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-B (Brazil)</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QComboBox" name="region">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-B (Brazil)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-C (China)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-HK (Hong Kong)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-J (Japan)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-K (Korea)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-T (Taiwan)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-U (US)</string>
</property>
</item>
<item>
<property name="text">
<string>Other</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-A (Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AF (South Africa)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AU (Austria)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-BE (Belgium)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-E (Europe/Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-F (France)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-FI (Finland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-G (Germany)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-GR (Greece)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-I (Italy)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-IN (India)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-M (Europe/Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NL (Netherlands)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NO (Norway)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-P (Portugal)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-PL (Poland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-R (Russia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-S (Spain)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SC (Scandinavia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SW (Sweden)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SWI (Switzerland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-UK (United Kingdom)</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreRegion">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Restore</string>
</property>
</widget>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-C (China)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-HK (Hong Kong)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-J (Japan)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-K (Korea)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-T (Taiwan)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-U (US)</string>
</property>
</item>
<item>
<property name="text">
<string>Other</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-A (Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AF (South Africa)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AU (Austria)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-BE (Belgium)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-E (Europe/Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-F (France)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-FI (Finland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-G (Germany)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-GR (Greece)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-I (Italy)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-IN (India)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-M (Europe/Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NL (Netherlands)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NO (Norway)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-P (Portugal)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-PL (Poland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-R (Russia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-S (Spain)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SC (Scandinavia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SW (Sweden)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SWI (Switzerland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-UK (United Kingdom)</string>
</property>
</item>
</widget>
</layout>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
@@ -488,22 +510,22 @@
<height>60</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="searchHash">
<property name="text">
<string>Search on Redump.org...</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="text">
<string>Search on Redump.org...</string>
</property>
</widget>
</item>
</layout>
@@ -181,7 +181,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
//////////////////////////////////////////////////////////////////////////
// HW Renderer Fixes
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderBW, "EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderLevel, "EmuCore/GS", "UserHacks_CPUSpriteRenderLevel", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuCLUTRender, "EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
@@ -515,9 +514,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
// Hardware Fixes tab
{
dialog->registerWidgetHelp(m_ui.halfScreenFix, tr("Half Screen Fix"), tr("Automatic (Default)"),
tr("Control the half-screen fix detection on texture shuffling."));
dialog->registerWidgetHelp(m_ui.cpuSpriteRenderBW, tr("CPU Sprite Renderer Size"), tr("0 (Disabled)"), tr(""));
dialog->registerWidgetHelp(m_ui.cpuCLUTRender, tr("Software CLUT Render"), tr("0 (Disabled)"), tr(""));
@@ -1050,7 +1046,6 @@ void GraphicsSettingsWidget::resetManualHardwareFixes()
check_bool("EmuCore/GS", "UserHacks", false);
check_int("EmuCore/GS", "UserHacks_Half_Bottom_Override", -1);
check_int("EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
check_int("EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
check_int("EmuCore/GS", "UserHacks_GPUTargetCLUTMode", 0);
@@ -780,32 +780,6 @@
<string>Hardware Fixes</string>
</attribute>
<layout class="QFormLayout" name="hardwareFixesLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Half Screen Fix:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="halfScreenFix">
<item>
<property name="text">
<string>Automatic (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Force Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Force Enabled</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_36">
<property name="text">
+22 -13
View File
@@ -12,7 +12,7 @@
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "MainWindow.h"
@@ -59,9 +59,10 @@ SettingsDialog::SettingsDialog(QWidget* parent)
}
SettingsDialog::SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game,
std::string serial, u32 disc_crc)
std::string serial, u32 disc_crc, QString filename)
: QDialog(parent)
, m_sif(std::move(sif))
, m_filename(std::move(filename))
, m_serial(std::move(serial))
, m_disc_crc(disc_crc)
{
@@ -80,9 +81,9 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
if (isPerGameSettings())
{
QString summary = tr("<strong>Summary</strong><hr>This page shows details about the selected game. Changing the Input "
"Profile will set the controller binding scheme for this game to whichever profile is chosen, instead "
"of the default (Shared) configuration. The track list and dump verification can be used to determine "
"if your disc image matches a known good dump. If it does not match, the game may be broken.");
"Profile will set the controller binding scheme for this game to whichever profile is chosen, instead "
"of the default (Shared) configuration. The track list and dump verification can be used to determine "
"if your disc image matches a known good dump. If it does not match, the game may be broken.");
if (game)
{
addWidget(new GameSummaryWidget(game, this, m_ui.settingsContainer), tr("Summary"),
@@ -153,7 +154,7 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
addWidget(m_memory_card_settings = new MemoryCardSettingsWidget(this, m_ui.settingsContainer), tr("Memory Cards"),
QStringLiteral("memcard-line"),
tr("<strong>Memory Card Settings</strong><hr>Create and configure Memory Cards here.<br><br>Mouse over an option for "
"additional information."));
"additional information."));
addWidget(m_dev9_settings = new DEV9SettingsWidget(this, m_ui.settingsContainer), tr("Network & HDD"), QStringLiteral("global-line"),
tr("<strong>Network & HDD Settings</strong><hr>These options control the network connectivity and internal HDD storage of the "
@@ -330,6 +331,18 @@ bool SettingsDialog::eventFilter(QObject* object, QEvent* event)
return QDialog::eventFilter(object, event);
}
void SettingsDialog::setWindowTitle(const QString& title)
{
if (m_filename.isEmpty())
{
QDialog::setWindowTitle(title);
}
else
{
QDialog::setWindowTitle(QStringLiteral("%1 [%2]").arg(title, m_filename));
}
}
bool SettingsDialog::getEffectiveBoolValue(const char* section, const char* key, bool default_value) const
{
bool value;
@@ -544,16 +557,12 @@ void SettingsDialog::openGamePropertiesDialog(const GameList::Entry* game, const
}
std::string filename(VMManager::GetGameSettingsPath(serial, disc_crc));
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(std::move(filename));
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(filename);
if (FileSystem::FileExists(sif->GetFileName().c_str()))
sif->Load();
const QString window_title(tr("%1 [%2]")
.arg(QtUtils::StringViewToQString(title))
.arg(QtUtils::StringViewToQString(Path::GetFileName(sif->GetFileName()))));
SettingsDialog* dialog = new SettingsDialog(g_main_window, std::move(sif), game, std::move(serial), disc_crc);
dialog->setWindowTitle(window_title);
SettingsDialog* dialog = new SettingsDialog(g_main_window, std::move(sif), game, std::move(serial), disc_crc, QtUtils::StringViewToQString(Path::GetFileName(filename)));
dialog->setWindowTitle(QtUtils::StringViewToQString(title));
dialog->setModal(false);
dialog->show();
}
+5 -1
View File
@@ -51,7 +51,7 @@ class SettingsDialog final : public QDialog
public:
explicit SettingsDialog(QWidget* parent);
SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, std::string serial, u32 disc_crc);
SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, std::string serial, u32 disc_crc, QString filename = QString());
~SettingsDialog();
static void openGamePropertiesDialog(const GameList::Entry* game, const std::string_view& title, std::string serial, u32 disc_crc);
@@ -79,6 +79,8 @@ public:
void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text);
bool eventFilter(QObject* object, QEvent* event) override;
void setWindowTitle(const QString& title);
QString getCategory() const;
void setCategory(const char* category);
@@ -145,6 +147,8 @@ private:
QObject* m_current_help_widget = nullptr;
QMap<QObject*, QString> m_widget_help_text_map;
QString m_filename;
std::string m_serial;
u32 m_disc_crc;
};
File diff suppressed because it is too large Load Diff
@@ -1,68 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg5"
inkscape:export-filename="bitmap.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="emulation-line-black.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="2.6074562"
inkscape:cx="129.05298"
inkscape:cy="350.53321"
inkscape:window-width="1920"
inkscape:window-height="956"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
id="rect1182"
width="123.23695"
height="369.71085"
x="150.59978"
y="71.144577" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
id="rect1184"
width="78.14328"
height="307.17816"
x="273.83676"
y="133.67725" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:25;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
d="m 357.42017,445.85541 h 84.05005 l -84.05007,-84.05007"
id="path1190" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:25;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none"
d="M 142.36951,445.85542 H 67.083026 l 68.461054,-67.55757"
id="path1192" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8"?><svg id="emulation_Pcsx2_b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 513 512"><defs><style>.cls-1{fill:none;}</style></defs><rect class="cls-1" width="513" height="512"/><path id="Ps2_-_By_Kam" d="M451.22,440.88l-76.33-77.28V116.18h-88.14V53.64H138.51V362.17l-77.72,78.69c-2.92,2.96-3.78,7.35-2.18,11.19,1.6,3.84,5.33,6.32,9.49,6.32h81.81c.07,0,.13-.02,.2-.02h211.39c.19,.01,.39,.03,.59,.03h81.81c4.16,0,7.89-2.48,9.49-6.32,1.6-3.84,.75-8.23-2.17-11.19Zm-199.48-17.52h-78.24V88.64h78.24V423.36Zm88.15,0h-53.14V151.18h53.14V423.36Z"/></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 584 B

@@ -1 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:28px;}.cls-1,.cls-2{fill:none;stroke:#000;}.cls-2{stroke-width:29px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-1" d="M217.39,192.85c-.45,.33-52.68,43.98-52.68,43.98l52.04,47.7"/><path id="Forma_6" class="cls-1" d="M173.41,238.01l161.32-.73"/><path id="Forma_2" class="cls-1" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-2" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="PCSX2_-_Save_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#000000;stroke-width:45;}
.st1{stroke:#000000;stroke-width:15;}
.st2{fill:none;stroke:#000000;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:none;stroke:#000000;stroke-width:28;}
</style>
<g id="By_maxihplay">
<g id="seta">
<line class="st0" x1="192" y1="331.9" x2="192" y2="243.1"/>
<path class="st1" d="M140.5,258l48-48c1.9-1.9,5-1.9,6.9,0l48,48c3.1,3.1,0.9,8.3-3.4,8.3h-96.1C139.6,266.3,137.4,261,140.5,258
L140.5,258z"/>
</g>
<g id="save">
<path class="st2" d="M112.5,318.2c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10v-246c0,0,1.5-7.5,8.9-8.9
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
<path id="Forma_2" class="st3" d="M120.7,53.4l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
c-0.1-4.4,0-89.1,0-89.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:28px;}.cls-1,.cls-2{fill:none;stroke:#000;}.cls-2{stroke-width:29px;}</style></defs><g id="By_maxihplay"><path id="Forma_6" class="cls-1" d="M309.03,236.01l-132.24-.6"/><path id="Forma_5" class="cls-1" d="M261.32,188.75c.45,.33,52.68,43.98,52.68,43.98l-52.04,47.7"/><path id="Forma_2" class="cls-1" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-2" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="PCSX2_-_load_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
<style type="text/css">
.st0{stroke:#000000;stroke-width:15;}
.st1{fill:none;stroke:#000000;stroke-width:45;}
.st2{fill:none;stroke:#000000;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:none;stroke:#000000;stroke-width:28;}
</style>
<g id="By_maxihplay">
<g id="seta">
<path class="st0" d="M242.4,279.2l-48,48c-1.9,1.9-5,1.9-6.9,0l-48-48c-3.1-3.1-0.9-8.3,3.4-8.3H239
C243.3,270.9,245.5,276.2,242.4,279.2L242.4,279.2z"/>
<line class="st1" x1="190.9" y1="205.3" x2="190.9" y2="294.1"/>
</g>
<g id="load">
<path class="st2" d="M112.5,321.7c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10V65.5c0,0,1.5-7.5,8.9-8.9
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
<path id="Forma_2" class="st3" d="M120.7,56.6l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
c-0.1-4.4,0-89.1,0-89.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="guitar_PCSX2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 493.66 474.14"><defs><style>.cls-1,.cls-2{stroke:#000;stroke-miterlimit:10;}.cls-2{stroke-width:20px;}</style></defs><g id="By_maxihplay"><path id="corpo" class="cls-1" d="M188.41,192.55c-.75,14.56,6.75,47.85,20.03,67.76,19.32,28.97,42.12,37.78,54.54,43.83-17.76,8.99-43.47,27.14-53.18,55-1.18,3.26-1.97,6.71-2.27,10.24-3.93,46.09-40.35,63.32-70.26,64.13-14.57,.4-29.14-2.93-42.13-9.52-14.19-7.25-26.02-17.98-35.01-32.02-.41-.58-.81-1.15-1.22-1.73-10.27-14.2-16.49-30.76-17.93-47.84-1.42-16.1,1.39-31.83,7.86-45.71,2.65-5.68,10.86-21.28,28.05-31.02,12.09-6.86,22.94-7.51,28.71-8.28,17.19-2.32,61.18-15.77,82.82-64.84Zm19.53-71.29c-38.91,1.06-45.85,41.06-64.11,71.01-14.61,23.88-38.65,24.92-40.31,24.97h-.1C10.15,223.2-29.95,335.62,26.37,413.63c26.97,41.92,70.63,61.09,111.99,59.96,53.67-1.46,103.78-36.96,109.18-100.8h0c19.93-58.65,105.53-15.58,93.41-112.5-23.14,8.75-40.07,12.44-52.19,12.77-26.98,.73-31.85-18.13-27.99-30.95l-25.37-31.05c-20.51-52.62-2.68-69.18,10.16-86.16-6.4-1.49-32.54-3.87-37.62-3.63h0Z"/><path id="cordas" class="cls-2" d="M103.87,365.16l-2.97-3.25c-3.52-3.71-3.25-9.64,.56-13.17l65.75-61.02c3.8-3.52,9.64-3.25,13.17,.46l2.97,3.25c3.52,3.71,3.25,9.64-.56,13.17l-65.75,61.02c-3.71,3.52-9.64,3.25-13.17-.46h0Z"/><polygon id="braco" class="cls-2" points="239.3 255.5 223.65 236.65 365.87 104.18 382.89 121.88 239.3 255.5"/><path class="cls-1" d="M485.96,27.45l-16.94-18.24c-10.07-10.97-27.21-11.69-38.25-1.56l-68.38,63.39c-10.94,10.08-11.64,27.19-1.51,38.22l16.94,18.24c5.31,5.78,12.58,8.71,19.88,8.71,6.56,0,13.14-2.37,18.37-7.16l68.35-63.35c5.33-4.89,8.42-11.56,8.72-18.79,.29-7.2-2.23-14.09-7.17-19.47Z"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="guitar_PCSX2_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 494.7 469.9" style="enable-background:new 0 0 494.7 469.9;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
.st1{stroke:#000000;stroke-width:16;stroke-miterlimit:10;}
.st2{stroke:#000000;stroke-width:20;stroke-miterlimit:10;}
.st3{stroke:#000000;stroke-miterlimit:10;}
</style>
<g id="By_Maxihplay">
<rect class="st0" width="494.7" height="469.9"/>
<path id="cordas" class="st1" d="M134.6,352.6l-2.7-2.5c-3.2-2.9-3.3-7.9-0.4-11l51.8-55.2c3-3.1,7.9-3.3,11.1-0.5l2.7,2.5
c3.2,2.9,3.3,7.9,0.4,11l-51.8,55.2C142.8,355.3,137.8,355.4,134.6,352.6L134.6,352.6z"/>
<polygon id="braco" class="st2" points="252.2,244.7 232.4,225.7 383.6,69.1 405,86.5 "/>
<path id="cabeca" class="st3" d="M488.2,39.1l-72.5,75.3l-50.7-13.3l-11-38.9l80.6-52.8c2.3-1.4,5-1.8,7.7-1.4l41.6,13.3
c0.5,0.2,5.2,2,6.8,7C492,32.1,491,36.2,488.2,39.1L488.2,39.1z"/>
<g id="corpo">
<path d="M202.9,140.8L202.9,140.8 M190.2,169.1l24,70.8c2.6,7.6,7.4,14.1,13.8,18.9l55.7,41c-1.4,0.6-2.8,1.2-4.2,1.8
c-11.7,5-25,10.6-38.2,18.9c-0.6,0.4-1.1,0.8-1.7,1.1c-3.7,2.6-15.1,10.7-25,36.4c-6.4,16.5-9.5,26.1-15.1,54.2
c-1,5-1.9,8.4-2.7,10.7c-14.2,1.9-30.3,3.7-46.5,3.7c-30.9,0-52.4-6.5-65.9-20l-0.3-0.3c-4.5-4.5-18.2-18.2-28.7-36.1
c-10.8-18.4-14.2-34.2-10.3-47.2c1.5-5.1,3.4-10.8,5.5-15.2c4.7-9.8,12.9-17.5,23-21.5c7.1-2.8,15.1-5.4,23.7-8.1
c13-4.2,27.7-8.9,42.2-15.6c9.3-4.3,16.5-12.1,20.1-21.8C165.3,226.3,179.7,193.2,190.2,169.1 M193.2,105.1
c-10.1,0-20.9,7.4-27.6,22.9c-9.4,21.8-34.2,77.5-42.4,99.3c-21.1,9.8-43.9,14.9-63.8,22.9c-19.2,7.7-34.8,22.2-43.7,40.9
c-3.2,6.6-5.5,13.8-7.6,20.6c-17.4,56.8,39,112.6,49,122.5c24.9,24.9,60.3,31.4,93.5,31.4c20.2,0,39.6-2.4,55.3-4.5
c13.5-1.8,25.2-6.1,32.1-41.4c5.2-26.5,7.8-33.8,13.2-47.6c3.2-8.2,7.4-15.9,11.1-18.6c21-13.3,44.1-18.7,65.1-32
c0.9-0.7,10-8.5,9.1-20.9c-0.8-10.6-8.4-16.7-9.6-17.6l-75.6-55.7l-34.7-102.1C212.2,112,203,105.1,193.2,105.1L193.2,105.1
L193.2,105.1z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -1,68 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg5"
inkscape:export-filename="bitmap.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="emulation-line.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="2.6074562"
inkscape:cx="128.66947"
inkscape:cy="411.51219"
inkscape:window-width="1920"
inkscape:window-height="956"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
id="rect1182"
width="123.23695"
height="369.71085"
x="150.59978"
y="71.144577" />
<rect
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
id="rect1184"
width="78.14328"
height="307.17816"
x="273.83676"
y="133.67725" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:25;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
d="m 357.42017,445.85541 h 84.05005 l -84.05007,-84.05007"
id="path1190" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:25;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none"
d="M 142.36951,445.85542 H 67.083026 l 68.461054,-67.55757"
id="path1192" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8"?><svg id="emulation_Pcsx2_w" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 513 512"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:none;}</style></defs><rect class="cls-2" width="513" height="512"/><path id="Ps2_-_By_Kam" class="cls-1" d="M451.22,440.88l-76.33-77.28V116.18h-88.14V53.64H138.51V362.17l-77.72,78.69c-2.92,2.96-3.78,7.35-2.18,11.19,1.6,3.84,5.33,6.32,9.49,6.32h81.81c.07,0,.13-.02,.2-.02h211.39c.19,.01,.39,.03,.59,.03h81.81c4.16,0,7.89-2.48,9.49-6.32,1.6-3.84,.75-8.23-2.17-11.19Zm-199.48-17.52h-78.24V88.64h78.24V423.36Zm88.15,0h-53.14V151.18h53.14V423.36Z"/></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 616 B

@@ -1 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_Save" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:29px;}.cls-1,.cls-2{fill:none;stroke:#fff;}.cls-2{stroke-width:28px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-2" d="M217.39,192.85c-.45,.33-52.68,43.98-52.68,43.98l52.04,47.7"/><path id="Forma_6" class="cls-2" d="M173.41,238.01l161.32-.73"/><path id="Forma_2" class="cls-2" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-1" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="PCSX2_-_Save_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#FFFFFF;stroke-width:45;}
.st1{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:15;}
.st2{fill:none;stroke:#FFFFFF;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:none;stroke:#FFFFFF;stroke-width:28;}
</style>
<g id="By_maxihplay">
<g id="seta">
<line class="st0" x1="192" y1="331.9" x2="192" y2="243.1"/>
<path class="st1" d="M140.5,258l48-48c1.9-1.9,5-1.9,6.9,0l48,48c3.1,3.1,0.9,8.3-3.4,8.3h-96.1C139.6,266.3,137.4,261,140.5,258
L140.5,258z"/>
</g>
<g id="save">
<path class="st2" d="M112.5,318.2c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10v-246c0,0,1.5-7.5,8.9-8.9
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
<path id="Forma_2" class="st3" d="M120.7,53.4l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
c-0.1-4.4,0-89.1,0-89.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:29px;}.cls-1,.cls-2{fill:none;stroke:#fff;}.cls-2{stroke-width:28px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-2" d="M265.05,192.85c.45,.33,52.68,43.98,52.68,43.98l-52.04,47.7"/><path id="Forma_6" class="cls-2" d="M309.03,238.01l-132.24-.6"/><path id="Forma_2" class="cls-2" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-1" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="PCSX2_-_Load_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:15;}
.st1{fill:none;stroke:#FFFFFF;stroke-width:45;}
.st2{fill:none;stroke:#FFFFFF;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:none;stroke:#FFFFFF;stroke-width:28;}
</style>
<g id="By_maxihplay">
<g id="seta">
<path class="st0" d="M242.4,279.2l-48,48c-1.9,1.9-5,1.9-6.9,0l-48-48c-3.1-3.1-0.9-8.3,3.4-8.3H239
C243.3,270.9,245.5,276.2,242.4,279.2L242.4,279.2z"/>
<line class="st1" x1="190.9" y1="205.3" x2="190.9" y2="294.1"/>
</g>
<g id="load">
<path class="st2" d="M112.5,321.7c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10V65.5c0,0,1.5-7.5,8.9-8.9
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
<path id="Forma_2" class="st3" d="M120.7,56.6l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
c-0.1-4.4,0-89.1,0-89.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="guitar_PCSX2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 492.66 473.14"><defs><style>.cls-1,.cls-2{fill:#fff;}.cls-2{stroke:#fff;stroke-miterlimit:10;stroke-width:20px;}</style></defs><g id="By_maxihplay"><path id="corpo" class="cls-1" d="M187.91,192.05c-.75,14.56,6.75,47.85,20.03,67.76,19.32,28.97,42.12,37.78,54.54,43.83-17.76,8.99-43.47,27.14-53.18,55-1.18,3.26-1.97,6.71-2.27,10.24-3.93,46.09-40.35,63.32-70.26,64.13-14.57,.4-29.14-2.93-42.13-9.52-14.19-7.25-26.02-17.98-35.01-32.02-.41-.58-.81-1.15-1.22-1.73-10.27-14.2-16.49-30.76-17.93-47.84-1.42-16.1,1.39-31.83,7.86-45.71,2.65-5.68,10.86-21.28,28.05-31.02,12.09-6.86,22.94-7.51,28.71-8.28,17.19-2.32,61.18-15.77,82.82-64.84Zm19.53-71.29c-38.91,1.06-45.85,41.06-64.11,71.01-14.61,23.88-38.65,24.92-40.31,24.97h-.1C9.65,222.7-30.45,335.12,25.87,413.13c26.97,41.92,70.63,61.09,111.99,59.96,53.67-1.46,103.78-36.96,109.18-100.8h0c19.93-58.65,105.53-15.58,93.41-112.5-23.14,8.75-40.07,12.44-52.19,12.77-26.98,.73-31.85-18.13-27.99-30.95l-25.37-31.05c-20.51-52.62-2.68-69.18,10.16-86.16-6.4-1.49-32.54-3.87-37.62-3.63h0Z"/><path id="cordas" class="cls-2" d="M103.37,364.66l-2.97-3.25c-3.52-3.71-3.25-9.64,.56-13.17l65.75-61.02c3.8-3.52,9.64-3.25,13.17,.46l2.97,3.25c3.52,3.71,3.25,9.64-.56,13.17l-65.75,61.02c-3.71,3.52-9.64,3.25-13.17-.46h0Z"/><polygon id="braco" class="cls-2" points="238.8 255 223.15 236.15 365.37 103.68 382.39 121.38 238.8 255"/><path class="cls-1" d="M485.46,26.95l-16.94-18.24c-10.07-10.97-27.21-11.69-38.25-1.56l-68.38,63.39c-10.94,10.08-11.64,27.19-1.51,38.22l16.94,18.24c5.31,5.78,12.58,8.71,19.88,8.71,6.56,0,13.14-2.37,18.37-7.16l68.35-63.35c5.33-4.89,8.42-11.56,8.72-18.79,.29-7.2-2.23-14.09-7.17-19.47Z"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="guitar_PCSX2_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 494.7 469.9" style="enable-background:new 0 0 494.7 469.9;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
.st1{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:16;stroke-miterlimit:10;}
.st2{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:20;stroke-miterlimit:10;}
.st3{fill:#FFFFFF;stroke:#FFFFFF;stroke-miterlimit:10;}
.st4{fill:#FFFFFF;}
</style>
<g id="By_Maxihplay">
<rect class="st0" width="494.7" height="469.9"/>
<path id="cordas" class="st1" d="M134.6,352.6l-2.7-2.5c-3.2-2.9-3.3-7.9-0.4-11l51.8-55.2c3-3.1,7.9-3.3,11.1-0.5l2.7,2.5
c3.2,2.9,3.3,7.9,0.4,11l-51.8,55.2C142.8,355.3,137.8,355.4,134.6,352.6L134.6,352.6z"/>
<polygon id="braco" class="st2" points="252.2,244.7 232.4,225.7 383.6,69.1 405,86.5 "/>
<path id="cabeca" class="st3" d="M488.2,39.1l-72.5,75.3l-50.7-13.3l-11-38.9l80.6-52.8c2.3-1.4,5-1.8,7.7-1.4l41.6,13.3
c0.5,0.2,5.2,2,6.8,7C492,32.1,491,36.2,488.2,39.1L488.2,39.1z"/>
<g id="corpo">
<path class="st4" d="M202.9,140.8L202.9,140.8 M190.2,169.1l24,70.8c2.6,7.6,7.4,14.1,13.8,18.9l55.7,41c-1.4,0.6-2.8,1.2-4.2,1.8
c-11.7,5-25,10.6-38.2,18.9c-0.6,0.4-1.1,0.8-1.7,1.1c-3.7,2.6-15.1,10.7-25,36.4c-6.4,16.5-9.5,26.1-15.1,54.2
c-1,5-1.9,8.4-2.7,10.7c-14.2,1.9-30.3,3.7-46.5,3.7c-30.9,0-52.4-6.5-65.9-20l-0.3-0.3c-4.5-4.5-18.2-18.2-28.7-36.1
c-10.8-18.4-14.2-34.2-10.3-47.2c1.5-5.1,3.4-10.8,5.5-15.2c4.7-9.8,12.9-17.5,23-21.5c7.1-2.8,15.1-5.4,23.7-8.1
c13-4.2,27.7-8.9,42.2-15.6c9.3-4.3,16.5-12.1,20.1-21.8C165.3,226.3,179.7,193.2,190.2,169.1 M193.2,105.1
c-10.1,0-20.9,7.4-27.6,22.9c-9.4,21.8-34.2,77.5-42.4,99.3c-21.1,9.8-43.9,14.9-63.8,22.9c-19.2,7.7-34.8,22.2-43.7,40.9
c-3.2,6.6-5.5,13.8-7.6,20.6c-17.4,56.8,39,112.6,49,122.5c24.9,24.9,60.3,31.4,93.5,31.4c20.2,0,39.6-2.4,55.3-4.5
c13.5-1.8,25.2-6.1,32.1-41.4c5.2-26.5,7.8-33.8,13.2-47.6c3.2-8.2,7.4-15.9,11.1-18.6c21-13.3,44.1-18.7,65.1-32
c0.9-0.7,10-8.5,9.1-20.9c-0.8-10.6-8.4-16.7-9.6-17.6l-75.6-55.7l-34.7-102.1C212.2,112,203,105.1,193.2,105.1L193.2,105.1
L193.2,105.1z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

+16 -8
View File
@@ -132,6 +132,8 @@ namespace Achievements
static void UnlockAchievementCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data);
static void SubmitLeaderboardCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data, u32 lboard_id);
static s32 GetNotificationsDuration();
static bool s_active = false;
static bool s_logged_in = false;
static bool s_challenge_mode = false;
@@ -1117,9 +1119,9 @@ void Achievements::DisplayAchievementSummary()
summary.append(TRANSLATE_SV("Achievements", "Leaderboard submission is enabled."));
}
MTGS::RunOnGSThread([title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
if (FullscreenUI::IsInitialized())
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(summary), std::move(icon));
});
}
@@ -1137,9 +1139,9 @@ void Achievements::DisplayMasteredNotification()
std::string message(fmt::format(
"{} achievements, {} points{}", GetAchievementCount(), GetCurrentPointsForGame(), s_challenge_mode ? " (Hardcore Mode)" : ""));
MTGS::RunOnGSThread([title = std::move(title), message = std::move(message), icon = s_game_icon]() {
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = std::move(title), message = std::move(message), icon = s_game_icon]() {
if (FullscreenUI::IsInitialized())
ImGuiFullscreen::AddNotification(20.0f, std::move(title), std::move(message), std::move(icon));
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(message), std::move(icon));
});
}
@@ -1850,9 +1852,9 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, const std::string&
std::string summary = fmt::format(
"Your Score: {} (Best: {})\nLeaderboard Position: {} of {}", submitted_score, best_score, response.new_rank, response.num_entries);
MTGS::RunOnGSThread([title = lb->title, summary = std::move(summary), icon = s_game_icon]() {
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = lb->title, summary = std::move(summary), icon = s_game_icon]() {
if (FullscreenUI::IsInitialized())
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(summary), std::move(icon));
});
}
@@ -1894,8 +1896,8 @@ void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /
}
MTGS::RunOnGSThread(
[title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() {
ImGuiFullscreen::AddNotification(15.0f, std::move(title), std::move(description), std::move(icon));
[duration = GetNotificationsDuration(), title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() {
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(description), std::move(icon));
});
}
@@ -2153,6 +2155,12 @@ void Achievements::PokeMemory(unsigned address, unsigned num_bytes, void* ud, un
}
}
s32 Achievements::GetNotificationsDuration()
{
return EmuConfig.Achievements.NotificationsDuration;
}
#ifdef ENABLE_RAINTEGRATION
#include "RA_Consoles.h"
+3 -2
View File
@@ -765,7 +765,6 @@ struct Pcsx2Config
int SkipDrawEnd = 0;
GSHWAutoFlushLevel UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
s8 UserHacks_HalfBottomOverride = -1;
s8 UserHacks_HalfPixelOffset = 0;
s8 UserHacks_RoundSprite = 0;
s32 UserHacks_TCOffsetX = 0;
@@ -1262,12 +1261,14 @@ struct Pcsx2Config
PrimedIndicators : 1;
BITFIELD_END
s32 NotificationsDuration = 5;
AchievementsOptions();
void LoadSave(SettingsWrapper& wrap);
bool operator==(const AchievementsOptions& right) const
{
return OpEqu(bitset);
return OpEqu(bitset) && OpEqu(NotificationsDuration);
}
bool operator!=(const AchievementsOptions& right) const
+1 -1
View File
@@ -286,5 +286,5 @@ private:
static void WriteUInt16(u8* data, int* index, u16 value);
static void WriteUInt32(u8* data, int* index, u32 value);
static void WriteUInt64(u8* data, int* index, u64 value);
static void WritePaddedString(u8* data, int* index, std::string value, u32 len);
static void WritePaddedString(u8* data, int* index, const std::string& value, u32 len);
};
+1 -1
View File
@@ -37,7 +37,7 @@ void ATA::WriteUInt64(u8* data, int* index, u64 value)
}
//No null char
void ATA::WritePaddedString(u8* data, int* index, std::string value, u32 len)
void ATA::WritePaddedString(u8* data, int* index, const std::string& value, u32 len)
{
memset(&data[*index], (u8)' ', len);
memcpy(&data[*index], value.c_str(), value.length() < len ? value.length() : len);
+1 -1
View File
@@ -39,7 +39,7 @@ void HddCreate::Start()
Cleanup();
}
void HddCreate::WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes)
void HddCreate::WriteImage(const std::string& hddPath, u64 fileBytes, u64 zeroSizeBytes)
{
constexpr int buffsize = 4 * 1024;
const u8 buff[buffsize] = {0}; // 4kb.
+1 -1
View File
@@ -49,5 +49,5 @@ protected:
void SetCanceled();
private:
void WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes);
void WriteImage(const std::string& hddPath, u64 fileBytes, u64 zeroSizeBytes);
};
+3 -3
View File
@@ -277,7 +277,7 @@ namespace InternalServers
}
#ifdef _WIN32
void DNS_Server::GetHost(std::string url, DNS_State* state)
void DNS_Server::GetHost(const std::string& url, DNS_State* state)
{
//Need to convert to UTF16
const int size = MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, nullptr, 0);
@@ -347,7 +347,7 @@ namespace InternalServers
delete data;
}
#elif defined(__POSIX__)
void DNS_Server::GetHost(std::string url, DNS_State* state)
void DNS_Server::GetHost(const std::string& url, DNS_State* state)
{
//Need to spin up thread, pass the parms to it
@@ -357,7 +357,7 @@ namespace InternalServers
GetHostThread.detach();
}
void DNS_Server::GetAddrInfoThread(std::string url, DNS_State* state)
void DNS_Server::GetAddrInfoThread(const std::string& url, DNS_State* state)
{
addrinfo hints{0};
hints.ai_family = AF_INET;
+2 -2
View File
@@ -95,7 +95,7 @@ namespace InternalServers
private:
void LoadHostList();
bool CheckHostList(std::string url, DNS_State* state);
void GetHost(std::string url, DNS_State* state);
void GetHost(const std::string& url, DNS_State* state);
void FinaliseDNS(DNS_State* state);
#ifdef _WIN32
@@ -111,7 +111,7 @@ namespace InternalServers
static void __stdcall GetAddrInfoExCallback(DWORD dwError, DWORD dwBytes, OVERLAPPED* lpOverlapped);
#elif defined(__POSIX__)
void GetAddrInfoThread(std::string url, DNS_State* state);
void GetAddrInfoThread(const std::string& url, DNS_State* state);
#endif
};
} // namespace InternalServers
+3 -3
View File
@@ -1088,7 +1088,7 @@ void MTSAH( std::string& output ) { _sap("mtsah\t%s, 0x%04X") GPR_REG[DECODE_R
//***************************SPECIAL 2 CPU OPCODES*******************
const char* pmfhl_sub[] = { "lw", "uw", "slw", "lh", "sh" };
const char* pmfhl_sub[] = {"lw", "uw", "slw", "lh", "sh", "??", "??"};
void MADD( std::string& output ) { _sap("madd\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
void MADDU( std::string& output ) { _sap("maddu\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]);}
@@ -1104,8 +1104,8 @@ void MULTU1( std::string& output ) { _sap("multu1\t%s, %s, %s") GPR_REG[
void DIV1( std::string& output ) { _sap("div1\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
void DIVU1( std::string& output ) { _sap("divu1\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
//that have parametres that i haven't figure out how to display...
void PMFHL( std::string& output ) { _sap("pmfhl.%s \t%s") pmfhl_sub[DECODE_SA], GPR_REG[DECODE_RD]); }
void PMTHL( std::string& output ) { _sap("pmthl.%s \t%s") pmfhl_sub[DECODE_SA], GPR_REG[DECODE_RS]); }
void PMFHL( std::string& output ) { _sap("pmfhl.%s \t%s") pmfhl_sub[DECODE_SA & 0x7], GPR_REG[DECODE_RD]); }
void PMTHL( std::string& output ) { _sap("pmthl.%s \t%s") pmfhl_sub[DECODE_SA & 0x7], GPR_REG[DECODE_RS]); }
void PSLLH( std::string& output ) { _sap("psllh \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); }
void PSRLH( std::string& output ) { _sap("psrlh \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);}
void PSRAH( std::string& output ) { _sap("psrah \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);}
+57
View File
@@ -502,6 +502,63 @@ GSVector4i GSLocalMemory::GetRectForPageOffset(u32 base_bp, u32 offset_bp, u32 b
return GSVector4i(pgs * page_offset_xy).xyxy() + GSVector4i::loadh(pgs);
}
bool GSLocalMemory::HasOverlap(const u32 src_bp, const u32 src_bw, const u32 src_psm, const GSVector4i src_rect
, const u32 dst_bp, const u32 dst_bw, const u32 dst_psm, const GSVector4i dst_rect)
{
const u32 src_start_bp = GSLocalMemory::GetStartBlockAddress(src_bp, src_bw, src_psm, src_rect) & ~(BLOCKS_PER_PAGE - 1);
const u32 dst_start_bp = GSLocalMemory::GetStartBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) & ~(BLOCKS_PER_PAGE - 1);
u32 src_end_bp = ((GSLocalMemory::GetEndBlockAddress(src_bp, src_bw, src_psm, src_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
u32 dst_end_bp = ((GSLocalMemory::GetEndBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
if (src_start_bp == src_end_bp)
{
src_end_bp = (src_end_bp + BLOCKS_PER_PAGE) & ~(MAX_BLOCKS - 1);
}
if (dst_start_bp == dst_end_bp)
{
dst_end_bp = (dst_end_bp + BLOCKS_PER_PAGE) & ~(MAX_BLOCKS - 1);
}
// Source has wrapped, 2 separate checks.
if (src_end_bp <= src_start_bp)
{
// Destination has also wrapped, so they *have* to overlap.
if (dst_end_bp <= dst_start_bp)
{
return true;
}
else
{
if (dst_end_bp > src_start_bp)
return true;
if (dst_start_bp < src_end_bp)
return true;
}
}
else // No wrapping on source.
{
// Destination wraps.
if (dst_end_bp <= dst_start_bp)
{
if (src_end_bp > dst_start_bp)
return true;
if (src_start_bp < dst_end_bp)
return true;
}
else
{
if (dst_start_bp < src_end_bp && dst_end_bp > src_start_bp)
return true;
}
}
return false;
}
///////////////////
void GSLocalMemory::ReadTexture(const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA)
+1
View File
@@ -546,6 +546,7 @@ public:
GSPixelOffset* GetPixelOffset(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
GSPixelOffset4* GetPixelOffset4(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
std::vector<GSVector2i>* GetPage2TileMap(const GIFRegTEX0& TEX0);
static bool HasOverlap(u32 src_bp, u32 src_bw, u32 src_psm, GSVector4i src_rect, u32 dst_bp, u32 dst_bw, u32 dst_psm, GSVector4i dst_rect);
static bool IsPageAligned(u32 psm, const GSVector4i& rc);
static u32 GetStartBlockAddress(u32 bp, u32 bw, u32 psm, GSVector4i rect);
static u32 GetEndBlockAddress(u32 bp, u32 bw, u32 psm, GSVector4i rect);
+289 -135
View File
@@ -1416,7 +1416,7 @@ void GSState::GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r)
{
GL_REG("TRXDIR = 0x%x_%x", r->U32[1], r->U32[0]);
Flush(GSFlushReason::GSTRANSFER);
FlushWrite();
m_env.TRXDIR = r->TRXDIR;
@@ -1429,6 +1429,7 @@ void GSState::GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r)
m_tr.Init(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.BITBLTBUF, false);
break;
case 2: // local -> local
CheckWriteOverlap(true, true);
Move();
break;
default: // 3 deactivated as stated by manual. Tested on hardware and no transfers happen.
@@ -1476,6 +1477,7 @@ void GSState::Flush(GSFlushReason reason)
}
m_dirty_gs_regs = 0;
temp_draw_rect = GSVector4i::zero();
}
m_state_flush_reason = GSFlushReason::UNKNOWN;
@@ -1508,6 +1510,8 @@ void GSState::FlushWrite()
g_perfmon.Put(GSPerfMon::Swizzle, len);
s_transfer_n++;
if (m_tr.start >= m_tr.total)
m_env.TRXDIR.XDIR = 3;
}
// This function decides if the context has changed in a way which warrants flushing the draw.
@@ -1687,59 +1691,148 @@ void GSState::FlushPrim()
}
}
}
GSVector4i GSState::GetTEX0Rect()
{
GSVector4i ret = GSVector4i::zero();
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
if (prev_ctx.CLAMP.WMS <= 1) // CLAMP/REPEAT
{
ret.x = 0;
ret.z = 1 << prev_ctx.TEX0.TW;
}
else if (prev_ctx.CLAMP.WMS == 3) // REGION_REPEAT
{
ret.x = prev_ctx.CLAMP.MAXU;
ret.z = prev_ctx.CLAMP.MAXU | prev_ctx.CLAMP.MINU;
}
else // 2 REGION_CLAMP
{
ret.x = prev_ctx.CLAMP.MINU;
ret.z = prev_ctx.CLAMP.MAXU;
}
if (prev_ctx.CLAMP.WMT <= 1) // CLAMP/REPEAT
{
ret.y = 0;
ret.w = 1 << prev_ctx.TEX0.TH;
}
else if (prev_ctx.CLAMP.WMT == 3) // REGION_REPEAT
{
ret.y = prev_ctx.CLAMP.MAXV;
ret.w = prev_ctx.CLAMP.MAXV | prev_ctx.CLAMP.MINV;
}
else // 2 REGION_CLAMP
{
ret.y = prev_ctx.CLAMP.MINV;
ret.w = prev_ctx.CLAMP.MAXV;
}
return ret;
}
void GSState::CheckWriteOverlap(bool req_write, bool req_read)
{
const int w = m_env.TRXREG.RRW;
const int h = m_env.TRXREG.RRH;
const GIFRegBITBLTBUF& blit = m_env.BITBLTBUF;
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
const GSVector4i write_rect = GSVector4i(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, m_env.TRXPOS.DSAX + w, m_env.TRXPOS.DSAY + h);
const u32 write_start_bp = GSLocalMemory::GetStartBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect);
const u32 write_end_bp = ((GSLocalMemory::GetEndBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
const GSVector4i tex_rect = m_prev_env.PRIM.TME ? GetTEX0Rect() : GSVector4i::zero();
if (m_index.tail > 0)
{
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
// Cartoon Network overwrites its own Z buffer in the middle of a draw.
// Alias wraps its transfers, so be careful
const GSVector4i read_rect = GSVector4i(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.TRXPOS.SSAX + w, m_env.TRXPOS.SSAY + h);
if (req_write && m_prev_env.PRIM.TME)
{
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW, prev_ctx.TEX0.PSM, tex_rect))
{
Flush(GSFlushReason::UPLOADDIRTYTEX);
}
if (prev_ctx.TEX1.MXL > 0 && prev_ctx.TEX1.MMIN >= 2 && prev_ctx.TEX1.MMIN <= 5)
{
switch (prev_ctx.TEX1.MXL)
{
case 6:
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP6, prev_ctx.MIPTBP2.TBW6, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 6, tex_rect.y >> 6, tex_rect.z >> 6, tex_rect.w >> 6)))
Flush(GSFlushReason::UPLOADDIRTYTEX);
[[fallthrough]];
case 5:
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP5, prev_ctx.MIPTBP2.TBW5, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 5, tex_rect.y >> 5, tex_rect.z >> 5, tex_rect.w >> 5)))
Flush(GSFlushReason::UPLOADDIRTYTEX);
[[fallthrough]];
case 4:
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP4, prev_ctx.MIPTBP2.TBW4, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 4, tex_rect.y >> 4, tex_rect.z >> 4, tex_rect.w >> 4)))
Flush(GSFlushReason::UPLOADDIRTYTEX);
[[fallthrough]];
case 3:
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP3, prev_ctx.MIPTBP1.TBW3, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 3, tex_rect.y >> 3, tex_rect.z >> 3, tex_rect.w >> 3)))
Flush(GSFlushReason::UPLOADDIRTYTEX);
[[fallthrough]];
case 2:
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP2, prev_ctx.MIPTBP1.TBW2, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 2, tex_rect.y >> 2, tex_rect.z >> 2, tex_rect.w >> 2)))
Flush(GSFlushReason::UPLOADDIRTYTEX);
[[fallthrough]];
case 1:
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP1, prev_ctx.MIPTBP1.TBW1, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 1, tex_rect.y >> 1, tex_rect.z >> 1, tex_rect.w >> 1)))
Flush(GSFlushReason::UPLOADDIRTYTEX);
break;
}
}
}
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && (prev_ctx.TEST.AFAIL == 2 || prev_ctx.TEST.AFAIL == 0)) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || prev_ctx.TEST.DATE;
if (frame_required)
{
if ((req_write && GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW, prev_ctx.FRAME.PSM, temp_draw_rect)) ||
(req_read && GSLocalMemory::HasOverlap(blit.SBP, blit.SBW, blit.SPSM, read_rect, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW, prev_ctx.FRAME.PSM, temp_draw_rect)))
Flush(GSFlushReason::UPLOADDIRTYFRAME);
}
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
if (zbuf_required)
{
if ((req_write && GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW, prev_ctx.ZBUF.PSM, temp_draw_rect)) ||
(req_read && GSLocalMemory::HasOverlap(blit.SBP, blit.SBW, blit.SPSM, read_rect, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW, prev_ctx.ZBUF.PSM, temp_draw_rect)))
Flush(GSFlushReason::UPLOADDIRTYZBUF);
}
}
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
}
void GSState::Write(const u8* mem, int len)
{
if (m_env.TRXDIR.XDIR == 3)
return;
const int w = m_env.TRXREG.RRW;
int h = m_env.TRXREG.RRH;
const int h = m_env.TRXREG.RRH;
CheckWriteOverlap(true, false);
if (!m_tr.Update(w, h, GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].trbpp, len))
{
m_env.TRXDIR.XDIR = 3;
return;
}
GIFRegBITBLTBUF& blit = m_tr.m_blit;
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[blit.DPSM];
if (!m_tr.Update(w, h, psm.trbpp, len))
return;
if (m_tr.end == 0)
{
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
const u32 write_start_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, blit.DBP, blit.DBW);
const u32 write_end_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, blit.DBP, blit.DBW);
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
if (m_index.tail > 0)
{
const u32 tex_end_bp = m_mem.m_psm[prev_ctx.TEX0.PSM].info.bn((1 << prev_ctx.TEX0.TW) - 1, (1 << prev_ctx.TEX0.TH) - 1, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW);
if (m_prev_env.PRIM.TME && write_end_bp > prev_ctx.TEX0.TBP0 && write_start_bp <= tex_end_bp)
Flush(GSFlushReason::UPLOADDIRTYTEX);
else
{
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL == 2) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || (prev_ctx.TEST.ATE && prev_ctx.TEST.ATST > ATST_ALWAYS);
if (frame_required)
{
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
Flush(GSFlushReason::UPLOADDIRTYFRAME);
}
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
if (zbuf_required)
{
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
Flush(GSFlushReason::UPLOADDIRTYZBUF);
}
}
}
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
@@ -1780,7 +1873,7 @@ void GSState::Write(const u8* mem, int len)
g_perfmon.Put(GSPerfMon::Swizzle, len);
s_transfer_n++;
m_env.TRXDIR.XDIR = 3;
return;
}
}
@@ -1799,11 +1892,16 @@ void GSState::InitReadFIFO(u8* mem, int len)
if (len <= 0 || m_tr.total != 0)
return;
if (m_env.TRXDIR.XDIR == 3)
return;
const int w = m_env.TRXREG.RRW;
const int h = m_env.TRXREG.RRH;
const u16 bpp = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM].trbpp;
CheckWriteOverlap(false, true);
if (!m_tr.Update(w, h, bpp, len))
return;
@@ -1834,13 +1932,24 @@ void GSState::Read(u8* mem, int len)
if (len <= 0 || m_tr.total == 0)
return;
if (m_env.TRXDIR.XDIR == 3)
return;
const int w = m_env.TRXREG.RRW;
const int h = m_env.TRXREG.RRH;
const u16 bpp = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM].trbpp;
CheckWriteOverlap(false, true);
if (!m_tr.Update(w, h, bpp, len))
return;
const int draw = s_n;
if (draw != s_n)
DevCon.Warning("Warning! Possible incorrect data download");
// If it wraps memory, we need to break it up so we don't read out of bounds.
if ((m_tr.end + len) > m_mem.m_vmsize)
{
@@ -1856,6 +1965,9 @@ void GSState::Read(u8* mem, int len)
memcpy(mem, &m_tr.buff[m_tr.end], len);
m_tr.end += len;
}
if(m_tr.end >= m_tr.total)
m_env.TRXDIR.XDIR = 3;
}
void GSState::Move()
@@ -1864,6 +1976,9 @@ void GSState::Move()
// guitar hero copies the far end of the board to do a similar blend too
s_transfer_n++;
if (m_env.TRXDIR.XDIR == 3)
return;
int sx = m_env.TRXPOS.SSAX;
int sy = m_env.TRXPOS.SSAY;
int dx = m_env.TRXPOS.DSAX;
@@ -1909,43 +2024,6 @@ void GSState::Move()
const GSOffset spo = m_mem.GetOffset(sbp, sbw, m_env.BITBLTBUF.SPSM);
const GSOffset dpo = m_mem.GetOffset(dbp, dbw, m_env.BITBLTBUF.DPSM);
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
const u32 write_start_bp = m_mem.m_psm[m_env.BITBLTBUF.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, dbp, dbw);
const u32 write_end_bp = m_mem.m_psm[m_env.BITBLTBUF.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, dbp, dbw);
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
// Unknown if games use this one, but best to be safe.
if (m_index.tail > 0)
{
const u32 tex_end_bp = m_mem.m_psm[prev_ctx.TEX0.PSM].info.bn((1 << prev_ctx.TEX0.TW) - 1, (1 << prev_ctx.TEX0.TH) - 1, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW);
if (m_prev_env.PRIM.TME && write_end_bp >= prev_ctx.TEX0.TBP0 && write_start_bp <= tex_end_bp)
Flush(GSFlushReason::LOCALTOLOCALMOVE);
else
{
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL == 2) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || (prev_ctx.TEST.ATE && prev_ctx.TEST.ATST > ATST_ALWAYS);
if (frame_required)
{
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
Flush(GSFlushReason::UPLOADDIRTYFRAME);
}
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
if (zbuf_required)
{
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
Flush(GSFlushReason::UPLOADDIRTYZBUF);
}
}
}
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
@@ -1969,9 +2047,6 @@ void GSState::Move()
m_draw_transfers.push_back(new_transfer);
}
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
auto genericCopy = [=](const GSOffset& dpo, const GSOffset& spo, auto&& getPAHelper, auto&& pxCopyFn)
{
int _sy = sy, _dy = dy; // Faster with local copied variables, compiler optimizations are dumb
@@ -2116,6 +2191,8 @@ void GSState::Move()
(m_mem.*dpsm.wpa)(doff, (m_mem.*spsm.rpa)(soff));
});
}
m_env.TRXDIR.XDIR = 3;
}
void GSState::SoftReset(u32 mask)
@@ -2139,8 +2216,6 @@ void GSState::SoftReset(u32 mask)
void GSState::ReadFIFO(u8* mem, int size)
{
Flush(GSFlushReason::DOWNLOADFIFO);
size *= 16;
Read(mem, size);
@@ -2845,13 +2920,14 @@ __forceinline bool GSState::IsAutoFlushDraw(u32 prim)
const u32 frame_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
const bool frame_hit = m_context->FRAME.Block() == m_context->TEX0.TBP0 && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
const bool z_needed = !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && z_needed;
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
const u32 frame_z_bp = frame_hit ? m_context->FRAME.Block() : m_context->ZBUF.Block();
if ((frame_hit || zbuf_hit) && GSUtil::HasSharedBits(frame_z_bp, frame_z_psm, m_context->TEX0.TBP0, m_context->TEX0.PSM))
return true;
return false;
}
@@ -2948,10 +3024,6 @@ __forceinline void GSState::HandleAutoFlush()
break;
}
const int page_mask_x = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x - 1);
const int page_mask_y = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y - 1);
const GSVector4i page_mask = { page_mask_x, page_mask_y, page_mask_x, page_mask_y };
GSVector4i tex_coord;
// Prepare the currently processed vertex.
if (PRIM->FST)
@@ -2970,6 +3042,8 @@ __forceinline void GSState::HandleAutoFlush()
GSVector4i tex_rect = tex_coord.xyxy();
const GSLocalMemory::psm_t tex_psm = GSLocalMemory::m_psm[m_context->TEX0.PSM];
const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
// Get the rest of the rect.
for (int i = 0; i < (n - 1); i++)
{
@@ -2995,6 +3069,12 @@ __forceinline void GSState::HandleAutoFlush()
tex_rect.w = std::max(tex_rect.w, tex_coord.y);
}
// If the draw was 1 line thick, make it larger as rects are exclusive of ends.
if (tex_rect.x == tex_rect.z)
tex_rect += GSVector4i::cxpr(0, 0, 1, 0);
if (tex_rect.y == tex_rect.w)
tex_rect += GSVector4i::cxpr(0, 0, 0, 1);
// Get the last texture position from the last draw.
const GSVertex* v = &m_vertex.buff[m_index.buff[m_index.tail - (index_swap ? n : 1)]];
@@ -3012,27 +3092,75 @@ __forceinline void GSState::HandleAutoFlush()
tex_coord.y = (int)((1 << m_context->TEX0.TH) * t);
}
const GSVector4i last_tex_page = tex_coord.xyxy() & page_mask;
const GSVector4i tex_page = tex_rect.xyxy() & page_mask;
const int clamp_minu = m_context->CLAMP.MINU;
const int clamp_maxu = m_context->CLAMP.MAXU;
const int clamp_minv = m_context->CLAMP.MINV;
const int clamp_maxv = m_context->CLAMP.MAXV;
switch (m_context->CLAMP.WMS)
{
case CLAMP_REGION_CLAMP:
tex_rect.x = std::max(tex_rect.x, clamp_minu);
tex_rect.z = std::max(tex_rect.z, clamp_minu);
tex_coord.x = std::max(tex_coord.x, clamp_minu);
tex_rect.x = std::min(tex_rect.x, clamp_maxu);
tex_rect.z = std::min(tex_rect.z, clamp_maxu);
tex_coord.x = std::min(tex_coord.x, clamp_maxu);
break;
case CLAMP_REGION_REPEAT:
tex_rect.x = std::max(tex_rect.x, clamp_maxu);
tex_rect.z = std::max(tex_rect.z, clamp_maxu);
tex_coord.x = std::max(tex_coord.x, clamp_maxu);
tex_rect.x = std::min(tex_rect.x, (clamp_maxu | clamp_minu));
tex_rect.z = std::min(tex_rect.z, (clamp_maxu | clamp_minu));
tex_coord.x = std::min(tex_coord.x, (clamp_maxu | clamp_minu));
break;
default:
break;
}
switch (m_context->CLAMP.WMT)
{
case CLAMP_REGION_CLAMP:
tex_rect.y = std::max(tex_rect.y, clamp_minv);
tex_rect.w = std::max(tex_rect.w, clamp_minv);
tex_coord.y = std::max(tex_coord.y, clamp_minv);
tex_rect.y = std::min(tex_rect.y, clamp_maxv);
tex_rect.w = std::min(tex_rect.w, clamp_maxv);
tex_coord.y = std::min(tex_coord.y, clamp_maxv);
break;
case CLAMP_REGION_REPEAT:
tex_rect.y = std::max(tex_rect.y, clamp_maxv);
tex_rect.w = std::max(tex_rect.w, clamp_maxv);
tex_coord.y = std::max(tex_coord.y, clamp_maxv);
tex_rect.y = std::min(tex_rect.y, (clamp_maxv | clamp_minv));
tex_rect.w = std::min(tex_rect.w, (clamp_maxv | clamp_minv));
tex_coord.y = std::min(tex_coord.y, (clamp_maxv | clamp_minv));
break;
default:
break;
}
// Nothing being drawn intersect with the new texture, so no point in checking further.
if (tex_psm.depth == frame_psm.depth && tex_rect.rintersect(temp_draw_rect).rempty())
return;
const int tex_page_mask_x = ~(tex_psm.pgs.x - 1);
const int tex_page_mask_y = ~(tex_psm.pgs.y - 1);
const GSVector4i tex_page_mask = { tex_page_mask_x, tex_page_mask_y, tex_page_mask_x, tex_page_mask_y };
const GSVector4i last_tex_page = tex_coord.xyxy() & tex_page_mask;
const GSVector4i tex_page = tex_rect.xyxy() & tex_page_mask;
// Crossed page since last draw end
if (!tex_page.eq(last_tex_page) || m_texflush_flag)
{
const u32 frame_mask = GSLocalMemory::m_psm[m_context->TEX0.PSM].fmsk;
const bool frame_hit = (m_context->FRAME.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
// Make sure the format matches, otherwise the coordinates aren't gonna match, so the draws won't intersect.
if (GSUtil::HasCompatibleBits(m_context->TEX0.PSM, frame_z_psm) && (m_context->FRAME.FBW == m_context->TEX0.TBW))
if (tex_psm.bpp == frame_psm.bpp && (m_context->FRAME.FBW == m_context->TEX0.TBW))
{
// If the draw was 1 line thick, make it larger as rects are exclusive of ends.
if (tex_rect.x == tex_rect.z)
tex_rect += GSVector4i(0, 0, 1, 0);
if (tex_rect.y == tex_rect.w)
tex_rect += GSVector4i(0, 0, 0, 1);
const GSVector2i offset = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
const GSVector4i scissor = m_context->scissor.in;
GSVector4i old_tex_rect = GSVector4i::zero();
GSVector4i old_draw_rect = GSVector4i::zero();
int current_draw_end = m_index.tail;
while (current_draw_end >= n)
@@ -3041,29 +3169,49 @@ __forceinline void GSState::HandleAutoFlush()
{
const GSVertex* v = &m_vertex.buff[m_index.buff[i]];
tex_coord.x = (static_cast<int>(v->XYZ.X) - offset.x) >> 4;
tex_coord.y = (static_cast<int>(v->XYZ.Y) - offset.y) >> 4;
if (i == (current_draw_end - 1))
if (prim == GS_SPRITE && (i & 1))
{
old_tex_rect = tex_coord.xyxy();
tex_coord.x = ((static_cast<int>(v->XYZ.X) - offset.x) >> 4) - 1;
tex_coord.y = ((static_cast<int>(v->XYZ.Y) - offset.y) >> 4) - 1;
}
else
{
old_tex_rect.x = std::min(old_tex_rect.x, tex_coord.x);
old_tex_rect.z = std::max(old_tex_rect.z, tex_coord.x);
old_tex_rect.y = std::min(old_tex_rect.y, tex_coord.y);
old_tex_rect.w = std::max(old_tex_rect.w, tex_coord.y);
tex_coord.x = (static_cast<int>(v->XYZ.X) - offset.x) >> 4;
tex_coord.y = (static_cast<int>(v->XYZ.Y) - offset.y) >> 4;
}
if (tex_psm.depth != frame_psm.depth)
{
tex_coord.x ^= (frame_psm.pgs.x / 2);
tex_coord.y ^= (frame_psm.pgs.y / 2);
}
if (prim == GS_SPRITE && (i & 1))
{
tex_coord.x += 1;
tex_coord.y += 1;
}
if (i == (current_draw_end - 1))
{
old_draw_rect = tex_coord.xyxy();
}
else
{
old_draw_rect.x = std::min(old_draw_rect.x, tex_coord.x);
old_draw_rect.z = std::max(old_draw_rect.z, tex_coord.x);
old_draw_rect.y = std::min(old_draw_rect.y, tex_coord.y);
old_draw_rect.w = std::max(old_draw_rect.w, tex_coord.y);
}
}
if (old_tex_rect.x == old_tex_rect.z)
old_tex_rect += GSVector4i(0, 0, 1, 0);
if (old_tex_rect.y == old_tex_rect.w)
old_tex_rect += GSVector4i(0, 0, 0, 1);
if (old_draw_rect.x == old_draw_rect.z)
old_draw_rect += GSVector4i::cxpr(0, 0, 1, 0);
if (old_draw_rect.y == old_draw_rect.w)
old_draw_rect += GSVector4i::cxpr(0, 0, 0, 1);
old_tex_rect = tex_rect.rintersect(old_tex_rect);
if (!old_tex_rect.rintersect(scissor).rempty())
old_draw_rect = tex_rect.rintersect(old_draw_rect);
if (!old_draw_rect.rintersect(scissor).rempty())
{
Flush(GSFlushReason::AUTOFLUSH);
return;
@@ -3074,26 +3222,27 @@ __forceinline void GSState::HandleAutoFlush()
}
else // Storage of the TEX and FRAME/Z is different, so uhh, just fall back to flushing each page. It's slower, sorry.
{
if (m_context->FRAME.FBW == m_context->TEX0.TBW)
const int frame_width = (m_context->FRAME.FBW * 64) / frame_psm.pgs.x;
const int tex_width = (m_context->TEX0.TBW * 64) / tex_psm.pgs.x;
if ((frame_width == tex_width) || ((tex_rect.w / tex_psm.pgs.y) <= 1 && frame_width >= tex_width))
{
tex_rect += GSVector4i(0, 0, tex_page_mask.z, tex_page_mask.w); // round up to the next page as we will be comparing by page.
//We know we've changed page, so let's set the dimension to cover the page they're in (for different pixel orders)
tex_rect = tex_rect & page_mask;
tex_rect += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect
tex_rect.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x;
tex_rect.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y;
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(m_context->scissor.in);
area_out = area_out & page_mask;
area_out += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect
area_out.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x;
area_out.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y;
tex_rect &= tex_page_mask;
tex_rect = GSVector4i(tex_rect.x / tex_psm.pgs.x, tex_rect.y / tex_psm.pgs.y, tex_rect.z / tex_psm.pgs.x, tex_rect.w / tex_psm.pgs.y);
const int frame_page_mask_x = ~(frame_psm.pgs.x - 1);
const int frame_page_mask_y = ~(frame_psm.pgs.y - 1);
const GSVector4i frame_page_mask = { frame_page_mask_x, frame_page_mask_y, frame_page_mask_x, frame_page_mask_y };
GSVector4i area_out = temp_draw_rect;
area_out += GSVector4i(0, 0, frame_page_mask.z, frame_page_mask.w); // round up to the next page as we will be comparing by page.
area_out &= frame_page_mask;
area_out = GSVector4i(area_out.x / frame_psm.pgs.x, area_out.y / frame_psm.pgs.y, area_out.z / frame_psm.pgs.x, area_out.w / frame_psm.pgs.y);
if (!area_out.rintersect(tex_rect).rempty())
Flush(GSFlushReason::AUTOFLUSH);
}
else // Page width is different, so it's much more difficult to calculate where it's modifying.
else // Formats are too different so just flush it.
Flush(GSFlushReason::AUTOFLUSH);
}
}
@@ -3368,6 +3517,11 @@ __forceinline void GSState::VertexKick(u32 skip)
case GS_SPRITE:
buff[0] = static_cast<u16>(head + 0);
buff[1] = static_cast<u16>(head + 1);
// Update the first vert's Q for ease of doing Autoflush
if (!m_env.PRIM.FST)
m_vertex.buff[buff[0]].RGBAQ.Q = m_vertex.buff[buff[1]].RGBAQ.Q;
m_vertex.head = head + 2;
m_vertex.next = head + 2;
m_index.tail += 2;
+2
View File
@@ -393,6 +393,8 @@ public:
virtual void Move();
GSVector4i GetTEX0Rect();
void CheckWriteOverlap(bool req_write, bool req_read);
void Write(const u8* mem, int len);
void Read(u8* mem, int len);
void InitReadFIFO(u8* mem, int len);
+1
View File
@@ -316,6 +316,7 @@ struct alignas(16) GSHWDrawConfig
u32 ltf : 1;
// Shuffle and fbmask effect
u32 shuffle : 1;
u32 shuffle_same : 1;
u32 real16src: 1;
u32 read_ba : 1;
u32 write_rg : 1;
+1
View File
@@ -40,6 +40,7 @@ protected:
GSVector2i m_real_size{0, 0};
bool m_texture_shuffle = false;
bool m_copy_16bit_to_target_shuffle = false;
bool m_same_group_texture_shuffle = false;
virtual GSTexture* GetOutput(int i, float& scale, int& y_offset) = 0;
virtual GSTexture* GetFeedbackOutput(float& scale) { return nullptr; }
+3
View File
@@ -1776,6 +1776,9 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vert
{
// sfex3 (after the capcom logo), vf4 (first menu fading in), ffxii shadows, rumble roses shadows, persona4 shadows
CommitClear(rt);
CommitClear(ds);
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(ds), D3D11_CLEAR_STENCIL, 0.0f, 0);
// om
@@ -138,6 +138,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_REGION_RECT", sel.region_rect);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_SHUFFLE_SAME", sel.shuffle_same);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
+71
View File
@@ -36,6 +36,13 @@
#include <sstream>
#include <limits>
#ifdef ENABLE_OGL_DEBUG
#define USE_PIX
#include "WinPixEventRuntime/pix3.h"
static u32 s_debug_scope_depth = 0;
#endif
static bool IsDATMConvertShader(ShaderConvert i)
{
return (i == ShaderConvert::DATM_0 || i == ShaderConvert::DATM_1);
@@ -1108,16 +1115,79 @@ void GSDevice12::EndPresent()
InvalidateCachedState();
}
#ifdef ENABLE_OGL_DEBUG
static UINT Palette(float phase, const std::array<float, 3>& a, const std::array<float, 3>& b,
const std::array<float, 3>& c, const std::array<float, 3>& d)
{
std::array<float, 3> result;
result[0] = a[0] + b[0] * std::cos(6.28318f * (c[0] * phase + d[0]));
result[1] = a[1] + b[1] * std::cos(6.28318f * (c[1] * phase + d[1]));
result[2] = a[2] + b[2] * std::cos(6.28318f * (c[2] * phase + d[2]));
return PIX_COLOR(static_cast<BYTE>(result[0] * 255.0f),
static_cast<BYTE>(result[1] * 255.0f),
static_cast<BYTE>(result[2] * 255.0f));
}
#endif
void GSDevice12::PushDebugGroup(const char* fmt, ...)
{
#ifdef ENABLE_OGL_DEBUG
if (!GSConfig.UseDebugDevice)
return;
std::va_list ap;
va_start(ap, fmt);
const std::string buf(StringUtil::StdStringFromFormatV(fmt, ap));
va_end(ap);
const UINT color = Palette(
++s_debug_scope_depth, {0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 0.5f}, {0.8f, 0.90f, 0.30f});
PIXBeginEvent(GetCommandList(), color, "%s", buf.c_str());
#endif
}
void GSDevice12::PopDebugGroup()
{
#ifdef ENABLE_OGL_DEBUG
if (!GSConfig.UseDebugDevice)
return;
s_debug_scope_depth = (s_debug_scope_depth == 0) ? 0 : (s_debug_scope_depth - 1u);
PIXEndEvent(GetCommandList());
#endif
}
void GSDevice12::InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...)
{
#ifdef ENABLE_OGL_DEBUG
if (!GSConfig.UseDebugDevice)
return;
std::va_list ap;
va_start(ap, fmt);
const std::string buf(StringUtil::StdStringFromFormatV(fmt, ap));
va_end(ap);
if (buf.empty())
return;
static constexpr float colors[][3] = {
{0.1f, 0.1f, 0.0f}, // Cache
{0.1f, 0.1f, 0.0f}, // Reg
{0.5f, 0.0f, 0.5f}, // Debug
{0.0f, 0.5f, 0.5f}, // Message
{0.0f, 0.2f, 0.0f} // Performance
};
const float* fcolor = colors[static_cast<int>(category)];
const UINT color = PIX_COLOR(static_cast<BYTE>(fcolor[0] * 255.0f),
static_cast<BYTE>(fcolor[1] * 255.0f),
static_cast<BYTE>(fcolor[2] * 255.0f));
PIXSetMarker(GetCommandList(), color, "%s", buf.c_str());
#endif
}
bool GSDevice12::CheckFeatures()
@@ -2743,6 +2813,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_REGION_RECT", sel.region_rect);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_SHUFFLE_SAME", sel.shuffle_same);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
+43 -48
View File
@@ -523,19 +523,6 @@ bool GSHwHack::GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_GodHand(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (RTME && (RFBP == 0x0) && (RTBP0 == 0x2800) && RFPSM == RTPSM && RTPSM == PSMCT32)
{
skip = 1; // Blur
}
}
return true;
}
bool GSHwHack::GSC_KnightsOfTheTemple2(GSRendererHW& r, int& skip)
{
if (skip == 0)
@@ -864,6 +851,44 @@ bool GSHwHack::GSC_MetalGearSolid3(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_BigMuthaTruckers(GSRendererHW& r, int& skip)
{
// Rendering pattern:
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
// game needs to do a depth effect (so green channel to alpha),
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
// vertical resolution is half so only half is processed at once
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
const GIFRegTEX0& Texture = RTEX0;
GIFRegTEX0 Frame = {};
Frame.TBW = RFRAME.FBW;
Frame.TBP0 = RFRAME.Block();
const int frame_offset_pal = GSLocalMemory::GetEndBlockAddress(0xa00, 10, PSMCT32, GSVector4i(0, 0, 640, 256)) + 1;
const int frame_offset_ntsc = GSLocalMemory::GetEndBlockAddress(0xa00, 10, PSMCT32, GSVector4i(0, 0, 640, 224)) + 1;
const GSVector4i rect = GSVector4i(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y, r.m_vt.m_max.p.x, r.m_vt.m_max.p.y);
if (RPRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Texture.PSM == PSMCT16 && ((rect.w == 512 && Frame.TBP0 == frame_offset_pal) || (Frame.TBP0 == frame_offset_ntsc && rect.w == 448)))
{
// 224 ntsc, 256 pal.
GL_INS("GSC_BigMuthaTruckers half bottom offset %d", r.m_context->XYOFFSET.OFX >> 4);
const size_t count = r.m_vertex.next;
GSVertex* v = &r.m_vertex.buff[0];
const u16 offset = (u16)rect.w * 16;
for (size_t i = 0; i < count; i++)
v[i].XYZ.Y += offset;
r.m_vt.m_min.p.y += rect.w;
r.m_vt.m_max.p.y += rect.w;
r.m_cached_ctx.FRAME.FBP = 0x50; // 0xA00 >> 5
}
return true;
}
bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
const u32 n_vertices = r.m_vertex.next;
@@ -917,37 +942,6 @@ bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds
return true;
}
bool GSHwHack::OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
// Rendering pattern:
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
// game needs to do a depth effect (so green channel to alpha),
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
// vertical resolution is half so only half is processed at once
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
const GIFRegTEX0& Texture = RTEX0;
GIFRegTEX0 Frame = {};
Frame.TBW = RFRAME.FBW;
Frame.TBP0 = RFRAME.Block();
if (RPRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Frame.TBP0 == 0x00a00 && Texture.PSM == PSMT8H && (r.m_r.y == 256 || r.m_r.y == 224))
{
// 224 ntsc, 256 pal.
GL_INS("OI_BigMuthaTruckers half bottom offset");
const size_t count = r.m_vertex.next;
GSVertex* v = &r.m_vertex.buff[0];
const u16 offset = (u16)r.m_r.y * 16;
for (size_t i = 0; i < count; i++)
v[i].V += offset;
}
return true;
}
bool GSHwHack::OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
if (t && t->m_from_target) // Avoid slow framebuffer readback
@@ -1065,9 +1059,11 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
if (rt_again->m_unscaled_size.x < src->m_unscaled_size.x || rt_again->m_unscaled_size.y < src->m_unscaled_size.y)
{
rt_again->ResizeTexture(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
std::max(rt_again->m_unscaled_size.y, src->m_unscaled_size.y));
GSVector2i new_size = GSVector2i(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
std::max(rt_again->m_unscaled_size.y, src->m_unscaled_size.y));
rt_again->ResizeTexture(new_size.x, new_size.y);
rt = rt_again->m_texture;
rt_size = new_size;
}
}
@@ -1438,7 +1434,6 @@ bool GSHwHack::MV_Ico(GSRendererHW& r)
#define CRC_F(name) { #name, &GSHwHack::name }
const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_functions[] = {
CRC_F(GSC_GodHand),
CRC_F(GSC_KnightsOfTheTemple2),
CRC_F(GSC_Kunoichi),
CRC_F(GSC_Manhunt2),
@@ -1471,6 +1466,7 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
// Texture shuffle
CRC_F(GSC_DeathByDegreesTekkenNinaWilliams), // + Upscaling issues
CRC_F(GSC_BigMuthaTruckers),
// Upscaling hacks
CRC_F(GSC_UltramanFightingEvolution),
@@ -1481,7 +1477,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_before_draw_functions[] = {
CRC_F(OI_PointListPalette),
CRC_F(OI_BigMuthaTruckers),
CRC_F(OI_DBZBTGames),
CRC_F(OI_FFX),
CRC_F(OI_RozenMaidenGebetGarden),
+1 -2
View File
@@ -32,7 +32,6 @@ public:
static bool GSC_Kunoichi(GSRendererHW& r, int& skip);
static bool GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, int& skip);
static bool GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, int& skip);
static bool GSC_GodHand(GSRendererHW& r, int& skip);
static bool GSC_KnightsOfTheTemple2(GSRendererHW& r, int& skip);
static bool GSC_UltramanFightingEvolution(GSRendererHW& r, int& skip);
static bool GSC_TalesofSymphonia(GSRendererHW& r, int& skip);
@@ -45,9 +44,9 @@ public:
static bool GSC_NFSUndercover(GSRendererHW& r, int& skip);
static bool GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip);
static bool GSC_MetalGearSolid3(GSRendererHW& r, int& skip);
static bool GSC_BigMuthaTruckers(GSRendererHW& r, int& skip);
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_FFX(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
+215 -129
View File
@@ -337,7 +337,7 @@ void GSRendererHW::ExpandLineIndices()
}
// Fix the vertex position/tex_coordinate from 16 bits color to 32 bits color
void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba, GSTextureCache::Target* rt, GSTextureCache::Source* tex)
{
const u32 count = m_vertex.next;
GSVertex* v = &m_vertex.buff[0];
@@ -351,7 +351,8 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
const float tw = static_cast<float>(1u << m_cached_ctx.TEX0.TW);
int tex_pos = (PRIM->FST) ? first_vert.U : static_cast<int>(tw * first_vert.ST.S);
tex_pos &= 0xFF;
read_ba = (tex_pos > 112 && tex_pos < 144);
// "same group" means it can read blue and write alpha using C32 tricks
read_ba = (tex_pos > 112 && tex_pos < 144) || (m_same_group_texture_shuffle && (m_cached_ctx.FRAME.FBMSK & 0xFFFF0000) != 0xFFFF0000);
// Another way of selecting whether to read RG/BA is to use region repeat.
// Ace Combat 04 reads RG, writes to RGBA by setting a MINU of 1015.
@@ -406,60 +407,51 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
return;
}
bool half_bottom = false;
switch (GSConfig.UserHacks_HalfBottomOverride)
bool half_bottom_vert = true;
bool half_right_vert = true;
bool half_bottom_uv = true;
bool half_right_uv = true;
if (m_same_group_texture_shuffle)
{
case 0:
// Force Disabled.
// Force Disabled will help games such as Xenosaga.
// Xenosaga handles the half bottom as an vertex offset instead of a buffer offset which does the effect twice.
// Half bottom won't trigger a cache miss that skip the draw because it is still the normal buffer but with a vertices offset.
half_bottom = false;
break;
case 1:
// Force Enabled.
// Force Enabled will help games such as Superman Shadows of Apokolips, The Lord of the Rings: The Two Towers,
// Demon Stone, Midnight Club 3.
half_bottom = true;
break;
case -1:
default:
// Default, Automatic.
// Here's the idea
// TS effect is 16 bits but we emulate it on a 32 bits format
// Normally this means we need to divide size by 2.
//
// Some games do two TS effects on each half of the buffer.
// This makes a mess for us in the TC because we end up with two targets
// when we only want one, thus half screen bug.
//
// 32bits emulation means we can do the effect once but double the size.
// Test cases: Crash Twinsantiy and DBZ BT3
// Test Case: NFS: HP2 splits the effect h:256 and h:192 so 64
// Other games: Midnight Club 3 headlights, black bar in Xenosaga 3 dialogue,
// Firefighter FD18 fire occlusion, PSI Ops half screen green overlay, Lord of the Rings - Two Towers,
// Demon Stone , Sonic Unleashed, Lord of the Rings Two Towers,
// Superman Shadow of Apokolips, Matrix Path of Neo, Big Mutha Truckers
int maxvert = 0;
int minvert = 4096;
for (u32 i = 0; i < count; i++)
if (m_cached_ctx.FRAME.FBW != rt->m_TEX0.TBW && m_cached_ctx.FRAME.FBW == rt->m_TEX0.TBW * 2)
half_right_vert = false;
else
half_bottom_vert = false;
}
else
{
// Different source (maybe?)
// If a game does the texture and frame doubling differently, they can burn in hell.
if (m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block())
{
// No super source of truth here, since the width can get batted around, the valid is probably our best bet.
int tex_width = tex->m_target ? tex->m_from_target->m_valid.z : (tex->m_TEX0.TBW * 64);
int tex_tbw = tex->m_target ? tex->m_from_target_TEX0.TBW : tex->m_TEX0.TBW;
if (static_cast<int>(m_cached_ctx.TEX0.TBW * 64) >= std::min(tex_width * 2, 1024) && tex_tbw != m_cached_ctx.TEX0.TBW || (m_cached_ctx.TEX0.TBW * 64) < floor(m_vt.m_max.t.x))
{
int YCord = 0;
if (!PRIM->FST)
YCord = static_cast<int>((1 << m_cached_ctx.TEX0.TH) * (v[i].ST.T / v[i].RGBAQ.Q));
else
YCord = (v[i].V >> 4);
if (maxvert < YCord)
maxvert = YCord;
if (minvert > YCord)
minvert = YCord;
half_right_uv = false;
half_right_vert = false;
}
half_bottom = minvert == 0 && m_r.height() <= maxvert;
break;
else
{
half_bottom_uv = false;
half_bottom_vert = false;
}
}
else
{
if ((floor(m_vt.m_max.p.y) <= rt->m_valid.w) && ((floor(m_vt.m_max.p.x) > (m_cached_ctx.FRAME.FBW * 64)) || (rt->m_TEX0.TBW != m_cached_ctx.FRAME.FBW)))
{
half_right_vert = false;
half_right_uv = false;
}
else
{
half_bottom_vert = false;
half_bottom_uv = false;
}
}
}
if (PRIM->FST)
@@ -469,16 +461,16 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
for (u32 i = 0; i < count; i += 2)
{
if (write_ba)
v[i].XYZ.X -= 128u;
v[i].XYZ.X -= 128u;
else
v[i+1].XYZ.X += 128u;
v[i + 1].XYZ.X += 128u;
if (read_ba)
v[i].U -= 128u;
v[i].U -= 128u;
else
v[i+1].U += 128u;
v[i + 1].U += 128u;
if (!half_bottom)
if (!half_bottom_vert)
{
// Height is too big (2x).
const int tex_offset = v[i].V & 0xF;
@@ -488,9 +480,13 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
tmp = GSVector4i(tmp - offset).srl32(1) + offset;
v[i].XYZ.Y = static_cast<u16>(tmp.x);
v[i].V = static_cast<u16>(tmp.y);
v[i + 1].XYZ.Y = static_cast<u16>(tmp.z);
v[i + 1].V = static_cast<u16>(tmp.w);
if (!half_bottom_uv)
{
v[i].V = static_cast<u16>(tmp.y);
v[i + 1].V = static_cast<u16>(tmp.w);
}
}
}
}
@@ -502,16 +498,16 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
for (u32 i = 0; i < count; i += 2)
{
if (write_ba)
v[i].XYZ.X -= 128u;
v[i].XYZ.X -= 128u;
else
v[i+1].XYZ.X += 128u;
v[i + 1].XYZ.X += 128u;
if (read_ba)
v[i].ST.S -= offset_8pix;
v[i].ST.S -= offset_8pix;
else
v[i+1].ST.S += offset_8pix;
v[i + 1].ST.S += offset_8pix;
if (!half_bottom)
if (!half_bottom_vert)
{
// Height is too big (2x).
const GSVector4i offset(o.OFY, o.OFY);
@@ -521,9 +517,13 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
//fprintf(stderr, "Before %d, After %d\n", v[i + 1].XYZ.Y, tmp.y);
v[i].XYZ.Y = static_cast<u16>(tmp.x);
v[i].ST.T /= 2.0f;
v[i + 1].XYZ.Y = static_cast<u16>(tmp.y);
v[i + 1].ST.T /= 2.0f;
if (!half_bottom_uv)
{
v[i].ST.T /= 2.0f;
v[i + 1].ST.T /= 2.0f;
}
}
}
}
@@ -534,21 +534,41 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
else
m_vt.m_max.p.x += 8.0f;
if (!half_bottom)
if (!m_same_group_texture_shuffle)
{
const float delta_Y = m_vt.m_max.p.y - m_vt.m_min.p.y;
m_vt.m_max.p.y -= delta_Y / 2.0f;
if (read_ba)
m_vt.m_min.t.x -= 8.0f;
else
m_vt.m_max.t.x += 8.0f;
}
if (read_ba)
m_vt.m_min.t.x -= 8.0f;
else
m_vt.m_max.t.x += 8.0f;
if (!half_bottom)
if (!half_right_vert)
{
const float delta_T = m_vt.m_max.t.y - m_vt.m_min.t.y;
m_vt.m_max.t.y -= delta_T / 2.0f;
m_vt.m_min.p.x /= 2.0f;
m_vt.m_max.p.x /= 2.0f;
m_context->scissor.in.x = m_vt.m_min.p.x;
m_context->scissor.in.z = m_vt.m_max.p.x + 8.0f;
}
if (!half_bottom_vert)
{
m_vt.m_min.p.y /= 2.0f;
m_vt.m_max.p.y /= 2.0f;
m_context->scissor.in.y = m_vt.m_min.p.y;
m_context->scissor.in.w = m_vt.m_max.p.y + 8.0f;
}
// Only do this is the source is being interpreted as 16bit
if (!half_bottom_uv)
{
m_vt.m_min.t.y /= 2.0f;
m_vt.m_max.t.y /= 2.0f;
}
if (!half_right_uv)
{
m_vt.m_min.t.y /= 2.0f;
m_vt.m_max.t.y /= 2.0f;
}
}
@@ -849,19 +869,22 @@ bool GSRendererHW::IsSplitTextureShuffle(u32 rt_tbw)
// If this is a split texture shuffle, the next draw's FRAME/TEX0 should line up.
// Re-add the offset we subtracted in Draw() to get the original FBP/TBP0.. this won't handle wrapping. Oh well.
// "Potential" ones are for Jak3 which does a split shuffle on a 128x128 texture with a width of 256, writing to the lower half then offsetting 2 pages.
const u32 expected_next_FBP = (m_cached_ctx.FRAME.FBP + m_split_texture_shuffle_pages) + num_pages;
const u32 potential_expected_next_FBP = m_cached_ctx.FRAME.FBP + ((m_context->FRAME.FBW * 64) / aligned_rc.width());
const u32 expected_next_TBP0 = (m_cached_ctx.TEX0.TBP0 + (m_split_texture_shuffle_pages + num_pages) * BLOCKS_PER_PAGE);
const u32 potential_expected_next_TBP0 = m_cached_ctx.TEX0.TBP0 + (BLOCKS_PER_PAGE * ((m_context->TEX0.TBW * 64) / aligned_rc.width()));
GL_CACHE("IsSplitTextureShuffle: Draw covers %ux%u pages, next FRAME %x TEX %x",
static_cast<u32>(aligned_rc.width()) / frame_psm.pgs.x, pages_high, expected_next_FBP * BLOCKS_PER_PAGE,
expected_next_TBP0);
if (next_ctx.TEX0.TBP0 != expected_next_TBP0)
if (next_ctx.TEX0.TBP0 != expected_next_TBP0 && next_ctx.TEX0.TBP0 != potential_expected_next_TBP0)
{
GL_CACHE("IsSplitTextureShuffle: Mismatch on TBP0, expecting %x, got %x", expected_next_TBP0, next_ctx.TEX0.TBP0);
return false;
}
// Some games don't offset the FBP.
if (next_ctx.FRAME.FBP != expected_next_FBP && next_ctx.FRAME.FBP != m_cached_ctx.FRAME.FBP)
if (next_ctx.FRAME.FBP != expected_next_FBP && next_ctx.FRAME.FBP != m_cached_ctx.FRAME.FBP && next_ctx.FRAME.FBP != potential_expected_next_FBP)
{
GL_CACHE("IsSplitTextureShuffle: Mismatch on FBP, expecting %x, got %x", expected_next_FBP * BLOCKS_PER_PAGE,
next_ctx.FRAME.FBP * BLOCKS_PER_PAGE);
@@ -1238,6 +1261,9 @@ void GSRendererHW::Move()
return;
}
if (m_env.TRXDIR.XDIR == 3)
return;
const int sx = m_env.TRXPOS.SSAX;
const int sy = m_env.TRXPOS.SSAY;
const int dx = m_env.TRXPOS.DSAX;
@@ -1249,6 +1275,7 @@ void GSRendererHW::Move()
if (g_texture_cache->Move(m_env.BITBLTBUF.SBP, m_env.BITBLTBUF.SBW, m_env.BITBLTBUF.SPSM, sx, sy,
m_env.BITBLTBUF.DBP, m_env.BITBLTBUF.DBW, m_env.BITBLTBUF.DPSM, dx, dy, w, h))
{
m_env.TRXDIR.XDIR = 3;
// Handled entirely in TC, no need to update local memory.
return;
}
@@ -1900,6 +1927,7 @@ void GSRendererHW::Draw()
m_texture_shuffle = false;
m_copy_16bit_to_target_shuffle = false;
m_same_group_texture_shuffle = false;
const bool is_split_texture_shuffle = (m_split_texture_shuffle_pages > 0);
if (is_split_texture_shuffle)
@@ -2163,9 +2191,30 @@ void GSRendererHW::Draw()
GL_CACHE("Estimated texture region: %u,%u -> %u,%u", MIP_CLAMP.MINU, MIP_CLAMP.MINV, MIP_CLAMP.MAXU + 1,
MIP_CLAMP.MAXV + 1);
}
const bool possible_shuffle = m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 || IsPossibleChannelShuffle();
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle) :
g_texture_cache->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr, possible_shuffle);
GIFRegTEX0 FRAME_TEX0;
bool rt_32bit = false;
if (!no_rt && m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0 && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16)
{
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
FRAME_TEX0.U64 = 0;
FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block();
FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW;
FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM;
GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(FRAME_TEX0, GSVector2i(m_vt.m_max.p.x, m_vt.m_max.p.y), GetTextureScaleFactor(), GSTextureCache::RenderTarget, true,
fm);
if (tgt)
rt_32bit = tgt->m_32_bits_fmt;
tgt = nullptr;
}
const bool possible_shuffle = ((rt_32bit && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) || m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0) || IsPossibleChannelShuffle();
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME.Block()) :
g_texture_cache->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr, possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME.Block());
if (unlikely(!src))
{
GL_INS("ERROR: Source lookup failed, skipping.");
@@ -2253,7 +2302,7 @@ void GSRendererHW::Draw()
}
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::RenderTarget, true,
fm, false, force_preload, preserve_rt_color, m_r);
fm, false, force_preload, preserve_rt_color, m_r, src);
if (unlikely(!rt))
{
GL_INS("ERROR: Failed to create FRAME target, skipping.");
@@ -2277,7 +2326,7 @@ void GSRendererHW::Draw()
if (!ds)
{
ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::DepthStencil,
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, m_r);
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, m_r, src);
if (unlikely(!ds))
{
GL_INS("ERROR: Failed to create ZBUF target, skipping.");
@@ -2290,20 +2339,33 @@ void GSRendererHW::Draw()
if (process_texture)
{
GIFRegCLAMP MIP_CLAMP = m_cached_ctx.CLAMP;
const GSVertex* v = &m_vertex.buff[0];
if (rt)
{
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
// Initially code also tested the RT but it gives too much false-positive
const int first_x = ((v[0].XYZ.X - m_context->XYOFFSET.OFX) + 8) >> 4;
const int first_u = PRIM->FST ? ((v[0].U + 8) >> 4) : static_cast<int>(((1 << m_cached_ctx.TEX0.TW) * (v[0].ST.S / v[1].RGBAQ.Q)) + 0.5f);
const bool shuffle_coords = (first_x ^ first_u) & 8;
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r) + 1;
const bool draw_uses_target = src->m_from_target && ((src->m_from_target_TEX0.TBP0 <= m_cached_ctx.FRAME.Block() &&
src->m_from_target->UnwrappedEndBlock() > m_cached_ctx.FRAME.Block()) ||
(m_cached_ctx.FRAME.Block() < src->m_from_target_TEX0.TBP0 && draw_end > src->m_from_target_TEX0.TBP0));
// copy of a 16bit source in to this target, make sure it's opaque and not bilinear to reduce false positives.
m_copy_16bit_to_target_shuffle = m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block() && rt->m_32_bits_fmt == true && IsOpaque()
&& !(context->TEX1.MMIN & 1) && !src->m_32_bits_fmt && m_cached_ctx.FRAME.FBMSK;
}
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
// Initially code also tested the RT but it gives too much false-positive
//
// Both input and output are 16 bits and texture was initially 32 bits!
m_texture_shuffle = (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) && (tex_psm.bpp == 16)
&& draw_sprite_tex && (src->m_32_bits_fmt || m_copy_16bit_to_target_shuffle);
// It's not actually possible to do a C16->C16 texture shuffle of B to A as they are the same group
// However you can do it by using C32 and offsetting the target verticies to point to B A, then mask as appropriate.
m_same_group_texture_shuffle = draw_uses_target && (m_cached_ctx.TEX0.PSM & 0xE) == PSMCT32 && (m_cached_ctx.FRAME.PSM & 0x7) == PSMCT16 && (m_vt.m_min.p.x == 8.0f);
// Both input and output are 16 bits and texture was initially 32 bits! Same for the target, Sonic Unleash makes a new target which really is 16bit.
m_texture_shuffle = ((m_same_group_texture_shuffle || (tex_psm.bpp == 16)) && (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) &&
(shuffle_coords || rt->m_32_bits_fmt))
&& draw_sprite_tex && (src->m_32_bits_fmt || m_copy_16bit_to_target_shuffle);
};
// Okami mustn't call this code
if (m_texture_shuffle && m_vertex.next < 3 && PRIM->FST && ((m_cached_ctx.FRAME.FBMSK & fm_mask) == 0))
@@ -2314,7 +2376,6 @@ void GSRendererHW::Draw()
// Shadow of Memories/Destiny shouldn't call this code.
// Causes shadow flickering.
const GSVertex* v = &m_vertex.buff[0];
m_texture_shuffle = ((v[1].U - v[0].U) < 256) ||
// Tomb Raider Angel of Darkness relies on this behavior to produce a fog effect.
// In this case, the address of the framebuffer and texture are the same.
@@ -2505,9 +2566,23 @@ void GSRendererHW::Draw()
GSTextureCache::Target* old_rt = nullptr;
GSTextureCache::Target* old_ds = nullptr;
{
GSVector2i new_size = t_size;
// We need to adjust the size if it's a texture shuffle as we could end up making the RT twice the size.
if (rt && m_texture_shuffle && m_split_texture_shuffle_pages == 0)
{
if ((new_size.x > rt->m_valid.z && m_vt.m_max.p.x == new_size.x) || (new_size.y > rt->m_valid.w && m_vt.m_max.p.y == new_size.y))
{
if (new_size.y <= rt->m_valid.w && (rt->m_TEX0.TBW != m_cached_ctx.FRAME.FBW))
new_size.x /= 2;
else
new_size.y /= 2;
}
}
// We still need to make sure the dimensions of the targets match.
const int new_w = std::max(t_size.x, std::max(rt ? rt->m_unscaled_size.x : 0, ds ? ds->m_unscaled_size.x : 0));
const int new_h = std::max(t_size.y, std::max(rt ? rt->m_unscaled_size.y : 0, ds ? ds->m_unscaled_size.y : 0));
const int new_w = std::max(new_size.x, std::max(rt ? rt->m_unscaled_size.x : 0, ds ? ds->m_unscaled_size.x : 0));
const int new_h = std::max(new_size.y, std::max(rt ? rt->m_unscaled_size.y : 0, ds ? ds->m_unscaled_size.y : 0));
if (rt)
{
const u32 old_end_block = rt->m_end_block;
@@ -2518,6 +2593,7 @@ void GSRendererHW::Draw()
pxAssert(rt->GetScale() == target_scale);
if (rt->GetUnscaledWidth() != new_w || rt->GetUnscaledHeight() != new_h)
GL_INS("Resize RT from %dx%d to %dx%d", rt->GetUnscaledWidth(), rt->GetUnscaledHeight(), new_w, new_h);
rt->ResizeTexture(new_w, new_h);
if (!m_texture_shuffle && !m_channel_shuffle)
@@ -2527,8 +2603,8 @@ void GSRendererHW::Draw()
}
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
rt->UpdateDrawn(m_r, can_update_size || m_r.w <= (resolution.y * 2));
rt->UpdateValidity(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
rt->UpdateDrawn(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
// Probably changing to double buffering, so invalidate any old target that was next to it.
// This resolves an issue where the PCRTC will find the old target in FMV's causing flashing.
// Grandia Xtreme, Onimusha Warlord.
@@ -2582,7 +2658,7 @@ void GSRendererHW::Draw()
{
const int copy_width = (old_ds->m_texture->GetWidth()) > (ds->m_texture->GetWidth()) ? (ds->m_texture->GetWidth()) : old_ds->m_texture->GetWidth();
const int copy_height = (old_ds->m_texture->GetHeight()) > (ds->m_texture->GetHeight() - old_height) ? (ds->m_texture->GetHeight() - old_height) : old_ds->m_texture->GetHeight();
GL_INS("DS double buffer copy from FBP 0x%x, %dx%d => %d,%d", old_rt->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
GL_INS("DS double buffer copy from FBP 0x%x, %dx%d => %d,%d", old_ds->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
g_gs_device->CopyRect(old_ds->m_texture, ds->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
preserve_depth = true;
@@ -2731,7 +2807,7 @@ void GSRendererHW::Draw()
{
//rt->m_valid = rt->m_valid.runion(r);
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
rt->UpdateValidity(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
g_texture_cache->InvalidateVideoMem(context->offset.fb, m_r, false);
@@ -2744,7 +2820,7 @@ void GSRendererHW::Draw()
{
//ds->m_valid = ds->m_valid.runion(r);
// Limit to 2x the vertical height of the resolution (for double buffering)
ds->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
ds->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2) && !m_texture_shuffle);
g_texture_cache->InvalidateVideoMem(context->offset.zb, m_r, false);
@@ -2992,7 +3068,7 @@ void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
}
}
void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex)
{
// Uncomment to disable texture shuffle emulation.
// m_texture_shuffle = false;
@@ -3035,7 +3111,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
bool write_ba;
bool read_ba;
ConvertSpriteTextureShuffle(write_ba, read_ba);
ConvertSpriteTextureShuffle(write_ba, read_ba, rt, tex);
// If date is enabled you need to test the green channel instead of the
// alpha channel. Only enable this code in DATE mode to reduce the number
@@ -3044,6 +3120,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
m_conf.ps.read_ba = read_ba;
m_conf.ps.real16src = m_copy_16bit_to_target_shuffle;
m_conf.ps.shuffle_same = m_same_group_texture_shuffle;
// Please bang my head against the wall!
// 1/ Reduce the frame mask to a 16 bit format
const u32 m = m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk;
@@ -3389,21 +3466,23 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass)
{
// AA1: Don't enable blending on AA1, not yet implemented on hardware mode,
// it requires coverage sample so it's safer to turn it off instead.
const bool AA1 = PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
// PABE: Check condition early as an optimization.
const bool PABE = PRIM->ABE && m_draw_env->PABE.PABE && (GetAlphaMinMax().max < 128);
// FBMASK: Color is not written, no need to do blending.
const u32 temp_fbmask = m_conf.ps.dfmt == 2 ? 0x00F8F8F8 : 0x00FFFFFF;
const bool FBMASK = (m_cached_ctx.FRAME.FBMSK & temp_fbmask) == temp_fbmask;
// No blending or coverage anti-aliasing so early exit
if (FBMASK || PABE || !(PRIM->ABE || AA1))
{
m_conf.blend = {};
m_conf.ps.no_color1 = true;
return;
// AA1: Don't enable blending on AA1, not yet implemented on hardware mode,
// it requires coverage sample so it's safer to turn it off instead.
const bool AA1 = PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
// PABE: Check condition early as an optimization.
const bool PABE = PRIM->ABE && m_draw_env->PABE.PABE && (GetAlphaMinMax().max < 128);
// FBMASK: Color is not written, no need to do blending.
const u32 temp_fbmask = m_conf.ps.dfmt == 2 ? 0x00F8F8F8 : 0x00FFFFFF;
const bool FBMASK = (m_cached_ctx.FRAME.FBMSK & temp_fbmask) == temp_fbmask;
// No blending or coverage anti-aliasing so early exit
if (FBMASK || PABE || !(PRIM->ABE || AA1))
{
m_conf.blend = {};
m_conf.ps.no_color1 = true;
return;
}
}
// Compute the blending equation to detect special case
@@ -3433,11 +3512,20 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
m_conf.ps.fixed_one_a = 1;
m_conf.ps.blend_c = 0;
}
// 24 bits doesn't have an alpha channel so use 128 (1.0f) fix factor as equivalent.
else if (m_conf.ps.dfmt == 1 && m_conf.ps.blend_c == 1)
else if (m_conf.ps.blend_c == 1)
{
AFIX = 128;
m_conf.ps.blend_c = 2;
// When both rt alpha min and max are equal replace Ad with Af, easier to manage.
if (rt_alpha_min == rt_alpha_max)
{
AFIX = rt_alpha_min;
m_conf.ps.blend_c = 2;
}
// 24 bits doesn't have an alpha channel so use 128 (1.0f) fix factor as equivalent.
else if (m_conf.ps.dfmt == 1)
{
AFIX = 128;
m_conf.ps.blend_c = 2;
}
}
// Get alpha value
@@ -3445,12 +3533,10 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
const bool alpha_c0_one = (m_conf.ps.blend_c == 0 && (GetAlphaMinMax().min == 128) && (GetAlphaMinMax().max == 128));
const bool alpha_c0_high_min_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().min > 128);
const bool alpha_c0_high_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max > 128);
const bool alpha_c1_zero = (m_conf.ps.blend_c == 1 && rt_alpha_min == 0 && rt_alpha_max == 0);
const bool alpha_c1_one = (m_conf.ps.blend_c == 1 && rt_alpha_min == 128 && rt_alpha_max == 128);
const bool alpha_c2_zero = (m_conf.ps.blend_c == 2 && AFIX == 0u);
const bool alpha_c2_one = (m_conf.ps.blend_c == 2 && AFIX == 128u);
const bool alpha_c2_high_one = (m_conf.ps.blend_c == 2 && AFIX > 128u);
const bool alpha_one = alpha_c0_one || alpha_c1_one || alpha_c2_one;
const bool alpha_one = alpha_c0_one || alpha_c2_one;
// Optimize blending equations, must be done before index calculation
if ((m_conf.ps.blend_a == m_conf.ps.blend_b) || ((m_conf.ps.blend_b == m_conf.ps.blend_d) && alpha_one))
@@ -3469,7 +3555,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
m_conf.ps.blend_b = 0;
m_conf.ps.blend_c = 0;
}
else if (alpha_c0_zero || alpha_c1_zero || alpha_c2_zero)
else if (alpha_c0_zero || alpha_c2_zero)
{
// C == 0.0f
// (A - B) * C, result will be 0.0f so set A B to Cs
@@ -3940,7 +4026,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
{
m_conf.ps.blend_hw = 1;
}
else if (blend_flag & (BLEND_HW_CLR2))
else if (blend_flag & BLEND_HW_CLR2)
{
if (m_conf.ps.blend_c == 2)
m_conf.cb_ps.TA_MaxDepth_Af.a = static_cast<float>(AFIX) / 128.0f;
@@ -4097,7 +4183,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
// Force a 32 bits access (normally shuffle is done on 16 bits)
// m_ps_sel.tex_fmt = 0; // removed as an optimization
m_conf.ps.aem = TEXA.AEM;
ASSERT(tex->m_target);
//ASSERT(tex->m_target);
// Require a float conversion if the texure is a depth otherwise uses Integral scaling
if (psm.depth)
@@ -4665,7 +4751,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_prim_overlap = PrimitiveOverlap();
EmulateTextureShuffleAndFbmask(rt);
EmulateTextureShuffleAndFbmask(rt, tex);
const GSDevice::FeatureSupport features = g_gs_device->Features();
@@ -5514,7 +5600,7 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
const bool clear_depth = (m_cached_ctx.FRAME.FBP > m_cached_ctx.ZBUF.ZBP);
const u32 base = clear_depth ? m_cached_ctx.ZBUF.ZBP : m_cached_ctx.FRAME.FBP;
const u32 half = clear_depth ? m_cached_ctx.FRAME.FBP : m_cached_ctx.ZBUF.ZBP;
const bool enough_bits = clear_depth ? (frame_psm.trbpp >= zbuf_psm.trbpp) : (zbuf_psm.trbpp >= frame_psm.trbpp);
// Size of the current draw
const u32 w_pages = (m_r.z + (frame_psm.pgs.x - 1)) / frame_psm.pgs.x;
const u32 h_pages = (m_r.w + (frame_psm.pgs.y - 1)) / frame_psm.pgs.y;
@@ -5528,7 +5614,7 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
// GTA: LCS does this setup, along with a few other games. Thankfully if it's a zero clear, we'll clear both
// separately, and the end result is the same because it gets invalidated. That's better than falsely detecting
// double half clears, and ending up with 1024 high render targets which really shouldn't be.
if ((frame_psm.fmt != zbuf_psm.fmt && m_cached_ctx.FRAME.FBMSK != ((zbuf_psm.fmt == 1) ? 0xFF000000u : 0)) ||
if ((!enough_bits && frame_psm.fmt != zbuf_psm.fmt && m_cached_ctx.FRAME.FBMSK != ((zbuf_psm.fmt == 1) ? 0xFF000000u : 0)) ||
!GSUtil::HasCompatibleBits(m_cached_ctx.FRAME.PSM & ~0x30, m_cached_ctx.ZBUF.PSM & ~0x30)) // Bit depth is not the same (i.e. 32bit + 16bit).
{
GL_INS("Inconsistent FRAME [%s, %08x] and ZBUF [%s] formats, not using double-half clear.",
+2 -2
View File
@@ -86,7 +86,7 @@ private:
void ResetStates();
void SetupIA(float target_scale, float sx, float sy);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only);
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass);
@@ -194,7 +194,7 @@ public:
void Lines2Sprites();
bool VerifyIndices();
void ExpandLineIndices();
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba);
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba, GSTextureCache::Target* rt, GSTextureCache::Source* tex);
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
GSVector4i ComputeBoundingBox(const GSVector2i& rtsize, float rtscale);
void MergeSprite(GSTextureCache::Source* tex);
+212 -59
View File
@@ -586,7 +586,7 @@ __ri static GSTextureCache::Source* FindSourceInMap(const GIFRegTEX0& TEX0, cons
return nullptr;
}
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, bool palette)
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, const u32 frame_fbp, bool palette)
{
if (GSConfig.UserHacks_DisableDepthSupport)
{
@@ -646,12 +646,19 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
const GSVector2i page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
const bool can_translate = CanTranslate(bp, TEX0.TBW, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
const bool swizzle_match = psm_s.depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
GSVector4i new_rect = r;
if (linear)
{
new_rect.z -= 1;
new_rect.w -= 1;
}
if (can_translate)
{
if (swizzle_match)
{
target_rc = TranslateAlignedRectByPage(t, bp, psm, TEX0.TBW, r);
target_rc = TranslateAlignedRectByPage(t, bp, psm, TEX0.TBW, new_rect);
}
else
{
@@ -669,8 +676,8 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
{
target_rc.x &= ~(page_size.x - 1);
target_rc.y &= ~(page_size.y - 1);
target_rc.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
target_rc.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
target_rc.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
target_rc.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
}
target_rc = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, TEX0.TBW, target_rc);
}
@@ -681,6 +688,11 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
inside_target = true;
}
}
if (linear)
{
new_rect.z += 1;
new_rect.w += 1;
}
}
}
@@ -688,7 +700,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
{
for (Target* t : m_dst[RenderTarget])
{
if (t->m_age <= 1 && t->m_TEX0.TBP0 == bp && t->HasValidAlpha())
if (t->m_age <= 1 && t->m_TEX0.TBP0 == bp && t->m_TEX0.TBW == TEX0.TBW && t->HasValidAlpha())
{
GL_CACHE("TC depth: Using RT %x instead of depth because of missing alpha", t->m_TEX0.TBP0);
@@ -764,7 +776,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
else
{
// This is a bit of a worry, since it could load junk from local memory... but it's better than skipping the draw.
return LookupSource(TEX0, TEXA, CLAMP, r, nullptr, possible_shuffle);
return LookupSource(TEX0, TEXA, CLAMP, r, nullptr, possible_shuffle, linear, frame_fbp);
}
ASSERT(src->m_texture);
@@ -773,7 +785,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
return src;
}
GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle)
GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle, const bool linear, const u32 frame_fbp)
{
GL_CACHE("TC: Lookup Source <%d,%d => %d,%d> (0x%x, %s, BW: %u, CBP: 0x%x, TW: %d, TH: %d)", r.x, r.y, r.z, r.w, TEX0.TBP0, psm_str(TEX0.PSM), TEX0.TBW, TEX0.CBP, 1 << TEX0.TW, 1 << TEX0.TH);
@@ -857,6 +869,13 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
if (rect_clean && tex_overlaps && !t->m_dirty.empty() && width_match)
{
GSVector4i new_rect = r;
if (linear)
{
new_rect.z -= 1;
new_rect.w -= 1;
}
bool partial = false;
// If it's compatible and page aligned, then handle it this way.
// It's quicker, and Surface Offsets can get it wrong.
@@ -865,14 +884,14 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
if (bp > t->m_TEX0.TBP0)
{
const GSVector2i page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
const bool can_translate = CanTranslate(bp, bw, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
const bool can_translate = CanTranslate(bp, bw, psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
if (can_translate)
{
if (swizzle_match)
{
new_rect = TranslateAlignedRectByPage(t, bp, psm, bw, r);
new_rect = TranslateAlignedRectByPage(t, bp, psm, bw, new_rect);
}
else
{
@@ -887,8 +906,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
{
new_rect.x &= ~(page_size.x - 1);
new_rect.y &= ~(page_size.y - 1);
new_rect.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
new_rect.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
new_rect.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
new_rect.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
}
new_rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, new_rect);
}
@@ -901,7 +920,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
sok.elems[0].bp = bp;
sok.elems[0].bw = bw;
sok.elems[0].psm = psm;
sok.elems[0].rect = r;
sok.elems[0].rect = new_rect;
sok.elems[1].bp = t->m_TEX0.TBP0;
sok.elems[1].bw = t->m_TEX0.TBW;
sok.elems[1].psm = t->m_TEX0.PSM;
@@ -941,6 +960,12 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// If not all channels are clean/dirty or only part of the rect is dirty, we need to update the target.
if (((channels & channel_mask) != channel_mask || partial))
t->Update();
if (linear)
{
new_rect.z += 1;
new_rect.w += 1;
}
}
else
{
@@ -981,13 +1006,23 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
}
}
else
dst = t;
{
// We don't have a shader for this.
if (!possible_shuffle && TEX0.PSM == PSMT8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp != 32)
{
continue;
}
else
{
dst = t;
found_t = true;
tex_merge_rt = false;
x_offset = 0;
y_offset = 0;
break;
found_t = true;
tex_merge_rt = false;
x_offset = 0;
y_offset = 0;
break;
}
}
}
}
else if (t_clean && (t->m_TEX0.TBW >= 16) && GSUtil::HasSharedBits(bp, psm, t->m_TEX0.TBP0 + t->m_TEX0.TBW * 0x10, t->m_TEX0.PSM))
@@ -1006,51 +1041,80 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// Make sure the texture actually is INSIDE the RT, it's possibly not valid if it isn't.
// Also check BP >= TBP, create source isn't equpped to expand it backwards and all data comes from the target. (GH3)
else if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && psm >= PSMCT32 &&
psm <= PSMCT16S && GSUtil::HasCompatibleBits(t->m_TEX0.PSM, psm) && (t->Overlaps(bp, bw, psm, r) || t->Wraps()) &&
t->m_age <= 1 && (!found_t || dst->m_TEX0.TBW < bw))
psm <= PSMCT16S && (GSUtil::HasCompatibleBits(t->m_TEX0.PSM, psm) ||
(possible_shuffle && t->m_TEX0.PSM <= PSMCT24 && ((((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == bp))
&& (t->Overlaps(bp, bw, psm, r) || t->Wraps()) &&
t->m_age <= 1 && (!found_t || dst->m_TEX0.TBW < bw))
{
// PSM equality needed because CreateSource does not handle PSM conversion.
// Only inclusive hit to limit false hits.
GSVector4i rect = r;
int src_bw = bw;
int src_psm = psm;
// If the input is C16 and it's actually a shuffle of 32bits we need to correct the size.
if ((t->m_TEX0.PSM & 0xF) == PSMCT32 && (psm & 0x7) == PSMCT16 && possible_shuffle)
{
src_psm = t->m_TEX0.PSM;
// If it's taking double width for the shuffle, half that.
if (src_bw == (t->m_TEX0.TBW * 2))
{
src_bw = t->m_TEX0.TBW;
rect.x /= 2;
rect.z /= 2;
}
else
{
rect.y /= 2;
rect.w /= 2;
}
}
if (bp > t->m_TEX0.TBP0)
{
GSVector4i new_rect = rect;
if (linear)
{
new_rect.z -= 1;
new_rect.w -= 1;
}
// Check if it is possible to hit with valid <x,y> offset on the given Target.
// Fixes Jak eyes rendering.
// Fixes Xenosaga 3 last dungeon graphic bug.
// Fixes Pause menu in The Getaway.
const bool can_translate = CanTranslate(bp, bw, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
const bool can_translate = CanTranslate(bp, bw, psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
if (can_translate)
{
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
const bool swizzle_match = GSLocalMemory::m_psm[src_psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
const GSVector2i& page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
const GSVector4i page_mask(GSVector4i((page_size.x - 1), (page_size.y - 1)).xyxy());
GSVector4i rect = r & ~page_mask;
rect = new_rect & ~page_mask;
if (swizzle_match)
{
rect = TranslateAlignedRectByPage(t, bp, psm, bw, r);
rect.x -= r.x;
rect.y -= r.y;
rect = TranslateAlignedRectByPage(t, bp, psm, bw, new_rect);
rect.x -= new_rect.x;
rect.y -= new_rect.y;
}
else
{
// If it's not page aligned, grab the whole pages it covers, to be safe.
if (GSLocalMemory::m_psm[psm].bpp != GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp)
{
const GSVector2i& dst_page_size = GSLocalMemory::m_psm[psm].pgs;
rect = GSVector4i(rect.x / page_size.x, rect.y / page_size.y, (rect.z + (page_size.x - 1)) / page_size.x, (rect.w + (page_size.y - 1)) / page_size.y);
rect = GSVector4i(rect.x * dst_page_size.x, rect.y * dst_page_size.y, rect.z * dst_page_size.x, rect.w * dst_page_size.y);
const GSVector2i& dst_page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
new_rect = GSVector4i(new_rect.x / page_size.x, new_rect.y / page_size.y, (new_rect.z + (page_size.x - 1)) / page_size.x, (new_rect.w + (page_size.y - 1)) / page_size.y);
new_rect = GSVector4i(new_rect.x * dst_page_size.x, new_rect.y * dst_page_size.y, new_rect.z * dst_page_size.x, new_rect.w * dst_page_size.y);
}
else
{
rect.x &= ~(page_size.x - 1);
rect.y &= ~(page_size.y - 1);
rect.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
rect.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
new_rect.x &= ~(page_size.x - 1);
new_rect.y &= ~(page_size.y - 1);
new_rect.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
new_rect.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
}
rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, rect);
rect.x -= r.x & ~(page_size.y - 1);
rect.y -= r.x & ~(page_size.y - 1);
rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, new_rect);
rect.x -= new_rect.x & ~(page_size.y - 1);
rect.y -= new_rect.x & ~(page_size.y - 1);
}
rect = rect.rintersect(t->m_valid);
@@ -1076,16 +1140,19 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
dst = t;
tex_merge_rt = false;
found_t = true;
continue;
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
break;
else
continue;
}
else
{
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, psm, r, t);
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, psm, new_rect, t);
if (!so.is_valid && t->Wraps())
{
// Improves Beyond Good & Evil shadow.
const u32 bp_unwrap = bp + GSTextureCache::MAX_BP + 0x1;
so = ComputeSurfaceOffset(bp_unwrap, bw, psm, r, t);
so = ComputeSurfaceOffset(bp_unwrap, bw, psm, new_rect, t);
}
if (so.is_valid)
{
@@ -1096,7 +1163,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
tex_merge_rt = false;
found_t = true;
// Keep looking, just in case there is an exact match (Situation: Target frame drawn inside target frame, current makes a separate texture)
continue;
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
break;
else
continue;
}
}
}
@@ -1125,7 +1195,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
dst = t;
tex_merge_rt = false;
found_t = true;
continue;
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
break;
else
continue;
}
// Strictly speaking this path is no longer needed, but I'm leaving it here for now because Guitar
@@ -1139,7 +1212,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// Prefer a target inside over a target outside.
found_t = false;
continue;
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
break;
else
continue;
}
}
}
@@ -1172,11 +1248,16 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
GIFRegTEX0 depth_TEX0;
depth_TEX0.U32[0] = TEX0.U32[0] | (0x30u << 20u);
depth_TEX0.U32[1] = TEX0.U32[1];
return LookupDepthSource(depth_TEX0, TEXA, CLAMP, r, possible_shuffle);
return LookupDepthSource(depth_TEX0, TEXA, CLAMP, r, possible_shuffle, linear, frame_fbp);
}
else
{
return LookupDepthSource(TEX0, TEXA, CLAMP, r, possible_shuffle, true);
if (!possible_shuffle && TEX0.PSM == PSMT8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp != 32)
{
continue;
}
else
return LookupDepthSource(TEX0, TEXA, CLAMP, r, possible_shuffle, linear, frame_fbp, true);
}
}
}
@@ -1275,6 +1356,15 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if (bp == t->m_TEX0.TBP0)
{
// if It's an old target and it's being completely overwritten, kill it.
if (!preserve_rgb && !preserve_alpha && TEX0.TBW != t->m_TEX0.TBW && TEX0.TBW > 1 && t->m_age >= 1)
{
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to width change to %d", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM), TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list.erase(i);
delete t;
continue;
}
list.MoveFront(i.Index());
dst = t;
@@ -1476,7 +1566,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
dst_match = t;
}
}
// We only want to use a matched target if it's actually being used.
if (dst_match)
{
calcRescale(dst_match);
@@ -1501,6 +1591,9 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high && psm_s.trbpp != 24;
dst->m_valid_rgb = dst_match->m_valid_rgb;
if(GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
dst->m_TEX0.TBW = dst_match->m_TEX0.TBW; // Be careful of shuffles of the depth as C16, but using a buffer width of 16 (Mercenaries).
ShaderConvert shader;
// m_32_bits_fmt gets set on a shuffle or if the format isn't 16bit.
// In this case it needs to make sure it isn't part of a shuffle, where it needs to be interpreted as 32bits.
@@ -1580,7 +1673,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
}
GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, float scale, int type,
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_target, const GSVector4i draw_rect)
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_target, const GSVector4i draw_rect, GSTextureCache::Source* src)
{
if (type == DepthStencil)
{
@@ -1595,7 +1688,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
Target* dst = Target::Create(TEX0, size.x, size.y, scale, type, true);
PreloadTarget(TEX0, size, valid_size, is_frame, preload, preserve_target, draw_rect, dst);;
PreloadTarget(TEX0, size, valid_size, is_frame, preload, preserve_target, draw_rect, dst, src);
dst->m_is_frame = is_frame;
@@ -1618,7 +1711,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
}
void GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst)
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src)
{
// In theory new textures contain invalidated data. Still in theory a new target
// must contains the content of the GS memory.
@@ -1750,17 +1843,75 @@ void GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
auto j = i;
Target* t = *j;
// could be overwriting a double buffer, so if it's the second half of it, just reduce the size down to half.
if (dst != t && t->m_TEX0.TBW == dst->m_TEX0.TBW &&
t->m_TEX0.PSM == dst->m_TEX0.PSM &&
((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == dst->m_TEX0.TBP0)
{
//DevCon.Warning("Found one %x->%x BW %d PSM %x (new target %x->%x BW %d PSM %x)", t->m_TEX0.TBP0, t->m_end_block, t->m_TEX0.TBW, t->m_TEX0.PSM, dst->m_TEX0.TBP0, dst->m_end_block, dst->m_TEX0.TBW, dst->m_TEX0.PSM);
GSVector4i new_valid = t->m_valid;
new_valid.w /= 2;
t->ResizeValidity(new_valid);
return;
}
if (dst != t && t->m_TEX0.TBW == dst->m_TEX0.TBW && t->m_TEX0.PSM == dst->m_TEX0.PSM && t->m_TEX0.TBW > 4)
if(t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid))
{
// could be overwriting a double buffer, so if it's the second half of it, just reduce the size down to half.
if (((((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == dst->m_TEX0.TBP0)
{
GSVector4i new_valid = t->m_valid;
new_valid.w /= 2;
GL_INS("RT resize buffer for FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, t->m_valid.width(), t->m_valid.height(), new_valid.width(), new_valid.height());
t->ResizeValidity(new_valid);
return;
}
// The new texture is behind it but engulfs the whole thing, shrink the new target so it grows in the HW Draw resize.
else if (((((dst->UnwrappedEndBlock() + 1) - dst->m_TEX0.TBP0) >> 1) + dst->m_TEX0.TBP0) == t->m_TEX0.TBP0)
{
if (dst->m_TEX0.TBW == 2)
{
i++;
continue;
}
int overlapping_pages = ((dst->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 5;
int y_reduction = (overlapping_pages / dst->m_TEX0.TBW) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
if (y_reduction == 0 || (overlapping_pages % dst->m_TEX0.TBW))
{
i++;
continue;
}
if (preserve_target || preload)
{
const int copy_width = (t->m_texture->GetWidth()) > (dst->m_texture->GetWidth()) ? (dst->m_texture->GetWidth()) : t->m_texture->GetWidth();
const int copy_height = y_reduction * t->m_scale;
const int old_height = (dst->m_valid.w - y_reduction) * dst->m_scale;
GL_INS("RT double buffer copy from FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
// Clear the dirty first
dst->Update();
// Invalidate has been moved to after DrawPrims(), because we might kill the current sources' backing.
if (!t->m_valid_rgb || !(t->m_valid_alpha_high || t->m_valid_alpha_low))
{
const GSVector4 src_rect = GSVector4(0, 0, copy_width, copy_height) / (GSVector4(t->m_texture->GetSize()).xyxy());
const GSVector4 dst_rect = GSVector4(0, old_height, copy_width, copy_height);
g_gs_device->StretchRect(t->m_texture, src_rect, dst->m_texture, dst_rect, t->m_valid_rgb, t->m_valid_rgb, t->m_valid_rgb, t->m_valid_alpha_high || t->m_valid_alpha_low);
}
else
{
// Invalidate has been moved to after DrawPrims(), because we might kill the current sources' backing.
g_gs_device->CopyRect(t->m_texture, dst->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
}
}
if (src && src->m_target && src->m_from_target == t)
{
// This should never happen as we're making a new target so the src should never be something it overlaps, but just incase..
GSVector4i new_valid = t->m_valid;
new_valid.y = std::max(new_valid.y - y_reduction, 0);
new_valid.w = std::max(new_valid.w - y_reduction, 0);
t->m_TEX0.TBP0 += (y_reduction / GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y) << 5;
t->ResizeValidity(new_valid);
}
else
{
InvalidateSourcesFromTarget(t);
i = list.erase(j);
delete t;
}
return;
}
}
i++;
}
}
@@ -3666,6 +3817,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
src->m_alpha_minmax.second = (using_both ? std::max(TEXA.TA1, TEXA.TA0) : (using_ta1 ? TEXA.TA1 : TEXA.TA0));
}
}
src->m_32_bits_fmt = dst->m_32_bits_fmt;
if (psm.pal > 0)
{
@@ -3726,6 +3878,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
src->m_alpha_minmax.second = (using_both ? std::max(TEXA.TA1, TEXA.TA0) : (using_ta1 ? TEXA.TA1 : TEXA.TA0));
}
}
src->m_32_bits_fmt = dst->m_32_bits_fmt;
dst->Update();
+4 -4
View File
@@ -424,7 +424,7 @@ protected:
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region);
void PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst);
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr);
// Returns scaled texture size.
static GSVector2i ScaleRenderTargetSize(const GSVector2i& sz, float scale);
@@ -473,8 +473,8 @@ public:
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size);
std::shared_ptr<Palette> LookupPaletteObject(const u32* clut, u16 pal, bool need_gs_texture);
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle);
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, bool palette = false);
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle, const bool linear, const u32 frame_fbp = 0xFFFFFFFF);
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, const u32 frame_fbp = 0xFFFFFFFF, bool palette = false);
Target* FindTargetOverlap(Target* target, int type, int psm);
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
@@ -482,7 +482,7 @@ public:
const GSVector4i draw_rc = GSVector4i::zero());
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size,float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
const GSVector4i draw_rc = GSVector4i::zero());
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
Target* LookupDisplayTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale);
/// Looks up a target in the cache, and only returns it if the BP/BW match exactly.
+18 -5
View File
@@ -673,7 +673,7 @@ MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name)
{
NSString* msg = [NSString stringWithFormat:@"Failed to load shader %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]);
pxFailRel("Failed to load shader, check the log for more details.");
pxFailRel([msg UTF8String]);
}
return fn;
}
@@ -689,7 +689,7 @@ MRCOwned<id<MTLRenderPipelineState>> GSDeviceMTL::MakePipeline(MTLRenderPipeline
{
NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]);
pxFailRel("Failed to create pipeline, check the log for more details.");
pxFailRel([msg UTF8String]);
}
return res;
}
@@ -709,7 +709,7 @@ MRCOwned<id<MTLComputePipelineState>> GSDeviceMTL::MakeComputePipeline(id<MTLFun
{
NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]);
pxFailRel("Failed to create pipeline, check the log for more details.");
pxFailRel([msg UTF8String]);
}
return res;
}
@@ -1803,6 +1803,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantB(m_fn_constants, pssel.adjt, GSMTLConstantIndex_PS_ADJT);
setFnConstantB(m_fn_constants, pssel.ltf, GSMTLConstantIndex_PS_LTF);
setFnConstantB(m_fn_constants, pssel.shuffle, GSMTLConstantIndex_PS_SHUFFLE);
setFnConstantB(m_fn_constants, pssel.shuffle_same, GSMTLConstantIndex_PS_SHUFFLE_SAME);
setFnConstantB(m_fn_constants, pssel.read_ba, GSMTLConstantIndex_PS_READ_BA);
setFnConstantB(m_fn_constants, pssel.real16src, GSMTLConstantIndex_PS_READ16_SRC);
setFnConstantB(m_fn_constants, pssel.write_rg, GSMTLConstantIndex_PS_WRITE_RG);
@@ -2048,6 +2049,18 @@ static id<MTLTexture> getTexture(GSTexture* tex)
return tex ? static_cast<GSTextureMTL*>(tex)->GetTexture() : nil;
}
static bool usesStencil(GSHWDrawConfig::DestinationAlphaMode dstalpha)
{
switch (dstalpha)
{
case GSHWDrawConfig::DestinationAlphaMode::Stencil:
case GSHWDrawConfig::DestinationAlphaMode::StencilOne:
return true;
default:
return false;
}
}
void GSDeviceMTL::MREInitHWDraw(GSHWDrawConfig& config, const Map& verts)
{
MRESetScissor(config.scissor);
@@ -2141,8 +2154,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
}
// Try to reduce render pass restarts
if (!stencil && config.depth.key == DepthStencilSelector::NoDepth().key && (m_current_render.color_target != rt || m_current_render.depth_target != config.ds))
config.ds = nullptr;
if (!config.ds && m_current_render.color_target == rt && stencil == m_current_render.stencil_target && m_current_render.depth_target != config.tex)
config.ds = m_current_render.depth_target;
if (!rt && config.ds == m_current_render.depth_target && m_current_render.color_target != config.tex)
@@ -2160,6 +2171,8 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
BeginRenderPass(@"RenderHW", rt, MTLLoadActionLoad, config.ds, MTLLoadActionLoad, stencil, MTLLoadActionLoad);
id<MTLRenderCommandEncoder> mtlenc = m_current_render.encoder;
FlushDebugEntries(mtlenc);
if (usesStencil(config.destination_alpha))
[mtlenc setStencilReferenceValue:1];
MREInitHWDraw(config, allocation);
if (config.require_one_barrier || config.require_full_barrier)
MRESetTexture(config.rt, GSMTLTextureIndexRenderTarget);
@@ -182,6 +182,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_ADJT,
GSMTLConstantIndex_PS_LTF,
GSMTLConstantIndex_PS_SHUFFLE,
GSMTLConstantIndex_PS_SHUFFLE_SAME,
GSMTLConstantIndex_PS_READ_BA,
GSMTLConstantIndex_PS_READ16_SRC,
GSMTLConstantIndex_PS_WRITE_RG,
+19 -6
View File
@@ -41,6 +41,7 @@ constant bool PS_ADJS [[function_constant(GSMTLConstantIndex_PS_AD
constant bool PS_ADJT [[function_constant(GSMTLConstantIndex_PS_ADJT)]];
constant bool PS_LTF [[function_constant(GSMTLConstantIndex_PS_LTF)]];
constant bool PS_SHUFFLE [[function_constant(GSMTLConstantIndex_PS_SHUFFLE)]];
constant bool PS_SHUFFLE_SAME [[function_constant(GSMTLConstantIndex_PS_SHUFFLE_SAME)]];
constant bool PS_READ_BA [[function_constant(GSMTLConstantIndex_PS_READ_BA)]];
constant bool PS_READ16_SRC [[function_constant(GSMTLConstantIndex_PS_READ16_SRC)]];
constant bool PS_WRITE_RG [[function_constant(GSMTLConstantIndex_PS_WRITE_RG)]];
@@ -1021,7 +1022,16 @@ struct PSMain
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(cb.ta * 255.5f);
if (PS_READ16_SRC)
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
if (PS_SHUFFLE_SAME)
{
if (PS_READ_BA)
C = (denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80);
else
C.ga = C.rg;
}
// Copy of a 16bit source in to this target
else if (PS_READ16_SRC)
{
C.rb = (denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5);
if (denorm_c.a & 0x80)
@@ -1029,13 +1039,16 @@ struct PSMain
else
C.ga = (denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80);
}
// Write RB part. Mask will take care of the correct destination
else if (PS_READ_BA)
{
C.rb = C.bb;
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
}
else
{
C.rb = PS_READ_BA ? C.bb : C.rr;
if (PS_READ_BA)
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
else
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
C.rb = C.rr;
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
}
}
@@ -1378,6 +1378,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_BLEND_D {}\n", sel.blend_d)
+ fmt::format("#define PS_IIP {}\n", sel.iip)
+ fmt::format("#define PS_SHUFFLE {}\n", sel.shuffle)
+ fmt::format("#define PS_SHUFFLE_SAME {}\n", sel.shuffle_same)
+ fmt::format("#define PS_READ_BA {}\n", sel.read_ba)
+ fmt::format("#define PS_READ16_SRC {}\n", sel.real16src)
+ fmt::format("#define PS_WRITE_RG {}\n", sel.write_rg)
File diff suppressed because it is too large Load Diff
+55 -50
View File
@@ -16,10 +16,10 @@
#pragma once
#include "GS/Renderers/Common/GSDevice.h"
#include "GS/GSVector.h"
#include "GS/Renderers/Vulkan/GSTextureVK.h"
#include "GS/Renderers/Vulkan/VKLoader.h"
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
#include "GS/GSVector.h"
#include "common/HashCombine.h"
#include "common/ReadbackSpinManager.h"
@@ -50,13 +50,10 @@ public:
bool vk_ext_provoking_vertex : 1;
bool vk_ext_memory_budget : 1;
bool vk_ext_calibrated_timestamps : 1;
bool vk_ext_line_rasterization : 1;
bool vk_ext_rasterization_order_attachment_access : 1;
bool vk_ext_attachment_feedback_loop_layout : 1;
bool vk_ext_full_screen_exclusive : 1;
bool vk_khr_driver_properties : 1;
bool vk_khr_fragment_shader_barycentric : 1;
bool vk_khr_shader_draw_parameters : 1;
};
// Global state accessors
@@ -108,9 +105,6 @@ public:
__fi VKStreamBuffer& GetTextureUploadBuffer() { return m_texture_stream_buffer; }
VkCommandBuffer GetCurrentInitCommandBuffer();
/// Allocates a descriptor set from the pool reserved for the current frame.
VkDescriptorSet AllocateDescriptorSet(VkDescriptorSetLayout set_layout);
/// Allocates a descriptor set from the pool reserved for the current frame.
VkDescriptorSet AllocatePersistentDescriptorSet(VkDescriptorSetLayout set_layout);
@@ -199,12 +193,11 @@ private:
};
using ExtensionList = std::vector<const char*>;
static bool SelectInstanceExtensions(
ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils);
static bool SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils);
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
bool SelectDeviceFeatures();
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);
void ProcessDeviceExtensions();
bool ProcessDeviceExtensions();
bool CreateAllocator();
bool CreateCommandBuffers();
@@ -237,7 +230,6 @@ private:
// [0] - Init (upload) command buffer, [1] - draw command buffer
VkCommandPool command_pool = VK_NULL_HANDLE;
std::array<VkCommandBuffer, 2> command_buffers{VK_NULL_HANDLE, VK_NULL_HANDLE};
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
VkFence fence = VK_NULL_HANDLE;
u64 fence_counter = 0;
s32 spin_id = -1;
@@ -388,11 +380,7 @@ public:
enum : u32
{
NUM_TFX_DYNAMIC_OFFSETS = 2,
NUM_TFX_DRAW_TEXTURES = 2,
NUM_TFX_RT_TEXTURES = 2,
NUM_TFX_TEXTURES = NUM_TFX_DRAW_TEXTURES + NUM_TFX_RT_TEXTURES,
NUM_CONVERT_TEXTURES = 1,
NUM_CONVERT_SAMPLERS = 1,
NUM_UTILITY_SAMPLERS = 1,
CONVERT_PUSH_CONSTANTS_SIZE = 96,
NUM_CAS_PIPELINES = 2,
@@ -401,10 +389,18 @@ public:
{
TFX_DESCRIPTOR_SET_UBO,
TFX_DESCRIPTOR_SET_TEXTURES,
TFX_DESCRIPTOR_SET_RT,
NUM_TFX_DESCRIPTOR_SETS,
};
enum TFX_TEXTURES : u32
{
TFX_TEXTURE_TEXTURE,
TFX_TEXTURE_PALETTE,
TFX_TEXTURE_RT,
TFX_TEXTURE_PRIMID,
NUM_TFX_TEXTURES
};
enum DATE_RENDER_PASS : u32
{
DATE_RENDER_PASS_NONE = 0,
@@ -419,8 +415,7 @@ private:
VkPipelineLayout m_utility_pipeline_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout m_tfx_ubo_ds_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout m_tfx_sampler_ds_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout m_tfx_rt_texture_ds_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout m_tfx_texture_ds_layout = VK_NULL_HANDLE;
VkPipelineLayout m_tfx_pipeline_layout = VK_NULL_HANDLE;
VKStreamBuffer m_vertex_stream_buffer;
@@ -449,7 +444,8 @@ private:
VkPipeline m_shadeboost_pipeline = {};
std::unordered_map<u32, VkShaderModule> m_tfx_vertex_shaders;
std::unordered_map<GSHWDrawConfig::PSSelector, VkShaderModule, GSHWDrawConfig::PSSelectorHash> m_tfx_fragment_shaders;
std::unordered_map<GSHWDrawConfig::PSSelector, VkShaderModule, GSHWDrawConfig::PSSelectorHash>
m_tfx_fragment_shaders;
std::unordered_map<PipelineSelector, VkPipeline, PipelineSelectorHash> m_tfx_pipelines;
VkRenderPass m_utility_color_render_pass_load = VK_NULL_HANDLE;
@@ -473,15 +469,18 @@ private:
std::string m_tfx_source;
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
GSTexture* CreateSurface(
GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
const GSRegEXTBUF& EXTBUF, u32 c, const bool linear) final;
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
bool DoCAS(
GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
VkSampler GetSampler(GSHWDrawConfig::SamplerSelector ss);
void ClearSamplerCache() final;
@@ -520,7 +519,8 @@ public:
__fi static GSDeviceVK* GetInstance() { return static_cast<GSDeviceVK*>(g_gs_device.get()); }
static void GetAdaptersAndFullscreenModes(std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
static void GetAdaptersAndFullscreenModes(
std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
/// Returns true if Vulkan is suitable as a default for the devices in the system.
static bool IsSuitableDefaultRenderer();
@@ -571,7 +571,8 @@ public:
bool green, bool blue, bool alpha) override;
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
PresentShader shader, float shaderTime, bool linear) override;
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
void DrawMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTextureVK* dTex, ShaderConvert shader);
void BeginRenderPassForStretchRect(
@@ -583,8 +584,10 @@ public:
void BlitRect(GSTexture* sTex, const GSVector4i& sRect, u32 sLevel, GSTexture* dTex, const GSVector4i& dRect,
u32 dLevel, bool linear);
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
void UpdateCLUTTexture(
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM,
GSTexture* dTex, u32 DBW, u32 DPSM) override;
void SetupDATE(GSTexture* rt, GSTexture* ds, bool datm, const GSVector4i& bbox);
GSTextureVK* SetupPrimitiveTrackingDATE(GSHWDrawConfig& config);
@@ -631,8 +634,7 @@ public:
bool ApplyUtilityState(bool already_execed = false);
bool ApplyTFXState(bool already_execed = false);
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset);
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
void SetIndexBuffer(VkBuffer buffer);
void SetBlendConstants(u8 color);
void SetLineWidth(float width);
@@ -657,25 +659,32 @@ public:
private:
enum DIRTY_FLAG : u32
{
DIRTY_FLAG_TFX_SAMPLERS_DS = (1 << 0),
DIRTY_FLAG_TFX_RT_TEXTURE_DS = (1 << 1),
DIRTY_FLAG_TFX_DYNAMIC_OFFSETS = (1 << 2),
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 3),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 4),
DIRTY_FLAG_LINE_WIDTH = (1 << 5),
DIRTY_FLAG_VERTEX_BUFFER = (1 << 6),
DIRTY_FLAG_INDEX_BUFFER = (1 << 7),
DIRTY_FLAG_VIEWPORT = (1 << 8),
DIRTY_FLAG_SCISSOR = (1 << 9),
DIRTY_FLAG_PIPELINE = (1 << 10),
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 11),
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 12),
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3
DIRTY_FLAG_TFX_UBO = (1 << 4),
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 5),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 6),
DIRTY_FLAG_LINE_WIDTH = (1 << 7),
DIRTY_FLAG_INDEX_BUFFER = (1 << 8),
DIRTY_FLAG_VIEWPORT = (1 << 9),
DIRTY_FLAG_SCISSOR = (1 << 10),
DIRTY_FLAG_PIPELINE = (1 << 11),
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 12),
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 13),
DIRTY_BASE_STATE = DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE |
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_SAMPLERS_DS | DIRTY_FLAG_TFX_RT_TEXTURE_DS,
DIRTY_FLAG_TFX_TEXTURE_TEX = (DIRTY_FLAG_TFX_TEXTURE_0 << 0),
DIRTY_FLAG_TFX_TEXTURE_PALETTE = (DIRTY_FLAG_TFX_TEXTURE_0 << 1),
DIRTY_FLAG_TFX_TEXTURE_RT = (DIRTY_FLAG_TFX_TEXTURE_0 << 2),
DIRTY_FLAG_TFX_TEXTURE_PRIMID = (DIRTY_FLAG_TFX_TEXTURE_0 << 3),
DIRTY_FLAG_TFX_TEXTURES = DIRTY_FLAG_TFX_TEXTURE_TEX | DIRTY_FLAG_TFX_TEXTURE_PALETTE |
DIRTY_FLAG_TFX_TEXTURE_RT | DIRTY_FLAG_TFX_TEXTURE_PRIMID,
DIRTY_BASE_STATE = DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR |
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES,
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_UTILITY_TEXTURE,
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
ALL_DIRTY_STATE = DIRTY_BASE_STATE | DIRTY_TFX_STATE | DIRTY_UTILITY_STATE | DIRTY_CONSTANT_BUFFER_STATE,
};
enum class PipelineLayout
@@ -688,6 +697,7 @@ private:
void InitializeState();
bool CreatePersistentDescriptorSets();
void SetInitialState(VkCommandBuffer cmdbuf);
void ApplyBaseState(u32 flags, VkCommandBuffer cmdbuf);
// Which bindings/state has to be updated before the next draw.
@@ -695,12 +705,7 @@ private:
FeedbackLoopFlag m_current_framebuffer_feedback_loop = FeedbackLoopFlag_None;
bool m_warned_slow_spin = false;
// input assembly
VkBuffer m_vertex_buffer = VK_NULL_HANDLE;
VkDeviceSize m_vertex_buffer_offset = 0;
VkBuffer m_index_buffer = VK_NULL_HANDLE;
VkDeviceSize m_index_buffer_offset = 0;
VkIndexType m_index_type = VK_INDEX_TYPE_UINT16;
GSTextureVK* m_current_render_target = nullptr;
GSTextureVK* m_current_depth_target = nullptr;
+18 -13
View File
@@ -15,14 +15,14 @@
#include "PrecompiledHeader.h"
#include "GS/GSGL.h"
#include "GS/GSPerfMon.h"
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
#include "GS/Renderers/Vulkan/GSTextureVK.h"
#include "GS/Renderers/Vulkan/VKBuilders.h"
#include "GS/GSPerfMon.h"
#include "GS/GSGL.h"
#include "common/BitUtils.h"
#include "common/Assertions.h"
#include "common/BitUtils.h"
static constexpr const VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
@@ -52,7 +52,8 @@ static VkImageLayout GetVkImageLayout(GSTextureVK::Layout layout)
static VkAccessFlagBits GetFeedbackLoopInputAccessBits()
{
return GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_ACCESS_SHADER_READ_BIT : VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
return GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_ACCESS_SHADER_READ_BIT :
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
}
GSTextureVK::GSTextureVK(Type type, Format format, int width, int height, int levels, VkImage image,
@@ -111,10 +112,11 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
case Type::RenderTarget:
{
pxAssert(levels == 1);
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
ici.usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
}
break;
@@ -124,7 +126,8 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
ici.usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT : 0);
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
0);
vci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
}
break;
@@ -430,8 +433,8 @@ bool GSTextureVK::Map(GSMap& m, const GSVector4i* r, int layer)
m_map_area = r ? *r : GetRect();
m_map_level = layer;
m.pitch =
Common::AlignUpPow2(CalcUploadPitch(m_map_area.width()), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
m.pitch = Common::AlignUpPow2(
CalcUploadPitch(m_map_area.width()), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
// see note in Update() for the reason why.
const u32 required_size = CalcUploadSize(m_map_area.height(), m.pitch);
@@ -459,7 +462,8 @@ void GSTextureVK::Unmap()
const u32 width = m_map_area.width();
const u32 height = m_map_area.height();
const u32 pitch = Common::AlignUpPow2(CalcUploadPitch(width), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
const u32 pitch =
Common::AlignUpPow2(CalcUploadPitch(width), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
const u32 required_size = CalcUploadSize(height, pitch);
VKStreamBuffer& buffer = GSDeviceVK::GetInstance()->GetTextureUploadBuffer();
const u32 buffer_offset = buffer.GetCurrentOffset();
@@ -826,7 +830,8 @@ GSDownloadTextureVK::~GSDownloadTextureVK()
std::unique_ptr<GSDownloadTextureVK> GSDownloadTextureVK::Create(u32 width, u32 height, GSTexture::Format format)
{
const u32 buffer_size = GetBufferSize(width, height, format, GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
const u32 buffer_size =
GetBufferSize(width, height, format, GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0u, buffer_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0u, nullptr};
+3 -3
View File
@@ -15,8 +15,8 @@
#pragma once
#include "GS/GS.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "GS/GS.h"
#include "GS/Renderers/Vulkan/VKLoader.h"
#include <limits>
@@ -76,8 +76,8 @@ public:
void OverrideImageLayout(Layout new_layout);
void TransitionToLayout(VkCommandBuffer command_buffer, Layout new_layout);
void TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, int start_level, int num_levels,
Layout old_layout, Layout new_layout);
void TransitionSubresourcesToLayout(
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout);
/// Framebuffers are lazily allocated.
VkFramebuffer GetFramebuffer(bool feedback_loop);
+16
View File
@@ -141,6 +141,11 @@ void Vulkan::DescriptorSetLayoutBuilder::Clear()
m_ci.bindingCount = 0;
}
void Vulkan::DescriptorSetLayoutBuilder::SetPushFlag()
{
m_ci.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
}
VkDescriptorSetLayout Vulkan::DescriptorSetLayoutBuilder::Create(VkDevice device)
{
VkDescriptorSetLayout layout;
@@ -724,6 +729,17 @@ void Vulkan::DescriptorSetUpdateBuilder::Update(VkDevice device, bool clear /*=
Clear();
}
void Vulkan::DescriptorSetUpdateBuilder::PushUpdate(
VkCommandBuffer cmdbuf, VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 set, bool clear /*= true*/)
{
pxAssert(m_num_writes > 0);
vkCmdPushDescriptorSetKHR(cmdbuf, bind_point, layout, set, m_num_writes, m_writes.data());
if (clear)
Clear();
}
void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
{
+4 -1
View File
@@ -48,6 +48,7 @@ namespace Vulkan
DescriptorSetLayoutBuilder();
void Clear();
void SetPushFlag();
VkDescriptorSetLayout Create(VkDevice device);
@@ -209,7 +210,7 @@ namespace Vulkan
VkSpecializationInfo m_si;
std::array<VkSpecializationMapEntry, MAX_SPECIALIZATION_CONSTANTS> m_smap_entries;
std::array<u8, SPECIALIZATION_CONSTANT_SIZE* MAX_SPECIALIZATION_CONSTANTS> m_smap_constants;
std::array<u8, SPECIALIZATION_CONSTANT_SIZE * MAX_SPECIALIZATION_CONSTANTS> m_smap_constants;
};
class SamplerBuilder
@@ -248,6 +249,8 @@ namespace Vulkan
void Clear();
void Update(VkDevice device, bool clear = true);
void PushUpdate(VkCommandBuffer cmdbuf, VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 set,
bool clear = true);
void AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
@@ -243,4 +243,7 @@ VULKAN_DEVICE_ENTRY_POINT(vkReleaseFullScreenExclusiveModeEXT, false)
// VK_EXT_calibrated_timestamps
VULKAN_DEVICE_ENTRY_POINT(vkGetCalibratedTimestampsEXT, false)
// VK_KHR_push_descriptor
VULKAN_DEVICE_ENTRY_POINT(vkCmdPushDescriptorSetKHR, false)
#endif // VULKAN_DEVICE_ENTRY_POINT
+24 -22
View File
@@ -15,10 +15,10 @@
#include "PrecompiledHeader.h"
#include "GS/Renderers/Vulkan/VKShaderCache.h"
#include "GS/GS.h"
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
#include "GS/Renderers/Vulkan/VKBuilders.h"
#include "GS/GS.h"
#include "GS/Renderers/Vulkan/VKShaderCache.h"
#include "Config.h"
#include "ShaderCacheVersion.h"
@@ -44,28 +44,29 @@
std::unique_ptr<VKShaderCache> g_vulkan_shader_cache;
namespace {
namespace
{
#pragma pack(push, 4)
struct VK_PIPELINE_CACHE_HEADER
{
u32 header_length;
u32 header_version;
u32 vendor_id;
u32 device_id;
u8 uuid[VK_UUID_SIZE];
};
struct VK_PIPELINE_CACHE_HEADER
{
u32 header_length;
u32 header_version;
u32 vendor_id;
u32 device_id;
u8 uuid[VK_UUID_SIZE];
};
struct CacheIndexEntry
{
u64 source_hash_low;
u64 source_hash_high;
u32 source_length;
u32 shader_type;
u32 file_offset;
u32 blob_size;
};
struct CacheIndexEntry
{
u64 source_hash_low;
u64 source_hash_high;
u32 source_length;
u32 shader_type;
u32 file_offset;
u32 blob_size;
};
#pragma pack(pop)
}
} // namespace
static bool ValidatePipelineCacheHeader(const VK_PIPELINE_CACHE_HEADER& header)
{
@@ -478,7 +479,8 @@ bool VKShaderCache::FlushPipelineCache()
return false;
size_t data_size;
VkResult res = vkGetPipelineCacheData(GSDeviceVK::GetInstance()->GetDevice(), m_pipeline_cache, &data_size, nullptr);
VkResult res =
vkGetPipelineCacheData(GSDeviceVK::GetInstance()->GetDevice(), m_pipeline_cache, &data_size, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkGetPipelineCacheData() failed: ");
+4 -3
View File
@@ -16,11 +16,11 @@
#include "PrecompiledHeader.h"
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
#include "GS/Renderers/Vulkan/VKBuilders.h"
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
#include "common/BitUtils.h"
#include "common/Assertions.h"
#include "common/BitUtils.h"
#include "common/Console.h"
VKStreamBuffer::VKStreamBuffer() = default;
@@ -79,7 +79,8 @@ bool VKStreamBuffer::Create(VkBufferUsageFlags usage, u32 size)
VmaAllocationInfo ai = {};
VkBuffer new_buffer = VK_NULL_HANDLE;
VmaAllocation new_allocation = VK_NULL_HANDLE;
VkResult res = vmaCreateBuffer(GSDeviceVK::GetInstance()->GetAllocator(), &bci, &aci, &new_buffer, &new_allocation, &ai);
VkResult res =
vmaCreateBuffer(GSDeviceVK::GetInstance()->GetAllocator(), &bci, &aci, &new_buffer, &new_allocation, &ai);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
@@ -35,6 +35,7 @@ public:
__fi bool IsValid() const { return (m_buffer != VK_NULL_HANDLE); }
__fi VkBuffer GetBuffer() const { return m_buffer; }
__fi const VkBuffer* GetBufferPtr() const { return &m_buffer; }
__fi u8* GetHostPointer() const { return m_host_pointer; }
__fi u8* GetCurrentHostPointer() const { return m_host_pointer + m_current_offset; }
__fi u32 GetCurrentSize() const { return m_size; }
+9 -7
View File
@@ -16,8 +16,8 @@
#include "PrecompiledHeader.h"
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
#include "GS/Renderers/Vulkan/VKSwapChain.h"
#include "GS/Renderers/Vulkan/VKBuilders.h"
#include "GS/Renderers/Vulkan/VKSwapChain.h"
#include "common/Assertions.h"
#include "common/CocoaTools.h"
@@ -183,8 +183,8 @@ static VkFormat GetLinearFormat(VkFormat format)
std::optional<VkSurfaceFormatKHR> VKSwapChain::SelectSurfaceFormat(VkSurfaceKHR surface)
{
u32 format_count;
VkResult res =
vkGetPhysicalDeviceSurfaceFormatsKHR(GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &format_count, nullptr);
VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(
GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &format_count, nullptr);
if (res != VK_SUCCESS || format_count == 0)
{
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
@@ -254,8 +254,8 @@ std::optional<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surf
{
VkResult res;
u32 mode_count;
res =
vkGetPhysicalDeviceSurfacePresentModesKHR(GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &mode_count, nullptr);
res = vkGetPhysicalDeviceSurfacePresentModesKHR(
GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &mode_count, nullptr);
if (res != VK_SUCCESS || mode_count == 0)
{
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
@@ -376,7 +376,8 @@ bool VKSwapChain::CreateSwapChain()
GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex(),
GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex(),
}};
if (GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex() != GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex())
if (GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex() !=
GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex())
{
swap_chain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swap_chain_info.queueFamilyIndexCount = 2;
@@ -462,7 +463,8 @@ bool VKSwapChain::CreateSwapChain()
ImageSemaphores sema;
const VkSemaphoreCreateInfo semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
res = vkCreateSemaphore(GSDeviceVK::GetInstance()->GetDevice(), &semaphore_info, nullptr, &sema.available_semaphore);
res = vkCreateSemaphore(
GSDeviceVK::GetInstance()->GetDevice(), &semaphore_info, nullptr, &sema.available_semaphore);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
-7
View File
@@ -601,9 +601,6 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
case GSHWFixId::SkipDrawEnd:
return (config.SkipDrawEnd == value);
case GSHWFixId::HalfBottomOverride:
return (config.UserHacks_HalfBottomOverride == value);
case GSHWFixId::HalfPixelOffset:
return (config.UpscaleMultiplier <= 1.0f || config.UserHacks_HalfPixelOffset == value);
@@ -782,10 +779,6 @@ void GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
config.SkipDrawEnd = value;
break;
case GSHWFixId::HalfBottomOverride:
config.UserHacks_HalfBottomOverride = value;
break;
case GSHWFixId::HalfPixelOffset:
config.UserHacks_HalfPixelOffset = value;
break;
+104 -11
View File
@@ -19,6 +19,7 @@
#include "Elfheader.h"
#include "GameList.h"
#include "Host.h"
#include "INISettingsInterface.h"
#include "VMManager.h"
#include "common/Assertions.h"
@@ -75,10 +76,10 @@ namespace GameList
static bool GetGameListEntryFromCache(const std::string& path, GameList::Entry* entry);
static void ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths,
const PlayedTimeMap& played_time_map, ProgressCallback* progress);
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress);
static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map);
static bool ScanFile(
std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, const PlayedTimeMap& played_time_map);
static bool ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini);
static void LoadCache();
static bool LoadEntriesFromCache(std::FILE* stream);
@@ -94,6 +95,8 @@ namespace GameList
static PlayedTimeMap LoadPlayedTimeMap(const std::string& path);
static PlayedTimeEntry UpdatePlayedTimeFile(
const std::string& path, const std::string& serial, std::time_t last_time, std::time_t add_time);
static std::string GetCustomPropertiesFile();
} // namespace GameList
static std::vector<GameList::Entry> s_entries;
@@ -586,7 +589,7 @@ static bool IsPathExcluded(const std::vector<std::string>& excluded_paths, const
}
void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths,
const PlayedTimeMap& played_time_map, ProgressCallback* progress)
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress)
{
Console.WriteLn("Scanning %s%s", path, recursive ? " (recursively)" : "");
@@ -619,7 +622,7 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
}
progress->SetFormattedStatusText("Scanning '%s'...", FileSystem::GetDisplayNameFromPath(ffd.FileName).c_str());
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map);
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map, custom_attributes_ini);
progress->SetProgressValue(files_scanned);
}
@@ -648,8 +651,8 @@ bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp,
return true;
}
bool GameList::ScanFile(
std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, const PlayedTimeMap& played_time_map)
bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini)
{
// don't block UI while scanning
lock.unlock();
@@ -675,13 +678,28 @@ bool GameList::ScanFile(
return true;
}
auto iter = played_time_map.find(entry.serial);
const auto iter = played_time_map.find(entry.serial);
if (iter != played_time_map.end())
{
entry.last_played_time = iter->second.last_played_time;
entry.total_played_time = iter->second.total_played_time;
}
auto custom_title = custom_attributes_ini.GetOptionalStringValue(entry.path.c_str(), "Title");
if (custom_title)
{
entry.title = std::move(custom_title.value());
}
const auto custom_region = custom_attributes_ini.GetOptionalIntValue(entry.path.c_str(), "Region");
if (custom_region)
{
const int custom_region_value = custom_region.value();
if (custom_region_value >= 0 && custom_region_value < static_cast<int>(Region::Count))
{
entry.region = static_cast<Region>(custom_region_value);
}
}
lock.lock();
// remove if present
@@ -764,6 +782,8 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
const std::vector<std::string> dirs(Host::GetBaseStringListSetting("GameList", "Paths"));
const std::vector<std::string> recursive_dirs(Host::GetBaseStringListSetting("GameList", "RecursivePaths"));
const PlayedTimeMap played_time(LoadPlayedTimeMap(GetPlayedTimeFile()));
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
custom_attributes_ini.Load();
if (!dirs.empty() || !recursive_dirs.empty())
{
@@ -777,7 +797,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
if (progress->IsCancelled())
break;
ScanDirectory(dir.c_str(), false, only_cache, excluded_paths, played_time, progress);
ScanDirectory(dir.c_str(), false, only_cache, excluded_paths, played_time, custom_attributes_ini, progress);
progress->SetProgressValue(++directory_counter);
}
for (const std::string& dir : recursive_dirs)
@@ -785,7 +805,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
if (progress->IsCancelled())
break;
ScanDirectory(dir.c_str(), true, only_cache, excluded_paths, played_time, progress);
ScanDirectory(dir.c_str(), true, only_cache, excluded_paths, played_time, custom_attributes_ini, progress);
progress->SetProgressValue(++directory_counter);
}
}
@@ -804,6 +824,8 @@ bool GameList::RescanPath(const std::string& path)
std::unique_lock lock(s_mutex);
const PlayedTimeMap played_time(LoadPlayedTimeMap(GetPlayedTimeFile()));
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
custom_attributes_ini.Load();
{
// cancel if excluded
@@ -813,7 +835,7 @@ bool GameList::RescanPath(const std::string& path)
}
// re-scan!
if (!ScanFile(path, sd.ModificationTime, lock, played_time))
if (!ScanFile(path, sd.ModificationTime, lock, played_time, custom_attributes_ini))
return true;
// update cache.. this is far from ideal, but since everything's variable length, all we can do.
@@ -1319,3 +1341,74 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
return true;
}
std::string GameList::GetCustomPropertiesFile()
{
return Path::Combine(EmuFolders::Settings, "custom_properties.ini");
}
void GameList::CheckCustomAttributesForPath(const std::string& path, bool& has_custom_title, bool& has_custom_region)
{
INISettingsInterface names(GetCustomPropertiesFile());
if (names.Load())
{
has_custom_title = names.ContainsValue(path.c_str(), "Title");
has_custom_region = names.ContainsValue(path.c_str(), "Region");
}
}
void GameList::SaveCustomTitleForPath(const std::string& path, const std::string& custom_title)
{
INISettingsInterface names(GetCustomPropertiesFile());
names.Load();
if (!custom_title.empty())
{
names.SetStringValue(path.c_str(), "Title", custom_title.c_str());
}
else
{
names.DeleteValue(path.c_str(), "Title");
}
if (names.Save())
{
// Let the cache update by rescanning
RescanPath(path);
}
}
void GameList::SaveCustomRegionForPath(const std::string& path, int custom_region)
{
INISettingsInterface names(GetCustomPropertiesFile());
names.Load();
if (custom_region >= 0)
{
names.SetIntValue(path.c_str(), "Region", custom_region);
}
else
{
names.DeleteValue(path.c_str(), "Region");
}
if (names.Save())
{
// Let the cache update by rescanning
RescanPath(path);
}
}
std::string GameList::GetCustomTitleForPath(const std::string& path)
{
std::string ret;
std::unique_lock lock(s_mutex);
const GameList::Entry* entry = GetEntryForPath(path.c_str());
if (entry)
{
ret = entry->title;
}
return ret;
}
+6
View File
@@ -154,4 +154,10 @@ namespace GameList
/// the use_serial parameter. save_callback optionall takes the entry and the path the new cover is saved to.
bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial = false, ProgressCallback* progress = nullptr,
std::function<void(const Entry*, std::string)> save_callback = {});
// Custom properties support
void CheckCustomAttributesForPath(const std::string& path, bool& has_custom_title, bool& has_custom_region);
void SaveCustomTitleForPath(const std::string& path, const std::string& custom_title);
void SaveCustomRegionForPath(const std::string& path, int custom_region);
std::string GetCustomTitleForPath(const std::string& path);
} // namespace GameList
+1 -1
View File
@@ -329,7 +329,7 @@ static u32 WRITERING_DMA(u32* pMem, u32 qwc)
// so we can get away with transferring "most" of it when it's a big packet.
// Use Wallace and Gromit Project Zoo or The Suffering for testing
if (qwc > 64)
qwc = qwc - 64;
qwc = qwc * 0.5f;
else
qwc = std::min(qwc, 8u);
}
+1 -1
View File
@@ -95,7 +95,7 @@ __fi void IPUProcessInterrupt()
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
IPUWorker();
if (ipuRegs.ctrl.BUSY)
if (ipuRegs.ctrl.BUSY && !IPU1Status.DataRequested && !(cpuRegs.interrupt & 1 << IPU_PROCESS))
{
CPU_INT(IPU_PROCESS, ProcessedData ? ProcessedData : 64);
}
+4 -2
View File
@@ -93,6 +93,8 @@ int IPU_Fifo_Input::write(const u32* pMem, int size)
if (g_BP.IFC == 8)
IPU1Status.DataRequested = false;
CPU_INT(IPU_PROCESS, transfer_size * BIAS);
return transfer_size;
}
@@ -106,7 +108,7 @@ int IPU_Fifo_Input::read(void *value)
if(ipu1ch.chcr.STR && cpuRegs.eCycle[4] == 0x9999)
{
CPU_INT( DMAC_TO_IPU, 4);
CPU_INT( DMAC_TO_IPU, std::min(8U, ipu1ch.qwc));
}
if (g_BP.IFC == 0) return 0;
@@ -183,7 +185,7 @@ void WriteFIFO_IPUin(const mem128_t* value)
{
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
{
CommandExecuteQueued = true;
CommandExecuteQueued = false;
CPU_INT(IPU_PROCESS, 8);
}
}
+37 -25
View File
@@ -983,7 +983,7 @@ __fi static void finishmpeg2sliceIDEC()
__ri static bool mpeg2sliceIDEC()
{
u16 code;
static bool ready_to_decode = true;
switch (ipu_cmd.pos[0])
{
case 0:
@@ -1007,6 +1007,13 @@ __ri static bool mpeg2sliceIDEC()
ipu_cmd.pos[0] = 2;
while (1)
{
if (ready_to_decode == true)
{
ready_to_decode = false;
CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now.
ipu_cmd.pos[0] = 2;
return false;
}
// IPU0 isn't ready for data, so let's wait for it to be
if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[1] <= 2)
{
@@ -1116,15 +1123,13 @@ __ri static bool mpeg2sliceIDEC()
ipu_dither(rgb32, rgb16, decoder.dte);
decoder.SetOutputTo(rgb16);
}
ProcessedData += decoder.ipu0_data;
ipu_cmd.pos[1] = 2;
return false;
[[fallthrough]];
case 2:
{
pxAssert(decoder.ipu0_data > 0);
ready_to_decode = true;
uint read = ipu_fifo.out.write((u32*)decoder.GetIpuDataPtr(), decoder.ipu0_data);
decoder.AdvanceIpuDataBy(read);
@@ -1205,6 +1210,7 @@ __ri static bool mpeg2sliceIDEC()
ipu_cmd.pos[1] = 0;
ipu_cmd.pos[2] = 0;
ready_to_decode = true;
}
finish_idec:
@@ -1269,6 +1275,7 @@ finish_idec:
__fi static bool mpeg2_slice()
{
int DCT_offset, DCT_stride;
static bool ready_to_decode = true;
macroblock_8& mb8 = decoder.mb8;
macroblock_16& mb16 = decoder.mb16;
@@ -1299,7 +1306,12 @@ __fi static bool mpeg2_slice()
case 2:
ipu_cmd.pos[0] = 2;
if (ready_to_decode == true)
{
ready_to_decode = false;
CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now.
return false;
}
// IPU0 isn't ready for data, so let's wait for it to be
if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[0] <= 3)
{
@@ -1406,23 +1418,23 @@ __fi static bool mpeg2_slice()
{
if (decoder.macroblock_modes & MACROBLOCK_PATTERN)
{
switch(ipu_cmd.pos[1])
switch (ipu_cmd.pos[1])
{
case 0:
{
// Get coded block pattern
const CBPtab* tab;
u16 code = UBITS(16);
{
// Get coded block pattern
const CBPtab* tab;
u16 code = UBITS(16);
if (code >= 0x2000)
tab = CBP_7 + (UBITS(7) - 16);
else
tab = CBP_9 + UBITS(9);
if (code >= 0x2000)
tab = CBP_7 + (UBITS(7) - 16);
else
tab = CBP_9 + UBITS(9);
DUMPBITS(tab->len);
decoder.coded_block_pattern = tab->cbp;
}
[[fallthrough]];
DUMPBITS(tab->len);
decoder.coded_block_pattern = tab->cbp;
}
[[fallthrough]];
case 1:
if (decoder.coded_block_pattern & 0x20)
@@ -1490,9 +1502,11 @@ __fi static bool mpeg2_slice()
}
break;
jNO_DEFAULT;
jNO_DEFAULT;
}
}
else
DevCon.Warning("No macroblock mode");
}
// Send The MacroBlock via DmaIpuFrom
@@ -1500,14 +1514,11 @@ __fi static bool mpeg2_slice()
coded_block_pattern = decoder.coded_block_pattern;
decoder.SetOutputTo(mb16);
ProcessedData += decoder.ipu0_data;
ipu_cmd.pos[0] = 3;
return false;
[[fallthrough]];
case 3:
{
pxAssert(decoder.ipu0_data > 0);
ready_to_decode = true;
uint read = ipu_fifo.out.write((u32*)decoder.GetIpuDataPtr(), decoder.ipu0_data);
decoder.AdvanceIpuDataBy(read);
@@ -1577,6 +1588,7 @@ __fi static bool mpeg2_slice()
break;
}
ready_to_decode = true;
return true;
}
+4 -9
View File
@@ -149,12 +149,6 @@ void IPU1dma()
}
}
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
{
CommandExecuteQueued = true;
CPU_INT(IPU_PROCESS, totalqwc * BIAS);
}
IPU_LOG("Completed Call IPU1 DMA QWC Remaining %x Finished %d In Progress %d tadr %x", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1ch.tadr);
}
@@ -198,13 +192,14 @@ void IPU0dma()
dmacRegs.stadr.ADDR = ipu0ch.madr;
}
IPU_INT_FROM( readsize * BIAS );
if (!ipu0ch.qwc)
IPU_INT_FROM(readsize * BIAS);
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
{
CommandExecuteQueued = true;
CommandExecuteQueued = false;
CPU_SET_DMASTALL(DMAC_FROM_IPU, true);
CPU_INT(IPU_PROCESS, readsize * BIAS);
IPUProcessInterrupt();
}
}
-2
View File
@@ -3232,8 +3232,6 @@ void FullscreenUI::DrawGraphicsSettingsPage()
static constexpr const char* s_auto_flush_options[] = {
"Disabled (Default)", "Enabled (Sprites Only)", "Enabled (All Primitives)"};
DrawIntListSetting(bsi, "Half-Bottom Override", "Control the half-screen fix detection on texture shuffling.", "EmuCore/GS",
"UserHacks_Half_Bottom_Override", -1, s_generic_options, std::size(s_generic_options), -1);
DrawIntListSetting(bsi, "CPU Sprite Render Size", "Uses software renderer to draw texture decompression-like sprites.",
"EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0, s_cpu_sprite_render_bw_options, std::size(s_cpu_sprite_render_bw_options));
DrawIntListSetting(bsi, "CPU Sprite Render Level", "Determines filter level for CPU sprite render.", "EmuCore/GS",
+5 -4
View File
@@ -138,9 +138,10 @@ bool ImGuiManager::Initialize()
io.BackendUsingLegacyNavInputArray = 0;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
s_window_width = static_cast<float>(g_gs_device->GetWindowWidth());
s_window_height = static_cast<float>(g_gs_device->GetWindowHeight());
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
io.DisplaySize.x = static_cast<float>(g_gs_device->GetWindowWidth());
io.DisplaySize.y = static_cast<float>(g_gs_device->GetWindowHeight());
io.DisplaySize = ImVec2(s_window_width, s_window_height);
SetKeyMap();
SetStyle();
@@ -656,7 +657,7 @@ void ImGuiManager::DrawOSDMessages()
const float margin = std::ceil(10.0f * scale);
const float padding = std::ceil(8.0f * scale);
const float rounding = std::ceil(5.0f * scale);
const float max_width = ImGui::GetIO().DisplaySize.x - (margin + padding) * 2.0f;
const float max_width = s_window_width - (margin + padding) * 2.0f;
float position_x = margin;
float position_y = margin;
@@ -679,7 +680,7 @@ void ImGuiManager::DrawOSDMessages()
const float opacity = std::min(time_remaining, 1.0f);
const u32 alpha = static_cast<u32>(opacity * 255.0f);
if (position_y >= ImGui::GetIO().DisplaySize.y)
if (position_y >= s_window_height)
break;
const ImVec2 pos(position_x, position_y);
+8 -10
View File
@@ -118,9 +118,9 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y)
{ \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, \
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
@@ -261,7 +261,7 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y)
{
const ImVec2 history_size(200.0f * scale, 50.0f * scale);
ImGui::SetNextWindowSize(ImVec2(history_size.x, history_size.y));
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - margin - history_size.x, position_y));
ImGui::SetNextWindowPos(ImVec2(GetWindowWidth() - margin - history_size.x, position_y));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.25f));
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_PlotLines, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
@@ -390,8 +390,6 @@ void ImGuiManager::DrawSettingsOverlay()
APPEND("AF={} ", EmuConfig.GS.MaxAnisotropy);
if (GSConfig.Dithering != 2)
APPEND("DI={} ", GSConfig.Dithering);
if (GSConfig.UserHacks_HalfBottomOverride >= 0)
APPEND("HBO={} ", GSConfig.UserHacks_HalfBottomOverride);
if (GSConfig.UserHacks_HalfPixelOffset > 0)
APPEND("HPO={} ", GSConfig.UserHacks_HalfPixelOffset);
if (GSConfig.UserHacks_RoundSprite > 0)
@@ -451,15 +449,15 @@ void ImGuiManager::DrawSettingsOverlay()
const float shadow_offset = 1.0f * scale;
const float margin = 10.0f * scale;
ImFont* font = ImGuiManager::GetFixedFont();
const float position_y = ImGui::GetIO().DisplaySize.y - margin - font->FontSize;
const float position_y = GetWindowHeight() - margin - font->FontSize;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
ImVec2 text_size =
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, text.c_str(), text.c_str() + text.length(), nullptr);
dl->AddText(font, font->FontSize,
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100),
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100),
text.c_str(), text.c_str() + text.length());
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), IM_COL32(255, 255, 255, 255),
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), IM_COL32(255, 255, 255, 255),
text.c_str(), text.c_str() + text.length());
}
@@ -631,9 +629,9 @@ void ImGuiManager::DrawInputRecordingOverlay(float& position_y)
{ \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, \
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
// TODO - icon list that would be nice to add
+8 -3
View File
@@ -616,7 +616,6 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(SkipDrawStart) &&
OpEqu(UserHacks_AutoFlush) &&
OpEqu(UserHacks_HalfBottomOverride) &&
OpEqu(UserHacks_HalfPixelOffset) &&
OpEqu(UserHacks_RoundSprite) &&
OpEqu(UserHacks_TCOffsetX) &&
@@ -814,7 +813,6 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
GSSettingIntEx(SkipDrawEnd, "UserHacks_SkipDraw_End");
SkipDrawEnd = std::max(SkipDrawStart, SkipDrawEnd);
GSSettingIntEx(UserHacks_HalfBottomOverride, "UserHacks_Half_Bottom_Override");
GSSettingIntEx(UserHacks_HalfPixelOffset, "UserHacks_HalfPixelOffset");
GSSettingIntEx(UserHacks_RoundSprite, "UserHacks_round_sprite_offset");
GSSettingIntEx(UserHacks_TCOffsetX, "UserHacks_TCOffsetX");
@@ -882,7 +880,6 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
UserHacks_NativePaletteDraw = false;
UserHacks_DisableSafeFeatures = false;
UserHacks_DisableRenderFixes = false;
UserHacks_HalfBottomOverride = -1;
UserHacks_HalfPixelOffset = 0;
UserHacks_RoundSprite = 0;
UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
@@ -1429,6 +1426,7 @@ Pcsx2Config::AchievementsOptions::AchievementsOptions()
Notifications = true;
SoundEffects = true;
PrimedIndicators = true;
NotificationsDuration = 5;
}
void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
@@ -1444,6 +1442,13 @@ void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBool(Notifications);
SettingsWrapBitBool(SoundEffects);
SettingsWrapBitBool(PrimedIndicators);
SettingsWrapBitfield(NotificationsDuration);
if (wrap.IsLoading())
{
//Clamp in case setting was updated manually using the INI
NotificationsDuration = std::clamp(NotificationsDuration, 3, 10);
}
}
#endif
-3
View File
@@ -109,11 +109,8 @@ void Pad::LoadConfig(const SettingsInterface& si)
const float axis_deadzone = si.GetFloatValue(section.c_str(), "Deadzone", Pad::DEFAULT_STICK_DEADZONE);
const float axis_scale = si.GetFloatValue(section.c_str(), "AxisScale", Pad::DEFAULT_STICK_SCALE);
const float trigger_deadzone = si.GetFloatValue(section.c_str(), "TriggerDeadzone", Pad::DEFAULT_TRIGGER_DEADZONE);
const float trigger_scale = si.GetFloatValue(section.c_str(), "TriggerScale", Pad::DEFAULT_TRIGGER_SCALE);
const float button_deadzone = si.GetFloatValue(section.c_str(), "ButtonDeadzone", Pad::DEFAULT_BUTTON_DEADZONE);
pad->SetAxisScale(axis_deadzone, axis_scale);
pad->SetTriggerScale(trigger_deadzone, trigger_scale);
pad->SetButtonDeadzone(button_deadzone);
if (ci->vibration_caps != Pad::VibrationCapabilities::NoVibration)
-1
View File
@@ -45,7 +45,6 @@ public: // Public members
virtual void Set(u32 index, float value) = 0;
virtual void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> right) = 0;
virtual void SetAxisScale(float deadzone, float scale) = 0;
virtual void SetTriggerScale(float deadzone, float scale) = 0;
virtual float GetVibrationScale(u32 motor) const = 0;
virtual void SetVibrationScale(u32 motor, float scale) = 0;
virtual float GetPressureModifier() const = 0;
+2 -10
View File
@@ -639,8 +639,8 @@ void PadDualshock2::Set(u32 index, float value)
}
else if (IsTriggerKey(index))
{
const float s_value = std::clamp(value * this->triggerScale, 0.0f, 1.0f);
const float dz_value = (this->triggerDeadzone > 0.0f && s_value < this->triggerDeadzone) ? 0.0f : s_value;
const float s_value = std::clamp(value, 0.0f, 1.0f);
const float dz_value = (this->buttonDeadzone > 0.0f && s_value < this->buttonDeadzone) ? 0.0f : s_value;
this->rawInputs[index] = static_cast<u8>(dz_value * 255.0f);
if (dz_value > 0.0f)
this->buttons &= ~(1u << bitmaskMapping[index]);
@@ -727,12 +727,6 @@ void PadDualshock2::SetAxisScale(float deadzone, float scale)
this->axisScale = scale;
}
void PadDualshock2::SetTriggerScale(float deadzone, float scale)
{
this->triggerDeadzone = deadzone;
this->triggerScale = scale;
}
float PadDualshock2::GetVibrationScale(u32 motor) const
{
return this->vibrationScale[motor];
@@ -828,8 +822,6 @@ bool PadDualshock2::Freeze(StateWrapper& sw)
sw.Do(&vibrationMotors);
sw.Do(&axisScale);
sw.Do(&axisDeadzone);
sw.Do(&triggerScale);
sw.Do(&triggerDeadzone);
sw.Do(&vibrationScale);
sw.Do(&pressureModifier);
sw.Do(&buttonDeadzone);
-3
View File
@@ -88,8 +88,6 @@ private:
std::array<u8, VIBRATION_MOTORS> vibrationMotors;
float axisScale;
float axisDeadzone;
float triggerScale;
float triggerDeadzone;
std::array<float, 2> vibrationScale;
// When the pressure modifier binding is activated, this is multiplied against
// all values in pressures, to artificially reduce pressures and give players
@@ -133,7 +131,6 @@ public:
void Set(u32 index, float value) override;
void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> right) override;
void SetAxisScale(float deadzone, float scale) override;
void SetTriggerScale(float deadzone, float scale) override;
float GetVibrationScale(u32 motor) const override;
void SetVibrationScale(u32 motor, float scale) override;
float GetPressureModifier() const override;
-5
View File
@@ -337,11 +337,6 @@ void PadGuitar::SetAxisScale(float deadzone, float scale)
this->whammyAxisScale = scale;
}
void PadGuitar::SetTriggerScale(float deadzone, float scale)
{
}
float PadGuitar::GetVibrationScale(u32 motor) const
{
return 0;
-1
View File
@@ -70,7 +70,6 @@ public:
void Set(u32 index, float value) override;
void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> right) override;
void SetAxisScale(float deadzone, float scale) override;
void SetTriggerScale(float deadzone, float scale) override;
float GetVibrationScale(u32 motor) const override;
void SetVibrationScale(u32 motor, float scale) override;
float GetPressureModifier() const override;
-5
View File
@@ -60,11 +60,6 @@ void PadNotConnected::SetAxisScale(float deadzone, float scale)
}
void PadNotConnected::SetTriggerScale(float deadzone, float scale)
{
}
float PadNotConnected::GetVibrationScale(u32 motor) const
{
return 0;
-1
View File
@@ -29,7 +29,6 @@ public:
void Set(u32 index, float value) override;
void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> right) override;
void SetAxisScale(float deadzone, float scale) override;
void SetTriggerScale(float deadzone, float scale) override;
float GetVibrationScale(u32 motor) const override;
void SetVibrationScale(u32 motor, float scale) override;
float GetPressureModifier() const override;
-2
View File
@@ -109,8 +109,6 @@ namespace Pad
// Default stick deadzone/sensitivity.
static constexpr float DEFAULT_STICK_DEADZONE = 0.0f;
static constexpr float DEFAULT_STICK_SCALE = 1.33f;
static constexpr float DEFAULT_TRIGGER_DEADZONE = 0.0f;
static constexpr float DEFAULT_TRIGGER_SCALE = 1.0f;
static constexpr float DEFAULT_MOTOR_SCALE = 1.0f;
static constexpr float DEFAULT_PRESSURE_MODIFIER = 0.5f;
static constexpr float DEFAULT_BUTTON_DEADZONE = 0.0f;
+1 -1
View File
@@ -37,7 +37,7 @@ enum class FreezeAction
// [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated.
static const u32 g_SaveVersion = (0x9A3C << 16) | 0x0000;
static const u32 g_SaveVersion = (0x9A3D << 16) | 0x0000;
// the freezing data between submodules and core
+1 -1
View File
@@ -15,4 +15,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 29;
static constexpr u32 SHADER_CACHE_VERSION = 33;
+1 -1
View File
@@ -267,7 +267,7 @@ namespace usb_eyetoy
return devList;
}
static int v4l_open(std::string selectedDevice)
static int v4l_open(const std::string& selectedDevice)
{
char dev_name[64];
struct v4l2_capability cap;

Some files were not shown because too many files have changed in this diff Show More