GPUTexture partial update #92613

Closed
opened 2021-10-29 15:42:07 +02:00 by Jeroen Bakker · 7 comments
Member

Current state: WIP - Still want to perform some tests and discuss with Brecht and Clement.

Partial Updates

Blender supports partial updating of images. When a user draws a stroke on an image or in the 3d view during texture painting only the area that is changed is send to the GPU. This can be done directly or post-poned until the next time any GPU texture is requested from the image.

BKE_image_update_gputexture: called during painting and will update all known GPU textures of the given image.
BKE_image_update_gputexture_delayed: called during rendering and will update GPU textures when the first GPU texture is requested.

Currently this works as an image keeps track of each GPU texture and its usage/texture format.
Limitation of this solution is that GPU textures are updated, even when not used (anymore).
Different texture formats would be implemented as part of the image. The gputexture attribute of Image might get a new dimension and add gaps.

typedef struct Image{
  ...
  /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes, 2 = IMA_TEXTURE_RESOLUTION_LEN. */
  struct GPUTexture *gputexture[3][2][2];

Approach

Record the changes in partial_updates inside the image. A circular ring with a fix size. When a partial update is recorded it will store the area inside the list. When an area wants to perform a partial update of an GPU texture it can retrieve the combined area from its last partial_update_id to the last recorded transaction. When the last partial_update_id isn't known anymore (was removed from the partial update list) a full update should be done.
When done the last partial_update_id is updated to the latest recorded partial_update_id.

NOTE: The area of partial updates are recorded in tiles. (see IMA_PARTIAL_REFRESH_TILE_SIZE default size (256*256)

NOTE: this is an C API, an C++ API could be different.

/*
 * Calculate the rect to update.
 * \param image: The image to request the rect from.
 * \param from_parial_update_id: last known parial_update_id  from the caller. When -1 is requested the full list is requested.
 * \param r_area_to_update: area that contains the area from all updates between `from_partial_update_id+1` to the last_recorded_partial_update_id known by the image. When from_partial_update is the last recorded partial update id it will contain no tiles.
 * \param r_last_recorded_partial_update_id: received the last known recorded partial_update id of the image.
 *     caller should use this the next time when invoking this function.
 * \return: false - couldn't determine the rect, caller should perform a full update.
 *   true: determined area, caller can perform a partial update.
 */
bool BKE_image_partial_update_calc_area(&const Image *image, int from_transaction_id, ImagePartialUpdateArea &r_area_to_update, int &r_last_recorded_transaction_id)

The ImagePartialUpdateArea is a data structure that stores the tiles (tile coordinates) that have changes. We need to experiment with the best data structure.

  • TileMerging: does it make a difference when we merge neighboring tiles into a single bigger tile. It saves some allocations. The data structure
  • Reduce circle list by moving to the next element in the circle when an area is requested. Would reduce the amount of allocations and reduce evaluating same data multiple times.
  • Keep track of the area to update per listener. least amount of evaluation, but how to setup a clean API for this?

I think TileMerging and reducing elements in the circle would benefit the most. The reduction would almost be the same as keeping tract of listeners, except in a clean way for the API user.

BKE_image_update_gputexture will be removed. as all updated needs to happen delayed.
BKE_image_update_gputexture_delayed will add a rect to the partial_update circular list. partial_update id will be increased. Note that it isn't decided if atomics are needed. Could be that everything works from the main thread.

Use case

  • Space Image will manage their own GPU textures to draw huge images, without allocating/transferring too much towards GPU.
  • Cycles partial texture updates?

Alternatives

During design some alternatives had been research:

  • Use wmMsgBus to communicate the areas that needed to be uploaded: wmMsgBus is designed for DNA/RNA UI communication.
  • Use of wmNotify
Current state: `WIP` - Still want to perform some tests and discuss with Brecht and Clement. ## Partial Updates Blender supports partial updating of images. When a user draws a stroke on an image or in the 3d view during texture painting only the area that is changed is send to the GPU. This can be done directly or post-poned until the next time any GPU texture is requested from the image. `BKE_image_update_gputexture`: called during painting and will update all known GPU textures of the given image. `BKE_image_update_gputexture_delayed`: called during rendering and will update GPU textures when the first GPU texture is requested. Currently this works as an image keeps track of each GPU texture and its usage/texture format. Limitation of this solution is that GPU textures are updated, even when not used (anymore). Different texture formats would be implemented as part of the image. The gputexture attribute of Image might get a new dimension and add gaps. ``` typedef struct Image{ ... /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes, 2 = IMA_TEXTURE_RESOLUTION_LEN. */ struct GPUTexture *gputexture[3][2][2]; ``` ## Approach Record the changes in partial_updates inside the image. A circular ring with a fix size. When a partial update is recorded it will store the area inside the list. When an area wants to perform a partial update of an GPU texture it can retrieve the combined area from its last partial_update_id to the last recorded transaction. When the last partial_update_id isn't known anymore (was removed from the partial update list) a full update should be done. When done the last partial_update_id is updated to the latest recorded partial_update_id. NOTE: The area of partial updates are recorded in tiles. (see `IMA_PARTIAL_REFRESH_TILE_SIZE` default size (256*256) NOTE: this is an C API, an C++ API could be different. ``` /* * Calculate the rect to update. * \param image: The image to request the rect from. * \param from_parial_update_id: last known parial_update_id from the caller. When -1 is requested the full list is requested. * \param r_area_to_update: area that contains the area from all updates between `from_partial_update_id+1` to the last_recorded_partial_update_id known by the image. When from_partial_update is the last recorded partial update id it will contain no tiles. * \param r_last_recorded_partial_update_id: received the last known recorded partial_update id of the image. * caller should use this the next time when invoking this function. * \return: false - couldn't determine the rect, caller should perform a full update. * true: determined area, caller can perform a partial update. */ bool BKE_image_partial_update_calc_area(&const Image *image, int from_transaction_id, ImagePartialUpdateArea &r_area_to_update, int &r_last_recorded_transaction_id) ``` The `ImagePartialUpdateArea` is a data structure that stores the tiles (tile coordinates) that have changes. We need to experiment with the best data structure. * TileMerging: does it make a difference when we merge neighboring tiles into a single bigger tile. It saves some allocations. The data structure * Reduce circle list by moving to the next element in the circle when an area is requested. Would reduce the amount of allocations and reduce evaluating same data multiple times. * Keep track of the area to update per listener. least amount of evaluation, but how to setup a clean API for this? I think TileMerging and reducing elements in the circle would benefit the most. The reduction would almost be the same as keeping tract of listeners, except in a clean way for the API user. `BKE_image_update_gputexture` will be removed. as all updated needs to happen delayed. `BKE_image_update_gputexture_delayed` will add a rect to the partial_update circular list. partial_update id will be increased. Note that it isn't decided if atomics are needed. Could be that everything works from the main thread. ## Use case * Space Image will manage their own GPU textures to draw huge images, without allocating/transferring too much towards GPU. * Cycles partial texture updates? ## Alternatives During design some alternatives had been research: * Use wmMsgBus to communicate the areas that needed to be uploaded: wmMsgBus is designed for DNA/RNA UI communication. * Use of wmNotify
Jeroen Bakker self-assigned this 2021-10-29 15:42:07 +02:00
Author
Member

Changed status from 'Needs Triage' to: 'Confirmed'

Changed status from 'Needs Triage' to: 'Confirmed'
Author
Member

Added subscriber: @Jeroen-Bakker

Added subscriber: @Jeroen-Bakker

Added subscriber: @JacobMerrill-1

Added subscriber: @JacobMerrill-1

I noted that drawing itself is quite slow when the brush size is maxed out,

I noted that drawing itself is quite slow when the brush size is maxed out,

This issue was referenced by 0a32ac02e9

This issue was referenced by 0a32ac02e976a4723802ed09bed64c0625e889a2
Author
Member

Changed status from 'Confirmed' to: 'Resolved'

Changed status from 'Confirmed' to: 'Resolved'
Author
Member

Design has been implementation (blender 3.1)

Design has been implementation (blender 3.1)
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#92613
No description provided.