Efficient thread-safe ktxTexture_VkUploadEx_WithSuballocator() with queue guard callbacks - #1231
Efficient thread-safe ktxTexture_VkUploadEx_WithSuballocator() with queue guard callbacks#1231toomuchvoltage wants to merge 8 commits into
ktxTexture_VkUploadEx_WithSuballocator() with queue guard callbacks#1231Conversation
…in conjunction with guarded suballocator callbacks.
We used to use |
|
I'm using |
The improved validation was post 1.4.313. The reporter of issue #1092 was using 1.4.335. |
…stead at the source.
You were right. This turned out to be unnecessary and I've reverted the change related to it. I realized I could just set the initial layout to |
… test the feature. An actual test case would need multiple textures simultaneously uploaded to and the entire test environment to re-use the mutexes provided in relevant scenarios. Such scenarios include other simultaneous accesses to the queue creating textures or arena `VkDeviceMemory`s.
|
Hi @MarkCallow I just added 82a5bad to demonstrate sample usage of the guarded callbacks. Truth is, it won't stress test the feature nor is that really feasible with the current single-texture test cases. Even if there were test cases requiring multiple textures, the guards within would need to be used application-wide where ever applicable. (i.e. if the graphics queue is creating textures, that would mean re-use for all accesses to graphics queue. Or any arena VkDeviceMemory accesses globally.) If you feel like this is unnecessary, I can revert. Eager to hear back. |
It is great to have a test even if it is not a stress test. I would love to have non-interactive tests of the uploaders, maybe using gtest like I will properly review this PR early next week. Please be aware that I will not merge this until v5.0.0 has been released. I can't give a date for that at present. |
|
Hi @MarkCallow , just circling back on this. It's perfectly fine if this goes out post-5.0.0. Truth is these are on-the-field improvements resulting from a commercial game on Steam shipped with LibKTX2. I'm hesitant to link it since I personally wouldn't feel comfortable with the self promotion here, but of course figuring out the title is trivial given my handle. And I personally do not have experience with GHA CI, but I suspect paid plans (which this should be?) should have no issues with GPU'd instances. |
MarkCallow
left a comment
There was a problem hiding this comment.
Is it necessary to use guarded memory allocation callbacks when using the queue guards
As I am no expert in this, I would like to find an expert to review it. From my side it looks fine except for a couple of minor comment issues.
|
Hi @MarkCallow , appreciate the feedback. The guarded memory callbacks are absolutely necessary. That said, I may have done a more heavy handed version than is necessary with VMA since I used my own pattern from my engine (which obviously needs to be more explicit): https://github.com/toomuchvoltage/HighOmega-public/blob/sauray_vkquake2/HighOmega/src/gl.cpp#L313-L474 VMA's allocation, image/buffer bind and free calls are all thread-safe. However, mapping and unmapping calls are not. They only check to ensure that no For an expert pair of eyes, I would solicit Adam Sawicki's advice. He is the original author of VMA. His handle is @sawickiap on GitHub. I'm confident he's within reach for Khronos. |
|
All done @MarkCallow , ready for another pair of eyes. |
Thanks. Working on finding a reviewer. |
|
One of my Khronos colleagues asked codex to analyze this. This is what it said.
@toomuchvoltage you have already pointed out the last item. It sounds like we need to look at the vdi queue handling. What do you think? |
|
Hi, I'm the developer of the VMA library. I'm sorry for the delayed response. Mapping in VMA is thread-safe. About raw Vulkan (functions
However, using the recommended library functions
For more information, see this documentation chapter: |
So the main contribution of this PR is to make
ktxTexture_VkUploadEx_WithSuballocator()more efficient in a threaded environment. Previously, the entire call would have to be guarded with a queue guard which would effectively make a single upload call block other upload calls or any Vulkan call needing the same queue. With this PR and the introduction ofktxTexture_VkUploadEx_WithSuballocatorAndQueueGuard(), only submissions to the queue inside the call are guarded individually leaving other calls toUploadEx()(or just general queue accesses from Vulkan) unblocked until they need the queue.The PR also includes a couple of other fixes as well:
ktxTexture_LoadImageData()mid-call failed, it would return a failure code but leave mapped memory dangling. This normally would be wasteful but not fatal. However, if we are utilizing synchronization primitives in the callbacks this would lead to a deadlock. Mapping memory would need to enter the critical section of a memory guard (protectingVkDeviceMemoryaccess) and unmapping would have to leave it. This is originally how the issue was discovered.Also changing(erroneous, see below.)destStageFlags = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;todestStageFlags = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;to allow usage of the transfer queue as well. The PR was tested in an engine which exclusively creates textures (KTX or uncompressed) on the transfer queue before doing a queue family ownership transfer to graphics.This was tested on a video game environment with
385KTX textures being loaded by 6 asset loading threads. Resolutions ranged from5548x3636to32x32with the across the board average being1806.04x1755.35.The execution environment had the following hardware specs:
Timing statistics of upload (including the queue guard) before the optimization (3 runs in ms):
Here are the same statistics collected after the optimization:
Here's a screenshot of said game environment:

More information can be provided on the application if requested.