diff --git a/.gitignore b/.gitignore index 5e6b96b51c..a99238ff41 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ Windows/ipch # For ppsspp.ini, etc. *.ini +# Qt Linguist files +*.qm + Logs Memstick diff --git a/CMakeLists.txt b/CMakeLists.txt index c567215010..37509ed442 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -918,7 +918,11 @@ if(WIN32) endif() if(HEADLESS) - add_executable(PPSSPPHeadless headless/Headless.cpp headless/StubHost.h) + add_executable(PPSSPPHeadless + headless/Headless.cpp + headless/StubHost.h + headless/Compare.cpp + headless/Compare.h) target_link_libraries(PPSSPPHeadless ${CoreLibName} ${COCOA_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) setup_target_project(PPSSPPHeadless headless) diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index ecfa139883..ec3e7655be 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -78,11 +78,25 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated) } } -void ARMXEmitter::MOVI2R(ARMReg reg, u32 val) +void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize) { Operand2 op2; bool inverse; - if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + if (!optimize) + { + // Only used in backpatch atm + // Only support ARMv7 right now + if (cpu_info.bArmV7) { + MOVW(reg, val & 0xFFFF); + MOVT(reg, val, true); + } + else + { + // ARMv6 version won't use backpatch for now + // Run again with optimizations + MOVI2R(reg, val); + } + } else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { if (!inverse) MOV(reg, op2); else @@ -115,7 +129,14 @@ void ARMXEmitter::MOVI2R(ARMReg reg, u32 val) } } } - +// Moves IMM to memory location +void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val) +{ + // This moves imm to a memory location + MOVW(R14, val); MOVT(R14, val, true); + MOVW(R12, op); MOVT(R12, op, true); + STR(R12, R14); // R10 is what we want to store +} void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) { MOVI2R(reg, (u32)(func)); BL(reg); @@ -404,7 +425,7 @@ void ARMXEmitter::MOVT(ARMReg Rd, Operand2 Rm, bool TopBits) { WriteInstruction( void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, bool SetFlags) // This can get renamed later { - u32 op = InstOps[Op][Rm.GetType()]; // Type always decided by last operand + s32 op = InstOps[Op][Rm.GetType()]; // Type always decided by last operand u32 Data = Rm.GetData(); if (Rm.GetType() == TYPE_IMM) { @@ -419,7 +440,7 @@ void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, b break; } } - if (op == (u32)-1) + if (op == -1) _assert_msg_(DYNA_REC, false, "%s not yet support %d", InstNames[Op], Rm.GetType()); Write32(condition | (op << 21) | (SetFlags ? (1 << 20) : 0) | Rn << 16 | Rd << 12 | Data); } @@ -486,6 +507,10 @@ void ARMXEmitter::REV (ARMReg dest, ARMReg src ) { Write32(condition | (107 << 20) | (15 << 16) | (dest << 12) | (243 << 4) | src); } +void ARMXEmitter::REV16(ARMReg dest, ARMReg src) +{ + Write32(condition | (0x3DF << 16) | (dest << 12) | (0xFD << 4) | src); +} void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, Operand2 op2) { @@ -526,8 +551,17 @@ void ARMXEmitter::DMB () { Write32(0xF57FF05E); } +void ARMXEmitter::SVC(Operand2 op) +{ + Write32(condition | (0x0F << 24) | op.Imm24()); +} void ARMXEmitter::LDR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x41, src, dest, op);} +void ARMXEmitter::LDRH(ARMReg dest, ARMReg src, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F)); +} void ARMXEmitter::LDRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x45, src, dest, op);} void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) @@ -569,19 +603,249 @@ void ARMXEmitter::LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...) WriteRegStoreOp(0x89, dest, WriteBack, RegList); } -// NEON and ASIMD +ARMReg ARMXEmitter::SubBase(ARMReg Reg) +{ + if (Reg >= S0) + { + if (Reg >= D0) + { + if (Reg >= Q0) + return (ARMReg)((Reg - Q0) * 2); // Always gets encoded as a double register + return (ARMReg)(Reg - D0); + } + return (ARMReg)(Reg - S0); + } + return Reg; +} +// NEON Specific +void ARMXEmitter::VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd >= Q0, "Pass invalid register to VADD(integer)"); + _assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VADD(integer) when CPU doesn't support it"); + + // Gets encoded as a double register + Vd = SubBase(Vd); + Vn = SubBase(Vn); + Vm = SubBase(Vm); + + Write32((0xF2 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \ + | ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (1 << 6) \ + | ((Vm & 0x10) << 2) | (Vm & 0xF)); + +} +void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd >= Q0, "Pass invalid register to VSUB(integer)"); + _assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VSUB(integer) when CPU doesn't support it"); + + // Gets encoded as a double register + Vd = SubBase(Vd); + Vn = SubBase(Vn); + Vm = SubBase(Vm); + + Write32((0xF3 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \ + | ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (1 << 6) \ + | ((Vm & 0x10) << 2) | (Vm & 0xF)); + +} + +// VFP Specific + void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, Operand2 op) { _assert_msg_(DYNA_REC, Dest >= S0 && Dest <= D31, "Passed Invalid dest register to VLDR"); _assert_msg_(DYNA_REC, Base <= R15, "Passed invalid Base register to VLDR"); + _assert_msg_(DYNA_REC, !(op.Imm12() & 4), "Offset needs to be word aligned"); bool single_reg = Dest < D0; + + Dest = SubBase(Dest); + if (single_reg) - Dest = (ARMReg)(Dest - S0); + { + Write32(NO_COND | (0x1B << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \ + | ((Dest & 0x1E) << 11) | (10 << 8) | (op.Imm12() >> 2)); + + } else - Dest = (ARMReg)(Dest - D0); - Write32(NO_COND | (13 << 24) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \ - | (5 << 9) | (!single_reg << 8) | op.Imm8()); + { + Write32(NO_COND | (0x1B << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \ + | ((Dest & 0xF) << 12) | (11 << 8) | (op.Imm12() >> 2)); + } } +void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, Operand2 op) +{ + _assert_msg_(DYNA_REC, Src >= S0 && Src <= D31, "Passed invalid src register to VSTR"); + _assert_msg_(DYNA_REC, Base <= R15, "Passed invalid base register to VSTR"); + _assert_msg_(DYNA_REC, !(op.Imm12() & 4), "Offset needs to be word aligned"); + bool single_reg = Src < D0; + + Src = SubBase(Src); + + if (single_reg) + { + Write32(NO_COND | (0x1B << 23) | ((Src & 0x1) << 22) | (Base << 16) \ + | ((Src & 0x1E) << 11) | (10 << 8) | (op.Imm12() >> 2)); + + } + else + { + Write32(NO_COND | (0x1B << 23) | ((Src & 0x10) << 18) | (Base << 16) \ + | ((Src & 0xF) << 12) | (11 << 8) | (op.Imm12() >> 2)); + } +} +void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP"); + bool single_reg = Vd < D0; + + Vd = SubBase(Vd); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \ + | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \ + | (0x2F << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF)); + } +} +void ARMXEmitter::VCMP(ARMReg Vd) +{ + _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP"); + bool single_reg = Vd < D0; + + Vd = SubBase(Vd); + + if (single_reg) + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \ + | (0x2B << 6)); + } + else + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \ + | (0x2F << 6)); + } +} +void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd < Q0, "Pased invalid dest register to VSQRT"); + _assert_msg_(DYNA_REC, Vn < Q0, "Passed invalid Vn to VSQRT"); + _assert_msg_(DYNA_REC, Vm < Q0, "Passed invalid Vm to VSQRT"); + bool single_reg = Vd < D0; + + Vd = SubBase(Vd); + Vn = SubBase(Vn); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | ((Vn & 0x1E) << 16) \ + | ((Vd & 0x1E) << 11) | (0xA << 8) | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) \ + | (Vm >> 1)); + } + else + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \ + | ((Vd & 0xF) << 12) | (0xB << 8) | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) \ + | (Vm & 0xF)); + } +} +void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd < Q0, "Pased invalid dest register to VSQRT"); + _assert_msg_(DYNA_REC, Vm < Q0, "Passed invalid Vm to VSQRT"); + bool single_reg = Vd < D0; + + Vd = SubBase(Vd); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ + | ((Vd & 0x1E) << 11) | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ + | ((Vd & 0xF) << 12) | (0x2F << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + } +} +// VFP and ASIMD +void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD"); + _assert_msg_(DYNA_REC, Vn >= S0, "Passed invalid Vn to VADD"); + _assert_msg_(DYNA_REC, Vm >= S0, "Passed invalid Vm to VADD"); + bool single_reg = Vd < D0; + bool double_reg = Vd < Q0; + + Vd = SubBase(Vd); + Vn = SubBase(Vn); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ + | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 12) | (0x5 << 9) \ + | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + if (double_reg) + { + Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ + | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ + | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + } + else + { + _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VADD with Quad Reg without support!"); + Write32((0xF2 << 24) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \ + | ((Vd & 0xF) << 12) | (0xD << 8) | ((Vn & 0x10) << 3) \ + | (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + } + } +} +void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VSUB"); + _assert_msg_(DYNA_REC, Vn >= S0, "Passed invalid Vn to VSUB"); + _assert_msg_(DYNA_REC, Vm >= S0, "Passed invalid Vm to VSUB"); + bool single_reg = Vd < D0; + bool double_reg = Vd < Q0; + + Vd = SubBase(Vd); + Vn = SubBase(Vn); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ + | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 12) | (0x5 << 9) \ + | ((Vn & 0x1) << 7) | (1 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + if (double_reg) + { + Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ + | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ + | ((Vn & 0x10) << 3) | (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + } + else + { + _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VADD with Quad Reg without support!"); + Write32((0xF2 << 24) | (1 << 21) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \ + | ((Vd & 0xF) << 12) | (0xD << 8) | ((Vn & 0x10) << 3) \ + | (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + } + } +} + void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) { if (Dest > R15) @@ -600,6 +864,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) { // Move 64bit from Arm reg _assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM to NEON"); + return; } } } @@ -613,13 +878,13 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) Src = (ARMReg)(Src - S0); Write32(NO_COND | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \ | (0xA << 8) | ((Src & 0x1) << 7) | (1 << 4)); - return; } else { // Move 64bit To Arm reg _assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM From NEON"); + return; } } else @@ -632,32 +897,35 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) int SrcSize = Src < D0 ? 1 : Src < Q0 ? 2 : 4; int DestSize = Dest < D0 ? 1 : Dest < Q0 ? 2 : 4; bool Single = DestSize == 1; + bool Quad = DestSize == 4; + _assert_msg_(DYNA_REC, SrcSize == DestSize, "VMOV doesn't support moving different register sizes"); + + Dest = SubBase(Dest); + Src = SubBase(Src); + if (Single) { - Dest = (ARMReg)(Dest - S0); - Src = (ARMReg)(Src - S0); Write32(NO_COND | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \ | (0x5 << 9) | (1 << 6) | ((Src & 0x1) << 5) | ((Src & 0x1E) >> 1)); } else { // Double and quad - bool Quad = DestSize == 4; if (Quad) { + _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use quad registers when you don't support ASIMD."); // Gets encoded as a Double register - Dest = (ARMReg)((Dest - Q0) * 2); - Src = (ARMReg)((Src - Q0) * 2); + Write32((0xF2 << 24) | ((Dest & 0x10) << 18) | (2 << 20) | ((Src & 0xF) << 16) \ + | ((Dest & 0xF) << 12) | (1 << 8) | ((Src & 0x10) << 3) | (1 << 6) \ + | ((Src & 0x10) << 1) | (1 << 4) | (Src & 0xF)); + } else { - Dest = (ARMReg)(Dest - D0); - Src = (ARMReg)(Src - D0); + Write32(NO_COND | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x3 << 20) | ((Dest & 0xF) << 12) \ + | (0x2D << 6) | ((Src & 0x10) << 1) | (Src & 0xF)); } - Write32((0xF2 << 24) | ((Dest & 0x10) << 18) | (1 << 21) | ((Src & 0xF) << 16) \ - | ((Dest & 0xF) << 12) | (1 << 8) | ((Src & 0x10) << 3) | (Quad << 6) \ - | ((Src & 0x10) << 1) | (1 << 4) | (Src & 0xF)); } } diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index f5be1f5d06..636a9a646b 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -97,6 +97,13 @@ enum ShiftType ST_ROR = 3, ST_RRX = 4 }; +enum IntegerSize +{ + I_I8 = 0, + I_I16, + I_I32, + I_I64 +}; enum { @@ -309,6 +316,9 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated); inline Operand2 R(ARMReg Reg) { return Operand2(Reg, TYPE_REG); } inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); } inline Operand2 Mem(void *ptr) { return Operand2((u32)ptr, TYPE_IMM); } +//usage: struct {int e;} s; STRUCT_OFFSET(s,e) +#define STRUCT_OFF(str,elem) ((u32)((u32)&(str).elem-(u32)&(str))) + struct FixupBranch { @@ -418,6 +428,7 @@ public: void SBC (ARMReg dest, ARMReg src, Operand2 op2); void SBCS(ARMReg dest, ARMReg src, Operand2 op2); void REV (ARMReg dest, ARMReg src); + void REV16 (ARMReg dest, ARMReg src); void RSC (ARMReg dest, ARMReg src, Operand2 op2); void RSCS(ARMReg dest, ARMReg src, Operand2 op2); void TST ( ARMReg src, Operand2 op2); @@ -459,6 +470,7 @@ public: void LDR (ARMReg dest, ARMReg src, Operand2 op2 = 0); // Offset adds to the base register in LDR void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRH(ARMReg dest, ARMReg src, Operand2 op = 0); void LDRB(ARMReg dest, ARMReg src, Operand2 op2 = 0); void STR (ARMReg dest, ARMReg src, Operand2 op2 = 0); // Offset adds on to the destination register in STR @@ -473,23 +485,37 @@ public: // dest contains the result if the instruction managed to store the value void STREX(ARMReg dest, ARMReg base, ARMReg op); void DMB (); + void SVC(Operand2 op); // NEON and ASIMD instructions // None of these will be created with conditional since ARM // is deprecating conditional execution of ASIMD instructions. - // Some ASIMD instructions don't even have a conditional encoding. + // ASIMD instructions don't even have a conditional encoding. + + // Subtracts the base from the register to give us the real one + ARMReg SubBase(ARMReg Reg); + // NEON Only + void VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm); + void VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm); - void VLDR(ARMReg dest, ARMReg Base, Operand2 op); + // VFP Only + void VLDR(ARMReg Dest, ARMReg Base, Operand2 op); + void VSTR(ARMReg Src, ARMReg Base, Operand2 op); + void VCMP(ARMReg Vd, ARMReg Vm); + // Compares against zero + void VCMP(ARMReg Vd); + void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm); + void VSQRT(ARMReg Vd, ARMReg Vm); + + // NEON and VFP + void VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm); + void VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VMOV(ARMReg Dest, ARMReg Src); void QuickCallFunction(ARMReg scratchreg, void *func); // Utility functions - void MOVI2R(ARMReg reg, u32 val); - void ARMABI_ShowConditions(); - void ARMABI_Return(); - - void UpdateAPSR(bool NZCVQ, u8 Flags, bool GE, u8 GEval); - + void MOVI2R(ARMReg reg, u32 val, bool optimize = true); + void ARMABI_MOVI2M(Operand2 op, Operand2 val); }; // class ARMXEmitter diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index 09773a7974..b9ae0be8ae 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -33,7 +33,9 @@ #include #include #include +#ifndef __SYMBIAN32__ #include +#endif #include "Common.h" #include "FileUtil.h" @@ -44,6 +46,18 @@ namespace std { using tr1::is_pointer; } #endif +#ifdef __SYMBIAN32__ +namespace std { + template + struct bool_constant { + typedef bool_constant type; + static const bool value = bool_value; + }; + template const bool bool_constant::value; + template struct is_pointer : public bool_constant {}; + template struct is_pointer : public bool_constant {}; +} +#endif template struct LinkedListItem : public T diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index 56ba1d8631..0fadc6ef2a 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -318,7 +318,7 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text) Len = LOG_PENDING_MAX - 16; char ColorAttr[16] = ""; - int ColorLen = 0; + size_t ColorLen = 0; if (bUseColor) { // Not ANSI, since the console doesn't support it, but ANSI-like. @@ -333,7 +333,7 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text) u32 prevLogWritePos = logWritePos; if (logWritePos + ColorLen + Len >= LOG_PENDING_MAX) { - for (u32 i = 0; i < ColorLen; ++i) + for (size_t i = 0; i < ColorLen; ++i) logPending[(logWritePos + i) % LOG_PENDING_MAX] = ColorAttr[i]; logWritePos += ColorLen; if (logWritePos >= LOG_PENDING_MAX) diff --git a/Common/IniFile.h b/Common/IniFile.h index 09ce6ca0de..29410e21cc 100644 --- a/Common/IniFile.h +++ b/Common/IniFile.h @@ -95,7 +95,7 @@ public: return false; } values.clear(); - for(int i = 0; i < temp.size(); i++) + for(size_t i = 0; i < temp.size(); i++) { std::vector key_val; SplitString(temp[i],'_',key_val); diff --git a/Common/x64Emitter.cpp b/Common/x64Emitter.cpp index 516dfa5fb6..a4b16d7a84 100644 --- a/Common/x64Emitter.cpp +++ b/Common/x64Emitter.cpp @@ -1292,6 +1292,8 @@ void XEmitter::CVTDQ2PS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0x5B, true, reg void XEmitter::CVTPD2DQ(X64Reg regOp, OpArg arg) {WriteSSEOp(64, 0xE6, false, regOp, arg);} void XEmitter::CVTPS2DQ(X64Reg regOp, OpArg arg) {WriteSSEOp(64, 0x5B, true, regOp, arg);} +void XEmitter::CVTSI2SS(X64Reg xregdest, OpArg arg) {WriteSSEOp(32, 0x2A, false, xregdest, arg);} +void XEmitter::CVTSS2SI(X64Reg xregdest, OpArg arg) {WriteSSEOp(32, 0x2D, false, xregdest, arg);} void XEmitter::CVTTSS2SI(X64Reg xregdest, OpArg arg) {WriteSSEOp(32, 0x2C, false, xregdest, arg);} void XEmitter::CVTTPS2DQ(X64Reg xregdest, OpArg arg) {WriteSSEOp(32, 0x5B, false, xregdest, arg);} diff --git a/Common/x64Emitter.h b/Common/x64Emitter.h index 7099c95f9f..c2fc305359 100644 --- a/Common/x64Emitter.h +++ b/Common/x64Emitter.h @@ -564,6 +564,8 @@ public: void CVTDQ2PS(X64Reg regOp, OpArg arg); void CVTPS2DQ(X64Reg regOp, OpArg arg); + void CVTSI2SS(X64Reg xregdest, OpArg arg); // Yeah, destination really is a GPR like EAX! + void CVTSS2SI(X64Reg xregdest, OpArg arg); // Yeah, destination really is a GPR like EAX! void CVTTSS2SI(X64Reg xregdest, OpArg arg); // Yeah, destination really is a GPR like EAX! void CVTTPS2DQ(X64Reg regOp, OpArg arg); diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index c4f0420870..22762fbaa1 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -55,7 +55,7 @@ namespace if(dataSize == -1) { dataSize = pspFileSystem.GetFileInfo(filename).size; - *data = new u8[dataSize]; + *data = new u8[(size_t)dataSize]; } size_t result = pspFileSystem.ReadFile(handle, *data, dataSize); diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index c413209c0c..4b74a422e1 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -123,6 +123,12 @@ CISOFileBlockDevice::~CISOFileBlockDevice() bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) { + if ((u32)blockNumber >= numBlocks) + { + memset(outPtr, 0, 2048); + return false; + } + u32 idx = index[blockNumber]; u32 idx2 = index[blockNumber+1]; u8 inbuffer[4096]; //too big @@ -153,7 +159,7 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) if(inflateInit2(&z, -15) != Z_OK) { ERROR_LOG(LOADER, "deflateInit ERROR : %s\n", (z.msg) ? z.msg : "???"); - return 1; + return false; } z.avail_in = readSize; z.next_out = outPtr; @@ -165,14 +171,17 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) //if (status != Z_OK) { ERROR_LOG(LOADER, "block %d:inflate : %s[%d]\n", blockNumber, (z.msg) ? z.msg : "error", status); + inflateEnd(&z); return 1; } int cmp_size = blockSize - z.avail_out; if (cmp_size != (int)blockSize) { ERROR_LOG(LOADER, "block %d : block size error %d != %d\n", blockNumber, cmp_size, blockSize); - return 1; + inflateEnd(&z); + return false; } + inflateEnd(&z); } return true; } diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index b5ea2004bc..00a163fc5c 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -31,9 +31,6 @@ #endif -#undef DeleteFile - - #if HOST_IS_CASE_SENSITIVE static bool FixFilenameCase(const std::string &path, std::string &filename) @@ -248,7 +245,7 @@ bool DirectoryFileSystem::RenameFile(const std::string &from, const std::string return retValue; } -bool DirectoryFileSystem::DeleteFile(const std::string &filename) { +bool DirectoryFileSystem::RemoveFile(const std::string &filename) { std::string fullName = GetLocalPath(filename); #ifdef _WIN32 bool retValue = (::DeleteFileA(fullName.c_str()) == TRUE); @@ -266,7 +263,7 @@ bool DirectoryFileSystem::DeleteFile(const std::string &filename) { fullName = GetLocalPath(fullName); #ifdef _WIN32 - retValue = (::DeleteFile(fullName.c_str()) == TRUE); + retValue = (::DeleteFileA(fullName.c_str()) == TRUE); #else retValue = (0 == unlink(fullName.c_str())); #endif diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index 739a8427ad..61ee605406 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -66,7 +66,7 @@ public: bool MkDir(const std::string &dirname); bool RmDir(const std::string &dirname); bool RenameFile(const std::string &from, const std::string &to); - bool DeleteFile(const std::string &filename); + bool RemoveFile(const std::string &filename); bool GetHostPath(const std::string &inpath, std::string &outpath); private: diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index f055811ebc..d24efafeff 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -110,7 +110,7 @@ public: virtual bool MkDir(const std::string &dirname) = 0; virtual bool RmDir(const std::string &dirname) = 0; virtual bool RenameFile(const std::string &from, const std::string &to) = 0; - virtual bool DeleteFile(const std::string &filename) = 0; + virtual bool RemoveFile(const std::string &filename) = 0; virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0; }; @@ -130,7 +130,7 @@ public: virtual bool MkDir(const std::string &dirname) {return false;} virtual bool RmDir(const std::string &dirname) {return false;} virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} - virtual bool DeleteFile(const std::string &filename) {return false;} + virtual bool RemoveFile(const std::string &filename) {return false;} virtual bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;} }; diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index ab90080829..96f8b3f39e 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -44,7 +44,7 @@ public: virtual bool MkDir(const std::string &dirname) {return false;} virtual bool RmDir(const std::string &dirname) {return false;} virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} - virtual bool DeleteFile(const std::string &filename) {return false;} + virtual bool RemoveFile(const std::string &filename) {return false;} private: struct TreeEntry diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index b72ec7f098..7c971264ed 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -363,13 +363,13 @@ bool MetaFileSystem::RenameFile(const std::string &from, const std::string &to) } } -bool MetaFileSystem::DeleteFile(const std::string &filename) +bool MetaFileSystem::RemoveFile(const std::string &filename) { std::string of; IFileSystem *system; if (MapFilePath(filename, of, &system)) { - return system->DeleteFile(of); + return system->RemoveFile(of); } else { diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index b54e153613..b6bd5d9954 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -89,7 +89,7 @@ public: virtual bool MkDir(const std::string &dirname); virtual bool RmDir(const std::string &dirname); virtual bool RenameFile(const std::string &from, const std::string &to); - virtual bool DeleteFile(const std::string &filename); + virtual bool RemoveFile(const std::string &filename); // TODO: void IoCtl(...) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 8e61522cee..f6e4c4f583 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -25,6 +25,7 @@ #include "Thread.h" #include "../Core/CoreTiming.h" +#include "../Core/CoreParameter.h" #include "../MIPS/MIPS.h" #include "../HLE/HLE.h" #include "sceAudio.h" @@ -78,7 +79,7 @@ static int hCountTotal; //unused static int vCount; static int isVblank; static bool hasSetMode; -double lastFrameTime; +static double lastFrameTime; std::vector vblankWaitingThreads; @@ -269,12 +270,11 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { host->EndFrame(); #ifdef _WIN32 - static double lastFrameTime = 0.0; // Best place to throttle the frame rate on non vsynced platforms is probably here. Let's try it. time_update(); if (lastFrameTime == 0.0) lastFrameTime = time_now_d(); - if (!GetAsyncKeyState(VK_TAB)) { + if (!GetAsyncKeyState(VK_TAB) && !PSP_CoreParameter().headLess) { while (time_now_d() < lastFrameTime + 1.0 / 60.0) { Common::SleepCurrentThread(1); time_update(); @@ -357,6 +357,18 @@ u32 sceDisplaySetFramebuf() { return 0; } +bool __DisplayGetFramebuf(u8 **topaddr, u32 *linesize, u32 *pixelFormat, int mode) { + const FrameBufferState &fbState = mode == 1 ? latchedFramebuf : framebuf; + if (topaddr != NULL) + *topaddr = Memory::GetPointer(fbState.topaddr); + if (linesize != NULL) + *linesize = fbState.pspFramebufLinesize; + if (pixelFormat != NULL) + *pixelFormat = fbState.pspFramebufFormat; + + return true; +} + u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int mode) { const FrameBufferState &fbState = mode == 1 ? latchedFramebuf : framebuf; DEBUG_LOG(HLE,"sceDisplayGetFramebuf(*%08x = %08x, *%08x = %08x, *%08x = %08x, %i)", diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index c75faf93f7..fd6de956e3 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -26,6 +26,9 @@ void Register_sceDisplay(); // will return true once after every end-of-frame. bool __DisplayFrameDone(); +// Get information about the current framebuffer. +bool __DisplayGetFramebuf(u8 **topaddr, u32 *linesize, u32 *pixelFormat, int mode); + typedef void (*VblankCallback)(); // Listen for vblank events. Only register during init. void __DisplayListenVblank(VblankCallback callback); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 15c535cf4c..69c24f6c43 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -17,7 +17,6 @@ #ifdef _WIN32 #include -#undef DeleteFile #endif #include "../Config.h" @@ -38,6 +37,9 @@ #include "sceKernelMemory.h" #include "sceKernelThread.h" +// For headless screenshots. +#include "sceDisplay.h" + #define ERROR_ERRNO_FILE_NOT_FOUND 0x80010002 #define ERROR_MEMSTICK_DEVCTL_BAD_PARAMS 0x80220081 @@ -144,9 +146,10 @@ public: p.Do(callbackID); p.Do(callbackArg); p.Do(asyncResult); - p.Do(closePending); p.Do(pendingAsyncResult); p.Do(sectorBlockMode); + p.Do(closePending); + p.Do(info); p.Do(openMode); p.DoMarker("File"); } @@ -549,7 +552,7 @@ u32 sceIoRemove(const char *filename) { if(!pspFileSystem.GetFileInfo(filename).exists) return ERROR_ERRNO_FILE_NOT_FOUND; - pspFileSystem.DeleteFile(filename); + pspFileSystem.RemoveFile(filename); return 0; } @@ -782,6 +785,15 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, SaveState::Verify(); // TODO: Maybe save/load to a file just to be sure? return 0; + + case 0x20: // EMULATOR_DEVCTL__EMIT_SCREENSHOT + u8 *topaddr; + u32 linesize, pixelFormat; + + __DisplayGetFramebuf(&topaddr, &linesize, &pixelFormat, 0); + // TODO: Convert based on pixel format / mode / something? + host->SendDebugScreenshot(topaddr, linesize, 272); + return 0; } ERROR_LOG(HLE, "sceIoDevCtl: UNKNOWN PARAMETERS"); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index e74464b129..b31719cc08 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -157,6 +157,7 @@ void __KernelDoState(PointerWrap &p) p.DoMarker("KernelObjects"); __InterruptsDoState(p); + // Memory needs to be after kernel objects, which may free kernel memory. __KernelMemoryDoState(p); __KernelThreadingDoState(p); __KernelAlarmDoState(p); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index ad236c2039..293bab900e 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1249,7 +1249,7 @@ void sceKernelCheckThreadStack() u32 error; Thread *t = kernelObjects.Get(__KernelGetCurThread(), error); if (t) { - u32 diff = abs((long)((s64)t->stackBlock - (s64)currentMIPS->r[MIPS_REG_SP])); + u32 diff = labs((long)((s64)t->stackBlock - (s64)currentMIPS->r[MIPS_REG_SP])); WARN_LOG(HLE, "%i=sceKernelCheckThreadStack()", diff); RETURN(diff); } else { diff --git a/Core/Host.h b/Core/Host.h index 29cc5b2fc4..d426289d14 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -63,6 +63,7 @@ public: // Used for headless. virtual void SendDebugOutput(const std::string &output) {} + virtual void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) {} }; extern Host *host; diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 23a7096e8b..2673fcada6 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -57,6 +57,7 @@ namespace MIPSComp { s32 simm = (s32)(s16)(op & 0xFFFF); // sign extension u32 uimm = op & 0xFFFF; + u32 suimm = (u32)(s32)simm; int rt = _RT; int rs = _RS; @@ -113,29 +114,27 @@ namespace MIPSComp } break; -/* case 11: // R(rt) = R(rs) < uimm; break; //sltiu { gpr.MapDirtyIn(rt, rs); Operand2 op2; bool negated; - if (TryMakeOperand2_AllowNegation(uimm, op2, &negated)) { + if (TryMakeOperand2_AllowNegation(suimm, op2, &negated)) { if (!negated) CMP(gpr.R(rs), op2); else CMN(gpr.R(rs), op2); } else { - ARMABI_MOVI2R(R0, uimm); + MOVI2R(R0, suimm); CMP(gpr.R(rs), R0); } SetCC(CC_LO); - ARMABI_MOVI2R(gpr.R(rt), 1); + MOVI2R(gpr.R(rt), 1); SetCC(CC_HS); - ARMABI_MOVI2R(gpr.R(rt), 0); + MOVI2R(gpr.R(rt), 0); SetCC(CC_AL); } break; - */ case 15: // R(rt) = uimm << 16; //lui gpr.SetImm(rt, uimm << 16); diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index f19c93be48..580ce90510 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -266,10 +266,10 @@ const MIPSInstruction tableSpecial2[64] = //24 {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, //32 - INSTR("cvt.s.w", &Jit::Comp_Generic, Dis_FPU2op, Int_FPU2op, 0), + INSTR("cvt.s.w", &Jit::Comp_FPU2op, Dis_FPU2op, Int_FPU2op, 0), {-2}, {-2}, {-2}, //36 - INSTR("cvt.w.s", &Jit::Comp_Generic, Dis_FPU2op, Int_FPU2op, 0), + INSTR("cvt.w.s", &Jit::Comp_FPU2op, Dis_FPU2op, Int_FPU2op, 0), {-2}, INSTR("dis.int", &Jit::Comp_Generic, Dis_Generic, Int_Interrupt, 0), {-2}, diff --git a/Core/MIPS/x86/CompFPU.cpp b/Core/MIPS/x86/CompFPU.cpp index c7981b8d3b..7494d649f9 100644 --- a/Core/MIPS/x86/CompFPU.cpp +++ b/Core/MIPS/x86/CompFPU.cpp @@ -181,15 +181,12 @@ void Jit::Comp_FPU2op(u32 op) fpr.ReleaseSpillLocks(); break; - case 12: //FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s case 4: //F(fd) = sqrtf(F(fs)); break; //sqrt -/* fpr.Lock(fd, fs); // this probably works, just badly tested + fpr.SpillLock(fd, fs); // this probably works, just badly tested fpr.BindToRegister(fd, fd == fs, true); SQRTSS(fpr.RX(fd), fpr.R(fs)); - fpr.UnlockAll(); - break;*/ - Comp_Generic(op); + fpr.ReleaseSpillLocks(); return; case 13: //FsI(fd) = F(fs)>=0 ? (int)floorf(F(fs)) : (int)ceilf(F(fs)); break;//trunc.w.s @@ -200,9 +197,15 @@ void Jit::Comp_FPU2op(u32 op) fpr.ReleaseSpillLocks(); break; + case 32: //F(fd) = (float)FsI(fs); break; //cvt.s.w + fpr.StoreFromRegister(fs); + CVTSI2SS(XMM0, fpr.R(fs)); + MOVSS(fpr.R(fd), XMM0); + break; + + case 12: //FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s case 14: //FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s case 15: //FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s - case 32: //F(fd) = (float)FsI(fs); break; //cvt.s.w case 36: //FsI(fd) = (int) F(fs); break; //cvt.w.s default: Comp_Generic(op); diff --git a/Core/MIPS/x86/RegCacheFPU.cpp b/Core/MIPS/x86/RegCacheFPU.cpp index 9be44494ba..9a2027e1e3 100644 --- a/Core/MIPS/x86/RegCacheFPU.cpp +++ b/Core/MIPS/x86/RegCacheFPU.cpp @@ -15,7 +15,6 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#pragma once #include "Common/Log.h" #include "Common/x64Emitter.h" @@ -81,7 +80,7 @@ void FPURegCache::ReleaseSpillLocks() { regs[i].locked = false; } -void FPURegCache::BindToRegister(int i, bool doLoad, bool makeDirty) { +void FPURegCache::BindToRegister(const int i, bool doLoad, bool makeDirty) { _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - load - imm"); if (!regs[i].away) { // Reg is at home in the memory register file. Let's pull it out. @@ -206,4 +205,4 @@ void FPURegCache::FlushR(X64Reg reg) { if (xregs[reg].mipsReg != -1) { StoreFromRegister(xregs[reg].mipsReg); } -} \ No newline at end of file +} diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index bf4a2a8fde..8a450081c3 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -77,9 +77,10 @@ namespace SaveState Memory::DoState(p); MemoryStick_DoState(p); currentMIPS->DoState(p); - pspFileSystem.DoState(p); HLEDoState(p); __KernelDoState(p); + // Kernel object destructors might close open files, so do the filesystem last. + pspFileSystem.DoState(p); } void Enqueue(SaveState::Operation op) diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index b12e2e18c5..9f6b4b8f50 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -124,7 +124,6 @@ static const int flushBeforeCommandList[] = { GE_CMD_SIGNAL, GE_CMD_FINISH, GE_CMD_BJUMP, - GE_CMD_OFFSETADDR, GE_CMD_REGION1,GE_CMD_REGION2, GE_CMD_FRAMEBUFPTR, GE_CMD_FRAMEBUFWIDTH, @@ -515,6 +514,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { // Throughmode changed, let's make the proj matrix dirty. shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); } + shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET); // This sets through-mode or not, as well. break; @@ -679,6 +679,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_TEXSIZE0: gstate_c.curTextureWidth = 1 << (gstate.texsize[0] & 0xf); gstate_c.curTextureHeight = 1 << ((gstate.texsize[0]>>8) & 0xf); + shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET); //fall thru - ignoring the mipmap sizes for now case GE_CMD_TEXSIZE1: case GE_CMD_TEXSIZE2: @@ -799,16 +800,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_VIEWPORTY1: case GE_CMD_VIEWPORTX2: case GE_CMD_VIEWPORTY2: - break; - case GE_CMD_VIEWPORTZ1: - gstate_c.zScale = getFloat24(data) / 65535.f; - break; - case GE_CMD_VIEWPORTZ2: - gstate_c.zOff = getFloat24(data) / 65535.f; - break; - case GE_CMD_LIGHTENABLE0: case GE_CMD_LIGHTENABLE1: case GE_CMD_LIGHTENABLE2: diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index d2e31fa334..bafae7aacb 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -77,7 +77,7 @@ void GenerateFragmentShader(char *buffer) #if defined(GLSL_ES_1_0) - WRITE(p, "precision mediump float;\n"); + WRITE(p, "precision lowp float;\n"); #elif !defined(FORCE_OPENGL_2_0) WRITE(p, "#version 110\n"); #endif @@ -86,30 +86,38 @@ void GenerateFragmentShader(char *buffer) int doTexture = (gstate.textureMapEnable & 1) && !(gstate.clearmode & 1); bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear(); + bool enableAlphaTest = (gstate.alphaTestEnable & 1) && !gstate.isModeClear(); + bool enableColorTest = (gstate.colorTestEnable & 1) && !gstate.isModeClear(); + if (doTexture) WRITE(p, "uniform sampler2D tex;\n"); - if ((gstate.alphaTestEnable & 1) || (gstate.colorTestEnable & 1)) { + if (enableAlphaTest || enableColorTest) { WRITE(p, "uniform vec4 u_alphacolorref;\n"); } - WRITE(p, "uniform vec3 u_texenv;\n"); + if (gstate.textureMapEnable & 1) { + WRITE(p, "uniform vec3 u_texenv;\n"); + } WRITE(p, "varying vec4 v_color0;\n"); if (lmode) WRITE(p, "varying vec3 v_color1;\n"); if (enableFog) { WRITE(p, "uniform vec3 u_fogcolor;\n"); +#if defined(GLSL_ES_1_0) + WRITE(p, "varying mediump float v_fogdepth;\n"); +#else WRITE(p, "varying float v_fogdepth;\n"); +#endif } if (doTexture) WRITE(p, "varying vec2 v_texcoord;\n"); WRITE(p, "void main() {\n"); - WRITE(p, " vec4 v;\n"); if (gstate.clearmode & 1) { // Clear mode does not allow any fancy shading. - WRITE(p, " v = v_color0;\n"); + WRITE(p, " gl_FragColor = v_color0;\n"); } else { @@ -119,59 +127,58 @@ void GenerateFragmentShader(char *buffer) WRITE(p, " vec4 s = vec4(v_color1, 0.0);\n"); secondary = " + s"; } else { - WRITE(p, " vec4 s = vec4(0.0, 0.0, 0.0, 0.0);\n"); secondary = ""; } if (gstate.textureMapEnable & 1) { WRITE(p, " vec4 t = texture2D(tex, v_texcoord);\n"); - WRITE(p, " vec4 p = clamp(v_color0, 0.0, 1.0);\n"); + WRITE(p, " vec4 p = v_color0;\n"); if (gstate.texfunc & 0x100) { // texfmt == RGBA switch (gstate.texfunc & 0x7) { case GE_TEXFUNC_MODULATE: - WRITE(p, " v = t * p%s;\n", secondary); break; + WRITE(p, " vec4 v = t * p%s;\n", secondary); break; case GE_TEXFUNC_DECAL: - WRITE(p, " v = vec4(1.0 - t.a * p.rgb + t.a * u_texenv.rgb, p.a)%s;\n", secondary); break; + WRITE(p, " vec4 v = vec4(1.0 - t.a * p.rgb + t.a * u_texenv.rgb, p.a)%s;\n", secondary); break; case GE_TEXFUNC_BLEND: - WRITE(p, " v = vec4((1.0 - t.rgb) * p.rgb + t.rgb * u_texenv.rgb, p.a * t.a)%s;\n", secondary); break; + WRITE(p, " vec4 v = vec4((1.0 - t.rgb) * p.rgb + t.rgb * u_texenv.rgb, p.a * t.a)%s;\n", secondary); break; case GE_TEXFUNC_REPLACE: - WRITE(p, " v = t%s;\n", secondary); break; + WRITE(p, " vec4 v = t%s;\n", secondary); break; case GE_TEXFUNC_ADD: - WRITE(p, " v = vec4(t.rgb + p.rgb, p.a * t.a)%s;\n", secondary); break; + WRITE(p, " vec4 v = vec4(t.rgb + p.rgb, p.a * t.a)%s;\n", secondary); break; default: - WRITE(p, " v = p;\n"); break; + WRITE(p, " vec4 v = p;\n"); break; } } else { // texfmt == RGB switch (gstate.texfunc & 0x7) { case GE_TEXFUNC_MODULATE: - WRITE(p, " v = vec4(t.rgb * p.rgb, p.a)%s;\n", secondary); break; + WRITE(p, " vec4 v = vec4(t.rgb * p.rgb, p.a)%s;\n", secondary); break; case GE_TEXFUNC_DECAL: - WRITE(p, " v = vec4(t.rgb, p.a)%s;\n", secondary); break; + WRITE(p, " vec4 v = vec4(t.rgb, p.a)%s;\n", secondary); break; case GE_TEXFUNC_BLEND: - WRITE(p, " v = vec4(1.0 - t.rgb) * p.rgb + t.rgb * u_texenv.rgb, p.a)%s;\n", secondary); break; + WRITE(p, " vec4 v = vec4(1.0 - t.rgb) * p.rgb + t.rgb * u_texenv.rgb, p.a)%s;\n", secondary); break; case GE_TEXFUNC_REPLACE: - WRITE(p, " v = vec4(t.rgb, p.a)%s;\n", secondary); break; + WRITE(p, " vec4 v = vec4(t.rgb, p.a)%s;\n", secondary); break; case GE_TEXFUNC_ADD: - WRITE(p, " v = vec4(t.rgb + p.rgb, p.a)%s;\n", secondary); break; + WRITE(p, " vec4 v = vec4(t.rgb + p.rgb, p.a)%s;\n", secondary); break; default: - WRITE(p, " v = p;\n"); break; + WRITE(p, " vec4 v = p;\n"); break; } } } else { // No texture mapping - WRITE(p, " v = clamp(v_color0, 0.0, 1.0)%s;\n", secondary); + WRITE(p, " vec4 v = v_color0 %s;\n", secondary); } // Color doubling if (gstate.texfunc & 0x10000) { - WRITE(p, " v = v * vec4(2.0, 2.0, 2.0, 2.0);"); + WRITE(p, " v = v * 2.0;\n"); } - if (gstate.alphaTestEnable & 1) { + if (enableAlphaTest) { int alphaTestFunc = gstate.alphatest & 7; const char *alphaTestFuncs[] = { "#", "#", " == ", " != ", " < ", " <= ", " > ", " >= " }; // never/always don't make sense if (alphaTestFuncs[alphaTestFunc][0] != '#') - WRITE(p, "if (!(v.a %s u_alphacolorref.a)) discard;", alphaTestFuncs[alphaTestFunc]); + WRITE(p, " if (!(v.a %s u_alphacolorref.a)) discard;\n", alphaTestFuncs[alphaTestFunc]); } // Disabled for now until we actually find a need for it. @@ -187,20 +194,20 @@ void GenerateFragmentShader(char *buffer) if (enableFog) { WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n"); - WRITE(p, " v = mix(vec4(u_fogcolor, v.a), v, fogCoef);\n"); + WRITE(p, " gl_FragColor = mix(vec4(u_fogcolor, v.a), v, fogCoef);\n"); // WRITE(p, " v.x = v_depth;\n"); + } else { + WRITE(p, " gl_FragColor = v;\n"); } - } #ifdef DEBUG_SHADER if (doTexture) { - WRITE(p, " v = texture2D(tex, v_texcoord);\n"); + WRITE(p, " gl_FragColor = texture2D(tex, v_texcoord);\n"); } else { - WRITE(p, " v = vec4(1,0,1,1);\n"); + WRITE(p, " gl_FragColor = vec4(1,0,1,1);\n"); } #endif - WRITE(p, " gl_FragColor = v;\n"); WRITE(p, "}\n"); } diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 71957ec335..2ce7a880dd 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -39,7 +39,7 @@ Shader::Shader(const char *code, uint32_t shaderType) { OutputDebugString(code); #endif shader = glCreateShader(shaderType); - glShaderSource(shader, 1, &code, 0); + glShaderSource(shader, 1, &code, 0); glCompileShader(shader); GLint success; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); @@ -264,7 +264,16 @@ void LinkedShader::updateUniforms() { // Texturing if (u_uvscaleoffset != -1 && (dirtyUniforms & DIRTY_UVSCALEOFFSET)) { - const float uvscaleoff[4] = { gstate_c.uScale, gstate_c.vScale, gstate_c.uOff, gstate_c.vOff}; + float uvscaleoff[4] = { gstate_c.uScale, gstate_c.vScale, gstate_c.uOff, gstate_c.vOff}; + if (gstate.isModeThrough()) { + uvscaleoff[0] /= gstate_c.curTextureWidth; + uvscaleoff[1] /= gstate_c.curTextureHeight; + uvscaleoff[2] /= gstate_c.curTextureWidth; + uvscaleoff[3] /= gstate_c.curTextureHeight; + } else { + uvscaleoff[0] *= 2.0f; + uvscaleoff[1] *= 2.0f; + } glUniform4fv(u_uvscaleoffset, 1, uvscaleoff); } diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index aa21957afc..9d9deca257 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -171,7 +171,7 @@ void TransformDrawEngine::ApplyDrawState(int prim) { bool wantDepthTest = gstate.isModeClear() || gstate.isDepthTestEnabled(); glstate.depthTest.set(wantDepthTest); if(wantDepthTest) { - // Force GL_ALWAYS if mode clear + // Force GL_ALWAYS if mode clear - without depth test, no depth write. int depthTestFunc = gstate.isModeClear() ? 1 : gstate.getDepthTestFunc(); glstate.depthFunc.set(ztests[depthTestFunc]); } @@ -208,10 +208,6 @@ void TransformDrawEngine::ApplyDrawState(int prim) { bool wantDepthWrite = gstate.isModeClear() || gstate.isDepthWriteEnabled(); glstate.depthWrite.set(wantDepthWrite ? GL_TRUE : GL_FALSE); - - float depthRangeMin = gstate_c.zOff - gstate_c.zScale; - float depthRangeMax = gstate_c.zOff + gstate_c.zScale; - glstate.depthRange.set(depthRangeMin, depthRangeMax); } void TransformDrawEngine::UpdateViewportAndProjection() { @@ -240,14 +236,13 @@ void TransformDrawEngine::UpdateViewportAndProjection() { if (throughmode) { // No viewport transform here. Let's experiment with using region. glstate.viewport.set((0 + regionX1) * renderWidthFactor, (0 - regionY1) * renderHeightFactor, (regionX2 - regionX1) * renderWidthFactor, (regionY2 - regionY1) * renderHeightFactor); + glstate.depthRange.set(1.0, 0.0); } else { // These we can turn into a glViewport call, offset by offsetX and offsetY. Math after. float vpXa = getFloat24(gstate.viewportx1); float vpXb = getFloat24(gstate.viewportx2); float vpYa = getFloat24(gstate.viewporty1); float vpYb = getFloat24(gstate.viewporty2); - float vpZa = getFloat24(gstate.viewportz1); // / 65536.0f should map it to OpenGL's 0.0-1.0 Z range - float vpZb = getFloat24(gstate.viewportz2); // / 65536.0f // The viewport transform appears to go like this: // Xscreen = -offsetX + vpXb + vpXa * Xview @@ -263,10 +258,6 @@ void TransformDrawEngine::UpdateViewportAndProjection() { float vpWidth = fabsf(gstate_c.vpWidth); float vpHeight = fabsf(gstate_c.vpHeight); - // TODO: These two should feed into glDepthRange somehow. - float vpZ0 = (vpZb - vpZa) / 65536.0f; - float vpZ1 = (vpZa * 2) / 65536.0f; - vpX0 *= renderWidthFactor; vpY0 *= renderHeightFactor; vpWidth *= renderWidthFactor; @@ -277,5 +268,11 @@ void TransformDrawEngine::UpdateViewportAndProjection() { glstate.viewport.set(vpX0, vpY0, vpWidth, vpHeight); // Sadly, as glViewport takes integers, we will not be able to support sub pixel offsets this way. But meh. // shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); + + float zScale = getFloat24(gstate.viewportz1) / 65535.f; + float zOff = getFloat24(gstate.viewportz2) / 65535.f; + float depthRangeMin = zOff - zScale; + float depthRangeMax = zOff + zScale; + glstate.depthRange.set(depthRangeMin, depthRangeMax); } } diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index abd968d292..7714961d2b 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -112,6 +112,8 @@ void TextureCache::InvalidateAll(bool force) { } TextureCache::TexCacheEntry *TextureCache::GetEntryAt(u32 texaddr) { + // If no CLUT, as in framebuffer textures, cache key is simply texaddr. + auto iter = cache.find(texaddr); for (auto entry = cache.begin(); entry != cache.end(); ++entry) { if (entry->second.addr == texaddr) { return &entry->second; @@ -121,11 +123,13 @@ TextureCache::TexCacheEntry *TextureCache::GetEntryAt(u32 texaddr) { } void TextureCache::NotifyFramebuffer(u32 address, FBO *fbo) { + // Must be in VRAM so | 0x04000000 it is. TexCacheEntry *entry = GetEntryAt(address | 0x04000000); if (entry) { INFO_LOG(HLE, "Render to texture detected at %08x!", address); if (!entry->fbo) entry->fbo = fbo; + // TODO: Delete the original non-fbo texture too. } } @@ -370,9 +374,9 @@ GLenum getClutDestFormat(GEPaletteFormat format) { return 0; } -const u8 texByteAlignMap[] = {2, 2, 2, 4}; +static const u8 texByteAlignMap[] = {2, 2, 2, 4}; -const GLuint MinFiltGL[8] = { +static const GLuint MinFiltGL[8] = { GL_NEAREST, GL_LINEAR, GL_NEAREST, @@ -383,11 +387,16 @@ const GLuint MinFiltGL[8] = { GL_LINEAR_MIPMAP_LINEAR, }; -const GLuint MagFiltGL[2] = { +static const GLuint MagFiltGL[2] = { GL_NEAREST, GL_LINEAR }; +// OpenGL ES 2.0 workaround. Let's see if this hackery works. +#ifndef GL_TEXTURE_LOD_BIAS +#define GL_TEXTURE_LOD_BIAS 0x8501 +#endif + // This should not have to be done per texture! OpenGL is silly yo // TODO: Dirty-check this against the current texture. void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { @@ -396,8 +405,17 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { bool sClamp = gstate.texwrap & 1; bool tClamp = (gstate.texwrap>>8) & 1; - // TODO: remove this line when we support mipmaps - minFilt &= 1; // no mipmaps yet + if (entry.maxLevel == 0) { + // Enforce no mip filtering, for safety. + minFilt &= 1; // no mipmaps yet + } else { + // TODO: Is this a signed value? Which direction? + float lodBias = 0.0; // -(float)((gstate.texlevel >> 16) & 0xFF) / 16.0f; + if (force || entry.lodBias != lodBias) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, lodBias); + entry.lodBias = lodBias; + } + } if (g_Config.bLinearFiltering) { magFilt |= 1; @@ -462,7 +480,7 @@ struct DXT5Block { }; static inline u32 makecol(int r, int g, int b, int a) { - return (a << 24)|(r << 16)|(g << 8)|b; + return (a << 24) | (r << 16) | (g << 8) | b; } // This could probably be done faster by decoding two or four blocks at a time with SSE/NEON. @@ -641,7 +659,6 @@ void TextureCache::SetTexture() { return; } - u8 level = 0; u32 format = gstate.texformat & 0xF; if (format >= 11) { ERROR_LOG(G3D, "Unknown texture format %i", format); @@ -649,38 +666,50 @@ void TextureCache::SetTexture() { } bool hasClut = formatUsesClut[format]; + const u8 *texptr = Memory::GetPointer(texaddr); + u32 clutformat = gstate.clutformat & 3; u32 clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2); - const u8 *texptr = Memory::GetPointer(texaddr); - u32 texhash = texptr ? MiniHash((const u32*)texptr) : 0; + u64 cachekey = texaddr; + if (formatUsesClut[format]) + cachekey |= (u64)clutaddr << 32; - u64 cachekey = texaddr ^ texhash; - if (hasClut) { - cachekey |= (u64) clutaddr << 32; + int maxLevel = ((gstate.texmode >> 16) & 0x7); + + // Adjust maxLevel to actually present levels.. + for (int i = 0; i <= maxLevel; i++) { + // If encountering levels pointing to nothing, adjust max level. + u32 levelTexaddr = (gstate.texaddr[i] & 0xFFFFF0) | ((gstate.texbufwidth[i] << 8) & 0x0F000000); + if (!Memory::IsValidAddress(levelTexaddr)) { + maxLevel = i - 1; + break; + } } + u32 texhash = MiniHash((const u32 *)Memory::GetPointer(texaddr)); + int w = 1 << (gstate.texsize[0] & 0xf); - int h = 1 << ((gstate.texsize[0]>>8) & 0xf); + int h = 1 << ((gstate.texsize[0] >> 8) & 0xf); - // Check for FBO - slow! - TexCacheEntry *fboEntry = GetEntryAt(texaddr); - if (fboEntry && fboEntry->fbo) { - fbo_bind_color_as_texture(fboEntry->fbo, 0); - UpdateSamplingParams(*fboEntry, false); - - int fbow, fboh; - fbo_get_dimensions(fboEntry->fbo, &fbow, &fboh); - - gstate_c.curTextureWidth = w / fbow; // except not - - gstate_c.curTextureHeight = h / fboh; - return; - } - TexCache::iterator iter = cache.find(cachekey); if (iter != cache.end()) { - //Validate the texture here (width, height etc) TexCacheEntry &entry = iter->second; + // Check for FBO - slow! + if (entry.fbo) { + fbo_bind_color_as_texture(entry.fbo, 0); + UpdateSamplingParams(entry, false); + + int fbow, fboh; + fbo_get_dimensions(entry.fbo, &fbow, &fboh); + + // Almost certain this isn't right. + gstate_c.curTextureWidth = fbow; + gstate_c.curTextureHeight = fboh; + return; + } + + //Validate the texture here (width, height etc) int dim = gstate.texsize[0] & 0xF0F; bool match = true; @@ -710,6 +739,8 @@ void TextureCache::SetTexture() { match = false; } } + if (entry.maxLevel != maxLevel) + match = false; if (match) { //got one! @@ -738,6 +769,8 @@ void TextureCache::SetTexture() { entry.format = format; entry.frameCounter = gpuStats.numFrames; entry.fbo = 0; + entry.maxLevel = maxLevel; + entry.lodBias = 0.0f; if (format >= GE_TFMT_CLUT4 && format <= GE_TFMT_CLUT32) { entry.clutformat = clutformat; @@ -759,38 +792,86 @@ void TextureCache::SetTexture() { for (u32 i = 0; i < (entry.sizeInRAM * 2) / 4; ++i) entry.fullhash += *checkp++; - gstate_c.curTextureWidth=w; - gstate_c.curTextureHeight=h; - GLenum dstFmt = 0; - u32 texByteAlign = 1; + gstate_c.curTextureWidth = w; + gstate_c.curTextureHeight = h; + glGenTextures(1, &entry.texture); + glBindTexture(GL_TEXTURE_2D, entry.texture); + +#ifdef USING_GLES2 + // GLES2 doesn't have support for a "Max lod" which is critical as PSP games often + // don't specify mips all the way down. As a result, we either need to manually generate + // the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not + // be as good quality as the game's own (might even be better in some cases though). + + // For now, I choose to use autogen mips on GLES2 and the game's own on other platforms. + // As is usual, GLES3 will solve this problem nicely but wide distribution of that is + // years away. + LoadTextureLevel(entry, 0); + if (entry.maxLevel > 0) + glGenerateMipmap(GL_TEXTURE_2D); +#else + for (int i = 0; i <= entry.maxLevel; i++) { + LoadTextureLevel(entry, i); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, entry.maxLevel); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, entry.maxLevel); +#endif + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4); + + UpdateSamplingParams(entry, true); + + //glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + //glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + cache[cachekey] = entry; +} + + +void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level) +{ void *finalBuf = NULL; + // TODO: only do this once + u32 texByteAlign = 1; + // TODO: Look into using BGRA for 32-bit textures when the GL_EXT_texture_format_BGRA8888 extension is available, as it's faster than RGBA on some chips. + GLenum dstFmt = 0; // TODO: Actually decode the mipmaps. - switch (format) + u32 texaddr = (gstate.texaddr[level] & 0xFFFFF0) | ((gstate.texbufwidth[level] << 8) & 0x0F000000); + + int bufw = gstate.texbufwidth[level] & 0x3ff; + + + int w = 1 << (gstate.texsize[level] & 0xf); + int h = 1 << ((gstate.texsize[level] >> 8) & 0xf); + const u8 *texptr = Memory::GetPointer(texaddr); + + switch (entry.format) { case GE_TFMT_CLUT4: - dstFmt = getClutDestFormat((GEPaletteFormat)(gstate.clutformat & 3)); + dstFmt = getClutDestFormat((GEPaletteFormat)(entry.clutformat)); - switch (clutformat) { + switch (entry.clutformat) { case GE_CMODE_16BIT_BGR5650: case GE_CMODE_16BIT_ABGR5551: case GE_CMODE_16BIT_ABGR4444: { ReadClut16(clutBuf16); const u16 *clut = clutBuf16; - u32 clutSharingOff = 0;//gstate.mipmapShareClut ? 0 : level * 16; + u32 clutSharingOffset = 0; //(gstate.mipmapShareClut & 1) ? 0 : level * 16; texByteAlign = 2; if (!(gstate.texmode & 1)) { const u8 *addr = Memory::GetPointer(texaddr); for (int i = 0; i < bufw * h; i += 2) { u8 index = *addr++; - tmpTexBuf16[i + 0] = clut[GetClutIndex((index >> 0) & 0xf) + clutSharingOff]; - tmpTexBuf16[i + 1] = clut[GetClutIndex((index >> 4) & 0xf) + clutSharingOff]; + tmpTexBuf16[i + 0] = clut[GetClutIndex((index >> 0) & 0xf) + clutSharingOffset]; + tmpTexBuf16[i + 1] = clut[GetClutIndex((index >> 4) & 0xf) + clutSharingOffset]; } } else { UnswizzleFromMem(texaddr, 0, level); @@ -800,7 +881,7 @@ void TextureCache::SetTexture() { u32 k, index; for (k = 0; k < 8; k++) { index = (n >> (k * 4)) & 0xf; - tmpTexBuf16[i + k] = clut[GetClutIndex(index) + clutSharingOff]; + tmpTexBuf16[i + k] = clut[GetClutIndex(index) + clutSharingOffset]; } } } @@ -863,11 +944,11 @@ void TextureCache::SetTexture() { case GE_TFMT_4444: case GE_TFMT_5551: case GE_TFMT_5650: - if (format == GE_TFMT_4444) + if (entry.format == GE_TFMT_4444) dstFmt = GL_UNSIGNED_SHORT_4_4_4_4; - else if (format == GE_TFMT_5551) + else if (entry.format == GE_TFMT_5551) dstFmt = GL_UNSIGNED_SHORT_5_5_5_1; - else if (format == GE_TFMT_5650) + else if (entry.format == GE_TFMT_5650) dstFmt = GL_UNSIGNED_SHORT_5_6_5; texByteAlign = 2; @@ -931,7 +1012,7 @@ void TextureCache::SetTexture() { break; case GE_TFMT_DXT5: - ERROR_LOG(G3D, "Unhandled compressed texture, format %i! swizzle=%i", format, gstate.texmode & 1); + ERROR_LOG(G3D, "Unhandled compressed texture, format %i! swizzle=%i", entry.format, gstate.texmode & 1); dstFmt = GL_UNSIGNED_BYTE; { u32 *dst = tmpTexBuf32; @@ -951,7 +1032,7 @@ void TextureCache::SetTexture() { break; default: - ERROR_LOG(G3D, "Unknown Texture Format %d!!!", format); + ERROR_LOG(G3D, "Unknown Texture Format %d!!!", entry.format); finalBuf = tmpTexBuf32; return; } @@ -999,19 +1080,8 @@ void TextureCache::SetTexture() { //glPixelStorei(GL_PACK_ROW_LENGTH, bufw); glPixelStorei(GL_PACK_ALIGNMENT, texByteAlign); - INFO_LOG(G3D, "Creating texture %i from %08x: %i x %i (stride: %i). fmt: %i", entry.texture, entry.addr, w, h, bufw, entry.format); + INFO_LOG(HLE, "Creating texture level %i/%i from %08x: %i x %i (stride: %i). fmt: %i", level, entry.maxLevel, texaddr, w, h, bufw, entry.format); - glGenTextures(1, &entry.texture); - glBindTexture(GL_TEXTURE_2D, entry.texture); GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; - glTexImage2D(GL_TEXTURE_2D, 0, components, w, h, 0, components, dstFmt, finalBuf); - // glGenerateMipmap(GL_TEXTURE_2D); - UpdateSamplingParams(entry, true); - - //glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - //glPixelStorei(GL_PACK_ROW_LENGTH, 0); - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - cache[cachekey] = entry; + glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components, dstFmt, finalBuf); } diff --git a/GPU/GLES/TextureCache.h b/GPU/GLES/TextureCache.h index 0bf593764a..7f8e4ad9a7 100644 --- a/GPU/GLES/TextureCache.h +++ b/GPU/GLES/TextureCache.h @@ -58,6 +58,8 @@ private: u32 texture; //GLuint int invalidHint; u32 fullhash; + int maxLevel; + float lodBias; // Cache the current filter settings so we can avoid setting it again. u8 magFilt; @@ -70,6 +72,7 @@ private: void *UnswizzleFromMem(u32 texaddr, u32 bytesPerPixel, u32 level); void *readIndexedTex(int level, u32 texaddr, int bytesPerIndex); void UpdateSamplingParams(TexCacheEntry &entry, bool force); + void LoadTextureLevel(TexCacheEntry &entry, int level); TexCacheEntry *GetEntryAt(u32 texaddr); diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index e43a0d5d1a..2e61fe289d 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -262,7 +262,7 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ } Color4 lightDiff(gstate_c.lightColor[1][l], 0.0f); - Color4 diff = (lightDiff * *diffuse) * (dot * lightScale); + Color4 diff = (lightDiff * *diffuse) * (max(dot, 0.0f) * lightScale); // Real PSP specular Vec3 toViewer(0,0,1); @@ -276,10 +276,10 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ halfVec.Normalize(); dot = halfVec * norm; - if (dot >= 0) + if (dot > 0) { Color4 lightSpec(gstate_c.lightColor[2][l], 0.0f); - lightSum1 += (lightSpec * *specular * (powf(dot, specCoef_)*lightScale)); + lightSum1 += (lightSpec * *specular * (powf(dot, specCoef_) * lightScale)); } } dots[l] = dot; @@ -315,6 +315,12 @@ static const GlTypeInfo GLComp[] = { {GL_UNSIGNED_BYTE, 2, GL_TRUE},// DEC_U8_2, {GL_UNSIGNED_BYTE, 3, GL_TRUE},// DEC_U8_3, {GL_UNSIGNED_BYTE, 4, GL_TRUE},// DEC_U8_4, + {GL_UNSIGNED_SHORT, 1, GL_TRUE},// DEC_U16_1, + {GL_UNSIGNED_SHORT, 2, GL_TRUE},// DEC_U16_2, + {GL_UNSIGNED_SHORT, 3, GL_TRUE},// DEC_U16_3, + {GL_UNSIGNED_SHORT, 4, GL_TRUE},// DEC_U16_4, + {GL_UNSIGNED_BYTE, 2, GL_FALSE},// DEC_U8A_2, + {GL_UNSIGNED_SHORT, 2, GL_FALSE},// DEC_U16A_2, }; static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) { @@ -399,6 +405,13 @@ void TransformDrawEngine::SoftwareTransformAndDraw( vertexCount = 0x10000/3; #endif + float uscale = 1.0f; + float vscale = 1.0f; + if (throughmode) { + uscale /= gstate_c.curTextureWidth; + vscale /= gstate_c.curTextureHeight; + } + Lighter lighter; float fog_end = getFloat24(gstate.fog1); float fog_slope = getFloat24(gstate.fog2); @@ -430,6 +443,9 @@ void TransformDrawEngine::SoftwareTransformAndDraw( if (reader.hasUV()) { reader.ReadUV(uv); + + uv[0] *= uscale; + uv[1] *= vscale; } fogCoef = 1.0f; // Scale UV? @@ -529,8 +545,8 @@ void TransformDrawEngine::SoftwareTransformAndDraw( { case 0: // UV mapping // Texture scale/offset is only performed in this mode. - uv[0] = ruv[0]*gstate_c.uScale + gstate_c.uOff; - uv[1] = ruv[1]*gstate_c.vScale + gstate_c.vOff; + uv[0] = uscale * (ruv[0]*gstate_c.uScale + gstate_c.uOff); + uv[1] = vscale * (ruv[1]*gstate_c.vScale + gstate_c.vOff); break; case 1: { @@ -580,8 +596,12 @@ void TransformDrawEngine::SoftwareTransformAndDraw( memcpy(&transformed[index].x, v, 3 * sizeof(float)); transformed[index].fog = fogCoef; memcpy(&transformed[index].u, uv, 2 * sizeof(float)); - memcpy(&transformed[index].color0, c0, 4 * sizeof(float)); - memcpy(&transformed[index].color1, c1, 3 * sizeof(float)); + for (int i = 0; i < 4; i++) { + transformed[index].color0[i] = c0[i] * 255.0f; + } + for (int i = 0; i < 4; i++) { + transformed[index].color1[i] = c1[i] * 255.0f; + } } // Step 2: expand rectangles. @@ -674,8 +694,8 @@ void TransformDrawEngine::SoftwareTransformAndDraw( } glVertexAttribPointer(program->a_position, 4, GL_FLOAT, GL_FALSE, vertexSize, drawBuffer); if (program->a_texcoord != -1) glVertexAttribPointer(program->a_texcoord, 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 4 * 4); - if (program->a_color0 != -1) glVertexAttribPointer(program->a_color0, 4, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 6 * 4); - if (program->a_color1 != -1) glVertexAttribPointer(program->a_color1, 3, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 10 * 4); + if (program->a_color0 != -1) glVertexAttribPointer(program->a_color0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 6 * 4); + if (program->a_color1 != -1) glVertexAttribPointer(program->a_color1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 7 * 4); if (drawIndexed) { if (useVBO) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_[curVbo_]); @@ -800,6 +820,7 @@ u32 TransformDrawEngine::ComputeHash() { u32 fullhash = 0; int vertexSize = dec.GetDecVtxFmt().stride; + // TODO: Add some caps both for numDrawCalls and num verts to check? for (int i = 0; i < numDrawCalls; i++) { if (!drawCalls[i].inds) { fullhash += CityHash32((const char *)drawCalls[i].verts, vertexSize * drawCalls[i].vertexCount); @@ -842,7 +863,7 @@ void TransformDrawEngine::ClearTrackedVertexArrays() { void TransformDrawEngine::DecimateTrackedVertexArrays() { int threshold = gpuStats.numFrames - VAI_KILL_AGE; for (auto iter = vai_.begin(); iter != vai_.end(); ) { - if (iter->second->lastFrame < threshold ) { + if (iter->second->lastFrame < threshold) { delete iter->second; vai_.erase(iter++); } @@ -901,6 +922,7 @@ void TransformDrawEngine::Flush() { u32 dataHash = ComputeHash(); vai->hash = dataHash; vai->status = VertexArrayInfo::VAI_HASHING; + vai->drawsUntilNextFullHash = 0; DecodeVerts(); // writes to indexGen goto rotateVBO; } @@ -909,27 +931,33 @@ void TransformDrawEngine::Flush() { // But if we get this far it's likely to be worth creating a vertex buffer. case VertexArrayInfo::VAI_HASHING: { - u32 newHash = ComputeHash(); vai->numDraws++; - // TODO: tweak - if (vai->numDraws > 100000) { - vai->status = VertexArrayInfo::VAI_RELIABLE; - } - if (newHash == vai->hash) { - gpuStats.numCachedDrawCalls++; + if (vai->drawsUntilNextFullHash == 0) { + u32 newHash = ComputeHash(); + // exponential backoff up to 16 frames + vai->drawsUntilNextFullHash = std::min(16, vai->numDraws); + // TODO: tweak + //if (vai->numDraws > 1000) { + // vai->status = VertexArrayInfo::VAI_RELIABLE; + //} + if (newHash != vai->hash) { + vai->status = VertexArrayInfo::VAI_UNRELIABLE; + if (vai->vbo) { + glDeleteBuffers(1, &vai->vbo); + vai->vbo = 0; + } + if (vai->ebo) { + glDeleteBuffers(1, &vai->ebo); + vai->ebo = 0; + } + DecodeVerts(); + goto rotateVBO; + } } else { - vai->status = VertexArrayInfo::VAI_UNRELIABLE; - if (vai->vbo) { - glDeleteBuffers(1, &vai->vbo); - vai->vbo = 0; - } - if (vai->ebo) { - glDeleteBuffers(1, &vai->ebo); - vai->ebo = 0; - } - DecodeVerts(); - goto rotateVBO; + vai->drawsUntilNextFullHash--; + // TODO: "mini-hashing" the first 32 bytes of the vertex/index data or something. } + if (vai->vbo == 0) { DecodeVerts(); vai->numVerts = indexGen.VertexCount(); @@ -951,6 +979,7 @@ void TransformDrawEngine::Flush() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } else { + gpuStats.numCachedDrawCalls++; glBindBuffer(GL_ARRAY_BUFFER, vai->vbo); if (vai->ebo) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vai->ebo); diff --git a/GPU/GLES/TransformPipeline.h b/GPU/GLES/TransformPipeline.h index 02a3a8fd9a..91e0219bb5 100644 --- a/GPU/GLES/TransformPipeline.h +++ b/GPU/GLES/TransformPipeline.h @@ -57,6 +57,7 @@ public: numDraws = 0; lastFrame = gpuStats.numFrames; numVerts = 0; + drawsUntilNextFullHash = 0; } ~VertexArrayInfo(); enum Status { @@ -73,6 +74,7 @@ public: u32 vbo; u32 ebo; + // TODO: see if we can avoid having this full thing here. DecVtxFormat decFmt; // Precalculated parameter for drawdrawElements @@ -83,6 +85,7 @@ public: u8 numDCs; int numDraws; int lastFrame; // So that we can forget. + u16 drawsUntilNextFullHash; }; diff --git a/GPU/GLES/VertexDecoder.cpp b/GPU/GLES/VertexDecoder.cpp index d615b6d4d0..8bfc77a451 100644 --- a/GPU/GLES/VertexDecoder.cpp +++ b/GPU/GLES/VertexDecoder.cpp @@ -73,6 +73,12 @@ int DecFmtSize(u8 fmt) { case DEC_U8_2: return 4; case DEC_U8_3: return 4; case DEC_U8_4: return 4; + case DEC_U16_1: return 4; + case DEC_U16_2: return 4; + case DEC_U16_3: return 8; + case DEC_U16_4: return 8; + case DEC_U8A_2: return 4; + case DEC_U16A_2: return 4; default: return 0; } @@ -118,10 +124,10 @@ void VertexDecoder::Step_WeightsU8() const void VertexDecoder::Step_WeightsU16() const { - float *wt = (float *)(decoded_ + decFmt.w0off); + u16 *wt = (u16 *)(decoded_ + decFmt.w0off); const u16 *wdata = (const u16*)(ptr_); for (int j = 0; j < nweights; j++) - wt[j] = (float)wdata[j] / 65535.0f; + wt[j] = wdata[j]; } // Float weights should be uncommon, we can live with having to multiply these by 2.0 @@ -138,41 +144,42 @@ void VertexDecoder::Step_WeightsFloat() const void VertexDecoder::Step_TcU8() const { - float *uv = (float *)(decoded_ + decFmt.uvoff); + u8 *uv = (u8*)(decoded_ + decFmt.uvoff); const u8 *uvdata = (const u8*)(ptr_ + tcoff); - for (int j = 0; j < 2; j++) - uv[j] = (float)uvdata[j] / 128.0f; + uv[0] = uvdata[0]; + uv[1] = uvdata[1]; } void VertexDecoder::Step_TcU16() const { - float *uv = (float *)(decoded_ + decFmt.uvoff); + u16 *uv = (u16 *)(decoded_ + decFmt.uvoff); const u16 *uvdata = (const u16*)(ptr_ + tcoff); - uv[0] = (float)uvdata[0] / 32768.0f; - uv[1] = (float)uvdata[1] / 32768.0f; + uv[0] = uvdata[0]; + uv[1] = uvdata[1]; } void VertexDecoder::Step_TcU16Through() const { - float *uv = (float *)(decoded_ + decFmt.uvoff); + u16 *uv = (u16 *)(decoded_ + decFmt.uvoff); const u16 *uvdata = (const u16*)(ptr_ + tcoff); - uv[0] = (float)uvdata[0] / (float)(gstate_c.curTextureWidth); - uv[1] = (float)uvdata[1] / (float)(gstate_c.curTextureHeight); + uv[0] = uvdata[0]; + uv[1] = uvdata[1]; } void VertexDecoder::Step_TcFloat() const { float *uv = (float *)(decoded_ + decFmt.uvoff); const float *uvdata = (const float*)(ptr_ + tcoff); - memcpy(uv, uvdata, sizeof(float) * 2); + uv[0] = uvdata[0] * 0.5f; + uv[1] = uvdata[1] * 0.5f; } void VertexDecoder::Step_TcFloatThrough() const { float *uv = (float *)(decoded_ + decFmt.uvoff); const float *uvdata = (const float*)(ptr_ + tcoff); - uv[0] = uvdata[0] / (float)(gstate_c.curTextureWidth); - uv[1] = uvdata[1] / (float)(gstate_c.curTextureHeight); + uv[0] = uvdata[0] * 0.5f; + uv[1] = uvdata[1] * 0.5f; } void VertexDecoder::Step_Color565() const @@ -557,6 +564,9 @@ void VertexDecoder::SetVertexType(u32 fmt) { if (weighttype == GE_VTYPE_WEIGHT_8BIT >> GE_VTYPE_WEIGHT_SHIFT) { fmtBase = DEC_U8_1; weightSize = 1; + } else if (weighttype == GE_VTYPE_WEIGHT_16BIT >> GE_VTYPE_WEIGHT_SHIFT) { + fmtBase = DEC_U16_1; + weightSize = 2; } if (nweights < 5) { @@ -580,8 +590,18 @@ void VertexDecoder::SetVertexType(u32 fmt) { steps_[numSteps_++] = throughmode ? tcstep_through[tc] : tcstep[tc]; - // All UV decode to DEC_FLOAT2 currently. - decFmt.uvfmt = DEC_FLOAT_2; + switch (tc) { + case GE_VTYPE_TC_8BIT >> GE_VTYPE_TC_SHIFT: + decFmt.uvfmt = throughmode ? DEC_U8A_2 : DEC_U8_2; + break; + case GE_VTYPE_TC_16BIT >> GE_VTYPE_TC_SHIFT: + decFmt.uvfmt = throughmode ? DEC_U16A_2 : DEC_U16_2; + break; + case GE_VTYPE_TC_FLOAT >> GE_VTYPE_TC_SHIFT: + decFmt.uvfmt = DEC_FLOAT_2; + break; + } + decFmt.uvoff = decOff; decOff += DecFmtSize(decFmt.uvfmt); } diff --git a/GPU/GLES/VertexDecoder.h b/GPU/GLES/VertexDecoder.h index 04be647258..6e006d7398 100644 --- a/GPU/GLES/VertexDecoder.h +++ b/GPU/GLES/VertexDecoder.h @@ -38,6 +38,12 @@ enum { DEC_U8_2, DEC_U8_3, DEC_U8_4, + DEC_U16_1, + DEC_U16_2, + DEC_U16_3, + DEC_U16_4, + DEC_U8A_2, + DEC_U16A_2, }; int DecFmtSize(u8 fmt); @@ -58,8 +64,8 @@ struct TransformedVertex { float x, y, z, fog; // in case of morph, preblend during decode float u; float v; // scaled by uscale, vscale, if there - float color0[4]; // prelit - float color1[3]; // prelit + u8 color0[4]; // prelit + u8 color1[3]; // prelit }; DecVtxFormat GetTransformedVtxFormat(const DecVtxFormat &fmt); @@ -189,16 +195,16 @@ public: break; case DEC_S16_3: { - s16 *p = (s16 *)(data_ + decFmt_.posoff); + const s16 *p = (s16 *)(data_ + decFmt_.posoff); for (int i = 0; i < 3; i++) - pos[i] = p[i] / 32767.0f; + pos[i] = p[i] * (1.f / 32768.f); } break; case DEC_S8_3: { - s8 *p = (s8 *)(data_ + decFmt_.posoff); + const s8 *p = (s8 *)(data_ + decFmt_.posoff); for (int i = 0; i < 3; i++) - pos[i] = p[i] / 127.0f; + pos[i] = p[i] * (1.f / 128.f); } break; default: @@ -214,16 +220,16 @@ public: break; case DEC_S16_3: { - s16 *p = (s16 *)(data_ + decFmt_.nrmoff); + const s16 *p = (s16 *)(data_ + decFmt_.nrmoff); for (int i = 0; i < 3; i++) - nrm[i] = p[i] / 32767.0f; + nrm[i] = p[i] * (1.f / 32768.f); } break; case DEC_S8_3: { - s8 *p = (s8 *)(data_ + decFmt_.nrmoff); + const s8 *p = (s8 *)(data_ + decFmt_.nrmoff); for (int i = 0; i < 3; i++) - nrm[i] = p[i] / 127.0f; + nrm[i] = p[i] * (1.f / 128.0f); } break; default: @@ -233,9 +239,37 @@ public: } void ReadUV(float uv[2]) { + const u8 *b = (const u8 *)(data_ + decFmt_.uvoff); + const u16 *s = (const u16 *)(data_ + decFmt_.uvoff); + const float *f = (const float *)(data_ + decFmt_.uvoff); switch (decFmt_.uvfmt) { + case DEC_U8_2: + uv[0] = b[0] * (1.f / 128.f); + uv[1] = b[1] * (1.f / 128.f); + break; + + case DEC_U16_2: + uv[0] = s[0] * (1.f / 32768.f); + uv[1] = s[1] * (1.f / 32768.f); + break; + case DEC_FLOAT_2: - memcpy(uv, data_ + decFmt_.uvoff, 8); break; + if (isThrough()) { + uv[0] = f[0]; + uv[1] = f[1]; + } else { + uv[0] = f[0] * 2.0f; + uv[1] = f[1] * 2.0f; + } + break; + + case DEC_U16A_2: + { + const u16 *p = (const u16 *)(data_ + decFmt_.uvoff); + uv[0] = (float)p[0]; + uv[1] = (float)p[1]; + } + break; default: ERROR_LOG(G3D, "Reader: Unsupported UV Format"); break; @@ -248,7 +282,7 @@ public: { const u8 *p = (const u8 *)(data_ + decFmt_.c0off); for (int i = 0; i < 4; i++) - color[i] = p[i] / 255.0f; + color[i] = p[i] * (1.f / 255.f); } break; case DEC_FLOAT_4: @@ -265,7 +299,7 @@ public: { const u8 *p = (const u8 *)(data_ + decFmt_.c1off); for (int i = 0; i < 3; i++) - color[i] = p[i] / 255.0f; + color[i] = p[i] * (1.f / 255.f); } break; case DEC_FLOAT_4: @@ -277,34 +311,48 @@ public: } void ReadWeights(float weights[8]) { + const float *f = (const float *)(data_ + decFmt_.w0off); const u8 *p = (const u8 *)(data_ + decFmt_.w0off); + const u16 *s = (const u16 *)(data_ + decFmt_.w0off); switch (decFmt_.w0fmt) { - case DEC_FLOAT_1: memcpy(weights, data_ + decFmt_.w0off, 4); break; - case DEC_FLOAT_2: memcpy(weights, data_ + decFmt_.w0off, 8); break; - case DEC_FLOAT_3: memcpy(weights, data_ + decFmt_.w0off, 12); break; - case DEC_FLOAT_4: memcpy(weights, data_ + decFmt_.w0off, 16); break; - case DEC_U8_1: weights[0] = p[0] / 128.f; break; - case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i] = p[i] / 128.f; break; - case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i] = p[i] / 128.f; break; - case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i] = p[i] / 128.f; break; + case DEC_FLOAT_1: + case DEC_FLOAT_2: + case DEC_FLOAT_3: + case DEC_FLOAT_4: + for (int i = 0; i <= decFmt_.w0fmt - DEC_FLOAT_1; i++) + weights[i] = f[i] * 2.0; + break; + case DEC_U8_1: weights[0] = p[0] * (1.f / 128.f); break; + case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i] = p[i] * (1.f / 128.f); break; + case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i] = p[i] * (1.f / 128.f); break; + case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i] = p[i] * (1.f / 128.f); break; + case DEC_U16_1: weights[0] = s[0] * (1.f / 32768.f); break; + case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i] = s[i] * (1.f / 32768.f); break; + case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i] = s[i] * (1.f / 32768.f); break; + case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i] = s[i] * (1.f / 32768.f); break; default: ERROR_LOG(G3D, "Reader: Unsupported W0 Format"); break; } + f = (const float*)(data_ + decFmt_.w1off); p = (const u8 *)(data_ + decFmt_.w1off); + s = (const u16 *)(data_ + decFmt_.w1off); switch (decFmt_.w1fmt) { case 0: // It's fine for there to be w0 weights but not w1. break; - case DEC_FLOAT_1: memcpy(weights + 4, data_ + decFmt_.w1off, 4); break; - case DEC_FLOAT_2: memcpy(weights + 4, data_ + decFmt_.w1off, 8); break; - case DEC_FLOAT_3: memcpy(weights + 4, data_ + decFmt_.w1off, 12); break; - case DEC_FLOAT_4: memcpy(weights + 4, data_ + decFmt_.w1off, 16); break; - case DEC_U8_1: weights[4] = p[0] / 128.f; break; - case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i+4] = p[i] / 128.f; break; - case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i+4] = p[i] / 128.f; break; - case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i+4] = p[i] / 128.f; break; + case DEC_FLOAT_1: + case DEC_FLOAT_2: + case DEC_FLOAT_3: + case DEC_FLOAT_4: + for (int i = 0; i <= decFmt_.w1fmt - DEC_FLOAT_1; i++) + weights[i+4] = f[i] * 2.0; + break; + case DEC_U16_1: weights[4] = s[0] * (1.f / 32768.f); break; + case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i+4] = s[i] * (1.f / 32768.f); break; + case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i+4] = s[i] * (1.f / 32768.f); break; + case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i+4] = s[i] * (1.f / 32768.f); break; default: ERROR_LOG(G3D, "Reader: Unsupported W1 Format"); break; @@ -314,7 +362,7 @@ public: bool hasColor0() const { return decFmt_.c0fmt != 0; } bool hasNormal() const { return decFmt_.nrmfmt != 0; } bool hasUV() const { return decFmt_.uvfmt != 0; } - + bool isThrough() const { return (vtype_ & GE_VTYPE_THROUGH) != 0; } void Goto(int index) { data_ = base_ + index * decFmt_.stride; } diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index 84524d66f2..d6cc3770c9 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -297,7 +297,7 @@ void GenerateVertexShader(int prim, char *buffer) { const char *specular = (gstate.materialupdate & 4) ? "unlitColor" : "u_matspecular.rgb"; if (gstate.lightingEnable & 1) { - WRITE(p, " vec4 lightSum0 = vec4(0.0);\n"); + WRITE(p, " vec4 lightSum0 = u_ambient * %s + vec4(u_matemissive, 0.0);\n", ambient); WRITE(p, " vec3 lightSum1 = vec3(0.0);\n"); } @@ -345,7 +345,7 @@ void GenerateVertexShader(int prim, char *buffer) { if (gstate.lightingEnable & 1) { // Sum up ambient, emissive here. - WRITE(p, " v_color0 = clamp(lightSum0 + u_ambient * %s + vec4(u_matemissive, 0.0), 0.0, 1.0);\n", ambient); + WRITE(p, " v_color0 = clamp(lightSum0, 0.0, 1.0);\n"); if (lmode) { WRITE(p, " v_color1 = clamp(lightSum1, 0.0, 1.0);\n"); } else { @@ -375,7 +375,7 @@ void GenerateVertexShader(int prim, char *buffer) { WRITE(p, " vec3 temp_tc = a_position.xyz;\n"); break; case 1: // Use unscaled UV as source - WRITE(p, " vec3 temp_tc = vec3(a_texcoord.xy, 0.0);\n"); + WRITE(p, " vec3 temp_tc = vec3(a_texcoord.xy * 2.0f, 0.0);\n"); break; case 2: // Use normalized transformed normal as source WRITE(p, " vec3 temp_tc = normalize(a_normal);\n"); diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 8e5eae495c..84d9d8dcea 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -145,7 +145,7 @@ struct GPUgstate clutformat, texfilter, texwrap, - padxxxxx, + texlevel, texfunc, texenvcolor, texflush, @@ -236,8 +236,8 @@ struct GPUStateCache bool textureChanged; - float uScale,vScale,zScale; - float uOff,vOff,zOff; + float uScale,vScale; + float uOff,vOff; float zMin, zMax; float lightpos[4][3]; float lightdir[4][3]; diff --git a/GPU/GeDisasm.cpp b/GPU/GeDisasm.cpp index 49d0c735f4..c75dc1ba93 100644 --- a/GPU/GeDisasm.cpp +++ b/GPU/GeDisasm.cpp @@ -521,13 +521,13 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer) { case GE_CMD_VIEWPORTZ1: { float zScale = getFloat24(data) / 65535.f; - sprintf(buffer, "Z scale: %f", zScale); + sprintf(buffer, "Viewport Z scale: %f", zScale); } break; case GE_CMD_VIEWPORTZ2: { float zOff = getFloat24(data) / 65535.f; - sprintf(buffer, "Z pos: %f", zOff); + sprintf(buffer, "Viewport Z pos: %f", zOff); } break; diff --git a/Qt/Common.pro b/Qt/Common.pro index 56c4949e51..5ad334ca6e 100755 --- a/Qt/Common.pro +++ b/Qt/Common.pro @@ -48,7 +48,8 @@ SOURCES += ../Common/ColorUtil.cpp \ ../Common/Timer.cpp \ ../Common/Version.cpp \ ../Common/Crypto/*.cpp -HEADERS += ../Common/ColorUtil.h \ +HEADERS += ../Common/ChunkFile.h \ + ../Common/ColorUtil.h \ ../Common/ConsoleListener.h \ ../Common/ExtendedTrace.h \ ../Common/FileSearch.h \ diff --git a/Qt/EmuThread.cpp b/Qt/EmuThread.cpp index 935766b810..1b88068ed0 100644 --- a/Qt/EmuThread.cpp +++ b/Qt/EmuThread.cpp @@ -17,22 +17,72 @@ #include "gfx_es2/fbo.h" #include "gfx_es2/gl_state.h" #include "GPU/GPUState.h" +#include "android/jni/ui_atlas.h" +#include "native/util/random/rng.h" +#include "native/base/timeutil.h" +#include "native/base/colorutil.h" #include "qtemugl.h" #include "QtHost.h" -void EmuThread_Start(QString filename, QtEmuGL* w) +namespace +{ + + static const int symbols[4] = { + I_CROSS, + I_CIRCLE, + I_SQUARE, + I_TRIANGLE + }; + + static const uint32_t colors[4] = { + /* + 0xFF6666FF, // blue + 0xFFFF6666, // red + 0xFFFF66FF, // pink + 0xFF66FF66, // green + */ + 0xC0FFFFFF, + 0xC0FFFFFF, + 0xC0FFFFFF, + 0xC0FFFFFF, + }; + + static void DrawBackground(float alpha) { + static float xbase[100] = {0}; + static float ybase[100] = {0}; + if (xbase[0] == 0.0f) { + GMRng rng; + for (int i = 0; i < 100; i++) { + xbase[i] = rng.F() * dp_xres; + ybase[i] = rng.F() * dp_yres; + } + } + glClearColor(0.1f,0.2f,0.43f,1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + ui_draw2d.DrawImageStretch(I_BG, 0, 0, dp_xres, dp_yres); + float t = time_now(); + for (int i = 0; i < 100; i++) { + float x = xbase[i]; + float y = ybase[i] + 40*cos(i * 7.2 + t * 1.3); + float angle = sin(i + t); + int n = i & 3; + ui_draw2d.DrawImageRotated(symbols[n], x, y, 1.0f, angle, colorAlpha(colors[n], alpha * 0.1f)); + } + } + +} + +void EmuThread_Start(QtEmuGL* w) { //_dbg_clear_(); - fileToStart = filename; glWindow = w; + glWindow->doneCurrent(); glWindow->start_rendering(); } void EmuThread_Stop() { -// DSound_UpdateSound(); - Core_Stop(); if(glWindow) { glWindow->stop_rendering(); @@ -40,6 +90,31 @@ void EmuThread_Stop() host->UpdateUI(); } +void EmuThread_StartGame(QString filename) +{ + if(glWindow) + { + glWindow->start_game(filename); + } +} + +void EmuThread_StopGame() +{ + if(glWindow) + { + glWindow->stop_game(); + } +} + +void EmuThread_LockDraw(bool value) +{ + if(glWindow) + { + glWindow->LockDraw(value); + } +} + + void EmuThread::init(InputState *inputState) { input_state = inputState; @@ -50,8 +125,6 @@ void EmuThread::run() running = true; setCurrentThreadName("EmuThread"); - g_State.bEmuThreadStarted = true; - host->UpdateUI(); host->InitGL(); @@ -64,80 +137,181 @@ void EmuThread::run() INFO_LOG(BOOT, "Starting up hardware."); - CoreParameter coreParameter; - coreParameter.fileToStart = fileToStart.toStdString(); - coreParameter.enableSound = true; - coreParameter.gpuCore = GPU_GLES; - coreParameter.cpuCore = (CPUCore)g_Config.iCpuCore; - coreParameter.enableDebugging = true; - coreParameter.printfEmuLog = false; - coreParameter.headLess = false; - coreParameter.renderWidth = 480 * g_Config.iWindowZoom; - coreParameter.renderHeight = 272 * g_Config.iWindowZoom; - coreParameter.outputWidth = dp_xres; - coreParameter.outputHeight = dp_yres; - coreParameter.pixelWidth = pixel_xres; - coreParameter.pixelHeight = pixel_yres; - coreParameter.startPaused = !g_Config.bAutoRun; - - std::string error_string; - if (!PSP_Init(coreParameter, &error_string)) - { - ERROR_LOG(BOOT, "Error loading: %s", error_string.c_str()); - FinalShutdown(); - return; - } - - LayoutGamepad(dp_xres, dp_yres); - - _dbg_update_(); - - host->UpdateDisassembly(); - Core_EnableStepping(coreParameter.startPaused ? TRUE : FALSE); - - g_State.bBooted = true; -#ifdef _DEBUG - host->UpdateMemView(); -#endif - host->BootDone(); - QElapsedTimer timer; while(running) { //UpdateGamepad(*input_state); timer.start(); - UpdateInputState(input_state); + gameMutex.lock(); + bool gRun = gameRunning; + gameMutex.unlock(); - for (int i = 0; i < controllistCount; i++) { - if (input_state->pad_buttons_down & controllist[i].emu_id) { - __CtrlButtonDown(controllist[i].psp_id); + if(gRun) + { + gameMutex.lock(); + + glWindow->makeCurrent(); + if(needInitGame) + { + g_State.bEmuThreadStarted = true; + + CoreParameter coreParameter; + coreParameter.fileToStart = fileToStart.toStdString(); + coreParameter.enableSound = true; + coreParameter.gpuCore = GPU_GLES; + coreParameter.cpuCore = (CPUCore)g_Config.iCpuCore; + coreParameter.enableDebugging = true; + coreParameter.printfEmuLog = false; + coreParameter.headLess = false; + coreParameter.renderWidth = 480 * g_Config.iWindowZoom; + coreParameter.renderHeight = 272 * g_Config.iWindowZoom; + coreParameter.outputWidth = dp_xres; + coreParameter.outputHeight = dp_yres; + coreParameter.pixelWidth = pixel_xres; + coreParameter.pixelHeight = pixel_yres; + coreParameter.startPaused = !g_Config.bAutoRun; + + std::string error_string; + if (!PSP_Init(coreParameter, &error_string)) + { + ERROR_LOG(BOOT, "Error loading: %s", error_string.c_str()); + FinalShutdown(); + return; + } + + LayoutGamepad(dp_xres, dp_yres); + + _dbg_update_(); + + host->UpdateDisassembly(); + Core_EnableStepping(coreParameter.startPaused ? TRUE : FALSE); + + g_State.bBooted = true; + #ifdef _DEBUG + host->UpdateMemView(); + #endif + host->BootDone(); + needInitGame = false; } - if (input_state->pad_buttons_up & controllist[i].emu_id) { - __CtrlButtonUp(controllist[i].psp_id); + UpdateInputState(input_state); + + for (int i = 0; i < controllistCount; i++) { + if (input_state->pad_buttons_down & controllist[i].emu_id) { + __CtrlButtonDown(controllist[i].psp_id); + } + if (input_state->pad_buttons_up & controllist[i].emu_id) { + __CtrlButtonUp(controllist[i].psp_id); + } } + __CtrlSetAnalog(input_state->pad_lstick_x, input_state->pad_lstick_y); + + EndInputState(input_state); + + glstate.Restore(); + glViewport(0, 0, pixel_xres, pixel_yres); + Matrix4x4 ortho; + ortho.setOrtho(0.0f, dp_xres, dp_yres, 0.0f, -1.0f, 1.0f); + glsl_bind(UIShader_Get()); + glUniformMatrix4fv(UIShader_Get()->u_worldviewproj, 1, GL_FALSE, ortho.getReadPtr()); + + + ReapplyGfxState(); + + Core_Run(); + + // Hopefully coreState is now CORE_NEXTFRAME + if (coreState == CORE_NEXTFRAME) { + // set back to running for the next frame + coreState = CORE_RUNNING; + + qint64 time = timer.elapsed(); + const int frameTime = (1.0f/60.0f) * 1000; + gameMutex.unlock(); + if(time < frameTime) + { + glWindow->doneCurrent(); + msleep(frameTime-time); + glWindow->makeCurrent(); + } + gameMutex.lock(); + timer.start(); + } + + fbo_unbind(); + + UIShader_Prepare(); + + uiTexture->Bind(0); + + glViewport(0, 0, pixel_xres, pixel_yres); + + ui_draw2d.Begin(DBMODE_NORMAL); + + //if (g_Config.bShowTouchControls) + // DrawGamepad(ui_draw2d); + + glsl_bind(UIShader_Get()); + ui_draw2d.End(); + ui_draw2d.Flush(UIShader_Get()); + + + // Tiled renderers like PowerVR should benefit greatly from this. However - seems I can't call it? +#if defined(USING_GLES2) + bool hasDiscard = false; // TODO + if (hasDiscard) { + //glDiscardFramebuffer(GL_COLOR_EXT | GL_DEPTH_EXT | GL_STENCIL_EXT); + } +#endif + glWindow->swapBuffers(); + glWindow->doneCurrent(); + gameMutex.unlock(); } - __CtrlSetAnalog(input_state->pad_lstick_x, input_state->pad_lstick_y); + else + { + gameMutex.lock(); + glWindow->makeCurrent(); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - EndInputState(input_state); + time_update(); + float t = (float)frames_ / 60.0f; + frames_++; - glstate.Restore(); - glViewport(0, 0, pixel_xres, pixel_yres); - Matrix4x4 ortho; - ortho.setOrtho(0.0f, dp_xres, dp_yres, 0.0f, -1.0f, 1.0f); - glsl_bind(UIShader_Get()); - glUniformMatrix4fv(UIShader_Get()->u_worldviewproj, 1, GL_FALSE, ortho.getReadPtr()); + float alpha = t; + if (t > 1.0f) alpha = 1.0f; + float alphaText = alpha; + //if (t > 2.0f) alphaText = 3.0f - t; + + glstate.Restore(); + glViewport(0, 0, pixel_xres, pixel_yres); + Matrix4x4 ortho; + ortho.setOrtho(0.0f, dp_xres, dp_yres, 0.0f, -1.0f, 1.0f); + glsl_bind(UIShader_Get()); + glUniformMatrix4fv(UIShader_Get()->u_worldviewproj, 1, GL_FALSE, ortho.getReadPtr()); - ReapplyGfxState(); + ReapplyGfxState(); - Core_Run(); + UIShader_Prepare(); + UIBegin(); + DrawBackground(alpha); - // Hopefully coreState is now CORE_NEXTFRAME - if (coreState == CORE_NEXTFRAME) { - // set back to running for the next frame - coreState = CORE_RUNNING; + ui_draw2d.SetFontScale(1.5f, 1.5f); + ui_draw2d.DrawText(UBUNTU48, "PPSSPP", dp_xres / 2, dp_yres / 2 - 30, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); + ui_draw2d.SetFontScale(1.0f, 1.0f); + ui_draw2d.DrawText(UBUNTU24, "Created by Henrik Rydgard", dp_xres / 2, dp_yres / 2 + 40, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); + ui_draw2d.DrawText(UBUNTU24, "Free Software under GPL 2.0", dp_xres / 2, dp_yres / 2 + 70, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); + ui_draw2d.DrawText(UBUNTU24, "www.ppsspp.org", dp_xres / 2, dp_yres / 2 + 130, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); + UIEnd(); + + glsl_bind(UIShader_Get()); + ui_draw2d.Flush(UIShader_Get()); + + glWindow->swapBuffers(); + glWindow->doneCurrent(); + gameMutex.unlock(); qint64 time = timer.elapsed(); const int frameTime = (1.0f/60.0f) * 1000; if(time < frameTime) @@ -147,41 +321,18 @@ void EmuThread::run() timer.start(); } - fbo_unbind(); - - UIShader_Prepare(); - - uiTexture->Bind(0); - - glViewport(0, 0, pixel_xres, pixel_yres); - - ui_draw2d.Begin(DBMODE_NORMAL); - - //if (g_Config.bShowTouchControls) - // DrawGamepad(ui_draw2d); - - glsl_bind(UIShader_Get()); - ui_draw2d.End(); - ui_draw2d.Flush(UIShader_Get()); - - - // Tiled renderers like PowerVR should benefit greatly from this. However - seems I can't call it? -#if defined(USING_GLES2) - bool hasDiscard = false; // TODO - if (hasDiscard) { - //glDiscardFramebuffer(GL_COLOR_EXT | GL_DEPTH_EXT | GL_STENCIL_EXT); - } -#endif - - glWindow->swapBuffers(); } - glWindow->doneCurrent(); + + if(gameRunning) + { + stopGame(); + } + } void EmuThread::Shutdown() { host->PrepareShutdown(); - PSP_Shutdown(); FinalShutdown(); } void EmuThread::FinalShutdown() @@ -189,7 +340,6 @@ void EmuThread::FinalShutdown() host->ShutdownGL(); - delete uiTexture; uiTexture = NULL; @@ -197,10 +347,6 @@ void EmuThread::FinalShutdown() gl_lost_manager_shutdown(); - // TODO - //The CPU should return when a game is stopped and cleanup should be done here, - //so we can restart the plugins (or load new ones) for the next game - g_State.bEmuThreadStarted = false; //_endthreadex(0); //return 0; } @@ -210,4 +356,31 @@ void EmuThread::setRunning(bool value) running = false; } +void EmuThread::startGame(QString filename) +{ + gameMutex.lock(); + needInitGame = true; + gameRunning = true; + fileToStart = filename; + gameMutex.unlock(); + +} + +void EmuThread::stopGame() +{ + Core_Stop(); + gameMutex.lock(); + gameRunning = false; + + PSP_Shutdown(); + + // TODO + //The CPU should return when a game is stopped and cleanup should be done here, + //so we can restart the plugins (or load new ones) for the next game + g_State.bEmuThreadStarted = false; + frames_ = 0; + + gameMutex.unlock(); +} + diff --git a/Qt/EmuThread.h b/Qt/EmuThread.h index 27d66842ff..183c9efad0 100644 --- a/Qt/EmuThread.h +++ b/Qt/EmuThread.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include "input/input_state.h" class QtEmuGL; @@ -7,17 +8,27 @@ class QtEmuGL; class EmuThread : public QThread { public: - EmuThread() : running(false) {} + EmuThread() : running(false), gameRunning(false), needInitGame(false), frames_(0) {} void init(InputState* inputState); void run(); void FinalShutdown(); void setRunning(bool value); + void startGame(QString filename); + void stopGame(); + QMutex gameMutex; public slots: void Shutdown(); private: InputState* input_state; bool running; + bool gameRunning; + bool needInitGame; + int frames_; + }; -void EmuThread_Start(QString filename, QtEmuGL* w); +void EmuThread_Start(QtEmuGL* w); void EmuThread_Stop(); +void EmuThread_StartGame(QString filename); +void EmuThread_StopGame(); +void EmuThread_LockDraw(bool value); diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index 76ab64cefa..3446744f32 100755 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -1,32 +1,29 @@ TARGET = PPSSPPQt -# Ubuntu user, remove multimedia -QT += core gui opengl multimedia + +QT += core gui opengl +CONFIG += mobility +MOBILITY += multimedia include(Settings.pri) -linux { - CONFIG += mobility link_pkgconfig - MOBILITY += multimedia -} -else { - QT += multimedia -} # Libs symbian: LIBS += -lCore.lib -lCommon.lib -lNative.lib -lcone -leikcore -lavkon -lezlib - blackberry: LIBS += -L. -lCore -lCommon -lNative -lscreen -lsocket -lstdc++ - win32: LIBS += -L. -lCore -lCommon -lNative -lwinmm -lws2_32 -lkernel32 -luser32 -lgdi32 -lshell32 -lcomctl32 -ldsound -lxinput +linux: LIBS += -L. -lCore -lCommon -lNative -linux { - LIBS += -L. -lCore -lCommon -lNative +desktop_ui { PRE_TARGETDEPS += ./libCommon.a ./libCore.a ./libNative.a + CONFIG += link_pkgconfig packagesExist(sdl) { - DEFINES += QT_HAS_SDL - PKGCONFIG += sdl + DEFINES += QT_HAS_SDL + PKGCONFIG += sdl } } +TRANSLATIONS = languages/ppsspp_en.ts \ + languages/ppsspp_pl.ts + # Main SOURCES += ../native/base/QtMain.cpp HEADERS += ../native/base/QtMain.h @@ -41,51 +38,25 @@ SOURCES += ../android/jni/EmuScreen.cpp \ INCLUDEPATH += .. ../Common ../native # Temporarily only use new UI for Linux desktop -linux:!mobile_platform { - SOURCES += mainwindow.cpp \ - debugger_disasm.cpp \ - EmuThread.cpp\ - QtHost.cpp \ - qtemugl.cpp \ - ctrldisasmview.cpp \ - ctrlregisterlist.cpp \ - controls.cpp \ - qkeyedit.cpp \ - gamepaddialog.cpp - HEADERS += mainwindow.h \ - debugger_disasm.h \ - EmuThread.h \ - QtHost.h \ - qtemugl.h \ - ctrldisasmview.h \ - ctrlregisterlist.h \ - controls.h \ - qkeyedit.h \ - gamepaddialog.h +desktop_ui { + MOC_DIR = moc + UI_DIR = ui + SOURCES += *.cpp + HEADERS += *.h + FORMS += *.ui + RESOURCES += resources.qrc } else { SOURCES += ../android/jni/NativeApp.cpp } # Packaging symbian { - vendorinfo = "%{\"Qtness\"}" ":\"Qtness\"" - packageheader = "$${LITERAL_HASH}{\"PPSSPP\"}, (0xE0095B1D), 0, 6, 1, TYPE=SA" - deployinfo.pkg_prerules = packageheader vendorinfo + deploy.pkg_prerules = "$${LITERAL_HASH}{\"PPSSPP\"}, (0xE0095B1D), 0, 6, 1, TYPE=SA" "%{\"Qtness\"}" ":\"Qtness\"" assets.sources = ../android/assets/ui_atlas.zim ../assets/ppge_atlas.zim assets.path = E:/PPSSPP - DEPLOYMENT += deployinfo assets + DEPLOYMENT += deploy assets ICON = ../assets/icon.svg -# 268MB maximum + # 268MB maximum TARGET.EPOCHEAPSIZE = 0x40000 0x10000000 TARGET.EPOCSTACKSIZE = 0x10000 } - -linux:!mobile_platform { - FORMS += mainwindow.ui \ - debugger_disasm.ui \ - controls.ui\ - gamepaddialog.ui - - RESOURCES += \ - resources.qrc -} diff --git a/Qt/QtHost.cpp b/Qt/QtHost.cpp index 46642b6190..54468d2046 100644 --- a/Qt/QtHost.cpp +++ b/Qt/QtHost.cpp @@ -38,8 +38,7 @@ void QtHost::ShutdownGL() void QtHost::SetWindowTitle(const char *message) { - // Really need a better way to deal with versions. - QString title = "PPSSPP v0.5 - " + QString::fromUtf8(message); + QString title = "PPSSPP " + QString(PPSSPP_VERSION_STR) + " - " + QString::fromUtf8(message); mainWindow->setWindowTitle(title); } diff --git a/Qt/Settings.pri b/Qt/Settings.pri index 151315dd54..508abe4688 100644 --- a/Qt/Settings.pri +++ b/Qt/Settings.pri @@ -1,9 +1,11 @@ DEFINES += USING_QT_UI blackberry|symbian|contains(MEEGO_EDITION,harmattan): CONFIG += mobile_platform unix:!blackberry:!symbian:!macx: CONFIG += linux +linux:!mobile_platform: CONFIG += desktop_ui # Global specific -QMAKE_CXXFLAGS += -std=c++0x -Wno-unused-function -Wno-unused-variable -Wno-multichar -Wno-uninitialized -Wno-ignored-qualifiers -Wno-missing-field-initializers -Wno-unused-parameter +QMAKE_CXXFLAGS += -Wno-unused-function -Wno-unused-variable -Wno-multichar -Wno-uninitialized -Wno-ignored-qualifiers -Wno-missing-field-initializers -Wno-unused-parameter +QMAKE_CXXFLAGS += -std=c++0x -ffast-math -fno-strict-aliasing # Arch specific contains(QT_ARCH, i686)|contains(QT_ARCH, x86)|contains(QT_ARCH, x86_64): { @@ -34,4 +36,3 @@ symbian: { DEFINES += __MARM_ARMV6__ CONFIG += 4.6.3 } - diff --git a/Qt/controls.cpp b/Qt/controls.cpp index da16878695..da248d0eb8 100644 --- a/Qt/controls.cpp +++ b/Qt/controls.cpp @@ -1,6 +1,8 @@ #include "controls.h" #include "ui_controls.h" #include "Core/Config.h" +#include "EmuThread.h" +#include Controls_ controllist[] = { {"Edit_Start", "Start", Qt::Key_1, PAD_BUTTON_START, CTRL_START}, @@ -43,6 +45,12 @@ Controls::~Controls() void Controls::showEvent(QShowEvent*) { +#ifdef Q_WS_X11 + // Hack to remove the X11 crash with threaded opengl when opening the first dialog + EmuThread_LockDraw(true); + QTimer::singleShot(100, this, SLOT(releaseLock())); +#endif + for(int i = 0; i < controllistCount; i++) { if(g_Config.iMappingMap.find(i) != g_Config.iMappingMap.end()) @@ -62,6 +70,11 @@ void Controls::showEvent(QShowEvent*) } } +void Controls::releaseLock() +{ + EmuThread_LockDraw(false); +} + void Controls::on_buttonBox_accepted() { for(int i = 0; i < controllistCount; i++) diff --git a/Qt/controls.h b/Qt/controls.h index 347fd8b250..b63a74523c 100644 --- a/Qt/controls.h +++ b/Qt/controls.h @@ -32,6 +32,7 @@ public: void showEvent(QShowEvent *); private slots: + void releaseLock(); void on_buttonBox_accepted(); private: diff --git a/Qt/gamepaddialog.cpp b/Qt/gamepaddialog.cpp index da657ad470..5094fc032c 100644 --- a/Qt/gamepaddialog.cpp +++ b/Qt/gamepaddialog.cpp @@ -2,6 +2,7 @@ #include "ui_gamepaddialog.h" #include #include "Core/Config.h" +#include "EmuThread.h" // Input struct GamePadInfo @@ -15,26 +16,26 @@ struct GamePadInfo // Initial values are PS3 controller GamePadInfo GamepadPadMapping[] = { - {0, 14, 0, "Prev_X", "Cross"}, //A - {0, 13, 0, "Prev_O", "Circle"}, //B - {0, 15, 0, "Prev_S", "Square"}, //X - {0, 12, 0, "Prev_T", "Triangle"}, //Y - {0, 10, 0, "Prev_LT", "Left Trigger"}, //LBUMPER - {0, 11, 0, "Prev_RT", "Right Trigger"}, //RBUMPER - {0, 3, 0, "Prev_Start", "Start"}, //START - {0, 0, 0, "Prev_Select", "Select"}, //SELECT - {0, 4, 0, "Prev_Up", "Up"}, //UP - {0, 6, 0, "Prev_Down", "Down"}, //DOWN - {0, 7, 0, "Prev_Left", "Left"}, //LEFT - {0, 5, 0, "Prev_Right", "Right"}, //RIGHT - {0, 0, 0, ""}, //MENU (event) - {0, 16, 0, "Prev_Home", "Home"}, //BACK + {0, 14, 0, "Prev_X", QT_TRANSLATE_NOOP("gamepadMapping", "Cross")}, //A + {0, 13, 0, "Prev_O", QT_TRANSLATE_NOOP("gamepadMapping", "Circle")}, //B + {0, 15, 0, "Prev_S", QT_TRANSLATE_NOOP("gamepadMapping", "Square")}, //X + {0, 12, 0, "Prev_T", QT_TRANSLATE_NOOP("gamepadMapping", "Triangle")}, //Y + {0, 10, 0, "Prev_LT", QT_TRANSLATE_NOOP("gamepadMapping", "Left Trigger")}, //LBUMPER + {0, 11, 0, "Prev_RT", QT_TRANSLATE_NOOP("gamepadMapping", "Right Trigger")}, //RBUMPER + {0, 3, 0, "Prev_Start", QT_TRANSLATE_NOOP("gamepadMapping", "Start")}, //START + {0, 0, 0, "Prev_Select", QT_TRANSLATE_NOOP("gamepadMapping", "Select")}, //SELECT + {0, 4, 0, "Prev_Up", QT_TRANSLATE_NOOP("gamepadMapping", "Up")}, //UP + {0, 6, 0, "Prev_Down", QT_TRANSLATE_NOOP("gamepadMapping", "Down")}, //DOWN + {0, 7, 0, "Prev_Left", QT_TRANSLATE_NOOP("gamepadMapping", "Left")}, //LEFT + {0, 5, 0, "Prev_Right", QT_TRANSLATE_NOOP("gamepadMapping", "Right")}, //RIGHT + {0, 0, 0, ""}, //MENU (event) + {0, 16, 0, "Prev_Home", QT_TRANSLATE_NOOP("gamepadMapping", "Home")}, //BACK // Special case for analog stick - {1, 0, -1, "Prev_ALeft", "Stick left"}, - {1, 0, 1, "Prev_ARight", "Stick right"}, - {1, 1, -1, "Prev_AUp", "Stick up"}, - {1, 1, 1, "Prev_ADown", "Stick bottom"} + {1, 0, -1, "Prev_ALeft", QT_TRANSLATE_NOOP("gamepadMapping", "Stick left")}, + {1, 0, 1, "Prev_ARight", QT_TRANSLATE_NOOP("gamepadMapping", "Stick right")}, + {1, 1, -1, "Prev_AUp", QT_TRANSLATE_NOOP("gamepadMapping", "Stick up")}, + {1, 1, 1, "Prev_ADown", QT_TRANSLATE_NOOP("gamepadMapping", "Stick bottom")} }; // id for mapping in config start at offset 200 to not get over key mapping @@ -96,13 +97,41 @@ GamePadDialog::~GamePadDialog() delete ui; } +void GamePadDialog::showEvent(QShowEvent *) +{ +#ifdef Q_WS_X11 + // Hack to remove the X11 crash with threaded opengl when opening the first dialog + EmuThread_LockDraw(true); + QTimer::singleShot(100, this, SLOT(releaseLock())); +#endif +} + +void GamePadDialog::changeEvent(QEvent *event) +{ + QDialog::changeEvent(event); + + if (0 != event) + { + if (event->type() == QEvent::LanguageChange) + { + ui->retranslateUi(this); + on_refreshListBtn_clicked(); + } + } +} + +void GamePadDialog::releaseLock() +{ + EmuThread_LockDraw(false); +} + void GamePadDialog::on_refreshListBtn_clicked() { #if QT_HAS_SDL if(m_joystick) { SDL_JoystickClose(m_joystick); - ui->JoyName->setText("No GamePad"); + ui->JoyName->setText(tr("No gamepad")); m_joystick = 0; } SDL_QuitSubSystem(SDL_INIT_JOYSTICK); @@ -114,7 +143,7 @@ void GamePadDialog::on_refreshListBtn_clicked() { QListWidgetItem* item = new QListWidgetItem(); QString padName = SDL_JoystickName(i); - if(padName == "") padName = "Unknown GamePad"; + if(padName == "") padName = tr("Unknown gamepad"); item->setText(padName); item->setData(Qt::UserRole,i); ui->GamePadList->addItem(item); @@ -255,7 +284,7 @@ void GamePadDialog::on_SelectPadBtn_clicked() ui->comboPSPButton->clear(); QTreeWidgetItem* buttonItem = new QTreeWidgetItem(); - buttonItem->setText(0,"Buttons"); + buttonItem->setText(0,tr("Buttons")); ui->padValues->addTopLevelItem(buttonItem); for(int i = 0; i < SDL_JoystickNumButtons(m_joystick); i++) @@ -269,37 +298,37 @@ void GamePadDialog::on_SelectPadBtn_clicked() buttonItem->addChild(item); int id = i << 8; - ui->comboPadInput->addItem("Button "+QVariant(i).toString(),GetIntFromMapping(i,0,0)); + ui->comboPadInput->addItem(tr("Button %1").arg(i),GetIntFromMapping(i,0,0)); } QTreeWidgetItem* axesItem = new QTreeWidgetItem(); - axesItem->setText(0,"Axes"); + axesItem->setText(0,tr("Axes")); ui->padValues->addTopLevelItem(axesItem); for(int i = 0; i < SDL_JoystickNumAxes(m_joystick); i++) { QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QVariant(i).toString()+" Neg"); + item->setText(0,tr("%1 Neg").arg(i)); item->setText(1,QVariant(0).toString()); item->setData(0, Qt::UserRole,1); item->setData(0, Qt::UserRole+1,i); item->setData(0, Qt::UserRole+2,-1); axesItem->addChild(item); - ui->comboPadInput->addItem("Axes "+QVariant(i).toString()+" Neg",GetIntFromMapping(i,1,-1)); + ui->comboPadInput->addItem(tr("Axes %1 Neg").arg(i),GetIntFromMapping(i,1,-1)); item = new QTreeWidgetItem(); - item->setText(0,QVariant(i).toString()+" Pos"); + item->setText(0,tr("%1 Pos").arg(i)); item->setText(1,QVariant(0).toString()); item->setData(0, Qt::UserRole,1); item->setData(0, Qt::UserRole+1,i); item->setData(0, Qt::UserRole+2,1); axesItem->addChild(item); - ui->comboPadInput->addItem("Axes "+QVariant(i).toString()+" Pos",GetIntFromMapping(i,1,1)); + ui->comboPadInput->addItem(tr("Axes %1 Pos").arg(i),GetIntFromMapping(i,1,1)); } QTreeWidgetItem* hatsItem = new QTreeWidgetItem(); - hatsItem->setText(0,"Hats"); + hatsItem->setText(0,tr("Hats")); ui->padValues->addTopLevelItem(hatsItem); for(int i = 0; i < SDL_JoystickNumHats(m_joystick); i++) @@ -312,14 +341,14 @@ void GamePadDialog::on_SelectPadBtn_clicked() item->setData(0, Qt::UserRole+2,0); hatsItem->addChild(item); - ui->comboPadInput->addItem("Button "+QVariant(i).toString(),GetIntFromMapping(i,2,0)); + ui->comboPadInput->addItem(tr("Button %1").arg(i),GetIntFromMapping(i,2,0)); } for(int i = 0; i < 18; i++) { if(GamepadPadMapping[i].Name != "") { - ui->comboPSPButton->addItem(GamepadPadMapping[i].Name,i); + ui->comboPSPButton->addItem(QApplication::translate("gamepadMapping", GamepadPadMapping[i].Name.toStdString().c_str()),i); } } @@ -334,9 +363,9 @@ void GamePadDialog::SetViewMode() ui->refreshListBtn->setEnabled(true); ui->SelectPadBtn->setEnabled(true); if(!m_joystick) - ui->JoyName->setText("No GamePad"); + ui->JoyName->setText(tr("No gamepad")); else - ui->JoyName->setText(QString("Current gamepad : ")+SDL_JoystickName(m_joyId)+""); + ui->JoyName->setText(tr("Current gamepad: %1").arg(SDL_JoystickName(m_joyId))); #endif } diff --git a/Qt/gamepaddialog.h b/Qt/gamepaddialog.h index dabae24b4a..24163731cc 100644 --- a/Qt/gamepaddialog.h +++ b/Qt/gamepaddialog.h @@ -23,7 +23,11 @@ public: void SetViewMode(); void SetCalibMode(); void CalibNextButton(); +protected: + void showEvent(QShowEvent *); + void changeEvent(QEvent *); private slots: + void releaseLock(); void on_refreshListBtn_clicked(); void on_SelectPadBtn_clicked(); diff --git a/Qt/languages/ppsspp_en.ts b/Qt/languages/ppsspp_en.ts new file mode 100644 index 0000000000..9343a24500 --- /dev/null +++ b/Qt/languages/ppsspp_en.ts @@ -0,0 +1,634 @@ + + + + + Controls + + + Controls + + + + + Debugger_Disasm + + + Dialog + + + + + Ctr: + + + + + &Go to + + + + + &PC + + + + + &LR + + + + + Tab 1 + + + + + Tab 2 + + + + + &Go + + + + + Stop + + + + + Step &Into + + + + + Step &Over + + + + + S&kip + + + + + Next &HLE + + + + + GamePadDialog + + + Gamepad Configuration + + + + + GamePad List + + + + + Refresh + + + + + Select + + + + + Gamepad Values : + + + + + TextLabel + + + + + Assign Gamepad input + + + + + to PSP button/axis + + + + + Assign + + + + + Press buttons on your gamePad to verify mapping : + + + + + + <b>No gamepad</b> + + + + + <b>Unknown gamepad</b> + + + + + Buttons + + + + + + Button %1 + + + + + Axes + + + + + %1 Neg + + + + + Axes %1 Neg + + + + + %1 Pos + + + + + Axes %1 Pos + + + + + Hats + + + + + <b>Current gamepad: %1</b> + + + + + MainWindow + + + PPSSPP + + + + + &File + + + + + &Emulation + + + + + Debu&g + + + + + &Options + + + + + &Log Levels + + + + + G3D + + + + + HLE + + + + + Default + + + + + Zoom + + + + + Language + + + + + &Help + + + + + &Open... + + + + + &Close + + + + + - + + + + + Quickload state + + + + + F4 + + + + + Quicksave state + + + + + F2 + + + + + &Load State File... + + + + + &Save State File... + + + + + E&xit + + + + + &Run + + + + + F7 + + + + + &Pause + + + + + F8 + + + + + R&eset + + + + + &Interpreter + + + + + &Slightly Faster Interpreter + + + + + &Dynarec + + + + + Load &Map File... + + + + + &Save Map File... + + + + + &Reset Symbol Table + + + + + &Disassembly + + + + + Ctrl+D + + + + + &Log Console + + + + + Ctrl+L + + + + + Memory &View... + + + + + Ctrl+M + + + + + Keyboard &Controls + + + + + &Toggle Full Screen + + + + + F12 + + + + + &Buffered Rendering + + + + + F5 + + + + + &Hardware Transform + + + + + F6 + + + + + &Linear Filtering + + + + + &Wireframe (experimental) + + + + + &Display Raw Framebuffer + + + + + &Show Debug Statistics + + + + + Screen &1x + + + + + Ctrl+1 + + + + + Screen &2x + + + + + Ctrl+2 + + + + + Screen &3x + + + + + Ctrl+3 + + + + + Screen &4x + + + + + Ctrl+4 + + + + + &Fast Memory (dynarec, unstable) + + + + + &Ignore illegal reads/writes + + + + + &Go to http://www.ppsspp.org/ + + + + + &About PPSSPP... + + + + + &Use VBO + + + + + + + Debug + + + + + + + Warning + + + + + + + Error + + + + + + + Info + + + + + GamePad Controls + + + + + No translations + + + + + gamepadMapping + + + Cross + + + + + Circle + + + + + Square + + + + + Triangle + + + + + Left Trigger + + + + + Right Trigger + + + + + Start + + + + + Select + + + + + Up + + + + + Down + + + + + Left + + + + + Right + + + + + Home + + + + + Stick left + + + + + Stick right + + + + + Stick up + + + + + Stick bottom + + + + diff --git a/Qt/languages/ppsspp_pl.ts b/Qt/languages/ppsspp_pl.ts new file mode 100644 index 0000000000..c2d398d637 --- /dev/null +++ b/Qt/languages/ppsspp_pl.ts @@ -0,0 +1,634 @@ + + + + + Controls + + + Controls + + + + + Debugger_Disasm + + + Dialog + + + + + Ctr: + + + + + &Go to + + + + + &PC + + + + + &LR + + + + + Tab 1 + + + + + Tab 2 + + + + + &Go + + + + + Stop + + + + + Step &Into + + + + + Step &Over + + + + + S&kip + + + + + Next &HLE + + + + + GamePadDialog + + + Gamepad Configuration + Konfiguracja kontrolera + + + + GamePad List + Lista kontrolerów + + + + Refresh + Odśwież + + + + Select + Wybierz + + + + Gamepad Values : + Wartości przycisków/osi: + + + + TextLabel + + + + + Assign Gamepad input + Przypisz przycisk + + + + to PSP button/axis + do przycisku/osi PSP + + + + Assign + Przypisz + + + + Press buttons on your gamePad to verify mapping : + Naciśnij przyciski na kontrolerze: + + + + + <b>No gamepad</b> + <b>Nie wykryto pada</b> + + + + <b>Unknown gamepad</b> + <b>Nieznany pad</b> + + + + Buttons + Przyciski + + + + + Button %1 + Przycisk %1 + + + + Axes + Osie + + + + %1 Neg + %1 zanegowany + + + + Axes %1 Neg + Oś %1 zanegowana + + + + %1 Pos + %1 pozycja + + + + Axes %1 Pos + Pozycja osi %1 + + + + Hats + + + + + <b>Current gamepad: %1</b> + <b>Wybrany pad: %1</b> + + + + MainWindow + + + PPSSPP + PPSSPP + + + + &File + &Plik + + + + &Emulation + &Emulacja + + + + Debu&g + &Debugger + + + + &Options + &Opcje + + + + &Log Levels + P&oziomy logowania + + + + G3D + G3D + + + + HLE + HLE + + + + Default + Domyślne + + + + Zoom + Zoom + + + + Language + Język + + + + &Help + Pomo&c + + + + &Open... + &Otwórz... + + + + &Close + &Zamknij + + + + - + + + + + Quickload state + Wczytaj stan + + + + F4 + F4 + + + + Quicksave state + Zapisz stan + + + + F2 + F2 + + + + &Load State File... + &Wczytaj plik stanu... + + + + &Save State File... + &Zapisz plik stanu... + + + + E&xit + Wyj&dź + + + + &Run + &Uruchom + + + + F7 + F7 + + + + &Pause + &Pauza + + + + F8 + F8 + + + + R&eset + &Reset + + + + &Interpreter + &Interpreter + + + + &Slightly Faster Interpreter + &Szybszy interpreter + + + + &Dynarec + R&ekompilacja (Dynarec) + + + + Load &Map File... + &Wczytaj plik mapy... + + + + &Save Map File... + &Zapisz plik mapy... + + + + &Reset Symbol Table + Zresetuj &tablicę symboli + + + + &Disassembly + &Dekompiluj + + + + Ctrl+D + Ctrl+D + + + + &Log Console + &Konsola logowania + + + + Ctrl+L + Ctrl+L + + + + Memory &View... + Widok &pamięci... + + + + Ctrl+M + Ctrl+M + + + + Keyboard &Controls + Ustawienia &klawiatury + + + + &Toggle Full Screen + &Pełny ekran + + + + F12 + F12 + + + + &Buffered Rendering + &Buffered rendering + + + + F5 + F5 + + + + &Hardware Transform + &Hardware Transform + + + + F6 + F6 + + + + &Linear Filtering + &Linear Filtering + + + + &Wireframe (experimental) + + + + + &Display Raw Framebuffer + + + + + &Show Debug Statistics + Pokaż &statystyki emulacji + + + + Screen &1x + &1x + + + + Ctrl+1 + Ctrl+1 + + + + Screen &2x + &2x + + + + Ctrl+2 + Ctrl+2 + + + + Screen &3x + &3x + + + + Ctrl+3 + Ctrl+3 + + + + Screen &4x + &4x + + + + Ctrl+4 + Ctrl+4 + + + + &Fast Memory (dynarec, unstable) + &Fast memory (wymagany Dynarec, niestabilne) + + + + &Ignore illegal reads/writes + &Ignoruj nieprawidłowe odczyty/zapisy + + + + &Go to http://www.ppsspp.org/ + &Idź do http://www.ppsspp.org + + + + &About PPSSPP... + &O PPSSPP... + + + + &Use VBO + Użyj &VBO + + + + + + Debug + Debug + + + + + + Warning + Ostrzeżenia + + + + + + Error + Błędy + + + + + + Info + Info + + + + GamePad Controls + &Ustawienia pada + + + + No translations + + + + + gamepadMapping + + + Cross + Krzyżyk + + + + Circle + Kółko + + + + Square + Kwadrat + + + + Triangle + Trójkąt + + + + Left Trigger + Lewy trigger + + + + Right Trigger + Prawy trigger + + + + Start + Start + + + + Select + Select + + + + Up + Góra + + + + Down + Dół + + + + Left + Lewo + + + + Right + Prawo + + + + Home + Klawisz Home + + + + Stick left + Lewo (analog) + + + + Stick right + Prawo (analog) + + + + Stick up + Góra (analog) + + + + Stick bottom + Dół (analog) + + + diff --git a/Qt/mainui.cpp b/Qt/mainui.cpp deleted file mode 100644 index c210f09c7e..0000000000 --- a/Qt/mainui.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "mainui.h" - -#include -#include -#include "base/display.h" -#include "base/logging.h" -#include "base/timeutil.h" -#include "file/zip_read.h" -#include "base/NativeApp.h" -#include "net/resolve.h" -#include "base/display.h" - - -void SimulateGamepad(InputState *input) { - input->pad_lstick_x = 0; - input->pad_lstick_y = 0; - input->pad_rstick_x = 0; - input->pad_rstick_y = 0; - - if (input->pad_buttons & (1<<14)) - input->pad_lstick_y=1; - else if (input->pad_buttons & (1<<15)) - input->pad_lstick_y=-1; - if (input->pad_buttons & (1<<16)) - input->pad_lstick_x=-1; - else if (input->pad_buttons & (1<<17)) - input->pad_lstick_x=1; -} - -MainUI::~MainUI() { - NativeShutdownGraphics(); -} - -bool MainUI::event(QEvent *e) -{ - QList touchPoints; - switch(e->type()) - { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - touchPoints = static_cast(e)->touchPoints(); - foreach (const QTouchEvent::TouchPoint &touchPoint, touchPoints) { - switch (touchPoint.state()) { - case Qt::TouchPointStationary: - break; - case Qt::TouchPointPressed: - case Qt::TouchPointReleased: - input_state.pointer_down[touchPoint.id()] = (touchPoint.state() == Qt::TouchPointPressed); - case Qt::TouchPointMoved: - input_state.pointer_x[touchPoint.id()] = touchPoint.pos().x() * dpi_scale; - input_state.pointer_y[touchPoint.id()] = touchPoint.pos().y() * dpi_scale; - break; - default: - break; - } - } - break; - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - input_state.pointer_down[0] = (e->type() == QEvent::MouseButtonPress); - case QEvent::MouseMove: - input_state.pointer_x[0] = ((QMouseEvent*)e)->pos().x() * dpi_scale; - input_state.pointer_y[0] = ((QMouseEvent*)e)->pos().y() * dpi_scale; - break; - case QEvent::KeyPress: - for (int b = 0; b < 14; b++) { - if (((QKeyEvent*)e)->key() == buttonMappings[b]) - input_state.pad_buttons |= (1<key() == buttonMappings[b]) - input_state.pad_buttons &= ~(1<accept(); - return true; -} - -void MainUI::initializeGL() -{ -#ifndef USING_GLES2 - glewInit(); -#endif - NativeInitGraphics(); -} -void MainUI::paintGL() -{ - SimulateGamepad(&input_state); - UpdateInputState(&input_state); - NativeUpdate(input_state); - EndInputState(&input_state); - NativeRender(); - - update(); -} diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 36e828bce8..125043c00b 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -48,6 +48,7 @@ MainWindow::MainWindow(QWidget *parent) : DialogManager::AddDlg(vfpudlg = new CVFPUDlg(_hInstance, hwndMain, currentDebugMIPS)); */ // Update(); + createLanguageMenu(); UpdateMenus(); int zoom = g_Config.iWindowZoom; @@ -55,16 +56,16 @@ MainWindow::MainWindow(QWidget *parent) : if (zoom > 4) zoom = 4; SetZoom(zoom); + EmuThread_Start(w); + if (!fileToStart.isNull()) { SetPlaying(fileToStart); //Update(); UpdateMenus(); - EmuThread_Start(fileToStart, w); + EmuThread_StartGame(fileToStart); } - else - BrowseAndBoot(); if (!fileToStart.isNull() && stateToLoad != NULL) SaveState::Load(stateToLoad); @@ -195,7 +196,7 @@ void MainWindow::BrowseAndBoot(void) { QFileInfo info(filename); g_Config.currentDirectory = info.absolutePath().toStdString(); - EmuThread_Start(filename, w); + EmuThread_StartGame(filename); } } @@ -324,7 +325,7 @@ void MainWindow::on_action_EmulationStop_triggered() if (memoryWindow[i]) SendMessage(memoryWindow[i]->GetDlgHandle(), WM_CLOSE, 0, 0);*/ - EmuThread_Stop(); + EmuThread_StopGame(); SetPlaying(0); //Update(); UpdateMenus(); @@ -485,6 +486,7 @@ void MainWindow::on_action_OptionsHardwareTransform_triggered() void MainWindow::on_action_FileExit_triggered() { on_action_EmulationStop_triggered(); + EmuThread_Stop(); QApplication::exit(0); } @@ -778,3 +780,102 @@ void MainWindow::on_action_OptionsGamePadControls_triggered() QMessageBox::information(this,"Gamepad","You need to compile with SDL to have Gamepad support.", QMessageBox::Ok); #endif } + +void MainWindow::on_language_changed(QAction *action) +{ + if (0 != action) + { + loadLanguage(action->data().toString()); + } +} + +void switchTranslator(QTranslator &translator, const QString &filename) +{ + qApp->removeTranslator(&translator); + + if (translator.load(filename)) + qApp->installTranslator(&translator); +} + +void MainWindow::loadLanguage(const QString& language) +{ + if (currentLanguage != language) + { + currentLanguage = language; + QLocale locale = QLocale(currentLanguage); + QLocale::setDefault(locale); + QString languageName = QLocale::languageToString(locale.language()); + switchTranslator(translator, QString("languages/ppsspp_%1.qm").arg(language)); + } +} + +void MainWindow::createLanguageMenu() +{ + QActionGroup *langGroup = new QActionGroup(ui->menuLanguage); + langGroup->setExclusive(true); + + connect(langGroup, SIGNAL(triggered(QAction *)), this, SLOT(on_language_changed(QAction *))); + + QString defaultLocale = QLocale::system().name(); + defaultLocale.truncate(defaultLocale.lastIndexOf('_')); + languagePath = QApplication::applicationDirPath(); + languagePath.append("/languages"); + QDir langDir(languagePath); + QStringList fileNames = langDir.entryList(QStringList("ppsspp_*.qm")); + + if (fileNames.size() == 0) + { + QAction *action = new QAction(tr("No translations"), this); + action->setCheckable(false); + action->setDisabled(true); + ui->menuLanguage->addAction(action); + langGroup->addAction(action); + } + + for (int i = 0; i < fileNames.size(); ++i) + { + QString locale = fileNames[i]; + locale.truncate(locale.lastIndexOf('.')); + locale.remove(0, locale.indexOf('_') + 1); + + //QString language = QLocale::languageToString(QLocale(locale).language()); + QString language = QLocale(locale).nativeLanguageName(); + QAction *action = new QAction(language, this); + action->setCheckable(true); + action->setData(locale); + + ui->menuLanguage->addAction(action); + langGroup->addAction(action); + + // TODO check en as default until we save language to config + if ("en" == locale) + { + action->setChecked(true); + currentLanguage = "en"; + } + } +} + +void MainWindow::changeEvent(QEvent *event) +{ + QMainWindow::changeEvent(event); + + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + case QEvent::LocaleChange: + { + QString locale = QLocale::system().name(); + locale.truncate(locale.lastIndexOf('_')); + loadLanguage(locale); + } + break; + default: + break; + } + } +} diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index d8a41a0e99..dbb27e3323 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -2,6 +2,7 @@ #define MAINWINDOW_H #include +#include #include "Core/Core.h" #include "input/input_state.h" @@ -135,7 +136,18 @@ private slots: void on_action_OptionsGamePadControls_triggered(); + void on_language_changed(QAction *action); + private: + void loadLanguage(const QString &language); + void createLanguageMenu(); + void changeEvent(QEvent *); + + QTranslator translator; + QTranslator qtTranslator; + QString currentLanguage; + QString languagePath; + Ui::MainWindow *ui; QtEmuGL* w; diff --git a/Qt/mainwindow.ui b/Qt/mainwindow.ui index 1a563fe407..acf08f6a94 100644 --- a/Qt/mainwindow.ui +++ b/Qt/mainwindow.ui @@ -44,7 +44,7 @@ 0 0 800 - 23 + 21 @@ -135,6 +135,11 @@ + + + Language + + @@ -155,6 +160,7 @@ + diff --git a/Qt/qtemugl.cpp b/Qt/qtemugl.cpp index 6e1d54062a..f9b72ec725 100644 --- a/Qt/qtemugl.cpp +++ b/Qt/qtemugl.cpp @@ -38,6 +38,28 @@ void QtEmuGL::stop_rendering() thread.Shutdown(); } +void QtEmuGL::start_game(QString filename) +{ + thread.startGame(filename); +} + +void QtEmuGL::stop_game() +{ + thread.stopGame(); +} + +void QtEmuGL::LockDraw(bool value) +{ + if(value) + { + thread.gameMutex.lock(); + } + else + { + thread.gameMutex.unlock(); + } +} + void QtEmuGL::resizeEvent(QResizeEvent *evt) { // TODO diff --git a/Qt/qtemugl.h b/Qt/qtemugl.h index 228d21be12..23b33a430d 100644 --- a/Qt/qtemugl.h +++ b/Qt/qtemugl.h @@ -16,6 +16,9 @@ public: void start_rendering(); void stop_rendering(); + void start_game(QString filename); + void stop_game(); + void LockDraw(bool value); protected: void initializeGL(); diff --git a/README.md b/README.md index d6bff2fa81..e632d17bd9 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,7 @@ with the mouse. Alternatively, a Qt-based frontend is available in the Qt/ dir. Open PPSSPPQt.pro with Qt Creator. Install libsdl1.2 if you want to use USB Gamepad. -For Ubuntu user (>= 12.10) : -- Remove multimedia in PPSSPP.pro on line 3 +If the build has an error about finding mobility or multimedia: - Install the package "qtmobility-dev" Building for OSX diff --git a/Windows/main.cpp b/Windows/main.cpp index c875f0c7b2..b568189d07 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -191,6 +191,8 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin } } + VFSShutdown(); + LogManager::Shutdown(); DialogManager::DestroyAll(); timeEndPeriod(1); diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index fe88bd76d4..99fb62bb6e 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -179,8 +179,8 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,176,217,50,14 PUSHBUTTON "Cancel",IDCANCEL,230,217,50,14 - LTEXT "NOTE: Currently controls are NOT configurable.",IDC_STATIC,7,7,188,10 - LISTBOX IDC_LISTCONTROLS,7,19,273,184,LBS_SORT | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LTEXT "NOTE: Currently controls are NOT configurable.\nHowever: Xbox 360 controllers are supported! Just plug one in and use it!",IDC_STATIC,7,7,188,30 + LISTBOX IDC_LISTCONTROLS,7,39,273,164,LBS_SORT | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP END diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 9f3ce7a4e4..236c3630d5 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="61" + android:versionName="0.61" > @@ -26,4 +26,4 @@ - \ No newline at end of file + diff --git a/android/jni/MenuScreens.cpp b/android/jni/MenuScreens.cpp index 0f54e7c339..89af85abd2 100644 --- a/android/jni/MenuScreens.cpp +++ b/android/jni/MenuScreens.cpp @@ -451,6 +451,7 @@ static const char *credits[] = "soywiz", "kovensky", "raven02", + "xele", "", "Written in C++ for speed and portability", "", diff --git a/headless/Compare.cpp b/headless/Compare.cpp new file mode 100644 index 0000000000..02252d9833 --- /dev/null +++ b/headless/Compare.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2012- PPSSPP 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 git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Compare.h" +#include "FileUtil.h" + +#include + +bool CompareOutput(const std::string bootFilename) +{ + std::string expect_filename = bootFilename.substr(bootFilename.length() - 4) + ".expected"; + if (File::Exists(expect_filename)) + { + // TODO: Do the compare here + return true; + } + else + { + fprintf(stderr, "Expectation file %s not found", expect_filename.c_str()); + return false; + } +} + +inline int ComparePixel(u32 pix1, u32 pix2) +{ + // For now, if they're different at all except alpha, it's an error. + if ((pix1 & 0xFFFFFF) != (pix2 & 0xFFFFFF)) + return 1; + return 0; +} + +double CompareScreenshot(const u8 *pixels, int w, int h, int stride, const std::string screenshotFilename, std::string &error) +{ + u32 *pixels32 = (u32 *) pixels; + // We assume the bitmap is the specified size, not including whatever stride. + u32 *reference = (u32 *) calloc(w * h, sizeof(u32)); + + FILE *bmp = fopen(screenshotFilename.c_str(), "rb"); + if (bmp) + { + // The bitmap header is 14 + 40 bytes. We could validate it but the test would fail either way. + fseek(bmp, 14 + 40, SEEK_SET); + fread(reference, sizeof(u32), w * h, bmp); + fclose(bmp); + } + else + { + error = "Unable to read screenshot: " + screenshotFilename; + free(reference); + return -1.0f; + } + + u32 errors = 0; + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + errors += ComparePixel(pixels32[y * stride + x], reference[y * w + x]); + } + + free(reference); + + return (double) errors / (double) (w * h); +} \ No newline at end of file diff --git a/headless/Compare.h b/headless/Compare.h new file mode 100644 index 0000000000..f51f6c1be2 --- /dev/null +++ b/headless/Compare.h @@ -0,0 +1,23 @@ +// Copyright (c) 2012- PPSSPP 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 git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include "Globals.h" + +bool CompareOutput(std::string bootFilename); +double CompareScreenshot(const u8 *pixels, int w, int h, int stride, const std::string screenshotFilename, std::string &error); \ No newline at end of file diff --git a/headless/Headless.cpp b/headless/Headless.cpp index b01338982e..3cc16ae7f9 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -13,6 +13,7 @@ #include "Log.h" #include "LogManager.h" +#include "Compare.h" #include "StubHost.h" #ifdef _WIN32 #include "WindowsHeadlessHost.h" @@ -59,10 +60,14 @@ void printUsage(const char *progname, const char *reason) HEADLESSHOST_CLASS h1; HeadlessHost h2; if (typeid(h1) != typeid(h2)) + { fprintf(stderr, " --graphics use the full gpu backend (slower)\n"); + fprintf(stderr, " --screenshot=FILE compare against a screenshot\n"); + } + fprintf(stderr, " -i use the interpreter\n"); fprintf(stderr, " -f use the fast interpreter\n"); - fprintf(stderr, " -j use jit (overrides -f)\n"); + fprintf(stderr, " -j use jit (default)\n"); fprintf(stderr, " -c, --compare compare with output in file.expected\n"); fprintf(stderr, "\nSee headless.txt for details.\n"); } @@ -77,6 +82,7 @@ int main(int argc, const char* argv[]) const char *bootFilename = 0; const char *mountIso = 0; + const char *screenshotFilename = 0; bool readMount = false; for (int i = 1; i < argc; i++) @@ -91,6 +97,8 @@ int main(int argc, const char* argv[]) readMount = true; else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--log")) fullLog = true; + else if (!strcmp(argv[i], "-i")) + useJit = false; else if (!strcmp(argv[i], "-j")) useJit = true; else if (!strcmp(argv[i], "-f")) @@ -99,6 +107,8 @@ int main(int argc, const char* argv[]) autoCompare = true; else if (!strcmp(argv[i], "--graphics")) useGraphics = true; + else if (!strncmp(argv[i], "--screenshot=", strlen("--screenshot=")) && strlen(argv[i]) > strlen("--screenshot=")) + screenshotFilename = argv[i] + strlen("--screenshot="); else if (bootFilename == 0) bootFilename = argv[i]; else @@ -146,7 +156,7 @@ int main(int argc, const char* argv[]) coreParameter.fileToStart = bootFilename; coreParameter.mountIso = mountIso ? mountIso : ""; coreParameter.startPaused = false; - coreParameter.cpuCore = useJit ? CPU_JIT : (fastInterpreter ? CPU_FASTINTERPRETER : CPU_INTERPRETER); + coreParameter.cpuCore = fastInterpreter ? CPU_FASTINTERPRETER : (useJit ? CPU_JIT : CPU_INTERPRETER); coreParameter.gpuCore = headlessHost->isGLWorking() ? GPU_GLES : GPU_NULL; coreParameter.enableSound = false; coreParameter.headLess = true; @@ -174,6 +184,9 @@ int main(int argc, const char* argv[]) host->BootDone(); + if (screenshotFilename != 0) + headlessHost->SetComparisonScreenshot(screenshotFilename); + coreState = CORE_RUNNING; while (coreState == CORE_RUNNING) { @@ -195,17 +208,7 @@ int main(int argc, const char* argv[]) headlessHost = NULL; if (autoCompare) - { - std::string expect_filename = std::string(bootFilename).substr(strlen(bootFilename - 4)) + ".expected"; - if (File::Exists(expect_filename)) - { - // TODO: Do the compare here - } - else - { - fprintf(stderr, "Expectation file %s not found", expect_filename.c_str()); - } - } + CompareOutput(bootFilename); return 0; } diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index bc5cb35624..ecfadbd1ab 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -145,6 +145,7 @@ + NotUsing NotUsing @@ -177,6 +178,7 @@ + diff --git a/headless/Headless.vcxproj.filters b/headless/Headless.vcxproj.filters index 5982bf35db..e0debd7e7b 100644 --- a/headless/Headless.vcxproj.filters +++ b/headless/Headless.vcxproj.filters @@ -4,6 +4,7 @@ + @@ -11,5 +12,6 @@ + \ No newline at end of file diff --git a/headless/StubHost.h b/headless/StubHost.h index 9445aee06c..1533c381c3 100644 --- a/headless/StubHost.h +++ b/headless/StubHost.h @@ -50,6 +50,7 @@ public: virtual bool AttemptLoadSymbolMap() {return false;} virtual void SendDebugOutput(const std::string &output) { printf("%s", output.c_str()); } + virtual void SetComparisonScreenshot(const std::string &filename) {} virtual bool isGLWorking() { return false; } }; \ No newline at end of file diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 606235c26a..5e7448ae23 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "WindowsHeadlessHost.h" +#include "Compare.h" #include #include @@ -89,6 +90,54 @@ void WindowsHeadlessHost::SendDebugOutput(const std::string &output) OutputDebugString(output.c_str()); } +void WindowsHeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) +{ + // We ignore the current framebuffer parameters and just grab the full screen. + const static int FRAME_WIDTH = 512; + const static int FRAME_HEIGHT = 272; + u8 *pixels = new u8[FRAME_WIDTH * FRAME_HEIGHT * 4]; + + // TODO: Maybe this code should be moved into GLES_GPU. + glReadBuffer(GL_FRONT); + glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, pixels); + + std::string error; + double errors = CompareScreenshot(pixels, FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH, comparisonScreenshot, error); + if (errors < 0) + fprintf_s(out, "%s\n", error.c_str()); + + if (errors > 0) + { + fprintf_s(out, "Screenshot error: %f%%\n", errors * 100.0f); + + // Lazy, just read in the original header to output the failed screenshot. + u8 header[14 + 40] = {0}; + FILE *bmp = fopen(comparisonScreenshot.c_str(), "rb"); + if (bmp) + { + fread(&header, sizeof(header), 1, bmp); + fclose(bmp); + } + + FILE *saved = fopen("__testfailure.bmp", "wb"); + if (saved) + { + fwrite(&header, sizeof(header), 1, saved); + fwrite(pixels, sizeof(u32), FRAME_WIDTH * FRAME_HEIGHT, saved); + fclose(saved); + + fprintf_s(out, "Actual output written to: __testfailure.bmp\n"); + } + } + + delete [] pixels; +} + +void WindowsHeadlessHost::SetComparisonScreenshot(const std::string &filename) +{ + comparisonScreenshot = filename; +} + void WindowsHeadlessHost::InitGL() { glOkay = false; diff --git a/headless/WindowsHeadlessHost.h b/headless/WindowsHeadlessHost.h index 28e51ee543..5abc20ca05 100644 --- a/headless/WindowsHeadlessHost.h +++ b/headless/WindowsHeadlessHost.h @@ -35,6 +35,8 @@ public: virtual bool isGLWorking() { return glOkay; } virtual void SendDebugOutput(const std::string &output); + virtual void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h); + virtual void SetComparisonScreenshot(const std::string &filename); private: bool ResizeGL(); @@ -45,4 +47,5 @@ private: HDC hDC; HGLRC hRC; FILE *out; + std::string comparisonScreenshot; }; \ No newline at end of file diff --git a/native b/native index f22ad17d40..3caced8524 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit f22ad17d40c00d9a60bd21f53820012b302d7559 +Subproject commit 3caced8524c06cabcf968942a7780d80337de7bf diff --git a/pspautotests b/pspautotests index 4f047eb8c7..c4427bd55d 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 4f047eb8c76c6388a63a87ec72e98c64670152d5 +Subproject commit c4427bd55d57af2484f5ccd4ec1bed2bcf674395 diff --git a/test.py b/test.py index 52b2b906b1..2d98e03079 100755 --- a/test.py +++ b/test.py @@ -109,6 +109,12 @@ tests_good = [ "threads/semaphores/refer/refer", "threads/semaphores/signal/signal", "threads/semaphores/wait/wait", + "threads/vpl/vpl", + "threads/vpl/delete", + "threads/vpl/free", + "threads/vpl/priority", + "threads/vpl/refer", + "threads/vpl/try", "power/power", "umd/callbacks/umd", "umd/wait/wait", @@ -124,11 +130,13 @@ tests_next = [ "threads/msgpipe/msgpipe", "threads/scheduling/scheduling", "threads/threads/threads", - "threads/vpl/vpl", "threads/vtimers/vtimer", + "threads/vpl/allocate", + "threads/vpl/create", "threads/wakeup/wakeup", "gpu/simple/simple", "gpu/triangle/triangle", + "gpu/commands/basic", "hle/check_not_used_uids", "font/fonttest", "io/cwd/cwd", @@ -222,6 +230,8 @@ def run_tests(test_list, args): cmdline = [PPSSPP_EXE, elf_filename] cmdline.extend([i for i in args if i not in ['-v', '-g']]) + if os.path.exists(expected_filename + ".bmp"): + cmdline.extend(["--screenshot=" + expected_filename + ".bmp", "--graphics"]) c = Command(cmdline) c.run(TIMEOUT) @@ -231,6 +241,7 @@ def run_tests(test_list, args): if c.timeout: print(output) print("Test exceded limit of %d seconds." % TIMEOUT) + tests_failed.append(test) tcprint("##teamcity[testFailed name='%s' message='Test timeout']" % test) tcprint("##teamcity[testFinished name='%s']" % test) continue