Use DWMEnableBlurBehindWindow instead of WS_EX_LAYERED

Affects per-pixel transparency

The current method renders to the screen by copying the GLES output to a
DIB for transparency using the CPU instead of rendering directly to the
window via the GPU. This is slower and also forces the window to be borderless
as WS_EX_LAYERED affects the non-client region as well.

This change uses DWMEnableBlurBehindWindow which allows using the standard
glClearColor() background alpha and is also performed through the GPU,
eliminating CPU bottlenecks
This commit is contained in:
Technohacker 2020-05-10 18:10:19 +05:30
parent aa57bb0473
commit 9584f24be5
No known key found for this signature in database
GPG key ID: 2AA912BBD7784F5C
5 changed files with 15 additions and 123 deletions

View file

@ -247,10 +247,6 @@ public:
virtual bool get_window_per_pixel_transparency_enabled() const { return false; }
virtual void set_window_per_pixel_transparency_enabled(bool p_enabled) {}
virtual uint8_t *get_layered_buffer_data() { return NULL; }
virtual Size2 get_layered_buffer_size() { return Size2(0, 0); }
virtual void swap_layered_buffer() {}
virtual void set_ime_active(const bool p_active) {}
virtual void set_ime_position(const Point2 &p_pos) {}
virtual Point2 get_ime_selection() const { return Point2(); }

View file

@ -448,18 +448,7 @@ void RasterizerGLES2::output_lens_distorted_to_screen(RID p_render_target, const
void RasterizerGLES2::end_frame(bool p_swap_buffers) {
if (OS::get_singleton()->is_layered_allowed()) {
if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED)
Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
if (data) {
glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
OS::get_singleton()->swap_layered_buffer();
return;
}
#endif
} else {
if (!OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
//clear alpha
glColorMask(false, false, false, true);
glClearColor(0, 0, 0, 1);

View file

@ -372,18 +372,7 @@ void RasterizerGLES3::output_lens_distorted_to_screen(RID p_render_target, const
void RasterizerGLES3::end_frame(bool p_swap_buffers) {
if (OS::get_singleton()->is_layered_allowed()) {
if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED)
Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
if (data) {
glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
OS::get_singleton()->swap_layered_buffer();
return;
}
#endif
} else {
if (!OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
//clear alpha
glColorMask(false, false, false, true);
glClearColor(0, 0, 0, 1);

View file

@ -974,27 +974,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
maximized = false;
minimized = false;
}
if (is_layered_allowed() && layered_window) {
DeleteObject(hBitmap);
RECT r;
GetWindowRect(hWnd, &r);
dib_size = Size2i(r.right - r.left, r.bottom - r.top);
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = dib_size.x;
bmi.bmiHeader.biHeight = dib_size.y;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
SelectObject(hDC_dib, hBitmap);
ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
}
//return 0; // Jump Back
} break;
@ -2233,77 +2212,29 @@ void OS_Windows::set_window_per_pixel_transparency_enabled(bool p_enabled) {
if (p_enabled) {
set_borderless_window(true);
//enable per-pixel alpha
hDC_dib = CreateCompatibleDC(GetDC(hWnd));
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
RECT r;
GetWindowRect(hWnd, &r);
dib_size = Size2(r.right - r.left, r.bottom - r.top);
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = dib_size.x;
bmi.bmiHeader.biHeight = dib_size.y;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
SelectObject(hDC_dib, hBitmap);
ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
DWM_BLURBEHIND bb = { 0 };
HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
bb.hRgnBlur = hRgn;
bb.fEnable = TRUE;
DwmEnableBlurBehindWindow(hWnd, &bb);
layered_window = true;
} else {
//disable per-pixel alpha
layered_window = false;
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
//cleanup
DeleteObject(hBitmap);
DeleteDC(hDC_dib);
DWM_BLURBEHIND bb = { 0 };
HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
bb.hRgnBlur = hRgn;
bb.fEnable = FALSE;
DwmEnableBlurBehindWindow(hWnd, &bb);
}
}
}
uint8_t *OS_Windows::get_layered_buffer_data() {
return (is_layered_allowed() && layered_window) ? dib_data : NULL;
}
Size2 OS_Windows::get_layered_buffer_size() {
return (is_layered_allowed() && layered_window) ? dib_size : Size2();
}
void OS_Windows::swap_layered_buffer() {
if (is_layered_allowed() && layered_window) {
//premultiply alpha
for (int y = 0; y < dib_size.y; y++) {
for (int x = 0; x < dib_size.x; x++) {
float alpha = (float)dib_data[y * (int)dib_size.x * 4 + x * 4 + 3] / (float)0xFF;
dib_data[y * (int)dib_size.x * 4 + x * 4 + 0] *= alpha;
dib_data[y * (int)dib_size.x * 4 + x * 4 + 1] *= alpha;
dib_data[y * (int)dib_size.x * 4 + x * 4 + 2] *= alpha;
}
}
//swap layered window buffer
POINT ptSrc = { 0, 0 };
SIZE sizeWnd = { (long)dib_size.x, (long)dib_size.y };
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = AC_SRC_ALPHA;
bf.SourceConstantAlpha = 0xFF;
UpdateLayeredWindow(hWnd, NULL, NULL, &sizeWnd, hDC_dib, &ptSrc, 0, &bf, ULW_ALPHA);
}
}
void OS_Windows::set_borderless_window(bool p_borderless) {
if (video_mode.borderless_window == p_borderless)
return;
@ -3477,7 +3408,6 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
drop_events = false;
key_event_pos = 0;
layered_window = false;
hBitmap = NULL;
force_quit = false;
alt_mem = false;
gr_mem = false;
@ -3534,11 +3464,6 @@ OS_Windows::~OS_Windows() {
wintab_WTClose(wtctx);
wtctx = 0;
}
if (is_layered_allowed() && layered_window) {
DeleteObject(hBitmap);
DeleteDC(hDC_dib);
}
#ifdef STDOUT_FILE
fclose(stdo);
#endif

View file

@ -49,6 +49,7 @@
#include "drivers/xaudio2/audio_driver_xaudio2.h"
#endif
#include <dwmapi.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
@ -297,10 +298,6 @@ class OS_Windows : public OS {
HWND hWnd;
Point2 last_pos;
HBITMAP hBitmap; //DIB section for layered window
uint8_t *dib_data;
Size2 dib_size;
HDC hDC_dib;
bool layered_window;
uint32_t move_timer_id;
@ -452,10 +449,6 @@ public:
virtual bool get_window_per_pixel_transparency_enabled() const;
virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
virtual uint8_t *get_layered_buffer_data();
virtual Size2 get_layered_buffer_size();
virtual void swap_layered_buffer();
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
virtual Error close_dynamic_library(void *p_library_handle);
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false);