Global filter not working

That’s not really how the pre_get_posts works. The original parameters you’ve sent to the function are not good.

The only one required is $query.

And the workaround for you will be to run your function in the pre_get_posts function to return an array of post_ID corresponding on the variation you want to include, then in the pre_get_posts function, you will need to $query->set arguments, like this (that’s just an example, you’ve got to find the right ones):

$query->set('post__in', $include_products);
return $query; // at the end of the function

You can read more about the action here

UPDATE
About meta_query argument (also tax_query…) : it always has to be an array of array to enable any relation between different meta_key,
So, your code need to be,

$query->set('meta_query', array($meta_query) );

Or in a different way embed the array of array directly in $meta_query

$meta_query = array(
    array(
        'key' => '_visibility',
        'value' => 'visible',
        'compare' => '='
     )
);

UPDATE 2

You can try to make it easier, using the WP_Query post__in parameter, but I don’t know if it will work as it will depends on the extra woocommerce filter and action to hide product, in that case you might try different priority for the pre_get_posts action :

function tm_remove_product_stock_region($query) {

$product_variable = new WC_Product_Variable();
$product_variations = $product_variable->get_available_variations();

// you don't need to run the function in the loop, the user_region is always the same
/** Get users region. */
$user_region = tm_get_user_region();

$post__in = array();

foreach ( $product_variations as $variation ) {

    /** Get products regions. */
    $product_region = $variation['attributes']['attribute_pa_regions'];

    if ( $user_region === $product_region && ! $variation['is_in_stock'] ) {

        $post__in[] = $variation['product_id'];

    }
    else if ( $user_region === $product_region && $variation['is_in_stock'] ) {

        $post__in[] = $variation['product_id'];

    }

}
if(is_array($post__in) && $post__in != null){
    $query->set('post__in', $post__in);
}
return $query;

}

//Look into apply_filter
add_action('pre_get_posts', 'tm_remove_product_stock_region');

Hope it helps !