# a laundry list of recurring HLSL nightmares

## Recommended Posts

I've had a lot of trouble working with HLSL... mainly because the documentation is holier than the holiest of swiss cheese.

Probably my sensibilities were most offended when I managed to put two and two together and realize that the integer registers had to be treated as a four component for loop initialization vector (ie, x is the stop condition, y is the initial value, z is the increment, and w if anything I forget) and the whole for loop syntax was really a crock of bullshit (which the docs of course fail to mention)

But I digress. I've accrued such a laundry list of nagging quandaries, I decided it was time to seek serious help. The equally dysfunctional XNA forums are down atm/again so rather than laying my beef on Microsoft's doorstep, I remembered this forum.

Some quick background. Basically I'm simulating the fixed function pipeline. Or rather taking some software which was written for FF and providing a mechanism for clients to override the functionality with custom shaders. So the FF states are basically tracked and written to select shader constant registers as they change.

All shaders assumed to be written in HLSL declare what states they are interested in via the :register(c0) syntax.

So generally speaking this seems to work well. However a few of the constant values occasionally become mysteriously bad. This is in no way caused by SetPixelShaderConstant calls. Pretty much these few constants must be constantly fed their values before every drawing call to make them stick.

My theory is they are getting written over by const like temporary variables declared in individual shader/function blocks. I tried mapping them to constant registers with the :register syntax, but even though the docs say:

[Storage_Class] [Type_Modifier] Type Name[Index]
[: Semantic]
[Annotations]
[= Initial_Value]
[: Packoffset]
[: Register];

It seems to be impossible to both have an initial value and use the :register syntax. So if you declare a constant of any kind like so:

float2 x = {0,0} : register(c0);

It won't compile. The only thing that compiles is:

float2 x = {0,0}; : register(c0);

But this doesn't seem to do any register mapping (at least in the cases I tried.)

Secondly I've had a ton of trouble associating textures with SetTexture and SetSamplerState. I'm not even sure it's possible...

Using the :TEXTURE0 style semantic may or may not implicate the individual texture variables. I'm not sure, the order of declaration may be all that matters.

It's seemingly impossible to assign semantics/registers to samplers if you need to initialize them with sampler_state, for the same reason above. So it may be assigning them to a texture variable via the sampler_state syntax is not good enough.

It's just not practical to ensure that every shaders required textures are congruent, 1, 2, 3, etc. For instance texture0 is the model texture. Texture1 is reserved for brightness correction in some cases, so texture2 is a dither texture. Shaders never need texture1 as of yet, because they can do the brightness adjustment based on a single constant. But tex1 gets that spot because the SetTextureStageState etc requires each stage be back to back.

So ideally what is required is a shader to be able to have one :TEXTURE0 texture, and another :TEXTURE2 texture, and no :TEXTURE1 texture... which correspond to SetTexture(0) and SetTexture(2).

I've yet to be able to achieve that with HLSL. I tried just now creating a black placeholder texture so there is always something set, ie. SetTexture(0,1,2) to no avail.

PS: As a bonus I'm curious how long a SetTexture is good for. The docs don't say. I think I've done some tests that show you can't just set it and forget it. I know SetRenderTarget invalidates SetViewport, but I'd like to know what other states are unreliable if any.

Thanks.

##### Share on other sites
MJP    19786
[quote]
However a few of the constant values occasionally become mysteriously bad. This is in no way caused by SetPixelShaderConstant calls.
[/qquote]

Like most device state, there's only 3 ways to change it:

1. Resetting the device
2. Applying a state block
3. Calling the function that sets it.

If the state is changing, it has to be one of those things. Even D3DX helpers like Effects and constant tables that set state must do so through one of those methods. If you're having trouble tracking down the point at which at state change occurs, I would recommend using PIX to capture a frame. You can then go through your frame call by call, and inspect device state before and after.

Quote:
 My theory is they are getting written over by const like temporary variables declared in individual shader/function blocks. I tried mapping them to constant registers with the :register syntax, but even though the docs say:[Storage_Class] [Type_Modifier] Type Name[Index][: Semantic][Annotations][= Initial_Value][: Packoffset][: Register];It seems to be impossible to both have an initial value and use the :register syntax. So if you declare a constant of any kind like so:float2 x = {0,0} : register(c0);It won't compile. The only thing that compiles is:float2 x = {0,0}; : register(c0);But this doesn't seem to do any register mapping (at least in the cases I tried.)

I think the syntax you need is this:
float2 x : register(c0) = {0, 0};

However giving an initial value for a uniform (which is a shader constant that's visible to the app and can have its value set) doesn't really make sense in most cases. This is because the app always specifies the value for the constant, which means the app would have to know about this initial value and use it to initialized whatever app-side data structures are used for setting the shader constants. The effects framework is capable of doing that so it can make sense in that case, but it doesn't sound like you're using effects (are you?). And even if you are using effects, mapping a constant to a register implies that your app is taking direct control of shader constants rather than having the effect do it.

Quote:
 Secondly I've had a ton of trouble associating textures with SetTexture and SetSamplerState. I'm not even sure it's possible...Using the :TEXTURE0 style semantic may or may not implicate the individual texture variables. I'm not sure, the order of declaration may be all that matters.It's seemingly impossible to assign semantics/registers to samplers if you need to initialize them with sampler_state, for the same reason above. So it may be assigning them to a texture variable via the sampler_state syntax is not good enough.It's just not practical to ensure that every shaders required textures are congruent, 1, 2, 3, etc. For instance texture0 is the model texture. Texture1 is reserved for brightness correction in some cases, so texture2 is a dither texture. Shaders never need texture1 as of yet, because they can do the brightness adjustment based on a single constant. But tex1 gets that spot because the SetTextureStageState etc requires each stage be back to back.So ideally what is required is a shader to be able to have one :TEXTURE0 texture, and another :TEXTURE2 texture, and no :TEXTURE1 texture... which correspond to SetTexture(0) and SetTexture(2).I've yet to be able to achieve that with HLSL. I tried just now creating a black placeholder texture so there is always something set, ie. SetTexture(0,1,2) to no avail.

Assigning a semantic doesn't do anything on its own. All it does is provide some metadata that the host app can retrieve. It's up to the host app to actually do anything with that data. So that's why assigning that semantic isn't doing anything.

To bind a sampler to an index you can use with SetTexture or SetSamplerState, you bind it to a sampler register (the same way you bind a uniform to a constant register). The sampler registers for ps_2_0 and ps_3_0 are s0-s15.

Also I should mention that if you're not using effects, assigning a sampler state won't do anything.

Quote:
 As a bonus I'm curious how long a SetTexture is good for. The docs don't say. I think I've done some tests that show you can't just set it and forget it. I know SetRenderTarget invalidates SetViewport, but I'd like to know what other states are unreliable if any.

Same as shader constant registers. It's good until you reset, you apply a state block, or you call SetTexture.

[Edited by - MJP on October 11, 2010 12:03:11 PM]

##### Share on other sites
DieterVW    724
The docs do say that initial values for constants are meaningless and must be set by the application.

Docs:
Initial Value
"Global variables—that are not marked static or extern—are not compiled into the shader and cannot be optimized. To initialize this type of global variable, use reflection to get their value and then set the value from the API using a constant buffer."

To make it more clear, it should probably say something like:

"Global variables—that are not marked static or extern—are not compiled into the shader, are not set automatically, and cannot be optimized. To initialize this type of global variable, use reflection to get their value and then set the value from the API using a constant buffer. This process of reflecting the initial value and setting it can be taken care of automatically by using Effects."

##### Share on other sites
There's a lot here I'll have to think about / read more carefully, but it all looks pretty positive.

Off the top of my head, I'm pretty sure I tried putting the :register syntax before the assignment and it did not compile, but I will need to check again. Anyway if that's correct the docs are wrong there, unless the the lines below are not meant to be read as coming after the top line (which is pretty damn confusing)

There's a slim chance ApplyStateBlock might be the problem, I will have to test that to rule it out. I did not realize shader constants were considered states as such since they bypass the fixed function API more or less. I hope so.

I'm a little confused about semantics... if they are strictly superficial, then they would not be useful for declaring per vertex inputs. The annotation I understood as superficial (not effecting execution) but the semantics not so.

No the D3DX Effects API is not being used / probably would not make sense in this context. Also to be clear, not using the Effects API is not incompatible with the sampler_state syntax. In other words, the values supplied to sampler_state take precedence over the current state of SetSamplerState (edited: or I'm pretty damn sure that's my experience anyway... apologies for assertive wording)

[Edited by - Mick is Stumped on October 11, 2010 7:42:29 PM]

##### Share on other sites
DieterVW    724
The Sampler state blocks for member assignment in HLSL code are Effects specific. The normal HLSL does not support that syntax. You'll have to set sampler state from the API too. Same goes with blending and rasterizing.

##### Share on other sites
MJP    19786
Quote:
 Original post by Mick is StumpedOff the top of my head, I'm pretty sure I tried putting the :register syntax before the assignment and it did not compile, but I will need to check again. Anyway if that's correct the docs are wrong there, unless the the lines below are not meant to be read as coming after the top line (which is pretty damn confusing)

I tried it very quickly using one of the samples in the SDK, and it compiled. And yeah it would appear the docs are wrong, unless we're both missing something.

Quote:
 Original post by Mick is StumpedI'm a little confused about semantics... if they are strictly superficial, then they would not be useful for declaring per vertex inputs. The annotation I understood as superficial (not effecting execution) but the semantics not so.

Sorry, I meant semantics for your uniforms. For inputs/outputs to your shaders, semantics obviously have meaning to the pipeline.

##### Share on other sites
@DieterVW, I won't swear my life on it, but I'm nearly positive at some point I could force a sampler in the shader to linear or point filtering for example by specifying the desired mode in sampler_state form (no Effects interface involved)

@MJP, there were definitely some places in the code where ApplyStateBlock could cause trouble, but I fixed everything as near as I can tell without any gain in terms of the unreliable constant states. I'm sure the fixes have hardened the code some...

But my theory at this point is basically, because we are not explicitly setting the constants before setting the shader... in other words the constants just change as the interface states change, and the shaders are basically set before each drawing call (all shaders are expected to use the same constants in the same way)

[theory continued]
The HLSL compiled code is basically taking over some constants in some shaders because it thinks the current shader does not care about them... loading it with some arbitrary constant value from the shader's code, subsequently screwing the values that had been supplied by SetShaderConstant.

DieterVW says that register mapped values cannot be initialized by the shader (as I understand him) so I'm thinking the only thing to try is to just explicitly map any true constants to constant registers and initialize them all one time when the device is created...

This would of course make setting up shaders a lot more cumbersome for users. Some other approach, like forcing the compiler to treat automatic variables like float(1,1,1) as such / not try to map them to whatever constant registers it likes (if that ever happens)

FYI: For the record, I think I've already tried disabling all optimizations.

Thanks for all of the sound advice,

PS: I'd really appreciate it if someone could ensure me / explain what conditions are required for there to be a one to one mapping between the SetTexture index and a desired texture/sampler inside a shader... AND even if the shader only declares say index 0 and index 2 with no mention of index 1.

EDITED: I'd really like to get this sorted out because I'm a little skeptical about this approach in general, even though broadly speaking it seems to work quite well. For instance however I worry that code compiled in different user environments will for example bungle some other set of constant registers. I'm pretty near convinced it all comes down to how the compiler is treating the code.

[Edited by - Mick is Stumped on October 11, 2010 7:35:21 PM]

##### Share on other sites
MJP    19786
1. So something you said in your last post reminded me of something I forgot about, which is that shaders can temporarily set the values of constant registers. The compiler does this for literals and other constants in the shader code, so that they can be expressed in the shader assembly. You can see these values if you look at the disassembly for a shader, they look like this:
    vs_2_0    def c5, 0.159154937, 0.5, 6.28318548, -3.14159274    def c6, 2.38732409, 0.5, 0.100000001, 0    def c7, 1, 0, 0, 0    def c8, -1.55009923e-006, -2.17013894e-005, 0.00260416674, 0.00026041668    def c9, -0.020833334, -0.125, 1, 0.5

These values don't persist however (and the compiler only uses constant registers not used by that program), so they shouldn't mess with any values you've set onto the registers through SetVertexShaderConstantF/SetPixelShaderConstantF.

2. If you have a global constant in your shader code that you don't want to be visible to the app (which means it won't be mapped to a constant register), you use the "static" modifier.

3. Like I said before, you map a sampler to an index by binding it to a register.
sampler2D diffuseMap : register(s2);

That sampler is bound to the texture set with SetTexture(2, ...), as well as any sampler states set with SetSamplerState(2, ...).

##### Share on other sites
Quote:
 Original post by MJP1. So something you said in your last post reminded me of something I forgot about, which is that shaders can temporarily set the values of constant registers. The compiler does this for literals and other constants in the shader code, so that they can be expressed in the shader assembly. You can see these values if you look at the disassembly for a shader, they look like this: vs_2_0 def c5, 0.159154937, 0.5, 6.28318548, -3.14159274 def c6, 2.38732409, 0.5, 0.100000001, 0 def c7, 1, 0, 0, 0 def c8, -1.55009923e-006, -2.17013894e-005, 0.00260416674, 0.00026041668 def c9, -0.020833334, -0.125, 1, 0.5These values don't persist however (and the compiler only uses constant registers not used by that program), so they shouldn't mess with any values you've set onto the registers through SetVertexShaderConstantF/SetPixelShaderConstantF.

This is basically what I've been suspecting, except the problem in "my" case is there are many different shaders, some depend on some constants, others don't, but the constants need to remain pristine, because if one shader writes over a constant (something that should never happen) the next shader up to bat probably needs that constant, but... as you can see it was clobbered with bogus data by the last shader (again, the app only updates the constants when their expected values change -- versus when the shaders change, which it does not do)

Quote:
 2. If you have a global constant in your shader code that you don't want to be visible to the app (which means it won't be mapped to a constant register), you use the "static" modifier.

My understanding of the static keyword is that it's only used to make a locale variable remain persistent. In which case it would not be a constant I'd suspect. I've not seen any docs about other uses of 'static' though in C++ there are admittedly at least a few.

We do need however some way to make the compiler not use those registers. The constants are declared in every program at the moment, but only some "entrypoints" use some constants, so the compiler probably considers the unreferenced ones candidates for "taking over".

Quote:
 3. Like I said before, you map a sampler to an index by binding it to a register.sampler2D diffuseMap : register(s2);That sampler is bound to the texture set with SetTexture(2, ...), as well as any sampler states set with SetSamplerState(2, ...).

Yes, this is my understanding up to this point. I will need to try some stuff. There are also texture registers I think, t0~n. I think the Direct3D interfaces always use SetSamplerState(1) for SetTexture(1).

I think what I was reaching for was a definitive statement that yes registers(s2) is the same as SamplerState(2) ... ie. HLSL will not take it upon itself to rewire things because there are now instructions that reference s1 for example.

##### Share on other sites
Is it possible the "shared" keyword is what I'm looking for? I'm not sure what I was thinking it meant before, but if it means, don't clobber this constant ever, then that's what I need... will try that now!!

UPDATE: shared did not help, but I'm still curious what it is useful for.

##### Share on other sites
I was able to get the samplers mapped. Also realized there doesn't seem to be any reason to declare textures, unless there is something I'm missing (there probably is)

Still pretty sure the compiler is clobbering the constants / need to find a way to make it not do that. Unfortunately the code I'm debugging is a string literal, and the D3DX api being used to compile it, I don't know if it does ASM output. I was kinda under the impression there was no longer an ASM standard / that was kind of handled by the drivers.

I noticed the visuals were pretty close whether the wrong sampler register was being used or the pixel shader didn't compile, so chances are some of the tests I did never actually compiled. There should probably be an assertion statement somewhere in there.

##### Share on other sites
MJP    19786
Quote:
 Original post by Mick is StumpedThis is basically what I've been suspecting, except the problem in "my" case is there are many different shaders, some depend on some constants, others don't, but the constants need to remain pristine, because if one shader writes over a constant (something that should never happen) the next shader up to bat probably needs that constant, but... as you can see it was clobbered with bogus data by the last shader (again, the app only updates the constants when their expected values change -- versus when the shaders change, which it does not do)

Well like I said it should only ever temporarily set values for constants you don't use, so it shouldn't be a problem. At work our Windows renderer works by explicitly mapping out constants to constant registers and only setting them when they change, and that has worked fine. Have you located a specific point in the frame when a constant value isn't what you expect? PIX should help you track that down, if you haven't done so already.

Quote:
 Original post by Mick is StumpedMy understanding of the static keyword is that it's only used to make a locale variable remain persistent. In which case it would not be a constant I'd suspect. I've not seen any docs about other uses of 'static' though in C++ there are admittedly at least a few.

On a global variable it makes it non-visible to application. This is similar to what it means to mark a variable or function at file scope with "static" in C++, where it indicates that it doesn't have external linkage. It's listed in the documentation.

Quote:
 Original post by Mick is StumpedWe do need however some way to make the compiler not use those registers. The constants are declared in every program at the moment, but only some "entrypoints" use some constants, so the compiler probably considers the unreferenced ones candidates for "taking over".

Yeah that's how it works, but it shouldn't be an issue because when it "takes over" a constant it's only temporary. When you use a shader that uses a constant mapped to that variable, the compiler shouldn't use that register for an immediate constant and it should have the value you gave it with Set*ShaderConstantF. I suppose it's possible for the compiler to screw up and define an immediate constant using a register your program uses, but I've never seen that happen. If that were the case we could easily verify it by looking at the resulting assembly.

Quote:
 Original post by Mick is StumpedYes, this is my understanding up to this point. I will need to try some stuff. There are also texture registers I think, t0~n. I think the Direct3D interfaces always use SetSamplerState(1) for SetTexture(1). I think what I was reaching for was a definitive statement that yes registers(s2) is the same as SamplerState(2) ... ie. HLSL will not take it upon itself to rewire things because there are now instructions that reference s1 for example.

There are no texture registers in SM2.0 or SM3.0, only sampler registers. There are in SM4.0, which is only for D3D10 and above.

As for "shared", it's only for effects. Basically you mark uniforms as being shared between different effects, and the compiler attempts to map them to the same constant register so that the framework can avoid redundantly setting the same constant multiple times.

##### Share on other sites
MJP    19786
Quote:
 Original post by Mick is StumpedI was able to get the samplers mapped. Also realized there doesn't seem to be any reason to declare textures, unless there is something I'm missing (there probably is)

It's only for Effects. It basically just gives an Effect a name to which you can bind your textures, and then you assign that texture to a sampler state. If you're not using Effects you can just declare your samplers and use those directly.

Quote:
 Original post by Mick is StumpedStill pretty sure the compiler is clobbering the constants / need to find a way to make it not do that. Unfortunately the code I'm debugging is a string literal, and the D3DX api being used to compile it, I don't know if it does ASM output. I was kinda under the impression there was no longer an ASM standard / that was kind of handled by the drivers.

Yup, shaders are compiled to an intermediate "assembly" format. Basically it's a very simple vector instruction/register set that's JIT compiled by the driver into the native bytecode used by the GPU. You'll never see the bytecode, but you can certainly look at the compiled assembly (in fact in SM3.0 and below you can actually write shaders in assembly, in SM4.0 and above you have to use HLSL). If you want to see the compiled assembly the two easiest ways are to use fxc.exe and compile the shader, or just look at the shader object in PIX. You can also debug a shader at either the source code or the assembly level in PIX.

There's plenty of documentation for the instruction and registers for the SM2.0 and SM3.0 shader profiles. For instance the ps_3_0 register documentation is here, and the instructions are here. I'd recommending taking a look through and reading through some of the assembly generated for your programs (or better yet stepping through them in PIX). It can give you a lot of insight into things that might seem totally bizarre when you're just writing HLSL.

Quote:
 Original post by Mick is StumpedI noticed the visuals were pretty close whether the wrong sampler register was being used or the pixel shader didn't compile, so chances are some of the tests I did never actually compiled. There should probably be an assertion statement somewhere in there.

If you haven't done so already, try turning on the debug runtimes. It's in the DirectX Control Panel. It will put information about errors in the debug spew, and you can set it to break on errors if you can't figure out where you need to put the assert.

##### Share on other sites
This is not an easy topic to find info on via websearch etc. I can tell you this conversation is about doing the same thing:

http://www.bokebb.com/dev/english/1999/posts/199917373.shtml

Unfortunately it doesn't appear to have resolved itself / is not dated / doesn't seem to have a way to track the poster down. But it may be clearer than my explanation, and is anyway good supplemental info.

To me the Effects system seems to have gone overboard, and I think what I'm trying to do is an equally valid way to use the shader framework, but it's become completely dominated by the effects mentality.

I did notice the alternative D3DXAssembleShader. Worst case scenario I reckon is the HLSL can be converted to ASM, then the ASM can be filtered so all of the constant register references map to safe registers (and passed along to D3DXAssembleShader instead)

At least up til the ASM is deprecated...

I think I have a good handle on the technology, but I've not (and really don't have time to) committed to really investing in shader development in terms of IDEs, debuggers/visualizers etc. I will at some point when the mood is right I'm sure, but I'd prefer to get everything straight at the API level first.

There doesn't anyway seem to be a way to protect registers from the HLSL compiler. And I think I understand what you're saying, that Direct3D tracks the constant values independent of the hardware, but that's just not what seems to be happening.

One quote in the link from above seems to hit the nail on the head:

Quote:
 So now I'm wondering how DirectX deals with constant registers. Does DirectX [keep] the shader constants that were defined in the shader (i.e def c0, ...), when changing shaders? Or does it reupload the constants it needs when a shader is set? Perhaps this is more of a Display Driver detail, and not specified by DirectX how it is done.

I just don't see anything in the docs that say this isn't a driver implementation call. This guy seems to be running into the same wall I am. I just can't think of anything else which could possibly be happening, and it seems logical.

What I need at this point is an API for generating the ASM from an HLSL string. If I can get a recommendation on that I'd really appreciate it. The compiler D3DX uses I think can do this with some command line switches, but it doesn't seem to be exposed at first glance via the D3DX API.

Well I need that to proceed at this point I think, but I suppose in the meantime the string can be logged to a file and I can run that thru a compiler to see the ASM for myself...

Will do that tomorrow if not later tonight.

##### Share on other sites
remigius    1172
I have to admit I skimmed over most of the topic, but I'm going from this:

Quote:
 Some quick background. Basically I'm simulating the fixed function pipeline. Or rather taking some software which was written for FF and providing a mechanism for clients to override the functionality with custom shaders. So the FF states are basically tracked and written to select shader constant registers as they change.

In my limited experience getting a shader to work with FFP methods (or register addressing) is a pain, so I was thinking if it wouldn't be a better option to write a 'thicker' wrapper around your shader(s) instead of making them act like the FFP. This would allow you to for example intercept D3DDevice::SetTexture calls and convert them to ID3DXBaseEffect::SetTexture calls, which should be a lot more reliable and straightforward.

If you can't convert the original code to use your wrapper, it might be an option to hijack the D3D API (here's how) and bend it to your will. It's far from a pretty solution, but it might be viable if the FFP/Effects combination is really giving you the run-around.

##### Share on other sites
@remigus,

Basically what I'm involved with is taking some old software which we don't have source code for, and making it interoperable with modern computing environments or whatever.

So there is a launcher that launches the original executable in a frozen state, overwrites some of the process's memory and lets it go. Meanwhile all the libraries it links to (including most of the core DirectX 7) libraries have been replaced with custom libraries, along with many crt hooks.

So like for a Direct3D 9 "port" the DirectX 7 calls are converted to D3D9. Some elements of DirectX7 like colorkey are not part of D3D9 so shaders are used to simulate that, but that's pretty simple.

Replacing the DX7 functionality with vertex shaders is more complex. And not practical considering the libraries that do the "translation" are not supposed to be tailored to the client app. But we want our users to be able to use custom shaders (which are tailored to the client app) so we have a system to override the shaders in use (no vertex shader by default) which can be accessed by installing callbacks basically around the interfaces which are relevant to the client app.

By the time we work up a D3D10 port, it probably will make more sense to reinterpret the states in a more holistic way/use the effects interface. But for D3D9 there is an established framework, and its easier for the custom shaders to just piggy back on that. At the least, it's less work to maintain at the moment than having yet another target.

Unless someone can think of a better idea, the approach that will most likely be taken is to preprocess the shader ASM and remap any registers mentioned in def c0 type instructions to safe constant registers.

I feel let down that this functionality (probably) isn't builtin to HLSL. I mean if the file being compiled has a bunch of constant registers explicitly declared, it would be simple to have an option that says don't use any of those registers even if they are not referenced by the main routine being compiled.

EDITED: On the up side, using the assemble routine will probably make it easier to integrate non HLSL shader files... if for any reason that is preferential.

[Edited by - Mick is Stumped on October 12, 2010 8:11:46 PM]

##### Share on other sites

I started setting up an fxc subprocess (all but tested) for piping in the asm for shaders... because that seemed like the only route Microsoft had left.

Then just now I stumbled across D3DXDisassembleShader / D3DDisassemble10Effect.

Gotta admit it seems a little circular to compile/assemble the shader, only to disassemble / reassemble it... but it takes out the fxc dependency and should presumably get the job done in terms of getting at the asm.

Might be that way because the display drivers are allowed to optimize the object code or whatever. At the least it would be nice if D3DXDisassembleShader was mentioned in the Compile/Assemble doc pages (for those of us without the presence of mind to read the D3DX function listings)