merge from upstream

This commit is contained in:
Sergey P 2023-02-01 13:33:07 +03:00
commit bf2cabb82c
170 changed files with 5991 additions and 1904 deletions

View file

@ -38,7 +38,11 @@ include:
# MacOS 64-bit
- project: 'libretro-infrastructure/ci-templates'
file: '/osx-x64.yml'
file: '/osx-cmake-x86.yml'
# MacOS arm64
- project: 'libretro-infrastructure/ci-templates'
file: '/osx-cmake-arm64.yml'
################################## CELLULAR ################################
# Android
@ -92,7 +96,16 @@ libretro-build-osx-x64:
tags:
- macosx-packaging
extends:
- .libretro-osx-x64-make-default
- .libretro-osx-cmake-x86_64
- .core-defs
- .make-defs
# MacOS 64-bit
libretro-build-osx-arm64:
tags:
- macosx-packaging
extends:
- .libretro-osx-cmake-arm64
- .core-defs
- .make-defs

3
.gitmodules vendored
View file

@ -38,3 +38,6 @@
path = assets/debugger
url = https://github.com/unknownbrackets/ppsspp-debugger.git
branch = bundled
[submodule "cpu_features"]
path = ext/cpu_features
url = https://github.com/google/cpu_features.git

View file

@ -256,6 +256,7 @@ include(FindThreads)
if(APPLE)
find_library(COCOA_LIBRARY Cocoa)
find_library(IOKIT_LIBRARY IOKit)
find_library(QUARTZ_CORE_LIBRARY QuartzCore)
endif()
@ -775,12 +776,13 @@ add_library(Common STATIC
Common/SysError.cpp
Common/TimeUtil.cpp
Common/TimeUtil.h
Common/Battery/Battery.h
)
include_directories(Common)
setup_target_project(Common Common)
target_link_libraries(Common Ext::Snappy)
target_link_libraries(Common Ext::Snappy cpu_features)
if(USING_GLES2 OR (USING_EGL AND NOT USING_FBDEV))
find_package(X11)
@ -1150,6 +1152,7 @@ elseif(IOS)
ios/iCade/iCadeState.h
UI/DarwinMemoryStickManager.mm
UI/DarwinMemoryStickManager.h
Common/Battery/AppleBatteryClient.m
)
set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreVideo -framework CoreMedia -framework CoreServices" )
@ -1170,6 +1173,7 @@ elseif(IOS)
set_source_files_properties(ios/CameraHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(ios/LocationHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(UI/DarwinMemoryStickManager.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
set(TargetBin PPSSPP)
elseif(USING_QT_UI)
set(CMAKE_AUTOMOC ON)
@ -1227,9 +1231,10 @@ elseif(TARGET SDL2::SDL2)
endif()
set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2)
if(APPLE)
set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm SDL/SDLCocoaMetalLayer.h SDL/SDLCocoaMetalLayer.mm UI/DarwinMemoryStickManager.mm)
set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm SDL/SDLCocoaMetalLayer.h SDL/SDLCocoaMetalLayer.mm UI/DarwinMemoryStickManager.mm Common/Battery/AppleBatteryClient.m)
set_source_files_properties(UI/DarwinMemoryStickManager.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY})
set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY})
elseif(USING_EGL)
set(nativeExtraLibs ${nativeExtraLibs} pthread)
endif()
@ -2378,7 +2383,7 @@ if(HEADLESS)
)
endif()
add_executable(PPSSPPHeadless ${HeadlessSource})
target_link_libraries(PPSSPPHeadless ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${LinkCommon})
target_link_libraries(PPSSPPHeadless ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} ${LinkCommon})
setup_target_project(PPSSPPHeadless headless)
endif()
@ -2398,7 +2403,7 @@ if(UNITTEST)
Core/MIPS/ARM/ArmRegCache.cpp
Core/MIPS/ARM/ArmRegCacheFPU.cpp
)
target_link_libraries(PPSSPPUnitTest ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${LinkCommon} Common)
target_link_libraries(PPSSPPUnitTest ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} ${LinkCommon} Common)
setup_target_project(PPSSPPUnitTest unittest)
add_test(arm64_emitter PPSSPPUnitTest Arm64Emitter)
add_test(arm_emitter PPSSPPUnitTest ArmEmitter)

View file

@ -26,6 +26,20 @@
#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64)
#if PPSSPP_ARCH(ARM)
#include "ext/cpu_features/include/cpuinfo_arm.h"
#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
#define USE_CPU_FEATURES 1
#endif
#elif PPSSPP_ARCH(ARM64) && defined(__aarch64__)
#include "ext/cpu_features/include/cpuinfo_aarch64.h"
#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
#define USE_CPU_FEATURES 1
#endif
#endif
#include <ctype.h>
#include "Common/CommonTypes.h"
@ -337,31 +351,81 @@ void CPUInfo::Detect()
bNEON = true;
bASIMD = true;
#endif
#if PPSSPP_ARCH(ARM) && defined(USE_CPU_FEATURES)
cpu_features::ArmInfo info = cpu_features::GetArmInfo();
bSwp = info.features.swp;
bHalf = info.features.half;
bThumb = info.features.thumb;
bFastMult = info.features.fastmult;
bEDSP = info.features.edsp;
bThumbEE = info.features.thumbee;
bNEON = info.features.neon;
bTLS = info.features.tls;
bVFP = info.features.vfp;
bVFPv3 = info.features.vfpv3;
bVFPv4 = info.features.vfpv4;
bIDIVa = info.features.idiva;
bIDIVt = info.features.idivt;
#endif
#if PPSSPP_ARCH(ARM64) && defined(USE_CPU_FEATURES)
cpu_features::Aarch64Info info = cpu_features::GetAarch64Info();
bFP = info.features.fp;
bASIMD = info.features.asimd;
bSVE = info.features.sve;
bSVE2 = info.features.sve2;
bFRINT = info.features.frint;
#endif
}
std::vector<std::string> CPUInfo::Features() {
std::vector<std::string> features;
struct Flag {
bool &flag;
const char *str;
};
const Flag list[] = {
{ bSwp, "SWP" },
{ bHalf, "Half" },
{ bThumb, "Thumb" },
{ bFastMult, "FastMult" },
{ bEDSP, "EDSP" },
{ bThumbEE, "ThumbEE" },
{ bTLS, "TLS" },
{ bVFP, "VFP" },
{ bVFPv3, "VFPv3" },
{ bVFPv4, "VFPv4" },
{ bNEON, "NEON" },
{ bIDIVa, "IDIVa" },
{ bIDIVt, "IDIVt" },
{ bFRINT, "FRINT" },
{ bSVE, "SVE" },
{ bSVE2, "SVE2" },
{ CPU64bit, "64-bit" },
};
for (auto &item : list) {
if (item.flag) {
features.push_back(item.str);
}
}
return features;
}
// Turn the cpu info into a string we can show
std::string CPUInfo::Summarize()
{
std::string CPUInfo::Summarize() {
std::string sum;
if (num_cores == 1)
sum = StringFromFormat("%s, %d core", cpu_string, num_cores);
else
sum = StringFromFormat("%s, %d cores", cpu_string, num_cores);
if (bSwp) sum += ", SWP";
if (bHalf) sum += ", Half";
if (bThumb) sum += ", Thumb";
if (bFastMult) sum += ", FastMult";
if (bEDSP) sum += ", EDSP";
if (bThumbEE) sum += ", ThumbEE";
if (bTLS) sum += ", TLS";
if (bVFP) sum += ", VFP";
if (bVFPv3) sum += ", VFPv3";
if (bVFPv4) sum += ", VFPv4";
if (bNEON) sum += ", NEON";
if (bIDIVa) sum += ", IDIVa";
if (bIDIVt) sum += ", IDIVt";
if (CPU64bit) sum += ", 64-bit";
auto features = Features();
for (std::string &feature : features) {
sum += ", " + feature;
}
return sum;
}

View file

@ -0,0 +1,96 @@
//
// AppleBatteryClient.m
// PPSSPP
//
// Created by Serena on 24/01/2023.
//
#include "Battery.h"
#import <Foundation/Foundation.h>
#if PPSSPP_PLATFORM(MAC)
#include <IOKit/ps/IOPSKeys.h>
#include <IOKit/ps/IOPowerSources.h>
#elif PPSSPP_PLATFORM(IOS)
#import <UIKit/UIKit.h>
#endif
@interface AppleBatteryClient : NSObject
+(instancetype)sharedClient;
-(void)setNeedsToUpdateLevel;
@property int batteryLevel;
@end
void _powerSourceRunLoopCallback(void * __unused ctx) {
// IOKit has told us that battery information has changed, now update the batteryLevel var
[[AppleBatteryClient sharedClient] setNeedsToUpdateLevel];
}
// You may ask,
// "Why an entire class?
// Why not just call the UIDevice/IOKitPowerSource functions every time getCurrentBatteryCapacity() is called?"
// Well, calling the UIDevice/IOKitPowerSource functions very frequently (every second, it seems?) is expensive
// So, instead, I made a class with a cached batteryLevel property
// that only gets set when it needs to.
@implementation AppleBatteryClient
+ (instancetype)sharedClient {
static AppleBatteryClient *client;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
client = [AppleBatteryClient new];
[client initialSetup];
[client setNeedsToUpdateLevel];
});
return client;
}
-(void)initialSetup {
#if TARGET_OS_IOS
// on iOS, this needs to be true to get the battery level
// and it needs to be set just once, so do it here
UIDevice.currentDevice.batteryMonitoringEnabled = YES;
// Register for when the battery % changes
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(setNeedsToUpdateLevel)
name:UIDeviceBatteryLevelDidChangeNotification object:nil];
#elif TARGET_OS_MAC
CFRunLoopSourceRef loop = IOPSNotificationCreateRunLoopSource(_powerSourceRunLoopCallback, nil);
CFRunLoopAddSource(CFRunLoopGetMain(), loop, kCFRunLoopDefaultMode);
#endif
}
- (void)setNeedsToUpdateLevel {
#if TARGET_OS_IOS
// `-[UIDevice batteryLevel]` returns the % like '0.(actual %)' (ie, 0.28 when the battery is 28%)
// so multiply it by 100 to get a visually appropriate version
self.batteryLevel = [[UIDevice currentDevice] batteryLevel] * 100;
#elif TARGET_OS_MAC
CFTypeRef snapshot = IOPSCopyPowerSourcesInfo();
NSArray *sourceList = (__bridge NSArray *)IOPSCopyPowerSourcesList(snapshot);
if (!sourceList) {
if (snapshot) CFRelease(snapshot);
return;
}
for (NSDictionary *source in sourceList) {
// kIOPSCurrentCapacityKey = battery level
NSNumber *currentCapacity = [source objectForKey:@(kIOPSCurrentCapacityKey)];
if (currentCapacity) {
// we found what we want
self.batteryLevel = currentCapacity.intValue;
break;
}
}
CFRelease(snapshot);
#endif
}
@end
int getCurrentBatteryCapacity() {
return [[AppleBatteryClient sharedClient] batteryLevel];
}

32
Common/Battery/Battery.h Normal file
View file

@ -0,0 +1,32 @@
//
// Battery.h
// PPSSPP
//
// Created by Serena on 24/01/2023.
//
// NOTE: Though this is a general purpose header file,
// though the implementation right now is Darwin specific
// In case any future platform implementations are made for other platforms,
// define the function below in their own file
#ifndef BATTERY_H
#define BATTERY_H
#include "ppsspp_config.h"
//#include <Foundation/Foundation.h>
#ifdef __cplusplus
extern "C" {
#endif
#if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(MAC)
#define CAN_DISPLAY_CURRENT_BATTERY_CAPACITY
/// Get the current battery %.
int getCurrentBatteryCapacity();
#endif /* PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(MAC) */
#ifdef __cplusplus
}
#endif
#endif /* BATTERY_H */

View file

@ -19,6 +19,12 @@
#include "ppsspp_config.h"
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#include "ext/cpu_features/include/cpuinfo_x86.h"
#if defined(CPU_FEATURES_OS_FREEBSD) || defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID) || defined(CPU_FEATURES_OS_MACOS) || defined(CPU_FEATURES_OS_WINDOWS)
#define USE_CPU_FEATURES 1
#endif
#ifdef __ANDROID__
#include <sys/stat.h>
#include <fcntl.h>
@ -141,6 +147,10 @@ static std::vector<int> ParseCPUList(const std::string &filename) {
// Detects the various cpu features
void CPUInfo::Detect() {
#ifdef USE_CPU_FEATURES
cpu_features::X86Info info = cpu_features::GetX86Info();
#endif
memset(this, 0, sizeof(*this));
#if PPSSPP_ARCH(X86)
Mode64bit = false;
@ -185,7 +195,50 @@ void CPUInfo::Detect() {
vendor = VENDOR_OTHER;
// Set reasonable default brand string even if brand string not available.
strcpy(brand_string, cpu_string);
#ifdef USE_CPU_FEATURES
if (info.brand_string[0])
strcpy(brand_string, info.brand_string);
else
#endif
strcpy(brand_string, cpu_string);
#ifdef USE_CPU_FEATURES
switch (cpu_features::GetX86Microarchitecture(&info)) {
case cpu_features::INTEL_ATOM_BNL:
case cpu_features::INTEL_ATOM_SMT:
case cpu_features::INTEL_ATOM_GMT:
case cpu_features::INTEL_ATOM_GMT_PLUS:
case cpu_features::INTEL_ATOM_TMT:
bAtom = true;
break;
default:
bAtom = false;
break;
}
bPOPCNT = info.features.popcnt;
bBMI1 = info.features.bmi1;
bBMI2 = info.features.bmi2;
bBMI2_fast = bBMI2 && (vendor != VENDOR_AMD || info.family >= 0x19);
bMOVBE = info.features.movbe;
bLZCNT = info.features.lzcnt;
bRTM = info.features.rtm;
bSSE = info.features.sse;
bSSE2 = info.features.sse2;
bSSE3 = info.features.sse3;
bSSSE3 = info.features.ssse3;
bSSE4_1 = info.features.sse4_1;
bSSE4_2 = info.features.sse4_2;
bSSE4A = info.features.sse4a;
bAES = info.features.aes;
bSHA = info.features.sha;
bF16C = info.features.f16c;
bAVX = info.features.avx;
bAVX2 = info.features.avx2;
bFMA3 = info.features.fma3;
bFMA4 = info.features.fma4;
#endif
// Detect family and other misc stuff.
bool ht = false;
@ -193,16 +246,19 @@ void CPUInfo::Detect() {
logical_cpu_count = 1;
if (max_std_fn >= 1) {
do_cpuid(cpu_id, 0x00000001);
#ifndef USE_CPU_FEATURES
int family = ((cpu_id[0] >> 8) & 0xf) + ((cpu_id[0] >> 20) & 0xff);
int model = ((cpu_id[0] >> 4) & 0xf) + ((cpu_id[0] >> 12) & 0xf0);
// Detect people unfortunate enough to be running PPSSPP on an Atom
if (family == 6 && (model == 0x1C || model == 0x26 || model == 0x27 || model == 0x35 || model == 0x36 ||
model == 0x37 || model == 0x4A || model == 0x4D || model == 0x5A || model == 0x5D))
bAtom = true;
#endif
logical_cpu_count = (cpu_id[1] >> 16) & 0xFF;
ht = (cpu_id[3] >> 28) & 1;
#ifndef USE_CPU_FEATURES
if ((cpu_id[3] >> 25) & 1) bSSE = true;
if ((cpu_id[3] >> 26) & 1) bSSE2 = true;
if ((cpu_id[2]) & 1) bSSE3 = true;
@ -215,6 +271,7 @@ void CPUInfo::Detect() {
bFMA3 = true;
}
if ((cpu_id[2] >> 25) & 1) bAES = true;
#endif
if ((cpu_id[3] >> 24) & 1)
{
@ -222,6 +279,7 @@ void CPUInfo::Detect() {
bFXSR = true;
}
#ifndef USE_CPU_FEATURES
// AVX support requires 3 separate checks:
// - Is the AVX bit set in CPUID? (>>28)
// - Is the XSAVE bit set in CPUID? ( >>26)
@ -257,8 +315,10 @@ void CPUInfo::Detect() {
}
bBMI2_fast = bBMI2 && (vendor != VENDOR_AMD || family >= 0x19);
#endif
}
if (max_ex_fn >= 0x80000004) {
#ifndef USE_CPU_FEATURES
// Extract brand string
do_cpuid(cpu_id, 0x80000002);
memcpy(brand_string, cpu_id, sizeof(cpu_id));
@ -266,13 +326,16 @@ void CPUInfo::Detect() {
memcpy(brand_string + 16, cpu_id, sizeof(cpu_id));
do_cpuid(cpu_id, 0x80000004);
memcpy(brand_string + 32, cpu_id, sizeof(cpu_id));
#endif
}
if (max_ex_fn >= 0x80000001) {
// Check for more features.
do_cpuid(cpu_id, 0x80000001);
if (cpu_id[2] & 1) bLAHFSAHF64 = true;
#ifndef USE_CPU_FEATURES
if ((cpu_id[2] >> 6) & 1) bSSE4A = true;
if ((cpu_id[2] >> 16) & 1) bFMA4 = true;
#endif
if ((cpu_id[2] >> 11) & 1) bXOP = true;
// CmpLegacy (bit 2) is deprecated.
if ((cpu_id[3] >> 29) & 1) bLongMode = true;
@ -418,34 +481,63 @@ void CPUInfo::Detect() {
logical_cpu_count = 1;
}
// Turn the cpu info into a string we can show
std::string CPUInfo::Summarize()
{
std::string sum;
if (num_cores == 1)
sum = StringFromFormat("%s, %d core", cpu_string, num_cores);
else
{
sum = StringFromFormat("%s, %d cores", cpu_string, num_cores);
if (HTT) sum += StringFromFormat(" (%i logical threads per physical core)", logical_cpu_count);
std::vector<std::string> CPUInfo::Features() {
std::vector<std::string> features;
struct Flag {
bool &flag;
const char *str;
};
const Flag list[] = {
{ bSSE, "SSE" },
{ bSSE2, "SSE2" },
{ bSSE3, "SSE3" },
{ bSSSE3, "SSSE3" },
{ bSSE4_1, "SSE4.1" },
{ bSSE4_2, "SSE4.2" },
{ bSSE4A, "SSE4A" },
{ HTT, "HTT" },
{ bAVX, "AVX" },
{ bAVX2, "AVX2" },
{ bFMA3, "FMA3" },
{ bFMA4, "FMA4" },
{ bAES, "AES" },
{ bSHA, "SHA" },
{ bXOP, "XOP" },
{ bRTM, "TSX" },
{ bF16C, "F16C" },
{ bBMI1, "BMI1" },
{ bBMI2, "BMI2" },
{ bPOPCNT, "POPCNT" },
{ bMOVBE, "MOVBE" },
{ bLZCNT, "LZCNT" },
{ bLongMode, "64-bit support" },
};
for (auto &item : list) {
if (item.flag) {
features.push_back(item.str);
}
}
return features;
}
// Turn the cpu info into a string we can show
std::string CPUInfo::Summarize() {
std::string sum;
if (num_cores == 1) {
sum = StringFromFormat("%s, %d core", cpu_string, num_cores);
} else {
sum = StringFromFormat("%s, %d cores", cpu_string, num_cores);
if (HTT)
sum += StringFromFormat(" (%i logical threads per physical core)", logical_cpu_count);
}
auto features = Features();
for (std::string &feature : features) {
sum += ", " + feature;
}
if (bSSE) sum += ", SSE";
if (bSSE2) sum += ", SSE2";
if (bSSE3) sum += ", SSE3";
if (bSSSE3) sum += ", SSSE3";
if (bSSE4_1) sum += ", SSE4.1";
if (bSSE4_2) sum += ", SSE4.2";
if (bSSE4A) sum += ", SSE4A";
if (HTT) sum += ", HTT";
if (bAVX) sum += ", AVX";
if (bAVX2) sum += ", AVX2";
if (bFMA3) sum += ", FMA3";
if (bFMA4) sum += ", FMA4";
if (bAES) sum += ", AES";
if (bSHA) sum += ", SHA";
if (bXOP) sum += ", XOP";
if (bRTM) sum += ", TSX";
if (bLongMode) sum += ", 64-bit support";
return sum;
}

View file

@ -20,6 +20,7 @@
#include "ppsspp_config.h"
#include <string>
#include <vector>
enum CPUVendor {
VENDOR_INTEL = 0,
@ -68,6 +69,7 @@ struct CPUInfo {
bool bSSE4A;
bool bAES;
bool bSHA;
bool bF16C;
// x86 : SIMD 256 bit
bool bAVX;
bool bAVX2;
@ -92,6 +94,9 @@ struct CPUInfo {
// ARMv8 specific
bool bFP;
bool bASIMD;
bool bSVE;
bool bSVE2;
bool bFRINT;
// MIPS specific
bool bXBurst1;
@ -105,6 +110,7 @@ struct CPUInfo {
bool RiscV_C;
bool RiscV_V;
bool RiscV_B;
bool RiscV_Zicsr;
// Quirks
struct {
@ -120,6 +126,7 @@ struct CPUInfo {
explicit CPUInfo();
// Turn the cpu info into a string we can show
std::vector<std::string> Features();
std::string Summarize();
private:

View file

@ -1021,6 +1021,9 @@
<Text Include="..\ext\libpng17\CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ext\cpu_features.vcxproj">
<Project>{c249f016-7f82-45cf-bb6e-0642a988c4d3}</Project>
</ProjectReference>
<ProjectReference Include="..\ext\zlib\zlib.vcxproj">
<Project>{f761046e-6c38-4428-a5f1-38391a37bb34}</Project>
</ProjectReference>

View file

@ -53,11 +53,20 @@ void CPUInfo::Detect()
logical_cpu_count = 2;
}
std::vector<std::string> CPUInfo::Features() {
std::vector<std::string> features;
return features;
}
// Turn the cpu info into a string we can show
std::string CPUInfo::Summarize()
{
std::string CPUInfo::Summarize() {
std::string sum;
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
auto features = Features();
for (std::string &feature : features) {
sum += ", " + feature;
}
return sum;
}
#endif

View file

@ -1166,17 +1166,20 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
glBindBuffer(GL_ARRAY_BUFFER, buf);
curArrayBuffer = buf;
}
int enable = layout->semanticsMask_ & ~attrMask;
int disable = (~layout->semanticsMask_) & attrMask;
for (int i = 0; i < 7; i++) { // SEM_MAX
if (enable & (1 << i)) {
glEnableVertexAttribArray(i);
}
if (disable & (1 << i)) {
glDisableVertexAttribArray(i);
if (attrMask != layout->semanticsMask_) {
int enable = layout->semanticsMask_ & ~attrMask;
int disable = (~layout->semanticsMask_) & attrMask;
for (int i = 0; i < 7; i++) { // SEM_MAX
if (enable & (1 << i)) {
glEnableVertexAttribArray(i);
}
if (disable & (1 << i)) {
glDisableVertexAttribArray(i);
}
}
attrMask = layout->semanticsMask_;
}
attrMask = layout->semanticsMask_;
for (size_t i = 0; i < layout->entries.size(); i++) {
auto &entry = layout->entries[i];
glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.bindVertexBuffer.offset + entry.offset));

View file

@ -2,6 +2,7 @@
#include <cstdint>
#include <vector>
#include <set>
#include <unordered_map>
#include "Common/GPU/OpenGL/GLCommon.h"

View file

@ -6,6 +6,7 @@
#include "Common/VR/PPSSPPVR.h"
#include "Common/Log.h"
#include "Common/TimeUtil.h"
#include "Common/MemoryUtil.h"
#include "Common/Math/math_util.h"
@ -112,6 +113,8 @@ void GLDeleter::Perform(GLRenderManager *renderManager, bool skipGLCalls) {
}
GLRenderManager::~GLRenderManager() {
_dbg_assert_(!run_);
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
_assert_(frameData_[i].deleter.IsEmpty());
_assert_(frameData_[i].deleter_prev.IsEmpty());
@ -123,7 +126,6 @@ GLRenderManager::~GLRenderManager() {
void GLRenderManager::ThreadStart(Draw::DrawContext *draw) {
queueRunner_.CreateDeviceObjects();
threadFrame_ = threadInitFrame_;
renderThreadId = std::this_thread::get_id();
if (newInflightFrames_ != -1) {
@ -169,24 +171,16 @@ void GLRenderManager::ThreadStart(Draw::DrawContext *draw) {
void GLRenderManager::ThreadEnd() {
INFO_LOG(G3D, "ThreadEnd");
// Wait for any shutdown to complete in StopThread().
std::unique_lock<std::mutex> lock(mutex_);
queueRunner_.DestroyDeviceObjects();
VLOG("PULL: Quitting");
VLOG(" PULL: Quitting");
// Good point to run all the deleters to get rid of leftover objects.
// Good time to run all the deleters to get rid of leftover objects.
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
// Since we're in shutdown, we should skip the GL calls on Android.
frameData_[i].deleter.Perform(this, skipGLCalls_);
frameData_[i].deleter_prev.Perform(this, skipGLCalls_);
for (int j = 0; j < (int)frameData_[i].steps.size(); j++) {
delete frameData_[i].steps[j];
}
frameData_[i].steps.clear();
frameData_[i].initSteps.clear();
}
deleter_.Perform(this, skipGLCalls_);
for (int i = 0; i < (int)steps_.size(); i++) {
delete steps_[i];
}
@ -194,104 +188,68 @@ void GLRenderManager::ThreadEnd() {
initSteps_.clear();
}
// Unlike in Vulkan, this isn't a full independent function, instead it gets called every frame.
//
// This means that we have to block and run the render queue until we've presented one frame,
// at which point we can leave.
//
// NOTE: If run_ is true, we WILL run a task!
bool GLRenderManager::ThreadFrame() {
std::unique_lock<std::mutex> lock(mutex_);
if (!run_)
if (!run_) {
return false;
}
GLRRenderThreadTask task;
// In case of syncs or other partial completion, we keep going until we complete a frame.
do {
if (nextFrame) {
threadFrame_++;
if (threadFrame_ >= inflightFrames_)
threadFrame_ = 0;
}
FrameData &frameData = frameData_[threadFrame_];
{
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
while (!frameData.readyForRun && run_) {
VLOG("PULL: Waiting for frame[%d].readyForRun", threadFrame_);
frameData.pull_condVar.wait(lock);
}
if (!frameData.readyForRun && !run_) {
// This means we're out of frames to render and run_ is false, so bail.
return false;
}
VLOG("PULL: Setting frame[%d].readyForRun = false", threadFrame_);
frameData.readyForRun = false;
frameData.deleter_prev.Perform(this, skipGLCalls_);
frameData.deleter_prev.Take(frameData.deleter);
// Previously we had a quick exit here that avoided calling Run() if run_ was suddenly false,
// but that created a race condition where frames could end up not finished properly on resize etc.
while (true) {
// Pop a task of the queue and execute it.
// NOTE: We need to actually wait for a task, we can't just bail!
// Only increment next time if we're done.
nextFrame = frameData.type == GLRRunType::END;
_assert_(frameData.type == GLRRunType::END || frameData.type == GLRRunType::SYNC);
{
std::unique_lock<std::mutex> lock(pushMutex_);
while (renderThreadQueue_.empty()) {
pushCondVar_.wait(lock);
}
task = renderThreadQueue_.front();
renderThreadQueue_.pop();
}
VLOG("PULL: Running frame %d", threadFrame_);
if (firstFrame) {
INFO_LOG(G3D, "Running first frame (%d)", threadFrame_);
firstFrame = false;
// We got a task! We can now have pushMutex_ unlocked, allowing the host to
// push more work when it feels like it, and just start working.
if (task.runType == GLRRunType::EXIT) {
// Oh, host wanted out. Let's leave, and also let's notify the host.
// This is unlike Vulkan too which can just block on the thread existing.
std::unique_lock<std::mutex> lock(syncMutex_);
syncCondVar_.notify_one();
syncDone_ = true;
break;
}
// Render the scene.
Run(threadFrame_);
VLOG("PULL: Finished frame %d", threadFrame_);
} while (!nextFrame);
VLOG(" PULL: Frame %d RUN (%0.3f)", task.frame, time_now_d());
if (Run(task)) {
// Swap requested, so we just bail the loop.
break;
}
};
return true;
}
void GLRenderManager::StopThread() {
// Since we don't control the thread directly, this will only pause the thread.
// There's not really a lot to do here anymore.
INFO_LOG(G3D, "GLRenderManager::StopThread()");
if (run_) {
run_ = false;
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
auto &frameData = frameData_[i];
{
std::unique_lock<std::mutex> lock(frameData.push_mutex);
frameData.push_condVar.notify_all();
}
{
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
frameData.pull_condVar.notify_all();
}
}
// Wait until we've definitely stopped the threadframe.
std::unique_lock<std::mutex> lock(mutex_);
INFO_LOG(G3D, "GL submission thread paused. Frame=%d", curFrame_);
// Eat whatever has been queued up for this frame if anything.
Wipe();
// Wait for any fences to finish and be resignaled, so we don't have sync issues.
// Also clean out any queued data, which might refer to things that might not be valid
// when we restart...
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
auto &frameData = frameData_[i];
std::unique_lock<std::mutex> lock(frameData.push_mutex);
if (frameData.readyForRun || frameData.steps.size() != 0) {
Crash();
}
frameData.readyForRun = false;
frameData.readyForSubmit = false;
for (size_t i = 0; i < frameData.steps.size(); i++) {
delete frameData.steps[i];
}
frameData.steps.clear();
frameData.initSteps.clear();
while (!frameData.readyForFence) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (stop)", i);
frameData.push_condVar.wait(lock);
}
}
std::unique_lock<std::mutex> lock(pushMutex_);
GLRRenderThreadTask exitTask{};
exitTask.runType = GLRRunType::EXIT;
renderThreadQueue_.push(exitTask);
pushCondVar_.notify_one();
} else {
INFO_LOG(G3D, "GL submission thread was already paused.");
WARN_LOG(G3D, "GL submission thread was already paused.");
}
}
@ -450,60 +408,51 @@ void GLRenderManager::CopyImageToMemorySync(GLRTexture *texture, int mipLevel, i
}
void GLRenderManager::BeginFrame() {
VLOG("BeginFrame");
#ifdef _DEBUG
curProgram_ = nullptr;
#endif
int curFrame = GetCurFrame();
FrameData &frameData = frameData_[curFrame];
// Make sure the very last command buffer from the frame before the previous has been fully executed.
FrameData &frameData = frameData_[curFrame];
{
std::unique_lock<std::mutex> lock(frameData.push_mutex);
VLOG("PUSH: BeginFrame (curFrame = %d, readyForFence = %d, time=%0.3f)", curFrame, (int)frameData.readyForFence, time_now_d());
std::unique_lock<std::mutex> lock(frameData.fenceMutex);
while (!frameData.readyForFence) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame);
frameData.push_condVar.wait(lock);
frameData.fenceCondVar.wait(lock);
}
frameData.readyForFence = false;
frameData.readyForSubmit = true;
}
VLOG("PUSH: Fencing %d", curFrame);
// glFenceSync(&frameData.fence...)
// Must be after the fence - this performs deletes.
VLOG("PUSH: BeginFrame %d", curFrame);
if (!run_) {
WARN_LOG(G3D, "BeginFrame while !run_!");
}
// vulkan_->BeginFrame();
// In GL, we have to do deletes on the submission thread.
insideFrame_ = true;
}
void GLRenderManager::Finish() {
curRenderStep_ = nullptr;
curRenderStep_ = nullptr; // EndCurRenderStep is this simple here.
int curFrame = GetCurFrame();
FrameData &frameData = frameData_[curFrame];
{
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
VLOG("PUSH: Frame[%d].readyForRun = true, notifying pull", curFrame);
frameData.steps = std::move(steps_);
steps_.clear();
frameData.initSteps = std::move(initSteps_);
initSteps_.clear();
frameData.readyForRun = true;
frameData.type = GLRRunType::END;
frameData_[curFrame_].deleter.Take(deleter_);
}
// Notify calls do not in fact need to be done with the mutex locked.
frameData.pull_condVar.notify_all();
frameData_[curFrame].deleter.Take(deleter_);
VLOG("PUSH: Finish, pushing task. curFrame = %d", curFrame);
GLRRenderThreadTask task;
task.frame = curFrame;
task.runType = GLRRunType::PRESENT;
{
std::unique_lock<std::mutex> lock(pushMutex_);
renderThreadQueue_.push(task);
renderThreadQueue_.back().initSteps = std::move(initSteps_);
renderThreadQueue_.back().steps = std::move(steps_);
initSteps_.clear();
steps_.clear();
pushCondVar_.notify_one();
}
curFrame_++;
if (curFrame_ >= inflightFrames_)
@ -512,65 +461,19 @@ void GLRenderManager::Finish() {
insideFrame_ = false;
}
void GLRenderManager::BeginSubmitFrame(int frame) {
FrameData &frameData = frameData_[frame];
// Render thread. Returns true if the caller should handle a swap.
bool GLRenderManager::Run(GLRRenderThreadTask &task) {
FrameData &frameData = frameData_[task.frame];
if (!frameData.hasBegun) {
frameData.hasBegun = true;
frameData.deleter_prev.Perform(this, skipGLCalls_);
frameData.deleter_prev.Take(frameData.deleter);
}
}
// Render thread
void GLRenderManager::Submit(int frame, bool triggerFence) {
FrameData &frameData = frameData_[frame];
// In GL, submission happens automatically in Run().
// When !triggerFence, we notify after syncing with Vulkan.
if (triggerFence) {
VLOG("PULL: Frame %d.readyForFence = true", frame);
std::unique_lock<std::mutex> lock(frameData.push_mutex);
_assert_(frameData.readyForSubmit);
frameData.readyForFence = true;
frameData.readyForSubmit = false;
frameData.push_condVar.notify_all();
}
}
// Render thread
void GLRenderManager::EndSubmitFrame(int frame) {
FrameData &frameData = frameData_[frame];
frameData.hasBegun = false;
Submit(frame, true);
if (!frameData.skipSwap) {
if (swapIntervalChanged_) {
swapIntervalChanged_ = false;
if (swapIntervalFunction_) {
swapIntervalFunction_(swapInterval_);
}
}
if (swapFunction_) {
swapFunction_();
}
} else {
frameData.skipSwap = false;
}
}
// Render thread
void GLRenderManager::Run(int frame) {
BeginSubmitFrame(frame);
FrameData &frameData = frameData_[frame];
auto &stepsOnThread = frameData_[frame].steps;
auto &initStepsOnThread = frameData_[frame].initSteps;
// queueRunner_.LogSteps(stepsOnThread);
queueRunner_.RunInitSteps(initStepsOnThread, skipGLCalls_);
initStepsOnThread.clear();
queueRunner_.RunInitSteps(task.initSteps, skipGLCalls_);
// Run this after RunInitSteps so any fresh GLRBuffers for the pushbuffers can get created.
if (!skipGLCalls_) {
@ -584,13 +487,12 @@ void GLRenderManager::Run(int frame) {
int passes = GetVRPassesCount();
for (int i = 0; i < passes; i++) {
PreVRFrameRender(i);
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1, true);
queueRunner_.RunSteps(task.steps, skipGLCalls_, i < passes - 1, true);
PostVRFrameRender();
}
} else {
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, false, false);
queueRunner_.RunSteps(task.steps, skipGLCalls_, false, false);
}
stepsOnThread.clear();
if (!skipGLCalls_) {
for (auto iter : frameData.activePushBuffers) {
@ -598,89 +500,88 @@ void GLRenderManager::Run(int frame) {
}
}
switch (frameData.type) {
case GLRRunType::END:
EndSubmitFrame(frame);
bool swapRequest = false;
switch (task.runType) {
case GLRRunType::PRESENT:
if (!frameData.skipSwap) {
if (swapIntervalChanged_) {
swapIntervalChanged_ = false;
if (swapIntervalFunction_) {
swapIntervalFunction_(swapInterval_);
}
}
// This is the swapchain framebuffer flip.
if (swapFunction_) {
VLOG(" PULL: SwapFunction()");
swapFunction_();
if (!retainControl_) {
// get out of here.
swapRequest = true;
}
} else {
VLOG(" PULL: SwapRequested");
swapRequest = true;
}
} else {
frameData.skipSwap = false;
}
frameData.hasBegun = false;
VLOG(" PULL: Frame %d.readyForFence = true", task.frame);
{
std::lock_guard<std::mutex> lock(frameData.fenceMutex);
frameData.readyForFence = true;
frameData.fenceCondVar.notify_one();
// At this point, we're done with this framedata (for now).
}
break;
case GLRRunType::SYNC:
EndSyncFrame(frame);
frameData.hasBegun = false;
// glFinish is not actually necessary here, and won't be unless we start using
// glBufferStorage. Then we need to use fences.
{
std::unique_lock<std::mutex> lock(syncMutex_);
syncDone_ = true;
syncCondVar_.notify_one();
}
break;
default:
_assert_(false);
}
VLOG("PULL: Finished running frame %d", frame);
VLOG(" PULL: ::Run(): Done running tasks");
return swapRequest;
}
void GLRenderManager::FlushSync() {
int curFrame = curFrame_;
FrameData &frameData = frameData_[curFrame];
{
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
VLOG("PUSH: Frame[%d].readyForRun = true (sync)", curFrame);
frameData.initSteps = std::move(initSteps_);
initSteps_.clear();
frameData.steps = std::move(steps_);
VLOG("PUSH: Frame[%d].readyForRun = true (sync)", curFrame_);
GLRRenderThreadTask task;
task.frame = curFrame_;
task.runType = GLRRunType::SYNC;
std::unique_lock<std::mutex> lock(pushMutex_);
renderThreadQueue_.push(task);
renderThreadQueue_.back().initSteps = std::move(initSteps_);
renderThreadQueue_.back().steps = std::move(steps_);
pushCondVar_.notify_one();
steps_.clear();
frameData.readyForRun = true;
_assert_(frameData.readyForFence == false);
frameData.type = GLRRunType::SYNC;
frameData.pull_condVar.notify_all();
}
{
std::unique_lock<std::mutex> lock(frameData.push_mutex);
std::unique_lock<std::mutex> lock(syncMutex_);
// Wait for the flush to be hit, since we're syncing.
while (!frameData.readyForFence) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (sync)", curFrame);
frameData.push_condVar.wait(lock);
}
frameData.readyForFence = false;
frameData.readyForSubmit = true;
}
}
// Render thread
void GLRenderManager::EndSyncFrame(int frame) {
FrameData &frameData = frameData_[frame];
Submit(frame, false);
// glFinish is not actually necessary here, and won't be until we start using
// glBufferStorage. Then we need to use fences.
// glFinish();
// At this point we can resume filling the command buffers for the current frame since
// we know the device is idle - and thus all previously enqueued command buffers have been processed.
// No need to switch to the next frame number.
{
std::unique_lock<std::mutex> lock(frameData.push_mutex);
frameData.readyForFence = true;
frameData.readyForSubmit = true;
frameData.push_condVar.notify_all();
}
}
void GLRenderManager::Wipe() {
initSteps_.clear();
for (auto step : steps_) {
delete step;
}
steps_.clear();
}
void GLRenderManager::WaitUntilQueueIdle() {
// Just wait for all frames to be ready.
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
FrameData &frameData = frameData_[i];
std::unique_lock<std::mutex> lock(frameData.push_mutex);
// Ignore unsubmitted frames.
while (!frameData.readyForFence && frameData.readyForRun) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (wait idle)", i);
frameData.push_condVar.wait(lock);
while (!syncDone_) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (sync)", curFrame_);
syncCondVar_.wait(lock);
}
syncDone_ = false;
}
}

View file

@ -7,6 +7,7 @@
#include <set>
#include <string>
#include <mutex>
#include <queue>
#include <condition_variable>
#include "Common/GPU/OpenGL/GLCommon.h"
@ -349,11 +350,29 @@ private:
GLBufferStrategy strategy_ = GLBufferStrategy::SUBDATA;
};
enum class GLRRunType {
END,
SYNC,
class GLRInputLayout {
public:
struct Entry {
int location;
int count;
GLenum type;
GLboolean normalized;
int stride;
intptr_t offset;
};
std::vector<Entry> entries;
int semanticsMask_ = 0;
};
enum class GLRRunType {
PRESENT,
SYNC,
EXIT,
};
class GLRenderManager;
class GLPushBuffer;
class GLDeleter {
public:
void Perform(GLRenderManager *renderManager, bool skipGLCalls);
@ -373,18 +392,14 @@ public:
std::vector<GLPushBuffer *> pushBuffers;
};
class GLRInputLayout {
public:
struct Entry {
int location;
int count;
GLenum type;
GLboolean normalized;
int stride;
intptr_t offset;
};
std::vector<Entry> entries;
int semanticsMask_ = 0;
// These are enqueued from the main thread,
// and the render thread pops them off
struct GLRRenderThreadTask {
std::vector<GLRStep *> steps;
std::vector<GLRInitStep> initSteps;
int frame;
GLRRunType runType;
};
// Note: The GLRenderManager is created and destroyed on the render thread, and the latter
@ -395,9 +410,15 @@ public:
GLRenderManager() {}
~GLRenderManager();
void SetInvalidationCallback(InvalidationCallback callback) {
invalidationCallback_ = callback;
}
void ThreadStart(Draw::DrawContext *draw);
void ThreadEnd();
bool ThreadFrame(); // Returns true if it did anything. False means the queue was empty.
void SetErrorCallback(ErrorCallbackFn callback, void *userdata) {
queueRunner_.SetErrorCallback(callback, userdata);
}
@ -406,27 +427,17 @@ public:
caps_ = caps;
}
void ThreadStart(Draw::DrawContext *draw);
void ThreadEnd();
bool ThreadFrame(); // Returns false to request exiting the loop.
// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.
void BeginFrame();
// Can run on a different thread!
void Finish();
void Run(int frame);
// Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown.
void Wipe();
// Wait until no frames are pending. Use during shutdown before freeing pointers.
void WaitUntilQueueIdle();
void Finish();
bool Run(GLRRenderThreadTask &task);
// Creation commands. These were not needed in Vulkan since there we can do that on the main thread.
// We pass in width/height here even though it's not strictly needed until we support glTextureStorage
// and then we'll also need formats and stuff.
GLRTexture *CreateTexture(GLenum target, int width, int height, int depth, int numMips) {
GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE };
GLRInitStep step { GLRInitStepType::CREATE_TEXTURE };
step.create_texture.texture = new GLRTexture(caps_, width, height, depth, numMips);
step.create_texture.texture->target = target;
initSteps_.push_back(step);
@ -981,8 +992,9 @@ public:
_dbg_assert_(foundCount == 1);
}
void SetSwapFunction(std::function<void()> swapFunction) {
void SetSwapFunction(std::function<void()> swapFunction, bool retainControl) {
swapFunction_ = swapFunction;
retainControl_ = retainControl;
}
void SetSwapIntervalFunction(std::function<void(int)> swapIntervalFunction) {
@ -1014,13 +1026,8 @@ public:
}
private:
void BeginSubmitFrame(int frame);
void EndSubmitFrame(int frame);
void Submit(int frame, bool triggerFence);
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
void FlushSync();
void EndSyncFrame(int frame);
// When using legacy functionality for push buffers (glBufferData), we need to flush them
// before actually making the glDraw* calls. It's best if the render manager handles that.
@ -1030,26 +1037,14 @@ private:
// Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
struct FrameData {
std::mutex push_mutex;
std::condition_variable push_condVar;
std::mutex pull_mutex;
std::condition_variable pull_condVar;
bool readyForFence = true;
bool readyForRun = false;
bool readyForSubmit = false;
bool skipSwap = false;
GLRRunType type = GLRRunType::END;
// GLuint fence; For future AZDO stuff?
std::vector<GLRStep *> steps;
std::vector<GLRInitStep> initSteps;
std::mutex fenceMutex;
std::condition_variable fenceCondVar;
bool readyForFence = true;
// Swapchain.
bool hasBegun = false;
uint32_t curSwapchainImage = -1;
GLDeleter deleter;
GLDeleter deleter_prev;
@ -1067,16 +1062,23 @@ private:
// Execution time state
bool run_ = true;
// Thread is managed elsewhere, and should call ThreadFrame.
std::mutex mutex_;
int threadInitFrame_ = 0;
GLQueueRunner queueRunner_;
// Thread state
int threadFrame_ = -1;
// For pushing data on the queue.
std::mutex pushMutex_;
std::condition_variable pushCondVar_;
bool nextFrame = false;
bool firstFrame = true;
std::queue<GLRRenderThreadTask> renderThreadQueue_;
// For readbacks and other reasons we need to sync with the render thread.
std::mutex syncMutex_;
std::condition_variable syncCondVar_;
bool firstFrame_ = true;
bool vrRenderStarted_ = false;
bool syncDone_ = false;
GLDeleter deleter_;
bool skipGLCalls_ = false;
@ -1085,6 +1087,7 @@ private:
std::function<void()> swapFunction_;
std::function<void(int)> swapIntervalFunction_;
bool retainControl_ = false;
GLBufferStrategy bufferStrategy_ = GLBufferStrategy::SUBDATA;
int inflightFrames_ = MAX_INFLIGHT_FRAMES;

View file

@ -212,7 +212,6 @@ GLuint ShaderStageToOpenGL(ShaderStage stage) {
class OpenGLShaderModule : public ShaderModule {
public:
OpenGLShaderModule(GLRenderManager *render, ShaderStage stage, const std::string &tag) : render_(render), stage_(stage), tag_(tag) {
DEBUG_LOG(G3D, "Shader module created (%p)", this);
glstage_ = ShaderStageToOpenGL(stage);
}

View file

@ -1197,11 +1197,11 @@ void VulkanRenderManager::Finish() {
int curFrame = vulkan_->GetCurFrame();
FrameData &frameData = frameData_[curFrame];
VLOG("PUSH: Frame[%d]", curFrame);
VKRRenderThreadTask task;
task.frame = curFrame;
task.runType = VKRRunType::PRESENT;
{
VLOG("PUSH: Frame[%d]", curFrame);
VKRRenderThreadTask task;
task.frame = curFrame;
task.runType = VKRRunType::PRESENT;
std::unique_lock<std::mutex> lock(pushMutex_);
renderThreadQueue_.push(task);
renderThreadQueue_.back().steps = std::move(steps_);
@ -1327,7 +1327,7 @@ void VulkanRenderManager::FlushSync() {
std::unique_lock<std::mutex> lock(syncMutex_);
// Wait for the flush to be hit, since we're syncing.
while (!frameData.syncDone) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (sync)", curFrame);
VLOG("PUSH: Waiting for frame[%d].syncDone = 1 (sync)", curFrame);
syncCondVar_.wait(lock);
}
frameData.syncDone = false;

View file

@ -198,7 +198,6 @@ public:
const std::string &GetSource() const { return source_; }
~VKShaderModule() {
if (module_) {
DEBUG_LOG(G3D, "Queueing %s (shmodule %p) for release", tag_.c_str(), module_);
VkShaderModule shaderModule = module_->BlockUntilReady();
vulkan_->Delete().QueueDeleteShaderModule(shaderModule);
vulkan_->Delete().QueueCallback([](VulkanContext *context, void *m) {
@ -213,7 +212,7 @@ public:
}
private:
VulkanContext *vulkan_;
VulkanContext *vulkan_ = nullptr;
Promise<VkShaderModule> *module_ = nullptr;
VkShaderStageFlagBits vkstage_;
bool ok_ = false;
@ -268,7 +267,6 @@ public:
vkrDesc = new VKRGraphicsPipelineDesc();
}
~VKPipeline() {
DEBUG_LOG(G3D, "Queueing %s (pipeline) for release", tag_.c_str());
if (pipeline) {
pipeline->QueueForDeletion(vulkan_);
}

View file

@ -23,6 +23,7 @@ const char *GetDeviceName(int deviceId) {
case DEVICE_ID_XINPUT_3: return "x360_4";
case DEVICE_ID_ACCELEROMETER: return "accelerometer";
case DEVICE_ID_MOUSE: return "mouse";
case DEVICE_ID_XR_HMD: return "xr_hmd";
case DEVICE_ID_XR_CONTROLLER_LEFT: return "xr_l";
case DEVICE_ID_XR_CONTROLLER_RIGHT: return "xr_r";
default:

View file

@ -31,6 +31,7 @@ enum {
DEVICE_ID_XINPUT_2 = 22,
DEVICE_ID_XINPUT_3 = 23,
DEVICE_ID_ACCELEROMETER = 30,
DEVICE_ID_XR_HMD = 39,
DEVICE_ID_XR_CONTROLLER_LEFT = 40,
DEVICE_ID_XR_CONTROLLER_RIGHT = 41,
DEVICE_ID_TOUCH = 42,

View file

@ -263,13 +263,19 @@ typedef enum _keycode_t {
NKCODE_EXT_MOUSEWHEEL_UP = 1008,
NKCODE_EXT_MOUSEWHEEL_DOWN = 1009,
// Virtual reality controller motion
// Virtual reality motion
NKCODE_EXT_MOTION_UP = 1101,
NKCODE_EXT_MOTION_DOWN = 1102,
NKCODE_EXT_MOTION_LEFT = 1103,
NKCODE_EXT_MOTION_RIGHT = 1104,
NKCODE_EXT_MOTION_FORWARD = 1105,
// Virtual reality rotation
NKCODE_EXT_ROTATION_UP = 1111,
NKCODE_EXT_ROTATION_DOWN = 1112,
NKCODE_EXT_ROTATION_LEFT = 1113,
NKCODE_EXT_ROTATION_RIGHT = 1114,
NKCODE_MAX
} keycode_t;

View file

@ -197,6 +197,28 @@ void CPUInfo::Detect()
#endif
}
std::vector<std::string> CPUInfo::Features() {
std::vector<std::string> features;
struct Flag {
bool &flag;
const char *str;
};
const Flag list[] = {
{ bXBurst1, "XBurst1" },
{ bXBurst2, "XBurst2" },
{ CPU64bit, "64-bit" },
};
for (auto &item : list) {
if (item.flag) {
features.push_back(item.str);
}
}
return features;
}
// Turn the cpu info into a string we can show
std::string CPUInfo::Summarize()
{
@ -205,10 +227,11 @@ std::string CPUInfo::Summarize()
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
else
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
if (bXBurst1) sum += ", XBurst1";
if (bXBurst2) sum += ", XBurst2";
if (CPU64bit) sum += ", 64-bit";
auto features = Features();
for (std::string &feature : features) {
sum += ", " + feature;
}
return sum;
}

View file

@ -18,11 +18,12 @@ public:
// Public variables since it doesn't make sense
// to bother with accessors for all these.
int status = 100;
// Intentional misspelling.
char *referer = nullptr;
char *referer = nullptr; // Intentional misspelling.
char *user_agent = nullptr;
char *resource = nullptr;
char *params = nullptr;
int content_length = -1;
std::unordered_map<std::string, std::string> other;
enum RequestType {

View file

@ -81,7 +81,7 @@ Request::~Request() {
}
delete in_;
if (!out_->Empty()) {
ERROR_LOG(IO, "Output not empty - connection abort?");
ERROR_LOG(IO, "Output not empty - connection abort? (%s)", this->header_.resource);
}
delete out_;
}

View file

@ -37,8 +37,9 @@ bool Buffer::FlushSocket(uintptr_t sock, double timeout, bool *cancelled) {
}
}
int sent = send(sock, &data_[pos], (int)(end - pos), MSG_NOSIGNAL);
// TODO: Do we need some retry logic here, instead of just giving up?
if (sent < 0) {
ERROR_LOG(IO, "FlushSocket failed");
ERROR_LOG(IO, "FlushSocket failed to send: %d", errno);
return false;
}
pos += sent;

View file

@ -18,6 +18,12 @@
#include "ppsspp_config.h"
#if PPSSPP_ARCH(RISCV64)
#include "ext/cpu_features/include/cpuinfo_riscv.h"
#if defined(CPU_FEATURES_OS_LINUX)
#define USE_CPU_FEATURES 1
#endif
#include <cstring>
#include <set>
#include <sstream>
@ -174,20 +180,63 @@ void CPUInfo::Detect()
RiscV_C = ExtensionSupported(hwcap, 'C');
RiscV_V = ExtensionSupported(hwcap, 'V');
RiscV_B = ExtensionSupported(hwcap, 'B');
// Let's assume for now...
RiscV_Zicsr = RiscV_M && RiscV_A && RiscV_F && RiscV_D;
#ifdef USE_CPU_FEATURES
cpu_features::RiscvInfo info = cpu_features::GetRiscvInfo();
CPU64bit = info.features.RV64I;
RiscV_M = info.features.M;
RiscV_A = info.features.A;
RiscV_F = info.features.F;
RiscV_D = info.features.D;
RiscV_C = info.features.C;
RiscV_Zicsr = info.features.Zicsr;
truncate_cpy(brand_string, info.uarch);
#endif
}
std::vector<std::string> CPUInfo::Features() {
std::vector<std::string> features;
struct Flag {
bool &flag;
const char *str;
};
const Flag list[] = {
{ RiscV_M, "Muldiv" },
{ RiscV_A, "Atomic" },
{ RiscV_F, "Float" },
{ RiscV_D, "Double" },
{ RiscV_C, "Compressed" },
{ RiscV_V, "Vector" },
{ RiscV_B, "Bitmanip" },
{ RiscV_Zicsr, "Zicsr" },
{ CPU64bit, "64-bit" },
};
for (auto &item : list) {
if (item.flag) {
features.push_back(item.str);
}
}
return features;
}
// Turn the cpu info into a string we can show
std::string CPUInfo::Summarize()
{
std::string CPUInfo::Summarize() {
std::string sum;
if (num_cores == 1)
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
else
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
if (CPU64bit) sum += ", 64-bit";
//TODO: parse "isa : rv64imafdc" from /proc/cpuinfo
auto features = Features();
for (std::string &feature : features) {
sum += ", " + feature;
}
return sum;
}

View file

@ -50,8 +50,7 @@ static inline bool SupportsAtomic() {
}
static inline bool SupportsZicsr() {
// TODO
return false;
return cpu_info.RiscV_Zicsr;
}
static inline bool SupportsVector() {
@ -63,6 +62,11 @@ static inline bool SupportsBitmanip(char zbx) {
return cpu_info.RiscV_B;
}
static inline bool SupportsFloatHalf(bool allowMin = false) {
// TODO
return false;
}
enum class Opcode32 {
// Note: invalid, just used for FixupBranch.
ZERO = 0b0000000,
@ -227,6 +231,7 @@ enum class Funct3 {
enum class Funct2 {
S = 0b00,
D = 0b01,
H = 0b10,
Q = 0b11,
C_SRLI = 0b00,
@ -875,10 +880,13 @@ static inline u16 EncodeCJ(Opcode16 op, s32 simm12, Funct3 funct3) {
return (u16)op | (imm11_4_9_8_10_6_7_3_2_1_5 << 2) | ((u16)funct3 << 13);
}
static inline Funct3 BitsToFunct3(int bits, bool useFloat = false) {
static inline Funct3 BitsToFunct3(int bits, bool useFloat = false, bool allowHalfMin = false) {
int bitsSupported = useFloat ? FloatBitsSupported() : BitsSupported();
_assert_msg_(bitsSupported >= bits, "Cannot use funct3 width %d, only have %d", bits, bitsSupported);
switch (bits) {
case 16:
_assert_msg_(SupportsFloatHalf(allowHalfMin), "Cannot use width 16 without Zfh/Zfhmin");
return Funct3::LS_H;
case 32:
return Funct3::LS_W;
case 64:
@ -889,9 +897,12 @@ static inline Funct3 BitsToFunct3(int bits, bool useFloat = false) {
}
}
static inline Funct2 BitsToFunct2(int bits) {
static inline Funct2 BitsToFunct2(int bits, bool allowHalfMin = false) {
_assert_msg_(FloatBitsSupported() >= bits, "Cannot use funct2 width %d, only have %d", bits, FloatBitsSupported());
switch (bits) {
case 16:
_assert_msg_(SupportsFloatHalf(allowHalfMin), "Cannot use width 16 without Zfh/Zfhmin");
return Funct2::H;
case 32:
return Funct2::S;
case 64:
@ -916,6 +927,9 @@ static inline int FConvToFloatBits(FConv c) {
return 32;
case FConv::D:
return 64;
case FConv::H:
_assert_msg_(SupportsFloatHalf(true), "Cannot use width 16 without Zfh/Zfhmin");
return 16;
case FConv::Q:
return 128;
}
@ -926,6 +940,7 @@ static inline int FConvToIntegerBits(FConv c) {
switch (c) {
case FConv::S:
case FConv::D:
case FConv::H:
case FConv::Q:
break;
@ -1976,7 +1991,7 @@ void RiscVEmitter::FL(int bits, RiscVReg rd, RiscVReg rs1, s32 simm12) {
}
}
Write32(EncodeI(Opcode32::LOAD_FP, rd, BitsToFunct3(bits, true), rs1, simm12));
Write32(EncodeI(Opcode32::LOAD_FP, rd, BitsToFunct3(bits, true, true), rs1, simm12));
}
void RiscVEmitter::FS(int bits, RiscVReg rs2, RiscVReg rs1, s32 simm12) {
@ -2000,7 +2015,7 @@ void RiscVEmitter::FS(int bits, RiscVReg rs2, RiscVReg rs1, s32 simm12) {
}
}
Write32(EncodeS(Opcode32::STORE_FP, BitsToFunct3(bits, true), rs1, rs2, simm12));
Write32(EncodeS(Opcode32::STORE_FP, BitsToFunct3(bits, true, true), rs1, rs2, simm12));
}
void RiscVEmitter::FMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm) {
@ -2069,8 +2084,8 @@ void RiscVEmitter::FCVT(FConv to, FConv from, RiscVReg rd, RiscVReg rs1, Round r
if (integerBits == 0) {
// Convert between float widths.
Funct2 fromFmt = BitsToFunct2(FConvToFloatBits(from));
Funct2 toFmt = BitsToFunct2(FConvToFloatBits(to));
Funct2 fromFmt = BitsToFunct2(FConvToFloatBits(from), true);
Funct2 toFmt = BitsToFunct2(FConvToFloatBits(to), true);
if (FConvToFloatBits(to) > FConvToFloatBits(from)) {
_assert_msg_(rm == Round::DYNAMIC || rm == Round::NEAREST_EVEN, "Invalid rounding mode for widening FCVT");
rm = Round::NEAREST_EVEN;
@ -2090,6 +2105,7 @@ void RiscVEmitter::FMV(FMv to, FMv from, RiscVReg rd, RiscVReg rs1) {
switch (to == FMv::X ? from : to) {
case FMv::D: bits = 64; break;
case FMv::W: bits = 32; break;
case FMv::H: bits = 16; break;
case FMv::X: bits = 0; break;
}
@ -2099,7 +2115,7 @@ void RiscVEmitter::FMV(FMv to, FMv from, RiscVReg rd, RiscVReg rs1) {
_assert_msg_(from == FMv::X ? IsGPR(rs1) : IsFPR(rs1), "%s rs1 of wrong type", __func__);
Funct5 funct5 = to == FMv::X ? Funct5::FMV_TOX : Funct5::FMV_FROMX;
Write32(EncodeR(Opcode32::OP_FP, rd, Funct3::FMV, rs1, F0, BitsToFunct2(bits), funct5));
Write32(EncodeR(Opcode32::OP_FP, rd, Funct3::FMV, rs1, F0, BitsToFunct2(bits, true), funct5));
}
void RiscVEmitter::FEQ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) {

View file

@ -90,11 +90,13 @@ enum class FConv {
S = 0x1000,
D = 0x1001,
H = 0x1002,
Q = 0x1003,
};
enum class FMv {
X,
H,
W,
D,
};

View file

@ -217,6 +217,16 @@ void ThreadManager::Init(int numRealCores, int numLogicalCoresPerCpu) {
}
void ThreadManager::EnqueueTask(Task *task) {
if (task->Type() == TaskType::DEDICATED_THREAD) {
std::thread th([=](Task *task) {
SetCurrentThreadName("DedicatedThreadTask");
task->Run();
task->Release();
}, task);
th.detach();
return;
}
_assert_msg_(IsInitialized(), "ThreadManager not initialized");
int minThread;
@ -270,6 +280,8 @@ void ThreadManager::EnqueueTask(Task *task) {
}
void ThreadManager::EnqueueTaskOnThread(int threadNum, Task *task) {
_assert_msg_(task->Type() != TaskType::DEDICATED_THREAD, "Dedicated thread tasks can't be put on specific threads");
_assert_msg_(threadNum >= 0 && threadNum < (int)global_->threads_.size(), "Bad threadnum or not initialized");
ThreadContext *thread = global_->threads_[threadNum];

View file

@ -8,6 +8,7 @@
enum class TaskType {
CPU_COMPUTE,
IO_BLOCKING,
DEDICATED_THREAD, // These can never get stuck in queue behind others, but are more expensive to launch. Cannot use I/O.
};
// Implement this to make something that you can run on the thread manager.

View file

@ -172,7 +172,7 @@ void UIContext::ActivateTopScissor() {
int h = std::max(0.0f, ceilf(scale_y * bounds.h));
if (x < 0 || y < 0 || x + w > pixel_xres || y + h > pixel_yres) {
// This won't actually report outside a game, but we can try.
ERROR_LOG(G3D, "UI scissor out of bounds in %sScreen: %d,%d-%d,%d / %d,%d", screenTag_ ? screenTag_ : "N/A", x, y, w, h, pixel_xres, pixel_yres);
DEBUG_LOG(G3D, "UI scissor out of bounds in %sScreen: %d,%d-%d,%d / %d,%d", screenTag_ ? screenTag_ : "N/A", x, y, w, h, pixel_xres, pixel_yres);
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x >= pixel_xres) { x = pixel_xres - 1; }

View file

@ -343,7 +343,6 @@ void SliderFloatPopupScreen::CreatePopupContents(UI::ViewGroup *parent) {
edit_->SetTextColor(dc.theme->itemStyle.fgColor);
edit_->SetTextAlign(FLAG_DYNAMIC_ASCII);
edit_->OnTextChange.Handle(this, &SliderFloatPopupScreen::OnTextChange);
changing_ = false;
lin->Add(edit_);
if (!units_.empty())
lin->Add(new TextView(units_, new LinearLayoutParams(10.0f)))->SetTextColor(dc.theme->itemStyle.fgColor);

View file

@ -99,7 +99,7 @@ private:
class SliderFloatPopupScreen : public PopupScreen {
public:
SliderFloatPopupScreen(float *value, float minValue, float maxValue, const std::string &title, float step = 1.0f, const std::string &units = "", bool liveUpdate = false)
: PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), originalValue_(*value), minValue_(minValue), maxValue_(maxValue), step_(step), changing_(false), liveUpdate_(liveUpdate) {}
: PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), originalValue_(*value), minValue_(minValue), maxValue_(maxValue), step_(step), liveUpdate_(liveUpdate) {}
void CreatePopupContents(UI::ViewGroup *parent) override;
const char *tag() const override { return "SliderFloatPopup"; }
@ -114,14 +114,14 @@ private:
void OnCompleted(DialogResult result) override;
UI::SliderFloat *slider_ = nullptr;
UI::TextEdit *edit_ = nullptr;
std::string units_ = nullptr;
float sliderValue_;
float originalValue_;
std::string units_;
float sliderValue_ = 0.0f;
float originalValue_ = 0.0f;
float *value_;
float minValue_;
float maxValue_;
float step_;
bool changing_;
bool changing_ = false;
bool liveUpdate_;
};
@ -361,7 +361,7 @@ private:
std::string placeHolder_;
std::string defaultText_;
int maxLen_;
bool restoreFocus_;
bool restoreFocus_ = false;
};
class ChoiceWithValueDisplay : public AbstractChoiceWithValueDisplay {

View file

@ -202,7 +202,9 @@ void ScreenManager::sendMessage(const char *msg, const char *value) {
if (!strcmp(msg, "recreateviews"))
RecreateAllViews();
if (!strcmp(msg, "lost_focus")) {
TouchInput input;
TouchInput input{};
input.x = -50000.0f;
input.y = -50000.0f;
input.flags = TOUCH_RELEASE_ALL;
input.timestamp = time_now_d();
input.id = 0;
@ -238,7 +240,9 @@ void ScreenManager::push(Screen *screen, int layerFlags) {
// Release touches and unfocus.
UI::SetFocusedView(nullptr);
TouchInput input;
TouchInput input{};
input.x = -50000.0f;
input.y = -50000.0f;
input.flags = TOUCH_RELEASE_ALL;
input.timestamp = time_now_d();
input.id = 0;

View file

@ -217,14 +217,13 @@ UI::EventReturn UIScreen::OnCancel(UI::EventParams &e) {
}
PopupScreen::PopupScreen(std::string title, std::string button1, std::string button2)
: box_(0), defaultButton_(nullptr), title_(title) {
: title_(title) {
auto di = GetI18NCategory("Dialog");
if (!button1.empty())
button1_ = di->T(button1.c_str());
if (!button2.empty())
button2_ = di->T(button2.c_str());
alpha_ = 0.0f;
alpha_ = 0.0f; // inherited
}
void PopupScreen::touch(const TouchInput &touch) {

View file

@ -97,8 +97,8 @@ protected:
void update() override;
private:
UI::LinearLayout *box_;
UI::Button *defaultButton_;
UI::LinearLayout *box_ = nullptr;
UI::Button *defaultButton_ = nullptr;
std::string title_;
std::string button1_;
std::string button2_;
@ -110,7 +110,7 @@ private:
int frames_ = 0;
int finishFrame_ = -1;
DialogResult finishResult_;
DialogResult finishResult_ = DR_CANCEL;
bool hasPopupOrigin_ = false;
Point popupOrigin_;
float offsetY_ = 0.0f;

View file

@ -998,7 +998,8 @@ void TextView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz
bounds.w -= bulletOffset;
}
dc.MeasureTextRect(small_ ? dc.theme->uiFontSmall : dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), (int)text_.length(), bounds, &w, &h, textAlign_);
w += pad_ * 2.0f;
h += pad_ * 2.0f;
if (bullet_) {
w += bulletOffset;
}
@ -1044,9 +1045,9 @@ void TextView::Draw(UIContext &dc) {
if (shadow_) {
uint32_t shadowColor = 0x80000000;
dc.DrawTextRect(text_.c_str(), textBounds.Offset(1.0f, 1.0f), shadowColor, textAlign_);
dc.DrawTextRect(text_.c_str(), textBounds.Offset(1.0f + pad_, 1.0f + pad_), shadowColor, textAlign_);
}
dc.DrawTextRect(text_.c_str(), textBounds, textColor, textAlign_);
dc.DrawTextRect(text_.c_str(), textBounds.Offset(pad_, pad_), textColor, textAlign_);
if (small_) {
// If we changed font style, reset it.
dc.SetFontStyle(dc.theme->uiFont);

View file

@ -870,6 +870,8 @@ public:
BitCheckBox(uint32_t *bitfield, uint32_t bit, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = nullptr)
: CheckBox(nullptr, text, smallText, layoutParams), bitfield_(bitfield), bit_(bit) {
}
BitCheckBox(int *bitfield, int bit, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = nullptr) : BitCheckBox((uint32_t *)bitfield, (uint32_t)bit, text, smallText, layoutParams) {}
void Toggle() override;
bool Toggled() const override;
@ -932,6 +934,7 @@ public:
void SetFocusable(bool focusable) { focusable_ = focusable; }
void SetClip(bool clip) { clip_ = clip; }
void SetBullet(bool bullet) { bullet_ = bullet; }
void SetPadding(float pad) { pad_ = pad; }
bool CanBeFocused() const override { return focusable_; }
@ -945,6 +948,7 @@ private:
bool focusable_;
bool clip_;
bool bullet_ = false;
float pad_ = 0.0f;
};
class TextEdit : public View {

View file

@ -103,6 +103,10 @@ static std::vector<ButtonMapping> controllerMapping[2] = {
rightControllerMapping
};
static bool controllerMotion[2][5] = {};
static bool hmdMotion[4] = {};
static float hmdMotionLast[2] = {};
static float hmdMotionDiff[2] = {};
static float hmdMotionDiffLast[2] = {};
static int mouseController = 1;
static bool mousePressed = false;
@ -309,6 +313,70 @@ void UpdateVRInput(bool haptics, float dp_xscale, float dp_yscale) {
}
}
// Head control
if (g_Config.iHeadRotation) {
float pitch = -VR_GetHMDAngles().x;
float yaw = -VR_GetHMDAngles().y;
bool disable = pspKeys[CTRL_SCREEN] || appMode == VR_MENU_MODE;
bool isVR = !IsFlatVRScene();
// calculate delta angles of the rotation
if (isVR) {
float f = g_Config.bHeadRotationSmoothing ? 0.5f : 1.0f;
float deltaPitch = pitch - hmdMotionLast[0];
float deltaYaw = yaw - hmdMotionLast[1];
while (deltaYaw >= 180) deltaYaw -= 360;
while (deltaYaw < -180) deltaYaw += 360;
hmdMotionLast[0] = pitch;
hmdMotionLast[1] = yaw;
hmdMotionDiffLast[0] = hmdMotionDiffLast[0] * (1-f) + hmdMotionDiff[0] * f;
hmdMotionDiffLast[1] = hmdMotionDiffLast[1] * (1-f) + hmdMotionDiff[1] * f;
hmdMotionDiff[0] += deltaPitch;
hmdMotionDiff[1] += deltaYaw;
pitch = hmdMotionDiff[0];
yaw = hmdMotionDiff[1];
}
bool activate;
float limit = isVR ? g_Config.fHeadRotationScale : 20;
keyInput.deviceId = DEVICE_ID_XR_HMD;
// vertical rotations
if (g_Config.iHeadRotation == 2) {
//up
activate = !disable && pitch > limit;
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
keyInput.keyCode = NKCODE_EXT_ROTATION_UP;
if (hmdMotion[0] != activate) NativeKey(keyInput);
if (isVR && activate) hmdMotionDiff[0] -= limit;
hmdMotion[0] = activate;
//down
activate = !disable && pitch < -limit;
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
keyInput.keyCode = NKCODE_EXT_ROTATION_DOWN;
if (hmdMotion[1] != activate) NativeKey(keyInput);
if (isVR && activate) hmdMotionDiff[0] += limit;
hmdMotion[1] = activate;
}
//left
activate = !disable && yaw < -limit;
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
keyInput.keyCode = NKCODE_EXT_ROTATION_LEFT;
if (hmdMotion[2] != activate) NativeKey(keyInput);
if (isVR && activate) hmdMotionDiff[1] += limit;
hmdMotion[2] = activate;
//right
activate = !disable && yaw > limit;
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
keyInput.keyCode = NKCODE_EXT_ROTATION_RIGHT;
if (hmdMotion[3] != activate) NativeKey(keyInput);
if (isVR && activate) hmdMotionDiff[1] -= limit;
hmdMotion[3] = activate;
}
// Camera adjust
if (pspKeys[VIRTKEY_VR_CAMERA_ADJUST]) {
for (auto& device : pspAxis) {
@ -606,11 +674,20 @@ bool StartVRRender() {
invView = XrPosef_Inverse(invView);
}
// create updated quaternion
// decompose rotation
XrVector3f rotation = XrQuaternionf_ToEulerAngles(invView.orientation);
XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, mx * ToRadians(rotation.x));
XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, my * ToRadians(rotation.y));
XrQuaternionf roll = XrQuaternionf_CreateFromVectorAngle({0, 0, 1}, mz * ToRadians(rotation.z));
float mPitch = mx * ToRadians(rotation.x);
float mYaw = my * ToRadians(rotation.y);
float mRoll = mz * ToRadians(rotation.z);
// use in-game camera interpolated rotation
if (g_Config.iHeadRotation >= 2) mPitch = -mx * ToRadians(hmdMotionDiffLast[0]); // vertical
if (g_Config.iHeadRotation >= 1) mYaw = -my * ToRadians(hmdMotionDiffLast[1]); // horizontal
// create updated quaternion
XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, mPitch);
XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, mYaw);
XrQuaternionf roll = XrQuaternionf_CreateFromVectorAngle({0, 0, 1}, mRoll);
invView.orientation = XrQuaternionf_Multiply(roll, XrQuaternionf_Multiply(pitch, yaw));
float M[16];

View file

@ -465,3 +465,7 @@ void* VR_BindFramebuffer(engine_t *engine) {
XrView VR_GetView(int eye) {
return projections[eye];
}
XrVector3f VR_GetHMDAngles() {
return hmdorientation;
}

View file

@ -48,3 +48,4 @@ void VR_SetConfigFloat( VRConfigFloat config, float value );
void* VR_BindFramebuffer(engine_t *engine);
XrView VR_GetView(int eye);
XrVector3f VR_GetHMDAngles();

View file

@ -842,7 +842,7 @@ static ConfigSetting graphicsSettings[] = {
ConfigSetting("CardboardScreenSize", &g_Config.iCardboardScreenSize, 50, true, true),
ConfigSetting("CardboardXShift", &g_Config.iCardboardXShift, 0, true, true),
ConfigSetting("CardboardYShift", &g_Config.iCardboardYShift, 0, true, true),
ConfigSetting("ShowFPSCounter", &g_Config.iShowFPSCounter, 0, true, true),
ConfigSetting("iShowStatusFlags", &g_Config.iShowStatusFlags, 0, true, true),
ReportedConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, true, false),
ConfigSetting("FailedGraphicsBackends", &g_Config.sFailedGPUBackends, ""),
ConfigSetting("DisabledGraphicsBackends", &g_Config.sDisabledGPUBackends, ""),
@ -1197,6 +1197,9 @@ static ConfigSetting vrSettings[] = {
ConfigSetting("VRFieldOfView", &g_Config.fFieldOfViewPercentage, 100.0f),
ConfigSetting("VRHeadUpDisplayScale", &g_Config.fHeadUpDisplayScale, 0.3f),
ConfigSetting("VRMotionLength", &g_Config.fMotionLength, 0.5f),
ConfigSetting("VRHeadRotationScale", &g_Config.fHeadRotationScale, 5.0f),
ConfigSetting("VRHeadRotationSmoothing", &g_Config.bHeadRotationSmoothing, false),
ConfigSetting("VRHeadRotation", &g_Config.iHeadRotation, 0),
ConfigSetting(false),
};
@ -1761,7 +1764,10 @@ void Config::CleanRecent() {
}
}
INFO_LOG(SYSTEM, "CleanRecent took %0.2f", time_now_d() - startTime);
double recentTime = time_now_d() - startTime;
if (recentTime > 0.1) {
INFO_LOG(SYSTEM, "CleanRecent took %0.2f", recentTime);
}
recentIsos = cleanedRecent;
});
}
@ -1809,17 +1815,31 @@ const Path Config::FindConfigFile(const std::string &baseFilename) {
return filename;
}
void Config::RestoreDefaults() {
void Config::RestoreDefaults(RestoreSettingsBits whatToRestore) {
if (bGameSpecific) {
deleteGameConfig(gameId_);
createGameConfig(gameId_);
Load();
} else {
if (File::Exists(iniFilename_))
File::Delete(iniFilename_);
ClearRecentIsos();
currentDirectory = defaultCurrentDirectory;
if (whatToRestore & RestoreSettingsBits::SETTINGS) {
if (File::Exists(iniFilename_))
File::Delete(iniFilename_);
}
if (whatToRestore & RestoreSettingsBits::CONTROLS) {
if (File::Exists(controllerIniFilename_))
File::Delete(controllerIniFilename_);
}
if (whatToRestore & RestoreSettingsBits::RECENT) {
ClearRecentIsos();
currentDirectory = defaultCurrentDirectory;
}
if (whatToRestore & (RestoreSettingsBits::SETTINGS | RestoreSettingsBits::CONTROLS)) {
Load();
}
}
Load();
}
bool Config::hasGameConfig(const std::string &pGameId) {

View file

@ -270,7 +270,7 @@ public:
// UI
bool bShowDebuggerOnLoad;
int iShowFPSCounter;
int iShowStatusFlags;
bool bShowRegionOnGameIcon;
bool bShowIDOnGameIcon;
float fGameGridScale;
@ -475,6 +475,9 @@ public:
float fFieldOfViewPercentage;
float fHeadUpDisplayScale;
float fMotionLength;
float fHeadRotationScale;
bool bHeadRotationSmoothing;
int iHeadRotation;
// Debugger
int iDisasmWindowX;
@ -523,7 +526,7 @@ public:
void Load(const char *iniFileName = nullptr, const char *controllerIniFilename = nullptr);
bool Save(const char *saveReason);
void Reload();
void RestoreDefaults();
void RestoreDefaults(RestoreSettingsBits whatToRestore);
//per game config managment, should maybe be in it's own class
void changeGameSpecific(const std::string &gameId = "", const std::string &title = "");

View file

@ -22,6 +22,7 @@
#ifndef _MSC_VER
#include <strings.h>
#endif
#include "Common/Common.h"
#include "Common/CommonFuncs.h"
const int PSP_MODEL_FAT = 0;
@ -65,6 +66,13 @@ enum class GPUBackend {
VULKAN = 3,
};
enum class RestoreSettingsBits : int {
SETTINGS = 1,
CONTROLS = 2,
RECENT = 4,
};
ENUM_CLASS_BITOPS(RestoreSettingsBits);
inline std::string GPUBackendToString(GPUBackend backend) {
switch (backend) {
case GPUBackend::OPENGL:
@ -129,3 +137,10 @@ enum class AnalogFpsMode {
MAPPED_DIRECTION = 1,
MAPPED_DIR_TO_OPPOSITE_DIR = 2,
};
// for Config.iShowStatusFlags
enum class ShowStatusFlags {
FPS_COUNTER = 1 << 1,
SPEED_COUNTER = 1 << 2,
BATTERY_PERCENT = 1 << 3,
};

View file

@ -199,7 +199,6 @@ bool UpdateScreenScale(int width, int height) {
dp_yres = new_dp_yres;
pixel_xres = width;
pixel_yres = height;
INFO_LOG(G3D, "pixel_res: %dx%d. Calling NativeResized()", pixel_xres, pixel_yres);
NativeResized();
return true;
}
@ -328,14 +327,14 @@ void Core_ProcessStepping() {
// Many platforms, like Android, do not call this function but handle things on their own.
// Instead they simply call NativeRender and NativeUpdate directly.
void Core_Run(GraphicsContext *ctx) {
bool Core_Run(GraphicsContext *ctx) {
host->UpdateDisassembly();
while (true) {
if (GetUIState() != UISTATE_INGAME) {
Core_StateProcessed();
if (GetUIState() == UISTATE_EXIT) {
UpdateRunLoop();
return;
return false;
}
Core_RunLoop(ctx);
continue;
@ -348,7 +347,7 @@ void Core_Run(GraphicsContext *ctx) {
Core_RunLoop(ctx);
if (coreState == CORE_POWERDOWN) {
Core_StateProcessed();
return;
return true;
}
break;
@ -359,10 +358,10 @@ void Core_Run(GraphicsContext *ctx) {
// Exit loop!!
Core_StateProcessed();
return;
return true;
case CORE_NEXTFRAME:
return;
return true;
}
}
}

View file

@ -27,8 +27,10 @@ class GraphicsContext;
// called from emu thread
void UpdateRunLoop();
void Core_Run(GraphicsContext *ctx);
// Returns false when an UI exit state is detected.
bool Core_Run(GraphicsContext *ctx);
void Core_Stop();
// For platforms that don't call Core_Run
void Core_SetGraphicsContext(GraphicsContext *ctx);

View file

@ -233,16 +233,14 @@ void Shutdown()
ClearPendingEvents();
UnregisterAllEvents();
while(eventPool)
{
while (eventPool) {
Event *ev = eventPool;
eventPool = ev->next;
delete ev;
}
std::lock_guard<std::mutex> lk(externalEventLock);
while(eventTsPool)
{
while (eventTsPool) {
Event *ev = eventTsPool;
eventTsPool = ev->next;
delete ev;
@ -251,7 +249,12 @@ void Shutdown()
u64 GetTicks()
{
return (u64)globalTimer + slicelength - currentMIPS->downcount;
if (currentMIPS) {
return (u64)globalTimer + slicelength - currentMIPS->downcount;
} else {
// Reporting can actually end up here during weird task switching sequences on Android
return false;
}
}
u64 GetIdleTicks()

View file

@ -1168,6 +1168,7 @@ static int sceFontFindOptimumFont(u32 libHandle, u32 fontStylePtr, u32 errorCode
}
auto requestedStyle = PSPPointer<const PGFFontStyle>::Create(fontStylePtr);
DEBUG_LOG(SCEFONT, "requestedStyle fontAttributes %i,fontCountry %i,fontExpire %i,fontFamily %i,fontFileName %s,fontH %f,fontHRes %f,fontLanguage %i,fontName %s,fontRegion %i,fontStyle %i,fontStyleSub %i,fontV %f,fontVRes %f,fontWeight %f", requestedStyle->fontAttributes, requestedStyle->fontCountry, requestedStyle->fontExpire, requestedStyle->fontFamily, requestedStyle->fontFileName, requestedStyle->fontH, requestedStyle->fontHRes, requestedStyle->fontLanguage, requestedStyle->fontName, requestedStyle->fontRegion, requestedStyle->fontStyle, requestedStyle->fontStyleSub, requestedStyle->fontV, requestedStyle->fontVRes, requestedStyle->fontWeight);
// Find the first nearest match for H/V, OR the last exact match for others.
float hRes = requestedStyle->fontHRes > 0.0f ? (float)requestedStyle->fontHRes : fontLib->FontHRes();
@ -1235,6 +1236,7 @@ static int sceFontFindFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) {
DEBUG_LOG(SCEFONT, "sceFontFindFont(%x, %x, %x)", libHandle, fontStylePtr, errorCodePtr);
auto requestedStyle = PSPPointer<const PGFFontStyle>::Create(fontStylePtr);
DEBUG_LOG(SCEFONT, "requestedStyle fontAttributes %i,fontCountry %i,fontExpire %i,fontFamily %i,fontFileName %s,fontH %f,fontHRes %f,fontLanguage %i,fontName %s,fontRegion %i,fontStyle %i,fontStyleSub %i,fontV %f,fontVRes %f,fontWeight %f", requestedStyle->fontAttributes, requestedStyle->fontCountry, requestedStyle->fontExpire, requestedStyle->fontFamily, requestedStyle->fontFileName, requestedStyle->fontH, requestedStyle->fontHRes, requestedStyle->fontLanguage, requestedStyle->fontName, requestedStyle->fontRegion, requestedStyle->fontStyle, requestedStyle->fontStyleSub, requestedStyle->fontV, requestedStyle->fontVRes, requestedStyle->fontWeight);
// Find the closest exact match for the fields specified.
float hRes = requestedStyle->fontHRes > 0.0f ? (float)requestedStyle->fontHRes : fontLib->FontHRes();

View file

@ -310,6 +310,10 @@ static const KeyMap_IntStrPair key_names[] = {
{NKCODE_EXT_MOTION_LEFT, "MotionLeft"},
{NKCODE_EXT_MOTION_RIGHT, "MotionRight"},
{NKCODE_EXT_MOTION_FORWARD, "MotionFwd"},
{NKCODE_EXT_ROTATION_UP, "RotationUp"},
{NKCODE_EXT_ROTATION_DOWN, "RotationDown"},
{NKCODE_EXT_ROTATION_LEFT, "RotationLeft"},
{NKCODE_EXT_ROTATION_RIGHT, "RotationRight"},
{NKCODE_START_QUESTION, "¿"},
{NKCODE_LEFTBRACE, "{"},

View file

@ -2446,7 +2446,7 @@ void FramebufferManagerCommon::NotifyRenderResized(int msaaLevel) {
#ifdef _WIN32
// Seems related - if you're ok with numbers all the time, show some more :)
if (g_Config.iShowFPSCounter != 0) {
if (g_Config.iShowStatusFlags != 0) {
ShowScreenResolution();
}
#endif

View file

@ -521,7 +521,7 @@ float ToScaledDepthFromIntegerScale(float z) {
const float depthSliceFactor = DepthSliceFactor();
if (gstate_c.Use(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) {
const double doffset = 0.5 * (depthSliceFactor - 1.0) * (1.0 / depthSliceFactor);
// Use one bit for each value, rather than 1.0 / (25535.0 * 256.0).
// Use one bit for each value, rather than 1.0 / (65535.0 * 256.0).
return (float)((double)z * (1.0 / 16777215.0) + doffset);
} else {
const float offset = 0.5f * (depthSliceFactor - 1.0f) * (1.0f / depthSliceFactor);
@ -730,6 +730,12 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
if (maxz == 65535) {
maxz += fullDepthRange;
}
} else if (maxz == 65535) {
// This means clamp isn't enabled, but we still want to allow values up to 65535.99.
// If DepthSliceFactor() is 1.0, though, this would make out.depthRangeMax exceed 1.
// Since that would clamp, it would make Z=1234 not match between draws when maxz changes.
if (DepthSliceFactor() > 1.0f)
maxz = 65535.99f;
}
// Okay. So, in our shader, -1 will map to minz, and +1 will map to maxz.
float halfActualZRange = (maxz - minz) * (1.0f / 2.0f);
@ -748,6 +754,7 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
}
// OpenGL will clamp these for us anyway, and Direct3D will error if not clamped.
// Of course, if this happens we've skewed out.depthScale/out.zOffset and may get z-fighting.
out.depthRangeMin = std::max(out.depthRangeMin, 0.0f);
out.depthRangeMax = std::min(out.depthRangeMax, 1.0f);
}

View file

@ -128,11 +128,6 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
}
GPU_GLES::~GPU_GLES() {
if (draw_) {
GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
render->Wipe();
}
// If we're here during app shutdown (exiting the Windows app in-game, for example)
// everything should already be cleared since DeviceLost has been run.

View file

@ -98,12 +98,17 @@ static Promise<VkShaderModule> *CompileShaderModuleAsync(VulkanContext *vulkan,
#if defined(_DEBUG)
// Don't parallelize in debug mode, pathological behavior due to mutex locks in allocator which is HEAVILY used by glslang.
return Promise<VkShaderModule>::AlreadyDone(compile());
bool singleThreaded = true;
#else
return Promise<VkShaderModule>::Spawn(&g_threadManager, compile, TaskType::CPU_COMPUTE);
bool singleThreaded = false;
#endif
}
if (singleThreaded) {
return Promise<VkShaderModule>::AlreadyDone(compile());
} else {
return Promise<VkShaderModule>::Spawn(&g_threadManager, compile, TaskType::CPU_COMPUTE);
}
}
VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, FragmentShaderFlags flags, const char *code)
: vulkan_(vulkan), id_(id), flags_(flags) {

View file

@ -90,7 +90,6 @@ public:
}
void StopThread() override {
renderManager_->WaitUntilQueueIdle();
renderManager_->StopThread();
}

View file

@ -423,7 +423,7 @@ void MainWindow::forumAct()
void MainWindow::goldAct()
{
QDesktopServices::openUrl(QUrl("https://central.ppsspp.org/buygold"));
QDesktopServices::openUrl(QUrl("https://www.ppsspp.org/buygold"));
}
void MainWindow::gitAct()
@ -655,8 +655,6 @@ void MainWindow::createMenus()
->addEventChecked(&g_Config.bHardwareTransform);
gameSettingsMenu->add(new MenuAction(this, SLOT(vertexCacheAct()), QT_TR_NOOP("&Vertex cache")))
->addEventChecked(&g_Config.bVertexCache);
gameSettingsMenu->add(new MenuAction(this, SLOT(showFPSAct()), QT_TR_NOOP("&Show FPS counter")))
->addEventChecked(&g_Config.iShowFPSCounter);
gameSettingsMenu->addSeparator();
gameSettingsMenu->add(new MenuAction(this, SLOT(audioAct()), QT_TR_NOOP("Enable s&ound")))
->addEventChecked(&g_Config.bEnableSound);

View file

@ -184,7 +184,6 @@ private slots:
g_Config.bShowDebugStats = !g_Config.bShowDebugStats;
NativeMessageReceived("clear jit", "");
}
void showFPSAct() { g_Config.iShowFPSCounter = g_Config.iShowFPSCounter ? 0 : 3; } // 3 = both speed and FPS
// Help
void websiteAct();

View file

@ -429,7 +429,7 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std:
#else
SDL_GL_SwapWindow(window_);
#endif
});
}, false);
renderManager_->SetSwapIntervalFunction([&](int interval) {
INFO_LOG(G3D, "SDL SwapInterval: %d", interval);

View file

@ -42,7 +42,6 @@ public:
}
void StopThread() override {
renderManager_->WaitUntilQueueIdle();
renderManager_->StopThread();
}

View file

@ -1011,7 +1011,7 @@ int main(int argc, char *argv[]) {
case SDL_BUTTON_LEFT:
{
mouseDown = true;
TouchInput input;
TouchInput input{};
input.x = mx;
input.y = my;
input.flags = TOUCH_DOWN | TOUCH_MOUSE;
@ -1065,7 +1065,7 @@ int main(int argc, char *argv[]) {
}
case SDL_MOUSEMOTION:
if (mouseDown) {
TouchInput input;
TouchInput input{};
input.x = mx;
input.y = my;
input.flags = TOUCH_MOVE | TOUCH_MOUSE;
@ -1080,7 +1080,7 @@ int main(int argc, char *argv[]) {
case SDL_BUTTON_LEFT:
{
mouseDown = false;
TouchInput input;
TouchInput input{};
input.x = mx;
input.y = my;
input.flags = TOUCH_UP | TOUCH_MOUSE;

View file

@ -970,6 +970,12 @@ public:
if (img_.isValid()) {
scales[0] *= scaleX_;
scales[1] *= scaleY_;
if (timeLastPressed_ >= 0.0) {
double sincePress = time_now_d() - timeLastPressed_;
if (sincePress < 1.0) {
c = colorBlend(c, dc.theme->itemDownStyle.background.color, (float)sincePress);
}
}
dc.Draw()->DrawImageRotatedStretch(img_, bounds_.Offset(offsetX_, offsetY_), scales, angle_, c);
}
}
@ -1010,6 +1016,10 @@ public:
return button_;
}
void NotifyPressed() {
timeLastPressed_ = time_now_d();
}
private:
int button_;
ImageID img_;
@ -1021,6 +1031,7 @@ private:
float offsetY_ = 0.0f;
bool flipHBG_ = false;
int *selectedButton_ = nullptr;
double timeLastPressed_ = -1.0;
};
class MockPSP : public UI::AnchorLayout {
@ -1030,8 +1041,11 @@ public:
MockPSP(UI::LayoutParams *layoutParams = nullptr);
void SelectButton(int btn);
void FocusButton(int btn);
void NotifyPressed(int btn);
float GetPopupOffset();
bool SubviewFocused(View *view) override;
UI::Event ButtonClick;
private:
@ -1042,6 +1056,7 @@ private:
UI::EventReturn OnSelectButton(UI::EventParams &e);
std::unordered_map<int, MockButton *> buttons_;
UI::TextView *labelView_ = nullptr;
int selectedButton_ = 0;
};
@ -1070,6 +1085,10 @@ MockPSP::MockPSP(UI::LayoutParams *layoutParams) : AnchorLayout(layoutParams) {
AddButton(CTRL_CIRCLE, ImageID("I_CIRCLE"), ImageID("I_ROUND_LINE"), 0.0f, LayoutSize(23.0f, 23.0f, 446.0f, 74.0f))->SetScale(0.7f);
AddButton(CTRL_CROSS, ImageID("I_CROSS"), ImageID("I_ROUND_LINE"), 0.0f, LayoutSize(23.0f, 23.0f, 419.0f, 102.0f))->SetScale(0.7f);
AddButton(CTRL_SQUARE, ImageID("I_SQUARE"), ImageID("I_ROUND_LINE"), 0.0f, LayoutSize(23.0f, 23.0f, 392.0f, 74.0f))->SetScale(0.7f);
labelView_ = Add(new UI::TextView(""));
labelView_->SetShadow(true);
labelView_->SetVisibility(UI::V_GONE);
}
void MockPSP::SelectButton(int btn) {
@ -1077,9 +1096,31 @@ void MockPSP::SelectButton(int btn) {
}
void MockPSP::FocusButton(int btn) {
MockButton *view = buttons_[selectedButton_];
if (view)
MockButton *view = buttons_[btn];
if (view) {
view->SetFocus();
} else {
labelView_->SetVisibility(UI::V_GONE);
}
}
void MockPSP::NotifyPressed(int btn) {
MockButton *view = buttons_[btn];
if (view)
view->NotifyPressed();
}
bool MockPSP::SubviewFocused(View *view) {
for (auto it : buttons_) {
if (view == it.second) {
labelView_->SetVisibility(UI::V_VISIBLE);
labelView_->SetText(KeyMap::GetPspButtonName(it.first));
const Bounds &pos = view->GetBounds().Offset(-GetBounds().x, -GetBounds().y);
labelView_->ReplaceLayoutParams(new UI::AnchorLayoutParams(pos.centerX(), pos.y2() + 5, UI::NONE, UI::NONE));
}
}
return AnchorLayout::SubviewFocused(view);
}
float MockPSP::GetPopupOffset() {
@ -1162,6 +1203,50 @@ void VisualMappingScreen::CreateViews() {
root_->Add(rightColumn);
}
bool VisualMappingScreen::key(const KeyInput &key) {
if (key.flags & KEY_DOWN) {
std::vector<int> pspKeys;
KeyMap::KeyToPspButton(key.deviceId, key.keyCode, &pspKeys);
for (int pspKey : pspKeys) {
switch (pspKey) {
case VIRTKEY_AXIS_X_MIN:
case VIRTKEY_AXIS_Y_MIN:
case VIRTKEY_AXIS_X_MAX:
case VIRTKEY_AXIS_Y_MAX:
psp_->NotifyPressed(VIRTKEY_AXIS_Y_MAX);
break;
default:
psp_->NotifyPressed(pspKey);
break;
}
}
}
return UIDialogScreenWithGameBackground::key(key);
}
void VisualMappingScreen::axis(const AxisInput &axis) {
std::vector<int> results;
if (axis.value >= g_Config.fAnalogDeadzone * 0.7f)
KeyMap::AxisToPspButton(axis.deviceId, axis.axisId, 1, &results);
if (axis.value <= g_Config.fAnalogDeadzone * -0.7f)
KeyMap::AxisToPspButton(axis.deviceId, axis.axisId, -1, &results);
for (int result : results) {
switch (result) {
case VIRTKEY_AXIS_X_MIN:
case VIRTKEY_AXIS_Y_MIN:
case VIRTKEY_AXIS_X_MAX:
case VIRTKEY_AXIS_Y_MAX:
psp_->NotifyPressed(VIRTKEY_AXIS_Y_MAX);
break;
default:
psp_->NotifyPressed(result);
break;
}
}
UIDialogScreenWithGameBackground::axis(axis);
}
void VisualMappingScreen::resized() {
UIDialogScreenWithGameBackground::resized();
RecreateViews();

View file

@ -177,6 +177,9 @@ public:
const char *tag() const override { return "VisualMapping"; }
bool key(const KeyInput &key) override;
void axis(const AxisInput &axis) override;
protected:
void CreateViews() override;

View file

@ -704,10 +704,9 @@ void SystemInfoScreen::CreateViews() {
tabHolder->AddTab(si->T("CPU Extensions"), cpuExtensionsScroll);
cpuExtensions->Add(new ItemHeader(si->T("CPU Extensions")));
std::vector<std::string> exts;
SplitString(cpu_info.Summarize(), ',', exts);
for (size_t i = 2; i < exts.size(); i++) {
cpuExtensions->Add(new TextView(exts[i], new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
std::vector<std::string> exts = cpu_info.Features();
for (std::string &ext : exts) {
cpuExtensions->Add(new TextView(ext, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
ViewGroup *driverBugsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));

View file

@ -25,6 +25,7 @@ using namespace std::placeholders;
#include "Common/Render/TextureAtlas.h"
#include "Common/GPU/OpenGL/GLFeatures.h"
#include "Common/Render/Text/draw_text.h"
#include "Common/Battery/Battery.h"
#include "Common/UI/Root.h"
#include "Common/UI/UI.h"
@ -1308,23 +1309,30 @@ static void DrawFPS(UIContext *ctx, const Bounds &bounds) {
FontID ubuntu24("UBUNTU24");
float vps, fps, actual_fps;
__DisplayGetFPS(&vps, &fps, &actual_fps);
char fpsbuf[256];
switch (g_Config.iShowFPSCounter) {
case 1:
snprintf(fpsbuf, sizeof(fpsbuf), "Speed: %0.1f%%", vps / (59.94f / 100.0f)); break;
case 2:
snprintf(fpsbuf, sizeof(fpsbuf), "FPS: %0.1f", actual_fps); break;
case 3:
snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / (59.94f / 100.0f)); break;
default:
return;
char fpsbuf[256]{};
if (g_Config.iShowStatusFlags == ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) {
snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / (59.94f / 100.0f));
} else {
if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::FPS_COUNTER) {
snprintf(fpsbuf, sizeof(fpsbuf), "FPS: %0.1f", actual_fps);
}
if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::SPEED_COUNTER) {
snprintf(fpsbuf, sizeof(fpsbuf), "%s Speed: %0.1f%%", fpsbuf, vps / (59.94f / 100.0f));
}
}
#ifdef CAN_DISPLAY_CURRENT_BATTERY_CAPACITY
if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::BATTERY_PERCENT) {
snprintf(fpsbuf, sizeof(fpsbuf), "%s Battery: %d%%", fpsbuf, getCurrentBatteryCapacity());
}
#endif
ctx->Flush();
ctx->BindFontTexture();
ctx->Draw()->SetFontScale(0.7f, 0.7f);
ctx->Draw()->DrawText(ubuntu24, fpsbuf, bounds.x2() - 8, 12, 0xc0000000, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
ctx->Draw()->DrawText(ubuntu24, fpsbuf, bounds.x2() - 10, 10, 0xFF3fFF3f, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
ctx->Draw()->DrawText(ubuntu24, fpsbuf, bounds.x2() - 8, 20, 0xc0000000, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
ctx->Draw()->DrawText(ubuntu24, fpsbuf, bounds.x2() - 10, 19, 0xFF3fFF3f, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
ctx->Draw()->SetFontScale(1.0f, 1.0f);
ctx->Flush();
ctx->RebindTexture();
@ -1504,7 +1512,7 @@ bool EmuScreen::hasVisibleUI() {
// Regular but uncommon UI.
if (saveStatePreview_->GetVisibility() != UI::V_GONE || loadingSpinner_->GetVisibility() == UI::V_VISIBLE)
return true;
if (!osm.IsEmpty() || g_Config.bShowTouchControls || g_Config.iShowFPSCounter != 0)
if (!osm.IsEmpty() || g_Config.bShowTouchControls || g_Config.iShowStatusFlags != 0)
return true;
if (g_Config.bEnableCardboardVR || g_Config.bEnableNetworkChat)
return true;
@ -1551,7 +1559,7 @@ void EmuScreen::renderUI() {
DrawAudioDebugStats(ctx, ctx->GetLayoutBounds());
}
if (g_Config.iShowFPSCounter && !invalid_) {
if (g_Config.iShowStatusFlags && !invalid_) {
DrawFPS(ctx, ctx->GetLayoutBounds());
}

View file

@ -31,6 +31,7 @@
#include "Common/System/Display.h" // Only to check screen aspect ratio with pixel_yres/pixel_xres
#include "Common/System/System.h"
#include "Common/Battery/Battery.h"
#include "Common/System/NativeApp.h"
#include "Common/Data/Color/RGBAUtil.h"
#include "Common/Math/curves.h"
@ -211,22 +212,8 @@ void GameSettingsScreen::CreateViews() {
// Scrolling action menu to the right.
using namespace UI;
auto di = GetI18NCategory("Dialog");
auto gr = GetI18NCategory("Graphics");
auto co = GetI18NCategory("Controls");
auto a = GetI18NCategory("Audio");
auto sa = GetI18NCategory("Savedata");
auto se = GetI18NCategory("Search");
auto sy = GetI18NCategory("System");
auto n = GetI18NCategory("Networking");
auto ms = GetI18NCategory("MainSettings");
auto dev = GetI18NCategory("Developer");
auto ri = GetI18NCategory("RemoteISO");
auto ps = GetI18NCategory("PostShaders");
auto th = GetI18NCategory("Themes");
auto vr = GetI18NCategory("VR");
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
auto di = GetI18NCategory("Dialog");
root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
@ -259,10 +246,59 @@ void GameSettingsScreen::CreateViews() {
settingInfo_->Show(oldSettingInfo_, nullptr);
}
// TODO: These currently point to global settings, not game specific ones.
// Graphics
LinearLayout *graphicsSettings = AddTab("GameSettingsGraphics", ms->T("Graphics"));
CreateGraphicsSettings(graphicsSettings);
LinearLayout *controlsSettings = AddTab("GameSettingsControls", ms->T("Controls"));
CreateControlsSettings(controlsSettings);
LinearLayout *audioSettings = AddTab("GameSettingsAudio", ms->T("Audio"));
CreateAudioSettings(audioSettings);
LinearLayout *networkingSettings = AddTab("GameSettingsNetworking", ms->T("Networking"));
CreateNetworkingSettings(networkingSettings);
LinearLayout *tools = AddTab("GameSettingsTools", ms->T("Tools"));
CreateToolsSettings(tools);
LinearLayout *systemSettings = AddTab("GameSettingsSystem", ms->T("System"));
systemSettings->SetSpacing(0);
CreateSystemSettings(systemSettings);
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
if (deviceType == DEVICE_TYPE_VR) {
LinearLayout *vrSettings = AddTab("GameSettingsVR", ms->T("VR"));
CreateVRSettings(vrSettings);
}
#if !defined(MOBILE_DEVICE) || PPSSPP_PLATFORM(ANDROID)
// Hide search if screen is too small.
if (dp_xres < dp_yres || dp_yres >= 500) {
auto se = GetI18NCategory("Search");
// Search
LinearLayout *searchSettings = AddTab("GameSettingsSearch", ms->T("Search"), true);
searchSettings->Add(new ItemHeader(se->T("Find settings")));
if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) {
searchSettings->Add(new ChoiceWithValueDisplay(&searchFilter_, se->T("Filter"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeSearchFilter);
} else {
searchSettings->Add(new PopupTextInputChoice(&searchFilter_, se->T("Filter"), "", 64, screenManager()))->OnChange.Handle(this, &GameSettingsScreen::OnChangeSearchFilter);
}
clearSearchChoice_ = searchSettings->Add(new Choice(se->T("Clear filter")));
clearSearchChoice_->OnClick.Handle(this, &GameSettingsScreen::OnClearSearchFilter);
noSearchResults_ = searchSettings->Add(new TextView(se->T("No settings matched '%1'"), new LinearLayoutParams(Margins(20, 5))));
ApplySearchFilter();
}
#endif
}
// Graphics
void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) {
auto gr = GetI18NCategory("Graphics");
auto vr = GetI18NCategory("VR");
using namespace UI;
graphicsSettings->Add(new ItemHeader(gr->T("Rendering Mode")));
@ -307,6 +343,8 @@ void GameSettingsScreen::CreateViews() {
return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;
});
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
if (deviceType != DEVICE_TYPE_VR) {
CheckBox *softwareGPU = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareRendering, gr->T("Software Rendering", "Software Rendering (slow)")));
softwareGPU->SetEnabled(!PSP_IsInited());
@ -563,12 +601,20 @@ void GameSettingsScreen::CreateViews() {
});
graphicsSettings->Add(new ItemHeader(gr->T("Overlay Information")));
static const char *fpsChoices[] = { "None", "Speed", "FPS", "Both" };
graphicsSettings->Add(new PopupMultiChoice(&g_Config.iShowFPSCounter, gr->T("Show FPS Counter"), fpsChoices, 0, ARRAY_SIZE(fpsChoices), gr->GetName(), screenManager()));
graphicsSettings->Add(new CheckBox(&g_Config.bShowDebugStats, gr->T("Show Debug Statistics")))->OnClick.Handle(this, &GameSettingsScreen::OnJitAffectingSetting);
graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::FPS_COUNTER, gr->T("Show FPS Counter")));
graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::SPEED_COUNTER, gr->T("Show Speed")));
#ifdef CAN_DISPLAY_CURRENT_BATTERY_CAPACITY
graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::BATTERY_PERCENT, gr->T("Show Battery %")));
#endif
// Audio
LinearLayout *audioSettings = AddTab("GameSettingsAudio", ms->T("Audio"));
graphicsSettings->Add(new CheckBox(&g_Config.bShowDebugStats, gr->T("Show Debug Statistics")))->OnClick.Handle(this, &GameSettingsScreen::OnJitAffectingSetting);
}
void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {
using namespace UI;
auto a = GetI18NCategory("Audio");
auto ms = GetI18NCategory("MainSettings");
audioSettings->Add(new ItemHeader(ms->T("Audio")));
audioSettings->Add(new CheckBox(&g_Config.bEnableSound, a->T("Enable Sound")));
@ -626,9 +672,15 @@ void GameSettingsScreen::CreateViews() {
PopupMultiChoiceDynamic *MicChoice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sMicDevice, a->T("Microphone Device"), micList, nullptr, screenManager()));
MicChoice->OnChoice.Handle(this, &GameSettingsScreen::OnMicDeviceChange);
}
}
// Control
LinearLayout *controlsSettings = AddTab("GameSettingsControls", ms->T("Controls"));
void GameSettingsScreen::CreateControlsSettings(UI::ViewGroup *controlsSettings) {
using namespace UI;
auto co = GetI18NCategory("Controls");
auto ms = GetI18NCategory("MainSettings");
int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);
controlsSettings->Add(new ItemHeader(ms->T("Controls")));
controlsSettings->Add(new Choice(co->T("Control Mapping")))->OnClick.Handle(this, &GameSettingsScreen::OnControlMapping);
@ -718,7 +770,7 @@ void GameSettingsScreen::CreateViews() {
controlsSettings->Add(new ItemHeader(co->T("Mouse", "Mouse settings")));
CheckBox *mouseControl = controlsSettings->Add(new CheckBox(&g_Config.bMouseControl, co->T("Use Mouse Control")));
mouseControl->OnClick.Add([=](EventParams &e) {
if(g_Config.bMouseControl)
if (g_Config.bMouseControl)
settingInfo_->Show(co->T("MouseControl Tip", "You can now map mouse in control mapping screen by pressing the 'M' icon."), e.v);
return UI::EVENT_CONTINUE;
});
@ -727,8 +779,13 @@ void GameSettingsScreen::CreateViews() {
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSmoothing, 0.0f, 0.95f, co->T("Mouse smoothing"), 0.05f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bMouseControl);
#endif
}
}
LinearLayout *networkingSettings = AddTab("GameSettingsNetworking", ms->T("Networking"));
void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSettings) {
using namespace UI;
auto n = GetI18NCategory("Networking");
auto ms = GetI18NCategory("MainSettings");
networkingSettings->Add(new ItemHeader(ms->T("Networking")));
@ -813,8 +870,16 @@ void GameSettingsScreen::CreateViews() {
networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, n->T("Port offset", "Port offset (0 = PSP compatibility)"), 100, screenManager()));
networkingSettings->Add(new PopupSliderChoice(&g_Config.iMinTimeout, 0, 15000, n->T("Minimum Timeout", "Minimum Timeout (override in ms, 0 = default)"), 50, screenManager()));
networkingSettings->Add(new CheckBox(&g_Config.bForcedFirstConnect, n->T("Forced First Connect", "Forced First Connect (faster Connect)")));
}
LinearLayout *tools = AddTab("GameSettingsTools", ms->T("Tools"));
void GameSettingsScreen::CreateToolsSettings(UI::ViewGroup *tools) {
using namespace UI;
auto sa = GetI18NCategory("Savedata");
auto sy = GetI18NCategory("System");
auto ms = GetI18NCategory("MainSettings");
auto dev = GetI18NCategory("Developer");
auto ri = GetI18NCategory("RemoteISO");
tools->Add(new ItemHeader(ms->T("Tools")));
// These were moved here so use the wrong translation objects, to avoid having to change all inis... This isn't a sustainable situation :P
@ -822,9 +887,14 @@ void GameSettingsScreen::CreateViews() {
tools->Add(new Choice(dev->T("System Information")))->OnClick.Handle(this, &GameSettingsScreen::OnSysInfo);
tools->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &GameSettingsScreen::OnDeveloperTools);
tools->Add(new Choice(ri->T("Remote disc streaming")))->OnClick.Handle(this, &GameSettingsScreen::OnRemoteISO);
}
// System
LinearLayout *systemSettings = AddTab("GameSettingsSystem", ms->T("System"));
void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) {
using namespace UI;
auto sy = GetI18NCategory("System");
auto vr = GetI18NCategory("VR");
auto th = GetI18NCategory("Themes");
systemSettings->Add(new ItemHeader(sy->T("UI")));
@ -870,10 +940,11 @@ void GameSettingsScreen::CreateViews() {
PopupMultiChoiceDynamic *theme = systemSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sThemeName, sy->T("Theme"), GetThemeInfoNames(), th->GetName(), screenManager()));
theme->OnChoice.Add([=](EventParams &e) {
UpdateTheme(screenManager()->getUIContext());
return UI::EVENT_CONTINUE;
});
Draw::DrawContext *draw = screenManager()->getDrawContext();
if (!draw->GetBugs().Has(Draw::Bugs::RASPBERRY_SHADER_COMP_HANG)) {
// We use shaders without tint capability on hardware with this driver bug.
PopupSliderChoiceFloat *tint = new PopupSliderChoiceFloat(&g_Config.fUITint, 0.0, 1.0, sy->T("Color Tint"), 0.01f, screenManager());
@ -991,6 +1062,8 @@ void GameSettingsScreen::CreateViews() {
#if PPSSPP_PLATFORM(ANDROID)
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
auto co = GetI18NCategory("Controls");
static const char *screenRotation[] = { "Auto", "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed", "Landscape Auto" };
PopupMultiChoice *rot = systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenRotation, co->T("Screen Rotation"), screenRotation, 0, ARRAY_SIZE(screenRotation), co->GetName(), screenManager()));
rot->OnChoice.Handle(this, &GameSettingsScreen::OnScreenRotation);
@ -1000,7 +1073,6 @@ void GameSettingsScreen::CreateViews() {
}
}
#endif
systemSettings->Add(new CheckBox(&g_Config.bCheckForNewVersion, sy->T("VersionCheck", "Check for new versions of PPSSPP")));
systemSettings->Add(new Choice(sy->T("Restore Default Settings")))->OnClick.Handle(this, &GameSettingsScreen::OnRestoreDefaultSettings);
systemSettings->Add(new CheckBox(&g_Config.bEnableStateUndo, sy->T("Savestate slot backups")));
@ -1010,6 +1082,7 @@ void GameSettingsScreen::CreateViews() {
systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Use system native keyboard")));
systemSettings->Add(new CheckBox(&g_Config.bCacheFullIsoInRam, sy->T("Cache ISO in RAM", "Cache full ISO in RAM")))->SetEnabled(!PSP_IsInited());
systemSettings->Add(new CheckBox(&g_Config.bCheckForNewVersion, sy->T("VersionCheck", "Check for new versions of PPSSPP")));
systemSettings->Add(new ItemHeader(sy->T("Cheats", "Cheats")));
CheckBox *enableCheats = systemSettings->Add(new CheckBox(&g_Config.bEnableCheats, sy->T("Enable Cheats")));
@ -1017,10 +1090,9 @@ void GameSettingsScreen::CreateViews() {
enableReportsCheckbox_->SetEnabled(Reporting::IsSupported());
return UI::EVENT_CONTINUE;
});
systemSettings->SetSpacing(0);
systemSettings->Add(new ItemHeader(sy->T("PSP Settings")));
static const char *models[] = {"PSP-1000", "PSP-2000/3000"};
static const char *models[] = { "PSP-1000", "PSP-2000/3000" };
systemSettings->Add(new PopupMultiChoice(&g_Config.iPSPModel, sy->T("PSP Model"), models, 0, ARRAY_SIZE(models), sy->GetName(), screenManager()))->SetEnabled(!PSP_IsInited());
// TODO: Come up with a way to display a keyboard for mobile users,
// so until then, this is Windows/Desktop only.
@ -1043,52 +1115,41 @@ void GameSettingsScreen::CreateViews() {
systemSettings->Add(new CheckBox(&g_Config.bSaveLoadResetsAVdumping, sy->T("Reset Recording on Save/Load State")));
#endif
systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, sy->T("Day Light Saving")));
static const char *dateFormat[] = { "YYYYMMDD", "MMDDYYYY", "DDMMYYYY"};
static const char *dateFormat[] = { "YYYYMMDD", "MMDDYYYY", "DDMMYYYY" };
systemSettings->Add(new PopupMultiChoice(&g_Config.iDateFormat, sy->T("Date Format"), dateFormat, 0, 3, sy->GetName(), screenManager()));
static const char *timeFormat[] = { "24HR", "12HR" };
systemSettings->Add(new PopupMultiChoice(&g_Config.iTimeFormat, sy->T("Time Format"), timeFormat, 0, 2, sy->GetName(), screenManager()));
static const char *buttonPref[] = { "Use O to confirm", "Use X to confirm" };
systemSettings->Add(new PopupMultiChoice(&g_Config.iButtonPreference, sy->T("Confirmation Button"), buttonPref, 0, 2, sy->GetName(), screenManager()));
}
#if !defined(MOBILE_DEVICE) || PPSSPP_PLATFORM(ANDROID)
// Hide search if screen is too small.
if (dp_xres < dp_yres || dp_yres >= 500) {
// Search
LinearLayout *searchSettings = AddTab("GameSettingsSearch", ms->T("Search"), true);
void GameSettingsScreen::CreateVRSettings(UI::ViewGroup *vrSettings) {
using namespace UI;
searchSettings->Add(new ItemHeader(se->T("Find settings")));
if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) {
searchSettings->Add(new ChoiceWithValueDisplay(&searchFilter_, se->T("Filter"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeSearchFilter);
} else {
searchSettings->Add(new PopupTextInputChoice(&searchFilter_, se->T("Filter"), "", 64, screenManager()))->OnChange.Handle(this, &GameSettingsScreen::OnChangeSearchFilter);
}
clearSearchChoice_ = searchSettings->Add(new Choice(se->T("Clear filter")));
clearSearchChoice_->OnClick.Handle(this, &GameSettingsScreen::OnClearSearchFilter);
noSearchResults_ = searchSettings->Add(new TextView(se->T("No settings matched '%1'"), new LinearLayoutParams(Margins(20, 5))));
auto vr = GetI18NCategory("VR");
ApplySearchFilter();
}
#endif
vrSettings->Add(new ItemHeader(vr->T("Virtual reality")));
vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Virtual reality")));
CheckBox *vr6DoF = vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("6DoF movement")));
vr6DoF->SetEnabledPtr(&g_Config.bEnableVR);
vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Stereoscopic vision (Experimental)")));
vrSettings->Add(new CheckBox(&g_Config.bForce72Hz, vr->T("Force 72Hz update")));
if (deviceType == DEVICE_TYPE_VR) {
LinearLayout *vrSettings = AddTab("GameSettingsVR", ms->T("VR"));
vrSettings->Add(new ItemHeader(vr->T("Virtual reality")));
vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Virtual reality")));
CheckBox *vr6DoF = vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("6DoF movement")));
vr6DoF->SetEnabledPtr(&g_Config.bEnableVR);
vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Stereoscopic vision (Experimental)")));
vrSettings->Add(new CheckBox(&g_Config.bForce72Hz, vr->T("Force 72Hz update")));
vrSettings->Add(new ItemHeader(vr->T("VR camera")));
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvasDistance, 1.0f, 15.0f, vr->T("Distance to 2D menus and scenes"), 1.0f, screenManager(), ""));
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fFieldOfViewPercentage, 100.0f, 200.0f, vr->T("Field of view scale"), 10.0f, screenManager(), vr->T("% of native FoV")));
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fHeadUpDisplayScale, 0.0f, 1.5f, vr->T("Heads-up display scale"), 0.1f, screenManager(), ""));
vrSettings->Add(new ItemHeader(vr->T("VR camera")));
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvasDistance, 1.0f, 15.0f, vr->T("Distance to 2D menus and scenes"), 1.0f, screenManager(), ""));
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fFieldOfViewPercentage, 100.0f, 200.0f, vr->T("Field of view scale"), 10.0f, screenManager(), vr->T("% of native FoV")));
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fHeadUpDisplayScale, 0.0f, 1.5f, vr->T("Heads-up display scale"), 0.1f, screenManager(), ""));
vrSettings->Add(new ItemHeader(vr->T("VR controllers")));
vrSettings->Add(new CheckBox(&g_Config.bEnableMotions, vr->T("Map controller movements to keys")));
PopupSliderChoiceFloat *vrMotions = vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMotionLength, 0.3f, 1.0f, vr->T("Motion needed to generate action"), 0.1f, screenManager(), vr->T("m")));
vrMotions->SetEnabledPtr(&g_Config.bEnableMotions);
}
vrSettings->Add(new ItemHeader(vr->T("Experts only")));
static const char *vrHeadRotations[] = { vr->T("Disabled"), vr->T("Horizontal only"), vr->T("Horizontal and vertical") };
vrSettings->Add(new PopupMultiChoice(&g_Config.iHeadRotation, vr->T("Map HMD rotations on keys instead of VR camera"), vrHeadRotations, 0, ARRAY_SIZE(vrHeadRotations), vr->GetName(), screenManager()));
PopupSliderChoiceFloat *vrHeadRotationScale = vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fHeadRotationScale, 0.1f, 10.0f, vr->T("Game camera rotation step per frame"), 0.1f, screenManager(), "°"));
vrHeadRotationScale->SetEnabledFunc([&] { return g_Config.iHeadRotation > 0; });
CheckBox *vrHeadRotationSmoothing = vrSettings->Add(new CheckBox(&g_Config.bHeadRotationSmoothing, vr->T("Game camera uses rotation smoothing")));
vrHeadRotationSmoothing->SetEnabledFunc([&] { return g_Config.iHeadRotation > 0; });
vrSettings->Add(new CheckBox(&g_Config.bEnableMotions, vr->T("Map controller movements to keys")));
PopupSliderChoiceFloat *vrMotions = vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMotionLength, 0.3f, 1.0f, vr->T("Motion needed to generate action"), 0.1f, screenManager(), vr->T("m")));
vrMotions->SetEnabledPtr(&g_Config.bEnableMotions);
}
UI::LinearLayout *GameSettingsScreen::AddTab(const char *tag, const std::string &title, bool isSearch) {
@ -1834,27 +1895,24 @@ void DeveloperToolsScreen::onFinish(DialogResult result) {
}
void GameSettingsScreen::CallbackRestoreDefaults(bool yes) {
if (yes)
g_Config.RestoreDefaults();
if (yes) {
g_Config.RestoreDefaults(RestoreSettingsBits::SETTINGS);
}
host->UpdateUI();
}
UI::EventReturn GameSettingsScreen::OnRestoreDefaultSettings(UI::EventParams &e) {
auto dev = GetI18NCategory("Developer");
auto di = GetI18NCategory("Dialog");
if (g_Config.bGameSpecific)
{
auto sy = GetI18NCategory("System");
if (g_Config.bGameSpecific) {
screenManager()->push(
new PromptScreen(gamePath_, dev->T("RestoreGameDefaultSettings", "Are you sure you want to restore the game-specific settings back to the ppsspp defaults?\n"), di->T("OK"), di->T("Cancel"),
std::bind(&GameSettingsScreen::CallbackRestoreDefaults, this, std::placeholders::_1)));
} else {
const char *title = sy->T("Restore Default Settings");
screenManager()->push(new RestoreSettingsScreen(title));
}
else
{
screenManager()->push(
new PromptScreen(gamePath_, dev->T("RestoreDefaultSettings", "Are you sure you want to restore all settings(except control mapping)\nback to their defaults?\nYou can't undo this.\nPlease restart PPSSPP after restoring settings."), di->T("OK"), di->T("Cancel"),
std::bind(&GameSettingsScreen::CallbackRestoreDefaults, this, std::placeholders::_1)));
}
return UI::EVENT_DONE;
}
@ -2196,3 +2254,32 @@ void GestureMappingScreen::CreateViews() {
vert->Add(new ItemHeader(co->T("Double tap")));
vert->Add(new PopupMultiChoice(&g_Config.iDoubleTapGesture, mc->T("Double tap button"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
}
RestoreSettingsScreen::RestoreSettingsScreen(const char *title)
: PopupScreen(title, "OK", "Cancel") {}
void RestoreSettingsScreen::CreatePopupContents(UI::ViewGroup *parent) {
using namespace UI;
// Carefully re-use various translations.
auto ga = GetI18NCategory("Game");
auto ms = GetI18NCategory("MainSettings");
auto mm = GetI18NCategory("MainMenu");
auto dev = GetI18NCategory("Developer");
const char *text = dev->T(
"RestoreDefaultSettings",
"Restore these settings back to their defaults?\nYou can't undo this.\nPlease restart PPSSPP after restoring settings.");
TextView *textView = parent->Add(new TextView(text, FLAG_WRAP_TEXT, false));
textView->SetPadding(10.0f);
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::SETTINGS, ga->T("Game Settings")));
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::CONTROLS, ga->T("Controls")));
parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::RECENT, mm->T("Recent")));
}
void RestoreSettingsScreen::OnCompleted(DialogResult result) {
if (result == DialogResult::DR_OK) {
g_Config.RestoreDefaults((RestoreSettingsBits)restoreFlags_);
}
}

View file

@ -21,7 +21,9 @@
#include <condition_variable>
#include <mutex>
#include <thread>
#include "Common/UI/UIScreen.h"
#include "Core/ConfigValues.h"
#include "UI/MiscScreens.h"
// Per-game settings screen - enables you to configure graphic options, control options, etc
@ -45,31 +47,39 @@ protected:
void RecreateViews() override;
private:
void CreateGraphicsSettings(UI::ViewGroup *graphicsSettings);
void CreateControlsSettings(UI::ViewGroup *tools);
void CreateAudioSettings(UI::ViewGroup *audioSettings);
void CreateNetworkingSettings(UI::ViewGroup *networkingSettings);
void CreateToolsSettings(UI::ViewGroup *tools);
void CreateSystemSettings(UI::ViewGroup *systemSettings);
void CreateVRSettings(UI::ViewGroup *vrSettings);
UI::LinearLayout *AddTab(const char *tag, const std::string &title, bool isSearch = false);
void ApplySearchFilter();
void TriggerRestart(const char *why);
std::string gameID_;
UI::CheckBox *enableReportsCheckbox_;
UI::Choice *layoutEditorChoice_;
UI::Choice *displayEditor_;
UI::CheckBox *enableReportsCheckbox_ = nullptr;
UI::Choice *layoutEditorChoice_ = nullptr;
UI::Choice *displayEditor_ = nullptr;
UI::Choice *backgroundChoice_ = nullptr;
UI::PopupMultiChoice *resolutionChoice_;
UI::CheckBox *frameSkipAuto_;
SettingInfoMessage *settingInfo_;
UI::Choice *clearSearchChoice_;
UI::TextView *noSearchResults_;
UI::PopupMultiChoice *resolutionChoice_ = nullptr;
UI::CheckBox *frameSkipAuto_ = nullptr;
SettingInfoMessage *settingInfo_ = nullptr;
UI::Choice *clearSearchChoice_ = nullptr;
UI::TextView *noSearchResults_ = nullptr;
#ifdef _WIN32
UI::CheckBox *SavePathInMyDocumentChoice;
UI::CheckBox *SavePathInOtherChoice;
UI::CheckBox *SavePathInMyDocumentChoice = nullptr;
UI::CheckBox *SavePathInOtherChoice = nullptr;
// Used to enable/disable the above two options.
bool installed_;
bool otherinstalled_;
bool installed_ = false;
bool otherinstalled_ = false;
#endif
std::string memstickDisplay_;
UI::TabHolder *tabHolder_;
UI::TabHolder *tabHolder_ = nullptr;
std::vector<UI::LinearLayout *> settingTabContents_;
std::vector<UI::TextView *> settingTabFilterNotices_;
@ -125,18 +135,18 @@ private:
UI::EventReturn OnClearSearchFilter(UI::EventParams &e);
// Temporaries to convert setting types, cache enabled, etc.
int iAlternateSpeedPercent1_;
int iAlternateSpeedPercent2_;
int iAlternateSpeedPercentAnalog_;
int prevInflightFrames_;
int iAlternateSpeedPercent1_ = 0;
int iAlternateSpeedPercent2_ = 0;
int iAlternateSpeedPercentAnalog_ = 0;
int prevInflightFrames_ = -1;
bool enableReports_ = false;
bool enableReportsSet_ = false;
bool analogSpeedMapped_ = false;
std::string searchFilter_;
//edit the game-specific settings and restore the global settings after exiting
bool editThenRestore_;
// edit the game-specific settings and restore the global settings after exiting
bool editThenRestore_ = false;
// Android-only
std::string pendingMemstickFolder_;
@ -205,6 +215,7 @@ protected:
private:
void ResolverThread();
void SendEditKey(int keyCode, int flags = 0);
UI::EventReturn OnNumberClick(UI::EventParams &e);
UI::EventReturn OnPointClick(UI::EventParams &e);
UI::EventReturn OnDeleteClick(UI::EventParams &e);
@ -244,3 +255,14 @@ public:
const char *tag() const override { return "GestureMapping"; }
};
class RestoreSettingsScreen : public PopupScreen {
public:
RestoreSettingsScreen(const char *title);
void CreatePopupContents(UI::ViewGroup *parent) override;
const char *tag() const override { return "RestoreSettingsScreen"; }
private:
void OnCompleted(DialogResult result) override;
int restoreFlags_ = (int)(RestoreSettingsBits::SETTINGS); // RestoreSettingsBits enum
};

View file

@ -319,6 +319,10 @@ namespace CustomKey {
#ifndef MOBILE_DEVICE
{ ImageID::invalid(), VIRTKEY_RECORD },
#endif
{ ImageID::invalid(), VIRTKEY_AXIS_X_MIN },
{ ImageID::invalid(), VIRTKEY_AXIS_Y_MIN },
{ ImageID::invalid(), VIRTKEY_AXIS_X_MAX },
{ ImageID::invalid(), VIRTKEY_AXIS_Y_MAX },
};
static_assert(ARRAY_SIZE(comboKeyList) <= 64, "Too many key for a uint64_t bit mask");
};
@ -357,5 +361,9 @@ namespace GestureKey {
#ifndef MOBILE_DEVICE
VIRTKEY_RECORD,
#endif
VIRTKEY_AXIS_X_MIN,
VIRTKEY_AXIS_Y_MIN,
VIRTKEY_AXIS_X_MAX,
VIRTKEY_AXIS_Y_MAX,
};
}

View file

@ -1024,10 +1024,10 @@ void MainScreen::CreateViews() {
scrollHomebrew->SetTag("MainScreenHomebrew");
GameBrowser *tabAllGames = new GameBrowser(Path(g_Config.currentDirectory), BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(),
mm->T("How to get games"), "https://www.ppsspp.org/getgames.html",
mm->T("How to get games"), "https://www.ppsspp.org/getgames",
new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
GameBrowser *tabHomebrew = new GameBrowser(GetSysDirectory(DIRECTORY_GAME), BrowseFlags::HOMEBREW_STORE, &g_Config.bGridView3, screenManager(),
mm->T("How to get homebrew & demos", "How to get homebrew && demos"), "https://www.ppsspp.org/gethomebrew.html",
mm->T("How to get homebrew & demos", "How to get homebrew && demos"), "https://www.ppsspp.org/gethomebrew",
new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
scrollAllGames_->Add(tabAllGames);
@ -1221,7 +1221,7 @@ UI::EventReturn MainScreen::OnDownloadUpgrade(UI::EventParams &e) {
LaunchBrowser("market://details?id=org.ppsspp.ppsspp");
}
#elif PPSSPP_PLATFORM(WINDOWS)
LaunchBrowser("https://www.ppsspp.org/downloads.html");
LaunchBrowser("https://www.ppsspp.org/download");
#else
// Go directly to ppsspp.org and let the user sort it out
// (for details and in case downloads doesn't have their platform.)
@ -1406,7 +1406,7 @@ UI::EventReturn MainScreen::OnSupport(UI::EventParams &e) {
#ifdef __ANDROID__
LaunchBrowser("market://details?id=org.ppsspp.ppssppgold");
#else
LaunchBrowser("https://central.ppsspp.org/buygold");
LaunchBrowser("https://www.ppsspp.org/buygold");
#endif
return UI::EVENT_DONE;
}

View file

@ -302,6 +302,8 @@ void MemStickScreen::CreateViews() {
}
UI::EventReturn MemStickScreen::OnHelp(UI::EventParams &params) {
// I'm letting the old redirect handle this one, as the target is within /docs on the website,
// and that structure may change a bit.
LaunchBrowser("https://www.ppsspp.org/guide_storage.html");
return UI::EVENT_DONE;

View file

@ -842,7 +842,7 @@ UI::EventReturn CreditsScreen::OnSupport(UI::EventParams &e) {
#ifdef __ANDROID__
LaunchBrowser("market://details?id=org.ppsspp.ppssppgold");
#else
LaunchBrowser("https://central.ppsspp.org/buygold");
LaunchBrowser("https://www.ppsspp.org/buygold");
#endif
return UI::EVENT_DONE;
}
@ -862,7 +862,7 @@ UI::EventReturn CreditsScreen::OnPPSSPPOrg(UI::EventParams &e) {
}
UI::EventReturn CreditsScreen::OnPrivacy(UI::EventParams &e) {
LaunchBrowser("https://www.ppsspp.org/privacy.html");
LaunchBrowser("https://www.ppsspp.org/privacy");
return UI::EVENT_DONE;
}

View file

@ -1479,7 +1479,7 @@ void NativeInputBoxReceived(std::function<void(bool, const std::string &)> cb, b
void NativeResized() {
// NativeResized can come from any thread so we just set a flag, then process it later.
INFO_LOG(G3D, "NativeResized - setting flag");
VERBOSE_LOG(G3D, "NativeResized - setting flag");
resized = true;
}

View file

@ -662,6 +662,11 @@
<None Include="..\..\Common\Math\fast\fast_matrix_neon.S" />
<None Include="..\..\Common\Math\lin\matrix_neon.s" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\cpu_features_UWP\cpu_features_UWP.vcxproj">
<Project>{c249f016-7f82-45cf-bb6e-0642a988c4d3}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View file

@ -32,6 +32,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzstd_UWP", "libzstd_UWP\
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc_UWP", "miniupnpc_UWP\miniupnpc_UWP.vcxproj", "{D31FD4F0-53EB-477C-9DC7-149796F628E2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpu_features_UWP", "cpu_features_UWP\cpu_features_UWP.vcxproj", "{C249F016-7F82-45CF-BB6E-0642A988C4D3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@ -372,6 +374,30 @@ Global
{D31FD4F0-53EB-477C-9DC7-149796F628E2}.UWP Gold|Win32.Build.0 = UWP Gold|Win32
{D31FD4F0-53EB-477C-9DC7-149796F628E2}.UWP Gold|x64.ActiveCfg = UWP Gold|x64
{D31FD4F0-53EB-477C-9DC7-149796F628E2}.UWP Gold|x64.Build.0 = UWP Gold|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|ARM.ActiveCfg = Debug|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|ARM.Build.0 = Debug|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|ARM64.Build.0 = Debug|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|Win32.ActiveCfg = Debug|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|Win32.Build.0 = Debug|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|x64.ActiveCfg = Debug|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|x64.Build.0 = Debug|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|ARM.ActiveCfg = Release|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|ARM.Build.0 = Release|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|ARM64.ActiveCfg = Release|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|ARM64.Build.0 = Release|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|Win32.ActiveCfg = Release|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|Win32.Build.0 = Release|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|x64.ActiveCfg = Release|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|x64.Build.0 = Release|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.UWP Gold|ARM.ActiveCfg = UWP Gold|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.UWP Gold|ARM.Build.0 = UWP Gold|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.UWP Gold|ARM64.ActiveCfg = UWP Gold|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.UWP Gold|ARM64.Build.0 = UWP Gold|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.UWP Gold|Win32.ActiveCfg = UWP Gold|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.UWP Gold|Win32.Build.0 = UWP Gold|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.UWP Gold|x64.ActiveCfg = UWP Gold|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.UWP Gold|x64.Build.0 = UWP Gold|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,442 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="UWP Gold|ARM">
<Configuration>UWP Gold</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="UWP Gold|ARM64">
<Configuration>UWP Gold</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="UWP Gold|Win32">
<Configuration>UWP Gold</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="UWP Gold|x64">
<Configuration>UWP Gold</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{C249F016-7F82-45CF-BB6E-0642A988C4D3}</ProjectGuid>
<Keyword>StaticLibrary</Keyword>
<RootNamespace>cpufeatures</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformMinVersion>10.0.15063.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='UWP Gold|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>../../ext/cpu_features/include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>STACK_LINE_READER_BUFFER_SIZE=1024;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_aarch64.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_arm.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_mips.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_ppc.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_riscv.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_s390x.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_x86.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpu_features_cache_info.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpu_features_macros.h" />
<ClInclude Include="..\..\ext\cpu_features\include\internal\bit_utils.h" />
<ClInclude Include="..\..\ext\cpu_features\include\internal\cpuid_x86.h" />
<ClInclude Include="..\..\ext\cpu_features\include\internal\filesystem.h" />
<ClInclude Include="..\..\ext\cpu_features\include\internal\stack_line_reader.h" />
<ClInclude Include="..\..\ext\cpu_features\include\internal\string_view.h" />
<ClInclude Include="..\..\ext\cpu_features\include\internal\windows_utils.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
<Text Include="..\..\ext\cpu_features\CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\ext\cpu_features\src\copy.inl" />
<None Include="..\..\ext\cpu_features\src\define_introspection.inl" />
<None Include="..\..\ext\cpu_features\src\define_introspection_and_hwcaps.inl" />
<None Include="..\..\ext\cpu_features\src\equals.inl" />
<None Include="..\..\ext\cpu_features\src\impl_x86__base_implementation.inl" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\ext\cpu_features\src\filesystem.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_aarch64_linux_or_android.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_arm_linux_or_android.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_mips_linux_or_android.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_ppc_linux.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_riscv_linux.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_s390x_linux.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_x86_freebsd.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_x86_linux_or_android.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_x86_macos.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_x86_windows.c" />
<ClCompile Include="..\..\ext\cpu_features\src\stack_line_reader.c" />
<ClCompile Include="..\..\ext\cpu_features\src\string_view.c" />
<ClCompile Include="pch.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UWP Gold|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UWP Gold|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UWP Gold|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Header Files\internal">
<UniqueIdentifier>{530543f7-fdda-43c7-8982-427e4d69e74d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_arm.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_mips.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_ppc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_riscv.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_s390x.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_x86.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\cpu_features_cache_info.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\cpu_features_macros.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\cpuinfo_aarch64.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\internal\filesystem.h">
<Filter>Header Files\internal</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\internal\stack_line_reader.h">
<Filter>Header Files\internal</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\internal\string_view.h">
<Filter>Header Files\internal</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\internal\windows_utils.h">
<Filter>Header Files\internal</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\internal\bit_utils.h">
<Filter>Header Files\internal</Filter>
</ClInclude>
<ClInclude Include="..\..\ext\cpu_features\include\internal\cpuid_x86.h">
<Filter>Header Files\internal</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="..\..\ext\cpu_features\CMakeLists.txt">
<Filter>Resource Files</Filter>
</Text>
</ItemGroup>
<ItemGroup>
<None Include="..\..\ext\cpu_features\src\impl_x86__base_implementation.inl">
<Filter>Source Files</Filter>
</None>
<None Include="..\..\ext\cpu_features\src\copy.inl">
<Filter>Source Files</Filter>
</None>
<None Include="..\..\ext\cpu_features\src\define_introspection.inl">
<Filter>Source Files</Filter>
</None>
<None Include="..\..\ext\cpu_features\src\define_introspection_and_hwcaps.inl">
<Filter>Source Files</Filter>
</None>
<None Include="..\..\ext\cpu_features\src\equals.inl">
<Filter>Source Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.c" />
<ClCompile Include="..\..\ext\cpu_features\src\impl_x86_freebsd.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_x86_linux_or_android.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_x86_macos.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_x86_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\stack_line_reader.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\string_view.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\filesystem.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_aarch64_linux_or_android.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_arm_linux_or_android.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_mips_linux_or_android.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_ppc_linux.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_riscv_linux.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\ext\cpu_features\src\impl_s390x_linux.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1 @@
#include "pch.h"

View file

@ -0,0 +1,4 @@
#pragma once
#include "targetver.h"

View file

@ -0,0 +1,8 @@
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>

1
Windows/.gitignore vendored
View file

@ -1,2 +1,3 @@
*.VC.VC.opendb
*.VC.db
enc_temp_folder

View file

@ -1,5 +1,3 @@
// NOTE: Apologies for the quality of this code, this is really from pre-opensource Dolphin - that is, 2003.
#include "Core/Config.h"
#include "Core/MemMap.h"
#include "Windows/resource.h"
@ -13,6 +11,7 @@
#include "Windows/Debugger/Debugger_Disasm.h"
#include "Windows/Debugger/Debugger_VFPUDlg.h"
#include "Windows/Debugger/DebuggerShared.h"
// #include "Windows/W32Util/DarkMode.h"
#include "Windows/main.h"
#include "Windows/Debugger/CtrlRegisterList.h"
@ -350,10 +349,8 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
switch(message)
{
case WM_INITDIALOG:
{
return TRUE;
}
break;
// DarkModeInitDialog(m_hDlg);
return TRUE;
case WM_NOTIFY:
switch (wParam)
@ -692,7 +689,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
}
break;
}
return FALSE;
return 0; // DarkModeDlgProc(m_hDlg, message, wParam, lParam);
}
void CDisasm::updateThreadLabel(bool clear)

View file

@ -1,5 +1,3 @@
// NOTE: Apologies for the quality of this code, this is really from pre-opensource Dolphin - that is, 2003.
#pragma once
#include "Windows/W32Util/DialogManager.h"

View file

@ -82,7 +82,9 @@ static void EmuThreadFunc(GraphicsContext *graphicsContext) {
// This way they can load a new game.
if (!Core_IsActive())
UpdateUIState(UISTATE_MENU);
Core_Run(g_graphicsContext);
if (!Core_Run(g_graphicsContext)) {
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
}
}
emuThreadState = (int)EmuThreadState::STOPPED;
@ -99,12 +101,14 @@ static void EmuThreadStart(GraphicsContext *graphicsContext) {
}
static void EmuThreadStop() {
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
if (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED &&
emuThreadState != (int)EmuThreadState::STOPPED) {
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
}
}
static void EmuThreadJoin() {
emuThread.join();
emuThread = std::thread();
INFO_LOG(SYSTEM, "EmuThreadJoin - joined");
}

View file

@ -432,7 +432,7 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) {
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
SetGPUBackend(GPUBackend::OPENGL);
renderManager_->SetSwapFunction([&]() {::SwapBuffers(hDC); });
renderManager_->SetSwapFunction([&]() {::SwapBuffers(hDC); }, true);
if (wglSwapIntervalEXT) {
// glew loads wglSwapIntervalEXT if available
renderManager_->SetSwapIntervalFunction([&](int interval) {
@ -500,6 +500,5 @@ void WindowsGLContext::ThreadEnd() {
}
void WindowsGLContext::StopThread() {
renderManager_->WaitUntilQueueIdle();
renderManager_->StopThread();
}

View file

@ -56,6 +56,8 @@
#include "Windows/GPU/WindowsGLContext.h"
#include "Windows/GEDebugger/GEDebugger.h"
#endif
#include "Windows/W32Util/DarkMode.h"
#include "Windows/W32Util/UAHMenuBar.h"
#include "Windows/Debugger/Debugger_Disasm.h"
#include "Windows/Debugger/Debugger_MemoryDlg.h"
@ -177,7 +179,7 @@ namespace MainWindow
WNDCLASSEX wcdisp;
memset(&wcdisp, 0, sizeof(wcdisp));
// Display Window
// Display Window (contained in main window)
wcdisp.cbSize = sizeof(WNDCLASSEX);
wcdisp.style = CS_HREDRAW | CS_VREDRAW;
wcdisp.lpfnWndProc = (WNDPROC)DisplayProc;
@ -728,13 +730,52 @@ namespace MainWindow
return 0;
}
RECT MapRectFromClientToWndCoords(HWND hwnd, const RECT & r)
{
RECT wnd_coords = r;
// map to screen
MapWindowPoints(hwnd, NULL, reinterpret_cast<POINT *>(&wnd_coords), 2);
RECT scr_coords;
GetWindowRect(hwnd, &scr_coords);
// map to window coords by substracting the window coord origin in
// screen coords.
OffsetRect(&wnd_coords, -scr_coords.left, -scr_coords.top);
return wnd_coords;
}
RECT GetNonclientMenuBorderRect(HWND hwnd)
{
RECT r;
GetClientRect(hwnd, &r);
r = MapRectFromClientToWndCoords(hwnd, r);
int y = r.top - 1;
return {
r.left,
y,
r.right,
y + 1
};
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
LRESULT darkResult = 0;
if (UAHDarkModeWndProc(hWnd, message, wParam, lParam, &darkResult)) {
return darkResult;
}
switch (message) {
case WM_CREATE:
if (!DoesVersionMatchWindows(6, 0, 0, 0, true)) {
// Remove the D3D11 choice on versions below XP
RemoveMenu(GetMenu(hWnd), ID_OPTIONS_DIRECT3D11, MF_BYCOMMAND);
}
if (g_darkModeSupported) {
SendMessageW(hWnd, WM_THEMECHANGED, 0, 0);
}
break;
case WM_USER_GET_BASE_POINTER:
@ -749,6 +790,26 @@ namespace MainWindow
}
break;
// Hack to kill the white line underneath the menubar.
// From https://stackoverflow.com/questions/57177310/how-to-paint-over-white-line-between-menu-bar-and-client-area-of-window
case WM_NCPAINT:
case WM_NCACTIVATE:
{
if (!IsDarkModeEnabled() || IsIconic(hWnd)) {
return DefWindowProc(hWnd, message, wParam, lParam);
}
auto result = DefWindowProc(hWnd, message, wParam, lParam);
// Paint over the line with pure black. Could also try to figure out the dark theme color.
HDC hdc = GetWindowDC(hWnd);
RECT r = GetNonclientMenuBorderRect(hWnd);
HBRUSH red = CreateSolidBrush(RGB(0, 0, 0));
FillRect(hdc, &r, red);
DeleteObject(red);
ReleaseDC(hWnd, hdc);
return result;
}
case WM_GETMINMAXINFO:
{
MINMAXINFO *minmax = reinterpret_cast<MINMAXINFO *>(lParam);
@ -804,7 +865,7 @@ namespace MainWindow
case WM_ERASEBKGND:
// This window is always covered by DisplayWindow. No reason to erase.
return 1;
return 0;
case WM_MOVE:
SavePosition();
@ -1048,6 +1109,23 @@ namespace MainWindow
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_SETTINGCHANGE:
{
if (g_darkModeSupported && IsColorSchemeChangeMessage(lParam))
SendMessageW(hWnd, WM_THEMECHANGED, 0, 0);
}
return DefWindowProc(hWnd, message, wParam, lParam);
case WM_THEMECHANGED:
{
if (g_darkModeSupported)
{
_AllowDarkModeForWindow(hWnd, g_darkModeEnabled);
RefreshTitleBarThemeColor(hWnd);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);

View file

@ -44,6 +44,7 @@
#include "Windows/W32Util/Misc.h"
#include "Windows/InputBox.h"
#include "Windows/main.h"
#include "Windows/W32Util/DarkMode.h"
#include "Core/HLE/sceUmd.h"
#include "Core/SaveState.h"
@ -65,7 +66,7 @@ namespace MainWindow {
static bool menuShaderInfoLoaded = false;
std::vector<ShaderInfo> menuShaderInfo;
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
void SetIngameMenuItemStates(HMENU menu, const GlobalUIState state) {
UINT menuEnable = state == UISTATE_INGAME || state == UISTATE_EXCEPTION ? MF_ENABLED : MF_GRAYED;
@ -278,7 +279,6 @@ namespace MainWindow {
TranslateMenuItem(menu, ID_TEXTURESCALING_DEPOSTERIZE);
TranslateMenuItem(menu, ID_OPTIONS_HARDWARETRANSFORM);
TranslateMenuItem(menu, ID_OPTIONS_VERTEXCACHE);
TranslateMenuItem(menu, ID_OPTIONS_SHOWFPS);
TranslateMenuItem(menu, ID_EMULATION_SOUND);
TranslateMenuItem(menu, ID_EMULATION_CHEATS, g_Config.bSystemControls ? L"\tCtrl+T" : L"");
TranslateMenuItem(menu, ID_EMULATION_CHAT, g_Config.bSystemControls ? L"\tCtrl+C" : L"");
@ -882,11 +882,6 @@ namespace MainWindow {
case ID_OPTIONS_VERTEXCACHE:
g_Config.bVertexCache = !g_Config.bVertexCache;
break;
case ID_OPTIONS_SHOWFPS:
g_Config.iShowFPSCounter = g_Config.iShowFPSCounter ? 0 : 3; // 3 = both speed and FPS
break;
case ID_OPTIONS_TEXTUREFILTERING_AUTO: setTexFiltering(TEX_FILTER_AUTO); break;
case ID_OPTIONS_NEARESTFILTERING: setTexFiltering(TEX_FILTER_FORCE_NEAREST); break;
case ID_OPTIONS_LINEARFILTERING: setTexFiltering(TEX_FILTER_FORCE_LINEAR); break;
@ -925,7 +920,7 @@ namespace MainWindow {
break;
case ID_HELP_BUYGOLD:
ShellExecute(NULL, L"open", L"https://central.ppsspp.org/buygold", NULL, NULL, SW_SHOWNORMAL);
ShellExecute(NULL, L"open", L"https://www.ppsspp.org/buygold", NULL, NULL, SW_SHOWNORMAL);
break;
case ID_HELP_OPENFORUM:
@ -942,7 +937,7 @@ namespace MainWindow {
case ID_HELP_ABOUT:
DialogManager::EnableAll(FALSE);
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)AboutDlgProc);
DialogManager::EnableAll(TRUE);
break;
@ -984,7 +979,6 @@ namespace MainWindow {
CHECKITEM(ID_OPTIONS_HARDWARETRANSFORM, g_Config.bHardwareTransform);
CHECKITEM(ID_DEBUG_BREAKONLOAD, !g_Config.bAutoRun);
CHECKITEM(ID_OPTIONS_VERTEXCACHE, g_Config.bVertexCache);
CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.iShowFPSCounter);
CHECKITEM(ID_OPTIONS_FRAMESKIP_AUTO, g_Config.bAutoFrameSkip);
CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.iFrameSkip != FRAMESKIP_OFF);
CHECKITEM(ID_OPTIONS_FRAMESKIPTYPE_COUNT, g_Config.iFrameSkipType == FRAMESKIPTYPE_COUNT);
@ -1274,24 +1268,31 @@ namespace MainWindow {
}
// Message handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
LRESULT CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG:
{
W32Util::CenterWindow(hDlg);
HWND versionBox = GetDlgItem(hDlg, IDC_VERSION);
std::string windowText = System_GetPropertyBool(SYSPROP_APP_GOLD) ? "PPSSPP Gold " : "PPSSPP ";
windowText.append(PPSSPP_GIT_VERSION);
SetWindowText(versionBox, ConvertUTF8ToWString(windowText).c_str());
}
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
case WM_INITDIALOG:
{
W32Util::CenterWindow(hDlg);
HWND versionBox = GetDlgItem(hDlg, IDC_VERSION);
std::string windowText = System_GetPropertyBool(SYSPROP_APP_GOLD) ? "PPSSPP Gold " : "PPSSPP ";
windowText.append(PPSSPP_GIT_VERSION);
SetWindowText(versionBox, ConvertUTF8ToWString(windowText).c_str());
DarkModeInitDialog(hDlg);
return TRUE;
}
break;
case WM_COMMAND:
{
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
return FALSE;
}
default:
return DarkModeDlgProc(hDlg, message, wParam, lParam);
}
return FALSE;
}

View file

@ -87,6 +87,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{999C12EA
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzstd", "..\ext\libzstd.vcxproj", "{8BFD8150-94D5-4BF9-8A50-7BD9929A0850}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpu_features", "..\ext\cpu_features.vcxproj", "{C249F016-7F82-45CF-BB6E-0642A988C4D3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@ -357,6 +359,22 @@ Global
{8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|Win32.Build.0 = Release|Win32
{8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|x64.ActiveCfg = Release|x64
{8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|x64.Build.0 = Release|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|ARM.ActiveCfg = Debug|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|ARM.Build.0 = Debug|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|ARM64.Build.0 = Debug|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|Win32.ActiveCfg = Debug|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|Win32.Build.0 = Debug|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|x64.ActiveCfg = Debug|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Debug|x64.Build.0 = Debug|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|ARM.ActiveCfg = Release|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|ARM.Build.0 = Release|ARM
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|ARM64.ActiveCfg = Release|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|ARM64.Build.0 = Release|ARM64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|Win32.ActiveCfg = Release|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|Win32.Build.0 = Release|Win32
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|x64.ActiveCfg = Release|x64
{C249F016-7F82-45CF-BB6E-0642A988C4D3}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -372,6 +390,7 @@ Global
{B7DED405-40A2-48F8-9382-538F10D442F1} = {999C12EA-A74F-4868-84F7-64C258043C49}
{D8A71225-178B-424E-96C1-CC3BE2C1B047} = {39FCACF8-10D9-4D8D-97AA-7507436AD932}
{8BFD8150-94D5-4BF9-8A50-7BD9929A0850} = {39FCACF8-10D9-4D8D-97AA-7507436AD932}
{C249F016-7F82-45CF-BB6E-0642A988C4D3} = {39FCACF8-10D9-4D8D-97AA-7507436AD932}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2FD47774-A031-48F4-B645-A49A3140A29B}

View file

@ -248,7 +248,7 @@
<AdditionalOptions>$(EXTERNAL_COMPILE_OPTIONS)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../ffmpeg/Windows/x86/lib</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@ -287,7 +287,7 @@
<AdditionalOptions>$(EXTERNAL_COMPILE_OPTIONS)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../ffmpeg/Windows/x86_64/lib</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(OutDir)$(ProjectName).pdb</ProgramDatabaseFile>
@ -322,7 +322,7 @@
<AdditionalOptions>$(EXTERNAL_COMPILE_OPTIONS)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../ffmpeg/Windows/aarch64/lib</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(OutDir)$(ProjectName).pdb</ProgramDatabaseFile>
@ -355,7 +355,7 @@
<AdditionalOptions>$(EXTERNAL_COMPILE_OPTIONS)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../ffmpeg/Windows/arm/lib</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(OutDir)$(ProjectName).pdb</ProgramDatabaseFile>
@ -392,7 +392,7 @@
<AdditionalOptions>$(EXTERNAL_COMPILE_OPTIONS)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../ffmpeg/Windows/x86/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -440,7 +440,7 @@
<AdditionalOptions>$(EXTERNAL_COMPILE_OPTIONS)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../ffmpeg/Windows/x86_64/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@ -482,7 +482,7 @@
<AdditionalOptions>$(EXTERNAL_COMPILE_OPTIONS)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../ffmpeg/Windows/aarch64/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@ -522,7 +522,7 @@
<AdditionalOptions>$(EXTERNAL_COMPILE_OPTIONS)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../ffmpeg/Windows/arm/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@ -917,6 +917,7 @@
<ClCompile Include="TouchInputHandler.cpp" />
<ClCompile Include="GPU\WindowsVulkanContext.cpp" />
<ClCompile Include="W32Util\ContextMenu.cpp" />
<ClCompile Include="W32Util\DarkMode.cpp" />
<ClCompile Include="W32Util\DialogManager.cpp" />
<ClCompile Include="W32Util\Misc.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)%(Filename)2.obj</ObjectFileName>
@ -939,6 +940,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="W32Util\UAHMenuBar.cpp" />
<ClCompile Include="WASAPIStream.cpp" />
<ClCompile Include="WindowsAudio.cpp" />
<ClCompile Include="WindowsHost.cpp" />
@ -1484,7 +1486,9 @@
<ClInclude Include="TouchInputHandler.h" />
<ClInclude Include="GPU\WindowsVulkanContext.h" />
<ClInclude Include="W32Util\ContextMenu.h" />
<ClInclude Include="W32Util\DarkMode.h" />
<ClInclude Include="W32Util\DialogManager.h" />
<ClInclude Include="W32Util\IatHook.h" />
<ClInclude Include="W32Util\Misc.h" />
<ClInclude Include="W32Util\ShellUtil.h" />
<ClInclude Include="W32Util\TabControl.h" />
@ -1492,6 +1496,7 @@
<ClInclude Include="MainWindow.h" />
<ClInclude Include="DSoundStream.h" />
<ClInclude Include="GPU\WindowsGLContext.h" />
<ClInclude Include="W32Util\UAHMenuBar.h" />
<ClInclude Include="WASAPIStream.h" />
<ClInclude Include="WindowsAudio.h" />
<ClInclude Include="WindowsHost.h" />

View file

@ -59,6 +59,9 @@
<Filter Include="Build\CMake">
<UniqueIdentifier>{8f39c005-9738-41c5-a8d8-cfc05bf178e8}</UniqueIdentifier>
</Filter>
<Filter Include="Windows\Darkmode">
<UniqueIdentifier>{e6f1a7f6-807b-484e-9595-bdb58ecaa2ae}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Debugger\CtrlDisAsmView.cpp">
@ -271,6 +274,12 @@
<ClCompile Include="..\libretro\LibretroGLCoreContext.cpp">
<Filter>Other Platforms\libretro</Filter>
</ClCompile>
<ClCompile Include="W32Util\DarkMode.cpp">
<Filter>Windows\W32Util</Filter>
</ClCompile>
<ClCompile Include="W32Util\UAHMenuBar.cpp">
<Filter>Windows\W32Util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Debugger\CtrlDisAsmView.h">
@ -553,6 +562,15 @@
<ClInclude Include="..\ios\iCade\iCadeReaderView.h">
<Filter>Other Platforms\iOS\iCade</Filter>
</ClInclude>
<ClInclude Include="W32Util\DarkMode.h">
<Filter>Windows\W32Util</Filter>
</ClInclude>
<ClInclude Include="W32Util\IatHook.h">
<Filter>Windows\W32Util</Filter>
</ClInclude>
<ClInclude Include="W32Util\UAHMenuBar.h">
<Filter>Windows\W32Util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="icon1.ico">
@ -820,4 +838,4 @@
<Filter>Other Platforms\SDL</Filter>
</Text>
</ItemGroup>
</Project>
</Project>

View file

@ -0,0 +1,229 @@
#include "IatHook.h"
#include "DarkMode.h"
fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr;
fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = nullptr;
fnAllowDarkModeForWindow _AllowDarkModeForWindow = nullptr;
fnAllowDarkModeForApp _AllowDarkModeForApp = nullptr;
fnFlushMenuThemes _FlushMenuThemes = nullptr;
fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = nullptr;
fnIsDarkModeAllowedForWindow _IsDarkModeAllowedForWindow = nullptr;
fnGetIsImmersiveColorUsingHighContrast _GetIsImmersiveColorUsingHighContrast = nullptr;
fnOpenNcThemeData _OpenNcThemeData = nullptr;
// 1903 18362
fnShouldSystemUseDarkMode _ShouldSystemUseDarkMode = nullptr;
fnSetPreferredAppMode _SetPreferredAppMode = nullptr;
fnSetWindowTheme _SetWindowTheme = nullptr;
bool g_darkModeSupported = false;
bool g_darkModeEnabled = false;
DWORD g_buildNumber = 0;
bool AllowDarkModeForWindow(HWND hWnd, bool allow)
{
if (g_darkModeSupported)
return _AllowDarkModeForWindow(hWnd, allow);
return false;
}
bool IsHighContrast()
{
HIGHCONTRASTW highContrast = { sizeof(highContrast) };
if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast), &highContrast, FALSE))
return highContrast.dwFlags & HCF_HIGHCONTRASTON;
return false;
}
void RefreshTitleBarThemeColor(HWND hWnd)
{
BOOL dark = FALSE;
if (_IsDarkModeAllowedForWindow(hWnd) &&
_ShouldAppsUseDarkMode() &&
!IsHighContrast())
{
dark = TRUE;
}
if (g_buildNumber < 18362)
SetPropW(hWnd, L"UseImmersiveDarkModeColors", reinterpret_cast<HANDLE>(static_cast<INT_PTR>(dark)));
else if (_SetWindowCompositionAttribute)
{
WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &dark, sizeof(dark) };
_SetWindowCompositionAttribute(hWnd, &data);
}
}
bool IsColorSchemeChangeMessage(LPARAM lParam)
{
bool is = false;
if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, TRUE) == CSTR_EQUAL)
{
_RefreshImmersiveColorPolicyState();
is = true;
}
_GetIsImmersiveColorUsingHighContrast(IHCM_REFRESH);
return is;
}
bool IsColorSchemeChangeMessage(UINT message, LPARAM lParam)
{
if (message == WM_SETTINGCHANGE)
return IsColorSchemeChangeMessage(lParam);
return false;
}
void AllowDarkModeForApp(bool allow)
{
if (_AllowDarkModeForApp)
_AllowDarkModeForApp(allow);
else if (_SetPreferredAppMode)
_SetPreferredAppMode(allow ? AllowDark : Default);
}
void FixDarkScrollBar()
{
// Disable this, doesn't look good.
return;
HMODULE hComctl = LoadLibraryExW(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hComctl)
{
auto addr = FindDelayLoadThunkInModule(hComctl, "uxtheme.dll", 49); // OpenNcThemeData
if (addr)
{
DWORD oldProtect;
if (VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), PAGE_READWRITE, &oldProtect))
{
auto MyOpenThemeData = [](HWND hWnd, LPCWSTR classList) -> HTHEME {
if (wcscmp(classList, L"ScrollBar") == 0)
{
hWnd = nullptr;
classList = L"Explorer::ScrollBar";
}
return _OpenNcThemeData(hWnd, classList);
};
addr->u1.Function = reinterpret_cast<ULONG_PTR>(static_cast<fnOpenNcThemeData>(MyOpenThemeData));
VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), oldProtect, &oldProtect);
}
}
}
}
void DarkModeInitDialog(HWND hDlg) {
if (g_darkModeSupported) {
_SetWindowTheme(GetDlgItem(hDlg, IDOK), L"Explorer", nullptr);
SendMessageW(hDlg, WM_THEMECHANGED, 0, 0);
}
}
LRESULT DarkModeDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
constexpr COLORREF darkBkColor = 0x383838;
constexpr COLORREF darkTextColor = 0xFFFFFF;
static HBRUSH hbrBkgnd = nullptr;
switch (message) {
case WM_CTLCOLORDLG:
case WM_CTLCOLORSTATIC:
{
if (g_darkModeSupported && g_darkModeEnabled)
{
HDC hdc = reinterpret_cast<HDC>(wParam);
SetTextColor(hdc, darkTextColor);
SetBkColor(hdc, darkBkColor);
if (!hbrBkgnd)
hbrBkgnd = CreateSolidBrush(darkBkColor);
return reinterpret_cast<INT_PTR>(hbrBkgnd);
}
break;
}
case WM_SETTINGCHANGE:
{
if (g_darkModeSupported && IsColorSchemeChangeMessage(lParam))
SendMessageW(hDlg, WM_THEMECHANGED, 0, 0);
break;
}
case WM_THEMECHANGED:
{
if (g_darkModeSupported)
{
_AllowDarkModeForWindow(hDlg, g_darkModeEnabled);
RefreshTitleBarThemeColor(hDlg);
HWND hButton = GetDlgItem(hDlg, IDOK);
_AllowDarkModeForWindow(hButton, g_darkModeEnabled);
SendMessageW(hButton, WM_THEMECHANGED, 0, 0);
UpdateWindow(hDlg);
}
break;
}
}
return FALSE;
}
bool IsDarkModeEnabled() {
return g_darkModeEnabled;
}
constexpr bool CheckBuildNumber(DWORD buildNumber)
{
// TODO: This is BS.
return (buildNumber == 17763 || // 1809
buildNumber == 18362 || // 1903
buildNumber == 18363 || // 1909
buildNumber >= 19041); // Windows 11
}
void InitDarkMode()
{
auto RtlGetNtVersionNumbers = reinterpret_cast<fnRtlGetNtVersionNumbers>(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers"));
if (RtlGetNtVersionNumbers)
{
DWORD major, minor;
RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber);
g_buildNumber &= ~0xF0000000;
if (major == 10 && minor == 0 && CheckBuildNumber(g_buildNumber))
{
HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hUxtheme)
{
_OpenNcThemeData = reinterpret_cast<fnOpenNcThemeData>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(49)));
_RefreshImmersiveColorPolicyState = reinterpret_cast<fnRefreshImmersiveColorPolicyState>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104)));
_GetIsImmersiveColorUsingHighContrast = reinterpret_cast<fnGetIsImmersiveColorUsingHighContrast>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(106)));
_ShouldAppsUseDarkMode = reinterpret_cast<fnShouldAppsUseDarkMode>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(132)));
_AllowDarkModeForWindow = reinterpret_cast<fnAllowDarkModeForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133)));
auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135));
if (g_buildNumber < 18362)
_AllowDarkModeForApp = reinterpret_cast<fnAllowDarkModeForApp>(ord135);
else
_SetPreferredAppMode = reinterpret_cast<fnSetPreferredAppMode>(ord135);
//_FlushMenuThemes = reinterpret_cast<fnFlushMenuThemes>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136)));
_IsDarkModeAllowedForWindow = reinterpret_cast<fnIsDarkModeAllowedForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137)));
_SetWindowCompositionAttribute = reinterpret_cast<fnSetWindowCompositionAttribute>(GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetWindowCompositionAttribute"));
_SetWindowTheme = reinterpret_cast<fnSetWindowTheme>(GetProcAddress(hUxtheme, "SetWindowTheme"));
if (_OpenNcThemeData &&
_RefreshImmersiveColorPolicyState &&
_ShouldAppsUseDarkMode &&
_AllowDarkModeForWindow &&
(_AllowDarkModeForApp || _SetPreferredAppMode) &&
//_FlushMenuThemes &&
_IsDarkModeAllowedForWindow)
{
g_darkModeSupported = true;
AllowDarkModeForApp(true);
_RefreshImmersiveColorPolicyState();
g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast();
FixDarkScrollBar();
}
}
}
}
}

110
Windows/W32Util/DarkMode.h Normal file
View file

@ -0,0 +1,110 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <CommCtrl.h>
#include <Uxtheme.h>
#include <WindowsX.h>
#include <Vssym32.h>
#include "IatHook.h"
enum IMMERSIVE_HC_CACHE_MODE
{
IHCM_USE_CACHED_VALUE,
IHCM_REFRESH
};
// 1903 18362
enum PreferredAppMode
{
Default,
AllowDark,
ForceDark,
ForceLight,
Max
};
enum WINDOWCOMPOSITIONATTRIB
{
WCA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_HOLOGRAPHIC = 23,
WCA_EXCLUDED_FROM_DDA = 24,
WCA_PASSIVEUPDATEMODE = 25,
WCA_USEDARKMODECOLORS = 26,
WCA_LAST = 27
};
struct WINDOWCOMPOSITIONATTRIBDATA
{
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
};
using fnRtlGetNtVersionNumbers = void (WINAPI *)(LPDWORD major, LPDWORD minor, LPDWORD build);
using fnSetWindowCompositionAttribute = BOOL (WINAPI *)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*);
// 1809 17763
using fnShouldAppsUseDarkMode = bool (WINAPI *)(); // ordinal 132
using fnAllowDarkModeForWindow = bool (WINAPI *)(HWND hWnd, bool allow); // ordinal 133
using fnAllowDarkModeForApp = bool (WINAPI *)(bool allow); // ordinal 135, in 1809
using fnFlushMenuThemes = void (WINAPI *)(); // ordinal 136
using fnRefreshImmersiveColorPolicyState = void (WINAPI *)(); // ordinal 104
using fnIsDarkModeAllowedForWindow = bool (WINAPI *)(HWND hWnd); // ordinal 137
using fnGetIsImmersiveColorUsingHighContrast = bool (WINAPI *)(IMMERSIVE_HC_CACHE_MODE mode); // ordinal 106
using fnOpenNcThemeData = HTHEME(WINAPI *)(HWND hWnd, LPCWSTR pszClassList); // ordinal 49
// 1903 18362
using fnShouldSystemUseDarkMode = bool (WINAPI *)(); // ordinal 138
using fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode); // ordinal 135, in 1903
using fnIsDarkModeAllowedForApp = bool (WINAPI *)(); // ordinal 139
using fnSetWindowTheme = void (WINAPI*)(HWND, LPCWSTR, LPCWSTR);
//---------------------------------------------------------------------------
extern fnSetWindowCompositionAttribute _SetWindowCompositionAttribute;
extern fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode;
extern fnAllowDarkModeForWindow _AllowDarkModeForWindow;
extern fnAllowDarkModeForApp _AllowDarkModeForApp;
extern fnFlushMenuThemes _FlushMenuThemes;
extern fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState;
extern fnIsDarkModeAllowedForWindow _IsDarkModeAllowedForWindow;
extern fnGetIsImmersiveColorUsingHighContrast _GetIsImmersiveColorUsingHighContrast;
extern fnOpenNcThemeData _OpenNcThemeData;
// 1903 18362
extern fnShouldSystemUseDarkMode _ShouldSystemUseDarkMode;
extern fnSetPreferredAppMode _SetPreferredAppMode;
extern fnSetWindowTheme _SetWindowTheme;
extern bool g_darkModeSupported;
extern bool g_darkModeEnabled;
void InitDarkMode();
bool AllowDarkModeForWindow(HWND hWnd, bool allow);
void RefreshTitleBarThemeColor(HWND hWnd);
bool IsColorSchemeChangeMessage(LPARAM lParam);
bool IsDarkModeEnabled();
void DarkModeInitDialog(HWND hDlg);
LRESULT DarkModeDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

91
Windows/W32Util/IatHook.h Normal file
View file

@ -0,0 +1,91 @@
// This file contains code from
// https://github.com/stevemk14ebr/PolyHook_2_0/blob/master/sources/IatHook.cpp
// which is licensed under the MIT License.
// See PolyHook_2_0-LICENSE for more information.
#pragma once
template <typename T, typename T1, typename T2>
constexpr T RVA2VA(T1 base, T2 rva)
{
return reinterpret_cast<T>(reinterpret_cast<ULONG_PTR>(base) + rva);
}
template <typename T>
constexpr T DataDirectoryFromModuleBase(void *moduleBase, size_t entryID)
{
auto dosHdr = reinterpret_cast<PIMAGE_DOS_HEADER>(moduleBase);
auto ntHdr = RVA2VA<PIMAGE_NT_HEADERS>(moduleBase, dosHdr->e_lfanew);
auto dataDir = ntHdr->OptionalHeader.DataDirectory;
return RVA2VA<T>(moduleBase, dataDir[entryID].VirtualAddress);
}
inline PIMAGE_THUNK_DATA FindAddressByName(void *moduleBase, PIMAGE_THUNK_DATA impName, PIMAGE_THUNK_DATA impAddr, const char *funcName)
{
for (; impName->u1.Ordinal; ++impName, ++impAddr)
{
if (IMAGE_SNAP_BY_ORDINAL(impName->u1.Ordinal))
continue;
auto import = RVA2VA<PIMAGE_IMPORT_BY_NAME>(moduleBase, impName->u1.AddressOfData);
if (strcmp(import->Name, funcName) != 0)
continue;
return impAddr;
}
return nullptr;
}
inline PIMAGE_THUNK_DATA FindAddressByOrdinal(void *moduleBase, PIMAGE_THUNK_DATA impName, PIMAGE_THUNK_DATA impAddr, uint16_t ordinal)
{
for (; impName->u1.Ordinal; ++impName, ++impAddr)
{
if (IMAGE_SNAP_BY_ORDINAL(impName->u1.Ordinal) && IMAGE_ORDINAL(impName->u1.Ordinal) == ordinal)
return impAddr;
}
return nullptr;
}
inline PIMAGE_THUNK_DATA FindIatThunkInModule(void *moduleBase, const char *dllName, const char *funcName)
{
auto imports = DataDirectoryFromModuleBase<PIMAGE_IMPORT_DESCRIPTOR>(moduleBase, IMAGE_DIRECTORY_ENTRY_IMPORT);
for (; imports->Name; ++imports)
{
if (_stricmp(RVA2VA<LPCSTR>(moduleBase, imports->Name), dllName) != 0)
continue;
auto origThunk = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->OriginalFirstThunk);
auto thunk = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->FirstThunk);
return FindAddressByName(moduleBase, origThunk, thunk, funcName);
}
return nullptr;
}
inline PIMAGE_THUNK_DATA FindDelayLoadThunkInModule(void *moduleBase, const char *dllName, const char *funcName)
{
auto imports = DataDirectoryFromModuleBase<PIMAGE_DELAYLOAD_DESCRIPTOR>(moduleBase, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
for (; imports->DllNameRVA; ++imports)
{
if (_stricmp(RVA2VA<LPCSTR>(moduleBase, imports->DllNameRVA), dllName) != 0)
continue;
auto impName = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->ImportNameTableRVA);
auto impAddr = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->ImportAddressTableRVA);
return FindAddressByName(moduleBase, impName, impAddr, funcName);
}
return nullptr;
}
inline PIMAGE_THUNK_DATA FindDelayLoadThunkInModule(void *moduleBase, const char *dllName, uint16_t ordinal)
{
auto imports = DataDirectoryFromModuleBase<PIMAGE_DELAYLOAD_DESCRIPTOR>(moduleBase, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
for (; imports->DllNameRVA; ++imports)
{
if (_stricmp(RVA2VA<LPCSTR>(moduleBase, imports->DllNameRVA), dllName) != 0)
continue;
auto impName = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->ImportNameTableRVA);
auto impAddr = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->ImportAddressTableRVA);
return FindAddressByOrdinal(moduleBase, impName, impAddr, ordinal);
}
return nullptr;
}

View file

@ -0,0 +1,115 @@
#include "Common/CommonWindows.h"
#include <Uxtheme.h>
#include <vsstyle.h>
#include "Windows/W32Util/UAHMenuBar.h"
#include "Windows/W32Util/DarkMode.h"
static HTHEME g_menuTheme = nullptr;
// processes messages related to UAH / custom menubar drawing.
// return true if handled, false to continue with normal processing in your wndproc
bool UAHDarkModeWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *lr)
{
if (!IsDarkModeEnabled() && message != WM_THEMECHANGED) {
return false;
}
switch (message)
{
case WM_UAHDRAWMENU:
{
UAHMENU *pUDM = (UAHMENU *)lParam;
RECT rc = { 0 };
// get the menubar rect
{
MENUBARINFO mbi = { sizeof(mbi) };
GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi);
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
// the rcBar is offset by the window rect
rc = mbi.rcBar;
OffsetRect(&rc, -rcWindow.left, -rcWindow.top);
rc.top -= 1;
}
if (!g_menuTheme) {
g_menuTheme = OpenThemeData(hWnd, L"Menu");
}
DrawThemeBackground(g_menuTheme, pUDM->hdc, MENU_POPUPITEM, MPI_NORMAL, &rc, nullptr);
return true;
}
case WM_UAHDRAWMENUITEM:
{
UAHDRAWMENUITEM *pUDMI = (UAHDRAWMENUITEM *)lParam;
// get the menu item string
wchar_t menuString[256] = { 0 };
MENUITEMINFO mii = { sizeof(mii), MIIM_STRING };
{
mii.dwTypeData = menuString;
mii.cch = (sizeof(menuString) / 2) - 1;
GetMenuItemInfo(pUDMI->um.hmenu, pUDMI->umi.iPosition, TRUE, &mii);
}
// get the item state for drawing
DWORD dwFlags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
int iTextStateID = 0;
int iBackgroundStateID = 0;
{
if ((pUDMI->dis.itemState & ODS_INACTIVE) | (pUDMI->dis.itemState & ODS_DEFAULT)) {
// normal display
iTextStateID = MPI_NORMAL;
iBackgroundStateID = MPI_NORMAL;
}
if (pUDMI->dis.itemState & ODS_HOTLIGHT) {
// hot tracking
iTextStateID = MPI_HOT;
iBackgroundStateID = MPI_HOT;
}
if (pUDMI->dis.itemState & ODS_SELECTED) {
// clicked -- MENU_POPUPITEM has no state for this, though MENU_BARITEM does
iTextStateID = MPI_HOT;
iBackgroundStateID = MPI_HOT;
}
if ((pUDMI->dis.itemState & ODS_GRAYED) || (pUDMI->dis.itemState & ODS_DISABLED)) {
// disabled / grey text
iTextStateID = MPI_DISABLED;
iBackgroundStateID = MPI_DISABLED;
}
if (pUDMI->dis.itemState & ODS_NOACCEL) {
dwFlags |= DT_HIDEPREFIX;
}
}
if (!g_menuTheme) {
g_menuTheme = OpenThemeData(hWnd, L"Menu");
}
DrawThemeBackground(g_menuTheme, pUDMI->um.hdc, MENU_POPUPITEM, iBackgroundStateID, &pUDMI->dis.rcItem, nullptr);
DrawThemeText(g_menuTheme, pUDMI->um.hdc, MENU_POPUPITEM, iTextStateID, menuString, mii.cch, dwFlags, 0, &pUDMI->dis.rcItem);
return true;
}
case WM_THEMECHANGED:
{
if (g_menuTheme) {
CloseThemeData(g_menuTheme);
g_menuTheme = nullptr;
}
// continue processing in main wndproc
return false;
}
default:
return false;
}
}

View file

@ -0,0 +1,74 @@
#pragma once
// MIT license, see LICENSE
// Copyright(c) 2021 adzm / Adam D. Walling
// processes messages related to UAH / custom menubar drawing.
// return true if handled, false to continue with normal processing in your wndproc
bool UAHDarkModeWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr);
// window messages related to menu bar drawing
#define WM_UAHDESTROYWINDOW 0x0090 // handled by DefWindowProc
#define WM_UAHDRAWMENU 0x0091 // lParam is UAHMENU
#define WM_UAHDRAWMENUITEM 0x0092 // lParam is UAHDRAWMENUITEM
#define WM_UAHINITMENU 0x0093 // handled by DefWindowProc
#define WM_UAHMEASUREMENUITEM 0x0094 // lParam is UAHMEASUREMENUITEM
#define WM_UAHNCPAINTMENUPOPUP 0x0095 // handled by DefWindowProc
// describes the sizes of the menu bar or menu item
typedef union tagUAHMENUITEMMETRICS
{
// cx appears to be 14 / 0xE less than rcItem's width!
// cy 0x14 seems stable, i wonder if it is 4 less than rcItem's height which is always 24 atm
struct {
DWORD cx;
DWORD cy;
} rgsizeBar[2];
struct {
DWORD cx;
DWORD cy;
} rgsizePopup[4];
} UAHMENUITEMMETRICS;
// not really used in our case but part of the other structures
typedef struct tagUAHMENUPOPUPMETRICS
{
DWORD rgcx[4];
DWORD fUpdateMaxWidths : 2; // from kernel symbols, padded to full dword
} UAHMENUPOPUPMETRICS;
// hmenu is the main window menu; hdc is the context to draw in
typedef struct tagUAHMENU
{
HMENU hmenu;
HDC hdc;
DWORD dwFlags; // no idea what these mean, in my testing it's either 0x00000a00 or sometimes 0x00000a10
} UAHMENU;
// menu items are always referred to by iPosition here
typedef struct tagUAHMENUITEM
{
int iPosition; // 0-based position of menu item in menubar
UAHMENUITEMMETRICS umim;
UAHMENUPOPUPMETRICS umpm;
} UAHMENUITEM;
// the DRAWITEMSTRUCT contains the states of the menu items, as well as
// the position index of the item in the menu, which is duplicated in
// the UAHMENUITEM's iPosition as well
typedef struct UAHDRAWMENUITEM
{
DRAWITEMSTRUCT dis; // itemID looks uninitialized
UAHMENU um;
UAHMENUITEM umi;
} UAHDRAWMENUITEM;
// the MEASUREITEMSTRUCT is intended to be filled with the size of the item
// height appears to be ignored, but width can be modified
typedef struct tagUAHMEASUREMENUITEM
{
MEASUREITEMSTRUCT mis;
UAHMENU um;
UAHMENUITEM umi;
} UAHMEASUREMENUITEM;

View file

@ -42,6 +42,7 @@
#include "Common/Thread/ThreadUtil.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Net/Resolve.h"
#include "W32Util/DarkMode.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
@ -290,7 +291,7 @@ static int ScreenRefreshRateHz() {
return 60; // default value
} else {
if (lpDevMode.dmFields & DM_DISPLAYFREQUENCY) {
return lpDevMode.dmDisplayFrequency > 15 ? lpDevMode.dmDisplayFrequency : 60;
return lpDevMode.dmDisplayFrequency > 60 ? lpDevMode.dmDisplayFrequency : 60;
} else {
return 60;
}
@ -553,6 +554,8 @@ static void WinMainInit() {
// FMA3 support in the 2013 CRT is broken on Vista and Windows 7 RTM (fixed in SP1). Just disable it.
_set_FMA3_enable(0);
#endif
InitDarkMode();
}
static void WinMainCleanup() {

View file

@ -639,7 +639,6 @@ BEGIN
END
MENUITEM "Hardware Transform", ID_OPTIONS_HARDWARETRANSFORM
MENUITEM "Vertex Cache", ID_OPTIONS_VERTEXCACHE
MENUITEM "Show FPS Counter", ID_OPTIONS_SHOWFPS
MENUITEM "", 0, MFT_SEPARATOR
MENUITEM "Enable Sound", ID_EMULATION_SOUND
MENUITEM "", 0, MFT_SEPARATOR

View file

@ -158,7 +158,6 @@
#define ID_DEBUG_BREAKONLOAD 40039
#define ID_DEBUG_DUMPNEXTFRAME 40040
#define ID_OPTIONS_VERTEXCACHE 40041
#define ID_OPTIONS_SHOWFPS 40042
#define ID_OPTIONS_FRAMESKIP 40044
#define IDC_MEMCHECK 40045
#define ID_FILE_MEMSTICK 40046

View file

@ -4,6 +4,10 @@ SRC := ../..
include $(CLEAR_VARS)
include $(LOCAL_PATH)/Locals.mk
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../../ext/cpu_features/include
LOCAL_CFLAGS += -DSTACK_LINE_READER_BUFFER_SIZE=1024 -DHAVE_DLFCN_H
# http://software.intel.com/en-us/articles/getting-started-on-optimizing-ndk-project-for-multiple-cpu-architectures
ifeq ($(TARGET_ARCH_ABI),x86)
@ -119,7 +123,21 @@ EXT_FILES := \
$(SRC)/ext/udis86/syn-intel.c \
$(SRC)/ext/udis86/syn.c \
$(SRC)/ext/udis86/udis86.c \
$(SRC)/ext/xbrz/xbrz.cpp
$(SRC)/ext/xbrz/xbrz.cpp \
$(SRC)/ext/cpu_features/src/filesystem.c \
$(SRC)/ext/cpu_features/src/hwcaps.c \
$(SRC)/ext/cpu_features/src/impl_aarch64_linux_or_android.c \
$(SRC)/ext/cpu_features/src/impl_arm_linux_or_android.c \
$(SRC)/ext/cpu_features/src/impl_mips_linux_or_android.c \
$(SRC)/ext/cpu_features/src/impl_ppc_linux.c \
$(SRC)/ext/cpu_features/src/impl_riscv_linux.c \
$(SRC)/ext/cpu_features/src/impl_s390x_linux.c \
$(SRC)/ext/cpu_features/src/impl_x86_freebsd.c \
$(SRC)/ext/cpu_features/src/impl_x86_linux_or_android.c \
$(SRC)/ext/cpu_features/src/impl_x86_macos.c \
$(SRC)/ext/cpu_features/src/impl_x86_windows.c \
$(SRC)/ext/cpu_features/src/stack_line_reader.c \
$(SRC)/ext/cpu_features/src/string_view.c
EXEC_AND_LIB_FILES := \
$(ARCH_FILES) \

View file

@ -38,7 +38,6 @@ bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int
void AndroidJavaEGLGraphicsContext::ShutdownFromRenderThread() {
INFO_LOG(G3D, "AndroidJavaEGLGraphicsContext::Shutdown");
renderManager_->WaitUntilQueueIdle();
renderManager_ = nullptr; // owned by draw_.
delete draw_;
draw_ = nullptr;

View file

@ -40,7 +40,6 @@ public:
}
void StopThread() override {
renderManager_->WaitUntilQueueIdle();
renderManager_->StopThread();
}

View file

@ -335,7 +335,7 @@ static void EmuThreadFunc() {
while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) {
UpdateRunLoopAndroid(env);
}
INFO_LOG(SYSTEM, "QUIT_REQUESTED found, left loop. Setting state to STOPPED.");
INFO_LOG(SYSTEM, "QUIT_REQUESTED found, left EmuThreadFunc loop. Setting state to STOPPED.");
emuThreadState = (int)EmuThreadState::STOPPED;
NativeShutdownGraphics();
@ -868,17 +868,18 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) {
EmuThreadStop("shutdown");
INFO_LOG(SYSTEM, "BeginAndroidShutdown");
graphicsContext->BeginAndroidShutdown();
// Skipping GL calls, the old context is gone.
while (graphicsContext->ThreadFrame()) {
INFO_LOG(SYSTEM, "graphicsContext->ThreadFrame executed to clear buffers");
}
INFO_LOG(SYSTEM, "Joining emuthread");
EmuThreadJoin();
INFO_LOG(SYSTEM, "Joined emuthread");
// Now, it could be that we had some frames queued up. Get through them.
// We're on the render thread, so this is synchronous.
do {
INFO_LOG(SYSTEM, "Executing graphicsContext->ThreadFrame to clear buffers");
} while (graphicsContext->ThreadFrame());
graphicsContext->ThreadEnd();
INFO_LOG(SYSTEM, "ThreadEnd called.");
graphicsContext->ShutdownFromRenderThread();
INFO_LOG(SYSTEM, "Graphics context now shut down from NativeApp_shutdown");
INFO_LOG(SYSTEM, "Joining emuthread");
EmuThreadJoin();
}
INFO_LOG(SYSTEM, "NativeApp.shutdown() -- begin");

Binary file not shown.

View file

@ -262,7 +262,7 @@ Random = ‎عشوائي
Replace textures = ‎إستبدال الرسوم
Reset = Reset
Reset limited logging = Reset limited logging
RestoreDefaultSettings = Are you sure you want to restore all settings back to their defaults?\nControl mapping settings are not changed.\n\nYou can't undo this.\nPlease restart PPSSPP for the changes to take effect.
RestoreDefaultSettings = Restore these settings back to their defaults?\nYou can't undo this.\nPlease restart PPSSPP after restoring settings.
RestoreGameDefaultSettings = Are you sure you want to restore the game-specific settings\nback to the PPSSPP defaults?
Resume = Resume
Run CPU Tests = ‎شغل فحوص المعالج
@ -530,6 +530,8 @@ Must Restart = ‎يجب عليك إعادة تشغيل البرنامج لكي
Native device resolution = ‎حجم الجهاز الأساسي
Nearest = Nearest
No buffer = No buffer
Show Battery % = Show Battery %
Show Speed = Show Speed
Skip Buffer Effects = Skip buffer effects
None = ‎لا شئ
Number of Frames = Number of frames

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