win32: cache GetConsoleMode state for stdout/stderr

GetConsoleMode() can be quite slow and in mpv the mode never changes, so
we can just check it once.

Fixes performance when writing lots of logs to terminal.
This commit is contained in:
Kacper Michajłow 2024-03-16 23:36:46 +01:00
parent 7bdd673a72
commit 8ee25db71f
5 changed files with 45 additions and 33 deletions

View File

@ -304,30 +304,6 @@ static int mp_vfprintf(FILE *stream, const char *format, va_list args)
return vfprintf(stream, format, args);
}
#else
static int mp_check_console(HANDLE wstream)
{
if (wstream != INVALID_HANDLE_VALUE) {
unsigned int filetype = GetFileType(wstream);
if (!((filetype == FILE_TYPE_UNKNOWN) &&
(GetLastError() != ERROR_SUCCESS)))
{
filetype &= ~(FILE_TYPE_REMOTE);
if (filetype == FILE_TYPE_CHAR) {
DWORD ConsoleMode;
int ret = GetConsoleMode(wstream, &ConsoleMode);
if (!(!ret && (GetLastError() == ERROR_INVALID_HANDLE))) {
// This seems to be a console
return 1;
}
}
}
}
return 0;
}
static int mp_vfprintf(FILE *stream, const char *format, va_list args)
{

View File

@ -53,20 +53,16 @@ static void attempt_native_out_vt(HANDLE hOut, DWORD basemode)
SetConsoleMode(hOut, basemode);
}
static bool is_native_out_vt(HANDLE hOut)
{
DWORD cmode;
return GetConsoleMode(hOut, &cmode) &&
(cmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) &&
!(cmode & DISABLE_NEWLINE_AUTO_RETURN);
}
#define hSTDIN GetStdHandle(STD_INPUT_HANDLE)
#define hSTDOUT GetStdHandle(STD_OUTPUT_HANDLE)
#define hSTDERR GetStdHandle(STD_ERROR_HANDLE)
#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
static bool is_console[STDERR_FILENO + 1];
static bool is_vt[STDERR_FILENO + 1];
static short stdoutAttrs = 0; // copied from the screen buffer on init
static const unsigned char ansi2win32[8] = {
0,
@ -94,6 +90,23 @@ static HANDLE death;
static mp_thread input_thread;
static struct input_ctx *input_ctx;
static bool is_native_out_vt_internal(HANDLE hOut)
{
DWORD cmode;
return GetConsoleMode(hOut, &cmode) &&
(cmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) &&
!(cmode & DISABLE_NEWLINE_AUTO_RETURN);
}
static bool is_native_out_vt(HANDLE hOut)
{
if (hOut == hSTDOUT)
return is_vt[STDOUT_FILENO];
if (hOut == hSTDERR)
return is_vt[STDERR_FILENO];
return is_native_out_vt_internal(hOut);
}
void terminal_get_size(int *w, int *h)
{
CONSOLE_SCREEN_BUFFER_INFO cinfo;
@ -360,6 +373,17 @@ static bool is_a_console(HANDLE h)
return GetConsoleMode(h, &(DWORD){0});
}
bool mp_check_console(void *handle)
{
if (handle == hSTDIN)
return is_console[STDIN_FILENO];
if (handle == hSTDOUT)
return is_console[STDOUT_FILENO];
if (handle == hSTDERR)
return is_console[STDERR_FILENO];
return is_a_console(handle);
}
static void reopen_console_handle(DWORD std, int fd, FILE *stream)
{
HANDLE handle = GetStdHandle(std);
@ -419,6 +443,16 @@ void terminal_init(void)
cmode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
attempt_native_out_vt(hSTDOUT, cmode);
attempt_native_out_vt(hSTDERR, cmode);
// Init for mp_check_console(), this never changes during runtime
is_console[STDIN_FILENO] = is_a_console(hSTDIN);
is_console[STDOUT_FILENO] = is_a_console(hSTDOUT);
is_console[STDERR_FILENO] = is_a_console(hSTDERR);
// Init for is_native_out_vt(), this is never disabled/changed during runtime
is_vt[STDOUT_FILENO] = is_native_out_vt_internal(hSTDOUT);
is_vt[STDERR_FILENO] = is_native_out_vt_internal(hSTDERR);
GetConsoleScreenBufferInfo(hSTDOUT, &cinfo);
stdoutAttrs = cinfo.wAttributes;
}

View File

@ -53,6 +53,7 @@ void terminal_get_size2(int *rows, int *cols, int *px_width, int *px_height);
// Windows only.
void mp_write_console_ansi(void *wstream, char *buf);
bool mp_check_console(void *handle);
/* Windows-only function to attach to the parent process's console */
bool terminal_try_attach(void);

View File

@ -4,6 +4,7 @@
#include "options/m_option.h"
#include "options/path.h"
#include "osdep/subprocess.h"
#include "osdep/terminal.h"
#include "test_utils.h"
#ifdef NDEBUG
@ -106,7 +107,8 @@ void mp_msg(struct mp_log *log, int lev, const char *format, ...) {};
int mp_msg_find_level(const char *s) {return 0;};
int mp_msg_level(struct mp_log *log) {return 0;};
void mp_msg_set_max_level(struct mp_log *log, int lev) {};
void mp_write_console_ansi(void) {};
void mp_write_console_ansi(void *wstream, char *buf) {};
bool mp_check_console(void *handle) { return false; };
void mp_set_avdict(AVDictionary **dict, char **kv) {};
struct mp_log *mp_log_new(void *talloc_ctx, struct mp_log *parent,
const char *name) { return NULL; };

View File

@ -52,6 +52,5 @@ void mp_msg(struct mp_log *log, int lev, const char *format, ...)
int mp_msg_find_level(const char *s);
int mp_msg_level(struct mp_log *log);
void mp_msg_set_max_level(struct mp_log *log, int lev);
void mp_write_console_ansi(void);
typedef struct AVDictionary AVDictionary;
void mp_set_avdict(AVDictionary **dict, char **kv);