ppsspp/GPU/Software/BinManager.cpp
2022-01-13 19:07:41 -08:00

176 lines
5.9 KiB
C++

// Copyright (c) 2022- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "GPU/Software/BinManager.h"
#include "GPU/Software/Rasterizer.h"
#include "GPU/Software/RasterizerRectangle.h"
BinManager::BinManager() {
queueRange_.x1 = 0x7FFFFFFF;
queueRange_.y1 = 0x7FFFFFFF;
queueRange_.x2 = 0;
queueRange_.y2 = 0;
}
void BinManager::UpdateState() {
stateIndex_ = (int)states_.size();
states_.resize(states_.size() + 1);
ComputeRasterizerState(&states_.back());
DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0);
DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0);
ScreenCoords screenScissorTL = TransformUnit::DrawingToScreen(scissorTL);
ScreenCoords screenScissorBR = TransformUnit::DrawingToScreen(scissorBR);
scissor_.x1 = screenScissorTL.x;
scissor_.y1 = screenScissorTL.y;
scissor_.x2 = screenScissorBR.x + 15;
scissor_.y2 = screenScissorBR.y + 15;
}
void BinManager::AddTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2) {
Vec2<int> d01((int)v0.screenpos.x - (int)v1.screenpos.x, (int)v0.screenpos.y - (int)v1.screenpos.y);
Vec2<int> d02((int)v0.screenpos.x - (int)v2.screenpos.x, (int)v0.screenpos.y - (int)v2.screenpos.y);
Vec2<int> d12((int)v1.screenpos.x - (int)v2.screenpos.x, (int)v1.screenpos.y - (int)v2.screenpos.y);
// Drop primitives which are not in CCW order by checking the cross product.
if (d01.x * d02.y - d01.y * d02.x < 0)
return;
// If all points have identical coords, we'll have 0 weights and not skip properly, so skip here.
if (d01.x == 0 && d01.y == 0 && d02.x == 0 && d02.y == 0)
return;
// Was it fully outside the scissor?
const BinCoords range = Range(v0, v1, v2);
if (range.x2 < range.x1 || range.y2 < range.y1)
return;
queue_.push_back(BinItem{ BinItemType::TRIANGLE, stateIndex_, range, v0, v1, v2 });
Expand(range);
}
void BinManager::AddClearRect(const VertexData &v0, const VertexData &v1) {
const BinCoords range = Range(v0, v1);
if (range.x2 < range.x1 || range.y2 < range.y1)
return;
queue_.push_back(BinItem{ BinItemType::CLEAR_RECT, stateIndex_, range, v0, v1 });
Expand(range);
}
void BinManager::AddSprite(const VertexData &v0, const VertexData &v1) {
const BinCoords range = Range(v0, v1);
if (range.x2 < range.x1 || range.y2 < range.y1)
return;
queue_.push_back(BinItem{ BinItemType::SPRITE, stateIndex_, range, v0, v1 });
Expand(range);
}
void BinManager::AddLine(const VertexData &v0, const VertexData &v1) {
const BinCoords range = Range(v0, v1);
if (range.x2 < range.x1 || range.y2 < range.y1)
return;
queue_.push_back(BinItem{ BinItemType::LINE, stateIndex_, range, v0, v1 });
Expand(range);
}
void BinManager::AddPoint(const VertexData &v0) {
const BinCoords range = Range(v0);
if (range.x2 < range.x1 || range.y2 < range.y1)
return;
queue_.push_back(BinItem{ BinItemType::POINT, stateIndex_, range, v0 });
Expand(range);
}
void BinManager::Flush() {
for (const BinItem &item : queue_) {
switch (item.type) {
case BinItemType::TRIANGLE:
Rasterizer::DrawTriangle(item.v0, item.v1, item.v2, item.range, states_[item.stateIndex]);
break;
case BinItemType::CLEAR_RECT:
Rasterizer::ClearRectangle(item.v0, item.v1, item.range, states_[item.stateIndex]);
break;
case BinItemType::SPRITE:
Rasterizer::DrawSprite(item.v0, item.v1, item.range, states_[item.stateIndex]);
break;
case BinItemType::LINE:
Rasterizer::DrawLine(item.v0, item.v1, item.range, states_[item.stateIndex]);
break;
case BinItemType::POINT:
Rasterizer::DrawPoint(item.v0, item.range, states_[item.stateIndex]);
break;
}
}
queue_.clear();
states_.clear();
stateIndex_ = -1;
queueRange_.x1 = 0x7FFFFFFF;
queueRange_.y1 = 0x7FFFFFFF;
queueRange_.x2 = 0;
queueRange_.y2 = 0;
}
BinCoords BinManager::Scissor(BinCoords range) {
range.x1 = std::max(range.x1, scissor_.x1);
range.y1 = std::max(range.y1, scissor_.y1);
range.x2 = std::min(range.x2, scissor_.x2);
range.y2 = std::min(range.y2, scissor_.y2);
return range;
}
BinCoords BinManager::Range(const VertexData &v0, const VertexData &v1, const VertexData &v2) {
BinCoords range;
range.x1 = std::min(std::min(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) & ~0xF;
range.y1 = std::min(std::min(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) & ~0xF;
range.x2 = std::max(std::max(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) | 0xF;
range.y2 = std::max(std::max(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) | 0xF;
return Scissor(range);
}
BinCoords BinManager::Range(const VertexData &v0, const VertexData &v1) {
BinCoords range;
range.x1 = std::min(v0.screenpos.x, v1.screenpos.x) & ~0xF;
range.y1 = std::min(v0.screenpos.y, v1.screenpos.y) & ~0xF;
range.x2 = std::max(v0.screenpos.x, v1.screenpos.x) | 0xF;
range.y2 = std::max(v0.screenpos.y, v1.screenpos.y) | 0xF;
return Scissor(range);
}
BinCoords BinManager::Range(const VertexData &v0) {
BinCoords range;
range.x1 = v0.screenpos.x & ~0xF;
range.y1 = v0.screenpos.y & ~0xF;
range.x2 = v0.screenpos.x | 0xF;
range.y2 = v0.screenpos.y | 0xF;
return Scissor(range);
}
void BinManager::Expand(const BinCoords &range) {
queueRange_.x1 = std::min(queueRange_.x1, range.x1);
queueRange_.y1 = std::min(queueRange_.y1, range.y1);
queueRange_.x2 = std::max(queueRange_.x2, range.x2);
queueRange_.y2 = std::max(queueRange_.y2, range.y2);
}