bake files
This commit is contained in:
parent
26a1d91319
commit
53e206e7c2
@ -10,6 +10,7 @@ namespace api {
|
||||
int32_t id;
|
||||
uint32_t tick;
|
||||
};
|
||||
FrameGraphNodePtr mCurrentNode;
|
||||
uint32_t mTickStamp{1};
|
||||
uint32_t mSurfaceID{0};
|
||||
table<TextureSampler, void*> mTextureSamplerPool;
|
||||
@ -28,6 +29,7 @@ namespace api {
|
||||
TextureDesc& GetEditorSurface() { return ResolveTexture(mEditorSurfaceID); }
|
||||
#endif //
|
||||
public:
|
||||
FrameGraphNodePtr GetCurrentNode() { return mCurrentNode; };
|
||||
template<typename T>
|
||||
FrameGraphNodePtr AddRenderPass() { return AddRenderPass(&T::Setup, &T::Execute); }
|
||||
FrameGraphNodePtr AddRenderPass(const RenderPassSetupFunction& setup, const RenderPassNodeExecuteFn& executor);
|
||||
|
||||
@ -73,6 +73,8 @@ namespace api{
|
||||
bool IsOutput() { return any(flag & RenderPassNodeFlag::Output); }
|
||||
bool IsFirstInput() { return any(flag & RenderPassNodeFlag::FirstInput); }
|
||||
bool IsLastOutput() { return any(flag & RenderPassNodeFlag::LastOutput); }
|
||||
bool IsWaitTransfer() { return any(flag & RenderPassNodeFlag::WaitTransfer); }
|
||||
void SetWaitTransfer() { flag |= RenderPassNodeFlag::WaitTransfer; }
|
||||
FrameGraphEdgePtr GetInput(int i) { return inEdges[i]; };
|
||||
FrameGraphEdgePtr GetOutput(int i) { return outEdges[i]; };
|
||||
FrameGraphEdgePtr FindInput(Name name)
|
||||
|
||||
@ -32,6 +32,7 @@ namespace api {
|
||||
Output = 0x01,
|
||||
FirstInput = 0x02,
|
||||
LastOutput = 0x04,
|
||||
WaitTransfer = 0x08,
|
||||
};
|
||||
enum class BufferUsage : uint8_t {
|
||||
None = 0,
|
||||
|
||||
@ -127,6 +127,7 @@ namespace api {
|
||||
void FrameGraph::Execute(FRenderView& view)
|
||||
{
|
||||
for (auto& node : mNodes) {
|
||||
mCurrentNode = node;
|
||||
switch (node.type) {
|
||||
case RenderPassType::Render:
|
||||
ExecuteRenderPass(node.node, view);
|
||||
|
||||
@ -7,6 +7,7 @@ namespace api{
|
||||
RenderAPI* mApi;
|
||||
UIRenderSystem* mRenderSystem;
|
||||
Noesis::DeviceCaps mCaps;
|
||||
std::vector<TextureBarrier> mTextureBarriers;
|
||||
bool mStereoSupport;
|
||||
SINGLETON_IMPL(UIRenderDevice)
|
||||
public:
|
||||
|
||||
@ -84,6 +84,7 @@ namespace api {
|
||||
UITexture* uiTex = (UITexture*)texture;
|
||||
TextureUpdateArgs args{.x = x, .y = y, .width = width,.height = height, .mipLevel = level, .data = data};
|
||||
Context().UpdateTexture(uiTex->desc, args, ResourceState::TRANSFER_DST);
|
||||
mTextureBarriers.push_back(uiTex->desc.ToBarrier(ResourceState::TRANSFER_DST, ResourceState::READ_ONLY));
|
||||
}
|
||||
void UIRenderDevice::BeginOffscreenRender()
|
||||
{
|
||||
@ -94,9 +95,12 @@ namespace api {
|
||||
}
|
||||
void UIRenderDevice::EndOffscreenRender()
|
||||
{
|
||||
int b = 10000;
|
||||
for (int a = 1; a < b; a++) {
|
||||
b--;
|
||||
if (!mTextureBarriers.empty()) {
|
||||
ResourceBarrierDesc desc{};
|
||||
desc.textureBarriersCount = mTextureBarriers.size();
|
||||
desc.pTextureBarriers = mTextureBarriers.data();
|
||||
Context().ExecuteResourceBarriers(desc);
|
||||
mTextureBarriers.clear();
|
||||
}
|
||||
}
|
||||
void UIRenderDevice::BeginOnscreenRender()
|
||||
@ -156,6 +160,7 @@ namespace api {
|
||||
}
|
||||
void UIRenderDevice::DrawBatch(const Noesis::Batch& batch)
|
||||
{
|
||||
EndOffscreenRender();
|
||||
mRenderSystem->DrawBatch(batch);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include "asset/res/guid.h"
|
||||
#include "backend.h"
|
||||
#ifdef API_DEBUG
|
||||
#define gLogSemaphore(...) //zlog::info(__VA_ARGS__)
|
||||
#define gLogSemaphore(...) zlog::error(__VA_ARGS__)
|
||||
#else
|
||||
#define gLogSemaphore(...)
|
||||
#endif
|
||||
|
||||
@ -6,9 +6,12 @@ namespace vkn {
|
||||
using api::RenderPassState;
|
||||
struct VulkanContext : public api::RenderContext {
|
||||
VkFence surfaceFence;
|
||||
VkFence transferFence;
|
||||
uint8_t waitFenceNums[8]{ 2,2,2,2,2,2,2,2 };//最多不能超过8个
|
||||
VkSemaphore surfaceSemaphore;
|
||||
VkSemaphore presentSemaphore;
|
||||
VkSemaphore graphSemaphore;
|
||||
VkSemaphore transferSemaphore;
|
||||
VkCommandBuffer transferCommand;
|
||||
VkCommandBuffer surfaceCommand;
|
||||
VkCommandBuffer command;
|
||||
|
||||
@ -23,6 +23,7 @@ namespace vkn {
|
||||
class VulkanUISystem : public api::UIRenderSystem {
|
||||
private:
|
||||
Device* mDevice;
|
||||
uint8_t mWaitTransferCount;
|
||||
bool mIsLinearRendering;
|
||||
bool mHasExtendedDynamicState;
|
||||
uint32_t mLastTextureHashValue{1};
|
||||
|
||||
@ -460,6 +460,12 @@ namespace vkn {
|
||||
waitSemaphores[semaphoreCount++] = ctx.surfaceSemaphore;
|
||||
gLogSemaphore("-----wait {:#x}", (uintptr_t)ctx.surfaceSemaphore);
|
||||
}
|
||||
if (node->IsWaitTransfer()) {
|
||||
ctx.FlushCommand();
|
||||
waitDstStageMasks[semaphoreCount] = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
waitSemaphores[semaphoreCount++] = ctx.transferSemaphore;
|
||||
gLogSemaphore("-----wait {:#x}", (uintptr_t)ctx.transferSemaphore);
|
||||
}
|
||||
for (auto& it : node->dependencies) {
|
||||
RenderPassInfo* inputInfo = GetRenderPassInfo(it->name ,it->hash);
|
||||
waitDstStageMasks[semaphoreCount] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
|
||||
@ -50,7 +50,7 @@ namespace vkn {
|
||||
VkImageMemoryBarrier vkApiGetTextureTransition(VkPipelineStageFlags& mSrcStage, VkPipelineStageFlags& mDstStage, const TextureBarrier& barrier) {
|
||||
VkAccessFlags srcAccessMask, dstAccessMask;
|
||||
VkPipelineStageFlags srcStage, dstStage;
|
||||
|
||||
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
switch (barrier.mSrcState) {
|
||||
case ResourceState::UNDEFINED:
|
||||
srcAccessMask = 0;
|
||||
@ -115,12 +115,14 @@ namespace vkn {
|
||||
dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
break;
|
||||
case ResourceState::DEPTH_SAMPLER:
|
||||
dstAccessMask =
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
break;
|
||||
case ResourceState::PRESENT:
|
||||
case ResourceState::UNDEFINED:
|
||||
@ -131,7 +133,7 @@ namespace vkn {
|
||||
mSrcStage |= srcStage;
|
||||
mDstStage |= dstStage;
|
||||
VkImageSubresourceRange subresources{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.aspectMask = aspectMask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
|
||||
@ -104,14 +104,21 @@ namespace vkn {
|
||||
}
|
||||
void VulkanContext::FlushCommand()
|
||||
{
|
||||
if (waitFenceNums[frame])
|
||||
return;
|
||||
waitFenceNums[frame] = 1;
|
||||
if (transferCommand) {
|
||||
vkEndCommandBuffer(transferCommand);
|
||||
VkSubmitInfo submitInfo{};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &transferCommand;
|
||||
vkQueueSubmit(Backend::RenderWorker->GetQueue().Ptr(), 1, &submitInfo, nullptr);
|
||||
submitInfo.pSignalSemaphores = &transferSemaphore;
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
gLogSemaphore("+++++sign {:#x}", (uintptr_t)transferSemaphore);
|
||||
vkQueueSubmit(Backend::RenderWorker->GetQueue().Ptr(), 1, &submitInfo, transferFence);
|
||||
transferCommand = nullptr;
|
||||
waitFenceNums[frame] = 2;
|
||||
}
|
||||
}
|
||||
VkCommandBuffer VulkanContext::GetTransferCommand()
|
||||
@ -150,6 +157,7 @@ namespace vkn {
|
||||
{
|
||||
auto& barrier = desc.pTextureBarriers[i];
|
||||
auto desc = vkApiGetTextureTransition(srcStageMask, dstStageMask, barrier);
|
||||
zlog::info("textureBarrier::{:#x} {} {}::{}", (uintptr_t)barrier.mTexture.image,(uint8_t)renderPassState,(uint8_t)barrier.mSrcState, (uint8_t)barrier.mDstState);
|
||||
imageBarriers.push_back(desc);
|
||||
}
|
||||
if (dstStageMask == 0) {
|
||||
|
||||
@ -51,6 +51,16 @@ namespace vkn {
|
||||
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
VkAttachmentDescription stencilAttachment = {};
|
||||
stencilAttachment.format = VK_FORMAT_S8_UINT;
|
||||
stencilAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
stencilAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
stencilAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
stencilAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
stencilAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
stencilAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
stencilAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference colorAttachmentRef = {};
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
@ -63,15 +73,17 @@ namespace vkn {
|
||||
VkSubpassDependency dependency = {};
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
|
||||
VkAttachmentDescription attachmentList[2] = { colorAttachment , stencilAttachment };
|
||||
VkRenderPassCreateInfo info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
info.attachmentCount = 1;
|
||||
info.pAttachments = &colorAttachment;
|
||||
info.attachmentCount = 2;
|
||||
info.pAttachments = attachmentList;
|
||||
info.subpassCount = 1;
|
||||
info.pSubpasses = &subpass;
|
||||
info.dependencyCount = 1;
|
||||
@ -136,15 +148,26 @@ namespace vkn {
|
||||
}
|
||||
void VulkanImguiEditor::Setup(FrameGraph& graph, RenderPassBuilder& builder)
|
||||
{
|
||||
TextureDesc stencil = graph.GetRenderSurface();
|
||||
stencil.id = 0;
|
||||
stencil.format = TinyImageFormat_S8_UINT;
|
||||
stencil.image = nullptr;
|
||||
stencil.usage = TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT;
|
||||
graph.AcquireTexture(stencil);
|
||||
builder.Name(ImguiPassName)
|
||||
.Type(RenderPassNodeType::Imgui, RenderPassNodeFlag::Output)
|
||||
.Write(graph.GetRenderSurface(), ResourceState::COLOR_ATTACHMENT);
|
||||
.Write(graph.GetRenderSurface(), ResourceState::COLOR_ATTACHMENT)
|
||||
.Write(stencil, ResourceState::DEPTH_ATTACHMENT);
|
||||
if (gEngineConfig.IsRenderEditorSurface) {
|
||||
builder.Read(graph.GetSurface(), ResourceState::READ_ONLY);
|
||||
}
|
||||
}
|
||||
void VulkanImguiEditor::Execute(FrameGraph& graph, RenderPassContext& context)
|
||||
{
|
||||
auto& surface = graph.GetRenderSurface();
|
||||
context->SetViewport(0.0f, 0.0f, (float)surface.width, (float)surface.height, 0.f, 1.f);
|
||||
context->SetScissor(0, 0, surface.width, surface.height);
|
||||
|
||||
graph.GetRenderSurface().state = ResourceState::PRESENT;
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
|
||||
@ -10,13 +10,15 @@ namespace vkn {
|
||||
void VulkanUISystem::InitNoesisRender(bool linearRendering, bool stereoSupport, SampleCount sampleCount_)
|
||||
{
|
||||
mDevice = &VulkanAPI::Ptr()->GetBackend().GetDevice();
|
||||
auto info = VulkanAPI::Ptr()->GetRenderPassInfo("ImguiPass", 0);
|
||||
mRenderPass = info->pass;
|
||||
mHasExtendedDynamicState = false;
|
||||
mIsLinearRendering = linearRendering;
|
||||
VkPhysicalDeviceProperties mDeviceProperties;
|
||||
vkGetPhysicalDeviceProperties(mDevice->GetPhysical(), &mDeviceProperties);
|
||||
mMinUniformBufferOffsetAlignment = mDeviceProperties.limits.minUniformBufferOffsetAlignment;
|
||||
VkSampleCountFlagBits sampleCount = vkApiGetSmpleCountFlag(sampleCount_);
|
||||
CreateRenderPass(sampleCount);
|
||||
//CreateRenderPass(sampleCount);
|
||||
CreateLayouts();
|
||||
LoadShaders(stereoSupport);
|
||||
CreatePipelines(sampleCount);
|
||||
@ -28,6 +30,7 @@ namespace vkn {
|
||||
mCommandBuffer = ctx->command;
|
||||
mFrameNumber = ctx->frameNumber;
|
||||
mSafeFrameNumber = ctx->frameNumber - ctx->frameCount;
|
||||
mWaitTransferCount = 0;
|
||||
}
|
||||
void VulkanUISystem::DrawBatch(const Noesis::Batch& batch)
|
||||
{
|
||||
@ -56,6 +59,10 @@ namespace vkn {
|
||||
{
|
||||
vkCmdDrawIndexed(mCommandBuffer, batch.numIndices, 1, firstIndex, 0, 0);
|
||||
}
|
||||
if (mWaitTransferCount == 1) {
|
||||
mWaitTransferCount += 100;
|
||||
VulkanAPI::Ptr()->graph.GetCurrentNode()->SetWaitTransfer();
|
||||
}
|
||||
}
|
||||
void VulkanUISystem::SetBuffers(const Noesis::Batch& batch)
|
||||
{
|
||||
@ -192,6 +199,9 @@ namespace vkn {
|
||||
}
|
||||
void VulkanUISystem::FillImageInfo(UITexture* texture, uint8_t sampler, VkDescriptorSet set, BaseVector<VkDescriptorImageInfo>& images, BaseVector<VkWriteDescriptorSet>& writes, uint32_t& binding)
|
||||
{
|
||||
if (texture->desc.state != ResourceState::READ_ONLY) {
|
||||
mWaitTransferCount++;
|
||||
}
|
||||
VkDescriptorImageInfo& info = images.EmplaceBack();
|
||||
info.sampler = mSamplers[sampler];
|
||||
info.imageView = (VkImageView)texture->imageView;
|
||||
|
||||
@ -59,15 +59,16 @@ namespace vkn {
|
||||
zlog::error("Failed to create swap chain.");
|
||||
}
|
||||
mFrames = args.framesInFlight;
|
||||
uint32_t flightFrames = args.framesInFlight * 2;
|
||||
pmr::vector<VkImage> swapchain_images{ FramePool() };
|
||||
uint32_t imageCount = 0;
|
||||
vkGetSwapchainImagesKHR(device.Ptr(), mPtr, &imageCount, nullptr);
|
||||
swapchain_images.resize(imageCount);
|
||||
vkGetSwapchainImagesKHR(device.Ptr(), mPtr, &imageCount, swapchain_images.data());
|
||||
mFences.reserve(mFrames);
|
||||
mFences.reserve(flightFrames);
|
||||
mSurfaces.reserve(imageCount);
|
||||
mCommands.reserve(mFrames + mFrames);
|
||||
mSemaphores.reserve(mFrames + mFrames);
|
||||
mCommands.reserve(flightFrames);
|
||||
mSemaphores.reserve(flightFrames + mFrames);
|
||||
TextureDesc desc{};
|
||||
desc.width = args.width;
|
||||
desc.height = args.height;
|
||||
@ -83,9 +84,11 @@ namespace vkn {
|
||||
mSurfaces.push_back(desc);
|
||||
mSemaphores.push_back(mDevice.CreateSemaphore());
|
||||
mSemaphores.push_back(mDevice.CreateSemaphore());
|
||||
mSemaphores.push_back(mDevice.CreateSemaphore());
|
||||
mCommands.push_back(Backend::RenderWorker->AllocateBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY));
|
||||
mCommands.push_back(Backend::RenderWorker->AllocateBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY));
|
||||
mFences.push_back(mDevice.CreateFence(VK_FENCE_CREATE_SIGNALED_BIT));
|
||||
mFences.push_back(mDevice.CreateFence(VK_FENCE_CREATE_SIGNALED_BIT));
|
||||
}
|
||||
for (int i = mFrames; i < imageCount; i++) {
|
||||
desc.image = swapchain_images[i];
|
||||
@ -95,18 +98,23 @@ namespace vkn {
|
||||
void VulkanSwapchain::Aquire(VulkanContext& ctx)
|
||||
{
|
||||
VkFence surfaceFence = mFences[ctx.frame];
|
||||
VkFence transferFence = mFences[ctx.frame];
|
||||
VkSemaphore surfaceSemaphore = mSemaphores[ctx.frame];
|
||||
ctx.surfaceCommand = mCommands[ctx.frame];
|
||||
ctx.surfaceFence = surfaceFence;
|
||||
ctx.surfaceSemaphore = surfaceSemaphore;
|
||||
ctx.presentSemaphore = mSemaphores[ctx.frame + mFrames];
|
||||
if (vkWaitForFences(mDevice.Ptr(), 1, &surfaceFence, VK_TRUE, UINT64_MAX) != VK_SUCCESS)
|
||||
ctx.transferSemaphore = mSemaphores[ctx.frame + mFrames + mFrames];
|
||||
VkFence waitFence[2] = { surfaceFence, transferFence };
|
||||
uint32_t fenceNum = ctx.waitFenceNums[ctx.frame];
|
||||
if (vkWaitForFences(mDevice.Ptr(), fenceNum, waitFence, VK_TRUE, UINT64_MAX) != VK_SUCCESS)
|
||||
throw std::runtime_error("Failed to wait for fence!");
|
||||
vkAcquireNextImageKHR(mDevice.Ptr(), mPtr, UINT64_MAX, surfaceSemaphore, VK_NULL_HANDLE, &ctx.presentFrame);
|
||||
vkResetFences(mDevice.Ptr(), 1, &surfaceFence);
|
||||
vkResetFences(mDevice.Ptr(), fenceNum, waitFence);
|
||||
ctx.surface = mSurfaces[ctx.presentFrame];
|
||||
ctx.graphSemaphore = nullptr;
|
||||
gLogSemaphore("aquire------------:: {:#x}", (uintptr_t)surfaceSemaphore);
|
||||
ctx.waitFenceNums[ctx.frame] = 0;
|
||||
gLogSemaphore("aquire------------:: {:#x} transfer::{:#x}", (uintptr_t)surfaceSemaphore, (uintptr_t)ctx.transferSemaphore);
|
||||
}
|
||||
void VulkanSwapchain::Present(VulkanContext& ctx)
|
||||
{
|
||||
@ -119,6 +127,7 @@ namespace vkn {
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pImageIndices = &ctx.presentFrame;
|
||||
presentInfo.pResults = VK_NULL_HANDLE;
|
||||
zlog::flush();
|
||||
Backend::RenderWorker->Present(presentInfo);
|
||||
ctx.frame = (ctx.frame + 1) % mFrames;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user