Who can tell me what this bit of C++ does? - c++

CUSTOMVERTEX* pVertexArray;
if( FAILED( m_pVB->Lock( 0, 0, (void**)&pVertexArray, 0 ) ) ) {
return E_FAIL;
}
pVertexArray[0].position = D3DXVECTOR3(-1.0, -1.0, 1.0);
pVertexArray[1].position = D3DXVECTOR3(-1.0, 1.0, 1.0);
pVertexArray[2].position = D3DXVECTOR3( 1.0, -1.0, 1.0);
...
I've not touched C++ for a while - hence the topic but this bit of code is confusing myself. After the m_pVB->Lock is called the array is initialized.
This is great and all but the problem I'm having is how this happens. The code underneath uses nine elements, but another function (pretty much copy/paste) of the code I'm working with only access say four elements.
CUSTOMVERTEX is a struct, but I was under the impression that this matters not and that an array of structs/objects need to be initialized to a fixed size.
Can anyone clear this up?
Edit:
Given the replies, how does it know that I require nine elements in the array, or four etc...?
So as long as the buffer is big enough, the elements are legal. If so, this code is setting the buffer size if I'm not mistaken.
if( FAILED( m_pd3dDevice->CreateVertexBuffer( vertexCount * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pVB, NULL ) ) ) {
return E_FAIL;
}

m_pVB points to a graphics object, in this case presumably a vertex buffer. The data held by this object will not generally be in CPU-accessible memory - it may be held in onboard RAM of your graphics hardware or not allocated at all; and it may be in use by the GPU at any particular time; so if you want to read from it or write to it, you need to tell your graphics subsystem this, and that's what the Lock() function does - synchronise with the GPU, ensure there is a buffer in main memory big enough for the data and it contains the data you expect at this time from the CPU's point of view, and return to you the pointer to that main memory. There will need to be a corresponding Unlock() call to tell the GPU that you are done reading / mutating the object.
To answer your question about how the size of the buffer is determined, look at where the vertex buffer is being constructed - you should see a description of the vertex format and an element count being passed to the function that creates it.

You're pasing a pointer to the CUSTOMVERTEX pointer (pointer to a pointer) into the lock function so lock itself must be/ needs to be creating the CUSTOMVERTEX object and setting your pointer to point to the object it creates.

In order to modify a vertex buffer in DX you have to lock it. To enforce this the DX API will only reveal the guts of a VB through calling Lock on it.
Your code is passing in the address of pVertexArray which Lock points at the VB's internal data. The code then proceeds to modify the vertex data, presumably in preparation for rendering.

You're asking the wrong question, it's not how does it know that you require x objects, it's how YOU know that IT requires x objects. You pass a pointer to a pointer to your struct in and the function returns the pointer to your struct already allocated in memory (from when you first initialized the vertex buffer). Everything is always there, you're just requesting a pointer to the array to work with it, then "release it" so dx knows to read the vertex buffer and upload it to the gpu.

When you created the vertex buffer, you had to specify a size. When you call Lock(), you're passing 0 as the size to lock, which tells it to lock the entire size of the vertex buffer.

Related

Byte Array Initialization Causes DirectX to Crash

So I'm trying to gain access to vertex buffers on the GPU. Specifically I need to do some calculations with the vertices. So in order to do that I attempt to map the resource (vertex buffer) from the GPU, and copy it into system memory so the CPU can access the vertices. I used the following SO thread to put the code together: How to read vertices from vertex buffer in Direct3d11
Here is my code:
HRESULT hr = pSwapchain->GetDevice(__uuidof(ID3D11Device), (void**)&pDevice);
if (FAILED(hr))
return false;
pDevice->GetImmediateContext(&pContext);
pContext->OMGetRenderTargets(1, &pRenderTargetView, nullptr);
//Vertex Buffer
ID3D11Buffer* veBuffer;
UINT Stride;
UINT veBufferOffset;
pContext->IAGetVertexBuffers(0, 1, &veBuffer, &Stride, &veBufferOffset);
D3D11_MAPPED_SUBRESOURCE mapped_rsrc;
pContext->Map(veBuffer, NULL, D3D11_MAP_READ, NULL, &mapped_rsrc);
void* vert = new BYTE[mapped_rsrc.DepthPitch]; //DirectX crashes on this line...
memcpy(vert, mapped_rsrc.pData, mapped_rsrc.DepthPitch);
pContext->Unmap(veBuffer, 0);
I'm somewhat of a newbie when it comes to C++. So my assumptions may be incorrect. The initialization value that
mapped_rsrc.DepthPitch
returns is quite large. It returns 343597386. According to the documentation I listed below, it states that the return value of DepthPitch is returned in bytes. If I replace the initialization value with a much smaller number, like 10, the code runs just fine. From what I read about the Map() function here: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_mapped_subresource
It states :
Note The runtime might assign values to RowPitch and DepthPitch that
are larger than anticipated because there might be padding between
rows and depth.
Could this have something to do with the large value that is being returned? If so, does that mean I have to parse DepthPitch to remove any unneeded data? Or maybe it is an issue with the way vert is initialized?
There was no Vertex Buffer bound, so your IAGetVertexBuffers failed to return anything. You have to create a VB.
See Microsoft Docs: How to Create a Vertex Buffer
As someone new to DirectX 11, you should take a look at DirectX Tool Kit.

3D pointer seg error

I want to stick 2D arrays in a 3D array together, first i defined the 3D array in the following way
int ***grid;
grid=new int **[number];
then I want to assign the 2D arrays to the 3D construct
for(i=0;i<number;i++)
grid[i]=rk4tillimpact2dens(...);
with
int** rk4tillimpact2dens(...
...
static int** grid;
grid=new int*[600];
for(i=0;i<600;i++)
grid[i]=new int[600];
memset(grid,0x0,sizeof(grid));
...
return(grid);
}
so far no problem, everything works fine, but when I want to access the 3D array afterwards I get a seg fault. Like that e.g.
printf("%d",grid[1][1][1]);
What is my mistake?
Best,
Hannes
Oh, sorry, it was typo in my question, I did
printf("%d",grid[1][1][1]);
it's not working :(. But even
printf("%d",&grid[1][1][1]);
or
printf("%d",*grid[1][1][1]);
would not work. The strange thing is, that there are no errors unless I try to access the array
First, you discard the very first row of each matrix with that memset (the actual row is leaked). While technically grid[1][1][1] should still be readable, it probably becomes corrupt in some other place.
Can you provide a minimal verifiable example? This is likely to solve your problem.
To clear out the memory allocated for grid, you can't do the whole NxN matrix with one memset, it isn't contiguous memory. Since each row is allocated as a separate memory block, you need to clear them individually.
for(i=0;i<600;i++) {
grid[i]=new int[600];
memset(grid[i], 0, sizeof(int) * 600);
}
The 600 value should be a named constant, and not a hardcoded number.
And grid does not need to be a static variable.
Printing out the address
printf("%p",&grid[1][1][1]);
You are printing the address here. That's why you may not get what you desire to see.
printf("%d",grid[1][1][1]);
This will print the array element.
And to read an input from stdin you will use scanf() which requires you to pass address of an variable.
scanf("%d",&grid[1][1][1]);
Zeroing out the allocated memory
Also you can't get the size of the array using sizeof. SO to initialize with 0 you use memset on the chunks that are allocated at once with a new.
In your case example would be Like 1201ProgramAlarm pointed out
for(int i = 0; i < 600; i++){
...
memset(grid[i],0,sizeof(int)*600);
}
There is another way you can initialise an allocated memory in c++.
grid[i]=new int[600]();
For example:
int** rk4tillimpact2dens(...
...
static int** grid;
grid=new int*[600];
for(i=0;i<600;i++)
grid[i]=new int[600]();
...
return(grid);
}
Do you expect memset(grid,0x0,sizeof(grid)); not to zero the pointer values you've just assigned to grid[0] through to grid[599]? If so, you should test that theory by inspecting the pointer values of grid[0] through to grid[599] before and after that call to memset, to find out what memset does to true (more on that later) arrays.
Your program is dereferencing a null pointer which results directly from that line of code. Typically, a crash can be expected when you attempt to dereference a null pointer, because null pointers don't reference any objects. This explains your observation of a crash, and your observation of the crash disappearing when you comment out that call to memset. You can't expect good things to happen if you try to use the value of something which isn't an object, such as grid[1][... where grid[1] is a pointer consisting entirely of zero bits.
The term 3D array doesn't mean what you think it means, by the way. Arrays in C and C++ are considered to be a single allocation, where-as what your code is producing seems to be multiple allocations, associated in a hierarchical form; you've allocated a tree as opposed to an array, and memset isn't appropriate to zero a tree. Perhaps your experiments could be better guided from this point on by a book regarding algorithms, such as Algorithms in C, parts 1-4 by Robert Sedgewick.
For the meantime, in C, the following will get you a pointer to a 2D array which you can mostly use as though it's a 3D array:
void *make_grid(size_t x, size_t y, size_t z) {
int (*grid)[y][z] = malloc(x * sizeof *grid);
/* XXX: use `grid` as though it's a 3D array here.
* i.e. grid[0][1][2] = 42;
*/
return grid;
}
Assuming make_grid returns something non-null, you can use a single call to memset to zero the entirety of the array pointed to by that function because there's a single call to malloc matching that a single call to memset... Otherwise, if you want to zero a tree, you'll probably want to call memset n times for n items.
In C++, I don't think you'll find many who discourage the use of std::vector in place of arrays. You might want to at least consider that option, as well as the other options you have (such as trees; it seems like you want to use a tree, which is fine because trees have perfectly appropriate usecases that arrays aren't valid for, and you haven't given us enough context to tell which would be most appropriate for you).

Allocator with dense and sparse pointers - what's going on?

i'm trying to write a handle allocator in C++. this allocator would "handle" (hue hue hue) the allocation of handles for referencing assets (such as textures, uniforms, etc) in a game engine. for instance, inside a function for creating a texture, the handle allocator would be called to create a TextureHandle. when the texture was destroyed, the handle allocator would free the TextureHandle.
i'm reading through the source of BX, a library that includes a handle allocator just for this purpose - it's the base library of the popular library BGFX, a cross-platform abstraction over different rendering APIs.
before i start explaining what's baffling me, let me first outline what this class essentially looks like:
class HandleAllocator {
public:
constructor, destructor
getters: getNumHandles, getMaxHandles
u16 alloc();
void free(u16 handle);
bool isValid(u16 handle) const;
void reset();
private:
u16* getDensePointer() const;
u16* getSparsePointer() const;
u16 _numHandles;
u16 _maxHandles;
}
here's what getDensePointer() looks like:
u8* ptr = (u8*)reinterpret_cast<const u8*>(this);
return (u16*)&ptr[sizeof(HandleAlloc)];
as far as i understand it, this function is returning a pointer to the end of the class in memory, although i don't understand why the this pointer is first cast to a uint8_t* before being dereferenced and used with the array-index operator on the next line.
here's what's weird to me. the constructor calls the reset() function, which looks like this.
_numHandles = 0;
u16* dense = getDensePointer();
for(u16 ii=0, num = _maxHandles; ii < num; ++ii) {
dense[ii] = ii;
}
if getDensePointer returns a pointer to the end of the class in memory, how is it safe to be writing to memory beyond the end of the class in this for loop? how do i know this isn't stomping on something stored adjacent to it?
i'm a total noob, i realize the answer to this is probably obvious and betrays a total lack of knowledge on my part, but go easy on me..
To answer the first question, ask yourself why pointers have a type. In the end, they are just variables that are meant to store memory addresses. Any variable with a range large enough to store all possible memory addresses could do. They what is the difference between, let's say, int* and u8*?
The difference is the way operations are performed on them. Besides dereferencing, which is another story, pointer arithmetic is also involved. Let's take the following declarations: int *p; u8 *u;. Now, p+2, in order to have sense, will return the address at p+8 (the address of the second integer, if you'd like) while u+2 would return the address of u+2 (since u8 has a size of 1).
Now, sizeof gives you the size of the type in bytes. You want to move sizeof(x) bytes, so you need to index the array (or do pointer arithmetic, they are equivalent here) on a byte-sized data type. And that's why you cast it to u8.
Now, for the second question,
how do i know this isn't stomping on something stored adjacent to it?
simply by making sure nothing is there. This is done during the creation of the handler. For example, if you have:
HandleAllocator *h = new HandleAllocator[3]
you can freely call reset on h[0] and have 2 handlers worth of memory to play with. Without more details, it's hard to tell the exact way this excess memory is allocated and what's its purpose.

copying between interleaved openGL Vertex Buffer Objects

using opengl 3.3, radeon 3870HD, c++..
I got question about interleaved arrays of data. I got in my application structure in vector, which is send as data to buffer object. Something like this:
struct data{
int a;
int b;
int c;
};
std::vector<data> datVec;
...
glBufferData(GL_ARRAY_BUFFER, sizeof(data)*datVec.size(), &datVec[0], GL_DYNAMIC_DRAW);
this is ok I use this thing very often. But what I create is interleaved array so data are like:
a1,b1,c1,a2,b2,c2,a3,b3,c3
Now I send this thing down for processing in GPU and with transform feedback I read back into buffer for example b variables. So it looks like:
bU1, bU2, bU3
I'd like to copy updated values into interleaved buffer, can this be done with some single command like glCopyBufferSubData? This one isn't suitable as it only takes offset and size not stride (probably it's something like memcpy in c++)... The result should look like:
a1, bU1, c1, a2, bU2, c2, a3, bU3, c3
If not is there better approach than these 2 mine?
map updated buffer, copy values into temp storage in app, unmap updated, map data buffer and itterating through it set new values
separate buffers on constant buffer and variable buffer. constant will stay same over time but using glCopyBufferSubData the variable one can be updated in single call..
Thanks
glMapBuffer seems like a better solution for what you are doing.
The basic idea, from what I can tell, is to map the buffer into your address space, and then update the buffer manually using your own update method (iterative loop likely).
glBindBuffer(GL_ARRAY_BUFFER, buffer_id);
void *buffer = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
if (buffer == NULL)
//Handle Error, usually means lack of virtual memory
for (int i = 1; i < bufferLen; i += stride /* 3, in this case */)
buffer[i] = newValue;
glUnmapBuffer(GL_ARRAY_BUFFER);
I would separate the dynamic part with a static one (your point 2).
If you still want to keep them interleaved into a single buffer, and you have some spare video memory, you can do the following:
Copy the original interleaved array into a backup one. This requires memory for all components rather than only dynamic ones, how it was originally.
Transform Feedback into the original interleaved, carrying the static values unchanged.

Help with memory leak (malloc)

I'v followed a tutorial to use OGL tesselaton. In one of the callbacks there is a malloc and it creates a leak every time I render a new frame.
void CALLBACK combineCallback(GLdouble coords[3], GLdouble *vertex_data[4],
GLfloat weight[4], GLdouble **dataOut)
{
GLdouble *vertex;
vertex = (GLdouble *) malloc(6 * sizeof(GLdouble));
vertex[0] = coords[0];
vertex[1] = coords[1];
vertex[2] = coords[2];
for (int i = 3; i < 6; i++)
{
vertex[i] = weight[0] * vertex_data[0][i] +
weight[1] * vertex_data[0][i] +
weight[2] * vertex_data[0][i] +
weight[3] * vertex_data[0][i];
}
*dataOut = vertex;
}
I'v tried to free(vertex) but then the polygons did not render. I also tried allocating on the heap then doing delete(vertex) but then the polygon rendered awkwardly. I'm not sure what to do.
Thanks
You should call free on whatever dataOut points to. For example, if you did this from the calling function:
combineCallback (coords, vertex_data, weight, &dataOut);
then you should call free (dataOut) after you're done using it later. If you free (vertex), that effectively means whatever dataOut points to is free to be overwritten because you assigned the address of vertex to *dataOut. In other words, don't free vertex; free whatever dataOut points to.
The tool you want to look at is called valgrind, at http://valgrind.org/. That's assuming you're running on a Linux system.
As a note to readers, the "-grind" in valgrind is not related to the English word "grind".
The "Val" as in the world "value". The "grind" is pronounced with a short 'i' -- ie. "grinned" (rhymes with "tinned") rather than "grined" (rhymes with "find").
You can't free the vertex because you are assigning the memory to the dataOut object. You need to free the dataOut object once you are done with it.
I'm not sure how to fix your memory leak situation, however, one thing I would point out, is that using "delete" (I assume you meant the c++ 'delete' operator) to clean up memory that was allocated with "malloc", is highly discouraged.
Generally speaking, you need to stick with the appropriate functions for what you used to allocate in the first place. In C++ you would use 'delete' after allocating memory with 'new', and in C you would use 'free' after allocating memory with 'malloc'.
According to the docs for GLUT_TESS_COMBINE,
The user is responsible for freeing the memory some time after gluTessEndPolygon is called.
One way is to add the memory you allocate to a linked list and free it all when you're done.
You need to free the data somewhere outside this function, after you're finished using it. Without seeing the other code, it's a bit hard to guess where that will be though. You might be better off allocating the data elsewhere, and passing a pointer to it in so this can modify that other data. This is particularly true when you're likely to generate quite a bit of data (it can avoid allocating and freeing a lot of data).