GPU/Scissor: Implemented scissor testing in the hw renderer.
We're doing this in the shader since it's not possible to provide the Exclude functionality using glScissor.
This commit is contained in:
parent
232594bee4
commit
3ec4a6d856
3 changed files with 61 additions and 3 deletions
|
@ -150,6 +150,8 @@ void RasterizerOpenGL::DrawTriangles() {
|
|||
SyncFramebuffer();
|
||||
SyncDrawState();
|
||||
|
||||
SyncScissorTest();
|
||||
|
||||
if (state.draw.shader_dirty) {
|
||||
SetShader();
|
||||
state.draw.shader_dirty = false;
|
||||
|
@ -222,6 +224,11 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
|
|||
SyncDepthTest();
|
||||
break;
|
||||
|
||||
// Scissor test
|
||||
case PICA_REG_INDEX(scissor_test.mode):
|
||||
state.draw.shader_dirty = true;
|
||||
break;
|
||||
|
||||
// Logic op
|
||||
case PICA_REG_INDEX(output_merger.logic_op):
|
||||
SyncLogicOp();
|
||||
|
@ -499,6 +506,7 @@ void RasterizerOpenGL::SetShader() {
|
|||
// Update uniforms
|
||||
SyncAlphaTest();
|
||||
SyncCombinerColor();
|
||||
SyncScissorTest();
|
||||
auto& tev_stages = Pica::g_state.regs.GetTevStages();
|
||||
for (int index = 0; index < tev_stages.size(); ++index)
|
||||
SyncTevConstColor(index, tev_stages[index]);
|
||||
|
@ -657,6 +665,32 @@ void RasterizerOpenGL::SyncDepthTest() {
|
|||
state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncScissorTest() {
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
|
||||
GLsizei viewport_height = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_y).ToFloat32() * 2;
|
||||
|
||||
GLsizei viewport_corner_y = -(GLsizei)static_cast<float>(regs.viewport_corner.y)
|
||||
+ regs.framebuffer.GetHeight() - viewport_height;
|
||||
|
||||
// OpenGL uses different y coordinates, so negate corner offset and flip origin
|
||||
GLint scissor_bottom = viewport_height - (GLsizei)regs.scissor_test.bottom + viewport_corner_y;
|
||||
|
||||
GLint scissor_top = viewport_height - (GLsizei)regs.scissor_test.top - 1;
|
||||
|
||||
if (uniform_block_data.data.scissor_right != regs.scissor_test.right ||
|
||||
uniform_block_data.data.scissor_bottom != scissor_bottom ||
|
||||
uniform_block_data.data.scissor_left != regs.scissor_test.left + 1 ||
|
||||
uniform_block_data.data.scissor_top != scissor_top) {
|
||||
|
||||
uniform_block_data.data.scissor_right = regs.scissor_test.right;
|
||||
uniform_block_data.data.scissor_bottom = scissor_bottom;
|
||||
uniform_block_data.data.scissor_left = regs.scissor_test.left + 1;
|
||||
uniform_block_data.data.scissor_top = scissor_top;
|
||||
uniform_block_data.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncCombinerColor() {
|
||||
auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
|
||||
if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) {
|
||||
|
|
|
@ -36,6 +36,8 @@ struct PicaShaderConfig {
|
|||
res.alpha_test_func = regs.output_merger.alpha_test.enable ?
|
||||
regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always;
|
||||
|
||||
res.scissor_test_mode = regs.scissor_test.mode;
|
||||
|
||||
// Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling
|
||||
// the GetTevStages() function) because BitField explicitly disables copies.
|
||||
|
||||
|
@ -87,6 +89,7 @@ struct PicaShaderConfig {
|
|||
};
|
||||
|
||||
Pica::Regs::CompareFunc alpha_test_func;
|
||||
Pica::Regs::ScissorMode scissor_test_mode;
|
||||
std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
|
||||
u8 combiner_buffer_input;
|
||||
};
|
||||
|
@ -191,16 +194,19 @@ private:
|
|||
GLfloat tex_coord2[2];
|
||||
};
|
||||
|
||||
/// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned
|
||||
/// Uniform structure for the Uniform Buffer Object, all members must be aligned as per the OpenGL std140 layout specification
|
||||
struct UniformData {
|
||||
// A vec4 color for each of the six tev stages
|
||||
std::array<GLfloat, 4> const_color[6];
|
||||
std::array<GLfloat, 4> tev_combiner_buffer_color;
|
||||
GLint alphatest_ref;
|
||||
INSERT_PADDING_BYTES(12);
|
||||
GLint scissor_right;
|
||||
GLint scissor_bottom;
|
||||
GLint scissor_left;
|
||||
GLint scissor_top;
|
||||
};
|
||||
|
||||
static_assert(sizeof(UniformData) == 0x80, "The size of the UniformData structure has changed, update the structure in the shader");
|
||||
static_assert(sizeof(UniformData) == 0x84, "The size of the UniformData structure has changed, update the structure in the shader");
|
||||
static_assert(sizeof(UniformData) < 16000, "UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
/// Reconfigure the OpenGL color texture to use the given format and dimensions
|
||||
|
@ -239,6 +245,9 @@ private:
|
|||
/// Syncs the depth test states to match the PICA register
|
||||
void SyncDepthTest();
|
||||
|
||||
/// Syncs the scissor test state to match the PICA register
|
||||
void SyncScissorTest();
|
||||
|
||||
/// Syncs the TEV constant color to match the PICA register
|
||||
void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage);
|
||||
|
||||
|
|
|
@ -327,6 +327,7 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) {
|
|||
|
||||
in vec4 primary_color;
|
||||
in vec2 texcoord[3];
|
||||
in vec4 gl_FragCoord;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
|
@ -334,6 +335,10 @@ layout (std140) uniform shader_data {
|
|||
vec4 const_color[NUM_TEV_STAGES];
|
||||
vec4 tev_combiner_buffer_color;
|
||||
int alphatest_ref;
|
||||
int scissor_right;
|
||||
int scissor_bottom;
|
||||
int scissor_left;
|
||||
int scissor_top;
|
||||
};
|
||||
|
||||
uniform sampler2D tex[3];
|
||||
|
@ -341,6 +346,16 @@ uniform sampler2D tex[3];
|
|||
void main() {
|
||||
)";
|
||||
|
||||
// Append the scissor test
|
||||
if (config.scissor_test_mode == Regs::ScissorMode::Include || config.scissor_test_mode == Regs::ScissorMode::Exclude) {
|
||||
out += "if (scissor_left <= scissor_right || scissor_top >= scissor_bottom) discard;\n";
|
||||
out += "if (";
|
||||
// Negate the condition if we have to keep only the pixels outside the scissor box
|
||||
if (config.scissor_test_mode == Regs::ScissorMode::Include)
|
||||
out += "!";
|
||||
out += "(gl_FragCoord.x >= scissor_right && gl_FragCoord.x <= scissor_left && gl_FragCoord.y >= scissor_top && gl_FragCoord.y <= scissor_bottom)) discard;\n";
|
||||
}
|
||||
|
||||
// Do not do any sort of processing if it's obvious we're not going to pass the alpha test
|
||||
if (config.alpha_test_func == Regs::CompareFunc::Never) {
|
||||
out += "discard; }";
|
||||
|
|
Loading…
Reference in a new issue