Path: blob/master/Common/GPU/Vulkan/VulkanRenderManager.h
5971 views
#pragma once12// VulkanRenderManager takes the role that a GL driver does of sequencing and optimizing render passes.3// Only draws and binds are handled here, resource creation and allocations are handled as normal -4// that's the nice thing with Vulkan.56#include <atomic>7#include <condition_variable>8#include <cstdint>9#include <mutex>10#include <thread>11#include <queue>1213#include "Common/Math/Statistics.h"14#include "Common/Thread/Promise.h"15#include "Common/System/Display.h"16#include "Common/GPU/Vulkan/VulkanContext.h"17#include "Common/GPU/Vulkan/VulkanBarrier.h"18#include "Common/Data/Convert/SmallDataConvert.h"19#include "Common/Data/Collections/FastVec.h"20#include "Common/Math/math_util.h"21#include "Common/GPU/DataFormat.h"22#include "Common/GPU/MiscTypes.h"23#include "Common/GPU/Vulkan/VulkanQueueRunner.h"24#include "Common/GPU/Vulkan/VulkanFramebuffer.h"25#include "Common/GPU/Vulkan/VulkanDescSet.h"26#include "Common/GPU/thin3d.h"2728// Forward declaration29VK_DEFINE_HANDLE(VmaAllocation);3031struct BoundingRect {32int x1;33int y1;34int x2;35int y2;3637BoundingRect() {38Reset();39}4041void Reset() {42x1 = 65535;43y1 = 65535;44x2 = -65535;45y2 = -65535;46}4748bool Empty() const {49return x2 < 0;50}5152void SetRect(int x, int y, int width, int height) {53x1 = x;54y1 = y;55x2 = width;56y2 = height;57}5859void Apply(const VkRect2D &rect) {60if (rect.offset.x < x1) x1 = rect.offset.x;61if (rect.offset.y < y1) y1 = rect.offset.y;62int rect_x2 = rect.offset.x + rect.extent.width;63int rect_y2 = rect.offset.y + rect.extent.height;64if (rect_x2 > x2) x2 = rect_x2;65if (rect_y2 > y2) y2 = rect_y2;66}6768VkRect2D ToVkRect2D() const {69VkRect2D rect;70rect.offset.x = x1;71rect.offset.y = y1;72rect.extent.width = x2 - x1;73rect.extent.height = y2 - y1;74return rect;75}76};7778// All the data needed to create a graphics pipeline.79// TODO: Compress this down greatly.80class VKRGraphicsPipelineDesc : public Draw::RefCountedObject {81public:82VKRGraphicsPipelineDesc() : Draw::RefCountedObject("VKRGraphicsPipelineDesc") {}8384VkPipelineCache pipelineCache = VK_NULL_HANDLE;85VkPipelineColorBlendStateCreateInfo cbs{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };86VkPipelineColorBlendAttachmentState blend0{};87VkPipelineDepthStencilStateCreateInfo dss{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };88VkDynamicState dynamicStates[6]{};89VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };90VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };91VkPipelineRasterizationProvokingVertexStateCreateInfoEXT rs_provoking{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT };9293// Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish.94Promise<VkShaderModule> *vertexShader = nullptr;95Promise<VkShaderModule> *fragmentShader = nullptr;96Promise<VkShaderModule> *geometryShader = nullptr;9798// These are for pipeline creation failure logging.99// TODO: Store pointers to the string instead? Feels iffy but will probably work.100std::string vertexShaderSource;101std::string fragmentShaderSource;102std::string geometryShaderSource;103104VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;105VkVertexInputAttributeDescription attrs[8]{};106VkVertexInputBindingDescription ibd{};107VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };108VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };109110VKRPipelineLayout *pipelineLayout = nullptr;111112// Does not include the render pass type, it's passed in separately since the113// desc is persistent.114RPKey rpKey{};115};116117// Wrapped pipeline. Does own desc!118struct VKRGraphicsPipeline {119VKRGraphicsPipeline(PipelineFlags flags, const char *tag) : flags_(flags), tag_(tag) {}120~VKRGraphicsPipeline();121122bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType, VkSampleCountFlagBits sampleCount, double scheduleTime, int countToCompile);123void DestroyVariants(VulkanContext *vulkan, bool msaaOnly);124125// This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this.126void QueueForDeletion(VulkanContext *vulkan);127128// This blocks until any background compiles are finished.129// Used during game shutdown before we clear out shaders that these compiles depend on.130void BlockUntilCompiled();131132u32 GetVariantsBitmask() const;133134void LogCreationFailure() const;135136VKRGraphicsPipelineDesc *desc = nullptr;137Promise<VkPipeline> *pipeline[(size_t)RenderPassType::TYPE_COUNT]{};138std::mutex mutex_; // protects the pipeline array139140VkSampleCountFlagBits SampleCount() const { return sampleCount_; }141142const char *Tag() const { return tag_.c_str(); }143private:144void DestroyVariantsInstant(VkDevice device);145146std::string tag_;147PipelineFlags flags_;148VkSampleCountFlagBits sampleCount_ = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;149};150151struct CompileQueueEntry {152CompileQueueEntry(VKRGraphicsPipeline *p, VkRenderPass _compatibleRenderPass, RenderPassType _renderPassType, VkSampleCountFlagBits _sampleCount)153: type(Type::GRAPHICS), graphics(p), compatibleRenderPass(_compatibleRenderPass), renderPassType(_renderPassType), sampleCount(_sampleCount) {}154enum class Type {155GRAPHICS,156};157Type type;158VkRenderPass compatibleRenderPass;159RenderPassType renderPassType;160VKRGraphicsPipeline* graphics = nullptr;161VkSampleCountFlagBits sampleCount;162};163164// Pending descriptor sets.165// TODO: Sort these by VKRPipelineLayout to avoid storing it for each element.166struct PendingDescSet {167int offset; // This is in whole PackedDescriptors, not bytes. probably enough with a u16.168u8 count;169VkDescriptorSet set;170};171172struct PackedDescriptor {173union {174struct {175VkImageView view;176VkSampler sampler;177} image;178struct {179VkBuffer buffer;180uint32_t range;181uint32_t offset;182} buffer;183#if false184struct {185VkBuffer buffer;186uint64_t range; // write range and a zero offset in one operation with this.187} buffer_zero_offset;188#endif189};190};191192static_assert(sizeof(PackedDescriptor) == 16, "PackedDescriptor should be exactly 16 bytes");193static_assert(sizeof(PackedDescriptor::image) == 16, "PackedDescriptor should be exactly 16 bytes");194static_assert(sizeof(PackedDescriptor::buffer) == 16, "PackedDescriptor should be exactly 16 bytes");195196// Note that we only support a single descriptor set due to compatibility with some ancient devices.197// We should probably eventually give that up eventually.198struct VKRPipelineLayout {199~VKRPipelineLayout();200201enum { MAX_DESC_SET_BINDINGS = 10 };202BindingType bindingTypes[MAX_DESC_SET_BINDINGS];203204uint32_t bindingTypesCount = 0;205VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;206VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; // only support 1 for now.207int pushConstSize = 0;208const char *tag = nullptr;209210struct FrameData {211FrameData() : pool("N/A", true) {}212213VulkanDescSetPool pool;214FastVec<PackedDescriptor> descData_;215FastVec<PendingDescSet> descSets_;216// TODO: We should be able to get away with a single descData_/descSets_ and then send it along,217// but it's easier to just segregate by frame id.218int flushedDescriptors_ = 0;219};220221FrameData frameData[VulkanContext::MAX_INFLIGHT_FRAMES];222223void FlushDescSets(VulkanContext *vulkan, int frame, QueueProfileContext *profile);224void SetTag(const char *tag) {225this->tag = tag;226for (int i = 0; i < ARRAY_SIZE(frameData); i++) {227frameData[i].pool.SetTag(tag);228}229}230};231232class VulkanRenderManager {233public:234VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);235~VulkanRenderManager();236237// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.238void BeginFrame(bool enableProfiling, bool enableLogProfiler);239// These can run on a different thread!240void Finish();241void Present();242void CheckNothingPending();243244void SetInvalidationCallback(InvalidationCallback callback) {245invalidationCallback_ = callback;246}247248// This starts a new step containing a render pass (unless it can be trivially merged into the previous one, which is pretty common).249//250// After a "CopyFramebuffer" or the other functions that start "steps", you need to call this beforce251// making any new render state changes or draw calls.252//253// The following dynamic state needs to be reset by the caller after calling this (and will thus not safely carry over from254// the previous one):255// * Viewport/Scissor256// * Stencil parameters257// * Blend color258//259// (Most other state is directly decided by your choice of pipeline and descriptor set, so not handled here).260//261// It can be useful to use GetCurrentStepId() to figure out when you need to send all this state again, if you're262// not keeping track of your calls to this function on your own.263void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag);264265// Returns an ImageView corresponding to a framebuffer. Is called BindFramebufferAsTexture to maintain a similar interface266// as the other backends, even though there's no actual binding happening here.267// For layer, we use the same convention as thin3d, where layer = -1 means all layers together. For texturing, that means that you268// get an array texture view.269VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int layer);270271bool CopyFramebufferToMemory(VKRFramebuffer *src, VkImageAspectFlags aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, Draw::ReadbackMode mode, const char *tag);272void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);273274void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag);275void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag);276277// Deferred creation, like in GL. Unlike GL though, the purpose is to allow background creation and avoiding278// stalling the emulation thread as much as possible.279// We delay creating pipelines until the end of the current render pass, so we can create the right type immediately.280// Unless a variantBitmask is passed in, in which case we can just go ahead.281// WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it.282VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, bool cacheLoad, const char *tag);283284VKRPipelineLayout *CreatePipelineLayout(BindingType *bindingTypes, size_t bindingCount, bool geoShadersEnabled, const char *tag);285void DestroyPipelineLayout(VKRPipelineLayout *pipelineLayout);286287void ReportBadStateForDraw();288289int WaitForPipelines();290291void NudgeCompilerThread() {292compileQueueMutex_.lock();293compileCond_.notify_one();294compileQueueMutex_.unlock();295}296297void AssertInRenderPass() const {298_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);299}300301// This is the first call in a draw operation. Instead of asserting like we used to, you can now check the302// return value and skip the draw if we're in a bad state. In that case, call ReportBadState.303// The old assert wasn't very helpful in figuring out what caused it anyway...304bool BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VKRPipelineLayout *pipelineLayout) {305_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && pipeline != nullptr);306if (!curRenderStep_ || curRenderStep_->stepType != VKRStepType::RENDER) {307return false;308}309VkRenderData &data = curRenderStep_->commands.push_uninitialized();310data.cmd = VKRRenderCommand::BIND_GRAPHICS_PIPELINE;311pipelinesToCheck_.push_back(pipeline);312data.graphics_pipeline.pipeline = pipeline;313data.graphics_pipeline.pipelineLayout = pipelineLayout;314// This can be used to debug cases where depth/stencil rendering is used on color-only framebuffers.315// if ((flags & PipelineFlags::USES_DEPTH_STENCIL) && curRenderStep_->render.framebuffer && !curRenderStep_->render.framebuffer->HasDepth()) {316// DebugBreak();317// }318curPipelineFlags_ |= flags;319curPipelineLayout_ = pipelineLayout;320return true;321}322323void SetViewport(const VkViewport &vp) {324_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);325_dbg_assert_((int)vp.width >= 0);326_dbg_assert_((int)vp.height >= 0);327VkRenderData &data = curRenderStep_->commands.push_uninitialized();328data.cmd = VKRRenderCommand::VIEWPORT;329data.viewport.vp.x = vp.x;330data.viewport.vp.y = vp.y;331data.viewport.vp.width = vp.width;332data.viewport.vp.height = vp.height;333// We can't allow values outside this range unless we use VK_EXT_depth_range_unrestricted.334// Sometimes state mapping produces 65536/65535 which is slightly outside.335// TODO: This should be fixed at the source.336data.viewport.vp.minDepth = clamp_value(vp.minDepth, 0.0f, 1.0f);337data.viewport.vp.maxDepth = clamp_value(vp.maxDepth, 0.0f, 1.0f);338curStepHasViewport_ = true;339}340341// It's OK to set scissor outside the valid range - the function will automatically clip.342void SetScissor(int x, int y, int width, int height) {343_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);344345if (x < 0) {346width += x; // since x is negative, this shrinks width.347x = 0;348}349if (y < 0) {350height += y;351y = 0;352}353354if (x + width > curWidth_) {355width = curWidth_ - x;356}357if (y + height > curHeight_) {358height = curHeight_ - y;359}360361// Check validity.362if (width < 0 || height < 0 || x >= curWidth_ || y >= curHeight_) {363// TODO: If any of the dimensions are now zero or negative, we should flip a flag and not do draws, probably.364// Instead, if we detect an invalid scissor rectangle, we just put a 1x1 rectangle in the upper left corner.365x = 0;366y = 0;367width = 1;368height = 1;369}370371VkRect2D rc;372rc.offset.x = x;373rc.offset.y = y;374rc.extent.width = width;375rc.extent.height = height;376377curRenderArea_.Apply(rc);378379VkRenderData &data = curRenderStep_->commands.push_uninitialized();380data.cmd = VKRRenderCommand::SCISSOR;381data.scissor.scissor = rc;382curStepHasScissor_ = true;383}384385void SetStencilParams(uint8_t writeMask, uint8_t compareMask, uint8_t refValue) {386_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);387VkRenderData &data = curRenderStep_->commands.push_uninitialized();388data.cmd = VKRRenderCommand::STENCIL;389data.stencil.stencilWriteMask = writeMask;390data.stencil.stencilCompareMask = compareMask;391data.stencil.stencilRef = refValue;392}393394void SetBlendFactor(uint32_t color) {395_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);396VkRenderData &data = curRenderStep_->commands.push_uninitialized();397data.cmd = VKRRenderCommand::BLEND;398data.blendColor.color = color;399}400401void PushConstants(VkShaderStageFlags stages, int offset, int size, void *constants) {402_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);403_dbg_assert_(size + offset < 40);404VkRenderData &data = curRenderStep_->commands.push_uninitialized();405data.cmd = VKRRenderCommand::PUSH_CONSTANTS;406data.push.stages = stages;407data.push.offset = offset;408data.push.size = size;409memcpy(data.push.data, constants, size);410}411412void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask);413414// Cheaply set that we don't care about the contents of a surface at the start of the current render pass.415// This set the corresponding load-op of the current render pass to DONT_CARE.416// Useful when we don't know at bind-time whether we will overwrite the surface or not.417void SetLoadDontCare(VkImageAspectFlags aspects) {418_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);419if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)420curRenderStep_->render.colorLoad = VKRRenderPassLoadAction::DONT_CARE;421if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)422curRenderStep_->render.depthLoad = VKRRenderPassLoadAction::DONT_CARE;423if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)424curRenderStep_->render.stencilLoad = VKRRenderPassLoadAction::DONT_CARE;425}426427// Cheaply set that we don't care about the contents of a surface at the end of the current render pass.428// This set the corresponding store-op of the current render pass to DONT_CARE.429void SetStoreDontCare(VkImageAspectFlags aspects) {430_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);431if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)432curRenderStep_->render.colorStore = VKRRenderPassStoreAction::DONT_CARE;433if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)434curRenderStep_->render.depthStore = VKRRenderPassStoreAction::DONT_CARE;435if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)436curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE;437}438439// Descriptors will match the current pipeline layout, set by the last call to BindPipeline.440// Count is the count of void*s. Two are needed for COMBINED_IMAGE_SAMPLER, everything else is a single one.441// The goal is to keep this function very small and fast, and do the expensive work on the render thread or442// another thread.443PackedDescriptor *PushDescriptorSet(int count, int *descSetIndex) {444_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);445446int curFrame = vulkan_->GetCurFrame();447448VKRPipelineLayout::FrameData &data = curPipelineLayout_->frameData[curFrame];449450size_t offset = data.descData_.size();451PackedDescriptor *retval = data.descData_.extend_uninitialized(count);452453int setIndex = (int)data.descSets_.size();454PendingDescSet &descSet = data.descSets_.push_uninitialized();455descSet.offset = (uint32_t)offset;456descSet.count = count;457descSet.set = VK_NULL_HANDLE; // to be filled in458*descSetIndex = setIndex;459return retval;460}461462void Draw(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {463_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);464VkRenderData &data = curRenderStep_->commands.push_uninitialized();465data.cmd = VKRRenderCommand::DRAW;466data.draw.count = count;467data.draw.offset = offset;468data.draw.descSetIndex = descSetIndex;469data.draw.vbuffer = vbuffer;470data.draw.voffset = voffset;471data.draw.numUboOffsets = numUboOffsets;472_dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.draw.uboOffsets));473for (int i = 0; i < numUboOffsets; i++)474data.draw.uboOffsets[i] = uboOffsets[i];475curRenderStep_->render.numDraws++;476}477478void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances) {479_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);480VkRenderData &data = curRenderStep_->commands.push_uninitialized();481data.cmd = VKRRenderCommand::DRAW_INDEXED;482data.drawIndexed.count = count;483data.drawIndexed.instances = numInstances;484data.drawIndexed.descSetIndex = descSetIndex;485data.drawIndexed.vbuffer = vbuffer;486data.drawIndexed.voffset = voffset;487data.drawIndexed.ibuffer = ibuffer;488data.drawIndexed.ioffset = ioffset;489data.drawIndexed.numUboOffsets = numUboOffsets;490_dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.drawIndexed.uboOffsets));491for (int i = 0; i < numUboOffsets; i++)492data.drawIndexed.uboOffsets[i] = uboOffsets[i];493curRenderStep_->render.numDraws++;494}495496// These can be useful both when inspecting in RenderDoc, and when manually inspecting recorded commands497// in the debugger.498void DebugAnnotate(const char *annotation) {499_dbg_assert_(curRenderStep_);500VkRenderData &data = curRenderStep_->commands.push_uninitialized();501data.cmd = VKRRenderCommand::DEBUG_ANNOTATION;502data.debugAnnotation.annotation = annotation;503}504505VkCommandBuffer GetInitCmd();506507bool CreateBackbuffers();508void DestroyBackbuffers();509510bool HasBackbuffers() {511return queueRunner_.HasBackbuffers();512}513514void SetInflightFrames(int f) {515newInflightFrames_ = f < 1 || f > VulkanContext::MAX_INFLIGHT_FRAMES ? VulkanContext::MAX_INFLIGHT_FRAMES : f;516}517518VulkanContext *GetVulkanContext() {519return vulkan_;520}521522// Be careful with this. Only meant to be used for fetching render passes for shader cache initialization.523VulkanQueueRunner *GetQueueRunner() {524return &queueRunner_;525}526527std::string GetGpuProfileString() const {528return frameData_[vulkan_->GetCurFrame()].profile.profileSummary;529}530531bool NeedsSwapchainRecreate() const {532// Accepting a few of these makes shutdown simpler.533return outOfDateFrames_ > VulkanContext::MAX_INFLIGHT_FRAMES;534}535536VulkanBarrierBatch &PostInitBarrier() {537return postInitBarrier_;538}539540void ResetStats();541542void StartThreads();543void StopThreads();544545size_t GetNumSteps() const {546return steps_.size();547}548549private:550void EndCurRenderStep();551552void RenderThreadFunc();553void CompileThreadFunc();554555void Run(VKRRenderThreadTask &task);556557// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).558void FlushSync();559560void PresentWaitThreadFunc();561void PollPresentTiming();562563void ResetDescriptorLists(int frame);564void FlushDescriptors(int frame);565566void SanityCheckPassesOnAdd();567bool CreateSwapchainViewsAndDepth(VkCommandBuffer cmdInit, VulkanBarrierBatch *barriers, FrameDataShared &frameDataShared);568569FrameDataShared frameDataShared_;570571FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];572int newInflightFrames_ = -1;573int inflightFramesAtStart_ = 0;574575int outOfDateFrames_ = 0;576577// Submission time state578579// Note: These are raw backbuffer-sized. Rotated.580int curWidthRaw_ = -1;581int curHeightRaw_ = -1;582583// Pre-rotation (as you'd expect).584int curWidth_ = -1;585int curHeight_ = -1;586587bool insideFrame_ = false;588// probably doesn't need to be atomic.589std::atomic<bool> runCompileThread_{};590591bool useRenderThread_ = true;592bool measurePresentTime_ = false;593594// This is the offset within this frame, in case of a mid-frame sync.595VKRStep *curRenderStep_ = nullptr;596bool curStepHasViewport_ = false;597bool curStepHasScissor_ = false;598PipelineFlags curPipelineFlags_{};599BoundingRect curRenderArea_;600601std::vector<VKRStep *> steps_;602603// Execution time state604VulkanContext *vulkan_;605std::thread renderThread_;606VulkanQueueRunner queueRunner_;607608// For pushing data on the queue.609std::mutex pushMutex_;610std::condition_variable pushCondVar_;611612std::queue<VKRRenderThreadTask *> renderThreadQueue_;613614// For readbacks and other reasons we need to sync with the render thread.615std::mutex syncMutex_;616std::condition_variable syncCondVar_;617618// Shader compilation thread to compile while emulating the rest of the frame.619// Only one right now but we could use more.620std::thread compileThread_;621// Sync622std::condition_variable compileCond_;623std::mutex compileQueueMutex_;624std::vector<CompileQueueEntry> compileQueue_;625626// Thread for measuring presentation delay.627std::thread presentWaitThread_;628629// pipelines to check and possibly create at the end of the current render pass.630std::vector<VKRGraphicsPipeline *> pipelinesToCheck_;631632// For nicer output in the little internal GPU profiler.633SimpleStat initTimeMs_;634SimpleStat totalGPUTimeMs_;635SimpleStat renderCPUTimeMs_;636SimpleStat descUpdateTimeMs_;637638VulkanBarrierBatch postInitBarrier_;639640std::function<void(InvalidationCallbackFlags)> invalidationCallback_;641642uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;643HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;644645VKRPipelineLayout *curPipelineLayout_ = nullptr;646std::vector<VKRPipelineLayout *> pipelineLayouts_;647};648649650