0

So I've got a rendering engine where in certain applications, it makes sense to use a vertex buffer, while in other applications it makes sense to use a triangle buffer. I'll briefly explain the difference so it is clear:

  • Vertex Buffer: This approach means that triangles are stored using 2 arrays. A vertex buffer and an index buffer. The vertex buffer contains all of the unique vertex information, while the index buffer contains all of the face definitions as indices of the vertex buffer. So for example, to obtain the 2nd vertex in a triangle with index idx you would call: vertex_buffer[index_buffer[3*idx+1]] (assuming both vertex_buffer and index_buffer are just 1d arrays).

  • Triangle Buffer: This approach means that each triangle is represented by a struct containing all of its necessary data. So to access the first element of the 2nd vertex in a triangle with index idx you would call: triangle_buffer[idx].vertex1[0].

The benefit of the triangle buffer approach is that it requires fewer reads from memory for each individual triangle at the expense of potentially duplicating data (as is the case if you have a continuous geometry and so triangles share vertices). The benefit of the vertex buffer approach is that it does not duplicate any memory at the expense of needing more reads from memory to get both the index and subsequent vertex. For a continuous geometry processed coherently though, this is less of an issue as previously loaded triangle vertices will still be in cache.

There are times when it makes more sense to use one of these approaches over the other, and I'd like that to be configurable at compile time. My first gut thought on how to do that would be to create a wrapper function for the this process of fetching triangle data, and inlining that where needed. So the function(s) might look like this:

template <typename Scalar> inline Vertex<Scalar> get_triangle_vertex0(size_t triangle_idx) { #if defined(TRIANGLE_BUFFER) return triangle_buffer[idx].vertex0 #else return vertex_buffer[index_buffer[3*idx+1]] #endif } template <typename Scalar> inline Vertex<Scalar> get_triangle_vertex1(size_t triangle_idx) { #if defined(TRIANGLE_BUFFER) return triangle_buffer[idx].vertex1 #else return vertex_buffer[index_buffer[3*idx+1] + 1] #endif } 

NOTE: For this is just rough pseudo code just meant to outline the rough concept of my initial plan, which is to wrap each of the indexing methods in an inlined function so that at compile time you can decide between each method.

Is this the correct way of handling this sort of thing? Is there a performance penalty for this? Or is there a more sensible way to achieve the same result?

The only alternative I can think of is to go through and manually put the preprocessor directives everywhere I need to access triangle data. Which is why I thought an inline function like this might help consolidate that and keep things clean.

15
  • 2
    inline is a hint to the compiler that a function should be expanded inline; as such, it has no enforceable consequences. Its visible meaning is that the same definition can occur in multiple translation units, and the compiler is free to pick any one of them. Template functions, like your get_triangle_vertex, are always inline in that sense. Whether the compiler expands the function inline is up to the compiler. In short: you don't need to mark this function inline. Chances are the compiler will see that it's just a wrapper and expand it inline. Commented Feb 6, 2023 at 18:10
  • 1
    And, yes, the approach you're taking makes sense. This kind of code is quite common. Commented Feb 6, 2023 at 18:12
  • 1
    @ChrisGnam That's reducing compile-time at the cost of optimization opportunities (specifically inlining). If you use that approach the inline keyword doesn't really make sense at all in the context (and I am not even sure whether it is allowed to declare, but not define, an inline template in a translation unit where it would be implicitly instantiated). Commented Feb 6, 2023 at 18:30
  • 1
    inline is not a hint to the compiler, it's a linker directive that indicates that a function is defined in multiple translation units (and is really just used to get around the one-definition-rule). For template functions it is pointless, they already behave as if they're inline. Commented Feb 6, 2023 at 18:43
  • 1
    Are all the translation units involved either compiled with -DTRIANGLE_BUFFER or -UTRIANGLE_BUFFER? Without mixing some of one and some of the other? Commented Feb 6, 2023 at 18:50

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.