Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/image_gpu.c
| Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
| #include "GPU_state.h" | #include "GPU_state.h" | ||||
| #include "GPU_texture.h" | #include "GPU_texture.h" | ||||
| #include "PIL_time.h" | #include "PIL_time.h" | ||||
| /* Prototypes. */ | /* Prototypes. */ | ||||
| static void gpu_free_unused_buffers(void); | static void gpu_free_unused_buffers(void); | ||||
| static void image_free_gpu(Image *ima, const bool immediate); | static void image_free_gpu(Image *ima, const bool immediate); | ||||
| static void image_update_gputexture_ex( | |||||
| Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h); | |||||
| /* Internal structs. */ | |||||
| typedef struct ImagePartialRefresh { | |||||
| struct ImagePartialRefresh *next, *prev; | |||||
| int offset_x; | |||||
| int offset_y; | |||||
| int width; | |||||
| int height; | |||||
| } ImagePartialRefresh; | |||||
| /* Is the alpha of the `GPUTexture` for a given image/ibuf premultiplied. */ | /* Is the alpha of the `GPUTexture` for a given image/ibuf premultiplied. */ | ||||
| bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) | bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) | ||||
| { | { | ||||
| if (image) { | if (image) { | ||||
| /* Render result and compositor output are always premultiplied */ | /* Render result and compositor output are always premultiplied */ | ||||
| if (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { | if (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { | ||||
| return true; | return true; | ||||
| Show All 9 Lines | else if (ibuf) { | ||||
| } | } | ||||
| else { | else { | ||||
| return image ? (image->alpha_mode == IMA_ALPHA_PREMUL) : true; | return image ? (image->alpha_mode == IMA_ALPHA_PREMUL) : true; | ||||
| } | } | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
fclem: Nitpick: Maybe `size_match()` is better function name (inverted obviously).
But to be fair… | |||||
| /** \name UDIM gpu texture | /** \name UDIM gpu texture | ||||
| * \{ */ | * \{ */ | ||||
| static bool is_over_resolution_limit(int w, int h) | static bool is_over_resolution_limit(int w, int h) | ||||
| { | { | ||||
| return (w > GPU_texture_size_with_limit(w) || h > GPU_texture_size_with_limit(h)); | return (w > GPU_texture_size_with_limit(w) || h > GPU_texture_size_with_limit(h)); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 219 Lines • ▼ Show 20 Lines | static GPUTexture *image_get_gpu_texture(Image *ima, | ||||
| if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer || | if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer || | ||||
| ima->gpu_slot != requested_slot) { | ima->gpu_slot != requested_slot) { | ||||
| ima->gpu_pass = requested_pass; | ima->gpu_pass = requested_pass; | ||||
| ima->gpu_layer = requested_layer; | ima->gpu_layer = requested_layer; | ||||
| ima->gpu_slot = requested_slot; | ima->gpu_slot = requested_slot; | ||||
| ima->gpuflag |= IMA_GPU_REFRESH; | ima->gpuflag |= IMA_GPU_REFRESH; | ||||
| } | } | ||||
| /* currently, gpu refresh tagging is used by ima sequences */ | ImageTile *tile = BKE_image_get_tile(ima, 0); | ||||
| if (ima->gpuflag & IMA_GPU_REFRESH) { | if (ima->gpuflag & IMA_GPU_REFRESH) { | ||||
| image_free_gpu(ima, true); | image_free_gpu(ima, true); | ||||
| ima->gpuflag &= ~IMA_GPU_REFRESH; | |||||
| } | } | ||||
| else if (ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) { | |||||
| ImagePartialRefresh *refresh_area; | |||||
Done Inline ActionsIf ibuf is NULL and partial refresh is requested, I think it should do a full free of the image. I'm not sure if that happens in practice, but seems safer. brecht: If `ibuf` is `NULL` and partial refresh is requested, I think it should do a full free of the… | |||||
| while ((refresh_area = BLI_pophead(&ima->gpu_refresh_areas))) { | |||||
| printf("%s: partial_refres(%d, %d, %d, %d)\n", | |||||
| __func__, | |||||
| refresh_area->offset_x, | |||||
Done Inline Actionstile is assumed to be non-NULL here and ok here, it's not clear that is safe. brecht: `tile` is assumed to be non-NULL here and ok here, it's not clear that is safe. | |||||
| refresh_area->offset_y, | |||||
| refresh_area->width, | |||||
| refresh_area->height); | |||||
| image_update_gputexture_ex(ima, | |||||
| tile, | |||||
| ibuf, | |||||
| refresh_area->offset_x, | |||||
| refresh_area->offset_y, | |||||
| refresh_area->width, | |||||
| refresh_area->height); | |||||
| MEM_freeN(refresh_area); | |||||
| } | |||||
| } | |||||
| ima->gpuflag &= ~(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH); | |||||
| /* currently, gpu refresh tagging is used by ima sequences */ | |||||
Done Inline ActionsBKE_image_tag_time is for garbage collection, not image sequences. So I'm not sure what this comment is about. brecht: `BKE_image_tag_time` is for garbage collection, not image sequences. So I'm not sure what this… | |||||
| /* Tag as in active use for garbage collector. */ | /* Tag as in active use for garbage collector. */ | ||||
| BKE_image_tag_time(ima); | BKE_image_tag_time(ima); | ||||
| /* Test if we already have a texture. */ | /* Test if we already have a texture. */ | ||||
| const int current_view = iuser ? ((iuser->flag & IMA_SHOW_STEREO) != 0 ? iuser->multiview_eye : | const int current_view = iuser ? ((iuser->flag & IMA_SHOW_STEREO) != 0 ? iuser->multiview_eye : | ||||
| iuser->view) : | iuser->view) : | ||||
| 0; | 0; | ||||
| GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view); | GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view); | ||||
| if (*tex) { | if (*tex) { | ||||
| return *tex; | return *tex; | ||||
| } | } | ||||
| /* Check if we have a valid image. If not, we return a dummy | /* Check if we have a valid image. If not, we return a dummy | ||||
| * texture with zero bind-code so we don't keep trying. */ | * texture with zero bind-code so we don't keep trying. */ | ||||
| ImageTile *tile = BKE_image_get_tile(ima, 0); | |||||
| if (tile == NULL || tile->ok == 0) { | if (tile == NULL || tile->ok == 0) { | ||||
| *tex = image_gpu_texture_error_create(textarget); | *tex = image_gpu_texture_error_create(textarget); | ||||
| return *tex; | return *tex; | ||||
| } | } | ||||
| /* check if we have a valid image buffer */ | /* check if we have a valid image buffer */ | ||||
| ImBuf *ibuf_intern = ibuf; | ImBuf *ibuf_intern = ibuf; | ||||
| if (ibuf_intern == NULL) { | if (ibuf_intern == NULL) { | ||||
| ▲ Show 20 Lines • Show All 401 Lines • ▼ Show 20 Lines | static void gpu_texture_update_from_ibuf( | ||||
| } | } | ||||
| else { | else { | ||||
| ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; | ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; | ||||
| } | } | ||||
| GPU_texture_unbind(tex); | GPU_texture_unbind(tex); | ||||
| } | } | ||||
| static void image_update_gputexture_ex( | |||||
| Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h) | |||||
| { | |||||
| GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; | |||||
| /* Check if we need to update the main gputexture. */ | |||||
| if (tex != NULL && tile == ima->tiles.first) { | |||||
| gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); | |||||
| } | |||||
| /* Check if we need to update the array gputexture. */ | |||||
| tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; | |||||
| if (tex != NULL) { | |||||
| gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); | |||||
| } | |||||
| } | |||||
| /* Partial update of texture for texture painting. This is often much | /* Partial update of texture for texture painting. This is often much | ||||
| * quicker than fully updating the texture for high resolution images. */ | * quicker than fully updating the texture for high resolution images. */ | ||||
| void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h) | void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h) | ||||
| { | { | ||||
| ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); | ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); | ||||
| ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); | ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); | ||||
| if ((ibuf == NULL) || (w == 0) || (h == 0)) { | if ((ibuf == NULL) || (w == 0) || (h == 0)) { | ||||
| /* Full reload of texture. */ | /* Full reload of texture. */ | ||||
| BKE_image_free_gputextures(ima); | BKE_image_free_gputextures(ima); | ||||
| } | } | ||||
| image_update_gputexture_ex(ima, tile, ibuf, x, y, w, h); | |||||
| GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; | BKE_image_release_ibuf(ima, ibuf, NULL); | ||||
| /* Check if we need to update the main gputexture. */ | |||||
| if (tex != NULL && tile == ima->tiles.first) { | |||||
| gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); | |||||
| } | } | ||||
| /* Check if we need to update the array gputexture. */ | void BKE_image_update_gpu_texture_delayed( | ||||
| tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; | struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h) | ||||
| if (tex != NULL) { | { | ||||
| gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); | /* Check for full refresh. */ | ||||
| if (ibuf && x == 0 && y == 0 && w == ibuf->x && h == ibuf->y) { | |||||
| ima->gpuflag |= IMA_GPU_REFRESH; | |||||
| } | |||||
| /* Check if we can promote partial refresh to a full refresh. */ | |||||
| if ((ima->gpuflag & (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) == | |||||
| (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) { | |||||
| ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH; | |||||
| BLI_freelistN(&ima->gpu_refresh_areas); | |||||
| } | |||||
| /* Image is already marked for complete refresh. */ | |||||
| if (ima->gpuflag & IMA_GPU_REFRESH) { | |||||
| return; | |||||
| } | } | ||||
| BKE_image_release_ibuf(ima, ibuf, NULL); | ImagePartialRefresh *area = MEM_mallocN(sizeof(ImagePartialRefresh), __func__); | ||||
| area->offset_x = x; | |||||
Done Inline ActionsThis has O(n^2) time complexity, but I guess it's not that since n is only the number of threads in practice. If it was for all tiles in the image it would be a bigger problem, see rB8c113eb0c475: Render: Use GHash for storing render parts. brecht: This has O(n^2) time complexity, but I guess it's not that since n is only the number of… | |||||
| area->offset_y = y; | |||||
| area->width = w; | |||||
| area->height = h; | |||||
| BLI_addtail(&ima->gpu_refresh_areas, area); | |||||
| ima->gpuflag |= IMA_GPU_PARTIAL_REFRESH; | |||||
| } | } | ||||
| /* these two functions are called on entering and exiting texture paint mode, | /* these two functions are called on entering and exiting texture paint mode, | ||||
| * temporary disabling/enabling mipmapping on all images for quick texture | * temporary disabling/enabling mipmapping on all images for quick texture | ||||
| * updates with glTexSubImage2D. images that didn't change don't have to be | * updates with glTexSubImage2D. images that didn't change don't have to be | ||||
| * re-uploaded to OpenGL */ | * re-uploaded to OpenGL */ | ||||
| void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) | void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) | ||||
| { | { | ||||
| Show All 25 Lines | |||||
Nitpick: Maybe size_match() is better function name (inverted obviously).
But to be fair, having a function that is used only once and is a one liner seems a bit silly and make the code a bit harder to read.