Path: blob/master/Common/GPU/Vulkan/VulkanRenderManager.h
3188 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; // 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};191192// Note that we only support a single descriptor set due to compatibility with some ancient devices.193// We should probably eventually give that up eventually.194struct VKRPipelineLayout {195~VKRPipelineLayout();196197enum { MAX_DESC_SET_BINDINGS = 10 };198BindingType bindingTypes[MAX_DESC_SET_BINDINGS];199200uint32_t bindingTypesCount = 0;201VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;202VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; // only support 1 for now.203int pushConstSize = 0;204const char *tag = nullptr;205206struct FrameData {207FrameData() : pool("N/A", true) {}208209VulkanDescSetPool pool;210FastVec<PackedDescriptor> descData_;211FastVec<PendingDescSet> descSets_;212// TODO: We should be able to get away with a single descData_/descSets_ and then send it along,213// but it's easier to just segregate by frame id.214int flushedDescriptors_ = 0;215};216217FrameData frameData[VulkanContext::MAX_INFLIGHT_FRAMES];218219void FlushDescSets(VulkanContext *vulkan, int frame, QueueProfileContext *profile);220void SetTag(const char *tag) {221this->tag = tag;222for (int i = 0; i < ARRAY_SIZE(frameData); i++) {223frameData[i].pool.SetTag(tag);224}225}226};227228class VulkanRenderManager {229public:230VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);231~VulkanRenderManager();232233// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.234void BeginFrame(bool enableProfiling, bool enableLogProfiler);235// These can run on a different thread!236void Finish();237void Present();238void CheckNothingPending();239240void SetInvalidationCallback(InvalidationCallback callback) {241invalidationCallback_ = callback;242}243244// This starts a new step containing a render pass (unless it can be trivially merged into the previous one, which is pretty common).245//246// After a "CopyFramebuffer" or the other functions that start "steps", you need to call this beforce247// making any new render state changes or draw calls.248//249// The following dynamic state needs to be reset by the caller after calling this (and will thus not safely carry over from250// the previous one):251// * Viewport/Scissor252// * Stencil parameters253// * Blend color254//255// (Most other state is directly decided by your choice of pipeline and descriptor set, so not handled here).256//257// It can be useful to use GetCurrentStepId() to figure out when you need to send all this state again, if you're258// not keeping track of your calls to this function on your own.259void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag);260261// Returns an ImageView corresponding to a framebuffer. Is called BindFramebufferAsTexture to maintain a similar interface262// as the other backends, even though there's no actual binding happening here.263// For layer, we use the same convention as thin3d, where layer = -1 means all layers together. For texturing, that means that you264// get an array texture view.265VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int layer);266267bool 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);268void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);269270void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag);271void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag);272273// Deferred creation, like in GL. Unlike GL though, the purpose is to allow background creation and avoiding274// stalling the emulation thread as much as possible.275// We delay creating pipelines until the end of the current render pass, so we can create the right type immediately.276// Unless a variantBitmask is passed in, in which case we can just go ahead.277// WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it.278VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, bool cacheLoad, const char *tag);279280VKRPipelineLayout *CreatePipelineLayout(BindingType *bindingTypes, size_t bindingCount, bool geoShadersEnabled, const char *tag);281void DestroyPipelineLayout(VKRPipelineLayout *pipelineLayout);282283void ReportBadStateForDraw();284285int WaitForPipelines();286287void NudgeCompilerThread() {288compileQueueMutex_.lock();289compileCond_.notify_one();290compileQueueMutex_.unlock();291}292293void AssertInRenderPass() const {294_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);295}296297// This is the first call in a draw operation. Instead of asserting like we used to, you can now check the298// return value and skip the draw if we're in a bad state. In that case, call ReportBadState.299// The old assert wasn't very helpful in figuring out what caused it anyway...300bool BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VKRPipelineLayout *pipelineLayout) {301_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && pipeline != nullptr);302if (!curRenderStep_ || curRenderStep_->stepType != VKRStepType::RENDER) {303return false;304}305VkRenderData &data = curRenderStep_->commands.push_uninitialized();306data.cmd = VKRRenderCommand::BIND_GRAPHICS_PIPELINE;307pipelinesToCheck_.push_back(pipeline);308data.graphics_pipeline.pipeline = pipeline;309data.graphics_pipeline.pipelineLayout = pipelineLayout;310// This can be used to debug cases where depth/stencil rendering is used on color-only framebuffers.311// if ((flags & PipelineFlags::USES_DEPTH_STENCIL) && curRenderStep_->render.framebuffer && !curRenderStep_->render.framebuffer->HasDepth()) {312// DebugBreak();313// }314curPipelineFlags_ |= flags;315curPipelineLayout_ = pipelineLayout;316return true;317}318319void SetViewport(const VkViewport &vp) {320_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);321_dbg_assert_((int)vp.width >= 0);322_dbg_assert_((int)vp.height >= 0);323VkRenderData &data = curRenderStep_->commands.push_uninitialized();324data.cmd = VKRRenderCommand::VIEWPORT;325data.viewport.vp.x = vp.x;326data.viewport.vp.y = vp.y;327data.viewport.vp.width = vp.width;328data.viewport.vp.height = vp.height;329// We can't allow values outside this range unless we use VK_EXT_depth_range_unrestricted.330// Sometimes state mapping produces 65536/65535 which is slightly outside.331// TODO: This should be fixed at the source.332data.viewport.vp.minDepth = clamp_value(vp.minDepth, 0.0f, 1.0f);333data.viewport.vp.maxDepth = clamp_value(vp.maxDepth, 0.0f, 1.0f);334curStepHasViewport_ = true;335}336337// It's OK to set scissor outside the valid range - the function will automatically clip.338void SetScissor(int x, int y, int width, int height) {339_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);340341if (x < 0) {342width += x; // since x is negative, this shrinks width.343x = 0;344}345if (y < 0) {346height += y;347y = 0;348}349350if (x + width > curWidth_) {351width = curWidth_ - x;352}353if (y + height > curHeight_) {354height = curHeight_ - y;355}356357// Check validity.358if (width < 0 || height < 0 || x >= curWidth_ || y >= curHeight_) {359// TODO: If any of the dimensions are now zero or negative, we should flip a flag and not do draws, probably.360// Instead, if we detect an invalid scissor rectangle, we just put a 1x1 rectangle in the upper left corner.361x = 0;362y = 0;363width = 1;364height = 1;365}366367VkRect2D rc;368rc.offset.x = x;369rc.offset.y = y;370rc.extent.width = width;371rc.extent.height = height;372373curRenderArea_.Apply(rc);374375VkRenderData &data = curRenderStep_->commands.push_uninitialized();376data.cmd = VKRRenderCommand::SCISSOR;377data.scissor.scissor = rc;378curStepHasScissor_ = true;379}380381void SetStencilParams(uint8_t writeMask, uint8_t compareMask, uint8_t refValue) {382_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);383VkRenderData &data = curRenderStep_->commands.push_uninitialized();384data.cmd = VKRRenderCommand::STENCIL;385data.stencil.stencilWriteMask = writeMask;386data.stencil.stencilCompareMask = compareMask;387data.stencil.stencilRef = refValue;388}389390void SetBlendFactor(uint32_t color) {391_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);392VkRenderData &data = curRenderStep_->commands.push_uninitialized();393data.cmd = VKRRenderCommand::BLEND;394data.blendColor.color = color;395}396397void PushConstants(VkShaderStageFlags stages, int offset, int size, void *constants) {398_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);399_dbg_assert_(size + offset < 40);400VkRenderData &data = curRenderStep_->commands.push_uninitialized();401data.cmd = VKRRenderCommand::PUSH_CONSTANTS;402data.push.stages = stages;403data.push.offset = offset;404data.push.size = size;405memcpy(data.push.data, constants, size);406}407408void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask);409410// Cheaply set that we don't care about the contents of a surface at the start of the current render pass.411// This set the corresponding load-op of the current render pass to DONT_CARE.412// Useful when we don't know at bind-time whether we will overwrite the surface or not.413void SetLoadDontCare(VkImageAspectFlags aspects) {414_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);415if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)416curRenderStep_->render.colorLoad = VKRRenderPassLoadAction::DONT_CARE;417if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)418curRenderStep_->render.depthLoad = VKRRenderPassLoadAction::DONT_CARE;419if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)420curRenderStep_->render.stencilLoad = VKRRenderPassLoadAction::DONT_CARE;421}422423// Cheaply set that we don't care about the contents of a surface at the end of the current render pass.424// This set the corresponding store-op of the current render pass to DONT_CARE.425void SetStoreDontCare(VkImageAspectFlags aspects) {426_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);427if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)428curRenderStep_->render.colorStore = VKRRenderPassStoreAction::DONT_CARE;429if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)430curRenderStep_->render.depthStore = VKRRenderPassStoreAction::DONT_CARE;431if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)432curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE;433}434435// Descriptors will match the current pipeline layout, set by the last call to BindPipeline.436// Count is the count of void*s. Two are needed for COMBINED_IMAGE_SAMPLER, everything else is a single one.437// The goal is to keep this function very small and fast, and do the expensive work on the render thread or438// another thread.439PackedDescriptor *PushDescriptorSet(int count, int *descSetIndex) {440_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);441442int curFrame = vulkan_->GetCurFrame();443444VKRPipelineLayout::FrameData &data = curPipelineLayout_->frameData[curFrame];445446size_t offset = data.descData_.size();447PackedDescriptor *retval = data.descData_.extend_uninitialized(count);448449int setIndex = (int)data.descSets_.size();450PendingDescSet &descSet = data.descSets_.push_uninitialized();451descSet.offset = (uint32_t)offset;452descSet.count = count;453// descSet.set = VK_NULL_HANDLE; // to be filled in454*descSetIndex = setIndex;455return retval;456}457458void Draw(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {459_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);460VkRenderData &data = curRenderStep_->commands.push_uninitialized();461data.cmd = VKRRenderCommand::DRAW;462data.draw.count = count;463data.draw.offset = offset;464data.draw.descSetIndex = descSetIndex;465data.draw.vbuffer = vbuffer;466data.draw.voffset = voffset;467data.draw.numUboOffsets = numUboOffsets;468_dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.draw.uboOffsets));469for (int i = 0; i < numUboOffsets; i++)470data.draw.uboOffsets[i] = uboOffsets[i];471curRenderStep_->render.numDraws++;472}473474void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances) {475_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);476VkRenderData &data = curRenderStep_->commands.push_uninitialized();477data.cmd = VKRRenderCommand::DRAW_INDEXED;478data.drawIndexed.count = count;479data.drawIndexed.instances = numInstances;480data.drawIndexed.descSetIndex = descSetIndex;481data.drawIndexed.vbuffer = vbuffer;482data.drawIndexed.voffset = voffset;483data.drawIndexed.ibuffer = ibuffer;484data.drawIndexed.ioffset = ioffset;485data.drawIndexed.numUboOffsets = numUboOffsets;486_dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.drawIndexed.uboOffsets));487for (int i = 0; i < numUboOffsets; i++)488data.drawIndexed.uboOffsets[i] = uboOffsets[i];489curRenderStep_->render.numDraws++;490}491492// These can be useful both when inspecting in RenderDoc, and when manually inspecting recorded commands493// in the debugger.494void DebugAnnotate(const char *annotation) {495_dbg_assert_(curRenderStep_);496VkRenderData &data = curRenderStep_->commands.push_uninitialized();497data.cmd = VKRRenderCommand::DEBUG_ANNOTATION;498data.debugAnnotation.annotation = annotation;499}500501VkCommandBuffer GetInitCmd();502503bool CreateBackbuffers();504void DestroyBackbuffers();505506bool HasBackbuffers() {507return queueRunner_.HasBackbuffers();508}509510void SetInflightFrames(int f) {511newInflightFrames_ = f < 1 || f > VulkanContext::MAX_INFLIGHT_FRAMES ? VulkanContext::MAX_INFLIGHT_FRAMES : f;512}513514VulkanContext *GetVulkanContext() {515return vulkan_;516}517518// Be careful with this. Only meant to be used for fetching render passes for shader cache initialization.519VulkanQueueRunner *GetQueueRunner() {520return &queueRunner_;521}522523std::string GetGpuProfileString() const {524return frameData_[vulkan_->GetCurFrame()].profile.profileSummary;525}526527bool NeedsSwapchainRecreate() const {528// Accepting a few of these makes shutdown simpler.529return outOfDateFrames_ > VulkanContext::MAX_INFLIGHT_FRAMES;530}531532VulkanBarrierBatch &PostInitBarrier() {533return postInitBarrier_;534}535536void ResetStats();537538void StartThreads();539void StopThreads();540541size_t GetNumSteps() const {542return steps_.size();543}544545private:546void EndCurRenderStep();547548void RenderThreadFunc();549void CompileThreadFunc();550551void Run(VKRRenderThreadTask &task);552553// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).554void FlushSync();555556void PresentWaitThreadFunc();557void PollPresentTiming();558559void ResetDescriptorLists(int frame);560void FlushDescriptors(int frame);561562void SanityCheckPassesOnAdd();563bool CreateSwapchainViewsAndDepth(VkCommandBuffer cmdInit, VulkanBarrierBatch *barriers, FrameDataShared &frameDataShared);564565FrameDataShared frameDataShared_;566567FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];568int newInflightFrames_ = -1;569int inflightFramesAtStart_ = 0;570571int outOfDateFrames_ = 0;572573// Submission time state574575// Note: These are raw backbuffer-sized. Rotated.576int curWidthRaw_ = -1;577int curHeightRaw_ = -1;578579// Pre-rotation (as you'd expect).580int curWidth_ = -1;581int curHeight_ = -1;582583bool insideFrame_ = false;584// probably doesn't need to be atomic.585std::atomic<bool> runCompileThread_{};586587bool useRenderThread_ = true;588bool measurePresentTime_ = false;589590// This is the offset within this frame, in case of a mid-frame sync.591VKRStep *curRenderStep_ = nullptr;592bool curStepHasViewport_ = false;593bool curStepHasScissor_ = false;594PipelineFlags curPipelineFlags_{};595BoundingRect curRenderArea_;596597std::vector<VKRStep *> steps_;598599// Execution time state600VulkanContext *vulkan_;601std::thread renderThread_;602VulkanQueueRunner queueRunner_;603604// For pushing data on the queue.605std::mutex pushMutex_;606std::condition_variable pushCondVar_;607608std::queue<VKRRenderThreadTask *> renderThreadQueue_;609610// For readbacks and other reasons we need to sync with the render thread.611std::mutex syncMutex_;612std::condition_variable syncCondVar_;613614// Shader compilation thread to compile while emulating the rest of the frame.615// Only one right now but we could use more.616std::thread compileThread_;617// Sync618std::condition_variable compileCond_;619std::mutex compileQueueMutex_;620std::vector<CompileQueueEntry> compileQueue_;621622// Thread for measuring presentation delay.623std::thread presentWaitThread_;624625// pipelines to check and possibly create at the end of the current render pass.626std::vector<VKRGraphicsPipeline *> pipelinesToCheck_;627628// For nicer output in the little internal GPU profiler.629SimpleStat initTimeMs_;630SimpleStat totalGPUTimeMs_;631SimpleStat renderCPUTimeMs_;632SimpleStat descUpdateTimeMs_;633634VulkanBarrierBatch postInitBarrier_;635636std::function<void(InvalidationCallbackFlags)> invalidationCallback_;637638uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;639HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;640641VKRPipelineLayout *curPipelineLayout_ = nullptr;642std::vector<VKRPipelineLayout *> pipelineLayouts_;643};644645646