Passing unknown block of data with it's size?

Started by
29 comments, last by cozzie 7 years, 4 months ago

NSFW:

struct s1

{

// members

};

struct s2

{

// members

};

void do_something(void *p,unsigned size)

{

// do something with the data pointed to by p

}

void main

{

st1 joe;

st2 fred;

do_something( (void*)(&joe), sizeof(st1) );

do_something( (void*)(&fred), sizeof(st2) );

}
this code is not "stupid programmer proof" but is all that's technically required to do the job.
be sure to check the return type of sizeof() i'm guessing its unsigned.
note that a template is the preferred method these days.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Advertisement

thanks

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

be sure to check the return type of sizeof() i'm guessing its unsigned.

Its size_t to be precise.

this code is not "stupid programmer proof" but is all that's technically required to do the job.

I'm going to shit bricks if even the best programmer won't at least once in a while get that code wrong, I can just see the copy-paste fuckups resulting in black-screens before my eyes :D

Srysl, just use templates, if not for the safety factor, then to save the extra typing (putting (void*)& and sizeof(Struct) would totally annoy me in the long run) :)

No worries, templates it is :)
I still think I have to pass the sizeof struct to know what struct I'm passing to the template function (I think)

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

I still think I have to pass the sizeof struct to know what struct I'm passing to the template function (I think)

You shouldn't have to. The template resolves to the type of the struct you passed, you when you have template<typename T>, you can then just use sizeof(T). If you go over the article I linked, you should get a good feel for it, its really easy once you get a hang of it.

EDIT: I pretty much posted the correct implementation of the function in my first post already, you can take that as a reference :)

NSFW:

struct s1
{
// members
};

struct s2
{
// members
};


void do_something(void *p,unsigned size)
{
// do something with the data pointed to by p
}

void main
{
st1 joe;
st2 fred;
do_something( (void*)(&joe), sizeof(st1) );
do_something( (void*)(&fred), sizeof(st2) );
}

this code is not "stupid programmer proof" but is all that's technically required to do the job.

be sure to check the return type of sizeof() i'm guessing its unsigned.

note that a template is the preferred method these days.

EWWW my eyes, my eyes! The code, it burns! :(

~ disregard this post - I don't know what I'm doing ~

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

@SeanMiddleTech: do you have an example how this would work? - how do I convert my struct to a void * (in the called function), or do I need another type?)


It's Middle*ditch*. Yes, my name is just as stupid-sounding as it looks. Thanks, parents! :P

In any case, I'm not sure what you're asking about specifically. You can convert any struct into a void*.

Your use case of constant buffers still isn't quite at the root. If you just want to memcpy a struct into a mapped buffer, you don't need to know which struct is passed in. Just take the void* address of the struct and its size, since that's all memcpy itself requires. The template approach others advocated helps to remove programmer error, e.g.:

template <typename T>
auto simple_update_buffer(ConstantBuffer* buffer, T const& value) -> enable_if_t<is_object_v<T>>>
{
  void* address = buffer->map();
  memcpy(address, &value, sizeof(value));
  buffer->unmap();
}
The advantage being that you can't accidentally pass in the wrong size. The enable_if bit is an extra simple-ish check to make sure you don't accidentally pass in a pointer (because otherwise the function would bind to a reference-to-pointer).

Though in this case, I might argue that if you have different kinds of constant buffers that you should use the type system and make actually different constant buffer types, to ensure that you create your buffers with the right size and only update them with the proper values, e.g.

// a constant buffer that holds a copy of struct T
template <typename T>
class ConstantBuffer
{
  Buffer* _buffer = nullptr;

public:
  void allocate()
  {
    // guarantee that the buffer is allocated for the size of T
    _buffer = device.CreateConstantBuffer(sizeof(T));
  }

  void update(T const& value)
  {
    // guarantee that the buffer can only be updated with a copy of T
    void* mapped = _buffer->map();
    memcpy(mapper, &value, sizeof(value));
    _buffer->unmap();
  }
};

...

ConstantBuffer<PerFrameData> perFrameCB;
ConstantBuffer<PerModelData> modelCB;

PerFrameData data;

perFrameCB.update(data); // OK
modelCB.update(data); // COMPILE ERROR - saved you from a stupid mistake

The above of course is a simplistic example. You can make the wrapper support arrays of the given type with the proper size calculations, partial updates, etc. if required (e.g. for updating individual instances of a streaming per-instance buffer), to more intelligently use mapped addresses, etc.

The point isn't to use templates to do fancy meta-programming tricks (which are, 90% of the time, a stupid idea - they just make your code harder to read, harder to debug, and slower to compile) but to augment the types in your code so that the compiler enforces rules like "only the per-frame structure can be copied into the per-frame constant buffer."

Use the type system to make your life easier and less bug-prone.

(And as an aside, I'm not saying that this is the best way to do a constant buffer. It's just one of many ways to do things.)

Sean Middleditch – Game Systems Engineer – Join my team!

Thanks to all the input, I've managed to get it up and running using templates (yiha, my first template :cool:).
There's one thing I'd like to improve:

- some way of making sure that no 'rubbish' is being passed to the update function
-- for example like SeanMiddleDitch mentioned above
-- I also thought of storing the original sizeof(T)'s when creating the buffers, and then compare them when Update is called. That wouldn't assure the correct/ same struct/ set of data, but it will be a preventing of the user passing a wrong CBuffer struct to update one.
What would be my (other) options do add a check like that with the existing approach/code?
Note: I know the way of returning errors (true/false) and using the handle/ID's can be arguable/ done otherwise, but let's park that outside the primary goal (learning and applying templates :))

// DEFINITION

#ifndef CD3DCBUFFER_MGR_H
#define CD3DCBUFFER_MGR_H

#include <d3d11.h>
#include <vector>
#include <atlbase.h>


/**************************************************************************************/
/***							CD3D CBUFFER MGR CLASS								***/
/*** Manages creation and usage (lifetime) of Constant Buffers, D3D11				***/
/**************************************************************************************/

class CD3dCBufferMgr 
{
public:
	CD3dCBufferMgr(ID3D11Device *pDevice);
	~CD3dCBufferMgr();

	CD3dCBufferMgr(const CD3dCBufferMgr& other) = delete;				// copy constructor: not allowed
	CD3dCBufferMgr(CD3dCBufferMgr&& other) = delete;					// move constructor: not allowed
	CD3dCBufferMgr& operator=(const CD3dCBufferMgr& other) = delete;	// copy assignment: not allowed
	CD3dCBufferMgr& operator=(CD3dCBufferMgr&& other) = delete;			// move assignment: not allowed

	ID3D11Buffer* GetCBufferPtr(const unsigned int pHandle)		const;
	
	template<typename T>bool Add(const T *pBufferData, unsigned int *pIdCreated)
	{
		unsigned int newId = 0;
	
		// find empty spot
		for(size_t i=0;newId == 0 && i<mCBuffers.size();++i)
		{
			if(mCBuffers[i] == nullptr)	newId = i;
		}

		if(newId == 0)
		{
			newId = mCBuffers.size();
			mCBuffers.emplace_back();
		}

		// Create the constant buffer
		D3D11_BUFFER_DESC cBufferDesc;
		cBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
		cBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		cBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		cBufferDesc.MiscFlags = 0;
		cBufferDesc.StructureByteStride = 0;
		cBufferDesc.ByteWidth = sizeof(T);

		// pointer to initial data
		D3D11_SUBRESOURCE_DATA initData;
		initData.SysMemPitch = 0;
		initData.SysMemSlicePitch = 0;
		initData.pSysMem = pBufferData;

		if(FAILED(mDevice->CreateBuffer(&cBufferDesc, &initData, &mCBuffers[newId]))) 
		{
			// writte debug logging here, not in this small test appl
			return false;
		}

		*pIdCreated = newId;
		return true;
	}

	template<typename T>bool Update(ID3D11DeviceContext *pDeviceContext, unsigned int pHandle, const T *pBufferData)
	{
		if(pHandle >= mCBuffers.size()) return false;

		D3D11_MAPPED_SUBRESOURCE mappedRes;
		if(FAILED(pDeviceContext->Map(mCBuffers[pHandle], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedRes)))
		{
			// writte debug logging here, not in this small test appl
			return false;
		}

		T *dataPtr = (T*)mappedRes.pData;
		*dataPtr = *pBufferData;
		pDeviceContext->Unmap(mCBuffers[pHandle], 0);

		return true;
	}

	bool Release(const unsigned int pHandle);
		
private:
	std::vector<CComPtr<ID3D11Buffer>>	mCBuffers;
	
	CComPtr<ID3D11Device>			mDevice;
};

// DECLARATION

#include "cd3dcbuffermgr.h"


/**************************************************************************************/
/***								CONSTRUCTOR										***/
/*** ==> usage: when creating a CD3dCBufferMgr object 								***/
/*** ==> sets all variables in the class object to initial/ passed values			***/
/**************************************************************************************/

CD3dCBufferMgr::CD3dCBufferMgr(ID3D11Device *pDevice) : mDevice(pDevice)
{	
	// nothing yet
}

/**************************************************************************************/
/***								DESTRUCTOR										***/
/*** ==> usage: when CD3dCBufferMgr object is not needed anymore					***/
/*** ==> releases stuff, COM objects, memory etc. 									***/
/**************************************************************************************/

CD3dCBufferMgr::~CD3dCBufferMgr()
{
	// nothing yet
}

/**************************************************************************************/
/***									RELEASE										***/
/*** ==> usage: to free up a CB and make room for another one						***/
/*** ==> sets the requested CB to nullptr for reuse									***/
/**************************************************************************************/

bool CD3dCBufferMgr::Release(const unsigned int pHandle)
{
	if(pHandle >= mCBuffers.size()) return false;

	mCBuffers[pHandle] = nullptr;

	return true;
}




/**************************************************************************************/
/***								GET CBUFFER PTR							  CONST	***/
/*** ==> usage: to retrieve a pointer to a specific Constant Buffer					***/
/*** ==> returns the (const) pointer									 			***/
/**************************************************************************************/

ID3D11Buffer* CD3dCBufferMgr::GetCBufferPtr(const unsigned int pHandle) const
{
	if(pHandle >= mCBuffers.size()) return nullptr;
	return mCBuffers[pHandle];
}

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

It's Middle*ditch*. Yes, my name is just as stupid-sounding as it looks. Thanks, parents!

I don't know why, but up until now I've always read it as SeanMiddleTech as well... brain farts.

This topic is closed to new replies.

Advertisement