Merge pull request #3047 from ReinUsesLisp/clip-control
gl_rasterizer: Emulate viewport flipping with ARB_clip_control
This commit is contained in:
		
						commit
						a8295d2c53
					
				@ -257,10 +257,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
 | 
			
		||||
 | 
			
		||||
        GLShader::MaxwellUniformData ubo{};
 | 
			
		||||
        ubo.SetFromRegs(gpu, stage);
 | 
			
		||||
        ubo.SetFromRegs(gpu);
 | 
			
		||||
        const auto [buffer, offset] =
 | 
			
		||||
            buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment());
 | 
			
		||||
 | 
			
		||||
@ -269,10 +267,11 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
 | 
			
		||||
 | 
			
		||||
        Shader shader{shader_cache.GetStageProgram(program)};
 | 
			
		||||
 | 
			
		||||
        const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
 | 
			
		||||
        SetupDrawConstBuffers(stage_enum, shader);
 | 
			
		||||
        SetupDrawGlobalMemory(stage_enum, shader);
 | 
			
		||||
        const auto texture_buffer_usage{SetupDrawTextures(stage_enum, shader, base_bindings)};
 | 
			
		||||
        // Stage indices are 0 - 5
 | 
			
		||||
        const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1);
 | 
			
		||||
        SetupDrawConstBuffers(stage, shader);
 | 
			
		||||
        SetupDrawGlobalMemory(stage, shader);
 | 
			
		||||
        const auto texture_buffer_usage{SetupDrawTextures(stage, shader, base_bindings)};
 | 
			
		||||
 | 
			
		||||
        const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage};
 | 
			
		||||
        const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
 | 
			
		||||
@ -1055,6 +1054,15 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
 | 
			
		||||
    }
 | 
			
		||||
    state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0;
 | 
			
		||||
    state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0;
 | 
			
		||||
 | 
			
		||||
    bool flip_y = false;
 | 
			
		||||
    if (regs.viewport_transform[0].scale_y < 0.0) {
 | 
			
		||||
        flip_y = !flip_y;
 | 
			
		||||
    }
 | 
			
		||||
    if (regs.screen_y_control.y_negate != 0) {
 | 
			
		||||
        flip_y = !flip_y;
 | 
			
		||||
    }
 | 
			
		||||
    state.clip_control.origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerOpenGL::SyncClipEnabled(
 | 
			
		||||
@ -1077,28 +1085,14 @@ void RasterizerOpenGL::SyncClipCoef() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerOpenGL::SyncCullMode() {
 | 
			
		||||
    auto& maxwell3d = system.GPU().Maxwell3D();
 | 
			
		||||
 | 
			
		||||
    const auto& regs = maxwell3d.regs;
 | 
			
		||||
    const auto& regs = system.GPU().Maxwell3D().regs;
 | 
			
		||||
 | 
			
		||||
    state.cull.enabled = regs.cull.enabled != 0;
 | 
			
		||||
    if (state.cull.enabled) {
 | 
			
		||||
        state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
 | 
			
		||||
        state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face);
 | 
			
		||||
 | 
			
		||||
        const bool flip_triangles{regs.screen_y_control.triangle_rast_flip == 0 ||
 | 
			
		||||
                                  regs.viewport_transform[0].scale_y < 0.0f};
 | 
			
		||||
 | 
			
		||||
        // If the GPU is configured to flip the rasterized triangles, then we need to flip the
 | 
			
		||||
        // notion of front and back. Note: We flip the triangles when the value of the register is 0
 | 
			
		||||
        // because OpenGL already does it for us.
 | 
			
		||||
        if (flip_triangles) {
 | 
			
		||||
            if (state.cull.front_face == GL_CCW)
 | 
			
		||||
                state.cull.front_face = GL_CW;
 | 
			
		||||
            else if (state.cull.front_face == GL_CW)
 | 
			
		||||
                state.cull.front_face = GL_CCW;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerOpenGL::SyncPrimitiveRestart() {
 | 
			
		||||
 | 
			
		||||
@ -1892,10 +1892,6 @@ private:
 | 
			
		||||
    Expression EmitVertex(Operation operation) {
 | 
			
		||||
        ASSERT_MSG(stage == ProgramType::Geometry,
 | 
			
		||||
                   "EmitVertex is expected to be used in a geometry shader.");
 | 
			
		||||
 | 
			
		||||
        // If a geometry shader is attached, it will always flip (it's the last stage before
 | 
			
		||||
        // fragment). For more info about flipping, refer to gl_shader_gen.cpp.
 | 
			
		||||
        code.AddLine("gl_Position.xy *= viewport_flip.xy;");
 | 
			
		||||
        code.AddLine("EmitVertex();");
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
@ -1903,14 +1899,12 @@ private:
 | 
			
		||||
    Expression EndPrimitive(Operation operation) {
 | 
			
		||||
        ASSERT_MSG(stage == ProgramType::Geometry,
 | 
			
		||||
                   "EndPrimitive is expected to be used in a geometry shader.");
 | 
			
		||||
 | 
			
		||||
        code.AddLine("EndPrimitive();");
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Expression YNegate(Operation operation) {
 | 
			
		||||
        // Config pack's third value is Y_NEGATE's state.
 | 
			
		||||
        return {"config_pack[2]", Type::Uint};
 | 
			
		||||
        return {"y_direction", Type::Float};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <u32 element>
 | 
			
		||||
 | 
			
		||||
@ -20,8 +20,7 @@ std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const
 | 
			
		||||
    std::string out = GetCommonDeclarations();
 | 
			
		||||
    out += R"(
 | 
			
		||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
 | 
			
		||||
    vec4 viewport_flip;
 | 
			
		||||
    uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
 | 
			
		||||
    float y_direction;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
)";
 | 
			
		||||
@ -35,23 +34,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
 | 
			
		||||
void main() {
 | 
			
		||||
    execute_vertex();
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
    if (ir_b) {
 | 
			
		||||
        out += "    execute_vertex_b();";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    out += R"(
 | 
			
		||||
 | 
			
		||||
    // Set Position Y direction
 | 
			
		||||
    gl_Position.y *= utof(config_pack[2]);
 | 
			
		||||
    // Check if the flip stage is VertexB
 | 
			
		||||
    // Config pack's second value is flip_stage
 | 
			
		||||
    if (config_pack[1] == 1) {
 | 
			
		||||
        // Viewport can be flipped, which is unsupported by glViewport
 | 
			
		||||
        gl_Position.xy *= viewport_flip.xy;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
    out += "}\n";
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -59,8 +45,7 @@ std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir) {
 | 
			
		||||
    std::string out = GetCommonDeclarations();
 | 
			
		||||
    out += R"(
 | 
			
		||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
 | 
			
		||||
    vec4 viewport_flip;
 | 
			
		||||
    uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
 | 
			
		||||
    float y_direction;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
)";
 | 
			
		||||
@ -87,8 +72,7 @@ layout (location = 6) out vec4 FragColor6;
 | 
			
		||||
layout (location = 7) out vec4 FragColor7;
 | 
			
		||||
 | 
			
		||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
 | 
			
		||||
    vec4 viewport_flip;
 | 
			
		||||
    uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
 | 
			
		||||
    float y_direction;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
@ -40,27 +40,11 @@ void ProgramManager::UpdatePipeline() {
 | 
			
		||||
    old_state = current_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) {
 | 
			
		||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell) {
 | 
			
		||||
    const auto& regs = maxwell.regs;
 | 
			
		||||
    const auto& state = maxwell.state;
 | 
			
		||||
 | 
			
		||||
    // TODO(bunnei): Support more than one viewport
 | 
			
		||||
    viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
 | 
			
		||||
    viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
 | 
			
		||||
 | 
			
		||||
    instance_id = state.current_instance;
 | 
			
		||||
 | 
			
		||||
    // Assign in which stage the position has to be flipped
 | 
			
		||||
    // (the last stage before the fragment shader).
 | 
			
		||||
    constexpr u32 geometry_index = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
 | 
			
		||||
    if (maxwell.regs.shader_config[geometry_index].enable) {
 | 
			
		||||
        flip_stage = geometry_index;
 | 
			
		||||
    } else {
 | 
			
		||||
        flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value.
 | 
			
		||||
    y_direction = regs.screen_y_control.y_negate == 0 ? 1.f : -1.f;
 | 
			
		||||
    y_direction = regs.screen_y_control.y_negate == 0 ? 1.0f : -1.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenGL::GLShader
 | 
			
		||||
 | 
			
		||||
@ -18,17 +18,12 @@ namespace OpenGL::GLShader {
 | 
			
		||||
/// @note Always keep a vec4 at the end. The GL spec is not clear whether the alignment at
 | 
			
		||||
///       the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
 | 
			
		||||
///       Not following that rule will cause problems on some AMD drivers.
 | 
			
		||||
struct MaxwellUniformData {
 | 
			
		||||
    void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell, std::size_t shader_stage);
 | 
			
		||||
struct alignas(16) MaxwellUniformData {
 | 
			
		||||
    void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell);
 | 
			
		||||
 | 
			
		||||
    alignas(16) GLvec4 viewport_flip;
 | 
			
		||||
    struct alignas(16) {
 | 
			
		||||
        GLuint instance_id;
 | 
			
		||||
        GLuint flip_stage;
 | 
			
		||||
        GLfloat y_direction;
 | 
			
		||||
    };
 | 
			
		||||
    GLfloat y_direction;
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect");
 | 
			
		||||
static_assert(sizeof(MaxwellUniformData) == 16, "MaxwellUniformData structure size is incorrect");
 | 
			
		||||
static_assert(sizeof(MaxwellUniformData) < 16384,
 | 
			
		||||
              "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -410,6 +410,12 @@ void OpenGLState::ApplyAlphaTest() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OpenGLState::ApplyClipControl() {
 | 
			
		||||
    if (UpdateValue(cur_state.clip_control.origin, clip_control.origin)) {
 | 
			
		||||
        glClipControl(clip_control.origin, GL_NEGATIVE_ONE_TO_ONE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OpenGLState::ApplyTextures() {
 | 
			
		||||
    if (const auto update = UpdateArray(cur_state.textures, textures)) {
 | 
			
		||||
        glBindTextures(update->first, update->second, textures.data() + update->first);
 | 
			
		||||
@ -453,6 +459,7 @@ void OpenGLState::Apply() {
 | 
			
		||||
    ApplyImages();
 | 
			
		||||
    ApplyPolygonOffset();
 | 
			
		||||
    ApplyAlphaTest();
 | 
			
		||||
    ApplyClipControl();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OpenGLState::EmulateViewportWithScissor() {
 | 
			
		||||
 | 
			
		||||
@ -146,6 +146,10 @@ public:
 | 
			
		||||
 | 
			
		||||
    std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
 | 
			
		||||
 | 
			
		||||
    struct {
 | 
			
		||||
        GLenum origin = GL_LOWER_LEFT;
 | 
			
		||||
    } clip_control;
 | 
			
		||||
 | 
			
		||||
    OpenGLState();
 | 
			
		||||
 | 
			
		||||
    /// Get the currently active OpenGL state
 | 
			
		||||
@ -182,6 +186,7 @@ public:
 | 
			
		||||
    void ApplyDepthClamp();
 | 
			
		||||
    void ApplyPolygonOffset();
 | 
			
		||||
    void ApplyAlphaTest();
 | 
			
		||||
    void ApplyClipControl();
 | 
			
		||||
 | 
			
		||||
    /// Resets any references to the given resource
 | 
			
		||||
    OpenGLState& UnbindTexture(GLuint handle);
 | 
			
		||||
 | 
			
		||||
@ -817,6 +817,9 @@ QStringList GMainWindow::GetUnsupportedGLExtensions() {
 | 
			
		||||
    if (!GLAD_GL_ARB_multi_bind) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!GLAD_GL_ARB_clip_control) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_clip_control"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Extensions required to support some texture formats.
 | 
			
		||||
    if (!GLAD_GL_EXT_texture_compression_s3tc) {
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,7 @@ private:
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
 | 
			
		||||
    std::vector<std::string> unsupported_ext;
 | 
			
		||||
    std::vector<std::string_view> unsupported_ext;
 | 
			
		||||
 | 
			
		||||
    if (!GLAD_GL_ARB_buffer_storage)
 | 
			
		||||
        unsupported_ext.push_back("ARB_buffer_storage");
 | 
			
		||||
@ -62,6 +62,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
 | 
			
		||||
        unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
 | 
			
		||||
    if (!GLAD_GL_ARB_multi_bind)
 | 
			
		||||
        unsupported_ext.push_back("ARB_multi_bind");
 | 
			
		||||
    if (!GLAD_GL_ARB_clip_control)
 | 
			
		||||
        unsupported_ext.push_back("ARB_clip_control");
 | 
			
		||||
 | 
			
		||||
    // Extensions required to support some texture formats.
 | 
			
		||||
    if (!GLAD_GL_EXT_texture_compression_s3tc)
 | 
			
		||||
@ -71,8 +73,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
 | 
			
		||||
    if (!GLAD_GL_ARB_depth_buffer_float)
 | 
			
		||||
        unsupported_ext.push_back("ARB_depth_buffer_float");
 | 
			
		||||
 | 
			
		||||
    for (const std::string& ext : unsupported_ext)
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
 | 
			
		||||
    for (const auto& extension : unsupported_ext)
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", extension);
 | 
			
		||||
 | 
			
		||||
    return unsupported_ext.empty();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user