mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 02:29:51 +01:00
Merge branch 'master' into TemporaryHackishBranch
# Conflicts: # UI/GameSettingsScreen.cpp # assets/shaders/defaultshaders.ini
This commit is contained in:
commit
0f6fa442dd
94 files changed
+1022
-796
No files matched your search
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,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" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -96,7 +96,7 @@ enum NormalSSEOps
|
||||
};
|
||||
|
||||
|
||||
void XEmitter::SetCodePointer(u8 *ptr)
|
||||
void XEmitter::SetCodePointer(u8 *ptr, u8 *writePtr)
|
||||
{
|
||||
code = ptr;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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" },
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user