State objects: identity, binding and what reaches the renderer
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Read at 009d40f5; no test was run. CNA's comments describe the freeze and same-reference rules as measured on Microsoft XNA; per-family pixel results are not interchangeable.
XNA's four state objects are reference types that become immutable once a device has used them. C++ has no garbage-collected references, so CNA has to decide what copying, assigning and mutating a BlendState means. This page states CNA's answer at this snapshot (a shared payload with XNA's freeze rule), exactly which fields reach the renderer and when, the profile checks made at assignment and at draw time, and where renderer families still differ. It is for readers porting XNA state code, writing custom states, or trying to understand why a state change did or did not take effect. The usage guide is Graphics state.
Identity: copy construction versus assignment
Each of BlendState, DepthStencilState, RasterizerState and SamplerState (BlendState.cpp and its siblings) derives from GraphicsResource and keeps its fields in a private payload, std::shared_ptr<State>, that also carries two flags: isBound and isDisposed. The two C++ copy operations are given different meanings on purpose:
- Copy construction creates a new, independent, mutable state. It copies the field values into a fresh payload with
isBoundandisDisposedcleared.BlendState custom = BlendState::Opaque;is CNA's spelling of C#'snew BlendState()initialised from a preset. - Copy assignment creates an alias.
a = b;makesashareb's payload and its resource identity (ShareResourceIdentityWith): name, tag, device, disposed flag andDisposingsubscribers behave as one resource, as two C# references to one object would. Disposing any alias disposes them all, and theDisposingevent is raised on each alias with the canonical object as sender.
The device stores its current states in fields of these types and fills them by assignment, so after device.setBlendStateProperty(custom) the device's field and custom are aliases of one payload. The payload outlives the local object that supplied it: if custom goes out of scope, the device keeps the state, its name and its tag. That is the C++ form of XNA's reference semantics, and it is safe for stack objects and temporaries.
The static presets (BlendState::Opaque and the others below) are const objects whose payloads are created already bound, with names such as "BlendState.Opaque". They are not GPU allocations and never need disposal. Because an assigned preset is aliased rather than copied, a preset's getGraphicsDeviceProperty() reports the device that most recently bound it, exactly as a shared XNA preset's GraphicsDevice would.
Bound states are frozen
Assigning a state to a device calls BindForUse, which sets the payload's isBound flag. From then on every property setter of that payload, reached through any alias, throws InvalidOperationException ("Cannot modify a BlendState after it has been bound to a GraphicsDevice."). This is Microsoft XNA's rule, which CNA's test file names as the authority over FNA's mutable behaviour (SOFTWARE-232). Two consequences follow:
- Mutating the object you assigned throws; it cannot silently change, or silently fail to change, the device state.
- Mutating through the device's getter,
device.getBlendStateProperty().setColorSourceBlendProperty(…), also throws, because the device's field is an alias of the same bound payload.
The working pattern is to configure a state before assigning it, and to change state by assigning a different object. To derive a variation of the current state, copy-construct it, which yields a fresh unbound payload:
BlendState custom; // unbound, mutable
custom.setColorSourceBlendProperty(Blend::SourceAlpha);
custom.setColorDestinationBlendProperty(Blend::One);
custom.setAlphaSourceBlendProperty(Blend::SourceAlpha); // Reach: alpha must mirror colour
custom.setAlphaDestinationBlendProperty(Blend::One);
device.setBlendStateProperty(custom); // binds and applies; custom is now frozen
// custom.setColorSourceBlendProperty(Blend::Zero); // would throw InvalidOperationException
BlendState variant = device.getBlendStateProperty(); // copy construction: a new, unbound state
variant.setColorSourceBlendProperty(Blend::One);
variant.setAlphaSourceBlendProperty(Blend::One);
device.setBlendStateProperty(variant); // the required submission step
Assignment also checks disposal: binding a disposed state throws ObjectDisposedException. There is one deliberate exception, again measured from XNA. Microsoft XNA compares the incoming reference with the one it already holds before it calls Apply(), so assigning the state that is already active is a no-op, even if that state has since been disposed. CNA reproduces this by comparing payloads (SOFTWARE-350): setBlendStateProperty, setDepthStencilStateProperty and setRasterizerStateProperty return early for the active payload. The standalone device properties below mark their parent state "dirty", and a dirty state is re-applied, and its disposal detected, even when the same payload is assigned. A state may be shared by several devices; each device keeps its own cached identity. GraphicsDeviceDefaultStateTests.cpp pins all of these rules for the three device-wide states and for sampler slots.
Code written against an older CNA revision in which the device copied states by value, and which mutated a state after assigning it (for example to "update" the device through the getter), now throws. The fix is always the same: configure, then assign; never mutate after assignment.
What reaches the renderer, and when
Assignment and drawing split the work between them (GraphicsDevice.cpp):
| State | Submitted | Renderer calls |
|---|---|---|
BlendState | At assignment | ApplyBlendState with the six factor and function ordinals and a BlendWriteState holding all four per-target ColorWriteChannels masks and the state's MultiSampleMask; then SetBlendFactor with the state's BlendFactor |
DepthStencilState | At assignment (skipped at device construction on a renderer without depth-stencil support) | ApplyDepthStencilState with all sixteen fields, including two-sided stencil; then SetReferenceStencil |
RasterizerState | At assignment and before every draw | ApplyRasterizerState (cull mode, fill mode, scissor enable, depth bias, slope-scaled bias) and ApplyRasterizerMultiSampleState (MultiSampleAntiAlias) |
Pixel SamplerStates | Before every draw, for all sixteen slots | ApplySamplerState (filter, AddressU, AddressV, maximum anisotropy), ApplySamplerMipState (MaxMipLevel, MipMapLevelOfDetailBias) and ApplySamplerAddressW |
Vertex SamplerStates | Read by compiled-effect draws | Handed to the renderer, with the vertex texture collection, in the compiled effect's draw parameters; the EasyGL, OPENGL4 and SOFTWARE compiled-effect runtimes read them, while VULKAN and WEBGPU refuse a compiled effect whose vertex shader samples a texture |
Re-applying the rasterizer state at every draw follows FNA, which does the same, and has a practical reason: its native effect depends on the destination's sample and depth configuration, which can change between draws. SpriteBatch uses the same device setters: at each flush it assigns its blend, sampler-slot-0, depth-stencil and rasterizer states (by default AlphaBlend, LinearClamp, DepthStencilState::None and CullCounterClockwise; a null argument means the default), then re-applies samplers from slot 1 upward. The deferred batch retains the caller's state objects until that flush.
Standalone device properties
Three XNA device properties can change one piece of a state without replacing the state object: BlendFactor, MultiSampleMask and ReferenceStencil. Each calls the renderer immediately (SetBlendFactor; ApplyBlendState with the current blend equation and masks and only the coverage mask replaced; SetReferenceStencil) and marks its parent state dirty so that the next assignment of the same state object re-applies it. The device's MultiSampleMask therefore reaches the renderer; it does not rewrite the caller-visible BlendState that supplied the rest of the state.
Commit after success
The device updates its public cache only after the renderer calls return. A renderer that rejects a state (a Direct3D 9 render-state failure, or Metal refusing an unsupported write mask) therefore leaves the device describing the state that is really installed, not the one that was refused. The scissor rectangle is validated first as well: a rectangle that does not fit inside the active render surface throws ArgumentException.
Profile checks at assignment and at draw time
setBlendStateProperty validates the state against the profile before binding it. Under Reach, separate alpha blending (alpha factors or function that differ from the colour ones) and SourceAlphaSaturation as a destination factor throw NotSupportedException; on both profiles BlendFunction::Min and Max require One/One factors. The user guide's BlendState section shows the Reach rule with an example. Other state rules are checked when a draw is submitted, as XNA's VerifyCanDraw does: a float or half-float texture sampled with any filter other than Point, blending or masked colour writes into a non-blendable float target, and a non-power-of-two texture sampled with a non-Clamp address mode under Reach all throw from the draw call. The details are on Surface formats: rules checked at draw time.
BlendState
Four presets, with factors in (colour source, alpha source, colour destination, alpha destination) order: Additive (SourceAlpha, SourceAlpha, One, One), AlphaBlend (One, One, InverseSourceAlpha, InverseSourceAlpha, the premultiplied-alpha equation), NonPremultiplied (SourceAlpha, SourceAlpha, InverseSourceAlpha, InverseSourceAlpha) and Opaque (One, One, Zero, Zero). A default-constructed state is opaque: One/Zero with Add, all four write masks All, an opaque-white blend factor and a MultiSampleMask of -1 (all samples).
Blend has thirteen values (One, Zero, the source and destination colour and alpha factors and their inverses, BlendFactor, InverseBlendFactor, SourceAlphaSaturation); BlendFunction has five (Add, Subtract, ReverseSubtract, Min, Max). ColorWriteChannels is a flags enum (Red = 1, Green = 2, Blue = 4, Alpha = 8, All = 15) with hand-written bitwise operators, and a state carries four of them, one per render-target slot.
Whether a pixel is premultiplied depends on where it came from, not on the blend state: cna-content's texture processor premultiplies by default, as XNA's TextureProcessor.PremultiplyAlpha does, so built content pairs with AlphaBlend; loose images decoded at run time (a Texture2D file-path constructor, FromStream) are straight alpha and pair with NonPremultiplied. See Content pipeline for the processor parameter.
What renderers do with it
The shared bridge carries the whole value surface, but every ApplyBlendState override makes its own decision about the BlendWriteState, and native support is narrower on some families. Examples at this snapshot: SDL_GPU bakes all four write masks into its pipeline key but does not support MultiSampleMask, because SDL documents the sample-mask fields as reserved; Metal throws NotSupportedException for anything but all-channel writes and the default sample mask; Canvas maps the factors to a composite operation and cannot express write masks at all; the Vulkan history of this hook, a single hard-coded equation replaced by a real per-factor mapping, is on Vulkan: blend state. The strongest shared evidence is computed-expectation testing rather than property round trips: the parity fixture blend_states (registered for EasyGL, WebGPU, SDL_GPU and OpenGL 4) checks every factor, function, separate channel, blend factor and write mask against an equation, and blend_state_matrix_contract_test.cpp runs a factor and function matrix on EasyGL, Software and OpenGL 4.
DepthStencilState
Three presets: Default (depth test and write on), DepthRead (test on, write off) and None (both off). Sixteen properties: DepthBufferEnable, DepthBufferWriteEnable, DepthBufferFunction; StencilEnable, StencilFunction, StencilMask, StencilWriteMask, ReferenceStencil, StencilPass, StencilFail, StencilDepthBufferFail; TwoSidedStencilMode; and four counter-clockwise variants (CounterClockwiseStencilFunction, …Pass, …Fail, …DepthBufferFail). The defaults are XNA's: depth test and write on with CompareFunction::LessEqual, stencil off, stencil function Always, both masks -1, every operation Keep, reference 0. CompareFunction and StencilOperation have eight values each.
ReferenceStencil reaches the renderer both inside the whole-state application and through the standalone SetReferenceStencil, whose interface default does nothing. At this snapshot EasyGL, OpenGL 4, Vulkan, WebGPU, SDL_GPU, Direct3D 9, 11 and 12, Metal, FNA3D, Software, GDI and PortableGL implement it; EasyGL, whose GL call sets function, reference and mask together, re-issues glStencilFunc with the remembered function and mask, and simply keeps the value while the stencil test is off. A stencil test also needs stencil bits: the back buffer gets them only when its depth format is Depth24Stencil8, which the platform turns into a stencil request when it creates the GL context (SDL_GL_STENCIL_SIZE on SDL, the visual or config choice on X11 and Wayland), and a render target gets them through the same depth format. The user guide lists which renderers have a stencil buffer at all under DepthStencilState. Vulkan's stencil history, the front- and back-face convention and dynamic stencil state are on Vulkan: depth-stencil state. Shared evidence: stencil_matrix_contract_test.cpp (EasyGL, Software, OpenGL 4) and the parity fixtures stencil_states and stencil_compare (all eight compare functions); TwoSidedStencilTests.cpp covers the shared two-sided mapping. Depth-function evidence has two layers. The parity fixture depth_states (registered as <Renderer>_Parity_depth_states for EasyGL, WebGPU, SDL_GPU and OpenGL 4) runs all eight CompareFunctions as an 8×3 matrix of nearer, equal and farther fragments, so no pair of functions shares a signature. The older, narrower test is easygl_depthstencilstate_compare_function_test.cpp, five discriminating cases (Always, Never, Less, LessEqual, Greater) registered as EasyGL_DepthStencilState_CompareFunction and, from the same source, Vulkan_DepthStencilState_CompareFunction; VULKAN is not part of the parity corpus, so for it that five-case test is not a sweep of all eight values.
Example: masking a planar reflection
A planar mirror needs two depth-stencil states: one that writes a mark into the stencil buffer where the mirror is drawn, and one that draws the reflected scene only where the mark is. Both states are fully configured before either is assigned, so neither mutation-after-binding nor a separate reference change is involved. The first pass writes the reference value unconditionally (Always with Replace), so the technique does not depend on the stencil buffer having been cleared to any particular value.
// Needs a stencil plane: Depth24Stencil8 on the back buffer or on the render target.
DepthStencilState writeMirrorMask;
writeMirrorMask.setStencilEnableProperty(true);
writeMirrorMask.setStencilFunctionProperty(CompareFunction::Always);
writeMirrorMask.setStencilPassProperty(StencilOperation::Replace);
writeMirrorMask.setReferenceStencilProperty(1);
DepthStencilState maskedByMirror;
maskedByMirror.setStencilEnableProperty(true);
maskedByMirror.setStencilFunctionProperty(CompareFunction::Equal);
maskedByMirror.setReferenceStencilProperty(1); // draw only where the mirror wrote 1
graphicsDevice.setDepthStencilStateProperty(writeMirrorMask);
DrawMirrorQuad(); // pass 1: mark the mirror's footprint
graphicsDevice.setDepthStencilStateProperty(maskedByMirror);
DrawReflectedScene(); // pass 2: reflected view, masked to the mirror
graphicsDevice.setDepthStencilStateProperty(DepthStencilState::Default);
Tutorial 63: The stencil buffer builds the same idea into a complete program.
RasterizerState
Three presets: CullClockwise, CullCounterClockwise (the device default) and CullNone. Six properties: CullMode (None, CullClockwiseFace, CullCounterClockwiseFace), FillMode (Solid, WireFrame), MultiSampleAntiAlias, ScissorTestEnable, DepthBias and SlopeScaleDepthBias. The defaults are counter-clockwise culling, solid fill, multisample antialiasing on, scissor off and zero biases.
- Winding. XNA's front face is clockwise as displayed. Vulkan sets
VK_FRONT_FACE_CLOCKWISEon every pipeline, and its stock vertex shaders' Y negation only converts Direct3D-style clip space to Vulkan's, so it mirrors nothing:CullClockwiseFaceisVK_CULL_MODE_FRONT_BIT. Shared winding fixtures (frontface_winding_test.cpp, therasterizerstate_cullmode_*tests) check the convention per family. - Wireframe.
FillMode::WireFrameis honoured natively where the API has a polygon mode. EasyGL usesglPolygonModeon desktop GL,GL_NV_polygon_modeon ES andWEBGL_polygon_modein the browser; on a context with none of them it refuses a triangle draw in wireframe withNotSupportedExceptionrather than approximating it with lines. QuerySupportsCapability(GraphicsCapability::WireFrame)first; the per-family answers are on the RasterizerState guide. - Depth bias.
DepthBiasandSlopeScaleDepthBiasmap to the native constant and slope factors (on Vulkan,vkCmdSetDepthBias). A long-standing Vulkan test failure atDepthBias = -1e6turned out to be a test written against OpenGL's depth convention, not a renderer defect; the corrected test and its guard leg are described on Vulkan: the depth-bias case. - MultiSampleAntiAlias. Forwarded through
ApplyRasterizerMultiSampleState. EasyGL togglesGL_MULTISAMPLEon desktop GL 3.3 only, because OpenGL ES has no such switch; OpenGL 4 and Software also implement the hook, and every other family keeps the no-op default.
Scissor: an enable bit and a separate rectangle
ScissorTestEnable is only an enable switch; the rectangle lives in GraphicsDevice.ScissorRectangle, and each property routes only its own half to the renderer. Families differ in how they combine the two:
- EasyGL, OpenGL 4, Vulkan, WebGPU, SDL_GPU, Direct3D 9/11/12, Direct2D, Metal, PortableGL and Software keep an independent Boolean and rectangle. Direct3D 12 stores both and computes the effective rectangle per draw, clamped to the target, because it re-records its command lists; Software intersects the rectangle into its raster clip only while scissor testing is on.
SDL_RENDERERhas no rasterizer hook, so it ignores the enable bit and applies the rectangle as an always-active clip (the device resets the rectangle to the full target on every target switch, which makes that harmless by default).CANVASandFREEDIRECTnever clip;HEADLESSvalidates the rectangle against the bound target and traces it.- An empty rectangle is itself a divergence: Direct3D 11 and 12 rasterise nothing, while Vulkan and EasyGL treat a zero-sized rectangle as the full target.
deferred_scissor_capture_test.cpprecords the answer per family.
Examples: shadow-map bias and a wireframe toggle
Shadow acne, the self-shadowing pattern a shadow-mapped surface shows when its own depth is compared against itself, is fixed with a small constant bias plus a slope-scaled term that grows at grazing angles:
RasterizerState shadowMapBias;
shadowMapBias.setCullModeProperty(CullMode::CullClockwiseFace);
shadowMapBias.setDepthBiasProperty(0.0015f);
shadowMapBias.setSlopeScaleDepthBiasProperty(1.5f);
graphicsDevice.setRasterizerStateProperty(shadowMapBias);
// ... render the scene from the light's view into the shadow map ...
// A wireframe debug overlay is a separate, fully configured state object.
RasterizerState wireframe;
wireframe.setFillModeProperty(FillMode::WireFrame);
wireframe.setCullModeProperty(CullMode::None);
const bool canWireframe =
graphicsDevice.SupportsCapability(CNA::GraphicsCapability::WireFrame);
graphicsDevice.setRasterizerStateProperty(showWireframe && canWireframe
? wireframe
: RasterizerState::CullCounterClockwise);
Both states are built once (for example as members) and assigned as needed; neither is modified after its first assignment.
SamplerState
Six presets, exactly the combinations XNA ships: AnisotropicClamp, AnisotropicWrap, LinearClamp, LinearWrap, PointClamp and PointWrap. Seven properties: AddressU, AddressV, AddressW (TextureAddressMode: Wrap, Clamp, Mirror), Filter (TextureFilter, nine values: Linear, Point, Anisotropic, LinearMipPoint, PointMipLinear and four min/mag/mip combinations), MaxAnisotropy, MaxMipLevel and MipMapLevelOfDetailBias. A default-constructed sampler wraps on all three axes with Linear filtering, MaxAnisotropy 4, MaxMipLevel 0 and no bias.
GraphicsDevice.SamplerStates has sixteen slots, each initialised to LinearWrap. Assigning a state to a slot, device.getSamplerStatesProperty()[0] = state;, binds it exactly as a device-wide assignment does, so the assigned state is frozen afterwards; assigning the payload a slot already holds is a no-op. The vertex-stage collection has four slots under HiDef and none under Reach, where indexing it throws ArgumentOutOfRangeException.
Sampler controls by family
All sixteen pixel slots are re-submitted before every draw, including the MaxMipLevel, bias and AddressW hooks, whose interface defaults do nothing. At this snapshot:
| Control | Families that implement it | Notes |
|---|---|---|
MaxMipLevel, MipMapLevelOfDetailBias | EasyGL, OpenGL 4, Vulkan, WebGPU, SDL_GPU, Direct3D 9/11/12, FNA3D, Software | MaxMipLevel is XNA's most detailed usable level, a lower bound on the level of detail (GL_TEXTURE_MIN_LOD, SDL's min_lod); a negative value converts to an unsigned integer and clamps, as XNA's DWORD channel does. EasyGL's ES 2.0 and WebGL 1 profiles cannot represent either; the LOD bias needs desktop GL there. WebGPU clamps the bias to about ±16, the WGSL range. Metal throws NotSupportedException for non-default values; PortableGL ignores both because its textures have one level. |
AddressW | EasyGL (not the ES 2.0 generation), OpenGL 4, Vulkan, WebGPU, SDL_GPU, Direct3D 9/11/12, FNA3D, Software | Observable only when a volume texture is sampled; a family that does not implement the hook must not invent a W mode of its own |
MaxAnisotropy | EasyGL, OpenGL 4, Vulkan, WebGPU, SDL_GPU, Direct3D 9/11/12, FNA3D, Metal, Software | EasyGL clamps to the driver's GL_EXT_texture_filter_anisotropic maximum and writes the value on every application, because its per-slot GL sampler object is reused (REMED-GFX-174); Vulkan clamps to maxSamplerAnisotropy; Direct3D 11 clamps to 1–16 in its sampler cache; SDL_GPU clamps to 1–16 and keys its sampler cache on it; Software computes an anisotropic footprint; OpenGL 4 clamps to the maximum it queries at start-up, Metal clamps to 1–16 and applies it only under the Anisotropic filter, and FNA3D forwards the value (minimum 1). Direct3D 9 passes the value to D3DSAMP_MAXANISOTROPY without checking it against the device caps, and its AnisotropicFiltering capability is the inherited default. |
Tests: sampler_lod_addressw_contract_test.cpp (EasyGL, OpenGL 4, Direct3D 11/12), sampler_component_isolation_contract_test.cpp (Vulkan, EasyGL, Software, OpenGL 4, Direct3D 11/12), WebGpuSamplerAddressWTests.cpp, and the parity fixture sampler_filters (filters, mip filters, anisotropy and which slot a stock draw reads).
Mip-aware filtering
Every TextureFilter value names a mipmap component as well as a minification and a magnification one: Linear is linear in all three and Point is point in all three. EasyGL maps them accordingly (Linear to GL_LINEAR_MIPMAP_LINEAR, Point to GL_NEAREST_MIPMAP_NEAREST, the mixed values to the matching GL pairs), as FNA3D's GL driver does (REMED-GFX-175). That is safe on one-level textures only because EasyGL clamps each texture's sampled level range to its real level count; before that clamp existed, plain Point and Linear were mapped to non-mipmapped filters to avoid incomplete, black textures, which left a real mip chain unused under the default filter. At this snapshot a minified, mipmapped texture is mip-filtered under the default Linear filter on EasyGL, as elsewhere.
Examples: crisp pixel art and a distant floor
// Pixel art: hard texel edges. PointClamp is point in min, mag and mip.
spriteBatch.Begin(SpriteSortMode::Deferred, BlendState::AlphaBlend,
&SamplerState::PointClamp, nullptr, nullptr);
// ... draw sprites ...
spriteBatch.End();
// A minified, mipmapped floor: blocky texels, smooth transitions between levels.
SamplerState blockyButMipAware;
blockyButMipAware.setFilterProperty(TextureFilter::PointMipLinear);
blockyButMipAware.setAddressUProperty(TextureAddressMode::Wrap);
blockyButMipAware.setAddressVProperty(TextureAddressMode::Wrap);
graphicsDevice.getSamplerStatesProperty()[0] = blockyButMipAware; // binds and freezes it
Under Reach, a wrapping sampler on a non-power-of-two texture throws at draw time; tile power-of-two textures or request HiDef.
Preset names and a hidden construction defect
Each state class at some point had presets without a Name. That sounds cosmetic, but adding the names is what exposed a real defect: the device's constructor had not copied DepthStencilState::Default and RasterizerState::CullCounterClockwise into its state fields at all, and nobody had noticed because the default-constructed values happened to coincide with the presets. Once a name distinguished them, tests failed (CNA's records call these Tasks 302, 311 and 312). At this snapshot every preset is named ("BlendState.Opaque", "DepthStencilState.Default" and so on), and the device constructor assigns BlendState::Opaque, DepthStencilState::Default (when the renderer supports depth-stencil) and RasterizerState::CullCounterClockwise through the ordinary setters, as FNA's constructor does, so each is pushed to the renderer and the device's states alias the named presets. GraphicsDeviceDefaultStateTests.cpp checks the names and values. The general lesson is a testing one: a value that coincides with a default proves nothing until something can tell the two apart.
Evidence and its limits
Checked by reading the CNA source at 009d40f5; not executed. The identity, freeze, disposal and submission rules are shared code, pinned by GraphicsDeviceDefaultStateTests.cpp, BlendStateTests.cpp, DepthStencilStateTests.cpp, RasterizerStateTests.cpp, SamplerStateTests.cpp, SamplerStateCollectionTests.cpp and GraphicsProfileBlendStateTests.cpp. Per-family statements were read from each family's renderer and name the registered tests where they were found; a family's pixel result is not interchangeable with another's, and none of these tests was run for this page. CNA's comments describe the freeze and same-reference rules as measured on the Microsoft XNA runtime.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- Graphics state · Tutorial 63: The stencil buffer
- Architecture
- Graphics architecture: state and render targets
- Maintainer workflow
- Change public XNA behaviour
- Tests and validation
- Test architecture
- Reference
- Test target index
- Deep dives
- Vulkan draw-time state · Surface formats