CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/GPU/Vulkan/VulkanRenderManager.h
Views: 1401
1
#pragma once
2
3
// VulkanRenderManager takes the role that a GL driver does of sequencing and optimizing render passes.
4
// Only draws and binds are handled here, resource creation and allocations are handled as normal -
5
// that's the nice thing with Vulkan.
6
7
#include <algorithm>
8
#include <atomic>
9
#include <condition_variable>
10
#include <cstdint>
11
#include <mutex>
12
#include <thread>
13
#include <queue>
14
15
#include "Common/Math/Statistics.h"
16
#include "Common/Thread/Promise.h"
17
#include "Common/System/Display.h"
18
#include "Common/GPU/Vulkan/VulkanContext.h"
19
#include "Common/GPU/Vulkan/VulkanBarrier.h"
20
#include "Common/Data/Convert/SmallDataConvert.h"
21
#include "Common/Data/Collections/FastVec.h"
22
#include "Common/Math/math_util.h"
23
#include "Common/GPU/DataFormat.h"
24
#include "Common/GPU/MiscTypes.h"
25
#include "Common/GPU/Vulkan/VulkanQueueRunner.h"
26
#include "Common/GPU/Vulkan/VulkanFramebuffer.h"
27
#include "Common/GPU/Vulkan/VulkanDescSet.h"
28
#include "Common/GPU/thin3d.h"
29
30
// Forward declaration
31
VK_DEFINE_HANDLE(VmaAllocation);
32
33
struct BoundingRect {
34
int x1;
35
int y1;
36
int x2;
37
int y2;
38
39
BoundingRect() {
40
Reset();
41
}
42
43
void Reset() {
44
x1 = 65535;
45
y1 = 65535;
46
x2 = -65535;
47
y2 = -65535;
48
}
49
50
bool Empty() const {
51
return x2 < 0;
52
}
53
54
void SetRect(int x, int y, int width, int height) {
55
x1 = x;
56
y1 = y;
57
x2 = width;
58
y2 = height;
59
}
60
61
void Apply(const VkRect2D &rect) {
62
if (rect.offset.x < x1) x1 = rect.offset.x;
63
if (rect.offset.y < y1) y1 = rect.offset.y;
64
int rect_x2 = rect.offset.x + rect.extent.width;
65
int rect_y2 = rect.offset.y + rect.extent.height;
66
if (rect_x2 > x2) x2 = rect_x2;
67
if (rect_y2 > y2) y2 = rect_y2;
68
}
69
70
VkRect2D ToVkRect2D() const {
71
VkRect2D rect;
72
rect.offset.x = x1;
73
rect.offset.y = y1;
74
rect.extent.width = x2 - x1;
75
rect.extent.height = y2 - y1;
76
return rect;
77
}
78
};
79
80
// All the data needed to create a graphics pipeline.
81
// TODO: Compress this down greatly.
82
class VKRGraphicsPipelineDesc : public Draw::RefCountedObject {
83
public:
84
VKRGraphicsPipelineDesc() : Draw::RefCountedObject("VKRGraphicsPipelineDesc") {}
85
86
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
87
VkPipelineColorBlendStateCreateInfo cbs{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
88
VkPipelineColorBlendAttachmentState blend0{};
89
VkPipelineDepthStencilStateCreateInfo dss{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
90
VkDynamicState dynamicStates[6]{};
91
VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
92
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
93
VkPipelineRasterizationProvokingVertexStateCreateInfoEXT rs_provoking{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT };
94
95
// Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish.
96
Promise<VkShaderModule> *vertexShader = nullptr;
97
Promise<VkShaderModule> *fragmentShader = nullptr;
98
Promise<VkShaderModule> *geometryShader = nullptr;
99
100
// These are for pipeline creation failure logging.
101
// TODO: Store pointers to the string instead? Feels iffy but will probably work.
102
std::string vertexShaderSource;
103
std::string fragmentShaderSource;
104
std::string geometryShaderSource;
105
106
VkPrimitiveTopology topology;
107
VkVertexInputAttributeDescription attrs[8]{};
108
VkVertexInputBindingDescription ibd{};
109
VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
110
VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
111
112
VKRPipelineLayout *pipelineLayout = nullptr;
113
114
// Does not include the render pass type, it's passed in separately since the
115
// desc is persistent.
116
RPKey rpKey{};
117
};
118
119
// Wrapped pipeline. Does own desc!
120
struct VKRGraphicsPipeline {
121
VKRGraphicsPipeline(PipelineFlags flags, const char *tag) : flags_(flags), tag_(tag) {}
122
~VKRGraphicsPipeline();
123
124
bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType, VkSampleCountFlagBits sampleCount, double scheduleTime, int countToCompile);
125
void DestroyVariants(VulkanContext *vulkan, bool msaaOnly);
126
127
// This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this.
128
void QueueForDeletion(VulkanContext *vulkan);
129
130
// This blocks until any background compiles are finished.
131
// Used during game shutdown before we clear out shaders that these compiles depend on.
132
void BlockUntilCompiled();
133
134
u32 GetVariantsBitmask() const;
135
136
void LogCreationFailure() const;
137
138
VKRGraphicsPipelineDesc *desc = nullptr;
139
Promise<VkPipeline> *pipeline[(size_t)RenderPassType::TYPE_COUNT]{};
140
141
VkSampleCountFlagBits SampleCount() const { return sampleCount_; }
142
143
const char *Tag() const { return tag_.c_str(); }
144
145
private:
146
void DestroyVariantsInstant(VkDevice device);
147
148
std::string tag_;
149
PipelineFlags flags_;
150
VkSampleCountFlagBits sampleCount_ = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;
151
};
152
153
struct CompileQueueEntry {
154
CompileQueueEntry(VKRGraphicsPipeline *p, VkRenderPass _compatibleRenderPass, RenderPassType _renderPassType, VkSampleCountFlagBits _sampleCount)
155
: type(Type::GRAPHICS), graphics(p), compatibleRenderPass(_compatibleRenderPass), renderPassType(_renderPassType), sampleCount(_sampleCount) {}
156
enum class Type {
157
GRAPHICS,
158
};
159
Type type;
160
VkRenderPass compatibleRenderPass;
161
RenderPassType renderPassType;
162
VKRGraphicsPipeline* graphics = nullptr;
163
VkSampleCountFlagBits sampleCount;
164
};
165
166
// Pending descriptor sets.
167
// TODO: Sort these by VKRPipelineLayout to avoid storing it for each element.
168
struct PendingDescSet {
169
int offset; // probably enough with a u16.
170
u8 count;
171
VkDescriptorSet set;
172
};
173
174
struct PackedDescriptor {
175
union {
176
struct {
177
VkImageView view;
178
VkSampler sampler;
179
} image;
180
struct {
181
VkBuffer buffer;
182
uint32_t range;
183
uint32_t offset;
184
} buffer;
185
#if false
186
struct {
187
VkBuffer buffer;
188
uint64_t range; // write range and a zero offset in one operation with this.
189
} buffer_zero_offset;
190
#endif
191
};
192
};
193
194
// Note that we only support a single descriptor set due to compatibility with some ancient devices.
195
// We should probably eventually give that up eventually.
196
struct VKRPipelineLayout {
197
~VKRPipelineLayout();
198
199
enum { MAX_DESC_SET_BINDINGS = 10 };
200
BindingType bindingTypes[MAX_DESC_SET_BINDINGS];
201
202
uint32_t bindingTypesCount = 0;
203
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
204
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; // only support 1 for now.
205
int pushConstSize = 0;
206
const char *tag = nullptr;
207
208
struct FrameData {
209
FrameData() : pool("N/A", true) {}
210
211
VulkanDescSetPool pool;
212
FastVec<PackedDescriptor> descData_;
213
FastVec<PendingDescSet> descSets_;
214
// TODO: We should be able to get away with a single descData_/descSets_ and then send it along,
215
// but it's easier to just segregate by frame id.
216
int flushedDescriptors_ = 0;
217
};
218
219
FrameData frameData[VulkanContext::MAX_INFLIGHT_FRAMES];
220
221
void FlushDescSets(VulkanContext *vulkan, int frame, QueueProfileContext *profile);
222
void SetTag(const char *tag) {
223
this->tag = tag;
224
for (int i = 0; i < ARRAY_SIZE(frameData); i++) {
225
frameData[i].pool.SetTag(tag);
226
}
227
}
228
};
229
230
class VulkanRenderManager {
231
public:
232
VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);
233
~VulkanRenderManager();
234
235
// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.
236
void BeginFrame(bool enableProfiling, bool enableLogProfiler);
237
// These can run on a different thread!
238
void Finish();
239
void Present();
240
void CheckNothingPending();
241
242
void SetInvalidationCallback(InvalidationCallback callback) {
243
invalidationCallback_ = callback;
244
}
245
246
// This starts a new step containing a render pass (unless it can be trivially merged into the previous one, which is pretty common).
247
//
248
// After a "CopyFramebuffer" or the other functions that start "steps", you need to call this beforce
249
// making any new render state changes or draw calls.
250
//
251
// The following dynamic state needs to be reset by the caller after calling this (and will thus not safely carry over from
252
// the previous one):
253
// * Viewport/Scissor
254
// * Stencil parameters
255
// * Blend color
256
//
257
// (Most other state is directly decided by your choice of pipeline and descriptor set, so not handled here).
258
//
259
// It can be useful to use GetCurrentStepId() to figure out when you need to send all this state again, if you're
260
// not keeping track of your calls to this function on your own.
261
void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag);
262
263
// Returns an ImageView corresponding to a framebuffer. Is called BindFramebufferAsTexture to maintain a similar interface
264
// as the other backends, even though there's no actual binding happening here.
265
// For layer, we use the same convention as thin3d, where layer = -1 means all layers together. For texturing, that means that you
266
// get an array texture view.
267
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int layer);
268
269
bool 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);
270
void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag);
271
272
void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag);
273
void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag);
274
275
// Deferred creation, like in GL. Unlike GL though, the purpose is to allow background creation and avoiding
276
// stalling the emulation thread as much as possible.
277
// We delay creating pipelines until the end of the current render pass, so we can create the right type immediately.
278
// Unless a variantBitmask is passed in, in which case we can just go ahead.
279
// WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it.
280
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, bool cacheLoad, const char *tag);
281
282
VKRPipelineLayout *CreatePipelineLayout(BindingType *bindingTypes, size_t bindingCount, bool geoShadersEnabled, const char *tag);
283
void DestroyPipelineLayout(VKRPipelineLayout *pipelineLayout);
284
285
void ReportBadStateForDraw();
286
287
void NudgeCompilerThread() {
288
compileMutex_.lock();
289
compileCond_.notify_one();
290
compileMutex_.unlock();
291
}
292
293
// This is the first call in a draw operation. Instead of asserting like we used to, you can now check the
294
// return value and skip the draw if we're in a bad state. In that case, call ReportBadState.
295
// The old assert wasn't very helpful in figuring out what caused it anyway...
296
bool BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VKRPipelineLayout *pipelineLayout) {
297
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && pipeline != nullptr);
298
if (!curRenderStep_ || curRenderStep_->stepType != VKRStepType::RENDER) {
299
return false;
300
}
301
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
302
data.cmd = VKRRenderCommand::BIND_GRAPHICS_PIPELINE;
303
pipelinesToCheck_.push_back(pipeline);
304
data.graphics_pipeline.pipeline = pipeline;
305
data.graphics_pipeline.pipelineLayout = pipelineLayout;
306
// This can be used to debug cases where depth/stencil rendering is used on color-only framebuffers.
307
// if ((flags & PipelineFlags::USES_DEPTH_STENCIL) && curRenderStep_->render.framebuffer && !curRenderStep_->render.framebuffer->HasDepth()) {
308
// DebugBreak();
309
// }
310
curPipelineFlags_ |= flags;
311
curPipelineLayout_ = pipelineLayout;
312
return true;
313
}
314
315
void SetViewport(const VkViewport &vp) {
316
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
317
_dbg_assert_((int)vp.width >= 0);
318
_dbg_assert_((int)vp.height >= 0);
319
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
320
data.cmd = VKRRenderCommand::VIEWPORT;
321
data.viewport.vp.x = vp.x;
322
data.viewport.vp.y = vp.y;
323
data.viewport.vp.width = vp.width;
324
data.viewport.vp.height = vp.height;
325
// We can't allow values outside this range unless we use VK_EXT_depth_range_unrestricted.
326
// Sometimes state mapping produces 65536/65535 which is slightly outside.
327
// TODO: This should be fixed at the source.
328
data.viewport.vp.minDepth = clamp_value(vp.minDepth, 0.0f, 1.0f);
329
data.viewport.vp.maxDepth = clamp_value(vp.maxDepth, 0.0f, 1.0f);
330
curStepHasViewport_ = true;
331
}
332
333
// It's OK to set scissor outside the valid range - the function will automatically clip.
334
void SetScissor(int x, int y, int width, int height) {
335
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
336
337
if (x < 0) {
338
width += x; // since x is negative, this shrinks width.
339
x = 0;
340
}
341
if (y < 0) {
342
height += y;
343
y = 0;
344
}
345
346
if (x + width > curWidth_) {
347
width = curWidth_ - x;
348
}
349
if (y + height > curHeight_) {
350
height = curHeight_ - y;
351
}
352
353
// Check validity.
354
if (width < 0 || height < 0 || x >= curWidth_ || y >= curHeight_) {
355
// TODO: If any of the dimensions are now zero or negative, we should flip a flag and not do draws, probably.
356
// Instead, if we detect an invalid scissor rectangle, we just put a 1x1 rectangle in the upper left corner.
357
x = 0;
358
y = 0;
359
width = 1;
360
height = 1;
361
}
362
363
VkRect2D rc;
364
rc.offset.x = x;
365
rc.offset.y = y;
366
rc.extent.width = width;
367
rc.extent.height = height;
368
369
curRenderArea_.Apply(rc);
370
371
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
372
data.cmd = VKRRenderCommand::SCISSOR;
373
data.scissor.scissor = rc;
374
curStepHasScissor_ = true;
375
}
376
377
void SetStencilParams(uint8_t writeMask, uint8_t compareMask, uint8_t refValue) {
378
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
379
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
380
data.cmd = VKRRenderCommand::STENCIL;
381
data.stencil.stencilWriteMask = writeMask;
382
data.stencil.stencilCompareMask = compareMask;
383
data.stencil.stencilRef = refValue;
384
}
385
386
void SetBlendFactor(uint32_t color) {
387
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
388
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
389
data.cmd = VKRRenderCommand::BLEND;
390
data.blendColor.color = color;
391
}
392
393
void PushConstants(VkPipelineLayout pipelineLayout, VkShaderStageFlags stages, int offset, int size, void *constants) {
394
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
395
_dbg_assert_(size + offset < 40);
396
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
397
data.cmd = VKRRenderCommand::PUSH_CONSTANTS;
398
data.push.stages = stages;
399
data.push.offset = offset;
400
data.push.size = size;
401
memcpy(data.push.data, constants, size);
402
}
403
404
void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask);
405
406
// Cheaply set that we don't care about the contents of a surface at the start of the current render pass.
407
// This set the corresponding load-op of the current render pass to DONT_CARE.
408
// Useful when we don't know at bind-time whether we will overwrite the surface or not.
409
void SetLoadDontCare(VkImageAspectFlags aspects) {
410
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
411
if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)
412
curRenderStep_->render.colorLoad = VKRRenderPassLoadAction::DONT_CARE;
413
if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
414
curRenderStep_->render.depthLoad = VKRRenderPassLoadAction::DONT_CARE;
415
if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)
416
curRenderStep_->render.stencilLoad = VKRRenderPassLoadAction::DONT_CARE;
417
}
418
419
// Cheaply set that we don't care about the contents of a surface at the end of the current render pass.
420
// This set the corresponding store-op of the current render pass to DONT_CARE.
421
void SetStoreDontCare(VkImageAspectFlags aspects) {
422
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
423
if (aspects & VK_IMAGE_ASPECT_COLOR_BIT)
424
curRenderStep_->render.colorStore = VKRRenderPassStoreAction::DONT_CARE;
425
if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
426
curRenderStep_->render.depthStore = VKRRenderPassStoreAction::DONT_CARE;
427
if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT)
428
curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE;
429
}
430
431
// Descriptors will match the current pipeline layout, set by the last call to BindPipeline.
432
// Count is the count of void*s. Two are needed for COMBINED_IMAGE_SAMPLER, everything else is a single one.
433
// The goal is to keep this function very small and fast, and do the expensive work on the render thread or
434
// another thread.
435
PackedDescriptor *PushDescriptorSet(int count, int *descSetIndex) {
436
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
437
438
int curFrame = vulkan_->GetCurFrame();
439
440
VKRPipelineLayout::FrameData &data = curPipelineLayout_->frameData[curFrame];
441
442
size_t offset = data.descData_.size();
443
PackedDescriptor *retval = data.descData_.extend_uninitialized(count);
444
445
int setIndex = (int)data.descSets_.size();
446
PendingDescSet &descSet = data.descSets_.push_uninitialized();
447
descSet.offset = (uint32_t)offset;
448
descSet.count = count;
449
// descSet.set = VK_NULL_HANDLE; // to be filled in
450
*descSetIndex = setIndex;
451
return retval;
452
}
453
454
void Draw(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
455
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
456
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
457
data.cmd = VKRRenderCommand::DRAW;
458
data.draw.count = count;
459
data.draw.offset = offset;
460
data.draw.descSetIndex = descSetIndex;
461
data.draw.vbuffer = vbuffer;
462
data.draw.voffset = voffset;
463
data.draw.numUboOffsets = numUboOffsets;
464
_dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.draw.uboOffsets));
465
for (int i = 0; i < numUboOffsets; i++)
466
data.draw.uboOffsets[i] = uboOffsets[i];
467
curRenderStep_->render.numDraws++;
468
}
469
470
void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances) {
471
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
472
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
473
data.cmd = VKRRenderCommand::DRAW_INDEXED;
474
data.drawIndexed.count = count;
475
data.drawIndexed.instances = numInstances;
476
data.drawIndexed.descSetIndex = descSetIndex;
477
data.drawIndexed.vbuffer = vbuffer;
478
data.drawIndexed.voffset = voffset;
479
data.drawIndexed.ibuffer = ibuffer;
480
data.drawIndexed.ioffset = ioffset;
481
data.drawIndexed.numUboOffsets = numUboOffsets;
482
_dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.drawIndexed.uboOffsets));
483
for (int i = 0; i < numUboOffsets; i++)
484
data.drawIndexed.uboOffsets[i] = uboOffsets[i];
485
curRenderStep_->render.numDraws++;
486
}
487
488
// These can be useful both when inspecting in RenderDoc, and when manually inspecting recorded commands
489
// in the debugger.
490
void DebugAnnotate(const char *annotation) {
491
_dbg_assert_(curRenderStep_);
492
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
493
data.cmd = VKRRenderCommand::DEBUG_ANNOTATION;
494
data.debugAnnotation.annotation = annotation;
495
}
496
497
VkCommandBuffer GetInitCmd();
498
499
bool CreateBackbuffers();
500
void DestroyBackbuffers();
501
502
bool HasBackbuffers() {
503
return queueRunner_.HasBackbuffers();
504
}
505
506
void SetInflightFrames(int f) {
507
newInflightFrames_ = f < 1 || f > VulkanContext::MAX_INFLIGHT_FRAMES ? VulkanContext::MAX_INFLIGHT_FRAMES : f;
508
}
509
510
VulkanContext *GetVulkanContext() {
511
return vulkan_;
512
}
513
514
// Be careful with this. Only meant to be used for fetching render passes for shader cache initialization.
515
VulkanQueueRunner *GetQueueRunner() {
516
return &queueRunner_;
517
}
518
519
std::string GetGpuProfileString() const {
520
return frameData_[vulkan_->GetCurFrame()].profile.profileSummary;
521
}
522
523
bool NeedsSwapchainRecreate() const {
524
// Accepting a few of these makes shutdown simpler.
525
return outOfDateFrames_ > VulkanContext::MAX_INFLIGHT_FRAMES;
526
}
527
528
VulkanBarrierBatch &PostInitBarrier() {
529
return postInitBarrier_;
530
}
531
532
void ResetStats();
533
534
void StartThreads();
535
void StopThreads();
536
537
size_t GetNumSteps() const {
538
return steps_.size();
539
}
540
541
private:
542
void EndCurRenderStep();
543
544
void RenderThreadFunc();
545
void CompileThreadFunc();
546
547
void Run(VKRRenderThreadTask &task);
548
549
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
550
void FlushSync();
551
552
void PresentWaitThreadFunc();
553
void PollPresentTiming();
554
555
void ResetDescriptorLists(int frame);
556
void FlushDescriptors(int frame);
557
558
void SanityCheckPassesOnAdd();
559
560
FrameDataShared frameDataShared_;
561
562
FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];
563
int newInflightFrames_ = -1;
564
int inflightFramesAtStart_ = 0;
565
566
int outOfDateFrames_ = 0;
567
568
// Submission time state
569
570
// Note: These are raw backbuffer-sized. Rotated.
571
int curWidthRaw_ = -1;
572
int curHeightRaw_ = -1;
573
574
// Pre-rotation (as you'd expect).
575
int curWidth_ = -1;
576
int curHeight_ = -1;
577
578
bool insideFrame_ = false;
579
// probably doesn't need to be atomic.
580
std::atomic<bool> runCompileThread_;
581
582
bool useRenderThread_ = true;
583
bool measurePresentTime_ = false;
584
585
// This is the offset within this frame, in case of a mid-frame sync.
586
VKRStep *curRenderStep_ = nullptr;
587
bool curStepHasViewport_ = false;
588
bool curStepHasScissor_ = false;
589
PipelineFlags curPipelineFlags_{};
590
BoundingRect curRenderArea_;
591
592
std::vector<VKRStep *> steps_;
593
594
// Execution time state
595
VulkanContext *vulkan_;
596
std::thread renderThread_;
597
VulkanQueueRunner queueRunner_;
598
599
// For pushing data on the queue.
600
std::mutex pushMutex_;
601
std::condition_variable pushCondVar_;
602
603
std::queue<VKRRenderThreadTask *> renderThreadQueue_;
604
605
// For readbacks and other reasons we need to sync with the render thread.
606
std::mutex syncMutex_;
607
std::condition_variable syncCondVar_;
608
609
// Shader compilation thread to compile while emulating the rest of the frame.
610
// Only one right now but we could use more.
611
std::thread compileThread_;
612
// Sync
613
std::condition_variable compileCond_;
614
std::mutex compileMutex_;
615
std::vector<CompileQueueEntry> compileQueue_;
616
617
// Thread for measuring presentation delay.
618
std::thread presentWaitThread_;
619
620
// pipelines to check and possibly create at the end of the current render pass.
621
std::vector<VKRGraphicsPipeline *> pipelinesToCheck_;
622
623
// For nicer output in the little internal GPU profiler.
624
SimpleStat initTimeMs_;
625
SimpleStat totalGPUTimeMs_;
626
SimpleStat renderCPUTimeMs_;
627
SimpleStat descUpdateTimeMs_;
628
629
VulkanBarrierBatch postInitBarrier_;
630
631
std::function<void(InvalidationCallbackFlags)> invalidationCallback_;
632
633
uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;
634
HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;
635
636
VKRPipelineLayout *curPipelineLayout_ = nullptr;
637
std::vector<VKRPipelineLayout *> pipelineLayouts_;
638
};
639
640