Merge branch 'master' into TemporaryHackishBranch

# Conflicts:
#	UI/GameSettingsScreen.cpp
#	assets/shaders/defaultshaders.ini
This commit is contained in:
LunaMoo committed 2020-05-18 02:58:43 +02:00
commit 0f6fa442dd
94 files changed
+1022 -796

No files matched your search

+1
View File
@@ -1070,6 +1070,7 @@ add_library(native STATIC
ext/native/util/text/wrap_text.h
ext/native/util/text/wrap_text.cpp
ext/native/util/const_map.h
ext/native/util/tiny_set.h
ext/native/ext/jpge/jpgd.cpp
ext/native/ext/jpge/jpgd.h
ext/native/ext/jpge/jpge.cpp
+18 -9
View File
@@ -275,10 +275,14 @@ static int EncodeSize(int size) {
}
}
void ARM64XEmitter::SetCodePointer(u8* ptr)
ARM64XEmitter::ARM64XEmitter(const u8 *ptr, u8 *writePtr) {
SetCodePointer(ptr, writePtr);
}
void ARM64XEmitter::SetCodePointer(const u8 *ptr, u8 *writePtr)
{
m_code = ptr;
m_startcode = m_code;
m_writable = writePtr;
m_lastCacheFlushEnd = ptr;
}
@@ -289,7 +293,7 @@ const u8* ARM64XEmitter::GetCodePointer() const
u8* ARM64XEmitter::GetWritableCodePtr()
{
return m_code;
return m_writable;
}
void ARM64XEmitter::ReserveCodeSpace(u32 bytes)
@@ -321,11 +325,11 @@ void ARM64XEmitter::FlushIcache()
m_lastCacheFlushEnd = m_code;
}
void ARM64XEmitter::FlushIcacheSection(u8* start, u8* end)
void ARM64XEmitter::FlushIcacheSection(const u8 *start, const u8 *end)
{
#if defined(IOS)
// Header file says this is equivalent to: sys_icache_invalidate(start, end - start);
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
sys_cache_control(kCacheFunctionPrepareForExecution, (void *)start, end - start);
#elif PPSSPP_PLATFORM(WINDOWS)
FlushInstructionCache(GetCurrentProcess(), start, end - start);
#elif PPSSPP_ARCH(ARM64)
@@ -911,7 +915,7 @@ void ARM64XEmitter::SetJumpTarget(FixupBranch const& branch)
inst = ((branch.bit & 0x20) << 26) | (0x1B << 25) | (Not << 24) | ((branch.bit & 0x1F) << 19) | (MaskImm14(distance) << 5) | reg;
}
break;
case 5: // B (uncoditional)
case 5: // B (unconditional)
_assert_msg_(DYNA_REC, IsInRangeImm26(distance), "%s(%d): Received too large distance: %llx", __FUNCTION__, branch.type, distance);
inst = (0x5 << 26) | MaskImm26(distance);
break;
@@ -920,7 +924,9 @@ void ARM64XEmitter::SetJumpTarget(FixupBranch const& branch)
inst = (0x25 << 26) | MaskImm26(distance);
break;
}
*(u32*)branch.ptr = inst;
ptrdiff_t writable = m_writable - m_code;
*(u32 *)(branch.ptr + writable) = inst;
}
FixupBranch ARM64XEmitter::CBZ(ARM64Reg Rt)
@@ -3924,8 +3930,11 @@ void ARM64XEmitter::SUBSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
}
void ARM64CodeBlock::PoisonMemory(int offset) {
u32* ptr = (u32*)(region + offset);
u32* maxptr = (u32*)(region + region_size - offset);
// So we can adjust region to writable space. Might be zero.
ptrdiff_t writable = m_writable - m_code;
u32 *ptr = (u32 *)(region + offset + writable);
u32 *maxptr = (u32 *)(region + region_size - offset + writable);
// If our memory isn't a multiple of u32 then this won't write the last remaining bytes with anything
// Less than optimal, but there would be nothing we could do but throw a runtime warning anyway.
// AArch64: 0xD4200000 = BRK 0
+11 -13
View File
@@ -162,7 +162,8 @@ enum RoundingMode {
struct FixupBranch
{
u8* ptr;
// Pointer to executable code address.
const u8 *ptr;
// Type defines
// 0 = CBZ (32bit)
// 1 = CBNZ (32bit)
@@ -338,11 +339,12 @@ public:
class ARM64XEmitter
{
friend class ARM64FloatEmitter;
friend class ARM64CodeBlock;
private:
u8* m_code;
u8* m_startcode;
u8* m_lastCacheFlushEnd;
const u8 *m_code = nullptr;
u8 *m_writable = nullptr;
const u8 *m_lastCacheFlushEnd = nullptr;
void EncodeCompareBranchInst(u32 op, ARM64Reg Rt, const void* ptr);
void EncodeTestBranchInst(u32 op, ARM64Reg Rt, u8 bits, const void* ptr);
@@ -376,34 +378,30 @@ private:
protected:
inline void Write32(u32 value)
{
*(u32*)m_code = value;
*(u32 *)m_writable = value;
m_code += 4;
m_writable += 4;
}
public:
ARM64XEmitter()
: m_code(nullptr), m_startcode(nullptr), m_lastCacheFlushEnd(nullptr)
{
}
ARM64XEmitter(u8* code_ptr) {
m_code = code_ptr;
m_lastCacheFlushEnd = code_ptr;
m_startcode = code_ptr;
}
ARM64XEmitter(const u8 *codePtr, u8 *writablePtr);
virtual ~ARM64XEmitter()
{
}
void SetCodePointer(u8* ptr);
void SetCodePointer(const u8 *ptr, u8 *writePtr);
const u8* GetCodePointer() const;
void ReserveCodeSpace(u32 bytes);
const u8* AlignCode16();
const u8* AlignCodePage();
void FlushIcache();
void FlushIcacheSection(u8* start, u8* end);
void FlushIcacheSection(const u8* start, const u8* end);
u8* GetWritableCodePtr();
// FixupBranch branching
+1 -1
View File
@@ -586,7 +586,7 @@ void ARMXEmitter::QuickCallFunction(ARMReg reg, const void *func) {
}
}
void ARMXEmitter::SetCodePointer(u8 *ptr)
void ARMXEmitter::SetCodePointer(u8 *ptr, u8 *writePtr)
{
code = ptr;
startcode = code;
+1 -1
View File
@@ -443,7 +443,7 @@ public:
}
virtual ~ARMXEmitter() {}
void SetCodePointer(u8 *ptr);
void SetCodePointer(u8 *ptr, u8 *writePtr);
const u8 *GetCodePointer() const;
void ReserveCodeSpace(u32 bytes);
-102
View File
@@ -1,102 +0,0 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
// IWYU pragma: private, include "Common/Atomic.h"
#pragma once
#include "CommonTypes.h"
// Atomic operations are performed in a single step by the CPU. It is
// impossible for other threads to see the operation "half-done."
//
// Some atomic operations can be combined with different types of memory
// barriers called "Acquire semantics" and "Release semantics", defined below.
//
// Acquire semantics: Future memory accesses cannot be relocated to before the
// operation.
//
// Release semantics: Past memory accesses cannot be relocated to after the
// operation.
//
// These barriers affect not only the compiler, but also the CPU.
namespace Common
{
inline void AtomicAdd(volatile u32& target, u32 value)
{
__sync_add_and_fetch(&target, value);
}
inline void AtomicAnd(volatile u32& target, u32 value)
{
__sync_and_and_fetch(&target, value);
}
inline void AtomicDecrement(volatile u32& target)
{
__sync_add_and_fetch(&target, -1);
}
inline void AtomicIncrement(volatile u32& target)
{
__sync_add_and_fetch(&target, 1);
}
inline void AtomicOr(volatile u32& target, u32 value)
{
__sync_or_and_fetch(&target, value);
}
// Support clang versions older than 3.4.
#if __clang__
#if !__has_feature(cxx_atomic)
template <typename T>
_Atomic(T)* ToC11Atomic(volatile T* loc)
{
return (_Atomic(T)*) loc;
}
#define __atomic_load_n(p, m) __c11_atomic_load(ToC11Atomic(p), m)
#define __atomic_store_n(p, v, m) __c11_atomic_store(ToC11Atomic(p), v, m)
#define __atomic_exchange_n(p, v, m) __c11_atomic_exchange(ToC11Atomic(p), v, m)
#endif
#endif
#ifndef __ATOMIC_RELAXED
#error __ATOMIC_RELAXED not defined; your compiler version is too old.
#endif
template <typename T>
inline T AtomicLoad(volatile T& src)
{
return __atomic_load_n(&src, __ATOMIC_RELAXED);
}
template <typename T>
inline T AtomicLoadAcquire(volatile T& src)
{
return __atomic_load_n(&src, __ATOMIC_ACQUIRE);
}
template <typename T, typename U>
inline void AtomicStore(volatile T& dest, U value)
{
__atomic_store_n(&dest, value, __ATOMIC_RELAXED);
}
template <typename T, typename U>
inline void AtomicStoreRelease(volatile T& dest, U value)
{
__atomic_store_n(&dest, value, __ATOMIC_RELEASE);
}
template <typename T, typename U>
inline T* AtomicExchangeAcquire(T* volatile& loc, U newval)
{
return __atomic_exchange_n(&loc, newval, __ATOMIC_ACQ_REL);
}
}
-101
View File
@@ -1,101 +0,0 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
// IWYU pragma: private, include "Common/Atomic.h"
#pragma once
#include <intrin.h>
#include "CommonTypes.h"
#include "CommonWindows.h"
// Atomic operations are performed in a single step by the CPU. It is
// impossible for other threads to see the operation "half-done."
//
// Some atomic operations can be combined with different types of memory
// barriers called "Acquire semantics" and "Release semantics", defined below.
//
// Acquire semantics: Future memory accesses cannot be relocated to before the
// operation.
//
// Release semantics: Past memory accesses cannot be relocated to after the
// operation.
//
// These barriers affect not only the compiler, but also the CPU.
//
// NOTE: Acquire and Release are not differentiated right now. They perform a
// full memory barrier instead of a "one-way" memory barrier. The newest
// Windows SDK has Acquire and Release versions of some Interlocked* functions.
namespace Common
{
inline void AtomicAdd(volatile u32& target, u32 value)
{
_InterlockedExchangeAdd((volatile LONG*)&target, (LONG)value);
}
inline void AtomicAnd(volatile u32& target, u32 value)
{
_InterlockedAnd((volatile LONG*)&target, (LONG)value);
}
inline void AtomicIncrement(volatile u32& target)
{
_InterlockedIncrement((volatile LONG*)&target);
}
inline void AtomicDecrement(volatile u32& target)
{
_InterlockedDecrement((volatile LONG*)&target);
}
inline void AtomicOr(volatile u32& target, u32 value)
{
_InterlockedOr((volatile LONG*)&target, (LONG)value);
}
// For the comment below to hold, better only use this with 32-bit types..
template <typename T>
inline T AtomicLoad(volatile T& src)
{
return src; // 32-bit reads are always atomic.
}
// For the comment below to hold, better only use this with 32-bit types..
template <typename T>
inline T AtomicLoadAcquire(volatile T& src)
{
T result = src; // 32-bit reads are always atomic.
_ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics.
return result;
}
// For the comment below to hold, better only use this with 32-bit types..
template <typename T, typename U>
inline void AtomicStore(volatile T& dest, U value)
{
dest = (T)value; // 32-bit writes are always atomic.
}
// For the comment below to hold, better only use this with 32-bit types..
template <typename T, typename U>
inline void AtomicStoreRelease(volatile T& dest, U value)
{
_WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics.
dest = (T)value; // 32-bit writes are always atomic
}
#ifdef __MINGW32__
void *_InterlockedExchangePointer_acq(void *volatile *, void volatile *);
#endif
template <typename T, typename U>
inline T* AtomicExchangeAcquire(T* volatile& loc, U newval)
{
return (T*)_InterlockedExchangePointer_acq((void* volatile*)&loc, (void*)newval);
}
}
-29
View File
@@ -1,29 +0,0 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#pragma once
#ifdef _WIN32
#include "Atomic_Win32.h"
#else
// GCC-compatible compiler assumed!
#include "Atomic_GCC.h"
#endif
+37 -14
View File
@@ -4,6 +4,7 @@
#pragma once
#include <cstddef>
#include "Common.h"
#include "MemoryUtil.h"
@@ -15,14 +16,13 @@
class CodeBlockCommon {
public:
CodeBlockCommon() : region(nullptr), region_size(0) {}
CodeBlockCommon() {}
virtual ~CodeBlockCommon() {}
bool IsInSpace(const u8 *ptr) {
return (ptr >= region) && (ptr < (region + region_size));
}
virtual void SetCodePtr(u8 *ptr) = 0;
virtual const u8 *GetCodePtr() const = 0;
u8 *GetBasePtr() {
@@ -33,9 +33,15 @@ public:
return ptr - region;
}
virtual const u8 *GetCodePtrFromWritablePtr(u8 *ptr) = 0;
virtual u8 *GetWritablePtrFromCodePtr(const u8 *ptr) = 0;
protected:
u8 *region;
size_t region_size;
virtual void SetCodePtr(u8 *ptr) = 0;
// Note: this should be the readable/executable side if writable is a different pointer.
u8 *region = nullptr;
size_t region_size = 0;
};
template<class T> class CodeBlock : public CodeBlockCommon, public T {
@@ -48,15 +54,16 @@ private:
virtual void PoisonMemory(int offset) = 0;
public:
CodeBlock() : writeStart_(nullptr) {}
CodeBlock() {}
virtual ~CodeBlock() { if (region) FreeCodeSpace(); }
// Call this before you generate any code.
void AllocCodeSpace(int size) {
region_size = size;
// The protection will be set to RW if PlatformIsWXExclusive.
region = (u8*)AllocateExecutableMemory(region_size);
T::SetCodePointer(region);
region = (u8 *)AllocateExecutableMemory(region_size);
writableRegion = region;
T::SetCodePointer(region, writableRegion);
}
// Always clear code space with breakpoints, so that if someone accidentally executes
@@ -104,26 +111,42 @@ public:
ProtectMemoryPages(region, region_size, MEM_PROT_READ | MEM_PROT_WRITE);
FreeMemoryPages(region, region_size);
region = nullptr;
writableRegion = nullptr;
region_size = 0;
}
void SetCodePtr(u8 *ptr) override {
T::SetCodePointer(ptr);
}
const u8 *GetCodePtr() const override {
return T::GetCodePointer();
}
void ResetCodePtr(int offset) {
T::SetCodePointer(region + offset);
void ResetCodePtr(size_t offset) {
T::SetCodePointer(region + offset, writableRegion + offset);
}
size_t GetSpaceLeft() const {
return region_size - (T::GetCodePointer() - region);
}
const u8 *GetCodePtrFromWritablePtr(u8 *ptr) override {
// So we can adjust region to writable space. Might be zero.
ptrdiff_t writable = T::GetWritableCodePtr() - T::GetCodePointer();
return ptr - writable;
}
u8 *GetWritablePtrFromCodePtr(const u8 *ptr) override {
// So we can adjust region to writable space. Might be zero.
ptrdiff_t writable = T::GetWritableCodePtr() - T::GetCodePointer();
return (u8 *)ptr + writable;
}
protected:
void SetCodePtr(u8 *ptr) override {
T::SetCodePointer(ptr, GetWritablePtrFromCodePtr(ptr));
}
private:
const uint8_t *writeStart_;
// Note: this is a readable pointer.
const uint8_t *writeStart_ = nullptr;
uint8_t *writableRegion = nullptr;
};
-3
View File
@@ -384,9 +384,6 @@
<ClInclude Include="Arm64Emitter.h" />
<ClInclude Include="ArmCommon.h" />
<ClInclude Include="ArmEmitter.h" />
<ClInclude Include="Atomics.h" />
<ClInclude Include="Atomic_GCC.h" />
<ClInclude Include="Atomic_Win32.h" />
<ClInclude Include="BitScan.h" />
<ClInclude Include="BitSet.h" />
<ClInclude Include="ColorConvNEON.h" />
-3
View File
@@ -3,9 +3,6 @@
<ItemGroup>
<ClInclude Include="stdafx.h" />
<ClInclude Include="ABI.h" />
<ClInclude Include="Atomics.h" />
<ClInclude Include="Atomic_GCC.h" />
<ClInclude Include="Atomic_Win32.h" />
<ClInclude Include="ChunkFile.h" />
<ClInclude Include="Common.h" />
<ClInclude Include="CommonFuncs.h" />
+8 -9
View File
@@ -15,7 +15,7 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <atomic>
#include <algorithm> // min
#include <string> // System: To be able to add strings with "+"
#include <math.h>
@@ -35,7 +35,6 @@
#include "util/text/utf8.h"
#include "Common.h"
#include "ConsoleListener.h" // Common
#include "Atomics.h"
#if defined(USING_WIN_UI)
const int LOG_PENDING_MAX = 120 * 10000;
@@ -49,8 +48,8 @@ HANDLE ConsoleListener::hTriggerEvent = NULL;
CRITICAL_SECTION ConsoleListener::criticalSection;
char *ConsoleListener::logPending = NULL;
volatile u32 ConsoleListener::logPendingReadPos = 0;
volatile u32 ConsoleListener::logPendingWritePos = 0;
std::atomic<u32> ConsoleListener::logPendingReadPos;
std::atomic<u32> ConsoleListener::logPendingWritePos;
#endif
ConsoleListener::ConsoleListener() : bHidden(true)
@@ -188,7 +187,7 @@ void ConsoleListener::Close()
{
if (hThread != NULL)
{
Common::AtomicStoreRelease(logPendingWritePos, (u32) -1);
logPendingWritePos.store((u32)-1, std::memory_order_release);
SetEvent(hTriggerEvent);
WaitForSingleObject(hThread, LOG_SHUTDOWN_DELAY_MS);
@@ -315,7 +314,7 @@ void ConsoleListener::LogWriterThread()
WaitForSingleObject(hTriggerEvent, INFINITE);
Sleep(LOG_LATENCY_DELAY_MS);
u32 logRemotePos = Common::AtomicLoadAcquire(logPendingWritePos);
u32 logRemotePos = logPendingWritePos.load(std::memory_order_acquire);
if (logRemotePos == (u32) -1)
break;
else if (logRemotePos == logPendingReadPos)
@@ -323,7 +322,7 @@ void ConsoleListener::LogWriterThread()
else
{
EnterCriticalSection(&criticalSection);
logRemotePos = Common::AtomicLoadAcquire(logPendingWritePos);
logRemotePos = logPendingWritePos.load(std::memory_order_acquire);
int start = 0;
if (logRemotePos < logPendingReadPos)
@@ -398,7 +397,7 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text)
}
EnterCriticalSection(&criticalSection);
u32 logWritePos = Common::AtomicLoad(logPendingWritePos);
u32 logWritePos = logPendingWritePos.load();
u32 prevLogWritePos = logWritePos;
if (logWritePos + ColorLen + Len >= LOG_PENDING_MAX)
{
@@ -448,7 +447,7 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text)
return;
}
Common::AtomicStoreRelease(logPendingWritePos, logWritePos);
logPendingWritePos.store(logWritePos, std::memory_order::memory_order_release);
LeaveCriticalSection(&criticalSection);
SetEvent(hTriggerEvent);
+5 -2
View File
@@ -17,6 +17,8 @@
#pragma once
#include <atomic>
#include "LogManager.h"
#ifdef _WIN32
@@ -44,6 +46,7 @@ public:
void Show(bool bShow);
bool Hidden() const { return bHidden; }
private:
#if defined(USING_WIN_UI)
HWND hWnd;
@@ -60,8 +63,8 @@ private:
static CRITICAL_SECTION criticalSection;
static char *logPending;
static volatile u32 logPendingReadPos;
static volatile u32 logPendingWritePos;
static std::atomic<u32> logPendingReadPos;
static std::atomic<u32> logPendingWritePos;
int openWidth_;
int openHeight_;
+1 -9
View File
@@ -36,15 +36,7 @@
#endif
static const char *validationLayers[] = {
"VK_LAYER_LUNARG_standard_validation",
/*
"VK_LAYER_GOOGLE_threading",
"VK_LAYER_LUNARG_draw_state",
"VK_LAYER_LUNARG_image",
"VK_LAYER_LUNARG_mem_tracker",
"VK_LAYER_LUNARG_object_tracker",
"VK_LAYER_LUNARG_param_checker",
*/
"VK_LAYER_KHRONOS_validation",
/*
// For layers included in the Android NDK.
"VK_LAYER_GOOGLE_threading",
+1 -1
View File
@@ -96,7 +96,7 @@ enum NormalSSEOps
};
void XEmitter::SetCodePointer(u8 *ptr)
void XEmitter::SetCodePointer(u8 *ptr, u8 *writePtr)
{
code = ptr;
}
+1 -1
View File
@@ -372,7 +372,7 @@ public:
void WriteModRM(int mod, int rm, int reg);
void WriteSIB(int scale, int index, int base);
void SetCodePointer(u8 *ptr);
void SetCodePointer(u8 *ptr, u8 *writePtr);
const u8 *GetCodePointer() const;
void ReserveCodeSpace(int bytes);
+38 -1
View File
@@ -462,6 +462,12 @@ static ConfigSetting generalSettings[] = {
ConfigSetting("GridView2", &g_Config.bGridView2, true),
ConfigSetting("GridView3", &g_Config.bGridView3, false),
ConfigSetting("ComboMode", &g_Config.iComboMode, 0),
ConfigSetting("RightAnalogUp", &g_Config.iRightAnalogUp, 0),
ConfigSetting("RightAnalogDown", &g_Config.iRightAnalogDown, 0),
ConfigSetting("RightAnalogLeft", &g_Config.iRightAnalogLeft, 0),
ConfigSetting("RightAnalogRight", &g_Config.iRightAnalogRight, 0),
ConfigSetting("RightAnalogPress", &g_Config.iRightAnalogPress, 0),
ConfigSetting("RightAnalogCustom", &g_Config.bRightAnalogCustom, false),
// "default" means let emulator decide, "" means disable.
ConfigSetting("ReportingHost", &g_Config.sReportHost, "default"),
@@ -795,7 +801,6 @@ static ConfigSetting soundSettings[] = {
ConfigSetting("Enable", &g_Config.bEnableSound, true, true, true),
ConfigSetting("AudioBackend", &g_Config.iAudioBackend, 0, true, true),
ConfigSetting("ExtraAudioBuffering", &g_Config.bExtraAudioBuffering, false, true, false),
ConfigSetting("AudioResampler", &g_Config.bAudioResampler, true, true, true),
ConfigSetting("GlobalVolume", &g_Config.iGlobalVolume, VOLUME_MAX, true, true),
ConfigSetting("SASVolume", &g_Config.iSASVolume, 8, true, true),
ConfigSetting("ATRACMP3Volume", &g_Config.iATRACMP3Volume, 8, true, true),
@@ -1206,6 +1211,12 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
}
}
auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap();
mPostShaderSetting.clear();
for (auto it : postShaderSetting) {
mPostShaderSetting[it.first] = std::stof(it.second);
}
// This caps the exponent 4 (so 16x.)
if (iAnisotropyLevel > 4) {
iAnisotropyLevel = 4;
@@ -1317,6 +1328,14 @@ void Config::Save(const char *saveReason) {
pinnedPaths->Set(keyName, vPinnedPaths[i]);
}
if (!bGameSpecific) {
IniFile::Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting");
postShaderSetting->Clear();
for (auto it = mPostShaderSetting.begin(), end = mPostShaderSetting.end(); it != end; ++it) {
postShaderSetting->Set(it->first.c_str(), it->second);
}
}
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
control->Delete("DPadRadius");
@@ -1566,6 +1585,12 @@ bool Config::saveGameConfig(const std::string &pGameId, const std::string &title
}
});
IniFile::Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting");
postShaderSetting->Clear();
for (auto it = mPostShaderSetting.begin(), end = mPostShaderSetting.end(); it != end; ++it) {
postShaderSetting->Set(it->first.c_str(), it->second);
}
KeyMap::SaveToIni(iniFile);
iniFile.Save(fullIniFilePath);
@@ -1584,6 +1609,12 @@ bool Config::loadGameConfig(const std::string &pGameId, const std::string &title
IniFile iniFile;
iniFile.Load(iniFileNameFull);
auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap();
mPostShaderSetting.clear();
for (auto it : postShaderSetting) {
mPostShaderSetting[it.first] = std::stof(it.second);
}
IterateSettings(iniFile, [](IniFile::Section *section, ConfigSetting *setting) {
if (setting->perGame_) {
setting->Get(section);
@@ -1608,6 +1639,12 @@ void Config::unloadGameConfig() {
}
});
auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap();
mPostShaderSetting.clear();
for (auto it : postShaderSetting) {
mPostShaderSetting[it.first] = std::stof(it.second);
}
LoadStandardControllerIni();
}
}
+9 -1
View File
@@ -214,6 +214,7 @@ public:
int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High
bool bHardwareTessellation;
std::string sPostShaderName; // Off for off.
std::map<std::string, float> mPostShaderSetting;
bool bGfxDebugOutput;
bool bGfxDebugSplitSubmit;
int iInflightFrames;
@@ -269,7 +270,6 @@ public:
bool bShowDebugStats;
bool bShowAudioDebug;
bool bShowGpuProfile;
bool bAudioResampler;
//Analog stick tilting
//the base x and y tilt. this inclination is treated as (0,0) and the tilt input
@@ -296,6 +296,14 @@ public:
//Combo key screen flag
int iComboMode;
// Right analog binding
int iRightAnalogUp;
int iRightAnalogDown;
int iRightAnalogLeft;
int iRightAnalogRight;
int iRightAnalogPress;
bool bRightAnalogCustom;
// Disable diagonals
bool bDisableDpadDiagonals;
bool bGamepadOnlyFocused;
+5 -6
View File
@@ -15,7 +15,7 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <atomic>
#include <vector>
#include <cstdio>
#include <mutex>
@@ -24,7 +24,6 @@
#include "profiler/profiler.h"
#include "Common/MsgHandler.h"
#include "Common/Atomics.h"
#include "Core/CoreTiming.h"
#include "Core/Core.h"
#include "Core/Config.h"
@@ -76,7 +75,7 @@ Event *eventPool = 0;
Event *eventTsPool = 0;
int allocatedTsEvents = 0;
// Optimization to skip MoveEvents when possible.
volatile u32 hasTsEvents = 0;
std::atomic<u32> hasTsEvents;
// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block
// as we can already reach that structure through a register.
@@ -255,7 +254,7 @@ void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata
tsLast->next = ne;
tsLast = ne;
Common::AtomicStoreRelease(hasTsEvents, 1);
hasTsEvents.store(1, std::memory_order::memory_order_release);
}
// Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread
@@ -535,7 +534,7 @@ void ProcessFifoWaitEvents()
void MoveEvents()
{
Common::AtomicStoreRelease(hasTsEvents, 0);
hasTsEvents.store(0, std::memory_order::memory_order_release);
std::lock_guard<std::mutex> lk(externalEventLock);
// Move events from async queue into main queue
@@ -579,7 +578,7 @@ void Advance()
globalTimer += cyclesExecuted;
currentMIPS->downcount = slicelength;
if (Common::AtomicLoadAcquire(hasTsEvents))
if (hasTsEvents.load(std::memory_order_acquire))
MoveEvents();
ProcessFifoWaitEvents();
-1
View File
@@ -766,7 +766,6 @@ void PSPOskDialog::RenderKeyboard()
float previewLeftSide = (480.0f - (12.0f * drawLimit)) / 2.0f;
float title = (480.0f - (0.5f * drawLimit)) / 2.0f;
PPGeDrawText(oskDesc.c_str(), title , 20, PPGE_ALIGN_CENTER, 0.5f, CalcFadedColor(0xFFFFFFFF));
std::u16string result;
+3 -6
View File
@@ -21,7 +21,6 @@
#include "Common/CommonTypes.h"
#include "Common/ChunkFile.h"
#include "Common/FixedSizeQueue.h"
#include "Common/Atomics.h"
#ifdef _M_SSE
#include <emmintrin.h>
@@ -47,7 +46,6 @@
#include "Core/Util/AudioFormat.h"
StereoResampler resampler;
AudioDebugStats g_AudioDebugStats;
// Should be used to lock anything related to the outAudioQueue.
// atomic locks are used on the lock. TODO: make this lock-free
@@ -100,7 +98,7 @@ static void __AudioCPUMHzChange() {
void __AudioInit() {
memset(&g_AudioDebugStats, 0, sizeof(g_AudioDebugStats));
resampler.ResetStatCounters();
mixFrequency = 44100;
srcFrequency = 0;
@@ -459,9 +457,8 @@ int __AudioMix(short *outstereo, int numFrames, int sampleRate) {
return resampler.Mix(outstereo, numFrames, false, sampleRate);
}
const AudioDebugStats *__AudioGetDebugStats() {
resampler.GetAudioDebugStats(&g_AudioDebugStats);
return &g_AudioDebugStats;
void __AudioGetDebugStats(char *buf, size_t bufSize) {
resampler.GetAudioDebugStats(buf, bufSize);
}
void __PushExternalAudio(const s32 *audio, int numSamples) {
+2 -1
View File
@@ -26,6 +26,7 @@ struct AudioDebugStats {
int underrunCount;
int overrunCount;
int instantSampleRate;
int targetSampleRate;
int lastPushSize;
};
@@ -44,7 +45,7 @@ void __AudioWakeThreads(AudioChannel &chan, int result, int step);
void __AudioWakeThreads(AudioChannel &chan, int result);
int __AudioMix(short *outstereo, int numSamples, int sampleRate);
const AudioDebugStats *__AudioGetDebugStats();
void __AudioGetDebugStats(char *buf, size_t bufSize);
void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu!
// Audio Dumping stuff
+6 -10
View File
@@ -76,16 +76,12 @@ static u32 sceMp4GetMovieInfo(u32 mp4, u32 unknown2)
return 0;
}
static u32 sceMp4TrackSampleBufAvailableSize(u32 mp4, u32 unknown2)
{
ERROR_LOG(ME, "UNIMPL sceMp4TrackSampleBufAvailableSize(mp4 %i, unknown2 %08x)", mp4, unknown2);
return 0;
static u32 sceMp4TrackSampleBufAvailableSize(u32 mp4, u32 trackAddr, u32 writableSamplesAddr, u32 writableBytesAddr) {
return hleLogError(ME, 0, "unimplemented");
}
static u32 sceMp4Delete()
{
ERROR_LOG(ME, "UNIMPL sceMp4Delete()");
return 0;
static u32 sceMp4Delete(u32 mp4) {
return hleLogError(ME, 0, "unimplemented");
}
static u32 sceMp4AacDecodeInitResource(int unknown)
@@ -402,7 +398,7 @@ const HLEFunction sceMp4[] =
{0X68651CBC, &WrapU_V<sceMp4Init>, "sceMp4Init", 'x', "" },
{0X9042B257, &WrapU_V<sceMp4Finish>, "sceMp4Finish", 'x', "" },
{0XB1221EE7, &WrapU_UUUU<sceMp4Create>, "sceMp4Create", 'x', "xxxx" },
{0X538C2057, &WrapU_V<sceMp4Delete>, "sceMp4Delete", 'x', "" },
{0X538C2057, &WrapU_U<sceMp4Delete>, "sceMp4Delete", 'x', "x" },
{0X113E9E7B, &WrapU_V<sceMp4GetNumberOfMetaData>, "sceMp4GetNumberOfMetaData", 'x', "" },
{0X7443AF1D, &WrapU_UU<sceMp4GetMovieInfo>, "sceMp4GetMovieInfo", 'x', "xx" },
{0X5EB65F26, &WrapU_V<sceMp4GetNumberOfSpecificTrack>, "sceMp4GetNumberOfSpecificTrack", 'x', "" },
@@ -418,7 +414,7 @@ const HLEFunction sceMp4[] =
{0XF7C51EC1, &WrapU_V<sceMp4GetSampleInfo>, "sceMp4GetSampleInfo", 'x', "" },
{0X74A1CA3E, &WrapU_V<sceMp4SearchSyncSampleNum>, "sceMp4SearchSyncSampleNum", 'x', "" },
{0XD8250B75, nullptr, "sceMp4PutSampleNum", '?', "" },
{0X8754ECB8, &WrapU_UU<sceMp4TrackSampleBufAvailableSize>, "sceMp4TrackSampleBufAvailableSize", 'x', "xx" },
{0X8754ECB8, &WrapU_UUUU<sceMp4TrackSampleBufAvailableSize>, "sceMp4TrackSampleBufAvailableSize", 'x', "xppp" },
{0X31BCD7E0, nullptr, "sceMp4TrackSampleBufPut", '?', "" },
{0X5601A6F0, &WrapU_UUUU<sceMp4GetAacAu>, "sceMp4GetAacAu", 'x', "xxxx" },
{0X7663CB5C, &WrapU_UUUUU<sceMp4AacDecode>, "sceMp4AacDecode", 'x', "xxxxx" },
+2 -2
View File
@@ -1441,8 +1441,6 @@ static int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts)
return ERROR_PSMFPLAYER_INVALID_PARAM;
}
WARN_LOG(ME, "scePsmfPlayerStart(%08x, %08x, %d)", psmfPlayer, psmfPlayerData, initPts);
psmfplayer->AbortFinish();
psmfplayer->mediaengine->setVideoStream(playerData->videoStreamNum);
psmfplayer->videoCodec = playerData->videoCodec;
@@ -1455,6 +1453,8 @@ static int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts)
psmfplayer->playMode = playerData->playMode;
psmfplayer->playSpeed = playerData->playSpeed;
WARN_LOG(ME, "scePsmfPlayerStart(%08x, %08x, %d,(mode %d, speed %d)", psmfPlayer, psmfPlayerData, initPts, playerData->playMode, playerData->playSpeed);
// Does not alter current pts, it just catches up when Update()/etc. get there.
int delayUs = psmfplayer->status == PSMF_PLAYER_STATUS_PLAYING ? 3000 : 0;
+147 -100
View File
@@ -19,23 +19,27 @@
// 16 bit Stereo
#define MAX_SAMPLES_DEFAULT (4096) // 2*64ms - had to double it for nVidia Shield which has huge buffers
#define MAX_SAMPLES_EXTRA (8192)
// These must be powers of 2.
#define MAX_BUFSIZE_DEFAULT (4096) // 2*64ms - had to double it for nVidia Shield which has huge buffers
#define MAX_BUFSIZE_EXTRA (8192)
#define LOW_WATERMARK_DEFAULT 1680 // 40 ms
#define LOW_WATERMARK_EXTRA 3360 // 80 ms
#define TARGET_BUFSIZE_MARGIN 512
#define MAX_FREQ_SHIFT 200 // per 32000 Hz
#define TARGET_BUFSIZE_DEFAULT 1680 // 40 ms
#define TARGET_BUFSIZE_EXTRA 3360 // 80 ms
#define MAX_FREQ_SHIFT 600.0f // how far off can we be from 44100 Hz
#define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset
#define CONTROL_AVG 32
#define CONTROL_AVG 32.0f
#include <cstring>
#include <atomic>
#include "base/logging.h"
#include "base/timeutil.h"
#include "base/NativeApp.h"
#include "Common/ChunkFile.h"
#include "Common/MathUtil.h"
#include "Common/Atomics.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
#include "Core/HW/StereoResampler.h"
@@ -55,19 +59,10 @@
#endif
StereoResampler::StereoResampler()
: m_bufsize(MAX_SAMPLES_DEFAULT)
, m_lowwatermark(LOW_WATERMARK_DEFAULT)
, m_input_sample_rate(44100)
, m_indexW(0)
, m_indexR(0)
, m_numLeftI(0.0f)
, m_frac(0)
, underrunCount_(0)
, overrunCount_(0)
, sample_rate_(0.0f)
, lastBufSize_(0) {
: m_maxBufsize(MAX_BUFSIZE_DEFAULT)
, m_targetBufsize(TARGET_BUFSIZE_DEFAULT) {
// Need to have space for the worst case in case it changes.
m_buffer = new int16_t[MAX_SAMPLES_EXTRA * 2]();
m_buffer = new int16_t[MAX_BUFSIZE_EXTRA * 2]();
// Some Android devices are v-synced to non-60Hz framerates. We simply timestretch audio to fit.
// TODO: should only do this if auto frameskip is off?
@@ -75,7 +70,9 @@ StereoResampler::StereoResampler()
// If framerate is "close"...
if (refresh != 60.0f && refresh > 50.0f && refresh < 70.0f) {
SetInputSampleRate((int)(44100 * (refresh / 60.0f)));
int input_sample_rate = (int)(44100 * (refresh / 60.0f));
ILOG("StereoResampler: Adjusting target sample rate to %dHz", input_sample_rate);
m_input_sample_rate = input_sample_rate;
}
UpdateBufferSize();
@@ -88,11 +85,18 @@ StereoResampler::~StereoResampler() {
void StereoResampler::UpdateBufferSize() {
if (g_Config.bExtraAudioBuffering) {
m_bufsize = MAX_SAMPLES_EXTRA;
m_lowwatermark = LOW_WATERMARK_EXTRA;
m_maxBufsize = MAX_BUFSIZE_EXTRA;
m_targetBufsize = TARGET_BUFSIZE_EXTRA;
} else {
m_bufsize = MAX_SAMPLES_DEFAULT;
m_lowwatermark = LOW_WATERMARK_DEFAULT;
m_maxBufsize = MAX_BUFSIZE_DEFAULT;
m_targetBufsize = TARGET_BUFSIZE_DEFAULT;
int systemBufsize = System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER);
if (systemBufsize > 0 && m_targetBufsize < systemBufsize + TARGET_BUFSIZE_MARGIN) {
m_targetBufsize = std::min(4096, systemBufsize + TARGET_BUFSIZE_MARGIN);
if (m_targetBufsize * 2 > MAX_BUFSIZE_DEFAULT)
m_maxBufsize = MAX_BUFSIZE_EXTRA;
}
}
}
@@ -154,70 +158,80 @@ inline void ClampBufferToS16WithVolume(s16 *out, const s32 *in, size_t size) {
}
void StereoResampler::Clear() {
memset(m_buffer, 0, m_bufsize * 2 * sizeof(int16_t));
memset(m_buffer, 0, m_maxBufsize * 2 * sizeof(int16_t));
}
// Executed from sound stream thread
// Executed from sound stream thread, pulling sound out of the buffer.
unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool consider_framelimit, int sample_rate) {
if (!samples)
return 0;
unsigned int currentSample = 0;
unsigned int currentSample;
// Cache access in non-volatile variable
// This is the only function changing the read value, so it's safe to
// cache it locally although it's written here.
// The writing pointer will be modified outside, but it will only increase,
// so we will just ignore new written data while interpolating.
// so we will just ignore new written data while interpolating (until it wraps...).
// Without this cache, the compiler wouldn't be allowed to optimize the
// interpolation loop.
u32 indexR = Common::AtomicLoad(m_indexR);
u32 indexW = Common::AtomicLoad(m_indexW);
u32 indexR = m_indexR.load();
u32 indexW = m_indexW.load();
const int INDEX_MASK = (m_bufsize * 2 - 1);
const int INDEX_MASK = (m_maxBufsize * 2 - 1);
lastBufSize_ = (indexR - m_indexW) & INDEX_MASK;
// We force on the audio resampler if the output sample rate doesn't match the input.
if (!g_Config.bAudioResampler && sample_rate == (int)m_input_sample_rate) {
for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2) {
s16 l1 = m_buffer[indexR & INDEX_MASK]; //current
s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current
samples[currentSample] = l1;
samples[currentSample + 1] = r1;
indexR += 2;
}
sample_rate_ = (float)sample_rate;
} else {
// Drift prevention mechanism
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
m_numLeftI = (numLeft + m_numLeftI*(CONTROL_AVG - 1)) / CONTROL_AVG;
float offset = (m_numLeftI - m_lowwatermark) * CONTROL_FACTOR;
if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT;
if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT;
// Drift prevention mechanism.
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
// If we had to discard samples the last frame due to underrun,
// apply an adjustment here. Otherwise we'll overestimate how many
// samples we need.
numLeft -= droppedSamples_;
droppedSamples_ = 0;
sample_rate_ = (float)(m_input_sample_rate + offset);
const u32 ratio = (u32)(65536.0 * sample_rate_ / (double)sample_rate);
// m_numLeftI here becomes a lowpass filtered version of numLeft.
m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1.0f)) / CONTROL_AVG;
// TODO: consider a higher-quality resampling algorithm.
// TODO: Add a fast path for 1:1.
for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2) {
u32 indexR2 = indexR + 2; //next sample
s16 l1 = m_buffer[indexR & INDEX_MASK]; //current
s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current
s16 l2 = m_buffer[indexR2 & INDEX_MASK]; //next
s16 r2 = m_buffer[(indexR2 + 1) & INDEX_MASK]; //next
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16;
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16;
samples[currentSample] = sampleL;
samples[currentSample + 1] = sampleR;
m_frac += ratio;
indexR += 2 * (u16)(m_frac >> 16);
m_frac &= 0xffff;
// Here we try to keep the buffer size around m_lowwatermark (which is
// really now more like desired_buffer_size) by adjusting the speed.
// Note that the speed of adjustment here does not take the buffer size into
// account. Since this is called once per "output frame", the frame size
// will affect how fast this algorithm reacts, which can't be a good thing.
float offset = (m_numLeftI - (float)m_targetBufsize) * CONTROL_FACTOR;
if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT;
if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT;
output_sample_rate_ = (float)(m_input_sample_rate + offset);
const u32 ratio = (u32)(65536.0 * output_sample_rate_ / (double)sample_rate);
ratio_ = ratio;
// TODO: consider a higher-quality resampling algorithm.
// TODO: Add a fast path for 1:1.
u32 frac = m_frac;
for (currentSample = 0; currentSample < numSamples * 2; currentSample += 2) {
if (((indexW - indexR) & INDEX_MASK) <= 2) {
// Ran out!
// int missing = numSamples * 2 - currentSample;
// ILOG("Resampler underrun: %d (numSamples: %d, currentSample: %d)", missing, numSamples, currentSample / 2);
underrunCount_++;
break;
}
u32 indexR2 = indexR + 2; //next sample
s16 l1 = m_buffer[indexR & INDEX_MASK]; //current
s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current
s16 l2 = m_buffer[indexR2 & INDEX_MASK]; //next
s16 r2 = m_buffer[(indexR2 + 1) & INDEX_MASK]; //next
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16;
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16;
samples[currentSample] = sampleL;
samples[currentSample + 1] = sampleR;
frac += ratio;
indexR += 2 * (frac >> 16);
frac &= 0xffff;
}
m_frac = frac;
int realSamples = currentSample;
if (currentSample < numSamples * 2)
underrunCount_++;
// Let's not count the underrun padding here.
outputSampleCount_ += currentSample / 2;
// Padding with the last value to reduce clicking
short s[2];
@@ -229,64 +243,97 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool
}
// Flush cached variable
Common::AtomicStore(m_indexR, indexR);
m_indexR.store(indexR);
//if (realSamples != numSamples * 2) {
// ILOG("Underrun! %i / %i", realSamples / 2, numSamples);
//}
lastBufSize_ = (m_indexW - m_indexR) & INDEX_MASK;
return realSamples / 2;
// TODO: What should we actually return here?
return currentSample / 2;
}
void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) {
// Executes on the emulator thread, pushing sound into the buffer.
void StereoResampler::PushSamples(const s32 *samples, unsigned int numSamples) {
inputSampleCount_ += numSamples;
UpdateBufferSize();
const int INDEX_MASK = (m_bufsize * 2 - 1);
const int INDEX_MASK = (m_maxBufsize * 2 - 1);
// Cache access in non-volatile variable
// indexR isn't allowed to cache in the audio throttling loop as it
// needs to get updates to not deadlock.
u32 indexW = Common::AtomicLoad(m_indexW);
u32 indexW = m_indexW.load();
u32 cap = m_bufsize * 2;
// If unthottling, no need to fill up the entire buffer, just screws up timing after releasing unthrottle.
if (PSP_CoreParameter().unthrottle)
cap = m_lowwatermark * 2;
u32 cap = m_maxBufsize * 2;
// If unthrottling, no need to fill up the entire buffer, just screws up timing after releasing unthrottle.
if (PSP_CoreParameter().unthrottle) {
cap = m_targetBufsize * 2;
}
// Check if we have enough free space
// indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW
if (num_samples * 2 + ((indexW - Common::AtomicLoad(m_indexR)) & INDEX_MASK) >= cap) {
if (!PSP_CoreParameter().unthrottle)
if (numSamples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= cap) {
if (!PSP_CoreParameter().unthrottle) {
overrunCount_++;
}
// TODO: "Timestretch" by doing a windowed overlap with existing buffer content?
return;
}
int over_bytes = num_samples * 4 - (m_bufsize * 2 - (indexW & INDEX_MASK)) * sizeof(short);
int over_bytes = numSamples * 4 - (m_maxBufsize * 2 - (indexW & INDEX_MASK)) * sizeof(short);
if (over_bytes > 0) {
ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, (num_samples * 4 - over_bytes) / 2);
ClampBufferToS16WithVolume(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes / 2);
ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, (numSamples * 4 - over_bytes) / 2);
ClampBufferToS16WithVolume(&m_buffer[0], samples + (numSamples * 4 - over_bytes) / sizeof(short), over_bytes / 2);
} else {
ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 2);
ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, numSamples * 2);
}
Common::AtomicAdd(m_indexW, num_samples * 2);
lastPushSize_ = num_samples;
m_indexW += numSamples * 2;
lastPushSize_ = numSamples;
}
void StereoResampler::GetAudioDebugStats(AudioDebugStats *stats) {
stats->buffered = lastBufSize_;
stats->underrunCount += underrunCount_;
void StereoResampler::GetAudioDebugStats(char *buf, size_t bufSize) {
double elapsed = real_time_now() - startTime_;
double effective_input_sample_rate = (double)inputSampleCount_ / elapsed;
double effective_output_sample_rate = (double)outputSampleCount_ / elapsed;
snprintf(buf, bufSize,
"Audio buffer: %d/%d (target: %d)\n"
"Filtered: %0.2f\n"
"Underruns: %d\n"
"Overruns: %d\n"
"Sample rate: %d (input: %d)\n"
"Effective input sample rate: %0.2f\n"
"Effective output sample rate: %0.2f\n"
"Push size: %d\n"
"Ratio: %0.6f\n",
lastBufSize_,
m_maxBufsize,
m_targetBufsize,
m_numLeftI,
underrunCountTotal_,
overrunCountTotal_,
(int)output_sample_rate_,
m_input_sample_rate,
effective_input_sample_rate,
effective_output_sample_rate,
lastPushSize_,
(float)ratio_ / 65536.0f);
underrunCountTotal_ += underrunCount_;
overrunCountTotal_ += overrunCount_;
underrunCount_ = 0;
stats->overrunCount += overrunCount_;
overrunCount_ = 0;
stats->watermark = m_lowwatermark;
stats->bufsize = m_bufsize * 2;
stats->instantSampleRate = (int)sample_rate_;
stats->lastPushSize = lastPushSize_;
// Use this to remove the bias from the startup.
// if (elapsed > 3.0) {
//ResetStatCounters();
// }
}
void StereoResampler::SetInputSampleRate(unsigned int rate) {
m_input_sample_rate = rate;
void StereoResampler::ResetStatCounters() {
underrunCount_ = 0;
overrunCount_ = 0;
underrunCountTotal_ = 0;
overrunCountTotal_ = 0;
inputSampleCount_ = 0;
outputSampleCount_ = 0;
startTime_ = real_time_now();
}
void StereoResampler::DoState(PointerWrap &p) {
+30 -16
View File
@@ -19,7 +19,8 @@
#pragma once
#include <string>
#include <cstdint>
#include <atomic>
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
@@ -42,23 +43,36 @@ public:
void DoState(PointerWrap &p);
void GetAudioDebugStats(AudioDebugStats *stats);
void GetAudioDebugStats(char *buf, size_t bufSize);
void ResetStatCounters();
protected:
private:
void UpdateBufferSize();
void SetInputSampleRate(unsigned int rate);
int m_bufsize;
int m_lowwatermark;
unsigned int m_input_sample_rate;
int m_maxBufsize;
int m_targetBufsize;
unsigned int m_input_sample_rate = 44100;
int16_t *m_buffer;
volatile u32 m_indexW;
volatile u32 m_indexR;
float m_numLeftI;
u32 m_frac;
int underrunCount_;
int overrunCount_;
float sample_rate_;
int lastBufSize_;
int lastPushSize_;
std::atomic<u32> m_indexW;
std::atomic<u32> m_indexR;
float m_numLeftI = 0.0f;
u32 m_frac = 0;
float output_sample_rate_ = 0.0;
int lastBufSize_ = 0;
int lastPushSize_ = 0;
u32 ratio_ = 0;
int underrunCount_ = 0;
int overrunCount_ = 0;
int underrunCountTotal_ = 0;
int overrunCountTotal_ = 0;
int droppedSamples_ = 0;
int64_t inputSampleCount_ = 0;
int64_t outputSampleCount_ = 0;
double startTime_ = 0.0;
};
+3 -3
View File
@@ -304,17 +304,17 @@ const u8 *ArmJit::DoJit(u32 em_address, JitBlock *b)
JumpTarget backJump = GetCodePtr();
gpr.SetRegImm(R0, js.blockStart);
B((const void *)outerLoopPCInR0);
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
SetCC(CC_LT);
B(backJump);
SetCC(CC_AL);
} else if (jo.useForwardJump) {
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
SetCC(CC_LT);
bail = B();
SetCC(CC_AL);
} else {
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
SetCC(CC_LT);
gpr.SetRegImm(R0, js.blockStart);
B((const void *)outerLoopPCInR0);
+5 -5
View File
@@ -300,13 +300,13 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) {
const u8 *backJump = GetCodePtr();
MOVI2R(SCRATCH1, js.blockStart);
B((const void *)outerLoopPCInSCRATCH1);
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
B(CC_LT, backJump);
} else if (jo.useForwardJump) {
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
bail = B(CC_LT);
} else if (jo.enableBlocklink) {
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
MOVI2R(SCRATCH1, js.blockStart);
FixupBranch skip = B(CC_GE);
B((const void *)outerLoopPCInSCRATCH1);
@@ -442,7 +442,7 @@ void Arm64Jit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) {
if (PlatformIsWXExclusive()) {
ProtectMemoryPages(exitPoint, 32, MEM_PROT_READ | MEM_PROT_WRITE);
}
ARM64XEmitter emit(exitPoint);
ARM64XEmitter emit(GetCodePtrFromWritablePtr(exitPoint), exitPoint);
emit.B(checkedEntry);
// TODO: Write stuff after, convering up the now-unused instructions.
emit.FlushIcache();
@@ -459,7 +459,7 @@ void Arm64Jit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
ProtectMemoryPages(checkedEntry, 16, MEM_PROT_READ | MEM_PROT_WRITE);
}
ARM64XEmitter emit(checkedEntry);
ARM64XEmitter emit(GetCodePtrFromWritablePtr(checkedEntry), checkedEntry);
emit.MOVI2R(SCRATCH1, originalAddress);
emit.STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc));
emit.B(MIPSComp::jit->GetDispatcher());
+3 -2
View File
@@ -186,7 +186,7 @@ void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, cons
// Make binary searches and stuff work ok
b.normalEntry = codePtr;
b.checkedEntry = (u8 *)codePtr; // Ugh, casting away const..
b.checkedEntry = codePtr;
proxyBlockMap_.insert(std::make_pair(startAddress, num_blocks_));
AddBlockMap(num_blocks_);
@@ -541,7 +541,8 @@ void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {
if (b->checkedEntry) {
// We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive.
if (type != DestroyType::CLEAR) {
MIPSComp::jit->UnlinkBlock(b->checkedEntry, b->originalAddress);
u8 *writableEntry = codeBlock_->GetWritablePtrFromCodePtr(b->checkedEntry);
MIPSComp::jit->UnlinkBlock(writableEntry, b->originalAddress);
}
} else {
ERROR_LOG(JIT, "Unlinking block with no entry: %08x (%d)", b->originalAddress, block_num);
+1 -1
View File
@@ -59,7 +59,7 @@ enum class DestroyType {
struct JitBlock {
bool ContainsAddress(u32 em_address);
u8 *checkedEntry; // not const, may need to write through this to unlink
const u8 *checkedEntry; // const, we have to translate to writable.
const u8 *normalEntry;
u8 *exitPtrs[MAX_JIT_BLOCK_EXITS]; // to be able to rewrite the exit jump
+1 -1
View File
@@ -335,7 +335,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b) {
js.PrefixStart();
// We add a check before the block, used when entering from a linked block.
b->checkedEntry = (u8 *)GetCodePtr();
b->checkedEntry = GetCodePtr();
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
FixupBranch skip = J_CC(CC_NS);
MOV(32, MIPSSTATE_VAR(pc), Imm32(js.blockStart));
-1
View File
@@ -16,7 +16,6 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/Common.h"
#include "Common/Atomics.h"
#include "Core/Core.h"
#include "Core/MemMap.h"
+19 -15
View File
@@ -71,14 +71,16 @@ namespace SaveState
struct Operation
{
Operation(OperationType t, const std::string &f, Callback cb, void *cbUserData_)
: type(t), filename(f), callback(cb), cbUserData(cbUserData_)
// The slot number is for visual purposes only. Set to -1 for operations where we don't display a message for example.
Operation(OperationType t, const std::string &f, int slot_, Callback cb, void *cbUserData_)
: type(t), filename(f), callback(cb), slot(slot_), cbUserData(cbUserData_)
{
}
OperationType type;
std::string filename;
Callback callback;
int slot;
void *cbUserData;
};
@@ -317,29 +319,29 @@ namespace SaveState
Core_UpdateSingleStep();
}
void Load(const std::string &filename, Callback callback, void *cbUserData)
void Load(const std::string &filename, int slot, Callback callback, void *cbUserData)
{
Enqueue(Operation(SAVESTATE_LOAD, filename, callback, cbUserData));
Enqueue(Operation(SAVESTATE_LOAD, filename, slot, callback, cbUserData));
}
void Save(const std::string &filename, Callback callback, void *cbUserData)
void Save(const std::string &filename, int slot, Callback callback, void *cbUserData)
{
Enqueue(Operation(SAVESTATE_SAVE, filename, callback, cbUserData));
Enqueue(Operation(SAVESTATE_SAVE, filename, slot, callback, cbUserData));
}
void Verify(Callback callback, void *cbUserData)
{
Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), callback, cbUserData));
Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), -1, callback, cbUserData));
}
void Rewind(Callback callback, void *cbUserData)
{
Enqueue(Operation(SAVESTATE_REWIND, std::string(""), callback, cbUserData));
Enqueue(Operation(SAVESTATE_REWIND, std::string(""), -1, callback, cbUserData));
}
void SaveScreenshot(const std::string &filename, Callback callback, void *cbUserData)
{
Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, callback, cbUserData));
Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, -1, callback, cbUserData));
}
bool CanRewind()
@@ -436,7 +438,7 @@ namespace SaveState
{
std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
if (!fn.empty()) {
Load(fn, callback, cbUserData);
Load(fn, slot, callback, cbUserData);
} else {
auto sy = GetI18NCategory("System");
if (callback)
@@ -493,7 +495,7 @@ namespace SaveState
RenameIfExists(shot, shotUndo);
}
SaveScreenshot(shot, Callback(), 0);
Save(fn + ".tmp", renameCallback, cbUserData);
Save(fn + ".tmp", slot, renameCallback, cbUserData);
} else {
auto sy = GetI18NCategory("System");
if (callback)
@@ -738,6 +740,8 @@ namespace SaveState
if (strlen(i18nSaveFailure) == 0)
i18nSaveFailure = sc->T("Failed to save state");
std::string slot_prefix = op.slot >= 0 ? StringFromFormat("(%d) ", op.slot + 1) : "";
switch (op.type)
{
case SAVESTATE_LOAD:
@@ -745,7 +749,7 @@ namespace SaveState
// Use the state's latest version as a guess for saveStateInitialGitVersion.
result = CChunkFileReader::Load(op.filename, &saveStateInitialGitVersion, state, &reason);
if (result == CChunkFileReader::ERROR_NONE) {
callbackMessage = sc->T("Loaded State");
callbackMessage = slot_prefix + sc->T("Loaded State");
callbackResult = Status::SUCCESS;
hasLoadedState = true;
@@ -754,11 +758,11 @@ namespace SaveState
// Using save states instead of saves simulates many hour play sessions.
// Sometimes this exposes game bugs that were rarely seen on real devices,
// because few people played on a real PSP for 10 hours straight.
callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs.");
callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs.");
callbackResult = Status::WARNING;
} else if (!g_Config.bHideStateWarnings && IsOldVersion()) {
// Save states also preserve bugs from old PPSSPP versions, so warn.
callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs.");
callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs.");
callbackResult = Status::WARNING;
}
@@ -795,7 +799,7 @@ namespace SaveState
}
result = CChunkFileReader::Save(op.filename, title, PPSSPP_GIT_VERSION, state);
if (result == CChunkFileReader::ERROR_NONE) {
callbackMessage = sc->T("Saved State");
callbackMessage = slot_prefix + sc->T("Saved State");
callbackResult = Status::SUCCESS;
#ifndef MOBILE_DEVICE
if (g_Config.bSaveLoadResetsAVdumping) {
+2 -2
View File
@@ -62,11 +62,11 @@ namespace SaveState
// Load the specified file into the current state (async.)
// Warning: callback will be called on a different thread.
void Load(const std::string &filename, Callback callback = Callback(), void *cbUserData = 0);
void Load(const std::string &filename, int slot, Callback callback = Callback(), void *cbUserData = 0);
// Save the current state to the specified file (async.)
// Warning: callback will be called on a different thread.
void Save(const std::string &filename, Callback callback = Callback(), void *cbUserData = 0);
void Save(const std::string &filename, int slot, Callback callback = Callback(), void *cbUserData = 0);
CChunkFileReader::Error SaveToRam(std::vector<u8> &state);
CChunkFileReader::Error LoadFromRam(std::vector<u8> &state);
+38 -8
View File
@@ -1353,11 +1353,10 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd
VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb) {
// For now we'll keep these on the same struct as the ones that can get displayed
// (and blatantly copy work already done above while at it).
VirtualFramebuffer *nvfb = 0;
VirtualFramebuffer *nvfb = nullptr;
// We maintain a separate vector of framebuffer objects for blitting.
for (size_t i = 0; i < bvfbs_.size(); ++i) {
VirtualFramebuffer *v = bvfbs_[i];
for (VirtualFramebuffer *v : bvfbs_) {
if (v->fb_address == vfb->fb_address && v->format == vfb->format) {
if (v->bufferWidth == vfb->bufferWidth && v->bufferHeight == vfb->bufferHeight) {
nvfb = v;
@@ -1407,6 +1406,34 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram
return nvfb;
}
bool FramebufferManagerCommon::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
// When updating VRAM, it need to be exact format.
if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) {
switch (nvfb->format) {
case GE_FORMAT_4444:
nvfb->colorDepth = Draw::FBO_4444;
break;
case GE_FORMAT_5551:
nvfb->colorDepth = Draw::FBO_5551;
break;
case GE_FORMAT_565:
nvfb->colorDepth = Draw::FBO_565;
break;
case GE_FORMAT_8888:
default:
nvfb->colorDepth = Draw::FBO_8888;
break;
}
}
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, (Draw::FBColorDepth)nvfb->colorDepth });
if (!nvfb->fbo) {
ERROR_LOG(FRAMEBUF, "Error creating GL FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight);
return false;
}
return true;
}
void FramebufferManagerCommon::ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor) {
if (currentRenderVfb_) {
if ((currentRenderVfb_->usageFlags & FB_USAGE_DOWNLOAD_CLEAR) != 0) {
@@ -1971,8 +1998,10 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb,
PackFramebufferSync_(vfb, x, y, w, h);
} else {
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0);
PackFramebufferSync_(nvfb, x, y, w, h);
if (nvfb) {
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0);
PackFramebufferSync_(nvfb, x, y, w, h);
}
}
textureCache_->ForgetLastTexture();
@@ -2016,9 +2045,10 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0);
PackFramebufferSync_(nvfb, x, y, w, h);
if (nvfb) {
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0);
PackFramebufferSync_(nvfb, x, y, w, h);
}
textureCache_->ForgetLastTexture();
RebindFramebuffer();
+1 -1
View File
@@ -339,7 +339,7 @@ protected:
void DownloadFramebufferOnSwitch(VirtualFramebuffer *vfb);
void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp);
VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb);
virtual bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0;
virtual bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb);
virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0;
VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format);
+24 -5
View File
@@ -39,13 +39,17 @@ void LoadPostShaderInfo(std::vector<std::string> directories) {
std::vector<ShaderInfo> notVisible;
shaderInfo.clear();
ShaderInfo off;
ShaderInfo off{};
off.visible = true;
off.name = "Off";
off.section = "Off";
off.outputResolution = false;
off.isUpscalingFilter = false;
off.SSAAFilterLevel = 0;
off.requires60fps = false;
for (size_t i = 0; i < ARRAY_SIZE(off.settings); ++i) {
off.settings[i].name = "";
off.settings[i].value = 0.0f;
off.settings[i].minValue = -1.0f;
off.settings[i].maxValue = 1.0f;
off.settings[i].step = 0.01f;
}
shaderInfo.push_back(off);
auto appendShader = [&](const ShaderInfo &info) {
@@ -103,6 +107,21 @@ void LoadPostShaderInfo(std::vector<std::string> directories) {
section.Get("SSAA", &info.SSAAFilterLevel, 0);
section.Get("60fps", &info.requires60fps, false);
for (size_t i = 0; i < ARRAY_SIZE(info.settings); ++i) {
auto &setting = info.settings[i];
section.Get(StringFromFormat("SettingName%d", i + 1).c_str(), &setting.name, "");
section.Get(StringFromFormat("SettingDefaultValue%d", i + 1).c_str(), &setting.value, 0.0f);
section.Get(StringFromFormat("SettingMinValue%d", i + 1).c_str(), &setting.minValue, -1.0f);
section.Get(StringFromFormat("SettingMaxValue%d", i + 1).c_str(), &setting.maxValue, 1.0f);
section.Get(StringFromFormat("SettingStep%d", i + 1).c_str(), &setting.step, 0.01f);
// Populate the default setting value.
std::string section = StringFromFormat("%sSettingValue%d", info.section.c_str(), i + 1);
if (!setting.name.empty() && g_Config.mPostShaderSetting.find(section) == g_Config.mPostShaderSetting.end()) {
g_Config.mPostShaderSetting.insert(std::pair<std::string, float>(section, setting.value));
}
}
// Let's ignore shaders we can't support. TODO: Not a very good check
if (gl_extensions.IsGLES && !gl_extensions.GLES3) {
bool requiresIntegerSupport;
+9
View File
@@ -44,6 +44,15 @@ struct ShaderInfo {
// Force constant/max refresh for animated filters
bool requires60fps;
struct Setting {
std::string name;
float value;
float maxValue;
float minValue;
float step;
};
Setting settings[4];
// TODO: Add support for all kinds of fun options like mapping the depth buffer,
// SRGB texture reads, etc.
+7 -1
View File
@@ -158,6 +158,11 @@ void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int buffer
// The shader translator tacks this onto our shaders, if we don't set it they render garbage.
uniforms->gl_HalfPixel[0] = u_pixel_delta * 0.5f;
uniforms->gl_HalfPixel[1] = v_pixel_delta * 0.5f;
uniforms->setting[0] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue1"];;
uniforms->setting[1] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue2"];
uniforms->setting[2] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue3"];
uniforms->setting[3] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue4"];
}
static std::string ReadShaderSrc(const std::string &filename) {
@@ -221,7 +226,8 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo *shaderInfo, const Sha
{ "u_texelDelta", 1, 1, Draw::UniformType::FLOAT2, offsetof(PostShaderUniforms, texelDelta) },
{ "u_pixelDelta", 2, 2, Draw::UniformType::FLOAT2, offsetof(PostShaderUniforms, pixelDelta) },
{ "u_time", 3, 3, Draw::UniformType::FLOAT4, offsetof(PostShaderUniforms, time) },
{ "u_video", 4, 4, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, video) },
{ "u_setting", 4, 4, Draw::UniformType::FLOAT4, offsetof(PostShaderUniforms, setting) },
{ "u_video", 5, 5, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, video) },
} };
Draw::Pipeline *pipeline = CreatePipeline({ vs, fs }, true, &postShaderDesc);
if (!pipeline)
+1
View File
@@ -31,6 +31,7 @@ struct CardboardSettings {
struct PostShaderUniforms {
float texelDelta[2]; float pixelDelta[2];
float time[4];
float setting[4];
float video; float pad[3];
// Used on Direct3D9.
float gl_HalfPixel[4];
+4 -1
View File
@@ -86,6 +86,7 @@ cbuffer data : register(b0) {
float2 u_texelDelta;
float2 u_pixelDelta;
float4 u_time;
float4 u_setting;
float u_video;
};
)";
@@ -101,6 +102,7 @@ layout (std140, set = 0, binding = 0) uniform Data {
vec2 u_texelDelta;
vec2 u_pixelDelta;
vec4 u_time;
vec4 u_setting;
float u_video;
};
)";
@@ -110,7 +112,8 @@ float4 gl_HalfPixel : register(c0);
float2 u_texelDelta : register(c1);
float2 u_pixelDelta : register(c2);
float4 u_time : register(c3);
float u_video : register(c4);
float4 u_setting : register(c4);
float u_video : register(c5);
)";
// SPIRV-Cross' HLSL output has some deficiencies we need to work around.
+1 -1
View File
@@ -285,7 +285,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
if (!CompileStep(dec, i)) {
EndWrite();
// Reset the code ptr and return zero to indicate that we failed.
SetCodePtr(const_cast<u8 *>(start));
ResetCodePtr(GetOffset(start));
char temp[1024] = {0};
dec.ToString(temp);
INFO_LOG(G3D, "Could not compile vertex decoder: %s", temp);
+1 -1
View File
@@ -248,7 +248,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
if (!CompileStep(dec, i)) {
EndWrite();
// Reset the code ptr (effectively undoing what we generated) and return zero to indicate that we failed.
SetCodePtr(const_cast<u8 *>(start));
ResetCodePtr(GetOffset(start));
char temp[1024] = {0};
dec.ToString(temp);
ERROR_LOG(G3D, "Could not compile vertex decoder, failed at step %d: %s", i, temp);
+1 -1
View File
@@ -251,7 +251,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int
if (!CompileStep(dec, i)) {
EndWrite();
// Reset the code ptr and return zero to indicate that we failed.
SetCodePtr(const_cast<u8 *>(start));
ResetCodePtr(GetOffset(start));
return 0;
}
}
-13
View File
@@ -366,19 +366,6 @@ void FramebufferManagerD3D11::BindFramebufferAsColorTexture(int stage, VirtualFr
}
}
bool FramebufferManagerD3D11::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
nvfb->colorDepth = Draw::FBO_8888;
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth });
if (!(nvfb->fbo)) {
ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight);
return false;
}
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
return true;
}
void FramebufferManagerD3D11::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
// Nothing to do here.
}
-1
View File
@@ -60,7 +60,6 @@ protected:
// Used by ReadFramebufferToMemory and later framebuffer block copies
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override;
bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
private:
-13
View File
@@ -379,19 +379,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
}
}
bool FramebufferManagerDX9::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
nvfb->colorDepth = Draw::FBO_8888;
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth });
if (!(nvfb->fbo)) {
ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight);
return false;
}
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
return true;
}
void FramebufferManagerDX9::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
// Nothing to do here.
}
-1
View File
@@ -71,7 +71,6 @@ protected:
// Used by ReadFramebufferToMemory and later framebuffer block copies
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override;
bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
private:
+1 -2
View File
@@ -93,8 +93,7 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int
DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address);
// TODO: On desktop, we can just directly download, but for now testing.
const bool useColorPath = true; // gl_extensions.IsGLES;
const bool useColorPath = gl_extensions.IsGLES;
bool format16Bit = false;
if (useColorPath) {
+1 -1
View File
@@ -426,7 +426,7 @@ void DrawEngineGLES::DoFlush() {
// for a very minor speed boost.
if (useElements) {
size_t esz = sizeof(short) * indexGen.VertexCount();
vai->ebo = render_->CreateBuffer(GL_ARRAY_BUFFER, esz, GL_STATIC_DRAW);
vai->ebo = render_->CreateBuffer(GL_ELEMENT_ARRAY_BUFFER, esz, GL_STATIC_DRAW);
render_->BufferSubdata(vai->ebo, 0, esz, (uint8_t *)decIndex, false);
} else {
vai->ebo = 0;
+1 -29
View File
@@ -313,39 +313,11 @@ void FramebufferManagerGLES::BindFramebufferAsColorTexture(int stage, VirtualFra
}
}
bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
// When updating VRAM, it need to be exact format.
if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) {
switch (nvfb->format) {
case GE_FORMAT_4444:
nvfb->colorDepth = Draw::FBO_4444;
break;
case GE_FORMAT_5551:
nvfb->colorDepth = Draw::FBO_5551;
break;
case GE_FORMAT_565:
nvfb->colorDepth = Draw::FBO_565;
break;
case GE_FORMAT_8888:
default:
nvfb->colorDepth = Draw::FBO_8888;
break;
}
}
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, (Draw::FBColorDepth)nvfb->colorDepth });
if (!nvfb->fbo) {
ERROR_LOG(FRAMEBUF, "Error creating GL FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight);
return false;
}
return true;
}
void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
_assert_msg_(G3D, nvfb->fbo, "Expecting a valid nvfb in UpdateDownloadTempBuffer");
// Discard the previous contents of this buffer where possible.
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
if (gl_extensions.GLES3) {
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
} else if (gl_extensions.IsGLES) {
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
-1
View File
@@ -64,7 +64,6 @@ protected:
// Used by ReadFramebufferToMemory and later framebuffer block copies
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override;
bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
private:
+1 -1
View File
@@ -2448,7 +2448,7 @@ void GPUCommon::DoState(PointerWrap &p) {
DisplayList_v1 oldDL;
p.Do(oldDL);
// On 32-bit, they're the same, on 64-bit oldDL is bigger.
memcpy(&dls[i], &oldDL, sizeof(DisplayList));
memcpy(&dls[i], &oldDL, sizeof(DisplayList_v1));
// Fix the other fields. Let's hope context wasn't important, it was a pointer.
dls[i].context = 0;
dls[i].offsetAddr = oldDL.offsetAddr;
+2 -2
View File
@@ -79,7 +79,7 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) {
if (!Jit_ReadTextureFormat(id)) {
EndWrite();
SetCodePtr(const_cast<u8 *>(start));
ResetCodePtr(GetOffset(start));
return nullptr;
}
@@ -106,7 +106,7 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
if (!Jit_ReadTextureFormat(id)) {
EndWrite();
SetCodePtr(const_cast<u8 *>(nearest));
ResetCodePtr(GetOffset(nearest));
return nullptr;
}
-13
View File
@@ -325,19 +325,6 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V
}
}
bool FramebufferManagerVulkan::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
nvfb->colorDepth = Draw::FBO_8888;
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth });
if (!(nvfb->fbo)) {
ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight);
return false;
}
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
return true;
}
void FramebufferManagerVulkan::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
// Nothing to do here.
}
-1
View File
@@ -71,7 +71,6 @@ protected:
// Used by ReadFramebufferToMemory and later framebuffer block copies
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override;
bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
private:
+1
View File
@@ -231,6 +231,7 @@ void GPU_Vulkan::CheckGPUFeatures() {
features |= GPU_SUPPORTS_INSTANCE_RENDERING;
features |= GPU_SUPPORTS_VERTEX_TEXTURE_FETCH;
features |= GPU_SUPPORTS_TEXTURE_FLOAT;
features |= GPU_PREFER_CPU_DOWNLOAD;
if (vulkan_->GetDeviceFeatures().enabled.wideLines) {
features |= GPU_SUPPORTS_WIDE_LINES;
+15 -11
View File
@@ -48,6 +48,8 @@ static int browseFileEvent = -1;
static int browseFolderEvent = -1;
QTCamera *qtcamera = nullptr;
SDL_AudioSpec g_retFmt;
#ifdef SDL
static SDL_AudioDeviceID audioDev = 0;
@@ -56,35 +58,35 @@ extern void mixaudio(void *userdata, Uint8 *stream, int len) {
}
static void InitSDLAudioDevice() {
SDL_AudioSpec fmt, ret_fmt;
SDL_AudioSpec fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.freq = 44100;
fmt.format = AUDIO_S16;
fmt.channels = 2;
fmt.samples = 2048;
fmt.samples = 1024;
fmt.callback = &mixaudio;
fmt.userdata = nullptr;
audioDev = 0;
if (!g_Config.sAudioDevice.empty()) {
audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (audioDev <= 0) {
WLOG("Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str());
}
}
if (audioDev <= 0) {
audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
}
if (audioDev <= 0) {
ELOG("Failed to open audio: %s", SDL_GetError());
} else {
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
if (g_retFmt.samples != fmt.samples) // Notify, but still use it
ELOG("Output audio samples: %d (requested: %d)", g_retFmt.samples, fmt.samples);
if (g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) {
ELOG("Sound buffer format does not match requested format.");
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
ELOG("Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq);
ELOG("Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format);
ELOG("Output audio channels: %d (requested: %d)", g_retFmt.channels, fmt.channels);
ELOG("Provided output format does not match requirement, turning audio off");
SDL_CloseAudioDevice(audioDev);
}
@@ -146,7 +148,9 @@ std::string System_GetProperty(SystemProperty prop) {
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return 44100;
return g_retFmt.freq;
case SYSPROP_AUDIO_FRAMES_PER_BUFFER:
return g_retFmt.samples;
case SYSPROP_DEVICE_TYPE:
#if defined(__ANDROID__)
return DEVICE_TYPE_MOBILE;
+2 -2
View File
@@ -182,7 +182,7 @@ void MainWindow::lstateAct()
if (dialog.exec())
{
QStringList fileNames = dialog.selectedFiles();
SaveState::Load(fileNames[0].toStdString(), SaveStateActionFinished, this);
SaveState::Load(fileNames[0].toStdString(), -1, SaveStateActionFinished, this);
}
}
@@ -197,7 +197,7 @@ void MainWindow::sstateAct()
if (dialog.exec())
{
QStringList fileNames = dialog.selectedFiles();
SaveState::Save(fileNames[0].toStdString(), SaveStateActionFinished, this);
SaveState::Save(fileNames[0].toStdString(), -1, SaveStateActionFinished, this);
}
}
+32 -18
View File
@@ -68,6 +68,8 @@ static int g_DesktopWidth = 0;
static int g_DesktopHeight = 0;
static float g_RefreshRate = 60.f;
static SDL_AudioSpec g_retFmt;
int getDisplayNumber(void) {
int displayNumber = 0;
char * displayNumberStr;
@@ -82,21 +84,21 @@ int getDisplayNumber(void) {
return displayNumber;
}
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
NativeMix((short *)stream, len / 4);
void sdl_mixaudio_callback(void *userdata, Uint8 *stream, int len) {
NativeMix((short *)stream, len / (2 * 2));
}
static SDL_AudioDeviceID audioDev = 0;
// Must be called after NativeInit().
static void InitSDLAudioDevice(const std::string &name = "") {
SDL_AudioSpec fmt, ret_fmt;
SDL_AudioSpec fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.freq = 44100;
fmt.format = AUDIO_S16;
fmt.channels = 2;
fmt.samples = 2048;
fmt.callback = &mixaudio;
fmt.samples = 1024;
fmt.callback = &sdl_mixaudio_callback;
fmt.userdata = nullptr;
std::string startDevice = name;
@@ -106,24 +108,25 @@ static void InitSDLAudioDevice(const std::string &name = "") {
audioDev = 0;
if (!startDevice.empty()) {
audioDev = SDL_OpenAudioDevice(startDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
audioDev = SDL_OpenAudioDevice(startDevice.c_str(), 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (audioDev <= 0) {
WLOG("Failed to open audio device: %s", startDevice.c_str());
}
}
if (audioDev <= 0) {
audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
ILOG("SDL: Trying a different device");
audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
}
if (audioDev <= 0) {
ELOG("Failed to open audio: %s", SDL_GetError());
} else {
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
if (g_retFmt.samples != fmt.samples) // Notify, but still use it
ELOG("Output audio samples: %d (requested: %d)", g_retFmt.samples, fmt.samples);
if (g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) {
ELOG("Sound buffer format does not match requested format.");
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
ELOG("Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq);
ELOG("Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format);
ELOG("Output audio channels: %d (requested: %d)", g_retFmt.channels, fmt.channels);
ELOG("Provided output format does not match requirement, turning audio off");
SDL_CloseAudioDevice(audioDev);
}
@@ -319,7 +322,9 @@ std::string System_GetProperty(SystemProperty prop) {
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return 44100;
return g_retFmt.freq;
case SYSPROP_AUDIO_FRAMES_PER_BUFFER:
return g_retFmt.samples;
case SYSPROP_DEVICE_TYPE:
#if defined(MOBILE_DEVICE)
return DEVICE_TYPE_MOBILE;
@@ -474,6 +479,8 @@ int main(int argc, char *argv[]) {
printf("DEBUG: Vulkan is not available, not using Vulkan.\n");
}
SDL_version compiled;
SDL_version linked;
int set_xres = -1;
int set_yres = -1;
int w = 0, h = 0;
@@ -531,6 +538,15 @@ int main(int argc, char *argv[]) {
}
}
SDL_VERSION(&compiled);
SDL_GetVersion(&linked);
printf("Info: We compiled against SDL version %d.%d.%d", compiled.major, compiled.minor, compiled.patch);
if (compiled.minor != linked.minor || compiled.patch != linked.patch) {
printf(", but we are linking against SDL version %d.%d.%d., be aware that this can lead to unexpected behaviors\n", linked.major, linked.minor, linked.patch);
} else {
printf(" and we are linking against SDL version %d.%d.%d. :)\n", linked.major, linked.minor, linked.patch);
}
// Get the video info before doing anything else, so we don't get skewed resolution results.
// TODO: support multiple displays correctly
SDL_DisplayMode displayMode;
@@ -842,6 +858,7 @@ int main(int argc, char *argv[]) {
NativeKey(key);
break;
}
#if !SDL_VERSION_ATLEAST(2, 0, 10)
// This behavior doesn't feel right on a macbook with a touchpad.
#if !PPSSPP_PLATFORM(MAC)
case SDL_FINGERMOTION:
@@ -851,7 +868,6 @@ int main(int argc, char *argv[]) {
touchEvent.motion.type = SDL_MOUSEMOTION;
touchEvent.motion.timestamp = event.tfinger.timestamp;
touchEvent.motion.windowID = SDL_GetWindowID(window);
touchEvent.motion.which = SDL_TOUCH_MOUSEID;
touchEvent.motion.state = SDL_GetMouseState(NULL, NULL);
touchEvent.motion.x = event.tfinger.x * w;
touchEvent.motion.y = event.tfinger.y * h;
@@ -868,7 +884,6 @@ int main(int argc, char *argv[]) {
touchEvent.button.type = SDL_MOUSEBUTTONDOWN;
touchEvent.button.timestamp = SDL_GetTicks();
touchEvent.button.windowID = SDL_GetWindowID(window);
touchEvent.button.which = SDL_TOUCH_MOUSEID;
touchEvent.button.button = SDL_BUTTON_LEFT;
touchEvent.button.state = SDL_PRESSED;
touchEvent.button.clicks = 1;
@@ -878,7 +893,6 @@ int main(int argc, char *argv[]) {
touchEvent.motion.type = SDL_MOUSEMOTION;
touchEvent.motion.timestamp = SDL_GetTicks();
touchEvent.motion.windowID = SDL_GetWindowID(window);
touchEvent.motion.which = SDL_TOUCH_MOUSEID;
touchEvent.motion.x = event.tfinger.x * w;
touchEvent.motion.y = event.tfinger.y * h;
// Any real mouse cursor should also move
@@ -897,7 +911,6 @@ int main(int argc, char *argv[]) {
touchEvent.button.type = SDL_MOUSEBUTTONUP;
touchEvent.button.timestamp = SDL_GetTicks();
touchEvent.button.windowID = SDL_GetWindowID(window);
touchEvent.button.which = SDL_TOUCH_MOUSEID;
touchEvent.button.button = SDL_BUTTON_LEFT;
touchEvent.button.state = SDL_RELEASED;
touchEvent.button.clicks = 1;
@@ -906,6 +919,7 @@ int main(int argc, char *argv[]) {
SDL_PushEvent(&touchEvent);
break;
}
#endif
#endif
case SDL_MOUSEBUTTONDOWN:
switch (event.button.button) {
+8 -3
View File
@@ -502,7 +502,7 @@ void SystemInfoScreen::CreateViews() {
deviceSpecs->Add(new ItemHeader(si->T("OS Information")));
deviceSpecs->Add(new InfoItem(si->T("Memory Page Size"), StringFromFormat(si->T("%d bytes"), GetMemoryProtectPageSize())));
deviceSpecs->Add(new InfoItem(si->T("RW/RX exclusive"), PlatformIsWXExclusive() ? di->T("Active") : di->T("Inactive")));
#ifdef ANDROID
#if PPSSPP_PLATFORM(ANDROID)
deviceSpecs->Add(new InfoItem(si->T("Sustained perf mode"), System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE) ? di->T("Supported") : di->T("Unsupported")));
#endif
@@ -512,14 +512,19 @@ void SystemInfoScreen::CreateViews() {
#endif
deviceSpecs->Add(new InfoItem(si->T("PPSSPP build"), build));
#ifdef __ANDROID__
deviceSpecs->Add(new ItemHeader(si->T("Audio Information")));
deviceSpecs->Add(new InfoItem(si->T("Sample rate"), StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE))));
deviceSpecs->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER))));
int framesPerBuffer = System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER);
if (framesPerBuffer > 0) {
deviceSpecs->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", framesPerBuffer)));
}
#if PPSSPP_PLATFORM(ANDROID)
deviceSpecs->Add(new InfoItem(si->T("Optimal sample rate"), StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE))));
deviceSpecs->Add(new InfoItem(si->T("Optimal frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER))));
#endif
deviceSpecs->Add(new ItemHeader(si->T("Display Information")));
#if PPSSPP_PLATFORM(ANDROID)
deviceSpecs->Add(new InfoItem(si->T("Native Resolution"), StringFromFormat("%dx%d",
System_GetPropertyInt(SYSPROP_DISPLAY_XRES),
System_GetPropertyInt(SYSPROP_DISPLAY_YRES))));
+3 -14
View File
@@ -412,7 +412,7 @@ void EmuScreen::sendMessage(const char *message, const char *value) {
} else if (!strcmp(message, "boot")) {
const char *ext = strrchr(value, '.');
if (ext != nullptr && !strcmp(ext, ".ppst")) {
SaveState::Load(value, &AfterStateBoot);
SaveState::Load(value, -1, &AfterStateBoot);
} else {
PSP_Shutdown();
bootPending_ = true;
@@ -1288,19 +1288,8 @@ static void DrawDebugStats(DrawBuffer *draw2d, const Bounds &bounds) {
static void DrawAudioDebugStats(DrawBuffer *draw2d, const Bounds &bounds) {
FontID ubuntu24("UBUNTU24");
char statbuf[1024] = { 0 };
const AudioDebugStats *stats = __AudioGetDebugStats();
snprintf(statbuf, sizeof(statbuf),
"Audio buffer: %d/%d (low watermark: %d)\n"
"Underruns: %d\n"
"Overruns: %d\n"
"Sample rate: %d\n"
"Push size: %d\n",
stats->buffered, stats->bufsize, stats->watermark,
stats->underrunCount,
stats->overrunCount,
stats->instantSampleRate,
stats->lastPushSize);
char statbuf[4096] = { 0 };
__AudioGetDebugStats(statbuf, sizeof(statbuf));
draw2d->SetFontScale(0.7f, 0.7f);
draw2d->DrawTextRect(ubuntu24, statbuf, bounds.x + 11, bounds.y + 31, bounds.w - 20, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT);
draw2d->DrawTextRect(ubuntu24, statbuf, bounds.x + 10, bounds.y + 30, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT);
+12 -4
View File
@@ -292,6 +292,17 @@ void GameSettingsScreen::CreateViews() {
return g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
});
auto shaderChain = GetPostShaderChain(g_Config.sPostShaderName);
for (auto shaderInfo : shaderChain) {
for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) {
auto &setting = shaderInfo->settings[i];
if (!setting.name.empty()) {
auto &value = g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderInfo->section.c_str(), i + 1)];
graphicsSettings->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, ps->T(setting.name), setting.step, screenManager()));
}
}
}
#if !defined(MOBILE_DEVICE)
graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange);
if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) {
@@ -574,10 +585,6 @@ void GameSettingsScreen::CreateViews() {
CheckBox *extraAudio = audioSettings->Add(new CheckBox(&g_Config.bExtraAudioBuffering, a->T("AudioBufferingForBluetooth", "Bluetooth-friendly buffer (slower)")));
extraAudio->SetEnabledPtr(&g_Config.bEnableSound);
//#endif
if (System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE) == 44100) {
CheckBox *resampling = audioSettings->Add(new CheckBox(&g_Config.bAudioResampler, a->T("Audio sync", "Audio sync (resampling)")));
resampling->SetEnabledPtr(&g_Config.bEnableSound);
}
PopupSliderChoice *sasVol = audioSettings->Add(new PopupSliderChoice(&g_Config.iSASVolume, 0, MAX_CONFIG_VOLUME, a->T("SAS volume"), screenManager()));
sasVol->SetEnabledPtr(&g_Config.bEnableSound);
@@ -1480,6 +1487,7 @@ UI::EventReturn GameSettingsScreen::OnPostProcShader(UI::EventParams &e) {
UI::EventReturn GameSettingsScreen::OnPostProcShaderChange(UI::EventParams &e) {
NativeMessageReceived("gpu_resized", "");
RecreateViews(); // Update setting name
return UI::EVENT_DONE;
}
+138 -2
View File
@@ -490,6 +490,138 @@ void PSPStick::ProcessTouch(float x, float y, bool down) {
}
}
PSPCustomStick::PSPCustomStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams)
: PSPStick(bgImg, stickImg, stickDownImg, -1, scale, layoutParams) {
}
void PSPCustomStick::Draw(UIContext &dc) {
float opacity = GetButtonOpacity();
if (opacity <= 0.0f)
return;
if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2) {
opacity *= 1.35f;
}
uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f);
uint32_t color = colorAlpha(0x808080, opacity);
if (centerX_ < 0.0f) {
centerX_ = bounds_.centerX();
centerY_ = bounds_.centerY();
}
float stickX = centerX_;
float stickY = centerY_;
float dx, dy;
dx = posX_;
dy = -posY_;
dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_)
dc.Draw()->DrawImage(stickDownImg_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_, downBg, ALIGN_CENTER);
dc.Draw()->DrawImage(stickImageIndex_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_, colorBg, ALIGN_CENTER);
}
void PSPCustomStick::Touch(const TouchInput &input) {
GamepadView::Touch(input);
if (input.flags & TOUCH_RELEASE_ALL) {
dragPointerId_ = -1;
centerX_ = bounds_.centerX();
centerY_ = bounds_.centerY();
posX_ = 0.0f;
posY_ = 0.0f;
return;
}
if (input.flags & TOUCH_DOWN) {
if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y)) {
if (g_Config.bAutoCenterTouchAnalog) {
centerX_ = input.x;
centerY_ = input.y;
} else {
centerX_ = bounds_.centerX();
centerY_ = bounds_.centerY();
}
dragPointerId_ = input.id;
ProcessTouch(input.x, input.y, true);
}
}
if (input.flags & TOUCH_MOVE) {
if (input.id == dragPointerId_) {
ProcessTouch(input.x, input.y, true);
}
}
if (input.flags & TOUCH_UP) {
if (input.id == dragPointerId_) {
dragPointerId_ = -1;
centerX_ = bounds_.centerX();
centerY_ = bounds_.centerY();
ProcessTouch(input.x, input.y, false);
}
}
}
void PSPCustomStick::ProcessTouch(float x, float y, bool down) {
static const int button[16] = {CTRL_LTRIGGER, CTRL_RTRIGGER, CTRL_SQUARE, CTRL_TRIANGLE, CTRL_CIRCLE, CTRL_CROSS, CTRL_UP, CTRL_DOWN, CTRL_LEFT, CTRL_RIGHT, CTRL_START, CTRL_SELECT};
if (down && centerX_ >= 0.0f) {
float inv_stick_size = 1.0f / (stick_size_ * scale_);
float dx = (x - centerX_) * inv_stick_size;
float dy = (y - centerY_) * inv_stick_size;
dx = std::min(1.0f, std::max(-1.0f, dx));
dy = std::min(1.0f, std::max(-1.0f, dy));
if (g_Config.iRightAnalogRight != 0) {
if (dx > 0.5f)
__CtrlButtonDown(button[g_Config.iRightAnalogRight-1]);
else
__CtrlButtonUp(button[g_Config.iRightAnalogRight-1]);
}
if (g_Config.iRightAnalogLeft != 0) {
if (dx < -0.5f)
__CtrlButtonDown(button[g_Config.iRightAnalogLeft-1]);
else
__CtrlButtonUp(button[g_Config.iRightAnalogLeft-1]);
}
if (g_Config.iRightAnalogUp != 0) {
if (dy < -0.5f)
__CtrlButtonDown(button[g_Config.iRightAnalogUp-1]);
else
__CtrlButtonUp(button[g_Config.iRightAnalogUp-1]);
}
if (g_Config.iRightAnalogDown != 0) {
if (dy > 0.5f)
__CtrlButtonDown(button[g_Config.iRightAnalogDown-1]);
else
__CtrlButtonUp(button[g_Config.iRightAnalogDown-1]);
}
if (g_Config.iRightAnalogPress != 0)
__CtrlButtonDown(button[g_Config.iRightAnalogPress-1]);
posX_ = dx;
posY_ = dy;
} else {
if (g_Config.iRightAnalogUp != 0)
__CtrlButtonUp(button[g_Config.iRightAnalogUp-1]);
if (g_Config.iRightAnalogDown != 0)
__CtrlButtonUp(button[g_Config.iRightAnalogDown-1]);
if (g_Config.iRightAnalogLeft != 0)
__CtrlButtonUp(button[g_Config.iRightAnalogLeft-1]);
if (g_Config.iRightAnalogRight != 0)
__CtrlButtonUp(button[g_Config.iRightAnalogRight-1]);
if (g_Config.iRightAnalogPress != 0)
__CtrlButtonUp(button[g_Config.iRightAnalogPress-1]);
posX_ = 0.0f;
posY_ = 0.0f;
}
}
void InitPadLayout(float xres, float yres, float globalScale) {
const float scale = globalScale;
const int halfW = xres / 2;
@@ -720,8 +852,12 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) {
if (g_Config.touchAnalogStick.show)
root->Add(new PSPStick(stickBg, stickImage, ImageID("I_STICK"), 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick)));
if (g_Config.touchRightAnalogStick.show)
root->Add(new PSPStick(stickBg, stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
if (g_Config.touchRightAnalogStick.show) {
if (g_Config.bRightAnalogCustom)
root->Add(new PSPCustomStick(stickBg, stickImage, ImageID("I_STICK"), g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
else
root->Add(new PSPStick(stickBg, stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
}
addComboKey(g_Config.iCombokey0, g_Config.bComboToggle0, roundImage, ImageID("I_ROUND"), comboKeyImages[0], g_Config.touchCombo0);
addComboKey(g_Config.iCombokey1, g_Config.bComboToggle1, roundImage, ImageID("I_ROUND"), comboKeyImages[1], g_Config.touchCombo1);
+18 -3
View File
@@ -160,9 +160,7 @@ public:
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
private:
void ProcessTouch(float x, float y, bool down);
protected:
int dragPointerId_;
ImageID bgImg_;
ImageID stickImageIndex_;
@@ -174,6 +172,23 @@ private:
float centerX_;
float centerY_;
private:
void ProcessTouch(float x, float y, bool down);
};
class PSPCustomStick : public PSPStick {
public:
PSPCustomStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams);
void Touch(const TouchInput &input) override;
void Draw(UIContext &dc) override;
private:
void ProcessTouch(float x, float y, bool down);
float posX_ = 0.0f;
float posY_ = 0.0f;
};
//initializes the layout from Config. if a default layout does not exist,
+10 -19
View File
@@ -601,7 +601,7 @@ void GameBrowser::Draw(UIContext &dc) {
}
}
static bool IsValidPBP(const std::string &path) {
static bool IsValidPBP(const std::string &path, bool allowHomebrew) {
if (!File::Exists(path))
return false;
@@ -613,7 +613,7 @@ static bool IsValidPBP(const std::string &path) {
ParamSFOData sfo;
sfo.ReadSFO(sfoData);
if (sfo.GetValueString("DISC_ID").empty())
if (!allowHomebrew && sfo.GetValueString("DISC_ID").empty())
return false;
if (sfo.GetValueString("CATEGORY") == "ME")
@@ -628,7 +628,6 @@ void GameBrowser::Refresh() {
lastScale_ = g_Config.fGameGridScale;
lastLayoutWasGrid_ = *gridStyle_;
homebrewStoreButton_ = nullptr;
// Kill all the contents
Clear();
@@ -706,7 +705,7 @@ void GameBrowser::Refresh() {
bool isGame = !fileInfo[i].isDirectory;
bool isSaveData = false;
// Check if eboot directory
if (!isGame && path_.GetPath().size() >= 4 && IsValidPBP(path_.GetPath() + fileInfo[i].name + "/EBOOT.PBP"))
if (!isGame && path_.GetPath().size() >= 4 && IsValidPBP(path_.GetPath() + fileInfo[i].name + "/EBOOT.PBP", true))
isGame = true;
else if (!isGame && File::Exists(path_.GetPath() + fileInfo[i].name + "/PSP_GAME/SYSDIR"))
isGame = true;
@@ -785,9 +784,7 @@ void GameBrowser::Refresh() {
if (browseFlags_ & BrowseFlags::HOMEBREW_STORE) {
Add(new Spacer());
homebrewStoreButton_ = Add(new Choice(mm->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT)));
} else {
homebrewStoreButton_ = nullptr;
Add(new Choice(mm->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT)))->OnClick.Handle(this, &GameBrowser::OnHomebrewStore);
}
if (!lastText_.empty() && gameButtons.empty()) {
@@ -908,7 +905,12 @@ UI::EventReturn GameBrowser::OnRecentClear(UI::EventParams &e) {
return UI::EVENT_DONE;
}
MainScreen::MainScreen() : highlightProgress_(0.0f), prevHighlightProgress_(0.0f), backFromStore_(false), lockBackgroundAudio_(false) {
UI::EventReturn GameBrowser::OnHomebrewStore(UI::EventParams &e) {
screenManager_->push(new StoreScreen());
return UI::EVENT_DONE;
}
MainScreen::MainScreen() {
System_SendMessage("event", "mainscreen");
SetBackgroundAudioGame("");
lastVertical_ = UseVerticalLayout();
@@ -918,7 +920,6 @@ MainScreen::~MainScreen() {
SetBackgroundAudioGame("");
}
void MainScreen::CreateViews() {
// Information in the top left.
// Back button to the bottom left.
@@ -974,11 +975,6 @@ void MainScreen::CreateViews() {
mm->T("How to get homebrew & demos", "How to get homebrew && demos"), "https://www.ppsspp.org/gethomebrew.html",
new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
Choice *hbStore = tabHomebrew->HomebrewStoreButton();
if (hbStore) {
hbStore->OnClick.Handle(this, &MainScreen::OnHomebrewStore);
}
scrollAllGames->Add(tabAllGames);
gameBrowsers_.push_back(tabAllGames);
scrollHomebrew->Add(tabHomebrew);
@@ -1327,11 +1323,6 @@ UI::EventReturn MainScreen::OnCredits(UI::EventParams &e) {
return UI::EVENT_DONE;
}
UI::EventReturn MainScreen::OnHomebrewStore(UI::EventParams &e) {
screenManager()->push(new StoreScreen());
return UI::EVENT_DONE;
}
UI::EventReturn MainScreen::OnSupport(UI::EventParams &e) {
#ifdef __ANDROID__
LaunchBrowser("market://details?id=org.ppsspp.ppssppgold");
+8 -11
View File
@@ -53,8 +53,6 @@ public:
UI::Event OnHoldChoice;
UI::Event OnHighlight;
UI::Choice *HomebrewStoreButton() { return homebrewStoreButton_; }
void FocusGame(const std::string &gamePath);
void SetPath(const std::string &path);
void Draw(UIContext &dc) override;
@@ -81,14 +79,14 @@ private:
UI::EventReturn PinToggleClick(UI::EventParams &e);
UI::EventReturn GridSettingsClick(UI::EventParams &e);
UI::EventReturn OnRecentClear(UI::EventParams &e);
UI::EventReturn OnHomebrewStore(UI::EventParams &e);
UI::ViewGroup *gameList_ = nullptr;
PathBrowser path_;
bool *gridStyle_;
bool *gridStyle_ = nullptr;
BrowseFlags browseFlags_;
std::string lastText_;
std::string lastLink_;
UI::Choice *homebrewStoreButton_ = nullptr;
std::string focusGamePath_;
bool listingPending_ = false;
float lastScale_ = 1.0f;
@@ -132,21 +130,20 @@ protected:
UI::EventReturn OnExit(UI::EventParams &e);
UI::EventReturn OnDownloadUpgrade(UI::EventParams &e);
UI::EventReturn OnDismissUpgrade(UI::EventParams &e);
UI::EventReturn OnHomebrewStore(UI::EventParams &e);
UI::EventReturn OnAllowStorage(UI::EventParams &e);
UI::LinearLayout *upgradeBar_;
UI::TabHolder *tabHolder_;
UI::LinearLayout *upgradeBar_ = nullptr;
UI::TabHolder *tabHolder_ = nullptr;
std::string restoreFocusGamePath_;
std::vector<GameBrowser *> gameBrowsers_;
std::string highlightedGamePath_;
std::string prevHighlightedGamePath_;
float highlightProgress_;
float prevHighlightProgress_;
bool backFromStore_;
bool lockBackgroundAudio_;
float highlightProgress_ = 0.0f;
float prevHighlightProgress_ = 0.0f;
bool backFromStore_ = false;
bool lockBackgroundAudio_ = false;
bool lastVertical_;
bool confirmedTemporary_ = false;
+1 -1
View File
@@ -710,7 +710,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
#endif
if (!boot_filename.empty() && stateToLoad != NULL) {
SaveState::Load(stateToLoad, [](SaveState::Status status, const std::string &message, void *) {
SaveState::Load(stateToLoad, -1, [](SaveState::Status status, const std::string &message, void *) {
if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) {
osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0);
}
+1
View File
@@ -244,6 +244,7 @@ void ProductView::CreateViews() {
auto di = GetI18NCategory("Dialog");
wasInstalled_ = IsGameInstalled();
if (!wasInstalled_) {
launchButton_ = nullptr;
installButton_ = Add(new Button(st->T("Install")));
installButton_->OnClick.Handle(this, &ProductView::OnInstall);
} else {
+52 -10
View File
@@ -70,6 +70,7 @@ void TouchControlVisibilityScreen::CreateViews() {
gridsettings.fillCells = true;
GridLayout *grid = vert->Add(new GridLayout(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
static const char* rightAnalogKey = "Right Analog Stick (tap to customize)";
toggles_.clear();
toggles_.push_back({ "Circle", &g_Config.bShowTouchCircle, ImageID("I_CIRCLE") });
toggles_.push_back({ "Cross", &g_Config.bShowTouchCross, ImageID("I_CROSS") });
@@ -81,7 +82,7 @@ void TouchControlVisibilityScreen::CreateViews() {
toggles_.push_back({ "Select", &g_Config.touchSelectKey.show, ImageID("I_SELECT") });
toggles_.push_back({ "Dpad", &g_Config.touchDpad.show, ImageID::invalid() });
toggles_.push_back({ "Analog Stick", &g_Config.touchAnalogStick.show, ImageID::invalid() });
toggles_.push_back({ "Right Analog Stick\n(not used by most games)", &g_Config.touchRightAnalogStick.show, ImageID::invalid() });
toggles_.push_back({ rightAnalogKey, &g_Config.touchRightAnalogStick.show, ImageID::invalid() });
toggles_.push_back({ "Unthrottle", &g_Config.touchUnthrottleKey.show, ImageID::invalid() });
toggles_.push_back({ "Combo0", &g_Config.touchCombo0.show, ImageID("I_1") });
toggles_.push_back({ "Combo1", &g_Config.touchCombo1.show, ImageID("I_2") });
@@ -95,7 +96,6 @@ void TouchControlVisibilityScreen::CreateViews() {
toggles_.push_back({ "Auto Analog Rotation (CCW)", &g_Config.touchAnalogRotationCCWKey.show, ImageID::invalid() });
auto mc = GetI18NCategory("MappableControls");
for (auto toggle : toggles_) {
LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
row->SetSpacing(0);
@@ -103,16 +103,20 @@ void TouchControlVisibilityScreen::CreateViews() {
CheckBox *checkbox = new CheckBox(toggle.show, "", "", new LinearLayoutParams(50, WRAP_CONTENT));
row->Add(checkbox);
Choice *choice;
if (toggle.img.isValid()) {
choice = new CheckBoxChoice(toggle.img, checkbox, new LinearLayoutParams(1.0f));
if (toggle.key == rightAnalogKey) {
Choice *rightAnalog = new Choice(co->T(rightAnalogKey), "", false, new LinearLayoutParams(1.0f));
rightAnalog->SetCentered(true);
row->Add(rightAnalog)->OnClick.Handle(this, &TouchControlVisibilityScreen::RightAnalogBindScreen);
} else {
choice = new CheckBoxChoice(mc->T(toggle.key), checkbox, new LinearLayoutParams(1.0f));
Choice *choice;
if (toggle.img.isValid()) {
choice = new CheckBoxChoice(toggle.img, checkbox, new LinearLayoutParams(1.0f));
} else {
choice = new CheckBoxChoice(mc->T(toggle.key), checkbox, new LinearLayoutParams(1.0f));
}
choice->SetCentered(true);
row->Add(choice);
}
choice->SetCentered(true);
row->Add(choice);
grid->Add(row);
}
}
@@ -121,6 +125,38 @@ void TouchControlVisibilityScreen::onFinish(DialogResult result) {
g_Config.Save("TouchControlVisibilityScreen::onFinish");
}
void RightAnalogMappingScreen::CreateViews() {
using namespace UI;
auto di = GetI18NCategory("Dialog");
auto co = GetI18NCategory("Controls");
root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(leftColumnWidth - 10, WRAP_CONTENT, 10, NONE, NONE, 10));
root_->Add(back)->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, leftColumnWidth, new AnchorLayoutParams(10, 0, 10, 0, false));
root_->Add(tabHolder);
ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL);
tabHolder->AddTab(co->T("Binds"), rightPanel);
LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)));
vert->SetSpacing(0);
static const char *rightAnalogButton[] = {"None", "Left trigger", "Right trigger", "Square", "Triangle", "Circle", "Cross", "D-PAD Up", "D-PAD Down", "D-PAD Left", "D-PAD Right", "Start", "Select"};
vert->Add(new CheckBox(&g_Config.bRightAnalogCustom, co->T("Use custom right analog")));
PopupMultiChoice *rightAnalogUp = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogUp, co->T("Right analog up"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager()));
PopupMultiChoice *rightAnalogDown = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogDown, co->T("Right analog down"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager()));
PopupMultiChoice *rightAnalogLeft = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogLeft, co->T("Right analog left"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager()));
PopupMultiChoice *rightAnalogRight = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogRight, co->T("Right analog right"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager()));
PopupMultiChoice *rightAnalogPress = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogPress, co->T("Keep this button pressed when right analog is pressed"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager()));
vert->Add(new CheckBox(&g_Config.touchRightAnalogStick.show, co->T("Show right analog")));
rightAnalogUp->SetEnabledPtr(&g_Config.bRightAnalogCustom);
rightAnalogDown->SetEnabledPtr(&g_Config.bRightAnalogCustom);
rightAnalogLeft->SetEnabledPtr(&g_Config.bRightAnalogCustom);
rightAnalogRight->SetEnabledPtr(&g_Config.bRightAnalogCustom);
rightAnalogPress->SetEnabledPtr(&g_Config.bRightAnalogCustom);
}
UI::EventReturn TouchControlVisibilityScreen::OnToggleAll(UI::EventParams &e) {
for (auto toggle : toggles_) {
*toggle.show = nextToggleAll_;
@@ -130,6 +166,12 @@ UI::EventReturn TouchControlVisibilityScreen::OnToggleAll(UI::EventParams &e) {
return UI::EVENT_DONE;
}
UI::EventReturn TouchControlVisibilityScreen::RightAnalogBindScreen(UI::EventParams &e) {
screenManager()->push(new RightAnalogMappingScreen());
return UI::EVENT_DONE;
}
UI::EventReturn CheckBoxChoice::HandleClick(UI::EventParams &e) {
checkbox_->Toggle();
+6
View File
@@ -37,8 +37,14 @@ public:
protected:
UI::EventReturn OnToggleAll(UI::EventParams &e);
UI::EventReturn RightAnalogBindScreen(UI::EventParams &e);
private:
std::vector<TouchButtonToggle> toggles_;
bool nextToggleAll_ = true;
};
class RightAnalogMappingScreen : public UIDialogScreenWithBackground {
public:
void CreateViews() override;
};
+1 -4
View File
@@ -383,9 +383,6 @@
<ClInclude Include="..\..\Common\Arm64Emitter.h" />
<ClInclude Include="..\..\Common\ArmCommon.h" />
<ClInclude Include="..\..\Common\ArmEmitter.h" />
<ClInclude Include="..\..\Common\Atomics.h" />
<ClInclude Include="..\..\Common\Atomic_GCC.h" />
<ClInclude Include="..\..\Common\Atomic_Win32.h" />
<ClInclude Include="..\..\Common\BitSet.h" />
<ClInclude Include="..\..\Common\ChunkFile.h" />
<ClInclude Include="..\..\Common\CodeBlock.h" />
@@ -476,4 +473,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
+1 -4
View File
@@ -53,9 +53,6 @@
<ClInclude Include="..\..\Common\Arm64Emitter.h" />
<ClInclude Include="..\..\Common\ArmCommon.h" />
<ClInclude Include="..\..\Common\ArmEmitter.h" />
<ClInclude Include="..\..\Common\Atomic_GCC.h" />
<ClInclude Include="..\..\Common\Atomic_Win32.h" />
<ClInclude Include="..\..\Common\Atomics.h" />
<ClInclude Include="..\..\Common\BitSet.h" />
<ClInclude Include="..\..\Common\ChunkFile.h" />
<ClInclude Include="..\..\Common\CodeBlock.h" />
@@ -99,4 +96,4 @@
<Filter>Crypto</Filter>
</ClInclude>
</ItemGroup>
</Project>
</Project>
+2 -2
View File
@@ -649,14 +649,14 @@ namespace MainWindow {
case ID_FILE_LOADSTATEFILE:
if (W32Util::BrowseForFileName(true, hWnd, L"Load state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::Load(fn, SaveStateActionFinished);
SaveState::Load(fn, -1, SaveStateActionFinished);
}
break;
case ID_FILE_SAVESTATEFILE:
if (W32Util::BrowseForFileName(false, hWnd, L"Save state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::Save(fn, SaveStateActionFinished);
SaveState::Save(fn, -1, SaveStateActionFinished);
}
break;
+5 -6
View File
@@ -10,8 +10,7 @@ precision mediump int;
uniform sampler2D sampler0;
varying vec2 v_texcoord0;
float amount = 0.60; // suitable range = 0.00 - 1.00
float power = 0.5; // suitable range = 0.0 - 1.0
uniform vec4 u_setting;
void main()
{
@@ -21,9 +20,9 @@ void main()
for(int i= -3 ;i < 3; i++)
{
sum += texture2D(sampler0, v_texcoord0 + vec2(-1, i)*0.004) * amount;
sum += texture2D(sampler0, v_texcoord0 + vec2(0, i)*0.004) * amount;
sum += texture2D(sampler0, v_texcoord0 + vec2(1, i)*0.004) * amount;
sum += texture2D(sampler0, v_texcoord0 + vec2(-1, i)*0.004) * u_setting.x;
sum += texture2D(sampler0, v_texcoord0 + vec2(0, i)*0.004) * u_setting.x;
sum += texture2D(sampler0, v_texcoord0 + vec2(1, i)*0.004) * u_setting.x;
}
if (color.r < 0.3 && color.g < 0.3 && color.b < 0.3)
@@ -42,7 +41,7 @@ void main()
}
}
bloom = mix(color, bloom, power);
bloom = mix(color, bloom, u_setting.y);
gl_FragColor.rgb = bloom;
gl_FragColor.a = 1.0;
}
+2 -2
View File
@@ -10,7 +10,7 @@ precision mediump float;
precision mediump int;
#endif
const float bb = 0.5; // effects black border sensitivity; from 0.0 to 1.0
uniform vec4 u_setting;
uniform sampler2D sampler0;
@@ -39,7 +39,7 @@ void main()
float d2=dot(abs(c20-c02),dt);
float hl=dot(abs(c01-c21),dt);
float vl=dot(abs(c10-c12),dt);
float d = bb*(d1+d2+hl+vl)/(dot(c11,dt)+0.15);
float d = u_setting.x*(d1+d2+hl+vl)/(dot(c11,dt)+0.15);
float lc = 4.0*length(c11);
float f = fract(lc); f*=f;
+20
View File
@@ -0,0 +1,20 @@
// Color correction
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
uniform sampler2D sampler0;
varying vec2 v_texcoord0;
uniform vec4 u_setting;
void main()
{
vec3 rgb = texture2D( sampler0, v_texcoord0 ).xyz;
rgb = vec3(mix(vec3(dot(rgb, vec3(0.299, 0.587, 0.114))), rgb, u_setting.y));
rgb = (rgb-0.5)*u_setting.z+0.5+u_setting.x-1.0;
gl_FragColor.rgb = pow(rgb, vec3(1.0/u_setting.w));
gl_FragColor.a = 1.0;
}
+52
View File
@@ -32,10 +32,23 @@ Vertex=fxaa.vsh
Name=Bloom
Fragment=bloom.fsh
Vertex=fxaa.vsh
SettingName1=Amount
SettingDefaultValue1=0.6
SettingMaxValue1=1.0
SettingMinValue1=0.0
SettingName2=Power
SettingDefaultValue2=0.5
SettingMaxValue2=1.0
SettingMinValue2=0.0
[Sharpen]
Name=Sharpen
Fragment=sharpen.fsh
Vertex=fxaa.vsh
SettingName1=Amount
SettingDefaultValue1=1.5
SettingMaxValue1=3.0
SettingMinValue1=1.0
SettingStep1=0.1
[InverseColors]
Name=Inverse Colors
Author=Henrik
@@ -46,10 +59,24 @@ Name=Scanlines (CRT)
Fragment=scanlines.fsh
Vertex=fxaa.vsh
OutputResolution=True
SettingName1=Amount
SettingDefaultValue1=1.0
SettingMaxValue1=1.0
SettingMinValue1=0.0
SettingStep1=0.1
SettingName2=Intensity
SettingDefaultValue2=0.5
SettingMaxValue2=1.0
SettingMinValue2=0.0
SettingStep2=0.05
[Cartoon]
Name=Cartoon
Fragment=cartoon.fsh
Vertex=cartoon.vsh
SettingName1=Black border
SettingDefaultValue1=0.5
SettingMaxValue1=1.0
SettingMinValue1=0.0
[4xHqGLSL]
Name=4xHqGLSL Upscaler
Fragment=4xhqglsl.fsh
@@ -129,3 +156,28 @@ Author=DeSmuME team based on Hyllian xBR work, conversion and tweaks by LunaMoo
Fragment=xBRZf-DUns.fsh
Vertex=5xBR.vsh
OutputResolution=True
[ColorCorrection]
Name=Color correction
Fragment=colorcorrection.fsh
Vertex=fxaa.vsh
SettingName1=Brightness
SettingDefaultValue1=1.0
SettingMaxValue1=2.0
SettingMinValue1=0.1
SettingStep1=0.05
SettingName2=Saturation
SettingDefaultValue2=1.0
SettingMaxValue2=2.0
SettingMinValue2=0.0
SettingStep2=0.1
SettingName3=Constrast
SettingDefaultValue3=1.0
SettingMaxValue3=3.0
SettingMinValue3=0.1
SettingStep3=0.1
SettingName4=Gamma
SettingDefaultValue4=1.0
SettingMaxValue4=2.0
SettingMinValue4=0.1
SettingStep4=0.05
+3 -4
View File
@@ -8,13 +8,12 @@ precision mediump int;
uniform sampler2D sampler0;
varying vec2 v_texcoord0;
float amount = 1.0; // suitable range = 0.0 - 1.0
float intensity = 0.5; // suitable range = 0.0 - 1.0
uniform vec4 u_setting;
void main()
{
float pos0 = ((v_texcoord0.y + 1.0) * 170.0*amount);
float pos1 = cos((fract( pos0 ) - 0.5)*3.1415926*intensity)*1.5;
float pos0 = ((v_texcoord0.y + 1.0) * 170.0*u_setting.x);
float pos1 = cos((fract( pos0 ) - 0.5)*3.1415926*u_setting.y)*1.5;
vec4 rgb = texture2D( sampler0, v_texcoord0 );
// slight contrast curve
+3 -3
View File
@@ -8,12 +8,12 @@ precision mediump int;
uniform sampler2D sampler0;
varying vec2 v_texcoord0;
float amount = 1.5;
uniform vec4 u_setting;
void main()
{
vec3 color = texture2D(sampler0, v_texcoord0.xy).xyz;
color -= texture2D(sampler0, v_texcoord0.xy+0.0001).xyz*7.0*amount;
color += texture2D(sampler0, v_texcoord0.xy-0.0001).xyz*7.0*amount;
color -= texture2D(sampler0, v_texcoord0.xy+0.0001).xyz*7.0*u_setting.x;
color += texture2D(sampler0, v_texcoord0.xy-0.0001).xyz*7.0*u_setting.x;
gl_FragColor.rgb = color;
}
+1 -1
View File
@@ -22,7 +22,7 @@ if(NOT PPSSPP_PI_MODEL4)
endif()
if(PPSSPP_PI_MODEL4)
set(ARCH_FLAGS "-march=armv7-a -mcpu=cortex-a72 -mtune=cortex-a72 -mfpu=neon-vfpv4 -mfloat-abi=hard")
set(ARCH_FLAGS "-march=armv7-a -mtune=cortex-a72 -mfpu=neon-vfpv4 -mfloat-abi=hard")
else()
set(ARCH_FLAGS "-mcpu=cortex-a7 -mfpu=neon -mfloat-abi=hard")
endif()
+1
View File
@@ -287,6 +287,7 @@ size_t getFilesInDir(const char *directory, std::vector<FileInfo> *files, const
info.isDirectory = isDirectory(info.fullName);
info.exists = true;
info.size = 0;
info.isWritable = false; // TODO - implement some kind of check
if (!info.isDirectory) {
std::string ext = getFileExtension(info.fullName);
if (filter) {
+1
View File
@@ -489,6 +489,7 @@
<ClInclude Include="util\text\shiftjis.h" />
<ClInclude Include="util\text\utf16.h" />
<ClInclude Include="util\text\utf8.h" />
<ClInclude Include="util\tiny_set.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="base\backtrace.cpp" />
+3
View File
@@ -350,6 +350,9 @@
<ClInclude Include="ui\root.h">
<Filter>ui</Filter>
</ClInclude>
<ClInclude Include="util\tiny_set.h">
<Filter>util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="gfx\gl_debug_log.cpp">
+11 -8
View File
@@ -161,8 +161,8 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
case GLRInitStepType::BUFFER_SUBDATA:
{
GLRBuffer *buffer = step.buffer_subdata.buffer;
glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_);
glBufferSubData(GL_ARRAY_BUFFER, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data);
glBindBuffer(buffer->target_, buffer->buffer_);
glBufferSubData(buffer->target_, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data);
if (step.buffer_subdata.deleteData)
delete[] step.buffer_subdata.data;
CHECK_GL_ERROR_IF_DEBUG();
@@ -827,13 +827,16 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) {
{
GLenum attachments[3];
int count = 0;
bool isFBO = step.render.framebuffer != nullptr;
bool hasDepth = isFBO ? step.render.framebuffer->z_stencil_ : false;
if (c.clear.clearMask & GL_COLOR_BUFFER_BIT)
attachments[count++] = GL_COLOR_ATTACHMENT0;
if (c.clear.clearMask & GL_DEPTH_BUFFER_BIT)
attachments[count++] = GL_DEPTH_ATTACHMENT;
if (c.clear.clearMask & GL_STENCIL_BUFFER_BIT)
attachments[count++] = GL_STENCIL_BUFFER_BIT;
glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments);
attachments[count++] = isFBO ? GL_COLOR_ATTACHMENT0 : GL_COLOR;
if (hasDepth && (c.clear.clearMask & GL_DEPTH_BUFFER_BIT))
attachments[count++] = isFBO ? GL_DEPTH_ATTACHMENT : GL_DEPTH;
if (hasDepth && (c.clear.clearMask & GL_STENCIL_BUFFER_BIT))
attachments[count++] = isFBO ? GL_STENCIL_ATTACHMENT : GL_STENCIL;
if (glInvalidateFramebuffer != nullptr && count != 0)
glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments);
CHECK_GL_ERROR_IF_DEBUG();
break;
}
+6
View File
@@ -6,6 +6,7 @@
#include "gfx/gl_common.h"
#include "thin3d/DataFormat.h"
#include "util/tiny_set.h"
struct GLRViewport {
float x, y, w, h, minZ, maxZ;
@@ -295,9 +296,14 @@ struct GLRStep {
GLRStep(GLRStepType _type) : stepType(_type) {}
GLRStepType stepType;
std::vector<GLRRenderData> commands;
TinySet<const GLRFramebuffer *, 8> dependencies;
union {
struct {
GLRFramebuffer *framebuffer;
GLRRenderPassAction color;
GLRRenderPassAction depth;
GLRRenderPassAction stencil;
// Note: not accurate.
int numDraws;
} render;
struct {
+19
View File
@@ -279,6 +279,9 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
GLRStep *step = new GLRStep{ GLRStepType::RENDER };
// This is what queues up new passes, and can end previous ones.
step->render.framebuffer = fb;
step->render.color = color;
step->render.depth = depth;
step->render.stencil = stencil;
step->render.numDraws = 0;
steps_.push_back(step);
@@ -308,6 +311,12 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
}
curRenderStep_ = step;
if (fb) {
if (color == GLRRenderPassAction::KEEP || depth == GLRRenderPassAction::KEEP || stencil == GLRRenderPassAction::KEEP) {
step->dependencies.insert(fb);
}
}
// Every step clears this state.
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
}
@@ -319,6 +328,7 @@ void GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding,
data.bind_fb_texture.framebuffer = fb;
data.bind_fb_texture.aspect = aspectBit;
curRenderStep_->commands.push_back(data);
curRenderStep_->dependencies.insert(fb);
}
void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask) {
@@ -328,6 +338,10 @@ void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR
step->copy.src = src;
step->copy.dst = dst;
step->copy.aspectMask = aspectMask;
step->dependencies.insert(src);
bool fillsDst = dst && srcRect.x == 0 && srcRect.y == 0 && srcRect.w == dst->width && srcRect.h == dst->height;
if (dstPos.x != 0 || dstPos.y != 0 || !fillsDst)
step->dependencies.insert(dst);
steps_.push_back(step);
// Every step clears this state.
@@ -342,6 +356,10 @@ void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR
step->blit.dst = dst;
step->blit.aspectMask = aspectMask;
step->blit.filter = filter;
step->dependencies.insert(src);
bool fillsDst = dst && dstRect.x == 0 && dstRect.y == 0 && dstRect.w == dst->width && dstRect.h == dst->height;
if (!fillsDst)
step->dependencies.insert(dst);
steps_.push_back(step);
// Every step clears this state.
@@ -356,6 +374,7 @@ bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspec
step->readback.srcRect = { x, y, w, h };
step->readback.aspectMask = aspectBits;
step->readback.dstFormat = destFormat;
step->dependencies.insert(src);
steps_.push_back(step);
// Every step clears this state.
+40 -28
View File
@@ -1,4 +1,4 @@
#include <map>
#include <unordered_map>
#include "base/timeutil.h"
#include "DataFormat.h"
@@ -752,13 +752,27 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
void VulkanQueueRunner::ApplyRenderPassMerge(std::vector<VKRStep *> &steps) {
// First let's count how many times each framebuffer is rendered to.
// If it's more than one, let's do our best to merge them. This can help God of War quite a bit.
std::map<VKRFramebuffer *, int> counts;
std::unordered_map<VKRFramebuffer *, int> counts;
for (int i = 0; i < (int)steps.size(); i++) {
if (steps[i]->stepType == VKRStepType::RENDER) {
counts[steps[i]->render.framebuffer]++;
}
}
auto mergeRenderSteps = [](VKRStep *dst, VKRStep *src) {
// OK. Now, if it's a render, slurp up all the commands and kill the step.
// Also slurp up any pretransitions.
dst->preTransitions.insert(dst->preTransitions.end(), src->preTransitions.begin(), src->preTransitions.end());
dst->commands.insert(dst->commands.end(), src->commands.begin(), src->commands.end());
// So we don't consider it for other things, maybe doesn't matter.
src->dependencies.clear();
src->stepType = VKRStepType::RENDER_SKIP;
};
auto renderHasClear = [](const VKRStep *step) {
const auto &r = step->render;
return r.color == VKRRenderPassAction::CLEAR || r.depth == VKRRenderPassAction::CLEAR || r.stencil == VKRRenderPassAction::CLEAR;
};
// Now, let's go through the steps. If we find one that is rendered to more than once,
// we'll scan forward and slurp up any rendering that can be merged across.
for (int i = 0; i < (int)steps.size(); i++) {
@@ -767,39 +781,36 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector<VKRStep *> &steps) {
TinySet<VKRFramebuffer *, 8> touchedFramebuffers; // must be the same fast-size as the dependencies TinySet for annoying reasons.
for (int j = i + 1; j < (int)steps.size(); j++) {
// If any other passes are reading from this framebuffer as-is, we cancel the scan.
if (steps[j]->dependencies.contains(fb)) {
// Reading from itself means a KEEP, which is okay.
if (steps[j]->stepType != VKRStepType::RENDER || steps[j]->render.framebuffer != fb)
break;
}
switch (steps[j]->stepType) {
case VKRStepType::RENDER:
if (steps[j]->dependencies.contains(fb)) {
goto done_fb;
}
// Prevent Unknown's example case from https://github.com/hrydgard/ppsspp/pull/12242
if (steps[j]->dependencies.contains(touchedFramebuffers)) {
goto done_fb;
}
if (steps[j]->render.framebuffer == fb &&
steps[j]->render.color != VKRRenderPassAction::CLEAR &&
steps[j]->render.depth != VKRRenderPassAction::CLEAR &&
steps[j]->render.stencil != VKRRenderPassAction::CLEAR) {
// ok. Now, if it's a render, slurp up all the commands
// and kill the step.
// Also slurp up any pretransitions.
steps[i]->preTransitions.insert(steps[i]->preTransitions.end(), steps[j]->preTransitions.begin(), steps[j]->preTransitions.end());
steps[i]->commands.insert(steps[i]->commands.end(), steps[j]->commands.begin(), steps[j]->commands.end());
steps[j]->stepType = VKRStepType::RENDER_SKIP;
}
// Remember the framebuffer this wrote to. We can't merge with later passes that depend on these.
if (steps[j]->render.framebuffer != fb) {
if (steps[j]->render.framebuffer == fb) {
// Prevent Unknown's example case from https://github.com/hrydgard/ppsspp/pull/12242
if (renderHasClear(steps[j]) || steps[j]->dependencies.contains(touchedFramebuffers)) {
goto done_fb;
} else {
// Safe to merge, great.
mergeRenderSteps(steps[i], steps[j]);
}
} else {
// Remember the framebuffer this wrote to. We can't merge with later passes that depend on these.
touchedFramebuffers.insert(steps[j]->render.framebuffer);
}
break;
case VKRStepType::COPY:
if (steps[j]->copy.src == fb || steps[j]->copy.dst == fb) {
if (steps[j]->copy.dst == fb) {
// Without framebuffer "renaming", we can't merge past a clobbered fb.
goto done_fb;
}
touchedFramebuffers.insert(steps[j]->copy.dst);
break;
case VKRStepType::BLIT:
if (steps[j]->blit.src == fb || steps[j]->blit.dst == fb) {
if (steps[j]->blit.dst == fb) {
// Without framebuffer "renaming", we can't merge past a clobbered fb.
goto done_fb;
}
touchedFramebuffers.insert(steps[j]->blit.dst);
@@ -807,12 +818,13 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector<VKRStep *> &steps) {
case VKRStepType::READBACK:
// Not sure this has much effect, when executed READBACK is always the last step
// since we stall the GPU and wait immediately after.
if (steps[j]->readback.src == fb) {
goto done_fb;
}
break;
case VKRStepType::RENDER_SKIP:
case VKRStepType::READBACK_IMAGE:
break;
default:
break;
// We added a new step? Might be unsafe.
goto done_fb;
}
}
done_fb:
+1 -64
View File
@@ -6,6 +6,7 @@
#include "Common/Vulkan/VulkanContext.h"
#include "math/dataconv.h"
#include "thin3d/DataFormat.h"
#include "util/tiny_set.h"
class VKRFramebuffer;
struct VKRImage;
@@ -17,70 +18,6 @@ enum {
QUEUE_HACK_RENDERPASS_MERGE = 8,
};
// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded.
template <class T, int MaxFastSize>
struct TinySet {
~TinySet() { delete slowLookup_; }
inline void insert(T t) {
// Fast linear scan.
for (int i = 0; i < fastCount; i++) {
if (fastLookup_[i] == t)
return; // We already have it.
}
// Fast insertion
if (fastCount < MaxFastSize) {
fastLookup_[fastCount++] = t;
return;
}
// Fall back to slow path.
insertSlow(t);
}
bool contains(T t) const {
for (int i = 0; i < fastCount; i++) {
if (fastLookup_[i] == t)
return true;
}
if (slowLookup_) {
for (auto x : *slowLookup_) {
if (x == t)
return true;
}
}
return false;
}
bool contains(const TinySet<T, MaxFastSize> &otherSet) {
// Awkward, kind of ruins the fun.
for (int i = 0; i < fastCount; i++) {
if (otherSet.contains(fastLookup_[i]))
return true;
}
if (slowLookup_) {
for (auto x : *slowLookup_) {
if (otherSet.contains(x))
return true;
}
}
return false;
}
private:
void insertSlow(T t) {
if (!slowLookup_) {
slowLookup_ = new std::vector<T>();
} else {
for (size_t i = 0; i < slowLookup_->size(); i++) {
if ((*slowLookup_)[i] == t)
return;
}
}
slowLookup_->push_back(t);
}
T fastLookup_[MaxFastSize];
int fastCount = 0;
int slowCount = 0;
std::vector<T> *slowLookup_ = nullptr;
};
enum class VKRRenderCommand : uint8_t {
REMOVED,
BIND_PIPELINE,
+16
View File
@@ -493,6 +493,13 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
step->render.finalColorLayout = !fb ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
steps_.push_back(step);
if (fb) {
// If there's a KEEP, we naturally read from the framebuffer.
if (color == VKRRenderPassAction::KEEP || depth == VKRRenderPassAction::KEEP || stencil == VKRRenderPassAction::KEEP) {
step->dependencies.insert(fb);
}
}
curRenderStep_ = step;
if (fb) {
curWidth_ = fb->width;
@@ -516,6 +523,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a
step->readback.src = src;
step->readback.srcRect.offset = { x, y };
step->readback.srcRect.extent = { (uint32_t)w, (uint32_t)h };
step->dependencies.insert(src);
steps_.push_back(step);
curRenderStep_ = nullptr;
@@ -824,6 +832,10 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
step->copy.srcRect = srcRect;
step->copy.dst = dst;
step->copy.dstPos = dstPos;
step->dependencies.insert(src);
bool fillsDst = dst && srcRect.offset.x == 0 && srcRect.offset.y == 0 && srcRect.extent.width == dst->width && srcRect.extent.height == dst->height;
if (dstPos.x != 0 || dstPos.y != 0 || !fillsDst)
step->dependencies.insert(dst);
std::unique_lock<std::mutex> lock(mutex_);
steps_.push_back(step);
@@ -862,6 +874,10 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
step->blit.dst = dst;
step->blit.dstRect = dstRect;
step->blit.filter = filter;
step->dependencies.insert(src);
bool fillsDst = dst && dstRect.offset.x == 0 && dstRect.offset.y == 0 && dstRect.extent.width == dst->width && dstRect.extent.height == dst->height;
if (!fillsDst)
step->dependencies.insert(dst);
std::unique_lock<std::mutex> lock(mutex_);
steps_.push_back(step);
+71
View File
@@ -0,0 +1,71 @@
#pragma once
#include <vector>
// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded.
template <class T, int MaxFastSize>
struct TinySet {
~TinySet() { delete slowLookup_; }
inline void insert(T t) {
// Fast linear scan.
for (int i = 0; i < fastCount; i++) {
if (fastLookup_[i] == t)
return; // We already have it.
}
// Fast insertion
if (fastCount < MaxFastSize) {
fastLookup_[fastCount++] = t;
return;
}
// Fall back to slow path.
insertSlow(t);
}
bool contains(T t) const {
for (int i = 0; i < fastCount; i++) {
if (fastLookup_[i] == t)
return true;
}
if (slowLookup_) {
for (auto x : *slowLookup_) {
if (x == t)
return true;
}
}
return false;
}
bool contains(const TinySet<T, MaxFastSize> &otherSet) {
// Awkward, kind of ruins the fun.
for (int i = 0; i < fastCount; i++) {
if (otherSet.contains(fastLookup_[i]))
return true;
}
if (slowLookup_) {
for (auto x : *slowLookup_) {
if (otherSet.contains(x))
return true;
}
}
return false;
}
void clear() {
delete slowLookup_;
slowLookup_ = nullptr;
fastCount = 0;
}
private:
void insertSlow(T t) {
if (!slowLookup_) {
slowLookup_ = new std::vector<T>();
} else {
for (size_t i = 0; i < slowLookup_->size(); i++) {
if ((*slowLookup_)[i] == t)
return;
}
}
slowLookup_->push_back(t);
}
T fastLookup_[MaxFastSize];
int fastCount = 0;
std::vector<T> *slowLookup_ = nullptr;
};
+1 -1
View File
@@ -430,7 +430,7 @@ int main(int argc, const char* argv[])
#endif
if (stateToLoad != NULL)
SaveState::Load(stateToLoad);
SaveState::Load(stateToLoad, -1);
std::vector<std::string> failedTests;
std::vector<std::string> passedTests;
-1
View File
@@ -317,7 +317,6 @@ void retro_init(void) {
g_Config.iGlobalVolume = VOLUME_MAX - 1;
g_Config.iAltSpeedVolume = -1;
g_Config.bEnableSound = true;
g_Config.bAudioResampler = false;
g_Config.iCwCheatRefreshRate = 60;
g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE;
+1 -1
View File
@@ -38,7 +38,7 @@ bool TestArm64Emitter() {
u32 code[512];
ARM64XEmitter emitter((u8 *)code);
ARM64XEmitter emitter((u8 *)code, (u8 *)code);
ARM64FloatEmitter fp(&emitter);
emitter.MOVfromSP(X3);