LukasBanana / LLGL

Low Level Graphics Library (LLGL) is a thin abstraction layer for the modern graphics APIs OpenGL, Direct3D, Vulkan, and Metal
BSD 3-Clause "New" or "Revised" License
2.03k stars 135 forks source link

OpenGL mipmap generation problem? #103

Closed jayzhen521 closed 6 months ago

jayzhen521 commented 7 months ago

Environment: commit:a41b095c windows vs2022

In the example of Example_RenderTarget, the usage of texture seems to follow the process below:

  1. Create a texture, and by default, mipmaps are created based on its dimensions.
  2. Drawing is performed by rendering to the texture (at this time, except for the mipmap at level 0, the rest are not drawn).
  3. When using the texture, it is "mandatory to call" GenerateMips(*renderTargetTex), otherwise, the following situation will occur: image

My understanding of mipmaps is not very deep. It took me 2 days to identify the problem(forgot regenerating). This seems unreasonable because it makes regenerating mipmaps a necessity. Should mipmaps be implicitly created after rendering to the texture to maintain consistency? Or just creating texture without creating mipmaps by default?

LukasBanana commented 7 months ago

My understanding of mipmaps is not very deep. It took me 2 days to identify the problem

Sorry to hear you were struggling with the interface, these things might not be intuitive to newcomers but this behavior aligns with the underlying rendering APIs. In fact, D3D12 doesn't even support generating MIP-maps since this is not only an expensive operation but it's also pretty common for professional game engines to either provide all MIPs in advance (e.g. the .DDS file format supports having multiple MIP levels in one file) or generating them with custom filters (most common example is the HiZ buffer where you don't want the average of four pixels but only the minimum or maximum value). Moreover, most game engines heavily rely on compressed textures and no API I'm aware of supports automatic MIP-map generation of such textures; Even rendering into such textures is not supported for most compressed formats (if any).

LLGL allows generating MIPs at texture creation time (via MiscFlags::GenerateMips) for convenience, so you don't have to encode and submit a command buffer just to initialize your textures. But that flag doesn't do anything beyond the initialization and a RenderTarget can only render into a single MIP level (see AttachmentDescriptor::mipLevel) but potentially into more than one target. Therefore, it is not the render target's responsibility to render into any other MIP-map that you specified in the attachment descriptors.

The only automation with respect to render targets that LLGL does for you is automatically resolving multi-sampled targets into their non-multi-sampled counterparts. I.e. each time you call EndRenderPass() LLGL will automatically update the resolve color attachments (see resolveAttachments) so you can use them as regular textures in your shaders (multi-sampled textures are sampled very differently).

Should mipmaps be implicitly created after rendering to the texture to maintain consistency?

No, mostly because MIP-map generation is time consuming and you might not want the default MIP filter (we're talking about microseonds here but depending on the texture dimension, this can actually go into the milliseconds and with a 60 FPS time budget, you only have 16ms for your entire frame). As for the filter, some APIs use a simple box filter (i.e. average value of four pixels) and others use a more sophisticated filter to avoid undersampling. When you create a gaussian blur effect for example, you might actually want to apply your own filter to update the MIP levels or perhaps you have an effect that only requires the first two MIP maps to be updated. Having LLGL always update all MIP-maps the texture has is simply overkill.

For the sake of completeness: Only OpenGL 1.4 used to have a texture parameter to automatically update MIPs when the texture content changed. That was GL_GENERATE_MIPMAP which is deprecated since GL 3.0.

Or just creating texture without creating mipmaps by default?

Just to be on the same page: Creating MIP-maps and updating them are two distinctive operations. When you create a texture and keep the default value of TextureDescriptor::mipLevels at 0, LLGL will determine the maximum number of MIPs for you. Otherwise, you can specify how many MIP maps you want, perhaps only three or four for the gaussian blur. Generating (or updating) the content of those created MIPs is a separate process (equivalent of calling GenerateMips right after CreateTexture) and I think it's convenient to have that feature for all the textures you don't intend to update (or render into rather) every frame.

I hope that explanation helps. Don't hesitate to ask any other questions you might have.

jayzhen521 commented 6 months ago

Sorry! I have been on holiday recently and only saw your reply today. Thank you very much for the comprehensive and detailed explanation; it has been a great help to me.