129 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
#include "iwa/util/render_loop.hpp"
 | 
						|
 | 
						|
#include <mijin/async/task_mutex.hpp>
 | 
						|
#include "iwa/device.hpp"
 | 
						|
#include "iwa/instance.hpp"
 | 
						|
 | 
						|
namespace iwa
 | 
						|
{
 | 
						|
namespace
 | 
						|
{
 | 
						|
// BIG BIG TODO: This is a dumb workaround for sharing images (e.g. the UI image) between multiple renderers.
 | 
						|
//               The reason is that the layout change mechanism doesn't work if multiple command buffers (that are executed
 | 
						|
//               sequentially) are recorded in parallel.
 | 
						|
//               A possible fix could be to move the state tracking mechanism to the renderer and generate the barriers
 | 
						|
//               before submitting.
 | 
						|
mijin::TaskMutex gRenderMutex;
 | 
						|
}
 | 
						|
 | 
						|
RenderLoop::RenderLoop(ObjectPtr<Device> owner, RenderLoopCreationArgs args)
 | 
						|
        : super_t(std::move(owner)), mAdvanceDeleteQueue(args.flags.advanceDeleteQueue)
 | 
						|
{
 | 
						|
    mAlternating.resize(args.parallelFrames);
 | 
						|
 | 
						|
    ObjectPtr<CommandPool> commandPool = std::move(args.commandPool);
 | 
						|
    if (!commandPool)
 | 
						|
    {
 | 
						|
        commandPool = getOwner()->createChild<CommandPool>(CommandPoolCreationArgs{
 | 
						|
            .flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
 | 
						|
            .queueFamilyIndex = getOwner()->getDeviceInfo().graphicsQueueFamily
 | 
						|
        });
 | 
						|
    }
 | 
						|
    for (Alternating& alt : mAlternating)
 | 
						|
    {
 | 
						|
         alt.commandBuffer = commandPool->allocateCommandBuffer();
 | 
						|
         alt.renderDoneFence = getOwner()->createChild<Fence>(FenceCreationArgs{.flags = vk::FenceCreateFlagBits::eSignaled});
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RenderLoop::start() noexcept
 | 
						|
{
 | 
						|
    addTask(c_renderLoop());
 | 
						|
}
 | 
						|
 | 
						|
mijin::Task<> RenderLoop::c_init()
 | 
						|
{
 | 
						|
    co_return;
 | 
						|
}
 | 
						|
 | 
						|
mijin::SimpleTaskLoop& RenderLoop::getTaskLoop() const noexcept
 | 
						|
{
 | 
						|
    return getOwner()->getOwner()->getMainTaskLoop();
 | 
						|
}
 | 
						|
 | 
						|
mijin::Task<> RenderLoop::c_renderLoop()
 | 
						|
{
 | 
						|
    co_await c_init();
 | 
						|
 | 
						|
    while (!getOwner()->getOwner()->isQuitRequested())
 | 
						|
    {
 | 
						|
        Alternating& alt = mAlternating.at(mFrameIdx);
 | 
						|
 | 
						|
        // wait for the command buffer to be ready
 | 
						|
        co_await alt.renderDoneFence->c_wait();
 | 
						|
 | 
						|
        // reset the fence
 | 
						|
        alt.renderDoneFence->reset();
 | 
						|
 | 
						|
        vk::CommandBuffer cmdBuffer = alt.commandBuffer->getVkHandle();
 | 
						|
        cmdBuffer.begin(vk::CommandBufferBeginInfo{
 | 
						|
            .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
 | 
						|
        });
 | 
						|
 | 
						|
        // record the commands
 | 
						|
        RenderLoopRenderArgs renderArgs = {
 | 
						|
            .cmdBuffer = *alt.commandBuffer,
 | 
						|
            .frameIdx = mFrameIdx
 | 
						|
        };
 | 
						|
        { // gRenderMutex lock
 | 
						|
            const mijin::TaskMutexLock lock = co_await gRenderMutex.c_lock();
 | 
						|
            co_await c_render(renderArgs);
 | 
						|
 | 
						|
            std::vector<vk::Semaphore> waitSemaphores;
 | 
						|
            std::vector<vk::Semaphore> signalSemaphores;
 | 
						|
 | 
						|
            ImageReferenceFinalizeArgs finalizeArgs{
 | 
						|
                .cmdBuffer = *alt.commandBuffer,
 | 
						|
                .waitSemaphores = waitSemaphores,
 | 
						|
                .signalSemaphores = signalSemaphores
 | 
						|
            };
 | 
						|
            for (const ObjectPtr<ImageReference>& imageRef: renderArgs.usedImageReferences)
 | 
						|
            {
 | 
						|
                imageRef->finalize(finalizeArgs);
 | 
						|
            }
 | 
						|
 | 
						|
            cmdBuffer.end();
 | 
						|
 | 
						|
            // submit them
 | 
						|
            const vk::PipelineStageFlags waitStage = vk::PipelineStageFlagBits::eFragmentShader;
 | 
						|
            getOwner()->getGraphicsQueue().submit(vk::SubmitInfo{
 | 
						|
                .waitSemaphoreCount = static_cast<std::uint32_t>(waitSemaphores.size()),
 | 
						|
                .pWaitSemaphores = waitSemaphores.data(),
 | 
						|
                .pWaitDstStageMask = &waitStage,
 | 
						|
                .commandBufferCount = 1,
 | 
						|
                .pCommandBuffers = &cmdBuffer,
 | 
						|
                .signalSemaphoreCount = static_cast<std::uint32_t>(signalSemaphores.size()),
 | 
						|
                .pSignalSemaphores = signalSemaphores.data()
 | 
						|
            }, *alt.renderDoneFence);
 | 
						|
        } // gRenderMutex lock
 | 
						|
 | 
						|
        // finally present
 | 
						|
        for (const ObjectPtr<ImageReference>& imageRef : renderArgs.usedImageReferences)
 | 
						|
        {
 | 
						|
            co_await imageRef->c_present();
 | 
						|
        }
 | 
						|
 | 
						|
        // tick deleters
 | 
						|
        // TODO: what if there are multiple render loops?
 | 
						|
        if (mAdvanceDeleteQueue)
 | 
						|
        {
 | 
						|
            getOwner()->getOwner()->tickDeleteQueue();
 | 
						|
        }
 | 
						|
 | 
						|
        mFrameIdx = (mFrameIdx + 1) % mAlternating.size();
 | 
						|
    }
 | 
						|
    co_return;
 | 
						|
}
 | 
						|
} // namespace iwa
 |