diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 31707fa76d78..df668902480b 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -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; // 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()) { WARN_LOG_REPORT_ONCE(geimmcolor1, G3D, "Imm vertex used secondary color"); } diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index bda1f7e51ac1..e91d32f5d66c 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2447,21 +2447,22 @@ void GPUCommon::FlushImm() { int cullMode = (immFlags_ & GE_IMM_CULLFACE) != 0 ? 1 : 0; bool texturing = (immFlags_ & GE_IMM_TEXTURE) != 0; bool prevTexturing = gstate.isTextureMapEnabled(); + bool fog = (immFlags_ & GE_IMM_FOG) != 0; + bool prevFog = gstate.isFogEnabled(); 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_); } - if (texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither || prevShading != shading) { + if (texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither || prevShading != shading || prevFog != fog) { 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.fogEnable = (GE_CMD_FOGENABLE << 24) | (int)fog; gstate.ditherEnable = (GE_CMD_DITHERENABLE << 24) | (int)dither; 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.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)prevCullEnable; 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_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE); } diff --git a/GPU/Software/BinManager.cpp b/GPU/Software/BinManager.cpp index 3438cb222df5..7fee77b64cda 100644 --- a/GPU/Software/BinManager.cpp +++ b/GPU/Software/BinManager.cpp @@ -167,7 +167,7 @@ void BinManager::UpdateState(bool throughMode) { if (states_.Full()) Flush("states"); stateIndex_ = (uint16_t)states_.Push(RasterizerState()); - ComputeRasterizerState(&states_[stateIndex_], throughMode); + ComputeRasterizerState(&states_[stateIndex_]); states_[stateIndex_].samplerID.cached.clut = cluts_[clutIndex_].readable; ClearDirty(SoftDirty::PIXEL_ALL | SoftDirty::SAMPLER_ALL | SoftDirty::RAST_ALL); diff --git a/GPU/Software/FuncId.cpp b/GPU/Software/FuncId.cpp index 501b5aaf27fb..0d194d0012d4 100644 --- a/GPU/Software/FuncId.cpp +++ b/GPU/Software/FuncId.cpp @@ -48,11 +48,11 @@ static inline PixelBlendFactor OptimizeAlphaFactor(uint32_t color) { return PixelBlendFactor::FIX; } -void ComputePixelFuncID(PixelFuncID *id, bool throughMode) { +void ComputePixelFuncID(PixelFuncID *id) { id->fullKey = 0; // 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. id->dithering = gstate.isDitherEnabled(); id->fbFormat = gstate.FrameBufFormat(); @@ -169,7 +169,7 @@ void ComputePixelFuncID(PixelFuncID *id, bool throughMode) { } 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; if (id->stencilTest && id->earlyZChecks) { diff --git a/GPU/Software/FuncId.h b/GPU/Software/FuncId.h index 46307fc55cbb..d9d8e515731f 100644 --- a/GPU/Software/FuncId.h +++ b/GPU/Software/FuncId.h @@ -244,7 +244,7 @@ struct hash { }; -void ComputePixelFuncID(PixelFuncID *id, bool throughMode); +void ComputePixelFuncID(PixelFuncID *id); std::string DescribePixelFuncID(const PixelFuncID &id); void ComputeSamplerID(SamplerID *id); diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 1f4039d05ac1..fa7e0af3f6ab 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -93,8 +93,8 @@ static inline Vec4 Interpolate(const float &c0, const float &c1, const fl return Interpolate(c0, c1, c2, w0.Cast(), w1.Cast(), w2.Cast(), wsum_recip); } -void ComputeRasterizerState(RasterizerState *state, bool throughMode) { - ComputePixelFuncID(&state->pixelID, throughMode); +void ComputeRasterizerState(RasterizerState *state) { + ComputePixelFuncID(&state->pixelID); state->drawPixel = Rasterizer::GetSingleFunc(state->pixelID); 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->throughMode = throughMode; + state->throughMode = gstate.isModeThrough(); state->antialiasLines = gstate.isAntiAliasEnabled(); #if defined(SOFTGPU_MEMORY_TAGGING_DETAILED) || defined(SOFTGPU_MEMORY_TAGGING_BASIC) diff --git a/GPU/Software/Rasterizer.h b/GPU/Software/Rasterizer.h index 089f99a6573d..93e89f959616 100644 --- a/GPU/Software/Rasterizer.h +++ b/GPU/Software/Rasterizer.h @@ -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 void DrawTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const BinCoords &range, const RasterizerState &state); diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index c052a472cd6d..1278d9348361 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -71,46 +71,64 @@ void SoftwareDrawEngine::DispatchSubmitPrim(const void *verts, const void *inds, } void SoftwareDrawEngine::DispatchSubmitImm(GEPrimitiveType prim, TransformedVertex *buffer, int vertexCount, int cullMode) { - // TODO: Handle fog and secondary color somehow? - int vtype = GE_VTYPE_TC_FLOAT | GE_VTYPE_POS_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_THROUGH; - uint32_t vertTypeID = GetVertTypeID(vtype, 0); + uint32_t vertTypeID = GetVertTypeID(gstate.vertType, gstate.getUVGenMode()); int flipCull = cullMode != gstate.getCullMode() ? 1 : 0; // TODO: For now, just setting all dirty. transformUnit.SetDirty(SoftDirty(-1)); gstate.cullmode ^= flipCull; - // Instead of plumbing through properly (we'd need to inject these pretransformed vertices in the middle - // of SoftwareTransform(), which would take a lot of refactoring), we'll cheat and just turn these into - // 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]; - }; - std::vector temp; - temp.resize(vertexCount); - uint32_t color1Used = 0; - for (int i = 0; i < vertexCount; i++) { - // Since we're sending through, scale back up to w/h. - temp[i].uv[0] = buffer[i].u * gstate.getTextureWidth(0); - temp[i].uv[1] = buffer[i].v * gstate.getTextureHeight(0); - temp[i].color = buffer[i].color0_32; - temp[i].xyz[0] = buffer[i].pos[0]; - temp[i].xyz[1] = buffer[i].pos[1]; - temp[i].xyz[2] = buffer[i].pos[2]; - color1Used |= buffer[i].color1_32; - } + // TODO: This is a bit ugly. Should bypass when clipping... + uint32_t xScale = gstate.viewportxscale; + uint32_t xCenter = gstate.viewportxcenter; + uint32_t yScale = gstate.viewportyscale; + uint32_t yCenter = gstate.viewportycenter; + uint32_t zScale = gstate.viewportzscale; + uint32_t zCenter = gstate.viewportzcenter; + + // Force scale to 1 and center to zero. + gstate.viewportxscale = (GE_CMD_VIEWPORTXSCALE << 24) | 0x3F8000; + gstate.viewportxcenter = (GE_CMD_VIEWPORTXCENTER << 24) | 0x000000; + 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); - if (color1Used != 0 && gstate.isUsingSecondaryColor()) { - WARN_LOG_REPORT_ONCE(geimmcolor1, G3D, "Imm vertex used secondary color"); + for (int i = 0; i < vertexCount; i++) { + VertexData vert; + vert.clippos = ClipCoords(buffer[i].pos); + vert.texturecoords.x = buffer[i].u; + vert.texturecoords.y = buffer[i].v; + if (gstate.isModeThrough()) { + vert.texturecoords.x *= gstate.getTextureWidth(0); + vert.texturecoords.y *= gstate.getTextureHeight(0); + } 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); } - transformUnit.SubmitPrimitive(&temp[0], nullptr, prim, vertexCount, vertTypeID, nullptr, this); + gstate.viewportxscale = xScale; + gstate.viewportxcenter = xCenter; + gstate.viewportyscale = yScale; + gstate.viewportycenter = yCenter; + gstate.viewportzscale = zScale; + gstate.viewportzcenter = zCenter; 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)); } @@ -492,12 +510,13 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G return; 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); if (indices) GetIndexBounds(indices, vertex_count, vertex_type, &index_lower_bound, &index_upper_bound); - vdecoder.DecodeVerts(decoded_, vertices, index_lower_bound, index_upper_bound); + if (vertex_count != 0) + vdecoder.DecodeVerts(decoded_, vertices, index_lower_bound, index_upper_bound); 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) { if (cullType == CullType::OFF) { Clipper::ProcessTriangle(verts[0], verts[1], verts[2], verts[provoking], *binner_); diff --git a/GPU/Software/TransformUnit.h b/GPU/Software/TransformUnit.h index e19d69fff7d3..bcaf5859c740 100644 --- a/GPU/Software/TransformUnit.h +++ b/GPU/Software/TransformUnit.h @@ -129,6 +129,7 @@ class TransformUnit { 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 SubmitImmVertex(const VertexData &vert, SoftwareDrawEngine *drawEngine); bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices);