KTX 2.0 and basis file runtime import with KtxUnity

Hi fellow pixelpushers,

Let me introduce to you a Unity Package: KtxUnity

Basis Universal supercompression by Binomial is some awesome technology that allows fast downloads and low memory footprint textures whilst being platform independent (one texture format that works everywhere).

BasisU supercompression is supported in KTX 2.0, which is an open texture container format by the Khronos Group (in beta, release imminent). Another Khronos standard, the glTF 3D format supports KTX 2.0 textures as well and so this is of great interest for some industries and tinkerers like us.

The last months I’ve been helping Khronos to get KTX-Software (specifically the CMake build) ready and created Unity wrappers and a package for it.

KtxUnity works on WebGL and the major desktop and mobile platforms. It is pleasently fast due to threading via the C# job system.

It is actively used in a couple of glTF importers already (like my own, glTFast).

Some bits and optimizations are missing, but I feel confident enough to promote it at this point.

I’m glad if it is useful to some and I’m open for feedback and contributions.

Cheers,
Andi

4 Likes

Hey tteneder,

i am using your package in a Unity project for initializing textures that are downloaded from the web (WebGL app).
It’s pretty fast and working very well.
However, my textures are pretty big and WebGL is not multi-threaded and somehow memory-bottlenecked.
Initialising big textures (big amout of memory allocation) makes the WebGL app therefore freeze for a short period of time.
To solve this issue, i had to split the textures into multiple parts and reference all of them in the shader simultaneously.
I don’t like this solution much, however it’s the only working solution at the moment.

So is there any chance that you could introduce a function that doesn’t allocate the KTXTexture from the given byte[ ] at once (the download handler seems to do a pretty good job streaming the content into the byte[ ]), but, let’s say, it only allocates x% per frame? This function would need to be a coroutine that we could wait until it finishes (since WebGL is single-threaded).

Best Regards,
Kreshi

PS: Very very good job! I really appreciate what you have created here!

1 Like

Thanks for the flowers, great to see you can put it to good use!

Without taking a close look I think what you suggest could be achieved by splitting up the transcoding function. Either on a mipmap level or even more granular, on a block (or block range) level.

The fact that mipmap are re-sorted in memory after transcoding at the moment is also not great. Transcoding them in the correct order to start with would also improve things.

Combined with streaming (transcode small mip maps first and add more details as download progresses) would be the ideal solution.

Thanks for bringing this to my attention!

That being said: All of this requires deep changes in the underlying KTX-Software lib (maybe even BasisU). There’s no plan to do that right now, but I may look into mip-map level transcoding at some point.

Kind regards,
Andi

Hi tteneder,

We noticed the 64 bit Android plugin is not working on builds in release mode on KTX files, only basis files. Do you know what may be wrong with that?

Seems like a bug. Follow the GitHub issue for updates.

Splitting it on a block-level would be preferred, since a texture could have no mipmaps and even if it always had mipmpas, the highest resolution mipmap could still be too big to be transcoded at once.

That’s true, it would make mipmaps mandatory but in this case thats perfectly fine anyway.

I understand. Although I would be very happy about a block-level splitting solution since right now thats a performance bottleneck for high resolution textures :).
The mipmap streaming part i could potentially workaround myself by creating a custom data-format containing the mipmaps ordered as individual KTXTextures and load / set the newest mipmap as the main texture when ready.

Best Regards,
Kreshi

1 Like

I have managed to use ktx2 file on webgl with success but only if there is a few files downloaded at the time, if i have a lot of them then it randomly crashes. i tried to replace tasks with coroutines, it didn’t work as someone suggested at git repo issues.

errors are memory access out of bounds but i have printed current memory consumption in webgl it’s rather low. it’s hard to track line causes memory out of bounds. can somebody help.

also i was wondering if i can use basis files instead of ktx2. it might resolve the issue maybe? but i am not sure how to produce basis files. with ktx it’s easy to resize the texture and compress with basis i couldnt resize or i am doing something wrong

thanks!

here is the log from development build:

UnityLoader.js:1150 exception thrown: RuntimeError: memory access out of bounds,RuntimeError: memory access out of bounds
at _free (:wasm-function[102639]:0x1b6e732)
at __ZN17LowLevelAllocator4FreeEPvm (:wasm-function[9445]:0x35f7db)
at __ZN21UnityDefaultAllocatorI17LowLevelAllocatorE10DeallocateEPv (:wasm-function[14479]:0x590f23)
at __ZN13MemoryManager10DeallocateEPvPKci (:wasm-function[5218]:0x2240ea)
at __ZN13MemoryManager10DeallocateEPvRK10MemLabelIdPKci (:wasm-function[5215]:0x223846)
at __ZdlPv (:wasm-function[5290]:0x2292bd)
at _ktxTexture2_TranscodeBasis (:wasm-function[102537]:0x1b3bf15)
at _KtxNativeInstance_ktxTexture2_TranscodeBasis_mEF854FD64B36B71C37CD79C6BADD8A174F6C688F (:wasm-function[102291]:0x1b27265)
at _KtxTranscodeJob_Execute_m5D0F41A62046752B7A342892205276225A0382B5 (:wasm-function[102300]:0x1b283e8)
at _JobStruct_1_Execute_m5900B9D5D53AB2343FDF29E41D32FD1823E211E4_gshared (:wasm-function[86891]:0x17f8dc5)
at _ExecuteJobFunction_Invoke_m0A741C67F97C0CBA7AA3AAB630DBC79F3342430A_gshared (:wasm-function[86885]:0x17f8913)
at _Z237RuntimeInvoker_TrueVoid_t22962CB4C05B1D89B55A6E1139F0E87A90987017_RuntimeObject_Int32_t585191389E07734F19F3156FF88FB3EF4800D102_Int32_t585191389E07734F19F3156FF88FB3EF4800D102_RuntimeObject_Int32_t585191389E07734F19F3156FF88FB3EF4800D102PFvvEPK10MethodInfoPvPS4 (:wasm-function[44548]:0xff246e)
at dynCall_iiiii (:wasm-function[103255]:0x1b99472)
at Object.dynCall_iiiii (blob:https://www.replaced.com/04fcea6f-dbba-4376-b5d0-80e364b03f46:27843:40)
at invoke_iiiii (blob:https://www.replaced.com/04fcea6f-dbba-4376-b5d0-80e364b03f46:17719:33)
at __ZN6il2cpp2vm7Runtime6InvokeEPK10MethodInfoPvPS5_PP15Il2CppException (:wasm-function[39637]:0xf72e8a)
at _il2cpp_runtime_invoke (:wasm-function[38611]:0xf50084)
at __Z23scripting_method_invoke18ScriptingMethodPtr18ScriptingObjectPtrR18ScriptingArgumentsP21ScriptingExceptionPtrb (:wasm-function[5536]:0x23ad0c)
at __ZN19ScriptingInvocation6InvokeEP21ScriptingExceptionPtrb (:wasm-function[5533]:0x23a86e)
at __ZL10ExecuteJobP14ManagedJobDataPFvPvS1_S1_S1_iEiPh (:wasm-function[16120]:0x641256)
at __ZL19ForwardJobToManagedP14ManagedJobData (:wasm-function[16119]:0x64100f)
at _ZN18JobBatchDispatcher26ScheduleJobDependsInternalER8JobFencePFvPvES2_RKS0 (:wasm-function[9695]:0x380aaa)
at __Z18ScheduleManagedJobRK21JobScheduleParametersPv (:wasm-function[16118]:0x640f74)
at __Z36JobsUtility_CUSTOM_Schedule_InjectedR21JobScheduleParametersR8JobFence (:wasm-function[16664]:0x67a5d3)
at _JobsUtility_Schedule_Injected_mC918219A2045C68697BD2C7FCE7DCA515CE09C01 (:wasm-function[86969]:0x17fb9df)
at _JobsUtility_Schedule_m544BE1DBAEFF069809AE5304FD6BBFEE2927D4C3 (:wasm-function[86968]:0x17fb999)
at _IJobExtensions_Schedule_TisKtxTranscodeJob_t08D80333C4F4CB05B55FF154E746ED8763D84505_m3753A8C2E164F8D930FF6FA5AF0F5C45FB7FEE20_gshared (:wasm-function[58474]:0x125ce63)
at __Z126IJobExtensions_Schedule_TisKtxTranscodeJob_t08D80333C4F4CB05B55FF154E746ED8763D84505_m3753A8C2E164F8D930FF6FA5AF0F5C45FB7FEE2057KtxTranscodeJob_t08D80333C4F4CB05B55FF154E746ED8763D8450551JobHandle_tDA498A2E49AEDE014468F416A8A98A6B258D73D1PK10MethodInfo (:wasm-function[102287]:0x1b27163)
at _KtxNativeInstance_LoadBytesJob_m6E4F3394855DF18E768AF070CFA2A4AE52957315 (:wasm-function[102286]:0x1b270e7)
at _U3CTranscodeU3Ed__1_MoveNext_m265B7CCA22CDEADFC560D6B53BF7154593BF071D (:wasm-function[102296]:0x1b27754)
at __ZN21InterfaceFuncInvoker0IP64DeserializationContext_t51031F3163BAABA5CC204ACBE71ACE98C86E26D7E6InvokeEjP11Il2CppClassP12Il2CppObject (:wasm-function[30217]:0xd1b209)
at _SetupCoroutine_InvokeMoveNext_m9106BA4E8AE0E794B17F184F1021A53F1D071F31 (:wasm-function[88817]:0x1832020)
at _Z164RuntimeInvoker_FalseVoid_t22962CB4C05B1D89B55A6E1139F0E87A90987017_VoidU2A_t3A9D5EB5A83DC9C93DF2C4D6EA67B0F5E885889A_Int32_t585191389E07734F19F3156FF88FB3EF4800D102PFvvEPK10MethodInfoPvPS4 (:wasm-function[44799]:0xff6f16)
at dynCall_iiiii (:wasm-function[103255]:0x1b99472)
at Object.dynCall_iiiii (blob:https://www.replaced.com/04fcea6f-dbba-4376-b5d0-80e364b03f46:27843:40)
at invoke_iiiii (blob:https://www.replaced.com/04fcea6f-dbba-4376-b5d0-80e364b03f46:17719:33)
at __ZN6il2cpp2vm7Runtime6InvokeEPK10MethodInfoPvPS5_PP15Il2CppException (:wasm-function[39637]:0xf72e8a)
at _il2cpp_runtime_invoke (:wasm-function[38611]:0xf50084)
at __Z23scripting_method_invoke18ScriptingMethodPtr18ScriptingObjectPtrR18ScriptingArgumentsP21ScriptingExceptionPtrb (:wasm-function[5536]:0x23ad0c)
at __ZN19ScriptingInvocation6InvokeEP21ScriptingExceptionPtrb (:wasm-function[5533]:0x23a86e)
at __ZN9Coroutine14InvokeMoveNextEP21ScriptingExceptionPtr (:wasm-function[9795]:0x390031)
at __ZN9Coroutine3RunEPb (:wasm-function[9792]:0x38fd1c)
at __ZN13MonoBehaviour24TryCreateAndRunCoroutineE18ScriptingObjectPtr18ScriptingMethodPtrPP9Coroutine (:wasm-function[9789]:0x38fc97)
at __ZN13MonoBehaviour22StartCoroutineManaged2E18ScriptingObjectPtr (:wasm-function[16618]:0x677588)
at _Z43MonoBehaviour_CUSTOM_StartCoroutineManaged2P12Il2CppObjectS0 (:wasm-function[17135]:0x68ca99)
at _MonoBehaviour_StartCoroutineManaged2_m114327BBBA9F208E8BA2F8BBF71FA0E8E996F7B8 (:wasm-function[93412]:0x18f04f8)
at _MonoBehaviour_StartCoroutine_mBF8044CE06A35D76A69669ADD8977D05956616B7 (:wasm-function[93411]:0x18f0492)
at _U3CLoadBytesRoutineU3Ed__0_MoveNext_m18CCAD62B38F7581867595F9BDC0E18D4ECB3EE8 (:wasm-function[102294]:0x1b27506)
at __ZN21InterfaceFuncInvoker0IP64DeserializationContext_t51031F3163BAABA5CC204ACBE71ACE98C86E26D7E6InvokeEjP11Il2CppClassP12Il2CppObject (:wasm-function[30217]:0xd1b209)
at _SetupCoroutine_InvokeMoveNext_m9106BA4E8AE0E794B17F184F1021A53F1D071F31 (:wasm-function[88817]:0x1832020)
printErr @ UnityLoader.js:1150
vendor.abb54bb313dbc1964646.bundle.js:1 ERROR RuntimeError: memory access out of bounds
at _free (:wasm-function[102639]:0x1b6e732)
at __ZN17LowLevelAllocator4FreeEPvm (:wasm-function[9445]:0x35f7db)
at __ZN21UnityDefaultAllocatorI17LowLevelAllocatorE10DeallocateEPv (:wasm-function[14479]:0x590f23)
at __ZN13MemoryManager10DeallocateEPvPKci (:wasm-function[5218]:0x2240ea)
at __ZN13MemoryManager10DeallocateEPvRK10MemLabelIdPKci (:wasm-function[5215]:0x223846)
at __ZdlPv (:wasm-function[5290]:0x2292bd)
at _ktxTexture2_TranscodeBasis (:wasm-function[102537]:0x1b3bf15)
at _KtxNativeInstance_ktxTexture2_TranscodeBasis_mEF854FD64B36B71C37CD79C6BADD8A174F6C688F (:wasm-function[102291]:0x1b27265)
at _KtxTranscodeJob_Execute_m5D0F41A62046752B7A342892205276225A0382B5 (:wasm-function[102300]:0x1b283e8)
at _JobStruct_1_Execute_m5900B9D5D53AB2343FDF29E41D32FD1823E211E4_gshared (:wasm-function[86891]:0x17f8dc5)
at _ExecuteJobFunction_Invoke_m0A741C67F97C0CBA7AA3AAB630DBC79F3342430A_gshared (:wasm-function[86885]:0x17f8913)
at _Z237RuntimeInvoker_TrueVoid_t22962CB4C05B1D89B55A6E1139F0E87A90987017_RuntimeObject_Int32_t585191389E07734F19F3156FF88FB3EF4800D102_Int32_t585191389E07734F19F3156FF88FB3EF4800D102_RuntimeObject_Int32_t585191389E07734F19F3156FF88FB3EF4800D102PFvvEPK10MethodInfoPvPS4 (:wasm-function[44548]:0xff246e)
at dynCall_iiiii (:wasm-function[103255]:0x1b99472)
at Object.dynCall_iiiii (blob:https://www.replaced.com/04fcea6f-dbba-4376-b5d0-80e364b03f46:27843:40)
at invoke_iiiii (blob:https://www.replaced.com/04fcea6f-dbba-4376-b5d0-80e364b03f46:17719:33)
at __ZN6il2cpp2vm7Runtime6InvokeEPK10MethodInfoPvPS5_PP15Il2CppException (:wasm-function[39637]:0xf72e8a)
at _il2cpp_runtime_invoke (:wasm-function[38611]:0xf50084)
at __Z23scripting_method_invoke18ScriptingMethodPtr18ScriptingObjectPtrR18ScriptingArgumentsP21ScriptingExceptionPtrb (:wasm-function[5536]:0x23ad0c)
at __ZN19ScriptingInvocation6InvokeEP21ScriptingExceptionPtrb (:wasm-function[5533]:0x23a86e)
at __ZL10ExecuteJobP14ManagedJobDataPFvPvS1_S1_S1_iEiPh (:wasm-function[16120]:0x641256)
at __ZL19ForwardJobToManagedP14ManagedJobData (:wasm-function[16119]:0x64100f)
at _ZN18JobBatchDispatcher26ScheduleJobDependsInternalER8JobFencePFvPvES2_RKS0 (:wasm-function[9695]:0x380aaa)
at __Z18ScheduleManagedJobRK21JobScheduleParametersPv (:wasm-function[16118]:0x640f74)
at __Z36JobsUtility_CUSTOM_Schedule_InjectedR21JobScheduleParametersR8JobFence (:wasm-function[16664]:0x67a5d3)
at _JobsUtility_Schedule_Injected_mC918219A2045C68697BD2C7FCE7DCA515CE09C01 (:wasm-function[86969]:0x17fb9df)
at _JobsUtility_Schedule_m544BE1DBAEFF069809AE5304FD6BBFEE2927D4C3 (:wasm-function[86968]:0x17fb999)
at _IJobExtensions_Schedule_TisKtxTranscodeJob_t08D80333C4F4CB05B55FF154E746ED8763D84505_m3753A8C2E164F8D930FF6FA5AF0F5C45FB7FEE20_gshared (:wasm-function[58474]:0x125ce63)
at __Z126IJobExtensions_Schedule_TisKtxTranscodeJob_t08D80333C4F4CB05B55FF154E746ED8763D84505_m3753A8C2E164F8D930FF6FA5AF0F5C45FB7FEE2057KtxTranscodeJob_t08D80333C4F4CB05B55FF154E746ED8763D8450551JobHandle_tDA498A2E49AEDE014468F416A8A98A6B258D73D1PK10MethodInfo (:wasm-function[102287]:0x1b27163)
at _KtxNativeInstance_LoadBytesJob_m6E4F3394855DF18E768AF070CFA2A4AE52957315 (:wasm-function[102286]:0x1b270e7)
at _U3CTranscodeU3Ed__1_MoveNext_m265B7CCA22CDEADFC560D6B53BF7154593BF071D (:wasm-function[102296]:0x1b27754)
at __ZN21InterfaceFuncInvoker0IP64DeserializationContext_t51031F3163BAABA5CC204ACBE71ACE98C86E26D7E6InvokeEjP11Il2CppClassP12Il2CppObject (:wasm-function[30217]:0xd1b209)
at _SetupCoroutine_InvokeMoveNext_m9106BA4E8AE0E794B17F184F1021A53F1D071F31 (:wasm-function[88817]:0x1832020)
at _Z164RuntimeInvoker_FalseVoid_t22962CB4C05B1D89B55A6E1139F0E87A90987017_VoidU2A_t3A9D5EB5A83DC9C93DF2C4D6EA67B0F5E885889A_Int32_t585191389E07734F19F3156FF88FB3EF4800D102PFvvEPK10MethodInfoPvPS4 (:wasm-function[44799]:0xff6f16)
at dynCall_iiiii (:wasm-function[103255]:0x1b99472)
at Object.dynCall_iiiii (blob:https://www.replaced.com/04fcea6f-dbba-4376-b5d0-80e364b03f46:27843:40)
at invoke_iiiii (blob:https://www.replaced.com/04fcea6f-dbba-4376-b5d0-80e364b03f46:17719:33)
at __ZN6il2cpp2vm7Runtime6InvokeEPK10MethodInfoPvPS5_PP15Il2CppException (:wasm-function[39637]:0xf72e8a)
at _il2cpp_runtime_invoke (:wasm-function[38611]:0xf50084)
at __Z23scripting_method_invoke18ScriptingMethodPtr18ScriptingObjectPtrR18ScriptingArgumentsP21ScriptingExceptionPtrb (:wasm-function[5536]:0x23ad0c)
at __ZN19ScriptingInvocation6InvokeEP21ScriptingExceptionPtrb (:wasm-function[5533]:0x23a86e)
at __ZN9Coroutine14InvokeMoveNextEP21ScriptingExceptionPtr (:wasm-function[9795]:0x390031)
at __ZN9Coroutine3RunEPb (:wasm-function[9792]:0x38fd1c)
at __ZN13MonoBehaviour24TryCreateAndRunCoroutineE18ScriptingObjectPtr18ScriptingMethodPtrPP9Coroutine (:wasm-function[9789]:0x38fc97)
at __ZN13MonoBehaviour22StartCoroutineManaged2E18ScriptingObjectPtr (:wasm-function[16618]:0x677588)
at _Z43MonoBehaviour_CUSTOM_StartCoroutineManaged2P12Il2CppObjectS0 (:wasm-function[17135]:0x68ca99)
at _MonoBehaviour_StartCoroutineManaged2_m114327BBBA9F208E8BA2F8BBF71FA0E8E996F7B8 (:wasm-function[93412]:0x18f04f8)
at _MonoBehaviour_StartCoroutine_mBF8044CE06A35D76A69669ADD8977D05956616B7 (:wasm-function[93411]:0x18f0492)
at _U3CLoadBytesRoutineU3Ed__0_MoveNext_m18CCAD62B38F7581867595F9BDC0E18D4ECB3EE8 (:wasm-function[102294]:0x1b27506)
at __ZN21InterfaceFuncInvoker0IP64DeserializationContext_t51031F3163BAABA5CC204ACBE71ACE98C86E26D7E6InvokeEjP11Il2CppClassP12Il2CppObject (:wasm-function[30217]:0xd1b209)
at _SetupCoroutine_InvokeMoveNext_m9106BA4E8AE0E794B17F184F1021A53F1D071F31 (:wasm-function[88817]:0x1832020)

@pretender I can help :slight_smile:

I’ll answer in the corresponding github issue.

Hi @atteneder I am using this plugin and it’s working great! Only issue I have is that bigger textures cause stutter on WebGl (4096 textures) so I was wondering is there a way to wait until some flag is true to download or pause download/read (for example when camera is moving, I would delay when camera is not moving and when it’s starts to move then to pause again).

currently I am using this:

private async void DownloadKtxTextureRoutine(LayerTextureJob info, Action onComplete = null)
        {
            var texture = new KtxTexture();
            var result = await texture.LoadFromUrl(info.KtxUrl);
            if (result != null)
            {
                info.Texture = result.texture;
                if (info.IsSharedTexture) OnSharedTextureReady(info);
                else OnTextureReady(info);
            }
        }

and to start

private void RequestTextureJob(LayerTextureJob info)
{
         DownloadKtxTextureRoutine(info);
}

How to start it so it can pause while some flag is true? Can you help me understand how it works, is it possible when download starts to pause it. Maybe decompression is more of a problem?

Thank you!

@pretender As far as I see you’re wrapping the async LoadFromUrl and effectively make it sync (thus the download is blocking). Please look up how to call async methods properly. Hint: Monobehaviour entry points like Start or Update can be async as well!

What remains a problem is that transcoding and GPU upload remain blocking the main thread (since WebGL does not offer user threads). Currently there’s no way around that, but I’d love to experiment with JS worker threads and a separate transcoding webassembly context at some point.

in regarding speed what is better option to compress textures without mipmaps (are they going to be generated when texture is used in unity) and how to properly generate mipmaps, what option to use -automipmap, -mipmap or -genmipmap (i want as less as possible of processing in unity)
thank you!

@pretender

KtxUnity will not create mipmaps if the KTX file did not already contain them. Looking it up (KTX spec, Section 3.7) I realize this is a missing feature. I created an issue, but don’t plan on implementing it anytime soon.

Please consult the toktx documentation regarding its parameters. My conclusion:

  • automipmap is about the missing runtime generation, so that won’t work with KtxUnity
  • genmipmap creates them and allows customization (choose filter, etc.). Start with this, play with parameters and test if it’s working for you.
  • mipmap: if for some reason the genmipmap results are not what you expect, you can manually create individual mipmaps and feed them to toktx. Probably overkill for most use-cases, but good to have.

hth

Hello,

While trying to use ktx plugin in Unity, I encouter one strange issue. When I format heic format of image to png(using nodejs sharp with custom libvips) and encode it to ktx2 format. This image will have color channel BGR instead of normal RGB. So all blue turns to red and vice versa. Other format, jpg, png show normally without any issue.

Is there any ktx unity plugin settings to adjust? Or do I miss anything?

Hi,
Never experienced that. Would you mind open an issue on GitHub and providing sample images (original heic, png and ktx2)? Which format did you transcode it to?
Thanks

I have created an issue on GitHub.

I observed that the format ktx plugin import will be either dxt1, rgb565 or b5g6r5(both in Unity editor and windows standalone build). On mobile, currently the format shows astc_4x4. The format astc is correct but while using toktx cli tool, the block size is 6x6. But on mobile, it shows astc_4x4 insteade.

I just don’t know how to deal with random format on Windows standalone build(or Unity editor). Any suggestion?

Hello,

I understand that there are issues 67 and 68 being created to address the target format adjustment for particular platform. Just note down the observation that certain ktx2 format even being specified as ASTC in player setting(Texture compression format) for mobile, It will be loaded as ETC2_RGB.

Besides the format, that --bcmp flag used during “toktx” command, if applied the astc parameters are not being applied

toktx ignores all ASTC parameters and only takes --bcmp

I then remove --bcmp(without it or replacing with zcmp), the output file could not be read by KtxUnity plugin. It says the image need to be super compression. I just wonder what the correct flags should be used during “toktx” command so astc parameters are kept and the output file can be read by KtxUnity.

Sorry for the late reply.

KTX is a container format capable of delivering uncompressed formats, compressed formats (like ASTC) and super-compression (BasisUniversal), all with optional ZStandard compression on top.

KtxUnity was initially created to load BasisUniversal compressed KTX files and up until now cannot load other formats. So the problem is likely not the way you invoke toktx, but a missing feature in KtxUnity.

I’d love to have this lacking features added to KtxUnity, but to me it’s not a priority at the moment. Happy to review pull requests though.

hth

I could not install GitHub - KhronosGroup/KTX-Software: KTX (Khronos Texture) Library and Tools to my Mac,
I cannot convert textures into basis file right now,
is there a tutorial or some tips?
@tteneder

Where exactly do you struggle?

I recommend going with KTX 2.0, but if you want to convert to .basis files, you need the basisu command line utility from here:

https://github.com/BinomialLLC/basis_universal/releases

There’s the KTX Tools guide and the KTX artist guide. Both easily discoverable on KTX’ main site.