CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/Vulkan/VulkanRenderManager.h
Views: 1401
#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 <algorithm>7#include <atomic>8#include <condition_variable>9#include <cstdint>10#include <mutex>11#include <thread>12#include <queue>1314#include "Common/Math/Statistics.h"15#include "Common/Thread/Promise.h"16#include "Common/System/Display.h"17#include "Common/GPU/Vulkan/VulkanContext.h"18#include "Common/GPU/Vulkan/VulkanBarrier.h"19#include "Common/Data/Convert/SmallDataConvert.h"20#include "Common/Data/Collections/FastVec.h"21#include "Common/Math/math_util.h"22#include "Common/GPU/DataFormat.h"23#include "Common/GPU/MiscTypes.h"24#include "Common/GPU/Vulkan/VulkanQueueRunner.h"25#include "Common/GPU/Vulkan/VulkanFramebuffer.h"26#include "Common/GPU/Vulkan/VulkanDescSet.h"27#include "Common/GPU/thin3d.h"2829// Forward declaration30VK_DEFINE_HANDLE(VmaAllocation);3132struct BoundingRect {33int x1;34int y1;35int x2;36int y2;3738BoundingRect() {39Reset();40}4142void Reset() {43x1 = 65535;44y1 = 65535;45x2 = -65535;46y2 = -65535;47}4849bool Empty() const {50return x2 < 0;51}5253void SetRect(int x, int y, int width, int height) {54x1 = x;55y1 = y;56x2 = width;57y2 = height;58}5960void Apply(const VkRect2D &rect) {61if (rect.offset.x < x1) x1 = rect.offset.x;62if (rect.offset.y < y1) y1 = rect.offset.y;63int rect_x2 = rect.offset.x + rect.extent.width;64int rect_y2 = rect.offset.y + rect.extent.height;65if (rect_x2 > x2) x2 = rect_x2;66if (rect_y2 > y2) y2 = rect_y2;67}6869VkRect2D ToVkRect2D() const {70VkRect2D rect;71rect.offset.x = x1;72rect.offset.y = y1;73rect.extent.width = x2 - x1;74rect.extent.height = y2 - y1;75return rect;76}77};7879// All the data needed to create a graphics pipeline.80// TODO: Compress this down greatly.81class VKRGraphicsPipelineDesc : public Draw::RefCountedObject {82public:83VKRGraphicsPipelineDesc() : Draw::RefCountedObject("VKRGraphicsPipelineDesc") {}8485VkPipelineCache pipelineCache = VK_NULL_HANDLE;86VkPipelineColorBlendStateCreateInfo cbs{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };87VkPipelineColorBlendAttachmentState blend0{};88VkPipelineDepthStencilStateCreateInfo dss{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };89VkDynamicState dynamicStates[6]{};90VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };91VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };92VkPipelineRasterizationProvokingVertexStateCreateInfoEXT rs_provoking{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT };9394// Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish.95Promise<VkShaderModule> *vertexShader = nullptr;96Promise<VkShaderModule> *fragmentShader = nullptr;97Promise<VkShaderModule> *geometryShader = nullptr;9899// These are for pipeline creation failure logging.100// TODO: Store pointers to the string instead? Feels iffy but will probably work.101std::string vertexShaderSource;102std::string fragmentShaderSource;103std::string geometryShaderSource;104105VkPrimitiveTopology topology;106VkVertexInputAttributeDescription attrs[8]{};107VkVertexInputBindingDescription ibd{};108VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };109VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };110111VKRPipelineLayout *pipelineLayout = nullptr;112113// Does not include the render pass type, it's passed in separately since the114// desc is persistent.115RPKey rpKey{};116};117118// Wrapped pipeline. Does own desc!119struct VKRGraphicsPipeline {120VKRGraphicsPipeline(PipelineFlags flags, const char *tag) : flags_(flags), tag_(tag) {}121~VKRGraphicsPipeline();122123bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType, VkSampleCountFlagBits sampleCount, double scheduleTime, int countToCompile);124void DestroyVariants(VulkanContext *vulkan, bool msaaOnly);125126// This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this.127void QueueForDeletion(VulkanContext *vulkan);128129// This blocks until any background compiles are finished.130// Used during game shutdown before we clear out shaders that these compiles depend on.131void BlockUntilCompiled();132133u32 GetVariantsBitmask() const;134135void LogCreationFailure() const;136137VKRGraphicsPipelineDesc *desc = nullptr;138Promise<VkPipeline> *pipeline[(size_t)RenderPassType::TYPE_COUNT]{};139140VkSampleCountFlagBits SampleCount() const { return sampleCount_; }141142const char *Tag() const { return tag_.c_str(); }143144private:145void DestroyVariantsInstant(VkDevice device);146147std::string tag_;148PipelineFlags flags_;149VkSampleCountFlagBits sampleCount_ = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;150};151152struct CompileQueueEntry {153CompileQueueEntry(VKRGraphicsPipeline *p, VkRenderPass _compatibleRenderPass, RenderPassType _renderPassType, VkSampleCountFlagBits _sampleCount)154: type(Type::GRAPHICS), graphics(p), compatibleRenderPass(_compatibleRenderPass), renderPassType(_renderPassType), sampleCount(_sampleCount) {}155enum class Type {156GRAPHICS,157};158Type type;159VkRenderPass compatibleRenderPass;160RenderPassType renderPassType;161VKRGraphicsPipeline* graphics = nullptr;162VkSampleCountFlagBits sampleCount;163};164165// Pending descriptor sets.166// TODO: Sort these by VKRPipelineLayout to avoid storing it for each element.167struct PendingDescSet {168int offset; // probably enough with a u16.169u8 count;170VkDescriptorSet set;171};172173struct PackedDescriptor {174union {175struct {176VkImageView view;177VkSampler sampler;178} image;179struct {180VkBuffer buffer;181uint32_t range;182uint32_t offset;183} buffer;184#if false185struct {186VkBuffer buffer;187uint64_t range; // write range and a zero offset in one operation with this.188} buffer_zero_offset;189#endif190};191};192193// Note that we only support a single descriptor set due to compatibility with some ancient devices.194// We should probably eventually give that up eventually.195struct VKRPipelineLayout {196~VKRPipelineLayout();197198enum { MAX_DESC_SET_BINDINGS = 10 };199BindingType bindingTypes[MAX_DESC_SET_BINDINGS];200201uint32_t bindingTypesCount = 0;202VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;203VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; // only support 1 for now.204int pushConstSize = 0;205const char *tag = nullptr;206207struct FrameData {208FrameData() : pool("N/A", true) {}209210VulkanDescSetPool pool;211FastVec<PackedDescriptor> descData_;212FastVec<PendingDescSet> descSets_;213// TODO: We should be able to get away with a single descData_/descSets_ and then send it along,214// but it's easier to just segregate by frame id.215int flushedDescriptors_ = 0;216};217218FrameData frameData[VulkanContext::MAX_INFLIGHT_FRAMES];219220void FlushDescSets(VulkanContext *vulkan, int frame, QueueProfileContext *profile);221void SetTag(const char *tag) {222this->tag = tag;223for (int i = 0; i < ARRAY_SIZE(frameData); i++) {224frameData[i].pool.SetTag(tag);225}226}227};228229class VulkanRenderManager {230public:231VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);232~VulkanRenderManager();233234// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.235void BeginFrame(bool enableProfiling, bool enableLogProfiler);236// These can run on a different thread!237void Finish();238void Present();239void CheckNothingPending();240241void SetInvalidationCallback(InvalidationCallback callback) {242invalidationCallback_ = callback;243}244245// This starts a new step containing a render pass (unless it can be trivially merged into the previous one, which is pretty common).246//247// After a "CopyFramebuffer" or the other functions that start "steps", you need to call this beforce248// making any new render state changes or draw calls.249//250// The following dynamic state needs to be reset by the caller after calling this (and will thus not safely carry over from251// the previous one):252// * Viewport/Scissor253// * Stencil parameters254// * Blend color255//256// (Most other state is directly decided by your choice of pipeline and descriptor set, so not handled here).257//258// It can be useful to use GetCurrentStepId() to figure out when you need to send all this state again, if you're259// not keeping track of your calls to this function on your own.260void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag);261262// Returns an ImageView corresponding to a framebuffer. Is called BindFramebufferAsTexture to maintain a similar interface263// as the other backends, even though there's no actual binding happening here.264// For layer, we use the same convention as thin3d, where layer = -1 means all layers together. For texturing, that means that you265// get an array texture view.266VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int layer);267268bool 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);269void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);270271void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag);272void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag);273274// Deferred creation, like in GL. Unlike GL though, the purpose is to allow background creation and avoiding275// stalling the emulation thread as much as possible.276// We delay creating pipelines until the end of the current render pass, so we can create the right type immediately.277// Unless a variantBitmask is passed in, in which case we can just go ahead.278// WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it.279VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, bool cacheLoad, const char *tag);280281VKRPipelineLayout *CreatePipelineLayout(BindingType *bindingTypes, size_t bindingCount, bool geoShadersEnabled, const char *tag);282void DestroyPipelineLayout(VKRPipelineLayout *pipelineLayout);283284void ReportBadStateForDraw();285286void NudgeCompilerThread() {287compileMutex_.lock();288compileCond_.notify_one();289compileMutex_.unlock();290}291292// This is the first call in a draw operation. Instead of asserting like we used to, you can now check the293// return value and skip the draw if we're in a bad state. In that case, call ReportBadState.294// The old assert wasn't very helpful in figuring out what caused it anyway...295bool BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VKRPipelineLayout *pipelineLayout) {296_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && pipeline != nullptr);297if (!curRenderStep_ || curRenderStep_->stepType != VKRStepType::RENDER) {298return false;299}300VkRenderData &data = curRenderStep_->commands.push_uninitialized();301data.cmd = VKRRenderCommand::BIND_GRAPHICS_PIPELINE;302pipelinesToCheck_.push_back(pipeline);303data.graphics_pipeline.pipeline = pipeline;304data.graphics_pipeline.pipelineLayout = pipelineLayout;305// This can be used to debug cases where depth/stencil rendering is used on color-only framebuffers.306// if ((flags & PipelineFlags::USES_DEPTH_STENCIL) && curRenderStep_->render.framebuffer && !curRenderStep_->render.framebuffer->HasDepth()) {307// DebugBreak();308// }309curPipelineFlags_ |= flags;310curPipelineLayout_ = pipelineLayout;311return true;312}313314void SetViewport(const VkViewport &vp) {315_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);316_dbg_assert_((int)vp.width >= 0);317_dbg_assert_((int)vp.height >= 0);318VkRenderData &data = curRenderStep_->commands.push_uninitialized();319data.cmd = VKRRenderCommand::VIEWPORT;320data.viewport.vp.x = vp.x;321data.viewport.vp.y = vp.y;322data.viewport.vp.width = vp.width;323data.viewport.vp.height = vp.height;324// We can't allow values outside this range unless we use VK_EXT_depth_range_unrestricted.325// Sometimes state mapping produces 65536/65535 which is slightly outside.326// TODO: This should be fixed at the source.327data.viewport.vp.minDepth = clamp_value(vp.minDepth, 0.0f, 1.0f);328data.viewport.vp.maxDepth = clamp_value(vp.maxDepth, 0.0f, 1.0f);329curStepHasViewport_ = true;330}331332// It's OK to set scissor outside the valid range - the function will automatically clip.333void SetScissor(int x, int y, int width, int height) {334_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);335336if (x < 0) {337width += x; // since x is negative, this shrinks width.338x = 0;339}340if (y < 0) {341height += y;342y = 0;343}344345if (x + width > curWidth_) {346width = curWidth_ - x;347}348if (y + height > curHeight_) {349height = curHeight_ - y;350}351352// Check validity.353if (width < 0 || height < 0 || x >= curWidth_ || y >= curHeight_) {354// TODO: If any of the dimensions are now zero or negative, we should flip a flag and not do draws, probably.355// Instead, if we detect an invalid scissor rectangle, we just put a 1x1 rectangle in the upper left corner.356x = 0;357y = 0;358width = 1;359height = 1;360}361362VkRect2D rc;363rc.offset.x = x;364rc.offset.y = y;365rc.extent.width = width;366rc.extent.height = height;367368curRenderArea_.Apply(rc);369370VkRenderData &data = curRenderStep_->commands.push_uninitialized();371data.cmd = VKRRenderCommand::SCISSOR;372data.scissor.scissor = rc;373curStepHasScissor_ = true;374}375376void SetStencilParams(uint8_t writeMask, uint8_t compareMask, uint8_t refValue) {377_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);378VkRenderData &data = curRenderStep_->commands.push_uninitialized();379data.cmd = VKRRenderCommand::STENCIL;380data.stencil.stencilWriteMask = writeMask;381data.stencil.stencilCompareMask = compareMask;382data.stencil.stencilRef = refValue;383}384385void SetBlendFactor(uint32_t color) {386_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);387VkRenderData &data = curRenderStep_->commands.push_uninitialized();388data.cmd = VKRRenderCommand::BLEND;389data.blendColor.color = color;390}391392void PushConstants(VkPipelineLayout pipelineLayout, VkShaderStageFlags stages, int offset, int size, void *constants) {393_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);394_dbg_assert_(size + offset < 40);395VkRenderData &data = curRenderStep_->commands.push_uninitialized();396data.cmd = VKRRenderCommand::PUSH_CONSTANTS;397data.push.stages = stages;398data.push.offset = offset;399data.push.size = size;400memcpy(data.push.data, constants, size);401}402403void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask);404405// Cheaply set that we don't care about the contents of a surface at the start of the current render pass.406// This set the corresponding load-op of the current render pass to DONT_CARE.407// Useful when we don't know at bind-time whether we will overwrite the surface or not.408void SetLoadDontCare(VkImageAspectFlags aspects) {409_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);410if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)411curRenderStep_->render.colorLoad = VKRRenderPassLoadAction::DONT_CARE;412if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)413curRenderStep_->render.depthLoad = VKRRenderPassLoadAction::DONT_CARE;414if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)415curRenderStep_->render.stencilLoad = VKRRenderPassLoadAction::DONT_CARE;416}417418// Cheaply set that we don't care about the contents of a surface at the end of the current render pass.419// This set the corresponding store-op of the current render pass to DONT_CARE.420void SetStoreDontCare(VkImageAspectFlags aspects) {421_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);422if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)423curRenderStep_->render.colorStore = VKRRenderPassStoreAction::DONT_CARE;424if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)425curRenderStep_->render.depthStore = VKRRenderPassStoreAction::DONT_CARE;426if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)427curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE;428}429430// Descriptors will match the current pipeline layout, set by the last call to BindPipeline.431// Count is the count of void*s. Two are needed for COMBINED_IMAGE_SAMPLER, everything else is a single one.432// The goal is to keep this function very small and fast, and do the expensive work on the render thread or433// another thread.434PackedDescriptor *PushDescriptorSet(int count, int *descSetIndex) {435_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);436437int curFrame = vulkan_->GetCurFrame();438439VKRPipelineLayout::FrameData &data = curPipelineLayout_->frameData[curFrame];440441size_t offset = data.descData_.size();442PackedDescriptor *retval = data.descData_.extend_uninitialized(count);443444int setIndex = (int)data.descSets_.size();445PendingDescSet &descSet = data.descSets_.push_uninitialized();446descSet.offset = (uint32_t)offset;447descSet.count = count;448// descSet.set = VK_NULL_HANDLE; // to be filled in449*descSetIndex = setIndex;450return retval;451}452453void Draw(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {454_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);455VkRenderData &data = curRenderStep_->commands.push_uninitialized();456data.cmd = VKRRenderCommand::DRAW;457data.draw.count = count;458data.draw.offset = offset;459data.draw.descSetIndex = descSetIndex;460data.draw.vbuffer = vbuffer;461data.draw.voffset = voffset;462data.draw.numUboOffsets = numUboOffsets;463_dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.draw.uboOffsets));464for (int i = 0; i < numUboOffsets; i++)465data.draw.uboOffsets[i] = uboOffsets[i];466curRenderStep_->render.numDraws++;467}468469void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances) {470_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);471VkRenderData &data = curRenderStep_->commands.push_uninitialized();472data.cmd = VKRRenderCommand::DRAW_INDEXED;473data.drawIndexed.count = count;474data.drawIndexed.instances = numInstances;475data.drawIndexed.descSetIndex = descSetIndex;476data.drawIndexed.vbuffer = vbuffer;477data.drawIndexed.voffset = voffset;478data.drawIndexed.ibuffer = ibuffer;479data.drawIndexed.ioffset = ioffset;480data.drawIndexed.numUboOffsets = numUboOffsets;481_dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.drawIndexed.uboOffsets));482for (int i = 0; i < numUboOffsets; i++)483data.drawIndexed.uboOffsets[i] = uboOffsets[i];484curRenderStep_->render.numDraws++;485}486487// These can be useful both when inspecting in RenderDoc, and when manually inspecting recorded commands488// in the debugger.489void DebugAnnotate(const char *annotation) {490_dbg_assert_(curRenderStep_);491VkRenderData &data = curRenderStep_->commands.push_uninitialized();492data.cmd = VKRRenderCommand::DEBUG_ANNOTATION;493data.debugAnnotation.annotation = annotation;494}495496VkCommandBuffer GetInitCmd();497498bool CreateBackbuffers();499void DestroyBackbuffers();500501bool HasBackbuffers() {502return queueRunner_.HasBackbuffers();503}504505void SetInflightFrames(int f) {506newInflightFrames_ = f < 1 || f > VulkanContext::MAX_INFLIGHT_FRAMES ? VulkanContext::MAX_INFLIGHT_FRAMES : f;507}508509VulkanContext *GetVulkanContext() {510return vulkan_;511}512513// Be careful with this. Only meant to be used for fetching render passes for shader cache initialization.514VulkanQueueRunner *GetQueueRunner() {515return &queueRunner_;516}517518std::string GetGpuProfileString() const {519return frameData_[vulkan_->GetCurFrame()].profile.profileSummary;520}521522bool NeedsSwapchainRecreate() const {523// Accepting a few of these makes shutdown simpler.524return outOfDateFrames_ > VulkanContext::MAX_INFLIGHT_FRAMES;525}526527VulkanBarrierBatch &PostInitBarrier() {528return postInitBarrier_;529}530531void ResetStats();532533void StartThreads();534void StopThreads();535536size_t GetNumSteps() const {537return steps_.size();538}539540private:541void EndCurRenderStep();542543void RenderThreadFunc();544void CompileThreadFunc();545546void Run(VKRRenderThreadTask &task);547548// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).549void FlushSync();550551void PresentWaitThreadFunc();552void PollPresentTiming();553554void ResetDescriptorLists(int frame);555void FlushDescriptors(int frame);556557void SanityCheckPassesOnAdd();558559FrameDataShared frameDataShared_;560561FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];562int newInflightFrames_ = -1;563int inflightFramesAtStart_ = 0;564565int outOfDateFrames_ = 0;566567// Submission time state568569// Note: These are raw backbuffer-sized. Rotated.570int curWidthRaw_ = -1;571int curHeightRaw_ = -1;572573// Pre-rotation (as you'd expect).574int curWidth_ = -1;575int curHeight_ = -1;576577bool insideFrame_ = false;578// probably doesn't need to be atomic.579std::atomic<bool> runCompileThread_;580581bool useRenderThread_ = true;582bool measurePresentTime_ = false;583584// This is the offset within this frame, in case of a mid-frame sync.585VKRStep *curRenderStep_ = nullptr;586bool curStepHasViewport_ = false;587bool curStepHasScissor_ = false;588PipelineFlags curPipelineFlags_{};589BoundingRect curRenderArea_;590591std::vector<VKRStep *> steps_;592593// Execution time state594VulkanContext *vulkan_;595std::thread renderThread_;596VulkanQueueRunner queueRunner_;597598// For pushing data on the queue.599std::mutex pushMutex_;600std::condition_variable pushCondVar_;601602std::queue<VKRRenderThreadTask *> renderThreadQueue_;603604// For readbacks and other reasons we need to sync with the render thread.605std::mutex syncMutex_;606std::condition_variable syncCondVar_;607608// Shader compilation thread to compile while emulating the rest of the frame.609// Only one right now but we could use more.610std::thread compileThread_;611// Sync612std::condition_variable compileCond_;613std::mutex compileMutex_;614std::vector<CompileQueueEntry> compileQueue_;615616// Thread for measuring presentation delay.617std::thread presentWaitThread_;618619// pipelines to check and possibly create at the end of the current render pass.620std::vector<VKRGraphicsPipeline *> pipelinesToCheck_;621622// For nicer output in the little internal GPU profiler.623SimpleStat initTimeMs_;624SimpleStat totalGPUTimeMs_;625SimpleStat renderCPUTimeMs_;626SimpleStat descUpdateTimeMs_;627628VulkanBarrierBatch postInitBarrier_;629630std::function<void(InvalidationCallbackFlags)> invalidationCallback_;631632uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;633HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;634635VKRPipelineLayout *curPipelineLayout_ = nullptr;636std::vector<VKRPipelineLayout *> pipelineLayouts_;637};638639640