Material optimization: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
 
No edit summary
 
(48 intermediate revisions by 15 users not shown)
Line 1: Line 1:
When working with materials, there are a couple performance bottlenecks to keep in mind: fillrate (which usually affects all DirectX levels), and reducing vertex processing (which only is a bottleneck on DirectX7). As a level designer, the only real choice that you've got to combat any of these performance bottlenecks is to either reduce the amount of a particular expensive material on screen, or to turn off some of the expensive features in the material (like bumpmapping, for example). Note that you can make different choices about which features to use at each separate DirectX support level.
{{lang|{{PAGENAME}}|title=Material Optimization}}[[Category:Material System]]
{{back|Optimization (level design)}}
{{toc-right}}


One other important aspect of dealing with performance bottlenecks is that you're going to have to measure framerates to see how well you're doing. For Half-Life 2, we used 3 target machines (determined based on looking at our hardware survey stats) for measuring performance at various DirectX levels. These were:
== Shader Fallbacks ==
''(This section is only relevant if you are creating new materials. [[#Overdraw|Skip it?]])''
{{todo|Document $fallbackmaterial.}}


{|
It goes without saying that not all PCs are equal. Your materials will need to look good on a wide range of specifications, and the best way of achieving acceptable performance at the low-end without sacrificing fidelity at the high-end is with [[shader]] fallbacks.
!DX (DirectX) Level || Minimum Platform
 
The good news is that Valve's stock materials are already set up with fallbacks, where appropriate, and in most cases you won't need to do any work in this area at all. The even better news is that if you ''do'' need to create a fallback, all of the hard work of benchmarking different hardware and graphics settings has already been done by Valve and all you need do is choose between a handful of coarse [[DirectX Versions|DirectX levels]] and other miscellaneous graphics settings.
 
=== Usage ===
Shader fallbacks are parameters of the material like any other, and should go inside the material's original pair of curly braces (<code>{</code> and <code>}</code>).
 
==== Fallback Shaders ====
:{{src07|since}}
 
"<=dx90"
{
&lt;VMT Parameters to set&gt;
}
 
{{warning|You must wrap conditional statements in quotes if an equals sign <code>{{=}}</code> is used in it like the example above, or it will create a parsing error.}}
 
The parameters within a fallback are only applied if its opening statement is true. Accepted values include:
 
{| class="standard-table" style="width: 100%;"
! Name !! Condition !! Notes
|-
|<DX95||Below {{code|dxlevel 95}}||
|-
|<DX90
|rowspan=4|{{code|dxlevel 90}}||
|-
|<=DX90||
|-
|>=DX90||
|-
|>DX90||
|-
|>=DX90_20b
|rowspan=2|{{code|dxlevel 92}}||
|-
|<DX90_20b||
|-
|dx9||||Ditto of <code>>=DX90</code> for legacy compatibility
|-
|hdr||||
|-
|hdr_dx9||||Ditto of <code>hdr</code> for legacy compatibility
|-
|ldr||||
|-
|360||Game is running on the {{360|name}}||
|-
|srgb||Game is using correct [[sRGB]] blending
|-
|-
| DX9 || ATI 9800, 1 GB, P4 3.0 GHz
|GPU<1
| rowspan=4 |Comparison of <code>gpu_level</code> [[console variable]]
| rowspan=4 |{{l4d|since}} {{src13|not}}
|-
|-
| DX8 || NVidia GeForce4 Ti4600, 512 MB, P4 2.0 GHz
|GPU<2
|-
|-
| DX7 || NVidia GeForce2 MX, 256MB, P4 1.2 GHz
|GPU>=1
|-
|GPU>=2
|}
|}


Here's how you specify different material parameters for different DX support levels: Each shader has a specific fallback shader it uses if the shader determines it can't run on the particular DX level on the card currently being run. Within the definition of your material, you add a field with the same name as the fallback shader and a sub-block of fields which are the values of those fields that you want to use if that fallback shader is being used. A list of the most commonly used shaders and their fallback shaders is shown below.  
==== Fallback Shaders (Legacy) ====
{{collapse top|Fallback Shaders {{l4d|removed}}}}
{{note|The following material, created before conditionals were added to the engine, is actually a bad example. It is not future-proof: systems running HDR_DX9 will not see normal mapping because it is only defined for DX9 and DX8 mode.


Here's an example:
To avoid this issue in your own materials, use the conditional statements above or target high-end computers in the main body of the material, and use fallbacks only to remove/replace/tweak features for the benefit of older machines.}}
LightmappedGeneric_DX8
{
&lt;DX8 parameters&gt;
}
While a "top-level" shader is called in the first line of a material, the actual job of rendering is passed down by the engine to a specific 'family member'. You can pass commands that only apply to a single child shader by providing its name. '''[[Half-Life 2 Shader Fallbacks|There is a full list of fallback shaders here.]]'''
=== Example ===


  "LightmappedGeneric"
You can clear a value out by typing <code>""</code> (two quotes, nothing between).
 
  LightmappedGeneric
  {
  {
  "$basetexture" "Metal/metalwall063a"
  $basetexture metal/metalwall078a
  "$surfaceprop" "metal"
  $surfaceprop metal
"$envmap" "env_cubemap"
   
   
  "LightmappedGeneric_DX9"
  $envmap env_cubemap
$envmaptint "[.56 .56 .75]"
$envmapcontrast 1
$envmapsaturation 1
LightmappedGeneric_DX9
  {
  {
  "$bumpmap" "metal/metalwall063a_normal"
  $bumpmap metal/metalwall078a_normal
  "$normalmapalphaenvmapmask" 1
  $normalmapalphaenvmapmask 1
$envmaptint "[.09 .1 .12]"
  }
  }
   
   
  "LightmappedGeneric_DX8"
  LightmappedGeneric_DX8
  {
  {
  "$basetexture" "metal/citadel_metalwall063a"
  $bumpmap metal/metalwall078a_normal
  "$basealphaenvmapmask" 1
  $nodiffusebumplighting 1
$normalmapalphaenvmapmask 1
  }
  }
   
   
  "LightmappedGeneric_DX6"
  LightmappedGeneric_NoBump_DX8
  {
  {
  "$fallbackmaterial" "metal/metalwall063b"
  $basetexture metal/citadel_metalwall078a
$basealphaenvmapmask 1
  }
  }
  }
  }
{{Collapse bottom}}


In this example, the <code>$envmap</code> field is used no matter what DirectX level the game is being run at. The <code>$basetexture</code> field <code>metal/metalwall063a</code> is used for every fallback shader except <code>LightmappedGeneric_dx8</code> (used for running under DX8), which uses <code>metal/citadel_metalwall063a</code> for its <code>$basetexture.</code> Also under DX8 only, we add a field specifying that the special base texture used by dx8 has an envmap mask in its alpha channel. When using the <code>LightmappedGeneric_DX9</code> shader (used by DX9), we add a bumpmap and specify that the envmap mask is in the alpha channel of the bumpmap. Note that it's also possible to cause the material system to use a completely different material with a totally different set of shaders by specifying a <code>$fallbackmaterial</code> in a fallback block, which is done in this example when falling back to the <code>LightmappedGeneric_DX6</code> shader (used by DX7 and DX6).
=== Conditional Tests ===
:{{src07|since}}


For a listing of all of the fallback shaders used in Half-Life 2, see the document [[Half-Life 2 Shader Fallbacks]].
"srgb?$color" "[1.2 1.2 1.2]"
"!srgb?$color" "[0.8 0.8 0.8]"
 
There are also conditional tests that can also be applied directly to variables and inverted with a preceding exclamation point <code>!</code>.
 
{| class="standard-table" style="width: 100%;"
! Name !! Condition !! Notes
|-
|hdr||||
|-
|ldr||||
|-
|360||Game is running on the {{360|name}}||
|-
|srgb
| rowspan=3 |Game is using correct [[sRGB]] blending
|-
|srgb_gameconsole
| rowspan=2 |{{csgo|since}}
|-
|srgb_pc
|-
|lowfill||<code>mat_reduceparticles</code> [[console variable]] is true||{{l4d|since}} {{src13|also}} Has nothing to do with <code>mat_reducefillrate</code>
|-
|SonyPS3||Game is running on the {{ps3|name}}
| rowspan=10 |{{l4d|since}} {{src13|not}}
|-
|gameconsole
|-
|GPU<1
| rowspan=6 |Comparison of <code>gpu_level</code> [[console variable]]
|-
|GPU<2
|-
|GPU<3
|-
|GPU>=1
|-
|GPU>=2
|-
|GPU>=3
|-
|HighQualityCSM
| rowspan=2 |[[CSM]] quality
|-
|LowQualityCSM
|}
 
== Overdraw ==
[[File:mat_fillrate.jpg|thumb|250px|Using <code>mat_fillrate</code> to investigate overdraw. Problem areas end up bright red.]]
 
[[Wikipedia:Fillrate|Fillrate]] refers to the number of pixels that a video card can shove into its memory per second, independently of the time needed to decide what color they should be. It becomes an issue when the user tries to run a game at a higher resolution than the card is capable of, or more commonly when a given pixel is being processed and re-processed many times.
 
This latter effect is known as "overdraw" and occurs when a material is [[$translucent|translucent]] (particles are dreadful), uses [[normal mapping]], uses [[specular reflection]]s, and/or is refractive. [[Dynamic light]]s and effects like [[motion blur]] and [[HDR]] (but ''not'' [[color correction]]) are also culprits. You can spot overdraw  by checking {{ent|showbudget}} for an unusually large "Swap Buffer" bar, and examine its effect precisely with <tt>mat_fillrate</tt>.
 
The only true solution to overdraw is reducing the number of pixels that needs to be re-processed. This can be done with a shader fallback, by creating fewer objects, or by reducing screen resolution. There are also a few tricks that can be employed, albeit to limited effect, which follow.
{{tip|To temporarily work around fillrate issues, use {{ent|mat_viewportscale}}.}}
 
=== Particles ===
See [[:Category:Particle_System#Particle_Performance|Particle Performance]].
 
=== Refraction ===
Any object that crosses the plane of a refractive material will be rendered twice: once for the direct view, and again for the refracted view (it's also rendered for reflection, but that can't be helped). This becomes avoidable when the water is obscured—for instance, if the object is embedded in a [[displacement]] sand drift. By raising the model or cutting the water brush so that it doesn't extend beneath the displacement, a whole rendering pass can be avoided.
 
You can examine what is being rendered for refraction with <tt>mat_showwatertextures 1</tt>.
 
=== Normal Mapping ===
{{ent|LightmappedGeneric}} materials are the prime suspects here. To help a little, you can add {{ent|$nodiffusebumplighting|1}} to the material in question, but as ever, the only true solution is a fallback.
 
Creating [[$ssbump|SSBumps]] instead of generic normalmaps may save on performance slightly, although gains are likely to be low on modern hardware.
{{tip|If creating DirectX 8 fallbacks, use <code>LightmappedGeneric_NoBump_DX8</code> to differentiate between video cards that have and haven't been deemed suitable for normal mapping by Valve.}}


=== Water ===
=== Water ===
There are a couple of tricks to use to get performance back on levels that have water in them. Water is a pretty expensive shader, and you oftentimes can be fillrate bound when you use it. This can often be seen by typing <code>+showbudget</code> at the developer console: if the '''Swap Buffers''' bar is very high, you're likely running into a fill rate problem. To reduce this problem, make sure to cut the water brushes to eliminate parts that lie within other brushes or displacements.
With [[Water (shader)|Water]], overdraw is caused by both refraction and reflection, often over a large area. You can disable either or both if needed:
 
Another performance problem is that any object that is partially inside and partially outside of the water is rendered twice: once for the refraction, and once for the part of the scene that lies on the same side of the water plane as the player. You can see which objects are being rendered into the refraction by typing <code>mat_showwatertextures 1</code> in the developer console. Also typing <code>mat_wireframe 1</code> in the console can help to highlight the problem areas. Try moving any <code>prop_static</code> so they don't touch the water, and cut your water brushes so as few as possible models actually cross the water plane.


== Dealing with fillrate issues ==
;<code>$forcecheap 1</code>:Disables both reflection ''and'' refraction.
The materials which are most expensive from a fillrate perspective are the water and refract shaders, and any material which uses bumpmaps. The way you can tell you've got a fillrate problem if you see a big bar for swapbuffers when using <code>+showbudget</code>. We'll talk here about solutions for a couple of the most common offenders for fillrate: refraction, water, and bumpmapped shaders.
;<code>$refracttexture</code>
;<code>$reflecttexture</code>:Using these commands without passing a texture bypasses one or both.


For refractive shaders, the only real thing you can do is to use the <code>$fallbackmaterial</code> to make the shader not be refractive at the DX level where it's causing you fillrate issues. For water shaders, the fillrate cost of these shaders goes up when using refraction and reflection. You can turn both off by specifying <code>$forcecheap 1</code> in the .VMT. You can also disable refraction by <i>not</i> specifying a <code>$refracttexture</code> in the .VMT, and you can disable reflection by not specifying a <code>$reflecttexture</code>. If you've got fillrate and CPU to burn, you can forcibly make the water do local reflections (regardless of video config settings) by specifying <code>$forceexpensive 1</code>, and you can forcibly make the water reflect entities by specifying <code>$reflectentities 1</code>.
If you've got fillrate and CPU to burn, you can forcibly make the water do local reflections regardless of video config settings by specifying <tt>$forceexpensive 1</tt>, and you can forcibly make the water reflect entities by specifying <tt>$reflectentities 1</tt>.


For bumpmapping, usually the shader that's the fillrate offender is <code>LightmappedGeneric</code>. There are a couple things you can do to improve performance. Specifying <code>$nodiffusebumplighting 1</code> turns off diffuse bumpmapping, but specular bumpmapping still is active. Turning off specular bumpmapping will also improve performance, although there is a pretty big visual quality hit you take for turning it off. One feature we added to <code>LightmappedGeneric</code> to help turn off bumpmapping on those DX8 cards that just weren't up to snuff was by introducing a fallback called <code>LightmappedGeneric_NoBump_dx8</code>. This fallback is automatically used on DX8 machines which are specified as using <code>mat_reducefillrate 1</code> in the dxsupport.cfg file. In some materials used by the Citadel in Half-Life 2, we use this to allow us to specify the use of bumpmaps under dx9 and the normal dx8 path, but not under the low-fillrate DX8 path.
== Special Materials ==
{{main|Tool textures (Source)}}
===Nodraw===
[[File:Toolsnodraw.gif|right|tools\toolsnodraw]]


Low-end cards (DX7) also get both hosed from a fill-rate perspective and also from a vertex processing perspective by using unbumped specularity. As a result, by default, specularity is turned off on DX7 cards. To reactivate specularity for these materials under DX7, add <code>$multipass 1</code> to the .VMT. The <code>LightmappedGeneric_dx6, VertexLitGeneric_dx6, VertexLitGeneric_dx7</code>, and <code>UnlitGeneric_dx6</code> shaders will all respond to this material parameter.
<code>tools\toolsnodraw</code> prevents a surface from being drawn at all, prevents lightmaps from being compiled for it, and prevents it from reflecting light (it still casts shadows). Use it on surfaces that will never be seen by the player but can't be sealed off by another brush, such as the tops of roofs and ledges, the far side of building in inaccessible areas, particularly those in [[Menu_Background_Map|background map]]s, and underneath displacements and func_detail (it may be necessary to leave a few units of textured brush underneath them to avoid seams due to vertex precision inaccuracies).


----
===Black===
<code>tools/toolsblack</code> is a material that is pitch black and doesn't bounce any light. It's useful at the bottom of deep pits, in inaccessible caves, etc.
{{important|In some games (ex: {{css}} {{csgo}} {{dods}} {{L4d}} {{l4d2}}), toolsblack is lightmapped. In such instances, {{code|vgui/black}} should be used instead.}}


'''''[[Optimization (level design)|<< Return to Optimization (level design)]]'''''
===Skybox 2D===
A [[3D Skybox]] is drawn in full whenever the <code>tools/toolsskybox</code> material is being drawn. You can avoid this by using <code>tools/toolsskybox2d</code> in its place. Obviously, this only has an effect when the normal skybox material isn't visible, and it doesn't have any effect when there is no 3D skybox.

Latest revision as of 05:58, 22 May 2025

English (en)Español (es)Translate (Translate)
Optimization (level design)

Shader Fallbacks

(This section is only relevant if you are creating new materials. Skip it?)

Todo: Document $fallbackmaterial.

It goes without saying that not all PCs are equal. Your materials will need to look good on a wide range of specifications, and the best way of achieving acceptable performance at the low-end without sacrificing fidelity at the high-end is with shader fallbacks.

The good news is that Valve's stock materials are already set up with fallbacks, where appropriate, and in most cases you won't need to do any work in this area at all. The even better news is that if you do need to create a fallback, all of the hard work of benchmarking different hardware and graphics settings has already been done by Valve and all you need do is choose between a handful of coarse DirectX levels and other miscellaneous graphics settings.

Usage

Shader fallbacks are parameters of the material like any other, and should go inside the material's original pair of curly braces ({ and }).

Fallback Shaders

(in all games since Source 2007)
"<=dx90"
{
	<VMT Parameters to set>
}
Warning.pngWarning:You must wrap conditional statements in quotes if an equals sign = is used in it like the example above, or it will create a parsing error.

The parameters within a fallback are only applied if its opening statement is true. Accepted values include:

Name Condition Notes
<DX95 Below dxlevel 95
<DX90 dxlevel 90
<=DX90
>=DX90
>DX90
>=DX90_20b dxlevel 92
<DX90_20b
dx9 Ditto of >=DX90 for legacy compatibility
hdr
hdr_dx9 Ditto of hdr for legacy compatibility
ldr
360 Game is running on the Xbox 360
srgb Game is using correct sRGB blending
GPU<1 Comparison of gpu_level console variable (in all games since Left 4 Dead) (not in Source 2013)
GPU<2
GPU>=1
GPU>=2

Fallback Shaders (Legacy)

Fallback Shaders (removed since Left 4 Dead)
Note.pngNote:The following material, created before conditionals were added to the engine, is actually a bad example. It is not future-proof: systems running HDR_DX9 will not see normal mapping because it is only defined for DX9 and DX8 mode. To avoid this issue in your own materials, use the conditional statements above or target high-end computers in the main body of the material, and use fallbacks only to remove/replace/tweak features for the benefit of older machines.
LightmappedGeneric_DX8
{
	<DX8 parameters>
}

While a "top-level" shader is called in the first line of a material, the actual job of rendering is passed down by the engine to a specific 'family member'. You can pass commands that only apply to a single child shader by providing its name. There is a full list of fallback shaders here.

Example

You can clear a value out by typing "" (two quotes, nothing between).

LightmappedGeneric
{
	$basetexture metal/metalwall078a
	$surfaceprop metal

	$envmap	env_cubemap
	$envmaptint "[.56 .56 .75]"
	$envmapcontrast 1
	$envmapsaturation 1

	LightmappedGeneric_DX9
	{
		$bumpmap metal/metalwall078a_normal
		$normalmapalphaenvmapmask 1
		$envmaptint "[.09 .1 .12]"
	}

	LightmappedGeneric_DX8
	{
		$bumpmap metal/metalwall078a_normal
		$nodiffusebumplighting 1
		$normalmapalphaenvmapmask 1
	}

	LightmappedGeneric_NoBump_DX8
	{
		$basetexture metal/citadel_metalwall078a
		$basealphaenvmapmask 1
	}
}

Conditional Tests

(in all games since Source 2007)
"srgb?$color" "[1.2 1.2 1.2]"
"!srgb?$color" "[0.8 0.8 0.8]"

There are also conditional tests that can also be applied directly to variables and inverted with a preceding exclamation point !.

Name Condition Notes
hdr
ldr
360 Game is running on the Xbox 360
srgb Game is using correct sRGB blending
srgb_gameconsole (in all games since Counter-Strike: Global Offensive)
srgb_pc
lowfill mat_reduceparticles console variable is true (in all games since Left 4 Dead) (also in Source 2013) Has nothing to do with mat_reducefillrate
SonyPS3 Game is running on the PlayStation 3 (in all games since Left 4 Dead) (not in Source 2013)
gameconsole
GPU<1 Comparison of gpu_level console variable
GPU<2
GPU<3
GPU>=1
GPU>=2
GPU>=3
HighQualityCSM CSM quality
LowQualityCSM

Overdraw

Using mat_fillrate to investigate overdraw. Problem areas end up bright red.

Fillrate refers to the number of pixels that a video card can shove into its memory per second, independently of the time needed to decide what color they should be. It becomes an issue when the user tries to run a game at a higher resolution than the card is capable of, or more commonly when a given pixel is being processed and re-processed many times.

This latter effect is known as "overdraw" and occurs when a material is translucent (particles are dreadful), uses normal mapping, uses specular reflections, and/or is refractive. Dynamic lights and effects like motion blur and HDR (but not color correction) are also culprits. You can spot overdraw by checking showbudget for an unusually large "Swap Buffer" bar, and examine its effect precisely with mat_fillrate.

The only true solution to overdraw is reducing the number of pixels that needs to be re-processed. This can be done with a shader fallback, by creating fewer objects, or by reducing screen resolution. There are also a few tricks that can be employed, albeit to limited effect, which follow.

Tip.pngTip:To temporarily work around fillrate issues, use mat_viewportscale.

Particles

See Particle Performance.

Refraction

Any object that crosses the plane of a refractive material will be rendered twice: once for the direct view, and again for the refracted view (it's also rendered for reflection, but that can't be helped). This becomes avoidable when the water is obscured—for instance, if the object is embedded in a displacement sand drift. By raising the model or cutting the water brush so that it doesn't extend beneath the displacement, a whole rendering pass can be avoided.

You can examine what is being rendered for refraction with mat_showwatertextures 1.

Normal Mapping

LightmappedGeneric materials are the prime suspects here. To help a little, you can add $nodiffusebumplighting 1 to the material in question, but as ever, the only true solution is a fallback.

Creating SSBumps instead of generic normalmaps may save on performance slightly, although gains are likely to be low on modern hardware.

Tip.pngTip:If creating DirectX 8 fallbacks, use LightmappedGeneric_NoBump_DX8 to differentiate between video cards that have and haven't been deemed suitable for normal mapping by Valve.

Water

With Water, overdraw is caused by both refraction and reflection, often over a large area. You can disable either or both if needed:

$forcecheap 1
Disables both reflection and refraction.
$refracttexture
$reflecttexture
Using these commands without passing a texture bypasses one or both.

If you've got fillrate and CPU to burn, you can forcibly make the water do local reflections regardless of video config settings by specifying $forceexpensive 1, and you can forcibly make the water reflect entities by specifying $reflectentities 1.

Special Materials

Main article:  Tool textures (Source)

Nodraw

tools\toolsnodraw

tools\toolsnodraw prevents a surface from being drawn at all, prevents lightmaps from being compiled for it, and prevents it from reflecting light (it still casts shadows). Use it on surfaces that will never be seen by the player but can't be sealed off by another brush, such as the tops of roofs and ledges, the far side of building in inaccessible areas, particularly those in background maps, and underneath displacements and func_detail (it may be necessary to leave a few units of textured brush underneath them to avoid seams due to vertex precision inaccuracies).

Black

tools/toolsblack is a material that is pitch black and doesn't bounce any light. It's useful at the bottom of deep pits, in inaccessible caves, etc.

Icon-Important.pngImportant:In some games (ex: Counter-Strike: Source Counter-Strike: Global Offensive Day of Defeat: Source Left 4 Dead Left 4 Dead 2), toolsblack is lightmapped. In such instances, vgui/black should be used instead.

Skybox 2D

A 3D Skybox is drawn in full whenever the tools/toolsskybox material is being drawn. You can avoid this by using tools/toolsskybox2d in its place. Obviously, this only has an effect when the normal skybox material isn't visible, and it doesn't have any effect when there is no 3D skybox.