Using Arrays with Unions or Structures Not Working

CDev-8220 365 Reputation points
2025-03-08T03:02:37.23+00:00

I'm trying to use arrays, with CheckFeatureSupport, rather than check each feature with separate code.

I am using different types of elements, so I have to use either union or struct.

The problems I am having, is the array of the struct does not point to the element, and the array of the union succeeds, only if I have less than six elements.

Difficult to explain.

Here is the code:

	union mixedArray {
		D3D11_FEATURE_DATA_THREADING ma_threading{};
		/* a union can have at most, one field initializer */
		D3D11_FEATURE_DATA_DOUBLES ma_doubles;
		D3D11_FEATURE_DATA_FORMAT_SUPPORT ma_format_support;
		D3D11_FEATURE_DATA_FORMAT_SUPPORT2 ma_format_support2;
		D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS ma_d3d10x_hardware_options;
		/*D3D11_FEATURE_DATA_D3D11_OPTIONS ma_d3d11_options;
		D3D11_FEATURE_DATA_ARCHITECTURE_INFO ma_architecture_info;
		D3D11_FEATURE_DATA_D3D9_OPTIONS ma_d3d9_options;
		D3D11_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT ma_shader_min_precision_support;
		D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT ma_d3d9_shadow_support;
		D3D11_FEATURE_DATA_D3D11_OPTIONS1 ma_d3d11_options1;
		D3D11_FEATURE_DATA_D3D9_SIMPLE_INSTANCING_SUPPORT ma_d3d9_simple_instancing_support;
		D3D11_FEATURE_DATA_MARKER_SUPPORT ma_marker_support;
		D3D11_FEATURE_DATA_D3D9_OPTIONS1 ma_d3d9_options1;
		D3D11_FEATURE_DATA_D3D11_OPTIONS2 ma_d3d11_options2;
		D3D11_FEATURE_DATA_D3D11_OPTIONS3 ma_d3d11_options3;
		D3D11_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT ma_gpu_virtual_address_support;
		D3D11_FEATURE_DATA_D3D11_OPTIONS4 ma_d3d11_options4;
		D3D11_FEATURE_DATA_SHADER_CACHE ma_shader_cache;
		D3D11_FEATURE_DATA_D3D11_OPTIONS5 ma_d3d11_options5;
		D3D11_FEATURE_DATA_DISPLAYABLE ma_displayable;*/
	};
	mixedArray ma[21]{};

// in a for loop
hr = m_D3D11Device->CheckFeatureSupport(
	m_D3D11Features[0], 
	&ma[0],
	sizeof(ma[0])
);
if (SUCCEEDED(hr)) {
	text = "\nSUCCESS!\n"; writingString(text);
}
else {
	text = "\nFAILURE!\n"; writingString(text);
}

When I add the other items, the check fails.

Changing it to struct, does not allow me to use ma[0].

I have to manually do ma[0].ma_threading, which isn't useful to me.

Any suggestions?

Windows development Windows API - Win32
{count} votes

1 answer

Sort by: Most helpful
  1. Darran Rowe 1,986 Reputation points
    2025-03-09T02:51:46.8666667+00:00

    One of the most obvious issues is that this is going to give the wrong size for the data type size for CheckFeatureSupport.

    A union is going to be the smallest size that can fit all of the members. This means that sizeof(mixedArray) (and by extension sizeof(ma[n])) would be 8 bytes in size. This is due to D3D11_FEATURE_DATA_FORMAT_SUPPORT, D3D11_FEATURE_DATA_FORMAT_SUPPORT2 and D3D11_FEATURE_DATA_THREADING having two 4 byte members. But this would not be usable for D3D11_FEATURE_DATA_DOUBLES, since this is a single 4 byte member. Once D3D11_FEATURE_DATA_D3D11_OPTIONS is added, the union will increase to 56 bytes, which is much larger than D3D11 is expecting.

    One very important thing to note is that when developing for Direct3D, the debug layer is the most useful debugging tool that is available.

    int wmain()
    {
    	using Microsoft::WRL::ComPtr;
    
    	ComPtr<ID3D11Device> d3d11_device;
    	ComPtr<ID3D11DeviceContext> d3d11_devctx;
    
    	UINT d3d11_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
    
    	D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_1;
    	D3D_FEATURE_LEVEL out_fl{};
    #ifdef _DEBUG
    	d3d11_flags |= D3D11_CREATE_DEVICE_DEBUG;
    #endif
    
    	if (FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, d3d11_flags, &fl, 1, D3D11_SDK_VERSION, d3d11_device.ReleaseAndGetAddressOf(), &out_fl, d3d11_devctx.ReleaseAndGetAddressOf())))
    	{
    		return -1;
    	}
    
    	D3D11_FEATURE_DATA_D3D11_OPTIONS opts{};
    	//The size parameter is deliberately too large.
    	d3d11_device->CheckFeatureSupport(D3D11_FEATURE::D3D11_FEATURE_D3D11_OPTIONS, &opts, sizeof(D3D11_FEATURE_DATA_D3D11_OPTIONS)+4);
    
    	return 0;
    }
    

    This deliberately incorrect code will have:

    D3D11 ERROR: ID3D11Device::CheckFeatureSupport: Feature D3D11_FEATURE_D3D11_OPTIONS requires FeatureSupportDataSize to be the sizeof( D3D11_FEATURE_DATA_D3D11_OPTIONS ), which equals 56; but 60 was passed instead. [ STATE_GETTING ERROR #2097314: DEVICE_CHECKFEATURESUPPORT_MISMATCHED_DATA_SIZE]
    D3D11 ERROR: ID3D11Device::CheckFeatureSupport: Returning E_INVALIDARG, meaning the parameters were invalid. [ STATE_GETTING ERROR #2097315: DEVICE_CHECKFEATURESUPPORT_INVALIDARG_RETURN]
    

    in the debugger output. This is documented in the CheckFeatureSupport documentation:

    "Returns S_OK if successful; otherwise, returns E_INVALIDARG if an unsupported data type is passed to the pFeatureSupportData parameter or a size mismatch is detected for the FeatureSupportDataSize parameter."

    So because of how unions work, this will never work as intended.

    To give an easier to test example.

    union u
    {
    	signed char a;
    	short b;
    	int c;
    	long long d;
    };
    
    int wmain()
    {
    	auto fmt_string = std::format(L"{}\n", sizeof(u));
    	fputws(fmt_string.c_str(), stdout);
    
    	return 0;
    }
    

    Using sizeof(u) or any variation that will get the size of the union is used, then this union will always return 8. This is because the size of long long is 8 bytes. This is true even if the 1 byte member is the intended target.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.