Merge branch 'hrydgard:master' into feature_openxr_stereo

This commit is contained in:
Luboš Vonásek 2022-08-27 12:23:38 +02:00 committed by GitHub
commit 5e434bccdb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1673 additions and 233 deletions

View file

@ -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
)

View file

@ -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" />

View file

@ -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

File diff suppressed because it is too large Load diff

361
Common/RiscVEmitter.h Normal file
View 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;
};
};

View file

@ -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[] = {

View file

@ -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;
}

View file

@ -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;

View file

@ -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:

View file

@ -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);
}

View file

@ -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) {}

View file

@ -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");

View file

@ -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);
}

View file

@ -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);

View file

@ -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();

View file

@ -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.

View file

@ -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" />

View file

@ -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" />