Question:
Why does the same amount of pixels take dramatically less video memory if stored in a square texture than in a long rectangular texture?
Example:
I'm creating 360 4x16384 size textures with the glTexImage2D command. Internal format is GL_RGBA. Video memory: 1328 MB.
If I'm creating 360 256x256 textures with the same data, the memory usage is less than 100MB.
Using an integrated Intel HD4000 GPU.
It's not about the texture being rectangular. It's about one of the dimensions being extremely small.
In order to select texels from textures in an optimal fashion, hardware will employ what's known as swizzling. The general idea is that it will restructure the bytes in the texture so that pixels that neighbor each other in 2 dimensions will be neighbors in memory too. But doing this requires that the texture be of a certain minimum size in both dimensions.
Now, the texture filtering hardware can ignore this minimum size and only fetch from pixels within the texture's actual size is. But that extra storage is still there, taking up space to no useful purpose.
Given what you're seeing, there's a good chance that Intel's swizzling hardware has a base minimum size of 32 or 64 pixels.
In OpenGL, there's not much you can do to detect this incongruity other than what you've done here.
Related
If I understand correctly, if I was to set TEXTURE_MIN_FILTER to NEAREST then there's not much difference between sampler2DArray/TEXTURE_2D_ARRAY and sampler3D/TEXTURE_3D
The differences seem to be
GenerateMipmap will blend cross layers with 3D textures but not 2D arrays
the Z coordinate passed to texture in GLSL is 0 to 1 with 3D textures but an 0 to N (depth) in 2D arrays.
If filtering is not NEAREST 3D will blend across layers, 2D array will not.
Correct?
Incorrect. There's one more difference: mipmap sizes.
In a 3D texture, the width, height, and depth all decrease at lower mipmap sizes. In a 2D array texture, every mipmap level has the same number of array layers; only width and height decrease.
It's not just a matter of blending and some texture coordinate oddities; the very size of the texture data is different. It is very much a different kind of texture, as different from 3D textures as 2D textures are from 1D textures.
This is also why you cannot create a view texture of a 3D texture that is a 2D array, or vice-versa.
Apart from the answer already given, there is another difference worth noting: The size limits are also quite different. A single layer of an array texture may be as big as an standard 2D texture, and there is an extra limit on the number of layers, while for 3D textures, there is a limit constraining the maximum size in all dimensions.
For example, OpenGL 4.5 guarantees the following minimal values:
GL_MAX_TEXTURE_SIZE 16384
GL_MAX_ARRAY_TEXTURE_LAYERS 2048
GL_MAX_3D_TEXTURE_SIZE 2048
So a 16384 x 16384 x 16 array texture is fine (and should also fit into memory for every GL 4.5 capable GPU found in the real world), while a 3D texture of the same dimensions would be unsupported on most of todays implementations (even though the complete mipmap pyramid would consume less memory in the 3D texture case).
I baked texture on a 4k image and I downscaled it to 512 using Gimp but the quality is bad in Unity 3D. The image format is png.
Is it a good idea to bake texture on a 4k image and to downscale to 512 for mobile game?
What can I do to keep a good quality on baked texture with small size (512 or below) for mobile game development?
In general, you can expect to sacrifice some texture quality if you use a smaller texture. It makes sense: with less data, something must be lost, and that's usually fine details and sharp edges. There are some strategies available, though, to get the most out of your texture data:
Make sure your model's UVs are laid out as efficiently as possible. Parts where detail is important -- like faces -- should be given more UV space than parts where detail is unimportant -- like low-frequency clothing or the undersides of models. Minimize unused texture space.
Where feasible, you can use overlapping UVs to reuse similar portions of your UV space. If your model is exactly symmetrical, you could map the halves to the same space. Or if your model is a crate with identical sides, you could map each side to the same UV square.
See if you can decompose your texture into multiple textures that are less sensitive to downsizing, then layer them together. For instance, a landscape texture might be decomposable into a low-frequency color texture and a high-frequency noise texture. In that case, the low-frequency texture could be scaled much smaller without loss of quality, while the high-frequency texture might be replaceable with a cropped, tiled texture, or procedurally generated in the shader.
Experiment with different resizing algorithms. Most image editors have an option to resize using bicubic or bilinear methods. Bicubic does a better job preserving edges, while bilinear can sometimes be a better match for some content/shaders. You may also be able to improve a resized texture with careful use of blur or sharpen filters.
If the hardware targeted by your game supports it, you should evaluate using hardware compression formats, like ETC2 or ASTC. The ETC2 format, for example, can compress 24-bit RGB texture data to be six times smaller while still having the same image dimensions. The compression is lossy, so some image information is lost and there are some artifacts, but for some content it can look better than raw textures rescaled to a similar data size. Depending on content, some combination of resizing and hardware texture compression may get you closest to your desired quality/size tradeoff. The Unity manual claims it automatically picks a good default texture format when exporting, but you can try other ones in the export options.
Usually, the motivation for using smaller textures is to consume less video memory and improve performance. However, if the motivation is simply to reduce the download size of the game, you could try alternate image formats. JPG, for instance, lets you choose an image quality value to get a smaller file size. It's rarely used for games, though, because it takes up as much video memory as a similarly sized PNG, can have artifacts, and doesn't support alpha.
Using OpenGL, I want to calculate the necessary throughput to render a frame.
Assuming the worse case scenario, I have (1) a framebuffer and (2) 8 textures that are all fullscreen and the textures end up covering the whole screen (however, textures are likely flat rectangles, but not 1:1 scale).
So each pixel is tweaked from 9 RGB pixels (assuming all textures have some degree of transparency) and in an ideal world you would read exactly 9 pixels and then save the result in the framebuffer. Of course with filtering it could be reading more pixels (especially if the textures are not straight, etc.)
How do I compute an approximation of the number of memory access. So if my video board says it has a limit of 40Gb/s, I can make sure I have enough bandwidth to support the full load?
Some details as requested in comments:
Frames / texture sizes: 3840 x 2160 (4K)
Expected frame rate: 30 FPS
GPU: NVidia (one of the newest like Maxwell or Pascal at least)
This is for realtime graphics.
Let's say that there is a single mesh that we are rendering. We place a 1k (1024x1024) texture on it and it renders fine. Now let's say that we place a 4k texture on it but render only a 1k section of the texture by using different UVs on the same mesh.
Now both times, the visible surface has 1k texture on it. But one comes from 1k texture map the other from 4k texture map. Would there be a difference in performance, not counting increased VRAM usage from 4k map.
For all intents and purposes, no, there will be no difference.
By restricting the UVs to the top left 1024x1024 you'll be pulling in the same amount of texture data as if the texture were 1024x1024 and you read the entire thing. The number of texture samples remains the same as well.
It's impossible to rule it out completely of course without having low-level knowledge of every GPU past, present and future, but you should assume the performance will be the same.
Hey, I have a texture loaded with glTextImage2D.
I want to get the texture's size after it was loaded to the VRAM, what do I need to do?
My internal format is RGBA and the texture's format varies.
This might be helpful to you and you can probably infer the actual memory usage from it. However, I believe this still is rather an approximation:
http://www.geeks3d.com/20100531/programming-tips-how-to-know-the-graphics-memory-size-and-usage-in-opengl/
(query the total memory usage of OpenGL using NVIDIA or ATI specific extensions)
Also note that from my experience, approximating the memory usage by rough calculation was usually sufficient. Textures "should" be stored either 1,2 or 4 components without any significant overhead. Therefore for a WxH RGBA texture, calculate W*H*4. If you store float textures (i.e. GL_RGBA32F), calculate W*H*4*4. For Mip-Maps, add (1/3) additional memory consumption. Be aware however, that - at least to my knowledge - texture memory can also fragment, possibly leaving you with less available memory as estimated.
Use GetTexLevelParameter, which can give you (for each level):
Width and height *
Depth
Internal format *
Compressed format and relative size
(*) Uses these parameters for computing the texture size (for the specified level).
The single texture memory usage is dependent on the generated mipmaps. Indeed to compute correctly the memory used by a single texture you have to determine the mipmaps related to the textures, and then sum the memory usage for each level.
The mipmap count is determined by the OpenGL specification depending on the texture target: texture arrays elements has their own mipmaps set, each cube face texture has its own mipmap set.
The dimension of the mipmaps are halfed for each level, untill they goes to 1. In the case the dimension are not power of two, they are rounded to the lower integer.