Changeset View
Changeset View
Standalone View
Standalone View
source/blender/windowmanager/intern/wm_draw.c
| Show First 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BIF_gl.h" | #include "BIF_gl.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_image.h" | #include "BKE_image.h" | ||||
| #include "BKE_screen.h" | |||||
| #include "BKE_scene.h" | #include "BKE_scene.h" | ||||
| #include "BKE_workspace.h" | #include "BKE_workspace.h" | ||||
| #include "GHOST_C-api.h" | #include "GHOST_C-api.h" | ||||
| #include "ED_node.h" | #include "ED_node.h" | ||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "GPU_draw.h" | #include "GPU_draw.h" | ||||
| #include "GPU_extensions.h" | #include "GPU_extensions.h" | ||||
| #include "GPU_framebuffer.h" | |||||
| #include "GPU_immediate.h" | #include "GPU_immediate.h" | ||||
| #include "GPU_matrix.h" | |||||
| #include "GPU_texture.h" | |||||
| #include "GPU_viewport.h" | #include "GPU_viewport.h" | ||||
| #include "RE_engine.h" | #include "RE_engine.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "wm.h" | #include "wm.h" | ||||
| #include "wm_draw.h" | #include "wm_draw.h" | ||||
| #include "wm_window.h" | #include "wm_window.h" | ||||
| #include "wm_event_system.h" | #include "wm_event_system.h" | ||||
| #ifdef WITH_OPENSUBDIV | #ifdef WITH_OPENSUBDIV | ||||
| # include "BKE_subsurf.h" | # include "BKE_subsurf.h" | ||||
| #endif | #endif | ||||
| /* swap */ | |||||
| #define WIN_NONE_OK 0 | |||||
| #define WIN_BACK_OK 1 | |||||
| #define WIN_FRONT_OK 2 | |||||
| #define WIN_BOTH_OK 3 | |||||
| /* ******************* drawing, overlays *************** */ | |||||
| /* ******************* paint cursor *************** */ | |||||
| static void wm_paintcursor_draw(bContext *C, ARegion *ar) | static void wm_paintcursor_draw(bContext *C, ARegion *ar) | ||||
| { | { | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | wmWindowManager *wm = CTX_wm_manager(C); | ||||
| if (wm->paintcursors.first) { | |||||
| wmWindow *win = CTX_wm_window(C); | wmWindow *win = CTX_wm_window(C); | ||||
| bScreen *screen = WM_window_get_active_screen(win); | bScreen *screen = WM_window_get_active_screen(win); | ||||
| wmPaintCursor *pc; | wmPaintCursor *pc; | ||||
| if (ar->visible && ar == screen->active_region) { | if (ar->visible && ar == screen->active_region) { | ||||
| for (pc = wm->paintcursors.first; pc; pc = pc->next) { | for (pc = wm->paintcursors.first; pc; pc = pc->next) { | ||||
| if (pc->poll == NULL || pc->poll(C)) { | if (pc->poll == NULL || pc->poll(C)) { | ||||
| ARegion *ar_other = CTX_wm_region(C); | /* Prevent drawing outside region. */ | ||||
| GLint scissor[4]; | |||||
| glGetIntegerv(GL_SCISSOR_BOX, scissor); | |||||
| glScissor(ar->winrct.xmin, | |||||
| ar->winrct.ymin, | |||||
| BLI_rcti_size_x(&ar->winrct) + 1, | |||||
| BLI_rcti_size_y(&ar->winrct) + 1); | |||||
| if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) { | if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) { | ||||
| int x = 0, y = 0; | int x = 0, y = 0; | ||||
| wm_get_cursor_position(win, &x, &y); | wm_get_cursor_position(win, &x, &y); | ||||
| pc->draw(C, | pc->draw(C, | ||||
| x - ar_other->winrct.xmin, | x, | ||||
| y - ar_other->winrct.ymin, | y, | ||||
| pc->customdata); | pc->customdata); | ||||
| } | } | ||||
| else { | else { | ||||
| pc->draw(C, | pc->draw(C, | ||||
| win->eventstate->x - ar_other->winrct.xmin, | win->eventstate->x, | ||||
| win->eventstate->y - ar_other->winrct.ymin, | win->eventstate->y, | ||||
| pc->customdata); | pc->customdata); | ||||
| } | } | ||||
| } | |||||
| glScissor(scissor[0], scissor[1], scissor[2], scissor[3]); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* ********************* drawing, swap ****************** */ | |||||
| static void wm_area_mark_invalid_backbuf(ScrArea *sa) | static bool wm_draw_region_stereo_set(ScrArea *sa, ARegion *ar, eStereoViews sview) | ||||
| { | { | ||||
| if (sa->spacetype == SPACE_VIEW3D) | /* We could detect better when stereo is actually needed, by inspecting the | ||||
| ((View3D *)sa->spacedata.first)->flag |= V3D_INVALID_BACKBUF; | * image in the image editor and sequencer. */ | ||||
| if (ar->regiontype != RGN_TYPE_WINDOW) { | |||||
| return false; | |||||
| } | } | ||||
| static bool wm_area_test_invalid_backbuf(ScrArea *sa) | switch (sa->spacetype) { | ||||
| case SPACE_IMAGE: | |||||
| { | { | ||||
| if (sa->spacetype == SPACE_VIEW3D) | SpaceImage *sima = sa->spacedata.first; | ||||
| return (((View3D *)sa->spacedata.first)->flag & V3D_INVALID_BACKBUF) != 0; | sima->iuser.multiview_eye = sview; | ||||
| else | return true; | ||||
| } | |||||
| case SPACE_VIEW3D: | |||||
| { | |||||
| View3D *v3d = sa->spacedata.first; | |||||
| if (v3d->camera && v3d->camera->type == OB_CAMERA) { | |||||
| Camera *cam = v3d->camera->data; | |||||
| CameraBGImage *bgpic = cam->bg_images.first; | |||||
| v3d->multiview_eye = sview; | |||||
| if (bgpic) bgpic->iuser.multiview_eye = sview; | |||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | |||||
| } | |||||
| case SPACE_NODE: | |||||
| { | |||||
| SpaceNode *snode = sa->spacedata.first; | |||||
| if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { | |||||
| Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node"); | |||||
| ima->eye = sview; | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| case SPACE_SEQ: | |||||
| { | |||||
| SpaceSeq *sseq = sa->spacedata.first; | |||||
| sseq->multiview_eye = sview; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /* ********************* drawing ****************** */ | |||||
| static void wm_area_mark_invalid_backbuf(ScrArea *sa) | |||||
| { | |||||
| if (sa->spacetype == SPACE_VIEW3D) | |||||
| ((View3D *)sa->spacedata.first)->flag |= V3D_INVALID_BACKBUF; | |||||
| } | |||||
| static void wm_region_test_render_do_draw(const Scene *scene, struct Depsgraph *depsgraph, | static void wm_region_test_render_do_draw(const Scene *scene, struct Depsgraph *depsgraph, | ||||
| ScrArea *sa, ARegion *ar) | ScrArea *sa, ARegion *ar) | ||||
| { | { | ||||
| /* tag region for redraw from render engine preview running inside of it */ | /* tag region for redraw from render engine preview running inside of it */ | ||||
| if (sa->spacetype == SPACE_VIEW3D) { | if (sa->spacetype == SPACE_VIEW3D && ar->regiontype == RGN_TYPE_WINDOW) { | ||||
| RegionView3D *rv3d = ar->regiondata; | RegionView3D *rv3d = ar->regiondata; | ||||
| RenderEngine *engine = (rv3d) ? rv3d->render_engine : NULL; | RenderEngine *engine = rv3d->render_engine; | ||||
| GPUViewport *viewport = (rv3d) ? rv3d->viewport : NULL; | GPUViewport *viewport = WM_draw_region_get_viewport(ar, 0); | ||||
| if (engine && (engine->flag & RE_ENGINE_DO_DRAW)) { | if (engine && (engine->flag & RE_ENGINE_DO_DRAW)) { | ||||
| View3D *v3d = sa->spacedata.first; | View3D *v3d = sa->spacedata.first; | ||||
| rcti border_rect; | rcti border_rect; | ||||
| /* do partial redraw when possible */ | /* do partial redraw when possible */ | ||||
| if (ED_view3d_calc_render_border(scene, depsgraph, v3d, ar, &border_rect)) | if (ED_view3d_calc_render_border(scene, depsgraph, v3d, ar, &border_rect)) | ||||
| ED_region_tag_redraw_partial(ar, &border_rect); | ED_region_tag_redraw_partial(ar, &border_rect); | ||||
| else | else | ||||
| ED_region_tag_redraw(ar); | ED_region_tag_redraw(ar); | ||||
| engine->flag &= ~RE_ENGINE_DO_DRAW; | engine->flag &= ~RE_ENGINE_DO_DRAW; | ||||
| } | } | ||||
| else if (viewport && GPU_viewport_do_update(viewport)) { | else if (viewport && GPU_viewport_do_update(viewport)) { | ||||
| ED_region_tag_redraw(ar); | ED_region_tag_redraw(ar); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void wm_draw_region(bContext *C, ARegion *ar) | static bool wm_region_use_viewport(ScrArea *sa, ARegion *ar) | ||||
| { | { | ||||
| CTX_wm_region_set(C, ar); | return (sa->spacetype == SPACE_VIEW3D && ar->regiontype == RGN_TYPE_WINDOW); | ||||
| ED_region_do_draw(C, ar); | |||||
| ar->do_draw = false; | |||||
| CTX_wm_region_set(C, NULL); | |||||
| } | } | ||||
| /********************** draw all **************************/ | /********************** draw all **************************/ | ||||
| /* - reference method, draw all each time */ | /* - reference method, draw all each time */ | ||||
| typedef struct WindowDrawCB { | typedef struct WindowDrawCB { | ||||
| struct WindowDrawCB *next, *prev; | struct WindowDrawCB *next, *prev; | ||||
| Show All 29 Lines | |||||
| static void wm_draw_callbacks(wmWindow *win) | static void wm_draw_callbacks(wmWindow *win) | ||||
| { | { | ||||
| for (WindowDrawCB *wdc = win->drawcalls.first; wdc; wdc = wdc->next) { | for (WindowDrawCB *wdc = win->drawcalls.first; wdc; wdc = wdc->next) { | ||||
| wdc->draw(win, wdc->customdata); | wdc->draw(win, wdc->customdata); | ||||
| } | } | ||||
| } | } | ||||
| static void wm_method_draw_full(bContext *C, wmWindow *win) | |||||
| { | |||||
| bScreen *screen = WM_window_get_active_screen(win); | |||||
| ARegion *ar; | |||||
| /* draw area regions */ | /************************* Region drawing. ******************************** | ||||
| ED_screen_areas_iter(win, screen, sa) { | * | ||||
| CTX_wm_area_set(C, sa); | * Each region draws into its own framebuffer, which is then blit on the | ||||
| * window draw buffer. This helps with fast redrawing if only some regions | |||||
| * change. It also means we can share a single context for multiple windows, | |||||
| * so that for example VAOs can be shared between windows. */ | |||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | static void wm_draw_region_buffer_free(ARegion *ar) | ||||
| if (ar->visible) { | { | ||||
| CTX_wm_region_set(C, ar); | if (ar->draw_buffer) { | ||||
| ED_region_do_draw(C, ar); | for (int view = 0; view < 2; view++) { | ||||
| ar->do_draw = false; | if (ar->draw_buffer->offscreen[view]) { | ||||
| wm_paintcursor_draw(C, ar); | GPU_offscreen_free(ar->draw_buffer->offscreen[view]); | ||||
| CTX_wm_region_set(C, NULL); | |||||
| } | } | ||||
| if (ar->draw_buffer->viewport[view]) { | |||||
| GPU_viewport_free(ar->draw_buffer->viewport[view]); | |||||
| } | } | ||||
| wm_area_mark_invalid_backbuf(sa); | |||||
| CTX_wm_area_set(C, NULL); | |||||
| } | } | ||||
| ED_screen_draw_edges(win); | MEM_freeN(ar->draw_buffer); | ||||
| screen->do_draw = false; | ar->draw_buffer = NULL; | ||||
| wm_draw_callbacks(win); | |||||
| /* draw overlapping regions */ | |||||
| for (ar = screen->regionbase.first; ar; ar = ar->next) { | |||||
| if (ar->visible) { | |||||
| CTX_wm_menu_set(C, ar); | |||||
| ED_region_do_draw(C, ar); | |||||
| ar->do_draw = false; | |||||
| CTX_wm_menu_set(C, NULL); | |||||
| } | } | ||||
| } | } | ||||
| if (screen->do_draw_gesture) | static void wm_draw_offscreen_texture_parameters(GPUOffScreen *offscreen) | ||||
| wm_gesture_draw(win); | { | ||||
| } | /* Setup offscreen color texture for drawing. */ | ||||
| GPUTexture *texture = GPU_offscreen_color_texture(offscreen); | |||||
| /****************** draw overlap all **********************/ | /* We don't support multisample textures here. */ | ||||
| /* - redraw marked areas, and anything that overlaps it */ | BLI_assert(GPU_texture_target(texture) == GL_TEXTURE_2D); | ||||
| /* - 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 */ | glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture)); | ||||
| static void wm_flush_regions_down(wmWindow *win, bScreen *screen, rcti *dirty) | |||||
| { | |||||
| ARegion *ar; | |||||
| ED_screen_areas_iter(win, screen, sa) { | /* No mipmaps or filtering. */ | ||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | ||||
| if (BLI_rcti_isect(dirty, &ar->winrct, NULL)) { | /* GL_TEXTURE_BASE_LEVEL = 0 by default */ | ||||
| ar->do_draw = RGN_DRAW; | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||||
| memset(&ar->drawrct, 0, sizeof(ar->drawrct)); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||||
| ar->swap = WIN_NONE_OK; | |||||
| } | glBindTexture(GL_TEXTURE_2D, 0); | ||||
| } | |||||
| } | |||||
| } | } | ||||
| /* mark menu-regions to redraw if overlapped with rect */ | static void wm_draw_region_buffer_create(ARegion *ar, bool stereo, bool use_viewport) | ||||
| static void wm_flush_regions_up(bScreen *screen, rcti *dirty) | |||||
| { | { | ||||
| ARegion *ar; | if (ar->draw_buffer) { | ||||
| if (ar->draw_buffer->stereo != stereo) { | |||||
| for (ar = screen->regionbase.first; ar; ar = ar->next) { | /* Free draw buffer on stereo changes. */ | ||||
| if (BLI_rcti_isect(dirty, &ar->winrct, NULL)) { | wm_draw_region_buffer_free(ar); | ||||
| ar->do_draw = RGN_DRAW; | |||||
| memset(&ar->drawrct, 0, sizeof(ar->drawrct)); | |||||
| ar->swap = WIN_NONE_OK; | |||||
| } | |||||
| } | } | ||||
| } | else { | ||||
| /* Free offscreen buffer on size changes. Viewport auto resizes. */ | |||||
| static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) | GPUOffScreen *offscreen = ar->draw_buffer->offscreen[0]; | ||||
| if (offscreen && (GPU_offscreen_width(offscreen) != ar->winx || | |||||
| GPU_offscreen_height(offscreen) != ar->winy)) | |||||
| { | { | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | wm_draw_region_buffer_free(ar); | ||||
| bScreen *screen = WM_window_get_active_screen(win); | |||||
| ARegion *ar; | |||||
| static rcti rect = {0, 0, 0, 0}; | |||||
| /* after backbuffer selection draw, we need to redraw */ | |||||
| ED_screen_areas_iter(win, screen, sa) { | |||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) | |||||
| if (ar->visible && !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 */ | |||||
| ED_screen_areas_iter(win, screen, sa) { | |||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) | |||||
| if (ar->visible && 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->visible && 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->visible && ar->do_draw) | |||||
| wm_flush_regions_down(win, screen, &ar->winrct); | |||||
| } | } | ||||
| /* flush drag item */ | if (!ar->draw_buffer) { | ||||
| if (rect.xmin != rect.xmax) { | if (use_viewport) { | ||||
| wm_flush_regions_down(win, screen, &rect); | /* Allocate viewport which includes an offscreen buffer with depth | ||||
| rect.xmin = rect.xmax = 0; | * multisample, etc. */ | ||||
| ar->draw_buffer = MEM_callocN(sizeof(wmDrawBuffer), "wmDrawBuffer"); | |||||
| ar->draw_buffer->viewport[0] = GPU_viewport_create(); | |||||
| ar->draw_buffer->viewport[1] = (stereo) ? GPU_viewport_create() : NULL; | |||||
| } | } | ||||
| if (wm->drags.first) { | else { | ||||
| /* doesnt draw, fills rect with boundbox */ | /* Allocate offscreen buffer if it does not exist. This one has no | ||||
| wm_drags_draw(C, win, &rect); | * depth or multisample buffers. 3D view creates own buffers with | ||||
| * the data it needs. */ | |||||
| GPUOffScreen *offscreen = GPU_offscreen_create(ar->winx, ar->winy, 0, false, false, NULL); | |||||
| if (!offscreen) { | |||||
| return; | |||||
| } | } | ||||
| /* draw marked area regions */ | wm_draw_offscreen_texture_parameters(offscreen); | ||||
| ED_screen_areas_iter(win, screen, sa) { | |||||
| CTX_wm_area_set(C, sa); | |||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | GPUOffScreen *offscreen_right = NULL; | ||||
| if (ar->visible) { | if (stereo) { | ||||
| if (ar->do_draw) { | offscreen_right = GPU_offscreen_create(ar->winx, ar->winy, 0, false, false, NULL); | ||||
| CTX_wm_region_set(C, ar); | |||||
| ED_region_do_draw(C, ar); | |||||
| ar->do_draw = false; | |||||
| wm_paintcursor_draw(C, ar); | |||||
| CTX_wm_region_set(C, NULL); | |||||
| if (exchange) | if (!offscreen_right) { | ||||
| ar->swap = WIN_FRONT_OK; | GPU_offscreen_free(offscreen); | ||||
| return; | |||||
| } | } | ||||
| else if (exchange) { | |||||
| if (ar->swap == WIN_FRONT_OK) { | |||||
| CTX_wm_region_set(C, ar); | |||||
| ED_region_do_draw(C, ar); | |||||
| ar->do_draw = false; | |||||
| wm_paintcursor_draw(C, ar); | |||||
| CTX_wm_region_set(C, NULL); | |||||
| ar->swap = WIN_BOTH_OK; | wm_draw_offscreen_texture_parameters(offscreen_right); | ||||
| } | } | ||||
| else if (ar->swap == WIN_BACK_OK) | |||||
| ar->swap = WIN_FRONT_OK; | ar->draw_buffer = MEM_callocN(sizeof(wmDrawBuffer), "wmDrawBuffer"); | ||||
| else if (ar->swap == WIN_BOTH_OK) | ar->draw_buffer->offscreen[0] = offscreen; | ||||
| ar->swap = WIN_BOTH_OK; | ar->draw_buffer->offscreen[1] = offscreen_right; | ||||
| } | } | ||||
| ar->draw_buffer->bound_view = -1; | |||||
| ar->draw_buffer->stereo = stereo; | |||||
| } | } | ||||
| } | } | ||||
| wm_area_mark_invalid_backbuf(sa); | static void wm_draw_region_bind(ARegion *ar, int view) | ||||
| CTX_wm_area_set(C, NULL); | { | ||||
| if (!ar->draw_buffer) { | |||||
| return; | |||||
| } | } | ||||
| /* after area regions so we can do area 'overlay' drawing */ | if (ar->draw_buffer->viewport[view]) { | ||||
| if (screen->do_draw) { | GPU_viewport_bind(ar->draw_buffer->viewport[view], &ar->winrct); | ||||
| ED_screen_draw_edges(win); | |||||
| screen->do_draw = false; | |||||
| wm_draw_callbacks(win); | |||||
| if (exchange) | |||||
| screen->swap = WIN_FRONT_OK; | |||||
| } | } | ||||
| else if (exchange) { | else { | ||||
| if (screen->swap == WIN_FRONT_OK) { | GPU_offscreen_bind(ar->draw_buffer->offscreen[view], false); | ||||
| ED_screen_draw_edges(win); | |||||
| screen->do_draw = false; | |||||
| screen->swap = WIN_BOTH_OK; | |||||
| wm_draw_callbacks(win); | |||||
| } | |||||
| else if (screen->swap == WIN_BACK_OK) | |||||
| screen->swap = WIN_FRONT_OK; | |||||
| else if (screen->swap == WIN_BOTH_OK) | |||||
| screen->swap = WIN_BOTH_OK; | |||||
| } | } | ||||
| /* draw marked overlapping regions */ | ar->draw_buffer->bound_view = view; | ||||
| for (ar = screen->regionbase.first; ar; ar = ar->next) { | |||||
| if (ar->visible && ar->do_draw) { | /* For now scissor is expected by region drawing, we could disable it | ||||
| CTX_wm_menu_set(C, ar); | * and do the enable/disable in the specific cases that setup scissor. */ | ||||
| ED_region_do_draw(C, ar); | glEnable(GL_SCISSOR_TEST); | ||||
| ar->do_draw = false; | glScissor(0, 0, ar->winx, ar->winy); | ||||
| CTX_wm_menu_set(C, NULL); | |||||
| } | } | ||||
| static void wm_draw_region_unbind(ARegion *ar, int view) | |||||
| { | |||||
| if (!ar->draw_buffer) { | |||||
| return; | |||||
| } | } | ||||
| if (screen->do_draw_gesture) | glDisable(GL_SCISSOR_TEST); | ||||
| wm_gesture_draw(win); | |||||
| /* needs pixel coords in screen */ | ar->draw_buffer->bound_view = -1; | ||||
| if (wm->drags.first) { | |||||
| wm_drags_draw(C, win, NULL); | if (ar->draw_buffer->viewport[view]) { | ||||
| GPU_viewport_unbind(ar->draw_buffer->viewport[view]); | |||||
| } | |||||
| else { | |||||
| GPU_offscreen_unbind(ar->draw_buffer->offscreen[view], false); | |||||
| } | } | ||||
| } | } | ||||
| /****************** draw triple buffer ********************/ | static void wm_draw_region_blit(ARegion *ar, int view) | ||||
| /* - area regions are written into a texture, without any */ | |||||
| /* of the overlapping menus, brushes, gestures. these */ | |||||
| /* are redrawn each time. */ | |||||
| static void wm_draw_triple_free(wmDrawTriple *triple) | |||||
| { | { | ||||
| if (triple) { | if (!ar->draw_buffer) { | ||||
| glDeleteTextures(1, &triple->bind); | return; | ||||
| MEM_freeN(triple); | } | ||||
| if (ar->draw_buffer->viewport[view]) { | |||||
| GPU_viewport_draw_to_screen(ar->draw_buffer->viewport[view], &ar->winrct); | |||||
| } | |||||
| else { | |||||
| GPU_offscreen_draw_to_screen(ar->draw_buffer->offscreen[view], ar->winrct.xmin, ar->winrct.ymin); | |||||
| } | } | ||||
| } | } | ||||
| static void wm_draw_triple_fail(bContext *C, wmWindow *win) | GPUTexture *wm_draw_region_texture(ARegion *ar, int view) | ||||
| { | { | ||||
| wm_draw_window_clear(win); | if (!ar->draw_buffer) { | ||||
| return NULL; | |||||
| } | |||||
| win->drawfail = true; | if (ar->draw_buffer->viewport[view]) { | ||||
| wm_method_draw_overlap_all(C, win, 0); | return GPU_viewport_color_texture(ar->draw_buffer->viewport[view]); | ||||
| } | |||||
| else { | |||||
| return GPU_offscreen_color_texture(ar->draw_buffer->offscreen[view]); | |||||
| } | |||||
| } | } | ||||
| static bool wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple) | void wm_draw_region_blend(ARegion *ar, int view, bool blend) | ||||
| { | { | ||||
| /* compute texture sizes */ | if (!ar->draw_buffer) { | ||||
| const int sizex = WM_window_pixels_x(win); | return; | ||||
| const int sizey = WM_window_pixels_y(win); | } | ||||
| /* generate texture names */ | |||||
| glGenTextures(1, &triple->bind); | |||||
| /* proxy texture is only guaranteed to test for the cases that | /* Alpha is always 1, except when blend timer is running. */ | ||||
| * there is only one texture in use, which may not be the case */ | float alpha = ED_region_blend_alpha(ar); | ||||
| const GLint maxsize = GPU_max_texture_size(); | if (alpha <= 0.0f) { | ||||
| return; | |||||
| } | |||||
| if (sizex > maxsize || sizey > maxsize) { | if (!blend) { | ||||
| printf("WM: failed to allocate texture for triple buffer drawing " | alpha = 1.0f; | ||||
| "(texture too large for graphics card).\n"); | |||||
| return false; | |||||
| } | } | ||||
| /* setup actual texture */ | /* setup actual texture */ | ||||
| glBindTexture(GL_TEXTURE_2D, triple->bind); | GPUTexture *texture = wm_draw_region_texture(ar, view); | ||||
| glActiveTexture(GL_TEXTURE0); | |||||
| /* no mipmaps */ | glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture)); | ||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | |||||
| /* GL_TEXTURE_BASE_LEVEL = 0 by default */ | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | /* wmOrtho for the screen has this same offset */ | ||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | const float halfx = GLA_PIXEL_OFS / (BLI_rcti_size_x(&ar->winrct) + 1); | ||||
| const float halfy = GLA_PIXEL_OFS / (BLI_rcti_size_y(&ar->winrct) + 1); | |||||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, sizex, sizey, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); | |||||
| glBindTexture(GL_TEXTURE_2D, 0); | |||||
| return true; | if (blend) { | ||||
| /* GL_ONE because regions drawn offscreen have premultiplied alpha. */ | |||||
| glEnable(GL_BLEND); | |||||
| glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |||||
| } | } | ||||
| void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) | |||||
| { | |||||
| const int sizex = WM_window_pixels_x(win); | |||||
| const int sizey = WM_window_pixels_y(win); | |||||
| /* wmOrtho for the screen has this same offset */ | |||||
| const float ratiox = 1.0f; | |||||
| const float ratioy = 1.0f; | |||||
| const float halfx = GLA_PIXEL_OFS / sizex; | |||||
| const float halfy = GLA_PIXEL_OFS / sizey; | |||||
| const int activeTex = 7; /* arbitrary */ | |||||
| glActiveTexture(GL_TEXTURE0 + activeTex); | |||||
| glBindTexture(GL_TEXTURE_2D, triple->bind); | |||||
| float x1 = halfx; | |||||
| float x2 = ratiox + halfx; | |||||
| float y1 = halfy; | |||||
| float y2 = ratioy + halfy; | |||||
| GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); | GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); | ||||
| GPU_shader_bind(shader); | GPU_shader_bind(shader); | ||||
| glUniform1i(GPU_shader_get_uniform(shader, "image"), activeTex); | glUniform1i(GPU_shader_get_uniform(shader, "image"), 0); | ||||
| glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), x1, y1, x2, y2); | glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), halfx, halfy, 1.0f + halfx, 1.0f + halfy); | ||||
| glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), 0.0f, 0.0f, sizex, sizey); | glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), ar->winrct.xmin, ar->winrct.ymin, ar->winrct.xmax + 1, ar->winrct.ymax + 1); | ||||
| glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), alpha, alpha, alpha, alpha); | glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), alpha, alpha, alpha, alpha); | ||||
| GWN_draw_primitive(GWN_PRIM_TRI_STRIP, 4); | GWN_draw_primitive(GWN_PRIM_TRI_STRIP, 4); | ||||
| glBindTexture(GL_TEXTURE_2D, 0); | glBindTexture(GL_TEXTURE_2D, 0); | ||||
| } | |||||
| static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) | if (blend) { | ||||
| { | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
| const int sizex = WM_window_pixels_x(win); | |||||
| const int sizey = WM_window_pixels_y(win); | |||||
| glBindTexture(GL_TEXTURE_2D, triple->bind); | |||||
| /* what is GL_READ_BUFFER right now? */ | |||||
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, sizex, sizey); | |||||
| glBindTexture(GL_TEXTURE_2D, 0); | |||||
| } | |||||
| static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *triple) | |||||
| { | |||||
| float fac = ED_region_blend_factor(ar); | |||||
| /* region blend always is 1, except when blend timer is running */ | |||||
| if (fac < 1.0f) { | |||||
| wmViewport(&ar->winrct); | |||||
| glEnable(GL_BLEND); | |||||
| wm_triple_draw_textures(win, triple, 1.0f - fac); | |||||
| glDisable(GL_BLEND); | glDisable(GL_BLEND); | ||||
| } | } | ||||
| } | } | ||||
| static void wm_method_draw_triple(bContext *C, wmWindow *win) | GPUViewport *WM_draw_region_get_viewport(ARegion *ar, int view) | ||||
| { | { | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | if (!ar->draw_buffer) { | ||||
| wmDrawData *dd, *dd_next, *drawdata = win->drawdata.first; | return NULL; | ||||
| bScreen *screen = WM_window_get_active_screen(win); | |||||
| ARegion *ar; | |||||
| bool copytex = false; | |||||
| if (drawdata && drawdata->triple) { | |||||
| #if 0 /* why do we need to clear before overwriting? */ | |||||
| glClearColor(1, 1, 0, 0); | |||||
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
| #endif | |||||
| wmWindowViewport(win); | |||||
| wm_triple_draw_textures(win, drawdata->triple, 1.0f); | |||||
| } | } | ||||
| else { | |||||
| /* we run it when we start OR when we turn stereo on */ | |||||
| if (drawdata == NULL) { | |||||
| drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData"); | |||||
| BLI_addhead(&win->drawdata, drawdata); | |||||
| } | |||||
| drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); | |||||
| if (!wm_triple_gen_textures(win, drawdata->triple)) { | return ar->draw_buffer->viewport[view]; | ||||
| wm_draw_triple_fail(C, win); | |||||
| return; | |||||
| } | |||||
| } | } | ||||
| /* it means stereo was just turned off */ | GPUViewport *WM_draw_region_get_bound_viewport(ARegion *ar) | ||||
| /* note: we are removing all drawdatas that are not the first */ | { | ||||
| for (dd = drawdata->next; dd; dd = dd_next) { | if (!ar->draw_buffer || ar->draw_buffer->bound_view == -1) { | ||||
| dd_next = dd->next; | return NULL; | ||||
| } | |||||
| BLI_remlink(&win->drawdata, dd); | int view = ar->draw_buffer->bound_view; | ||||
| wm_draw_triple_free(dd->triple); | return ar->draw_buffer->viewport[view]; | ||||
| MEM_freeN(dd); | |||||
| } | } | ||||
| wmDrawTriple *triple = drawdata->triple; | static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) | ||||
| { | |||||
| bScreen *screen = WM_window_get_active_screen(win); | |||||
| /* draw marked area regions (also global ones) */ | /* Draw screen areas into own frame buffer. */ | ||||
| ED_screen_areas_iter(win, screen, sa) { | ED_screen_areas_iter(win, screen, sa) { | ||||
| CTX_wm_area_set(C, sa); | CTX_wm_area_set(C, sa); | ||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { | ||||
| if (ar->visible && ar->do_draw) { | if (ar->visible && ar->do_draw) { | ||||
| if (ar->overlap == false) { | |||||
| wm_draw_region(C, ar); | |||||
| copytex = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| wm_area_mark_invalid_backbuf(sa); | |||||
| CTX_wm_area_set(C, NULL); | |||||
| } | |||||
| if (copytex) { | |||||
| wmWindowViewport(win); | |||||
| wm_triple_copy_textures(win, triple); | |||||
| } | |||||
| if (wm->paintcursors.first) { | |||||
| ED_screen_areas_iter(win, screen, sa) { | |||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | |||||
| if (ar->visible && ar == screen->active_region) { | |||||
| CTX_wm_area_set(C, sa); | |||||
| CTX_wm_region_set(C, ar); | CTX_wm_region_set(C, ar); | ||||
| bool use_viewport = wm_region_use_viewport(sa, ar); | |||||
| /* make region ready for draw, scissor, pixelspace */ | if (stereo && wm_draw_region_stereo_set(sa, ar, STEREO_LEFT_ID)) { | ||||
| wmViewport(&ar->winrct); | wm_draw_region_buffer_create(ar, true, use_viewport); | ||||
| wm_paintcursor_draw(C, ar); | |||||
| CTX_wm_region_set(C, NULL); | for (int view = 0; view < 2; view++) { | ||||
| CTX_wm_area_set(C, NULL); | eStereoViews sview; | ||||
| } | if (view == 0) { | ||||
| sview = STEREO_LEFT_ID; | |||||
| } | } | ||||
| else { | |||||
| sview = STEREO_RIGHT_ID; | |||||
| wm_draw_region_stereo_set(sa, ar, sview); | |||||
| } | } | ||||
| wmWindowViewport(win); | wm_draw_region_bind(ar, view); | ||||
| ED_region_do_draw(C, ar); | |||||
| wm_draw_region_unbind(ar, view); | |||||
| } | |||||
| } | |||||
| else { | |||||
| wm_draw_region_buffer_create(ar, false, use_viewport); | |||||
| wm_draw_region_bind(ar, 0); | |||||
| ED_region_do_draw(C, ar); | |||||
| wm_draw_region_unbind(ar, 0); | |||||
| } | } | ||||
| /* draw overlapping area regions (always like popups) */ | ar->do_draw = false; | ||||
| ED_screen_areas_iter(win, screen, sa) { | CTX_wm_region_set(C, NULL); | ||||
| CTX_wm_area_set(C, sa); | |||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | |||||
| if (ar->visible && ar->overlap) { | |||||
| wm_draw_region(C, ar); | |||||
| wm_draw_region_blend(win, ar, triple); | |||||
| } | } | ||||
| } | } | ||||
| wm_area_mark_invalid_backbuf(sa); | |||||
| CTX_wm_area_set(C, NULL); | CTX_wm_area_set(C, NULL); | ||||
| } | } | ||||
| /* after area regions so we can do area 'overlay' drawing */ | /* Draw menus into their own framebuffer. */ | ||||
| ED_screen_draw_edges(win); | for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) { | ||||
| WM_window_get_active_screen(win)->do_draw = false; | |||||
| wm_draw_callbacks(win); | |||||
| /* draw floating regions (menus) */ | |||||
| for (ar = screen->regionbase.first; ar; ar = ar->next) { | |||||
| if (ar->visible) { | if (ar->visible) { | ||||
| CTX_wm_menu_set(C, ar); | CTX_wm_menu_set(C, ar); | ||||
| if (ar->type && ar->type->refresh) { | |||||
| /* UI code reads the OpenGL state, but we have to | |||||
| * refresh beforehand in case the menu size changes. */ | |||||
| wmViewport(&ar->winrct); | |||||
| ar->type->refresh(C, ar); | |||||
| } | |||||
| wm_draw_region_buffer_create(ar, false, false); | |||||
| wm_draw_region_bind(ar, 0); | |||||
| glClearColor(0, 0, 0, 0); | |||||
| glClear(GL_COLOR_BUFFER_BIT); | |||||
| ED_region_do_draw(C, ar); | ED_region_do_draw(C, ar); | ||||
| wm_draw_region_unbind(ar, 0); | |||||
| ar->do_draw = false; | ar->do_draw = false; | ||||
| CTX_wm_menu_set(C, NULL); | CTX_wm_menu_set(C, NULL); | ||||
| } | } | ||||
| } | } | ||||
| /* 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); | |||||
| } | |||||
| } | } | ||||
| static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoViews sview) | static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) | ||||
| { | { | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | wmWindowManager *wm = CTX_wm_manager(C); | ||||
| wmDrawData *drawdata; | |||||
| wmDrawTriple *triple_data, *triple_all; | |||||
| bScreen *screen = WM_window_get_active_screen(win); | bScreen *screen = WM_window_get_active_screen(win); | ||||
| ARegion *ar; | |||||
| int copytex = false; | |||||
| int id; | |||||
| /* we store the triple_data in sequence to triple_all */ | |||||
| for (id = 0; id < 2; id++) { | |||||
| drawdata = BLI_findlink(&win->drawdata, (sview * 2) + id); | |||||
| if (drawdata && drawdata->triple) { | |||||
| if (id == 0) { | |||||
| #if 0 /* why do we need to clear before overwriting? */ | |||||
| glClearColor(0, 0, 0, 0); | |||||
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
| #endif | |||||
| /* Draw into the window framebuffer, in full window coordinates. */ | |||||
| wmWindowViewport(win); | wmWindowViewport(win); | ||||
| glClearColor(0, 0, 0, 0); | |||||
| glClear(GL_COLOR_BUFFER_BIT); | |||||
| wm_triple_draw_textures(win, drawdata->triple, 1.0f); | /* Blit non-overlapping area regions. */ | ||||
| } | |||||
| } | |||||
| else { | |||||
| /* we run it when we start OR when we turn stereo on */ | |||||
| if (drawdata == NULL) { | |||||
| drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData"); | |||||
| BLI_addtail(&win->drawdata, drawdata); | |||||
| } | |||||
| drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); | |||||
| if (!wm_triple_gen_textures(win, drawdata->triple)) { | |||||
| wm_draw_triple_fail(C, win); | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| triple_data = ((wmDrawData *) BLI_findlink(&win->drawdata, sview * 2))->triple; | |||||
| triple_all = ((wmDrawData *) BLI_findlink(&win->drawdata, (sview * 2) + 1))->triple; | |||||
| /* draw marked area regions */ | |||||
| ED_screen_areas_iter(win, screen, sa) { | ED_screen_areas_iter(win, screen, sa) { | ||||
| CTX_wm_area_set(C, sa); | for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { | ||||
| if (ar->visible && ar->overlap == false) { | |||||
| switch (sa->spacetype) { | if (view == -1 && ar->draw_buffer && ar->draw_buffer->stereo) { | ||||
| case SPACE_IMAGE: | /* Stereo drawing from textures. */ | ||||
| { | if(win->stereo3d_format->display_mode == S3D_DISPLAY_ANAGLYPH) { | ||||
| SpaceImage *sima = sa->spacedata.first; | wm_stereo3d_draw_anaglyph(win, ar); | ||||
| sima->iuser.multiview_eye = sview; | |||||
| break; | |||||
| } | |||||
| case SPACE_VIEW3D: | |||||
| { | |||||
| View3D *v3d = sa->spacedata.first; | |||||
| if (v3d->camera && v3d->camera->type == OB_CAMERA) { | |||||
| Camera *cam = v3d->camera->data; | |||||
| CameraBGImage *bgpic = cam->bg_images.first; | |||||
| v3d->multiview_eye = sview; | |||||
| if (bgpic) bgpic->iuser.multiview_eye = sview; | |||||
| } | |||||
| break; | |||||
| } | |||||
| case SPACE_NODE: | |||||
| { | |||||
| SpaceNode *snode = sa->spacedata.first; | |||||
| if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { | |||||
| Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node"); | |||||
| ima->eye = sview; | |||||
| } | |||||
| break; | |||||
| } | |||||
| case SPACE_SEQ: | |||||
| { | |||||
| SpaceSeq *sseq = sa->spacedata.first; | |||||
| sseq->multiview_eye = sview; | |||||
| break; | |||||
| } | } | ||||
| else { | |||||
| wm_stereo3d_draw_interlace(win, ar); | |||||
| } | } | ||||
| /* draw marked area regions */ | |||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | |||||
| if (ar->visible && ar->do_draw) { | |||||
| if (ar->overlap == false) { | |||||
| CTX_wm_region_set(C, ar); | |||||
| ED_region_do_draw(C, ar); | |||||
| if (sview == STEREO_RIGHT_ID) | |||||
| ar->do_draw = false; | |||||
| CTX_wm_region_set(C, NULL); | |||||
| copytex = true; | |||||
| } | } | ||||
| else { | |||||
| /* Blit from offscreen buffer. */ | |||||
| wm_draw_region_blit(ar, 0); | |||||
| } | } | ||||
| } | } | ||||
| wm_area_mark_invalid_backbuf(sa); | |||||
| CTX_wm_area_set(C, NULL); | |||||
| } | } | ||||
| if (copytex) { | |||||
| wmWindowViewport(win); | |||||
| wm_triple_copy_textures(win, triple_data); | |||||
| } | } | ||||
| /* Draw paint cursors. */ | |||||
| if (wm->paintcursors.first) { | if (wm->paintcursors.first) { | ||||
| ED_screen_areas_iter(win, screen, sa) { | ED_screen_areas_iter(win, screen, sa) { | ||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { | ||||
| if (ar->visible && ar == screen->active_region) { | if (ar->visible && ar == screen->active_region) { | ||||
| CTX_wm_area_set(C, sa); | CTX_wm_area_set(C, sa); | ||||
| CTX_wm_region_set(C, ar); | CTX_wm_region_set(C, ar); | ||||
| /* make region ready for draw, scissor, pixelspace */ | /* make region ready for draw, scissor, pixelspace */ | ||||
| wmViewport(&ar->winrct); | |||||
| wm_paintcursor_draw(C, ar); | wm_paintcursor_draw(C, ar); | ||||
| CTX_wm_region_set(C, NULL); | CTX_wm_region_set(C, NULL); | ||||
| CTX_wm_area_set(C, NULL); | CTX_wm_area_set(C, NULL); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| wmWindowViewport(win); | wmWindowViewport(win); | ||||
| } | } | ||||
| /* draw overlapping area regions (always like popups) */ | /* Blend in overlapping area regions */ | ||||
| ED_screen_areas_iter(win, screen, sa) { | ED_screen_areas_iter(win, screen, sa) { | ||||
| CTX_wm_area_set(C, sa); | for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { | ||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | |||||
| if (ar->visible && ar->overlap) { | if (ar->visible && ar->overlap) { | ||||
| CTX_wm_region_set(C, ar); | wm_draw_region_blend(ar, 0, true); | ||||
| ED_region_do_draw(C, ar); | |||||
| if (sview == STEREO_RIGHT_ID) | |||||
| ar->do_draw = false; | |||||
| CTX_wm_region_set(C, NULL); | |||||
| wm_draw_region_blend(win, ar, triple_data); | |||||
| } | } | ||||
| } | } | ||||
| CTX_wm_area_set(C, NULL); | |||||
| } | } | ||||
| /* after area regions so we can do area 'overlay' drawing */ | /* After area regions so we can do area 'overlay' drawing. */ | ||||
| ED_screen_draw_edges(win); | ED_screen_draw_edges(win); | ||||
| if (sview == STEREO_RIGHT_ID) | |||||
| screen->do_draw = false; | |||||
| wm_draw_callbacks(win); | wm_draw_callbacks(win); | ||||
| /* draw floating regions (menus) */ | /* Blend in floating regions (menus). */ | ||||
| for (ar = screen->regionbase.first; ar; ar = ar->next) { | for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) { | ||||
| if (ar->visible) { | if (ar->visible) { | ||||
| CTX_wm_menu_set(C, ar); | wm_draw_region_blend(ar, 0, true); | ||||
| ED_region_do_draw(C, ar); | |||||
| if (sview == STEREO_RIGHT_ID) | |||||
| ar->do_draw = false; | |||||
| CTX_wm_menu_set(C, NULL); | |||||
| } | } | ||||
| } | } | ||||
| /* always draw, not only when screen tagged */ | /* always draw, not only when screen tagged */ | ||||
| if (win->gesture.first) | if (win->gesture.first) | ||||
| wm_gesture_draw(win); | wm_gesture_draw(win); | ||||
| /* needs pixel coords in screen */ | /* needs pixel coords in screen */ | ||||
| if (wm->drags.first) { | if (wm->drags.first) { | ||||
| wm_drags_draw(C, win, NULL); | wm_drags_draw(C, win, NULL); | ||||
| } | } | ||||
| } | |||||
| /* copy the ui + overlays */ | static void wm_draw_window(bContext *C, wmWindow *win) | ||||
| wmWindowViewport(win); | { | ||||
| wm_triple_copy_textures(win, triple_all); | bScreen *screen = WM_window_get_active_screen(win); | ||||
| bool stereo = WM_stereo3d_enabled(win, false); | |||||
| /* Draw area regions into their own framebuffer. This way we can redraw | |||||
| * the areas that need it, and blit the rest from existing framebuffers. */ | |||||
| wm_draw_window_offscreen(C, win, stereo); | |||||
| /* Now we draw into the window framebuffer, in full window coordinates. */ | |||||
| if (!stereo) { | |||||
| /* Regular mono drawing. */ | |||||
| wm_draw_window_onscreen(C, win, -1); | |||||
| } | |||||
| else if(win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) { | |||||
| /* For pageflip we simply draw to both back buffers. */ | |||||
| glDrawBuffer(GL_BACK_LEFT); | |||||
| wm_draw_window_onscreen(C, win, 0); | |||||
| glDrawBuffer(GL_BACK_RIGHT); | |||||
| wm_draw_window_onscreen(C, win, 1); | |||||
| glDrawBuffer(GL_BACK); | |||||
| } | |||||
| else if(ELEM(win->stereo3d_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) { | |||||
| /* For anaglyph and interlace, we draw individual regions with | |||||
| * stereo framebuffers using different shaders. */ | |||||
| wm_draw_window_onscreen(C, win, -1); | |||||
| } | |||||
| else { | |||||
| /* For side-by-side and top-bottom, we need to render each view to an | |||||
| * an offscreen texture and then draw it. This used to happen for all | |||||
| * stereo methods, but it's less efficient than drawing directly. */ | |||||
| const int width = WM_window_pixels_x(win); | |||||
| const int height = WM_window_pixels_y(win); | |||||
| GPUOffScreen *offscreen = GPU_offscreen_create(width, height, 0, false, false, NULL); | |||||
| if (offscreen) { | |||||
| GPUTexture *texture = GPU_offscreen_color_texture(offscreen); | |||||
| wm_draw_offscreen_texture_parameters(offscreen); | |||||
| for (int view = 0; view < 2; view++) { | |||||
| /* Draw view into offscreen buffer. */ | |||||
| GPU_offscreen_bind(offscreen, false); | |||||
| wm_draw_window_onscreen(C, win, view); | |||||
| GPU_offscreen_unbind(offscreen, false); | |||||
| /* Draw offscreen buffer to screen. */ | |||||
| glActiveTexture(GL_TEXTURE0); | |||||
| glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture)); | |||||
| if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) { | |||||
| wm_stereo3d_draw_sidebyside(win, view); | |||||
| } | |||||
| else { | |||||
| wm_stereo3d_draw_topbottom(win, view); | |||||
| } | |||||
| glBindTexture(GL_TEXTURE_2D, 0); | |||||
| } | |||||
| GPU_offscreen_free(offscreen); | |||||
| } | |||||
| else { | |||||
| /* Still draw something in case of allocation failure. */ | |||||
| wm_draw_window_onscreen(C, win, 0); | |||||
| } | |||||
| } | |||||
| screen->do_draw = false; | |||||
| } | } | ||||
| /****************** main update call **********************/ | /****************** main update call **********************/ | ||||
| /* quick test to prevent changing window drawable */ | /* quick test to prevent changing window drawable */ | ||||
| static bool wm_draw_update_test_window(wmWindow *win) | static bool wm_draw_update_test_window(wmWindow *win) | ||||
| { | { | ||||
| /*const*/ struct WorkSpace *workspace = WM_window_get_active_workspace(win); | struct WorkSpace *workspace = WM_window_get_active_workspace(win); | ||||
| /*const*/ Scene *scene = WM_window_get_active_scene(win); | Scene *scene = WM_window_get_active_scene(win); | ||||
| /*const*/ ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene); | ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene); | ||||
| struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); | struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); | ||||
| const bScreen *screen = WM_window_get_active_screen(win); | bScreen *screen = WM_window_get_active_screen(win); | ||||
| ARegion *ar; | ARegion *ar; | ||||
| bool do_draw = false; | bool do_draw = false; | ||||
| for (ar = screen->regionbase.first; ar; ar = ar->next) { | for (ar = screen->regionbase.first; ar; ar = ar->next) { | ||||
| if (ar->do_draw_overlay) { | if (ar->do_draw_overlay) { | ||||
| wm_tag_redraw_overlay(win, ar); | screen->do_draw_paintcursor = true; | ||||
| ar->do_draw_overlay = false; | ar->do_draw_overlay = false; | ||||
| } | } | ||||
| if (ar->visible && ar->do_draw) | if (ar->visible && ar->do_draw) | ||||
| do_draw = true; | do_draw = true; | ||||
| } | } | ||||
| ED_screen_areas_iter(win, screen, sa) { | ED_screen_areas_iter(win, screen, sa) { | ||||
| for (ar = sa->regionbase.first; ar; ar = ar->next) { | for (ar = sa->regionbase.first; ar; ar = ar->next) { | ||||
| Show All 16 Lines | static bool wm_draw_update_test_window(wmWindow *win) | ||||
| if (screen->do_draw_paintcursor) | if (screen->do_draw_paintcursor) | ||||
| return true; | return true; | ||||
| if (screen->do_draw_drag) | if (screen->do_draw_drag) | ||||
| return true; | return true; | ||||
| return false; | return false; | ||||
| } | } | ||||
| static int wm_automatic_draw_method(wmWindow *win) | void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *UNUSED(ar)) | ||||
| { | { | ||||
| /* We assume all supported GPUs now support triple buffer well. */ | if (win) { | ||||
| if (win->drawmethod == USER_DRAW_AUTOMATIC) { | |||||
| 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) { | |||||
| bScreen *screen = WM_window_get_active_screen(win); | bScreen *screen = WM_window_get_active_screen(win); | ||||
| if (wm_automatic_draw_method(win) != USER_DRAW_TRIPLE) | |||||
| ED_region_tag_redraw(ar); | |||||
| screen->do_draw_paintcursor = true; | screen->do_draw_paintcursor = true; | ||||
| } | } | ||||
| } | } | ||||
| void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *ar) | |||||
| { | |||||
| bScreen *screen = WM_window_get_active_screen(win); | |||||
| screen->do_draw_paintcursor = true; | |||||
| wm_tag_redraw_overlay(win, ar); | |||||
| } | |||||
| void wm_draw_update(bContext *C) | void wm_draw_update(bContext *C) | ||||
| { | { | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | wmWindowManager *wm = CTX_wm_manager(C); | ||||
| wmWindow *win; | wmWindow *win; | ||||
| #ifdef WITH_OPENSUBDIV | #ifdef WITH_OPENSUBDIV | ||||
| BKE_subsurf_free_unused_buffers(); | BKE_subsurf_free_unused_buffers(); | ||||
| #endif | #endif | ||||
| GPU_free_unused_buffers(); | GPU_free_unused_buffers(); | ||||
| for (win = wm->windows.first; win; win = win->next) { | for (win = wm->windows.first; win; win = win->next) { | ||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin); | GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin); | ||||
| if (state == GHOST_kWindowStateMinimized) { | if (state == GHOST_kWindowStateMinimized) { | ||||
| /* do not update minimized windows, gives issues on Intel (see T33223) | /* do not update minimized windows, gives issues on Intel (see T33223) | ||||
| * and AMD (see T50856). it seems logical to skip update for invisible | * and AMD (see T50856). it seems logical to skip update for invisible | ||||
| * window anyway. | * window anyway. | ||||
| */ | */ | ||||
| continue; | continue; | ||||
| } | } | ||||
| #endif | #endif | ||||
| if (win->drawmethod != U.wmdrawmethod) { | |||||
| wm_draw_window_clear(win); | |||||
| win->drawmethod = U.wmdrawmethod; | |||||
| } | |||||
| if (wm_draw_update_test_window(win)) { | if (wm_draw_update_test_window(win)) { | ||||
| bScreen *screen = WM_window_get_active_screen(win); | bScreen *screen = WM_window_get_active_screen(win); | ||||
| CTX_wm_window_set(C, win); | CTX_wm_window_set(C, win); | ||||
| /* sets context window+screen */ | /* sets context window+screen */ | ||||
| wm_window_make_drawable(wm, win); | wm_window_make_drawable(wm, win); | ||||
| /* notifiers for screen redraw */ | /* notifiers for screen redraw */ | ||||
| ED_screen_set_active_region(C, &win->eventstate->x); | ED_screen_set_active_region(C, &win->eventstate->x); | ||||
| ED_screen_ensure_updated(wm, win, screen); | ED_screen_ensure_updated(wm, win, screen); | ||||
| int drawmethod = wm_automatic_draw_method(win); | wm_draw_window(C, 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 { /* USER_DRAW_TRIPLE */ | |||||
| if ((WM_stereo3d_enabled(win, false)) == false) { | |||||
| wm_method_draw_triple(C, win); | |||||
| } | |||||
| else { | |||||
| wm_method_draw_triple_multiview(C, win, STEREO_LEFT_ID); | |||||
| wm_method_draw_triple_multiview(C, win, STEREO_RIGHT_ID); | |||||
| wm_method_draw_stereo3d(C, win); | |||||
| } | |||||
| } | |||||
| screen->do_draw_gesture = false; | screen->do_draw_gesture = false; | ||||
| screen->do_draw_paintcursor = false; | screen->do_draw_paintcursor = false; | ||||
| screen->do_draw_drag = false; | screen->do_draw_drag = false; | ||||
| wm_window_swap_buffers(win); | wm_window_swap_buffers(win); | ||||
| CTX_wm_window_set(C, NULL); | CTX_wm_window_set(C, NULL); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void wm_draw_data_free(wmWindow *win) | void wm_draw_region_clear(wmWindow *win, ARegion *UNUSED(ar)) | ||||
| { | |||||
| wmDrawData *dd; | |||||
| for (dd = win->drawdata.first; dd; dd = dd->next) { | |||||
| wm_draw_triple_free(dd->triple); | |||||
| } | |||||
| BLI_freelistN(&win->drawdata); | |||||
| } | |||||
| void wm_draw_window_clear(wmWindow *win) | |||||
| { | { | ||||
| bScreen *screen = WM_window_get_active_screen(win); | bScreen *screen = WM_window_get_active_screen(win); | ||||
| ARegion *ar; | screen->do_draw = true; | ||||
| wm_draw_data_free(win); | |||||
| /* clear screen swap flags */ | |||||
| if (screen) { | |||||
| ED_screen_areas_iter(win, screen, sa) { | |||||
| 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) | void WM_draw_region_free(ARegion *ar) | ||||
| { | { | ||||
| bScreen *screen = WM_window_get_active_screen(win); | wm_draw_region_buffer_free(ar); | ||||
| int drawmethod = wm_automatic_draw_method(win); | ar->visible = 0; | ||||
| if (ELEM(drawmethod, USER_DRAW_OVERLAP, USER_DRAW_OVERLAP_FLIP)) | |||||
| wm_flush_regions_down(win, screen, &ar->winrct); | |||||
| screen->do_draw = true; | |||||
| } | } | ||||
| void WM_redraw_windows(bContext *C) | void WM_redraw_windows(bContext *C) | ||||
| { | { | ||||
| wmWindow *win_prev = CTX_wm_window(C); | wmWindow *win_prev = CTX_wm_window(C); | ||||
| ScrArea *area_prev = CTX_wm_area(C); | ScrArea *area_prev = CTX_wm_area(C); | ||||
| ARegion *ar_prev = CTX_wm_region(C); | ARegion *ar_prev = CTX_wm_region(C); | ||||
| wm_draw_update(C); | wm_draw_update(C); | ||||
| CTX_wm_window_set(C, win_prev); | CTX_wm_window_set(C, win_prev); | ||||
| CTX_wm_area_set(C, area_prev); | CTX_wm_area_set(C, area_prev); | ||||
| CTX_wm_region_set(C, ar_prev); | CTX_wm_region_set(C, ar_prev); | ||||
| } | } | ||||