Commit b6387eb2 authored by David Reid's avatar David Reid

Add support for heap preallocation to nodes.

parent 5928aa99
...@@ -1018,6 +1018,8 @@ struct ma_node_base ...@@ -1018,6 +1018,8 @@ struct ma_node_base
ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */
}; };
MA_API ma_result ma_node_get_heap_size(const ma_node_config* pConfig, size_t* pHeapSizeInBytes);
MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);
MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);
MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode);
...@@ -4141,63 +4143,54 @@ static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusI ...@@ -4141,63 +4143,54 @@ static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusI
} }
MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) typedef struct
{ {
ma_node_base* pNodeBase = (ma_node_base*)pNode; size_t sizeInBytes;
ma_result result; size_t inputBusOffset;
ma_uint32 iInputBus; size_t outputBusOffset;
ma_uint32 iOutputBus; size_t cachedDataOffset;
size_t heapSizeInBytes = 0; ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */
size_t inputBusHeapOffsetInBytes = MA_SIZE_MAX; ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */
size_t outputBusHeapOffsetInBytes = MA_SIZE_MAX; } ma_node_heap_layout;
size_t cachedDataHeapOffsetInBytes = MA_SIZE_MAX;
if (pNodeBase == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pNodeBase);
if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {
return MA_INVALID_ARGS; /* Config is invalid. */
}
static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount)
{
ma_uint32 inputBusCount;
ma_uint32 outputBusCount;
pNodeBase->pNodeGraph = pNodeGraph; MA_ASSERT(pConfig != NULL);
pNodeBase->vtable = pConfig->vtable; MA_ASSERT(pInputBusCount != NULL);
pNodeBase->state = pConfig->initialState; MA_ASSERT(pOutputBusCount != NULL);
pNodeBase->stateTimes[ma_node_state_started] = 0;
pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */
/* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */
if (pNodeBase->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
pNodeBase->inputBusCount = pConfig->inputBusCount; inputBusCount = pConfig->inputBusCount;
} else { } else {
pNodeBase->inputBusCount = pNodeBase->vtable->inputBusCount; inputBusCount = pConfig->vtable->inputBusCount;
if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pNodeBase->vtable->inputBusCount) { if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) {
return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
} }
} }
if (pNodeBase->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
pNodeBase->outputBusCount = pConfig->outputBusCount; outputBusCount = pConfig->outputBusCount;
} else { } else {
pNodeBase->outputBusCount = pNodeBase->vtable->outputBusCount; outputBusCount = pConfig->vtable->outputBusCount;
if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pNodeBase->vtable->outputBusCount) { if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) {
return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
} }
} }
/* Bus counts must be within limits. */ /* Bus counts must be within limits. */
if (ma_node_get_input_bus_count(pNodeBase) > MA_MAX_NODE_BUS_COUNT || ma_node_get_output_bus_count(pNodeBase) > MA_MAX_NODE_BUS_COUNT) { if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
/* We must have channel counts for each bus. */ /* We must have channel counts for each bus. */
if ((pNodeBase->inputBusCount > 0 && pConfig->pInputChannels == NULL) || (pNodeBase->outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) {
return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */
} }
...@@ -4214,28 +4207,47 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p ...@@ -4214,28 +4207,47 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p
} }
/* *pInputBusCount = inputBusCount;
We may need to allocate memory on the heap. These are what we may need to put on the heap: *pOutputBusCount = outputBusCount;
+------------------------------------------------+ return MA_SUCCESS;
| Input Buses | Output Buses | Cached Audio Data | }
+------------------------------------------------+
We'll need to first count how much space we'll need to allocate. If it exceeds 0, we'll static ma_result ma_node_get_heap_layout(const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout)
allocate a buffer on the heap. {
*/ ma_result result;
heapSizeInBytes = 0; /* Always start at zero. */ ma_uint32 inputBusCount;
ma_uint32 outputBusCount;
MA_ASSERT(pHeapLayout != NULL);
MA_ZERO_OBJECT(pHeapLayout);
if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {
return MA_INVALID_ARGS;
}
result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount);
if (result != MA_SUCCESS) {
return result;
}
pHeapLayout->sizeInBytes = 0;
/* Input buses. */ /* Input buses. */
if (ma_node_get_input_bus_count(pNodeBase) > ma_countof(pNodeBase->_inputBuses)) { if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
inputBusHeapOffsetInBytes = heapSizeInBytes; pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes;
heapSizeInBytes += ma_node_get_input_bus_count(pNodeBase) * sizeof(*pNodeBase->pInputBuses); pHeapLayout->sizeInBytes += sizeof(ma_node_input_bus) * inputBusCount;
} else {
pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */
} }
/* Output buses. */ /* Output buses. */
if (ma_node_get_output_bus_count(pNodeBase) > ma_countof(pNodeBase->_outputBuses)) { if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
outputBusHeapOffsetInBytes = heapSizeInBytes; pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes;
heapSizeInBytes += ma_node_get_output_bus_count(pNodeBase) * sizeof(*pNodeBase->pOutputBuses); pHeapLayout->sizeInBytes += sizeof(ma_node_output_bus) * outputBusCount;
} else {
pHeapLayout->outputBusOffset = MA_SIZE_MAX;
} }
/* /*
...@@ -4254,55 +4266,105 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p ...@@ -4254,55 +4266,105 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p
now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile
time. It might also be worth investigating whether or not this can be configured at run time. time. It might also be worth investigating whether or not this can be configured at run time.
*/ */
if (ma_node_get_input_bus_count(pNode) == 0 && ma_node_get_output_bus_count(pNode) == 1) { if (inputBusCount == 0 && outputBusCount == 1) {
/* Fast path. No cache needed. */ /* Fast path. No cache needed. */
pHeapLayout->cachedDataOffset = MA_SIZE_MAX;
} else { } else {
/* Slow path. Cache needed. */ /* Slow path. Cache needed. */
size_t cachedDataSizeInBytes = 0; size_t cachedDataSizeInBytes = 0;
ma_uint32 iBus; ma_uint32 iBus;
cachedDataHeapOffsetInBytes = heapSizeInBytes; MA_ASSERT(MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS <= 0xFFFF); /* Clamped to 16 bits. */
pNodeBase->cachedDataCapInFramesPerBus = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; for (iBus = 0; iBus < inputBusCount; iBus += 1) {
MA_ASSERT(pNodeBase->cachedDataCapInFramesPerBus <= 0xFFFF); /* Clamped to 16 bits. */ cachedDataSizeInBytes += MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
}
for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { for (iBus = 0; iBus < outputBusCount; iBus += 1) {
cachedDataSizeInBytes += pNodeBase->cachedDataCapInFramesPerBus * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); cachedDataSizeInBytes += MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
} }
for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;
cachedDataSizeInBytes += pNodeBase->cachedDataCapInFramesPerBus * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); pHeapLayout->sizeInBytes += cachedDataSizeInBytes;
}
/*
Not technically part of the heap, but we can output the input and output bus counts so we can
avoid a redundant call to ma_node_translate_bus_counts().
*/
pHeapLayout->inputBusCount = inputBusCount;
pHeapLayout->outputBusCount = outputBusCount;
return MA_SUCCESS;
}
MA_API ma_result ma_node_get_heap_size(const ma_node_config* pConfig, size_t* pHeapSizeInBytes)
{
ma_result result;
ma_node_heap_layout heapLayout;
if (pHeapSizeInBytes == NULL) {
return MA_INVALID_ARGS;
} }
heapSizeInBytes += cachedDataSizeInBytes; *pHeapSizeInBytes = 0;
result = ma_node_get_heap_layout(pConfig, &heapLayout);
if (result != MA_SUCCESS) {
return result;
} }
/* Now that we know the size of the heap we can allocate memory and assign offsets. */ *pHeapSizeInBytes = heapLayout.sizeInBytes;
if (heapSizeInBytes > 0) {
pNodeBase->_pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); return MA_SUCCESS;
if (pNodeBase->_pHeap == NULL) { }
return MA_OUT_OF_MEMORY;
MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode)
{
ma_node_base* pNodeBase = (ma_node_base*)pNode;
ma_result result;
ma_node_heap_layout heapLayout;
ma_uint32 iInputBus;
ma_uint32 iOutputBus;
if (pNodeBase == NULL) {
return MA_INVALID_ARGS;
} }
pNodeBase->_ownsHeap = MA_TRUE; MA_ZERO_OBJECT(pNodeBase);
result = ma_node_get_heap_layout(pConfig, &heapLayout);
if (result != MA_SUCCESS) {
return result;
} }
/* Input Buses. */ pNodeBase->_pHeap = pHeap;
pNodeBase->pNodeGraph = pNodeGraph;
pNodeBase->vtable = pConfig->vtable;
pNodeBase->state = pConfig->initialState;
pNodeBase->stateTimes[ma_node_state_started] = 0;
pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */
pNodeBase->inputBusCount = heapLayout.inputBusCount;
pNodeBase->outputBusCount = heapLayout.outputBusCount;
if (heapLayout.inputBusOffset != MA_SIZE_MAX) {
pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
} else {
pNodeBase->pInputBuses = pNodeBase->_inputBuses; pNodeBase->pInputBuses = pNodeBase->_inputBuses;
if (inputBusHeapOffsetInBytes != MA_SIZE_MAX) {
pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pNodeBase->_pHeap, inputBusHeapOffsetInBytes);
} }
/* Output Buses. */ if (heapLayout.outputBusOffset != MA_SIZE_MAX) {
pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
} else {
pNodeBase->pOutputBuses = pNodeBase->_outputBuses; pNodeBase->pOutputBuses = pNodeBase->_outputBuses;
if (outputBusHeapOffsetInBytes != MA_SIZE_MAX) {
pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pNodeBase->_pHeap, outputBusHeapOffsetInBytes);
} }
/* Cached Audio Data. */ if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {
pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);
pNodeBase->cachedDataCapInFramesPerBus = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
} else {
pNodeBase->pCachedData = NULL; pNodeBase->pCachedData = NULL;
if (cachedDataHeapOffsetInBytes != MA_SIZE_MAX) {
pNodeBase->pCachedData = (float*)ma_offset_ptr(pNodeBase->_pHeap, cachedDataHeapOffsetInBytes);
} }
...@@ -4311,7 +4373,6 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p ...@@ -4311,7 +4373,6 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p
for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
if (pNodeBase->_ownsHeap) { ma_free(pNodeBase->_pHeap, pAllocationCallbacks); }
return result; return result;
} }
} }
...@@ -4319,7 +4380,6 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p ...@@ -4319,7 +4380,6 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p
for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
if (pNodeBase->_ownsHeap) { ma_free(pNodeBase->_pHeap, pAllocationCallbacks); }
return result; return result;
} }
} }
...@@ -4351,6 +4411,36 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p ...@@ -4351,6 +4411,36 @@ MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* p
return MA_SUCCESS; return MA_SUCCESS;
} }
MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)
{
ma_result result;
size_t heapSizeInBytes;
void* pHeap;
result = ma_node_get_heap_size(pConfig, &heapSizeInBytes);
if (result != MA_SUCCESS) {
return result;
}
if (heapSizeInBytes > 0) {
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
if (pHeap == NULL) {
return MA_OUT_OF_MEMORY;
}
} else {
pHeap = NULL;
}
result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);
if (result != MA_SUCCESS) {
ma_free(pHeap, pAllocationCallbacks);
return result;
}
((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;
return MA_SUCCESS;
}
MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
{ {
ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_node_base* pNodeBase = (ma_node_base*)pNode;
...@@ -12400,8 +12490,13 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo ...@@ -12400,8 +12490,13 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo
baseNodeConfig.pInputChannels = &channelsIn; baseNodeConfig.pInputChannels = &channelsIn;
baseNodeConfig.pOutputChannels = &channelsOut; baseNodeConfig.pOutputChannels = &channelsOut;
result = ma_node_get_heap_size(&baseNodeConfig, &tempHeapSize);
if (result != MA_SUCCESS) {
return result; /* Failed to retrieve the size of the heap for the base node. */
}
pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes;
pHeapLayout->sizeInBytes += 0; /* TODO: Implement pre-allocation for nodes. */ pHeapLayout->sizeInBytes += tempHeapSize;
/* Spatializer. */ /* Spatializer. */
...@@ -12487,7 +12582,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p ...@@ -12487,7 +12582,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p
baseNodeConfig.pInputChannels = &channelsIn; baseNodeConfig.pInputChannels = &channelsIn;
baseNodeConfig.pOutputChannels = &channelsOut; baseNodeConfig.pOutputChannels = &channelsOut;
result = ma_node_init(&pConfig->pEngine->nodeGraph, &baseNodeConfig, &pEngineNode->pEngine->allocationCallbacks, &pEngineNode->baseNode); /* TODO: Implement pre-allocation for nodes. */ result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
goto error0; goto error0;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment