Vulkan Storage Buffer Memory Mapping - c++

I'm refactoring and re-writing the guide made by VkGuide as to fit my idea of an engine. I'm using VMA to handle my memory needs.
I've stumbled upon a strange (?) issue in refactoring my memory mapping code into something more easily readable:
Original code:
Renderer fields
//-------------------------------------------------
struct ObjectData { matrix4 transform };
inline Frame& frame(); // Get current frame in flight
std::vector<RenderableObject> renderables;
//--------------------------------------------------
Drawing code
void* object_data;
vmaMapMemory(allocator, frame().object_buffer.allocation, &object_data);
auto* ssbo = static_cast<ObjectData*>(object_data);
for (int i = 0; i < renderables.size(); i++) {
auto& object = renderables[i];
ssbo[i].model_matrix = object.transform;
}
vmaUnmapMemory(allocator, frame().object_buffer.allocation)
and the refactored and templated namespace-local function:
struct AllocatedBuffer { VkBuffer buffer, VmaAllocation allocation };
template <typename T, typename Func>
void effect_mmap(VmaAllocator& allocator, AllocatedBuffer& buffer, Func&& effect)
{
void* object_data;
vmaMapMemory(allocator, buffer.allocation, &object_data);
auto* t_pointer = static_cast<T*>(object_data);
effect(*t_pointer);
vmaUnmapMemory(allocator, buffer.allocation);
}
My camera position and everything else seems to shift and work incorrectly. Following shows usage (which now should replace the original code):
MemoryMapper::effect_mmap<ObjectData>(allocator, frame().object_buffer, [&](ObjectData& data) {
for (auto& object : renderables) {
data.model_matrix = object.transform;
}
});
and here are the correct (the textures are still invalid, and I don't know why, that's for another day) and afters: Correct, and Incorrect.

Your usage example only ever sets the first SSBO in the buffer, as data is *t_pointer and never changes.
Change your code to pass t_pointer directly, change the type of the callback and then you can use it as
MemoryMapper::effect_mmap<ObjectData>(allocator, frame().object_buffer, [&](ObjectData* data) {
for (auto& object : renderables) {
data->model_matrix = object.transform;
data++;
}
});

Related

vector::reserve ran into read access violation when working with Vulkan API

C++20 CODE
Has to be C++20 cuz I've written some template in C++20
No C++20 no compile
Comp. with VS 2022
Happens when in Debug conf.
Don't happen when in Release conf.
Without debugger it still happen.
A exception was thrown in which:
_CONSTEXPR20 void _Orphan_range_unlocked(pointer _First, pointer _Last) const {
_Iterator_base12** _Pnext = &_Mypair._Myval2._Myproxy->_Myfirstiter;
while (*_Pnext) {
-----------^^^^^^^
const auto _Pnextptr = static_cast<const_iterator&>(**_Pnext)._Ptr;
if (_Pnextptr < _First || _Last < _Pnextptr) { // skip the iterator
const auto _Temp = *_Pnext; // TRANSITION, VSO-1269037
_Pnext = &_Temp->_Mynextiter;
} else { // orphan the iterator
const auto _Temp = *_Pnext; // TRANSITION, VSO-1269037
_Temp->_Myproxy = nullptr;
*_Pnext = _Temp->_Mynextiter;
}
}
}
where
_Mypair._Myval2._Myproxy is nullptr
The popup window reads:
Unhandled exception thrown: read access violation.
_Pnext was 0x8.
My code was:
struct Swapchain
{
struct SwapchainImage
{
VkImage img;
VkImageView view;
VkFramebuffer frame;
ImageDepthTest depth;
SwapchainImage(VkDevice device, VkImage image, const VkExtent2D& extent, VkFormat depth_format, VkSurfaceFormatKHR surface, VkRenderPass pass, const VkAllocationCallbacks* alloc = nullptr);
inline SwapchainImage(SwapchainImage&& e) noexcept : img(e.img), view(e.view), frame(e.frame), depth(std::move(e.depth)) { e.img = nullptr; e.view = nullptr; e.frame = nullptr; }
SwapchainImage(const SwapchainImage&) = delete;
~SwapchainImage() noexcept;
};
std::vector<SwapchainImage> imgs;
...
}
vkResult = BUG_CHECK(vkCreateSwapchainKHR, device, &createInfo, alloc, &this->swapchain);
this->depth_format = FindDepthFormat(phys);
uint32_t nSize = 0;
BUG_CHECK(vkGetSwapchainImagesKHR, device, this->swapchain, &nSize, nullptr);
std::vector<VkImage> vkImages(nSize);
this->imgs.reserve(nSize); // without this, everything is fine, nSize = 3 btw
BUG_CHECK(vkGetSwapchainImagesKHR, device, this->swapchain, &nSize, vkImages.data());
for (auto e : vkImages)
{
this->imgs.emplace_back(device, e, this->extent, this->depth_format, format, pass, alloc);
}
In my opinion, the reserve function shouldn't matter.
However I got this problem even though I tweaked my code:
simply making the constructor of this->imgs[*] do nothing.
The this->imgs is empty before reserve btw.
Don't know how could this happen.
I recalled that long ago I have written code like this with Vulkan API it ran into the same problem.
I understand that I can totally remove the reserve and everything will work.
But I want to know why is this code incompatible with Vulkan API, and typically swapchain related.
I can't represent this under simple situations such as std::vector<int>; // LMAO I texted ';' instead of '.', nice one!
I found the problem.
It was because I called the destructor beforehand.
The vector can be directly reused no problem after destruction if I don't call reserve.
Thank PaulMcKenzie's comment.
I will modify my code to prevent calling vector::~vector before using it.

What is the C++ way to do this instead of using function pointers?

I am working on a part of a program that applies different types of filters onto an already read data part of a bitmap image. The method in question gets the data stored in a 2-dim std::vector and furthermore a pointer to the function in which the filter is applied as arguments. By that we can generically apply different filters by using this method.
My question is, are function pointers the only way to achieve this, or does C++ offer a more beautiful and more readable solution to achieve it?
This is the method this question is about. Second argument is the function pointer that is used to access the function in the if/else statement inside the for loops.
void SteganoMessage::genFilter(std::vector<std::vector<uint32_t>> *d, uint32_t (*f)(uint32_t, size_t)){
int count = 0;
int pixel = getPixel();
for(auto itOuter = d->begin(); itOuter != d->end(); ++itOuter){
for(auto itInner = itOuter->begin(); itInner != itOuter->end(); ++itInner){
if(mode == true)
*itInner = f(*itInner, sizeof(*itInner));
else
*itInner = f(*itInner, this->getImage()->getBitmapHeader()->getBitCount()/8);
displayProgress(count, pixel);
}
}
displayProgress(0);
}
Call of genFilter function:
//...
{
genFilter(data, substB);
}
//...
While substB is a function of course.
Would be very thankful for a hint that leads me into the right direction where I could research or a code snippet that shows a possible more C++ like way to do it.
Type-preserving
The usual way to pass a function (or things that can be INVOKEd) in C++ is by using a template parameter:
// version #1
template <typename F>
void func(F f)
{
static_assert(std::is_invocable_v<F, std::uint32_t, std::size_t>);
// instead of f(*itInner, sizeof(*itInner))
std::invoke(f, *itInner, sizeof(*itInner));
}
You can also use SFINAE to prevent postponing the error to instantiation time. This also enables overloading:
// version #2
template <typename F>
std::enable_if_t<std::is_invocable_v<F, std::uint32_t, std::size_t>>
func(F f)
{
// no need to static_assert here
std::invoke(f, *itInner, sizeof(*itInner));
}
Since C++20, we can use concepts:
// version #3
template <std::Invocable<std::uint32_t, std::size_t> F>
void func(F f)
{
// same as above
}
Which can be simplified further, using an abbreviated function template, to:
// version #4
void func(std::Invocable<std::uint32_t, std::size_t> auto f)
{
// same as above
}
(This is still a function template rather than an ordinary function. It is equivalent to version #3.)
Type-erasing
You can also use std::function for type erasure:
// version #5
void func(std::function<void(std::uint32_t, std::size_t)> f)
{
// ...
f(*itInner, sizeof(*itInner));
}
Compared to the type-preserving alternatives (versions #1–4), this approach may reduce code bloat, but may incur runtime overhead for virtual function calling.
I agree with the already suggested comments and answer.
But if your question was about finding a way to avoid to pass a raw pointer to function as arguments and gain more control over the given filters, I think you can create a wrapping class with a functor that will handle the applied filters.
What is the motivation behind doing this ? Because a raw pointer to function does not give you the guarantee that the function is what you expect. You can pass any function which respect the prototype but is not a real filter and can do anything.
You can solve this problem this way (explanation below the code):
enum class FILTER_TYPE {MY_FILTER, MY_OTHER_FILTER};
class Filter
{
protected:
FILTER_TYPE f_type;
public:
Filter(FILTER_TYPE ft) : f_type(ft)
{}
uint32_t operator()(uint32_t a, size_t b) const
{
switch(f_type)
{
case FILTER_TYPE::MY_FILTER: return my_filter(a, b);
case FILTER_TYPE::MY_OTHER_FILTER: return my_other_filter(a, b);
}
}
private:
uint32_t my_filter(uint32_t a, size_t b) const
{
return a+static_cast<uint32_t>(b); // completely arbitrary
}
uint32_t my_other_filter(uint32_t a, size_t b) const
{
return a*static_cast<uint32_t>(b); // completely arbitrary
}
};
As you can see, you define all your different filters in the private section. Then you redefine the operator() in order to call the proper filter (selected by the FILTER_TYPE attribute).
Then, you can write your function this way:
void SteganoMessage::genFilter(std::vector <std::vector <uint32_t>> & data, const Filter & filter)
{
int count = 0;
int pixel = getPixel();
for(auto itOuter = data.begin(); itOuter != data.end(); ++itOuter)
{
for(auto itInner = itOuter->begin(); itInner != itOuter->end(); ++itInner)
{
if(mode == true)
*itInner = filter(*itInner, sizeof(*itInner));
else
*itInner = filter(*itInner, this->getImage()->getBitmapHeader()->getBitCount()/8);
displayProgress(count, pixel);
}
}
displayProgress(0);
}
This way, you have the guarantee that the argument is a well-defined filter, and you avoid the use of raw pointer to function (that make the code more readable).
I redefined the operator() in order to use the Filter instance as a function. It makes the code more intuitive in my opinion.
Last thing, I passed the data by reference instead of the address directly.
I hope it can be a good additional information.

Forwarding to in-place constructor

I have a message class which was previously a bit of a pain to work with, you had to construct the message class, tell it to allocate space for your object and then populate the space either by construction or memberwise.
I want to make it possible to construct the message object with an immediate, inline new of the resulting object, but to do so with a simple syntax at the call site while ensuring copy elision.
#include <cstdint>
typedef uint8_t id_t;
enum class MessageID { WorldPeace };
class Message
{
uint8_t* m_data; // current memory
uint8_t m_localData[64]; // upto 64 bytes.
id_t m_messageId;
size_t m_size; // amount of data used
size_t m_capacity; // amount of space available
// ...
public:
Message(size_t requestSize, id_t messageId)
: m_data(m_localData)
, m_messageId(messageId)
, m_size(0), m_capacity(sizeof(m_localData))
{
grow(requestSize);
}
void grow(size_t newSize)
{
if (newSize > m_capacity)
{
m_data = realloc((m_data == m_localData) ? nullptr : m_data, newSize);
assert(m_data != nullptr); // my system uses less brutal mem mgmt
m_size = newSize;
}
}
template<typename T>
T* allocatePtr()
{
size_t offset = size;
grow(offset + sizeof(T));
return (T*)(m_data + offset);
}
#ifdef USE_CPP11
template<typename T, typename Args...>
Message(id_t messageId, Args&&... args)
: Message(sizeof(T), messageID)
{
// we know m_data points to a large enough buffer
new ((T*)m_data) T (std::forward<Args>(args)...);
}
#endif
};
Pre-C++11 I had a nasty macro, CONSTRUCT_IN_PLACE, which did:
#define CONSTRUCT_IN_PLACE(Message, Typename, ...) \
new ((Message).allocatePtr<Typename>()) Typename (__VA_ARGS__)
And you would say:
Message outgoing(sizeof(MyStruct), MessageID::WorldPeace);
CONSTRUCT_IN_PLACE(outgoing, MyStruct, wpArg1, wpArg2, wpArg3);
With C++11, you would use
Message outgoing<MyStruct>(MessageID::WorldPeace, wpArg1, wpArg2, wpArg3);
But I find this to be messy. What I want to implement is:
template<typename T>
Message(id_t messageId, T&& src)
: Message(sizeof(T), messageID)
{
// we know m_data points to a large enough buffer
new ((T*)m_data) T (src);
}
So that the user uses
Message outgoing(MessageID::WorldPeace, MyStruct(wpArg1, wpArg2, wpArg3));
But it seems that this first constructs a temporary MyStruct on the stack turning the in-place new into a call to the move constructor of T.
Many of these messages are simple, often POD, and they are often in marshalling functions like this:
void dispatchWorldPeace(int wpArg1, int wpArg2, int wpArg3)
{
Message outgoing(MessageID::WorldPeace, MyStruct(wpArg1, wpArg2, wpArg3));
outgoing.send(g_listener);
}
So I want to avoid creating an intermediate temporary that is going to require a subsequent move/copy.
It seems like the compiler should be able to eliminate the temporary and the move and forward the construction all the way down to the in-place new.
What am I doing that is causing it not to? (GCC 4.8.1, Clang 3.5, MSVC 2013)
You won't be able to elide the copy/move in the placement new: copy elision is entirely based on the idea that the compiler knows at construction time where the object will eventually end up. Also, since copy elision actually changes the behavior of the program (after all, it won't call the respective constructor and the destructor even if they have side-effects) copy elision is limited to a few very specific cases (listed in 12.8 [class.copy] paragraph 31: essentially when returning a local variable by name, when throwing a local variable by name, when catching an exception of the correct type by value, and when copying/moving a temporary variable; see the clause for exact details). Since [placement] new is none of the contexts where the copy can be elided and the argument to constructor is clearly not a temporary (it is named), the copy/move will never be elided. Even adding the missing std::forward<T>(...) to your constructor will cause the copy/move to be elided:
template<typename T>
Message(id_t messageId, T&& src)
: Message(sizeof(T), messageID)
{
// placement new take a void* anyway, i.e., no need to cast
new (m_data) T (std::forward<T>(src));
}
I don't think you can explicitly specify a template parameter when calling a constructor. Thus, I think the closest you could probably get without constructing the object ahead of time and getting it copied/moved is something like this:
template <typename>
struct Tag {};
template <typename T, typename A>
Message::Message(Tag<T>, id_t messageId, A... args)
: Message(messageId, sizeof(T)) {
new(this->m_data) T(std::forward<A>(args)...);
}
One approach which might make things a bit nicer is using the id_t to map to the relevant type assuming that there is a mapping from message Ids to the relevant type:
typedef uint8_t id_t;
template <typename T, id_t id> struct Tag {};
struct MessageId {
static constexpr Tag<MyStruct, 1> WorldPeace;
// ...
};
template <typename T, id_t id, typename... A>
Message::Message(Tag<T, id>, A&&... args)
Message(id, sizeof(T)) {
new(this->m_data) T(std::forward<A>)(args)...);
}
Foreword
The conceptual barrier that even C++2049 cannot cross is that you require all the bits that compose your message to be aligned in a contiguous memory block.
The only way C++ can give you that is through the use of the placement new operator. Otherwise, objects will simply be constructed according to their storage class (on the stack or through whatever you define as a new operator).
It means any object you pass to your payload constructor will be first constructed (on the stack) and then used by the constructor (that will most likely copy-construct it).
Avoiding this copy completely is impossible. You may have a forward constructor doing the minimal amount of copy, but still the scalar parameters passed to the initializer will likely be copied, as will any data that the constructor of the initializer deemed necessary to memorize and/or produce.
If you want to be able to pass parameters freely to each of the constructors needed to build the complete message without them being first stored in the parameter objects, it will require
the use of a placement new operator for each of the sub-objects that compose the message,
the memorization of each single scalar parameter passed to the various sub-constructors,
specific code for each object to feed the placement new operator with the proper address and call the constructor of the sub-object.
You will end up with a toplevel message constructor taking all possible initial parameters and dispatching them to the various sub-objects constructors.
I don't even know if this is feasible, but the result would be very fragile and error-prone at any rate.
Is that what you want, just for the benefit of a bit of syntactic sugar?
If you're offering an API, you cannot cover all cases. The best approach is to make something that degrades nicely, IMHO.
The simple solution would be to limit payload constructor parameters to scalar values or implement "in-place sub-construction" for a limited set of message payloads that you can control. At your level you cannot do more than that to make sure the message construction proceeds with no extra copies.
Now the application software will be free to define constructors that take objects as parameters, and then the price to pay will be these extra copies.
Besides, this might be the most efficient approach, if the parameter is something costly to construct (i.e. the construction time is greater than the copy time, so it is more efficient to create a static object and modify it slightly between each message) or if it has a greater lifetime than your function for any reason.
a working, ugly solution
First, let's start with a vintage, template-less solution that does in-place construction.
The idea is to have the message pre-allocate the right kind of memory (local buffer of dynamic) depending on the size of the object.
The proper base address is then passed to a placement new to construct the message contents in place.
#include <cstdint>
#include <cstdio>
#include <new>
typedef uint8_t id_t;
enum class MessageID { WorldPeace, Armaggedon };
#define SMALL_BUF_SIZE 64
class Message {
id_t m_messageId;
uint8_t* m_data;
uint8_t m_localData[SMALL_BUF_SIZE];
public:
// choose the proper location for contents
Message (MessageID messageId, size_t size)
{
m_messageId = (id_t)messageId;
m_data = size <= SMALL_BUF_SIZE ? m_localData : new uint8_t[size];
}
// dispose of the contents if need be
~Message ()
{
if (m_data != m_localData) delete m_data;
}
// let placement new know about the contents location
void * location (void)
{
return m_data;
}
};
// a macro to do the in-place construction
#define BuildMessage(msg, id, obj, ... ) \
Message msg(MessageID::id, sizeof(obj)); \
new (msg.location()) obj (__VA_ARGS__); \
// example uses
struct small {
int a, b, c;
small (int a, int b, int c) :a(a),b(b),c(c) {}
};
struct big {
int lump[1000];
};
int main(void)
{
BuildMessage(msg1, WorldPeace, small, 1, 2, 3)
BuildMessage(msg2, Armaggedon, big)
}
This is just a trimmed down version of your initial code, with no templates at all.
I find it relatively clean and easy to use, but to each his own.
The only inefficiency I see here is the static allocation of 64 bytes that will be useless if the message is too big.
And of course all type information is lost once the messages are constructed, so accessing their contents afterward would be awkward.
About forwarding and construction in place
Basically, the new && qualifier does no magic. To do in-place construction, the compiler needs to know the address that will be used for object storage before calling the constructor.
Once you've invoked an object creation, the memory has been allocated and the && thing will only allow you to use that address to pass ownership of the said memory to another object without resorting to useless copies.
You can use templates to recognize a call to the Message constructor involving a given class passed as message contents, but that will be too late: the object will have been constructed before your constructor can do anything about its memory location.
I can't see a way to create a template on top of the Message class that would defer an object construction until you have decided at which location you want to construct it.
However, you could work on the classes defining the object contents to have some in-place construction automated.
This will not solve the general problem of passing objects to the constructor of the object that will be built in place.
To do that, you would need the sub-objects themselves to be constructed through a placement new, which would mean implementing a specific template interface for each of the initializers, and have each object provide the address of construction to each of its sub-objects.
Now for syntactic sugar.
To make the ugly templating worth the while, you can specialize your message classes to handle big and small messages differently.
The idea is to have a single lump of memory to pass to your sending function. So in case of small messages, the message header and contents are defined as local message properties, and for big ones, extra memory is allocated to include the message header.
Thus the magic DMA used to propell your messages through the system will have a clean data block to work with either way.
Dynamic allocations will still occur once per big message, and never for small ones.
#include <cstdint>
#include <new>
// ==========================================================================
// Common definitions
// ==========================================================================
// message header
enum class MessageID : uint8_t { WorldPeace, Armaggedon };
struct MessageHeader {
MessageID id;
uint8_t __padding; // one free byte here
uint16_t size;
};
// small buffer size
#define SMALL_BUF_SIZE 64
// dummy send function
int some_DMA_trick(int destination, void * data, uint16_t size);
// ==========================================================================
// Macro solution
// ==========================================================================
// -----------------------------------------
// Message class
// -----------------------------------------
class mMessage {
// local storage defined even for big messages
MessageHeader m_header;
uint8_t m_localData[SMALL_BUF_SIZE];
// pointer to the actual message
MessageHeader * m_head;
public:
// choose the proper location for contents
mMessage (MessageID messageId, uint16_t size)
{
m_head = size <= SMALL_BUF_SIZE
? &m_header
: (MessageHeader *) new uint8_t[size + sizeof (m_header)];
m_head->id = messageId;
m_head->size = size;
}
// dispose of the contents if need be
~mMessage ()
{
if (m_head != &m_header) delete m_head;
}
// let placement new know about the contents location
void * location (void)
{
return m_head+1;
}
// send a message
int send(int destination)
{
return some_DMA_trick (destination, m_head, (uint16_t)(m_head->size + sizeof (m_head)));
}
};
// -----------------------------------------
// macro to do the in-place construction
// -----------------------------------------
#define BuildMessage(msg, obj, id, ... ) \
mMessage msg (MessageID::id, sizeof(obj)); \
new (msg.location()) obj (__VA_ARGS__); \
// ==========================================================================
// Template solution
// ==========================================================================
#include <utility>
// -----------------------------------------
// template to check storage capacity
// -----------------------------------------
template<typename T>
struct storage
{
enum { local = sizeof(T)<=SMALL_BUF_SIZE };
};
// -----------------------------------------
// base message class
// -----------------------------------------
class tMessage {
protected:
MessageHeader * m_head;
tMessage(MessageHeader * head, MessageID id, uint16_t size)
: m_head(head)
{
m_head->id = id;
m_head->size = size;
}
public:
int send(int destination)
{
return some_DMA_trick (destination, m_head, (uint16_t)(m_head->size + sizeof (*m_head)));
}
};
// -----------------------------------------
// general message template
// -----------------------------------------
template<bool local_storage, typename message_contents>
class aMessage {};
// -----------------------------------------
// specialization for big messages
// -----------------------------------------
template<typename T>
class aMessage<false, T> : public tMessage
{
public:
// in-place constructor
template<class... Args>
aMessage(MessageID id, Args...args)
: tMessage(
(MessageHeader *)new uint8_t[sizeof(T)+sizeof(*m_head)], // dynamic allocation
id, sizeof(T))
{
new (m_head+1) T(std::forward<Args>(args)...);
}
// destructor
~aMessage ()
{
delete m_head;
}
// syntactic sugar to access contents
T& contents(void) { return *(T*)(m_head+1); }
};
// -----------------------------------------
// specialization for small messages
// -----------------------------------------
template<typename T>
class aMessage<true, T> : public tMessage
{
// message body defined locally
MessageHeader m_header;
uint8_t m_data[sizeof(T)]; // no need for 64 bytes here
public:
// in-place constructor
template<class... Args>
aMessage(MessageID id, Args...args)
: tMessage(
&m_header, // local storage
id, sizeof(T))
{
new (m_head+1) T(std::forward<Args>(args)...);
}
// syntactic sugar to access contents
T& contents(void) { return *(T*)(m_head+1); }
};
// -----------------------------------------
// helper macro to hide template ugliness
// -----------------------------------------
#define Message(T) aMessage<storage<T>::local, T>
// something like typedef aMessage<storage<T>::local, T> Message<T>
// ==========================================================================
// Example
// ==========================================================================
#include <cstdio>
#include <cstring>
// message sending
int some_DMA_trick(int destination, void * data, uint16_t size)
{
printf("sending %d bytes #%p to %08X\n", size, data, destination);
return 1;
}
// some dynamic contents
struct gizmo {
char * s;
gizmo(void) { s = nullptr; };
gizmo (const gizmo& g) = delete;
gizmo (const char * msg)
{
s = new char[strlen(msg) + 3];
strcpy(s, msg);
strcat(s, "#");
}
gizmo (gizmo&& g)
{
s = g.s;
g.s = nullptr;
strcat(s, "*");
}
~gizmo()
{
delete s;
}
gizmo& operator=(gizmo g)
{
std::swap(s, g.s);
return *this;
}
bool operator!=(gizmo& g)
{
return strcmp (s, g.s) != 0;
}
};
// some small contents
struct small {
int a, b, c;
gizmo g;
small (gizmo g, int a, int b, int c)
: a(a), b(b), c(c), g(std::move(g))
{
}
void trace(void)
{
printf("small: %d %d %d %s\n", a, b, c, g.s);
}
};
// some big contents
struct big {
gizmo lump[1000];
big(const char * msg = "?")
{
for (size_t i = 0; i != sizeof(lump) / sizeof(lump[0]); i++)
lump[i] = gizmo (msg);
}
void trace(void)
{
printf("big: set to ");
gizmo& first = lump[0];
for (size_t i = 1; i != sizeof(lump) / sizeof(lump[0]); i++)
if (lump[i] != first) { printf(" Erm... mostly "); break; }
printf("%s\n", first.s);
}
};
int main(void)
{
// macros
BuildMessage(mmsg1, small, WorldPeace, gizmo("Hi"), 1, 2, 3);
BuildMessage(mmsg2, big , Armaggedon, "Doom");
((small *)mmsg1.location())->trace();
((big *)mmsg2.location())->trace();
mmsg1.send(0x1000);
mmsg2.send(0x2000);
// templates
Message (small) tmsg1(MessageID::WorldPeace, gizmo("Hello"), 4, 5, 6);
Message (big ) tmsg2(MessageID::Armaggedon, "Damnation");
tmsg1.contents().trace();
tmsg2.contents().trace();
tmsg1.send(0x3000);
tmsg2.send(0x4000);
}
output:
small: 1 2 3 Hi#*
big: set to Doom#
sending 20 bytes #0xbf81be20 to 00001000
sending 4004 bytes #0x9e58018 to 00002000
small: 4 5 6 Hello#**
big: set to Damnation#
sending 20 bytes #0xbf81be0c to 00003000
sending 4004 bytes #0x9e5ce50 to 00004000
Arguments forwarding
I see little point in doing constructor parameters forwarding here.
Any bit of dynamic data referenced by the message contents would have to be either static or copied into the message body, otherwise the referenced data would vanish as soon as the message creator would go out of scope.
If the users of this wonderfully efficient library start passing around magic pointers and other global data inside messages, I wonder how the global system performance will like that. But that's none of my business, after all.
Macros
I resorted to a macro to hide the template ugliness in type definition.
If someone has an idea to get rid of it, I'm interested.
Efficiency
The template variation requires an extra forwarding of the contents parameters to reach the constructor. I can't see how that could be avoided.
The macro version wastes 68 bytes of memory for big messages, and some memory for small ones (64 - sizeof (contents object)).
Performance-wise, this extra bit of memory is the only gain the templates offer. Since all these objects are supposedly constructed on the stack and live for a handful of microseconds, it is pretty neglectible.
Compared to your initial version, this one should handle message sending more efficiently for big messages. Here again, if these messages are rare and only offered for convenience, the difference is not terribly useful.
The template version maintains a single pointer to the message payload, that could be spared for small messages if you implemented a specialized version of the send function.
Hardly worth the code duplication, IMHO.
A last word
I think I know pretty well how an operating system works and what performances concerns might be. I wrote quite a few real-time applications, plus some drivers and a couple of BSPs in my time.
I also saw more than once a very efficient system layer ruined by too permissive an interface that allowed application software programmers to do the silliest things without even knowing.
That is what triggered my initial reaction.
If I had my say in global system design, I would forbid all these magic pointers and other under-the-hood mingling with object references, to limit non-specialist users to an inoccuous use of system layers, instead of allowing them to inadvertently spread cockroaches through the system.
Unless the users of this interface are template and real-time savvies, they will not understand a bit what is going on beneath the syntactic sugar crust, and might very soon shoot themselves (and their co-workers and the application software) in the foot.
Suppose a poor application software programmer adds a puny field in one of its structs and crosses unknowingly the 64 bytes barrier. All of a sudden the system performance will crumble, and you will need Mr template & real time expert to explain the poor guy that what he did killed a lot of kittens.
Even worse, the system degradation might be progressive or unnoticeable at first, so one day you might wake up with thousands of lines of code that did dynamic allocations for years without anybody noticing, and the global overhaul to correct the problem might be huge.
If, on the other hand, all people in your company are munching at templates and mutexes for breakfast, syntactic sugar is not even required in the first place.

Using delete in a std::deque image buffer (OFX Plug-in)

I'm trying to program a video buffer in a std::deque in my OFX video plug-in. I would like to access previously processed images in order to process the current image. My idea is to push processed images to the front of the deque and pop them from the back if the buffer exceeds the maximum size.
The plug-in crashes when I try to free the memory of an image using delete before removing it from the buffer. I found out that I can add one or several images to the buffer and delete and remove them immediately afterwards with no problem. However, if I try to delete an image which has been added in an earlier cycle, it crashes.
The plug-in consists of the main class OFXPlugin and the processor class myplugin. The instance of OFXPlugin stays over time, but for every image to be processed it creates an instance of myplugin and destroys it after processing that frame.
I'm not sure if I'm doing something wrong in the way I use the deque, if I'm not allowed to free memory which has been allocated by another instance of myplugin or if I'm doing something illegal related to the OFX API.
The Code below shows the extracts of the plug-in related to the problem. It's based on the OFX Support examples. It crashes at delete videoBuffer_.back().img; in the function OFXPlugin::addToVBuff(OFX::Image *img, double t). I cannot catch an exception, apparently it is handled (ignored) in the OFX API.
Thanks a lot for your help!
myplugin.h
#include "ofxsImageEffect.h"
#include "ofxsMultiThread.h"
#include "../Support/Plugins/include/ofxsProcessing.H"
#include <deque>
// Video Buffer Element
typedef struct vBuffEl
{
OFX::Image* img;
double time;
} vBuffEl;
inline
bool operator==(const vBuffEl &a, const double b)
{
return a.time == b;
}
class myplugin : public OFX::ImageProcessor {
protected :
OFX::Image *_srcImg;
double _time;
OFXPlugin *_opInstance;
public :
// ctor
myplugin(OFX::ImageEffect &instance)
: OFX::ImageProcessor(instance)
, _srcImg(0)
, _time(0)
{}
void multiThreadProcessImages(OfxRectI procWindow);
void setOFXPlugin(OFXPlugin* opInstance) {_opInstance = opInstance;}
OFXPlugin* getOFXPlugin() {return _opInstance;}
void setTime(double argsTime) {_time = argsTime;}
double getTime() {return _time;}
void setSrcImg(OFX::Image *v) {_srcImg = v;}
OFX::Image* getSrcImg() {return _srcImg;}
};
class OFXPlugin : public OFX::ImageEffect {
protected :
OFX::Clip *dstClip_;
OFX::Clip *srcClip_;
double time_;
std::deque<vBuffEl> videoBuffer_;
public :
/** #brief ctor */
OFXPlugin(OfxImageEffectHandle handle);
/** #brief dtor */
~OFXPlugin();
/* Override the render */
virtual void render(const OFX::RenderArguments &args);
/* get the source Clip */
OFX::Clip* getSrcClip();
/* get the current time */
double getTime();
/* set up and run a processor */
void setupAndProcess(myplugin &, const OFX::RenderArguments &args);
/* add to video buffer */
void addToVBuff(OFX::Image *img, double t);
/* fetch a dst image from buffer */
void fetchDstImageBuff(double t, OFX::Image* &img, bool &buff);
};
myplugin.cpp
#include "myplugin.h"
#include <algorithm>
void myplugin::multiThreadProcessImages(OfxRectI procWindow)
{
// Do some filtering of the source image and store result in destination image
myfiltering(_dstImg, _srcImg, procWindow);
// add to buffer
_opInstance->addToVBuff(_dstImg, _time);
}
/* set up and run a processor */
void
OFXPlugin::setupAndProcess(myplugin &processor, const OFX::RenderArguments &args)
{
// get a dst image
std::auto_ptr<OFX::Image> dst(dstClip_->fetchImage(args.time));
OFX::BitDepthEnum dstBitDepth = dst->getPixelDepth();
OFX::PixelComponentEnum dstComponents = dst->getPixelComponents();
// fetch main input image
std::auto_ptr<OFX::Image> src(srcClip_->fetchImage(args.time));
// set the images
processor.setDstImg(dst.get());
processor.setSrcImg(src.get());
// set the render window
processor.setRenderWindow(args.renderWindow);
// set time
processor.setTime(args.time);
time_ = args.time;
// set OFXPlugin instance
processor.setOFXPlugin(this);
// Call the base class process member, this will call the derived templated process code
processor.process();
}
OFX::Clip* OFXPlugin::getSrcClip()
{
return srcClip_;
}
/* get the current time */
double
OFXPlugin::getTime()
{
return time_;
}
// the overridden render function
void
OFXPlugin::render(const OFX::RenderArguments &args)
{
try {
myplugin fred(*this);
setupAndProcess(fred, args);
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
/* add to video buffer */
void
OFXPlugin::addToVBuff(OFX::Image *img, double t)
{
try {
// if frame already exists in buffer, remove
std::deque<vBuffEl>::iterator it;
it = find(videoBuffer_.begin(), videoBuffer_.end(), t);
if(it != videoBuffer_.end())
{
delete it->img;
videoBuffer_.erase(it);
}
// add new frame to the front
vBuffEl e;
e.time = t;
e.img = new OFX::Image(img->getPropertySet().propSetHandle());
memcpy(e.img, img, sizeof(img));
videoBuffer_.push_front(e);
// remove elements at the end, if the buffer exceeds the max size
int LASTIMG_ARRAY_SIZE = 10;
while(videoBuffer_.size() > LASTIMG_ARRAY_SIZE)
{
delete videoBuffer_.back().img;
videoBuffer_.erase(--(videoBuffer_.end()));
}
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
/* fetch a dst image from buffer */
void
OFXPlugin::fetchDstImageBuff(double t, OFX::Image* &img, bool &buff)
{
try {
std::deque<vBuffEl>::iterator it;
it = find(videoBuffer_.begin(), videoBuffer_.end(), t);
if(it != videoBuffer_.end())
{
img = it->img; // return buffered dst image
buff = true;
}
else
{
img = getSrcClip()->fetchImage(t); // fetch and return src image
buff = false;
}
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
The statement
memcpy(e.img, img, sizeof(img));
doesn't do what you expect it to.
The sizeof operation of a pointer returns the size of the pointer, not what it points to. This means that in this case, you are only copying 4 or 8 bytes (depending on if you are on a 32 or 64 bit platform).
However, there is another worse problem hidden in that memcpy call. If the OFX::Image contains data member pointers, copying the data will copy the pointers and not the data. It's a shallow copy, not a deep copy. This is a reason C++ has copy constructors and copy assignment operators.
What you should to is a simple assignment, and hope that OFX::Image follows the rule of three:
*e.img = *img;

C++ Inheritance and how to pass and maintain subclass data through a superclass

Alright, wasn't quite sure how to word the question and couldn't find any duplicates that I think really address this situation.
Essentially I have a super class that gets extra data appended to it through a subclass. The container class for this data recognizes only the super class and adjust characteristics based on an id parameter in the super class.
I've actually never had to used inheritance in c++ till recently so forgive me if this is trivial. I'm under the impression that when I go to hard copy a bunch of data using the superclass, the subclass data is loss in translation so to speak. In order to bypass this limitation I'm trying to use a typecast-ed pointer however I now get a segmentation fault when trying to free the memory even when typecasting the pointer parameter in the free() function.
Here is the sample code...
Structs
// Super class
struct Vertex {
__declspec(align(4)) unsigned int vType; // Identifies the vertex type.
Vertex(const unsigned int _vType) : vType(_vType) { }
Vertex(const Vertex &_rV) : vType(_rV.vType) { } // Copy constructor
virtual ~Vertex() { }
unsigned int GetVType() const { return vType; }
};
// Subclass
// Id = 1
struct V_Pos : Vertex {
__declspec(align(4)) XMFLOAT3 position;
V_Pos(void) : Vertex(1) { }
V_Pos(XMFLOAT3 &_rPosition) : Vertex(1), position(_rPosition) { }
V_Pos(const V_Pos &_rV) : Vertex(_rV), position(_rV.GetPosition()) { } // Copy constructor
~V_Pos() { }
XMFLOAT3 GetPosition() const { return position; }
};
Here is how I'm currently copying the data.
// pBuffer is declared as a Vertex* data type
pBuffer = new V_Pos[_bufSize];
if (_pVBuffer->GetVType() == 1)
for (unsigned int i = 0; i < bufSize; ++i) {
V_Pos *_temp = (V_Pos*)&_pVBuffer[i];
pBuffer[i] = *_temp;
}
Here is how I am currently de-allocating the data.
if (pBuffer != 0) {
delete [] pBuffer;
pBuffer = 0;
}
What is the correct approach for this situation?
Edit 1 -
Updated the above code blocks to clarify the comment discussion under knulp's answer.
If you start mixing low level memory allocation with malloc()/free(), and C++ objects, you will run into a lot of troubles, while making your code almost unreadable.
You should create a new object with new on a proper constructor, which automatically 1) allocates memory and 2) initializes the struct. To properly free the memory you should use delete and the destructor.
You should copy using a copy constructor and an assignment operator. If you do not define them, the default ones are automatically defined by the compiler to perform a bitwise copy.
Why are you using a type field? C++ has a very strong typing features, so it makes very little sense to bypass all C++ mechanism to define a vType. Rather, define a base class, and two or more derived classes from there, and just eliminate the vtype field.
If you use clean OO programming, you will avoid all these problems from the start.
Your base class needs to have a virtual destructor. This will allow you to safely delete a derived class with a base class pointer.
Not that! Use a copy constructor.
// Super class
struct Vertex {
__declspec(align(4)) unsigned int vType; // Identifies the vertex type.
Vertex(const unsigned int _vType) : vType(_vType) { }
unsigned int GetVType() const { return vType; }
Vertex(const Vertex& v) : vType(v.vType) {}
};
// Subclass
// Id = 1
struct V_Pos : Vertex {
__declspec(align(4)) XMFLOAT3 position;
V_Pos(void) : Vertex(1) { }
V_Pos(XMFLOAT3 &_rPosition) : Vertex(1), position(_rPosition) { }
V_Pos(const V_Pos& v) : Vertex(v) {
position[0] = v.position[0];
position[1] = v.position[1];
position[2] = v.position[2];
}
};
Better yet, use a vecotr instead of XMFLOAT3;
Create a copy on the heap:
V_Pos original(...);
V_Pos * copyPtr = new V_Pos(original);