Jump to content
  • Advertisement
Sign in to follow this  
sebi707

Replacement for ID3DXConstantTable?

This topic is 2128 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to get rid of all D3DX9 dependencies since it's deprecated with the Windows 8 SDK. The last remaining part is the D3DXCompileShaderFromFile function which is replaced by D3DCompile. The problem is that D3DXCompileShaderFromFile creates a ID3DXConstantTable which I use to get the correct register for each shader variable. I tried the D3DReflect function but it doesn't seem to work for vs_3_0 and ps_3_0 shaders. What is the correct way to get the registers without D3DX9?

Share this post


Link to post
Share on other sites
Advertisement

As far as I know there's no alternative to ID3DXConstantTable. You could make an offline tool that uses D3DX to extract the data that you need and then save it out to your own data format. That way at least your actual app wouldn't have the dependency on D3DX.

Share this post


Link to post
Share on other sites

We had to support run-time compilation/reflection here so I implemented a solution using D3DDisassemble and then manually parsed the dissassembly to generate a look-up table for constants. It wasn't exactly easy (or pretty) but it seems to be working out fine.

Edited by doesnotcompute

Share this post


Link to post
Share on other sites

You could always declare the register bindings in the hlsl source code. That can save on constant setting calls too as you can make constants stay in the same slot even when you switch shader.

 

float4x4 world : register(c0);

float4 colour : register(c4);

Share this post


Link to post
Share on other sites

We had to support run-time compilation/reflection here so I implemented a solution using D3DDisassemble and then manually parsed the dissassembly to generate a look-up table for constants. It wasn't exactly easy (or pretty) but it seems to be working out fine.

Does this give you the name of the variables in the source? Like Adam_42 suggested I could choose the register explicitly in the shader but I want to access the variables by name like shader->SetVector("lightPosition", vec). So explicitly choosing a register would work for general things like world/view/projection matrices but not shader specific variables.

Edited by sebi707

Share this post


Link to post
Share on other sites

Yes the names are preserved. We use it to generate a look-up table so any constant can be accessed by its original name.

 

Here's a sample of some disassembly:

//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.16384
//
// Parameters:
//
//   struct
//   {
//       float4 diffuse;
//       float4 specular;
//
//   } MaterialColor;
//   
//   row_major float4x4 WorldViewProj;
//
//
// Registers:
//
//   Name          Reg   Size
//   ------------- ----- ----
//   WorldViewProj c0       4
//   MaterialColor c4       1
//

    vs_2_0
    dcl_position v0  // vertex<0,1,2>

#line 521 "_vs_dump_1000_00000000_00000000_00040009.1955"
    mul r0, v0.y, c1
    mad r0, v0.x, c0, r0
    mad r0, v0.z, c2, r0
    add oPos, r0, c3  // ::main<0,1,2,3>

#line 516
    mov oD0, c4  // ::main<4,5,6,7>

// approximately 5 instruction slots used

Don't be surprised if the compiler does things like allow constants to overlap if it determines part of an array or struct is unreferenced. If you have a lot of different shaders it make take some trial and error to work through some of these cases. 

 

In fact I think MaterialColor.specular is unreferenced in this shader, so you'll see that it is reporting the size of the struct as 1 register instead of 2. 

Edited by doesnotcompute

Share this post


Link to post
Share on other sites

In the last few hours I tried to write a parser that reads a compiled shader (either compiled by D3DCompile or offline by fxc) and generates a constant table. I found some very usefull source code from wine (wine-1.6\dlls\d3dx9_36\shader.c) that I used as a reference. It turns out reading the constant data straight from the compiled code isn't that difficult.

 

Here is the code that I came up with:

#include <vector>
#include <string>
#include <cstdint>

enum EREGISTER_SET
{
  RS_BOOL,
  RS_INT4,
  RS_FLOAT4,
  RS_SAMPLER
};

struct ConstantDesc
{
  std::string Name;
  EREGISTER_SET RegisterSet;
  int RegisterIndex;
  int RegisterCount;
  int Rows;
  int Columns;
  int Elements;
  int StructMembers;
  size_t Bytes;
};

class ConstantTable
{
public:
  bool Create(const void* data);

  size_t GetConstantCount() const { return m_constants.size(); }
  const std::string& GetCreator() const { return m_creator; } 

  const ConstantDesc* GetConstantByIndex(size_t i) const { return &m_constants[i]; }
  const ConstantDesc* GetConstantByName(const std::string& name) const;

private:
  std::vector<ConstantDesc> m_constants;
  std::string m_creator;
};

// Structs
struct CTHeader
{
  uint32_t Size;
  uint32_t Creator;
  uint32_t Version;
  uint32_t Constants;
  uint32_t ConstantInfo;
  uint32_t Flags;
  uint32_t Target;
};

struct CTInfo
{
  uint32_t Name;
  uint16_t RegisterSet;
  uint16_t RegisterIndex;
  uint16_t RegisterCount;
  uint16_t Reserved;
  uint32_t TypeInfo;
  uint32_t DefaultValue;
};

struct CTType
{
  uint16_t Class;
  uint16_t Type;
  uint16_t Rows;
  uint16_t Columns;
  uint16_t Elements;
  uint16_t StructMembers;
  uint32_t StructMemberInfo;
};

// Shader instruction opcodes
const uint32_t SIO_COMMENT = 0x0000FFFE;
const uint32_t SIO_END = 0x0000FFFF;
const uint32_t SI_OPCODE_MASK = 0x0000FFFF;
const uint32_t SI_COMMENTSIZE_MASK = 0x7FFF0000;
const uint32_t CTAB_CONSTANT = 0x42415443;

// Member functions
bool ConstantTable::Create(const void* data)
{
  const uint32_t* ptr = static_cast<const uint32_t*>(data);
  while(*++ptr != SIO_END)
  {
    if((*ptr & SI_OPCODE_MASK) == SIO_COMMENT)
    {
      // Check for CTAB comment
      uint32_t comment_size = (*ptr & SI_COMMENTSIZE_MASK) >> 16;
      if(*(ptr+1) != CTAB_CONSTANT)
      {
        ptr += comment_size;
        continue;
      }

      // Read header
      const char* ctab = reinterpret_cast<const char*>(ptr+2);
      size_t ctab_size = (comment_size-1)*4;

      const CTHeader* header = reinterpret_cast<const CTHeader*>(ctab);
      if(ctab_size < sizeof(*header) || header->Size != sizeof(*header))
        return false;
      m_creator = ctab + header->Creator;

      // Read constants
      m_constants.reserve(header->Constants);
      const CTInfo* info = reinterpret_cast<const CTInfo*>(ctab + header->ConstantInfo);
      for(uint32_t i = 0; i < header->Constants; ++i)
      {
        const CTType* type = reinterpret_cast<const CTType*>(ctab + info[i].TypeInfo);

        // Fill struct
        ConstantDesc desc;
        desc.Name = ctab + info[i].Name;
        desc.RegisterSet = static_cast<EREGISTER_SET>(info[i].RegisterSet);
        desc.RegisterIndex = info[i].RegisterIndex;
        desc.RegisterCount = info[i].RegisterCount;
        desc.Rows = type->Rows;
        desc.Columns = type->Columns;
        desc.Elements = type->Elements;
        desc.StructMembers = type->StructMembers;
        desc.Bytes = 4 * desc.Elements * desc.Rows * desc.Columns;
        m_constants.push_back(desc);
      }
      return true;
    }
  }
  return false;
}

const ConstantDesc* ConstantTable::GetConstantByName(const std::string& name) const
{
  std::vector<ConstantDesc>::const_iterator it;
  for(it = m_constants.begin(); it != m_constants.end(); ++it)
  {
    if(it->Name == name)
      return &(*it);
  }
  return NULL;
}

Hopefully someone might find this code useful. I don't read the default values for the variables because I don't need them. Otherwise the struct gives you most of the information like the D3DXCONSTANT_DESC struct. If you need the missing values it should be easy to implement them.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!