diff --git a/src/r_poly.cpp b/src/r_poly.cpp index 1cfe5c085..473e51c4d 100644 --- a/src/r_poly.cpp +++ b/src/r_poly.cpp @@ -51,7 +51,6 @@ void RenderPolyBsp::Render() SectorSpriteRanges.resize(numsectors); SortedSprites.clear(); PvsSectors.clear(); - SectorSpriteRanges.clear(); ScreenSprites.clear(); PolyStencilBuffer::Instance()->Clear(viewwidth, viewheight, 0); @@ -76,10 +75,8 @@ void RenderPolyBsp::Render() else RenderNode(nodes + numnodes - 1); // The head node is the last node output. - skydome.Render(worldToClip); - - // Render back to front (we don't have a zbuffer at the moment, sniff!): - if (!r_debug_cull) + // Render front to back using the stencil buffer + if (r_debug_cull) { for (auto it = PvsSectors.rbegin(); it != PvsSectors.rend(); ++it) RenderSubsector(*it); @@ -90,6 +87,8 @@ void RenderPolyBsp::Render() RenderSubsector(*it); } + skydome.Render(worldToClip); + RenderPlayerSprites(); DrawerCommandQueue::WaitForWorkers(); RenderScreenSprites(); // To do: should be called by FSoftwareRenderer::DrawRemainingPlayerSprites instead of here @@ -138,7 +137,7 @@ void RenderPolyBsp::RenderSubsector(subsector_t *sub) FTextureID floorpicnum = frontsector->GetTexture(sector_t::floor); FTexture *floortex = TexMan(floorpicnum); - if (floortex->UseType != FTexture::TEX_Null && floorpicnum != skyflatnum) + if (floortex->UseType != FTexture::TEX_Null) { TriVertex *vertices = PolyVertexBuffer::GetVertices(sub->numlines); if (!vertices) @@ -158,12 +157,16 @@ void RenderPolyBsp::RenderSubsector(subsector_t *sub) else if (fixedcolormap) uniforms.light = 256; uniforms.flags = 0; - PolyTriangleDrawer::draw(uniforms, vertices, sub->numlines, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, floortex); + + bool isSky = floorpicnum == skyflatnum; + if (!isSky) + PolyTriangleDrawer::draw(uniforms, vertices, sub->numlines, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, floortex, 0); + PolyTriangleDrawer::stencil(uniforms, vertices, sub->numlines, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, 0, isSky ? 255 : 254); } FTextureID ceilpicnum = frontsector->GetTexture(sector_t::ceiling); FTexture *ceiltex = TexMan(ceilpicnum); - if (ceiltex->UseType != FTexture::TEX_Null && ceilpicnum != skyflatnum) + if (ceiltex->UseType != FTexture::TEX_Null) { TriVertex *vertices = PolyVertexBuffer::GetVertices(sub->numlines); if (!vertices) @@ -183,7 +186,11 @@ void RenderPolyBsp::RenderSubsector(subsector_t *sub) else if (fixedcolormap) uniforms.light = 256; uniforms.flags = 0; - PolyTriangleDrawer::draw(uniforms, vertices, sub->numlines, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, ceiltex); + + bool isSky = ceilpicnum == skyflatnum; + if (!isSky) + PolyTriangleDrawer::draw(uniforms, vertices, sub->numlines, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, ceiltex, 0); + PolyTriangleDrawer::stencil(uniforms, vertices, sub->numlines, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, 0, isSky ? 255 : 254); } SpriteRange sprites = GetSpritesForSector(sub->sector); @@ -272,23 +279,25 @@ void RenderPolyBsp::AddLine(seg_t *line, sector_t *frontsector) bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; bool bothSkyFloor = frontsector->GetTexture(sector_t::floor) == skyflatnum && backsector->GetTexture(sector_t::floor) == skyflatnum; - if ((topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && !bothSkyCeiling && line->sidedef) + if ((topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && line->sidedef) { wall.SetCoords(line->v1->fPos(), line->v2->fPos(), topceilz1, topfloorz1, topceilz2, topfloorz2); wall.TopZ = topceilz1; wall.BottomZ = topfloorz1; wall.UnpeggedCeil = topceilz1; wall.Texpart = side_t::top; + wall.IsSky = bothSkyCeiling; wall.Render(worldToClip); } - if ((bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && !bothSkyFloor && line->sidedef) + if ((bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef) { wall.SetCoords(line->v1->fPos(), line->v2->fPos(), bottomceilz1, bottomfloorz2, bottomceilz2, bottomfloorz2); wall.TopZ = bottomceilz1; wall.BottomZ = bottomfloorz2; wall.UnpeggedCeil = topceilz1; wall.Texpart = side_t::bottom; + wall.IsSky = bothSkyFloor; wall.Render(worldToClip); } @@ -419,7 +428,7 @@ void RenderPolyBsp::AddSprite(AActor *thing, subsector_t *sub) uniforms.objectToClip = worldToClip; uniforms.light = (uint32_t)((thing->Sector->lightlevel + actualextralight) / 255.0f * 256.0f); uniforms.flags = 0; - PolyTriangleDrawer::draw(uniforms, vertices, 4, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, tex); + PolyTriangleDrawer::draw(uniforms, vertices, 4, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, tex, 254); } void RenderPolyBsp::AddWallSprite(AActor *thing, subsector_t *sub) @@ -1067,7 +1076,10 @@ void RenderPolyWall::Render(const TriMatrix &worldToClip) uniforms.objectToClip = worldToClip; uniforms.light = (uint32_t)(GetLightLevel() / 255.0f * 256.0f); uniforms.flags = 0; - PolyTriangleDrawer::draw(uniforms, vertices, 4, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, tex); + + if (!IsSky) + PolyTriangleDrawer::draw(uniforms, vertices, 4, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, tex, 0); + PolyTriangleDrawer::stencil(uniforms, vertices, 4, TriangleDrawMode::Fan, true, 0, viewwidth, 0, viewheight, 0, IsSky ? 255 : 254); } FTexture *RenderPolyWall::GetTexture() @@ -1395,7 +1407,7 @@ void PolySkyDome::CreateDome() void PolySkyDome::RenderRow(const TriUniforms &uniforms, FTexture *skytex, int row) { - PolyTriangleDrawer::draw(uniforms, &mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], TriangleDrawMode::Strip, false, 0, viewwidth, 0, viewheight, skytex); + PolyTriangleDrawer::draw(uniforms, &mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], TriangleDrawMode::Strip, false, 0, viewwidth, 0, viewheight, skytex, 255); } void PolySkyDome::RenderCapColorRow(const TriUniforms &uniforms, FTexture *skytex, int row, bool bottomCap) @@ -1403,7 +1415,7 @@ void PolySkyDome::RenderCapColorRow(const TriUniforms &uniforms, FTexture *skyte uint32_t solid = skytex->GetSkyCapColor(bottomCap); if (!r_swtruecolor) solid = RGB32k.RGB[(RPART(solid) >> 3)][(GPART(solid) >> 3)][(BPART(solid) >> 3)]; - PolyTriangleDrawer::fill(uniforms, &mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], TriangleDrawMode::Fan, bottomCap, 0, viewwidth, 0, viewheight, solid); + PolyTriangleDrawer::fill(uniforms, &mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], TriangleDrawMode::Fan, bottomCap, 0, viewwidth, 0, viewheight, solid, 255); } void PolySkyDome::Render(const TriMatrix &worldToClip) diff --git a/src/r_poly.h b/src/r_poly.h index 36cddfa30..d4af108e2 100644 --- a/src/r_poly.h +++ b/src/r_poly.h @@ -175,6 +175,7 @@ public: double UnpeggedCeil = 0.0; FSWColormap *Colormap = nullptr; bool Masked = false; + bool IsSky = false; private: FTexture *GetTexture(); diff --git a/src/r_poly_triangle.cpp b/src/r_poly_triangle.cpp index 4b599f931..7fb9aafa4 100644 --- a/src/r_poly_triangle.cpp +++ b/src/r_poly_triangle.cpp @@ -40,31 +40,39 @@ #include #endif -void PolyTriangleDrawer::draw(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, FTexture *texture) +void PolyTriangleDrawer::draw(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, FTexture *texture, int stenciltestvalue) { if (r_swtruecolor) - queue_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, (const uint8_t*)texture->GetPixelsBgra(), texture->GetWidth(), texture->GetHeight(), 0); + queue_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, (const uint8_t*)texture->GetPixelsBgra(), texture->GetWidth(), texture->GetHeight(), 0, stenciltestvalue, stenciltestvalue); else - draw_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, texture->GetPixels(), texture->GetWidth(), texture->GetHeight(), 0, nullptr, &ScreenPolyTriangleDrawer::draw); + draw_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, texture->GetPixels(), texture->GetWidth(), texture->GetHeight(), 0, stenciltestvalue, stenciltestvalue, nullptr, &ScreenPolyTriangleDrawer::draw); } -void PolyTriangleDrawer::fill(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, int solidcolor) +void PolyTriangleDrawer::fill(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, int solidcolor, int stenciltestvalue) { if (r_swtruecolor) - queue_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, nullptr, 0, 0, solidcolor); + queue_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, nullptr, 0, 0, solidcolor, stenciltestvalue, stenciltestvalue); else - draw_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, nullptr, 0, 0, solidcolor, nullptr, &ScreenPolyTriangleDrawer::fill); + draw_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, nullptr, 0, 0, solidcolor, stenciltestvalue, stenciltestvalue, nullptr, &ScreenPolyTriangleDrawer::fill); } -void PolyTriangleDrawer::queue_arrays(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor) +void PolyTriangleDrawer::stencil(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, int stenciltestvalue, int stencilwritevalue) +{ + if (r_swtruecolor) + queue_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, nullptr, 0, 0, 0xbeef, stenciltestvalue, stencilwritevalue); + else + draw_arrays(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, nullptr, 0, 0, 0, stenciltestvalue, stencilwritevalue, nullptr, &ScreenPolyTriangleDrawer::stencil); +} + +void PolyTriangleDrawer::queue_arrays(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor, int stenciltestvalue, int stencilwritevalue) { if (clipright < clipleft || clipleft < 0 || clipright > MAXWIDTH || clipbottom < cliptop || cliptop < 0 || clipbottom > MAXHEIGHT) return; - DrawerCommandQueue::QueueCommand(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, texturePixels, textureWidth, textureHeight, solidcolor); + DrawerCommandQueue::QueueCommand(uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, texturePixels, textureWidth, textureHeight, solidcolor, stenciltestvalue, stencilwritevalue); } -void PolyTriangleDrawer::draw_arrays(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor, DrawerThread *thread, void(*drawfunc)(const ScreenPolyTriangleDrawerArgs *, DrawerThread *)) +void PolyTriangleDrawer::draw_arrays(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor, int stenciltestvalue, int stencilwritevalue, DrawerThread *thread, void(*drawfunc)(const ScreenPolyTriangleDrawerArgs *, DrawerThread *)) { if (vcount < 3) return; @@ -81,6 +89,11 @@ void PolyTriangleDrawer::draw_arrays(const TriUniforms &uniforms, const TriVerte args.textureHeight = textureHeight; args.solidcolor = solidcolor; args.uniforms = &uniforms; + args.stencilTestValue = stenciltestvalue; + args.stencilWriteValue = stencilwritevalue; + args.stencilPitch = PolyStencilBuffer::Instance()->BlockWidth(); + args.stencilValues = PolyStencilBuffer::Instance()->Values(); + args.stencilMasks = PolyStencilBuffer::Instance()->Masks(); TriVertex vert[3]; if (mode == TriangleDrawMode::Normal) @@ -677,6 +690,158 @@ void ScreenPolyTriangleDrawer::fill(const ScreenPolyTriangleDrawerArgs *args, Dr } } +void ScreenPolyTriangleDrawer::stencil(const ScreenPolyTriangleDrawerArgs *args, DrawerThread *thread) +{ + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + int clipleft = args->clipleft; + int clipright = args->clipright; + int cliptop = args->cliptop; + int clipbottom = args->clipbottom; + int solidcolor = args->solidcolor; + uint8_t *stencilValues = args->stencilValues; + uint32_t *stencilMasks = args->stencilMasks; + int stencilPitch = args->stencilPitch; + uint8_t stencilTestValue = args->stencilTestValue; + uint8_t stencilWriteValue = args->stencilWriteValue; + + // 28.4 fixed-point coordinates + const int Y1 = (int)round(16.0f * v1.y); + const int Y2 = (int)round(16.0f * v2.y); + const int Y3 = (int)round(16.0f * v3.y); + + const int X1 = (int)round(16.0f * v1.x); + const int X2 = (int)round(16.0f * v2.x); + const int X3 = (int)round(16.0f * v3.x); + + // Deltas + const int DX12 = X1 - X2; + const int DX23 = X2 - X3; + const int DX31 = X3 - X1; + + const int DY12 = Y1 - Y2; + const int DY23 = Y2 - Y3; + const int DY31 = Y3 - Y1; + + // Fixed-point deltas + const int FDX12 = DX12 << 4; + const int FDX23 = DX23 << 4; + const int FDX31 = DX31 << 4; + + const int FDY12 = DY12 << 4; + const int FDY23 = DY23 << 4; + const int FDY31 = DY31 << 4; + + // Bounding rectangle + int minx = MAX((MIN(MIN(X1, X2), X3) + 0xF) >> 4, clipleft); + int maxx = MIN((MAX(MAX(X1, X2), X3) + 0xF) >> 4, clipright - 1); + int miny = MAX((MIN(MIN(Y1, Y2), Y3) + 0xF) >> 4, cliptop); + int maxy = MIN((MAX(MAX(Y1, Y2), Y3) + 0xF) >> 4, clipbottom - 1); + if (minx >= maxx || miny >= maxy) + return; + + // Block size, standard 8x8 (must be power of two) + const int q = 8; + + // Start in corner of 8x8 block + minx &= ~(q - 1); + miny &= ~(q - 1); + + // Half-edge constants + int C1 = DY12 * X1 - DX12 * Y1; + int C2 = DY23 * X2 - DX23 * Y2; + int C3 = DY31 * X3 - DX31 * Y3; + + // Correct for fill convention + if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++; + if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++; + if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++; + + // Loop through blocks + for (int y = miny; y < maxy; y += q) + { + // Is this row of blocks done by this thread? + if (thread && thread->skipped_by_thread(y / q)) continue; + + for (int x = minx; x < maxx; x += q) + { + // Corners of block + int x0 = x << 4; + int x1 = (x + q - 1) << 4; + int y0 = y << 4; + int y1 = (y + q - 1) << 4; + + // Evaluate half-space functions + bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0; + bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0; + bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0; + bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0; + int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3); + + bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0; + bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0; + bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0; + bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0; + int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3); + + bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0; + bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0; + bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0; + bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0; + int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3); + + // Skip block when outside an edge + if (a == 0x0 || b == 0x0 || c == 0x0) continue; + + // Check if block needs clipping + bool clipneeded = clipleft > x || clipright < (x + q) || cliptop > y || clipbottom < (y + q); + + PolyStencilBlock stencil(x / 8 + y / 8 * stencilPitch, stencilValues, stencilMasks); + + // Accept whole block when totally covered + if (a == 0xF && b == 0xF && c == 0xF && !clipneeded && stencil.IsSingleValue()) + { + // Reject whole block if the stencil test fails + if (stencil.Get(0, 0) != stencilTestValue) + continue; + stencil.Clear(stencilWriteValue); + } + else // Partially covered block + { + int CY1 = C1 + DX12 * y0 - DY12 * x0; + int CY2 = C2 + DX23 * y0 - DY23 * x0; + int CY3 = C3 + DX31 * y0 - DY31 * x0; + + for (int iy = 0; iy < q; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool visible = (ix + x >= clipleft) && (ix + x < clipright) && (cliptop <= y + iy) && (clipbottom > y + iy); + + if (CX1 > 0 && CX2 > 0 && CX3 > 0 && visible && stencil.Get(ix, iy) == stencilTestValue) + { + stencil.Set(ix, iy, stencilWriteValue); + } + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + } + } + } + } +} + void ScreenPolyTriangleDrawer::draw32(const ScreenPolyTriangleDrawerArgs *args, DrawerThread *thread) { uint32_t *dest = (uint32_t *)args->dest; @@ -692,6 +857,10 @@ void ScreenPolyTriangleDrawer::draw32(const ScreenPolyTriangleDrawerArgs *args, int textureWidth = args->textureWidth; int textureHeight = args->textureHeight; uint32_t light = args->uniforms->light; + uint8_t *stencilValues = args->stencilValues; + uint32_t *stencilMasks = args->stencilMasks; + int stencilPitch = args->stencilPitch; + uint8_t stencilTestValue = args->stencilTestValue; // 28.4 fixed-point coordinates const int Y1 = (int)round(16.0f * v1.y); @@ -831,9 +1000,15 @@ void ScreenPolyTriangleDrawer::draw32(const ScreenPolyTriangleDrawerArgs *args, uint32_t *buffer = dest; + PolyStencilBlock stencil(x / 8 + y / 8 * stencilPitch, stencilValues, stencilMasks); + // Accept whole block when totally covered - if (a == 0xF && b == 0xF && c == 0xF && !clipneeded) + if (a == 0xF && b == 0xF && c == 0xF && !clipneeded && stencil.IsSingleValue()) { + // Reject whole block if the stencil test fails + if (stencil.Get(0, 0) != stencilTestValue) + continue; + for (int iy = 0; iy < q; iy++) { uint32_t varying[TriVertex::NumVarying], varyingStep[TriVertex::NumVarying]; @@ -922,11 +1097,11 @@ void ScreenPolyTriangleDrawer::draw32(const ScreenPolyTriangleDrawerArgs *args, varyingStep[i] = (uint32_t)(step * 0x100000000LL); } - for (int ix = x; ix < x + q; ix++) + for (int ix = 0; ix < q; ix++) { - bool visible = ix >= clipleft && ix < clipright && (cliptop <= y + iy) && (clipbottom > y + iy); + bool visible = (ix + x >= clipleft) && (ix + x < clipright) && (cliptop <= y + iy) && (clipbottom > y + iy); - if (CX1 > 0 && CX2 > 0 && CX3 > 0 && visible) + if (CX1 > 0 && CX2 > 0 && CX3 > 0 && visible && stencil.Get(ix, iy) == stencilTestValue) { uint32_t ufrac = varying[0]; uint32_t vfrac = varying[1]; @@ -942,7 +1117,7 @@ void ScreenPolyTriangleDrawer::draw32(const ScreenPolyTriangleDrawerArgs *args, uint32_t fg_alpha = APART(fg); if (fg_alpha > 127) - buffer[ix] = 0xff000000 | (fg_red << 16) | (fg_green << 8) | fg_blue; + buffer[ix + x] = 0xff000000 | (fg_red << 16) | (fg_green << 8) | fg_blue; } for (int i = 0; i < TriVertex::NumVarying; i++) @@ -976,6 +1151,10 @@ void ScreenPolyTriangleDrawer::fill32(const ScreenPolyTriangleDrawerArgs *args, int cliptop = args->cliptop; int clipbottom = args->clipbottom; int solidcolor = args->solidcolor; + uint8_t *stencilValues = args->stencilValues; + uint32_t *stencilMasks = args->stencilMasks; + int stencilPitch = args->stencilPitch; + uint8_t stencilTestValue = args->stencilTestValue; // 28.4 fixed-point coordinates const int Y1 = (int)round(16.0f * v1.y); @@ -1072,9 +1251,15 @@ void ScreenPolyTriangleDrawer::fill32(const ScreenPolyTriangleDrawerArgs *args, uint32_t *buffer = dest; + PolyStencilBlock stencil(x / 8 + y / 8 * stencilPitch, stencilValues, stencilMasks); + // Accept whole block when totally covered - if (a == 0xF && b == 0xF && c == 0xF && !clipneeded) + if (a == 0xF && b == 0xF && c == 0xF && !clipneeded && stencil.IsSingleValue()) { + // Reject whole block if the stencil test fails + if (stencil.Get(0, 0) != stencilTestValue) + continue; + for (int iy = 0; iy < q; iy++) { for (int ix = x; ix < x + q; ix++) @@ -1097,13 +1282,13 @@ void ScreenPolyTriangleDrawer::fill32(const ScreenPolyTriangleDrawerArgs *args, int CX2 = CY2; int CX3 = CY3; - for (int ix = x; ix < x + q; ix++) + for (int ix = 0; ix < q; ix++) { - bool visible = ix >= clipleft && ix < clipright && (cliptop <= y + iy) && (clipbottom > y + iy); + bool visible = (ix + x >= clipleft) && (ix + x < clipright) && (cliptop <= y + iy) && (clipbottom > y + iy); - if (CX1 > 0 && CX2 > 0 && CX3 > 0 && visible) + if (CX1 > 0 && CX2 > 0 && CX3 > 0 && visible && stencil.Get(ix, iy) == stencilTestValue) { - buffer[ix] = solidcolor; + buffer[ix + x] = solidcolor; } CX1 -= FDY12; @@ -1138,8 +1323,8 @@ float ScreenPolyTriangleDrawer::grady(float x0, float y0, float x1, float y1, fl ///////////////////////////////////////////////////////////////////////////// -DrawPolyTrianglesCommand::DrawPolyTrianglesCommand(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor) - : uniforms(uniforms), vinput(vinput), vcount(vcount), mode(mode), ccw(ccw), clipleft(clipleft), clipright(clipright), cliptop(cliptop), clipbottom(clipbottom), texturePixels(texturePixels), textureWidth(textureWidth), textureHeight(textureHeight), solidcolor(solidcolor) +DrawPolyTrianglesCommand::DrawPolyTrianglesCommand(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor, int stenciltestvalue, int stencilwritevalue) + : uniforms(uniforms), vinput(vinput), vcount(vcount), mode(mode), ccw(ccw), clipleft(clipleft), clipright(clipright), cliptop(cliptop), clipbottom(clipbottom), texturePixels(texturePixels), textureWidth(textureWidth), textureHeight(textureHeight), solidcolor(solidcolor), stenciltestvalue(stenciltestvalue), stencilwritevalue(stencilwritevalue) { } @@ -1149,7 +1334,8 @@ void DrawPolyTrianglesCommand::Execute(DrawerThread *thread) uniforms, vinput, vcount, mode, ccw, clipleft, clipright, cliptop, clipbottom, texturePixels, textureWidth, textureHeight, solidcolor, - thread, texturePixels ? ScreenPolyTriangleDrawer::draw32 : ScreenPolyTriangleDrawer::fill32); + stenciltestvalue, stencilwritevalue, + thread, texturePixels ? ScreenPolyTriangleDrawer::draw32 : solidcolor != 0xbeef ? ScreenPolyTriangleDrawer::fill32 : ScreenPolyTriangleDrawer::stencil); } FString DrawPolyTrianglesCommand::DebugInfo() diff --git a/src/r_poly_triangle.h b/src/r_poly_triangle.h index f18361caa..931300169 100644 --- a/src/r_poly_triangle.h +++ b/src/r_poly_triangle.h @@ -31,17 +31,18 @@ struct ScreenPolyTriangleDrawerArgs; class PolyTriangleDrawer { public: - static void draw(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, FTexture *texture); - static void fill(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, int solidcolor); + static void draw(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, FTexture *texture, int stenciltestvalue); + static void fill(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, int solidcolor, int stenciltestvalue); + static void stencil(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, int stenciltestvalue, int stencilwritevalue); private: static TriVertex shade_vertex(const TriUniforms &uniforms, TriVertex v); - static void draw_arrays(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor, DrawerThread *thread, void(*drawfunc)(const ScreenPolyTriangleDrawerArgs *, DrawerThread *)); + static void draw_arrays(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor, int stenciltestvalue, int stencilwritevalue, DrawerThread *thread, void(*drawfunc)(const ScreenPolyTriangleDrawerArgs *, DrawerThread *)); static void draw_shaded_triangle(const TriVertex *vertices, bool ccw, ScreenPolyTriangleDrawerArgs *args, DrawerThread *thread, void(*drawfunc)(const ScreenPolyTriangleDrawerArgs *, DrawerThread *)); static bool cullhalfspace(float clipdistance1, float clipdistance2, float &t1, float &t2); static void clipedge(const TriVertex *verts, TriVertex *clippedvert, int &numclipvert); - static void queue_arrays(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor); + static void queue_arrays(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor, int stenciltestvalue, int stencilwritevalue); enum { max_additional_vertices = 16 }; @@ -75,16 +76,14 @@ public: int leveloffset = 0; for (int i = 1; i < 4; i++) { - int iy = i + 3; - x >>= 1; y >>= 1; bool same = - Values[(x << i) + (y << iy)] != value || - Values[((x + 1) << i) + (y << iy)] != value || - Values[(x << i) + ((y + 1) << iy)] != value || - Values[((x + 1) << i) + ((y + 1) << iy)] != value; + Values[(x << i) + (y << i) * 8] != value || + Values[((x + 1) << i) + (y << i) * 8] != value || + Values[(x << i) + ((y + 1) << i) * 8] != value || + Values[((x + 1) << i) + ((y + 1) << i) * 8] != value; int levelbit = 1 << (leveloffset + x + y * (8 >> i)); @@ -92,6 +91,8 @@ public: ValueMask = ValueMask & ~levelbit; else ValueMask = ValueMask | levelbit; + + leveloffset += (8 >> leveloffset) * (8 >> leveloffset); } if (Values[0] != value || Values[4] != value || Values[4 * 8] != value || Values[4 * 8 + 4] != value) @@ -114,6 +115,11 @@ public: ValueMask = 0xffffffff; } + bool IsSingleValue() const + { + return ValueMask == 0xffffffff; + } + private: uint8_t *Values; // [8 * 8]; uint32_t &ValueMask; // 4 * 4 + 2 * 2 + 1 bits indicating is Values are the same @@ -175,6 +181,11 @@ struct ScreenPolyTriangleDrawerArgs int textureHeight; int solidcolor; const TriUniforms *uniforms; + uint8_t *stencilValues; + uint32_t *stencilMasks; + int stencilPitch; + uint8_t stencilTestValue; + uint8_t stencilWriteValue; }; class ScreenPolyTriangleDrawer @@ -183,6 +194,8 @@ public: static void draw(const ScreenPolyTriangleDrawerArgs *args, DrawerThread *thread); static void fill(const ScreenPolyTriangleDrawerArgs *args, DrawerThread *thread); + static void stencil(const ScreenPolyTriangleDrawerArgs *args, DrawerThread *thread); + static void draw32(const ScreenPolyTriangleDrawerArgs *args, DrawerThread *thread); static void fill32(const ScreenPolyTriangleDrawerArgs *args, DrawerThread *thread); @@ -194,7 +207,7 @@ private: class DrawPolyTrianglesCommand : public DrawerCommand { public: - DrawPolyTrianglesCommand(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor); + DrawPolyTrianglesCommand(const TriUniforms &uniforms, const TriVertex *vinput, int vcount, TriangleDrawMode mode, bool ccw, int clipleft, int clipright, int cliptop, int clipbottom, const uint8_t *texturePixels, int textureWidth, int textureHeight, int solidcolor, int stenciltestvalue, int stencilwritevalue); void Execute(DrawerThread *thread) override; FString DebugInfo() override; @@ -213,6 +226,8 @@ private: int textureWidth; int textureHeight; int solidcolor; + int stenciltestvalue; + int stencilwritevalue; }; #endif