mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge branch 'hrydgard:master' into feature_openxr_stereo
This commit is contained in:
commit
5e434bccdb
18 changed files with 1673 additions and 233 deletions
|
@ -477,6 +477,8 @@ source_group(MIPS FILES ${CommonMIPS})
|
|||
set(CommonRISCV64
|
||||
${CommonJIT}
|
||||
Common/RiscVCPUDetect.cpp
|
||||
Common/RiscVEmitter.cpp
|
||||
Common/RiscVEmitter.h
|
||||
Core/MIPS/fake/FakeJit.cpp
|
||||
Core/MIPS/fake/FakeJit.h
|
||||
)
|
||||
|
|
|
@ -475,6 +475,7 @@
|
|||
<ClInclude Include="Render\Text\draw_text_uwp.h" />
|
||||
<ClInclude Include="Render\Text\draw_text_win.h" />
|
||||
<ClInclude Include="LogReporting.h" />
|
||||
<ClInclude Include="RiscVEmitter.h" />
|
||||
<ClInclude Include="Serialize\SerializeDeque.h" />
|
||||
<ClInclude Include="Serialize\SerializeFuncs.h" />
|
||||
<ClInclude Include="Serialize\SerializeList.h" />
|
||||
|
@ -896,6 +897,7 @@
|
|||
<ClCompile Include="Render\Text\draw_text_win.cpp" />
|
||||
<ClCompile Include="LogReporting.cpp" />
|
||||
<ClCompile Include="RiscVCPUDetect.cpp" />
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
<ClCompile Include="Serialize\Serializer.cpp" />
|
||||
<ClCompile Include="Data\Convert\ColorConv.cpp" />
|
||||
<ClCompile Include="ConsoleListener.cpp" />
|
||||
|
|
|
@ -421,6 +421,7 @@
|
|||
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RiscVEmitter.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
|
@ -798,6 +799,7 @@
|
|||
<ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp">
|
||||
<Filter>GPU\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RiscVEmitter.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Crypto">
|
||||
|
|
1155
Common/RiscVEmitter.cpp
Normal file
1155
Common/RiscVEmitter.cpp
Normal file
File diff suppressed because it is too large
Load diff
361
Common/RiscVEmitter.h
Normal file
361
Common/RiscVEmitter.h
Normal file
|
@ -0,0 +1,361 @@
|
|||
// Copyright (c) 2022- 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/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "Common/CodeBlock.h"
|
||||
#include "Common/Common.h"
|
||||
|
||||
namespace RiscVGen {
|
||||
|
||||
enum RiscVReg {
|
||||
X0 = 0, X1, X2, X3, X4, X5, X6, X7,
|
||||
X8, X9, X10, X11, X12, X13, X14, X15,
|
||||
X16, X17, X18, X19, X20, X21, X22, X23,
|
||||
X24, X25, X26, X27, X28, X29, X30, X31,
|
||||
|
||||
R_ZERO = 0,
|
||||
R_RA = 1,
|
||||
R_SP = 2,
|
||||
R_GP = 3,
|
||||
R_TP = 4,
|
||||
R_FP = 8,
|
||||
|
||||
F0 = 0x20, F1, F2, F3, F4, F5, F6, F7,
|
||||
F8, F9, F10, F11, F12, F13, F14, F15,
|
||||
F16, F17, F18, F19, F20, F21, F22, F23,
|
||||
F24, F25, F26, F27, F28, F29, F30, F31,
|
||||
};
|
||||
|
||||
enum class FixupBranchType {
|
||||
B,
|
||||
J,
|
||||
};
|
||||
|
||||
enum class Fence {
|
||||
I = 0b1000,
|
||||
O = 0b0100,
|
||||
R = 0b0010,
|
||||
W = 0b0001,
|
||||
RW = R | W,
|
||||
IO = I | O,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(Fence);
|
||||
|
||||
enum class Atomic {
|
||||
NONE = 0b00,
|
||||
ACQUIRE = 0b10,
|
||||
RELEASE = 0b01,
|
||||
SEQUENTIAL = 0b11,
|
||||
};
|
||||
|
||||
enum class Round {
|
||||
NEAREST_EVEN = 0b000,
|
||||
TOZERO = 0b001,
|
||||
DOWN = 0b010,
|
||||
UP = 0b011,
|
||||
NEAREST_MAX = 0b100,
|
||||
DYNAMIC = 0b111,
|
||||
};
|
||||
|
||||
enum class FConv {
|
||||
W = 0x0000,
|
||||
WU = 0x0001,
|
||||
L = 0x0002,
|
||||
LU = 0x0003,
|
||||
|
||||
S = 0x1000,
|
||||
D = 0x1001,
|
||||
Q = 0x1003,
|
||||
};
|
||||
|
||||
enum class FMv {
|
||||
X,
|
||||
W,
|
||||
D,
|
||||
};
|
||||
|
||||
enum class Csr {
|
||||
FFlags = 0x001,
|
||||
FRm = 0x002,
|
||||
FCsr = 0x003,
|
||||
|
||||
Cycle = 0xC00,
|
||||
Time = 0xC01,
|
||||
InstRet = 0xC02,
|
||||
CycleH = 0xC80,
|
||||
TimeH = 0xC81,
|
||||
InstRetH = 0xC82,
|
||||
};
|
||||
|
||||
struct FixupBranch {
|
||||
FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {}
|
||||
~FixupBranch();
|
||||
|
||||
const u8 *ptr = nullptr;
|
||||
FixupBranchType type;
|
||||
};
|
||||
|
||||
class RiscVEmitter {
|
||||
public:
|
||||
RiscVEmitter() {}
|
||||
RiscVEmitter(const u8 *codePtr, u8 *writablePtr);
|
||||
virtual ~RiscVEmitter() {}
|
||||
|
||||
void SetCodePointer(const u8 *ptr, u8 *writePtr);
|
||||
const u8 *GetCodePointer() const;
|
||||
u8 *GetWritableCodePtr();
|
||||
|
||||
void ReserveCodeSpace(u32 bytes);
|
||||
const u8 *AlignCode16();
|
||||
const u8 *AlignCodePage();
|
||||
void FlushIcache();
|
||||
void FlushIcacheSection(const u8 *start, const u8 *end);
|
||||
|
||||
void SetJumpTarget(FixupBranch &branch);
|
||||
bool BInRange(const void *func) const;
|
||||
bool JInRange(const void *func) const;
|
||||
|
||||
void LUI(RiscVReg rd, s32 simm32);
|
||||
void AUIPC(RiscVReg rd, s32 simm32);
|
||||
|
||||
void JAL(RiscVReg rd, const void *dst);
|
||||
void JALR(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
FixupBranch JAL(RiscVReg rd);
|
||||
|
||||
// Psuedo-instructions for convenience/clarity.
|
||||
void J(const void *dst) {
|
||||
JAL(R_ZERO, dst);
|
||||
}
|
||||
void JR(RiscVReg rs1, u32 simm12 = 0) {
|
||||
JALR(R_ZERO, rs1, simm12);
|
||||
}
|
||||
void RET() {
|
||||
JR(R_RA);
|
||||
}
|
||||
FixupBranch J() {
|
||||
return JAL(R_ZERO);
|
||||
}
|
||||
|
||||
void BEQ(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BNE(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BLT(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BGE(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BLTU(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
void BGEU(RiscVReg rs1, RiscVReg rs2, const void *dst);
|
||||
FixupBranch BEQ(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BNE(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BLT(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BGE(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BLTU(RiscVReg rs1, RiscVReg rs2);
|
||||
FixupBranch BGEU(RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void LB(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LH(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LW(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LBU(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LHU(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
|
||||
void SB(RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
void SH(RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
void SW(RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
|
||||
void ADDI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void SLTI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void SLTIU(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void XORI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void ORI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void ANDI(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
|
||||
void NOP() {
|
||||
ADDI(R_ZERO, R_ZERO, 0);
|
||||
}
|
||||
void MV(RiscVReg rd, RiscVReg rs1) {
|
||||
ADDI(rd, rs1, 0);
|
||||
}
|
||||
void NOT(RiscVReg rd, RiscVReg rs1) {
|
||||
XORI(rd, rs1, -1);
|
||||
}
|
||||
|
||||
void SLLI(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void SRLI(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void SRAI(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
|
||||
void ADD(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SUB(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SLL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SLT(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SLTU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void XOR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SRL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SRA(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void OR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void AND(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void NEG(RiscVReg rd, RiscVReg rs) {
|
||||
SUB(rd, R_ZERO, rs);
|
||||
}
|
||||
|
||||
void FENCE(Fence predecessor, Fence successor);
|
||||
void FENCE_TSO();
|
||||
|
||||
void ECALL();
|
||||
void EBREAK();
|
||||
|
||||
// 64-bit instructions - oens ending in W sign extend result to 32 bits.
|
||||
void LWU(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void LD(RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void SD(RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
void ADDIW(RiscVReg rd, RiscVReg rs1, s32 simm12);
|
||||
void SLLIW(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void SRLIW(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void SRAIW(RiscVReg rd, RiscVReg rs1, u32 shamt);
|
||||
void ADDW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SUBW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SLLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SRLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void SRAW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void NEGW(RiscVReg rd, RiscVReg rs) {
|
||||
SUBW(rd, R_ZERO, rs);
|
||||
}
|
||||
|
||||
// Integer multiplication and division.
|
||||
void MUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void MULH(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void MULHSU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void MULHU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void DIV(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void DIVU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void REM(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void REMU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
// 64-bit only multiply and divide.
|
||||
void MULW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void DIVW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void DIVUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void REMW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void REMUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
// Atomic memory operations.
|
||||
void LR(int bits, RiscVReg rd, RiscVReg addr, Atomic ordering);
|
||||
void SC(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOSWAP(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOADD(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOAND(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOOR(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOXOR(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOMIN(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOMAX(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOMINU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
void AMOMAXU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering);
|
||||
|
||||
// Floating point (same funcs for single/double/quad, if supported.)
|
||||
void FL(int bits, RiscVReg rd, RiscVReg addr, s32 simm12);
|
||||
void FS(int bits, RiscVReg rs2, RiscVReg addr, s32 simm12);
|
||||
void FLW(RiscVReg rd, RiscVReg addr, s32 simm12) {
|
||||
FL(32, rd, addr, simm12);
|
||||
}
|
||||
void FSW(RiscVReg rs2, RiscVReg addr, s32 simm12) {
|
||||
FS(32, rs2, addr, simm12);
|
||||
}
|
||||
|
||||
void FMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC);
|
||||
void FMSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC);
|
||||
void FNMSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC);
|
||||
void FNMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC);
|
||||
|
||||
void FADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC);
|
||||
void FSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC);
|
||||
void FMUL(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC);
|
||||
void FDIV(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC);
|
||||
void FSQRT(int bits, RiscVReg rd, RiscVReg rs1, Round rm = Round::DYNAMIC);
|
||||
|
||||
void FSGNJ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FSGNJN(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FSGNJX(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void FMV(int bits, RiscVReg rd, RiscVReg rs) {
|
||||
FSGNJ(bits, rd, rs, rs);
|
||||
}
|
||||
void FNEG(int bits, RiscVReg rd, RiscVReg rs) {
|
||||
FSGNJN(bits, rd, rs, rs);
|
||||
}
|
||||
void FABS(int bits, RiscVReg rd, RiscVReg rs) {
|
||||
FSGNJX(bits, rd, rs, rs);
|
||||
}
|
||||
|
||||
void FMIN(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FMAX(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
|
||||
void FCVT(FConv to, FConv from, RiscVReg rd, RiscVReg rs1, Round rm = Round::DYNAMIC);
|
||||
void FMV(FMv to, FMv from, RiscVReg rd, RiscVReg rs1);
|
||||
|
||||
void FEQ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FLT(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FLE(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2);
|
||||
void FCLASS(int bits, RiscVReg rd, RiscVReg rs1);
|
||||
|
||||
// Control state register manipulation.
|
||||
void CSRRW(RiscVReg rd, Csr csr, RiscVReg rs1);
|
||||
void CSRRS(RiscVReg rd, Csr csr, RiscVReg rs1);
|
||||
void CSRRC(RiscVReg rd, Csr csr, RiscVReg rs1);
|
||||
void CSRRWI(RiscVReg rd, Csr csr, u8 uimm5);
|
||||
void CSRRSI(RiscVReg rd, Csr csr, u8 uimm5);
|
||||
void CSRRCI(RiscVReg rd, Csr csr, u8 uimm5);
|
||||
|
||||
void FRRM(RiscVReg rd) {
|
||||
CSRRS(rd, Csr::FRm, X0);
|
||||
}
|
||||
void FSRM(RiscVReg rs) {
|
||||
CSRRW(X0, Csr::FRm, rs);
|
||||
}
|
||||
void FSRMI(RiscVReg rd, Round rm) {
|
||||
_assert_msg_(rm != Round::DYNAMIC, "Cannot set FRm to DYNAMIC");
|
||||
CSRRWI(rd, Csr::FRm, (uint8_t)rm);
|
||||
}
|
||||
void FSRMI(Round rm) {
|
||||
FSRMI(X0, rm);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetJumpTarget(FixupBranch &branch, const void *dst);
|
||||
bool BInRange(const void *src, const void *dst) const;
|
||||
bool JInRange(const void *src, const void *dst) const;
|
||||
|
||||
inline void Write32(u32 value) {
|
||||
*(u32 *)writable_ = value;
|
||||
code_ += 4;
|
||||
writable_ += 4;
|
||||
}
|
||||
inline void Write16(u16 value) {
|
||||
*(u16 *)writable_ = value;
|
||||
code_ += 2;
|
||||
writable_ += 2;
|
||||
}
|
||||
|
||||
const u8 *code_ = nullptr;
|
||||
u8 *writable_ = nullptr;
|
||||
const u8 *lastCacheFlushEnd_ = nullptr;
|
||||
};
|
||||
|
||||
class MIPSCodeBlock : public CodeBlock<RiscVEmitter> {
|
||||
private:
|
||||
void PoisonMemory(int offset) override;
|
||||
};
|
||||
|
||||
};
|
|
@ -331,7 +331,7 @@ struct ConfigSetting {
|
|||
section->Get(ini5_, &ptr_.customButton->repeat, default_.customButton.repeat);
|
||||
return true;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "Unexpected ini setting type");
|
||||
_dbg_assert_msg_(false, "Get(%s): Unexpected ini setting type: %d", iniKey_, (int)type_);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ struct ConfigSetting {
|
|||
section->Set(ini5_, ptr_.customButton->repeat);
|
||||
return;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "Unexpected ini setting type");
|
||||
_dbg_assert_msg_(false, "Set(%s): Unexpected ini setting type: %d", iniKey_, (int)type_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -406,22 +406,22 @@ struct ConfigSetting {
|
|||
// Doesn't report.
|
||||
return;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "Unexpected ini setting type");
|
||||
_dbg_assert_msg_(false, "Report(%s): Unexpected ini setting type: %d", iniKey_, (int)type_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *iniKey_;
|
||||
const char *ini2_;
|
||||
const char *ini3_;
|
||||
const char *ini4_;
|
||||
const char *ini5_;
|
||||
const char *iniKey_ = nullptr;
|
||||
const char *ini2_ = nullptr;
|
||||
const char *ini3_ = nullptr;
|
||||
const char *ini4_ = nullptr;
|
||||
const char *ini5_ = nullptr;
|
||||
Type type_;
|
||||
bool report_;
|
||||
bool save_;
|
||||
bool perGame_;
|
||||
SettingPtr ptr_;
|
||||
DefaultValue default_;
|
||||
DefaultValue default_{};
|
||||
Callback cb_;
|
||||
|
||||
// We only support transform for ints.
|
||||
|
@ -1208,6 +1208,8 @@ static ConfigSetting vrSettings[] = {
|
|||
ConfigSetting("VREnableStereo", &g_Config.bEnableStereo, true),
|
||||
ConfigSetting("VRCanvasDistance", &g_Config.iCanvasDistance, 6),
|
||||
ConfigSetting("VRFieldOfView", &g_Config.iFieldOfViewPercentage, 100),
|
||||
|
||||
ConfigSetting(false),
|
||||
};
|
||||
|
||||
static ConfigSectionSettings sections[] = {
|
||||
|
|
|
@ -104,9 +104,9 @@ void GenerateDepalShader300(ShaderWriter &writer, const DepalConfig &config) {
|
|||
if (config.bufferFormat == GE_FORMAT_DEPTH16 && config.textureFormat == GE_TFMT_5650) {
|
||||
// Convert depth to 565, without going through a CLUT.
|
||||
writer.C(" int idepth = int(clamp(depth, 0.0, 65535.0));\n");
|
||||
writer.C(" float r = (idepth & 31) / 31.0f;\n");
|
||||
writer.C(" float g = ((idepth >> 5) & 63) / 63.0f;\n");
|
||||
writer.C(" float b = ((idepth >> 11) & 31) / 31.0f;\n");
|
||||
writer.C(" float r = float(idepth & 31) / 31.0f;\n");
|
||||
writer.C(" float g = float((idepth >> 5) & 63) / 63.0f;\n");
|
||||
writer.C(" float b = float((idepth >> 11) & 31) / 31.0f;\n");
|
||||
writer.C(" vec4 outColor = vec4(r, g, b, 1.0);\n");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -253,6 +253,20 @@ Draw2DPipeline *Draw2D::Create2DPipeline(std::function<Draw2DPipelineInfo (Shade
|
|||
};
|
||||
}
|
||||
|
||||
void Draw2D::Blit(Draw2DPipeline *pipeline, float srcX1, float srcY1, float srcX2, float srcY2, float dstX1, float dstY1, float dstX2, float dstY2, float srcWidth, float srcHeight, float dstWidth, float dstHeight, bool linear, int scaleFactor) {
|
||||
float dX = 1.0f / (float)dstWidth;
|
||||
float dY = 1.0f / (float)dstHeight;
|
||||
float sX = 1.0f / (float)srcWidth;
|
||||
float sY = 1.0f / (float)srcHeight;
|
||||
Draw2DVertex vtx[4] = {
|
||||
{ -1.0f + 2.0f * dX * dstX1, -(1.0f - 2.0f * dY * dstY1), sX * srcX1, sY * srcY1 },
|
||||
{ -1.0f + 2.0f * dX * dstX2, -(1.0f - 2.0f * dY * dstY1), sX * srcX2, sY * srcY1 },
|
||||
{ -1.0f + 2.0f * dX * dstX1, -(1.0f - 2.0f * dY * dstY2), sX * srcX1, sY * srcY2 },
|
||||
{ -1.0f + 2.0f * dX * dstX2, -(1.0f - 2.0f * dY * dstY2), sX * srcX2, sY * srcY2 },
|
||||
};
|
||||
|
||||
DrawStrip2D(nullptr, vtx, 4, linear, pipeline, srcWidth, srcHeight, scaleFactor);
|
||||
}
|
||||
|
||||
void Draw2D::DrawStrip2D(Draw::Texture *tex, Draw2DVertex *verts, int vertexCount, bool linearFilter, Draw2DPipeline *pipeline, float texW, float texH, int scaleFactor) {
|
||||
using namespace Draw;
|
||||
|
|
|
@ -58,6 +58,8 @@ public:
|
|||
Draw2DPipeline *Create2DPipeline(std::function<Draw2DPipelineInfo(ShaderWriter &)> generate);
|
||||
|
||||
void DrawStrip2D(Draw::Texture *tex, Draw2DVertex *verts, int vertexCount, bool linearFilter, Draw2DPipeline *pipeline, float texW = 0.0f, float texH = 0.0f, int scaleFactor = 0);
|
||||
|
||||
void Blit(Draw2DPipeline *pipeline, float srcX1, float srcY1, float srcX2, float srcY2, float dstX1, float dstY1, float dstX2, float dstY2, float srcWidth, float srcHeight, float dstWidth, float dstHeight, bool linear, int scaleFactor);
|
||||
void Ensure2DResources();
|
||||
|
||||
private:
|
||||
|
|
|
@ -113,12 +113,11 @@ void FramebufferManagerCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, G
|
|||
VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const {
|
||||
addr &= 0x3FFFFFFF;
|
||||
VirtualFramebuffer *match = nullptr;
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *v = vfbs_[i];
|
||||
if (v->fb_address == addr) {
|
||||
for (auto vfb : vfbs_) {
|
||||
if (vfb->fb_address == addr) {
|
||||
// Could check w too but whatever (actually, might very well make sense to do so, depending on context).
|
||||
if (!match || v->last_frame_render > match->last_frame_render) {
|
||||
match = v;
|
||||
if (!match || vfb->last_frame_render > match->last_frame_render) {
|
||||
match = vfb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,8 +181,8 @@ void FramebufferManagerCommon::EstimateDrawingSize(u32 fb_address, int fb_stride
|
|||
if (viewport_width != region_width) {
|
||||
// The majority of the time, these are equal. If not, let's check what we know.
|
||||
u32 nearest_address = 0xFFFFFFFF;
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
const u32 other_address = vfbs_[i]->fb_address & 0x3FFFFFFF;
|
||||
for (auto vfb : vfbs_) {
|
||||
const u32 other_address = vfb->fb_address & 0x3FFFFFFF;
|
||||
if (other_address > fb_address && other_address < nearest_address) {
|
||||
nearest_address = other_address;
|
||||
}
|
||||
|
@ -306,10 +305,9 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
|||
// As there are no clear "framebuffer width" and "framebuffer height" registers,
|
||||
// we need to infer the size of the current framebuffer somehow.
|
||||
int drawing_width, drawing_height;
|
||||
EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)16), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height);
|
||||
EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height);
|
||||
|
||||
gstate_c.SetCurRTOffset(0, 0);
|
||||
bool vfbStrideChanged = false;
|
||||
|
||||
if (params.fb_address == params.z_address) {
|
||||
// Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure)
|
||||
|
@ -319,24 +317,12 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
|||
|
||||
// Find a matching framebuffer
|
||||
VirtualFramebuffer *vfb = nullptr;
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *v = vfbs_[i];
|
||||
|
||||
for (auto v : vfbs_) {
|
||||
const u32 bpp = BufferFormatBytesPerPixel(v->fb_format);
|
||||
|
||||
if (params.fb_address == v->fb_address && params.fb_format == v->fb_format && params.fb_stride == v->fb_stride) {
|
||||
vfb = v;
|
||||
|
||||
// Update fb stride in case it changed.
|
||||
//
|
||||
// In reality, this is probably a new different framebuffer... Can't really share
|
||||
// data between framebuffers with different strides! (or well, we can, with complex
|
||||
// conversion shaders mapping back to and from memory addresses).
|
||||
if (vfb->fb_stride != params.fb_stride) {
|
||||
vfb->fb_stride = params.fb_stride;
|
||||
vfbStrideChanged = true;
|
||||
}
|
||||
|
||||
if (vfb->z_address == 0 && vfb->z_stride == 0 && params.z_stride != 0) {
|
||||
// Got one that was created by CreateRAMFramebuffer. Since it has no depth buffer,
|
||||
// we just recreate it immediately.
|
||||
|
@ -460,7 +446,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
|||
// TODO: Is it worth trying to upload the depth buffer (only if it wasn't copied above..?)
|
||||
}
|
||||
|
||||
// Let's check for depth buffer overlap. Might be interesting.
|
||||
// Let's check for depth buffer overlap. Might be interesting (not that interesting anymore..)
|
||||
bool sharingReported = false;
|
||||
for (size_t i = 0, end = vfbs_.size(); i < end; ++i) {
|
||||
if (vfbs_[i]->z_stride != 0 && params.fb_address == vfbs_[i]->z_address) {
|
||||
|
@ -510,7 +496,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
|||
vfb->dirtyAfterDisplay = true;
|
||||
if ((skipDrawReason & SKIPDRAW_SKIPFRAME) == 0)
|
||||
vfb->reallyDirtyAfterDisplay = true;
|
||||
NotifyRenderFramebufferUpdated(vfb, vfbStrideChanged);
|
||||
NotifyRenderFramebufferUpdated(vfb);
|
||||
}
|
||||
|
||||
vfb->colorBindSeq = GetBindSeqCount();
|
||||
|
@ -862,21 +848,10 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer
|
|||
|
||||
textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_CREATED);
|
||||
|
||||
// Ugly...
|
||||
if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) {
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
|
||||
}
|
||||
if (gstate_c.curRTRenderWidth != vfb->renderWidth || gstate_c.curRTRenderHeight != vfb->renderHeight) {
|
||||
gstate_c.Dirty(DIRTY_PROJMATRIX);
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX);
|
||||
}
|
||||
NotifyRenderFramebufferUpdated(vfb);
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbStrideChanged) {
|
||||
if (vfbStrideChanged) {
|
||||
textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_UPDATED);
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb) {
|
||||
// ugly...
|
||||
if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) {
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
|
||||
|
@ -922,14 +897,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
|
|||
}
|
||||
textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_UPDATED);
|
||||
|
||||
// ugly... is all this needed?
|
||||
if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) {
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
|
||||
}
|
||||
if (gstate_c.curRTRenderWidth != vfb->renderWidth || gstate_c.curRTRenderHeight != vfb->renderHeight) {
|
||||
gstate_c.Dirty(DIRTY_PROJMATRIX);
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX);
|
||||
}
|
||||
NotifyRenderFramebufferUpdated(vfb);
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt) {
|
||||
|
@ -1255,8 +1223,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
|||
// "framebuffers" sitting in RAM (created from block transfer or similar) so we only take off the kernel
|
||||
// and uncached bits of the address when comparing.
|
||||
const u32 addr = fbaddr & 0x3FFFFFFF;
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *v = vfbs_[i];
|
||||
for (auto v : vfbs_) {
|
||||
const u32 v_addr = v->fb_address & 0x3FFFFFFF;
|
||||
const u32 v_size = ColorBufferByteSize(v);
|
||||
if (addr >= v_addr && addr < v_addr + v_size) {
|
||||
|
@ -1531,14 +1498,15 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
|
|||
dst &= 0x3FFFFFFF;
|
||||
src &= 0x3FFFFFFF;
|
||||
|
||||
// TODO: Merge the below into FindTransferFramebuffer
|
||||
|
||||
VirtualFramebuffer *dstBuffer = 0;
|
||||
VirtualFramebuffer *srcBuffer = 0;
|
||||
u32 dstY = (u32)-1;
|
||||
u32 dstH = 0;
|
||||
u32 srcY = (u32)-1;
|
||||
u32 srcH = 0;
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = vfbs_[i];
|
||||
for (auto vfb : vfbs_) {
|
||||
if (vfb->fb_stride == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1640,119 +1608,68 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
|
|||
}
|
||||
}
|
||||
|
||||
// Can't be const, in case it has to create a vfb unfortunately.
|
||||
void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp) {
|
||||
u32 dstYOffset = -1;
|
||||
u32 dstXOffset = -1;
|
||||
u32 srcYOffset = -1;
|
||||
u32 srcXOffset = -1;
|
||||
int width = srcWidth;
|
||||
int height = srcHeight;
|
||||
void FramebufferManagerCommon::FindTransferFramebuffer(VirtualFramebuffer *&buffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp, bool destination) {
|
||||
u32 xOffset = -1;
|
||||
u32 yOffset = -1;
|
||||
int transferWidth = width;
|
||||
int transferHeight = height;
|
||||
|
||||
dstBasePtr &= 0x3FFFFFFF;
|
||||
srcBasePtr &= 0x3FFFFFFF;
|
||||
basePtr &= 0x3FFFFFFF;
|
||||
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = vfbs_[i];
|
||||
for (auto vfb : vfbs_) {
|
||||
const u32 vfb_address = vfb->fb_address & 0x3FFFFFFF;
|
||||
const u32 vfb_size = ColorBufferByteSize(vfb);
|
||||
const u32 vfb_bpp = BufferFormatBytesPerPixel(vfb->fb_format);
|
||||
const u32 vfb_byteStride = vfb->fb_stride * vfb_bpp;
|
||||
const u32 vfb_byteWidth = vfb->width * vfb_bpp;
|
||||
|
||||
// These heuristics are a bit annoying.
|
||||
// The goal is to avoid using GPU block transfers for things that ought to be memory.
|
||||
// Maybe we should even check for textures at these places instead?
|
||||
if (vfb_address <= basePtr && basePtr < vfb_address + vfb_size) {
|
||||
const u32 byteOffset = basePtr - vfb_address;
|
||||
const u32 byteStride = stride * bpp;
|
||||
const u32 memYOffset = byteOffset / byteStride;
|
||||
|
||||
if (vfb_address <= dstBasePtr && dstBasePtr < vfb_address + vfb_size) {
|
||||
const u32 byteOffset = dstBasePtr - vfb_address;
|
||||
const u32 byteStride = dstStride * bpp;
|
||||
const u32 yOffset = byteOffset / byteStride;
|
||||
|
||||
// Some games use mismatching bitdepths. But make sure the stride matches.
|
||||
// Some games use mismatching bitdepths. But make sure the stride matches.
|
||||
// If it doesn't, generally this means we detected the framebuffer with too large a height.
|
||||
// Use bufferHeight in case of buffers that resize up and down often per frame (Valkyrie Profile.)
|
||||
bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->bufferHeight - dstHeight;
|
||||
|
||||
// TODO: Surely this first comparison should be <= ?
|
||||
// Or does the exact match (byteOffset == 0) case get handled elsewhere?
|
||||
bool match = memYOffset < yOffset && (int)memYOffset <= (int)vfb->bufferHeight - height;
|
||||
if (match && vfb_byteStride != byteStride) {
|
||||
// Grand Knights History copies with a mismatching stride but a full line at a time.
|
||||
// That's why we multiply by height, not width - this copy is a rectangle with the wrong stride but a line with the correct one.
|
||||
// Makes it hard to detect the wrong transfers in e.g. God of War.
|
||||
if (width != dstStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) {
|
||||
// However, some other games write cluts to framebuffers.
|
||||
// Let's catch this and upload. Otherwise reject the match.
|
||||
match = (vfb->usageFlags & FB_USAGE_CLUT) != 0;
|
||||
if (match) {
|
||||
dstWidth = byteStride * height / vfb_bpp;
|
||||
dstHeight = 1;
|
||||
if (transferWidth != stride || (byteStride * transferHeight != vfb_byteStride && byteStride * transferHeight != vfb_byteWidth)) {
|
||||
if (destination) {
|
||||
// However, some other games write cluts to framebuffers.
|
||||
// Let's catch this and upload. Otherwise reject the match.
|
||||
match = (vfb->usageFlags & FB_USAGE_CLUT) != 0;
|
||||
if (match) {
|
||||
width = byteStride * transferHeight / vfb_bpp;
|
||||
height = 1;
|
||||
}
|
||||
} else {
|
||||
match = false;
|
||||
}
|
||||
} else {
|
||||
dstWidth = byteStride * height / vfb_bpp;
|
||||
dstHeight = 1;
|
||||
width = byteStride * transferHeight / vfb_bpp;
|
||||
height = 1;
|
||||
}
|
||||
} else if (match) {
|
||||
dstWidth = width;
|
||||
dstHeight = height;
|
||||
width = transferWidth;
|
||||
height = transferHeight;
|
||||
}
|
||||
if (match) {
|
||||
dstYOffset = yOffset;
|
||||
dstXOffset = dstStride == 0 ? 0 : (byteOffset / bpp) % dstStride;
|
||||
dstBuffer = vfb;
|
||||
}
|
||||
}
|
||||
if (vfb_address <= srcBasePtr && srcBasePtr < vfb_address + vfb_size) {
|
||||
const u32 byteOffset = srcBasePtr - vfb_address;
|
||||
const u32 byteStride = srcStride * bpp;
|
||||
const u32 yOffset = byteOffset / byteStride;
|
||||
bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->bufferHeight - srcHeight;
|
||||
if (match && vfb_byteStride != byteStride) {
|
||||
if (width != srcStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) {
|
||||
match = false;
|
||||
} else {
|
||||
srcWidth = byteStride * height / vfb_bpp;
|
||||
srcHeight = 1;
|
||||
}
|
||||
} else if (match) {
|
||||
srcWidth = width;
|
||||
srcHeight = height;
|
||||
}
|
||||
if (match) {
|
||||
srcYOffset = yOffset;
|
||||
srcXOffset = srcStride == 0 ? 0 : (byteOffset / bpp) % srcStride;
|
||||
srcBuffer = vfb;
|
||||
xOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride;
|
||||
yOffset = memYOffset;
|
||||
buffer = vfb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (srcBuffer && !dstBuffer) {
|
||||
if (PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB ||
|
||||
(PSP_CoreParameter().compat.flags().IntraVRAMBlockTransferAllowCreateFB &&
|
||||
Memory::IsVRAMAddress(srcBuffer->fb_address) && Memory::IsVRAMAddress(dstBasePtr))) {
|
||||
GEBufferFormat ramFormat;
|
||||
// Try to guess the appropriate format. We only know the bpp from the block transfer command (16 or 32 bit).
|
||||
if (bpp == 4) {
|
||||
// Only one possibility unless it's doing split pixel tricks (which we could detect through stride maybe).
|
||||
ramFormat = GE_FORMAT_8888;
|
||||
} else if (srcBuffer->fb_format != GE_FORMAT_8888) {
|
||||
// We guess that the game will interpret the data the same as it was in the source of the copy.
|
||||
// Seems like a likely good guess, and works in Test Drive Unlimited.
|
||||
ramFormat = srcBuffer->fb_format;
|
||||
} else {
|
||||
// No info left - just fall back to something. But this is definitely split pixel tricks.
|
||||
ramFormat = GE_FORMAT_5551;
|
||||
}
|
||||
dstBuffer = CreateRAMFramebuffer(dstBasePtr, dstWidth, dstHeight, dstStride, ramFormat);
|
||||
}
|
||||
}
|
||||
|
||||
if (dstBuffer)
|
||||
dstBuffer->last_frame_used = gpuStats.numFlips;
|
||||
|
||||
if (dstYOffset != (u32)-1) {
|
||||
dstY += dstYOffset;
|
||||
dstX += dstXOffset;
|
||||
}
|
||||
if (srcYOffset != (u32)-1) {
|
||||
srcY += srcYOffset;
|
||||
srcX += srcXOffset;
|
||||
if (yOffset != (u32)-1) {
|
||||
x += xOffset;
|
||||
y += yOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1956,8 +1873,33 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
|
|||
int dstWidth = width;
|
||||
int dstHeight = height;
|
||||
|
||||
// This looks at the compat flags BlockTransferAllowCreateFB*.
|
||||
FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, dstWidth, dstHeight, bpp);
|
||||
// These modify the X/Y/W/H parameters depending on the memory offset of the base pointers from the actual buffers.
|
||||
FindTransferFramebuffer(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp, false);
|
||||
FindTransferFramebuffer(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp, true);
|
||||
|
||||
if (srcBuffer && !dstBuffer) {
|
||||
if (PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB ||
|
||||
(PSP_CoreParameter().compat.flags().IntraVRAMBlockTransferAllowCreateFB &&
|
||||
Memory::IsVRAMAddress(srcBuffer->fb_address) && Memory::IsVRAMAddress(dstBasePtr))) {
|
||||
GEBufferFormat ramFormat;
|
||||
// Try to guess the appropriate format. We only know the bpp from the block transfer command (16 or 32 bit).
|
||||
if (bpp == 4) {
|
||||
// Only one possibility unless it's doing split pixel tricks (which we could detect through stride maybe).
|
||||
ramFormat = GE_FORMAT_8888;
|
||||
} else if (srcBuffer->fb_format != GE_FORMAT_8888) {
|
||||
// We guess that the game will interpret the data the same as it was in the source of the copy.
|
||||
// Seems like a likely good guess, and works in Test Drive Unlimited.
|
||||
ramFormat = srcBuffer->fb_format;
|
||||
} else {
|
||||
// No info left - just fall back to something. But this is definitely split pixel tricks.
|
||||
ramFormat = GE_FORMAT_5551;
|
||||
}
|
||||
dstBuffer = CreateRAMFramebuffer(dstBasePtr, dstWidth, dstHeight, dstStride, ramFormat);
|
||||
}
|
||||
}
|
||||
|
||||
if (dstBuffer)
|
||||
dstBuffer->last_frame_used = gpuStats.numFlips;
|
||||
|
||||
if (dstBuffer && srcBuffer) {
|
||||
if (srcBuffer == dstBuffer) {
|
||||
|
@ -2032,13 +1974,15 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS
|
|||
}
|
||||
|
||||
if (MayIntersectFramebuffer(srcBasePtr) || MayIntersectFramebuffer(dstBasePtr)) {
|
||||
// TODO: Figure out how we can avoid repeating the search here.
|
||||
VirtualFramebuffer *dstBuffer = 0;
|
||||
VirtualFramebuffer *srcBuffer = 0;
|
||||
int srcWidth = width;
|
||||
int srcHeight = height;
|
||||
int dstWidth = width;
|
||||
int dstHeight = height;
|
||||
FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, dstWidth, dstHeight, bpp);
|
||||
FindTransferFramebuffer(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp, false);
|
||||
FindTransferFramebuffer(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp, true);
|
||||
|
||||
// A few games use this INSTEAD of actually drawing the video image to the screen, they just blast it to
|
||||
// the backbuffer. Detect this and have the framebuffermanager draw the pixels.
|
||||
|
@ -2499,9 +2443,7 @@ void FramebufferManagerCommon::RebindFramebuffer(const char *tag) {
|
|||
std::vector<FramebufferInfo> FramebufferManagerCommon::GetFramebufferList() const {
|
||||
std::vector<FramebufferInfo> list;
|
||||
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = vfbs_[i];
|
||||
|
||||
for (auto vfb : vfbs_) {
|
||||
FramebufferInfo info;
|
||||
info.fb_address = vfb->fb_address;
|
||||
info.z_address = vfb->z_address;
|
||||
|
@ -2724,17 +2666,6 @@ void FramebufferManagerCommon::BlitUsingRaster(
|
|||
draw_->GetFramebufferDimensions(src, &srcW, &srcH);
|
||||
draw_->GetFramebufferDimensions(dest, &destW, &destH);
|
||||
|
||||
float dX = 1.0f / (float)destW;
|
||||
float dY = 1.0f / (float)destH;
|
||||
float sX = 1.0f / (float)srcW;
|
||||
float sY = 1.0f / (float)srcH;
|
||||
Draw2DVertex vtx[4] = {
|
||||
{ -1.0f + 2.0f * dX * destX1, -(1.0f - 2.0f * dY * destY1), sX * srcX1, sY * srcY1 },
|
||||
{ -1.0f + 2.0f * dX * destX2, -(1.0f - 2.0f * dY * destY1), sX * srcX2, sY * srcY1 },
|
||||
{ -1.0f + 2.0f * dX * destX1, -(1.0f - 2.0f * dY * destY2), sX * srcX1, sY * srcY2 },
|
||||
{ -1.0f + 2.0f * dX * destX2, -(1.0f - 2.0f * dY * destY2), sX * srcX2, sY * srcY2 },
|
||||
};
|
||||
|
||||
// Unbind the texture first to avoid the D3D11 hazard check (can't set render target to things bound as textures and vice versa, not even temporarily).
|
||||
draw_->BindTexture(0, nullptr);
|
||||
// This will get optimized away in case it's already bound (in VK and GL at least..)
|
||||
|
@ -2745,7 +2676,7 @@ void FramebufferManagerCommon::BlitUsingRaster(
|
|||
draw_->SetViewports(1, &vp);
|
||||
draw_->SetScissorRect(0, 0, (int)dest->Width(), (int)dest->Height());
|
||||
|
||||
draw2D_.DrawStrip2D(nullptr, vtx, 4, linearFilter, pipeline, src->Width(), src->Height(), renderScaleFactor_);
|
||||
draw2D_.Blit(pipeline, srcX1, srcY1, srcX2, srcY2, destX1, destY1, destX2, destY2, (float)srcW, (float)srcH, (float)destW, (float)destH, linearFilter , renderScaleFactor_);
|
||||
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
|
||||
}
|
||||
|
|
|
@ -275,10 +275,15 @@ public:
|
|||
void UpdateFromMemory(u32 addr, int size, bool safe);
|
||||
void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor);
|
||||
bool PerformStencilUpload(u32 addr, int size, StencilUpload flags);
|
||||
|
||||
// Returns true if it's sure this is a direct FBO->FBO transfer and it has already handle it.
|
||||
// In that case we hardly need to actually copy the bytes in VRAM, they will be wrong anyway (unless
|
||||
// read framebuffers is on, in which case this should always return false).
|
||||
// If this returns false, a memory copy will happen and NotifyBlockTransferAfter will be called.
|
||||
bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
|
||||
|
||||
// This gets called after the memory copy, in case NotifyBlockTransferBefore returned false.
|
||||
// Otherwise it doesn't get called.
|
||||
void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
|
||||
|
||||
bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags);
|
||||
|
@ -402,7 +407,7 @@ protected:
|
|||
u32 ColorBufferByteSize(const VirtualFramebuffer *vfb) const;
|
||||
|
||||
void NotifyRenderFramebufferCreated(VirtualFramebuffer *vfb);
|
||||
void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbFormatChanged);
|
||||
void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb);
|
||||
void NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth);
|
||||
|
||||
void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst);
|
||||
|
@ -412,7 +417,9 @@ protected:
|
|||
|
||||
bool ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const;
|
||||
void DownloadFramebufferOnSwitch(VirtualFramebuffer *vfb);
|
||||
void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp);
|
||||
|
||||
void FindTransferFramebuffer(VirtualFramebuffer *&srcBuffer, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int bpp, bool destination);
|
||||
|
||||
VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb);
|
||||
virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {}
|
||||
|
||||
|
|
|
@ -1967,9 +1967,26 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
|
|||
draw_->BindSamplerStates(0, 1, &nearest);
|
||||
draw_->BindSamplerStates(1, 1, &clutSampler);
|
||||
|
||||
textureShaderCache_->ApplyShader(textureShader,
|
||||
framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight,
|
||||
gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset);
|
||||
// If min is not < max, then we don't have values (wasn't set during decode.)
|
||||
const KnownVertexBounds &bounds = gstate_c.vertBounds;
|
||||
float u1 = 0.0f;
|
||||
float v1 = 0.0f;
|
||||
float u2 = framebuffer->renderWidth;
|
||||
float v2 = framebuffer->renderHeight;
|
||||
if (bounds.minV < bounds.maxV) {
|
||||
u1 = bounds.minU + gstate_c.curTextureXOffset;
|
||||
v1 = bounds.minV + gstate_c.curTextureYOffset;
|
||||
u2 = bounds.maxU + gstate_c.curTextureXOffset;
|
||||
v2 = bounds.maxV + gstate_c.curTextureYOffset;
|
||||
// We need to reapply the texture next time since we cropped UV.
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
}
|
||||
u1 *= framebuffer->renderScaleFactor;
|
||||
v1 *= framebuffer->renderScaleFactor;
|
||||
u2 *= framebuffer->renderScaleFactor;
|
||||
v2 *= framebuffer->renderScaleFactor;
|
||||
|
||||
draw2D_->Blit(textureShader, u1, v1, u2, v2, u1, v1, u2, v2, framebuffer->renderWidth, framebuffer->renderHeight, framebuffer->renderWidth, framebuffer->renderHeight, false, framebuffer->renderScaleFactor);
|
||||
|
||||
draw_->BindTexture(0, nullptr);
|
||||
framebufferManager_->RebindFramebuffer("ApplyTextureFramebuffer");
|
||||
|
|
|
@ -247,51 +247,3 @@ std::string TextureShaderCache::DebugGetShaderString(std::string idstr, DebugSha
|
|||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void TextureShaderCache::ApplyShader(Draw2DPipeline *pipeline, float bufferW, float bufferH, int renderW, int renderH, const KnownVertexBounds &bounds, u32 uoff, u32 voff) {
|
||||
Draw2DVertex verts[4] = {
|
||||
{-1, -1, 0, 0 },
|
||||
{ 1, -1, 1, 0 },
|
||||
{-1, 1, 0, 1 },
|
||||
{ 1, 1, 1, 1 },
|
||||
};
|
||||
|
||||
// If min is not < max, then we don't have values (wasn't set during decode.)
|
||||
if (bounds.minV < bounds.maxV) {
|
||||
const float invWidth = 1.0f / bufferW;
|
||||
const float invHeight = 1.0f / bufferH;
|
||||
// Inverse of half = double.
|
||||
const float invHalfWidth = invWidth * 2.0f;
|
||||
const float invHalfHeight = invHeight * 2.0f;
|
||||
|
||||
const int u1 = bounds.minU + uoff;
|
||||
const int v1 = bounds.minV + voff;
|
||||
const int u2 = bounds.maxU + uoff;
|
||||
const int v2 = bounds.maxV + voff;
|
||||
|
||||
const float left = u1 * invHalfWidth - 1.0f;
|
||||
const float right = u2 * invHalfWidth - 1.0f;
|
||||
const float top = v1 * invHalfHeight - 1.0f;
|
||||
const float bottom = v2 * invHalfHeight - 1.0f;
|
||||
|
||||
const float uvleft = u1 * invWidth;
|
||||
const float uvright = u2 * invWidth;
|
||||
const float uvtop = v1 * invHeight;
|
||||
const float uvbottom = v2 * invHeight;
|
||||
|
||||
// Points are: BL, BR, TR, TL.
|
||||
verts[0] = Draw2DVertex{ left, bottom, uvleft, uvbottom };
|
||||
verts[1] = Draw2DVertex{ right, bottom, uvright, uvbottom };
|
||||
verts[2] = Draw2DVertex{ left, top, uvleft, uvtop };
|
||||
verts[3] = Draw2DVertex{ right, top, uvright, uvtop };
|
||||
|
||||
// We need to reapply the texture next time since we cropped UV.
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
}
|
||||
|
||||
Draw::Viewport vp{ 0.0f, 0.0f, (float)renderW, (float)renderH, 0.0f, 1.0f };
|
||||
draw_->BindPipeline(pipeline->pipeline);
|
||||
draw_->SetViewports(1, &vp);
|
||||
draw_->SetScissorRect(0, 0, renderW, renderH);
|
||||
draw_->DrawUP((const uint8_t *)verts, 4);
|
||||
}
|
||||
|
|
|
@ -48,8 +48,6 @@ public:
|
|||
|
||||
Draw::SamplerState *GetSampler(bool linearFilter);
|
||||
|
||||
void ApplyShader(Draw2DPipeline *pipeline, float bufferW, float bufferH, int renderW, int renderH, const KnownVertexBounds &bounds, u32 uoff, u32 voff);
|
||||
|
||||
void Clear();
|
||||
void Decimate();
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
|
||||
|
|
|
@ -2773,15 +2773,6 @@ void GPUCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat f
|
|||
}
|
||||
|
||||
void GPUCommon::DoBlockTransfer(u32 skipDrawReason) {
|
||||
// TODO: This is used a lot to copy data around between render targets and textures,
|
||||
// and also to quickly load textures from RAM to VRAM. So we should do checks like the following:
|
||||
// * Does dstBasePtr point to an existing texture? If so maybe reload it immediately.
|
||||
//
|
||||
// * Does srcBasePtr point to a render target, and dstBasePtr to a texture? If so
|
||||
// either copy between rt and texture or reassign the texture to point to the render target
|
||||
//
|
||||
// etc....
|
||||
|
||||
u32 srcBasePtr = gstate.getTransferSrcAddress();
|
||||
u32 srcStride = gstate.getTransferSrcStride();
|
||||
|
||||
|
|
|
@ -594,7 +594,7 @@ void GameSettingsScreen::CreateViews() {
|
|||
static const char *bufFilters[] = { "Linear", "Nearest", };
|
||||
graphicsSettings->Add(new PopupMultiChoice(&g_Config.iBufFilter, gr->T("Screen Scaling Filter"), bufFilters, 1, ARRAY_SIZE(bufFilters), gr->GetName(), screenManager()));
|
||||
|
||||
#ifdef PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
|
||||
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
|
||||
bool showCardboardSettings = deviceType != DEVICE_TYPE_VR;
|
||||
#else
|
||||
// If you enabled it through the ini, you can see this. Useful for testing.
|
||||
|
|
|
@ -479,6 +479,7 @@
|
|||
<ClInclude Include="..\..\Common\MemArena.h" />
|
||||
<ClInclude Include="..\..\Common\MemoryUtil.h" />
|
||||
<ClInclude Include="..\..\Common\MipsEmitter.h" />
|
||||
<ClInclude Include="..\..\Common\RiscVEmitter.h" />
|
||||
<ClInclude Include="..\..\Common\OSVersion.h" />
|
||||
<ClInclude Include="..\..\Common\StringUtils.h" />
|
||||
<ClInclude Include="..\..\Common\Swap.h" />
|
||||
|
@ -594,6 +595,7 @@
|
|||
<ClCompile Include="..\..\Common\MemoryUtil.cpp" />
|
||||
<ClCompile Include="..\..\Common\MipsCPUDetect.cpp" />
|
||||
<ClCompile Include="..\..\Common\MipsEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\RiscVEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\SysError.cpp" />
|
||||
<ClCompile Include="..\..\Common\OSVersion.cpp" />
|
||||
<ClCompile Include="..\..\Common\StringUtils.cpp" />
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
<ClCompile Include="..\..\Common\MemoryUtil.cpp" />
|
||||
<ClCompile Include="..\..\Common\MipsCPUDetect.cpp" />
|
||||
<ClCompile Include="..\..\Common\MipsEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\RiscVEmitter.cpp" />
|
||||
<ClCompile Include="..\..\Common\SysError.cpp" />
|
||||
<ClCompile Include="..\..\Common\OSVersion.cpp" />
|
||||
<ClCompile Include="..\..\Common\StringUtils.cpp" />
|
||||
|
@ -404,6 +405,7 @@
|
|||
<ClInclude Include="..\..\Common\MemArena.h" />
|
||||
<ClInclude Include="..\..\Common\MemoryUtil.h" />
|
||||
<ClInclude Include="..\..\Common\MipsEmitter.h" />
|
||||
<ClInclude Include="..\..\Common\RiscVEmitter.h" />
|
||||
<ClInclude Include="..\..\Common\OSVersion.h" />
|
||||
<ClInclude Include="..\..\Common\StringUtils.h" />
|
||||
<ClInclude Include="..\..\Common\Swap.h" />
|
||||
|
|
Loading…
Add table
Reference in a new issue