softgpu: Support fog and color1 on imm verts.

This commit is contained in:
Unknown W. Brackets 2022-09-17 19:42:41 -07:00
parent 35ba01e01f
commit 596b07bd2e
9 changed files with 109 additions and 43 deletions

View file

@ -215,6 +215,9 @@ void DrawEngineCommon::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex
int vtype = GE_VTYPE_TC_FLOAT | GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH; int vtype = GE_VTYPE_TC_FLOAT | GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH;
// TODO: Handle fog and secondary color somehow? // TODO: Handle fog and secondary color somehow?
if (gstate.isFogEnabled()) {
WARN_LOG_REPORT_ONCE(geimmfog, G3D, "Imm vertex used fog");
}
if (color1Used != 0 && gstate.isUsingSecondaryColor()) { if (color1Used != 0 && gstate.isUsingSecondaryColor()) {
WARN_LOG_REPORT_ONCE(geimmcolor1, G3D, "Imm vertex used secondary color"); WARN_LOG_REPORT_ONCE(geimmcolor1, G3D, "Imm vertex used secondary color");
} }

View file

@ -2447,21 +2447,22 @@ void GPUCommon::FlushImm() {
int cullMode = (immFlags_ & GE_IMM_CULLFACE) != 0 ? 1 : 0; int cullMode = (immFlags_ & GE_IMM_CULLFACE) != 0 ? 1 : 0;
bool texturing = (immFlags_ & GE_IMM_TEXTURE) != 0; bool texturing = (immFlags_ & GE_IMM_TEXTURE) != 0;
bool prevTexturing = gstate.isTextureMapEnabled(); bool prevTexturing = gstate.isTextureMapEnabled();
bool fog = (immFlags_ & GE_IMM_FOG) != 0;
bool prevFog = gstate.isFogEnabled();
bool dither = (immFlags_ & GE_IMM_DITHER) != 0; bool dither = (immFlags_ & GE_IMM_DITHER) != 0;
bool prevDither = gstate.isDitherEnabled(); bool prevDither = gstate.isDitherEnabled();
if ((immFlags_ & GE_IMM_CLIPMASK) != 0) { if ((immFlags_ & GE_IMM_CLIPMASK) != 0) {
WARN_LOG_REPORT_ONCE(geimmclipvalue, G3D, "Imm vertex used clip value, flags=%06x", immFlags_); 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_);
} }
if (texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither || prevShading != shading) { if (texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither || prevShading != shading || prevFog != fog) {
DispatchFlush(); DispatchFlush();
gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)antialias; gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)antialias;
gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)shading; gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)shading;
gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)cullEnable; gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)cullEnable;
gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)texturing; gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)texturing;
gstate.fogEnable = (GE_CMD_FOGENABLE << 24) | (int)fog;
gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)dither; gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)dither;
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE); gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE);
} }
@ -2473,6 +2474,7 @@ void GPUCommon::FlushImm() {
gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)prevShading; gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)prevShading;
gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)prevCullEnable; gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)prevCullEnable;
gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)prevTexturing; gstate.textureMapEnable = (GE_CMD_TEXTUREMAPENABLE << 24) | (int)prevTexturing;
gstate.fogEnable = (GE_CMD_FOGENABLE << 24) | (int)prevFog;
gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)prevDither; gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)prevDither;
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE); gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE);
} }

View file

@ -167,7 +167,7 @@ void BinManager::UpdateState(bool throughMode) {
if (states_.Full()) if (states_.Full())
Flush("states"); Flush("states");
stateIndex_ = (uint16_t)states_.Push(RasterizerState()); stateIndex_ = (uint16_t)states_.Push(RasterizerState());
ComputeRasterizerState(&states_[stateIndex_], throughMode); ComputeRasterizerState(&states_[stateIndex_]);
states_[stateIndex_].samplerID.cached.clut = cluts_[clutIndex_].readable; states_[stateIndex_].samplerID.cached.clut = cluts_[clutIndex_].readable;
ClearDirty(SoftDirty::PIXEL_ALL | SoftDirty::SAMPLER_ALL | SoftDirty::RAST_ALL); ClearDirty(SoftDirty::PIXEL_ALL | SoftDirty::SAMPLER_ALL | SoftDirty::RAST_ALL);

View file

@ -48,11 +48,11 @@ static inline PixelBlendFactor OptimizeAlphaFactor(uint32_t color) {
return PixelBlendFactor::FIX; return PixelBlendFactor::FIX;
} }
void ComputePixelFuncID(PixelFuncID *id, bool throughMode) { void ComputePixelFuncID(PixelFuncID *id) {
id->fullKey = 0; id->fullKey = 0;
// TODO: Could this be minz > 0x0000 || maxz < 0xFFFF? Maybe unsafe, depending on verts... // TODO: Could this be minz > 0x0000 || maxz < 0xFFFF? Maybe unsafe, depending on verts...
id->applyDepthRange = !throughMode; id->applyDepthRange = !gstate.isModeThrough();
// Dither happens even in clear mode. // Dither happens even in clear mode.
id->dithering = gstate.isDitherEnabled(); id->dithering = gstate.isDitherEnabled();
id->fbFormat = gstate.FrameBufFormat(); id->fbFormat = gstate.FrameBufFormat();
@ -169,7 +169,7 @@ void ComputePixelFuncID(PixelFuncID *id, bool throughMode) {
} }
id->applyLogicOp = gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY; id->applyLogicOp = gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY;
id->applyFog = gstate.isFogEnabled() && !throughMode; id->applyFog = gstate.isFogEnabled() && !gstate.isModeThrough();
id->earlyZChecks = id->DepthTestFunc() != GE_COMP_ALWAYS; id->earlyZChecks = id->DepthTestFunc() != GE_COMP_ALWAYS;
if (id->stencilTest && id->earlyZChecks) { if (id->stencilTest && id->earlyZChecks) {

View file

@ -244,7 +244,7 @@ struct hash<SamplerID> {
}; };
void ComputePixelFuncID(PixelFuncID *id, bool throughMode); void ComputePixelFuncID(PixelFuncID *id);
std::string DescribePixelFuncID(const PixelFuncID &id); std::string DescribePixelFuncID(const PixelFuncID &id);
void ComputeSamplerID(SamplerID *id); void ComputeSamplerID(SamplerID *id);

View file

@ -93,8 +93,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); return Interpolate(c0, c1, c2, w0.Cast<float>(), w1.Cast<float>(), w2.Cast<float>(), wsum_recip);
} }
void ComputeRasterizerState(RasterizerState *state, bool throughMode) { void ComputeRasterizerState(RasterizerState *state) {
ComputePixelFuncID(&state->pixelID, throughMode); ComputePixelFuncID(&state->pixelID);
state->drawPixel = Rasterizer::GetSingleFunc(state->pixelID); state->drawPixel = Rasterizer::GetSingleFunc(state->pixelID);
state->enableTextures = gstate.isTextureMapEnabled() && !state->pixelID.clearMode; state->enableTextures = gstate.isTextureMapEnabled() && !state->pixelID.clearMode;
@ -132,7 +132,7 @@ void ComputeRasterizerState(RasterizerState *state, bool throughMode) {
} }
state->shadeGouraud = gstate.getShadeMode() == GE_SHADE_GOURAUD; state->shadeGouraud = gstate.getShadeMode() == GE_SHADE_GOURAUD;
state->throughMode = throughMode; state->throughMode = gstate.isModeThrough();
state->antialiasLines = gstate.isAntiAliasEnabled(); state->antialiasLines = gstate.isAntiAliasEnabled();
#if defined(SOFTGPU_MEMORY_TAGGING_DETAILED) || defined(SOFTGPU_MEMORY_TAGGING_BASIC) #if defined(SOFTGPU_MEMORY_TAGGING_DETAILED) || defined(SOFTGPU_MEMORY_TAGGING_BASIC)

View file

@ -65,7 +65,7 @@ struct RasterizerState {
} }
}; };
void ComputeRasterizerState(RasterizerState *state, bool throughMode); void ComputeRasterizerState(RasterizerState *state);
// Draws a triangle if its vertices are specified in counter-clockwise order // 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); void DrawTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const BinCoords &range, const RasterizerState &state);

View file

@ -71,46 +71,64 @@ void SoftwareDrawEngine::DispatchSubmitPrim(const void *verts, const void *inds,
} }
void SoftwareDrawEngine::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode) { void SoftwareDrawEngine::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode) {
// TODO: Handle fog and secondary color somehow? uint32_t vertTypeID = GetVertTypeID(gstate.vertType, gstate.getUVGenMode());
int vtype = GE_VTYPE_TC_FLOAT | GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH;
uint32_t vertTypeID = GetVertTypeID(vtype, 0);
int flipCull = cullMode != gstate.getCullMode() ? 1 : 0; int flipCull = cullMode != gstate.getCullMode() ? 1 : 0;
// TODO: For now, just setting all dirty. // TODO: For now, just setting all dirty.
transformUnit.SetDirty(SoftDirty(-1)); transformUnit.SetDirty(SoftDirty(-1));
gstate.cullmode ^= flipCull; gstate.cullmode ^= flipCull;
// Instead of plumbing through properly (we'd need to inject these pretransformed vertices in the middle // TODO: This is a bit ugly. Should bypass when clipping...
// of SoftwareTransform(), which would take a lot of refactoring), we'll cheat and just turn these into uint32_t xScale = gstate.viewportxscale;
// through vertices. uint32_t xCenter = gstate.viewportxcenter;
// Since the only known use is Thrillville and it only uses it to clear, we just use color and pos. uint32_t yScale = gstate.viewportyscale;
struct ImmVertex { uint32_t yCenter = gstate.viewportycenter;
float uv[2]; uint32_t zScale = gstate.viewportzscale;
uint32_t color; uint32_t zCenter = gstate.viewportzcenter;
float xyz[3];
}; // Force scale to 1 and center to zero.
std::vector<ImmVertex> temp; gstate.viewportxscale = (GE_CMD_VIEWPORTXSCALE << 24) | 0x3F8000;
temp.resize(vertexCount); gstate.viewportxcenter = (GE_CMD_VIEWPORTXCENTER << 24) | 0x000000;
uint32_t color1Used = 0; gstate.viewportyscale = (GE_CMD_VIEWPORTYSCALE << 24) | 0x3F8000;
gstate.viewportycenter = (GE_CMD_VIEWPORTYCENTER << 24) | 0x000000;
// Z we scale to 65535 for neg z clipping.
gstate.viewportzscale = (GE_CMD_VIEWPORTZSCALE << 24) | 0x477FFF;
gstate.viewportzcenter = (GE_CMD_VIEWPORTZCENTER << 24) | 0x000000;
// Before we start, submit 0 prims to reset the prev prim type.
// Following submits will always be KEEP_PREVIOUS.
transformUnit.SubmitPrimitive(nullptr, nullptr, prim, 0, vertTypeID, nullptr, this);
for (int i = 0; i < vertexCount; i++) { for (int i = 0; i < vertexCount; i++) {
// Since we're sending through, scale back up to w/h. VertexData vert;
temp[i].uv[0] = buffer[i].u * gstate.getTextureWidth(0); vert.clippos = ClipCoords(buffer[i].pos);
temp[i].uv[1] = buffer[i].v * gstate.getTextureHeight(0); vert.texturecoords.x = buffer[i].u;
temp[i].color = buffer[i].color0_32; vert.texturecoords.y = buffer[i].v;
temp[i].xyz[0] = buffer[i].pos[0]; if (gstate.isModeThrough()) {
temp[i].xyz[1] = buffer[i].pos[1]; vert.texturecoords.x *= gstate.getTextureWidth(0);
temp[i].xyz[2] = buffer[i].pos[2]; vert.texturecoords.y *= gstate.getTextureHeight(0);
color1Used |= buffer[i].color1_32; } else {
vert.clippos.z *= 1.0f / 65535.0f;
}
vert.color0 = buffer[i].color0_32;
vert.color1 = gstate.isUsingSecondaryColor() ? buffer[i].color1_32 : 0;
vert.fogdepth = buffer[i].fog;
vert.screenpos.x = (int)(buffer[i].x * 16.0f);
vert.screenpos.y = (int)(buffer[i].y * 16.0f);
vert.screenpos.z = (u16)(u32)buffer[i].z;
transformUnit.SubmitImmVertex(vert, this);
} }
if (color1Used != 0 && gstate.isUsingSecondaryColor()) { gstate.viewportxscale = xScale;
WARN_LOG_REPORT_ONCE(geimmcolor1, G3D, "Imm vertex used secondary color"); gstate.viewportxcenter = xCenter;
} gstate.viewportyscale = yScale;
gstate.viewportycenter = yCenter;
transformUnit.SubmitPrimitive(&temp[0], nullptr, prim, vertexCount, vertTypeID, nullptr, this); gstate.viewportzscale = zScale;
gstate.viewportzcenter = zCenter;
gstate.cullmode ^= flipCull; gstate.cullmode ^= flipCull;
// TODO: Should really clear, but the vertex type is faked so things might need resetting... // TODO: Should really clear, but a bunch of values are forced so we this is safest.
transformUnit.SetDirty(SoftDirty(-1)); transformUnit.SetDirty(SoftDirty(-1));
} }
@ -492,11 +510,12 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
return; return;
u16 index_lower_bound = 0; u16 index_lower_bound = 0;
u16 index_upper_bound = vertex_count - 1; u16 index_upper_bound = vertex_count == 0 ? 0 : vertex_count - 1;
IndexConverter ConvertIndex(vertex_type, indices); IndexConverter ConvertIndex(vertex_type, indices);
if (indices) if (indices)
GetIndexBounds(indices, vertex_count, vertex_type, &index_lower_bound, &index_upper_bound); GetIndexBounds(indices, vertex_count, vertex_type, &index_lower_bound, &index_upper_bound);
if (vertex_count != 0)
vdecoder.DecodeVerts(decoded_, vertices, index_lower_bound, index_upper_bound); vdecoder.DecodeVerts(decoded_, vertices, index_lower_bound, index_upper_bound);
VertexReader vreader(decoded_, vtxfmt, vertex_type); VertexReader vreader(decoded_, vtxfmt, vertex_type);
@ -786,6 +805,47 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
} }
} }
void TransformUnit::SubmitImmVertex(const VertexData &vert, SoftwareDrawEngine *drawEngine) {
// Where we put it is different for STRIP/FAN types.
switch (prev_prim_) {
case GE_PRIM_POINTS:
case GE_PRIM_LINES:
case GE_PRIM_TRIANGLES:
case GE_PRIM_RECTANGLES:
// This is the easy one. SubmitPrimitive resets data_index_.
data_[data_index_++] = vert;
break;
case GE_PRIM_LINE_STRIP:
// This one alternates, and data_index_ > 0 means it draws a segment.
data_[(data_index_++) & 1] = vert;
break;
case GE_PRIM_TRIANGLE_STRIP:
data_[(data_index_++) % 3] = vert;
break;
case GE_PRIM_TRIANGLE_FAN:
if (data_index_ == 0) {
data_[data_index_++] = vert;
} else {
int provoking_index = 2 - ((data_index_++) % 2);
data_[provoking_index] = vert;
}
break;
default:
_assert_msg_(false, "Invalid prim type: %d", (int)prev_prim_);
break;
}
uint32_t vertTypeID = GetVertTypeID(gstate.vertType, gstate.getUVGenMode());
// This now processes the step with shared logic, given the existing data_.
isImmDraw_ = true;
SubmitPrimitive(nullptr, nullptr, GE_PRIM_KEEP_PREVIOUS, 0, vertTypeID, nullptr, drawEngine);
isImmDraw_ = false;
}
void TransformUnit::SendTriangle(CullType cullType, const VertexData *verts, int provoking) { void TransformUnit::SendTriangle(CullType cullType, const VertexData *verts, int provoking) {
if (cullType == CullType::OFF) { if (cullType == CullType::OFF) {
Clipper::ProcessTriangle(verts[0], verts[1], verts[2], verts[provoking], *binner_); Clipper::ProcessTriangle(verts[0], verts[1], verts[2], verts[provoking], *binner_);

View file

@ -129,6 +129,7 @@ public:
static ScreenCoords DrawingToScreen(const DrawingCoords &coords, u16 z); static ScreenCoords DrawingToScreen(const DrawingCoords &coords, u16 z);
void SubmitPrimitive(const void* vertices, const void* indices, GEPrimitiveType prim_type, int vertex_count, u32 vertex_type, int *bytesRead, SoftwareDrawEngine *drawEngine); void SubmitPrimitive(const void* vertices, const void* indices, GEPrimitiveType prim_type, int vertex_count, u32 vertex_type, int *bytesRead, SoftwareDrawEngine *drawEngine);
void SubmitImmVertex(const VertexData &vert, SoftwareDrawEngine *drawEngine);
bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices); bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);