#include "ScriptableRenderer.h" #include "RenderTexture.h" #include "BufferFactory.h" #include "ComputeShader.h" #include "RenderPass/CopyPass.h" #include "Core/ThreadPool.h" #include "Core/ThreadSyncManager.h" #include "Core/Logger.h" #include namespace sh::render { std::vector ScriptableRenderer::swapChainStates{}; ScriptableRenderer::ScriptableRenderer(IRenderContext& ctx) : ctx(ctx) { cpyPass = std::make_unique(); } ScriptableRenderer::~ScriptableRenderer() = default; SH_RENDER_API auto ScriptableRenderer::AddRenderPass(const core::Name& passName, RenderQueue renderQueue) -> ScriptableRenderPass& { allPasses.push_back(std::make_unique(passName, renderQueue)); return *allPasses.back(); } SH_RENDER_API auto ScriptableRenderer::ReadRenderTextureAsync(RenderTexture& rt, int x, int y) -> std::future> { SyncDirty(); SyncData data{}; data.src = &rt; data.x = x; data.y = y; return syncDatas.emplace_back(std::move(data)).promise.get_future(); } SH_RENDER_API void ScriptableRenderer::Dispatch(const ComputeShader& computeShader, uint32_t x, uint32_t y, uint32_t z) { CommandBuffer* const cmdPtr = ctx.AllocateCommandBuffer(true); if (cmdPtr == nullptr) { SH_ERROR("Failed to allocate command buffer!"); return; } cmdPtr->Begin(true); cmdPtr->Dispatch(computeShader, x, y, z); cmdPtr->End(); ctx.SubmitCommand(*cmdPtr); ctx.DeallocateCommandBuffer(*cmdPtr); } SH_RENDER_API auto ScriptableRenderer::HasPass(const core::Name& passName) const -> bool { for (const std::unique_ptr& pass : allPasses) { if (pass->passName == passName) return true; } return false; } SH_RENDER_API void ScriptableRenderer::SyncDirty() { if (bSyncDirty) return; core::ThreadSyncManager::PushSyncable(*this); bSyncDirty = true; } SH_RENDER_API void ScriptableRenderer::Sync() { for (auto& data : syncDatas) { BufferFactory::CreateInfo ci{}; ci.size = sizeof(uint8_t) * 4; std::unique_ptr buffer = BufferFactory::Create(ctx, ci); IRenderThrMethod::EnqueCopyImagePixelToBuffer(*cpyPass, *data.src, data.x, data.y, *buffer.get()); pendingReadbacks.push_back(PendingReadback{ std::move(buffer), std::move(data.promise) }); } syncDatas.clear(); bSyncDirty = false; } SH_RENDER_API void ScriptableRenderer::Setup(const RenderData& data) { for (const std::unique_ptr& pass : allPasses) activePasses.push_back(pass.get()); } SH_RENDER_API void ScriptableRenderer::Execute(const RenderData& data) { renderCallCount = 0; std::stable_sort(activePasses.begin(), activePasses.end(), [&](const ScriptableRenderPass* left, const ScriptableRenderPass* right) { return left->renderQueue < right->renderQueue; } ); for (ScriptableRenderPass* pass : activePasses) { IRenderThrMethod::Configure(*pass, data); renderCallCount += IRenderThrMethod::GetRenderCallCount(*pass); } // 각 패스에서 사용되는 렌더 텍스쳐를 추적하여 커맨드를 기록하기전에 배리어를 걸어줘야 함. std::vector> barriers(activePasses.size()); for (int i = 0; i < activePasses.size(); ++i) { barriers[i] = BuildBarrierInfo(*activePasses[i], data.GetFrameIdx()); } std::vector>> futurePasses; futurePasses.reserve(activePasses.size()); int idx = 0; for (ScriptableRenderPass* pass : activePasses) { futurePasses.push_back(core::ThreadPool::GetInstance()->AddTask( [idx, pass, &ctx = ctx, &data, &barriers]() -> std::pair { CommandBuffer* cmd = ctx.AllocateCommandBuffer(false); cmd->Begin(true); cmd->EmitBarrier(barriers[idx]); IRenderThrMethod::Record(*pass, *cmd, ctx, data); cmd->End(); return { pass, cmd }; } )); ++idx; } // 모든 패스가 기록이 될 때 까지 대기 for (auto& futurePass : futurePasses) { auto [pass, cmd] = futurePass.get(); recordedCmds.push_back({ *pass, *cmd }); } activePasses.clear(); } SH_RENDER_API void ScriptableRenderer::ExecuteTransfer(uint32_t imgIdx) { RenderData data{}; IRenderThrMethod::SetFrameIndex(data, imgIdx); IRenderThrMethod::SetDrawablesPtr(data, nullptr); IRenderThrMethod::Configure(*cpyPass, data); std::vector preBarriers = BuildBarrierInfo(*cpyPass, imgIdx); std::vector postBarriers; if (swapChainStates.size() > imgIdx) { postBarriers = { BarrierInfo{imgIdx, swapChainStates[imgIdx], ResourceUsage::Present} }; swapChainStates[imgIdx] = ResourceUsage::Present; } else { swapChainStates.resize(imgIdx + 1, ResourceUsage::Undefined); postBarriers = { BarrierInfo{imgIdx, ResourceUsage::Undefined, ResourceUsage::Present} }; swapChainStates[imgIdx] = ResourceUsage::Present; } CommandBuffer* cmd = ctx.AllocateCommandBuffer(false); cmd->Begin(true); cmd->EmitBarrier(preBarriers); IRenderThrMethod::Record(*cpyPass, *cmd, ctx, data); cmd->EmitBarrier(postBarriers); cmd->End(); recordedCmds.push_back({ *cpyPass, *cmd}); } SH_RENDER_API void ScriptableRenderer::CallReadbacks() { for (auto& p : pendingReadbacks) p.promise.set_value(std::move(p.buffer)); pendingReadbacks.clear(); } SH_RENDER_API void ScriptableRenderer::EnqueRenderPass(ScriptableRenderPass& pass) { activePasses.push_back(&pass); } SH_RENDER_API void ScriptableRenderer::ResetSubmittedCommands(IRenderContext& ctx) { for (RecordedCommand& recordedCmd : recordedCmds) ctx.DeallocateCommandBuffer(recordedCmd.cmd); recordedCmds.clear(); } SH_RENDER_API void ScriptableRenderer::ResetSwapChainStates() { for (auto& state : swapChainStates) state = ResourceUsage::Undefined; } auto ScriptableRenderer::BuildBarrierInfo(ScriptableRenderPass& pass, uint32_t imgIdx) -> std::vector { std::vector barriers{}; barriers.reserve(pass.GetRenderTextures().size()); for (auto& [rt, usage] : pass.GetRenderTextures()) { BarrierInfo& barrierInfo = barriers.emplace_back(); if (rt == nullptr) // 스왑체인인 경우 { barrierInfo.target = imgIdx; if (swapChainStates.size() <= imgIdx) swapChainStates.resize(imgIdx + 1, ResourceUsage::Undefined); barrierInfo.lastUsage = swapChainStates[imgIdx]; swapChainStates[imgIdx] = usage; } else { barrierInfo.target = rt; barrierInfo.lastUsage = rt->GetUsage(); IRenderThrMethod::ChangeUsage(const_cast(*rt), usage); } barrierInfo.curUsage = usage; } return barriers; } }//namespace