Fix: Realtime compositor outputs wrong EXR passes

The Image node in the realtime compositor will output wrong passes if multiple passes are used at the same time. This is because images can only store a single layer, pass, and view at a time. Furthermore, they rely on operators and RNA callbacks to invalidate the cache when a different layer, pass, or view is requested. In effect, when the image node requests multiple passes at the same time without any kind of cache invalidation, wrong passes are output.

While a proper fix is to allow caching of multiple layers, passes, and views at the same time. This patch implements a temporary workaround by explicitly ensuring an up-to-date cache through a call to BKE_image_ensure_gpu_texture() when before requesting a GPU texture.

Pull Request: https://projects.blender.org/blender/blender/pulls/105547
This commit is contained in:
Omar Emara 2023-03-21 14:57:12 +01:00 committed by Omar Emara
parent 1d43beab84
commit 04d128f545
3 changed files with 41 additions and 0 deletions

@ -493,11 +493,34 @@ struct ImBuf *BKE_image_get_first_ibuf(struct Image *image);
*/
struct GPUTexture *BKE_image_create_gpu_texture_from_ibuf(struct Image *image, struct ImBuf *ibuf);
/**
* Ensure that the cached GPU texture inside the image matches the pass, layer, and view of the
* given image user, if not, invalidate the cache such that the next call to the GPU texture
* retrieval functions such as BKE_image_get_gpu_texture updates the cache with an image that
* matches the give image user.
*
* This is provided as a separate function and not implemented as part of the GPU texture retrieval
* functions because the current cache system only allows a single pass, layer, and stereo view to
* be cached, so possible frequent invalidations of the cache can have performance implications,
* and making invalidation explicit by calling this function will help make that clear and pave the
* way for a more complete cache system in the future.
*/
void BKE_image_ensure_gpu_texture(struct Image *image, struct ImageUser *iuser);
/**
* Get the #GPUTexture for a given `Image`.
*
* `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already
* available. It is also required when requesting the #GPUTexture for a render result.
*
* The requested GPU texture will be cached for subsequent calls, but only a single layer, pass,
* and view can be cached at a time, so the cache should be invalidated in operators and RNA
* callbacks that change the layer, pass, or view of the image to maintain a correct cache state.
* However, in some cases, multiple layers, passes, or views might be needed at the same time, like
* is the case for the realtime compositor. This is currently not supported, so the caller should
* ensure that the requested layer is indeed the cached one and invalidated the cached otherwise by
* calling BKE_image_ensure_gpu_texture. This is a workaround until image can support a more
* complete caching system.
*/
struct GPUTexture *BKE_image_get_gpu_texture(struct Image *image,
struct ImageUser *iuser,

@ -336,6 +336,20 @@ static void image_gpu_texture_try_partial_update(Image *image, ImageUser *iuser)
}
}
void BKE_image_ensure_gpu_texture(Image *image, ImageUser *image_user)
{
if (!image) {
return;
}
/* Note that the image can cache both sterio views, so we only invalidate the cache if the view
* index is more than 2. */
if (image->gpu_pass != image_user->pass || image->gpu_layer != image_user->layer ||
(image->gpu_view != image_user->multi_index && image_user->multi_index >= 2)) {
BKE_image_partial_update_mark_full_update(image);
}
}
static GPUTexture *image_get_gpu_texture(Image *ima,
ImageUser *iuser,
ImBuf *ibuf,
@ -365,6 +379,9 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
ima->gpu_pass = requested_pass;
ima->gpu_layer = requested_layer;
ima->gpu_view = requested_view;
/* The cache should be invalidated here, but it is intentionally isn't due to possible
* performance implications, see the BKE_image_ensure_gpu_texture function for more
* information. */
}
#undef GPU_FLAGS_TO_CHECK

@ -516,6 +516,7 @@ class ImageOperation : public NodeOperation {
}
ImageUser image_user = compute_image_user_for_output(identifier);
BKE_image_ensure_gpu_texture(get_image(), &image_user);
GPUTexture *image_texture = BKE_image_get_gpu_texture(get_image(), &image_user, nullptr);
const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture));