CNA-BUG-006: SpriteEffect caches a MatrixTransform parameter that never exists, so OnApply() never sets the sprite projection
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. Nothing on this page was executed unless the Evidence section says so.
SpriteEffect looks up a MatrixTransform parameter its base Effect never creates, so Parameters["MatrixTransform"] is null and OnApply() returns before computing the orthographic projection and half-pixel offset.
- Identifier
CNA-BUG-006- Category
- Bug
- Subsystem
- Graphics & renderers
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Microsoft::Xna::Framework::Graphics::SpriteEffect: its MatrixTransform parameter and OnApply()
Expected behaviour
CNA's own SpriteEffect header documents the class as applying "a single world-view-projection matrix transform to a textured quad" and its OnApply() as applying "the sprite transform matrix parameter to the graphics device". OnApply() in SpriteEffect.cpp computes exactly that (Matrix::CreateOrthographicOffCenter over the viewport, multiplied after Matrix::CreateTranslation(-0.5f, -0.5f, 0.0f)). Note on XNA: XNA 4.0 has no public SpriteEffect type; its SpriteBatch owns a private Effect built from an embedded SpriteEffectCode blob and sets that effect's MatrixTransform parameter itself in PrepRenderState. A public SpriteEffect with a MatrixTransform parameter that Apply() fills is therefore a CNA class, not an XNA 4.0 contract.
Actual behaviour at TARGET
Both constructors call SpriteEffect::CacheEffectParameters(), which stores getParametersProperty()["MatrixTransform"]. Nothing ever adds a parameter of that name: Effect::Effect(GraphicsDevice&) adds only a technique named Default, and SpriteEffect adds no parameter. EffectParameterCollection::operator[](const std::string&) returns nullptr for an absent name, so matrixParam_ is always null and OnApply() returns at its first statement. Code that reads effect.getParametersProperty()["MatrixTransform"] on a SpriteEffect receives a null pointer, and Apply() never computes the projection its own documentation describes.
Source locations
modules/graphics/src/Xna/SpriteEffect.cpp— SpriteEffect::CacheEffectParameters (lookup of MatrixTransform) and SpriteEffect::OnApply (early return on a null parameter)modules/graphics/include/Microsoft/Xna/Framework/Graphics/SpriteEffect.hpp— class SpriteEffect: matrixParam_ and the class commentmodules/graphics/src/Xna/Effect.cpp— Effect::Effect(GraphicsDevice&) adds only the Default techniquemodules/graphics/src/Xna/EffectParameterCollection.cpp— EffectParameterCollection::operator[](const std::string&) returns nullptr for an unknown namemodules/graphics/tests/Microsoft/Xna/Framework/Graphics/SpriteEffectTests.cpp— only Clone() and Apply()-does-not-throw are testedmodules/c-api/src/CnaCApiEffects.cpp— cna_sprite_effect_create, the only in-tree creator outside tests
Evidence
Checked by reading at 009d40f5; not executed. No renderer consumes the SpriteEffect class: the renderers that run XNA's sprite shader (for example D3D9SpriteBatch.cpp, D3D12SpriteBatch.cpp and SoftwareSpriteBatch.cpp) load their own embedded compiled SpriteEffect.fxb and look up MatrixTransform in that blob, and SpriteBatch.cpp never instantiates the class. Its only in-tree creator outside tests is the C API's cna_sprite_effect_create. The defect is therefore latent for ordinary SpriteBatch drawing and visible to code that instantiates SpriteEffect directly.
Independently observed as a separate finding (merged): CacheEffectParameters looks the name up on an effect that has no parameters, matrixParam_ stays null and OnApply returns before building XNA's half-pixel orthographic projection; renderers supply sprite projection themselves, so ordinary drawing is unaffected.
Independent re-verification: Checked by reading at 009d40f5; not executed. No renderer consumes the SpriteEffect class: the renderers that run XNA's sprite shader (for example D3D9SpriteBatch.cpp, D3D12SpriteBatch.cpp and SoftwareSpriteBatch.cpp) load their own embedded compiled SpriteEffect.fxb and look up MatrixTransform in that blob, and SpriteBatch.cpp never instantiates the class. Its only in-tree creator outside tests is the C API's cna_sprite_effect_create. The defect is therefore latent for ordinary SpriteBatch drawing and visible only to code that instantiates SpriteEffect directly; because XNA 4.0 has no public SpriteEffect, that code is CNA-specific (C++ or the C API), not ported XNA code. XNA's own SpriteBatch keeps the MatrixTransform parameter on a private Effect and sets it itself.
Focused reproduction
// Illustrative; not compiled or run for this entry.
using namespace Microsoft::Xna::Framework::Graphics;
GraphicsDevice device;
SpriteEffect effect(device);
EffectParameter* transform = effect.getParametersProperty()["MatrixTransform"];
// XNA 4.0: a Matrix parameter that Apply() fills with halfPixelOffset * projection.
// CNA at 009d40f5: transform == nullptr, and effect.Apply() computes nothing.
effect.Apply();
Current tests
SpriteEffectTests.cpp has CloneReturnsIndependentSpriteEffect and CloneAndOriginalApplyIndependently (Apply() does not throw). Neither asserts that the parameter exists or that Apply() writes a value.
Regression test
A SpriteEffectTests.cpp case asserting that getParametersProperty()["MatrixTransform"] is non-null and, after Apply() on a device with a known viewport, holds CreateTranslation(-0.5, -0.5, 0) * CreateOrthographicOffCenter(0, width, height, 0, 0, 1).
Blast radius
Code that constructs SpriteEffect directly (C++ or cna_sprite_effect_create) and reads, sets or relies on MatrixTransform, for example XNA code ported unchanged. Ordinary SpriteBatch drawing is unaffected because each renderer supplies the sprite transform itself; the C API's cna_effect_is_exact_stock_sprite_effect identity query is unaffected.
Workaround
Do not rely on SpriteEffect's MatrixTransform; ordinary SpriteBatch drawing needs no explicit SpriteEffect.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- Effects: SpriteEffect
- Maintainer workflow
- Change public XNA behaviour
- Known issues
- Bug index