diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 2c0c6e2bdf..cbfe5b7c66 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -109,7 +109,7 @@ static bool IsReallyAClear(const TransformedVertex *transformed, int numVerts, f } // At the end, this calls ProjectClipAndExpand which will expand rectangles as necessary, or apply culling. -void SoftwareTransform::Transform(const float projMtx[16], Lin::Vec3 vpScale, Lin::Vec3 vpOffset, int prim, u32 vertType, const DecVtxFormat &decVtxFormat, int &numDecodedVerts, int vertsSize, int vertexCount, u16 *&inds, int indsSize, SoftwareTransformResult *result) { +SoftwareTransformAction SoftwareTransform::Transform(const float projMtx[16], Lin::Vec3 vpScale, Lin::Vec3 vpOffset, int prim, u32 vertType, const DecVtxFormat &decVtxFormat, int &numDecodedVerts, int vertsSize, int vertexCount, u16 *&inds, int indsSize, SoftwareTransformResult *result) { u8 *decoded = params_.decoded; TransformedVertex *transformed = params_.transformed; bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0; @@ -209,9 +209,8 @@ void SoftwareTransform::Transform(const float projMtx[16], Lin::Vec3 vpScale, Li if (!(params_.everUsedEqualDepth && gstate.isClearModeDepthMask() && result->depth > 0.0f && result->depth < 1.0f)) { result->color = transformed[1].color0_32; result->depth = depth; - result->action = SW_CLEAR; gpuStats.perFrame.numClears++; - return; + return SW_CLEAR; } } } @@ -363,23 +362,15 @@ void SoftwareTransform::Transform(const float projMtx[16], Lin::Vec3 vpScale, Li fogCoef = (v[2] + fog_end) * fog_slope; // Then transform by the projection. - float xyzw[4]; - Vec3ByMatrix44(xyzw, v, projMtx); - - // Here we also need to do the perspective device and apply the viewport. - // TODO: Move this to ProjectClipAndExpand. - float recip = 1.0f / xyzw[3]; - Lin::Vec3 xyz = vpOffset + vpScale.scaledBy(Lin::Vec3(xyzw[0] * recip, xyzw[1] * recip, xyzw[2] * recip)); - transformed[index].x = xyz.x; - transformed[index].y = xyz.y; - transformed[index].z = xyz.z; - transformed[index].pos[3] = xyzw[3]; + Vec3ByMatrix44(transformed[index].pos, v, projMtx); transformed[index].fog = fogCoef; memcpy(&transformed[index].uv, uv, 3 * sizeof(float)); transformed[index].color0_32 = c0.ToRGBA(); transformed[index].color1_32 = c1.ToRGBA(); + // Projection happens later in ProjectClipAndExpand. + // Vertex depth rounding is done in the shader, to simulate the 16-bit depth buffer. } } @@ -401,12 +392,28 @@ void SoftwareTransform::Transform(const float projMtx[16], Lin::Vec3 vpScale, Li } } - _dbg_assert_(result->action == SW_NOT_READY); - - ProjectClipAndExpand(prim, vertexCount, vertType, inds, indsSize, numDecodedVerts, vertsSize, result); + return ProjectClipAndExpand(prim, vertexCount, vertType, inds, indsSize, numDecodedVerts, vertsSize, result); } -void SoftwareTransform::ProjectClipAndExpand(int prim, int vertexCount, u32 vertType, u16 *&inds, int indsSize, int &numDecodedVerts, int vertsSize, SoftwareTransformResult *result) { +// Modifies the vertices in-place. Applies viewport and projection. +// TODO: SIMD. +void SoftwareTransform::ProjectVertices(TransformedVertex *transformed, int vertexCount) { + Lin::Vec3 vpOffset(gstate.getViewportXCenter(), gstate.getViewportYCenter(), gstate.getViewportZCenter()); + Lin::Vec3 vpScale(gstate.getViewportXScale(), gstate.getViewportYScale(), gstate.getViewportZScale()); + + for (int i = 0; i < vertexCount; i++) { + // Here we also need to do the perspective device and apply the viewport. + // TODO: Move this to ProjectClipAndExpand. + const float w = transformed[i].pos_w; + const float recip = 1.0f / w; + Lin::Vec3 xyz = vpOffset + vpScale.scaledBy(Lin::Vec3(transformed[i].x * recip, transformed[i].y * recip, transformed[i].z * recip)); + transformed[i].x = xyz.x; + transformed[i].y = xyz.y; + transformed[i].z = xyz.z; + } +} + +SoftwareTransformAction SoftwareTransform::ProjectClipAndExpand(int prim, int vertexCount, u32 vertType, u16 *&inds, int indsSize, int &numDecodedVerts, int vertsSize, SoftwareTransformResult *result) { TransformedVertex *transformed = params_.transformed; TransformedVertex *transformedExpanded = params_.transformedExpanded; bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0; @@ -419,10 +426,15 @@ void SoftwareTransform::ProjectClipAndExpand(int prim, int vertexCount, u32 vert bool useBufferedRendering = fbman->UseBufferedRendering(); if (prim == GE_PRIM_RECTANGLES) { + // TODO: We're now able to cull here. + if (!throughmode) { + ProjectVertices(transformed, numDecodedVerts); + } + if (!ExpandRectangles(vertexCount, numDecodedVerts, vertsSize, inds, indsSize, transformed, transformedExpanded, numTrans, throughmode, &result->pixelMapped)) { result->drawNumTrans = 0; result->pixelMapped = false; - return; + return SW_CULLED; } result->drawBuffer = transformedExpanded; @@ -440,26 +452,71 @@ void SoftwareTransform::ProjectClipAndExpand(int prim, int vertexCount, u32 vert } } } else if (prim == GE_PRIM_POINTS) { + if (!throughmode) { + ProjectVertices(transformed, numDecodedVerts); + } + result->pixelMapped = false; if (!ExpandPoints(vertexCount, numDecodedVerts, vertsSize, inds, indsSize, transformed, transformedExpanded, numTrans, throughmode)) { result->drawNumTrans = 0; - return; + return SW_CULLED; } result->drawBuffer = transformedExpanded; } else if (prim == GE_PRIM_LINES) { + if (!throughmode) { + ProjectVertices(transformed, numDecodedVerts); + } + result->pixelMapped = false; if (!ExpandLines(vertexCount, numDecodedVerts, vertsSize, inds, indsSize, transformed, transformedExpanded, numTrans, throughmode)) { result->drawNumTrans = 0; - return; + return SW_CULLED; } result->drawBuffer = transformedExpanded; - } else { - // We can simply draw the unexpanded buffer. + } else if (prim == GE_PRIM_TRIANGLES) { + // Triangles. We can simply draw the unexpanded buffer, although we do also take the opportunity to perform culling. numTrans = vertexCount; result->pixelMapped = false; - // If we don't support custom cull in the shader, process it here. - if (!gstate_c.Use(GPU_USE_CULL_DISTANCE) && vertexCount > 0 && !throughmode) { + if (throughmode) { + // Nothing to do, pass the vertices right through as-is. Well, we can go look for pixel mapping, but we don't do any culling or clipping. + if (g_Config.bSmart2DTexFiltering && !gstate_c.textureIsVideo) { + // We check some common cases for pixel mapping. + // TODO: It's not really optimal that some previous step has removed the triangle strip. + if (vertexCount <= 6 && prim == GE_PRIM_TRIANGLES) { + // It's enough to check UV deltas vs pos deltas between vertex pairs: + // 0-1 1-3 3-2 2-0. Maybe can even skip the last one. Probably some simple math can get us that sequence. + // Unfortunately we need to reverse the previous UV scaling operation. Fortunately these are powers of two + // so the operations are exact. + bool pixelMapped = true; + const u16 *indsIn = (const u16 *)inds; + const float uscale = gstate_c.curTextureWidth; + const float vscale = gstate_c.curTextureHeight; + for (int t = 0; t < vertexCount; t += 3) { + struct { int a; int b; } pairs[] = {{0, 1}, {1, 2}, {2, 0}}; + for (int i = 0; i < ARRAY_SIZE(pairs); i++) { + int a = indsIn[t + pairs[i].a]; + int b = indsIn[t + pairs[i].b]; + float du = fabsf((transformed[a].u - transformed[b].u) * uscale); + float dv = fabsf((transformed[a].v - transformed[b].v) * vscale); + float dx = fabsf(transformed[a].x - transformed[b].x); + float dy = fabsf(transformed[a].y - transformed[b].y); + if (du != dx || dv != dy) { + pixelMapped = false; + } + } + if (!pixelMapped) { + break; + } + } + result->pixelMapped = pixelMapped; + } + } + } else { + // Culling and clipping needs to be done here, it doesn't happen in the shader in the case of software transform. + // However, fast culling should already have taken care of the Z<-W and Z>W culling, but we check for it on a per-triangle + // basis here anyway. + const u16 *indsIn = (const u16 *)inds; u16 *newInds = inds + vertexCount; u16 *indsOut = newInds; @@ -468,28 +525,34 @@ void SoftwareTransform::ProjectClipAndExpand(int prim, int vertexCount, u32 vert outsideZ.resize(vertexCount); // First, check inside/outside directions for each index. + // We are still in clip space here, so we can cull aggressively in Z. + // TODO: This is so cheap now that we can probably avoid the buffer and just do the work below. for (int i = 0; i < vertexCount; ++i) { - float z = truncateToFloat24(transformed[indsIn[i]].z); + float z = transformed[indsIn[i]].z; float w = transformed[indsIn[i]].pos_w; - if (z > w) + if (z > w) { outsideZ[i] = 1; - else if (z < -w) + } else if (z < -w) { outsideZ[i] = -1; - else + } else { outsideZ[i] = 0; + } } - // Now, for each primitive type, throw away the indices if: - // - Depth clamp on, and ALL verts are outside *in the same direction*. - // - Depth clamp off, and ANY vert is outside. - if (prim == GE_PRIM_TRIANGLES && gstate.isDepthClipEnabled()) { + u16 *origInds = inds; + + // TODO: We should either merge the two loops, or avoid the second loop if no culling is needed. + + // Now, for each triangle, throw away the indices if: + // - Depth clip/clamp on, and ALL verts are outside *in the same direction*. + // - Depth clip/clamp off, and ANY vert is outside. + if (gstate.isDepthClipEnabled()) { numTrans = 0; for (int i = 0; i < vertexCount - 2; i += 3) { if (outsideZ[i + 0] != 0 && outsideZ[i + 0] == outsideZ[i + 1] && outsideZ[i + 0] == outsideZ[i + 2]) { // All outside, and all the same direction. Nuke this triangle. continue; } - memcpy(indsOut, indsIn + i, 3 * sizeof(uint16_t)); indsOut += 3; numTrans += 3; @@ -511,46 +574,47 @@ void SoftwareTransform::ProjectClipAndExpand(int prim, int vertexCount, u32 vert inds = newInds; } - } else if (throughmode && g_Config.bSmart2DTexFiltering && !gstate_c.textureIsVideo) { - // We check some common cases for pixel mapping. - // TODO: It's not really optimal that some previous step has removed the triangle strip. - if (vertexCount <= 6 && prim == GE_PRIM_TRIANGLES) { - // It's enough to check UV deltas vs pos deltas between vertex pairs: - // 0-1 1-3 3-2 2-0. Maybe can even skip the last one. Probably some simple math can get us that sequence. - // Unfortunately we need to reverse the previous UV scaling operation. Fortunately these are powers of two - // so the operations are exact. - bool pixelMapped = true; - const u16 *indsIn = (const u16 *)inds; - const float uscale = gstate_c.curTextureWidth; - const float vscale = gstate_c.curTextureHeight; - for (int t = 0; t < vertexCount; t += 3) { - struct { int a; int b; } pairs[] = { {0, 1}, {1, 2}, {2, 0} }; - for (int i = 0; i < ARRAY_SIZE(pairs); i++) { - int a = indsIn[t + pairs[i].a]; - int b = indsIn[t + pairs[i].b]; - float du = fabsf((transformed[a].u - transformed[b].u) * uscale); - float dv = fabsf((transformed[a].v - transformed[b].v) * vscale); - float dx = fabsf(transformed[a].x - transformed[b].x); - float dy = fabsf(transformed[a].y - transformed[b].y); - if (du != dx || dv != dy) { - pixelMapped = false; - } - } - if (!pixelMapped) { - break; + + // Now that we're done culling and generating clipped vertices if needed (not yet implemented), we go ahead and project. + ProjectVertices(transformed, numDecodedVerts); + + // Alright! Now, we can clamp the far plane, if the hardware lacks support for doing it for us. + // Now, this can only be done exactly if all vertices in a triangle are beyond the far plane. If not + // we need to cut it in two parts to clamp accurately. However, in most cases that matter, this is fine. + if (!gstate_c.Use(GPU_USE_DEPTH_CLAMP) && gstate.isDepthClipEnabled() && gstate.getDepthRangeMax() == 0xFFFF) { + for (int i = 0; i < vertexCount - 2; i += 3) { + bool v0InFront = transformed[indsIn[i]].z < 0.0f; + bool v1InFront = transformed[indsIn[i + 1]].z < 0.0f; + bool v2InFront = transformed[indsIn[i + 2]].z < 0.0f; + + bool v0Beyond = transformed[indsIn[i]].z >= 65535.0f; + bool v1Beyond = transformed[indsIn[i + 1]].z >= 65535.0f; + bool v2Beyond = transformed[indsIn[i + 2]].z >= 65535.0f; + + // Clamp the Z when we detect it's safe to do so. + // Fixes the sky problem in Shadow of Destiny (#9545) + if (v0Beyond && v1Beyond && v2Beyond) { + transformed[indsIn[i]].z = 65535.0f; + transformed[indsIn[i + 1]].z = 65535.0f; + transformed[indsIn[i + 2]].z = 65535.0f; + } else if (v0InFront && v1InFront && v2InFront) { + transformed[indsIn[i]].z = 0.0f; + transformed[indsIn[i + 1]].z = 0.0f; + transformed[indsIn[i + 2]].z = 0.0f; } } - result->pixelMapped = pixelMapped; } } + } else { + _dbg_assert_(false); } if (gstate.isModeClear()) { gpuStats.perFrame.numClears++; } - result->action = SW_DRAW_INDEXED; result->drawNumTrans = numTrans; + return SW_DRAW_INDEXED; } bool SoftwareTransform::ExpandRectangles(int vertexCount, int &numDecodedVerts, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int &numTrans, bool throughmode, bool *pixelMappedExactly) const { diff --git a/GPU/Common/SoftwareTransformCommon.h b/GPU/Common/SoftwareTransformCommon.h index 4d83f6ed78..fe8c89cb4c 100644 --- a/GPU/Common/SoftwareTransformCommon.h +++ b/GPU/Common/SoftwareTransformCommon.h @@ -28,13 +28,12 @@ class FramebufferManagerCommon; class TextureCacheCommon; enum SoftwareTransformAction { - SW_NOT_READY, SW_DRAW_INDEXED, SW_CLEAR, + SW_CULLED, // don't draw }; struct SoftwareTransformResult { - SoftwareTransformAction action; u32 color; float depth; @@ -71,11 +70,13 @@ public: SoftwareTransform(SoftwareTransformParams ¶ms) : params_(params) {} // indsSize is in indices, not bytes. - void Transform(const float projMtx[16], Lin::Vec3 vpScale, Lin::Vec3 vpOffset, int prim, u32 vertexType, const DecVtxFormat &decVtxFormat, int &numDecodedVerts, int vertsSize, int vertexCount, u16 *&inds, int indsSize, SoftwareTransformResult *result); + SoftwareTransformAction Transform(const float projMtx[16], Lin::Vec3 vpScale, Lin::Vec3 vpOffset, int prim, u32 vertexType, const DecVtxFormat &decVtxFormat, int &numDecodedVerts, int vertsSize, int vertexCount, u16 *&inds, int indsSize, SoftwareTransformResult *result); protected: // NOTE: The viewport must be up to date! - void ProjectClipAndExpand(int prim, int vertexCount, u32 vertType, u16 *&inds, int indsSize, int &numDecodedVerts, int vertsSize, SoftwareTransformResult *result); + SoftwareTransformAction ProjectClipAndExpand(int prim, int vertexCount, u32 vertType, u16 *&inds, int indsSize, int &numDecodedVerts, int vertsSize, SoftwareTransformResult *result); + + static void ProjectVertices(TransformedVertex *transformed, int vertexCount); bool ExpandRectangles(int vertexCount, int &numDecodedVerts, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int &numTrans, bool throughmode, bool *pixelMappedExactly) const; static bool ExpandLines(int vertexCount, int &numDecodedVerts, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int &numTrans, bool throughmode) ; static bool ExpandPoints(int vertexCount, int &numDecodedVerts, int vertsSize, u16 *&inds, int indsSize, const TransformedVertex *transformed, TransformedVertex *transformedExpanded, int &numTrans, bool throughmode) ; diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 6c576931fe..cbc9064d71 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -423,7 +423,7 @@ void DrawEngineD3D11::Flush() { params.allowSeparateAlphaClear = false; // D3D11 doesn't support separate alpha clears SoftwareTransform swTransform(params); - swTransform.Transform(gstate.projMatrix, gstate.getViewportScale(), gstate.getViewportOffset(), prim, swDec->VertexType(), swDec->GetDecVtxFmt(), numDecodedVerts_, VERTEX_BUFFER_MAX, vertexCount, inds, RemainingIndices(inds), &result); + const SoftwareTransformAction action = swTransform.Transform(gstate.projMatrix, gstate.getViewportScale(), gstate.getViewportOffset(), prim, swDec->VertexType(), swDec->GetDecVtxFmt(), numDecodedVerts_, VERTEX_BUFFER_MAX, vertexCount, inds, RemainingIndices(inds), &result); if (result.setSafeSize) framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight); @@ -439,7 +439,7 @@ void DrawEngineD3D11::Flush() { ApplyDrawState(prim); ApplyDrawStateLate(result.setStencil, result.stencilValue); - if (result.action == SW_DRAW_INDEXED) { + if (action == SW_DRAW_INDEXED) { D3D11VertexShader *vshader; D3D11FragmentShader *fshader; shaderManager_->GetShaders(prim, swDec->VertexType(), &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat, true); @@ -474,7 +474,7 @@ void DrawEngineD3D11::Flush() { pushInds_->EndPush(context_); context_->IASetIndexBuffer(pushInds_->Buf(), DXGI_FORMAT_R16_UINT, iOffset); context_->DrawIndexed(result.drawNumTrans, 0, 0); - } else if (result.action == SW_CLEAR) { + } else if (action == SW_CLEAR) { u32 clearColor = result.color; float clearDepth = result.depth; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 2127c73023..257b51de03 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -371,7 +371,7 @@ void DrawEngineGLES::Flush() { params.allowSeparateAlphaClear = true; SoftwareTransform swTransform(params); - swTransform.Transform(gstate.projMatrix, gstate.getViewportScale(), gstate.getViewportOffset(), prim, swDec->VertexType(), swDec->GetDecVtxFmt(), numDecodedVerts_, VERTEX_BUFFER_MAX, vertexCount, inds, RemainingIndices(inds), &result); + const SoftwareTransformAction action = swTransform.Transform(gstate.projMatrix, gstate.getViewportScale(), gstate.getViewportOffset(), prim, swDec->VertexType(), swDec->GetDecVtxFmt(), numDecodedVerts_, VERTEX_BUFFER_MAX, vertexCount, inds, RemainingIndices(inds), &result); if (result.setSafeSize) framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight); @@ -396,13 +396,13 @@ void DrawEngineGLES::Flush() { goto bail; } - if (result.action == SW_DRAW_INDEXED) { + if (action == SW_DRAW_INDEXED) { vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, numDecodedVerts_ * sizeof(TransformedVertex), 4, &vertexBuffer); indexBufferOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(uint16_t) * result.drawNumTrans, 2, &indexBuffer); render_->DrawIndexed( softwareInputLayout_, vertexBuffer, vertexBufferOffset, indexBuffer, indexBufferOffset, glprim[prim], result.drawNumTrans, GL_UNSIGNED_SHORT); - } else if (result.action == SW_CLEAR) { + } else if (action == SW_CLEAR) { u32 clearColor = result.color; float clearDepth = result.depth; diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 5eff2769ce..ed28edd9c0 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -585,11 +585,11 @@ u32 GPUCommonHW::CheckGPUFeatures() const { features |= GPU_USE_BLEND_MINMAX; } - if (draw_->GetDeviceCaps().maxClipDistances >= 3) { + if (draw_->GetDeviceCaps().maxClipDistances >= 3 && g_Config.bHardwareTransform) { features |= GPU_USE_CLIP_DISTANCE; } - if (draw_->GetDeviceCaps().maxCullDistances >= 1) { + if (draw_->GetDeviceCaps().maxCullDistances >= 1 && g_Config.bHardwareTransform) { features |= GPU_USE_CULL_DISTANCE; } diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 5b13364724..23e5d672a8 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -433,14 +433,14 @@ void DrawEngineVulkan::Flush() { params.everUsedEqualDepth = everUsedEqualDepth_; SoftwareTransform swTransform(params); - swTransform.Transform(gstate.projMatrix, gstate.getViewportScale(), gstate.getViewportOffset(), prim, swDec->VertexType(), swDec->GetDecVtxFmt(), numDecodedVerts_, VERTEX_BUFFER_MAX, vertexCount, inds, RemainingIndices(inds), &result); + const SoftwareTransformAction action = swTransform.Transform(gstate.projMatrix, gstate.getViewportScale(), gstate.getViewportOffset(), prim, swDec->VertexType(), swDec->GetDecVtxFmt(), numDecodedVerts_, VERTEX_BUFFER_MAX, vertexCount, inds, RemainingIndices(inds), &result); if (result.setSafeSize) framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight); // Only here, where we know whether to clear or to draw primitives, should we actually set the current framebuffer! Because that gives use the opportunity // to use a "pre-clear" render pass, for high efficiency on tilers. - if (result.action == SW_DRAW_INDEXED) { + if (action == SW_DRAW_INDEXED) { if (textureNeedsApply) { gstate_c.pixelMapped = result.pixelMapped; gstate_c.dstSquared = false; @@ -528,7 +528,7 @@ void DrawEngineVulkan::Flush() { u32 vbOffset = (uint32_t)pushVertex_->Push(result.drawBuffer, numDecodedVerts_ * sizeof(TransformedVertex), 4, &vbuf); u32 ibOffset = (uint32_t)pushIndex_->Push(inds, sizeof(short) * result.drawNumTrans, 4, &ibuf); renderManager->DrawIndexed(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1); - } else if (result.action == SW_CLEAR) { + } else if (action == SW_CLEAR) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. bool clearColor = gstate.isClearModeColorMask(); bool clearAlpha = gstate.isClearModeAlphaMask(); // and stencil