Files
ppsspp/GPU/Null/GPU_Null.cpp
T

306 lines
11 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cstring>
#include "Common/DbgNew.h"
#include "Common/Profiler/Profiler.h"
#include "Core/System.h"
#include "Core/Util/PPGeDraw.h"
#include "GPU/GPUCommon.h"
#include "GPU/GPUState.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Null/GPU_Null.h"
namespace {
// Only here for the vertex decoders (vertex sizes, bounding box tests). Queues and draws nothing.
class NullDrawEngine : public DrawEngineCommon {
public:
void DeviceLost() override {}
void DeviceRestore(Draw::DrawContext *draw) override {}
void Flush() override {}
void DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode, bool continuation) override {}
};
class GPU_Null : public GPUCommon {
public:
explicit GPU_Null(GraphicsContext *gfxCtx);
u32 CheckGPUFeatures() const override { return 0; }
void UpdateCmdInfo() override {}
void ExecuteOp(u32 op, u32 diff) override;
void GetStats(StringWriter &w) override {}
std::vector<const VirtualFramebuffer *> GetFramebufferList() const override { return {}; }
void DeviceLost() override { draw_ = nullptr; }
void DeviceRestore(Draw::DrawContext *draw) override {
draw_ = draw;
PPGeSetDrawContext(draw_);
}
void CheckDisplayResized() override { displayResized_ = false; }
void CheckConfigChanged(const DisplayLayoutConfig &config) override;
bool FramebufferDirty() override { return true; }
bool FramebufferReallyDirty() override { return true; }
void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override {
NotifyDisplay(framebuf, stride, format);
}
void SetCurFramebufferDirty(bool dirty) override {}
void PrepareCopyDisplayToOutput(const DisplayLayoutConfig &config) override {}
void CopyDisplayToOutput(const DisplayLayoutConfig &config) override {}
bool PresentedThisFrame() const override { return false; }
// Nothing is cached, and all of PSP memory is up to date, so the copies and sets are left to the caller.
void InvalidateCache(u32 addr, int size, GPUInvalidationType type) override {}
bool PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags = GPUCopyFlag::NONE) override;
bool PerformMemorySet(u32 dest, u8 v, int size) override;
void PerformWriteFormattedFromMemory(u32 addr, int size, int width, GEBufferFormat format, int rectWidth) override {
// Remembered for EstimateFillCycles.
NoteVideoRange(addr, (u32)size);
}
bool PerformWriteStencilFromMemory(u32 dest, int size, WriteStencil flags) override { return false; }
bool PerformReadbackStencilToMemory(u32 dest, int size) override { return false; }
void Execute_Prim(u32 op, u32 diff);
void Execute_Bezier(u32 op, u32 diff);
void Execute_Spline(u32 op, u32 diff);
void Execute_BlockTransferStart(u32 op, u32 diff);
typedef void (GPU_Null::*CmdFunc)(u32 op, u32 diff);
protected:
void FastRunLoop(DisplayList &list) override;
void BuildReportingInfo() override {
reportingPrimaryInfo_ = "Null";
reportingFullInfo_ = "Null";
}
private:
void SkipCurve(int numPointsU, int numPointsV);
NullDrawEngine drawEngine_;
};
struct NullCommandInfo {
u8 flags;
GPU_Null::CmdFunc func;
};
static NullCommandInfo nullCmdInfo[256];
struct NullCommandTableEntry {
u8 cmd;
u8 flags;
GPU_Null::CmdFunc func;
};
// Only the commands that do something beyond setting their register. The rest only matter for drawing.
const NullCommandTableEntry nullCommandTable[] = {
{ GE_CMD_OFFSETADDR, FLAG_EXECUTE, &GPUCommon::Execute_OffsetAddr },
{ GE_CMD_ORIGIN, FLAG_EXECUTE | FLAG_READS_PC, &GPUCommon::Execute_Origin },
{ GE_CMD_JUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_Jump },
{ GE_CMD_CALL, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_Call },
{ GE_CMD_RET, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_Ret },
{ GE_CMD_END, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_End },
{ GE_CMD_VADDR, FLAG_EXECUTE, &GPUCommon::Execute_Vaddr },
{ GE_CMD_IADDR, FLAG_EXECUTE, &GPUCommon::Execute_Iaddr },
{ GE_CMD_BJUMP, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_BJump },
{ GE_CMD_BOUNDINGBOX, FLAG_EXECUTE, &GPUCommon::Execute_BoundingBox },
{ GE_CMD_PRIM, FLAG_EXECUTE, &GPU_Null::Execute_Prim },
{ GE_CMD_BEZIER, FLAG_EXECUTE, &GPU_Null::Execute_Bezier },
{ GE_CMD_SPLINE, FLAG_EXECUTE, &GPU_Null::Execute_Spline },
{ GE_CMD_MORPHWEIGHT0, FLAG_EXECUTEONCHANGE, &GPUCommon::Execute_MorphWeight },
{ GE_CMD_MORPHWEIGHT1, FLAG_EXECUTEONCHANGE, &GPUCommon::Execute_MorphWeight },
{ GE_CMD_MORPHWEIGHT2, FLAG_EXECUTEONCHANGE, &GPUCommon::Execute_MorphWeight },
{ GE_CMD_MORPHWEIGHT3, FLAG_EXECUTEONCHANGE, &GPUCommon::Execute_MorphWeight },
{ GE_CMD_MORPHWEIGHT4, FLAG_EXECUTEONCHANGE, &GPUCommon::Execute_MorphWeight },
{ GE_CMD_MORPHWEIGHT5, FLAG_EXECUTEONCHANGE, &GPUCommon::Execute_MorphWeight },
{ GE_CMD_MORPHWEIGHT6, FLAG_EXECUTEONCHANGE, &GPUCommon::Execute_MorphWeight },
{ GE_CMD_MORPHWEIGHT7, FLAG_EXECUTEONCHANGE, &GPUCommon::Execute_MorphWeight },
{ GE_CMD_TRANSFERSTART, FLAG_EXECUTE | FLAG_READS_PC, &GPU_Null::Execute_BlockTransferStart },
{ GE_CMD_WORLDMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_WorldMtxNum },
{ GE_CMD_WORLDMATRIXDATA, FLAG_EXECUTE, &GPUCommon::Execute_WorldMtxData },
{ GE_CMD_VIEWMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_ViewMtxNum },
{ GE_CMD_VIEWMATRIXDATA, FLAG_EXECUTE, &GPUCommon::Execute_ViewMtxData },
{ GE_CMD_PROJMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_ProjMtxNum },
{ GE_CMD_PROJMATRIXDATA, FLAG_EXECUTE, &GPUCommon::Execute_ProjMtxData },
{ GE_CMD_TGENMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_TgenMtxNum },
{ GE_CMD_TGENMATRIXDATA, FLAG_EXECUTE, &GPUCommon::Execute_TgenMtxData },
{ GE_CMD_BONEMATRIXNUMBER, FLAG_EXECUTE | FLAG_READS_PC | FLAG_WRITES_PC, &GPUCommon::Execute_BoneMtxNum },
{ GE_CMD_BONEMATRIXDATA, FLAG_EXECUTE, &GPUCommon::Execute_BoneMtxData },
{ GE_CMD_VAP, FLAG_EXECUTE, &GPUCommon::Execute_ImmVertexAlphaPrim },
};
GPU_Null::GPU_Null(GraphicsContext *gfxCtx) : GPUCommon(gfxCtx, nullptr) {
memset(nullCmdInfo, 0, sizeof(nullCmdInfo));
for (const NullCommandTableEntry &entry : nullCommandTable) {
_dbg_assert_(nullCmdInfo[entry.cmd].func == nullptr);
nullCmdInfo[entry.cmd].flags = entry.flags;
nullCmdInfo[entry.cmd].func = entry.func;
}
drawEngine_.SetGPUCommon(this);
drawEngine_.Init();
drawEngineCommon_ = &drawEngine_;
// Nothing is queued, so there's nothing to flush.
flushOnParams_ = false;
BuildReportingInfo();
}
void GPU_Null::CheckConfigChanged(const DisplayLayoutConfig &config) {
if (configChanged_) {
drawEngine_.NotifyConfigChanged();
configChanged_ = false;
}
}
void GPU_Null::FastRunLoop(DisplayList &list) {
PROFILE_THIS_SCOPE("null_runloop");
if (!Memory::IsValidAddress(list.pc)) {
downcount = 0;
return;
}
const NullCommandInfo *cmdInfo = nullCmdInfo;
int dc = downcount;
for (; dc > 0; --dc) {
const u32 op = Memory::ReadUnchecked_U32(list.pc);
const u32 cmd = op >> 24;
const NullCommandInfo &info = cmdInfo[cmd];
const u32 diff = op ^ gstate.cmdmem[cmd];
if (diff) {
gstate.cmdmem[cmd] = op;
}
if ((info.flags & FLAG_EXECUTE) || (diff && (info.flags & FLAG_EXECUTEONCHANGE))) {
downcount = dc;
(this->*info.func)(op, diff);
dc = downcount;
}
list.pc += 4;
}
downcount = 0;
}
void GPU_Null::ExecuteOp(u32 op, u32 diff) {
const NullCommandInfo &info = nullCmdInfo[op >> 24];
if ((info.flags & FLAG_EXECUTE) || (diff && (info.flags & FLAG_EXECUTEONCHANGE))) {
(this->*info.func)(op, diff);
}
}
void GPU_Null::Execute_Prim(u32 op, u32 diff) {
const u32 count = op & 0xFFFF;
if (count == 0)
return;
FlushImm();
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
ERROR_LOG(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
return;
}
const void *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
const void *inds = nullptr;
const u32 vertType = gstate.vertType;
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
const int indexShift = ((vertType & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT) - 1;
if (!Memory::IsValidRange(gstate_c.indexAddr, count << indexShift)) {
ERROR_LOG(Log::G3D, "Bad index address %08x (%d)!", gstate_c.indexAddr, count);
return;
}
inds = Memory::GetPointerUnchecked(gstate_c.indexAddr);
}
const VertexDecoder *dec = drawEngine_.GetVertexDecoder(GetVertTypeID(vertType, gstate.getUVGenMode()));
const GEPrimitiveType prim = static_cast<GEPrimitiveType>((op >> 16) & 7);
if (gstate.isModeThrough()) {
cyclesExecuted += EstimateFillCycles(prim, verts, inds, count, dec, vertType);
}
const int cycles = EstimatePerVertexCost() * count;
gpuStats.perFrame.vertexGPUCycles += cycles;
if (!PSP_CoreParameter().compat.flags().FastEmulatedGPU) {
cyclesExecuted += cycles;
}
gstate_c.AdvanceVerts(vertType, count, count * dec->VertexSize());
}
// Moves the pointers past a curve like DrawEngineCommon::SubmitCurve would.
void GPU_Null::SkipCurve(int numPointsU, int numPointsV) {
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
ERROR_LOG(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
return;
}
const u32 vertType = gstate.vertType;
if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE && !Memory::IsValidAddress(gstate_c.indexAddr)) {
ERROR_LOG(Log::G3D, "Bad index address %08x!", gstate_c.indexAddr);
return;
}
const int count = numPointsU * numPointsV;
int bytesRead = 0;
// SubmitCurve draws nothing, and reads nothing, with fewer than four points either way.
if (numPointsU >= 4 && numPointsV >= 4) {
bytesRead = count * drawEngine_.GetVertexDecoder(GetVertTypeID(vertType, gstate.getUVGenMode()))->VertexSize();
}
gstate_c.AdvanceVerts(vertType, count, bytesRead);
}
void GPU_Null::Execute_Bezier(u32 op, u32 diff) {
SkipCurve(op & 0xFF, (op >> 8) & 0xFF);
}
void GPU_Null::Execute_Spline(u32 op, u32 diff) {
SkipCurve(op & 0xFF, (op >> 8) & 0xFF);
}
void GPU_Null::Execute_BlockTransferStart(u32 op, u32 diff) {
DoBlockTransfer(gstate_c.skipDrawReason);
}
bool GPU_Null::PerformMemoryCopy(u32 dest, u32 src, int size, GPUCopyFlag flags) {
if (!(flags & GPUCopyFlag::DEBUG_NOTIFIED))
recorder_.NotifyMemcpy(dest, src, size);
return false;
}
bool GPU_Null::PerformMemorySet(u32 dest, u8 v, int size) {
recorder_.NotifyMemset(dest, v, size);
return false;
}
} // namespace
GPUCommon *CreateNullGPU(GraphicsContext *gfxCtx) {
#ifdef USE_CRT_DBG
#undef new
#endif
return new GPU_Null(gfxCtx);
#ifdef USE_CRT_DBG
#define new DBG_NEW
#endif
}