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:
@@ -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 {
|
||||
|
@@ -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");
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
Reference in New Issue
Block a user