-
Notifications
You must be signed in to change notification settings - Fork 309
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Investigation: Generate mipmaps #386
Comments
You've almost lost me at the first sentence :)
Most 3D applications that are performance-sensitive wouldn't want to generate mipmaps at run-time, except for some cases where texture content is not known in advance (procedurally generated, captured from the environment, etc).
Mipmaps constitute for 1/3 of the original texture volume. Besides, an application can start perfectly fine while the mipmap levels are still being transferred. The best way for an app to start quickly is to pre-generate the mipmaps and start loading the higher (smaller) mips first, which would allow it to start much faster than waiting for the full textures. Once other mipmap levels arrive, an application can issue
Is having a 3rd party dependency such a big trouble in JS/WASM world? I'm surprised we'd be trying to reduce the ecosystem as opposed to relying on it to grow. Previously with
|
I would encourage that if the WebGPU WG would like runtime mipmap compression, that this is shipped as a library developed inside the WG and released as open-source for application authors to use. |
Including a simple blitting function instead of a mipmap generator would be less controversial. Note that Vulkan provides |
I agree with including a blit image command, though I wouldn't encourage using it in place of offline mipmap generation if possible. |
You need to see the big picture here. We have thousands of users who are relying on us (babylon.js for instance but pretty sure Three has the same concern) to generate their mipmaps. Not every developer is a AAA game developer. |
@deltakosh interesting that you mention this, because Three.js and Babylon.js are in a good position to implement it once and for all in their own codebase, using the most efficient methods available. |
So far (for webGPU) we are using webgl so I'm not sure it is the most efficient method :) We will obviously use a different technique if the spec ends up with no other option. |
@deltakosh is your use of WebGL justified by the inability to generate mipmaps with WebGPU directly? I mean, just having a shader that samples an image, and then running it for each level is not too difficult for you as a framework author. |
It is not, I agree and as I stated earlier we will do it if no other option is available. We are already resampling textures using shaders to make them POT so no big deal. But again it is more about the overall usability of the API. (also because of the fact that spec is not stabilized, we took some shortcuts for our first implementations) Also as mentioned by @magcius it is not just about sampling down a texture as we need to filter the edges differently |
I am wondering why we try to push a lot of the functionalities to external lib and outside of the browser api scope. I am thinking the browser APIs is an amazing place to simplify the user code and prevent everybody from writing again the same kind of code. Making it a lib means we expect people to use it so why not directly integrating in the browser ? (Even more as it is not just a trivial task to do well). I am afraid that currently to get started I would need to install a lib for mipmaps, one to deal with buffers upload, maybe a compiler, and probably some other utils. What is the main issue of keeping some default utils in the spec ? Like uploading imageData, we could argue that we do not need it but is it not good to have convenience method like those for the devs ? |
I would rather not have browser-implemented helper libraries, unless they're all guaranteed to be identical (and this is a very tricky thing to ensure). There isn't a one-size-fits-all solution, and there are a lot of constraints. The blit image approach won't work for filtering cubemap images, for instance. We're already seeing a huge delay getting the WebGPU MVP added into browsers. If we add it into the spec, it should not be added for MVP, and Babylon will have to ship mipmap generation code anyway... There will be third-party code involved, the question is whether it's shipped in the browser or shipped in the app (if we agree this is post-MVP, then it'll be shipped in both!). For platform consistency reasons, I would rather see it shipped in the app. |
IMO the problem isn't so much with having built-in helpers, it's those helpers incentivizing developers to write inefficient code. If the browser can generate mipmaps on-the-fly then the path of least resistance is to use uncompressed textures and waste a ton of memory/bandwidth, as was the case with WebGL. If WebGPU is going to have high-level texture helpers then I'd rather see the effort put towards supporting Basis Universal as a first-class feature with transcoding provided by the browser, so the easy path is also the fast path. |
Well if this is not done by the browser it will be done by the frameworks so this will not incentive users much to pick Basis path |
I would say that even with more compressed format and so on, we still need sometimes for procedural textures, textures from video or webcam capture, or even textures created from another canvas one easy path to mip maps. I totally get the MVP timing requirements and I do not think there are any rush for helpers in the spec. In frameworks like babylon or three we will be able to go around it but as a general API surface on a longer term than the MVP, i think it still makes sense to have methods simplifying the devs life. I might misunderstand the overall audience, but if we are not only targeting big engines out there, and we want to expand our reach, having convenience helpers is probably a good way forward. Also as in browsers, the less call to the native APIs, the faster, it might be a tiny performance boost compared to doing all of the calls manually. It might even help reducing the JS file size which more and more people seem to take as a strong concern and metric. |
I strongly believe this should be handled by applications and libraries. WebGL's GenerateMipmaps is unstandardized(!), poor (unguaranteed!) quality, and has a number of restrictions that surprise users (filterable+renderable formats only!). While superficially useful, it's pretty limited, and I don't think is something we should try to replicate. |
@litherum write:
The latest versions of D3D12 have render passes. If Mipmap generation can be done with a helper library, that seems like the best approach, assuming the helper library is not several hundred kilobytes in size. |
One important use case of mipmap generation is when a 3D scene renders to an offscreen buffer which is then textured directly in way that the sampling is not uniform. From the point of view of hardware and drivers, many GL drivers implement a highly tuned implementation of mipmap generation potentially closely tied to the GPU architecture, so those might win performance over one made from a library. I can imagine a scenario where a tiled based GPU could make it faster if the driver new as part of the render target that mipmaps where needed, but there is no API doing that so I doubt any GPU does that. On the otherhand, most of the time driver-done mipmap generation is a simple box-filter which is naturally the cheapest but not necessarily the best looking one. |
AFAICT, neither Vulkan nor D3D12 implements a GenerateMipmaps, so we don't have access to any highly optimized driver implementation, so the browsers would have to write our own. |
Discussed at the 2019-08-12 meeting. |
See above. The Microsoft-authored DirectX Tool Kit 12 includes support for it. |
It's hard to argue that 46,000 usages on GitHub is only superficially useful. |
I think we'd have to modify our charter to do this. |
To be fair, WebGL developers use it because OpenGL has it. We don't have this kind of legacy with WebGPU. So, I wouldn't treat this number as a strong indication that the built-in support is needed. |
If you consider OpenGL usages of this function, the count grows to over 200,000 usages. The fact that it's used so often in real code means these facilities are more than superficially useful. |
If you have the time and effort to do it, do it, just make sure that:
If you provide any less than this then its simply not worth the effort. @litherum you're right that everyone relies on OpenGL's and WebGL's mip-map generation, however they also accept (what the WebGPU WG has always been allergic to) inconsistency. The OpenGL (and WebGL) spec leave it completely up to the implementation as to:
Also mip-mapping a block compressed texture is a major pain. |
The only way to provide this feature is the way @magcius suggested, otherwise textures with minification will look different on different browsers (and possibly even backends/GPUs). |
I developed a benchmark suite for comparing Vulkan blits versus transfers and shader draws, see gfx-rs/gfx#2960 (comment) |
Mipmap generation via TEXTURE_BASE_LEVEL and TEXTURE_MAX_LEVEL does work in WebGL when used to preclude feedback. |
@Nehon thank you for extensive feedback!
Hmm. Wouldn't it be faster to transfer higher mips first, followed by lower mips? I'd consider it a win for slow-connection users, and it's only possible if you pre-generate the mips. Your website would then always display the lowest available mip level for a texture. Once a new one comes it, you'd re-create the
Yes, it is possible in WebGPU, as noted in other comments.
I guess we'll be keeping our ears wide open to listen if this is demanded wildly. D3D12 and Vulkan don't have built-in facilities for this, and D3D12 doesn't even have any blitting, so it's not totally clear how important the mipmap generation would be today. With proper support for compressed textures in the API, and the Basis Universal magic on the user side, it may be better to expect all of the mips coming this way. |
We do send low res textures first so that the time to interaction is the shortest possible. but we don't send the whole mipmap chain. Thanks for your answer. |
Note that GenerateMipmaps is not a hardware command on any platform. The platforms that have it: D3D11's GenerateMipmaps and GL's glGenerateMipmaps are fully implemented in the driver, either as special shaders or as other tools. One major reason that GenerateMipmaps was removed between D3D11 and D3D12 is because explicit APIs expose a lot more of the underlying hidden state that the driver can manage. As seen above, some methods of doing downsampling require tools like OUTPUT_ATTACHMENT. Others use a compute shader, which can require different usages. Unless the exact implementation of GenerateMipmaps is specified in the spec, with all of the bits changing, it's hard to implement such conveniences efficiently, since it's locked into an implementation. Having a standard library for this, possibly a port of the compute shader downsampler released by AMD, would be preferable to having it in the browser in my opinion. |
Yes that's my point. that's why I think the spec should be super specific about it. Whatever implementation used.
I don't get why a 3rd party library would be better in every aspect we discussed, and I fail to see the benefit of it considering everybody need to generate mip maps. |
Adding to V1.0 milestone instead of post-V1.0 like other feature requests because this has been a very common request so we need to make a decision on whether it is in 1.0 or not and stick to it. |
I've ported a limited version of SPD to WGSL here, but there's a few issues:
|
Thanks for the feedback! I don't think #822 would allow more storage bindings overall unfortunately, just grouping them as an array for convenience. How would read-write storage textures allow doing more than 6 mips at once? The number of storage texture bindings is a limit that can be increased if the hardware supports it too. Do you have some performance numbers? Esp. compared to doing a repeated manual blit? |
The first 6 mips only require workgroup shared memory, but mips 7-12 need to sample from the 6th mip texture (so need read-write in order to avoid a second dispatch). Though perhaps a storage buffer could be used as a workaround to handle the communication between workgroups. I haven't looked at the performance yet, I suspect the port still needs some work to be competitive. I'll update with some numbers when I have a chance |
I've updated the code to lift those last two limitations (thanks for the clarification), and done a quick performance comparison. The total cost of the draws/dispatch is similar between the two, most of the time savings are from avoiding the need to setup multiple passes. For generating 10 mip levels, the SPD port takes 0.3-0.4ms, whereas repeated blit takes about 2ms. |
That's quite a perf difference! Thanks for sharing. I assume that the textures you generate mipmaps for have the |
You'll need to mark the 6th mip texture as |
Yep, the only different from repeated blitting with a fragment shader is that it's using
Thanks. I'm guessing |
You can't have read-write access on a storage image in WebGPU !? |
Unfortunately not, it isn't a required capability in Metal. We gathered some statistics on the availability in Chrome macOS installs and there's about 10% of systems with no support at all, and another 50% systems that would support only |
While
Outside of regular textures, there are many use cases for generating/re-generating mipmaps at runtime, often to traverse hierarchical data structures or to approximate light traversal, e.g. LPV, VXCT (or generally where a volumetric representation is used to approximate a scene for e.g. lighting). Also as a side note, I found that up to today, manually generating mipmaps in WebGL without |
GL and GLES impose a limit that for This gets tricky with RGB8 textures (which aren't too much of a problem since they don't exist in reality), RGB9E5 and so on.
Its currently a 3 month long project by @achalpandeyy in our framework Devsh-Graphics-Programming/Nabla#343 Box Filter is a rather bad default, and there's a lot of things to worry about when implementing even that poor method. |
you should at least support Also just because it isn't a required capability, doesn't mean it shouldn't be exposed as a feature at all. |
…uweb#386) * Add dev server for standalone runner which compiles TS at runtime * Remove Grunt tasks no longer needed for standalone * Add dev server request logging * Address review feedback * Workaround the import cache for dev server .spec.ts imports * Apply suggestions from code review Co-authored-by: Kai Ninomiya <kainino@chromium.org> * Address comments from code review * Remove stray console.log Co-authored-by: Kai Ninomiya <kainino@chromium.org>
Late to this discussion and not sure why it popped up in my sites but ...
|
Found this discussion super interesting, as I was curious why WebGPU couldn't generate mipmaps. One suggestion I have is to consolidate the arguments for excluding mipmap generation from WebGPU in one place for easy reference, if that hasn't been done already (maybe @greggman's WebGPU from WebGL article?). I suspect this question will come up a lot, and this thread is a long, convoluted way to get to an understanding of the situation. |
I also ported SPD to WGSL, mainly so I can downsample my depth buffer but it can also generate mipmaps. It's on npm and here if anyone needs it :) |
That's amazing, thanks for sharing! |
SPD doesn't handle NPoT textures properly. Handling all corner cases is actually quite tricky: |
Generating mipmaps is something that most 3D applications want to do. As textures are often the largest asset 3D apps need to download, generating the smaller miplevels can save significant downloading time, causing the app to be able to launch earlier. We've heard that download times are a significant concern to Web game developers; there's a strong inverse correlation between how long a games takes to load and how many users actually end up playing the game.
WebGPU currently provides no facilities to generate mipmaps. It is true that a 3rd party library could do this, but taking an additional dependency and sending a library across the wire for something that most 3D authors will want to do seems like bad design. Indeed; the purpose of doing this in the browser is to decrease download sizes, and adding an additional framework dependency is contrary to that goal.
WebGL solved this by including WebGLRenderingContext.generateMipmap(). Developers clearly desire using this; a quick search of GitHub shows that it's used more than 46,000 times. Indeed, at least one developer is using this WebGL API to generate mipmaps and then passing the results into WebGPU.
WebGPU should provide built-in facilities for generating mipmaps. Of course, we shouldn't force developers to use the built-in facilities. It is true that different developers desire mipmap filtering algorithms, and they should be free to write their own mipmap generation code. However, for most authors, the presence of built-in mipmap generation facilities will be the difference between mipmaps existing in their app at all, and mipmaps not existing in their app.
Metal
It's a method on
MTLBlitCommandEncoder
. It is executed on the GPU, so the command needs to be submitted to the queue and is therefore ordered with relation to other GPU operations. Also, because this is inside a command encoder, all compute/graphics command encoders need to be closed before this can be issued.The docs have a note:
Direct3D 12
The core API doesn't have any support for generating mipmaps. However, the Microsoft-authored DirectX Tool Kit 12 includes support for it. Unfortunately, this Kit isn't included in the Windows SDK; Windows browsers would take a dependency on it. The docs say that it's implemented on top of a compute shader.
Direct3D 12 doesn't have passes, so there are no concerns there. However, the API isn't a one-shot generation; instead, it's a
ResourceUploadBatch
object that has to be opened and closed.Vulkan
Vulkan doesn't have any built-in facilities for generating mipmaps (because of course it doesn't). It looks like you can do it with (a ton of code around)
vkCmdBlitImage
, which means that any render passes need to be closed.Compute shader
Even if Windows browsers didn't want to take a dependency on DirectX Tool Kit 12, we could still implement it using a compute shader. This would require that all render passes are closed.
Recommendation
In keeping with the current design of putting blit-style commands directly on the
GPUCommandEncoder
, we can add agenerateMipmaps(texture)
call there. This will allow for us to require that any render or compute passes are closed before it's called. Such a design should be compatible with each of the above approaches.The text was updated successfully, but these errors were encountered: