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