Compare commits

...

39 Commits

Author SHA1 Message Date
Ty Lamontagne
9a75509614 EE Cache: Fix up TLB related register fetching 2024-12-27 14:18:32 -05:00
Ty Lamontagne
c513a29bcf EE Cache: Freeze cached entries in sstates
[SAVEVERSION+]
2024-12-27 14:18:32 -05:00
Ty Lamontagne
5d39c884b5 R5900: Improve the EE cache performance with SIMD 2024-12-27 14:18:32 -05:00
TheLastRar
6a0f811812 Build: Always set EnhancedInstructionSet for AVX2 configs
Replaces the march arguments for clang-cl
2024-12-24 22:05:50 -05:00
TheLastRar
f509fb6950 CI/Appimage: Suppress error when no tags are present 2024-12-24 12:00:56 -05:00
Ziemas
33a475a456 DebugTools: Fix GCC build, missing include 2024-12-24 13:47:49 +01:00
JordanTheToaster
d2fb90a700 GameDB: Various fixes 2024-12-23 19:40:29 -05:00
PCSX2 Bot
a34467dccd [ci skip] PAD: Update to latest controller database. 2024-12-23 17:58:45 +01:00
Hallkezz
c6d0f5e3cd GameDB: Change recommended blending level 2 to 4 for Twisted Metal: Head-On [Extra Twisted Edition]. (#12107)
Fixes menu text brightness in Twisted Metal: Head-On [Extra Twisted Edition].
2024-12-22 13:03:40 +01:00
PCSX2 Bot
34c9590cf1 [ci skip] Qt: Update Base Translation. 2024-12-18 19:06:27 -05:00
Ty Lamontagne
a2c7542e48 Savestates: Warn on savestate load and saves without mcd activity
[SAVEVERSION+]
2024-12-18 18:01:30 -05:00
TheLastRar
72a9f18456 3rdparty: Sync libchdr to commit cb077337d53392454e7100a0fd07139ca678e527 2024-12-18 17:58:44 -05:00
TheLastRar
7f59757eea 3rdparty/libchdr: Purge almost all remaining patches
Leaving only the chd_read_header_* functions, of which exists an equivalent in later libchdr versions
2024-12-18 17:58:44 -05:00
TheLastRar
3b89020082 3rdparty/libchdr: Purge now unused patches 2024-12-18 16:23:49 -05:00
TheLastRar
a7b07eb53f ChdReader: Use core_file to implement precaching 2024-12-18 16:23:49 -05:00
TheLastRar
58d13dac34 FileSystem: Add ReadFileWithPartialProgress for multiple files 2024-12-18 16:23:49 -05:00
Ty Lamontagne
5a8921dd22 IOPBios: Defer to iopMemSafeReadBytes when HLEing writes 2024-12-18 16:15:07 -05:00
Ty Lamontagne
f964dfaa5e IOPBios: Defer to iopMemSafeWriteBytes when HLEing reads 2024-12-18 16:15:07 -05:00
PCSX2 Bot
17274eb397 [ci skip] Qt: Update Base Translation. 2024-12-17 20:54:52 -05:00
TheLastRar
2f0b00a7a1 ChdFileReader: Migrate libchdr patch into PCSX2
Added function didn't need to be in libchdr
2024-12-17 13:35:10 -05:00
Mrlinkwii
260380abec CI: disable builds on controller database update 2024-12-17 19:22:03 +01:00
KamFretoZ
57fc87061d Cheats: Update notes on how PNACH 2.0 works
Co-Authored-By: Mrlinkwii <Mrlinkwii@users.noreply.github.com>
2024-12-17 11:27:45 -05:00
KamFretoZ
cc9f7e723a Qt/Patches: Get rid of the global WS/NI toggle 2024-12-17 11:27:45 -05:00
PCSX2 Bot
6a41e05694 PAD: Update to latest controller database. 2024-12-16 11:06:52 -05:00
TheLastRar
c8e1dc5328 ChdFileReader: Use core_file instead of modifing chd_open_file 2024-12-16 09:39:46 -05:00
lightningterror
c2ee5a0234 GS/HW: Get the proper context for the next draw if it's a split texture shuffle.
Draw might switch the context so make sure we get the correct one.
2024-12-16 08:37:39 +01:00
nassau-tk
3cafd2dc69 GameDB: Japanese game updates 2024-12-15 01:33:40 -06:00
JordanTheToaster
4b8890c438 3rdparty: Sync vkmemoryallocator to commit 5a53a198945ba8260fbc58fadb788745ce6aa263 2024-12-14 13:41:32 -05:00
JordanTheToaster
19882dc160 3rdparty: Sync d3d12memalloc to commit da380f69bd4547cd776c525ae225bb9d13df94e2 2024-12-14 13:41:32 -05:00
chaoticgd
0c21023bb2 Path: Prevent Path::RealPath from returning '.' and '..' components 2024-12-14 13:41:01 -05:00
chaoticgd
68e6ede47e Path: Add tests for Path::RealPath 2024-12-14 13:41:01 -05:00
chaoticgd
1ed3001358 FileSystem: Add CreateSymLink function 2024-12-14 13:41:01 -05:00
chaoticgd
b8ff171127 FileSystem: Improve directory deletion test slightly 2024-12-14 13:41:01 -05:00
lightningterror
7d16a915ed GS/HW: Adjust how we handle dithering on blend mix.
Allow dither adjust regardless of alpha. usually it is clamed to 1 anyway so we can expand it if alpha max is higher than 128.

Expand dither adjust to work in rev subtract conditions.
2024-12-13 08:27:00 +01:00
lightningterror
ecc24ce2e9 GS/TC: Use proper alpha min max for palettes.
If it's an old source made from target make sure it isn't a palette, alphas need to be used from the palette then.
2024-12-13 08:27:00 +01:00
TheLastRar
65748351c7 ChdFileReader: Correct extension check 2024-12-12 08:39:28 -05:00
Ty Lamontagne
a60489b6fd FileSystem: Add a case-insensitive match for nvm and mec file loading. 2024-12-12 08:37:16 -05:00
TheLastRar
e8dbcc31aa SaveState: Fix comparison warning 2024-12-10 21:26:14 +01:00
PCSX2 Bot
5dbaf194c5 PAD: Update to latest controller database. 2024-12-09 12:14:31 -05:00
48 changed files with 12478 additions and 9316 deletions

View File

@@ -22,7 +22,7 @@ jobs:
uses: peter-evans/create-pull-request@v7
with:
title: "PAD: Update to latest controller database"
commit-message: "PAD: Update to latest controller database."
commit-message: "[ci skip] PAD: Update to latest controller database."
committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
body: "Weekly automatic update of SDL Controller DB."

View File

@@ -206,7 +206,7 @@ GIT_VERSION=$(git tag --points-at HEAD)
if [[ "${GIT_VERSION}" == "" ]]; then
# In the odd event that we run this script before the release gets tagged.
GIT_VERSION=$(git describe --tags)
GIT_VERSION=$(git describe --tags || true)
if [[ "${GIT_VERSION}" == "" ]]; then
GIT_VERSION=$(git rev-parse HEAD)
fi

View File

@@ -1,4 +1,4 @@
Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved.
Copyright (c) 2019-2024 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -2,7 +2,7 @@
Easy to integrate memory allocation library for Direct3D 12.
**Documentation:** Browse online: [D3D12 Memory Allocator](https://gpuopen-librariesandsdks.github.io/D3D12MemoryAllocator/html/) (generated from Doxygen-style comments in [src/D3D12MemAlloc.h](src/D3D12MemAlloc.h))
**Documentation:** Browse online: [D3D12 Memory Allocator](https://gpuopen-librariesandsdks.github.io/D3D12MemoryAllocator/html/) (generated from Doxygen-style comments in [include/D3D12MemAlloc.h](include/D3D12MemAlloc.h))
**License:** MIT. See [LICENSE.txt](LICENSE.txt)
@@ -36,6 +36,7 @@ Additional features:
- Customization and integration with custom engines: Predefine appropriate macros to provide your own implementation of external facilities used by the library, like assert, mutex, and atomic.
- Support for resource aliasing (overlap).
- Custom memory pools: Create a pool with desired parameters (e.g. fixed or limited maximum size, custom `D3D12_HEAP_PROPERTIES` and `D3D12_HEAP_FLAGS`) and allocate memory out of it.
- Support for GPU Upload Heaps from preview Agility SDK (needs compilation with `D3D12MA_OPTIONS16_SUPPORTED` macro).
- Linear allocator: Create a pool with linear algorithm and use it for much faster allocations and deallocations in free-at-once, stack, double stack, or ring buffer fashion.
- Defragmentation: Let the library move data around to free some memory blocks and make your allocations better compacted.
- Statistics: Obtain brief or detailed statistics about the amount of memory used, unused, number of allocated heaps, number of allocations etc. - globally and per memory heap type. Current memory usage and budget as reported by the system can also be queried.
@@ -101,15 +102,18 @@ This software package uses third party software:
For more information see [NOTICES.txt](NOTICES.txt).
# Software using this library
- **[The Forge](https://github.com/ConfettiFX/The-Forge)** - cross-platform rendering framework. Apache License 2.0.
- **[Wicked Engine<img src="https://github.com/turanszkij/WickedEngine/blob/master/Content/logo_small.png" width="28px" align="center"/>](https://github.com/turanszkij/WickedEngine)** - 3D engine with modern graphics
[Some other projects on GitHub](https://github.com/search?q=D3D12MemAlloc.h&type=Code) and some game development studios that use DX12 in their games.
# See also
- **[Vcpkg](https://github.com/Microsoft/vcpkg)** dependency manager from Microsoft offers a port of this library that is easy to install.
- **[Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/)** - equivalent library for Vulkan. License: MIT.
- **[d3d12ma.c](https://github.com/milliewalky/d3d12ma.c)** - C bindings for this library. Author: Mateusz Maciejewski (Matt Walky). License: MIT.
- **[TerraFX.Interop.D3D12MemoryAllocator](https://github.com/terrafx/terrafx.interop.d3d12memoryallocator)** - interop bindings for this library for C#, as used by [TerraFX](https://github.com/terrafx/terrafx). License: MIT.
- **[Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/)** - equivalent library for Vulkan. License: MIT.
# Software using this library
- **[Qt Project](https://github.com/qt)**
- **[Ghost of Tsushima: Director's Cut PC](https://www.youtube.com/watch?v=cPKBDbCYctc&t=698s)** - Information avaliable in 11:38 of credits
- **[The Forge](https://github.com/ConfettiFX/The-Forge)** - cross-platform rendering framework. Apache License 2.0.
- **[Wicked Engine<img src="https://github.com/turanszkij/WickedEngine/blob/master/Content/logo_small.png" width="28px" align="center"/>](https://github.com/turanszkij/WickedEngine)** - 3D engine with modern graphics
[Some other projects on GitHub](https://github.com/search?q=D3D12MemAlloc.h&type=Code) and some game development studios that use DX12 in their games.

View File

@@ -24,9 +24,9 @@
/** \mainpage D3D12 Memory Allocator
<b>Version 2.1.0-development</b> (2023-07-05)
<b>Version 2.1.0-development</b> (2024-07-05)
Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved. \n
Copyright (c) 2019-2024 Advanced Micro Devices, Inc. All rights reserved. \n
License: MIT
Documentation of all members: D3D12MemAlloc.h
@@ -160,9 +160,9 @@ class D3D12MA_API IUnknownImpl : public IUnknown
{
public:
virtual ~IUnknownImpl() = default;
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
protected:
virtual void ReleaseThis() { delete this; }
private:
@@ -265,18 +265,18 @@ enum ALLOCATION_FLAGS
*/
ALLOCATION_FLAG_CAN_ALIAS = 0x10,
/** Allocation strategy that chooses smallest possible free range for the allocation
/** %Allocation strategy that chooses smallest possible free range for the allocation
to minimize memory usage and fragmentation, possibly at the expense of allocation time.
*/
ALLOCATION_FLAG_STRATEGY_MIN_MEMORY = 0x00010000,
/** Allocation strategy that chooses first suitable free range for the allocation -
/** %Allocation strategy that chooses first suitable free range for the allocation -
not necessarily in terms of the smallest offset but the one that is easiest and fastest to find
to minimize allocation time, possibly at the expense of allocation quality.
*/
ALLOCATION_FLAG_STRATEGY_MIN_TIME = 0x00020000,
/** Allocation strategy that chooses always the lowest offset in available space.
/** %Allocation strategy that chooses always the lowest offset in available space.
This is not the most efficient strategy but achieves highly packed data.
Used internally by defragmentation, not recomended in typical usage.
*/
@@ -402,8 +402,9 @@ struct TotalStatistics
- 1 = `D3D12_HEAP_TYPE_UPLOAD`
- 2 = `D3D12_HEAP_TYPE_READBACK`
- 3 = `D3D12_HEAP_TYPE_CUSTOM`
- 4 = `D3D12_HEAP_TYPE_GPU_UPLOAD`
*/
DetailedStatistics HeapType[4];
DetailedStatistics HeapType[5];
/** \brief One element for each memory segment group located at the following indices:
- 0 = `DXGI_MEMORY_SEGMENT_GROUP_LOCAL`
@@ -413,9 +414,9 @@ struct TotalStatistics
- When `IsUMA() == FALSE` (discrete graphics card):
- `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` (index 0) represents GPU memory
(resources allocated in `D3D12_HEAP_TYPE_DEFAULT` or `D3D12_MEMORY_POOL_L1`).
(resources allocated in `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_GPU_UPLOAD` or `D3D12_MEMORY_POOL_L1`).
- `DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL` (index 1) represents system memory
(resources allocated in `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`, or `D3D12_MEMORY_POOL_L0`).
(resources allocated in `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`, or `D3D12_MEMORY_POOL_L0`).
- When `IsUMA() == TRUE` (integrated graphics chip):
- `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` = (index 0) represents memory shared for all the resources.
- `DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL` = (index 1) is unused and always 0.
@@ -542,26 +543,6 @@ public:
*/
LPCWSTR GetName() const { return m_Name; }
/** \brief Returns `TRUE` if the memory of the allocation was filled with zeros when the allocation was created.
Returns `TRUE` only if the allocator is sure that the entire memory where the
allocation was created was filled with zeros at the moment the allocation was made.
Returns `FALSE` if the memory could potentially contain garbage data.
If it's a render-target or depth-stencil texture, it then needs proper
initialization with `ClearRenderTargetView`, `ClearDepthStencilView`, `DiscardResource`,
or a copy operation, as described on page
"ID3D12Device::CreatePlacedResource method - Notes on the required resource initialization" in Microsoft documentation.
Please note that rendering a fullscreen triangle or quad to the texture as
a render target is not a proper way of initialization!
See also articles:
- "Coming to DirectX 12: More control over memory allocation" on DirectX Developer Blog
- ["Initializing DX12 Textures After Allocation and Aliasing"](https://asawicki.info/news_1724_initializing_dx12_textures_after_allocation_and_aliasing).
*/
BOOL WasZeroInitialized() const { return m_PackedData.WasZeroInitialized(); }
protected:
void ReleaseThis() override;
@@ -620,29 +601,26 @@ private:
{
public:
PackedData() :
m_Type(0), m_ResourceDimension(0), m_ResourceFlags(0), m_TextureLayout(0), m_WasZeroInitialized(0) { }
m_Type(0), m_ResourceDimension(0), m_ResourceFlags(0), m_TextureLayout(0) { }
Type GetType() const { return (Type)m_Type; }
D3D12_RESOURCE_DIMENSION GetResourceDimension() const { return (D3D12_RESOURCE_DIMENSION)m_ResourceDimension; }
D3D12_RESOURCE_FLAGS GetResourceFlags() const { return (D3D12_RESOURCE_FLAGS)m_ResourceFlags; }
D3D12_TEXTURE_LAYOUT GetTextureLayout() const { return (D3D12_TEXTURE_LAYOUT)m_TextureLayout; }
BOOL WasZeroInitialized() const { return (BOOL)m_WasZeroInitialized; }
void SetType(Type type);
void SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension);
void SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags);
void SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout);
void SetWasZeroInitialized(BOOL wasZeroInitialized) { m_WasZeroInitialized = wasZeroInitialized ? 1 : 0; }
private:
UINT m_Type : 2; // enum Type
UINT m_ResourceDimension : 3; // enum D3D12_RESOURCE_DIMENSION
UINT m_ResourceFlags : 24; // flags D3D12_RESOURCE_FLAGS
UINT m_TextureLayout : 9; // enum D3D12_TEXTURE_LAYOUT
UINT m_WasZeroInitialized : 1; // BOOL
} m_PackedData;
Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, BOOL wasZeroInitialized);
Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment);
// Nothing here, everything already done in Release.
virtual ~Allocation() = default;
@@ -1065,6 +1043,16 @@ enum ALLOCATOR_FLAGS
to create its heaps on smaller alignment not suitable for MSAA textures.
*/
ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED = 0x8,
/** \brief Disable optimization that prefers creating small buffers as committed to avoid 64 KB alignment.
By default, the library prefers creating small buffers <= 32 KB as committed,
because drivers tend to pack them better, while placed buffers require 64 KB alignment.
This, however, may decrease performance, as creating committed resources involves allocation of implicit heaps,
which may take longer than creating placed resources in existing heaps.
Passing this flag will disable this committed preference globally for the allocator.
It can also be disabled for a single allocation by using #ALLOCATION_FLAG_STRATEGY_MIN_TIME.
*/
ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED = 0x10,
};
/// \brief Parameters of created Allocator object. To be used with CreateAllocator().
@@ -1130,6 +1118,15 @@ public:
- "ID3D12Device::GetCustomHeapProperties method (d3d12.h)"
*/
BOOL IsCacheCoherentUMA() const;
/** \brief Returns true if GPU Upload Heaps are supported on the current system.
When true, you can use `D3D12_HEAP_TYPE_GPU_UPLOAD`.
This flag is fetched from `D3D12_FEATURE_D3D12_OPTIONS16::GPUUploadHeapSupported`.
`#define D3D12MA_OPTIONS16_SUPPORTED 1` is needed for the compilation of this library. Otherwise the flag is always false.
*/
BOOL IsGPUUploadHeapSupported() const;
/** \brief Returns total amount of memory of specific segment group, in bytes.
\param memorySegmentGroup use `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` or DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL`.
@@ -1447,11 +1444,11 @@ enum VIRTUAL_ALLOCATION_FLAGS
*/
VIRTUAL_ALLOCATION_FLAG_UPPER_ADDRESS = ALLOCATION_FLAG_UPPER_ADDRESS,
/// Allocation strategy that tries to minimize memory usage.
/// %Allocation strategy that tries to minimize memory usage.
VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_MEMORY = ALLOCATION_FLAG_STRATEGY_MIN_MEMORY,
/// Allocation strategy that tries to minimize allocation time.
/// %Allocation strategy that tries to minimize allocation time.
VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_TIME = ALLOCATION_FLAG_STRATEGY_MIN_TIME,
/** \brief Allocation strategy that chooses always the lowest offset in available space.
/** %Allocation strategy that chooses always the lowest offset in available space.
This is not the most efficient strategy but achieves highly packed data.
*/
VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_OFFSET = ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,
@@ -1640,6 +1637,9 @@ ID3D12Device* device = (...)
D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
allocatorDesc.pDevice = device;
allocatorDesc.pAdapter = adapter;
// These flags are optional but recommended.
allocatorDesc.Flags = D3D12MA::ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED |
D3D12MA::ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED;
D3D12MA::Allocator* allocator;
HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator);
@@ -1864,6 +1864,9 @@ to obtain object D3D12MA::Pool. Example:
\code
POOL_DESC poolDesc = {};
poolDesc.HeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
// These flags are optional but recommended.
poolDesc.Flags = D3D12MA::POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED;
poolDesc.HeapFlags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
Pool* pool;
HRESULT hr = allocator->CreatePool(&poolDesc, &pool);

File diff suppressed because it is too large Load Diff

View File

@@ -10,12 +10,10 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __CDROM_H__
#define __CDROM_H__
#include <stdint.h>
#include <stdbool.h>
#include <libchdr/chdconfig.h>
/***************************************************************************
@@ -49,15 +47,11 @@ enum
enum
{
CD_SUB_NONE = 0, /* no subcode data stored */
CD_SUB_RAW_INTERLEAVED, /* raw interleaved 96 bytes per sector */
CD_SUB_RAW, /* raw non-interleaved 96 bytes per sector */
CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */
CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */
CD_SUB_NONE /* no subcode data stored */
};
const char* cdrom_get_subtype_string(uint32_t subtype);
bool cdrom_parse_subtype_string(const char* typestring, uint32_t* subtype, uint32_t* subsize);
#define CD_FLAG_GDROM 0x00000001 /* disc is a GD-ROM, all tracks should be stored with GD-ROM metadata */
#define CD_FLAG_GDROMLE 0x00000002 /* legacy GD-ROM, with little-endian CDDA data */
@@ -87,10 +81,10 @@ static inline uint32_t lba_to_msf(uint32_t lba)
{
uint8_t m, s, f;
m = (uint8_t)(lba / (60 * 75));
m = lba / (60 * 75);
lba -= m * (60 * 75);
s = (uint8_t)(lba / 75);
f = (uint8_t)(lba % 75);
s = lba / 75;
f = lba % 75;
return ((m / 10) << 20) | ((m % 10) << 16) |
((s / 10) << 12) | ((s % 10) << 8) |
@@ -113,6 +107,4 @@ static inline uint32_t lba_to_msf_alt(int lba)
return ret;
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* __CDROM_H__ */

View File

@@ -48,7 +48,6 @@ extern "C" {
#include <libchdr/coretypes.h>
#include <libchdr/chdconfig.h>
#include <stdbool.h>
/***************************************************************************
@@ -59,67 +58,67 @@ extern "C" {
V1 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] UINT32 length; // length of header (including tag and length fields)
[ 12] UINT32 version; // drive format version
[ 16] UINT32 flags; // flags (see below)
[ 20] UINT32 compression; // compression type
[ 24] UINT32 hunksize; // 512-byte sectors per hunk
[ 28] UINT32 totalhunks; // total # of hunks represented
[ 32] UINT32 cylinders; // number of cylinders on hard disk
[ 36] UINT32 heads; // number of heads on hard disk
[ 40] UINT32 sectors; // number of sectors on hard disk
[ 44] UINT8 md5[16]; // MD5 checksum of raw data
[ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t flags; // flags (see below)
[ 20] uint32_t compression; // compression type
[ 24] uint32_t hunksize; // 512-byte sectors per hunk
[ 28] uint32_t totalhunks; // total # of hunks represented
[ 32] uint32_t cylinders; // number of cylinders on hard disk
[ 36] uint32_t heads; // number of heads on hard disk
[ 40] uint32_t sectors; // number of sectors on hard disk
[ 44] uint8_t md5[16]; // MD5 checksum of raw data
[ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file
[ 76] (V1 header length)
V2 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] UINT32 length; // length of header (including tag and length fields)
[ 12] UINT32 version; // drive format version
[ 16] UINT32 flags; // flags (see below)
[ 20] UINT32 compression; // compression type
[ 24] UINT32 hunksize; // seclen-byte sectors per hunk
[ 28] UINT32 totalhunks; // total # of hunks represented
[ 32] UINT32 cylinders; // number of cylinders on hard disk
[ 36] UINT32 heads; // number of heads on hard disk
[ 40] UINT32 sectors; // number of sectors on hard disk
[ 44] UINT8 md5[16]; // MD5 checksum of raw data
[ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file
[ 76] UINT32 seclen; // number of bytes per sector
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t flags; // flags (see below)
[ 20] uint32_t compression; // compression type
[ 24] uint32_t hunksize; // seclen-byte sectors per hunk
[ 28] uint32_t totalhunks; // total # of hunks represented
[ 32] uint32_t cylinders; // number of cylinders on hard disk
[ 36] uint32_t heads; // number of heads on hard disk
[ 40] uint32_t sectors; // number of sectors on hard disk
[ 44] uint8_t md5[16]; // MD5 checksum of raw data
[ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file
[ 76] uint32_t seclen; // number of bytes per sector
[ 80] (V2 header length)
V3 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] UINT32 length; // length of header (including tag and length fields)
[ 12] UINT32 version; // drive format version
[ 16] UINT32 flags; // flags (see below)
[ 20] UINT32 compression; // compression type
[ 24] UINT32 totalhunks; // total # of hunks represented
[ 28] UINT64 logicalbytes; // logical size of the data (in bytes)
[ 36] UINT64 metaoffset; // offset to the first blob of metadata
[ 44] UINT8 md5[16]; // MD5 checksum of raw data
[ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file
[ 76] UINT32 hunkbytes; // number of bytes per hunk
[ 80] UINT8 sha1[20]; // SHA1 checksum of raw data
[100] UINT8 parentsha1[20];// SHA1 checksum of parent file
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t flags; // flags (see below)
[ 20] uint32_t compression; // compression type
[ 24] uint32_t totalhunks; // total # of hunks represented
[ 28] uint64_t logicalbytes; // logical size of the data (in bytes)
[ 36] uint64_t metaoffset; // offset to the first blob of metadata
[ 44] uint8_t md5[16]; // MD5 checksum of raw data
[ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file
[ 76] uint32_t hunkbytes; // number of bytes per hunk
[ 80] uint8_t sha1[20]; // SHA1 checksum of raw data
[100] uint8_t parentsha1[20];// SHA1 checksum of parent file
[120] (V3 header length)
V4 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] UINT32 length; // length of header (including tag and length fields)
[ 12] UINT32 version; // drive format version
[ 16] UINT32 flags; // flags (see below)
[ 20] UINT32 compression; // compression type
[ 24] UINT32 totalhunks; // total # of hunks represented
[ 28] UINT64 logicalbytes; // logical size of the data (in bytes)
[ 36] UINT64 metaoffset; // offset to the first blob of metadata
[ 44] UINT32 hunkbytes; // number of bytes per hunk
[ 48] UINT8 sha1[20]; // combined raw+meta SHA1
[ 68] UINT8 parentsha1[20];// combined raw+meta SHA1 of parent
[ 88] UINT8 rawsha1[20]; // raw data SHA1
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t flags; // flags (see below)
[ 20] uint32_t compression; // compression type
[ 24] uint32_t totalhunks; // total # of hunks represented
[ 28] uint64_t logicalbytes; // logical size of the data (in bytes)
[ 36] uint64_t metaoffset; // offset to the first blob of metadata
[ 44] uint32_t hunkbytes; // number of bytes per hunk
[ 48] uint8_t sha1[20]; // combined raw+meta SHA1
[ 68] uint8_t parentsha1[20];// combined raw+meta SHA1 of parent
[ 88] uint8_t rawsha1[20]; // raw data SHA1
[108] (V4 header length)
Flags:
@@ -131,17 +130,17 @@ extern "C" {
V5 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t compressors[4];// which custom compressors are used?
[ 32] uint64_t logicalbytes; // logical size of the data (in bytes)
[ 40] uint64_t mapoffset; // offset to the map
[ 48] uint64_t metaoffset; // offset to the first blob of metadata
[ 56] uint32_t hunkbytes; // number of bytes per hunk (512k maximum)
[ 60] uint32_t unitbytes; // number of bytes per unit within each hunk
[ 64] uint8_t rawsha1[20]; // raw data SHA1
[ 84] uint8_t sha1[20]; // combined raw+meta SHA1
[104] uint8_t parentsha1[20];// combined raw+meta SHA1 of parent
[ 8] uint32_t_t length; // length of header (including tag and length fields)
[ 12] uint32_t_t version; // drive format version
[ 16] uint32_t_t compressors[4];// which custom compressors are used?
[ 32] uint64_t_t logicalbytes; // logical size of the data (in bytes)
[ 40] uint64_t_t mapoffset; // offset to the map
[ 48] uint64_t_t metaoffset; // offset to the first blob of metadata
[ 56] uint32_t_t hunkbytes; // number of bytes per hunk (512k maximum)
[ 60] uint32_t_t unitbytes; // number of bytes per unit within each hunk
[ 64] uint8_t_t rawsha1[20]; // raw data SHA1
[ 84] uint8_t_t sha1[20]; // combined raw+meta SHA1
[104] uint8_t_t parentsha1[20];// combined raw+meta SHA1 of parent
[124] (V5 header length)
If parentsha1 != 0, we have a parent (no need for flags)
@@ -149,22 +148,22 @@ extern "C" {
V5 uncompressed map format:
[ 0] uint32_t offset; // starting offset / hunk size
[ 0] uint32_t_t offset; // starting offset / hunk size
V5 compressed map format header:
[ 0] uint32_t length; // length of compressed map
[ 0] uint32_t_t length; // length of compressed map
[ 4] UINT48 datastart; // offset of first block
[ 10] uint16_t crc; // crc-16 of the map
[ 12] uint8_t lengthbits; // bits used to encode complength
[ 13] uint8_t hunkbits; // bits used to encode self-refs
[ 14] uint8_t parentunitbits; // bits used to encode parent unit refs
[ 15] uint8_t reserved; // future use
[ 12] uint8_t_t lengthbits; // bits used to encode complength
[ 13] uint8_t_t hunkbits; // bits used to encode self-refs
[ 14] uint8_t_t parentunitbits; // bits used to encode parent unit refs
[ 15] uint8_t_t reserved; // future use
[ 16] (compressed header length)
Each compressed map entry, once expanded, looks like:
[ 0] uint8_t compression; // compression type
[ 0] uint8_t_t compression; // compression type
[ 1] UINT24 complength; // compressed length
[ 4] UINT48 offset; // offset
[ 10] uint16_t crc; // crc-16 of the data
@@ -221,7 +220,7 @@ extern "C" {
/* metadata parameters */
#define CHDMETATAG_WILDCARD 0
#define CHD_METAINDEX_APPEND ((UINT32)-1)
#define CHD_METAINDEX_APPEND ((uint32_t)-1)
/* metadata flags */
#define CHD_MDFLAGS_CHECKSUM 0x01 /* indicates data is checksummed */
@@ -259,7 +258,6 @@ extern "C" {
/* CHD open values */
#define CHD_OPEN_READ 1
#define CHD_OPEN_READWRITE 2
#define CHD_OPEN_TRANSFER_FILE 4 /* Freeing of the FILE* is now libchdr's responsibility if open was successful */
/* error types */
enum _chd_error
@@ -291,8 +289,7 @@ enum _chd_error
CHDERR_INVALID_STATE,
CHDERR_OPERATION_PENDING,
CHDERR_NO_ASYNC_OPERATION,
CHDERR_UNSUPPORTED_FORMAT,
CHDERR_CANCELLED,
CHDERR_UNSUPPORTED_FORMAT
};
typedef enum _chd_error chd_error;
@@ -310,32 +307,32 @@ typedef struct _chd_file chd_file;
typedef struct _chd_header chd_header;
struct _chd_header
{
UINT32 length; /* length of header data */
UINT32 version; /* drive format version */
UINT32 flags; /* flags field */
UINT32 compression[4]; /* compression type */
UINT32 hunkbytes; /* number of bytes per hunk */
UINT32 totalhunks; /* total # of hunks represented */
UINT64 logicalbytes; /* logical size of the data */
UINT64 metaoffset; /* offset in file of first metadata */
UINT64 mapoffset; /* TOOD V5 */
UINT8 md5[CHD_MD5_BYTES]; /* overall MD5 checksum */
UINT8 parentmd5[CHD_MD5_BYTES]; /* overall MD5 checksum of parent */
UINT8 sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */
UINT8 rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */
UINT8 parentsha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum of parent */
UINT32 unitbytes; /* TODO V5 */
UINT64 unitcount; /* TODO V5 */
UINT32 hunkcount; /* TODO V5 */
uint32_t length; /* length of header data */
uint32_t version; /* drive format version */
uint32_t flags; /* flags field */
uint32_t compression[4]; /* compression type */
uint32_t hunkbytes; /* number of bytes per hunk */
uint32_t totalhunks; /* total # of hunks represented */
uint64_t logicalbytes; /* logical size of the data */
uint64_t metaoffset; /* offset in file of first metadata */
uint64_t mapoffset; /* TOOD V5 */
uint8_t md5[CHD_MD5_BYTES]; /* overall MD5 checksum */
uint8_t parentmd5[CHD_MD5_BYTES]; /* overall MD5 checksum of parent */
uint8_t sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */
uint8_t rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */
uint8_t parentsha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum of parent */
uint32_t unitbytes; /* TODO V5 */
uint64_t unitcount; /* TODO V5 */
uint32_t hunkcount; /* TODO V5 */
/* map information */
UINT32 mapentrybytes; /* length of each entry in a map (V5) */
UINT8* rawmap; /* raw map data */
uint32_t mapentrybytes; /* length of each entry in a map (V5) */
uint8_t* rawmap; /* raw map data */
UINT32 obsolete_cylinders; /* obsolete field -- do not use! */
UINT32 obsolete_sectors; /* obsolete field -- do not use! */
UINT32 obsolete_heads; /* obsolete field -- do not use! */
UINT32 obsolete_hunksize; /* obsolete field -- do not use! */
uint32_t obsolete_cylinders; /* obsolete field -- do not use! */
uint32_t obsolete_sectors; /* obsolete field -- do not use! */
uint32_t obsolete_heads; /* obsolete field -- do not use! */
uint32_t obsolete_hunksize; /* obsolete field -- do not use! */
};
@@ -343,10 +340,10 @@ struct _chd_header
typedef struct _chd_verify_result chd_verify_result;
struct _chd_verify_result
{
UINT8 md5[CHD_MD5_BYTES]; /* overall MD5 checksum */
UINT8 sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */
UINT8 rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */
UINT8 metasha1[CHD_SHA1_BYTES]; /* SHA1 checksum of metadata */
uint8_t md5[CHD_MD5_BYTES]; /* overall MD5 checksum */
uint8_t sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */
uint8_t rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */
uint8_t metasha1[CHD_SHA1_BYTES]; /* SHA1 checksum of metadata */
};
@@ -372,10 +369,10 @@ struct _chd_verify_result
/* ----- CHD file management ----- */
/* create a new CHD file fitting the given description */
/* chd_error chd_create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); */
/* chd_error chd_create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t compression, chd_file *parent); */
/* same as chd_create(), but accepts an already-opened core_file object */
/* chd_error chd_create_file(core_file *file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); */
/* chd_error chd_create_file(core_file *file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t compression, chd_file *parent); */
/* open an existing CHD file */
CHD_EXPORT chd_error chd_open_core_file(core_file *file, int mode, chd_file *parent, chd_file **chd);
@@ -384,7 +381,6 @@ CHD_EXPORT chd_error chd_open(const char *filename, int mode, chd_file *parent,
/* precache underlying file */
CHD_EXPORT chd_error chd_precache(chd_file *chd);
CHD_EXPORT chd_error chd_precache_progress(chd_file* chd, bool(*progress)(size_t pos, size_t total, void* param), void* param);
/* close a CHD file */
CHD_EXPORT void chd_close(chd_file *chd);
@@ -392,13 +388,11 @@ CHD_EXPORT void chd_close(chd_file *chd);
/* return the associated core_file */
CHD_EXPORT core_file *chd_core_file(chd_file *chd);
/* return the overall size of a CHD, and any of its parents */
CHD_EXPORT UINT64 chd_get_compressed_size(chd_file* chd);
/* return an error string for the given CHD error */
CHD_EXPORT const char *chd_error_string(chd_error err);
/* ----- CHD header management ----- */
/* return a pointer to the extracted CHD header data */
@@ -408,21 +402,20 @@ CHD_EXPORT const chd_header *chd_get_header(chd_file *chd);
CHD_EXPORT chd_error chd_read_header_core_file(core_file *file, chd_header *header);
CHD_EXPORT chd_error chd_read_header_file(FILE *file, chd_header *header);
CHD_EXPORT chd_error chd_read_header(const char *filename, chd_header *header);
CHD_EXPORT bool chd_is_matching_parent(const chd_header* header, const chd_header* parent_header);
/* ----- core data read/write ----- */
/* read one hunk from the CHD file */
CHD_EXPORT chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer);
CHD_EXPORT chd_error chd_read(chd_file *chd, uint32_t hunknum, void *buffer);
/* ----- metadata management ----- */
/* get indexed metadata of a particular sort */
CHD_EXPORT chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag, UINT8 *resultflags);
CHD_EXPORT chd_error chd_get_metadata(chd_file *chd, uint32_t searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t *resultlen, uint32_t *resulttag, uint8_t *resultflags);
@@ -433,7 +426,7 @@ CHD_EXPORT chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 se
CHD_EXPORT chd_error chd_codec_config(chd_file *chd, int param, void *config);
/* return a string description of a codec */
CHD_EXPORT const char *chd_get_codec_name(UINT32 codec);
CHD_EXPORT const char *chd_get_codec_name(uint32_t codec);
#ifdef __cplusplus
}

View File

@@ -8,26 +8,13 @@
#include <streams/file_stream_transforms.h>
#endif
#ifndef ARRAY_LENGTH
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))
#if defined(__PS3__) || defined(__PSL1GHT__)
#undef UINT32
#undef UINT16
#undef UINT8
#undef INT32
#undef INT16
#undef INT8
#endif
typedef uint64_t UINT64;
typedef uint32_t UINT32;
typedef uint16_t UINT16;
typedef uint8_t UINT8;
typedef int64_t INT64;
typedef int32_t INT32;
typedef int16_t INT16;
typedef int8_t INT8;
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#endif
typedef struct chd_core_file {
/*
@@ -41,9 +28,9 @@ typedef struct chd_core_file {
* undefined because many implementations will seek to the end of the
* file and call ftell.
*
* on error, (UINT64)-1 is returned.
* on error, (uint64_t)-1 is returned.
*/
UINT64(*fsize)(struct chd_core_file*);
uint64_t(*fsize)(struct chd_core_file*);
/*
* should match the behavior of fread, except the FILE* argument at the end
@@ -55,7 +42,7 @@ typedef struct chd_core_file {
int (*fclose)(struct chd_core_file*);
// fseek clone
int (*fseek)(struct chd_core_file*, INT64, int);
int (*fseek)(struct chd_core_file*, int64_t, int);
} core_file;
static inline int core_fclose(core_file *fp) {
@@ -66,11 +53,11 @@ static inline size_t core_fread(core_file *fp, void *ptr, size_t len) {
return fp->fread(ptr, 1, len, fp);
}
static inline int core_fseek(core_file* fp, INT64 offset, int whence) {
static inline int core_fseek(core_file* fp, int64_t offset, int whence) {
return fp->fseek(fp, offset, whence);
}
static inline UINT64 core_fsize(core_file *fp)
static inline uint64_t core_fsize(core_file *fp)
{
return fp->fsize(fp);
}

View File

@@ -85,6 +85,6 @@ int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint
enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder);
enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder);
void huffman_build_lookup_table(struct huffman_decoder* decoder);
enum huffman_error huffman_build_lookup_table(struct huffman_decoder* decoder);
#endif

View File

@@ -20,35 +20,6 @@
#include <libchdr/cdrom.h>
const char* cdrom_get_subtype_string(uint32_t subtype)
{
switch (subtype)
{
case CD_SUB_RAW: return "RW";
case CD_SUB_RAW_INTERLEAVED: return "RW_RAW";
default: return "NONE";
}
}
bool cdrom_parse_subtype_string(const char* typestring, uint32_t* subtype, uint32_t* subsize)
{
// https://github.com/mamedev/mame/blob/d2d54fb8ed53a2e86d308067da8414f85b5929b0/src/lib/util/cdrom.cpp#L767
if (!strcmp(typestring, "RW"))
{
*subtype = CD_SUB_RAW;
*subsize = 96;
return true;
}
else if (!strcmp(typestring, "RW_RAW"))
{
*subtype = CD_SUB_RAW_INTERLEAVED;
*subsize = 96;
return true;
}
return false;
}
#ifdef WANT_RAW_DATA_SECTOR
/***************************************************************************

File diff suppressed because it is too large Load Diff

View File

@@ -230,7 +230,9 @@ enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, stru
return error;
/* build the lookup table */
huffman_build_lookup_table(decoder);
error = huffman_build_lookup_table(decoder);
if (error != HUFFERR_NONE)
return error;
/* determine final input length and report errors */
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
@@ -271,8 +273,16 @@ enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder,
/* then regenerate the tree */
error = huffman_assign_canonical_codes(smallhuff);
if (error != HUFFERR_NONE)
{
delete_huffman_decoder(smallhuff);
return error;
huffman_build_lookup_table(smallhuff);
}
error = huffman_build_lookup_table(smallhuff);
if (error != HUFFERR_NONE)
{
delete_huffman_decoder(smallhuff);
return error;
}
/* determine the maximum length of an RLE count */
temp = decoder->numcodes - 9;
@@ -308,7 +318,9 @@ enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder,
return error;
/* build the lookup table */
huffman_build_lookup_table(decoder);
error = huffman_build_lookup_table(decoder);
if (error != HUFFERR_NONE)
return error;
/* determine final input length and report errors */
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
@@ -523,8 +535,9 @@ enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decode
*-------------------------------------------------
*/
void huffman_build_lookup_table(struct huffman_decoder* decoder)
enum huffman_error huffman_build_lookup_table(struct huffman_decoder* decoder)
{
const lookup_value* lookupend = &decoder->lookup[(1u << decoder->maxbits)];
uint32_t curcode;
/* iterate over all codes */
for (curcode = 0; curcode < decoder->numcodes; curcode++)
@@ -533,9 +546,10 @@ void huffman_build_lookup_table(struct huffman_decoder* decoder)
struct node_t* node = &decoder->huffnode[curcode];
if (node->numbits > 0)
{
int shift;
lookup_value *dest;
lookup_value *destend;
int shift;
lookup_value *dest;
lookup_value *destend;
/* set up the entry */
lookup_value value = MAKE_LOOKUP(curcode, node->numbits);
@@ -543,8 +557,12 @@ void huffman_build_lookup_table(struct huffman_decoder* decoder)
shift = decoder->maxbits - node->numbits;
dest = &decoder->lookup[node->bits << shift];
destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
if (dest >= lookupend || destend >= lookupend || destend < dest)
return HUFFERR_INTERNAL_INCONSISTENCY;
while (dest <= destend)
*dest++ = value;
}
}
return HUFFERR_NONE;
}

View File

@@ -95,6 +95,7 @@ See also: [product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-me
- \subpage enabling_buffer_device_address
- \subpage vk_ext_memory_priority
- \subpage vk_amd_device_coherent_memory
- \subpage vk_khr_external_memory_win32
- \subpage general_considerations
- [Thread safety](@ref general_considerations_thread_safety)
- [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)
@@ -127,7 +128,9 @@ See documentation chapter: \ref statistics.
extern "C" {
#endif
#if !defined(VULKAN_H_)
#include <vulkan/vulkan.h>
#endif
#if !defined(VMA_VULKAN_VERSION)
#if defined(VK_VERSION_1_3)
@@ -240,6 +243,15 @@ extern "C" {
#endif
#endif
// Defined to 1 when VK_KHR_external_memory_win32 device extension is defined in Vulkan headers.
#if !defined(VMA_EXTERNAL_MEMORY_WIN32)
#if VK_KHR_external_memory_win32
#define VMA_EXTERNAL_MEMORY_WIN32 1
#else
#define VMA_EXTERNAL_MEMORY_WIN32 0
#endif
#endif
// Define these macros to decorate all public functions with additional code,
// before and after returned type, appropriately. This may be useful for
// exporting the functions when compiling VMA as a separate library. Example:
@@ -459,6 +471,15 @@ typedef enum VmaAllocatorCreateFlagBits
*/
VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100,
/**
Enables usage of VK_KHR_external_memory_win32 extension in the library.
You should set this flag if you found available and enabled this device extension,
while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
For more information, see \ref vk_khr_external_memory_win32.
*/
VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT = 0x00000200,
VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VmaAllocatorCreateFlagBits;
/// See #VmaAllocatorCreateFlagBits.
@@ -1033,6 +1054,11 @@ typedef struct VmaVulkanFunctions
/// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements;
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
PFN_vkGetMemoryWin32HandleKHR VMA_NULLABLE vkGetMemoryWin32HandleKHR;
#else
void* VMA_NULLABLE vkGetMemoryWin32HandleKHR;
#endif
} VmaVulkanFunctions;
/// Description of a Allocator to be created.
@@ -1810,6 +1836,9 @@ VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
\param allocator Allocator object.
\param pool Pool object.
\param[out] pPoolStats Statistics of specified pool.
Note that when using the pool from multiple threads, returned information may immediately
become outdated.
*/
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
VmaAllocator VMA_NOT_NULL allocator,
@@ -2050,6 +2079,40 @@ VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
VmaAllocation VMA_NOT_NULL allocation,
VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
#if VMA_EXTERNAL_MEMORY_WIN32
/**
\brief Given an allocation, returns Win32 handle that may be imported by other processes or APIs.
\param hTargetProcess Must be a valid handle to target process or null. If it's null, the function returns
handle for the current process.
\param[out] pHandle Output parameter that returns the handle.
The function fills `pHandle` with handle that can be used in target process.
The handle is fetched using function `vkGetMemoryWin32HandleKHR`.
When no longer needed, you must close it using:
\code
CloseHandle(handle);
\endcode
You can close it any time, before or after destroying the allocation object.
It is reference-counted internally by Windows.
Note the handle is returned for the entire `VkDeviceMemory` block that the allocation belongs to.
If the allocation is sub-allocated from a larger block, you may need to consider the offset of the allocation
(VmaAllocationInfo::offset).
If the function fails with `VK_ERROR_FEATURE_NOT_PRESENT` error code, please double-check
that VmaVulkanFunctions::vkGetMemoryWin32HandleKHR function pointer is set, e.g. either by using `VMA_DYNAMIC_VULKAN_FUNCTIONS`
or by manually passing it through VmaAllocatorCreateInfo::pVulkanFunctions.
For more information, see chapter \ref vk_khr_external_memory_win32.
*/
VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle);
#endif // VMA_EXTERNAL_MEMORY_WIN32
/** \brief Maps memory represented by given allocation and returns pointer to it.
Maps memory represented by given allocation to make it accessible to CPU code.
@@ -3097,7 +3160,7 @@ static void vma_aligned_free(void* VMA_NULLABLE ptr)
std::shared_mutex m_Mutex;
};
#define VMA_RW_MUTEX VmaRWMutex
#elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
#elif defined(_WIN32) && defined(WINVER) && defined(SRWLOCK_INIT) && WINVER >= 0x0600
// Use SRWLOCK from WinAPI.
// Minimum supported client = Windows Vista, server = Windows Server 2008.
class VmaRWMutex
@@ -3838,12 +3901,6 @@ struct VmaBufferImageUsage
const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0);
static void swap(VmaBufferImageUsage& lhs, VmaBufferImageUsage& rhs) noexcept
{
using std::swap;
swap(lhs.Value, rhs.Value);
}
VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo,
bool useKhrMaintenance5)
{
@@ -6073,6 +6130,84 @@ private:
#endif // _VMA_MAPPING_HYSTERESIS
#if VMA_EXTERNAL_MEMORY_WIN32
class VmaWin32Handle
{
public:
VmaWin32Handle() noexcept : m_hHandle(VMA_NULL) { }
explicit VmaWin32Handle(HANDLE hHandle) noexcept : m_hHandle(hHandle) { }
~VmaWin32Handle() noexcept { if (m_hHandle != VMA_NULL) { ::CloseHandle(m_hHandle); } }
VMA_CLASS_NO_COPY_NO_MOVE(VmaWin32Handle)
public:
// Strengthened
VkResult GetHandle(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, bool useMutex, HANDLE* pHandle) noexcept
{
*pHandle = VMA_NULL;
// Try to get handle first.
if (m_hHandle != VMA_NULL)
{
*pHandle = Duplicate(hTargetProcess);
return VK_SUCCESS;
}
VkResult res = VK_SUCCESS;
// If failed, try to create it.
{
VmaMutexLockWrite lock(m_Mutex, useMutex);
if (m_hHandle == VMA_NULL)
{
res = Create(device, memory, pvkGetMemoryWin32HandleKHR, &m_hHandle);
}
}
*pHandle = Duplicate(hTargetProcess);
return res;
}
operator bool() const noexcept { return m_hHandle != VMA_NULL; }
private:
// Not atomic
static VkResult Create(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE* pHandle) noexcept
{
VkResult res = VK_ERROR_FEATURE_NOT_PRESENT;
if (pvkGetMemoryWin32HandleKHR != VMA_NULL)
{
VkMemoryGetWin32HandleInfoKHR handleInfo{ };
handleInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
handleInfo.memory = memory;
handleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
res = pvkGetMemoryWin32HandleKHR(device, &handleInfo, pHandle);
}
return res;
}
HANDLE Duplicate(HANDLE hTargetProcess = VMA_NULL) const noexcept
{
if (!m_hHandle)
return m_hHandle;
HANDLE hCurrentProcess = ::GetCurrentProcess();
HANDLE hDupHandle = VMA_NULL;
if (!::DuplicateHandle(hCurrentProcess, m_hHandle, hTargetProcess ? hTargetProcess : hCurrentProcess, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
VMA_ASSERT(0 && "Failed to duplicate handle.");
}
return hDupHandle;
}
private:
HANDLE m_hHandle;
VMA_RW_MUTEX m_Mutex; // Protects access m_Handle
};
#else
class VmaWin32Handle
{
// ABI compatibility
void* placeholder = VMA_NULL;
VMA_RW_MUTEX placeholder2;
};
#endif // VMA_EXTERNAL_MEMORY_WIN32
#ifndef _VMA_DEVICE_MEMORY_BLOCK
/*
Represents a single block of device memory (`VkDeviceMemory`) with all the
@@ -6139,7 +6274,13 @@ public:
VkDeviceSize allocationLocalOffset,
VkImage hImage,
const void* pNext);
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult CreateWin32Handle(
const VmaAllocator hAllocator,
PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR,
HANDLE hTargetProcess,
HANDLE* pHandle)noexcept;
#endif // VMA_EXTERNAL_MEMORY_WIN32
private:
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
uint32_t m_MemoryTypeIndex;
@@ -6155,10 +6296,18 @@ private:
VmaMappingHysteresis m_MappingHysteresis;
uint32_t m_MapCount;
void* m_pMappedData;
VmaWin32Handle m_Handle;
};
#endif // _VMA_DEVICE_MEMORY_BLOCK
#ifndef _VMA_ALLOCATION_T
struct VmaAllocationExtraData
{
void* m_pMappedData = VMA_NULL; // Not null means memory is mapped.
VmaWin32Handle m_Handle;
};
struct VmaAllocation_T
{
friend struct VmaDedicatedAllocationListItemTraits;
@@ -6191,12 +6340,14 @@ public:
bool mapped);
// pMappedData not null means allocation is created with MAPPED flag.
void InitDedicatedAllocation(
VmaAllocator allocator,
VmaPool hParentPool,
uint32_t memoryTypeIndex,
VkDeviceMemory hMemory,
VmaSuballocationType suballocationType,
void* pMappedData,
VkDeviceSize size);
void Destroy(VmaAllocator allocator);
ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
VkDeviceSize GetAlignment() const { return m_Alignment; }
@@ -6240,6 +6391,10 @@ public:
void PrintParameters(class VmaJsonWriter& json) const;
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* hHandle) noexcept;
#endif // VMA_EXTERNAL_MEMORY_WIN32
private:
// Allocation out of VmaDeviceMemoryBlock.
struct BlockAllocation
@@ -6252,7 +6407,7 @@ private:
{
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
VkDeviceMemory m_hMemory;
void* m_pMappedData; // Not null means memory is mapped.
VmaAllocationExtraData* m_ExtraData;
VmaAllocation_T* m_Prev;
VmaAllocation_T* m_Next;
};
@@ -6277,6 +6432,8 @@ private:
#if VMA_STATS_STRING_ENABLED
VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown.
#endif
void EnsureExtraData(VmaAllocator hAllocator);
};
#endif // _VMA_ALLOCATION_T
@@ -10075,6 +10232,7 @@ public:
bool m_UseExtMemoryPriority;
bool m_UseKhrMaintenance4;
bool m_UseKhrMaintenance5;
bool m_UseKhrExternalMemoryWin32;
const VkDevice m_hDevice;
const VkInstance m_hInstance;
const bool m_AllocationCallbacksSpecified;
@@ -10438,7 +10596,7 @@ VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)
m_Id(0),
m_hMemory(VK_NULL_HANDLE),
m_MapCount(0),
m_pMappedData(VMA_NULL) {}
m_pMappedData(VMA_NULL){}
VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()
{
@@ -10681,6 +10839,14 @@ VkResult VmaDeviceMemoryBlock::BindImageMemory(
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
}
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult VmaDeviceMemoryBlock::CreateWin32Handle(const VmaAllocator hAllocator, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, HANDLE* pHandle) noexcept
{
VMA_ASSERT(pHandle);
return m_Handle.GetHandle(hAllocator->m_hDevice, m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
#ifndef _VMA_ALLOCATION_T_FUNCTIONS
@@ -10733,6 +10899,7 @@ void VmaAllocation_T::InitBlockAllocation(
}
void VmaAllocation_T::InitDedicatedAllocation(
VmaAllocator allocator,
VmaPool hParentPool,
uint32_t memoryTypeIndex,
VkDeviceMemory hMemory,
@@ -10747,16 +10914,29 @@ void VmaAllocation_T::InitDedicatedAllocation(
m_Size = size;
m_MemoryTypeIndex = memoryTypeIndex;
m_SuballocationType = (uint8_t)suballocationType;
if(pMappedData != VMA_NULL)
m_DedicatedAllocation.m_ExtraData = VMA_NULL;
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
if (pMappedData != VMA_NULL)
{
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
EnsureExtraData(allocator);
m_DedicatedAllocation.m_ExtraData->m_pMappedData = pMappedData;
}
}
void VmaAllocation_T::Destroy(VmaAllocator allocator)
{
FreeName(allocator);
if (GetType() == ALLOCATION_TYPE_DEDICATED)
{
vma_delete(allocator, m_DedicatedAllocation.m_ExtraData);
}
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_pMappedData = pMappedData;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
}
void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)
@@ -10861,8 +11041,9 @@ void* VmaAllocation_T::GetMappedData() const
}
break;
case ALLOCATION_TYPE_DEDICATED:
VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap()));
return m_DedicatedAllocation.m_pMappedData;
VMA_ASSERT((m_DedicatedAllocation.m_ExtraData != VMA_NULL && m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL) ==
(m_MapCount != 0 || IsPersistentMap()));
return m_DedicatedAllocation.m_ExtraData != VMA_NULL ? m_DedicatedAllocation.m_ExtraData->m_pMappedData : VMA_NULL;
default:
VMA_ASSERT(0);
return VMA_NULL;
@@ -10903,12 +11084,14 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa
VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
EnsureExtraData(hAllocator);
if (m_MapCount != 0 || IsPersistentMap())
{
if (m_MapCount < 0xFF)
{
VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
*ppData = m_DedicatedAllocation.m_pMappedData;
VMA_ASSERT(m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL);
*ppData = m_DedicatedAllocation.m_ExtraData->m_pMappedData;
++m_MapCount;
return VK_SUCCESS;
}
@@ -10929,7 +11112,7 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa
ppData);
if (result == VK_SUCCESS)
{
m_DedicatedAllocation.m_pMappedData = *ppData;
m_DedicatedAllocation.m_ExtraData->m_pMappedData = *ppData;
m_MapCount = 1;
}
return result;
@@ -10945,7 +11128,8 @@ void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
--m_MapCount;
if (m_MapCount == 0 && !IsPersistentMap())
{
m_DedicatedAllocation.m_pMappedData = VMA_NULL;
VMA_ASSERT(m_DedicatedAllocation.m_ExtraData != VMA_NULL);
m_DedicatedAllocation.m_ExtraData->m_pMappedData = VMA_NULL;
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
hAllocator->m_hDevice,
m_DedicatedAllocation.m_hMemory);
@@ -10981,8 +11165,33 @@ void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
json.WriteString(m_pName);
}
}
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult VmaAllocation_T::GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* pHandle) noexcept
{
auto pvkGetMemoryWin32HandleKHR = hAllocator->GetVulkanFunctions().vkGetMemoryWin32HandleKHR;
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
return m_BlockAllocation.m_Block->CreateWin32Handle(hAllocator, pvkGetMemoryWin32HandleKHR, hTargetProcess, pHandle);
case ALLOCATION_TYPE_DEDICATED:
EnsureExtraData(hAllocator);
return m_DedicatedAllocation.m_ExtraData->m_Handle.GetHandle(hAllocator->m_hDevice, m_DedicatedAllocation.m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);
default:
VMA_ASSERT(0);
return VK_ERROR_FEATURE_NOT_PRESENT;
}
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // VMA_STATS_STRING_ENABLED
void VmaAllocation_T::EnsureExtraData(VmaAllocator hAllocator)
{
if (m_DedicatedAllocation.m_ExtraData == VMA_NULL)
{
m_DedicatedAllocation.m_ExtraData = vma_new(hAllocator, VmaAllocationExtraData)();
}
}
void VmaAllocation_T::FreeName(VmaAllocator hAllocator)
{
if(m_pName)
@@ -11399,6 +11608,10 @@ void VmaBlockVector::Free(const VmaAllocation hAllocation)
}
IncrementallySortBlocks();
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
hAllocation->Destroy(m_hAllocator);
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
}
// Destruction of a free block. Deferred until this point, outside of mutex
@@ -11409,9 +11622,6 @@ void VmaBlockVector::Free(const VmaAllocation hAllocation)
pBlockToDelete->Destroy(m_hAllocator);
vma_delete(m_hAllocator, pBlockToDelete);
}
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
}
VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
@@ -12711,6 +12921,7 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0),
m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0),
m_UseKhrExternalMemoryWin32((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT) != 0),
m_hDevice(pCreateInfo->device),
m_hInstance(pCreateInfo->instance),
m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
@@ -12802,6 +13013,19 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
#if !(VMA_KHR_MAINTENANCE5)
if(m_UseKhrMaintenance5)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
#if !(VMA_EXTERNAL_MEMORY_WIN32)
if(m_UseKhrExternalMemoryWin32)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
@@ -13026,7 +13250,9 @@ void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVul
VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);
VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
VMA_COPY_IF_NOT_NULL(vkGetMemoryWin32HandleKHR);
#endif
#undef VMA_COPY_IF_NOT_NULL
}
@@ -13128,7 +13354,12 @@ void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR");
}
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
if (m_UseKhrExternalMemoryWin32)
{
VMA_FETCH_DEVICE_FUNC(vkGetMemoryWin32HandleKHR, PFN_vkGetMemoryWin32HandleKHR, "vkGetMemoryWin32HandleKHR");
}
#endif
#undef VMA_FETCH_DEVICE_FUNC
#undef VMA_FETCH_INSTANCE_FUNC
}
@@ -13177,6 +13408,12 @@ void VmaAllocator_T::ValidateVulkanFunctions()
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
}
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
if (m_UseKhrExternalMemoryWin32)
{
VMA_ASSERT(m_VulkanFunctions.vkGetMemoryWin32HandleKHR != VMA_NULL);
}
#endif
// Not validating these due to suspected driver bugs with these function
// pointers being null despite correct extension or Vulkan version is enabled.
@@ -13527,7 +13764,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
}
*pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);
(*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
(*pAllocation)->InitDedicatedAllocation(this, pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
if (isUserDataString)
(*pAllocation)->SetName(this, (const char*)pUserData);
else
@@ -13863,8 +14100,6 @@ void VmaAllocator_T::FreeMemory(
FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
}
allocation->FreeName(this);
switch(allocation->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
@@ -14335,7 +14570,6 @@ VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
}
return res;
}
VMA_FALLTHROUGH; // Fallthrough
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
return hAllocation->DedicatedAllocMap(this, ppData);
default:
@@ -14549,6 +14783,7 @@ void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
allocation->Destroy(this);
m_AllocationObjectAllocator.Free(allocation);
VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex);
@@ -16169,7 +16404,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
pImageCreateInfo,
allocator->GetAllocationCallbacks(),
pImage);
if(res >= 0)
if(res == VK_SUCCESS)
{
VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
@@ -16194,14 +16429,14 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
1, // allocationCount
pAllocation);
if(res >= 0)
if(res == VK_SUCCESS)
{
// 3. Bind image with memory.
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
{
res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
}
if(res >= 0)
if(res == VK_SUCCESS)
{
// All steps succeeded.
#if VMA_STATS_STRING_ENABLED
@@ -16434,6 +16669,15 @@ VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock V
VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
}
}
#if VMA_EXTERNAL_MEMORY_WIN32
VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle)
{
VMA_ASSERT(allocator && allocation && pHandle);
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
return allocation->GetWin32Handle(allocator, hTargetProcess, pHandle);
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // VMA_STATS_STRING_ENABLED
#endif // _VMA_PUBLIC_INTERFACE
#endif // VMA_IMPLEMENTATION
@@ -16567,6 +16811,7 @@ VK_EXT_memory_budget | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
VK_KHR_buffer_device_address | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
VK_EXT_memory_priority | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
VK_KHR_external_memory_win32 | #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT
Example with fetching pointers to Vulkan functions dynamically:
@@ -17053,7 +17298,7 @@ implementation whether the allocation succeeds or fails. You can change this beh
by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
not made if it would exceed the budget or if the budget is already exceeded.
VMA then tries to make the allocation from the next eligible Vulkan memory type.
The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
If all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
when creating resources that are not essential for the application (e.g. the texture
of a specific object) and not to pass it when creating critically important resources
@@ -18193,7 +18438,8 @@ allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VkBuffer buf;
VmaAllocation alloc;
VmaAllocationInfo allocInfo;
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
VkResult result = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
// Check result...
VkMemoryPropertyFlags memPropFlags;
vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);
@@ -18204,10 +18450,24 @@ if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
// [Executed in runtime]:
memcpy(allocInfo.pMappedData, myData, myDataSize);
result = vmaFlushAllocation(allocator, alloc, 0, VK_WHOLE_SIZE);
// Check result...
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.buffer = buf;
bufMemBarrier.offset = 0;
bufMemBarrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
}
else
{
// Allocation ended up in a non-mappable memory - need to transfer.
// Allocation ended up in a non-mappable memory - a transfer using a staging buffer is required.
VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
stagingBufCreateInfo.size = 65536;
stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
@@ -18220,18 +18480,46 @@ else
VkBuffer stagingBuf;
VmaAllocation stagingAlloc;
VmaAllocationInfo stagingAllocInfo;
vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
&stagingBuf, &stagingAlloc, stagingAllocInfo);
result = vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
&stagingBuf, &stagingAlloc, &stagingAllocInfo);
// Check result...
// [Executed in runtime]:
memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);
vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);
//vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT
result = vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);
// Check result...
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.buffer = stagingBuf;
bufMemBarrier.offset = 0;
bufMemBarrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
VkBufferCopy bufCopy = {
0, // srcOffset
0, // dstOffset,
myDataSize); // size
myDataSize, // size
};
vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);
VkBufferMemoryBarrier bufMemBarrier2 = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier2.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
bufMemBarrier2.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // We created a uniform buffer
bufMemBarrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier2.buffer = buf;
bufMemBarrier2.offset = 0;
bufMemBarrier2.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0, 0, nullptr, 1, &bufMemBarrier2, 0, nullptr);
}
\endcode
@@ -18264,14 +18552,22 @@ Please check "CONFIGURATION SECTION" in the code to find macros that you can def
before each include of this file or change directly in this file to provide
your own implementation of basic facilities like assert, `min()` and `max()` functions,
mutex, atomic etc.
The library uses its own implementation of containers by default, but you can switch to using
STL containers instead.
For example, define `VMA_ASSERT(expr)` before including the library to provide
custom implementation of the assertion, compatible with your project.
By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
and empty otherwise.
Similarly, you can define `VMA_LEAK_LOG_FORMAT` macro to enable printing of leaked (unfreed) allocations,
including their names and other parameters. Example:
\code
#define VMA_LEAK_LOG_FORMAT(format, ...) do { \
printf((format), __VA_ARGS__); \
printf("\n"); \
} while(false)
\endcode
\section config_Vulkan_functions Pointers to Vulkan functions
There are multiple ways to import pointers to Vulkan functions in the library.
@@ -18526,6 +18822,145 @@ Example use of this extension can be found in the code of the sample and test su
accompanying this library.
\page vk_khr_external_memory_win32 VK_KHR_external_memory_win32
On Windows, the VK_KHR_external_memory_win32 device extension allows exporting a Win32 `HANDLE`
of a `VkDeviceMemory` block, to be able to reference the memory on other Vulkan logical devices or instances,
in multiple processes, and/or in multiple APIs.
VMA offers support for it.
\section vk_khr_external_memory_win32_initialization Initialization
1) Make sure the extension is defined in the code by including following header before including VMA:
\code
#include <vulkan/vulkan_win32.h>
\endcode
2) Check if "VK_KHR_external_memory_win32" is available among device extensions.
Enable it when creating the `VkDevice` object.
3) Enable the usage of this extension in VMA by setting flag #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT
when calling vmaCreateAllocator().
4) Make sure that VMA has access to the `vkGetMemoryWin32HandleKHR` function by either enabling `VMA_DYNAMIC_VULKAN_FUNCTIONS` macro
or setting VmaVulkanFunctions::vkGetMemoryWin32HandleKHR explicitly.
For more information, see \ref quick_start_initialization_importing_vulkan_functions.
\section vk_khr_external_memory_win32_preparations Preparations
You can find example usage among tests, in file "Tests.cpp", function `TestWin32Handles()`.
To use the extenion, buffers need to be created with `VkExternalMemoryBufferCreateInfoKHR` attached to their `pNext` chain,
and memory allocations need to be made with `VkExportMemoryAllocateInfoKHR` attached to their `pNext` chain.
To make use of them, you need to use \ref custom_memory_pools. Example:
\code
// Define an example buffer and allocation parameters.
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
exampleBufCreateInfo.size = 0x10000; // Doesn't matter here.
exampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
exampleBufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo exampleAllocCreateInfo = {};
exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
// Find memory type index to use for the custom pool.
uint32_t memTypeIndex;
VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_Allocator,
&exampleBufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
// Check res...
// Create a custom pool.
constexpr static VkExportMemoryAllocateInfoKHR exportMemAllocInfo = {
VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VmaPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.memoryTypeIndex = memTypeIndex;
poolCreateInfo.pMemoryAllocateNext = (void*)&exportMemAllocInfo;
VmaPool pool;
res = vmaCreatePool(g_Allocator, &poolCreateInfo, &pool);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyPool(g_Allocator, pool);
\endcode
Note that the structure passed as VmaPoolCreateInfo::pMemoryAllocateNext must remain alive and unchanged
for the whole lifetime of the custom pool, because it will be used when the pool allocates a new device memory block.
No copy is made internally. This is why variable `exportMemAllocInfo` is defined as `static`.
\section vk_khr_external_memory_win32_memory_allocation Memory allocation
Finally, you can create a buffer with an allocation out of the custom pool.
The buffer should use same flags as the sample buffer used to find the memory type.
It should also specify `VkExternalMemoryBufferCreateInfoKHR` in its `pNext` chain.
\code
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.size = // Your desired buffer size.
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.pool = pool; // It is enough to set this one member.
VkBuffer buf;
VmaAllocation alloc;
res = vmaCreateBuffer(g_Allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyBuffer(g_Allocator, buf, alloc);
\endcode
If you need each allocation to have its own device memory block and start at offset 0, you can still do
by using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag. It works also with custom pools.
\section vk_khr_external_memory_win32_exporting_win32_handle Exporting Win32 handle
After the allocation is created, you can acquire a Win32 `HANDLE` to the `VkDeviceMemory` block it belongs to.
VMA function vmaGetMemoryWin32Handle() is a replacement of the Vulkan function `vkGetMemoryWin32HandleKHR`.
\code
HANDLE handle;
res = vmaGetMemoryWin32Handle(g_Allocator, alloc, nullptr, &handle);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, you must close the handle.
CloseHandle(handle);
\endcode
Documentation of the VK_KHR_external_memory_win32 extension states that:
> If handleType is defined as an NT handle, vkGetMemoryWin32HandleKHR must be called no more than once for each valid unique combination of memory and handleType.
This is ensured automatically inside VMA.
The library fetches the handle on first use, remembers it internally, and closes it when the memory block or dedicated allocation is destroyed.
Every time you call vmaGetMemoryWin32Handle(), VMA calls `DuplicateHandle` and returns a new handle that you need to close.
For further information, please check documentation of the vmaGetMemoryWin32Handle() function.
\page enabling_buffer_device_address Enabling buffer device address
Device extension VK_KHR_buffer_device_address

View File

@@ -95,6 +95,7 @@ See also: [product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-me
- \subpage enabling_buffer_device_address
- \subpage vk_ext_memory_priority
- \subpage vk_amd_device_coherent_memory
- \subpage vk_khr_external_memory_win32
- \subpage general_considerations
- [Thread safety](@ref general_considerations_thread_safety)
- [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)
@@ -127,7 +128,9 @@ See documentation chapter: \ref statistics.
extern "C" {
#endif
#if !defined(VULKAN_H_)
#include <vulkan/vulkan.h>
#endif
#if !defined(VMA_VULKAN_VERSION)
#if defined(VK_VERSION_1_3)
@@ -240,6 +243,15 @@ extern "C" {
#endif
#endif
// Defined to 1 when VK_KHR_external_memory_win32 device extension is defined in Vulkan headers.
#if !defined(VMA_EXTERNAL_MEMORY_WIN32)
#if VK_KHR_external_memory_win32
#define VMA_EXTERNAL_MEMORY_WIN32 1
#else
#define VMA_EXTERNAL_MEMORY_WIN32 0
#endif
#endif
// Define these macros to decorate all public functions with additional code,
// before and after returned type, appropriately. This may be useful for
// exporting the functions when compiling VMA as a separate library. Example:
@@ -459,6 +471,15 @@ typedef enum VmaAllocatorCreateFlagBits
*/
VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100,
/**
Enables usage of VK_KHR_external_memory_win32 extension in the library.
You should set this flag if you found available and enabled this device extension,
while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
For more information, see \ref vk_khr_external_memory_win32.
*/
VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT = 0x00000200,
VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VmaAllocatorCreateFlagBits;
/// See #VmaAllocatorCreateFlagBits.
@@ -1033,6 +1054,11 @@ typedef struct VmaVulkanFunctions
/// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements;
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
PFN_vkGetMemoryWin32HandleKHR VMA_NULLABLE vkGetMemoryWin32HandleKHR;
#else
void* VMA_NULLABLE vkGetMemoryWin32HandleKHR;
#endif
} VmaVulkanFunctions;
/// Description of a Allocator to be created.
@@ -1810,6 +1836,9 @@ VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
\param allocator Allocator object.
\param pool Pool object.
\param[out] pPoolStats Statistics of specified pool.
Note that when using the pool from multiple threads, returned information may immediately
become outdated.
*/
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
VmaAllocator VMA_NOT_NULL allocator,
@@ -2050,6 +2079,40 @@ VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
VmaAllocation VMA_NOT_NULL allocation,
VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
#if VMA_EXTERNAL_MEMORY_WIN32
/**
\brief Given an allocation, returns Win32 handle that may be imported by other processes or APIs.
\param hTargetProcess Must be a valid handle to target process or null. If it's null, the function returns
handle for the current process.
\param[out] pHandle Output parameter that returns the handle.
The function fills `pHandle` with handle that can be used in target process.
The handle is fetched using function `vkGetMemoryWin32HandleKHR`.
When no longer needed, you must close it using:
\code
CloseHandle(handle);
\endcode
You can close it any time, before or after destroying the allocation object.
It is reference-counted internally by Windows.
Note the handle is returned for the entire `VkDeviceMemory` block that the allocation belongs to.
If the allocation is sub-allocated from a larger block, you may need to consider the offset of the allocation
(VmaAllocationInfo::offset).
If the function fails with `VK_ERROR_FEATURE_NOT_PRESENT` error code, please double-check
that VmaVulkanFunctions::vkGetMemoryWin32HandleKHR function pointer is set, e.g. either by using `VMA_DYNAMIC_VULKAN_FUNCTIONS`
or by manually passing it through VmaAllocatorCreateInfo::pVulkanFunctions.
For more information, see chapter \ref vk_khr_external_memory_win32.
*/
VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle);
#endif // VMA_EXTERNAL_MEMORY_WIN32
/** \brief Maps memory represented by given allocation and returns pointer to it.
Maps memory represented by given allocation to make it accessible to CPU code.
@@ -3097,7 +3160,7 @@ static void vma_aligned_free(void* VMA_NULLABLE ptr)
std::shared_mutex m_Mutex;
};
#define VMA_RW_MUTEX VmaRWMutex
#elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
#elif defined(_WIN32) && defined(WINVER) && defined(SRWLOCK_INIT) && WINVER >= 0x0600
// Use SRWLOCK from WinAPI.
// Minimum supported client = Windows Vista, server = Windows Server 2008.
class VmaRWMutex
@@ -3838,12 +3901,6 @@ struct VmaBufferImageUsage
const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0);
static void swap(VmaBufferImageUsage& lhs, VmaBufferImageUsage& rhs) noexcept
{
using std::swap;
swap(lhs.Value, rhs.Value);
}
VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo,
bool useKhrMaintenance5)
{
@@ -6073,6 +6130,84 @@ private:
#endif // _VMA_MAPPING_HYSTERESIS
#if VMA_EXTERNAL_MEMORY_WIN32
class VmaWin32Handle
{
public:
VmaWin32Handle() noexcept : m_hHandle(VMA_NULL) { }
explicit VmaWin32Handle(HANDLE hHandle) noexcept : m_hHandle(hHandle) { }
~VmaWin32Handle() noexcept { if (m_hHandle != VMA_NULL) { ::CloseHandle(m_hHandle); } }
VMA_CLASS_NO_COPY_NO_MOVE(VmaWin32Handle)
public:
// Strengthened
VkResult GetHandle(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, bool useMutex, HANDLE* pHandle) noexcept
{
*pHandle = VMA_NULL;
// Try to get handle first.
if (m_hHandle != VMA_NULL)
{
*pHandle = Duplicate(hTargetProcess);
return VK_SUCCESS;
}
VkResult res = VK_SUCCESS;
// If failed, try to create it.
{
VmaMutexLockWrite lock(m_Mutex, useMutex);
if (m_hHandle == VMA_NULL)
{
res = Create(device, memory, pvkGetMemoryWin32HandleKHR, &m_hHandle);
}
}
*pHandle = Duplicate(hTargetProcess);
return res;
}
operator bool() const noexcept { return m_hHandle != VMA_NULL; }
private:
// Not atomic
static VkResult Create(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE* pHandle) noexcept
{
VkResult res = VK_ERROR_FEATURE_NOT_PRESENT;
if (pvkGetMemoryWin32HandleKHR != VMA_NULL)
{
VkMemoryGetWin32HandleInfoKHR handleInfo{ };
handleInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
handleInfo.memory = memory;
handleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
res = pvkGetMemoryWin32HandleKHR(device, &handleInfo, pHandle);
}
return res;
}
HANDLE Duplicate(HANDLE hTargetProcess = VMA_NULL) const noexcept
{
if (!m_hHandle)
return m_hHandle;
HANDLE hCurrentProcess = ::GetCurrentProcess();
HANDLE hDupHandle = VMA_NULL;
if (!::DuplicateHandle(hCurrentProcess, m_hHandle, hTargetProcess ? hTargetProcess : hCurrentProcess, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
VMA_ASSERT(0 && "Failed to duplicate handle.");
}
return hDupHandle;
}
private:
HANDLE m_hHandle;
VMA_RW_MUTEX m_Mutex; // Protects access m_Handle
};
#else
class VmaWin32Handle
{
// ABI compatibility
void* placeholder = VMA_NULL;
VMA_RW_MUTEX placeholder2;
};
#endif // VMA_EXTERNAL_MEMORY_WIN32
#ifndef _VMA_DEVICE_MEMORY_BLOCK
/*
Represents a single block of device memory (`VkDeviceMemory`) with all the
@@ -6139,7 +6274,13 @@ public:
VkDeviceSize allocationLocalOffset,
VkImage hImage,
const void* pNext);
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult CreateWin32Handle(
const VmaAllocator hAllocator,
PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR,
HANDLE hTargetProcess,
HANDLE* pHandle)noexcept;
#endif // VMA_EXTERNAL_MEMORY_WIN32
private:
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
uint32_t m_MemoryTypeIndex;
@@ -6155,10 +6296,18 @@ private:
VmaMappingHysteresis m_MappingHysteresis;
uint32_t m_MapCount;
void* m_pMappedData;
VmaWin32Handle m_Handle;
};
#endif // _VMA_DEVICE_MEMORY_BLOCK
#ifndef _VMA_ALLOCATION_T
struct VmaAllocationExtraData
{
void* m_pMappedData = VMA_NULL; // Not null means memory is mapped.
VmaWin32Handle m_Handle;
};
struct VmaAllocation_T
{
friend struct VmaDedicatedAllocationListItemTraits;
@@ -6191,12 +6340,14 @@ public:
bool mapped);
// pMappedData not null means allocation is created with MAPPED flag.
void InitDedicatedAllocation(
VmaAllocator allocator,
VmaPool hParentPool,
uint32_t memoryTypeIndex,
VkDeviceMemory hMemory,
VmaSuballocationType suballocationType,
void* pMappedData,
VkDeviceSize size);
void Destroy(VmaAllocator allocator);
ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
VkDeviceSize GetAlignment() const { return m_Alignment; }
@@ -6240,6 +6391,10 @@ public:
void PrintParameters(class VmaJsonWriter& json) const;
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* hHandle) noexcept;
#endif // VMA_EXTERNAL_MEMORY_WIN32
private:
// Allocation out of VmaDeviceMemoryBlock.
struct BlockAllocation
@@ -6252,7 +6407,7 @@ private:
{
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
VkDeviceMemory m_hMemory;
void* m_pMappedData; // Not null means memory is mapped.
VmaAllocationExtraData* m_ExtraData;
VmaAllocation_T* m_Prev;
VmaAllocation_T* m_Next;
};
@@ -6277,6 +6432,8 @@ private:
#if VMA_STATS_STRING_ENABLED
VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown.
#endif
void EnsureExtraData(VmaAllocator hAllocator);
};
#endif // _VMA_ALLOCATION_T
@@ -10075,6 +10232,7 @@ public:
bool m_UseExtMemoryPriority;
bool m_UseKhrMaintenance4;
bool m_UseKhrMaintenance5;
bool m_UseKhrExternalMemoryWin32;
const VkDevice m_hDevice;
const VkInstance m_hInstance;
const bool m_AllocationCallbacksSpecified;
@@ -10438,7 +10596,7 @@ VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)
m_Id(0),
m_hMemory(VK_NULL_HANDLE),
m_MapCount(0),
m_pMappedData(VMA_NULL) {}
m_pMappedData(VMA_NULL){}
VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()
{
@@ -10681,6 +10839,14 @@ VkResult VmaDeviceMemoryBlock::BindImageMemory(
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
}
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult VmaDeviceMemoryBlock::CreateWin32Handle(const VmaAllocator hAllocator, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, HANDLE* pHandle) noexcept
{
VMA_ASSERT(pHandle);
return m_Handle.GetHandle(hAllocator->m_hDevice, m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
#ifndef _VMA_ALLOCATION_T_FUNCTIONS
@@ -10733,6 +10899,7 @@ void VmaAllocation_T::InitBlockAllocation(
}
void VmaAllocation_T::InitDedicatedAllocation(
VmaAllocator allocator,
VmaPool hParentPool,
uint32_t memoryTypeIndex,
VkDeviceMemory hMemory,
@@ -10747,16 +10914,29 @@ void VmaAllocation_T::InitDedicatedAllocation(
m_Size = size;
m_MemoryTypeIndex = memoryTypeIndex;
m_SuballocationType = (uint8_t)suballocationType;
if(pMappedData != VMA_NULL)
m_DedicatedAllocation.m_ExtraData = VMA_NULL;
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
if (pMappedData != VMA_NULL)
{
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
EnsureExtraData(allocator);
m_DedicatedAllocation.m_ExtraData->m_pMappedData = pMappedData;
}
}
void VmaAllocation_T::Destroy(VmaAllocator allocator)
{
FreeName(allocator);
if (GetType() == ALLOCATION_TYPE_DEDICATED)
{
vma_delete(allocator, m_DedicatedAllocation.m_ExtraData);
}
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_pMappedData = pMappedData;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
}
void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)
@@ -10861,8 +11041,9 @@ void* VmaAllocation_T::GetMappedData() const
}
break;
case ALLOCATION_TYPE_DEDICATED:
VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap()));
return m_DedicatedAllocation.m_pMappedData;
VMA_ASSERT((m_DedicatedAllocation.m_ExtraData != VMA_NULL && m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL) ==
(m_MapCount != 0 || IsPersistentMap()));
return m_DedicatedAllocation.m_ExtraData != VMA_NULL ? m_DedicatedAllocation.m_ExtraData->m_pMappedData : VMA_NULL;
default:
VMA_ASSERT(0);
return VMA_NULL;
@@ -10903,12 +11084,14 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa
VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
EnsureExtraData(hAllocator);
if (m_MapCount != 0 || IsPersistentMap())
{
if (m_MapCount < 0xFF)
{
VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
*ppData = m_DedicatedAllocation.m_pMappedData;
VMA_ASSERT(m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL);
*ppData = m_DedicatedAllocation.m_ExtraData->m_pMappedData;
++m_MapCount;
return VK_SUCCESS;
}
@@ -10929,7 +11112,7 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa
ppData);
if (result == VK_SUCCESS)
{
m_DedicatedAllocation.m_pMappedData = *ppData;
m_DedicatedAllocation.m_ExtraData->m_pMappedData = *ppData;
m_MapCount = 1;
}
return result;
@@ -10945,7 +11128,8 @@ void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
--m_MapCount;
if (m_MapCount == 0 && !IsPersistentMap())
{
m_DedicatedAllocation.m_pMappedData = VMA_NULL;
VMA_ASSERT(m_DedicatedAllocation.m_ExtraData != VMA_NULL);
m_DedicatedAllocation.m_ExtraData->m_pMappedData = VMA_NULL;
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
hAllocator->m_hDevice,
m_DedicatedAllocation.m_hMemory);
@@ -10981,8 +11165,33 @@ void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
json.WriteString(m_pName);
}
}
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult VmaAllocation_T::GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* pHandle) noexcept
{
auto pvkGetMemoryWin32HandleKHR = hAllocator->GetVulkanFunctions().vkGetMemoryWin32HandleKHR;
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
return m_BlockAllocation.m_Block->CreateWin32Handle(hAllocator, pvkGetMemoryWin32HandleKHR, hTargetProcess, pHandle);
case ALLOCATION_TYPE_DEDICATED:
EnsureExtraData(hAllocator);
return m_DedicatedAllocation.m_ExtraData->m_Handle.GetHandle(hAllocator->m_hDevice, m_DedicatedAllocation.m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);
default:
VMA_ASSERT(0);
return VK_ERROR_FEATURE_NOT_PRESENT;
}
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // VMA_STATS_STRING_ENABLED
void VmaAllocation_T::EnsureExtraData(VmaAllocator hAllocator)
{
if (m_DedicatedAllocation.m_ExtraData == VMA_NULL)
{
m_DedicatedAllocation.m_ExtraData = vma_new(hAllocator, VmaAllocationExtraData)();
}
}
void VmaAllocation_T::FreeName(VmaAllocator hAllocator)
{
if(m_pName)
@@ -11399,6 +11608,10 @@ void VmaBlockVector::Free(const VmaAllocation hAllocation)
}
IncrementallySortBlocks();
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
hAllocation->Destroy(m_hAllocator);
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
}
// Destruction of a free block. Deferred until this point, outside of mutex
@@ -11409,9 +11622,6 @@ void VmaBlockVector::Free(const VmaAllocation hAllocation)
pBlockToDelete->Destroy(m_hAllocator);
vma_delete(m_hAllocator, pBlockToDelete);
}
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
}
VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
@@ -12711,6 +12921,7 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0),
m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0),
m_UseKhrExternalMemoryWin32((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT) != 0),
m_hDevice(pCreateInfo->device),
m_hInstance(pCreateInfo->instance),
m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
@@ -12802,6 +13013,19 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
#if !(VMA_KHR_MAINTENANCE5)
if(m_UseKhrMaintenance5)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
#if !(VMA_EXTERNAL_MEMORY_WIN32)
if(m_UseKhrExternalMemoryWin32)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
@@ -13026,7 +13250,9 @@ void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVul
VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);
VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
VMA_COPY_IF_NOT_NULL(vkGetMemoryWin32HandleKHR);
#endif
#undef VMA_COPY_IF_NOT_NULL
}
@@ -13128,7 +13354,12 @@ void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR");
}
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
if (m_UseKhrExternalMemoryWin32)
{
VMA_FETCH_DEVICE_FUNC(vkGetMemoryWin32HandleKHR, PFN_vkGetMemoryWin32HandleKHR, "vkGetMemoryWin32HandleKHR");
}
#endif
#undef VMA_FETCH_DEVICE_FUNC
#undef VMA_FETCH_INSTANCE_FUNC
}
@@ -13177,6 +13408,12 @@ void VmaAllocator_T::ValidateVulkanFunctions()
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
}
#endif
#if VMA_EXTERNAL_MEMORY_WIN32
if (m_UseKhrExternalMemoryWin32)
{
VMA_ASSERT(m_VulkanFunctions.vkGetMemoryWin32HandleKHR != VMA_NULL);
}
#endif
// Not validating these due to suspected driver bugs with these function
// pointers being null despite correct extension or Vulkan version is enabled.
@@ -13527,7 +13764,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
}
*pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);
(*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
(*pAllocation)->InitDedicatedAllocation(this, pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
if (isUserDataString)
(*pAllocation)->SetName(this, (const char*)pUserData);
else
@@ -13863,8 +14100,6 @@ void VmaAllocator_T::FreeMemory(
FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
}
allocation->FreeName(this);
switch(allocation->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
@@ -14335,7 +14570,6 @@ VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
}
return res;
}
VMA_FALLTHROUGH; // Fallthrough
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
return hAllocation->DedicatedAllocMap(this, ppData);
default:
@@ -14549,6 +14783,7 @@ void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
allocation->Destroy(this);
m_AllocationObjectAllocator.Free(allocation);
VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex);
@@ -16169,7 +16404,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
pImageCreateInfo,
allocator->GetAllocationCallbacks(),
pImage);
if(res >= 0)
if(res == VK_SUCCESS)
{
VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
@@ -16194,14 +16429,14 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
1, // allocationCount
pAllocation);
if(res >= 0)
if(res == VK_SUCCESS)
{
// 3. Bind image with memory.
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
{
res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
}
if(res >= 0)
if(res == VK_SUCCESS)
{
// All steps succeeded.
#if VMA_STATS_STRING_ENABLED
@@ -16434,6 +16669,15 @@ VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock V
VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
}
}
#if VMA_EXTERNAL_MEMORY_WIN32
VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle)
{
VMA_ASSERT(allocator && allocation && pHandle);
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
return allocation->GetWin32Handle(allocator, hTargetProcess, pHandle);
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // VMA_STATS_STRING_ENABLED
#endif // _VMA_PUBLIC_INTERFACE
#endif // VMA_IMPLEMENTATION
@@ -16567,6 +16811,7 @@ VK_EXT_memory_budget | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
VK_KHR_buffer_device_address | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
VK_EXT_memory_priority | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
VK_KHR_external_memory_win32 | #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT
Example with fetching pointers to Vulkan functions dynamically:
@@ -17053,7 +17298,7 @@ implementation whether the allocation succeeds or fails. You can change this beh
by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
not made if it would exceed the budget or if the budget is already exceeded.
VMA then tries to make the allocation from the next eligible Vulkan memory type.
The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
If all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
when creating resources that are not essential for the application (e.g. the texture
of a specific object) and not to pass it when creating critically important resources
@@ -18193,7 +18438,8 @@ allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VkBuffer buf;
VmaAllocation alloc;
VmaAllocationInfo allocInfo;
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
VkResult result = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
// Check result...
VkMemoryPropertyFlags memPropFlags;
vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);
@@ -18204,10 +18450,24 @@ if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
// [Executed in runtime]:
memcpy(allocInfo.pMappedData, myData, myDataSize);
result = vmaFlushAllocation(allocator, alloc, 0, VK_WHOLE_SIZE);
// Check result...
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.buffer = buf;
bufMemBarrier.offset = 0;
bufMemBarrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
}
else
{
// Allocation ended up in a non-mappable memory - need to transfer.
// Allocation ended up in a non-mappable memory - a transfer using a staging buffer is required.
VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
stagingBufCreateInfo.size = 65536;
stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
@@ -18220,18 +18480,46 @@ else
VkBuffer stagingBuf;
VmaAllocation stagingAlloc;
VmaAllocationInfo stagingAllocInfo;
vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
&stagingBuf, &stagingAlloc, stagingAllocInfo);
result = vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
&stagingBuf, &stagingAlloc, &stagingAllocInfo);
// Check result...
// [Executed in runtime]:
memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);
vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);
//vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT
result = vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);
// Check result...
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.buffer = stagingBuf;
bufMemBarrier.offset = 0;
bufMemBarrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
VkBufferCopy bufCopy = {
0, // srcOffset
0, // dstOffset,
myDataSize); // size
myDataSize, // size
};
vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);
VkBufferMemoryBarrier bufMemBarrier2 = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier2.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
bufMemBarrier2.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // We created a uniform buffer
bufMemBarrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier2.buffer = buf;
bufMemBarrier2.offset = 0;
bufMemBarrier2.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0, 0, nullptr, 1, &bufMemBarrier2, 0, nullptr);
}
\endcode
@@ -18264,14 +18552,22 @@ Please check "CONFIGURATION SECTION" in the code to find macros that you can def
before each include of this file or change directly in this file to provide
your own implementation of basic facilities like assert, `min()` and `max()` functions,
mutex, atomic etc.
The library uses its own implementation of containers by default, but you can switch to using
STL containers instead.
For example, define `VMA_ASSERT(expr)` before including the library to provide
custom implementation of the assertion, compatible with your project.
By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
and empty otherwise.
Similarly, you can define `VMA_LEAK_LOG_FORMAT` macro to enable printing of leaked (unfreed) allocations,
including their names and other parameters. Example:
\code
#define VMA_LEAK_LOG_FORMAT(format, ...) do { \
printf((format), __VA_ARGS__); \
printf("\n"); \
} while(false)
\endcode
\section config_Vulkan_functions Pointers to Vulkan functions
There are multiple ways to import pointers to Vulkan functions in the library.
@@ -18526,6 +18822,145 @@ Example use of this extension can be found in the code of the sample and test su
accompanying this library.
\page vk_khr_external_memory_win32 VK_KHR_external_memory_win32
On Windows, the VK_KHR_external_memory_win32 device extension allows exporting a Win32 `HANDLE`
of a `VkDeviceMemory` block, to be able to reference the memory on other Vulkan logical devices or instances,
in multiple processes, and/or in multiple APIs.
VMA offers support for it.
\section vk_khr_external_memory_win32_initialization Initialization
1) Make sure the extension is defined in the code by including following header before including VMA:
\code
#include <vulkan/vulkan_win32.h>
\endcode
2) Check if "VK_KHR_external_memory_win32" is available among device extensions.
Enable it when creating the `VkDevice` object.
3) Enable the usage of this extension in VMA by setting flag #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT
when calling vmaCreateAllocator().
4) Make sure that VMA has access to the `vkGetMemoryWin32HandleKHR` function by either enabling `VMA_DYNAMIC_VULKAN_FUNCTIONS` macro
or setting VmaVulkanFunctions::vkGetMemoryWin32HandleKHR explicitly.
For more information, see \ref quick_start_initialization_importing_vulkan_functions.
\section vk_khr_external_memory_win32_preparations Preparations
You can find example usage among tests, in file "Tests.cpp", function `TestWin32Handles()`.
To use the extenion, buffers need to be created with `VkExternalMemoryBufferCreateInfoKHR` attached to their `pNext` chain,
and memory allocations need to be made with `VkExportMemoryAllocateInfoKHR` attached to their `pNext` chain.
To make use of them, you need to use \ref custom_memory_pools. Example:
\code
// Define an example buffer and allocation parameters.
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
exampleBufCreateInfo.size = 0x10000; // Doesn't matter here.
exampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
exampleBufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo exampleAllocCreateInfo = {};
exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
// Find memory type index to use for the custom pool.
uint32_t memTypeIndex;
VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_Allocator,
&exampleBufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
// Check res...
// Create a custom pool.
constexpr static VkExportMemoryAllocateInfoKHR exportMemAllocInfo = {
VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VmaPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.memoryTypeIndex = memTypeIndex;
poolCreateInfo.pMemoryAllocateNext = (void*)&exportMemAllocInfo;
VmaPool pool;
res = vmaCreatePool(g_Allocator, &poolCreateInfo, &pool);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyPool(g_Allocator, pool);
\endcode
Note that the structure passed as VmaPoolCreateInfo::pMemoryAllocateNext must remain alive and unchanged
for the whole lifetime of the custom pool, because it will be used when the pool allocates a new device memory block.
No copy is made internally. This is why variable `exportMemAllocInfo` is defined as `static`.
\section vk_khr_external_memory_win32_memory_allocation Memory allocation
Finally, you can create a buffer with an allocation out of the custom pool.
The buffer should use same flags as the sample buffer used to find the memory type.
It should also specify `VkExternalMemoryBufferCreateInfoKHR` in its `pNext` chain.
\code
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.size = // Your desired buffer size.
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.pool = pool; // It is enough to set this one member.
VkBuffer buf;
VmaAllocation alloc;
res = vmaCreateBuffer(g_Allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyBuffer(g_Allocator, buf, alloc);
\endcode
If you need each allocation to have its own device memory block and start at offset 0, you can still do
by using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag. It works also with custom pools.
\section vk_khr_external_memory_win32_exporting_win32_handle Exporting Win32 handle
After the allocation is created, you can acquire a Win32 `HANDLE` to the `VkDeviceMemory` block it belongs to.
VMA function vmaGetMemoryWin32Handle() is a replacement of the Vulkan function `vkGetMemoryWin32HandleKHR`.
\code
HANDLE handle;
res = vmaGetMemoryWin32Handle(g_Allocator, alloc, nullptr, &handle);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, you must close the handle.
CloseHandle(handle);
\endcode
Documentation of the VK_KHR_external_memory_win32 extension states that:
> If handleType is defined as an NT handle, vkGetMemoryWin32HandleKHR must be called no more than once for each valid unique combination of memory and handleType.
This is ensured automatically inside VMA.
The library fetches the handle on first use, remembers it internally, and closes it when the memory block or dedicated allocation is destroyed.
Every time you call vmaGetMemoryWin32Handle(), VMA calls `DuplicateHandle` and returns a new handle that you need to close.
For further information, please check documentation of the vmaGetMemoryWin32Handle() function.
\page enabling_buffer_device_address Enabling buffer device address
Device extension VK_KHR_buffer_device_address

File diff suppressed because it is too large Load Diff

View File

@@ -176,8 +176,6 @@
030000004f04000020b3000000000000,Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
03000000bd12000002e0000000000000,Dual Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,
03000000ff1100003133000000000000,DualForce,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b1,platform:Windows,
030000008f0e00000910000000000000,Sony DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,
03000000317300000100000000000000,Sony DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
030000006f0e00003001000000000000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000bc2000000091000000000000,EasySMX Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -247,6 +245,7 @@
03000000f025000031c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f0250000c383000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f0250000c483000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d11800000094000000000000,Google Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,
030000004f04000026b3000000000000,GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
0300000079000000d418000000000000,GPD Win,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000c6240000025b000000000000,GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
@@ -489,13 +488,6 @@
030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000006f0e00008901000000000000,PDP Realmz Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,platform:Windows,
03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,
03000000e30500009605000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
030000004c050000da0c000000000000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000632500002306000000000000,Sony PlayStation Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d9040000160f000000000000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
030000004c0500003713000000000000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -601,6 +593,7 @@
030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,
0300000032150000a602000000000000,Razer Huntsman V3 Pro,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b12,dpright:b13,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows,
03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -685,13 +678,21 @@
03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
030000008f0e00000910000000000000,Sony DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,
03000000317300000100000000000000,Sony DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,
03000000e30500009605000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
030000004c050000da0c000000000000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000632500002306000000000000,Sony PlayStation Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d9040000160f000000000000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
03000000ff000000cb01000000000000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
030000004c0500003713000000000000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000341a00000208000000000000,Speedlink 6555,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,
03000000341a00000908000000000000,Speedlink 6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000380700001722000000000000,Speedlink Competition Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,x:b2,y:b3,platform:Windows,
030000008f0e00000800000000000000,Speedlink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d11800000094000000000000,Google Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,
03000000de280000fc11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000de280000ff11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
@@ -701,7 +702,6 @@
03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,
03000000790000001c18000000000000,STK 7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000380700003847000000000000,Street Fighter Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows,
030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
@@ -716,7 +716,8 @@
03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
03000000ba2200000701000000000000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b2,platform:Windows,
03000000c61100001000000000000000,Tencent Xianyou Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,
03000000790000002601000000000000,TGZ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,
03000000790000001c18000000000000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000790000002601000000000000,TGZ Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,
03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,platform:Windows,
030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
@@ -883,7 +884,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
0300000008100000e501000019040000,Anbernic Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
0300000008100000e501000019040000,Anbernic Handheld,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
@@ -920,6 +921,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X,
03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,
030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000280400000140000000020000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000007010000,GreenAsia Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,
@@ -996,11 +998,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,
030000004b120000014d000000010000,Nyko Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000790000001c18000000010000,PB Tails Choc,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000666600006706000088020000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c0500003713000000010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000d620000011a7000010050000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -1053,7 +1053,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X,
030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000666600006706000088020000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c0500003713000000010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,
030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
@@ -1207,10 +1209,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000491900000204000021000000,Amazon Fire Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
0300000008100000e501000001010000,Anbernic Gamepad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux,
0300000008100000e501000001010000,Anbernic Handheld,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux,
03000000020500000913000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000373500000710000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000005e0400008e02000072050000,Anbernic RG P01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
@@ -1284,6 +1285,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux,
190000004b4800000011000000010000,GO-Super Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b2,y:b3,platform:Linux,
03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000d11800000094000011010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
05000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000001010000,GPD Win Max 2 6800U Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
@@ -1299,7 +1302,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00005f00000011010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000000d0f00005001000009040000,Hori Fighting Commander OCTA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000000d0f00005001000009040000,Hori Fighting Commander Octa Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000000d0f00008500000010010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b3,y:b2,platform:Linux,
@@ -1344,6 +1347,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
0500000049190000030400001b010000,Ipega PG9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000491900000204000000000000,Ipega PG9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000300f00001101000010010000,Jess Tech Colour Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
03000000ba2200002010000001010000,Jess Technology Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
@@ -1494,10 +1498,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e0000ef02000007640000,PDP Xbox Series Kinetic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000666600006706000000010000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,
030000004c050000da0c000011010000,Sony PlayStation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000d9040000160f000000010000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
030000004c0500003713000011010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
@@ -1534,8 +1534,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
030000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
@@ -1552,9 +1552,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
030000004c050000f20d000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000004c050000f20d000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
050000004c050000f20d000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,
03000000222c00000225000011010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000222c00000025000011010000,Qanba Dragon Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
@@ -1631,12 +1633,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000004b2900000430000011000000,Snakebyte Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
03000000666600006706000000010000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,
030000004c050000da0c000011010000,Sony PlayStation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000d9040000160f000000010000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
030000004c0500003713000011010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
03000000250900000500000000010000,Sony PS2 pad with SmartJoy Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,
030000005e0400008e02000073050000,Speedlink Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d11800000094000011010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
05000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,
03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
@@ -1666,7 +1670,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux,
03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux,
03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux,
03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,
030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,
@@ -1715,10 +1719,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006f0e00001503000000020000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000000010000,Xbox 360 EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
0000000058626f782047616d65706100,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
030000005e0400008e02000072050000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00001304000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000ffff0000ffff000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
0000000058626f782047616d65706100,Xbox Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1730,9 +1735,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
060000005e040000ea02000016050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000005050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,

View File

@@ -453,6 +453,11 @@ std::string Path::RealPath(const std::string_view path)
}
}
}
// If any relative symlinks were resolved, there may be '.' and '..'
// components in the resultant path, which must be removed.
realpath = Path::Canonicalize(realpath);
#endif
return realpath;
@@ -994,6 +999,37 @@ std::FILE* FileSystem::OpenCFile(const char* filename, const char* mode, Error*
#endif
}
std::FILE* FileSystem::OpenCFileTryIgnoreCase(const char* filename, const char* mode, Error* error)
{
#if defined(_WIN32) || defined(__APPLE__)
return OpenCFile(filename, mode, error);
#else
std::FILE* fp = std::fopen(filename, mode);
const auto cur_errno = errno;
if (!fp)
{
const auto dir = std::string(Path::GetDirectory(filename));
FindResultsArray files;
if (FindFiles(dir.c_str(), "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES, &files))
{
for (auto& file : files)
{
if (StringUtil::compareNoCase(file.FileName, filename))
{
fp = std::fopen(file.FileName.c_str(), mode);
break;
}
}
}
}
if (!fp)
Error::SetErrno(error, cur_errno);
return fp;
#endif
}
int FileSystem::OpenFDFile(const char* filename, int flags, int mode, Error* error)
{
#ifdef _WIN32
@@ -1015,6 +1051,11 @@ FileSystem::ManagedCFilePtr FileSystem::OpenManagedCFile(const char* filename, c
return ManagedCFilePtr(OpenCFile(filename, mode, error));
}
FileSystem::ManagedCFilePtr FileSystem::OpenManagedCFileTryIgnoreCase(const char* filename, const char* mode, Error* error)
{
return ManagedCFilePtr(OpenCFileTryIgnoreCase(filename, mode, error));
}
std::FILE* FileSystem::OpenSharedCFile(const char* filename, const char* mode, FileShareMode share_mode, Error* error)
{
#ifdef _WIN32
@@ -1189,6 +1230,14 @@ size_t FileSystem::ReadFileWithProgress(std::FILE* fp, void* dst, size_t length,
{
progress->SetProgressRange(100);
return FileSystem::ReadFileWithPartialProgress(fp, dst, length, progress, 0, 100, error, chunk_size);
}
size_t FileSystem::ReadFileWithPartialProgress(std::FILE* fp, void* dst, size_t length,
ProgressCallback* progress, int startPercent, int endPercent, Error* error, size_t chunk_size)
{
const int deltaPercent = endPercent - startPercent;
size_t done = 0;
while (done < length)
{
@@ -1202,7 +1251,7 @@ size_t FileSystem::ReadFileWithProgress(std::FILE* fp, void* dst, size_t length,
break;
}
progress->SetProgressValue((done * 100) / length);
progress->SetProgressValue(startPercent + (done * deltaPercent) / length);
done += read_size;
}
@@ -1925,6 +1974,26 @@ bool FileSystem::SetPathCompression(const char* path, bool enable)
return result;
}
bool FileSystem::CreateSymLink(const char* link, const char* target)
{
// convert to wide string
const std::wstring wlink = GetWin32Path(link);
if (wlink.empty())
return false;
const std::wstring wtarget = GetWin32Path(target);
if (wtarget.empty())
return false;
// check if it's a directory
DWORD flags = 0;
if (DirectoryExists(target))
flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
// create the symbolic link
return CreateSymbolicLinkW(wlink.c_str(), wtarget.c_str(), flags) != 0;
}
bool FileSystem::IsSymbolicLink(const char* path)
{
// convert to wide string
@@ -2505,6 +2574,11 @@ bool FileSystem::SetPathCompression(const char* path, bool enable)
return false;
}
bool FileSystem::CreateSymLink(const char* link, const char* target)
{
return symlink(target, link) == 0;
}
bool FileSystem::IsSymbolicLink(const char* path)
{
struct stat sysStatData;

View File

@@ -106,7 +106,16 @@ namespace FileSystem
/// open files
using ManagedCFilePtr = std::unique_ptr<std::FILE, FileDeleter>;
ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode, Error* error = nullptr);
// Tries to open a file using the given filename, but if that fails searches
// the directory for a file with a case-insensitive match.
// This is the same as OpenManagedCFile on Windows and MacOS
ManagedCFilePtr OpenManagedCFileTryIgnoreCase(const char* filename, const char* mode, Error* error = nullptr);
std::FILE* OpenCFile(const char* filename, const char* mode, Error* error = nullptr);
// Tries to open a file using the given filename, but if that fails searches
// the directory for a file with a case-insensitive match.
// This is the same as OpenCFile on Windows and MacOS
std::FILE* OpenCFileTryIgnoreCase(const char* filename, const char* mode, Error* error = nullptr);
int FSeek64(std::FILE* fp, s64 offset, int whence);
s64 FTell64(std::FILE* fp);
s64 FSize64(std::FILE* fp);
@@ -135,6 +144,8 @@ namespace FileSystem
bool WriteStringToFile(const char* filename, const std::string_view sv);
size_t ReadFileWithProgress(std::FILE* fp, void* dst, size_t length, ProgressCallback* progress,
Error* error = nullptr, size_t chunk_size = 16 * 1024 * 1024);
size_t ReadFileWithPartialProgress(std::FILE* fp, void* dst, size_t length, ProgressCallback* progress,
int startPercent, int endPercent, Error* error = nullptr, size_t chunk_size = 16 * 1024 * 1024);
/// creates a directory in the local filesystem
/// if the directory already exists, the return value will be true.
@@ -169,6 +180,10 @@ namespace FileSystem
/// Does nothing and returns false on non-Windows platforms.
bool SetPathCompression(const char* path, bool enable);
// Creates a symbolic link. Note that on Windows this requires elevated
// privileges so this is mostly useful for testing purposes.
bool CreateSymLink(const char* link, const char* target);
/// Checks if a file or directory is a symbolic link.
bool IsSymbolicLink(const char* path);

View File

@@ -52,10 +52,10 @@
<!-- MSVC automatically adds __AVX__ and __AVX2__ appropriately -->
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">_M_X86;__SSE4_1__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet Condition="!$(Configuration.Contains(AVX2)) Or $(Configuration.Contains(Clang))">NotSet</EnableEnhancedInstructionSet>
<EnableEnhancedInstructionSet Condition="$(Configuration.Contains(AVX2)) And !$(Configuration.Contains(Clang))">AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
<EnableEnhancedInstructionSet Condition="'$(Platform)'=='ARM64' Or !$(Configuration.Contains(AVX2))">NotSet</EnableEnhancedInstructionSet>
<EnableEnhancedInstructionSet Condition="'$(Platform)'=='x64' And $(Configuration.Contains(AVX2))">AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
<!-- Allow SSE4 intrinsics on non-AVX Clang-cl builds -->
<AdditionalOptions Condition="'$(Platform)'=='x64' And $(Configuration.Contains(Clang)) And !$(Configuration.Contains(AVX2))"> -march=nehalem %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='x64' And $(Configuration.Contains(Clang)) And $(Configuration.Contains(AVX2))"> -march=haswell %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='ARM64' And $(Configuration.Contains(Clang))"> -march=armv8.4-a %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="!$(Configuration.Contains(Clang))">%(AdditionalOptions) /Zc:externConstexpr /Zc:__cplusplus /Zo /utf-8</AdditionalOptions>

View File

@@ -88,8 +88,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.interlacing, "EmuCore/GS", "deinterlace_mode", DEFAULT_INTERLACE_MODE);
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.bilinearFiltering, "EmuCore/GS", "linear_present_mode", static_cast<int>(GSPostBilinearMode::BilinearSmooth));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.widescreenPatches, "EmuCore", "EnableWideScreenPatches", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.noInterlacingPatches, "EmuCore", "EnableNoInterlacingPatches", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOffsets, "EmuCore/GS", "pcrtc_offsets", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOverscan, "EmuCore/GS", "pcrtc_overscan", false);
@@ -321,24 +319,22 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
}
#endif
// Get rid of widescreen/no-interlace checkboxes from per-game settings, unless the user previously had them set.
if (m_dialog->isPerGameSettings())
// Prompt user to get rid of widescreen/no-interlace config from the ini if the user has enabled them before.
if ((m_dialog->getBoolValue("EmuCore", "EnableWideScreenPatches", false) == true ||
m_dialog->getBoolValue("EmuCore", "EnableWideScreenPatches", false) == true) &&
!m_dialog->containsSettingValue("UI", "UserHasDeniedWSPatchWarning"))
{
if ((m_dialog->containsSettingValue("EmuCore", "EnableWideScreenPatches") || m_dialog->containsSettingValue("EmuCore", "EnableNoInterlacingPatches")) &&
QMessageBox::question(QtUtils::GetRootWidget(this), tr("Remove Unsupported Settings"),
tr("You currently have the <strong>Enable Widescreen Patches</strong> or <strong>Enable No-Interlacing Patches</strong> options enabled for this game.<br><br>"
"We no longer support these options, instead <strong>you should select the \"Patches\" section, and explicitly enable the patches you want.</strong><br><br>"
"Do you want to remove these options from your game configuration now?"),
if (QMessageBox::question(QtUtils::GetRootWidget(this), tr("Remove Unsupported Settings"),
tr("You previously had the <strong>Enable Widescreen Patches</strong> or <strong>Enable No-Interlacing Patches</strong> options enabled.<br><br>"
"We no longer provide these options, instead <strong>you should go to the \"Patches\" section on the per-game settings, and explicitly enable the patches that you want.</strong><br><br>"
"Do you want to remove these options from your configuration now?"),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
{
m_dialog->removeSettingValue("EmuCore", "EnableWideScreenPatches");
m_dialog->removeSettingValue("EmuCore", "EnableNoInterlacingPatches");
}
m_ui.displayGridLayout->removeWidget(m_ui.widescreenPatches);
m_ui.displayGridLayout->removeWidget(m_ui.noInterlacingPatches);
safe_delete(m_ui.widescreenPatches);
safe_delete(m_ui.noInterlacingPatches);
else
m_dialog->setBoolSettingValue("UI", "UserHasDeniedWSPatchWarning", true);
}
// Hide advanced options by default.
@@ -350,10 +346,13 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
m_ui.advancedTab = nullptr;
m_ui.gsDownloadMode = nullptr;
m_ui.gsDumpCompression = nullptr;
m_ui.texturePreloading = nullptr;
m_ui.exclusiveFullscreenControl = nullptr;
m_ui.useBlitSwapChain = nullptr;
m_ui.disableMailboxPresentation = nullptr;
m_ui.extendedUpscales = nullptr;
m_ui.spinCPUDuringReadbacks = nullptr;
m_ui.spinGPUDuringReadbacks = nullptr;
m_ui.skipPresentingDuplicateFrames = nullptr;
m_ui.overrideTextureBarriers = nullptr;
m_ui.disableFramebufferFetch = nullptr;
@@ -428,12 +427,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
// Display tab
{
dialog->registerWidgetHelp(m_ui.widescreenPatches, tr("Enable Widescreen Patches"), tr("Unchecked"),
tr("Automatically loads and applies widescreen patches on game start. Can cause issues."));
dialog->registerWidgetHelp(m_ui.noInterlacingPatches, tr("Enable No-Interlacing Patches"), tr("Unchecked"),
tr("Automatically loads and applies no-interlacing patches on game start. Can cause issues."));
dialog->registerWidgetHelp(m_ui.DisableInterlaceOffset, tr("Disable Interlace Offset"), tr("Unchecked"),
tr("Disables interlacing offset which may reduce blurring in some situations."));

View File

@@ -404,28 +404,28 @@
</item>
<item row="8" column="0" colspan="2">
<layout class="QGridLayout" name="displayGridLayout">
<item row="1" column="1">
<item row="0" column="1">
<widget class="QCheckBox" name="integerScaling">
<property name="text">
<string>Integer Scaling</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="widescreenPatches">
<item row="2" column="0">
<widget class="QCheckBox" name="PCRTCOverscan">
<property name="text">
<string>Apply Widescreen Patches</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="noInterlacingPatches">
<property name="text">
<string>Apply No-Interlacing Patches</string>
<string>Show Overscan</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="PCRTCOffsets">
<property name="text">
<string>Screen Offsets</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="PCRTCAntiBlur">
<property name="text">
<string>Anti-Blur</string>
@@ -435,27 +435,13 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="1" column="1">
<widget class="QCheckBox" name="DisableInterlaceOffset">
<property name="text">
<string>Disable Interlace Offset</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="PCRTCOffsets">
<property name="text">
<string>Screen Offsets</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="PCRTCOverscan">
<property name="text">
<string>Show Overscan</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@@ -2125,7 +2111,7 @@
</widget>
</item>
<item row="10" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_9">
<layout class="QGridLayout" name="advancedOptionsGrid">
<item row="2" column="0">
<widget class="QCheckBox" name="disableMailboxPresentation">
<property name="text">

File diff suppressed because it is too large Load Diff

View File

@@ -155,7 +155,7 @@ void cdvdLoadNVRAM()
{
Error error;
const std::string nvmfile = cdvdGetNVRAMPath();
auto fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "rb", &error);
auto fp = FileSystem::OpenManagedCFileTryIgnoreCase(nvmfile.c_str(), "rb", &error);
if (!fp || std::fread(s_nvram, sizeof(s_nvram), 1, fp.get()) != 1)
{
ERROR_LOG("Failed to open or read NVRAM at {}: {}", Path::GetFileName(nvmfile), error.GetDescription());
@@ -178,7 +178,7 @@ void cdvdLoadNVRAM()
// Also load the mechacon version while we're here.
const std::string mecfile = Path::ReplaceExtension(BiosPath, "mec");
fp = FileSystem::OpenManagedCFile(mecfile.c_str(), "rb", &error);
fp = FileSystem::OpenManagedCFileTryIgnoreCase(mecfile.c_str(), "rb", &error);
if (!fp || std::fread(&s_mecha_version, sizeof(s_mecha_version), 1, fp.get()) != 1)
{
s_mecha_version = DEFAULT_MECHA_VERSION;
@@ -186,7 +186,7 @@ void cdvdLoadNVRAM()
ERROR_LOG("Failed to open or read MEC file at {}: {}, creating default.", Path::GetFileName(nvmfile),
error.GetDescription());
fp.reset();
fp = FileSystem::OpenManagedCFile(mecfile.c_str(), "wb");
fp = FileSystem::OpenManagedCFileTryIgnoreCase(mecfile.c_str(), "wb");
if (!fp || std::fwrite(&s_mecha_version, sizeof(s_mecha_version), 1, fp.get()) != 1)
Host::ReportErrorAsync("Error", "Failed to write MEC file. Check your BIOS setup/permission settings.");
}
@@ -197,10 +197,10 @@ void cdvdSaveNVRAM()
{
Error error;
const std::string nvmfile = cdvdGetNVRAMPath();
auto fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "r+b", &error);
auto fp = FileSystem::OpenManagedCFileTryIgnoreCase(nvmfile.c_str(), "r+b", &error);
if (!fp)
{
fp = FileSystem::OpenManagedCFile(nvmfile.c_str(), "w+b", &error);
fp = FileSystem::OpenManagedCFileTryIgnoreCase(nvmfile.c_str(), "w+b", &error);
if (!fp) [[unlikely]]
{
ERROR_LOG("Failed to open NVRAM at {} for updating: {}", Path::GetFileName(nvmfile), error.GetDescription());

View File

@@ -20,6 +20,186 @@ static constexpr u32 MAX_PARENTS = 32; // Surely someone wouldn't be insane enou
static std::vector<std::pair<std::string, chd_header>> s_chd_hash_cache; // <filename, header>
static std::recursive_mutex s_chd_hash_cache_mutex;
// Provides an implementation of core_file which allows us to control if the underlying FILE handle is freed.
// Additionally, this class allows greater control and feedback while precaching CHD files.
// The lifetime of ChdCoreFileWrapper will be equal to that of the relevant chd_file,
// ChdCoreFileWrapper will also get destroyed if chd_open_core_file fails.
class ChdCoreFileWrapper
{
DeclareNoncopyableObject(ChdCoreFileWrapper);
private:
core_file m_core;
std::FILE* m_file;
bool m_free_file = false;
ChdCoreFileWrapper* m_parent = nullptr;
std::unique_ptr<u8[]> m_file_cache;
s64 m_file_cache_size;
s64 m_file_cache_pos;
public:
ChdCoreFileWrapper(std::FILE* file, ChdCoreFileWrapper* parent)
: m_file{file}
, m_parent{parent}
{
m_core.argp = this;
m_core.fsize = FSize;
m_core.fread = FRead;
m_core.fclose = FClose;
m_core.fseek = FSeek;
}
~ChdCoreFileWrapper()
{
if (m_free_file && m_file)
std::fclose(m_file);
}
core_file* GetCoreFile()
{
return &m_core;
}
static ChdCoreFileWrapper* FromCoreFile(core_file* file)
{
return reinterpret_cast<ChdCoreFileWrapper*>(file->argp);
}
void SetFileOwner(bool isOwner)
{
m_free_file = isOwner;
}
s64 GetPrecacheSize()
{
const s64 size = static_cast<size_t>(FileSystem::FSize64(m_file));
if (m_parent != nullptr)
return m_parent->GetPrecacheSize() + size;
else
return size;
}
bool Precache(ProgressCallback* progress, Error* error)
{
progress->SetProgressRange(100);
const s64 size = GetPrecacheSize();
return PrecacheInternal(progress, error, 0, size);
}
private:
bool PrecacheInternal(ProgressCallback* progress, Error* error, s64 startSize, s64 finalSize)
{
m_file_cache_size = FileSystem::FSize64(m_file);
if (m_file_cache_size <= 0)
{
Error::SetStringView(error, "Failed to determine file size.");
return false;
}
// Copy the current file position.
m_file_cache_pos = FileSystem::FTell64(m_file);
if (m_file_cache_pos <= 0)
{
Error::SetStringView(error, "Failed to determine file position.");
return false;
}
m_file_cache = std::make_unique_for_overwrite<u8[]>(m_file_cache_size);
if (FileSystem::FSeek64(m_file, 0, SEEK_SET) != 0 ||
FileSystem::ReadFileWithPartialProgress(
m_file, m_file_cache.get(), m_file_cache_size, progress,
(startSize * 100) / finalSize,
((startSize + m_file_cache_size) * 100) / finalSize,
error) != static_cast<size_t>(m_file_cache_size))
{
m_file_cache.reset();
// Precache failed, continue using file
// Restore file position incase it's used for subsequent reads
FileSystem::FSeek64(m_file, m_file_cache_pos, SEEK_SET);
Error::SetStringView(error, "Failed to read part of the file.");
return false;
}
startSize += m_file_cache_size;
if (m_parent)
{
if (!m_parent->PrecacheInternal(progress, error, startSize, finalSize))
{
// Precache failed, continue using file
// Restore file position incase it's used for subsequent reads
FileSystem::FSeek64(m_file, m_file_cache_pos, SEEK_SET);
m_file_cache.reset();
return false;
}
}
if (m_free_file)
std::fclose(m_file);
m_file = nullptr;
return true;
}
static u64 FSize(core_file* file)
{
ChdCoreFileWrapper* fileWrapper = FromCoreFile(file);
if (fileWrapper->m_file_cache)
return fileWrapper->m_file_cache_size;
else
return static_cast<u64>(FileSystem::FSize64(fileWrapper->m_file));
}
static size_t FRead(void* buffer, size_t elmSize, size_t elmCount, core_file* file)
{
ChdCoreFileWrapper* fileWrapper = FromCoreFile(file);
if (fileWrapper->m_file_cache)
{
// While currently libchdr only uses an elmCount of 1, we can't guarantee that will always be the case.
elmCount = std::min<size_t>(elmCount, std::max<s64>(fileWrapper->m_file_cache_size - fileWrapper->m_file_cache_pos, 0) / elmSize);
const size_t size = elmSize * elmCount;
std::memcpy(buffer, &fileWrapper->m_file_cache[fileWrapper->m_file_cache_pos], size);
return elmCount;
}
else
return std::fread(buffer, elmSize, elmCount, fileWrapper->m_file);
}
static int FClose(core_file* file)
{
// Destructor handles freeing the FILE handle.
delete FromCoreFile(file);
return 0;
}
static int FSeek(core_file* file, int64_t offset, int whence)
{
ChdCoreFileWrapper* fileWrapper = FromCoreFile(file);
if (fileWrapper->m_file_cache)
{
switch (whence)
{
case SEEK_SET:
fileWrapper->m_file_cache_pos = offset;
break;
case SEEK_CUR:
fileWrapper->m_file_cache_pos += offset;
break;
case SEEK_END:
fileWrapper->m_file_cache_pos = fileWrapper->m_file_cache_size + offset;
break;
default:
return -1;
}
return 0;
}
else
return FileSystem::FSeek64(fileWrapper->m_file, offset, whence);
}
};
ChdFileReader::ChdFileReader() = default;
ChdFileReader::~ChdFileReader()
@@ -27,13 +207,40 @@ ChdFileReader::~ChdFileReader()
pxAssert(!ChdFile);
}
static bool IsHeaderParentCHD(const chd_header& header, const chd_header& parent_header)
{
static const u8 nullmd5[CHD_MD5_BYTES]{};
static const u8 nullsha1[CHD_SHA1_BYTES]{};
// Check MD5 if it isn't empty.
if (std::memcmp(nullmd5, header.parentmd5, CHD_MD5_BYTES) != 0 &&
std::memcmp(nullmd5, parent_header.md5, CHD_MD5_BYTES) != 0 &&
std::memcmp(parent_header.md5, header.parentmd5, CHD_MD5_BYTES) != 0)
{
return false;
}
// Check SHA1 if it isn't empty.
if (std::memcmp(nullsha1, header.parentsha1, CHD_SHA1_BYTES) != 0 &&
std::memcmp(nullsha1, parent_header.sha1, CHD_SHA1_BYTES) != 0 &&
std::memcmp(parent_header.sha1, header.parentsha1, CHD_SHA1_BYTES) != 0)
{
return false;
}
return true;
}
static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePtr fp, Error* error, u32 recursion_level)
{
chd_file* chd;
chd_error err = chd_open_file(fp.get(), CHD_OPEN_READ | CHD_OPEN_TRANSFER_FILE, nullptr, &chd);
ChdCoreFileWrapper* core_wrapper = new ChdCoreFileWrapper(fp.get(), nullptr);
// libchdr will take ownership of core_wrapper, and will close/free it on failure.
chd_error err = chd_open_core_file(core_wrapper->GetCoreFile(), CHD_OPEN_READ, nullptr, &chd);
if (err == CHDERR_NONE)
{
// fp is now managed by libchdr
// core_wrapper should manage fp.
core_wrapper->SetFileOwner(true);
fp.release();
return chd;
}
@@ -73,14 +280,14 @@ static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePt
if (!StringUtil::compareNoCase(parent_dir, Path::GetDirectory(it->first)))
continue;
if (!chd_is_matching_parent(&header, &it->second))
if (!IsHeaderParentCHD(header, it->second))
continue;
// Re-check the header, it might have changed since we last opened.
chd_header parent_header;
auto parent_fp = FileSystem::OpenManagedSharedCFile(it->first.c_str(), "rb", FileSystem::FileShareMode::DenyWrite);
if (parent_fp && chd_read_header_file(parent_fp.get(), &parent_header) == CHDERR_NONE &&
chd_is_matching_parent(&header, &parent_header))
IsHeaderParentCHD(header, parent_header))
{
// Need to take a copy of the string, because the parent might add to the list and invalidate the iterator.
const std::string filename_to_open = it->first;
@@ -105,7 +312,7 @@ static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePt
parent_dir.c_str(), "*.*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_KEEP_ARRAY, &parent_files);
for (FILESYSTEM_FIND_DATA& fd : parent_files)
{
if (StringUtil::EndsWithNoCase(Path::GetExtension(fd.FileName), ".chd"))
if (!StringUtil::EndsWithNoCase(Path::GetExtension(fd.FileName), "chd"))
continue;
// Re-check the header, it might have changed since we last opened.
@@ -121,7 +328,7 @@ static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePt
else
s_chd_hash_cache.emplace_back(fd.FileName, parent_header);
if (!chd_is_matching_parent(&header, &parent_header))
if (!IsHeaderParentCHD(header, parent_header))
continue;
// Match! Open this one.
@@ -140,8 +347,10 @@ static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePt
return nullptr;
}
// Our last core file wrapper got freed, so make a new one.
core_wrapper = new ChdCoreFileWrapper(fp.get(), ChdCoreFileWrapper::FromCoreFile(chd_core_file(parent_chd)));
// Now try re-opening with the parent.
err = chd_open_file(fp.get(), CHD_OPEN_READ | CHD_OPEN_TRANSFER_FILE, parent_chd, &chd);
err = chd_open_core_file(core_wrapper->GetCoreFile(), CHD_OPEN_READ, parent_chd, &chd);
if (err != CHDERR_NONE)
{
Console.Error(fmt::format("Failed to open CHD '{}': {}", filename, chd_error_string(err)));
@@ -149,7 +358,8 @@ static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePt
return nullptr;
}
// fp now owned by libchdr
// core_wrapper should manage fp.
core_wrapper->SetFileOwner(true);
fp.release();
return chd;
}
@@ -192,28 +402,11 @@ bool ChdFileReader::Open2(std::string filename, Error* error)
bool ChdFileReader::Precache2(ProgressCallback* progress, Error* error)
{
if (!CheckAvailableMemoryForPrecaching(chd_get_compressed_size(ChdFile), error))
ChdCoreFileWrapper* fileWrapper = ChdCoreFileWrapper::FromCoreFile(chd_core_file(ChdFile));
if (!CheckAvailableMemoryForPrecaching(fileWrapper->GetPrecacheSize(), error))
return false;
progress->SetProgressRange(100);
const auto callback = [](size_t pos, size_t total, void* param) -> bool {
ProgressCallback* progress = static_cast<ProgressCallback*>(param);
const u32 percent = static_cast<u32>((pos * 100) / total);
progress->SetProgressValue(std::min<u32>(percent, 100));
return !progress->IsCancelled();
};
const chd_error cerror = chd_precache_progress(ChdFile, callback, progress);
if (cerror != CHDERR_NONE)
{
if (cerror != CHDERR_CANCELLED)
Error::SetStringView(error, "Failed to read part of the file.");
return false;
}
return true;
return fileWrapper->Precache(progress, error);
}
ThreadedFileReader::Chunk ChdFileReader::ChunkForOffset(u64 offset)

View File

@@ -230,49 +230,49 @@ void MapTLB(const tlbs& t, int i)
u32 saddr, eaddr;
COP0_LOG("MAP TLB %d: 0x%08X-> [0x%08X 0x%08X] S=%d G=%d ASID=%d Mask=0x%03X EntryLo0 PFN=%x EntryLo0 Cache=%x EntryLo1 PFN=%x EntryLo1 Cache=%x VPN2=%x",
i, t.VPN2, t.PFN0, t.PFN1, t.S >> 31, t.G, t.ASID,
t.Mask, t.EntryLo0 >> 6, (t.EntryLo0 & 0x38) >> 3, t.EntryLo1 >> 6, (t.EntryLo1 & 0x38) >> 3, t.VPN2);
i, t.VPN2(), t.PFN0(), t.PFN1(), t.isSPR() >> 31, t.isGlobal(), t.EntryHi.ASID,
t.Mask(), t.EntryLo0.PFN, t.EntryLo0.C, t.EntryLo1.PFN, t.EntryLo1.C, t.VPN2());
// According to the manual
// 'It [SPR] must be mapped into a contiguous 16 KB of virtual address space that is
// aligned on a 16KB boundary.Results are not guaranteed if this restriction is not followed.'
// Assume that the game isn't doing anything less-than-ideal with the scratchpad mapping and map it directly to eeMem->Scratch.
if (t.S)
if (t.isSPR())
{
if (t.VPN2 != 0x70000000)
Console.Warning("COP0: Mapping Scratchpad to non-default address 0x%08X", t.VPN2);
if (t.VPN2() != 0x70000000)
Console.Warning("COP0: Mapping Scratchpad to non-default address 0x%08X", t.VPN2());
vtlb_VMapBuffer(t.VPN2, eeMem->Scratch, Ps2MemSize::Scratch);
vtlb_VMapBuffer(t.VPN2(), eeMem->Scratch, Ps2MemSize::Scratch);
}
else
{
if (t.EntryLo0 & 0x2)
if (t.EntryLo0.V)
{
mask = ((~t.Mask) << 1) & 0xfffff;
saddr = t.VPN2 >> 12;
eaddr = saddr + t.Mask + 1;
mask = ((~t.Mask()) << 1) & 0xfffff;
saddr = t.VPN2() >> 12;
eaddr = saddr + t.Mask() + 1;
for (addr = saddr; addr < eaddr; addr++)
{
if ((addr & mask) == ((t.VPN2 >> 12) & mask))
if ((addr & mask) == ((t.VPN2() >> 12) & mask))
{ //match
memSetPageAddr(addr << 12, t.PFN0 + ((addr - saddr) << 12));
memSetPageAddr(addr << 12, t.PFN0() + ((addr - saddr) << 12));
Cpu->Clear(addr << 12, 0x400);
}
}
}
if (t.EntryLo1 & 0x2)
if (t.EntryLo1.V)
{
mask = ((~t.Mask) << 1) & 0xfffff;
saddr = (t.VPN2 >> 12) + t.Mask + 1;
eaddr = saddr + t.Mask + 1;
mask = ((~t.Mask()) << 1) & 0xfffff;
saddr = (t.VPN2() >> 12) + t.Mask() + 1;
eaddr = saddr + t.Mask() + 1;
for (addr = saddr; addr < eaddr; addr++)
{
if ((addr & mask) == ((t.VPN2 >> 12) & mask))
if ((addr & mask) == ((t.VPN2() >> 12) & mask))
{ //match
memSetPageAddr(addr << 12, t.PFN1 + ((addr - saddr) << 12));
memSetPageAddr(addr << 12, t.PFN1() + ((addr - saddr) << 12));
Cpu->Clear(addr << 12, 0x400);
}
}
@@ -280,27 +280,36 @@ void MapTLB(const tlbs& t, int i)
}
}
__inline u32 ConvertPageMask(const u32 PageMask)
{
const u32 mask = std::popcount(PageMask >> 13);
pxAssertMsg(!((mask & 1) || mask > 12), "Invalid page mask for this TLB entry. EE cache doesn't know what to do here.");
return (1 << (12 + mask)) - 1;
}
void UnmapTLB(const tlbs& t, int i)
{
//Console.WriteLn("Clear TLB %d: %08x-> [%08x %08x] S=%d G=%d ASID=%d Mask= %03X", i,t.VPN2,t.PFN0,t.PFN1,t.S,t.G,t.ASID,t.Mask);
u32 mask, addr;
u32 saddr, eaddr;
if (t.S)
if (t.isSPR())
{
vtlb_VMapUnmap(t.VPN2, 0x4000);
vtlb_VMapUnmap(t.VPN2(), 0x4000);
return;
}
if (t.EntryLo0 & 0x2)
if (t.EntryLo0.V)
{
mask = ((~t.Mask) << 1) & 0xfffff;
saddr = t.VPN2 >> 12;
eaddr = saddr + t.Mask + 1;
mask = ((~t.Mask()) << 1) & 0xfffff;
saddr = t.VPN2() >> 12;
eaddr = saddr + t.Mask() + 1;
// Console.WriteLn("Clear TLB: %08x ~ %08x",saddr,eaddr-1);
for (addr = saddr; addr < eaddr; addr++)
{
if ((addr & mask) == ((t.VPN2 >> 12) & mask))
if ((addr & mask) == ((t.VPN2() >> 12) & mask))
{ //match
memClearPageAddr(addr << 12);
Cpu->Clear(addr << 12, 0x400);
@@ -308,38 +317,74 @@ void UnmapTLB(const tlbs& t, int i)
}
}
if (t.EntryLo1 & 0x2)
if (t.EntryLo1.V)
{
mask = ((~t.Mask) << 1) & 0xfffff;
saddr = (t.VPN2 >> 12) + t.Mask + 1;
eaddr = saddr + t.Mask + 1;
mask = ((~t.Mask()) << 1) & 0xfffff;
saddr = (t.VPN2() >> 12) + t.Mask() + 1;
eaddr = saddr + t.Mask() + 1;
// Console.WriteLn("Clear TLB: %08x ~ %08x",saddr,eaddr-1);
for (addr = saddr; addr < eaddr; addr++)
{
if ((addr & mask) == ((t.VPN2 >> 12) & mask))
if ((addr & mask) == ((t.VPN2() >> 12) & mask))
{ //match
memClearPageAddr(addr << 12);
Cpu->Clear(addr << 12, 0x400);
}
}
}
for (size_t i = 0; i < cachedTlbs.count; i++)
{
if (cachedTlbs.PFN0s[i] == t.PFN0() && cachedTlbs.PFN1s[i] == t.PFN1() && cachedTlbs.PageMasks[i] == ConvertPageMask(t.PageMask.UL))
{
for (size_t j = i; j < cachedTlbs.count - 1; j++)
{
cachedTlbs.CacheEnabled0[j] = cachedTlbs.CacheEnabled0[j + 1];
cachedTlbs.CacheEnabled1[j] = cachedTlbs.CacheEnabled1[j + 1];
cachedTlbs.PFN0s[j] = cachedTlbs.PFN0s[j + 1];
cachedTlbs.PFN1s[j] = cachedTlbs.PFN1s[j + 1];
cachedTlbs.PageMasks[j] = cachedTlbs.PageMasks[j + 1];
}
cachedTlbs.count--;
break;
}
}
}
void WriteTLB(int i)
{
tlb[i].PageMask = cpuRegs.CP0.n.PageMask;
tlb[i].EntryHi = cpuRegs.CP0.n.EntryHi;
tlb[i].EntryLo0 = cpuRegs.CP0.n.EntryLo0;
tlb[i].EntryLo1 = cpuRegs.CP0.n.EntryLo1;
tlb[i].PageMask.UL = cpuRegs.CP0.n.PageMask;
tlb[i].EntryHi.UL = cpuRegs.CP0.n.EntryHi;
tlb[i].EntryLo0.UL = cpuRegs.CP0.n.EntryLo0;
tlb[i].EntryLo1.UL = cpuRegs.CP0.n.EntryLo1;
tlb[i].Mask = (cpuRegs.CP0.n.PageMask >> 13) & 0xfff;
tlb[i].nMask = (~tlb[i].Mask) & 0xfff;
tlb[i].VPN2 = ((cpuRegs.CP0.n.EntryHi >> 13) & (~tlb[i].Mask)) << 13;
tlb[i].ASID = cpuRegs.CP0.n.EntryHi & 0xfff;
tlb[i].G = cpuRegs.CP0.n.EntryLo0 & cpuRegs.CP0.n.EntryLo1 & 0x1;
tlb[i].PFN0 = (((cpuRegs.CP0.n.EntryLo0 >> 6) & 0xFFFFF) & (~tlb[i].Mask)) << 12;
tlb[i].PFN1 = (((cpuRegs.CP0.n.EntryLo1 >> 6) & 0xFFFFF) & (~tlb[i].Mask)) << 12;
tlb[i].S = cpuRegs.CP0.n.EntryLo0 & 0x80000000;
// Setting the cache mode to reserved values is vaguely defined in the manual.
// I found that SPR is set to cached regardless.
// Non-SPR entries default to uncached on reserved cache modes.
if (tlb[i].isSPR())
{
tlb[i].EntryLo0.C = 3;
tlb[i].EntryLo1.C = 3;
}
else
{
if (!tlb[i].EntryLo0.isValidCacheMode())
tlb[i].EntryLo0.C = 2;
if (!tlb[i].EntryLo1.isValidCacheMode())
tlb[i].EntryLo1.C = 2;
}
if (!tlb[i].isSPR() && ((tlb[i].EntryLo0.V && tlb[i].EntryLo0.isCached()) || (tlb[i].EntryLo1.V && tlb[i].EntryLo1.isCached())))
{
const size_t idx = cachedTlbs.count;
cachedTlbs.CacheEnabled0[idx] = tlb[i].EntryLo0.isCached() ? ~0 : 0;
cachedTlbs.CacheEnabled1[idx] = tlb[i].EntryLo1.isCached() ? ~0 : 0;
cachedTlbs.PFN1s[idx] = tlb[i].PFN1();
cachedTlbs.PFN0s[idx] = tlb[i].PFN0();
cachedTlbs.PageMasks[idx] = ConvertPageMask(tlb[i].PageMask.UL);
cachedTlbs.count++;
}
MapTLB(tlb[i], i);
}
@@ -355,49 +400,57 @@ namespace COP0 {
cpuRegs.CP0.n.Index, cpuRegs.CP0.n.PageMask, cpuRegs.CP0.n.EntryHi,
cpuRegs.CP0.n.EntryLo0, cpuRegs.CP0.n.EntryLo1);
int i = cpuRegs.CP0.n.Index & 0x3f;
const u8 i = cpuRegs.CP0.n.Index & 0x3f;
cpuRegs.CP0.n.PageMask = tlb[i].PageMask;
cpuRegs.CP0.n.EntryHi = tlb[i].EntryHi & ~(tlb[i].PageMask | 0x1f00);
cpuRegs.CP0.n.EntryLo0 = (tlb[i].EntryLo0 & ~1) | ((tlb[i].EntryHi >> 12) & 1);
cpuRegs.CP0.n.EntryLo1 = (tlb[i].EntryLo1 & ~1) | ((tlb[i].EntryHi >> 12) & 1);
if (i > 47)
{
Console.Warning("TLBR with index > 47! (%d)", i);
return;
}
cpuRegs.CP0.n.PageMask = tlb[i].PageMask.Mask << 13;
cpuRegs.CP0.n.EntryHi = tlb[i].EntryHi.UL & ~((tlb[i].PageMask.Mask << 13) | 0x1f00);
cpuRegs.CP0.n.EntryLo0 = tlb[i].EntryLo0.UL & ~(0xFC000000) & ~1;
cpuRegs.CP0.n.EntryLo1 = tlb[i].EntryLo1.UL & ~(0x7C000000) & ~1;
// "If both the Global bit of EntryLo0 and EntryLo1 are set to 1, the processor ignores the ASID during TLB lookup."
// This is reflected during TLBR, where G is only set if both EntryLo0 and EntryLo1 are global.
cpuRegs.CP0.n.EntryLo0 |= (tlb[i].EntryLo0.UL & 1) & (tlb[i].EntryLo1.UL & 1);
cpuRegs.CP0.n.EntryLo1 |= (tlb[i].EntryLo0.UL & 1) & (tlb[i].EntryLo1.UL & 1);
}
void TLBWI()
{
int j = cpuRegs.CP0.n.Index & 0x3f;
const u8 j = cpuRegs.CP0.n.Index & 0x3f;
//if (j > 48) return;
if (j > 47)
{
Console.Warning("TLBWI with index > 47! (%d)", j);
return;
}
COP0_LOG("COP0_TLBWI %d:%x,%x,%x,%x",
cpuRegs.CP0.n.Index, cpuRegs.CP0.n.PageMask, cpuRegs.CP0.n.EntryHi,
cpuRegs.CP0.n.EntryLo0, cpuRegs.CP0.n.EntryLo1);
UnmapTLB(tlb[j], j);
tlb[j].PageMask = cpuRegs.CP0.n.PageMask;
tlb[j].EntryHi = cpuRegs.CP0.n.EntryHi;
tlb[j].EntryLo0 = cpuRegs.CP0.n.EntryLo0;
tlb[j].EntryLo1 = cpuRegs.CP0.n.EntryLo1;
WriteTLB(j);
}
void TLBWR()
{
int j = cpuRegs.CP0.n.Random & 0x3f;
const u8 j = cpuRegs.CP0.n.Random & 0x3f;
//if (j > 48) return;
if (j > 47)
{
Console.Warning("TLBWR with random > 47! (%d)", j);
return;
}
DevCon.Warning("COP0_TLBWR %d:%x,%x,%x,%x\n",
cpuRegs.CP0.n.Random, cpuRegs.CP0.n.PageMask, cpuRegs.CP0.n.EntryHi,
cpuRegs.CP0.n.EntryLo0, cpuRegs.CP0.n.EntryLo1);
//if (j > 48) return;
UnmapTLB(tlb[j], j);
tlb[j].PageMask = cpuRegs.CP0.n.PageMask;
tlb[j].EntryHi = cpuRegs.CP0.n.EntryHi;
tlb[j].EntryLo0 = cpuRegs.CP0.n.EntryLo0;
tlb[j].EntryLo1 = cpuRegs.CP0.n.EntryLo1;
WriteTLB(j);
}
@@ -422,7 +475,7 @@ namespace COP0 {
cpuRegs.CP0.n.Index = 0xFFFFFFFF;
for (i = 0; i < 48; i++)
{
if (tlb[i].VPN2 == ((~tlb[i].Mask) & (EntryHi32.s.VPN2)) && ((tlb[i].G & 1) || ((tlb[i].ASID & 0xff) == EntryHi32.s.ASID)))
if (tlb[i].VPN2() == ((~tlb[i].Mask()) & (EntryHi32.s.VPN2)) && ((tlb[i].isGlobal()) || ((tlb[i].EntryHi.ASID & 0xff) == EntryHi32.s.ASID)))
{
cpuRegs.CP0.n.Index = i;
break;

View File

@@ -1268,8 +1268,6 @@ struct Pcsx2Config
EnablePatches : 1, // enables patch detection and application
EnableCheats : 1, // enables cheat detection and application
EnablePINE : 1, // enables inter-process communication
EnableWideScreenPatches : 1,
EnableNoInterlacingPatches : 1,
EnableFastBoot : 1,
EnableFastBootFastForward : 1,
EnableThreadPinning : 1,

View File

@@ -6,6 +6,7 @@
#include <atomic>
#include <thread>
#include <functional>
#include <mutex>
#include <shared_mutex>
#include <ccc/ast.h>

View File

@@ -1012,7 +1012,8 @@ bool GSRendererHW::NextDrawMatchesShuffle() const
{
// Make sure nothing unexpected has changed.
// Twinsanity seems to screw with ZBUF here despite it being irrelevant.
const GSDrawingContext& next_ctx = m_env.CTXT[m_backed_up_ctx];
const int get_next_ctx = (m_state_flush_reason == CONTEXTCHANGE) ? m_env.PRIM.CTXT : m_backed_up_ctx;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
if (((m_context->TEX0.U64 ^ next_ctx.TEX0.U64) & (~0x3FFF)) != 0 ||
m_context->TEX1.U64 != next_ctx.TEX1.U64 ||
m_context->CLAMP.U64 != next_ctx.CLAMP.U64 ||
@@ -4553,7 +4554,8 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
// Disable dithering on blend mix if needed.
if (m_conf.ps.dither)
{
const bool can_dither = (m_conf.ps.blend_a == 0 && m_conf.ps.blend_b == 1 && alpha_eq_less_one);
// TODO: Either exclude BMIX1_ALPHA_HIGH_ONE case or allow alpha > 1.0 on dither adjust, case is currently disabled.
const bool can_dither = (m_conf.ps.blend_a == 0 && m_conf.ps.blend_b == 1) || (m_conf.ps.blend_a == 1 && m_conf.ps.blend_b == 0);
m_conf.ps.dither = can_dither;
m_conf.ps.dither_adjust = can_dither;
}

View File

@@ -1742,9 +1742,12 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
TEX0.TBP0, psm_s.pal > 0 ? TEX0.CBP : 0,
psm_str(TEX0.PSM));
// If it's from a target, we need to make sure the alpha information is up to date, especially in 16/24 bit formats where it can change draw to draw.
// If it's an old source made from target make sure it isn't a palette,
// alphas need to be used from the palette then.
// If it's from a target, we need to make sure the alpha information is up to date,
// especially in 16/24 bit formats where it can change draw to draw.
// Guard against merged targets which don't actually link.
if (src->m_target && src->m_from_target)
if (!src->m_palette && src->m_target && src->m_from_target)
{
src->m_valid_alpha_minmax = true;
if (src->m_target_direct)

View File

@@ -3705,15 +3705,6 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
"EmuCore/GS", "StretchY", 100, 10, 300, FSUI_CSTR("%d%%"));
DrawIntRectSetting(bsi, FSUI_CSTR("Crop"), FSUI_CSTR("Crops the image, while respecting aspect ratio."), "EmuCore/GS", "CropLeft", 0,
"CropTop", 0, "CropRight", 0, "CropBottom", 0, 0, 720, 1, FSUI_CSTR("%dpx"));
if (!IsEditingGameSettings(bsi))
{
DrawToggleSetting(bsi, FSUI_CSTR("Enable Widescreen Patches"), FSUI_CSTR("Enables loading widescreen patches from pnach files."),
"EmuCore", "EnableWideScreenPatches", false);
DrawToggleSetting(bsi, FSUI_CSTR("Enable No-Interlacing Patches"),
FSUI_CSTR("Enables loading no-interlacing patches from pnach files."), "EmuCore", "EnableNoInterlacingPatches", false);
}
DrawIntListSetting(bsi, FSUI_CSTR("Bilinear Upscaling"), FSUI_CSTR("Smooths out the image when upscaling the console to the screen."),
"EmuCore/GS", "linear_present_mode", static_cast<int>(GSPostBilinearMode::BilinearSharp), s_bilinear_present_options,
std::size(s_bilinear_present_options), true);
@@ -7059,10 +7050,6 @@ TRANSLATE_NOOP("FullscreenUI", "Increases or decreases the virtual picture size
TRANSLATE_NOOP("FullscreenUI", "Crop");
TRANSLATE_NOOP("FullscreenUI", "Crops the image, while respecting aspect ratio.");
TRANSLATE_NOOP("FullscreenUI", "%dpx");
TRANSLATE_NOOP("FullscreenUI", "Enable Widescreen Patches");
TRANSLATE_NOOP("FullscreenUI", "Enables loading widescreen patches from pnach files.");
TRANSLATE_NOOP("FullscreenUI", "Enable No-Interlacing Patches");
TRANSLATE_NOOP("FullscreenUI", "Enables loading no-interlacing patches from pnach files.");
TRANSLATE_NOOP("FullscreenUI", "Bilinear Upscaling");
TRANSLATE_NOOP("FullscreenUI", "Smooths out the image when upscaling the console to the screen.");
TRANSLATE_NOOP("FullscreenUI", "Integer Upscaling");

View File

@@ -397,10 +397,9 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
EmuConfig.Cpu.Recompiler.GetEEClampMode(), static_cast<unsigned>(EmuConfig.Cpu.VU0FPCR.GetRoundMode()),
EmuConfig.Cpu.Recompiler.GetVUClampMode(), EmuConfig.GS.VsyncQueueSize);
if (EmuConfig.EnableCheats || EmuConfig.EnableWideScreenPatches || EmuConfig.EnableNoInterlacingPatches)
if (EmuConfig.EnableCheats)
{
APPEND("C={}{}{} ", EmuConfig.EnableCheats ? "C" : "", EmuConfig.EnableWideScreenPatches ? "W" : "",
EmuConfig.EnableNoInterlacingPatches ? "N" : "");
APPEND("CHT ");
}
if (GSIsHardwareRenderer())

View File

@@ -852,8 +852,16 @@ namespace R3000A
v0 = file->read(buf.get(), count);
for (s32 i = 0; i < (s32)v0; i++)
iopMemWrite8(data + i, buf[i]);
[[likely]]
if (v0 >= 0 && iopMemSafeWriteBytes(data, buf.get(), v0))
{
psxCpu->Clear(data, (v0 + 3) / 4);
}
else
{
for (s32 i = 0; i < static_cast<s32>(v0); i++)
iopMemWrite8(data + i, buf[i]);
}
pc = ra;
return 1;
@@ -899,8 +907,12 @@ namespace R3000A
{
auto buf = std::make_unique<char[]>(count);
for (u32 i = 0; i < count; i++)
buf[i] = iopMemRead8(data + i);
[[unlikely]]
if (!iopMemSafeReadBytes(data, buf.get(), count))
{
for (u32 i = 0; i < count; i++)
buf[i] = iopMemRead8(data + i);
}
v0 = file->write(buf.get(), count);

View File

@@ -163,9 +163,6 @@ namespace Patch
static void writeCheat();
static void handle_extended_t(const PatchCommand* p);
// Name of patches which will be auto-enabled based on global options.
static constexpr std::string_view WS_PATCH_NAME = "Widescreen 16:9";
static constexpr std::string_view NI_PATCH_NAME = "No-Interlacing";
static constexpr std::string_view PATCHES_ZIP_NAME = "patches.zip";
const char* PATCHES_CONFIG_SECTION = "Patches";
@@ -588,24 +585,6 @@ void Patch::ReloadEnabledLists()
s_enabled_cheats = {};
s_enabled_patches = Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY);
// Name based matching for widescreen/NI settings.
if (EmuConfig.EnableWideScreenPatches)
{
if (std::none_of(s_enabled_patches.begin(), s_enabled_patches.end(),
[](const std::string& it) { return (it == WS_PATCH_NAME); }))
{
s_enabled_patches.emplace_back(WS_PATCH_NAME);
}
}
if (EmuConfig.EnableNoInterlacingPatches)
{
if (std::none_of(s_enabled_patches.begin(), s_enabled_patches.end(),
[](const std::string& it) { return (it == NI_PATCH_NAME); }))
{
s_enabled_patches.emplace_back(NI_PATCH_NAME);
}
}
}
u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable_list)

View File

@@ -4,18 +4,16 @@
#pragma once
// Note about terminology:
// "patch" in pcsx2 terminology is a single pnach style patch line, e.g. patch=1,EE,001110e0,word,00000000
// "Patch" in PCSX2 terminology refers to a single pnach style patch line, e.g. `patch=1,EE,001110e0,word,00000000`
// Such patches can appear in several places:
// - At <CRC>.pnach files where each file could have several such patches:
// - At the "cheats" folder
// - UI name: "Cheats", controlled via system -> enable cheats
// - At the "cheats_ws" folder or inside "cheats_ws.zip" (the zip also called "widescreen cheats DB")
// - the latter is searched if the former is not found for a CRC
// - UI name: "Widescreen hacks/patches", controlled via system -> enable widescreen patches
// - At GameIndex.yaml inside a [patches] section
// - UI name: "Patches", controlled via system -> enable automatic game fixes
// - note that automatic game fixes also controls automatic config changes from GameIndex.dbf (UI name: "fixes")
//
// - At the "patches" folder or on the "patches.zip file inside the 'resources' folder
// - UI name: "Patch", Controlled via Per-Game Settings -> Patches
// - At the "cheats" folder
// - UI name: "Cheats", Controlled via Per-Game Settings -> Cheats -> Enable Cheat
// - At GameIndex.yaml inside a [patches] section
// - UI name: "Enable Compatibility Patches", controlled via Advanced section -> Enable compatability settings
// Note: The file name has to be exactly "<Serial>_<CRC>.pnach" (For example "SLPS-25399_CD62245A.pnach")
// Note #2: the old sytle of cheats are also supported but arent supported by the UI
#include "Config.h"

View File

@@ -1922,8 +1922,6 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
SettingsWrapBitBool(EnablePatches);
SettingsWrapBitBool(EnableCheats);
SettingsWrapBitBool(EnablePINE);
SettingsWrapBitBool(EnableWideScreenPatches);
SettingsWrapBitBool(EnableNoInterlacingPatches);
SettingsWrapBitBool(EnableFastBoot);
SettingsWrapBitBool(EnableFastBootFastForward);
SettingsWrapBitBool(EnableThreadPinning);

View File

@@ -36,6 +36,8 @@ u32 EEoCycle;
alignas(16) cpuRegistersPack _cpuRegistersPack;
alignas(16) tlbs tlb[48];
cachedTlbs_t cachedTlbs;
R5900cpu *Cpu = NULL;
static constexpr uint eeWaitCycles = 3072;
@@ -59,6 +61,7 @@ void cpuReset()
std::memset(&cpuRegs, 0, sizeof(cpuRegs));
std::memset(&fpuRegs, 0, sizeof(fpuRegs));
std::memset(&tlb, 0, sizeof(tlb));
cachedTlbs.count = 0;
cpuRegs.pc = 0xbfc00000; //set pc reg to stack
cpuRegs.CP0.n.Config = 0x440;

View File

@@ -5,6 +5,8 @@
#include "common/Pcsx2Defs.h"
#include <array>
// --------------------------------------------------------------------------------------
// EE Bios function name tables.
// --------------------------------------------------------------------------------------
@@ -160,17 +162,68 @@ struct fpuRegisters {
u32 ACCflag; // an internal accumulator overflow flag
};
union PageMask_t
{
struct
{
u32 : 13;
u32 Mask : 12;
u32 : 7;
};
u32 UL;
};
union EntryHi_t
{
struct
{
u32 ASID:8;
u32 : 5;
u32 VPN2:19;
};
u32 UL;
};
union EntryLo_t
{
struct
{
u32 G:1;
u32 V:1;
u32 D:1;
u32 C:3;
u32 PFN:20;
u32 : 5;
u32 S : 1; // Only used in EntryLo0
};
u32 UL;
constexpr bool isCached() const { return C == 0x3; }
constexpr bool isValidCacheMode() const { return C == 0x2 || C == 0x3 || C == 0x7; }
};
struct tlbs
{
u32 PageMask,EntryHi;
u32 EntryLo0,EntryLo1;
u32 Mask, nMask;
u32 G;
u32 ASID;
u32 VPN2;
u32 PFN0;
u32 PFN1;
u32 S;
PageMask_t PageMask;
EntryHi_t EntryHi;
EntryLo_t EntryLo0;
EntryLo_t EntryLo1;
// (((cpuRegs.CP0.n.EntryLo0 >> 6) & 0xFFFFF) & (~tlb[i].Mask())) << 12;
constexpr u32 PFN0() const { return (EntryLo0.PFN & ~Mask()) << 12; }
constexpr u32 PFN1() const { return (EntryLo1.PFN & ~Mask()) << 12; }
constexpr u32 VPN2() const {return ((EntryHi.VPN2) & (~Mask())) << 13; }
constexpr u32 Mask() const { return PageMask.Mask; }
constexpr bool isGlobal() const { return EntryLo0.G && EntryLo1.G; }
constexpr bool isSPR() const { return EntryLo0.S; }
constexpr bool operator==(const tlbs& other) const
{
return PageMask.UL == other.PageMask.UL &&
EntryHi.UL == other.EntryHi.UL &&
EntryLo0.UL == other.EntryLo0.UL &&
EntryLo1.UL == other.EntryLo1.UL;
}
};
#ifndef _PC_
@@ -211,6 +264,19 @@ struct cpuRegistersPack
alignas(16) extern cpuRegistersPack _cpuRegistersPack;
alignas(16) extern tlbs tlb[48];
struct cachedTlbs_t
{
u32 count;
alignas(16) std::array<u32, 48> PageMasks;
alignas(16) std::array<u32, 48> PFN1s;
alignas(16) std::array<u32, 48> CacheEnabled1;
alignas(16) std::array<u32, 48> PFN0s;
alignas(16) std::array<u32, 48> CacheEnabled0;
};
extern cachedTlbs_t cachedTlbs;
static cpuRegisters& cpuRegs = _cpuRegistersPack.cpuRegs;
static fpuRegisters& fpuRegs = _cpuRegistersPack.fpuRegs;

View File

@@ -5,6 +5,7 @@
#include "SIO/SioTypes.h"
#include "SIO/Memcard/MemoryCardProtocol.h"
#include "Counters.h"
#include "Host.h"
#include "IconsPromptFont.h"
@@ -128,6 +129,8 @@ void AutoEject::ClearAll()
// unsafe to shutdown the VM due to memcard access.
static std::atomic_uint32_t currentBusyTicks = 0;
uint32_t sioLastFrameMcdBusy = 0;
void MemcardBusy::Decrement()
{
if (currentBusyTicks.load(std::memory_order_relaxed) == 0)
@@ -139,6 +142,7 @@ void MemcardBusy::Decrement()
void MemcardBusy::SetBusy()
{
currentBusyTicks.store(300, std::memory_order_release);
sioLastFrameMcdBusy = g_FrameCount;
}
bool MemcardBusy::IsBusy()
@@ -149,4 +153,15 @@ bool MemcardBusy::IsBusy()
void MemcardBusy::ClearBusy()
{
currentBusyTicks.store(0, std::memory_order_release);
sioLastFrameMcdBusy = 0;
}
#include "common/Console.h"
void MemcardBusy::CheckSaveStateDependency()
{
if (g_FrameCount - sioLastFrameMcdBusy > NUM_FRAMES_BEFORE_SAVESTATE_DEPENDENCY_WARNING)
{
Host::AddIconOSDMessage("MemcardBusy", ICON_PF_MEMORY_CARD,
TRANSLATE_SV("MemoryCard", "The virtual console hasn't saved to your memory card for quite some time. Savestates should not be used in place of in-game saves."), Host::OSD_INFO_DURATION);
}
}

View File

@@ -118,10 +118,19 @@ namespace AutoEject
extern void ClearAll();
} // namespace AutoEject
// ~1 hour of memory card inactivity.
constexpr u32 NUM_FRAMES_BEFORE_SAVESTATE_DEPENDENCY_WARNING = 60 * 60 * 60;
// Set to the current frame count when there is memory card activity.
// Used to detect the last frame when memory card activity was detected,
// and if it exceeds a certain threshold, warns on savestate save/load.
extern uint32_t sioLastFrameMcdBusy;
namespace MemcardBusy
{
extern void Decrement();
extern void SetBusy();
extern bool IsBusy();
extern void ClearBusy();
extern void CheckSaveStateDependency();
}

View File

@@ -541,5 +541,6 @@ bool Sio2::DoState(StateWrapper& sw)
}
}
sw.Do(&sioLastFrameMcdBusy);
return sw.IsGood();
}

View File

@@ -15,12 +15,13 @@
#include "Host.h"
#include "MTGS.h"
#include "MTVU.h"
#include "SIO/Pad/Pad.h"
#include "Patch.h"
#include "R3000A.h"
#include "SIO/Multitap/MultitapProtocol.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Sio.h"
#include "SIO/Sio0.h"
#include "SIO/Sio2.h"
#include "SIO/Multitap/MultitapProtocol.h"
#include "SPU2/spu2.h"
#include "SaveState.h"
#include "StateWrapper.h"
@@ -182,6 +183,7 @@ bool SaveStateBase::FreezeInternals(Error* error)
Freeze(psxRegs); // iop regs
Freeze(fpuRegs);
Freeze(tlb); // tlbs
Freeze(cachedTlbs); // cached tlbs
Freeze(AllowParams1); //OSDConfig written (Fast Boot)
Freeze(AllowParams2);
@@ -315,7 +317,7 @@ memLoadingState::memLoadingState(const VmStateBuffer& load_from)
// Loading of state data from a memory buffer...
void memLoadingState::FreezeMem( void* data, int size )
{
if (m_idx + size > m_memory.size())
if (static_cast<u32>(m_idx + size) > m_memory.size())
m_error = true;
if (m_error)

View File

@@ -25,7 +25,7 @@ enum class FreezeAction
// [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated.
static const u32 g_SaveVersion = (0x9A51 << 16) | 0x0000;
static const u32 g_SaveVersion = (0x9A53 << 16) | 0x0000;
// the freezing data between submodules and core

View File

@@ -1818,6 +1818,7 @@ bool VMManager::DoLoadState(const char* filename)
MTGS::PresentCurrentFrame();
}
MemcardBusy::CheckSaveStateDependency();
return true;
}
@@ -1866,6 +1867,7 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
}
Host::OnSaveStateSaved(filename);
MemcardBusy::CheckSaveStateDependency();
return true;
}
@@ -2885,8 +2887,6 @@ void VMManager::CheckForEmulationSpeedConfigChanges(const Pcsx2Config& old_confi
void VMManager::CheckForPatchConfigChanges(const Pcsx2Config& old_config)
{
if (EmuConfig.EnableCheats == old_config.EnableCheats &&
EmuConfig.EnableWideScreenPatches == old_config.EnableWideScreenPatches &&
EmuConfig.EnableNoInterlacingPatches == old_config.EnableNoInterlacingPatches &&
EmuConfig.EnablePatches == old_config.EnablePatches)
{
return;

View File

@@ -226,7 +226,7 @@ static void LoadExtraRom(const char* ext, u32 offset, u32 size)
BiosRom.resize(offset + size);
auto fp = FileSystem::OpenManagedCFile(Bios1.c_str(), "rb");
auto fp = FileSystem::OpenManagedCFileTryIgnoreCase(Bios1.c_str(), "rb");
if (!fp || std::fread(&BiosRom[offset], static_cast<size_t>(std::min<s64>(size, filesize)), 1, fp.get()) != 1)
{
Console.Warning("BIOS Warning: %s could not be read (permission denied?)", ext);

View File

@@ -30,6 +30,7 @@
#include "fmt/core.h"
#include <bit>
#include <immintrin.h>
#include <map>
#include <unordered_set>
#include <unordered_map>
@@ -109,46 +110,77 @@ vtlb_private::VTLBVirtual::VTLBVirtual(VTLBPhysical phys, u32 paddr, u32 vaddr)
}
}
__inline int ConvertPageMask(u32 PageMask)
{
const u32 mask = std::popcount(PageMask >> 13);
pxAssertMsg(!((mask & 1) || mask > 12), "Invalid page mask for this TLB entry. EE cache doesn't know what to do here.");
return (1 << (12 + mask)) - 1;
}
__inline int CheckCache(u32 addr)
{
u32 mask;
// Check if the cache is enabled
if (((cpuRegs.CP0.n.Config >> 16) & 0x1) == 0)
{
//DevCon.Warning("Data Cache Disabled! %x", cpuRegs.CP0.n.Config);
return false; //
return false;
}
for (int i = 1; i < 48; i++)
const size_t size = cachedTlbs.count;
const int stride = 4;
__m128i addr_vec = _mm_set1_epi32(addr);
size_t i = 0;
for (; i + stride <= size; i += stride)
{
if (((tlb[i].EntryLo1 & 0x38) >> 3) == 0x3)
const __m128i pfn1_vec = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&cachedTlbs.PFN1s[i]));
const __m128i pfn0_vec = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&cachedTlbs.PFN0s[i]));
const __m128i mask_vec = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&cachedTlbs.PageMasks[i]));
const __m128i cached1_vec = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&cachedTlbs.CacheEnabled1[i]));
const __m128i cached0_vec = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&cachedTlbs.CacheEnabled0[i]));
const __m128i pfn1_end_vec = _mm_add_epi32(pfn1_vec, mask_vec);
const __m128i pfn0_end_vec = _mm_add_epi32(pfn0_vec, mask_vec);
// pfn0 <= addr
const __m128i gteLowerBound0 = _mm_or_si128(
_mm_cmpgt_epi32(addr_vec, pfn0_vec),
_mm_cmpeq_epi32(addr_vec, pfn0_vec));
// pfn0 + mask >= addr
const __m128i gteUpperBound0 = _mm_or_si128(
_mm_cmpgt_epi32(pfn0_end_vec, addr_vec),
_mm_cmpeq_epi32(pfn0_end_vec, addr_vec));
// pfn1 <= addr
const __m128i gteUpperBound1 = _mm_or_si128(
_mm_cmpgt_epi32(pfn1_end_vec, addr_vec),
_mm_cmpeq_epi32(pfn1_end_vec, addr_vec));
// pfn1 + mask >= addr
const __m128i gteLowerBound1 = _mm_or_si128(
_mm_cmpgt_epi32(addr_vec, pfn1_vec),
_mm_cmpeq_epi32(addr_vec, pfn1_vec));
// pfn0 <= addr <= pfn0 + mask
__m128i cmp0 = _mm_and_si128(gteLowerBound0, gteUpperBound0);
// pfn1 <= addr <= pfn1 + mask
__m128i cmp1 = _mm_and_si128(gteLowerBound1, gteUpperBound1);
cmp1 = _mm_and_si128(cmp1, cached1_vec);
cmp0 = _mm_and_si128(cmp0, cached0_vec);
const __m128i cmp = _mm_or_si128(cmp1, cmp0);
if (!_mm_testz_si128(cmp, cmp))
{
mask = ConvertPageMask(tlb[i].PageMask);
if ((addr >= tlb[i].PFN1) && (addr <= tlb[i].PFN1 + mask))
{
//DevCon.Warning("Yay! Cache check cache addr=%x, mask=%x, addr+mask=%x, VPN2=%x PFN0=%x", addr, mask, (addr & mask), tlb[i].VPN2, tlb[i].PFN0);
return true;
}
}
if (((tlb[i].EntryLo0 & 0x38) >> 3) == 0x3)
{
mask = ConvertPageMask(tlb[i].PageMask);
if ((addr >= tlb[i].PFN0) && (addr <= tlb[i].PFN0 + mask))
{
//DevCon.Warning("Yay! Cache check cache addr=%x, mask=%x, addr+mask=%x, VPN2=%x PFN0=%x", addr, mask, (addr & mask), tlb[i].VPN2, tlb[i].PFN0);
return true;
}
return true;
}
}
for (; i < size; i++)
{
const u32 mask = cachedTlbs.PageMasks[i];
if ((cachedTlbs.CacheEnabled1[i] && addr >= cachedTlbs.PFN1s[i] && addr <= cachedTlbs.PFN1s[i] + mask) ||
(cachedTlbs.CacheEnabled0[i] && addr >= cachedTlbs.PFN0s[i] && addr <= cachedTlbs.PFN0s[i] + mask))
{
return true;
}
}
return false;
}
// --------------------------------------------------------------------------------------

View File

@@ -7,8 +7,6 @@
#ifdef __linux__
#include <unistd.h>
static std::optional<std::string> create_test_directory()
{
for (u16 i = 0; i < UINT16_MAX; i++)
@@ -42,13 +40,13 @@ TEST(FileSystem, RecursiveDeleteDirectoryDontFollowSymbolicLinks)
std::string dir_to_delete = Path::Combine(*test_dir, "dir_to_delete");
ASSERT_TRUE(FileSystem::CreateDirectoryPath(dir_to_delete.c_str(), false));
std::string symlink_path = Path::Combine(dir_to_delete, "link");
ASSERT_EQ(symlink(target_dir.c_str(), symlink_path.c_str()), 0);
ASSERT_TRUE(FileSystem::CreateSymLink(symlink_path.c_str(), target_dir.c_str()));
// Delete the directory containing the symlink.
ASSERT_TRUE(dir_to_delete.starts_with("/tmp/"));
ASSERT_TRUE(dir_to_delete.starts_with("/tmp/pcsx2_filesystem_test_"));
ASSERT_TRUE(FileSystem::RecursiveDeleteDirectory(dir_to_delete.c_str()));
// Make sure the target file didn't get deleted.
// Make sure the file in the target directory didn't get deleted.
ASSERT_TRUE(FileSystem::FileExists(file_path.c_str()));
// Clean up.

View File

@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "common/FileSystem.h"
#include "common/Pcsx2Defs.h"
#include "common/Path.h"
#include <gtest/gtest.h>
@@ -238,16 +239,129 @@ TEST(Path, CreateFileURL)
#endif
}
#if 0
#if __linux__
// Relies on presence of files.
TEST(Path, RealPath)
static std::optional<std::string> create_test_directory()
{
#ifdef _WIN32
ASSERT_EQ(Path::RealPath("C:\\Users\\Me\\Desktop\\foo\\baz"), "C:\\Users\\Me\\Desktop\\foo\\bar\\baz");
#else
ASSERT_EQ(Path::RealPath("/lib/foo/bar"), "/usr/lib/foo/bar");
#endif
for (u16 i = 0; i < UINT16_MAX; i++)
{
std::string path = std::string("/tmp/pcsx2_path_test_") + std::to_string(i);
if (!FileSystem::DirectoryExists(path.c_str()))
{
if (!FileSystem::CreateDirectoryPath(path.c_str(), false))
break;
return path;
}
}
return std::nullopt;
}
TEST(Path, RealPathAbsoluteSymbolicLink)
{
std::optional<std::string> test_dir = create_test_directory();
ASSERT_TRUE(test_dir.has_value());
// Create a file to point at.
std::string file_path = Path::Combine(*test_dir, "file");
ASSERT_TRUE(FileSystem::WriteStringToFile(file_path.c_str(), "Hello, world!"));
// Create a symbolic link that points to said file.
std::string link_path = Path::Combine(*test_dir, "link");
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), file_path.c_str()));
// Make sure the symbolic link is resolved correctly.
ASSERT_EQ(Path::RealPath(link_path), file_path);
// Clean up.
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
ASSERT_TRUE(FileSystem::DeleteFilePath(file_path.c_str()));
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
}
TEST(Path, RealPathRelativeSymbolicLink)
{
std::optional<std::string> test_dir = create_test_directory();
ASSERT_TRUE(test_dir.has_value());
// Create a file to point at.
std::string file_path = Path::Combine(*test_dir, "file");
ASSERT_TRUE(FileSystem::WriteStringToFile(file_path.c_str(), "Hello, world!"));
// Create a symbolic link that points to said file.
std::string link_path = Path::Combine(*test_dir, "link");
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), "file"));
// Make sure the symbolic link is resolved correctly.
ASSERT_EQ(Path::RealPath(link_path), file_path);
// Clean up.
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
ASSERT_TRUE(FileSystem::DeleteFilePath(file_path.c_str()));
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
}
TEST(Path, RealPathDotDotSymbolicLink)
{
std::optional<std::string> test_dir = create_test_directory();
ASSERT_TRUE(test_dir.has_value());
// Create a file to point at.
std::string file_path = Path::Combine(*test_dir, "file");
ASSERT_TRUE(FileSystem::WriteStringToFile(file_path.c_str(), "Hello, world!"));
// Create a directory to put the link in.
std::string link_dir = Path::Combine(*test_dir, "dir");
ASSERT_TRUE(FileSystem::CreateDirectoryPath(link_dir.c_str(), false));
// Create a symbolic link that points to said file.
std::string link_path = Path::Combine(link_dir, "link");
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), "../file"));
// Make sure the symbolic link is resolved correctly.
ASSERT_EQ(Path::RealPath(link_path), file_path);
// Clean up.
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
ASSERT_TRUE(FileSystem::DeleteDirectory(link_dir.c_str()));
ASSERT_TRUE(FileSystem::DeleteFilePath(file_path.c_str()));
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
}
TEST(Path, RealPathCircularSymbolicLink)
{
std::optional<std::string> test_dir = create_test_directory();
ASSERT_TRUE(test_dir.has_value());
// Create a circular symbolic link.
std::string link_path = Path::Combine(*test_dir, "link");
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), "."));
// Make sure the link gets resolved correctly.
ASSERT_EQ(Path::RealPath(link_path), *test_dir);
ASSERT_EQ(Path::RealPath(Path::Combine(link_path, "link")), *test_dir);
// Clean up.
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
}
TEST(Path, RealPathLoopingSymbolicLink)
{
std::optional<std::string> test_dir = create_test_directory();
ASSERT_TRUE(test_dir.has_value());
// Create a symbolic link that points to itself.
std::string link_path = Path::Combine(*test_dir, "link");
ASSERT_TRUE(FileSystem::CreateSymLink(link_path.c_str(), "link"));
// Make sure this doesn't cause problems.
ASSERT_EQ(Path::RealPath(link_path), link_path);
// Clean up.
ASSERT_TRUE(FileSystem::DeleteSymbolicLink(link_path.c_str()));
ASSERT_TRUE(FileSystem::DeleteDirectory(test_dir->c_str()));
}
#endif