This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/windowmanager/intern/wm_draw.c
Brecht Van Lommel feeab1ad53 Fix #34997: when starting the game engine in one window and switching to a second
window, the game would stop drawing in the first and mess up the OpenGL state of
the second.

Also fixes glPushAttrib/glPopAttrib getting out of sync in some cases.
2013-04-18 16:28:39 +00:00

904 lines
24 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2007 Blender Foundation.
* All rights reserved.
*
*
* Contributor(s): Blender Foundation
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/windowmanager/intern/wm_draw.c
* \ingroup wm
*/
#include <stdlib.h>
#include <string.h>
#include <GL/glew.h>
#include "DNA_listBase.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_userdef_types.h"
#include "DNA_view3d_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_math_base.h"
#include "BIF_gl.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_screen.h"
#include "GHOST_C-api.h"
#include "ED_view3d.h"
#include "ED_screen.h"
#include "GPU_draw.h"
#include "GPU_extensions.h"
#include "RE_engine.h"
#include "WM_api.h"
#include "WM_types.h"
#include "wm.h"
#include "wm_draw.h"
#include "wm_window.h"
#include "wm_event_system.h"
/* swap */
#define WIN_NONE_OK 0
#define WIN_BACK_OK 1
#define WIN_FRONT_OK 2
#define WIN_BOTH_OK 3
/* ******************* drawing, overlays *************** */
static void wm_paintcursor_draw(bContext *C, ARegion *ar)
{
wmWindowManager *wm = CTX_wm_manager(C);
if (wm->paintcursors.first) {
wmWindow *win = CTX_wm_window(C);
bScreen *screen = win->screen;
wmPaintCursor *pc;
if (ar->swinid && screen->subwinactive == ar->swinid) {
for (pc = wm->paintcursors.first; pc; pc = pc->next) {
if (pc->poll == NULL || pc->poll(C)) {
ARegion *ar_other = CTX_wm_region(C);
if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) {
int x = 0, y = 0;
wm_get_cursor_position(win, &x, &y);
pc->draw(C,
x - ar_other->winrct.xmin,
y - ar_other->winrct.ymin,
pc->customdata);
}
else {
pc->draw(C,
win->eventstate->x - ar_other->winrct.xmin,
win->eventstate->y - ar_other->winrct.ymin,
pc->customdata);
}
}
}
}
}
}
/* ********************* drawing, swap ****************** */
static void wm_area_mark_invalid_backbuf(ScrArea *sa)
{
if (sa->spacetype == SPACE_VIEW3D)
((View3D *)sa->spacedata.first)->flag |= V3D_INVALID_BACKBUF;
}
static int wm_area_test_invalid_backbuf(ScrArea *sa)
{
if (sa->spacetype == SPACE_VIEW3D)
return (((View3D *)sa->spacedata.first)->flag & V3D_INVALID_BACKBUF);
else
return 1;
}
static void wm_region_test_render_do_draw(bScreen *screen, ScrArea *sa, ARegion *ar)
{
/* tag region for redraw from render engine preview running inside of it */
if (sa->spacetype == SPACE_VIEW3D) {
RegionView3D *rv3d = ar->regiondata;
RenderEngine *engine = (rv3d) ? rv3d->render_engine : NULL;
if (engine && (engine->flag & RE_ENGINE_DO_DRAW)) {
Scene *scene = screen->scene;
View3D *v3d = sa->spacedata.first;
rcti border_rect;
/* do partial redraw when possible */
if (ED_view3d_calc_render_border(scene, v3d, ar, &border_rect))
ED_region_tag_redraw_partial(ar, &border_rect);
else
ED_region_tag_redraw(ar);
engine->flag &= ~RE_ENGINE_DO_DRAW;
}
}
}
/********************** draw all **************************/
/* - reference method, draw all each time */
static void wm_method_draw_full(bContext *C, wmWindow *win)
{
bScreen *screen = win->screen;
ScrArea *sa;
ARegion *ar;
/* draw area regions */
for (sa = screen->areabase.first; sa; sa = sa->next) {
CTX_wm_area_set(C, sa);
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
wm_paintcursor_draw(C, ar);
ED_area_overdraw_flush(sa, ar);
CTX_wm_region_set(C, NULL);
}
}
wm_area_mark_invalid_backbuf(sa);
CTX_wm_area_set(C, NULL);
}
ED_screen_draw(win);
ED_area_overdraw(C);
/* draw overlapping regions */
for (ar = screen->regionbase.first; ar; ar = ar->next) {
if (ar->swinid) {
CTX_wm_menu_set(C, ar);
ED_region_do_draw(C, ar);
CTX_wm_menu_set(C, NULL);
}
}
if (screen->do_draw_gesture)
wm_gesture_draw(win);
}
/****************** draw overlap all **********************/
/* - redraw marked areas, and anything that overlaps it */
/* - it also handles swap exchange optionally, assuming */
/* that on swap no clearing happens and we get back the */
/* same buffer as we swapped to the front */
/* mark area-regions to redraw if overlapped with rect */
static void wm_flush_regions_down(bScreen *screen, rcti *dirty)
{
ScrArea *sa;
ARegion *ar;
for (sa = screen->areabase.first; sa; sa = sa->next) {
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (BLI_rcti_isect(dirty, &ar->winrct, NULL)) {
ar->do_draw = RGN_DRAW;
memset(&ar->drawrct, 0, sizeof(ar->drawrct));
ar->swap = WIN_NONE_OK;
}
}
}
}
/* mark menu-regions to redraw if overlapped with rect */
static void wm_flush_regions_up(bScreen *screen, rcti *dirty)
{
ARegion *ar;
for (ar = screen->regionbase.first; ar; ar = ar->next) {
if (BLI_rcti_isect(dirty, &ar->winrct, NULL)) {
ar->do_draw = RGN_DRAW;
memset(&ar->drawrct, 0, sizeof(ar->drawrct));
ar->swap = WIN_NONE_OK;
}
}
}
static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
{
wmWindowManager *wm = CTX_wm_manager(C);
bScreen *screen = win->screen;
ScrArea *sa;
ARegion *ar;
static rcti rect = {0, 0, 0, 0};
/* after backbuffer selection draw, we need to redraw */
for (sa = screen->areabase.first; sa; sa = sa->next)
for (ar = sa->regionbase.first; ar; ar = ar->next)
if (ar->swinid && !wm_area_test_invalid_backbuf(sa))
ED_region_tag_redraw(ar);
/* flush overlapping regions */
if (screen->regionbase.first) {
/* flush redraws of area regions up to overlapping regions */
for (sa = screen->areabase.first; sa; sa = sa->next)
for (ar = sa->regionbase.first; ar; ar = ar->next)
if (ar->swinid && ar->do_draw)
wm_flush_regions_up(screen, &ar->winrct);
/* flush between overlapping regions */
for (ar = screen->regionbase.last; ar; ar = ar->prev)
if (ar->swinid && ar->do_draw)
wm_flush_regions_up(screen, &ar->winrct);
/* flush redraws of overlapping regions down to area regions */
for (ar = screen->regionbase.last; ar; ar = ar->prev)
if (ar->swinid && ar->do_draw)
wm_flush_regions_down(screen, &ar->winrct);
}
/* flush drag item */
if (rect.xmin != rect.xmax) {
wm_flush_regions_down(screen, &rect);
rect.xmin = rect.xmax = 0;
}
if (wm->drags.first) {
/* doesnt draw, fills rect with boundbox */
wm_drags_draw(C, win, &rect);
}
/* draw marked area regions */
for (sa = screen->areabase.first; sa; sa = sa->next) {
CTX_wm_area_set(C, sa);
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid) {
if (ar->do_draw) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
wm_paintcursor_draw(C, ar);
ED_area_overdraw_flush(sa, ar);
CTX_wm_region_set(C, NULL);
if (exchange)
ar->swap = WIN_FRONT_OK;
}
else if (exchange) {
if (ar->swap == WIN_FRONT_OK) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
wm_paintcursor_draw(C, ar);
ED_area_overdraw_flush(sa, ar);
CTX_wm_region_set(C, NULL);
ar->swap = WIN_BOTH_OK;
}
else if (ar->swap == WIN_BACK_OK)
ar->swap = WIN_FRONT_OK;
else if (ar->swap == WIN_BOTH_OK)
ar->swap = WIN_BOTH_OK;
}
}
}
wm_area_mark_invalid_backbuf(sa);
CTX_wm_area_set(C, NULL);
}
/* after area regions so we can do area 'overlay' drawing */
if (screen->do_draw) {
ED_screen_draw(win);
if (exchange)
screen->swap = WIN_FRONT_OK;
}
else if (exchange) {
if (screen->swap == WIN_FRONT_OK) {
ED_screen_draw(win);
screen->swap = WIN_BOTH_OK;
}
else if (screen->swap == WIN_BACK_OK)
screen->swap = WIN_FRONT_OK;
else if (screen->swap == WIN_BOTH_OK)
screen->swap = WIN_BOTH_OK;
}
ED_area_overdraw(C);
/* draw marked overlapping regions */
for (ar = screen->regionbase.first; ar; ar = ar->next) {
if (ar->swinid && ar->do_draw) {
CTX_wm_menu_set(C, ar);
ED_region_do_draw(C, ar);
CTX_wm_menu_set(C, NULL);
}
}
if (screen->do_draw_gesture)
wm_gesture_draw(win);
/* needs pixel coords in screen */
if (wm->drags.first) {
wm_drags_draw(C, win, NULL);
}
}
#if 0
/******************** draw damage ************************/
/* - not implemented */
static void wm_method_draw_damage(bContext *C, wmWindow *win)
{
wm_method_draw_all(C, win);
}
#endif
/****************** draw triple buffer ********************/
/* - area regions are written into a texture, without any */
/* of the overlapping menus, brushes, gestures. these */
/* are redrawn each time. */
/* */
/* - if non-power of two textures are supported, that is */
/* used. if not, multiple smaller ones are used, with */
/* worst case wasted space being 23.4% for 3x3 textures */
#define MAX_N_TEX 3
typedef struct wmDrawTriple {
GLuint bind[MAX_N_TEX * MAX_N_TEX];
int x[MAX_N_TEX], y[MAX_N_TEX];
int nx, ny;
GLenum target;
} wmDrawTriple;
static void split_width(int x, int n, int *splitx, int *nx)
{
int a, newnx, waste;
/* if already power of two just use it */
if (is_power_of_2_i(x)) {
splitx[0] = x;
(*nx)++;
return;
}
if (n == 1) {
/* last part, we have to go larger */
splitx[0] = power_of_2_max_i(x);
(*nx)++;
}
else {
/* two or more parts to go, use smaller part */
splitx[0] = power_of_2_min_i(x);
newnx = ++(*nx);
split_width(x - splitx[0], n - 1, splitx + 1, &newnx);
for (waste = 0, a = 0; a < n; a++)
waste += splitx[a];
/* if we waste more space or use the same amount,
* revert deeper splits and just use larger */
if (waste >= power_of_2_max_i(x)) {
splitx[0] = power_of_2_max_i(x);
memset(splitx + 1, 0, sizeof(int) * (n - 1));
}
else
*nx = newnx;
}
}
static void wm_draw_triple_free(wmWindow *win)
{
if (win->drawdata) {
wmDrawTriple *triple = win->drawdata;
glDeleteTextures(triple->nx * triple->ny, triple->bind);
MEM_freeN(triple);
win->drawdata = NULL;
}
}
static void wm_draw_triple_fail(bContext *C, wmWindow *win)
{
wm_draw_window_clear(win);
win->drawfail = 1;
wm_method_draw_overlap_all(C, win, 0);
}
static int wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple)
{
GLint maxsize;
int x, y;
/* compute texture sizes */
if (GLEW_ARB_texture_rectangle || GLEW_NV_texture_rectangle || GLEW_EXT_texture_rectangle) {
triple->target = GL_TEXTURE_RECTANGLE_ARB;
triple->nx = 1;
triple->ny = 1;
triple->x[0] = WM_window_pixels_x(win);
triple->y[0] = WM_window_pixels_y(win);
}
else if (GPU_non_power_of_two_support()) {
triple->target = GL_TEXTURE_2D;
triple->nx = 1;
triple->ny = 1;
triple->x[0] = WM_window_pixels_x(win);
triple->y[0] = WM_window_pixels_y(win);
}
else {
triple->target = GL_TEXTURE_2D;
triple->nx = 0;
triple->ny = 0;
split_width(WM_window_pixels_x(win), MAX_N_TEX, triple->x, &triple->nx);
split_width(WM_window_pixels_y(win), MAX_N_TEX, triple->y, &triple->ny);
}
/* generate texture names */
glGenTextures(triple->nx * triple->ny, triple->bind);
if (!triple->bind[0]) {
/* not the typical failure case but we handle it anyway */
printf("WM: failed to allocate texture for triple buffer drawing (glGenTextures).\n");
return 0;
}
for (y = 0; y < triple->ny; y++) {
for (x = 0; x < triple->nx; x++) {
/* proxy texture is only guaranteed to test for the cases that
* there is only one texture in use, which may not be the case */
maxsize = GPU_max_texture_size();
if (triple->x[x] > maxsize || triple->y[y] > maxsize) {
glBindTexture(triple->target, 0);
printf("WM: failed to allocate texture for triple buffer drawing "
"(texture too large for graphics card).\n");
return 0;
}
/* setup actual texture */
glBindTexture(triple->target, triple->bind[x + y * triple->nx]);
glTexImage2D(triple->target, 0, GL_RGB8, triple->x[x], triple->y[y], 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(triple->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(triple->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
/* The current color is ignored if the GL_REPLACE texture environment is used. */
// glTexEnvi(triple->target, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBindTexture(triple->target, 0);
/* not sure if this works everywhere .. */
if (glGetError() == GL_OUT_OF_MEMORY) {
printf("WM: failed to allocate texture for triple buffer drawing (out of memory).\n");
return 0;
}
}
}
return 1;
}
static void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha)
{
float halfx, halfy, ratiox, ratioy;
int x, y, sizex, sizey, offx, offy;
glEnable(triple->target);
for (y = 0, offy = 0; y < triple->ny; offy += triple->y[y], y++) {
for (x = 0, offx = 0; x < triple->nx; offx += triple->x[x], x++) {
sizex = (x == triple->nx - 1) ? WM_window_pixels_x(win) - offx : triple->x[x];
sizey = (y == triple->ny - 1) ? WM_window_pixels_y(win) - offy : triple->y[y];
/* wmOrtho for the screen has this same offset */
ratiox = sizex;
ratioy = sizey;
halfx = GLA_PIXEL_OFS;
halfy = GLA_PIXEL_OFS;
/* texture rectangle has unnormalized coordinates */
if (triple->target == GL_TEXTURE_2D) {
ratiox /= triple->x[x];
ratioy /= triple->y[y];
halfx /= triple->x[x];
halfy /= triple->y[y];
}
glBindTexture(triple->target, triple->bind[x + y * triple->nx]);
glColor4f(1.0f, 1.0f, 1.0f, alpha);
glBegin(GL_QUADS);
glTexCoord2f(halfx, halfy);
glVertex2f(offx, offy);
glTexCoord2f(ratiox + halfx, halfy);
glVertex2f(offx + sizex, offy);
glTexCoord2f(ratiox + halfx, ratioy + halfy);
glVertex2f(offx + sizex, offy + sizey);
glTexCoord2f(halfx, ratioy + halfy);
glVertex2f(offx, offy + sizey);
glEnd();
}
}
glBindTexture(triple->target, 0);
glDisable(triple->target);
}
static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple)
{
int x, y, sizex, sizey, offx, offy;
for (y = 0, offy = 0; y < triple->ny; offy += triple->y[y], y++) {
for (x = 0, offx = 0; x < triple->nx; offx += triple->x[x], x++) {
sizex = (x == triple->nx - 1) ? WM_window_pixels_x(win) - offx : triple->x[x];
sizey = (y == triple->ny - 1) ? WM_window_pixels_y(win) - offy : triple->y[y];
glBindTexture(triple->target, triple->bind[x + y * triple->nx]);
glCopyTexSubImage2D(triple->target, 0, 0, 0, offx, offy, sizex, sizey);
}
}
glBindTexture(triple->target, 0);
}
static void wm_draw_region_blend(wmWindow *win, ARegion *ar)
{
float fac = ED_region_blend_factor(ar);
/* region blend always is 1, except when blend timer is running */
if (fac < 1.0f) {
wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct);
glEnable(GL_BLEND);
wm_triple_draw_textures(win, win->drawdata, 1.0f - fac);
glDisable(GL_BLEND);
}
}
static void wm_method_draw_triple(bContext *C, wmWindow *win)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmDrawTriple *triple;
bScreen *screen = win->screen;
ScrArea *sa;
ARegion *ar;
int copytex = 0, paintcursor = 1;
if (win->drawdata) {
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
wmSubWindowSet(win, screen->mainwin);
wm_triple_draw_textures(win, win->drawdata, 1.0f);
}
else {
win->drawdata = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple");
if (!wm_triple_gen_textures(win, win->drawdata)) {
wm_draw_triple_fail(C, win);
return;
}
}
triple = win->drawdata;
/* draw marked area regions */
for (sa = screen->areabase.first; sa; sa = sa->next) {
CTX_wm_area_set(C, sa);
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid && ar->do_draw) {
if (ar->overlap == 0) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
ED_area_overdraw_flush(sa, ar);
CTX_wm_region_set(C, NULL);
copytex = 1;
}
}
}
wm_area_mark_invalid_backbuf(sa);
CTX_wm_area_set(C, NULL);
}
if (copytex) {
wmSubWindowSet(win, screen->mainwin);
ED_area_overdraw(C);
wm_triple_copy_textures(win, triple);
}
if (paintcursor && wm->paintcursors.first) {
for (sa = screen->areabase.first; sa; sa = sa->next) {
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid && ar->swinid == screen->subwinactive) {
CTX_wm_area_set(C, sa);
CTX_wm_region_set(C, ar);
/* make region ready for draw, scissor, pixelspace */
ED_region_set(C, ar);
wm_paintcursor_draw(C, ar);
CTX_wm_region_set(C, NULL);
CTX_wm_area_set(C, NULL);
}
}
}
wmSubWindowSet(win, screen->mainwin);
}
/* draw overlapping area regions (always like popups) */
for (sa = screen->areabase.first; sa; sa = sa->next) {
CTX_wm_area_set(C, sa);
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid && ar->overlap) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
CTX_wm_region_set(C, NULL);
wm_draw_region_blend(win, ar);
}
}
CTX_wm_area_set(C, NULL);
}
/* after area regions so we can do area 'overlay' drawing */
ED_screen_draw(win);
/* draw floating regions (menus) */
for (ar = screen->regionbase.first; ar; ar = ar->next) {
if (ar->swinid) {
CTX_wm_menu_set(C, ar);
ED_region_do_draw(C, ar);
CTX_wm_menu_set(C, NULL);
/* when a menu is being drawn, don't do the paint cursors */
paintcursor = 0;
}
}
/* always draw, not only when screen tagged */
if (win->gesture.first)
wm_gesture_draw(win);
/* needs pixel coords in screen */
if (wm->drags.first) {
wm_drags_draw(C, win, NULL);
}
}
/****************** main update call **********************/
/* quick test to prevent changing window drawable */
static int wm_draw_update_test_window(wmWindow *win)
{
ScrArea *sa;
ARegion *ar;
int do_draw = FALSE;
for (ar = win->screen->regionbase.first; ar; ar = ar->next) {
if (ar->do_draw_overlay) {
wm_tag_redraw_overlay(win, ar);
ar->do_draw_overlay = FALSE;
}
if (ar->swinid && ar->do_draw)
do_draw = TRUE;
}
for (sa = win->screen->areabase.first; sa; sa = sa->next) {
for (ar = sa->regionbase.first; ar; ar = ar->next) {
wm_region_test_render_do_draw(win->screen, sa, ar);
if (ar->swinid && ar->do_draw)
do_draw = TRUE;
}
}
if (do_draw)
return 1;
if (win->screen->do_refresh)
return 1;
if (win->screen->do_draw)
return 1;
if (win->screen->do_draw_gesture)
return 1;
if (win->screen->do_draw_paintcursor)
return 1;
if (win->screen->do_draw_drag)
return 1;
return 0;
}
static int wm_automatic_draw_method(wmWindow *win)
{
/* Ideally all cards would work well with triple buffer, since if it works
* well gives the least redraws and is considerably faster at partial redraw
* for sculpting or drawing overlapping menus. For typically lower end cards
* copy to texture is slow though and so we use overlap instead there. */
if (win->drawmethod == USER_DRAW_AUTOMATIC) {
/* ATI opensource driver is known to be very slow at this */
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE))
return USER_DRAW_OVERLAP;
/* also Intel drivers are slow */
#if 0 /* 2.64 BCon3 period, let's try if intel now works... */
else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_UNIX, GPU_DRIVER_ANY))
return USER_DRAW_OVERLAP;
else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY))
return USER_DRAW_OVERLAP_FLIP;
else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_ANY))
return USER_DRAW_OVERLAP_FLIP;
#endif
/* Windows software driver darkens color on each redraw */
else if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_WIN, GPU_DRIVER_SOFTWARE))
return USER_DRAW_OVERLAP_FLIP;
else if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_UNIX, GPU_DRIVER_SOFTWARE))
return USER_DRAW_OVERLAP;
/* drawing lower color depth again degrades colors each time */
else if (GPU_color_depth() < 24)
return USER_DRAW_OVERLAP;
else
return USER_DRAW_TRIPLE;
}
else
return win->drawmethod;
}
bool WM_is_draw_triple(wmWindow *win)
{
/* function can get called before this variable is set in drawing code below */
if (win->drawmethod != U.wmdrawmethod)
win->drawmethod = U.wmdrawmethod;
return (USER_DRAW_TRIPLE == wm_automatic_draw_method(win));
}
void wm_tag_redraw_overlay(wmWindow *win, ARegion *ar)
{
/* for draw triple gestures, paint cursors don't need region redraw */
if (ar && win) {
if (wm_automatic_draw_method(win) != USER_DRAW_TRIPLE)
ED_region_tag_redraw(ar);
win->screen->do_draw_paintcursor = TRUE;
}
}
void wm_draw_update(bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win;
int drawmethod;
GPU_free_unused_buffers();
for (win = wm->windows.first; win; win = win->next) {
#ifdef WIN32
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin);
if (state == GHOST_kWindowStateMinimized) {
/* do not update minimized windows, it gives issues on intel drivers (see [#33223])
* anyway, it seems logical to skip update for invisible windows
*/
continue;
}
}
#endif
if (win->drawmethod != U.wmdrawmethod) {
wm_draw_window_clear(win);
win->drawmethod = U.wmdrawmethod;
}
if (wm_draw_update_test_window(win)) {
CTX_wm_window_set(C, win);
/* sets context window+screen */
wm_window_make_drawable(wm, win);
/* notifiers for screen redraw */
if (win->screen->do_refresh)
ED_screen_refresh(wm, win);
drawmethod = wm_automatic_draw_method(win);
if (win->drawfail)
wm_method_draw_overlap_all(C, win, 0);
else if (drawmethod == USER_DRAW_FULL)
wm_method_draw_full(C, win);
else if (drawmethod == USER_DRAW_OVERLAP)
wm_method_draw_overlap_all(C, win, 0);
else if (drawmethod == USER_DRAW_OVERLAP_FLIP)
wm_method_draw_overlap_all(C, win, 1);
else // if (drawmethod == USER_DRAW_TRIPLE)
wm_method_draw_triple(C, win);
win->screen->do_draw_gesture = FALSE;
win->screen->do_draw_paintcursor = FALSE;
win->screen->do_draw_drag = FALSE;
wm_window_swap_buffers(win);
CTX_wm_window_set(C, NULL);
}
}
}
void wm_draw_window_clear(wmWindow *win)
{
bScreen *screen = win->screen;
ScrArea *sa;
ARegion *ar;
int drawmethod = wm_automatic_draw_method(win);
if (drawmethod == USER_DRAW_TRIPLE)
wm_draw_triple_free(win);
/* clear screen swap flags */
if (screen) {
for (sa = screen->areabase.first; sa; sa = sa->next)
for (ar = sa->regionbase.first; ar; ar = ar->next)
ar->swap = WIN_NONE_OK;
screen->swap = WIN_NONE_OK;
}
}
void wm_draw_region_clear(wmWindow *win, ARegion *ar)
{
int drawmethod = wm_automatic_draw_method(win);
if (ELEM(drawmethod, USER_DRAW_OVERLAP, USER_DRAW_OVERLAP_FLIP))
wm_flush_regions_down(win->screen, &ar->winrct);
win->screen->do_draw = TRUE;
}
void WM_redraw_windows(bContext *C)
{
wmWindow *win_prev = CTX_wm_window(C);
ScrArea *area_prev = CTX_wm_area(C);
ARegion *ar_prev = CTX_wm_region(C);
wm_draw_update(C);
CTX_wm_window_set(C, win_prev);
CTX_wm_area_set(C, area_prev);
CTX_wm_region_set(C, ar_prev);
}