Home Technical Talk

How many textures per model is too many?

I feel like the answer is already somewhere on the forum, but I've searched for some time and haven't found anything...

I'm working with a proprietary engine, my programmer keeps saying that texture draw calls are one of the most damaging things to performance. Well, I'm trying to figure out what the better approach is: Single, unified unwrapped models or multi-sub object materials... not that I think they're mutually exclusive or anything.

I've noticed that most props in UT3 and UT2004 usually have one texture/material but I've also noticed props with 5 or more. So how are the other Polycounters out there make this decision?

I'm trying to figure out what is more computationally expensive - calling 5 unique unwrapped textures or re-using 5 tileable textures on multiple props. (5 was just a random number...) Or does it matter? If so, how much?

Of course my programmer doesn't really have an answer for me, this is more of the art side of things anyway.

Replies

  • Saiainoshi
    Offline / Send Message
    Saiainoshi polycounter lvl 9
    In all honesty, there are so many factors that can affect how many textures are applied to one model. For large scale things I would use multi-sub and probably a single texture or two for an asset. You have to ask yourself where the model will be placed in game.. like will it be in a high traffic area? How many of the same model is being placed throughout the world.. etc etc etc. It's hard to get an idea of what areas will be affected by batch count until majority of your level is polished, and NPCs are spawned.
  • Daaark
    Offline / Send Message
    Daaark polycounter lvl 17
    You can use as many as you want, but:

    The GPU works like a factory assembly line. You tweak all the settings, then you send it a large batch of vertices to work on. The bigger and fewer the batches, the better.

    If you want to upload a different set of textures into the texture units, or change vertex arrays, or change any other setting (alpha blender toggle, etc...) then you have to stop the whole factory, tweak those settings, and start it up again.

    This happens really fast, but it's relatively slow. It's the slowest thing to do on a GPU, so a good rendering engine will try to optimize around having the fewest state changes as possible.

    There is no difference between tiled and un-tiled textures other than the UV values assigned to the vertices. The GPU doesn't care, it just reads the numbers and samples from the proper spot in the texture data. Only thing that changes is the wrap mode state that tells it how to handle coords out of the 0-1 bounds.

    Changing shaders is also VERY expensive.
  • cryrid
    Offline / Send Message
    cryrid interpolator
    I once witnessed a man trying to put a vehicle with twenty-seven 1024x1024 textures into VBS2.... there were some preformance issues.
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    just get used to pack multiple parts of a object into 1 texture. Or even related objects into 1 texture.
    It has to do a lot with architecture of the engine and game, like how textures are reused and which ones are dynamic like swap able parts of a texture.

    And yes in the end you want to reduce drawing calls and every different texture in memory increases the changes of another drawing call even though in many cases the batching might do a good job.

    You could also unwrap and texture several objects independently and at some point stitch them together using photoshop and for example the UV transform modifier in max. Or the transform canvas tool in TexTools which uses the UV transform modifier as a base to shift and resize the UV area.
    textoolscanvasresize01.gif
  • Progg
    Offline / Send Message
    Progg polycounter lvl 11
    cryrid wrote: »
    I once witnessed a man trying to put a vehicle with twenty-seven 1024x1024 textures into VBS2.... there were some preformance issues.

    Screenshot or it didn't happen :P
  • mayaterror
    cryrid wrote: »
    I once witnessed a man trying to put a vehicle with twenty-seven 1024x1024 textures into VBS2.... there were some preformance issues.


    ...(Still laughing 30 min after reading this)...


    Good ideas, renderhjs, I'm facepalming over not thinking of the UVXform trick you mentioned. I already tried the "multiple parts in one texture" and that worked OK, but I wasn't sure if that was a "legit" method.

    I need to get with the program on TexTools, everyone here's always talking it up. I'll admit I tried it and didn't immediately see what the fuss was about, but every other post mentions using it to shift/offset UV's so there must be something to it.

    Let's get a little more specific here- the current object in question is a simple primitive thatched-roof hut, it has wood plank walls, wooden beams/trim, a stone foundation, and a brick chimney. That's a minimum of 5 textures. I've done a version with multiple tileable textures packed into one 512 square, single UV unwrap, and I've also done a version with 256 square multi-sub individual tileable textures. They look pretty much the same, though the detail on the muti-sub is a little better.

    This has been some great feedback so far, you've all given me some ideas but of course I'll keep reading if there's any more good tips!
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    I am used to putting a whole city into 1 giant texture because back in my uni days there was no other way to render under a 32bit windows using MentalRay. Memory limits were a constant plague and because of that frequent crashes, so we tried to be very radical and created 1 very big texture with all the environments of a city for a scene.
    It has been a habit ever since to first consider what can all be merged, and usually means not just 1 or 2 objects but several objects. My point here is that once you experienced limits (crash, slow downs, failures,..) you will appreciate thinking about it more - because being a CG artist (games or film) is always about working with technology in mind.

    The coder will love you if you can just put all all needed textures together into 1 sheet (if that works with the shaders, and level loading,...) - because at some point it might even get the drawing calls down to just 1! and that means super nice frame rates and space for processing intense effects or shaders.
  • dlx
    Tell your prgrammer that a bad shader compiler is much worse than texture fetches. :D!!

    Like Saiainoshi metioned, there are many things that will affect performance. Texture fetches are just one piece of the puzzle.

    What you seem to be asking about is the number of draw calls per object. Whether those draw calls use unique textures or the same texture is another matter. In DX9 (maybe DX10 as well?), a single draw call can access up to 16 textures.

    When you break an object into 5 materials, you are essentially turning it into 5 different objects. If that object is on screen 10 times, then you have 50 draw calls.

    For example, if you texture a motorcycle on one sheet, you might think it is one draw call. Then you create different shaders for rubber, chrome, paint, leather and glass, each with different values for specular, reflection and opacity. That motorcycle is now 5 separate draw calls that use the same texture.

    I have to disagree with renderhjs on the single texture/single draw call approach. So let's assume that you put everything in the scene on one big sheet and one material and collapse it into a single draw call. That should be ultra fast right? Not necessarily. Now you have a new problem. If even one vertex of an object is in view then you send the entire object to the renderer. Unless your engine has a snazzy way to cull individual polygons that are off screen before they go to the vertex shader (and it probably doesn't), you will waste performance by prepping off-screen geometry that never actually gets rendered. Having your entire scene as one asset could also be a workflow and bug fixing nightmare for the artist.

    In short, you have to balance memory, performance, quality and usability. Unique texture unwraps are artist friendly and tend to improve performance when used properly. They also cost more memory and can sacrifice pixel density. Reusable tiling textures can save memory and improve pixel density but will cost more draw calls.

    Discuss it with your programmer and see where the bottleneck occurs. Test the results with the game running. It would be even better if you can get your hands on the profiling tools and see the results for yourself... 'cause the art might not be the problem. ;)
  • cman2k
    Offline / Send Message
    cman2k polycounter lvl 17
    Saiainoshi wrote: »
    In all honesty, there are so many factors that can affect how many textures are applied to one model. For large scale things I would use multi-sub and probably a single texture or two for an asset. You have to ask yourself where the model will be placed in game.. like will it be in a high traffic area? How many of the same model is being placed throughout the world.. etc etc etc. It's hard to get an idea of what areas will be affected by batch count until majority of your level is polished, and NPCs are spawned.

    There's some good info in this thread, but this is particularly relevant.

    How important is your hut? How often will it be used? How many will you ever see on-screen at once? Does your renderer merge draw-calls for indentical objects?

    These are all really important questions. If your hut is on screen once as a landmark and is the focus of the scene, then you can probrably go with multiple draw-calls. If your hut is going to be used in a town where there will also be 50 other huts, and your renderer doesn't merge draw calls, then it's likely going to have an impact on performance that could have been avoided by merging it all onto one sheet.


    As a general rule of thumb, if I know I'm making something that will be used A LOT, then I make a point to use as few draw calls as possible. And If I'm making something that won't be used very much, then I'll give myself some breathing room.

    When it comes down to the end and you are analyzing overall performance of environment draw-calls, the highest frequency objects are the ones that will potentially gain you the biggest savings, so it's always where you turn first. Build those right the first time, and then optomize further if you need to.


    Here's another option I haven't heard mentioned;
    You can always have your model use multiple draw-calls, but LOD to a version that is baked to a single texture and lower resolution. This could mean that only the few near you are multiple draw calls, and the others all use only one draw call each. This is generally viable, but is also trading performance for memory, which isn't always feasible (especially on consoles). It's also good practice on large-scale environments because once these are in the middle-ground and not the foreground, they'll drop their draw-calls pretty quickly.

    Hope this helps.
  • mayaterror
    The current situation right now is that we use tileables for terrain, and some props, single-texture unwraps for most other assets. We've tested many situations with multi-sub and no problems implementing or using it. I haven't really tried to break the engine in that regard yet - maybe I should. There's no real problem, per se, I'm just wondering how others approach this so problems can be minimized ahead of time.

    We have several LOD and optimization functions, but while I can deal with making mip maps, mesh-based LOD gives me chills just thinking of it - unless it's handled on the programming side!

    Without going into too many specifics, we use a DX9 shader-based material system that supports use of diffuse, normal, specular, envrion/cube, and a few other things.

    We're really moving out of a testing and development phase and into a production phase, I'm trying to soak up as much as possible here so I can get my workflow down to avoid going back and re-making things.

    This has all been helpful, I can never get over how much just about everyone here really knows their shite, it's great.
  • Eric Chadwick
    Some good advice in here, especially like dlx' comment about how it's not always the art's problem. So true! I've seen programmers blame the art again and again, up until the point where they discover their culling code was broken, or some other major facepalm. Though it must be said that more often than not, the art IS at fault, so it is often prudent for graphics coders to examine the art for problems first. It's up to artists to know their stuff, so we can help the programmers optimize their end as well.

    Anyhow, in general the less material changes the better. To figure out what works and what doesn't, you'll want to make some test scenes and do some profiling in your engine. If you haven't seen these articles, they might help you get your head around some of the art tech issues.
    http://www.ericchadwick.com/examples/provost/byf1.html

    Also might help to check out the ExtremeTech 3D Pipeline Tutorial.
  • dlx
    cman2k wrote: »
    As a general rule of thumb, if I know I'm making something that will be used A LOT, then I make a point to use as few draw calls as possible. And If I'm making something that won't be used very much, then I'll give myself some breathing room.
    Exactly! Be efficient with the most frequently used assets. If an asset is the centerpiece of a level and only appears once then don't let anyone browbeat you into optimizations that aren't really necessary.

    Like cman2k pointed out, you could try making LOD models. Yes, you will have to spend memory to get that performance. If your programmer thinks LOD will help then give it a shot. Don't be afraid! It's not as hard as you think. It's a good skill to have and will teach you a lot about your engine. The first few LOD models you make will probably be awful and pop really bad when they swap, but THAT'S NORMAL until you find the sweet spot. Keep working with it and you'll get it right.
  • Daaark
    Offline / Send Message
    Daaark polycounter lvl 17
    dlx wrote: »
    ;)

    Some bad assumptions in here.

    Texture fetches = reading data from the textures that are loaded for use.
    Texture binding = loading the texture from one part of the GPU's ram into texture units. Very slow.

    You are also falsely correcting renderhjs about the one texture sheet. Every scene is going to get split up into multiple chunks for frustum culling, and put into different vertex arrays, which are much quicker to swap out then textures, material settings, or especially shaders. The speed cost of switching vertex array is considered negligible these days, unlike in the past, where people often threw EVERYTHING in one big vertex array.

    Every time a frame is rendered, the scene structure will throw out a list of everything to be drawn, and then that list will be sorted and organized down to the least possible draw calls.

    Sometimes, the entire scene is pre-drawn is one material just to find out how many pixels every object ended up taking up(occlusion querying), so you can know if a certain shader in the scene can be skipped altogether, or a cheaper version can be used.

    .set shader (most expensive)
    ..set up textures / material settings (less expensive)
    ...draw all vertex arrays that use the above settings (least expensive)
    repeat...

    Note that every part on every part of those previously mentioned motorcycles with 5 shaders that are using that shader and texture set can be drawn at the same time up there. 50 calls is 50 calls, but not the same as switching shaders and materials once between each of them. in this case, there are only 5 changes.

    Best to keep the draw calls under a few hundred. But they add up quick.

    And lol @ the 27 textures on a vehicle.

    ---

    Also, more on that ultra LOD stuff someone mentioned: Imposter Rendering.
  • cryrid
    Offline / Send Message
    cryrid interpolator
    Screenshot or it didn't happen :P
    I guess I was off, it was only 26 textures. The 26 one is still 1024s though, I've seen them.
    lavtextures.jpg
    http://www.youtube.com/watch?v=lJE5g18KLsQ#t=0m34s
    Heads up to mute the audio. Skip to 2 min for a quick shot of it in VBS2.
  • Progg
    Offline / Send Message
    Progg polycounter lvl 11
  • cryrid
    Offline / Send Message
    cryrid interpolator
    Headlights, those glass periscope things... I think I saw one that was just a canadian flag.
  • Cyrael
    Offline / Send Message
    Cyrael polycounter lvl 10
    cryrid wrote: »
    Headlights, those glass periscope things... I think I saw one that was just a canadian flag.



    hilarious. made my day. :-)
  • JacqueChoi
    Offline / Send Message
    JacqueChoi polycounter
    Simply put, you're asking a VERY engine specific question.

    Some engines handle Texture fetching better than others. Some engines handle drawcalls better than others.

    Other handle polygons at the expense of texture budget, and other games want to push animation power at the sacrifice of art quality.

    I think Drake was made with 45,000 Triangles, but to a lot of games that's the poly budget of 9 characters.


    REALLY depends on the tech, the title, and goals of what you're trying to make.




    General rule of thumb I've seen is 1 texture set per character.
  • Progg
    Offline / Send Message
    Progg polycounter lvl 11
    Yeah I was reading somewhere the characters in Uncharted 2 are close to 40-50k tris.. that's like double and triple what some Gears characters are isn't it? I can't confirm but that is crazy high. But then again Naughty Dog aimed the game for the PS3 hardware as well as re-using a lot of tileable textures instead of adding a new one for every asset.
  • Mark Dygert
    Elventy-seven, exactly.


    It's situational. I know people who are just starting out love their absolutes and concrete rules to follow but that's not how things work. Fuck it up a few times, get yelled at, learn and move on.

    It's not "how many textures can I use" (blanket statement).

    It's how many textures can I use for this object, under these conditions, doing these specific things, with this much time given.

    The fast way to get yourself in trouble is to deal in absolutes and not understand why the rule was laid down. For the most part you'll probably be in a position that has to set the rules for you or other people to follow.

    So the real question is "how do I set up the rules" not "what is the rule".
  • dlx
    Daaark wrote: »
    You are also falsely correcting renderhjs about the one texture sheet. Every scene is going to get split up into multiple chunks for frustum culling, and put into different vertex arrays, which are much quicker to swap out then textures, material settings, or especially shaders. The speed cost of switching vertex array is considered negligible these days, unlike in the past, where people often threw EVERYTHING in one big vertex array.
    Every scene? We don't know the size of the scene or this engine that mayaterror is using. How large would these arrays have to be? Because he's even entertaining the idea of multiple draw calls per object, I am assuming the scene is fairly complex. I doubt the one sheet approach would be viable for anything other than distant, low-res background geometry. If that's not the case then I need to try this myself and see it in action.

    I would love to see PIX captures of a full scene rendered this way. I think a lot of ms would be spent on portions of the scene that only resulted in a few pixels on screen. Unless the engine does one hell of a job of breaking up the scene, manually separating it is likely to provide better frustum culling (because we know what is playable space and what is background) and finer control over final performance.
    Note that every part on every part of those previously mentioned motorcycles with 5 shaders that are using that shader and texture set can be drawn at the same time up there. 50 calls is 50 calls, but not the same as switching shaders and materials once between each of them. in this case, there are only 5 changes.
    Drawn at the same time? Not sure what you mean here. Are we disagreeing? :) Certainly every part of one motorcycle that shares the same shader and material could be rendered at once, but I was referring to a set up where different parts of the motorcycle would use different material settings AND a different shader. Rubber without a reflection or opacity, glass without a normal map, etc. The texture would be the only thing shared. I'm not suggesting that is the only way to set up a motorcycle, of course.
    Best to keep the draw calls under a few hundred. But they add up quick.
    Again, I think you are assuming something about mayaterror's engine. It depends on the target platform and the desired frame rate. 30fps is achievable on console with 1000+ draw calls.
  • Shogun3d
    Offline / Send Message
    Shogun3d polycounter lvl 12
    Exercising best judgement should be a rule. Wait....
  • kdm3d
    kaburan wrote: »
    Exercising best judgement should be a rule. Wait....

    Isn't that an absolute? tsk tsk...
  • arshlevon
    Offline / Send Message
    arshlevon polycounter lvl 18
    JacqueChoi wrote: »
    Simply put, you're asking a VERY engine specific question.

    Some engines handle Texture fetching better than others. Some engines handle drawcalls better than others.

    This!

    our engine does texture streaming right off the disk, and it is actually better to use a ton of small stuff vs one big texture. also if you have lots and lots of characters its best to make everything separate for maximum reuse. if you made everything on one sheet then wanted to re-use the pants on something else, your kinda fucked.
  • yiannisk
    Offline / Send Message
    yiannisk polycounter lvl 14
    talking with our programmers a lot, it usually is bad to throw tons of small textures as it increases access time. but it really depends on the engine and the platform the result is heading to. there is no rule of thumb. but most engines the larger the list of textures and objects you give them to access the worst. (on the other hand making huge chunks of geometry or textures that might not be used in the same level is not very good either as you are loading unnecessary things increasing your memory usage with texture space that doesn't belong in that scene)

    Textures (as well as smoothing groups) can in some cases dramatically increase the amount of objects in your level regardless how you exported them. (ofc this can also be used to your advantage in some cases)

    i.e. if you have an object with 5 material IDs (or five diffuse textures applied to them) these objects then are split in multiple objects. even if you export your scene as one single piece, the engine will still break them down and treat them as separate objects based on the amount of materials you got. this way you increase not only texture fetch time but also object fetch time and this could be bad.

    for the engines we use, we often combine textures that need only a single channel into the "alpha" channel of some other texture. i.e. throw your specular B\W texture into the alpha of your normal map\specular colour map, or the parallax map in the alpha of the normal map. this is a way we can limit down the amount of textures per object.

    if we take crowd textures as an example then you should try to stick as many as you can in one pack from the people who belong into the same group. I have received crowd files with as many as 20 people crammed in one texture page.

    there is one very nice plug-in for max called flatiron. this is an amazing tool that can help you to easily combine (or break down) textures and it is a huge time saver while at the same time it gives you great flexibility to manage your textures in the max scene as you please, and at the same time exporting them to the engine as you need. it is not perfect and doesn't fit in every case but it is a huge time-saver none the less.
  • McGreed
    Offline / Send Message
    McGreed polycounter lvl 15
    @yiannisk
    I actually used that method a couple of times, because there is channels you don't need, and can use them for other maps, for example there was a time where I had to use the blue channel of normalmaps for specular. Its a way to press everything into fewer textures.
Sign In or Register to comment.