Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct some of immediate vertex handling #15978

Merged
merged 8 commits into from
Sep 7, 2022
2 changes: 1 addition & 1 deletion GPU/Common/FramebufferManagerCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,7 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
}
}

if (dst != currentRenderVfb_ && tookActions) {
if (currentRenderVfb_ && dst != currentRenderVfb_ && tookActions) {
// Will probably just change the name of the current renderpass, since one was started by the reinterpret itself.
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After Reinterpret");
}
Expand Down
3 changes: 3 additions & 0 deletions GPU/Common/VertexDecoderCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "GPU/ge_constants.h"
#include "GPU/Common/ShaderCommon.h"
#include "GPU/GPUCommon.h"
#include "GPU/GPUState.h"

#if PPSSPP_ARCH(ARM)
#include "Common/ArmEmitter.h"
Expand Down Expand Up @@ -299,6 +300,8 @@ class VertexReader {
bool hasNormal() const { return decFmt_.nrmfmt != 0; }
bool hasUV() const { return decFmt_.uvfmt != 0; }
bool isThrough() const { return (vtype_ & GE_VTYPE_THROUGH) != 0; }
bool skinningEnabled() const { return vertTypeIsSkinningEnabled(vtype_); }
int numBoneWeights() const { return vertTypeGetNumBoneWeights(vtype_); }
void Goto(int index) {
data_ = base_ + index * decFmt_.stride;
}
Expand Down
3 changes: 2 additions & 1 deletion GPU/Debugger/Debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ void SetBreakNext(BreakNext next) {
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_PRIM, true);
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true);
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true);
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_VAP, true);
} else if (next == BreakNext::CURVE) {
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true);
GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true);
Expand Down Expand Up @@ -111,7 +112,7 @@ bool NotifyCommand(u32 pc) {
}

bool process = true;
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE) {
if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE || cmd == GE_CMD_VAP) {
primsThisFrame++;

if (!restrictPrimRanges.empty()) {
Expand Down
2 changes: 0 additions & 2 deletions GPU/Debugger/GECommandTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,7 @@ static constexpr GECmdInfo geCmdInfo[] = {
{ GE_CMD_VTCT, "immt", GECmdFormat::FLOAT },
{ GE_CMD_VTCQ, "immq", GECmdFormat::FLOAT },
{ GE_CMD_VCV, "immrgb", GECmdFormat::RGB },
// TODO: Confirm if any other bits are used?
{ GE_CMD_VAP, "imma_prim", GECmdFormat::ALPHA_PRIM },
// TODO: Confirm it's 8 bit?
{ GE_CMD_VFC, "immfog", GECmdFormat::DATA8 },
{ GE_CMD_VSCV, "immrgb1", GECmdFormat::RGB },
{ GE_CMD_UNKNOWN_FA, "unknownfa", GECmdFormat::NONE },
Expand Down
2 changes: 1 addition & 1 deletion GPU/Debugger/GECommandTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ enum class GECmdFormat {
BLEND_MODE, // 4 bits srcfactor, 4 bits dstfactor, 3 bits equation.
DITHER_ROW, // 4 s.3.0 fixed point dither offsets.
LOGIC_OP, // 4 bits logic operation.
ALPHA_PRIM, // 8 bits alpha, 3 bits primitive type.
ALPHA_PRIM, // 8 bits alpha, 3 bits primitive type, 1 bit antialias, 6 bit clip?, 1 bit shading, 1 bit cullenable, 1 bit cullface, 1 bit tex enable, 1 bit fog, 1 bit dither.
};

struct GECmdInfo {
Expand Down
98 changes: 83 additions & 15 deletions GPU/GPUCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,7 @@ void GPUCommon::Execute_End(u32 op, u32 diff) {
break;

default:
FlushImm();
currentList->subIntrToken = prev & 0xFFFF;
UpdateState(GPUSTATE_DONE);
// Since we marked done, we have to restore the context now before the next list runs.
Expand Down Expand Up @@ -1651,6 +1652,7 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) {
u32 count = data & 0xFFFF;
if (count == 0)
return;
FlushImm();

// Upper bits are ignored.
GEPrimitiveType prim = static_cast<GEPrimitiveType>((data >> 16) & 7);
Expand Down Expand Up @@ -2379,40 +2381,60 @@ void GPUCommon::Execute_ImmVertexAlphaPrim(u32 op, u32 diff) {
return;
}

int prim = (op >> 8) & 0x7;
if (prim != GE_PRIM_KEEP_PREVIOUS) {
// Flush before changing the prim type. Only continue can be used to continue a prim.
FlushImm();
}

TransformedVertex &v = immBuffer_[immCount_++];

// Formula deduced from ThrillVille's clear.
int offsetX = gstate.getOffsetX16();
int offsetY = gstate.getOffsetY16();
v.x = ((gstate.imm_vscx & 0xFFFFFF) - offsetX) / 16.0f;
v.y = ((gstate.imm_vscy & 0xFFFFFF) - offsetY) / 16.0f;
// ThrillVille does a clear with this, additional parameters found via tests.
// The current vtype affects how the coordinate is processed.
if (gstate.isModeThrough()) {
v.x = ((int)(gstate.imm_vscx & 0xFFFF) - 0x8000) / 16.0f;
v.y = ((int)(gstate.imm_vscy & 0xFFFF) - 0x8000) / 16.0f;
} else {
int offsetX = gstate.getOffsetX16();
int offsetY = gstate.getOffsetY16();
v.x = ((int)(gstate.imm_vscx & 0xFFFF) - offsetX) / 16.0f;
v.y = ((int)(gstate.imm_vscy & 0xFFFF) - offsetY) / 16.0f;
}
v.z = gstate.imm_vscz & 0xFFFF;
v.pos_w = 1.0f;
v.u = getFloat24(gstate.imm_vtcs);
v.v = getFloat24(gstate.imm_vtct);
v.uv_w = getFloat24(gstate.imm_vtcq);
v.color0_32 = (gstate.imm_cv & 0xFFFFFF) | (gstate.imm_ap << 24);
v.fog = 0.0f; // we have no information about the scale here
// TODO: When !gstate.isModeThrough(), direct fog coefficient (0 = entirely fog), ignore fog flag (also GE_IMM_FOG.)
v.fog = (gstate.imm_fc & 0xFF) / 255.0f;
// TODO: Apply if gstate.isUsingSecondaryColor() && !gstate.isModeThrough(), ignore lighting flag.
v.color1_32 = gstate.imm_scv & 0xFFFFFF;
int prim = (op >> 8) & 0x7;
if (prim != GE_PRIM_KEEP_PREVIOUS) {
immPrim_ = (GEPrimitiveType)prim;
} else if (prim == GE_PRIM_KEEP_PREVIOUS && immCount_ == 2) {
// Flags seem to only be respected from the first prim.
immFlags_ = op & 0x00FFF800;
} else if (prim == GE_PRIM_KEEP_PREVIOUS && immPrim_ != GE_PRIM_INVALID) {
static constexpr int flushPrimCount[] = { 1, 2, 0, 3, 0, 0, 2, 0 };
// Instead of finding a proper point to flush, we just emit a full rectangle every time one
// is finished.
FlushImm();
// Need to reset immCount_ here. If we do it in FlushImm it could get skipped by gstate_c.skipDrawReason.
immCount_ = 0;
if (immCount_ == flushPrimCount[immPrim_ & 7])
FlushImm();
} else {
ERROR_LOG_REPORT_ONCE(imm_draw_prim, G3D, "Immediate draw: Unexpected primitive %d at count %d", prim, immCount_);
}
}

void GPUCommon::FlushImm() {
if (immCount_ == 0 || immPrim_ == GE_PRIM_INVALID)
return;

SetDrawType(DRAW_PRIM, immPrim_);
framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
if (framebufferManager_)
framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
// No idea how many cycles to skip, heh.
immCount_ = 0;
return;
}
UpdateUVScaleOffset();
Expand All @@ -2422,23 +2444,69 @@ void GPUCommon::FlushImm() {
// through vertices.
// Since the only known use is Thrillville and it only uses it to clear, we just use color and pos.
struct ImmVertex {
float uv[2];
uint32_t color;
float xyz[3];
};
ImmVertex temp[MAX_IMMBUFFER_SIZE];
uint32_t color1Used = 0;
for (int i = 0; i < immCount_; i++) {
// Since we're sending through, scale back up to w/h.
temp[i].uv[0] = immBuffer_[i].u * gstate.getTextureWidth(0);
temp[i].uv[1] = immBuffer_[i].v * gstate.getTextureHeight(0);
temp[i].color = immBuffer_[i].color0_32;
temp[i].xyz[0] = immBuffer_[i].pos[0];
temp[i].xyz[1] = immBuffer_[i].pos[1];
temp[i].xyz[2] = immBuffer_[i].pos[2];
color1Used |= immBuffer_[i].color1_32;
}
int vtype = GE_VTYPE_TC_FLOAT | GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH;

// TODO: Handle fog and secondary color somehow?

bool antialias = (immFlags_ & GE_IMM_ANTIALIAS) != 0;
bool prevAntialias = gstate.isAntiAliasEnabled();
bool shading = (immFlags_ & GE_IMM_SHADING) != 0;
bool prevShading = gstate.getShadeMode() == GE_SHADE_GOURAUD;
bool cullEnable = (immFlags_ & GE_IMM_CULLENABLE) != 0;
bool prevCullEnable = gstate.isCullEnabled();
int cullMode = (immFlags_ & GE_IMM_CULLFACE) != 0 ? 1 : 0;
bool texturing = (immFlags_ & GE_IMM_TEXTURE) != 0;
bool prevTexturing = gstate.isTextureMapEnabled();
bool dither = (immFlags_ & GE_IMM_DITHER) != 0;
bool prevDither = gstate.isDitherEnabled();

if ((immFlags_ & GE_IMM_CLIPMASK) != 0) {
WARN_LOG_REPORT_ONCE(geimmclipvalue, G3D, "Imm vertex used clip value, flags=%06x", immFlags_);
} else if ((immFlags_ & GE_IMM_FOG) != 0) {
WARN_LOG_REPORT_ONCE(geimmfog, G3D, "Imm vertex used fog, flags=%06x", immFlags_);
} else if (color1Used != 0 && gstate.isUsingSecondaryColor()) {
WARN_LOG_REPORT_ONCE(geimmcolor1, G3D, "Imm vertex used secondary color, flags=%06x", immFlags_);
}

if (texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither || prevShading != shading) {
DispatchFlush();
gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)antialias;
gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)shading;
gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)cullEnable;
gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)texturing;
gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)dither;
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE);
}
int vtype = GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH;

int bytesRead;
uint32_t vertTypeID = GetVertTypeID(vtype, 0);
drawEngineCommon_->DispatchSubmitImm(temp, nullptr, immPrim_, immCount_, vertTypeID, gstate.getCullMode(), &bytesRead);
// TOOD: In the future, make a special path for these.
drawEngineCommon_->DispatchSubmitImm(temp, nullptr, immPrim_, immCount_, vertTypeID, cullMode, &bytesRead);
// TODO: In the future, make a special path for these.
// drawEngineCommon_->DispatchSubmitImm(immBuffer_, immCount_);
immCount_ = 0;

gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)prevAntialias;
gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)prevShading;
gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)prevCullEnable;
gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)prevTexturing;
gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)prevDither;
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE);
}

void GPUCommon::ExecuteOp(u32 op, u32 diff) {
Expand Down
5 changes: 3 additions & 2 deletions GPU/GPUCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ class GPUCommon : public GPUInterface, public GPUDebugInterface {
void UpdatePC(u32 currentPC, u32 newPC);
void UpdateState(GPURunState state);
void FastLoadBoneMatrix(u32 target);
void FlushImm();

// TODO: Unify this.
virtual void FinishDeferred() {}
Expand Down Expand Up @@ -352,13 +353,13 @@ class GPUCommon : public GPUInterface, public GPUDebugInterface {

TransformedVertex immBuffer_[MAX_IMMBUFFER_SIZE];
int immCount_ = 0;
GEPrimitiveType immPrim_;
GEPrimitiveType immPrim_ = GE_PRIM_INVALID;
uint32_t immFlags_ = 0;

std::string reportingPrimaryInfo_;
std::string reportingFullInfo_;

private:
void FlushImm();
void CheckDepthUsage(VirtualFramebuffer *vfb);
void DoBlockTransfer(u32 skipDrawReason);
void DoExecuteCall(u32 target);
Expand Down
29 changes: 27 additions & 2 deletions GPU/GeDisasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1354,11 +1354,36 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
break;

case GE_CMD_VAP:
snprintf(buffer, bufsize, "Vertex draw: alpha=%02x, prim=%s, other=%06x", data & 0xFF, primTypes[(data >> 8) & 7], data & ~0x0007FF);
{
bool antialias = (data & GE_IMM_ANTIALIAS) != 0;
int clip = (data & GE_IMM_CLIPMASK) >> 12;
bool shading = (data & GE_IMM_SHADING) != 0;
bool cullEnable = (data & GE_IMM_CULLENABLE) != 0;
int cullMode = (data & GE_IMM_CULLFACE) != 0 ? 1 : 0;
bool texturing = (data & GE_IMM_TEXTURE) != 0;
bool dither = (data & GE_IMM_DITHER) != 0;
char *p = buffer;
p += snprintf(p, bufsize - (p - buffer), "Vertex draw: alpha=%02x, prim=%s", data & 0xFF, primTypes[(data >> 8) & 7]);
if (antialias)
p += snprintf(p, bufsize - (p - buffer), ", antialias");
if (clip != 0)
p += snprintf(p, bufsize - (p - buffer), ", clip=%02x", clip);
if (shading)
p += snprintf(p, bufsize - (p - buffer), ", shading");
if (cullEnable)
p += snprintf(p, bufsize - (p - buffer), ", cull=%s", cullMode == 1 ? "back (CCW)" : "front (CW)");
if (texturing)
p += snprintf(p, bufsize - (p - buffer), ", texturing");
if (dither)
p += snprintf(p, bufsize - (p - buffer), ", dither");
}
break;

case GE_CMD_VFC:
snprintf(buffer, bufsize, "Vertex fog: %06x", data);
if (data & ~0xFF)
snprintf(buffer, bufsize, "Vertex fog: %02x / %f (extra %04x)", data & 0xFF, (data & 0xFF) / 255.0f, data >> 8);
else
snprintf(buffer, bufsize, "Vertex fog: %02x / %f", data & 0xFF, (data & 0xFF) / 255.0f);
break;

case GE_CMD_VSCV:
Expand Down
4 changes: 2 additions & 2 deletions GPU/Software/BinManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ BinManager::~BinManager() {
}
}

void BinManager::UpdateState() {
void BinManager::UpdateState(bool throughMode) {
PROFILE_THIS_SCOPE("bin_state");
if (HasDirty(SoftDirty::PIXEL_ALL | SoftDirty::SAMPLER_ALL | SoftDirty::RAST_ALL)) {
if (states_.Full())
Flush("states");
stateIndex_ = (int)states_.Push(RasterizerState());
ComputeRasterizerState(&states_[stateIndex_]);
ComputeRasterizerState(&states_[stateIndex_], throughMode);
states_[stateIndex_].samplerID.cached.clut = cluts_[clutIndex_].readable;

ClearDirty(SoftDirty::PIXEL_ALL | SoftDirty::SAMPLER_ALL | SoftDirty::RAST_ALL);
Expand Down
2 changes: 1 addition & 1 deletion GPU/Software/BinManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class BinManager {
BinManager();
~BinManager();

void UpdateState();
void UpdateState(bool throughMode);
void UpdateClut(const void *src);

const Rasterizer::RasterizerState &State() {
Expand Down
6 changes: 3 additions & 3 deletions GPU/Software/Clipper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ static inline bool CheckOutsideZ(ClipCoords p, int &pos, int &neg) {
}

void ProcessRect(const VertexData &v0, const VertexData &v1, BinManager &binner) {
if (!gstate.isModeThrough()) {
if (!binner.State().throughMode) {
// We may discard the entire rect based on depth values.
int outsidePos = 0, outsideNeg = 0;
CheckOutsideZ(v0.clippos, outsidePos, outsideNeg);
Expand Down Expand Up @@ -181,7 +181,7 @@ void ProcessPoint(const VertexData &v0, BinManager &binner) {
}

void ProcessLine(const VertexData &v0, const VertexData &v1, BinManager &binner) {
if (gstate.isModeThrough()) {
if (binner.State().throughMode) {
// Actually, should clip this one too so we don't need to do bounds checks in the rasterizer.
binner.AddLine(v0, v1);
return;
Expand Down Expand Up @@ -221,7 +221,7 @@ void ProcessLine(const VertexData &v0, const VertexData &v1, BinManager &binner)

void ProcessTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const VertexData &provoking, BinManager &binner) {
int mask = 0;
if (!gstate.isModeThrough()) {
if (!binner.State().throughMode) {
mask |= CalcClipMask(v0.clippos);
mask |= CalcClipMask(v1.clippos);
mask |= CalcClipMask(v2.clippos);
Expand Down
6 changes: 3 additions & 3 deletions GPU/Software/FuncId.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ static inline PixelBlendFactor OptimizeAlphaFactor(uint32_t color) {
return PixelBlendFactor::FIX;
}

void ComputePixelFuncID(PixelFuncID *id) {
void ComputePixelFuncID(PixelFuncID *id, bool throughMode) {
id->fullKey = 0;

// TODO: Could this be minz > 0x0000 || maxz < 0xFFFF? Maybe unsafe, depending on verts...
id->applyDepthRange = !gstate.isModeThrough();
id->applyDepthRange = !throughMode;
// Dither happens even in clear mode.
id->dithering = gstate.isDitherEnabled();
id->fbFormat = gstate.FrameBufFormat();
Expand Down Expand Up @@ -162,7 +162,7 @@ void ComputePixelFuncID(PixelFuncID *id) {
}

id->applyLogicOp = gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY;
id->applyFog = gstate.isFogEnabled() && !gstate.isModeThrough();
id->applyFog = gstate.isFogEnabled() && !throughMode;
}

// Cache some values for later convenience.
Expand Down
2 changes: 1 addition & 1 deletion GPU/Software/FuncId.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ struct hash<SamplerID> {

};

void ComputePixelFuncID(PixelFuncID *id);
void ComputePixelFuncID(PixelFuncID *id, bool throughMode);
std::string DescribePixelFuncID(const PixelFuncID &id);

void ComputeSamplerID(SamplerID *id);
Expand Down
6 changes: 3 additions & 3 deletions GPU/Software/Rasterizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ static inline Vec4<float> Interpolate(const float &c0, const float &c1, const fl
return Interpolate(c0, c1, c2, w0.Cast<float>(), w1.Cast<float>(), w2.Cast<float>(), wsum_recip);
}

void ComputeRasterizerState(RasterizerState *state) {
ComputePixelFuncID(&state->pixelID);
void ComputeRasterizerState(RasterizerState *state, bool throughMode) {
ComputePixelFuncID(&state->pixelID, throughMode);
state->drawPixel = Rasterizer::GetSingleFunc(state->pixelID);

state->enableTextures = gstate.isTextureMapEnabled() && !state->pixelID.clearMode;
Expand Down Expand Up @@ -140,7 +140,7 @@ void ComputeRasterizerState(RasterizerState *state) {
}

state->shadeGouraud = gstate.getShadeMode() == GE_SHADE_GOURAUD;
state->throughMode = gstate.isModeThrough();
state->throughMode = throughMode;
state->antialiasLines = gstate.isAntiAliasEnabled();

state->screenOffsetX = gstate.getOffsetX16();
Expand Down
2 changes: 1 addition & 1 deletion GPU/Software/Rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ struct RasterizerState {
}
};

void ComputeRasterizerState(RasterizerState *state);
void ComputeRasterizerState(RasterizerState *state, bool throughMode);

// Draws a triangle if its vertices are specified in counter-clockwise order
void DrawTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const BinCoords &range, const RasterizerState &state);
Expand Down
Loading