1
1

Fix #106264: Color picker broken with Wayland & AMD GPU

- Use off-screen drawing when reading from the front-buffer isn't
  supported.

- Add a capabilities flag for reading the front-buffer which is always
  disabled on WAYLAND.

- Add GPU_offscreen_read_pixels_region, used for reading a sub-region of
  an off-screen buffer - use for color-picking a single pixel.

Fix from [0] with conflicts resolved, worked around [1] not being
applied by checking if Wayland is in use via WM_ghost_backend().

[0]: 6cc2c16d06
[1]: 4e51008a82
This commit is contained in:
2023-04-21 21:31:26 +10:00
parent 0a36e78fb4
commit 93dac7da2c
7 changed files with 103 additions and 27 deletions

View File

@@ -361,7 +361,13 @@ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3])
if (win) {
/* Fallback to simple opengl picker. */
WM_window_pixel_sample_read(wm, win, mval, r_col);
if (WM_capabilities_flag() & WM_CAPABILITY_GPU_FRONT_BUFFER_READ) {
WM_window_pixels_read_sample(wm, win, mval, r_col);
}
else {
WM_window_pixels_read_sample_offscreen(C, win, mval, r_col);
}
IMB_colormanagement_display_to_scene_linear_v3(r_col, display);
}
else {

View File

@@ -56,12 +56,15 @@ static int screenshot_data_create(bContext *C, wmOperator *op, ScrArea *area)
{
int dumprect_size[2];
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
/* do redraw so we don't show popups/menus */
WM_redraw_windows(C);
uint *dumprect = WM_window_pixels_read_offscreen(C, win, dumprect_size);
uint *dumprect = (WM_capabilities_flag() & WM_CAPABILITY_GPU_FRONT_BUFFER_READ) ?
WM_window_pixels_read(wm, win, dumprect_size) :
WM_window_pixels_read_offscreen(C, win, dumprect_size);
if (dumprect) {
ScreenshotData *scd = MEM_callocN(sizeof(ScreenshotData), "screenshot");

View File

@@ -286,6 +286,11 @@ void GPU_offscreen_free(GPUOffScreen *ofs);
void GPU_offscreen_bind(GPUOffScreen *ofs, bool save);
void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore);
void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat format, void *pixels);
/**
* A version of #GPU_offscreen_read_pixels that reads into a region.
*/
void GPU_offscreen_read_pixels_region(
GPUOffScreen *offscreen, eGPUDataFormat data_format, int x, int y, int w, int h, void *r_data);
void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y);
int GPU_offscreen_width(const GPUOffScreen *ofs);
int GPU_offscreen_height(const GPUOffScreen *ofs);

View File

@@ -701,15 +701,26 @@ void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y)
ofs_fb->blit_to(GPU_COLOR_BIT, 0, ctx->active_fb, 0, x, y);
}
void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat format, void *pixels)
void GPU_offscreen_read_pixels_region(
GPUOffScreen *ofs, eGPUDataFormat format, int x, int y, int w, int h, void *r_pixels)
{
BLI_assert(ELEM(format, GPU_DATA_UBYTE, GPU_DATA_FLOAT));
BLI_assert(x >= 0 && y >= 0 && w > 0 && h > 0);
BLI_assert(x + w <= GPU_texture_width(ofs->color));
BLI_assert(y + h <= GPU_texture_height(ofs->color));
GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs);
GPU_framebuffer_read_color(ofs_fb, x, y, w, h, 4, 0, format, r_pixels);
}
void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat format, void *r_pixels)
{
BLI_assert(ELEM(format, GPU_DATA_UBYTE, GPU_DATA_FLOAT));
const int w = GPU_texture_width(ofs->color);
const int h = GPU_texture_height(ofs->color);
GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs);
GPU_framebuffer_read_color(ofs_fb, 0, 0, w, h, 4, 0, format, pixels);
GPU_offscreen_read_pixels_region(ofs, format, 0, 0, w, h, r_pixels);
}
int GPU_offscreen_width(const GPUOffScreen *ofs)

View File

@@ -129,6 +129,10 @@ typedef enum eWM_CapabilitiesFlag {
WM_CAPABILITY_CURSOR_WARP = (1 << 0),
/** Ability to access window positions & move them. */
WM_CAPABILITY_WINDOW_POSITION = (1 << 1),
/**
* Reading from the back-buffer is supported.
*/
WM_CAPABILITY_GPU_FRONT_BUFFER_READ = (1 << 2),
} eWM_CapabilitiesFlag;
eWM_CapabilitiesFlag WM_capabilities_flag(void);
@@ -159,6 +163,11 @@ void WM_window_pixel_sample_read(const wmWindowManager *wm,
* the front-buffer state to be invalid under some EGL configurations.
*/
uint *WM_window_pixels_read(struct wmWindowManager *wm, struct wmWindow *win, int r_size[2]);
void WM_window_pixels_read_sample(const wmWindowManager *wm,
const wmWindow *win,
const int pos[2],
float r_col[3]);
/**
* Draw the window & read pixels from an off-screen buffer (slower than #WM_window_pixels_read).
*
@@ -166,6 +175,10 @@ uint *WM_window_pixels_read(struct wmWindowManager *wm, struct wmWindow *win, in
* (see in-line code comments for details).
*/
uint *WM_window_pixels_read_offscreen(struct bContext *C, struct wmWindow *win, int r_size[2]);
bool WM_window_pixels_read_sample_offscreen(struct bContext *C,
struct wmWindow *win,
const int pos[2],
float r_col[3]);
/**
* Support for native pixel size

View File

@@ -1237,6 +1237,36 @@ uint *WM_window_pixels_read_offscreen(bContext *C, wmWindow *win, int r_size[2])
return rect;
}
bool WM_window_pixels_read_sample_offscreen(bContext *C,
wmWindow *win,
const int pos[2],
float r_col[3])
{
/* A version of #WM_window_pixels_read_offscreen. */
const int size[2] = {WM_window_pixels_x(win), WM_window_pixels_y(win)};
zero_v3(r_col);
/* While this shouldn't happen, return in the case it does. */
BLI_assert((uint)pos[0] < (uint)size[0] && (uint)pos[1] < (uint)size[1]);
if (!((uint)pos[0] < (uint)size[0] && (uint)pos[1] < (uint)size[1])) {
return false;
}
GPUOffScreen *offscreen = GPU_offscreen_create(size[0], size[1], false, GPU_RGBA8, NULL);
if (UNLIKELY(!offscreen)) {
return false;
}
float rect_pixel[4];
GPU_offscreen_bind(offscreen, false);
wm_draw_window_onscreen(C, win, -1);
GPU_offscreen_unbind(offscreen, false);
GPU_offscreen_read_pixels_region(offscreen, GPU_DATA_FLOAT, pos[0], pos[1], 1, 1, rect_pixel);
GPU_offscreen_free(offscreen);
copy_v3_v3(r_col, rect_pixel);
return true;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -1827,6 +1827,10 @@ eWM_CapabilitiesFlag WM_capabilities_flag(void)
return flag;
}
/* NOTE: this is not the intended use of #WM_ghost_backend (for 3.5 release only). */
const char *ghost_backend = WM_ghost_backend();
const bool is_wayland = ghost_backend && STREQ(ghost_backend, "WAYLAND");
flag = 0;
if (GHOST_SupportsCursorWarp()) {
flag |= WM_CAPABILITY_CURSOR_WARP;
@@ -1834,6 +1838,9 @@ eWM_CapabilitiesFlag WM_capabilities_flag(void)
if (GHOST_SupportsWindowPosition()) {
flag |= WM_CAPABILITY_WINDOW_POSITION;
}
if (is_wayland == false) {
flag |= WM_CAPABILITY_GPU_FRONT_BUFFER_READ;
}
return flag;
}
@@ -2151,28 +2158,6 @@ wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mv
return win_other;
}
void WM_window_pixel_sample_read(const wmWindowManager *wm,
const wmWindow *win,
const int pos[2],
float r_col[3])
{
bool setup_context = wm->windrawable != win;
if (setup_context) {
GHOST_ActivateWindowDrawingContext(win->ghostwin);
GPU_context_active_set(win->gpuctx);
}
GPU_frontbuffer_read_pixels(pos[0], pos[1], 1, 1, 3, GPU_DATA_FLOAT, r_col);
if (setup_context) {
if (wm->windrawable) {
GHOST_ActivateWindowDrawingContext(wm->windrawable->ghostwin);
GPU_context_active_set(wm->windrawable->gpuctx);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -2217,6 +2202,29 @@ uint *WM_window_pixels_read(wmWindowManager *wm, wmWindow *win, int r_size[2])
return (uint *)rect;
}
void WM_window_pixels_read_sample(const wmWindowManager *wm,
const wmWindow *win,
const int pos[2],
float r_col[3])
{
BLI_assert(WM_capabilities_flag() & WM_CAPABILITY_GPU_FRONT_BUFFER_READ);
bool setup_context = wm->windrawable != win;
if (setup_context) {
GHOST_ActivateWindowDrawingContext(win->ghostwin);
GPU_context_active_set(win->gpuctx);
}
GPU_frontbuffer_read_pixels(pos[0], pos[1], 1, 1, 3, GPU_DATA_FLOAT, r_col);
if (setup_context) {
if (wm->windrawable) {
GHOST_ActivateWindowDrawingContext(wm->windrawable->ghostwin);
GPU_context_active_set(wm->windrawable->gpuctx);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */