Share via


Administración de recursos basada en límites

Muestra cómo administrar el período de vida de los datos de recursos mediante el seguimiento del progreso de la GPU a través de barreras. La memoria se puede volver a usar eficazmente con barreras que administran cuidadosamente la disponibilidad del espacio libre en la memoria, como en una implementación de búfer en anillo para un montón de carga.

Escenario de búfer de anillo

A continuación se muestra un ejemplo en el que una aplicación experimenta una demanda poco frecuente de memoria de montón de carga.

Un búfer de anillo es una manera de administrar un montón de carga. El búfer de anillo contiene los datos necesarios para los siguientes fotogramas. La aplicación mantiene un puntero de entrada de datos actual y una cola de desplazamiento de fotogramas para registrar cada fotograma y el desplazamiento inicial de los datos de recursos para ese fotograma.

Una aplicación crea un búfer de anillo basado en un búfer para cargar datos en la GPU para cada fotograma. Actualmente se ha representado el fotograma 2, el búfer de anillo se ajusta alrededor de los datos del fotograma 4, todos los datos necesarios para el fotograma 5 están presentes y es necesario asignar un búfer de constantes grande para el fotograma 6.

Figura 1 : la aplicación intenta subasignar para el búfer de constantes, pero encuentra memoria insuficiente libre.

memoria insuficiente libre en este búfer de anillo

Figura 2 : a través del sondeo de barrera, la aplicación detecta que se ha representado el fotograma 3, la cola de desplazamiento de fotogramas se actualiza y el estado actual del búfer de anillo sigue; sin embargo, la memoria libre todavía no es lo suficientemente grande como para acomodar el búfer de constantes.

memoria insuficiente después de que se haya representado el fotograma 3

Figura 3 : dada la situación, la CPU se bloquea (a través de la espera de barrera) hasta que se haya representado el fotograma 4, que libera la memoria subasignada para el fotograma 4.

el fotograma de representación 4 libera más del búfer de anillo

Figura 4 : ahora la memoria libre es lo suficientemente grande para el búfer de constantes y la subasignación se realiza correctamente; La aplicación copia los datos del búfer de constantes grandes en la memoria usada anteriormente por los datos de recursos para los fotogramas 3 y 4. El puntero de entrada actual se actualiza finalmente.

ahora hay espacio del marco 6 en el búfer de anillo

Si una aplicación implementa un búfer de anillo, el búfer de anillo debe ser lo suficientemente grande como para hacer frente al escenario de peor caso de los tamaños de los datos de recursos.

Muestra de búfer de anillo

En el código de ejemplo siguiente se muestra cómo se puede administrar un búfer de anillo, prestando atención a la rutina de subasignación que controla el sondeo de barreras y la espera. Por motivos de simplicidad, el ejemplo usa NOT_SUFFICIENT_MEMORY para ocultar los detalles de "memoria libre insuficiente encontrada en el montón", ya que esa lógica (basada en m_pDataCur y desplazamientos dentro de FrameOffsetQueue) no está estrechamente relacionada con montones o vallas. La muestra se simplifica para sacrificar la velocidad de fotogramas en lugar de la utilización de memoria.

Tenga en cuenta que se espera que la compatibilidad con el búfer en anillo sea un escenario popular; sin embargo, el diseño del montón no impide otro uso, como la parametrización de la lista de comandos y la reutilización.

struct FrameResourceOffset
{
    UINT frameIndex;
    UINT8* pResourceOffset;
};
std::queue<FrameResourceOffset> frameOffsetQueue;

void DrawFrame()
{
    float vertices[] = ...;
    UINT verticesOffset = 0;
    ThrowIfFailed(
        SetDataToUploadHeap(
            vertices, sizeof(float), sizeof(vertices) / sizeof(float), 
            4, // Max alignment requirement for vertex data is 4 bytes.
            verticesOffset
            ));

    float constants[] = ...;
    UINT constantsOffset = 0;
    ThrowIfFailed(
        SetDataToUploadHeap(
            constants, sizeof(float), sizeof(constants) / sizeof(float), 
            D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT,
            constantsOffset
            ));

    // Create vertex buffer views for the new binding model. 
    // Create constant buffer views for the new binding model. 
    // ...

    commandQueue->Execute(commandList);
    commandQueue->AdvanceFence();
}

HRESULT SuballocateFromHeap(SIZE_T uSize, UINT uAlign)
{
    if (NOT_SUFFICIENT_MEMORY(uSize, uAlign))
    {
        // Free up resources for frames processed by GPU; see Figure 2.
        UINT lastCompletedFrame = commandQueue->GetLastCompletedFence();
        FreeUpMemoryUntilFrame( lastCompletedFrame );

        while ( NOT_SUFFICIENT_MEMORY(uSize, uAlign)
            && !frameOffsetQueue.empty() )
        {
            // Block until a new frame is processed by GPU, then free up more memory; see Figure 3.
            UINT nextGPUFrame = frameOffsetQueue.front().frameIndex;
            commandQueue->SetEventOnFenceCompletion(nextGPUFrame, hEvent);
            WaitForSingleObject(hEvent, INFINITE);
            FreeUpMemoryUntilFrame( nextGPUFrame );
        }
    }

    if (NOT_SUFFICIENT_MEMORY(uSize, uAlign))
    {
        // Apps need to create a new Heap that is large enough for this resource.
        return E_HEAPNOTLARGEENOUGH;
    }
    else
    {
        // Update current data pointer for the new resource.
        m_pDataCur = reinterpret_cast<UINT8*>(
            Align(reinterpret_cast<SIZE_T>(m_pHDataCur), uAlign)
            );

        // Update frame offset queue if this is the first resource for a new frame; see Figure 4.
        UINT currentFrame = commandQueue->GetCurrentFence();
        if ( frameOffsetQueue.empty()
            || frameOffsetQueue.back().frameIndex < currentFrame )
        {
            FrameResourceOffset offset = {currentFrame, m_pDataCur};
            frameOffsetQueue.push(offset);
        }

        return S_OK;
    }
}

void FreeUpMemoryUntilFrame(UINT lastCompletedFrame)
{
    while ( !frameOffsetQueue.empty() 
        && frameOffsetQueue.first().frameIndex <= lastCompletedFrame )
    {
        frameOffsetQueue.pop();
    }
}

ID3D12Fence

Subasignación en los búferes