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:
Subv 2015-12-02 19:36:55 -05:00
parent 232594bee4
commit 3ec4a6d856
3 changed files with 61 additions and 3 deletions

View file

@ -150,6 +150,8 @@ void RasterizerOpenGL::DrawTriangles() {
SyncFramebuffer(); SyncFramebuffer();
SyncDrawState(); SyncDrawState();
SyncScissorTest();
if (state.draw.shader_dirty) { if (state.draw.shader_dirty) {
SetShader(); SetShader();
state.draw.shader_dirty = false; state.draw.shader_dirty = false;
@ -222,6 +224,11 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
SyncDepthTest(); SyncDepthTest();
break; break;
// Scissor test
case PICA_REG_INDEX(scissor_test.mode):
state.draw.shader_dirty = true;
break;
// Logic op // Logic op
case PICA_REG_INDEX(output_merger.logic_op): case PICA_REG_INDEX(output_merger.logic_op):
SyncLogicOp(); SyncLogicOp();
@ -499,6 +506,7 @@ void RasterizerOpenGL::SetShader() {
// Update uniforms // Update uniforms
SyncAlphaTest(); SyncAlphaTest();
SyncCombinerColor(); SyncCombinerColor();
SyncScissorTest();
auto& tev_stages = Pica::g_state.regs.GetTevStages(); auto& tev_stages = Pica::g_state.regs.GetTevStages();
for (int index = 0; index < tev_stages.size(); ++index) for (int index = 0; index < tev_stages.size(); ++index)
SyncTevConstColor(index, tev_stages[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; 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() { void RasterizerOpenGL::SyncCombinerColor() {
auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); 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) { if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) {

View file

@ -36,6 +36,8 @@ struct PicaShaderConfig {
res.alpha_test_func = regs.output_merger.alpha_test.enable ? res.alpha_test_func = regs.output_merger.alpha_test.enable ?
regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; 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 // Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling
// the GetTevStages() function) because BitField explicitly disables copies. // the GetTevStages() function) because BitField explicitly disables copies.
@ -87,6 +89,7 @@ struct PicaShaderConfig {
}; };
Pica::Regs::CompareFunc alpha_test_func; Pica::Regs::CompareFunc alpha_test_func;
Pica::Regs::ScissorMode scissor_test_mode;
std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {}; std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
u8 combiner_buffer_input; u8 combiner_buffer_input;
}; };
@ -191,16 +194,19 @@ private:
GLfloat tex_coord2[2]; 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 { struct UniformData {
// A vec4 color for each of the six tev stages // A vec4 color for each of the six tev stages
std::array<GLfloat, 4> const_color[6]; std::array<GLfloat, 4> const_color[6];
std::array<GLfloat, 4> tev_combiner_buffer_color; std::array<GLfloat, 4> tev_combiner_buffer_color;
GLint alphatest_ref; 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"); 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 /// 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 /// Syncs the depth test states to match the PICA register
void SyncDepthTest(); void SyncDepthTest();
/// Syncs the scissor test state to match the PICA register
void SyncScissorTest();
/// Syncs the TEV constant color to match the PICA register /// Syncs the TEV constant color to match the PICA register
void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage); void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage);

View file

@ -327,6 +327,7 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) {
in vec4 primary_color; in vec4 primary_color;
in vec2 texcoord[3]; in vec2 texcoord[3];
in vec4 gl_FragCoord;
out vec4 color; out vec4 color;
@ -334,6 +335,10 @@ layout (std140) uniform shader_data {
vec4 const_color[NUM_TEV_STAGES]; vec4 const_color[NUM_TEV_STAGES];
vec4 tev_combiner_buffer_color; vec4 tev_combiner_buffer_color;
int alphatest_ref; int alphatest_ref;
int scissor_right;
int scissor_bottom;
int scissor_left;
int scissor_top;
}; };
uniform sampler2D tex[3]; uniform sampler2D tex[3];
@ -341,6 +346,16 @@ uniform sampler2D tex[3];
void main() { 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 // 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) { if (config.alpha_test_func == Regs::CompareFunc::Never) {
out += "discard; }"; out += "discard; }";